The bot uses Discord’s modern slash command system with interactive components like select menus, buttons, and modals.
Command Overview
/streamer add Add a streamer to track
/streamer remove Remove a tracked streamer
/streamer list View all tracked streamers
Streamer Commands
/streamer add
Select streaming platform: Kick, Twitch, YouTube, Rumble, or TikTok
Streamer’s username or handle (@ symbol is automatically removed)
Required Permission: Manage Channels
Command Flow
Execute Command
User runs /streamer add platform:twitch username:ninja
Duplicate Check
Bot checks if the streamer is already being tracked: const streamerId = createStreamerId ( platform , cleanUsername );
const existingStreamers = getStreamers ( guildId );
if ( existingStreamers . some (( s ) => s . id === streamerId )) {
// Show error embed
}
Channel Selection
Bot shows an embed with a channel select menu: const embed = createAddPromptEmbed ( platform , cleanUsername );
const channelSelect = createChannelSelect ( platform , cleanUsername );
const cancelButton = createCancelButton ();
await interaction . reply ({
embeds: [ embed ],
components: [ channelSelect , cancelButton ],
ephemeral: true ,
});
User Selects Channel
User picks the Discord channel for alerts using the select menu
Confirmation
Bot saves the streamer and shows success message
Implementation
// From add.ts:15-68
export async function handleAdd (
interaction : ChatInputCommandInteraction ,
platform : Platform ,
username : string ,
) : Promise < void > {
const guildId = interaction . guildId ;
if ( ! guildId ) {
await interaction . reply ({
embeds: [
createErrorEmbed ( "Error" , "This command can only be used in a server." ),
],
ephemeral: true ,
});
return ;
}
// Clean username (remove @ if present)
const cleanUsername = username . replace ( / ^ @/ , "" ). trim (). toLowerCase ();
// Check for duplicate
const streamerId = createStreamerId ( platform , cleanUsername );
const existingStreamers = getStreamers ( guildId );
if ( existingStreamers . some (( s ) => s . id === streamerId )) {
await interaction . reply ({
embeds: [
createErrorEmbed (
"Already Tracking" ,
`** ${ cleanUsername } ** on ${ platform } is already being tracked.` ,
),
],
ephemeral: true ,
});
return ;
}
// Show channel selection
const embed = createAddPromptEmbed ( platform , cleanUsername );
const channelSelect = createChannelSelect ( platform , cleanUsername );
const cancelButton = createCancelButton ();
await interaction . reply ({
embeds: [ embed ],
components: [ channelSelect , cancelButton ],
ephemeral: true ,
});
}
Example Usage
Kick Streamer
Twitch Streamer
YouTube Channel
/streamer add platform:Kick username:trainwreckstv
The username is automatically cleaned - @ninja becomes ninja. Usernames are converted to lowercase for consistency.
/streamer remove
Required Permission: Manage Channels
Command Flow
Execute Command
User runs /streamer remove
Fetch Streamers
Bot retrieves all tracked streamers for the server: const streamers = getStreamers ( guildId );
if ( streamers . length === 0 ) {
await interaction . reply ({
embeds: [
createErrorEmbed (
"No Streamers" ,
"No streamers are being tracked. \n\n Use `/streamer add` to add a streamer." ,
),
],
ephemeral: true ,
});
return ;
}
Streamer Selection
Bot shows select menu with all tracked streamers: const embed = createRemoveSelectEmbed ();
const streamerSelect = createStreamerSelect ( streamers );
const cancelButton = createCancelButton ();
await interaction . reply ({
embeds: [ embed ],
components: [ streamerSelect , cancelButton ],
ephemeral: true ,
});
Confirmation Dialog
After selection, bot shows Yes/Cancel buttons for confirmation
Removal
Upon confirmation, bot removes the streamer and updates the database
Implementation
// From remove.ts:14-61
export async function handleRemove (
interaction : ChatInputCommandInteraction ,
) : Promise < void > {
const guildId = interaction . guildId ;
if ( ! guildId ) {
await interaction . reply ({
embeds: [
createErrorEmbed ( "Error" , "This command can only be used in a server." ),
],
ephemeral: true ,
});
return ;
}
// Get existing streamers
const streamers = getStreamers ( guildId );
if ( streamers . length === 0 ) {
await interaction . reply ({
embeds: [
createErrorEmbed (
"No Streamers" ,
"No streamers are being tracked. \n\n Use `/streamer add` to add a streamer." ,
),
],
ephemeral: true ,
});
return ;
}
// Show streamer selection
const embed = createRemoveSelectEmbed ();
const streamerSelect = createStreamerSelect ( streamers );
const cancelButton = createCancelButton ();
await interaction . reply ({
embeds: [ embed ],
components: [ streamerSelect , cancelButton ],
ephemeral: true ,
});
}
/streamer list
Shows all streamers being tracked in the current server.
Required Permission: None (available to all users)
Features
Implementation
// From list.ts:11-44
export async function handleList (
interaction : ChatInputCommandInteraction ,
) : Promise < void > {
const guildId = interaction . guildId ;
if ( ! guildId ) {
await interaction . reply ({
embeds: [
createErrorEmbed ( "Error" , "This command can only be used in a server." ),
],
ephemeral: true ,
});
return ;
}
const streamers = getStreamers ( guildId );
const totalPages = Math . ceil ( streamers . length / ITEMS_PER_PAGE ) || 1 ;
const embed = createListEmbed ( streamers , 1 );
const components =
totalPages > 1 ? [ createPaginationButtons ( 1 , totalPages )] : [];
await interaction . reply ({
embeds: [ embed ],
components ,
ephemeral: true ,
});
}
List Embed Structure
// From embeds.ts:128-163
export function createListEmbed (
streamers : Streamer [],
page : number = 1 ,
) : EmbedBuilder {
const totalPages = Math . ceil ( streamers . length / ITEMS_PER_PAGE ) || 1 ;
const startIndex = ( page - 1 ) * ITEMS_PER_PAGE ;
const endIndex = Math . min ( startIndex + ITEMS_PER_PAGE , streamers . length );
const pageStreamers = streamers . slice ( startIndex , endIndex );
const embed = new EmbedBuilder ()
. setColor ( Colors . INFO )
. setTitle (
`📋 Tracked Streamers ( ${ startIndex + 1 } - ${ endIndex } of ${ streamers . length } )` ,
)
. setTimestamp ();
if ( streamers . length === 0 ) {
embed . setDescription (
"No streamers are being tracked. \n\n Use `/streamer add` to add a streamer." ,
);
return embed ;
}
const description = pageStreamers
. map (( s ) => {
const platform = PLATFORMS [ s . platform ];
const liveIndicator = s . isLive ? " • 🔴 LIVE" : "" ;
return ` ${ platform . emoji } ** ${ s . username } ** • ${ platform . name }${ liveIndicator } \n └ Alerts → <# ${ s . channelId } >` ;
})
. join ( " \n\n " );
embed . setDescription ( description );
embed . setFooter ({ text: `Page ${ page } / ${ totalPages } ` });
return embed ;
}
Example Output
📋 Tracked Streamers (1-5 of 12)
🟣 **xqc** • Twitch • 🔴 LIVE
└ Alerts → #stream-alerts
🟢 **trainwreckstv** • Kick
└ Alerts → #stream-alerts
🔴 **MrBeast** • YouTube
└ Alerts → #youtube-streams
Page 1/3
Utility Commands
/help
Displays an interactive help menu with all available commands and supported platforms.
Required Permission: None
Implementation
// From help.ts:9-28
export const helpCommand : Command = {
data: new SlashCommandBuilder ()
. setName ( "help" )
. setDescription ( "Show bot help and commands" ),
async execute ( interaction : ChatInputCommandInteraction ) : Promise < void > {
const embed = createHelpEmbed ();
await interaction . reply ({
embeds: [ embed ],
ephemeral: true ,
});
logger . command (
"help" ,
interaction . user . tag ,
interaction . guild ?. name ?? "DM" ,
);
},
};
Help Embed Content
// From embeds.ts:262-296
export function createHelpEmbed () : EmbedBuilder {
return new EmbedBuilder ()
. setColor ( Colors . INFO )
. setTitle ( "📚 Streamer Alerts Bot - Help" )
. setDescription (
"Track your favorite streamers and get notified when they go live!" ,
)
. addFields (
{
name: "📡 Streamer Commands" ,
value: [
"`/streamer add <platform> <username>` - Add a streamer to track" ,
"`/streamer remove` - Remove a tracked streamer" ,
"`/streamer list` - List all tracked streamers" ,
]. join ( " \n " ),
inline: false ,
},
{
name: "🛠️ Utility Commands" ,
value: [
"`/help` - Show this help message" ,
"`/ping` - Check bot latency" ,
]. join ( " \n " ),
inline: false ,
},
{
name: "📺 Supported Platforms" ,
value: Object . values ( PLATFORMS )
. map (( p ) => ` ${ p . emoji } ${ p . name } ` )
. join ( " • " ),
inline: false ,
},
)
. setTimestamp ();
}
/ping
Checks bot response time and WebSocket latency.
Required Permission: None
Features
Round-trip time between command execution and response (in milliseconds)
WebSocket ping to Discord gateway (in milliseconds)
Implementation
// From ping.ts:9-37
export const pingCommand : Command = {
data: new SlashCommandBuilder ()
. setName ( "ping" )
. setDescription ( "Check bot latency" ),
async execute ( interaction : ChatInputCommandInteraction ) : Promise < void > {
const sent = await interaction . reply ({
content: "🏓 Pinging..." ,
ephemeral: true ,
fetchReply: true ,
});
const latency = sent . createdTimestamp - interaction . createdTimestamp ;
const apiLatency = Math . round ( interaction . client . ws . ping );
const embed = createPingEmbed ( latency , apiLatency );
await interaction . editReply ({
content: "" ,
embeds: [ embed ],
});
logger . command (
"ping" ,
interaction . user . tag ,
interaction . guild ?. name ?? "DM" ,
);
},
};
Ping Embed
// From embeds.ts:301-312
export function createPingEmbed (
latency : number ,
apiLatency : number ,
) : EmbedBuilder {
return new EmbedBuilder ()
. setColor ( Colors . INFO )
. setTitle ( "🏓 Pong!" )
. addFields (
{ name: "Latency" , value: ` ${ latency } ms` , inline: true },
{ name: "API Latency" , value: ` ${ apiLatency } ms` , inline: true },
);
}
Permission System
Commands have different permission requirements:
Command Permission Required Reason /streamer addManage ChannelsModifies server configuration /streamer removeManage ChannelsModifies server configuration /streamer listNone Read-only information /helpNone Public utility /pingNone Public utility
Interactive Components
The bot uses modern Discord UI components:
Error Handling
All commands include comprehensive error handling:
// Guild-only check
if ( ! guildId ) {
await interaction . reply ({
embeds: [
createErrorEmbed ( "Error" , "This command can only be used in a server." ),
],
ephemeral: true ,
});
return ;
}
// Permission checks handled by Discord.js automatically
// Error embeds for user-facing errors
const embed = createErrorEmbed (
"Already Tracking" ,
`** ${ username } ** on ${ platform } is already being tracked.` ,
);
Constants
// From constants.ts:57-68
export const ITEMS_PER_PAGE = 10 ; // Pagination limit
export const Colors = {
SUCCESS: 0x57f287 , // Green
ERROR: 0xed4245 , // Red
WARNING: 0xfee75c , // Yellow
INFO: 0x5865f2 , // Blurple
DEFAULT: 0x2b2d31 , // Dark gray
} as const ;