11import Bun from "bun" ;
22import { heapStats } from "bun:jsc" ;
33import {
4+ ActionRowBuilder ,
45 ApplicationCommandOptionType ,
56 ApplicationCommandType ,
67 AutocompleteInteraction ,
8+ ButtonBuilder ,
9+ ButtonStyle ,
710 ChannelType ,
811 ChatInputCommandInteraction ,
12+ ComponentType ,
13+ EmbedBuilder ,
914 GuildMember ,
1015 MessageFlags ,
1116 type ApplicationCommandOptionData ,
@@ -29,13 +34,18 @@ import {
2934 discordGetAllTrackedInGuild ,
3035 discordRemoveGuildTrackingChannel ,
3136} from "./db/discord" ;
32- import { Platform , YouTubeContentType } from "./types/types.d" ;
37+ import {
38+ Platform ,
39+ YouTubeContentType ,
40+ type PlatformTypes ,
41+ } from "./types/types.d" ;
3342import searchTwitch from "./utils/twitch/searchTwitch" ;
3443import { getStreamerName } from "./utils/twitch/getStreamerName" ;
3544import {
3645 addNewStreamerToTrack ,
3746 checkIfStreamerIsAlreadyTracked ,
3847} from "./db/twitch" ;
48+ import { config } from "./config" ;
3949
4050import client from "." ;
4151
@@ -915,9 +925,25 @@ const commands: Record<string, Command> = {
915925 return ;
916926 }
917927
918- const trackedChannels = await getAllTrackedInGuild ( guildId ) ;
928+ const trackedChannels = await discordGetAllTrackedInGuild ( guildId ) ;
919929
920- if ( trackedChannels . length === 0 ) {
930+ if ( ! trackedChannels || ! trackedChannels . success ) {
931+ console . error (
932+ "An error occurred while trying to get the tracked channels in this guild!" ,
933+ ) ;
934+ await interaction . reply ( {
935+ flags : MessageFlags . Ephemeral ,
936+ content :
937+ "An error occurred while trying to get the tracked channels in this guild! Please report this error!" ,
938+ } ) ;
939+
940+ return ;
941+ }
942+
943+ if (
944+ trackedChannels . data . youtubeSubscriptions . length === 0 &&
945+ trackedChannels . data . twitchSubscriptions . length === 0
946+ ) {
921947 await interaction . reply ( {
922948 flags : MessageFlags . Ephemeral ,
923949 content : "No channels are being tracked in this guild." ,
@@ -926,24 +952,219 @@ const commands: Record<string, Command> = {
926952 return ;
927953 }
928954
929- const filteredChannels = trackedChannels . filter (
930- ( channel ) => channel . guild_channel_id === channelId ,
931- ) ;
955+ const youtubeChannels =
956+ trackedChannels . data . youtubeSubscriptions ?? [ ] ;
957+ const twitchChannels =
958+ trackedChannels . data . twitchSubscriptions ?? [ ] ;
959+
960+ const allEntries = [
961+ ...youtubeChannels . map ( ( c ) => ( {
962+ type : "YouTube" as const ,
963+ name : c . youtubeChannel . youtubeChannelName ,
964+ id : c . youtubeChannel . youtubeChannelId ,
965+ notifyId : c . subscription . notificationChannelId ,
966+ } ) ) ,
967+ ...twitchChannels . map ( ( c ) => ( {
968+ type : "Twitch" as const ,
969+ name : c . twitchChannel . twitchChannelName ,
970+ id : c . twitchChannel . twitchChannelName ,
971+ notifyId : c . subscription . notificationChannelId ,
972+ } ) ) ,
973+ ] . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
974+
975+ type FilterType = "all" | PlatformTypes ;
976+ let currentPage = 0 ;
977+ let currentFilter : FilterType = "all" ;
978+
979+ const pageSize = config . discordComponentsPageSize ;
980+
981+ const filterEntries = ( filter : FilterType ) => {
982+ if ( filter === Platform . YouTube )
983+ return allEntries . filter ( ( e ) => e . type === "YouTube" ) ;
984+ if ( filter === Platform . Twitch )
985+ return allEntries . filter ( ( e ) => e . type === "Twitch" ) ;
986+
987+ return allEntries ;
988+ } ;
989+
990+ const getEmbed = (
991+ entries : typeof allEntries ,
992+ page : number ,
993+ filter : FilterType ,
994+ ) => {
995+ const totalPages = Math . ceil ( entries . length / pageSize ) ;
996+ const pageEntries = entries . slice (
997+ page * pageSize ,
998+ ( page + 1 ) * pageSize ,
999+ ) ;
1000+
1001+ const description =
1002+ pageEntries
1003+ . map ( ( entry ) => {
1004+ const link =
1005+ entry . type === "YouTube"
1006+ ? `https://www.youtube.com/channel/${ entry . id } `
1007+ : `https://www.twitch.tv/${ entry . id } ` ;
1008+
1009+ return `**[${ entry . name } ](${ link } )** • ${ entry . type } • <#${ entry . notifyId } >` ;
1010+ } )
1011+ . join ( "\n" ) || "No entries." ;
1012+
1013+ return new EmbedBuilder ( )
1014+ . setTitle ( "Tracked Channels" )
1015+ . setDescription ( description )
1016+ . setColor ( 0x5865f2 )
1017+ . setFooter ( {
1018+ text : `Page ${ page + 1 } of ${ Math . max ( totalPages , 1 ) } — Filter: ${ filter . toUpperCase ( ) } ` ,
1019+ } ) ;
1020+ } ;
1021+
1022+ const getButtons = (
1023+ filter : FilterType ,
1024+ page : number ,
1025+ entriesLength : number ,
1026+ ) => {
1027+ const totalPages = Math . ceil ( entriesLength / pageSize ) ;
1028+
1029+ const toggleRow =
1030+ new ActionRowBuilder < ButtonBuilder > ( ) . addComponents (
1031+ new ButtonBuilder ( )
1032+ . setCustomId ( "filter_all" )
1033+ . setLabel ( "🌐 All" )
1034+ . setStyle (
1035+ filter === "all"
1036+ ? ButtonStyle . Primary
1037+ : ButtonStyle . Secondary ,
1038+ ) ,
1039+ new ButtonBuilder ( )
1040+ . setCustomId ( "filter_youtube" )
1041+ . setLabel ( "❤️ YouTube" )
1042+ . setStyle (
1043+ filter === "youtube"
1044+ ? ButtonStyle . Primary
1045+ : ButtonStyle . Secondary ,
1046+ ) ,
1047+ new ButtonBuilder ( )
1048+ . setCustomId ( "filter_twitch" )
1049+ . setLabel ( "💜 Twitch" )
1050+ . setStyle (
1051+ filter === "twitch"
1052+ ? ButtonStyle . Primary
1053+ : ButtonStyle . Secondary ,
1054+ ) ,
1055+ ) ;
1056+
1057+ const navRow =
1058+ new ActionRowBuilder < ButtonBuilder > ( ) . addComponents (
1059+ new ButtonBuilder ( )
1060+ . setCustomId ( "prev_page" )
1061+ . setLabel ( "⬅️ Previous" )
1062+ . setStyle ( ButtonStyle . Secondary )
1063+ . setDisabled ( page === 0 ) ,
1064+ new ButtonBuilder ( )
1065+ . setCustomId ( "next_page" )
1066+ . setLabel ( "Next ➡️" )
1067+ . setStyle ( ButtonStyle . Secondary )
1068+ . setDisabled (
1069+ page >= totalPages - 1 || totalPages === 0 ,
1070+ ) ,
1071+ ) ;
1072+
1073+ return [ toggleRow , navRow ] ;
1074+ } ;
9321075
933- const newTrackedChannels = trackedChannels . filter (
934- ( channel ) => channel . guild_channel_id !== channelId ,
1076+ const entries = filterEntries ( currentFilter ) ;
1077+ const embed = getEmbed ( entries , currentPage , currentFilter ) ;
1078+ const buttons = getButtons (
1079+ currentFilter ,
1080+ currentPage ,
1081+ entries . length ,
9351082 ) ;
9361083
937- // idk what is happening here anymore, but this is because eslint and prettier are fighting so i put them to rest by using only one line
9381084 await interaction . reply ( {
1085+ embeds : [ embed ] ,
1086+ components : buttons ,
9391087 flags : MessageFlags . Ephemeral ,
940- content : `
941- ## Tracked channels in this channel (<#${ channelId } >):\n${ filteredChannels . length ? filteredChannels . map ( ( channel ) => `Platform: ${ channel . guild_platform } | User ID: ${ channel . platform_user_id } ` ) . join ( "\n" ) : "No channels are being tracked in this channel." }
942-
943- ## Other tracked channels in this guild:\n${ newTrackedChannels . map ( ( channel ) => `Platform: ${ channel . guild_platform } | User ID: ${ channel . platform_user_id } | Channel: <#${ channel . guild_channel_id } >` ) . join ( "\n" ) } ` ,
9441088 } ) ;
9451089
946- return ;
1090+ const message = await interaction . fetchReply ( ) ;
1091+
1092+ const collector = message . createMessageComponentCollector ( {
1093+ componentType : ComponentType . Button ,
1094+ time : config . discordCollectorTimeout ,
1095+ filter : ( i ) => i . user . id === interaction . user . id ,
1096+ } ) ;
1097+
1098+ collector . on ( "collect" , async ( i ) => {
1099+ let needsUpdate = false ;
1100+
1101+ switch ( i . customId ) {
1102+ case "filter_all" :
1103+ case "filter_youtube" :
1104+ case "filter_twitch" : {
1105+ const newFilter = i . customId . replace (
1106+ "filter_" ,
1107+ "" ,
1108+ ) as FilterType ;
1109+
1110+ if ( currentFilter !== newFilter ) {
1111+ currentFilter = newFilter ;
1112+ currentPage = 0 ;
1113+ needsUpdate = true ;
1114+ }
1115+ break ;
1116+ }
1117+ case "prev_page" :
1118+ if ( currentPage > 0 ) {
1119+ currentPage -- ;
1120+ needsUpdate = true ;
1121+ }
1122+ break ;
1123+ case "next_page" : {
1124+ const filteredEntries = filterEntries ( currentFilter ) ;
1125+ const totalPages = Math . ceil (
1126+ filteredEntries . length / pageSize ,
1127+ ) ;
1128+
1129+ if ( currentPage < totalPages - 1 ) {
1130+ currentPage ++ ;
1131+ needsUpdate = true ;
1132+ }
1133+ break ;
1134+ }
1135+ }
1136+
1137+ if ( needsUpdate ) {
1138+ const filteredEntries = filterEntries ( currentFilter ) ;
1139+
1140+ await i . update ( {
1141+ embeds : [
1142+ getEmbed (
1143+ filteredEntries ,
1144+ currentPage ,
1145+ currentFilter ,
1146+ ) ,
1147+ ] ,
1148+ components : getButtons (
1149+ currentFilter ,
1150+ currentPage ,
1151+ filteredEntries . length ,
1152+ ) ,
1153+ } ) ;
1154+ } else {
1155+ await i . deferUpdate ( ) ;
1156+ }
1157+ } ) ;
1158+
1159+ collector . on ( "end" , async ( ) => {
1160+ try {
1161+ await interaction . editReply ( {
1162+ components : [ ] ,
1163+ } ) ;
1164+ } catch ( err ) {
1165+ console . error ( "Failed to edit reply:" , err ) ;
1166+ }
1167+ } ) ;
9471168 } ,
9481169 } ,
9491170} ;
0 commit comments