cord-bot 1.0.5 → 1.1.1

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/bin/cord.ts CHANGED
@@ -20,6 +20,7 @@
20
20
  * cord reply <channel> <messageId> "message"
21
21
  * cord thread <channel> <messageId> "name"
22
22
  * cord react <channel> <messageId> "emoji"
23
+ * cord state <channel> <messageId> <state> (processing, done, error, or custom)
23
24
  */
24
25
 
25
26
  import { spawn, spawnSync } from 'bun';
@@ -375,6 +376,47 @@ async function addReaction() {
375
376
  console.log(`Added reaction: ${emoji}`);
376
377
  }
377
378
 
379
+ // State presets for thread status updates
380
+ const STATE_PRESETS: Record<string, string> = {
381
+ processing: '🤖 Processing...',
382
+ thinking: '🧠 Thinking...',
383
+ searching: '🔍 Searching...',
384
+ writing: '✍️ Writing...',
385
+ done: '✅ Done',
386
+ error: '❌ Something went wrong',
387
+ waiting: '⏳ Waiting for input...',
388
+ };
389
+
390
+ async function updateState() {
391
+ const channel = args[0];
392
+ const messageId = args[1];
393
+ const stateOrCustom = args[2];
394
+
395
+ if (!channel || !messageId || !stateOrCustom) {
396
+ console.error('Usage: cord state <channel> <messageId> <state>');
397
+ console.error('');
398
+ console.error('Preset states:');
399
+ console.error(' processing - 🤖 Processing...');
400
+ console.error(' thinking - 🧠 Thinking...');
401
+ console.error(' searching - 🔍 Searching...');
402
+ console.error(' writing - ✍️ Writing...');
403
+ console.error(' done - ✅ Done');
404
+ console.error(' error - ❌ Something went wrong');
405
+ console.error(' waiting - ⏳ Waiting for input...');
406
+ console.error('');
407
+ console.error('Or use custom text: cord state <channel> <messageId> "Custom status"');
408
+ process.exit(1);
409
+ }
410
+
411
+ const content = STATE_PRESETS[stateOrCustom.toLowerCase()] || stateOrCustom;
412
+
413
+ await apiCall('/command', {
414
+ command: 'edit-message',
415
+ args: { channel, message: messageId, content },
416
+ });
417
+ console.log(`Updated state: ${content}`);
418
+ }
419
+
378
420
  // ============ Management Commands ============
379
421
 
380
422
  async function setup() {
@@ -603,11 +645,17 @@ Discord Commands:
603
645
  react <channel> <messageId> "emoji"
604
646
  Add a reaction to a message
605
647
 
648
+ state <channel> <messageId> <state>
649
+ Update thread status with preset or custom text
650
+ Presets: processing, thinking, searching, writing, done, error, waiting
651
+
606
652
  Examples:
607
653
  cord send 123456789 "Hello world!"
608
654
  cord embed 123456789 "Status update" --title "Daily Report" --color green --field "Tasks:5 done:inline"
609
655
  cord buttons 123456789 "Approve?" --button label="Yes" id="approve" style="success" reply="Approved!"
610
656
  cord file 123456789 ./report.md "Here's the report"
657
+ cord state 123456789 1234567890 processing
658
+ cord state 123456789 1234567890 done
611
659
  `);
612
660
  }
613
661
 
@@ -668,6 +716,9 @@ switch (command) {
668
716
  case 'react':
669
717
  addReaction();
670
718
  break;
719
+ case 'state':
720
+ updateState();
721
+ break;
671
722
 
672
723
  default:
673
724
  console.log(`Unknown command: ${command}`);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cord-bot",
3
- "version": "1.0.5",
3
+ "version": "1.1.1",
4
4
  "description": "Discord bot that bridges messages to Claude Code sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -223,6 +223,40 @@ cord react 123456789 987654321 "👍"
223
223
 
224
224
  ---
225
225
 
226
+ ### state
227
+
228
+ Update a message with a status indicator. Use this to show work progress on a thread starter or status message.
229
+
230
+ ```bash
231
+ cord state <channel> <messageId> <state>
232
+ ```
233
+
234
+ **Preset states:**
235
+ | State | Display |
236
+ |-------|---------|
237
+ | `processing` | 🤖 Processing... |
238
+ | `thinking` | 🧠 Thinking... |
239
+ | `searching` | 🔍 Searching... |
240
+ | `writing` | ✍️ Writing... |
241
+ | `done` | ✅ Done |
242
+ | `error` | ❌ Something went wrong |
243
+ | `waiting` | ⏳ Waiting for input... |
244
+
245
+ **Examples:**
246
+
247
+ Using presets:
248
+ ```bash
249
+ cord state 123456789 987654321 processing
250
+ cord state 123456789 987654321 done
251
+ ```
252
+
253
+ Custom status:
254
+ ```bash
255
+ cord state 123456789 987654321 "🔄 Syncing database..."
256
+ ```
257
+
258
+ ---
259
+
226
260
  ## Choosing the Right Command
227
261
 
228
262
  | Use Case | Command |
@@ -231,7 +265,8 @@ cord react 123456789 987654321 "👍"
231
265
  | Formatted status update | `cord embed` |
232
266
  | Long content (logs, reports) | `cord file` |
233
267
  | User needs to make a choice | `cord buttons` |
234
- | Indicate processing | `cord typing` |
268
+ | Indicate processing (typing bubble) | `cord typing` |
269
+ | Update thread/message status | `cord state` |
235
270
  | Update previous message | `cord edit` |
236
271
  | Start a focused discussion | `cord thread` |
237
272
  | Quick acknowledgment | `cord react` |
@@ -263,15 +298,21 @@ cord buttons 123456789 "What would you like to do?" \
263
298
  # Start with typing indicator
264
299
  cord typing 123456789
265
300
 
266
- # Send initial status
267
- MSGID=$(cord send 123456789 "Processing... 0%" | grep -o '[0-9]*$')
301
+ # Send initial status message
302
+ MSGID=$(cord send 123456789 "🤖 Processing..." | grep -o '[0-9]*$')
268
303
 
269
- # Update as progress continues
270
- cord edit 123456789 $MSGID "Processing... 50%"
271
- cord edit 123456789 $MSGID "Processing... 100% Complete!"
304
+ # Update state as work progresses
305
+ cord state 123456789 $MSGID searching
306
+ cord state 123456789 $MSGID writing
307
+ cord state 123456789 $MSGID done
308
+ ```
272
309
 
273
- # Add completion reaction
274
- cord react 123456789 $MSGID "✅"
310
+ Or with custom progress:
311
+ ```bash
312
+ cord state 123456789 $MSGID "🔄 Step 1/3: Fetching data..."
313
+ cord state 123456789 $MSGID "🔄 Step 2/3: Processing..."
314
+ cord state 123456789 $MSGID "🔄 Step 3/3: Generating report..."
315
+ cord state 123456789 $MSGID done
275
316
  ```
276
317
 
277
318
  ### Report delivery
@@ -299,6 +340,16 @@ cord buttons 123456789 "Delete all archived items older than 30 days?" \
299
340
 
300
341
  ---
301
342
 
343
+ ## Auto-Complete Behavior
344
+
345
+ When a user adds a ✅ reaction to the **last message** in a thread, Cord automatically:
346
+ 1. Detects the reaction
347
+ 2. Updates the thread starter message to "✅ Done"
348
+
349
+ This provides a quick way for users to signal "conversation complete" without explicit commands.
350
+
351
+ ---
352
+
302
353
  ## HTTP API
303
354
 
304
355
  For advanced use cases (webhooks, external scripts), see [HTTP-API.md](./HTTP-API.md).
package/src/bot.ts CHANGED
@@ -29,6 +29,7 @@ const client = new Client({
29
29
  GatewayIntentBits.Guilds,
30
30
  GatewayIntentBits.GuildMessages,
31
31
  GatewayIntentBits.MessageContent,
32
+ GatewayIntentBits.GuildMessageReactions,
32
33
  ],
33
34
  });
34
35
 
@@ -176,6 +177,49 @@ client.on(Events.MessageCreate, async (message: Message) => {
176
177
  });
177
178
  });
178
179
 
180
+ // =========================================================================
181
+ // REACTION HANDLER: ✅ on last message marks thread as done
182
+ // =========================================================================
183
+ client.on(Events.MessageReactionAdd, async (reaction, user) => {
184
+ // Ignore bot reactions
185
+ if (user.bot) return;
186
+
187
+ // Only handle ✅ reactions
188
+ if (reaction.emoji.name !== '✅') return;
189
+
190
+ // Only handle reactions in threads
191
+ const channel = reaction.message.channel;
192
+ if (!channel.isThread()) return;
193
+
194
+ try {
195
+ const thread = channel;
196
+ const parentChannelId = thread.parentId;
197
+ if (!parentChannelId) return;
198
+
199
+ // Check if this is the last message in the thread
200
+ const messages = await thread.messages.fetch({ limit: 1 });
201
+ const lastMessage = messages.first();
202
+
203
+ if (!lastMessage || lastMessage.id !== reaction.message.id) {
204
+ // Reaction is not on the last message, ignore
205
+ return;
206
+ }
207
+
208
+ log(`✅ reaction on last message in thread ${thread.id}`);
209
+
210
+ // Update thread starter message to "Done"
211
+ // The thread ID equals the starter message ID (thread was created from that message)
212
+ const parentChannel = await client.channels.fetch(parentChannelId);
213
+ if (parentChannel?.isTextBased()) {
214
+ const starterMessage = await (parentChannel as TextChannel).messages.fetch(thread.id);
215
+ await starterMessage.edit('✅ Done');
216
+ log(`Thread ${thread.id} marked as Done`);
217
+ }
218
+ } catch (error) {
219
+ log(`Failed to mark thread done: ${error}`);
220
+ }
221
+ });
222
+
179
223
  // Start the bot
180
224
  const token = process.env.DISCORD_BOT_TOKEN;
181
225
  if (!token) {