Skip to content

Commit 1217247

Browse files
authored
Merge pull request #174 from GalvinPython/feat/v2-cleanup
2 parents 29c70c5 + 1d160cf commit 1217247

File tree

10 files changed

+460
-143
lines changed

10 files changed

+460
-143
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ CONFIG_DISCORD_COMPONENTS_PAGE_SIZE='10' # The number of channels to display per
2929
# Postgres URLs
3030
POSTGRES_URL='postgresql://user:password@server:port/database'
3131
POSTGRES_DEV_URL='postgresql://user:password@server:port/database'
32+
POSTGRES_STAGING_URL='postgresql://user:password@server:port/database'

drizzle.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { defineConfig } from "drizzle-kit";
33
const databaseUrl =
44
process.env.NODE_ENV === "development"
55
? process.env.POSTGRES_DEV_URL
6-
: process.env.POSTGRES_URL;
6+
: process.env.NODE_ENV === "staging"
7+
? process.env.POSTGRES_STAGING_URL
8+
: process.env.POSTGRES_URL;
79

810
console.log("Using database URL:", databaseUrl);
911

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
"scripts": {
2525
"db:generate": "bun drizzle-kit generate",
2626
"db:push:dev": "cross-env NODE_ENV=development bun drizzle-kit push",
27+
"db:push:staging": "cross-env NODE_ENV=staging bun drizzle-kit push",
2728
"db:push:prod": "bun drizzle-kit push",
29+
"db:migrate:staging": "bun src/db/migratedb.ts --staging",
30+
"db:migrate:prod": "bun src/db/migratedb.ts",
31+
"db:staging:reset": "bun src/db/resetStagingDatabase.ts --staging",
2832
"dev": "concurrently --names \"WEB,API,BOT\" --prefix-colors \"blue,green,magenta\" \"cd web && bun dev\" \"cd api && cargo watch -x \\\"run -- --dev\\\"\" \"bun --watch . --dev\"",
2933
"dev:bot": "bun --watch . --dev",
3034
"test:jetstream": "bun --watch src/utils/bluesky/jetstream.ts",

src/commands.ts

Lines changed: 177 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
GuildMember,
1616
InteractionContextType,
1717
MessageFlags,
18+
TextChannel,
1819
type ApplicationCommandOptionData,
1920
type CacheType,
2021
type CommandInteraction,
@@ -37,6 +38,9 @@ import {
3738
discordCheckIfDmChannelExists,
3839
discordGetAllTrackedInGuild,
3940
discordRemoveGuildTrackingChannel,
41+
discordUpdateSubscriptionAddChannel,
42+
discordUpdateSubscriptionCheckGuild,
43+
discordUpdateSubscriptionRemoveChannel,
4044
} from "./db/discord";
4145
import {
4246
Platform,
@@ -69,7 +73,7 @@ interface Command {
6973
}
7074

7175
// Context 2: Interaction can be used within Group DMs and DMs other than the app's bot user
72-
// /track, /tracked and /untracked can't be used in these contexts
76+
// /track, /tracked, /untracked and /updates can't be used in these contexts
7377
const commands: Record<string, Command> = {
7478
ping: {
7579
data: {
@@ -708,7 +712,7 @@ const commands: Record<string, Command> = {
708712
) {
709713
await interaction.reply({
710714
flags: MessageFlags.Ephemeral,
711-
content: `Started tracking the streamer ${platformUserId} (${platformUserId}) in <#${targetChannel?.id}>!`,
715+
content: `Started tracking the streamer ${streamerName} in <#${targetChannel?.id}>!`,
712716
});
713717
} else {
714718
await interaction.reply({
@@ -1174,6 +1178,177 @@ const commands: Record<string, Command> = {
11741178
});
11751179
},
11761180
},
1181+
updates: {
1182+
data: {
1183+
name: "updates",
1184+
description: "Enable or disable updates for Feedr in this channel",
1185+
integration_types: [0, 1],
1186+
contexts: [0, 1],
1187+
options: [
1188+
{
1189+
name: "state",
1190+
description: "Choose whether to enable or disable updates",
1191+
type: ApplicationCommandOptionType.String,
1192+
required: true,
1193+
choices: [
1194+
{
1195+
name: "Enable",
1196+
value: "enable",
1197+
},
1198+
{
1199+
name: "Disable",
1200+
value: "disable",
1201+
},
1202+
],
1203+
},
1204+
],
1205+
},
1206+
execute: async (interaction: CommandInteraction) => {
1207+
const isDm = !interaction.inGuild();
1208+
1209+
const channelId = interaction.channelId;
1210+
const guildId = isDm ? channelId : interaction.guildId;
1211+
1212+
if (!isDm && !guildId) {
1213+
await interaction.reply({
1214+
flags: MessageFlags.Ephemeral,
1215+
content: "An error occurred! Please report",
1216+
});
1217+
1218+
return;
1219+
}
1220+
1221+
// Check type of the channel
1222+
const targetChannel = await client.channels.fetch(channelId);
1223+
1224+
if (
1225+
targetChannel &&
1226+
(targetChannel.type === ChannelType.GuildText ||
1227+
targetChannel.type === ChannelType.GuildAnnouncement)
1228+
) {
1229+
if (
1230+
!isDm &&
1231+
!interaction.memberPermissions?.has(
1232+
PermissionFlagsBits.ManageChannels,
1233+
)
1234+
) {
1235+
// Check the permissions of the user
1236+
await interaction.reply({
1237+
flags: MessageFlags.Ephemeral,
1238+
content:
1239+
"You do not have the permission to manage channels!",
1240+
});
1241+
1242+
return;
1243+
}
1244+
}
1245+
1246+
// Check the permissions of the bot in the channel
1247+
const botMember = isDm
1248+
? null
1249+
: await interaction.guild?.members.fetchMe();
1250+
1251+
if (
1252+
botMember &&
1253+
!botMember
1254+
.permissionsIn(channelId)
1255+
.has(PermissionFlagsBits.SendMessages)
1256+
) {
1257+
await interaction.reply({
1258+
flags: MessageFlags.Ephemeral,
1259+
content:
1260+
"I do not have permission to send messages in that channel!",
1261+
});
1262+
1263+
return;
1264+
}
1265+
1266+
// Get the current state from the database
1267+
const currentDatabaseState =
1268+
await discordUpdateSubscriptionCheckGuild(guildId);
1269+
1270+
if (!currentDatabaseState || !currentDatabaseState.success) {
1271+
await interaction.reply({
1272+
flags: MessageFlags.Ephemeral,
1273+
content:
1274+
"An error occurred while trying to get the current update state from the database! Please report this error!",
1275+
});
1276+
1277+
return;
1278+
}
1279+
1280+
const currentState = Boolean(
1281+
currentDatabaseState.data[0].feedrUpdatesChannelId,
1282+
);
1283+
const desiredState = Boolean(
1284+
interaction.options.get("state")?.value === "enable",
1285+
);
1286+
1287+
if (currentState === desiredState) {
1288+
await interaction.reply({
1289+
flags: MessageFlags.Ephemeral,
1290+
content: `Updates are already ${
1291+
desiredState ? "enabled" : "disabled"
1292+
} in this channel!`,
1293+
});
1294+
1295+
return;
1296+
}
1297+
1298+
if (desiredState) {
1299+
// Enable updates
1300+
const updateSuccess = await discordUpdateSubscriptionAddChannel(
1301+
guildId,
1302+
channelId,
1303+
);
1304+
1305+
if (!updateSuccess || !updateSuccess.success) {
1306+
await interaction.reply({
1307+
flags: MessageFlags.Ephemeral,
1308+
content:
1309+
"An error occurred while trying to enable updates in this channel! Please report this error!",
1310+
});
1311+
1312+
return;
1313+
}
1314+
1315+
await client.channels
1316+
.fetch(channelId)
1317+
.then(async (channel) => {
1318+
if (channel?.isTextBased()) {
1319+
await (channel as TextChannel).send({
1320+
content: `Updates have been successfully enabled in this channel!`,
1321+
});
1322+
}
1323+
})
1324+
.catch(console.error);
1325+
1326+
await interaction.reply({
1327+
flags: MessageFlags.Ephemeral,
1328+
content:
1329+
"If a test message was sent, updates are enabled! If not, please report this as an error!",
1330+
});
1331+
} else {
1332+
// Disable updates
1333+
const updateSuccess =
1334+
await discordUpdateSubscriptionRemoveChannel(guildId);
1335+
1336+
if (!updateSuccess || !updateSuccess.success) {
1337+
await interaction.reply({
1338+
flags: MessageFlags.Ephemeral,
1339+
content:
1340+
"An error occurred while trying to disable updates in this channel! Please report this error!",
1341+
});
1342+
1343+
return;
1344+
}
1345+
1346+
await interaction.reply({
1347+
content: `Successfully disabled updates in <#${channelId}>!`,
1348+
});
1349+
}
1350+
},
1351+
},
11771352
};
11781353

11791354
// Convert commands to a Map

src/config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
// FILL IN THIS INFORMATION IN .ENV
22
export const runningInDevMode: boolean = process.argv.includes("--dev");
3+
4+
// Staging mode is for only testing the production database before breaking the actual production bot
5+
// Run `bun db:migrate:staging` to migrate the staging database and check for any mistakes before running `bun db:migrate:prod`
6+
// Do NOT use this mode for regular testing, use --dev for that
7+
export const runningInStagingMode: boolean = process.argv.includes("--staging");
8+
9+
if (runningInDevMode && runningInStagingMode) {
10+
throw new Error("Cannot run in both dev and staging mode!");
11+
}
12+
313
export interface Config {
414
youtubeInnertubeProxyUrl: string | null;
515
updateIntervalYouTube: number;
@@ -20,7 +30,9 @@ export const config: Config = {
2030
: 60_000,
2131
databaseUrl: runningInDevMode
2232
? process.env?.POSTGRES_DEV_URL
23-
: process.env?.POSTGRES_URL,
33+
: runningInStagingMode
34+
? process.env?.POSTGRES_STAGING_URL
35+
: process.env?.POSTGRES_URL,
2436
discordWaitForGuildCacheTime: process.env
2537
?.CONFIG_DISCORD_WAIT_FOR_GUILD_CACHE_TIME
2638
? parseInt(process.env?.CONFIG_DISCORD_WAIT_FOR_GUILD_CACHE_TIME) * 1000

src/db/discord.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,64 @@ export async function discordCheckIfDmChannelExists(
401401
return { success: false, data: [] };
402402
}
403403
}
404+
405+
// Bot updates
406+
export async function discordUpdateSubscriptionAddChannel(
407+
guildId: string,
408+
channelId: string,
409+
): Promise<{ success: boolean; data: [] }> {
410+
console.log(
411+
`Updating guild ${guildId} to set subscription channel to ${channelId}`,
412+
);
413+
414+
try {
415+
await db
416+
.update(dbDiscordTable)
417+
.set({ feedrUpdatesChannelId: channelId })
418+
.where(eq(dbDiscordTable.guildId, guildId));
419+
420+
return { success: true, data: [] };
421+
} catch (error) {
422+
console.error("Error updating subscription channel:", error);
423+
424+
return { success: false, data: [] };
425+
}
426+
}
427+
428+
export async function discordUpdateSubscriptionRemoveChannel(
429+
guildId: string,
430+
): Promise<{ success: boolean; data: [] }> {
431+
console.log(`Removing subscription channel for guild ${guildId}`);
432+
433+
try {
434+
await db
435+
.update(dbDiscordTable)
436+
.set({ feedrUpdatesChannelId: null })
437+
.where(eq(dbDiscordTable.guildId, guildId));
438+
439+
return { success: true, data: [] };
440+
} catch (error) {
441+
console.error("Error removing subscription channel:", error);
442+
443+
return { success: false, data: [] };
444+
}
445+
}
446+
447+
export async function discordUpdateSubscriptionCheckGuild(
448+
guildId: string,
449+
): Promise<{ success: boolean; data: (typeof dbDiscordTable.$inferSelect)[] }> {
450+
console.log(`Checking subscription settings for guild ${guildId}`);
451+
452+
try {
453+
const result = await db
454+
.select()
455+
.from(dbDiscordTable)
456+
.where(eq(dbDiscordTable.guildId, guildId));
457+
458+
return { success: true, data: result };
459+
} catch (error) {
460+
console.error("Error checking subscription settings for guild:", error);
461+
462+
return { success: false, data: [] };
463+
}
464+
}

0 commit comments

Comments
 (0)