Skip to content

Commit 555ccef

Browse files
committed
Split bot embed across multiple Discord messages to avoid size limits
The bot embed was erroring out when it exceeded Discord's 6000 character embed limit. Instead of aborting the update entirely, the embed now splits bot listings into multiple messages: - Each message gets a single embed with up to 4000 chars of bot listings - The first message keeps the "Castle bots - last updated" title - Overflow messages are created/updated as needed using indexed names - Overflow messages are automatically deleted when no longer needed - Added deleteInstructionsMessage() to InstructionsReadyAction - Changed InstructionsReadyAction name param from Name enum to string to support dynamic overflow message names https://claude.ai/code/session_01F92hfgrzp44RWTTyFXyMQB
1 parent 884eff2 commit 555ccef

File tree

2 files changed

+62
-24
lines changed

2 files changed

+62
-24
lines changed

src/features/raid-bots/bot-embed.ts

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { APIEmbed, EmbedBuilder, Utils } from "discord.js";
2-
import { botEmbedChannelId, knightRoleId, raiderRoleId } from "../../config";
1+
import { EmbedBuilder } from "discord.js";
2+
import { botEmbedChannelId, raiderRoleId } from "../../config";
33
import { Name } from "../../db/instructions";
44
import { Bot } from "../../services/bot/public-accounts-sheet";
55
import { InstructionsReadyAction } from "../../shared/action/instructions-ready-action-2";
@@ -27,11 +27,18 @@ const truncate = (str: string, maxLength: number) => {
2727
if (str.length <= maxLength) return str;
2828
return str.slice(0, maxLength - 3) + "...";
2929
};
30+
31+
// Discord embed description limit is 4096 characters.
32+
// Use 4000 to leave room for formatting.
33+
const MAX_EMBED_DESCRIPTION = 4000;
34+
35+
// Maximum number of overflow messages to manage.
36+
const MAX_OVERFLOW_MESSAGES = 5;
37+
3038
export const refreshBotEmbed = async () => {
3139
const publicAccounts = PublicAccountsFactory.getService();
32-
let botString = "";
3340
const botMessages: string[] = [];
34-
const start = moment.now();
41+
let botString = "";
3542
const bots = await publicAccounts.getBots();
3643

3744
bots.forEach((bot: Bot) => {
@@ -51,37 +58,54 @@ export const refreshBotEmbed = async () => {
5158
} ${getClassAbreviation(bot.class)}) - ${bot.location} ${
5259
pilotName ? "~~" : ""
5360
} ${pilotName ? "- " + pilotName : ""}\u200B\n`;
54-
if (botString.length + botRow.length > 3000) {
61+
if (botString.length + botRow.length > MAX_EMBED_DESCRIPTION) {
5562
botMessages.push(botString);
5663
botString = "";
5764
}
5865
botString += botRow;
5966
});
6067

61-
if (botString.length > 6000) {
62-
console.log(`Embed length too long (${botString.length}),
63-
not attempting update`);
64-
return;
68+
if (botString) {
69+
botMessages.push(botString);
6570
}
6671

67-
botMessages.push(botString);
68-
try {
72+
// Send the first message using the primary instruction action
73+
if (botMessages.length > 0) {
6974
await botEmbedInstructions
7075
.createOrUpdateInstructions({
71-
embeds: botMessages.map((message: string, idx: number) => {
72-
return new EmbedBuilder({
73-
title:
74-
idx === 0
75-
? `Castle bots - last updated ${moment().toLocaleString()}`
76-
: "",
77-
description: message,
78-
});
79-
}),
76+
embeds: [
77+
new EmbedBuilder({
78+
title: `Castle bots - last updated ${moment().toLocaleString()}`,
79+
description: botMessages[0],
80+
}),
81+
],
8082
})
8183
.catch((reason) => {
8284
log(`Embed update failed: ${reason}`);
8385
});
84-
} catch (err: unknown) {
85-
log("Failed to update bot embed");
86+
}
87+
88+
// Send overflow messages or clean up ones that are no longer needed
89+
for (let i = 1; i <= MAX_OVERFLOW_MESSAGES; i++) {
90+
const overflowAction = new InstructionsReadyAction(
91+
`${Name.BotStatusEmbed}_${i}`,
92+
botEmbedChannelId
93+
);
94+
if (i < botMessages.length) {
95+
await overflowAction
96+
.createOrUpdateInstructions({
97+
embeds: [
98+
new EmbedBuilder({
99+
description: botMessages[i],
100+
}),
101+
],
102+
})
103+
.catch((reason) => {
104+
log(`Overflow embed ${i} update failed: ${reason}`);
105+
});
106+
} else {
107+
// Delete overflow messages that are no longer needed
108+
await overflowAction.deleteInstructionsMessage().catch(() => {});
109+
}
86110
}
87111
};

src/shared/action/instructions-ready-action-2.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { DiscordAPIError, BaseMessageOptions } from "discord.js";
22
import { dataSource } from "../../db/data-source";
3-
import { Instructions, Name } from "../../db/instructions";
3+
import { Instructions } from "../../db/instructions";
44
import { getTextChannel } from "../..";
55

66
export class InstructionsReadyAction {
77
public constructor(
8-
private readonly name: Name,
8+
private readonly name: string,
99
private readonly channelId: string,
1010
private readonly threadName?: string
1111
) {}
@@ -39,6 +39,20 @@ export class InstructionsReadyAction {
3939
}
4040
}
4141

42+
public async deleteInstructionsMessage() {
43+
const message = await this.getInstructionsMessage();
44+
if (message) {
45+
await message.delete();
46+
}
47+
const instructions = await dataSource
48+
.getRepository(Instructions)
49+
.findOneBy({ name: this.name, canceled: false });
50+
if (instructions) {
51+
instructions.canceled = true;
52+
await dataSource.manager.save(instructions);
53+
}
54+
}
55+
4256
public async getThread() {
4357
const embed = await this.getInstructionsMessage();
4458
return embed?.thread;

0 commit comments

Comments
 (0)