Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/maybeanerd/MagiBot. MagiBot - the community building discord bot that adds joinsounds to your voice discord.gg/2Evcf4T
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257import { ActionRowBuilder, ButtonBuilder } from '@discordjs/builders';import Discord, { ButtonInteraction, ButtonStyle, ChatInputCommandInteraction, InteractionReplyOptions, Message, MessageComponentInteraction,} from 'discord.js';import { user } from './shared_assets';import { DeferReply } from './types/command';
export function doNothingOnError() { }
export function returnNullOnError() { return null;}export async function findMember( guild: Discord.Guild, userMention: string,): Promise<{ user: Discord.GuildMember | null; fuzzy?: boolean }> { if (!userMention || userMention.length === 0) { return { user: null }; } let mention = userMention.toLowerCase(); if (mention.startsWith('<@') && mention.endsWith('>')) { mention = mention.slice(2, -1); if (mention.startsWith('!')) { mention = mention.substr(1); } } // fails if its not a snowflake const member = await guild.members.fetch(mention).catch(returnNullOnError); if (member) { return { user: member, fuzzy: false }; } // it will sometimes only find one if multiple would fit (even with higher limit). // so we just call it fuzzy and take the first we get const membersFound = await guild.members.search({ query: mention, limit: 1 }); if (membersFound.size >= 1) { return { user: membersFound.first()!, fuzzy: true }; } return { user: null };}
export async function findRole(guild: Discord.Guild, ment: string) { if (!ment) { return false; } const mention = ment.toLowerCase(); if (mention.startsWith('<@&') && mention.endsWith('>')) { const id = mention.substr(3).slice(0, -1); return id; } const role = await guild.roles.fetch(mention); if (role) { return role.id; } if (mention.length >= 3) { let roleArray = guild.roles.cache.filter((rol) => rol.name.toLowerCase().startsWith(mention)); if (roleArray.size === 1) { return roleArray.first()!.id; } if (roleArray.size === 0) { roleArray = guild.roles.cache.filter((rol) => rol.name.toLowerCase().includes(mention)); if (roleArray.size === 1) { return roleArray.first()!.id; } } } return false;}
// for some reason eslint doesnt get this...// eslint-disable-next-line no-shadowexport const enum buttonInteractionId { 'confirmation' = 0x0001, 'queue' = 0x1001,}
export async function notifyAboutSlashCommand( message: Message, command: string,) { try { await message.reply(`This command has been moved to application (/) commands! You can use it via \`/${command}\`!
If you can't find it, either you are missing permissions, or the admins of this server have not given MagiBot permission to create application commands yet.To do the latter, re-invite the bot by clicking the big blue "Add to Server" button in the bot's profile!`); } catch (error) { console.error('Failed sending command has moved message:', error); }}
export function doesInteractionRequireFollowup( interaction: ChatInputCommandInteraction | ButtonInteraction,) { return interaction.replied || interaction.deferred;}
// this is an idea to implement rather reusable confirmation processes.// ; abortMessage, timeoutMessage and time are optional parametersexport async function interactionConfirmation( interaction: Discord.ChatInputCommandInteraction, question: string, deferralType?: DeferReply | false, abortMessage: string = 'Cancelled.', timeoutMessage: string = 'Timeouted.', timeoutTime: number = 20000,): Promise<MessageComponentInteraction | null> { const row = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId(`${buttonInteractionId.confirmation}-${interaction.id}-yes`) .setLabel('Yes') .setStyle(ButtonStyle.Success), new ButtonBuilder() .setCustomId(`${buttonInteractionId.confirmation}-${interaction.id}-no`) .setLabel('No') .setStyle(ButtonStyle.Danger), );
// always prefer ephemeral where possible const ephemeral = deferralType !== DeferReply.public;
const messageContent: InteractionReplyOptions = { content: question, components: [row as any], // type is incompatible for now ephemeral, }; if (doesInteractionRequireFollowup(interaction)) { await interaction.followUp(messageContent); } else { await interaction.reply(messageContent); } const questionMessage = (await interaction.fetchReply()) as Discord.Message; const messageForTimeout = timeoutMessage || 'Cancelled due to timeout.'; // only accept reactions from the user that created this question // eslint-disable-next-line max-len const filter = (intraction: MessageComponentInteraction) => intraction.user.id === interaction.member?.user.id && intraction.customId.startsWith( `${buttonInteractionId.confirmation}-${interaction.id}-`, ); const collector = questionMessage.createMessageComponentCollector({ filter, time: timeoutTime, }); const promise = new Promise<MessageComponentInteraction | null>((resolve) => { let alreadyResolved = false; collector.once('collect', async (collectionInteraction) => { alreadyResolved = true; // load info of that button const idParts = collectionInteraction.customId.split('-'); const isYesButton = idParts[2] === 'yes';
if (!isYesButton) { await collectionInteraction.reply({ content: abortMessage, ephemeral, }); } else { await collectionInteraction.deferReply(); } resolve(isYesButton ? collectionInteraction : null); collector.stop('Got an answer.'); }); collector.once('end', async () => { if (!alreadyResolved) { interaction.followUp({ content: messageForTimeout, ephemeral, }); resolve(null); } }); }); return promise;}export function printError(error: { response: { status: string; statusText: string };}) { console.error( `Errorstatus: ${error.response.status} ${error.response.statusText}`, );}
export async function asyncForEach<T, F, O>( values: Array<T> | Discord.Collection<string | number, T>, callback: ( input: T, index: number | string, optionalParams?: O) => Promise<F>, optParams?: O,) { if (Array.isArray(values)) { const arr = values.map((e, i) => callback(e, i, optParams)); return Promise.all<F>(arr); } const arr = values.map((e, i) => callback(e, i, optParams)); return Promise.all<F>(arr);}
// unusedexport async function asyncForEachFromFlint<T, F, N, O>( array: Array<T> | Map<N, T>, callback: (input: T, index: number | N, optParams?: O) => Promise<F>, optionalParams?: O,) { if (Array.isArray(array)) { const arr = array.map((e, i) => callback(e, i, optionalParams)); return Promise.all<F>(arr); } const arr: Array<Promise<F>> = []; array.forEach((e, i) => arr.push(callback(e, i, optionalParams))); return Promise.all<F>(arr);}
export async function asyncWait(ms: number) { return new Promise((resolve) => { setTimeout(resolve, ms); });}
export function getUserMention(userId: string | null | undefined) { if (!userId) { return ''; } return `<@${userId}>`;}
export function getRoleMention(roleId: string) { return `<@&${roleId}>`;}
export function getBotInviteUrl() { return `https://discord.com/api/oauth2/authorize?client_id=${user().id }&permissions=3212353&scope=bot%20applications.commands`;}
// eslint-disable-next-line no-shadowexport enum TimestampType { SHORT_TIME = 't', LONG_TIME = 'T', SHORT_DATE = 'd', LONG_DATE = 'D', SHORT_DATE_TIME = 'f', LONG_DATE_TIME = 'F', RELATIVE = 'R'}
export function formatAsTimestamp(date: Date, type: TimestampType = TimestampType.SHORT_DATE_TIME) { let timestamp = date.getTime(); timestamp /= 1000; timestamp = Math.floor(timestamp); return `<t:${timestamp}:${type}>`;}