@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 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 (!watcher) {
21
- const config = api.getConfig() as PluginConfig;
22
- watcher = new SoundCloudWatcher({
23
- clientId: config.clientId || '',
24
- clientSecret: config.clientSecret || '',
25
- username: config.username || '',
26
- myTracksLimit: config.myTracksLimit,
27
- dormantDays: config.dormantDays,
28
- logger: (...args: any[]) => logger.debug(...args),
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
- async function checkForUpdates(config: PluginConfig, sessionKey?: string) {
35
- if (!config.enabled) {
36
- logger.debug('SoundCloud watcher is disabled');
37
- return;
38
- }
65
+ function getSetupMessage(): string {
66
+ const config = loadConfig();
67
+
68
+ if (config) {
69
+ return `# SoundCloud Watcher Setup
39
70
 
40
- try {
41
- logger.info('Running SoundCloud check...');
42
- const message = await getWatcher().runCron();
43
-
44
- if (message) {
45
- logger.info('SoundCloud updates found');
46
-
47
- if (sessionKey) {
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
- function startChecking(config: PluginConfig, sessionKey?: string) {
66
- if (checkInterval) {
67
- clearInterval(checkInterval);
80
+ Then restart: \`openclaw gateway restart\``;
68
81
  }
82
+
83
+ return `# SoundCloud Watcher Setup
69
84
 
70
- const intervalMs = config.checkIntervalHours * 60 * 60 * 1000;
85
+ Not configured yet.
71
86
 
72
- checkForUpdates(config, sessionKey).catch((err) => {
73
- logger.error('Initial SoundCloud check failed:', err);
74
- });
87
+ ## Steps:
75
88
 
76
- checkInterval = setInterval(() => {
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
- logger.info(`SoundCloud watcher started (checking every ${config.checkIntervalHours}h)`);
83
- }
91
+ 2. Create config file:
92
+ \`\`\`bash
93
+ mkdir -p ~/.openclaw/secrets
94
+ nano ~/.openclaw/secrets/soundcloud.env
95
+ \`\`\`
84
96
 
85
- function stopChecking() {
86
- if (checkInterval) {
87
- clearInterval(checkInterval);
88
- checkInterval = null;
89
- logger.info('SoundCloud watcher stopped');
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: 'Interactive setup for SoundCloud credentials',
97
- handler: async (ctx: any) => {
98
- const config = api.getConfig() as PluginConfig;
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 (ctx: any) => {
145
- const result = await getWatcher().status();
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 (ctx: any) => {
154
- const result = await getWatcher().check();
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, ...usernames: string[]) => {
163
- if (usernames.length === 0) {
164
- return { text: 'Usage: /soundcloud-add <username> [username2] ...' };
165
- }
166
- const result = await getWatcher().addArtists(usernames);
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, username?: string) => {
175
- if (!username) {
176
- return { text: 'Usage: /soundcloud-remove <username>' };
177
- }
178
- const result = await getWatcher().removeArtist(username);
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 (ctx: any) => {
187
- const result = await getWatcher().listArtists();
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
- // Handle config changes
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
  }
@@ -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": "1.0.0",
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": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akilles/soundcloud-watcher",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "OpenClaw plugin to monitor SoundCloud account and track artist releases",
5
5
  "main": "index.ts",
6
6
  "openclaw": {