UNPKG

serverless_bots_addons

Version:

A package to improve serverless discord bots

639 lines 18.6 kB
/* eslint-disable no-prototype-builtins */ import { MessageComponentTypes, ButtonStyleTypes, TextStyleTypes, InteractionType, verifyKey } from 'discord-interactions'; import { IncomingMessage } from 'http'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function login(request: Request | IncomingMessage & { body: any }, publicKey?: string) { if (request instanceof Request) { const signature: string | string[] = request.headers.get('x-signature-ed25519')!; const timestamp: string | string[] = request.headers.get('x-signature-timestamp')!; const body: ArrayBuffer = await request.clone().arrayBuffer(); const isValidRequest = verifyKey( body, signature as string, timestamp as string, publicKey || process.env.PUBLIC_KEY! ); const interaction = request.json() as unknown as Interaction; if (!isValidRequest) { return { status: 401, ...interaction }; } return { status: 200, ...interaction }; } else { const signature: string | string[] = request.headers['x-signature-ed25519']!; const timestamp: string | string[] = request.headers['x-signature-timestamp']!; const body = Buffer.from(JSON.stringify(request.body)); const isValidRequest = verifyKey( body, signature as string, timestamp as string, publicKey || process.env.PUBLIC_KEY! ); const interaction = request.body as Interaction; if (!isValidRequest) { return { status: 401, ...interaction }; } return { status: 200, ...interaction }; } } export async function reply(interaction: Interaction, options: InteractionOptions, token?: string) { await fetch(`https://discord.com/api/v10/interactions/${interaction.id}/${interaction.token}/callback`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 4, data: { content: options.content, embeds: options.embeds, attachments: options.attachments, components: options.components, flags: options.ephemeral ? 64 : 0 } }) }); } export async function editReply(interaction: Interaction, options: InteractionEditOptions, token?: string) { let id = (token || process.env.TOKEN)!.split('.')[0]; id = atob(id); await fetch(`https://discord.com/api/v10/webhooks/${id}/${interaction.token}/messages/@original`, { method: 'PATCH', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 4, data: { ...options } }) }); } export async function deferReply(interaction: Interaction, options: InteractionDeferredOptions, token?: string) { await fetch(`https://discord.com/api/v10/interactions/${interaction.id}/${interaction.token}/callback`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 5, data: { flags: options.ephemeral ? 64 : 0 } }) }); } export async function deferUpdate(interaction: Interaction, token?: string) { await fetch(`https://discord.com/api/v10/interactions/${interaction.id}/${interaction.token}/callback`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 6 }) }); } export async function showModal(interaction: Interaction, options: ModalOptions, token?: string) { await fetch(`https://discord.com/api/v10/interactions/${interaction.id}/${interaction.token}/callback`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 9, data: { ...options } }) }); } export async function autocompleteResult(interaction: Interaction, options: AutocompleteOptions, token?: string) { await fetch(`https://discord.com/api/v10/interactions/${interaction.id}/${interaction.token}/callback`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 8, data: { ...options } }) }); } export async function followUp(interaction: Interaction, options: FollowupOptions, token?: string) { let id = (token || process.env.TOKEN)!.split('.')[0]; id = atob(id); await fetch(`https://discord.com/api/v10/webhooks/${id}/${interaction.token}`, { method: 'POST', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ ...options }) }); } export async function editFollowup(interaction: Interaction, options: FollowupOptions, token?: string) { let id = (token || process.env.TOKEN)!.split('.')[0]; id = atob(id); await fetch(`https://discord.com/api/v10/webhooks/${id}/${interaction.token}/messages/${interaction.message.id}`, { method: 'PATCH', headers: { 'Authorization': `Bot ${token || process.env.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ ...options }) }); } export function get(interaction: Interaction, value: string) { const hasOptions = interaction.data!.hasOwnProperty('options'); if (hasOptions == true) { const options = interaction.data!.options; for (let i = 0; i < options!.length; i++) { if (interaction.data!.options![i].name == value) { return interaction.data!.options![i].value; } } } else { const hasComponents = interaction.data!.hasOwnProperty('components'); if (hasComponents == true) { const components = interaction.data!.components; for (let i = 0; i < components!.length; i++) { if (interaction.data!.components![i].components[0].custom_id == value) { return interaction.data!.components![i].components[0].value; } } } } } export enum ApplicationCommandTypes { CHAT_INPUT = 1, USER = 2, MESSAGE = 3 } export enum ApplicationCommandOptionTypes { SUB_COMMAND = 1, SUB_COMMAND_GROUP = 2, STRING = 3, INTEGER = 4, BOOLEAN = 5, USER = 6, CHANNEL = 7, ROLE = 8, MENTIONABLE = 9, NUMBER = 10, ATTACHMENT = 11 } export enum ChannelTypes { GUILD_TEXT = 0, DM = 1, GUILD_VOICE = 2, GROUP_DM = 3, GUILD_CATEGORY = 4, GUILD_ANNOUNCEMENT = 5, ANNOUNCEMENT_THREAD = 10, PUBLIC_THREAD = 11, PRIVATE_THREAD = 12, GUILD_STAGE_VOICE = 13, GUILD_DIRECTORY = 14, GUILD_FORUM = 15, GUILD_MEDIA = 16 } export enum Permissions { CREATE_INSTANT_INVITE = 1 << 0, KICK_MEMBERS = 1 << 1, BAN_MEMBERS = 1 << 2, ADMINISTRATOR = 1 << 3, MANAGE_CHANNELS = 1 << 4, MANAGE_GUILD = 1 << 5, ADD_REACTIONS = 1 << 6, VIEW_AUDIT_LOG = 1 << 7, PRIORITY_SPEAKER = 1 << 8, STREAM = 1 << 9, VIEW_CHANNEL = 1 << 10, SEND_MESSAGES = 1 << 11, SEND_TTS_MESSAGES = 1 << 12, MANAGE_MESSAGES = 1 << 13, EMBED_LINKS = 1 << 14, ATTACH_FILES = 1 << 15, READ_MESSAGE_HISTORY = 1 << 16, MENTION_EVERYONE = 1 << 17, USE_EXTERNAL_EMOJIS = 1 << 18, VIEW_GUILD_INSIGHTS = 1 << 19, CONNECT = 1 << 20, SPEAK = 1 << 21, MUTE_MEMBERS = 1 << 22, DEAFEN_MEMBERS = 1 << 23, MOVE_MEMBERS = 1 << 24, USE_VAD = 1 << 25, CHANGE_NICKNAME = 1 << 26, MANAGE_NICKNAMES = 1 << 27, MANAGE_ROLES = 1 << 28, MANAGE_WEBHOOKS = 1 << 29, MANAGE_GUILD_EXPRESSIONS = 1 << 30, USE_APPLICATION_COMMANDS = 1 << 31, REQUEST_TO_SPEAK = 1 << 32, MANAGE_EVENTS = 1 << 33, MANAGE_THREADS = 1 << 34, CREATE_PUBLIC_THREADS = 1 << 35, CREATE_PRIVATE_THREADS = 1 << 36, USE_EXTERNAL_STICKERS = 1 << 37, SEND_MESSAGES_IN_THREADS = 1 << 38, USE_EMBEDDED_ACTIVITIES = 1 << 39, MODERATE_MEMBERS = 1 << 40, VIEW_CREATOR_MONETIZATION_ANALYTICS = 1 << 41, USE_SOUNDBOARD = 1 << 42, USE_EXTERNAL_SOUNDS = 1 << 45, SEND_VOICE_MESSAGES = 1 << 46 } export interface SlashCommandsStructure { type?: ApplicationCommandTypes, guild_id?: string, name: string, name_localizations?: LocalizationObject | null, description: string, description_localizations?: LocalizationObject | null, options?: ApplicationCommandOptions[], dm_permission?: boolean, default_member_permissions: string | null, nsfw?: boolean } export interface ApplicationCommandOptions { type: ApplicationCommandOptionTypes, name: string, name_localizations?: LocalizationObject | null, description: string, description_localizations?: LocalizationObject | null, required?: boolean, choices?: Options[], options?: ApplicationCommandOptions[], channel_types?: ChannelTypes[], min_value?: number, max_value?: number, min_length?: number max_length?: number, autocomplete?: boolean } export interface LocalizationObject { 'id'?: string, 'da'?: string, 'de'?: string, 'en-GB'?: string, 'en-US'?: string, 'es-ES'?: string, 'fr'?: string, 'hr'?: string, 'it'?: string, 'lt'?: string 'hu'?: string, 'nl'?: string, 'no'?: string, 'pl'?: string, 'pt-BR'?: string, 'ro'?: string, 'fi'?: string, 'sv-SE'?: string, 'vi'?: string, 'tr'?: string, 'cs'?: string, 'el'?: string, 'bg'?: string, 'ru'?: string, 'uk'?: string, 'hi'?: string, 'th'?: string, 'zh-CN'?: string, 'ja'?: string, 'zh-TW'?: string, 'ko'?: string } export interface Interaction { app_permissions: string, application_id: string, channel: Channel, channel_id: string, data?: { guild_id: string, id: string, name: string, resolved?: Resolved, target_id?: string options?: Data['options'], components?: Data['components'], custom_id?: string, component_type: number, values: [ label: string ] type: ApplicationCommandTypes }, guild: { features: string[], id: string, locale: string }, guild_id?: string, guild_locale?: string, id: string, locale?: string, member?: Member, user?: User token: string, type: InteractionType, version: 1, message: Message } interface Resolved { messages?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: Message }, users?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: User }, members?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: Member }, roles?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: Roles }, channels?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: Channel }, attachments?: { [T in Interaction['data'] extends { target_id: string } ? Interaction['data']['target_id'] : string]: Attachments } } export interface Message { id: string, channel_id: string, author: User, content: string, timestamp: string, edited_timestamp: string | null, tts: boolean, mention_everyone: boolean, attachments: Attachments[], embeds: Embeds[], reactions?: { count: number, me: boolean, emoji: Emoji }, pinned: boolean, flags?: number, interaction?: Interaction, thread?: Channel, components?: ButtonsComponent[] | SelectMenusComponent[] | TextInputsComponent[], position?: number } export interface ModalOptions { title: string, custom_id: string, components: ActionRow[] } export interface InteractionDeferredOptions { ephemeral: boolean } export interface InteractionOptions { content?: string, embeds?: Embeds[] components?: ActionRow[], attachments?: Attachments[] ephemeral: boolean } export interface InteractionEditOptions { content?: string, embeds?: Embeds[] components?: ActionRow[], attachments?: Attachments[] } export interface FollowupOptions { content?: string, embeds?: Embeds[], components?: ActionRow[] } export interface Options { name: string, name_localizations?: object | null, value: string | number } export interface AutocompleteOptions { choices: { name: string, value: string }[] } export interface Embeds { title?: string, type?: string, description?: string, url?: string, timestamp?: string, color?: number, footer?: { text: string, icon_url?: string }, image?: { url: string }, thumbnail?: { url: string }, author?: { name: string, url?: string, icon_url?: string }, fields?: { name: string, value: string, inline?: boolean }[] } export interface Attachments { id: string, filename: string, description?: string, content_type?: string, size: number, url: string, proxy_url: string, height?: number | null, width?: number | null, ephemeral?: boolean, duration_secs?: number, waveform?: string } export interface ActionRow { type: MessageComponentTypes.ACTION_ROW components: ButtonsComponent[] | SelectMenusComponent[] | TextInputsComponent[] } interface BaseComponent { type: MessageComponentTypes } export interface ButtonsComponent extends BaseComponent { custom_id?: string, style: ButtonStyleTypes, label?: string, emoji?: Emoji, url?: string, disabled?: boolean } export interface SelectMenusComponent extends BaseComponent { custom_id: string, options?: { label: string, value: string, description?: string, emoji?: Emoji, default?: boolean }[], channel_types?: ChannelTypes[], placeholder?: string, min_values?: string, max_values?: string, disabled?: boolean } export interface TextInputsComponent extends BaseComponent { custom_id: string, style: TextStyleTypes, label: string, min_length?: number, max_length?: number, required?: boolean value?: string, placeholder?: string } export interface Emoji { id: string | null, name: string | null, roles?: Roles[], user?: User, require_colons?: boolean, managed?: boolean, animated?: boolean, avaible?: boolean } export interface Channel { flags?: number, guild_id?: string, id: string, last_message_id?: string | null, last_pin_timestamp?: string | null, name?: string, nsfw?: boolean, parent_id?: string | null, permissions?: string, position?: number, rate_limit_per_user?: number, topic?: string, type?: ChannelTypes } export interface Member { avatar?: string | null, communication_disabled_until?: string | null, deaf: boolean, flags: number, joined_at: string, mute: boolean, nick?: string | null, pending?: boolean, permissions?: string, premium_since?: string | null, roles: string[], user: User, } export interface Roles { id: string, name: string, color: number, hoist: boolean, icon?: string | null, unicode_emoji?: string | null, position: number, permissions: string, managed: boolean, mentionable: boolean, tags?: { bot_id?: string, integration_id?: string, premium_subscriber?: null, subscription_listing_id?: string, available_for_purchase?: null, guild_connections?: null } } export interface User { id: string, username: string, discriminator: string, avatar: string | null, bot?: boolean, system?: boolean, mfa_enabled?: boolean, banner?: string, accent_color?: number, locale?: string, verified?: string, email?: string | null, flags?: number, premium_type?: PremiumType, public_flags?: number } export interface Guild { id: string, name: string, icon: string | null, icon_hash?: string | null, splash: string | null, discovery_splash: string | null, owner?: boolean, owner_id: string, permissions?: string, region?: string | null, afk_channel_id: string | null, afk_timeout: number, widget_enabled?: boolean, widget_channel_id?: string | null, verification_level: number, default_message_notifications: number, explicit_content_filter: number, roles: Roles, emojis: Emoji, features: string[], mfa_level: number, application_id: string | null, system_channel_id: string | null, system_channel_flags: number, rules_channel_id: string | null, max_presences?: number | null, vanity_url_code: string | null, description: string | null, banner: string | null, premium_tier: GuildPremiumTier, premium_subscription_count?: number, preferred_locate: string, nsfw_level: NSFW_Level } interface PremiumType { NONE: 0, NITRO_CLASSIC: 1 NITRO: 2, NITRO_BASIC: 3 } interface GuildPremiumTier { NONE: 0, TIER_1: 1 TIER_2: 2, TIER_3: 3 } interface NSFW_Level { DEFAULT: 0, EXPLICIT: 1, SAFE: 2, AGE_RESTRICTED: 3 } interface Data { options?: { name: string, type: number, value: string }[], components?: { components: { custom_id: string, type: number, value: string }[] }[] }