Skip to content

Commit ab9bb5f

Browse files
fix: errors when webhook status changes
Remove logging channel if channel has been deleted Remove webhook if webhook has been deleted and attempt to create another Await promise in try catch block
1 parent 4d2e839 commit ab9bb5f

File tree

8 files changed

+95
-29
lines changed

8 files changed

+95
-29
lines changed

src/interactions/commands/chatInput/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ async function handleLoggingChannelSetSubcommand({
598598
guildId: interaction.guild_id,
599599
embeds: [logEmbed],
600600
ignoreErrors: false,
601+
session,
601602
});
602603
}
603604
// If this fails it will be returned to the user

src/lib/logging/manager.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { RawFile } from "@discordjs/rest";
1+
import { RawFile, DiscordAPIError } from "@discordjs/rest";
22
import { Snowflake } from "discord-api-types/globals";
3-
import { APIEmbed, APIMessage } from "discord-api-types/v9";
3+
import { APIEmbed, APIMessage, RESTJSONErrorCodes } from "discord-api-types/v9";
44
import { FastifyInstance } from "fastify";
55

66
import { DiscordPermissions } from "../../consts";
@@ -84,13 +84,15 @@ export default class LoggingManager {
8484
message,
8585
embeds,
8686
files,
87+
session,
8788
}: {
8889
guildId: Snowflake;
8990

9091
ignoreErrors?: boolean;
9192
message?: string;
9293
embeds: APIEmbed[] | undefined;
9394
files?: RawFile[];
95+
session: GuildSession;
9496
}): Promise<APIMessage | void> {
9597
// Logs can be "passed" if the channel isn't set, or if the webhook doesn't exist and the bot cannot create a new webhook
9698
// This means that this function can be called without checking if the log channel is set
@@ -107,14 +109,26 @@ export default class LoggingManager {
107109
};
108110

109111
try {
110-
return this._webhookManager.sendWebhookMessage(
112+
return await this._webhookManager.sendWebhookMessage(
113+
// TODO: ADD AWAIT
111114
channelId,
112115
guildId,
113116
data,
114117
files
115118
);
116119
// eslint-disable-next-line no-empty
117120
} catch (error) {
121+
if (
122+
error instanceof DiscordAPIError &&
123+
error.code === RESTJSONErrorCodes.UnknownChannel
124+
) {
125+
// remove channel
126+
// This threw on the attempt to add / create a webhook on the channel
127+
// This is because the channel doesn't exist anymore
128+
await this.removeGuildLoggingChannel(session);
129+
return;
130+
}
131+
118132
if (!(ignoreErrors ?? false)) {
119133
throw error;
120134
}

src/lib/messages/delete.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,9 @@ async function deleteMessage({
239239
"\n\nEmbed representation can be found in the attachment.";
240240
}
241241
// Send log message
242-
await instance.loggingManager.sendLogMessage({
243-
guildId: session.guildId,
244-
embeds: [logEmbed],
245-
files,
242+
await session.sendLoggingMessage({
243+
logEmbeds: [logEmbed],
244+
files: files,
246245
});
247246
} catch (error) {
248247
if (error instanceof DiscordAPIError) {

src/lib/messages/edit.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ async function editMessage({
381381
}
382382

383383
// Send log message
384-
await instance.loggingManager.sendLogMessage({
385-
guildId: session.guildId,
386-
embeds: [logEmbed],
387-
files,
384+
385+
await session.sendLoggingMessage({
386+
logEmbeds: [logEmbed],
387+
files: files,
388388
});
389389
} catch (error) {
390390
if (error instanceof DiscordAPIError) {

src/lib/messages/send.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,9 @@ async function sendMessage({
274274
"\n\nEmbed representation can be found in the attachment.";
275275
}
276276
// Send log message
277-
await instance.loggingManager.sendLogMessage({
278-
guildId: session.guildId,
279-
embeds: [logEmbed],
280-
files,
277+
await session.sendLoggingMessage({
278+
logEmbeds: [logEmbed],
279+
files: files,
281280
});
282281
return messageResult;
283282
} catch (error) {

src/lib/permissions/manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class PermissionManager {
9595
actionBy: session.userId,
9696
actionType: ActionType.GENERAL,
9797
});
98-
await session.sendLoggingMessage(embed);
98+
await session.sendLoggingMessage({ logEmbeds: [embed] });
9999
}
100100

101101
private async _sendAllowDenyLogMessage({
@@ -179,7 +179,7 @@ class PermissionManager {
179179
actionBy: session.userId,
180180
actionType: ActionType.GENERAL,
181181
});
182-
await session.sendLoggingMessage(embed);
182+
await session.sendLoggingMessage({ logEmbeds: [embed] });
183183
}
184184

185185
async getAllGuildPermissions(

src/lib/session/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { RawFile } from "@discordjs/rest";
12
import { Snowflake } from "discord-api-types/globals";
23
import {
34
APIDMInteraction,
@@ -126,11 +127,22 @@ class GuildSession {
126127
});
127128
}
128129

129-
async sendLoggingMessage(logEmbed: APIEmbed): Promise<void> {
130+
async sendLoggingMessage({
131+
logEmbeds,
132+
message,
133+
files,
134+
}: {
135+
logEmbeds?: APIEmbed[];
136+
message?: string;
137+
files?: RawFile[];
138+
}): Promise<void> {
130139
await this._instance.loggingManager.sendLogMessage({
131-
embeds: [logEmbed],
140+
embeds: logEmbeds,
141+
message,
142+
files,
132143
guildId: this.guildId,
133144
ignoreErrors: true,
145+
session: this,
134146
});
135147
}
136148
}

src/lib/webhook/manager.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Snowflake } from "discord-api-types/globals";
33
import {
44
APIMessage,
55
RESTGetAPIChannelWebhooksResult,
6+
RESTJSONErrorCodes,
67
RESTPostAPIChannelWebhookResult,
78
RESTPostAPIWebhookWithTokenJSONBody,
89
RESTPostAPIWebhookWithTokenWaitResult,
@@ -54,6 +55,29 @@ export default class WebhookManager {
5455
},
5556
});
5657
}
58+
private _removeStoredWebhook(
59+
channelId: Snowflake,
60+
guildId: Snowflake
61+
): Promise<unknown> {
62+
return this._instance.prisma.channel.upsert({
63+
where: { id: BigInt(channelId) },
64+
update: { webhookId: null, webhookToken: null },
65+
create: {
66+
id: BigInt(channelId),
67+
webhookId: null,
68+
webhookToken: null,
69+
guild: {
70+
connectOrCreate: {
71+
where: { id: BigInt(guildId) },
72+
73+
create: {
74+
id: BigInt(guildId),
75+
},
76+
},
77+
},
78+
},
79+
});
80+
}
5781

5882
private async _getWebhookFromDiscord(
5983
channelId: Snowflake,
@@ -120,6 +144,7 @@ export default class WebhookManager {
120144
}
121145
// If here this means that there are no webhooks that match the application id, and the token is null
122146
// Therefore we need to create a new webhook
147+
123148
return await this._createWebhook(channelId, guildId);
124149
}
125150
private async _createWebhook(
@@ -138,7 +163,10 @@ export default class WebhookManager {
138163
)) as RESTPostAPIChannelWebhookResult;
139164
} catch (error) {
140165
if (error instanceof DiscordAPIError) {
141-
if (error.code === 403 || error.code === 50013) {
166+
if (
167+
error.status === 403 ||
168+
error.code === RESTJSONErrorCodes.MissingPermissions
169+
) {
142170
throw new UnexpectedFailure(
143171
InteractionOrRequestFinalStatus.BOT_MISSING_DISCORD_PERMISSION,
144172
"Missing the permission `MANAGE_WEBHOOKS` on that channel"
@@ -193,15 +221,28 @@ export default class WebhookManager {
193221
files?: RawFile[]
194222
): Promise<APIMessage> {
195223
const webhook = await this.getWebhook(channelId, guildId);
196-
197-
const message = (await this._instance.restClient.post(
198-
Routes.webhook(webhook.id, webhook.token),
199-
{
200-
body: data,
201-
files,
202-
query: new URLSearchParams({ wait: "true" }),
224+
try {
225+
const message = (await this._instance.restClient.post(
226+
Routes.webhook(webhook.id, webhook.token),
227+
{
228+
body: data,
229+
files,
230+
query: new URLSearchParams({ wait: "true" }),
231+
}
232+
)) as RESTPostAPIWebhookWithTokenWaitResult;
233+
return message;
234+
} catch (error) {
235+
if (
236+
error instanceof DiscordAPIError &&
237+
error.code === RESTJSONErrorCodes.UnknownWebhook
238+
) {
239+
// If the webhook is not found, it means that it has been deleted, so we need to recreate it
240+
// First delete the webhook so it is not attempted to be used again, incase creating the webhook fails
241+
await this._removeStoredWebhook(channelId, guildId);
242+
await this._createWebhook(channelId, guildId);
243+
return await this.sendWebhookMessage(channelId, guildId, data, files);
203244
}
204-
)) as RESTPostAPIWebhookWithTokenWaitResult;
205-
return message;
245+
throw error;
246+
}
206247
}
207248
}

0 commit comments

Comments
 (0)