• Stars
    star
    28
  • Rank 882,216 (Top 18 %)
  • Language
    Go
  • License
    MIT License
  • Created over 5 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library to notify about any (pluggable) activity on your machine, and let you take action as needed

Activity tracker

Mentioned in Awesome Go codecov Go Report Card version

It is a libary that lets you monitor certain activities on your machine, and then sends a heartbeat at a periodic (configurable) time detailing all the activity changes during that time. The activities that you want to track are monitored by pluggable handlers for those activities and can be added or removed according to your needs. An example of an activity is MouseCursorActivity, i.e. whether your mouse cursor was moved or not.

Installation

go get -u github.com/prashantgupta24/activity-tracker

Usage

heartbeatInterval := 60 //value always in seconds
workerInterval := 5     //seconds

activityTracker := &tracker.Instance{
	HeartbeatInterval: heartbeatInterval,
	WorkerInterval:    workerInterval,
	LogLevel:          logging.Info,
}

//This starts the tracker for all handlers currently implemented. It gives you a channel on
//which you can listen to for heartbeat objects
heartbeatCh := activityTracker.Start()

//if you only want to track certain handlers, you can use StartWithhandlers
//heartbeatCh := activityTracker.StartWithHandlers(handler.MouseClickHandler(), handler.MouseCursorHandler())


select {
case heartbeat := <-heartbeatCh:
	if !heartbeat.WasAnyActivity {
		logger.Infof("no activity detected in the last %v seconds", int(heartbeatInterval))
	} else {
		logger.Infof("activity detected in the last %v seconds.", int(heartbeatInterval))
		logger.Infof("Activity type:\n")
		for activityType, times := range heartbeat.ActivityMap {
			logger.Infof("activityType : %v times: %v\n", activityType, len(times))
		}
	}
}

Output

The above code created a tracker with all (Mouse-click, Mouse-movement, screen-change and machine-sleep) handlers activated. The heartbeat Interval is set to 60 seconds, i.e. every 60 seconds I received a heartbeat which mentioned all activities that were captured.

INFO[2019-03-30T15:52:01-07:00] starting activity tracker with 60s heartbeat and 5s worker Interval... 

INFO[2019-03-30T15:53:01-07:00] activity detected in the last 60 seconds.    

INFO[2019-03-30T15:53:01-07:00] Activity type:                               
INFO[2019-03-30T15:53:01-07:00] activityType : mouse-click times: 10         
INFO[2019-03-30T15:53:01-07:00] activityType : cursor-move times: 12
INFO[2019-03-30T15:53:01-07:00] activityType : screen-change times: 7
INFO[2019-03-30T15:53:01-07:00] activityType : machine-sleep times: 1
INFO[2019-03-30T15:53:01-07:00] activityType : machine-wake times: 1

How it works

There are 2 primary configs required for the tracker to work:

  • HeartbeatInterval

The Interval at which you want the heartbeat (in seconds, default 60s)

  • WorkerInterval

The Interval at which you want the checks to happen within a heartbeat (default 60s).

The activity tracker gives you a heartbeat object every 60 seconds, that is based on the HeartbeatInterval. But there is something else to understand here. In order for the tracker to know how many times an activity occured, like how many times you moved the cursor for example, it needs to query the mouse position every x seconds. That's where the WorkerInterval comes into play.

The WorkerInterval tells the tracker how frequently to check for an activity within a heartbeat. It does that by querying the handler associated with that activity. Let's say you want to know how many times the mouse cursor was moved within 60 seconds. You need to constantly ask the mouseCursorHandler every x seconds to see if the cursor moved. What you want to do is to start the tracker with the usual 60s HeartbeatInterval , configured with a Mouse-cursor handler. In this case, you set the WorkerInterval to 5 seconds. The tracker will then keep asking the mouse cursor handler every 5 seconds to see if there was a movement, and keep track each time there was a change. At the end of HeartbeatInterval, it will construct the heartbeat with all the changes and send it.

For example, in the output that you saw above, it says cursor-move times: 12. That doesn't mean the cursor was moved only 12 times. Since the WorkerInterval was 5 seconds in the example, that means cursorHandler was asked every 5 seconds (i.e. 12 times in 60 seconds) whether the cursor moved. And it replied that the cursor had indeed moved everytime.

Note : This is applicable only to pull-based handlers. For push-based handlers, WorkerInterval does not matter.

  • If you want to know how many times an activity occured within a heartbeat, you might want to set the WorkerInterval to a low value, so that it keeps quering the handlers.

  • If you are just concerned whether any activity happened within a heartbeat or not, you can set WorkerInterval to a high number (something around 10-15 seconds should do the trick). That way, the workers need not be bothered a lot of times within a heartbeat.

Note: If the WorkerInterval and the HeartbeatInterval are set the same, then the WorkerInterval always is started a fraction of a second before the HeartbeatInterval kicks in. This is done so that when the heartbeat is going to be generated at the end of HeartbeatInterval, the worker should have done its job of querying each of the handlers before that.

Usecase

Suppose you want to track Activities A, B and C on your machine, and you want to know how many times they occured every minute.

You want a report at the end of every minute saying Activity A happened 5 times, Activity B happened 3 times and Activity C happened 2 times.

First, you need to create a Handler for each of those activities. See sections below on how to create one. The main tracker object will simply ask each of the handlers every WorkerInterval amout of time whether that activity happened or not at that moment.

As another example, let's say you want to monitor whether there was any mouse click on your machine and you want to be notified every 5 minutes. What you do is start the Activity Tracker with just the mouse click handler and heartbeat Interval set to 5 minutes. The Start function of the library gives you a channel which receives a heartbeat every 5 minutes, and it has details on whether there was a click in those 5 minutes, and if yes, the times the click happened.

Components

Heartbeat struct

It is the data packet sent from the tracker library to the user.

type Heartbeat struct {
	WasAnyActivity bool
	ActivityMap    map[activity.Type][]time.Time //activity type with its times
	Time           time.Time                     //heartbeat time
}

WasAnyActivity tells if there was any activity within that time frame If there was, then the ActivityMap will tell you what type of activity it was and what all times it occured.

The Time field is the time of the Heartbeat sent (not to be confused with the activity time, which is the time the activity occured within the heartbeat).

Tracker

The tracker is the main struct for the library. The fields inside it are:

HeartbeatInterval int //the interval at which you want the heartbeat (in seconds, default 60s)
WorkerInterval    int //the interval at which you want the checks to happen within a heartbeat (in seconds, default 5s)
LogLevel          string //info or debug
LogFormat         string //text or json

- HeartbeatInterval

The Interval at which you want the heartbeat (in seconds, default 60s)

The HeartbeatInterval value can be set anywhere between 60 seconds - 300 seconds. Not setting it or setting it to anything other than the allowed range will revert it to default of 60s.

- WorkerInterval

The Interval at which you want the checks to happen within a heartbeat (default 60s).

The WorkerInterval value can be set anywhere between 4 seconds - 60 seconds. It CANNOT be more than HeartbeatInterval for obvious reasons. Not setting it or setting it to anything other than the allowed range will revert it to default of 60s.

State

The system.State struct captures the current state of the tracker, and the whole system in general. It is used by some of the handlers to respond to a certain system state.

It is passed to the handlers when performing the Trigger, so that the handlers can take an informed decision on whether to get activated or not at that instance.

For example, the sleepHandler changes the state of the system to sleeping, so that the mouseCursorHandler and mouseClickHandler don't need to do any work while the system remains in the sleep state.

Note: It also serves as a way of inter-handler communication.

Types of handlers

There are 2 types of handlers:

  • Push based
  • Pull based

The push based ones are those that automatically push to the tracker object when an activity happened. Examples are the mouseClickHander and machineSleepHandler. Whenever a mouse-click/machine-sleep happens, it sends the activity to the tracker object.

The pull based ones are those that the tracker has to ask the handler to know if there was any activity happening at that moment. Examples are mouseCursorHandler and screenChangeHandler. The asking is done through the Trigger function implemented by handlers.

It is up to you to define how to implement the handler. Some make sense to be pull based, since it is going to be memory intensive to make the mouse cursor movement handler push-based. It made sense to make it pull based.

New pluggable handlers for activities

//Handler interface
Start(*log.Logger, chan *activity.Instance)
Type() activity.Type
Trigger(system.State) //used to activate pull-based handlers
Close()

Any new type of handler for an activity can be easily added, it just needs to implement the above Handler interface and define what type of activity it is going to track (also add the new activity as well if it's a new activity), that's it! It can be plugged in with the tracker and then the tracker will include those activity checks in its heartbeat.

Note: Handlers have a one-to-many relationship with activity, i.e. each Handler can be associated with one or more activity (That becomes the value returned by handler's Type) On the other hand, each activity should be tracked by only ONE handler (which makes sense). As a fail-safe, if the tracker is started with more than one handler tracking the same activity, then only 1 handler will get registered for that activity.

Currently supported list of activities/handlers

Activities

MouseCursorMovement Type = "cursor-move"
MouseClick          Type = "mouse-click"
ScreenChange        Type = "screen-change"
MachineSleep        Type = "machine-sleep"
MachineWake         Type = "machine-wake"

Corresponding handlers

mouseCursorHandler
mouseClickHandler
screenChangeHandler
machineSleepHandler
  • Mouse click (whether any mouse click happened during the time frame)
  • Mouse cursor movement (whether the mouse cursor was moved during the time frame)
  • Screen change handler (whether the active window was changed)
  • Machine sleep/wake handler (this is added by default for fail-safe measures)

Thanks to robotgo for making a lot of the handlers possible.

Example

Check out the example here

Projects using this library

More Repositories

1

firewalld-rest

A rest application to update firewalld rules on a linux server
Go
338
star
2

automatic-mouse-mover

a minimalistic go library/app to keep your mac active and alive
Go
288
star
3

mac-sleep-notifier

macOS Sleep/ Wake notifications in golang
Go
32
star
4

lua-top-down-multiplayer

A simple top-down multiplayer game built using LÖVE
Lua
19
star
5

clipboard-manager

Clippy - A minimalistic clipboard manager in python
Python
16
star
6

go-clip

A minimalistic clipboard manager for Mac.
Go
13
star
7

snake-javascript

Learning sockets and javascript the fun way. This is the snake game using javascript and p5! Includes a leaderboard! (p5.js + socket.io + cookies + redis backend)
JavaScript
8
star
8

OpenWeatherMap

An app to query the OpenWeatherMap api and display the results on an HTML page. Uses Ajax, Express and Needle
HTML
2
star
9

python-air-hockey

An air hockey game in python using pyxel
Python
2
star
10

Ring-Message

An Android app which lets you save personalized messages for contacts, so when they call you or you call them, message pops up on screen reminding you what to ask or say!
Java
2
star
11

MyWebApplication

A simple web application which uses Servlets, JSP's, JSTL, Ajax and Hibernate
Java
2
star
12

bulk-email-downloader

A bulk email downloader from an IMAP server written in go
Go
2
star
13

vercel-js

JavaScript
1
star
14

go-fantasy

A golang based fantasy premier league scraper!
Go
1
star
15

go-design-patterns

go-design-patterns
Go
1
star
16

git-test

Testing git features
1
star
17

Salt-Stack

An attempt to understand Salt Stack and it's use in configuration management
Shell
1
star
18

go_scraper

a web scraper in go
Go
1
star
19

semantic-release-golang

A repo to test automatic semantic releaser for golang
1
star
20

mozart

An orchestrator to control script execution - moved to URL below
1
star
21

rock-paper-scissors

Playing rock, paper and scissors using Visual Recognition (with IBM Watson) using the webcam!
Python
1
star
22

go-chat

HTML
1
star
23

Coderbyte-challenges

Challenges completed for coderbyte.com
Java
1
star
24

bash_backup

Shell
1
star
25

go-fantasy-grpc

The gRPC implementation of my go-fantasy application. Purely for learning go-lang and grpc!
Go
1
star
26

prashantgupta24

1
star
27

tic-tac-toe

the famous tic-tac-toe in javascript! Complete with AI or 2 player ! (minimax + socket.io)
JavaScript
1
star
28

REST-messenger

A messenger application using RESTful web services
Java
1
star
29

python-web-scraper

A scraper in python for collecting car data from a website
Jupyter Notebook
1
star