{"id":13413146,"url":"https://github.com/prashantgupta24/activity-tracker","last_synced_at":"2025-12-26T14:54:11.315Z","repository":{"id":57496891,"uuid":"175297543","full_name":"prashantgupta24/activity-tracker","owner":"prashantgupta24","description":"A library to notify about any (pluggable) activity on your machine, and let you take action as needed","archived":false,"fork":false,"pushed_at":"2023-10-13T21:37:23.000Z","size":9002,"stargazers_count":28,"open_issues_count":2,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-07-31T20:51:56.175Z","etag":null,"topics":["activity","go","golang","golang-library","idle","tracker"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prashantgupta24.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-03-12T21:16:44.000Z","updated_at":"2024-05-18T04:43:12.000Z","dependencies_parsed_at":"2024-01-08T15:34:34.572Z","dependency_job_id":null,"html_url":"https://github.com/prashantgupta24/activity-tracker","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prashantgupta24%2Factivity-tracker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prashantgupta24%2Factivity-tracker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prashantgupta24%2Factivity-tracker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prashantgupta24%2Factivity-tracker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prashantgupta24","download_url":"https://codeload.github.com/prashantgupta24/activity-tracker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221498723,"owners_count":16833053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activity","go","golang","golang-library","idle","tracker"],"created_at":"2024-07-30T20:01:34.108Z","updated_at":"2025-12-26T14:54:11.288Z","avatar_url":"https://github.com/prashantgupta24.png","language":"Go","readme":"# Activity tracker\n\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) [![codecov](https://codecov.io/gh/prashantgupta24/activity-tracker/branch/master/graph/badge.svg)](https://codecov.io/gh/prashantgupta24/activity-tracker) [![Go Report Card](https://goreportcard.com/badge/github.com/prashantgupta24/activity-tracker)](https://goreportcard.com/report/github.com/prashantgupta24/activity-tracker) [![version][version-badge]][RELEASES]\n\nIt is a libary that lets you monitor certain activities on your machine, and then sends a [heartbeat](https://github.com/prashantgupta24/activity-tracker#heartbeat-struct) 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.\n\n## Installation\n\n` go get -u github.com/prashantgupta24/activity-tracker`\n\n## Usage\n\n```go\nheartbeatInterval := 60 //value always in seconds\nworkerInterval := 5     //seconds\n\nactivityTracker := \u0026tracker.Instance{\n\tHeartbeatInterval: heartbeatInterval,\n\tWorkerInterval:    workerInterval,\n\tLogLevel:          logging.Info,\n}\n\n//This starts the tracker for all handlers currently implemented. It gives you a channel on\n//which you can listen to for heartbeat objects\nheartbeatCh := activityTracker.Start()\n\n//if you only want to track certain handlers, you can use StartWithhandlers\n//heartbeatCh := activityTracker.StartWithHandlers(handler.MouseClickHandler(), handler.MouseCursorHandler())\n\n\nselect {\ncase heartbeat := \u003c-heartbeatCh:\n\tif !heartbeat.WasAnyActivity {\n\t\tlogger.Infof(\"no activity detected in the last %v seconds\", int(heartbeatInterval))\n\t} else {\n\t\tlogger.Infof(\"activity detected in the last %v seconds.\", int(heartbeatInterval))\n\t\tlogger.Infof(\"Activity type:\\n\")\n\t\tfor activityType, times := range heartbeat.ActivityMap {\n\t\t\tlogger.Infof(\"activityType : %v times: %v\\n\", activityType, len(times))\n\t\t}\n\t}\n}\n```\n\n## Output\n\nThe 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.\n\n```sh\nINFO[2019-03-30T15:52:01-07:00] starting activity tracker with 60s heartbeat and 5s worker Interval... \n\nINFO[2019-03-30T15:53:01-07:00] activity detected in the last 60 seconds.    \n\nINFO[2019-03-30T15:53:01-07:00] Activity type:                               \nINFO[2019-03-30T15:53:01-07:00] activityType : mouse-click times: 10         \nINFO[2019-03-30T15:53:01-07:00] activityType : cursor-move times: 12\nINFO[2019-03-30T15:53:01-07:00] activityType : screen-change times: 7\nINFO[2019-03-30T15:53:01-07:00] activityType : machine-sleep times: 1\nINFO[2019-03-30T15:53:01-07:00] activityType : machine-wake times: 1\n```\n\n## How it works\n\nThere are 2 primary configs required for the tracker to work:\n\n- `HeartbeatInterval ` \n\n\u003e The Interval at which you want the heartbeat (in seconds, default 60s)\n\n\n- `WorkerInterval` \n\n\u003e The Interval at which you want the checks to happen within a heartbeat (default 60s).\n\nThe activity tracker gives you a [heartbeat](https://github.com/prashantgupta24/activity-tracker#heartbeat-struct) 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.\n\nThe `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.\n\nFor 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. \n\n**Note :** This is applicable only to [pull-based handlers](https://github.com/prashantgupta24/activity-tracker#types-of-handlers). For push-based handlers, `WorkerInterval` does not matter.\n\n- 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.\n\n- 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`.\n\n\n\u003e 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. \n\n## Usecase\n\nSuppose you want to track Activities A, B and C on your machine, and you want to know how many times they occured every minute. \n\nYou 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.\n\nFirst, 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.\n\nAs 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.\n\n\n# Components\n\n### Heartbeat struct\n\nIt is the data packet sent from the tracker library to the user.\n\n```go\ntype Heartbeat struct {\n\tWasAnyActivity bool\n\tActivityMap    map[activity.Type][]time.Time //activity type with its times\n\tTime           time.Time                     //heartbeat time\n}\n```\n\n`WasAnyActivity` tells if there was any activity within that time frame\nIf there was, then the `ActivityMap` will tell you what type of activity it was and what all times it occured.\n\nThe `Time` field is the time of the Heartbeat sent (not to be confused with\nthe activity time, which is the time the activity occured within the `heartbeat`). \n\n### Tracker\n\nThe tracker is the main struct for the library. The fields inside it are:\n\n```go\nHeartbeatInterval int //the interval at which you want the heartbeat (in seconds, default 60s)\nWorkerInterval    int //the interval at which you want the checks to happen within a heartbeat (in seconds, default 5s)\nLogLevel          string //info or debug\nLogFormat         string //text or json\n```\n\n#### - `HeartbeatInterval` \n\nThe Interval at which you want the heartbeat (in seconds, default 60s)\n\n\u003e 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.\n\n#### - `WorkerInterval` \n\nThe Interval at which you want the checks to happen within a heartbeat (default 60s).\n\n\u003e 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.\n\n### State\n\nThe `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. \n\nIt 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.\n\nFor 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.\n\n\u003e Note: It also serves as a way of inter-handler communication.\n\n## Types of handlers\n\nThere are 2 types of handlers:\n\n- Push based\n- Pull based\n\n\nThe `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.\n\nThe `pull` based ones are those that the `tracker` has to ask the handler to know if there was any activity happening at that moment.\nExamples are `mouseCursorHandler` and `screenChangeHandler`. The `asking` is done through the `Trigger` function implemented by handlers.\n\nIt 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.\n\n## New pluggable handlers for activities\n\n```go\n//Handler interface\nStart(*log.Logger, chan *activity.Instance)\nType() activity.Type\nTrigger(system.State) //used to activate pull-based handlers\nClose()\n```\n\t\nAny 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.\n\n\u003e 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).\n\u003e 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.\n\n## Currently supported list of activities/handlers\n\n\n### Activities\n\n```go\nMouseCursorMovement Type = \"cursor-move\"\nMouseClick          Type = \"mouse-click\"\nScreenChange        Type = \"screen-change\"\nMachineSleep        Type = \"machine-sleep\"\nMachineWake         Type = \"machine-wake\"\n```\n\n### Corresponding handlers\n\n```go\nmouseCursorHandler\nmouseClickHandler\nscreenChangeHandler\nmachineSleepHandler\n```\n\t\n- Mouse click (whether any mouse click happened during the time frame)\n- Mouse cursor movement (whether the mouse cursor was moved during the time frame)\n- Screen change handler (whether the active window was changed)\n- Machine sleep/wake handler (**this is added by default for fail-safe measures**)\n\nThanks to [robotgo](https://github.com/go-vgo/robotgo) for making a lot of the handlers possible.\n\n## Example\n\nCheck out the example [here](https://github.com/prashantgupta24/activity-tracker/blob/master/example/example.go)\n\n## Projects using this library\n\n- [automatic-mouse-mover](https://github.com/prashantgupta24/automatic-mouse-mover): a minimalistic go library/app to prevent your mac from going to sleep\n\n\n[version-badge]: https://img.shields.io/github/release/prashantgupta24/activity-tracker.svg\n[RELEASES]: https://github.com/prashantgupta24/activity-tracker/releases\n","funding_links":[],"categories":["GUI","Relational Databases"],"sub_categories":["Advanced Console UIs","Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprashantgupta24%2Factivity-tracker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprashantgupta24%2Factivity-tracker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprashantgupta24%2Factivity-tracker/lists"}