cord-bot 1.0.2 → 1.0.5
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/.github/workflows/publish.yml +32 -0
- package/README.md +59 -58
- package/bin/cord.ts +469 -17
- package/package.json +6 -1
- package/skills/cord/HTTP-API.md +273 -0
- package/skills/cord/SKILL.md +304 -0
- package/src/api.ts +339 -0
- package/src/bot.ts +46 -1
package/bin/cord.ts
CHANGED
|
@@ -2,22 +2,57 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Cord CLI - Manage your Discord-Claude bridge
|
|
4
4
|
*
|
|
5
|
-
* Commands:
|
|
5
|
+
* Management Commands:
|
|
6
6
|
* cord start - Start bot and worker
|
|
7
7
|
* cord stop - Stop all processes
|
|
8
8
|
* cord status - Show running status
|
|
9
|
-
* cord logs - Show combined logs
|
|
10
9
|
* cord setup - Interactive setup wizard
|
|
10
|
+
*
|
|
11
|
+
* Discord Commands:
|
|
12
|
+
* cord send <channel> "message"
|
|
13
|
+
* cord embed <channel> "description" [--title, --color, --field, etc.]
|
|
14
|
+
* cord file <channel> <filepath> "message"
|
|
15
|
+
* cord buttons <channel> "prompt" --button label="..." id="..." [style, reply, webhook]
|
|
16
|
+
* cord typing <channel>
|
|
17
|
+
* cord edit <channel> <messageId> "content"
|
|
18
|
+
* cord delete <channel> <messageId>
|
|
19
|
+
* cord rename <threadId> "name"
|
|
20
|
+
* cord reply <channel> <messageId> "message"
|
|
21
|
+
* cord thread <channel> <messageId> "name"
|
|
22
|
+
* cord react <channel> <messageId> "emoji"
|
|
11
23
|
*/
|
|
12
24
|
|
|
13
25
|
import { spawn, spawnSync } from 'bun';
|
|
14
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
15
|
-
import { join } from 'path';
|
|
26
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync } from 'fs';
|
|
27
|
+
import { join, dirname } from 'path';
|
|
16
28
|
import * as readline from 'readline';
|
|
29
|
+
import { homedir } from 'os';
|
|
17
30
|
|
|
18
31
|
const PID_FILE = join(process.cwd(), '.cord.pid');
|
|
32
|
+
const API_BASE = process.env.CORD_API_URL || 'http://localhost:2643';
|
|
19
33
|
|
|
20
34
|
const command = process.argv[2];
|
|
35
|
+
const args = process.argv.slice(3);
|
|
36
|
+
|
|
37
|
+
// Color name to Discord color int mapping
|
|
38
|
+
const COLORS: Record<string, number> = {
|
|
39
|
+
red: 15158332, // 0xE74C3C
|
|
40
|
+
green: 3066993, // 0x2ECC71
|
|
41
|
+
blue: 3447003, // 0x3498DB
|
|
42
|
+
yellow: 16776960, // 0xFFFF00
|
|
43
|
+
purple: 10181046, // 0x9B59B6
|
|
44
|
+
orange: 15105570, // 0xE67E22
|
|
45
|
+
gray: 9807270, // 0x95A5A6
|
|
46
|
+
grey: 9807270, // 0x95A5A6
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Button style name to Discord style int mapping
|
|
50
|
+
const BUTTON_STYLES: Record<string, number> = {
|
|
51
|
+
primary: 1,
|
|
52
|
+
secondary: 2,
|
|
53
|
+
success: 3,
|
|
54
|
+
danger: 4,
|
|
55
|
+
};
|
|
21
56
|
|
|
22
57
|
async function prompt(question: string): Promise<string> {
|
|
23
58
|
const rl = readline.createInterface({
|
|
@@ -32,6 +67,316 @@ async function prompt(question: string): Promise<string> {
|
|
|
32
67
|
});
|
|
33
68
|
}
|
|
34
69
|
|
|
70
|
+
// ============ API Helper ============
|
|
71
|
+
|
|
72
|
+
async function apiCall(endpoint: string, body: any): Promise<any> {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(`${API_BASE}${endpoint}`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify(body),
|
|
78
|
+
});
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
if (!response.ok || data.error) {
|
|
81
|
+
console.error('Error:', data.error || 'Request failed');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
return data;
|
|
85
|
+
} catch (error: any) {
|
|
86
|
+
if (error.code === 'ECONNREFUSED') {
|
|
87
|
+
console.error('Error: Cannot connect to Cord API. Is the bot running? (cord start)');
|
|
88
|
+
} else {
|
|
89
|
+
console.error('Error:', error.message);
|
|
90
|
+
}
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============ Discord Commands ============
|
|
96
|
+
|
|
97
|
+
async function sendMessage() {
|
|
98
|
+
const channel = args[0];
|
|
99
|
+
const message = args[1];
|
|
100
|
+
if (!channel || !message) {
|
|
101
|
+
console.error('Usage: cord send <channel> "message"');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const result = await apiCall('/command', {
|
|
105
|
+
command: 'send-to-thread',
|
|
106
|
+
args: { thread: channel, message },
|
|
107
|
+
});
|
|
108
|
+
console.log(`Sent message: ${result.messageId}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function sendEmbed() {
|
|
112
|
+
const channel = args[0];
|
|
113
|
+
if (!channel) {
|
|
114
|
+
console.error('Usage: cord embed <channel> "description" [--title "..." --color green ...]');
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Parse flags and find positional description
|
|
119
|
+
const embed: any = {};
|
|
120
|
+
const fields: any[] = [];
|
|
121
|
+
let description = '';
|
|
122
|
+
let i = 1;
|
|
123
|
+
|
|
124
|
+
while (i < args.length) {
|
|
125
|
+
const arg = args[i];
|
|
126
|
+
if (arg === '--title' && args[i + 1]) {
|
|
127
|
+
embed.title = args[++i];
|
|
128
|
+
} else if (arg === '--url' && args[i + 1]) {
|
|
129
|
+
embed.url = args[++i];
|
|
130
|
+
} else if (arg === '--color' && args[i + 1]) {
|
|
131
|
+
const colorArg = args[++i].toLowerCase();
|
|
132
|
+
embed.color = COLORS[colorArg] || parseInt(colorArg.replace('0x', ''), 16) || 0;
|
|
133
|
+
} else if (arg === '--author' && args[i + 1]) {
|
|
134
|
+
embed.author = embed.author || {};
|
|
135
|
+
embed.author.name = args[++i];
|
|
136
|
+
} else if (arg === '--author-url' && args[i + 1]) {
|
|
137
|
+
embed.author = embed.author || {};
|
|
138
|
+
embed.author.url = args[++i];
|
|
139
|
+
} else if (arg === '--author-icon' && args[i + 1]) {
|
|
140
|
+
embed.author = embed.author || {};
|
|
141
|
+
embed.author.icon_url = args[++i];
|
|
142
|
+
} else if (arg === '--thumbnail' && args[i + 1]) {
|
|
143
|
+
embed.thumbnail = { url: args[++i] };
|
|
144
|
+
} else if (arg === '--image' && args[i + 1]) {
|
|
145
|
+
embed.image = { url: args[++i] };
|
|
146
|
+
} else if (arg === '--footer' && args[i + 1]) {
|
|
147
|
+
embed.footer = embed.footer || {};
|
|
148
|
+
embed.footer.text = args[++i];
|
|
149
|
+
} else if (arg === '--footer-icon' && args[i + 1]) {
|
|
150
|
+
embed.footer = embed.footer || {};
|
|
151
|
+
embed.footer.icon_url = args[++i];
|
|
152
|
+
} else if (arg === '--timestamp') {
|
|
153
|
+
embed.timestamp = new Date().toISOString();
|
|
154
|
+
} else if (arg === '--field' && args[i + 1]) {
|
|
155
|
+
const fieldStr = args[++i];
|
|
156
|
+
const parts = fieldStr.split(':');
|
|
157
|
+
if (parts.length >= 2) {
|
|
158
|
+
fields.push({
|
|
159
|
+
name: parts[0],
|
|
160
|
+
value: parts[1],
|
|
161
|
+
inline: parts[2]?.toLowerCase() === 'inline',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
} else if (!arg.startsWith('--')) {
|
|
165
|
+
description = arg;
|
|
166
|
+
}
|
|
167
|
+
i++;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (description) embed.description = description;
|
|
171
|
+
if (fields.length > 0) embed.fields = fields;
|
|
172
|
+
|
|
173
|
+
const result = await apiCall('/command', {
|
|
174
|
+
command: 'send-to-thread',
|
|
175
|
+
args: { thread: channel, embeds: [embed] },
|
|
176
|
+
});
|
|
177
|
+
console.log(`Sent embed: ${result.messageId}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function sendFile() {
|
|
181
|
+
const channel = args[0];
|
|
182
|
+
const filepath = args[1];
|
|
183
|
+
const message = args[2] || '';
|
|
184
|
+
|
|
185
|
+
if (!channel || !filepath) {
|
|
186
|
+
console.error('Usage: cord file <channel> <filepath> ["message"]');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!existsSync(filepath)) {
|
|
191
|
+
console.error(`Error: File not found: ${filepath}`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const fileContent = readFileSync(filepath, 'utf-8');
|
|
196
|
+
const fileName = filepath.split('/').pop() || 'file.txt';
|
|
197
|
+
|
|
198
|
+
const result = await apiCall('/send-with-file', {
|
|
199
|
+
channelId: channel,
|
|
200
|
+
fileName,
|
|
201
|
+
fileContent,
|
|
202
|
+
content: message,
|
|
203
|
+
});
|
|
204
|
+
console.log(`Sent file: ${result.messageId}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function sendButtons() {
|
|
208
|
+
const channel = args[0];
|
|
209
|
+
if (!channel) {
|
|
210
|
+
console.error('Usage: cord buttons <channel> "prompt" --button label="..." id="..." [style="success"] [reply="..."] [webhook="..."]');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let promptText = '';
|
|
215
|
+
const buttons: any[] = [];
|
|
216
|
+
let i = 1;
|
|
217
|
+
|
|
218
|
+
while (i < args.length) {
|
|
219
|
+
const arg = args[i];
|
|
220
|
+
if (arg === '--button') {
|
|
221
|
+
// Collect all following key=value pairs until next flag or end
|
|
222
|
+
const button: any = {};
|
|
223
|
+
i++;
|
|
224
|
+
while (i < args.length && !args[i].startsWith('--')) {
|
|
225
|
+
const kvMatch = args[i].match(/^(\w+)=(.*)$/);
|
|
226
|
+
if (kvMatch) {
|
|
227
|
+
const [, key, value] = kvMatch;
|
|
228
|
+
if (key === 'style') {
|
|
229
|
+
button.style = BUTTON_STYLES[value.toLowerCase()] || 1;
|
|
230
|
+
} else {
|
|
231
|
+
button[key] = value;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
i++;
|
|
235
|
+
}
|
|
236
|
+
if (button.label && button.id) {
|
|
237
|
+
// Convert to API format
|
|
238
|
+
const apiButton: any = {
|
|
239
|
+
label: button.label,
|
|
240
|
+
customId: button.id,
|
|
241
|
+
style: button.style || 1,
|
|
242
|
+
};
|
|
243
|
+
if (button.reply || button.webhook) {
|
|
244
|
+
apiButton.handler = {};
|
|
245
|
+
if (button.reply) {
|
|
246
|
+
apiButton.handler.type = 'inline';
|
|
247
|
+
apiButton.handler.content = button.reply;
|
|
248
|
+
apiButton.handler.ephemeral = true;
|
|
249
|
+
}
|
|
250
|
+
if (button.webhook) {
|
|
251
|
+
apiButton.handler.type = button.reply ? 'inline' : 'webhook';
|
|
252
|
+
apiButton.handler.webhookUrl = button.webhook;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
buttons.push(apiButton);
|
|
256
|
+
}
|
|
257
|
+
continue; // Don't increment i again
|
|
258
|
+
} else if (!arg.startsWith('--')) {
|
|
259
|
+
promptText = arg;
|
|
260
|
+
}
|
|
261
|
+
i++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (buttons.length === 0) {
|
|
265
|
+
console.error('Error: At least one --button is required');
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const result = await apiCall('/send-with-buttons', {
|
|
270
|
+
channelId: channel,
|
|
271
|
+
content: promptText,
|
|
272
|
+
buttons,
|
|
273
|
+
});
|
|
274
|
+
console.log(`Sent buttons: ${result.messageId}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function startTyping() {
|
|
278
|
+
const channel = args[0];
|
|
279
|
+
if (!channel) {
|
|
280
|
+
console.error('Usage: cord typing <channel>');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
await apiCall('/command', {
|
|
284
|
+
command: 'start-typing',
|
|
285
|
+
args: { channel },
|
|
286
|
+
});
|
|
287
|
+
console.log('Typing indicator sent');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function editMessage() {
|
|
291
|
+
const channel = args[0];
|
|
292
|
+
const messageId = args[1];
|
|
293
|
+
const content = args[2];
|
|
294
|
+
if (!channel || !messageId || !content) {
|
|
295
|
+
console.error('Usage: cord edit <channel> <messageId> "new content"');
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
await apiCall('/command', {
|
|
299
|
+
command: 'edit-message',
|
|
300
|
+
args: { channel, message: messageId, content },
|
|
301
|
+
});
|
|
302
|
+
console.log(`Edited message: ${messageId}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function deleteMessage() {
|
|
306
|
+
const channel = args[0];
|
|
307
|
+
const messageId = args[1];
|
|
308
|
+
if (!channel || !messageId) {
|
|
309
|
+
console.error('Usage: cord delete <channel> <messageId>');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
await apiCall('/command', {
|
|
313
|
+
command: 'delete-message',
|
|
314
|
+
args: { channel, message: messageId },
|
|
315
|
+
});
|
|
316
|
+
console.log(`Deleted message: ${messageId}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function renameThread() {
|
|
320
|
+
const threadId = args[0];
|
|
321
|
+
const name = args[1];
|
|
322
|
+
if (!threadId || !name) {
|
|
323
|
+
console.error('Usage: cord rename <threadId> "new name"');
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
await apiCall('/command', {
|
|
327
|
+
command: 'rename-thread',
|
|
328
|
+
args: { thread: threadId, name },
|
|
329
|
+
});
|
|
330
|
+
console.log(`Renamed thread: ${threadId}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function replyToMessage() {
|
|
334
|
+
const channel = args[0];
|
|
335
|
+
const messageId = args[1];
|
|
336
|
+
const message = args[2];
|
|
337
|
+
if (!channel || !messageId || !message) {
|
|
338
|
+
console.error('Usage: cord reply <channel> <messageId> "message"');
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
const result = await apiCall('/command', {
|
|
342
|
+
command: 'reply-to-message',
|
|
343
|
+
args: { channel, message: messageId, content: message },
|
|
344
|
+
});
|
|
345
|
+
console.log(`Replied to message: ${result.messageId}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function createThread() {
|
|
349
|
+
const channel = args[0];
|
|
350
|
+
const messageId = args[1];
|
|
351
|
+
const name = args[2];
|
|
352
|
+
if (!channel || !messageId || !name) {
|
|
353
|
+
console.error('Usage: cord thread <channel> <messageId> "thread name"');
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
const result = await apiCall('/command', {
|
|
357
|
+
command: 'create-thread',
|
|
358
|
+
args: { channel, message: messageId, name },
|
|
359
|
+
});
|
|
360
|
+
console.log(`Created thread: ${result.threadId}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function addReaction() {
|
|
364
|
+
const channel = args[0];
|
|
365
|
+
const messageId = args[1];
|
|
366
|
+
const emoji = args[2];
|
|
367
|
+
if (!channel || !messageId || !emoji) {
|
|
368
|
+
console.error('Usage: cord react <channel> <messageId> "emoji"');
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
await apiCall('/command', {
|
|
372
|
+
command: 'add-reaction',
|
|
373
|
+
args: { channel, message: messageId, emoji },
|
|
374
|
+
});
|
|
375
|
+
console.log(`Added reaction: ${emoji}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ============ Management Commands ============
|
|
379
|
+
|
|
35
380
|
async function setup() {
|
|
36
381
|
console.log('\n🔌 Cord Setup\n');
|
|
37
382
|
|
|
@@ -76,6 +421,23 @@ async function setup() {
|
|
|
76
421
|
console.log('⚠ Claude CLI not found. Install from: https://claude.ai/code');
|
|
77
422
|
}
|
|
78
423
|
|
|
424
|
+
// Install Claude Code skill
|
|
425
|
+
const skillsDir = join(homedir(), '.claude', 'skills', 'cord');
|
|
426
|
+
const cordRoot = join(dirname(import.meta.dir));
|
|
427
|
+
const sourceSkillsDir = join(cordRoot, 'skills', 'cord');
|
|
428
|
+
|
|
429
|
+
if (existsSync(sourceSkillsDir)) {
|
|
430
|
+
console.log('\n📚 Claude Code Skill');
|
|
431
|
+
console.log(' Teaches your assistant how to send Discord messages, embeds,');
|
|
432
|
+
console.log(' files, and interactive buttons.');
|
|
433
|
+
const installSkill = await prompt('Install skill? (Y/n): ');
|
|
434
|
+
if (installSkill.toLowerCase() !== 'n') {
|
|
435
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
436
|
+
cpSync(sourceSkillsDir, skillsDir, { recursive: true });
|
|
437
|
+
console.log(`✓ Skill installed to ${skillsDir}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
79
441
|
console.log('\n✨ Setup complete! Run: cord start\n');
|
|
80
442
|
}
|
|
81
443
|
|
|
@@ -180,25 +542,79 @@ function showHelp() {
|
|
|
180
542
|
console.log(`
|
|
181
543
|
Cord - Discord to Claude Code bridge
|
|
182
544
|
|
|
183
|
-
Usage: cord <command>
|
|
184
|
-
|
|
185
|
-
Commands:
|
|
186
|
-
start
|
|
187
|
-
stop
|
|
188
|
-
status
|
|
189
|
-
setup
|
|
190
|
-
help
|
|
545
|
+
Usage: cord <command> [options]
|
|
546
|
+
|
|
547
|
+
Management Commands:
|
|
548
|
+
start Start bot and worker
|
|
549
|
+
stop Stop all processes
|
|
550
|
+
status Show running status
|
|
551
|
+
setup Interactive setup wizard
|
|
552
|
+
help Show this help
|
|
553
|
+
|
|
554
|
+
Discord Commands:
|
|
555
|
+
send <channel> "message"
|
|
556
|
+
Send a text message
|
|
557
|
+
|
|
558
|
+
embed <channel> "description" [options]
|
|
559
|
+
Send an embed with optional formatting
|
|
560
|
+
--title "..." Embed title
|
|
561
|
+
--url "..." Title link URL
|
|
562
|
+
--color <name|hex> red, green, blue, yellow, purple, orange, or 0xHEX
|
|
563
|
+
--author "..." Author name
|
|
564
|
+
--author-url "..." Author link
|
|
565
|
+
--author-icon "..." Author icon URL
|
|
566
|
+
--thumbnail "..." Small image (top right)
|
|
567
|
+
--image "..." Large image (bottom)
|
|
568
|
+
--footer "..." Footer text
|
|
569
|
+
--footer-icon "..." Footer icon URL
|
|
570
|
+
--timestamp Add current timestamp
|
|
571
|
+
--field "Name:Value" Add field (use :inline for inline)
|
|
572
|
+
|
|
573
|
+
file <channel> <filepath> ["message"]
|
|
574
|
+
Send a file attachment
|
|
575
|
+
|
|
576
|
+
buttons <channel> "prompt" --button label="..." id="..." [options]
|
|
577
|
+
Send interactive buttons
|
|
578
|
+
Button options:
|
|
579
|
+
label="..." Button text (required)
|
|
580
|
+
id="..." Custom ID (required)
|
|
581
|
+
style="..." primary, secondary, success, danger
|
|
582
|
+
reply="..." Ephemeral reply when clicked
|
|
583
|
+
webhook="..." URL to POST click data to
|
|
584
|
+
|
|
585
|
+
typing <channel>
|
|
586
|
+
Show typing indicator
|
|
587
|
+
|
|
588
|
+
edit <channel> <messageId> "content"
|
|
589
|
+
Edit an existing message
|
|
590
|
+
|
|
591
|
+
delete <channel> <messageId>
|
|
592
|
+
Delete a message
|
|
593
|
+
|
|
594
|
+
rename <threadId> "name"
|
|
595
|
+
Rename a thread
|
|
596
|
+
|
|
597
|
+
reply <channel> <messageId> "message"
|
|
598
|
+
Reply to a specific message
|
|
599
|
+
|
|
600
|
+
thread <channel> <messageId> "name"
|
|
601
|
+
Create a thread from a message
|
|
602
|
+
|
|
603
|
+
react <channel> <messageId> "emoji"
|
|
604
|
+
Add a reaction to a message
|
|
191
605
|
|
|
192
606
|
Examples:
|
|
193
|
-
cord
|
|
194
|
-
cord
|
|
195
|
-
cord
|
|
196
|
-
cord
|
|
607
|
+
cord send 123456789 "Hello world!"
|
|
608
|
+
cord embed 123456789 "Status update" --title "Daily Report" --color green --field "Tasks:5 done:inline"
|
|
609
|
+
cord buttons 123456789 "Approve?" --button label="Yes" id="approve" style="success" reply="Approved!"
|
|
610
|
+
cord file 123456789 ./report.md "Here's the report"
|
|
197
611
|
`);
|
|
198
612
|
}
|
|
199
613
|
|
|
200
|
-
// Main
|
|
614
|
+
// ============ Main ============
|
|
615
|
+
|
|
201
616
|
switch (command) {
|
|
617
|
+
// Management
|
|
202
618
|
case 'start':
|
|
203
619
|
start();
|
|
204
620
|
break;
|
|
@@ -217,6 +633,42 @@ switch (command) {
|
|
|
217
633
|
case undefined:
|
|
218
634
|
showHelp();
|
|
219
635
|
break;
|
|
636
|
+
|
|
637
|
+
// Discord commands
|
|
638
|
+
case 'send':
|
|
639
|
+
sendMessage();
|
|
640
|
+
break;
|
|
641
|
+
case 'embed':
|
|
642
|
+
sendEmbed();
|
|
643
|
+
break;
|
|
644
|
+
case 'file':
|
|
645
|
+
sendFile();
|
|
646
|
+
break;
|
|
647
|
+
case 'buttons':
|
|
648
|
+
sendButtons();
|
|
649
|
+
break;
|
|
650
|
+
case 'typing':
|
|
651
|
+
startTyping();
|
|
652
|
+
break;
|
|
653
|
+
case 'edit':
|
|
654
|
+
editMessage();
|
|
655
|
+
break;
|
|
656
|
+
case 'delete':
|
|
657
|
+
deleteMessage();
|
|
658
|
+
break;
|
|
659
|
+
case 'rename':
|
|
660
|
+
renameThread();
|
|
661
|
+
break;
|
|
662
|
+
case 'reply':
|
|
663
|
+
replyToMessage();
|
|
664
|
+
break;
|
|
665
|
+
case 'thread':
|
|
666
|
+
createThread();
|
|
667
|
+
break;
|
|
668
|
+
case 'react':
|
|
669
|
+
addReaction();
|
|
670
|
+
break;
|
|
671
|
+
|
|
220
672
|
default:
|
|
221
673
|
console.log(`Unknown command: ${command}`);
|
|
222
674
|
showHelp();
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cord-bot",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Discord bot that bridges messages to Claude Code sessions",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/alexknowshtml/cord"
|
|
8
|
+
},
|
|
4
9
|
"module": "index.ts",
|
|
5
10
|
"type": "module",
|
|
6
11
|
"bin": {
|