@akilles/soundcloud-watcher 2.0.0 → 2.0.2
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/index.ts +112 -145
- package/openclaw.plugin.json +2 -37
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { SoundCloudWatcher } from './soundcloud_watcher';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
2
5
|
|
|
3
6
|
interface PluginConfig {
|
|
4
|
-
enabled: boolean;
|
|
5
7
|
clientId: string;
|
|
6
8
|
clientSecret: string;
|
|
7
9
|
username: string;
|
|
@@ -11,138 +13,115 @@ interface PluginConfig {
|
|
|
11
13
|
sessionKey?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
function loadConfig(): PluginConfig | null {
|
|
17
|
+
// Try loading from env file first
|
|
18
|
+
const envPath = join(homedir(), '.openclaw', 'secrets', 'soundcloud.env');
|
|
19
|
+
if (existsSync(envPath)) {
|
|
20
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
21
|
+
const env: Record<string, string> = {};
|
|
22
|
+
for (const line of content.split('\n')) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {
|
|
25
|
+
const [key, ...rest] = trimmed.split('=');
|
|
26
|
+
env[key.trim()] = rest.join('=').trim();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (env.SOUNDCLOUD_CLIENT_ID && env.SOUNDCLOUD_CLIENT_SECRET && env.MY_USERNAME) {
|
|
30
|
+
return {
|
|
31
|
+
clientId: env.SOUNDCLOUD_CLIENT_ID,
|
|
32
|
+
clientSecret: env.SOUNDCLOUD_CLIENT_SECRET,
|
|
33
|
+
username: env.MY_USERNAME,
|
|
34
|
+
checkIntervalHours: 6,
|
|
35
|
+
myTracksLimit: 10,
|
|
36
|
+
dormantDays: 90,
|
|
37
|
+
sessionKey: 'agent:main:main',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
14
44
|
export default function register(api: any) {
|
|
15
45
|
const logger = api.getLogger?.() || console;
|
|
16
|
-
let checkInterval: NodeJS.Timeout | null = null;
|
|
17
46
|
let watcher: SoundCloudWatcher | null = null;
|
|
18
47
|
|
|
19
|
-
function getWatcher(): SoundCloudWatcher {
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
function getWatcher(): SoundCloudWatcher | null {
|
|
49
|
+
if (watcher) return watcher;
|
|
50
|
+
|
|
51
|
+
const config = loadConfig();
|
|
52
|
+
if (!config) return null;
|
|
53
|
+
|
|
54
|
+
watcher = new SoundCloudWatcher({
|
|
55
|
+
clientId: config.clientId,
|
|
56
|
+
clientSecret: config.clientSecret,
|
|
57
|
+
username: config.username,
|
|
58
|
+
myTracksLimit: config.myTracksLimit,
|
|
59
|
+
dormantDays: config.dormantDays,
|
|
60
|
+
logger: (...args: any[]) => logger.debug?.(...args) || console.log(...args),
|
|
61
|
+
});
|
|
31
62
|
return watcher;
|
|
32
63
|
}
|
|
33
64
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
function getSetupMessage(): string {
|
|
66
|
+
const config = loadConfig();
|
|
67
|
+
|
|
68
|
+
if (config) {
|
|
69
|
+
return `# SoundCloud Watcher Setup
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
await api.tools.sessions_send({
|
|
50
|
-
sessionKey,
|
|
51
|
-
message,
|
|
52
|
-
});
|
|
53
|
-
} catch (err) {
|
|
54
|
-
logger.error('Failed to send notification:', err);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
logger.debug('No SoundCloud updates');
|
|
59
|
-
}
|
|
60
|
-
} catch (err) {
|
|
61
|
-
logger.error('Error during SoundCloud check:', err);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
71
|
+
Already configured!
|
|
72
|
+
|
|
73
|
+
- Username: ${config.username}
|
|
74
|
+
- Client ID: ${config.clientId.substring(0, 8)}...${config.clientId.slice(-4)}
|
|
75
|
+
- Check interval: ${config.checkIntervalHours} hours
|
|
76
|
+
|
|
77
|
+
To update credentials, edit:
|
|
78
|
+
\`~/.openclaw/secrets/soundcloud.env\`
|
|
64
79
|
|
|
65
|
-
|
|
66
|
-
if (checkInterval) {
|
|
67
|
-
clearInterval(checkInterval);
|
|
80
|
+
Then restart: \`openclaw gateway restart\``;
|
|
68
81
|
}
|
|
82
|
+
|
|
83
|
+
return `# SoundCloud Watcher Setup
|
|
69
84
|
|
|
70
|
-
|
|
85
|
+
Not configured yet.
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
logger.error('Initial SoundCloud check failed:', err);
|
|
74
|
-
});
|
|
87
|
+
## Steps:
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
checkForUpdates(config, sessionKey).catch((err) => {
|
|
78
|
-
logger.error('Periodic SoundCloud check failed:', err);
|
|
79
|
-
});
|
|
80
|
-
}, intervalMs);
|
|
89
|
+
1. Get credentials from https://soundcloud.com/you/apps
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
2. Create config file:
|
|
92
|
+
\`\`\`bash
|
|
93
|
+
mkdir -p ~/.openclaw/secrets
|
|
94
|
+
nano ~/.openclaw/secrets/soundcloud.env
|
|
95
|
+
\`\`\`
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
3. Add your credentials:
|
|
98
|
+
\`\`\`
|
|
99
|
+
SOUNDCLOUD_CLIENT_ID=your_client_id
|
|
100
|
+
SOUNDCLOUD_CLIENT_SECRET=your_client_secret
|
|
101
|
+
MY_USERNAME=your_soundcloud_username
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
4. Restart: \`openclaw gateway restart\`
|
|
105
|
+
|
|
106
|
+
5. Verify: \`/soundcloud-status\``;
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
// Register commands
|
|
94
110
|
api.registerCommand({
|
|
95
111
|
name: 'soundcloud-setup',
|
|
96
|
-
description: '
|
|
97
|
-
handler: async (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let message = '# SoundCloud Watcher Setup\n\n';
|
|
101
|
-
|
|
102
|
-
if (config.clientId && config.clientSecret && config.username) {
|
|
103
|
-
message += 'Already configured!\n\n';
|
|
104
|
-
message += `- Username: ${config.username}\n`;
|
|
105
|
-
message += `- Client ID: ${config.clientId.substring(0, 8)}...${config.clientId.slice(-4)}\n`;
|
|
106
|
-
message += `- Check interval: ${config.checkIntervalHours} hours\n`;
|
|
107
|
-
message += `- Session key: ${config.sessionKey || 'agent:main:main'}\n\n`;
|
|
108
|
-
message += 'To update, edit `~/.openclaw/openclaw.json` under:\n';
|
|
109
|
-
message += '`plugins.entries.soundcloud-watcher.config`\n\n';
|
|
110
|
-
message += 'Then restart: `openclaw gateway restart`';
|
|
111
|
-
} else {
|
|
112
|
-
message += 'Warning: Not configured yet\n\n';
|
|
113
|
-
message += '## Steps:\n\n';
|
|
114
|
-
message += '1. Get credentials from https://soundcloud.com/you/apps\n';
|
|
115
|
-
message += '2. Edit `~/.openclaw/openclaw.json`:\n\n';
|
|
116
|
-
message += '```json\n';
|
|
117
|
-
message += '{\n';
|
|
118
|
-
message += ' "plugins": {\n';
|
|
119
|
-
message += ' "entries": {\n';
|
|
120
|
-
message += ' "soundcloud-watcher": {\n';
|
|
121
|
-
message += ' "enabled": true,\n';
|
|
122
|
-
message += ' "config": {\n';
|
|
123
|
-
message += ' "clientId": "YOUR_CLIENT_ID",\n';
|
|
124
|
-
message += ' "clientSecret": "YOUR_CLIENT_SECRET",\n';
|
|
125
|
-
message += ' "username": "your_soundcloud_username",\n';
|
|
126
|
-
message += ' "checkIntervalHours": 6\n';
|
|
127
|
-
message += ' }\n';
|
|
128
|
-
message += ' }\n';
|
|
129
|
-
message += ' }\n';
|
|
130
|
-
message += ' }\n';
|
|
131
|
-
message += '}\n';
|
|
132
|
-
message += '```\n\n';
|
|
133
|
-
message += '3. Restart: `openclaw gateway restart`\n';
|
|
134
|
-
message += '4. Verify in chat: `/soundcloud-setup`\n';
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return { text: message };
|
|
112
|
+
description: 'Show SoundCloud watcher setup instructions',
|
|
113
|
+
handler: async () => {
|
|
114
|
+
return { text: getSetupMessage() };
|
|
138
115
|
},
|
|
139
116
|
});
|
|
140
117
|
|
|
141
118
|
api.registerCommand({
|
|
142
119
|
name: 'soundcloud-status',
|
|
143
120
|
description: 'Show SoundCloud watcher status',
|
|
144
|
-
handler: async (
|
|
145
|
-
const
|
|
121
|
+
handler: async () => {
|
|
122
|
+
const w = getWatcher();
|
|
123
|
+
if (!w) return { text: 'Not configured. Run /soundcloud-setup for instructions.' };
|
|
124
|
+
const result = await w.status();
|
|
146
125
|
return { text: result };
|
|
147
126
|
},
|
|
148
127
|
});
|
|
@@ -150,8 +129,10 @@ export default function register(api: any) {
|
|
|
150
129
|
api.registerCommand({
|
|
151
130
|
name: 'soundcloud-check',
|
|
152
131
|
description: 'Run an immediate SoundCloud check',
|
|
153
|
-
handler: async (
|
|
154
|
-
const
|
|
132
|
+
handler: async () => {
|
|
133
|
+
const w = getWatcher();
|
|
134
|
+
if (!w) return { text: 'Not configured. Run /soundcloud-setup for instructions.' };
|
|
135
|
+
const result = await w.check();
|
|
155
136
|
return { text: result };
|
|
156
137
|
},
|
|
157
138
|
});
|
|
@@ -159,11 +140,15 @@ export default function register(api: any) {
|
|
|
159
140
|
api.registerCommand({
|
|
160
141
|
name: 'soundcloud-add',
|
|
161
142
|
description: 'Add artist(s) to track',
|
|
162
|
-
handler: async (ctx: any
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
143
|
+
handler: async (ctx: any) => {
|
|
144
|
+
const w = getWatcher();
|
|
145
|
+
if (!w) return { text: 'Not configured. Run /soundcloud-setup for instructions.' };
|
|
146
|
+
|
|
147
|
+
const args = ctx.args?.trim();
|
|
148
|
+
if (!args) return { text: 'Usage: /soundcloud-add <username> [username2] ...' };
|
|
149
|
+
|
|
150
|
+
const usernames = args.split(/\s+/).filter(Boolean);
|
|
151
|
+
const result = await w.addArtists(usernames);
|
|
167
152
|
return { text: result };
|
|
168
153
|
},
|
|
169
154
|
});
|
|
@@ -171,11 +156,14 @@ export default function register(api: any) {
|
|
|
171
156
|
api.registerCommand({
|
|
172
157
|
name: 'soundcloud-remove',
|
|
173
158
|
description: 'Remove an artist from tracking',
|
|
174
|
-
handler: async (ctx: any
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
159
|
+
handler: async (ctx: any) => {
|
|
160
|
+
const w = getWatcher();
|
|
161
|
+
if (!w) return { text: 'Not configured. Run /soundcloud-setup for instructions.' };
|
|
162
|
+
|
|
163
|
+
const username = ctx.args?.trim();
|
|
164
|
+
if (!username) return { text: 'Usage: /soundcloud-remove <username>' };
|
|
165
|
+
|
|
166
|
+
const result = await w.removeArtist(username);
|
|
179
167
|
return { text: result };
|
|
180
168
|
},
|
|
181
169
|
});
|
|
@@ -183,34 +171,13 @@ export default function register(api: any) {
|
|
|
183
171
|
api.registerCommand({
|
|
184
172
|
name: 'soundcloud-list',
|
|
185
173
|
description: 'List all tracked artists',
|
|
186
|
-
handler: async (
|
|
187
|
-
const
|
|
174
|
+
handler: async () => {
|
|
175
|
+
const w = getWatcher();
|
|
176
|
+
if (!w) return { text: 'Not configured. Run /soundcloud-setup for instructions.' };
|
|
177
|
+
const result = await w.listArtists();
|
|
188
178
|
return { text: result };
|
|
189
179
|
},
|
|
190
180
|
});
|
|
191
181
|
|
|
192
|
-
|
|
193
|
-
api.onConfigChange?.((config: PluginConfig) => {
|
|
194
|
-
watcher = null; // Reset watcher so it picks up new config
|
|
195
|
-
if (config.enabled) {
|
|
196
|
-
const sessionKey = config.sessionKey || 'agent:main:main';
|
|
197
|
-
startChecking(config, sessionKey);
|
|
198
|
-
} else {
|
|
199
|
-
stopChecking();
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Initialize on load
|
|
204
|
-
const initialConfig = api.getConfig() as PluginConfig;
|
|
205
|
-
if (initialConfig.enabled) {
|
|
206
|
-
const sessionKey = initialConfig.sessionKey || 'agent:main:main';
|
|
207
|
-
startChecking(initialConfig, sessionKey);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Cleanup on unload
|
|
211
|
-
api.onUnload?.(() => {
|
|
212
|
-
stopChecking();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
logger.info('SoundCloud Watcher plugin loaded');
|
|
182
|
+
logger.info?.('SoundCloud Watcher plugin loaded') || console.log('SoundCloud Watcher plugin loaded');
|
|
216
183
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,49 +2,14 @@
|
|
|
2
2
|
"id": "soundcloud-watcher",
|
|
3
3
|
"name": "SoundCloud Watcher",
|
|
4
4
|
"description": "Monitor your SoundCloud account and track artist releases",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.0.2",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
10
|
"enabled": {
|
|
11
11
|
"type": "boolean",
|
|
12
|
-
"default": true
|
|
13
|
-
"description": "Enable/disable the SoundCloud watcher"
|
|
14
|
-
},
|
|
15
|
-
"clientId": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"description": "SoundCloud API Client ID"
|
|
18
|
-
},
|
|
19
|
-
"clientSecret": {
|
|
20
|
-
"type": "string",
|
|
21
|
-
"description": "SoundCloud API Client Secret"
|
|
22
|
-
},
|
|
23
|
-
"username": {
|
|
24
|
-
"type": "string",
|
|
25
|
-
"description": "Your SoundCloud username (URL slug)"
|
|
26
|
-
},
|
|
27
|
-
"checkIntervalHours": {
|
|
28
|
-
"type": "number",
|
|
29
|
-
"default": 6,
|
|
30
|
-
"minimum": 1,
|
|
31
|
-
"maximum": 24,
|
|
32
|
-
"description": "How often to check for updates (in hours)"
|
|
33
|
-
},
|
|
34
|
-
"myTracksLimit": {
|
|
35
|
-
"type": "number",
|
|
36
|
-
"default": 10,
|
|
37
|
-
"description": "Number of your tracks to monitor"
|
|
38
|
-
},
|
|
39
|
-
"dormantDays": {
|
|
40
|
-
"type": "number",
|
|
41
|
-
"default": 90,
|
|
42
|
-
"description": "Days before an artist is considered dormant"
|
|
43
|
-
},
|
|
44
|
-
"sessionKey": {
|
|
45
|
-
"type": "string",
|
|
46
|
-
"default": "agent:main:main",
|
|
47
|
-
"description": "OpenClaw session key for notifications (e.g., 'agent:main:main', 'telegram:chat123', etc.)"
|
|
12
|
+
"default": true
|
|
48
13
|
}
|
|
49
14
|
},
|
|
50
15
|
"required": []
|