Skip to content

Commit c13a81c

Browse files
author
Joshua Chittick
committed
fix: stabilize discord status message updates
1 parent 67aa7a5 commit c13a81c

File tree

3 files changed

+39
-15
lines changed

3 files changed

+39
-15
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ode",
3-
"version": "0.0.51",
3+
"version": "0.0.52",
44
"description": "Coding anywhere with your coding agents connected",
55
"module": "packages/core/index.ts",
66
"type": "module",

packages/config/local/sessions.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,22 @@ export function getSessionsWithPendingRequests(): PersistedSession[] {
284284
);
285285
}
286286

287+
export function findReplyThreadIdByStatusMessageTs(messageTs: string): string | null {
288+
if (activeSessions.size === 0) {
289+
loadAllSessions();
290+
}
291+
292+
for (const session of activeSessions.values()) {
293+
const activeRequest = session.activeRequest;
294+
if (!activeRequest) continue;
295+
if (activeRequest.statusMessageTs === messageTs) {
296+
return activeRequest.replyThreadId || null;
297+
}
298+
}
299+
300+
return null;
301+
}
302+
287303
// Deduplication
288304
function buildMessageDedupKey(channelId: string, threadId: string, messageTs: string): string {
289305
return `${channelId}:${threadId}:${messageTs}`;

packages/ims/discord/client.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ButtonStyle,
55
Client,
66
GatewayIntentBits,
7+
MessageFlags,
78
ModalBuilder,
89
Partials,
910
StringSelectMenuBuilder,
@@ -36,11 +37,12 @@ import {
3637
setGitHubInfoForUser,
3738
setUserGeneralSettings,
3839
} from "@/config";
40+
import { findReplyThreadIdByStatusMessageTs } from "@/config/local/sessions";
3941
import { isThreadActive, markThreadActive } from "@/config/local/settings";
4042
import { log } from "@/utils";
4143

4244
const DISCORD_MESSAGE_LIMIT = 2000;
43-
const DISCORD_THREAD_NAME_LIMIT = 100;
45+
const DISCORD_THREAD_NAME_LIMIT = 25;
4446
const DISCORD_MODAL_CHANNEL = "ode:modal:channel_details";
4547
const DISCORD_MODAL_GITHUB = "ode:modal:github";
4648
const STATUS_FORMAT_OPTIONS = ["aggressive", "medium", "minimum"] as const;
@@ -132,25 +134,31 @@ async function updateMessage(
132134
_asMarkdown = true
133135
): Promise<void> {
134136
try {
135-
const threadId = statusMessageThreadMap.get(messageId) || channelId;
137+
const mappedThreadId = statusMessageThreadMap.get(messageId);
138+
const persistedThreadId = findReplyThreadIdByStatusMessageTs(messageId);
139+
const threadId = mappedThreadId || persistedThreadId || channelId;
136140
if (!threadId) {
137141
log.warn("Cannot update Discord message without known thread", { messageId });
138142
return;
139143
}
144+
if (!mappedThreadId && persistedThreadId) {
145+
statusMessageThreadMap.set(messageId, persistedThreadId);
146+
}
140147
const channel = await resolveTextChannel(threadId);
141148
const message = await channel.messages.fetch(messageId);
142149
await message.edit(splitForDiscord(text)[0] ?? text);
143150
} catch (error) {
144151
log.warn("Failed to update Discord message", {
145152
messageId,
146153
channelId,
154+
resolvedChannelId: statusMessageThreadMap.get(messageId) || findReplyThreadIdByStatusMessageTs(messageId) || channelId,
147155
error: String(error),
148156
});
149157
}
150158
}
151159

152160
async function deleteMessage(_channelId: string, messageId: string): Promise<void> {
153-
const threadId = statusMessageThreadMap.get(messageId);
161+
const threadId = statusMessageThreadMap.get(messageId) || findReplyThreadIdByStatusMessageTs(messageId);
154162
if (!threadId) return;
155163
const channel = await resolveTextChannel(threadId);
156164
const message = await channel.messages.fetch(messageId);
@@ -539,7 +547,7 @@ async function handleLauncherButtonInteraction(interaction: any): Promise<void>
539547
channelId,
540548
userId: interaction.user.id,
541549
});
542-
await interaction.reply({ ...payload, ephemeral: true });
550+
await interaction.reply({ ...payload, flags: MessageFlags.Ephemeral });
543551
return;
544552
}
545553

@@ -548,7 +556,7 @@ async function handleLauncherButtonInteraction(interaction: any): Promise<void>
548556
channelId,
549557
userId: interaction.user.id,
550558
});
551-
await interaction.reply({ ...payload, ephemeral: true });
559+
await interaction.reply({ ...payload, flags: MessageFlags.Ephemeral });
552560
return;
553561
}
554562

@@ -573,7 +581,7 @@ async function handleModalSubmitInteraction(interaction: any): Promise<void> {
573581
setChannelBaseBranch(channelId, baseBranch);
574582
setChannelSystemMessage(channelId, channelSystemMessage);
575583

576-
await interaction.reply({ content: "Channel settings updated.", ephemeral: true });
584+
await interaction.reply({ content: "Channel settings updated.", flags: MessageFlags.Ephemeral });
577585
return;
578586
}
579587

@@ -586,7 +594,7 @@ async function handleModalSubmitInteraction(interaction: any): Promise<void> {
586594
gitName,
587595
gitEmail,
588596
});
589-
await interaction.reply({ content: "GitHub info updated.", ephemeral: true });
597+
await interaction.reply({ content: "GitHub info updated.", flags: MessageFlags.Ephemeral });
590598
}
591599
}
592600

@@ -606,7 +614,7 @@ async function handleGeneralSettingsComponentInteraction(interaction: any): Prom
606614
const selected = interaction.values?.[0] as string | undefined;
607615
const parsed = selected ? parseGeneralStatusFormat(selected) : null;
608616
if (!parsed) {
609-
await interaction.reply({ content: "Invalid status format.", ephemeral: true });
617+
await interaction.reply({ content: "Invalid status format.", flags: MessageFlags.Ephemeral });
610618
return true;
611619
}
612620
generalSettingsDrafts.set(key, {
@@ -622,7 +630,7 @@ async function handleGeneralSettingsComponentInteraction(interaction: any): Prom
622630
const selected = interaction.values?.[0] as string | undefined;
623631
const parsed = selected ? parseGitStrategy(selected) : null;
624632
if (!parsed) {
625-
await interaction.reply({ content: "Invalid git strategy.", ephemeral: true });
633+
await interaction.reply({ content: "Invalid git strategy.", flags: MessageFlags.Ephemeral });
626634
return true;
627635
}
628636
generalSettingsDrafts.set(key, {
@@ -640,7 +648,7 @@ async function handleGeneralSettingsComponentInteraction(interaction: any): Prom
640648
gitStrategy: draft.gitStrategy,
641649
});
642650
generalSettingsDrafts.delete(key);
643-
await interaction.reply({ content: "General settings updated.", ephemeral: true });
651+
await interaction.reply({ content: "General settings updated.", flags: MessageFlags.Ephemeral });
644652
return true;
645653
}
646654

@@ -663,7 +671,7 @@ async function handleChannelSettingsComponentInteraction(interaction: any): Prom
663671
const selected = interaction.values?.[0] as string | undefined;
664672
const parsed = selected ? parseProvider(selected) : null;
665673
if (!parsed || !isAgentEnabled(parsed)) {
666-
await interaction.reply({ content: "Selected provider is invalid or disabled.", ephemeral: true });
674+
await interaction.reply({ content: "Selected provider is invalid or disabled.", flags: MessageFlags.Ephemeral });
667675
return true;
668676
}
669677
const models = getProviderModels(parsed);
@@ -693,7 +701,7 @@ async function handleChannelSettingsComponentInteraction(interaction: any): Prom
693701
if (action === "save") {
694702
const models = getProviderModels(draft.provider);
695703
if (models.length > 0 && draft.model && !hasModel(models, draft.model)) {
696-
await interaction.reply({ content: "Selected model is no longer available.", ephemeral: true });
704+
await interaction.reply({ content: "Selected model is no longer available.", flags: MessageFlags.Ephemeral });
697705
return true;
698706
}
699707

@@ -704,7 +712,7 @@ async function handleChannelSettingsComponentInteraction(interaction: any): Prom
704712
setChannelModel(channelId, draft.model);
705713
}
706714
channelSettingsDrafts.delete(key);
707-
await interaction.reply({ content: "Channel provider/model updated.", ephemeral: true });
715+
await interaction.reply({ content: "Channel provider/model updated.", flags: MessageFlags.Ephemeral });
708716
return true;
709717
}
710718

@@ -881,7 +889,7 @@ export async function startDiscordRuntime(reason: string): Promise<boolean> {
881889

882890
await interaction.reply({
883891
...payload,
884-
ephemeral: true,
892+
flags: MessageFlags.Ephemeral,
885893
});
886894
} catch (error) {
887895
log.error("Discord interaction handler failed", { error: String(error) });

0 commit comments

Comments
 (0)