opencode-probleemwijken 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # opencode-probleemwijken
2
+
3
+ OpenCode plugin dat een willekeurig geluid afspeelt van de legendarische [Probleemwijken/Derkolk soundboard](https://www.derkolk.nl/probleemwijken/) wanneer een sessie klaar is.
4
+
5
+ ## Installatie
6
+
7
+ ### Optie 1: Lokale plugin (aanbevolen)
8
+
9
+ 1. Kopieer `plugin.ts` naar je OpenCode plugins directory:
10
+
11
+ ```bash
12
+ cp plugin.ts ~/.config/opencode/plugins/probleemwijken.ts
13
+ ```
14
+
15
+ 2. Kopieer de sounds naar je config directory:
16
+
17
+ ```bash
18
+ mkdir -p ~/.config/opencode/sounds/random-soundboard
19
+ cp sounds/*.mp3 ~/.config/opencode/sounds/random-soundboard/
20
+ ```
21
+
22
+ 3. Zorg dat je `@opencode-ai/plugin` hebt in `~/.config/opencode/package.json`:
23
+
24
+ ```json
25
+ {
26
+ "dependencies": {
27
+ "@opencode-ai/plugin": "^1.0.0"
28
+ }
29
+ }
30
+ ```
31
+
32
+ 4. Herstart OpenCode
33
+
34
+ ### Optie 2: Eigen geluiden toevoegen
35
+
36
+ Je kunt ook je eigen MP3/WAV/OGG/M4A bestanden toevoegen aan de `~/.config/opencode/sounds/random-soundboard/` directory.
37
+
38
+ ## Wat doet het?
39
+
40
+ Elke keer als OpenCode klaar is met een taak (session.idle) of een error krijgt (session.error), speelt de plugin een willekeurig geluid af uit de collectie.
41
+
42
+ ## Geluiden
43
+
44
+ De plugin bevat 36 klassieke Derkolk soundboard fragmenten:
45
+
46
+ - "VLIEG!"
47
+ - "Half elf"
48
+ - "Boertje!?"
49
+ - "Geert is inmiddels dronken"
50
+ - "Broodje"
51
+ - "Koffie"
52
+ - "Pitbull"
53
+ - "Tetete"
54
+ - ... en nog veel meer!
55
+
56
+ ## Platform ondersteuning
57
+
58
+ - **macOS**: Gebruikt `afplay` (ingebouwd)
59
+ - **Linux**: Gebruikt `mpv` of `ffplay`
60
+ - **Windows**: Gebruikt Windows Media Player via PowerShell
61
+
62
+ ## Credits
63
+
64
+ - Geluiden van [derkolk.nl/probleemwijken](https://www.derkolk.nl/probleemwijken/)
65
+ - Gebaseerd op [opencode-notifier](https://github.com/mohak34/opencode-notifier) door mohak34
66
+
67
+ ## Licentie
68
+
69
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ // src/config.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ var DEFAULT_CONFIG = {
6
+ enabled: true,
7
+ soundsDir: null,
8
+ events: {
9
+ complete: true,
10
+ subagent_complete: false,
11
+ error: true,
12
+ permission: false
13
+ }
14
+ };
15
+ function loadConfig() {
16
+ const configPath = join(homedir(), ".config", "opencode", "random-soundboard.json");
17
+ if (!existsSync(configPath)) {
18
+ return DEFAULT_CONFIG;
19
+ }
20
+ try {
21
+ const content = readFileSync(configPath, "utf-8");
22
+ const userConfig = JSON.parse(content);
23
+ return {
24
+ enabled: userConfig.enabled ?? DEFAULT_CONFIG.enabled,
25
+ soundsDir: userConfig.soundsDir ?? DEFAULT_CONFIG.soundsDir,
26
+ events: {
27
+ complete: userConfig.events?.complete ?? DEFAULT_CONFIG.events.complete,
28
+ subagent_complete: userConfig.events?.subagent_complete ?? DEFAULT_CONFIG.events.subagent_complete,
29
+ error: userConfig.events?.error ?? DEFAULT_CONFIG.events.error,
30
+ permission: userConfig.events?.permission ?? DEFAULT_CONFIG.events.permission
31
+ }
32
+ };
33
+ } catch {
34
+ return DEFAULT_CONFIG;
35
+ }
36
+ }
37
+ function isEventEnabled(config, event) {
38
+ if (!config.enabled)
39
+ return false;
40
+ return config.events[event] ?? false;
41
+ }
42
+
43
+ // src/sound.ts
44
+ import { platform } from "os";
45
+ import { join as join2, dirname, extname } from "path";
46
+ import { fileURLToPath } from "url";
47
+ import { existsSync as existsSync2, readdirSync } from "fs";
48
+ import { spawn } from "child_process";
49
+ var __dirname2 = dirname(fileURLToPath(import.meta.url));
50
+ var SUPPORTED_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac"];
51
+ function getBundledSoundsDir() {
52
+ const possiblePaths = [
53
+ join2(__dirname2, "..", "sounds"),
54
+ join2(__dirname2, "sounds")
55
+ ];
56
+ for (const path of possiblePaths) {
57
+ if (existsSync2(path)) {
58
+ return path;
59
+ }
60
+ }
61
+ return join2(__dirname2, "..", "sounds");
62
+ }
63
+ function getSoundsDirectory(config) {
64
+ if (config.soundsDir && existsSync2(config.soundsDir)) {
65
+ return config.soundsDir;
66
+ }
67
+ return getBundledSoundsDir();
68
+ }
69
+ function getSoundFiles(directory) {
70
+ if (!existsSync2(directory)) {
71
+ return [];
72
+ }
73
+ try {
74
+ const files = readdirSync(directory);
75
+ return files.filter((file) => {
76
+ const ext = extname(file).toLowerCase();
77
+ return SUPPORTED_EXTENSIONS.includes(ext);
78
+ }).map((file) => join2(directory, file));
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
83
+ function getRandomSound(sounds) {
84
+ if (sounds.length === 0)
85
+ return null;
86
+ const index = Math.floor(Math.random() * sounds.length);
87
+ return sounds[index];
88
+ }
89
+ async function runCommand(command, args) {
90
+ return new Promise((resolve, reject) => {
91
+ const proc = spawn(command, args, {
92
+ stdio: "ignore",
93
+ detached: false
94
+ });
95
+ proc.on("error", (err) => {
96
+ reject(err);
97
+ });
98
+ proc.on("close", (code) => {
99
+ if (code === 0) {
100
+ resolve();
101
+ } else {
102
+ reject(new Error(`Command exited with code ${code}`));
103
+ }
104
+ });
105
+ });
106
+ }
107
+ async function playOnLinux(soundPath) {
108
+ const players = [
109
+ { command: "mpv", args: ["--no-video", "--no-terminal", soundPath] },
110
+ { command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", soundPath] },
111
+ { command: "paplay", args: [soundPath] },
112
+ { command: "aplay", args: [soundPath] }
113
+ ];
114
+ for (const player of players) {
115
+ try {
116
+ await runCommand(player.command, player.args);
117
+ return;
118
+ } catch {
119
+ continue;
120
+ }
121
+ }
122
+ }
123
+ async function playOnMac(soundPath) {
124
+ await runCommand("afplay", [soundPath]);
125
+ }
126
+ async function playOnWindows(soundPath) {
127
+ const ext = extname(soundPath).toLowerCase();
128
+ if (ext === ".wav") {
129
+ const script = `& { (New-Object Media.SoundPlayer $args[0]).PlaySync() }`;
130
+ await runCommand("powershell", ["-c", script, soundPath]);
131
+ } else {
132
+ const script = `
133
+ $player = New-Object -ComObject WMPlayer.OCX
134
+ $player.URL = $args[0]
135
+ $player.controls.play()
136
+ Start-Sleep -Milliseconds 500
137
+ while ($player.playState -eq 3) { Start-Sleep -Milliseconds 100 }
138
+ $player.close()
139
+ `;
140
+ await runCommand("powershell", ["-c", script, soundPath]);
141
+ }
142
+ }
143
+ async function playRandomSound(config) {
144
+ const soundsDir = getSoundsDirectory(config);
145
+ const sounds = getSoundFiles(soundsDir);
146
+ if (sounds.length === 0) {
147
+ return;
148
+ }
149
+ const soundPath = getRandomSound(sounds);
150
+ if (!soundPath) {
151
+ return;
152
+ }
153
+ const os = platform();
154
+ try {
155
+ switch (os) {
156
+ case "darwin":
157
+ await playOnMac(soundPath);
158
+ break;
159
+ case "linux":
160
+ await playOnLinux(soundPath);
161
+ break;
162
+ case "win32":
163
+ await playOnWindows(soundPath);
164
+ break;
165
+ default:
166
+ break;
167
+ }
168
+ } catch {}
169
+ }
170
+
171
+ // src/index.ts
172
+ function getSessionIDFromEvent(event) {
173
+ const sessionID = event?.properties?.sessionID;
174
+ if (typeof sessionID === "string" && sessionID.length > 0) {
175
+ return sessionID;
176
+ }
177
+ return null;
178
+ }
179
+ async function isChildSession(client, sessionID) {
180
+ try {
181
+ const response = await client.session.get({ path: { id: sessionID } });
182
+ const parentID = response.data?.parentID;
183
+ return !!parentID;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+ var RandomSoundboardPlugin = async ({ client }) => {
189
+ const config = loadConfig();
190
+ return {
191
+ event: async ({ event }) => {
192
+ if (event.type === "permission.updated" || event.type === "permission.asked") {
193
+ if (isEventEnabled(config, "permission")) {
194
+ await playRandomSound(config);
195
+ }
196
+ }
197
+ if (event.type === "session.idle") {
198
+ const sessionID = getSessionIDFromEvent(event);
199
+ if (sessionID) {
200
+ const isChild = await isChildSession(client, sessionID);
201
+ const eventType = isChild ? "subagent_complete" : "complete";
202
+ if (isEventEnabled(config, eventType)) {
203
+ await playRandomSound(config);
204
+ }
205
+ } else {
206
+ if (isEventEnabled(config, "complete")) {
207
+ await playRandomSound(config);
208
+ }
209
+ }
210
+ }
211
+ if (event.type === "session.error") {
212
+ if (isEventEnabled(config, "error")) {
213
+ await playRandomSound(config);
214
+ }
215
+ }
216
+ }
217
+ };
218
+ };
219
+ var src_default = RandomSoundboardPlugin;
220
+ export {
221
+ src_default as default,
222
+ RandomSoundboardPlugin
223
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "opencode-probleemwijken",
3
+ "version": "1.0.0",
4
+ "description": "OpenCode plugin that plays a random Probleemwijken/Derkolk soundboard sound when a session completes",
5
+ "author": "Daan-Friday",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Daan-Friday/opencode-probleemwijken.git"
13
+ },
14
+ "homepage": "https://github.com/Daan-Friday/opencode-probleemwijken#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/Daan-Friday/opencode-probleemwijken/issues"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "sounds"
21
+ ],
22
+ "scripts": {
23
+ "build": "bun build src/index.ts --outdir dist --target node",
24
+ "prepublishOnly": "bun run build",
25
+ "typecheck": "tsc --noEmit"
26
+ },
27
+ "keywords": [
28
+ "opencode",
29
+ "opencode-plugin",
30
+ "soundboard",
31
+ "random",
32
+ "sound",
33
+ "notifications",
34
+ "probleemwijken",
35
+ "derkolk"
36
+ ],
37
+ "devDependencies": {
38
+ "@opencode-ai/plugin": "^1.0.224",
39
+ "@types/node": "^22.0.0",
40
+ "typescript": "^5.0.0"
41
+ },
42
+ "peerDependencies": {
43
+ "@opencode-ai/plugin": ">=1.0.0"
44
+ }
45
+ }
@@ -0,0 +1,2 @@
1
+ # Add your MP3/WAV/OGG sound files to this directory!
2
+ # The plugin will randomly pick one when OpenCode finishes a task.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/sounds/lul.mp3 ADDED
Binary file
package/sounds/nu.mp3 ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/sounds/vis.mp3 ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file