From 2a1ee9df416d2f4e9f668f5c06136828db35abfd Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 13:06:15 +0100 Subject: [PATCH 1/8] init of plugin --- .gitignore | 3 ++- plugins/script/config.go | 1 + plugins/script/plugin.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 plugins/script/config.go create mode 100644 plugins/script/plugin.go diff --git a/.gitignore b/.gitignore index c237551..e6f8dd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -./config \ No newline at end of file +./config +main* \ No newline at end of file diff --git a/plugins/script/config.go b/plugins/script/config.go new file mode 100644 index 0000000..4759160 --- /dev/null +++ b/plugins/script/config.go @@ -0,0 +1 @@ +package script diff --git a/plugins/script/plugin.go b/plugins/script/plugin.go new file mode 100644 index 0000000..f954d8e --- /dev/null +++ b/plugins/script/plugin.go @@ -0,0 +1,28 @@ +package script + +import ( + "github.com/phrp720/aw-sync-agent-plugins/models" +) + +type Plugin struct{} + +func (f *Plugin) Initialize() { + +} + +func (f *Plugin) Execute(events models.Events, watcher string, userID string, includeHostName bool) models.Events { + + return events +} + +func (f *Plugin) ReplicateConfig(path string) { + +} + +func (f *Plugin) Name() string { + return "aw-plugin-" + f.RawName() + ".yaml" +} + +func (f *Plugin) RawName() string { + return "script" +} From b3462841bb718541dff8e5226ac0b1952eda6b68 Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 14:24:56 +0100 Subject: [PATCH 2/8] script plugin implementation --- .gitignore | 2 +- config-examples/aw-plugin-filters.yaml | 209 +++++++++++++++++++++++++ config-examples/aw-plugin-scripts.yaml | 8 + models/constants.go | 7 + models/script.go | 10 ++ plugins/filter/operations.go | 15 +- plugins/filter/plugin.go | 7 +- plugins/manager.go | 3 + plugins/script/config.go | 43 +++++ plugins/script/operations.go | 50 ++++++ plugins/script/plugin.go | 68 +++++++- util/util.go | 21 +++ 12 files changed, 426 insertions(+), 17 deletions(-) create mode 100644 config-examples/aw-plugin-filters.yaml create mode 100644 config-examples/aw-plugin-scripts.yaml create mode 100644 models/constants.go create mode 100644 models/script.go create mode 100644 plugins/script/operations.go create mode 100644 util/util.go diff --git a/.gitignore b/.gitignore index e6f8dd1..7bcef76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .idea -./config +config main* \ No newline at end of file diff --git a/config-examples/aw-plugin-filters.yaml b/config-examples/aw-plugin-filters.yaml new file mode 100644 index 0000000..ca07e5a --- /dev/null +++ b/config-examples/aw-plugin-filters.yaml @@ -0,0 +1,209 @@ +Filters: + - Filter: + filter-name: Email Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: Gmail|Thunderbird|mutt|alpine|Yahoo|Hotmail + category: Email + + - Filter: + filter-name: Programming Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: GitHub|Stack Overflow|BitBucket|Gitlab|vim|Spyder|kate|Ghidra|Scite|code|eclipse|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual + category: Programming + + - Filter: + filter-name: Image Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: GIMP|Inkscape|Photoshop|Krita|Paint|Paint3D|Paint.NET|PaintShop|CorelDRAW|Illustrator|Lightroom|Acorn|Affinity|Pixelmator|Sketch|Figma|Gravit|Vectr|Canva|Photopea|SumoPaint|Pinta|Seashore|MyPaint|TuxPaint|KolourPaint|Karbon|CinePaint + category: Image + + - Filter: + filter-name: 3D Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: Blender|Maya|3ds Max|Cinema 4D|ZBrush|Houdini|Modo|LightWave|Rhino|SketchUp|AutoCAD|SolidWorks|Fusion 360|Tinkercad|OpenSCAD|FreeCAD|Sculptris|SculptGL|Clara.io|Vectary|Onshape|Shapr3D|SelfCAD|BlocksCAD|Morphi|TinkerCAD|Tinkercad|TinkerCAD + category: "3D" + + - Filter: + filter-name: Audio Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: Audacity|Ardour|LMMS|Reaper|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound Forge|Acid Pro|FL Studio|Ableton Live|Pro Tools|GarageBand|Logic Pro|Cubase|Reason|Bitwig Studio|Studio One|Cakewalk|Waveform|Tracktion|Mixcraft|Audition|Sound + category: Audio + + - Filter: + filter-name: Video Edit Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: Kdenlive|Shotcut|OpenShot|DaVinci Resolve|Lightworks|HitFilm Express|Avidemux|Blender + category: Video Edit + + - Filter: + filter-name: Video Category + enable: true + watchers: [] + target: + - key: app + value: Google.* + - key: title + value: YouTube|Plex|VLC + category: Video + + - Filter: + filter-name: Social Media Category + enable: true + watchers: [] + target: + - key: app + value: Google.* | Reddit | Facebook | Twitter | Instagram | devRant | LinkedIn | Pinterest | Snapchat | Tumblr | TikTok + - key: title + value: reddit|Facebook|Twitter|Instagram|devRant|LinkedIn|Pinterest|Snapchat|Tumblr|TikTok + category: Social Media + + - Filter: + filter-name: Music Category + enable: true + watchers: [] + target: + - key: app + value: Google.* | Spotify | Deezer | Apple Music | Amazon Music | Tidal | Pandora | SoundCloud | YouTube Music + - key: title + value: Spotify|Deezer|Apple Music|Amazon Music|Tidal|Pandora|SoundCloud|YouTube Music + category: Email + + - Filter: + filter-name: Communication Category + enable: true + watchers: [] + target: + - key: app + value: Google.* | Slack | Discord | Zoom | Microsoft Teams | Jitsi Meet | Cisco Webex | BlueJeans | GoToMeeting | Zoho Meeting + - key: title + value: Messenger|Telegram|Signal|WhatsApp|Rambox|Slack|Riot|Element|Discord|Nheko|NeoChat|Mattermost|Zulip|Rocket.Chat|Jitsi|Jami|Wire|Tox|Threema|Viber|Skype|Zoom|Microsoft Teams|Jitsi Meet|Cisco Webex|BlueJeans|GoToMeeting|Zoho Meeting + category: Communication + + + + + - Filter: + filter-name: "Youtube Plain Filtering" ## Name of the filter (optional) + enable: true + watchers: ## watchers where the filter will be applied (optional) + - "aw-watcher-window" + + target: ## Data Records that if match , do the filtering (mandatory) + + - key: "app" ## key to filter on + value: "Google.*" ## value to filter on REGEX + + - key: "title" ## key to filter on + value: "YouTube" ## value to filter on REGEX + + plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` + + - key: "title" ## key of record + value: "YouTube" ## value to replace + + - Filter: + filter-name: "Slack Plain Filtering" ## Name of the filter (optional) + enable: true + watchers: ## watchers where the filter will be applied (optional) + - "aw-watcher-window" + + target: ## Data Records that if match , do the filtering (mandatory) + + - key: "app" ## key to filter on + value: "Slack.*" ## value to filter on REGEX + + - key: "title" ## key to filter on + value: "Slack" ## value to filter on REGEX + + plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` + + - key: "title" ## key of record + value: "Slack" ## value to replace + + + - Filter: + filter-name: "Linkedin Plain Filtering" ## Name of the filter (optional) + enable: true + watchers: ## watchers where the filter will be applied (optional) + - "aw-watcher-window" + + target: ## Data Records that if match , do the filtering (mandatory) + + - key: "app" ## key to filter on + value: "Google.*" ## value to filter on REGEX + + - key: "title" ## key to filter on + value: "LinkedIn" ## value to filter on REGEX + + plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` + + - key: "title" ## key of record + value: "LinkedIn" ## value to replace + + - Filter: + filter-name: "VsCode Plain Filtering" ## Name of the filter (optional) + enable: true + watchers: ## watchers where the filter will be applied (optional) + - "aw-watcher-window" + + target: ## Data Records that if match , do the filtering (mandatory) + + - key: "app" ## key to filter on + value: "Google.*" ## value to filter on REGEX + + - key: "title" ## key to filter on + value: "Visual Studio Code" ## value to filter on REGEX + + plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` + + - key: "title" ## key of record + value: "VsCode" ## value to replace + + - Filter: + filter-name: "Grafana Plain Filtering" ## Name of the filter (optional) + enable: true + watchers: ## watchers where the filter will be applied (optional) + - "aw-watcher-window" + + target: ## Data Records that if match , do the filtering (mandatory) + + - key: "app" ## key to filter on + value: "Google.*" ## value to filter on REGEX + + - key: "title" ## key to filter on + value: "Grafana" ## value to filter on REGEX + + plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` + + - key: "title" ## key of record + value: "Grafana" ## value to replace \ No newline at end of file diff --git a/config-examples/aw-plugin-scripts.yaml b/config-examples/aw-plugin-scripts.yaml new file mode 100644 index 0000000..e2278c4 --- /dev/null +++ b/config-examples/aw-plugin-scripts.yaml @@ -0,0 +1,8 @@ +Scripts: + - Script: + name: "test-script.sh" + path: "/home/tester/Desktop/scripts/" + - Script: + name: "dummy-script.sh" + path: "/home/tester/Desktop/scripts/" + diff --git a/models/constants.go b/models/constants.go new file mode 100644 index 0000000..7ba95b6 --- /dev/null +++ b/models/constants.go @@ -0,0 +1,7 @@ +package models + +// Constants for the plugins RAW NAMES +const ( + FILTER = "filters" + SCRIPT = "scripts" +) diff --git a/models/script.go b/models/script.go new file mode 100644 index 0000000..396679a --- /dev/null +++ b/models/script.go @@ -0,0 +1,10 @@ +package models + +type ScriptConfig struct { + Scripts []Script `yaml:"Scripts"` +} + +type Script struct { + Name string `yaml:"name"` // ScriptName is the name of the script + Path string `yaml:"path"` // Path is the path of the script +} diff --git a/plugins/filter/operations.go b/plugins/filter/operations.go index 7ca9f2a..f00b117 100644 --- a/plugins/filter/operations.go +++ b/plugins/filter/operations.go @@ -3,6 +3,7 @@ package filter import ( "fmt" "github.com/phrp720/aw-sync-agent-plugins/models" + "github.com/phrp720/aw-sync-agent-plugins/util" "log" "strconv" "strings" @@ -43,7 +44,7 @@ func ValidateFilters(filters []models.Filter) ([]models.Filter, int, int, int) { func GetCategories(filters []models.Filter) []string { var categories []string for _, filter := range filters { - if filter.Category != "" && !contains(categories, filter.Category) { + if filter.Category != "" && !util.Contains(categories, filter.Category) { categories = append(categories, filter.Category) } } @@ -115,7 +116,7 @@ func Replace(data map[string]interface{}, plain []models.PlainReplace, regex []m func GetMatchingFilters(filters []models.Filter, watcher string) []models.Filter { var matchingFilters []models.Filter for _, filter := range filters { - if len(filter.Watchers) == 0 || contains(filter.Watchers, watcher) { + if len(filter.Watchers) == 0 || util.Contains(filter.Watchers, watcher) { matchingFilters = append(matchingFilters, filter) } } @@ -214,13 +215,3 @@ func PrintCategories(categories []string) { } fmt.Println(border) } - -// contains checks if a slice contains a given string -func contains(slice []string, item string) bool { - for _, s := range slice { - if s == item { - return true - } - } - return false -} diff --git a/plugins/filter/plugin.go b/plugins/filter/plugin.go index dfada5b..80e06af 100644 --- a/plugins/filter/plugin.go +++ b/plugins/filter/plugin.go @@ -30,6 +30,11 @@ func (f *Plugin) Initialize() { } func (f *Plugin) Execute(events models.Events, watcher string, userID string, includeHostName bool) models.Events { + + if config.Filters == nil { + return events + } + // Implementation //Apply the filters var modifiedEvents models.Events @@ -74,5 +79,5 @@ func (f *Plugin) Name() string { } func (f *Plugin) RawName() string { - return "filters" + return models.FILTER } diff --git a/plugins/manager.go b/plugins/manager.go index e6c6fc3..b9aa4e2 100644 --- a/plugins/manager.go +++ b/plugins/manager.go @@ -3,11 +3,14 @@ package plugins import ( "github.com/phrp720/aw-sync-agent-plugins/models" "github.com/phrp720/aw-sync-agent-plugins/plugins/filter" + "github.com/phrp720/aw-sync-agent-plugins/plugins/script" ) func Initialize() []models.Plugin { var plugins []models.Plugin + plugins = append(plugins, &filter.Plugin{}) + plugins = append(plugins, &script.Plugin{}) return plugins } diff --git a/plugins/script/config.go b/plugins/script/config.go index 4759160..06edebf 100644 --- a/plugins/script/config.go +++ b/plugins/script/config.go @@ -1 +1,44 @@ package script + +import ( + "github.com/phrp720/aw-sync-agent-plugins/models" + "gopkg.in/yaml.v3" + "io" + "log" + "os" + "path/filepath" +) + +// LoadYAMLConfig Load the YAML config file +func LoadYAMLConfig(filename string) models.ScriptConfig { + file, err := os.Open(filename) + + if err != nil { + log.Printf("No %s file found.", filename) + } else { + log.Printf("Loading scripts from %s file.", filename) + defer file.Close() + decoder := yaml.NewDecoder(file) + if err = decoder.Decode(&config); err != nil && err != io.EOF { + log.Fatalf("Error: Failed to decode filters file: %v", err) + } + + } + + return config +} + +// CreateConfigFile creates a config file to a given path based on the settings +func CreateConfigFile(path string, name string) error { + + fullPath := path + name + content, err := yaml.Marshal(&config) + if err != nil { + return err + } + dir := filepath.Dir(fullPath) + if err = os.MkdirAll(dir, 0755); err != nil { + return err + } + return os.WriteFile(fullPath, content, 0644) +} diff --git a/plugins/script/operations.go b/plugins/script/operations.go new file mode 100644 index 0000000..e30be5f --- /dev/null +++ b/plugins/script/operations.go @@ -0,0 +1,50 @@ +package script + +import ( + "fmt" + "github.com/phrp720/aw-sync-agent-plugins/models" + "github.com/phrp720/aw-sync-agent-plugins/util" + "log" + "strconv" + "strings" +) + +// PrintScripts prints the array of the registered scripts with borders +func PrintScripts(scripts []string) { + log.Print("Scripts Registered:") + + scriptsMap := map[string]string{ + "Scripts found": strconv.Itoa(len(scripts)), + "Scripts": strings.Join(scripts, ", "), + } + + maxKeyLength := 0 + maxValueLength := 0 + for key, value := range scriptsMap { + if len(key) > maxKeyLength { + maxKeyLength = len(key) + } + if len(value) > maxValueLength { + maxValueLength = len(value) + } + } + + borderLength := maxKeyLength + maxValueLength + 7 + border := strings.Repeat("-", borderLength) + fmt.Println(border) + for key, value := range scriptsMap { + fmt.Printf("| %-*s | %-*s |\n", maxKeyLength, key, maxValueLength, value) + } + fmt.Println(border) +} + +// GetScriptNames returns the names of the registered scripts +func GetScriptNames(scripts []models.Script) []string { + var scriptNames []string + for _, script := range scripts { + if script.Name != "" && !util.Contains(scriptNames, script.Name) { + scriptNames = append(scriptNames, script.Name) + } + } + return scriptNames +} diff --git a/plugins/script/plugin.go b/plugins/script/plugin.go index f954d8e..79c8455 100644 --- a/plugins/script/plugin.go +++ b/plugins/script/plugin.go @@ -1,22 +1,84 @@ package script import ( + "bytes" + "encoding/json" "github.com/phrp720/aw-sync-agent-plugins/models" + "github.com/phrp720/aw-sync-agent-plugins/util" + "log" + "os/exec" ) +var config models.ScriptConfig + type Plugin struct{} func (f *Plugin) Initialize() { - + config = LoadYAMLConfig("./config/" + f.Name()) + if config.Scripts != nil { + PrintScripts(GetScriptNames(config.Scripts)) + } } func (f *Plugin) Execute(events models.Events, watcher string, userID string, includeHostName bool) models.Events { + if config.Scripts == nil { + return events + } else { + log.Printf("Scripts") + } + // Convert events to JSON + eventsToJSON, err := json.Marshal(events) + if err != nil { + log.Printf("Error marshalling events: %v", err) + return events + } + + for _, script := range config.Scripts { + AbsPath := script.Path + script.Name + + if !util.FileExists(AbsPath) { + log.Printf("Error running %s : %s", script.Name, "No such file or directory.") + continue + } else { + log.Printf("Running %s", script.Name) + + } + cmd := exec.Command(AbsPath) + // Set up stdin and stdout + cmd.Stdin = bytes.NewReader(eventsToJSON) + var stdout bytes.Buffer + cmd.Stdout = &stdout + + // Run the command + err = cmd.Run() + if err != nil { + log.Printf("Error running %s : %v", script.Name, err) + return events + } + // Read the output and convert it back to events + var EventsBuffer models.Events + err = json.Unmarshal(stdout.Bytes(), &EventsBuffer) + if err != nil { + log.Printf("Error unmarshalling script output: %v", err) + return events + } + if EventsBuffer != nil { + events = EventsBuffer + log.Printf("Finished %s ", script.Name) + + } + + } + return events } func (f *Plugin) ReplicateConfig(path string) { - + err := CreateConfigFile(path, f.Name()) + if err != nil { + log.Print(err) + } } func (f *Plugin) Name() string { @@ -24,5 +86,5 @@ func (f *Plugin) Name() string { } func (f *Plugin) RawName() string { - return "script" + return models.SCRIPT } diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..747a690 --- /dev/null +++ b/util/util.go @@ -0,0 +1,21 @@ +package util + +import "os" + +// Contains checks if a slice contains a given string +func Contains(slice []string, item string) bool { + for _, s := range slice { + if s == item { + return true + } + } + return false +} + +func FileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} From 1845e6ea6ad7675818d2db5a6126db7c5cd6dd67 Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 14:28:06 +0100 Subject: [PATCH 3/8] script plugin implementation --- tests/script_test.go | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/script_test.go diff --git a/tests/script_test.go b/tests/script_test.go new file mode 100644 index 0000000..937801e --- /dev/null +++ b/tests/script_test.go @@ -0,0 +1,6 @@ +package tests + +import "testing" + +func TestScript(t *testing.T) { +} From d4b39a13fde1f73b7a091d6e8856556852c69bf8 Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 14:29:45 +0100 Subject: [PATCH 4/8] doc update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1e1ba62..4838488 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Plugins of aw-sync-suite Agent are used to extend the functionality of the agent | Plugin | Description | Has Config | Config File | Documentation | |-----------|-----------------------------------|------------|--------------------------|---------------------------------------------------------------------| | `filters` | Filters the data of ActivityWatch | ✅ | `aw-plugin-filters.yaml` | [📄](https://github.com/phrp720/aw-sync-suite-plugins/wiki/Filters) | +| `scripts` | Runs third party Scripts | ✅ | `aw-plugin-scripts.yaml` | [📄](https://github.com/phrp720/aw-sync-suite-plugins/wiki/Scripts) | ## ⚙️ How It Works From b32fcb9e421f5b55968eaef001eb796ff8402143 Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 14:35:25 +0100 Subject: [PATCH 5/8] doc update --- models/{plugin.go => plugin-core.go} | 0 plugins/manager.go | 1 + 2 files changed, 1 insertion(+) rename models/{plugin.go => plugin-core.go} (100%) diff --git a/models/plugin.go b/models/plugin-core.go similarity index 100% rename from models/plugin.go rename to models/plugin-core.go diff --git a/plugins/manager.go b/plugins/manager.go index b9aa4e2..e3b8377 100644 --- a/plugins/manager.go +++ b/plugins/manager.go @@ -9,6 +9,7 @@ import ( func Initialize() []models.Plugin { var plugins []models.Plugin + // Add all the plugins here plugins = append(plugins, &filter.Plugin{}) plugins = append(plugins, &script.Plugin{}) From 3ffd33ea173c62dc683cbdb8665cb59adbfe7503 Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 14:39:17 +0100 Subject: [PATCH 6/8] doc update --- README.md | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/README.md b/README.md index 4838488..99650d3 100644 --- a/README.md +++ b/README.md @@ -31,33 +31,7 @@ Plugins of aw-sync-suite Agent are used to extend the functionality of the agent ## ⚙️ How It Works -When the `aw-sync-agent` starts, it initializes the chosen plugins specified in the configuration. The workflow is as follows: - -1. **Initialization**: - - Upon startup, the agent reads the `aw-sync-settings.yaml` file to identify which plugins are enabled. - - Each plugin is initialized according to its specific requirements, preparing it for data processing. - -2. **Data Synchronization**: - - At every sync cycle, the agent collects data from the local ActivityWatch instance. - - The collected data is then passed through the initialized plugins sequentially. - -3. **Data Processing**: - - Each plugin processes the data according to its defined functionality (e.g., filtering, transformation). - - After processing, the plugins return the final dataset that has been modified or filtered as per the plugin logic. - -4. **Data Push to Prometheus**: - - The final processed data is then securely pushed to the Prometheus database using the remote-write feature. - -This modular approach allows for flexible and customizable data processing, ensuring that only the desired data is sent to Prometheus while maintaining the integrity and confidentiality of sensitive information. - -#### flow-diagram: - -
- - ![flow](plugins-flow-diagram.png) - -
- +For documentation about the plugin workflow in `aw-sync-agent`, please refer [here](https://github.com/phrp720/aw-sync-suite-plugins/wiki/%E2%9A%99%EF%B8%8F-Plugin-Workflow). ## 🛠️ How to create a plugin From 78297654f71086ff210fac83bb5fc5cb1d7df3db Mon Sep 17 00:00:00 2001 From: phrp Date: Thu, 6 Feb 2025 15:00:15 +0100 Subject: [PATCH 7/8] timeout added --- config-examples/aw-plugin-scripts.yaml | 10 +++++----- models/script.go | 5 +++-- plugins/script/plugin.go | 23 ++++++++++++++++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/config-examples/aw-plugin-scripts.yaml b/config-examples/aw-plugin-scripts.yaml index e2278c4..d857a13 100644 --- a/config-examples/aw-plugin-scripts.yaml +++ b/config-examples/aw-plugin-scripts.yaml @@ -1,8 +1,8 @@ Scripts: - Script: - name: "test-script.sh" - path: "/home/tester/Desktop/scripts/" + name: "test" + path: "/home/tester/Desktop/scripts/test-script.sh" - Script: - name: "dummy-script.sh" - path: "/home/tester/Desktop/scripts/" - + name: "dummy" + path: "/home/tester/Desktop/scripts/dummy-script.sh" + timeout: 10 diff --git a/models/script.go b/models/script.go index 396679a..684a986 100644 --- a/models/script.go +++ b/models/script.go @@ -5,6 +5,7 @@ type ScriptConfig struct { } type Script struct { - Name string `yaml:"name"` // ScriptName is the name of the script - Path string `yaml:"path"` // Path is the path of the script + Name string `yaml:"name"` // ScriptName is the name of the script + Path string `yaml:"path"` // Path is the path of the script + Timeout int `yaml:"timeout"` // Timeout is the timeout of the script in seconds } diff --git a/plugins/script/plugin.go b/plugins/script/plugin.go index 79c8455..af286a7 100644 --- a/plugins/script/plugin.go +++ b/plugins/script/plugin.go @@ -2,11 +2,14 @@ package script import ( "bytes" + "context" "encoding/json" + "errors" "github.com/phrp720/aw-sync-agent-plugins/models" "github.com/phrp720/aw-sync-agent-plugins/util" "log" "os/exec" + "time" ) var config models.ScriptConfig @@ -35,16 +38,25 @@ func (f *Plugin) Execute(events models.Events, watcher string, userID string, in } for _, script := range config.Scripts { - AbsPath := script.Path + script.Name - if !util.FileExists(AbsPath) { + if !util.FileExists(script.Path) { log.Printf("Error running %s : %s", script.Name, "No such file or directory.") continue } else { log.Printf("Running %s", script.Name) } - cmd := exec.Command(AbsPath) + + // Set a default timeout of 30 seconds + if script.Timeout == 0 { + script.Timeout = 30 + } + + // Create a context with a timeout + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(script.Timeout)*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, script.Path) // Set up stdin and stdout cmd.Stdin = bytes.NewReader(eventsToJSON) var stdout bytes.Buffer @@ -52,6 +64,11 @@ func (f *Plugin) Execute(events models.Events, watcher string, userID string, in // Run the command err = cmd.Run() + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + log.Printf("Error running %s : %s", script.Name, "Timeout exceeded") + return events + + } if err != nil { log.Printf("Error running %s : %v", script.Name, err) return events From 829d4e76cd64d3bdfa3d243a71b0e30f47a68574 Mon Sep 17 00:00:00 2001 From: Phillip Rafail Papadakis <60779103+phrp720@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:29:41 +0100 Subject: [PATCH 8/8] Update aw-plugin-filters.yaml --- config-examples/aw-plugin-filters.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/config-examples/aw-plugin-filters.yaml b/config-examples/aw-plugin-filters.yaml index ca07e5a..7d2c785 100644 --- a/config-examples/aw-plugin-filters.yaml +++ b/config-examples/aw-plugin-filters.yaml @@ -18,11 +18,7 @@ Filters: - key: app value: Google.* - key: title - value: GitHub|Stack Overflow|BitBucket|Gitlab|vim|Spyder|kate|Ghidra|Scite|code|eclipse|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad|gedit|atom|vscode|jupyter|pycharm|intellij|netbeans|eclipse|visual studio|visual - category: Programming - - - Filter: - filter-name: Image Category + value: GitHub|Stack Overflow|BitBucket|Gitlab|vim|Spyderbeans|eclipse|visual studio|visual studio code|emacs|vim|nano|sublime|notepad enable: true watchers: [] target: @@ -206,4 +202,4 @@ Filters: plain-replace: ## key value pairs to replace e.g. on the key `title` replace its value with `Email` - key: "title" ## key of record - value: "Grafana" ## value to replace \ No newline at end of file + value: "Grafana" ## value to replace