Skip to content

Commit 94c227d

Browse files
committed
feat(bot): fully implement dm tracking
1 parent 3836aca commit 94c227d

File tree

9 files changed

+101
-59
lines changed

9 files changed

+101
-59
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
# Moving back to npm for package management rather than bun
2-
# because wow dependabot is so broken for it 😭
31
# TODO: Labels on package updates after project restructure
42

53
version: 2
64
updates:
7-
- package-ecosystem: "npm"
5+
- package-ecosystem: "bun"
86
directory: "/"
97
schedule:
108
interval: "weekly"
119
target-branch: "dev"
1210

13-
- package-ecosystem: "npm"
11+
- package-ecosystem: "bun"
1412
directory: "/web"
1513
schedule:
1614
interval: "weekly"

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ target/
1111
*.sql
1212
dbtests.ts
1313
perftesting.ts
14-
drizzle/
14+
drizzle/
15+
fetchTest.ts

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,26 @@ These guidelines ensure predictable behavior and simplify error handling across
9696
> [!NOTE]
9797
> WIP update!
9898
99-
### Bot
99+
### Fixes
100100

101101
- Fixed the double notification bug
102+
103+
### Changes
104+
102105
- Moved to Postgres as our database engine
106+
107+
### Features
108+
103109
- Improved flow of `/track` command
104110
- Autocomplete for YouTube
105111
- Filter by videos, shorts and streams for YouTube!
112+
- `/tracked` is now improved and is an interactive embed!
113+
- Can now use search/autocomplete for `/track` for both YouTube and Twitch
106114

107-
### API
115+
### Known Issues
108116

109-
### Site
117+
- Twitch channel username doesn't show up in `/track`
118+
- Unable to subscribe to updates via the bot
110119

111120
## V1
112121

@@ -115,7 +124,7 @@ These guidelines ensure predictable behavior and simplify error handling across
115124
- Added a new command! `/tracked` ([#50](https://github.com/GalvinPython/feedr/issues/50))
116125
- See all the tracked channels in your server
117126
- The channel you ran the command in will appear first as there is no option to only see the current channel for now
118-
- Locale improvments ([#43](https://github.com/GalvinPython/feedr/issues/43))
127+
- Locale improvements ([#43](https://github.com/GalvinPython/feedr/issues/43))
119128

120129
### 1.3.0
121130

bun.lock

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"pg": "^8.15.6",
1212
},
1313
"devDependencies": {
14-
"@types/bun": "1.2.10",
14+
"@types/bun": "1.2.21",
1515
"@types/pg": "^8.11.14",
1616
"@typescript-eslint/eslint-plugin": "8.11.0",
1717
"@typescript-eslint/parser": "8.11.0",
@@ -131,7 +131,7 @@
131131

132132
"@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="],
133133

134-
"@types/bun": ["@types/bun@1.2.10", "", { "dependencies": { "bun-types": "1.2.10" } }, "sha512-eilv6WFM3M0c9ztJt7/g80BDusK98z/FrFwseZgT4bXCq2vPhXD4z8R3oddmAn+R/Nmz9vBn4kweJKmGTZj+lg=="],
134+
"@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
135135

136136
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
137137

@@ -141,6 +141,8 @@
141141

142142
"@types/pg": ["@types/pg@8.11.14", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, "sha512-qyD11E5R3u0eJmd1lB0WnWKXJGA7s015nyARWljfz5DcX83TKAIlY+QrmvzQTsbIe+hkiFtkyL2gHC6qwF6Fbg=="],
143143

144+
"@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
145+
144146
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
145147

146148
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.11.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.11.0", "@typescript-eslint/type-utils": "8.11.0", "@typescript-eslint/utils": "8.11.0", "@typescript-eslint/visitor-keys": "8.11.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA=="],
@@ -199,7 +201,7 @@
199201

200202
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
201203

202-
"bun-types": ["bun-types@1.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-b5ITZMnVdf3m1gMvJHG+gIfeJHiQPJak0f7925Hxu6ZN5VKA8AGy4GZ4lM+Xkn6jtWxg5S3ldWvfmXdvnkp3GQ=="],
204+
"bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
203205

204206
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
205207

@@ -227,6 +229,8 @@
227229

228230
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
229231

232+
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
233+
230234
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
231235

232236
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "module",
55
"version": "2.0.0-dev",
66
"devDependencies": {
7-
"@types/bun": "1.2.10",
7+
"@types/bun": "1.2.21",
88
"@types/pg": "^8.11.14",
99
"@typescript-eslint/eslint-plugin": "8.11.0",
1010
"@typescript-eslint/parser": "8.11.0",

src/commands.ts

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import search from "./utils/youtube/search";
3333
import {
3434
checkIfGuildIsTrackingUserAlready,
3535
discordAddGuildTrackingUser,
36+
discordAddNewGuild,
37+
discordCheckIfDmChannelExists,
3638
discordGetAllTrackedInGuild,
3739
discordRemoveGuildTrackingChannel,
3840
} from "./db/discord";
@@ -273,6 +275,8 @@ const commands: Record<string, Command> = {
273275
contexts: [0, 1],
274276
},
275277
execute: async (interaction: CommandInteraction) => {
278+
const isDm = !interaction.inGuild();
279+
276280
// Get the YouTube Channel ID
277281
const targetPlatform = (
278282
interaction as ChatInputCommandInteraction
@@ -284,7 +288,7 @@ const commands: Record<string, Command> = {
284288
const discordChannelId =
285289
(interaction.options.get("updates_channel")?.value as string) ??
286290
interaction.channelId;
287-
const guildId = interaction.guildId;
291+
const guildId = isDm ? discordChannelId : interaction.guildId;
288292

289293
// Log the autocomplete value
290294
console.log(`Autocomplete value: ${platformUserId}`);
@@ -316,33 +320,26 @@ const commands: Record<string, Command> = {
316320
return;
317321
}
318322

319-
// TODO: Enable DMs :)
320-
const isDm = interaction.channel?.isDMBased();
323+
console.log(interaction.channelId);
321324

322-
if (!guildId || isDm || isDm === undefined) {
323-
await interaction.reply({
324-
flags: MessageFlags.Ephemeral,
325-
content:
326-
"This command is not supported in DMs currently!\nNot a DM? Then the bot failed to get the guild info",
327-
});
328-
329-
return;
330-
}
325+
if (isDm) console.log("DM");
331326

332327
// TODO: Embed
333328
// Check the permissions of the user
334-
if (
335-
!interaction.memberPermissions?.has(
336-
PermissionFlagsBits.ManageChannels,
337-
)
338-
) {
339-
await interaction.reply({
340-
flags: MessageFlags.Ephemeral,
341-
content:
342-
"You do not have the permission to manage channels!",
343-
});
329+
if (!isDm) {
330+
if (
331+
!interaction.memberPermissions?.has(
332+
PermissionFlagsBits.ManageChannels,
333+
)
334+
) {
335+
await interaction.reply({
336+
flags: MessageFlags.Ephemeral,
337+
content:
338+
"You do not have the permission to manage channels!",
339+
});
344340

345-
return;
341+
return;
342+
}
346343
}
347344

348345
// TODO: Embed
@@ -397,15 +394,30 @@ const commands: Record<string, Command> = {
397394

398395
return;
399396
}
397+
} else if (isDm) {
398+
// DM channels don't need permission checks
400399
} else {
401400
await interaction.reply({
402401
flags: MessageFlags.Ephemeral,
403-
content: "The target channel is not a text channel!",
402+
content: `The target channel is not a text channel! ${isDm}`,
404403
});
405404

406405
return;
407406
}
408407

408+
// Before attempting to add the subscription, if it's a DM, check if it's already in the database. If not add it
409+
if (isDm) {
410+
console.log("CHECKING DM");
411+
const data = (
412+
await discordCheckIfDmChannelExists(discordChannelId)
413+
).data;
414+
415+
if (!data) {
416+
console.log("ADDING DM");
417+
await discordAddNewGuild(discordChannelId, true);
418+
}
419+
}
420+
409421
switch (targetPlatform) {
410422
case "youtube": {
411423
const contentType = interaction.options.get("content_type")
@@ -571,7 +583,7 @@ const commands: Record<string, Command> = {
571583

572584
await interaction.reply({
573585
flags: MessageFlags.Ephemeral,
574-
content: `Started tracking the channel ${youtubeChannelInfo?.channelName ?? platformUserId} in ${targetChannel.name}!`,
586+
content: `Started tracking the channel ${youtubeChannelInfo?.channelName ?? platformUserId} in <#${targetChannel?.id}>!`,
575587
});
576588
} else {
577589
await interaction.reply({
@@ -698,7 +710,7 @@ const commands: Record<string, Command> = {
698710
) {
699711
await interaction.reply({
700712
flags: MessageFlags.Ephemeral,
701-
content: `Started tracking the streamer ${platformUserId} (${platformUserId}) in ${targetChannel.name}!`,
713+
content: `Started tracking the streamer ${platformUserId} (${platformUserId}) in <#${targetChannel?.id}>!`,
702714
});
703715
} else {
704716
await interaction.reply({
@@ -819,24 +831,15 @@ const commands: Record<string, Command> = {
819831
contexts: [0, 1],
820832
},
821833
execute: async (interaction: CommandInteraction) => {
834+
const isDm = !interaction.inGuild();
835+
822836
// Get the YouTube Channel ID
823837
const platformUserId = interaction.options.get("user_id")
824838
?.value as string;
825-
const guildId = interaction.guildId;
826-
827-
// DMs are currently not supported, so throw back an error
828-
if (!guildId || interaction.channel?.isDMBased()) {
829-
await interaction.reply({
830-
flags: MessageFlags.Ephemeral,
831-
content:
832-
"This command is not supported in DMs currently!\nNot a DM? Then an error has occurred :(",
833-
});
834-
835-
return;
836-
}
837839

838840
// Check the permissions of the user
839841
if (
842+
!isDm &&
840843
!interaction.memberPermissions?.has(
841844
PermissionFlagsBits.ManageChannels,
842845
)
@@ -869,7 +872,7 @@ const commands: Record<string, Command> = {
869872
},
870873
autoComplete: async (interaction: AutocompleteInteraction) => {
871874
const trackedChannels = await discordGetAllTrackedInGuild(
872-
interaction.guildId as string,
875+
interaction.guildId ?? (interaction.channelId as string),
873876
);
874877

875878
console.dir(
@@ -916,14 +919,16 @@ const commands: Record<string, Command> = {
916919
contexts: [0, 1],
917920
},
918921
execute: async (interaction: CommandInteraction) => {
919-
const guildId = interaction.guildId;
920-
const channelId = interaction.channelId;
922+
let guildId = interaction.guildId;
923+
924+
const isDm = !interaction.inGuild();
921925

922-
if (!guildId || !channelId) {
926+
if (isDm) guildId = interaction.channelId;
927+
928+
if (!guildId) {
923929
await interaction.reply({
924930
flags: MessageFlags.Ephemeral,
925-
content:
926-
"You are likely in a DM, this command is not supported in DMs!",
931+
content: "An error occurred! Please report",
927932
});
928933

929934
return;

src/db/discord.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,28 @@ export async function discordRemoveGuildFromTracking(
376376
return { success: false, data: [] };
377377
}
378378
}
379+
380+
// Check if the DM channel is already in the "guilds" table
381+
export async function discordCheckIfDmChannelExists(
382+
channelId: string,
383+
): Promise<{ success: boolean; data: (typeof dbDiscordTable.$inferSelect)[] }> {
384+
console.log(`Checking if DM channel exists: ${channelId}`);
385+
386+
try {
387+
const result = await db
388+
.select()
389+
.from(dbDiscordTable)
390+
.where(
391+
and(
392+
eq(dbDiscordTable.guildId, channelId),
393+
eq(dbDiscordTable.isDm, true),
394+
),
395+
);
396+
397+
return { success: true, data: result };
398+
} catch (error) {
399+
console.error("Error checking if DM channel exists:", error);
400+
401+
return { success: false, data: [] };
402+
}
403+
}

src/utils/discord/updateGuildsOnStartup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default async function () {
3131

3232
// Find any guilds that are in the database but not in the current guilds
3333
const missingGuilds = data.filter(
34-
(guild) => !currentGuilds.includes(guild.guildId),
34+
(guild) => !currentGuilds.includes(guild.guildId) && !guild.isDm,
3535
);
3636

3737
// Find any guilds that are in the current guilds but not in the database

src/utils/youtube/search.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { config } from "../../config";
21
import type { InnertubeSearchRequest } from "../../types/youtube";
32

3+
import { config } from "../../config";
44
import formatLargeNumber from "../formatLargeNumber";
55

66
export default async function (query: string) {

0 commit comments

Comments
 (0)