Skip to content

Commit eebea59

Browse files
committed
feat: track youtube flow is complete 🎉 (closes #81)
1 parent 6f6168b commit eebea59

File tree

7 files changed

+280
-58
lines changed

7 files changed

+280
-58
lines changed

src/commands.ts

Lines changed: 147 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Bun from "bun";
22
import { heapStats } from "bun:jsc";
33
import {
4+
ApplicationCommandOptionType,
45
ApplicationCommandType,
56
AutocompleteInteraction,
67
ChannelType,
@@ -15,7 +16,6 @@ import { PermissionFlagsBits } from "discord-api-types/v8";
1516

1617
import checkIfChannelIdIsValid from "./utils/youtube/checkIfChannelIdIsValid";
1718
import {
18-
addNewGuildToTrackChannel,
1919
getAllTrackedInGuild,
2020
stopGuildTrackingChannel,
2121
twitchAddNewChannelToTrack,
@@ -31,8 +31,11 @@ import {
3131
addNewChannelToTrack,
3232
} from "./utils/db/youtube";
3333
import search from "./utils/youtube/search";
34-
import { checkIfGuildIsTrackingUserAlready } from "./utils/db/discord";
35-
import { Platform } from "./types/types.d";
34+
import {
35+
checkIfGuildIsTrackingUserAlready,
36+
discordAddGuildTrackingUser,
37+
} from "./utils/db/discord";
38+
import { Platform, YouTubeContentType } from "./types/types.d";
3639

3740
import client from ".";
3841

@@ -165,41 +168,73 @@ const commands: Record<string, Command> = {
165168
data: {
166169
options: [
167170
{
168-
name: "platform",
169-
description: "Select a supported platform to track",
170-
type: 3,
171-
required: true,
172-
choices: [
171+
type: ApplicationCommandOptionType.Subcommand,
172+
name: "youtube",
173+
description: "Track a YouTube channel",
174+
options: [
173175
{
174-
name: "Twitch",
175-
value: "twitch",
176+
type: ApplicationCommandOptionType.String,
177+
name: "channel_id",
178+
description: "Enter the YouTube channel ID",
179+
required: true,
180+
autocomplete: true,
176181
},
177182
{
178-
name: "YouTube",
179-
value: "youtube",
183+
type: ApplicationCommandOptionType.Integer,
184+
name: "content_type",
185+
description: "Select what content to track",
186+
required: true,
187+
choices: [
188+
{ name: "Videos Only", value: 1 },
189+
{ name: "Shorts Only", value: 2 },
190+
{ name: "Streams Only", value: 4 },
191+
{ name: "Videos & Shorts", value: 3 },
192+
{ name: "Videos & Streams", value: 5 },
193+
{ name: "Shorts & Streams", value: 6 },
194+
{ name: "Videos, Shorts, Streams", value: 7 },
195+
],
196+
},
197+
{
198+
type: ApplicationCommandOptionType.Channel,
199+
name: "updates_channel",
200+
description:
201+
"Channel to receive updates. If not specified, the current channel will be used.",
202+
required: false,
203+
},
204+
{
205+
type: ApplicationCommandOptionType.Role,
206+
name: "role",
207+
description: "Role to mention (optional)",
208+
required: false,
180209
},
181210
],
182211
},
183212
{
184-
name: "user_id",
185-
description:
186-
"Enter the YouTube channel ID or Twitch Streamer to track",
187-
type: 3,
188-
required: true,
189-
autocomplete: true,
190-
},
191-
{
192-
name: "updates_channel",
193-
description:
194-
"Enter the Guild channel to receive updates in.",
195-
type: 7,
196-
required: false,
197-
},
198-
{
199-
name: "role",
200-
description: "Enter the role to mention (optional)",
201-
type: 8,
202-
required: false,
213+
type: 1,
214+
name: "twitch",
215+
description: "Track a Twitch streamer",
216+
options: [
217+
{
218+
type: ApplicationCommandOptionType.String,
219+
name: "streamer_id",
220+
description: "Enter the Twitch streamer username",
221+
required: true,
222+
autocomplete: true,
223+
},
224+
{
225+
type: ApplicationCommandOptionType.Channel,
226+
name: "updates_channel",
227+
description:
228+
"Channel to receive updates. If not specified, the current channel will be used.",
229+
required: false,
230+
},
231+
{
232+
type: ApplicationCommandOptionType.Role,
233+
name: "role",
234+
description: "Role to mention (optional)",
235+
required: false,
236+
},
237+
],
203238
},
204239
],
205240
name: "track",
@@ -210,10 +245,13 @@ const commands: Record<string, Command> = {
210245
},
211246
execute: async (interaction: CommandInteraction) => {
212247
// Get the YouTube Channel ID
213-
const targetPlatform = interaction.options.get("platform")
214-
?.value as string;
215-
const platformUserId = interaction.options.get("user_id")
216-
?.value as string;
248+
const targetPlatform = (
249+
interaction as ChatInputCommandInteraction
250+
).options.getSubcommand();
251+
const platformUserId =
252+
targetPlatform === "youtube"
253+
? (interaction.options.get("channel_id")?.value as string)
254+
: (interaction.options.get("streamer_id")?.value as string);
217255
const discordChannelId =
218256
(interaction.options.get("updates_channel")?.value as string) ??
219257
interaction.channelId;
@@ -250,7 +288,9 @@ const commands: Record<string, Command> = {
250288
}
251289

252290
// TODO: Enable DMs :)
253-
if (!guildId || interaction.channel?.isDMBased()) {
291+
const isDm = interaction.channel?.isDMBased();
292+
293+
if (!guildId || isDm || isDm === undefined) {
254294
await interaction.reply({
255295
flags: MessageFlags.Ephemeral,
256296
content:
@@ -339,6 +379,18 @@ const commands: Record<string, Command> = {
339379

340380
switch (targetPlatform) {
341381
case "youtube": {
382+
const contentType = interaction.options.get("content_type")
383+
?.value as number;
384+
385+
if (!contentType) {
386+
await interaction.reply({
387+
flags: MessageFlags.Ephemeral,
388+
content: "Please specify a valid content type!",
389+
});
390+
391+
return;
392+
}
393+
342394
// Check that the channel ID is in a valid format
343395
if (
344396
platformUserId.length != 24 ||
@@ -363,14 +415,40 @@ const commands: Record<string, Command> = {
363415
return;
364416
}
365417

418+
// Check content type
419+
const shouldTrackVideos =
420+
(contentType & YouTubeContentType.Videos) !== 0;
421+
const shouldTrackShorts =
422+
(contentType & YouTubeContentType.Shorts) !== 0;
423+
const shouldTrackStreams =
424+
(contentType & YouTubeContentType.Streams) !== 0;
425+
426+
console.log(`Tracking Videos: ${shouldTrackVideos}`);
427+
console.log(`Tracking Shorts: ${shouldTrackShorts}`);
428+
console.log(`Tracking Streams: ${shouldTrackStreams}`);
429+
430+
// Optional: prevent empty tracking (e.g., bitmask = 0)
431+
if (
432+
!shouldTrackVideos &&
433+
!shouldTrackShorts &&
434+
!shouldTrackStreams
435+
) {
436+
await interaction.reply({
437+
flags: MessageFlags.Ephemeral,
438+
content: `You must select at least one type of content to track.`,
439+
});
440+
441+
return;
442+
}
443+
444+
// Check if the channel is already being tracked in the guild
366445
const trackedChannels =
367446
await checkIfGuildIsTrackingUserAlready(
368447
Platform.YouTube,
369448
platformUserId,
370449
guildId,
371450
);
372451

373-
// Check if the channel is already being tracked in the guild
374452
console.log(trackedChannels);
375453
if (!trackedChannels || !trackedChannels.success) {
376454
// TODO: Embed
@@ -394,14 +472,35 @@ const commands: Record<string, Command> = {
394472
}
395473

396474
// Check if the channel is already being tracked globally
397-
if (
398-
!(await checkIfChannelIsAlreadyTracked(platformUserId))
475+
const isChannelTracked =
476+
await checkIfChannelIsAlreadyTracked(platformUserId);
477+
478+
console.log(
479+
`Is channel ${platformUserId} tracked globally?`,
480+
isChannelTracked,
481+
);
482+
483+
if (!isChannelTracked.success) {
484+
await interaction.reply({
485+
flags: MessageFlags.Ephemeral,
486+
content:
487+
"An error occurred while trying to check if the channel is already being tracked globally! Please report this error!",
488+
});
489+
} else if (
490+
isChannelTracked.success &&
491+
isChannelTracked.data.length == 0
399492
) {
400-
if (!(await addNewChannelToTrack(platformUserId))) {
493+
console.log(
494+
`Channel ${platformUserId} is not tracked globally, adding it now...`,
495+
);
496+
const channelAdded =
497+
await addNewChannelToTrack(platformUserId);
498+
499+
if (!channelAdded.success) {
401500
await interaction.reply({
402501
flags: MessageFlags.Ephemeral,
403502
content:
404-
"An error occurred while trying to add the channel to track! This is a new channel being tracked globally, please report this error!",
503+
"An error occurred while trying to add the channel to track to the main YouTube database. Please report this issue!",
405504
});
406505

407506
return;
@@ -410,12 +509,17 @@ const commands: Record<string, Command> = {
410509

411510
// Add the guild to the database
412511
if (
413-
await addNewGuildToTrackChannel(
512+
await discordAddGuildTrackingUser(
414513
guildId,
514+
Platform.YouTube,
415515
platformUserId,
416516
discordChannelId,
417517
(interaction.options.get("role")
418518
?.value as string) ?? null,
519+
isDm,
520+
shouldTrackVideos,
521+
shouldTrackShorts,
522+
shouldTrackStreams,
419523
)
420524
) {
421525
const youtubeChannelInfo =
@@ -521,7 +625,7 @@ const commands: Record<string, Command> = {
521625
autoComplete: async (interaction: AutocompleteInteraction) => {
522626
try {
523627
const platform = interaction.options.get("platform")?.value;
524-
const query = interaction.options.get("user_id")?.value;
628+
const query = interaction.options.get("channel_id")?.value;
525629

526630
// If the query is empty or not a string, return an empty array
527631
if (!query || typeof query !== "string") {

src/events/ready.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ import fetchLatestUploads from "../utils/youtube/fetchLatestUploads";
1212
client.once(Events.ClientReady, async (bot) => {
1313
console.log(`Ready! Logged in as ${bot.user?.tag}`);
1414

15-
await cronUpdateBotInfo();
16-
new CronJob("0 * * * * *", async () => {
17-
await cronUpdateBotInfo();
18-
}).start();
15+
// TODO: Reimplement this when the bot is ready
16+
// await cronUpdateBotInfo();
17+
// new CronJob("0 * * * * *", async () => {
18+
// await cronUpdateBotInfo();
19+
// }).start();
1920

2021
fetchLatestUploads();
2122
setInterval(fetchLatestUploads, config.updateIntervalYouTube as number);
2223

2324
sendLatestUploads();
2425
setInterval(sendLatestUploads, config.updateIntervalYouTube as number);
2526

27+
// TODO: Twitch integration is not ready yet
2628
// One at a time
2729
// checkIfStreamersAreLive();
2830
// setInterval(checkIfStreamersAreLive, config.updateIntervalTwitch as number);

src/types/types.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,16 @@ export enum Platform {
44
YouTube = "youtube",
55
Twitch = "twitch",
66
}
7+
8+
export enum YouTubeContentType {
9+
Videos = 1 << 0,
10+
Shorts = 1 << 1,
11+
Streams = 1 << 2,
12+
}
13+
14+
// Helper for readability
15+
export const YouTubeContentTypeLabels = {
16+
[YouTubeContentType.Videos]: "Videos",
17+
[YouTubeContentType.Shorts]: "Shorts",
18+
[YouTubeContentType.Streams]: "Livestreams",
19+
};

0 commit comments

Comments
 (0)