diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml index bccd47892..505b8050d 100644 --- a/.github/workflows/pr_tests.yml +++ b/.github/workflows/pr_tests.yml @@ -8,22 +8,26 @@ jobs: deno: ["v1.x"] steps: - uses: actions/checkout@v2 + - run: git submodule update --init --recursive - uses: denoland/setup-deno@main with: deno-version: ${{ matrix.deno }} - name: Cache dependencies run: deno cache mod.ts + - name: Prepare configs file + run: cp configs.example.ts configs.ts - name: Run tests if requested by maintainers if: ${{ github.event.issue.pull_request && github.event.comment.body == 'run-tests' && (github.actor == 'Skillz4Killz' || github.actor == 'itohatweb') }} run: DISCORD_TOKEN=${{ env.DISCORD_TOKEN }} deno test --unstable --coverage=coverage -A tests/mod.ts env: DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} - # TODO: add coverage back when it is stable - # - name: Create coverage report - # run: deno --unstable --exclude=test coverage ./coverage --lcov > coverage.lcov - # - name: Collect and upload the coverage report - # uses: codecov/codecov-action@v1.0.10 - # with: - # file: ./coverage.lcov + - name: Create coverage report + if: github.ref == 'refs/heads/main' + run: deno coverage --exclude=tests ./coverage --lcov > coverage.lcov + - name: Collect and upload the coverage report + if: github.ref == 'refs/heads/main' + uses: codecov/codecov-action@v1.0.10 + with: + file: ./coverage.lcov env: DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} diff --git a/README.md b/README.md index 408771c38..8bef0b858 100644 --- a/README.md +++ b/README.md @@ -33,36 +33,37 @@ TODO: add coverage back when it is stable ### REST -- [x] Freedom from Invalid Request 1 Hour Downtimes - - [x] Discordeno will protect your bot from going down for an hour and will instead decrease the maximum downtime to 10 minutes. -- [x] Freedom from global rate limit errors - - [x] As your bot grows, you want to handle global rate limits better. Shards don't communicate fast enough to truly handle it properly so this allows 1 rest handler across the entire bot. - - [x] In fact, you can host multiple instances of your bot and all connect to the same rest server. -- [x] REST does not rest! - - [x] Separate rest means if your bot for whatever reason crashes, your requests that are queued will still keep going and will not be lost. - - [x] Seamless updates! When you want to update and reboot the bot, you could potentially lose tons of messages or responses that are in queue. Using this you could restart your bot without ever worrying about losing any responses. -- [x] Single source of contact to Discord API - - [x] This will allow you to make requests to discord from anywhere including a bot dashboard. You no longer need to have to communicate to your bot processes just to make a request or anything. Free up your bot process for processing bot events. -- [x] Scalability! Scalability! Scalability! +- ✅ Freedom from Invalid Request 1 Hour Downtimes + - ✅ Discordeno will protect your bot from going down for an hour and will instead decrease the maximum downtime to 10 minutes. +- ✅ Freedom from global rate limit errors + - ✅ As your bot grows, you want to handle global rate limits better. Shards don't communicate fast enough to truly handle it properly so this allows 1 rest handler across the entire bot. + - ✅ In fact, you can host multiple instances of your bot and all connect to the same rest server. +- ✅ REST does not rest! + - ✅ Separate rest means if your bot for whatever reason crashes, your requests that are queued will still keep going and will not be lost. + - ✅ Seamless updates! When you want to update and reboot the bot, you could potentially lose tons of messages or responses that are in queue. Using this you could restart your bot without ever worrying about losing any responses. +- ✅ Single source of contact to Discord API + - ✅ This will allow you to make requests to discord from anywhere including a bot dashboard. You no longer need to have to communicate to your bot processes just to make a request or anything. Free up your bot process for processing bot events. +- ✅ Scalability! Scalability! Scalability! ### Gateway -- [x] **Zero Downtime Updates:** - - [x] Your bot can be updated in a matter of seconds. With normal sharding, you have to restart which also has to process identifying all your shards with a 1/~5s rate limit. With WS handling moved to a proxy process, this allows you to instantly get the bot code restarted without any concerns of delays. If you have a bot on 200,000 servers normally this would mean a 20 minute delay to restart your bot if you made a small change and restarted. -- [x] **Zero Downtime Resharding:** - - [x] Discord stops letting your bot get added to new servers at certain points in time. For example, suppose you had 150,000 servers running 150 shards. The maximum amount of servers your shards could hold is 150 \* 2500 = 375,000. If your bot reaches this, it can no longer join new servers until it re-shards. - - [x] DD proxy provides 2 types of re-sharding. Automated and manual. You can also have both. - - [x] Automated: This system will automatically begin a Zero-downtime resharding process behind the scenes when you reach 80% of your maximum servers allowed by your shards. For example, since 375,000 was the max, at 300,000 we would begin re-sharding behind the scenes with ZERO DOWNTIME. - - [x] 80% of maximum servers reached (The % of 80% is customizable.) - - [x] Identify limits have room to allow re-sharding. (Also customizable) - - [x] Manual: You can also trigger this manually should you choose. -- [x] **Horizontal Scaling:** - - [x] The proxy system allows you to scale the bot horizontally. When you reach a huge size, you can either keep spending more money to keep beefing up your server or you can buy several cheaper servers and scale horizontally. The proxy means you can have WS handling on a completely separate system. -- [x] **No Loss Restarts:** -[x] When you restart a bot without the proxy system, normally you would lose many events. Users may be using commands or messages are sent that will not be filtered. As your bot's grow this number rises dramatically. Users may join who wont get the auto-roles or any other actions your bot should take. With the proxy system, you can keep restarting your bot and never lose any events. Events will be put into a queue while your bot is down(max size of queue is customizable), once the bot is available the queue will begin processing all events. -- [x] **Controllers:** - - [x] The controller aspect gives you full control over everything inside the proxy. You can provide a function to simply override the handler. For example, if you would like a certain function to do something different, instead of having to fork and maintain your fork, you can just provide a function to override. -- [x] **Clustering With Workers:** - - [x] Take full advantage of all your CPU cores by using workers to spread the load. Control how many shards per worker and how many workers to maximize efficiency! +- ✅ **Zero Downtime Updates:** + - ✅ Your bot can be updated in a matter of seconds. With normal sharding, you have to restart which also has to process identifying all your shards with a 1/~5s rate limit. With WS handling moved to a proxy process, this allows you to instantly get the bot code restarted without any concerns of delays. If you have a bot on 200,000 servers normally this would mean a 20 minute delay to restart your bot if you made a small change and restarted. +- ✅ **Zero Downtime Resharding:** + - ✅ Discord stops letting your bot get added to new servers at certain points in time. For example, suppose you had 150,000 servers running 150 shards. The maximum amount of servers your shards could hold is 150 \* 2500 = 375,000. If your bot reaches this, it can no longer join new servers until it re-shards. + - ✅ DD proxy provides 2 types of re-sharding. Automated and manual. You can also have both. + - ✅ Automated: This system will automatically begin a Zero-downtime resharding process behind the scenes when you reach 80% of your maximum servers allowed by your shards. For example, since 375,000 was the max, at 300,000 we would begin re-sharding behind the scenes with ZERO DOWNTIME. + - ✅ 80% of maximum servers reached (The % of 80% is customizable.) + - ✅ Identify limits have room to allow re-sharding. (Also customizable) + - ✅ Manual: You can also trigger this manually should you choose. +- ✅ **Horizontal Scaling:** + - ✅ The proxy system allows you to scale the bot horizontally. When you reach a huge size, you can either keep spending more money to keep beefing up your server or you can buy several cheaper servers and scale horizontally. The proxy means you can have WS handling on a completely separate system. +- ✅ **No Loss Restarts:** + - ✅ When you restart a bot without the proxy system, normally you would lose many events. Users may be using commands or messages are sent that will not be filtered. As your bot's grow this number rises dramatically. Users may join who wont get the auto-roles or any other actions your bot should take. With the proxy system, you can keep restarting your bot and never lose any events. Events will be put into a queue while your bot is down(max size of queue is customizable), once the bot is available the queue will begin processing all events. +- ✅ **Controllers:** + - ✅ The controller aspect gives you full control over everything inside the proxy. You can provide a function to simply override the handler. For example, if you would like a certain function to do something different, instead of having to fork and maintain your fork, you can just provide a function to override. +- ✅ **Clustering With Workers:** + - ✅ Take full advantage of all your CPU cores by using workers to spread the load. Control how many shards per worker and how many workers to maximize efficiency! ### Custom Cache @@ -75,10 +76,10 @@ Have your cache setup in any way you like. Redis, PGSQL or any cache layer you w Here is a minimal example to get started with: ```typescript -import { createBot, startBot } from "https://deno.land/x/discordeno/mod.ts"; -import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.9/mod.ts"; +import { createBot, startBot } from "https://deno.land/x/discordeno@13.0.0-rc15/mod.ts"; +import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.18/mod.ts"; -const bot = createBot({ +const baseBot = createBot({ token: Deno.env.get("DISCORD_TOKEN"), intents: ["Guilds", "GuildMessages"], botId: Deno.env.get("BOT_ID"), @@ -90,11 +91,12 @@ const bot = createBot({ // Process the message with your command handler here }, }, - cache: { isAsync: false }, }); -enableCachePlugin(bot); +const bot = enableCachePlugin(baseBot); + enableCacheSweepers(bot); + await startBot(bot); ``` diff --git a/mod.ts b/mod.ts index 2c0531975..df3b4248f 100644 --- a/mod.ts +++ b/mod.ts @@ -6,4 +6,3 @@ export * from "./src/transformers/mod.ts"; export * from "./src/types/mod.ts"; export * from "./src/util/mod.ts"; export * from "./src/ws/mod.ts"; -export * from "./src/cache.ts"; \ No newline at end of file diff --git a/src/bot.ts b/src/bot.ts index abe2482a9..a424d2717 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -49,10 +49,11 @@ import { resume, resharder, spawnShards, + prepareBuckets, createShard, identify, heartbeat, - tellClusterToIdentify, + tellWorkerToIdentify, sendShardMessage, DiscordenoShard, processGatewayQueue, @@ -77,7 +78,6 @@ import { urlToBase64 } from "./util/urlToBase64.ts"; import { transformAttachment } from "./transformers/attachment.ts"; import { transformEmbed } from "./transformers/embed.ts"; import { transformComponent } from "./transformers/component.ts"; -import { AsyncCache, AsyncCacheHandler, Cache, CacheHandler, createCache, TableNames } from "./cache.ts"; import { transformWebhook } from "./transformers/webhook.ts"; import { transformAuditlogEntry } from "./transformers/auditlogEntry.ts"; import { transformApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; @@ -86,22 +86,13 @@ import { calculateBits, calculatePermissions } from "./util/permissions.ts"; import { transformScheduledEvent } from "./transformers/scheduledEvent.ts"; import { DiscordenoScheduledEvent } from "./transformers/scheduledEvent.ts"; import { transformThreadMember } from "./transformers/threadMember.ts"; +import { transformApplicationCommandOption } from "./transformers/applicationCommandOption.ts"; +import { transformApplicationCommand } from "./transformers/applicationCommand.ts"; +import { transformWelcomeScreen } from "./transformers/welcomeScreen.ts"; +import { transformVoiceRegion } from "./transformers/voiceRegion.ts"; +import { transformWidget } from "./transformers/widget.ts"; -type CacheOptions = - | { - isAsync: true; - // deno-lint-ignore no-explicit-any - tableCreator: (tableName: TableNames) => AsyncCacheHandler; - } - | { - isAsync: false; - // deno-lint-ignore no-explicit-any - tableCreator?: (tableName: TableNames) => CacheHandler; - }; - -export function createBot( - options: CreateBotOptions -): Bot { +export function createBot(options: CreateBotOptions): Bot { const bot = { id: options.botId, applicationId: options.applicationId || options.botId, @@ -116,13 +107,33 @@ export function createBot( transformers: createTransformers(options.transformers ?? {}), enabledPlugins: new Set(), handleDiscordPayload: options.handleDiscordPayload, - } as unknown as Bot; + cache: { + unrepliedInteractions: new Set(), + fetchAllMembersProcessingRequests: new Map(), + }, + rest: createRestManager({ token: options.token, debug: options.events.debug }), + } as Bot; - // @ts-ignore itoh cache types plz - bot.cache = createCache(bot as Bot, options.cache); - bot.helpers = createHelpers(bot as Bot, options.helpers ?? {}); + bot.helpers = createHelpers(bot, options.helpers ?? {}); + bot.gateway = createGatewayManager({ + token: bot.token, + intents: bot.intents, + debug: bot.events.debug, + handleDiscordPayload: + bot.handleDiscordPayload ?? + async function (_, data: DiscordGatewayPayload, shardId: number) { + // TRIGGER RAW EVENT + bot.events.raw(bot as Bot, data, shardId); - return bot as unknown as Bot; + if (!data.t) return; + + // RUN DISPATCH CHECK + await bot.events.dispatchRequirements(bot as Bot, data, shardId); + bot.handlers[data.t as GatewayDispatchEventNames]?.(bot as Bot, data, shardId); + }, + }); + + return bot as Bot; } export function createEventHandlers(events: Partial): EventHandlers { @@ -262,36 +273,17 @@ export function createRestManager(options: CreateRestManagerOptions) { } export async function startBot(bot: Bot) { - // START REST - bot.rest = createRestManager({ token: bot.token, debug: bot.events.debug }); if (!bot.botGatewayData) bot.botGatewayData = await bot.helpers.getGatewayBot(); - // START WS - bot.gateway = createGatewayManager({ - token: bot.token, - intents: bot.intents, - urlWSS: bot.botGatewayData.url, - shardsRecommended: bot.botGatewayData.shards, - sessionStartLimitTotal: bot.botGatewayData.sessionStartLimit.total, - sessionStartLimitRemaining: bot.botGatewayData.sessionStartLimit.remaining, - sessionStartLimitResetAfter: bot.botGatewayData.sessionStartLimit.resetAfter, - maxConcurrency: bot.botGatewayData.sessionStartLimit.maxConcurrency, - lastShardId: bot.botGatewayData.shards, - maxShards: bot.botGatewayData.shards, - debug: bot.events.debug, - handleDiscordPayload: - bot.handleDiscordPayload ?? - async function (_, data: DiscordGatewayPayload, shardId: number) { - // TRIGGER RAW EVENT - bot.events.raw(bot as Bot, data, shardId); - - if (!data.t) return; - - // RUN DISPATCH CHECK - await bot.events.dispatchRequirements(bot as Bot, data, shardId); - bot.handlers[data.t as GatewayDispatchEventNames]?.(bot as Bot, data, shardId); - }, - }); + // SETUP GATEWAY LOGIN INFO + bot.gateway.urlWSS = bot.botGatewayData.url; + bot.gateway.shardsRecommended = bot.botGatewayData.shards; + bot.gateway.sessionStartLimitTotal = bot.botGatewayData.sessionStartLimit.total; + bot.gateway.sessionStartLimitRemaining = bot.botGatewayData.sessionStartLimit.remaining; + bot.gateway.sessionStartLimitResetAfter = bot.botGatewayData.sessionStartLimit.resetAfter; + bot.gateway.maxConcurrency = bot.botGatewayData.sessionStartLimit.maxConcurrency; + bot.gateway.lastShardId = bot.botGatewayData.shards; + bot.gateway.maxShards = bot.botGatewayData.shards; bot.gateway.spawnShards(bot.gateway); } @@ -344,8 +336,8 @@ export function createGatewayManager( spawnShardDelay: options.spawnShardDelay ?? 2600, maxShards: options.maxShards ?? options.shardsRecommended ?? 0, useOptimalLargeBotSharding: options.useOptimalLargeBotSharding ?? true, - shardsPerCluster: options.shardsPerCluster ?? 25, - maxClusters: options.maxClusters ?? 4, + shardsPerWorker: options.shardsPerWorker ?? 25, + maxWorkers: options.maxWorkers ?? 4, firstShardId: options.firstShardId ?? 0, lastShardId: options.lastShardId ?? options.maxShards ?? options.shardsRecommended ?? 1, token: options.token ?? "", @@ -369,11 +361,12 @@ export function createGatewayManager( buckets: new Collection(), utf8decoder: new TextDecoder(), + prepareBuckets: options.prepareBuckets ?? prepareBuckets, spawnShards: options.spawnShards ?? spawnShards, createShard: options.createShard ?? createShard, identify: options.identify ?? identify, heartbeat: options.heartbeat ?? heartbeat, - tellClusterToIdentify, + tellWorkerToIdentify, debug: options.debug || function () {}, resharder: options.resharder ?? resharder, handleOnMessage: options.handleOnMessage ?? handleOnMessage, @@ -397,7 +390,7 @@ export async function stopBot(bot: Bot) { return bot; } -export interface CreateBotOptions { +export interface CreateBotOptions { token: string; botId: bigint; applicationId?: bigint; @@ -406,7 +399,6 @@ export interface CreateBotOptions { botGatewayData?: GetGatewayBot; rest?: Omit; handleDiscordPayload?: GatewayManager["handleDiscordPayload"]; - cache: C; utils?: Partial>; transformers?: Partial>; helpers?: Partial; @@ -414,20 +406,9 @@ export interface CreateBotOptions { export type UnPromise> = T extends Promise ? K : never; -// export type CreatedBot = ReturnType; - -// export type Bot = CreatedBot & { -// utils: HelperUtils; -// rest: RestManager; -// gateway: GatewayManager; -// transformers: Transformers; -// helpers: Helpers; -// }; - -export interface Bot { +export interface Bot { id: bigint; applicationId: bigint; - token: string; intents: GatewayIntents; urlWSS: string; @@ -441,163 +422,18 @@ export interface Bot { handlers: ReturnType; activeGuildIds: Set; constants: ReturnType; - cache: C; + cache: { + unrepliedInteractions: Set; + fetchAllMembersProcessingRequests: Map; + }; enabledPlugins: Set; handleDiscordPayload?: GatewayManager["handleDiscordPayload"]; } -export interface Helpers { - addDiscoverySubcategory: typeof helpers.addDiscoverySubcategory; - addReaction: typeof helpers.addReaction; - addReactions: typeof helpers.addReactions; - addRole: typeof helpers.addRole; - avatarURL: typeof helpers.avatarURL; - banMember: typeof helpers.banMember; - batchEditApplicationCommandPermissions: typeof helpers.batchEditApplicationCommandPermissions; - channelOverwriteHasPermission: typeof helpers.channelOverwriteHasPermission; - cloneChannel: typeof helpers.cloneChannel; - connectToVoiceChannel: typeof helpers.connectToVoiceChannel; - createChannel: typeof helpers.createChannel; - createEmoji: typeof helpers.createEmoji; - createGuild: typeof helpers.createGuild; - createGuildFromTemplate: typeof helpers.createGuildFromTemplate; - createGuildTemplate: typeof helpers.createGuildTemplate; - createInvite: typeof helpers.createInvite; - createRole: typeof helpers.createRole; - createScheduledEvent: typeof helpers.createScheduledEvent; - createApplicationCommand: typeof helpers.createApplicationCommand; - createStageInstance: typeof helpers.createStageInstance; - createWebhook: typeof helpers.createWebhook; - deleteChannel: typeof helpers.deleteChannel; - deleteChannelOverwrite: typeof helpers.deleteChannelOverwrite; - deleteEmoji: typeof helpers.deleteEmoji; - deleteGuild: typeof helpers.deleteGuild; - deleteGuildTemplate: typeof helpers.deleteGuildTemplate; - deleteIntegration: typeof helpers.deleteIntegration; - deleteInvite: typeof helpers.deleteInvite; - deleteMessage: typeof helpers.deleteMessage; - deleteMessages: typeof helpers.deleteMessages; - deleteRole: typeof helpers.deleteRole; - deleteScheduledEvent: typeof helpers.deleteScheduledEvent; - deleteApplicationCommand: typeof helpers.deleteApplicationCommand; - deleteInteractionResponse: typeof helpers.deleteInteractionResponse; - deleteStageInstance: typeof helpers.deleteStageInstance; - deleteWebhook: typeof helpers.deleteWebhook; - deleteWebhookMessage: typeof helpers.deleteWebhookMessage; - deleteWebhookWithToken: typeof helpers.deleteWebhookWithToken; - disconnectMember: typeof helpers.disconnectMember; - editBotNickname: typeof helpers.editBotNickname; - editBotProfile: typeof helpers.editBotProfile; - editBotStatus: typeof helpers.editBotStatus; - editChannel: typeof helpers.editChannel; - editChannelOverwrite: typeof helpers.editChannelOverwrite; - editDiscovery: typeof helpers.editDiscovery; - editEmoji: typeof helpers.editEmoji; - editGuild: typeof helpers.editGuild; - editGuildTemplate: typeof helpers.editGuildTemplate; - editMember: typeof helpers.editMember; - editMessage: typeof helpers.editMessage; - editRole: typeof helpers.editRole; - editScheduledEvent: typeof helpers.editScheduledEvent; - editInteractionResponse: typeof helpers.editInteractionResponse; - editApplicationCommandPermissions: typeof helpers.editApplicationCommandPermissions; - editWebhook: typeof helpers.editWebhook; - editWebhookMessage: typeof helpers.editWebhookMessage; - editWebhookWithToken: typeof helpers.editWebhookWithToken; - editWelcomeScreen: typeof helpers.editWelcomeScreen; - editWidget: typeof helpers.editWidget; - emojiUrl: typeof helpers.emojiUrl; - fetchMembers: typeof helpers.fetchMembers; - followChannel: typeof helpers.followChannel; - getAuditLogs: typeof helpers.getAuditLogs; - getAvailableVoiceRegions: typeof helpers.getAvailableVoiceRegions; - getBan: typeof helpers.getBan; - getBans: typeof helpers.getBans; - getChannel: typeof helpers.getChannel; - getChannelInvites: typeof helpers.getChannelInvites; - getChannels: typeof helpers.getChannels; - getChannelWebhooks: typeof helpers.getChannelWebhooks; - getDiscoveryCategories: typeof helpers.getDiscoveryCategories; - getEmoji: typeof helpers.getEmoji; - getEmojis: typeof helpers.getEmojis; - getGatewayBot: typeof helpers.getGatewayBot; - getGuild: typeof helpers.getGuild; - getGuildPreview: typeof helpers.getGuildPreview; - getGuildTemplates: typeof helpers.getGuildTemplates; - getIntegrations: typeof helpers.getIntegrations; - getInvite: typeof helpers.getInvite; - getInvites: typeof helpers.getInvites; - getMember: typeof helpers.getMember; - getMembers: typeof helpers.getMembers; - getMessage: typeof helpers.getMessage; - getMessages: typeof helpers.getMessages; - getOriginalInteractionResponse: typeof helpers.getOriginalInteractionResponse; - getPins: typeof helpers.getPins; - getPruneCount: typeof helpers.getPruneCount; - getReactions: typeof helpers.getReactions; - getRoles: typeof helpers.getRoles; - getScheduledEvent: typeof helpers.getScheduledEvent; - getScheduledEvents: typeof helpers.getScheduledEvents; - getScheduledEventUsers: typeof helpers.getScheduledEventUsers; - getApplicationCommand: typeof helpers.getApplicationCommand; - getApplicationCommandPermission: typeof helpers.getApplicationCommandPermission; - getApplicationCommandPermissions: typeof helpers.getApplicationCommandPermissions; - getApplicationCommands: typeof helpers.getApplicationCommands; - getStageInstance: typeof helpers.getStageInstance; - getTemplate: typeof helpers.getTemplate; - getUser: typeof helpers.getUser; - getApplicationInfo: typeof helpers.getApplicationInfo; - getVanityURL: typeof helpers.getVanityURL; - getVoiceRegions: typeof helpers.getVoiceRegions; - getWebhook: typeof helpers.getWebhook; - getWebhookMessage: typeof helpers.getWebhookMessage; - getWebhooks: typeof helpers.getWebhooks; - getWebhookWithToken: typeof helpers.getWebhookWithToken; - getWelcomeScreen: typeof helpers.getWelcomeScreen; - getWidget: typeof helpers.getWidget; - getWidgetImageURL: typeof helpers.getWidgetImageURL; - getWidgetSettings: typeof helpers.getWidgetSettings; - guildBannerURL: typeof helpers.guildBannerURL; - guildIconURL: typeof helpers.guildIconURL; - guildSplashURL: typeof helpers.guildSplashURL; - kickMember: typeof helpers.kickMember; - leaveGuild: typeof helpers.leaveGuild; - moveMember: typeof helpers.moveMember; - pinMessage: typeof helpers.pinMessage; - pruneMembers: typeof helpers.pruneMembers; - publishMessage: typeof helpers.publishMessage; - removeAllReactions: typeof helpers.removeAllReactions; - removeDiscoverySubcategory: typeof helpers.removeDiscoverySubcategory; - removeReaction: typeof helpers.removeReaction; - removeReactionEmoji: typeof helpers.removeReactionEmoji; - removeRole: typeof helpers.removeRole; - getDmChannel: typeof helpers.getDmChannel; - sendInteractionResponse: typeof helpers.sendInteractionResponse; - sendMessage: typeof helpers.sendMessage; - sendWebhook: typeof helpers.sendWebhook; - startTyping: typeof helpers.startTyping; - swapChannels: typeof helpers.swapChannels; - syncGuildTemplate: typeof helpers.syncGuildTemplate; - unbanMember: typeof helpers.unbanMember; - unpinMessage: typeof helpers.unpinMessage; - updateBotVoiceState: typeof helpers.updateBotVoiceState; - updateStageInstance: typeof helpers.updateStageInstance; - upsertApplicationCommand: typeof helpers.upsertApplicationCommand; - upsertApplicationCommands: typeof helpers.upsertApplicationCommands; - validDiscoveryTerm: typeof helpers.validDiscoveryTerm; - addToThread: typeof helpers.addToThread; - deleteThread: typeof helpers.deleteThread; - editThread: typeof helpers.editThread; - getActiveThreads: typeof helpers.getActiveThreads; - getArchivedThreads: typeof helpers.getArchivedThreads; - getThreadMember: typeof helpers.getThreadMember; - getThreadMembers: typeof helpers.getThreadMembers; - joinThread: typeof helpers.joinThread; - leaveThread: typeof helpers.leaveThread; - removeThreadMember: typeof helpers.removeThreadMember; - startThreadWithoutMessage: typeof helpers.startThreadWithoutMessage; - startThreadWithMessage: typeof helpers.startThreadWithMessage; -} +export const defaultHelpers = { ...helpers }; +export type DefaultHelpers = typeof defaultHelpers; +// deno-lint-ignore no-empty-interface +export interface Helpers extends DefaultHelpers {} // Use interface for declaration merging export function createHelpers(bot: Bot, customHelpers?: Partial): FinalHelpers { const converted = {} as FinalHelpers; @@ -613,159 +449,8 @@ export function createHelpers(bot: Bot, customHelpers?: Partial): Final export function createBaseHelpers(options: Partial) { return { - addDiscoverySubcategory: options.addDiscoverySubcategory || helpers.addDiscoverySubcategory, - addReaction: options.addReaction || helpers.addReaction, - addReactions: options.addReactions || helpers.addReactions, - addRole: options.addRole || helpers.addRole, - avatarURL: options.avatarURL || helpers.avatarURL, - banMember: options.banMember || helpers.banMember, - batchEditApplicationCommandPermissions: - options.batchEditApplicationCommandPermissions || helpers.batchEditApplicationCommandPermissions, - channelOverwriteHasPermission: options.channelOverwriteHasPermission || helpers.channelOverwriteHasPermission, - cloneChannel: options.cloneChannel || helpers.cloneChannel, - connectToVoiceChannel: options.connectToVoiceChannel || helpers.connectToVoiceChannel, - createChannel: options.createChannel || helpers.createChannel, - createEmoji: options.createEmoji || helpers.createEmoji, - createGuild: options.createGuild || helpers.createGuild, - createGuildFromTemplate: options.createGuildFromTemplate || helpers.createGuildFromTemplate, - createGuildTemplate: options.createGuildTemplate || helpers.createGuildTemplate, - createInvite: options.createInvite || helpers.createInvite, - createRole: options.createRole || helpers.createRole, - createScheduledEvent: options.createScheduledEvent || helpers.createScheduledEvent, - createApplicationCommand: options.createApplicationCommand || helpers.createApplicationCommand, - createStageInstance: options.createStageInstance || helpers.createStageInstance, - createWebhook: options.createWebhook || helpers.createWebhook, - deleteChannel: options.deleteChannel || helpers.deleteChannel, - deleteChannelOverwrite: options.deleteChannelOverwrite || helpers.deleteChannelOverwrite, - deleteEmoji: options.deleteEmoji || helpers.deleteEmoji, - deleteGuild: options.deleteGuild || helpers.deleteGuild, - deleteGuildTemplate: options.deleteGuildTemplate || helpers.deleteGuildTemplate, - deleteIntegration: options.deleteIntegration || helpers.deleteIntegration, - deleteInvite: options.deleteInvite || helpers.deleteInvite, - deleteMessage: options.deleteMessage || helpers.deleteMessage, - deleteMessages: options.deleteMessages || helpers.deleteMessages, - deleteRole: options.deleteRole || helpers.deleteRole, - deleteScheduledEvent: options.deleteScheduledEvent || helpers.deleteScheduledEvent, - deleteApplicationCommand: options.deleteApplicationCommand || helpers.deleteApplicationCommand, - deleteInteractionResponse: options.deleteInteractionResponse || helpers.deleteInteractionResponse, - deleteStageInstance: options.deleteStageInstance || helpers.deleteStageInstance, - deleteWebhook: options.deleteWebhook || helpers.deleteWebhook, - deleteWebhookMessage: options.deleteWebhookMessage || helpers.deleteWebhookMessage, - deleteWebhookWithToken: options.deleteWebhookWithToken || helpers.deleteWebhookWithToken, - disconnectMember: options.disconnectMember || helpers.disconnectMember, - editBotNickname: options.editBotNickname || helpers.editBotNickname, - editBotProfile: options.editBotProfile || helpers.editBotProfile, - editBotStatus: options.editBotStatus || helpers.editBotStatus, - editChannel: options.editChannel || helpers.editChannel, - editChannelOverwrite: options.editChannelOverwrite || helpers.editChannelOverwrite, - editDiscovery: options.editDiscovery || helpers.editDiscovery, - editEmoji: options.editEmoji || helpers.editEmoji, - editGuild: options.editGuild || helpers.editGuild, - editGuildTemplate: options.editGuildTemplate || helpers.editGuildTemplate, - editMember: options.editMember || helpers.editMember, - editMessage: options.editMessage || helpers.editMessage, - editRole: options.editRole || helpers.editRole, - editScheduledEvent: options.editScheduledEvent || helpers.editScheduledEvent, - editInteractionResponse: options.editInteractionResponse || helpers.editInteractionResponse, - editApplicationCommandPermissions: - options.editApplicationCommandPermissions || helpers.editApplicationCommandPermissions, - editWebhook: options.editWebhook || helpers.editWebhook, - editWebhookMessage: options.editWebhookMessage || helpers.editWebhookMessage, - editWebhookWithToken: options.editWebhookWithToken || helpers.editWebhookWithToken, - editWelcomeScreen: options.editWelcomeScreen || helpers.editWelcomeScreen, - editWidget: options.editWidget || helpers.editWidget, - emojiUrl: options.emojiUrl || helpers.emojiUrl, - fetchMembers: options.fetchMembers || helpers.fetchMembers, - followChannel: options.followChannel || helpers.followChannel, - getAuditLogs: options.getAuditLogs || helpers.getAuditLogs, - getAvailableVoiceRegions: options.getAvailableVoiceRegions || helpers.getAvailableVoiceRegions, - getBan: options.getBan || helpers.getBan, - getBans: options.getBans || helpers.getBans, - getChannel: options.getChannel || helpers.getChannel, - getChannelInvites: options.getChannelInvites || helpers.getChannelInvites, - getChannels: options.getChannels || helpers.getChannels, - getChannelWebhooks: options.getChannelWebhooks || helpers.getChannelWebhooks, - getDiscoveryCategories: options.getDiscoveryCategories || helpers.getDiscoveryCategories, - getEmoji: options.getEmoji || helpers.getEmoji, - getEmojis: options.getEmojis || helpers.getEmojis, - getGatewayBot: options.getGatewayBot || helpers.getGatewayBot, - getGuild: options.getGuild || helpers.getGuild, - getGuildPreview: options.getGuildPreview || helpers.getGuildPreview, - getGuildTemplates: options.getGuildTemplates || helpers.getGuildTemplates, - getIntegrations: options.getIntegrations || helpers.getIntegrations, - getInvite: options.getInvite || helpers.getInvite, - getInvites: options.getInvites || helpers.getInvites, - getMember: options.getMember || helpers.getMember, - getMembers: options.getMembers || helpers.getMembers, - getMessage: options.getMessage || helpers.getMessage, - getMessages: options.getMessages || helpers.getMessages, - getOriginalInteractionResponse: options.getOriginalInteractionResponse || helpers.getOriginalInteractionResponse, - getPins: options.getPins || helpers.getPins, - getPruneCount: options.getPruneCount || helpers.getPruneCount, - getReactions: options.getReactions || helpers.getReactions, - getRoles: options.getRoles || helpers.getRoles, - getScheduledEvent: options.getScheduledEvent || helpers.getScheduledEvent, - getScheduledEventUsers: options.getScheduledEventUsers || helpers.getScheduledEventUsers, - getScheduledEvents: options.getScheduledEvents || helpers.getScheduledEvents, - getApplicationCommand: options.getApplicationCommand || helpers.getApplicationCommand, - getApplicationCommandPermission: options.getApplicationCommandPermission || helpers.getApplicationCommandPermission, - getApplicationCommandPermissions: - options.getApplicationCommandPermissions || helpers.getApplicationCommandPermissions, - getApplicationCommands: options.getApplicationCommands || helpers.getApplicationCommands, - getStageInstance: options.getStageInstance || helpers.getStageInstance, - getTemplate: options.getTemplate || helpers.getTemplate, - getUser: options.getUser || helpers.getUser, - getApplicationInfo: options.getApplicationInfo || helpers.getApplicationInfo, - getVanityURL: options.getVanityURL || helpers.getVanityURL, - getVoiceRegions: options.getVoiceRegions || helpers.getVoiceRegions, - getWebhook: options.getWebhook || helpers.getWebhook, - getWebhookMessage: options.getWebhookMessage || helpers.getWebhookMessage, - getWebhooks: options.getWebhooks || helpers.getWebhooks, - getWebhookWithToken: options.getWebhookWithToken || helpers.getWebhookWithToken, - getWelcomeScreen: options.getWelcomeScreen || helpers.getWelcomeScreen, - getWidget: options.getWidget || helpers.getWidget, - getWidgetImageURL: options.getWidgetImageURL || helpers.getWidgetImageURL, - getWidgetSettings: options.getWidgetSettings || helpers.getWidgetSettings, - guildBannerURL: options.guildBannerURL || helpers.guildBannerURL, - guildIconURL: options.guildIconURL || helpers.guildIconURL, - guildSplashURL: options.guildSplashURL || helpers.guildSplashURL, - kickMember: options.kickMember || helpers.kickMember, - leaveGuild: options.leaveGuild || helpers.leaveGuild, - moveMember: options.moveMember || helpers.moveMember, - pinMessage: options.pinMessage || helpers.pinMessage, - pruneMembers: options.pruneMembers || helpers.pruneMembers, - publishMessage: options.publishMessage || helpers.publishMessage, - removeAllReactions: options.removeAllReactions || helpers.removeAllReactions, - removeDiscoverySubcategory: options.removeDiscoverySubcategory || helpers.removeDiscoverySubcategory, - removeReaction: options.removeReaction || helpers.removeReaction, - removeReactionEmoji: options.removeReactionEmoji || helpers.removeReactionEmoji, - removeRole: options.removeRole || helpers.removeRole, - getDmChannel: options.getDmChannel || helpers.getDmChannel, - sendInteractionResponse: options.sendInteractionResponse || helpers.sendInteractionResponse, - sendMessage: options.sendMessage || helpers.sendMessage, - sendWebhook: options.sendWebhook || helpers.sendWebhook, - startTyping: options.startTyping || helpers.startTyping, - swapChannels: options.swapChannels || helpers.swapChannels, - syncGuildTemplate: options.syncGuildTemplate || helpers.syncGuildTemplate, - unbanMember: options.unbanMember || helpers.unbanMember, - unpinMessage: options.unpinMessage || helpers.unpinMessage, - updateBotVoiceState: options.updateBotVoiceState || helpers.updateBotVoiceState, - updateStageInstance: options.updateStageInstance || helpers.updateStageInstance, - upsertApplicationCommand: options.upsertApplicationCommand || helpers.upsertApplicationCommand, - upsertApplicationCommands: options.upsertApplicationCommands || helpers.upsertApplicationCommands, - validDiscoveryTerm: options.validDiscoveryTerm || helpers.validDiscoveryTerm, - addToThread: options.addToThread || helpers.addToThread, - deleteThread: options.deleteThread || helpers.deleteThread, - editThread: options.editThread || helpers.editThread, - getActiveThreads: options.getActiveThreads || helpers.getActiveThreads, - getArchivedThreads: options.getArchivedThreads || helpers.getArchivedThreads, - getThreadMember: options.getThreadMember || helpers.getThreadMember, - getThreadMembers: options.getThreadMembers || helpers.getThreadMembers, - joinThread: options.joinThread || helpers.joinThread, - leaveThread: options.leaveThread || helpers.leaveThread, - removeThreadMember: options.removeThreadMember || helpers.removeThreadMember, - startThreadWithoutMessage: options.startThreadWithoutMessage || helpers.startThreadWithoutMessage, - startThreadWithMessage: options.startThreadWithMessage || helpers.startThreadWithMessage, + ...defaultHelpers, + ...options, }; } @@ -791,9 +476,14 @@ export interface Transformers { component: typeof transformComponent; webhook: typeof transformWebhook; auditlogEntry: typeof transformAuditlogEntry; + applicationCommand: typeof transformApplicationCommand; + applicationCommandOption: typeof transformApplicationCommandOption; applicationCommandPermission: typeof transformApplicationCommandPermission; scheduledEvent: typeof transformScheduledEvent; threadMember: typeof transformThreadMember; + welcomeScreen: typeof transformWelcomeScreen; + voiceRegion: typeof transformVoiceRegion; + widget: typeof transformWidget; } export function createTransformers(options: Partial) { @@ -819,9 +509,14 @@ export function createTransformers(options: Partial) { snowflake: options.snowflake || snowflakeToBigint, webhook: options.webhook || transformWebhook, auditlogEntry: options.auditlogEntry || transformAuditlogEntry, + applicationCommand: options.applicationCommand || transformApplicationCommand, + applicationCommandOption: options.applicationCommandOption || transformApplicationCommandOption, applicationCommandPermission: options.applicationCommandPermission || transformApplicationCommandPermission, scheduledEvent: options.scheduledEvent || transformScheduledEvent, threadMember: options.threadMember || transformThreadMember, + welcomeScreen: options.welcomeScreen || transformWelcomeScreen, + voiceRegion: options.voiceRegion || transformVoiceRegion, + widget: options.widget || transformWidget, }; } @@ -843,9 +538,9 @@ export interface GatewayManager { /** Whether or not the resharder should automatically switch to LARGE BOT SHARDING when you are above 100K servers. */ useOptimalLargeBotSharding: boolean; /** The amount of shards to load per worker. */ - shardsPerCluster: number; + shardsPerWorker: number; /** The maximum amount of workers to use for your bot. */ - maxClusters: number; + maxWorkers: number; /** The first shard Id to start spawning. */ firstShardId: number; /** The last shard Id for this worker. */ @@ -880,7 +575,6 @@ export interface GatewayManager { { shardId: number; resolve: (value: unknown) => void; - startedAt: number; } >; /** Stored as bucketId: { workers: [workerId, [ShardIds]], createNextShard: boolean } */ @@ -901,6 +595,8 @@ export interface GatewayManager { // METHODS + /** Prepares the buckets for identifying */ + prepareBuckets: typeof prepareBuckets; /** The handler for spawning ALL the shards. */ spawnShards: typeof spawnShards; /** Create the websocket and adds the proper handlers to the websocket. */ @@ -912,7 +608,7 @@ export interface GatewayManager { /** Sends the discord payload to another server. */ handleDiscordPayload: (gateway: GatewayManager, data: GatewayPayload, shardId: number) => any; /** Tell the worker to begin identifying this shard */ - tellClusterToIdentify: typeof tellClusterToIdentify; + tellWorkerToIdentify: typeof tellWorkerToIdentify; /** Handle the different logs. Used for debugging. */ debug: (text: string, ...args: any[]) => unknown; /** Handles resharding the bot when necessary. */ diff --git a/src/cache.ts b/src/cache.ts deleted file mode 100644 index a7b1b834b..000000000 --- a/src/cache.ts +++ /dev/null @@ -1,296 +0,0 @@ -import type { Bot } from "./bot.ts"; -import type { DiscordenoChannel } from "./transformers/channel.ts"; -import type { DiscordenoGuild } from "./transformers/guild.ts"; -import type { DiscordenoMember, DiscordenoUser } from "./transformers/member.ts"; -import type { DiscordenoMessage } from "./transformers/message.ts"; -import { DiscordenoPresence } from "./transformers/presence.ts"; -import { GuildMember } from "./types/members/guildMember.ts"; -import { Collection } from "./util/collection.ts"; - -function messageSweeper(bot: Bot, message: DiscordenoMessage) { - // DM messages aren't needed - if (!message.guildId) return true; - - // Only delete messages older than 10 minutes - return Date.now() - message.timestamp > 600000; -} - -function memberSweeper(bot: Bot, member: DiscordenoMember) { - // Don't sweep the bot else strange things will happen - if (member.id === bot.id) return false; - - // Only sweep members who were not active the last 30 minutes - return Date.now() - member.cachedAt > 1800000; -} - -function guildSweeper(bot: Bot, guild: DiscordenoGuild) { - // Reset activity for next interval - if (bot.cache.activeGuildIds.delete(guild.id)) return false; - - // This is inactive guild. Not a single thing has happened for atleast 30 minutes. - // Not a reaction, not a message, not any event! - bot.cache.dispatchedGuildIds.add(guild.id); - - return true; -} - -function channelSweeper(bot: Bot, channel: DiscordenoChannel, key: bigint) { - // If this is in a guild and the guild was dispatched, then we can dispatch the channel - if (channel.guildId && bot.cache.dispatchedGuildIds.has(channel.guildId)) { - bot.cache.dispatchedChannelIds.add(channel.id); - return true; - } - - // THE KEY DM CHANNELS ARE STORED BY IS THE USER ID. If the user is not cached, we dont need to cache their dm channel. - if (!channel.guildId && !bot.cache.members.has(key)) return true; - - return false; -} - -export function createCache( - bot: Bot, - options: { - isAsync: true; - tableCreator: (bot: Bot, tableName: TableNames) => AsyncCacheHandler; - } -): AsyncCache; -export function createCache( - bot: Bot, - options: { - isAsync: false; - tableCreator?: (bot: Bot, tableName: TableNames) => CacheHandler; - } -): Cache; -export function createCache( - bot: Bot, - options: { - isAsync: boolean; - tableCreator?: (bot: Bot, tableName: TableNames) => CacheHandler | AsyncCacheHandler; - } -): Omit | Omit { - let cache: Cache | AsyncCache; - - if (options.isAsync) { - if (!options.tableCreator) { - throw new Error("Async cache requires a tableCreator to be passed."); - } - - cache = { - guilds: options.tableCreator(bot, "guilds"), - users: options.tableCreator(bot, "users"), - members: options.tableCreator(bot, "members"), - channels: options.tableCreator(bot, "channels"), - messages: options.tableCreator(bot, "messages"), - presences: options.tableCreator(bot, "presences"), - // threads: options.tableCreator(bot, "threads"), - unavailableGuilds: options.tableCreator(bot, "unavailableGuilds"), - dispatchedGuildIds: options.tableCreator(bot, "dispatchedGuildIds"), - dispatchedChannelIds: options.tableCreator(bot, "dispatchedChannelIds"), - activeGuildIds: options.tableCreator(bot, "activeGuildIds"), - unrepliedInteractions: new Set(), - fetchAllMembersProcessingRequests: new Map(), - execute: async function () { - throw new Error("Async Cache requires a custom execute function to be implemented."); - }, - } as AsyncCache; - } else { - if (!options.tableCreator) options.tableCreator = createTable; - - cache = { - guilds: options.tableCreator(bot, "guilds"), - users: options.tableCreator(bot, "users"), - members: options.tableCreator(bot, "members"), - channels: options.tableCreator(bot, "channels"), - messages: options.tableCreator(bot, "messages"), - presences: options.tableCreator(bot, "presences"), - // threads: options.tableCreator(bot, "threads"), - unavailableGuilds: options.tableCreator(bot, "unavailableGuilds"), - dispatchedGuildIds: new Set(), - dispatchedChannelIds: new Set(), - activeGuildIds: new Set(), - unrepliedInteractions: new Set(), - fetchAllMembersProcessingRequests: new Map(), - } as Cache; - - cache.execute = createExecute(cache); - } - - // Interaction sweeper in case users don't reply do slash commands - // PS: always reply .-. its good practise - // setInterval(() => { - // const values = cache.unrepliedInteractions.values(); - // const now = Date.now(); - // for (let val; (val = values.next().value); ) { - // // Interaction is older than 15 minutes - // // and a reply has never been send - // // so remove it from cache - // // PS: DON'T USE THIS CODE TO CONVERT DC SNOWFLAKES TO UNIX - // // SINCE U WILL GET AN INVALID RESULT - // if ((val >> 22n) + 1420071300000n < now) { - // cache.unrepliedInteractions.delete(val); - // } - // } - // }, 300000); - - return cache; -} - -export type CachedDiscordenoUser = DiscordenoUser & { guilds: Map }; - -export interface Cache { - guilds: CacheHandler; - users: CacheHandler; - members: CacheHandler; - channels: CacheHandler; - messages: CacheHandler; - presences: CacheHandler; - // threads: CacheHandler; - unavailableGuilds: CacheHandler; - dispatchedGuildIds: Set; - dispatchedChannelIds: Set; - activeGuildIds: Set; - unrepliedInteractions: Set; - fetchAllMembersProcessingRequests: Map; - execute: CacheExecutor; -} - -export interface CachedUnavailableGuild { - shardId: number; - since: number; - dispatched?: true; -} - -export interface AsyncCache { - guilds: AsyncCacheHandler; - users: AsyncCacheHandler; - members: CacheHandler; - channels: AsyncCacheHandler; - messages: AsyncCacheHandler; - presences: AsyncCacheHandler; - // threads: AsyncCacheHandler; - unavailableGuilds: AsyncCacheHandler; - dispatchedGuildIds: AsyncCacheHandler; - dispatchedChannelIds: AsyncCacheHandler; - activeGuildIds: AsyncCacheHandler; - unrepliedInteractions: Set; - fetchAllMembersProcessingRequests: Map; - execute: CacheExecutor; -} - -function createTable(bot: Bot, _table: TableNames): CacheHandler { - const table = new Collection(); - - // @ts-ignore TODO: fix type error itoh pwease - if (_table === "guilds") table.startSweeper({ filter: guildSweeper, interval: 3660000, bot }); - // @ts-ignore TODO: fix type error itoh pwease - if (_table === "channels") table.startSweeper({ filter: channelSweeper, interval: 3660000, bot }); - // @ts-ignore TODO: fix type error itoh pwease - if (_table === "messages") table.startSweeper({ filter: messageSweeper, interval: 300000, bot }); - // @ts-ignore TODO: fix type error itoh pwease - if (_table === "members") table.startSweeper({ filter: memberSweeper, interval: 300000, bot }); - if (_table === "presences") table.startSweeper({ filter: () => true, interval: 300000, bot }); - - return { - clear: () => table.clear(), - delete: (key) => table.delete(key), - has: (key) => table.has(key), - size: () => table.size, - set: (key, data) => !!table.set(key, data), - get: (key) => table.get(key), - forEach: (callback) => table.forEach(callback), - filter: (callback) => table.filter(callback), - }; -} - -export interface CacheHandler { - /** Completely empty this table. */ - clear(): void; - /** Delete the data related to this key from table. */ - delete(key: bigint): boolean; - /** Check if there is data assigned to this key. */ - has(key: bigint): boolean; - /** Check how many items are stored in this table. */ - size(): number; - /** Store new data to this table. */ - set(key: bigint, data: T): boolean; - /** Get a stored item from the table. */ - get(key: bigint): T | undefined; - // TODO: maybe its possible to stringify the function and send it to the custom cache handler :thinking: - /** - * Loop over each entry and execute callback function. - * @important This function NOT optimised and will force load everything when using custom cache. - */ - forEach(callback: (value: T, key: bigint) => unknown): void; - // TODO: maybe its possible to stringify the function and send it to the custom cache handler :thinking: - /** - * Loop over each entry and execute callback function. - * @important This function NOT optimised and will force load everything when using custom cache. - */ - filter(callback: (value: T, key: bigint) => boolean): Collection; -} - -export type AsyncCacheHandler = { - [K in keyof CacheHandler]: (...args: Parameters[K]>) => Promise[K]>>; -}; - -export type CacheExecutor = ( - type: - | "GET_ALL_MEMBERS" - | "DELETE_MESSAGES_FROM_CHANNEL" - | "DELETE_ROLE_FROM_MEMBER" - | "BULK_DELETE_MESSAGES" - | "GUILD_MEMBER_CHUNK" - | "GUILD_MEMBER_COUNT_DECREMENT" - | "GUILD_MEMBER_COUNT_INCREMENT" - | "DELETE_MESSAGES_FROM_GUILD" - | "DELETE_CHANNELS_FROM_GUILD" - | "DELETE_GUILD_FROM_MEMBER", - options: Record -) => Promise; - -export function createExecute(cache: Cache): CacheExecutor { - return function (type, options) { - switch (type) { - case "DELETE_MESSAGES_FROM_CHANNEL": - cache.messages.forEach((message) => { - if (message.channelId === options.channelId) { - cache.messages.delete(message.id); - } - }); - return; - case "BULK_DELETE_MESSAGES": - return options.messageIds - .map((id: bigint) => { - const cached = cache.messages.get(id); - if (!cached) return; - - cache.messages.delete(id); - - return cached; - }) - .filter((m: DiscordenoMessage) => m); - case "GUILD_MEMBER_CHUNK": - options.users.forEach((user: DiscordenoUser) => { - cache.users.set(user.id, user); - }); - // TODO: FIND A GOOD WAY FOR MEMBERS CACHE (GUILD ID) - // options.members.forEach((member) => { - // cache.members.set(member.id, member); - // }); - return; - } - }; -} - -export type TableNames = - | "channels" - | "users" - | "guilds" - | "messages" - | "presences" - | "threads" - | "unavailableGuilds" - | "members" - | "dispatchedGuildIds" - | "dispatchedChannelIds" - | "activeGuildIds"; diff --git a/src/handlers/channels/THREAD_DELETE.ts b/src/handlers/channels/THREAD_DELETE.ts index 82e41d922..763b52218 100644 --- a/src/handlers/channels/THREAD_DELETE.ts +++ b/src/handlers/channels/THREAD_DELETE.ts @@ -1,5 +1,3 @@ -// import { eventHandlers } from "../../bot.ts"; -// import { cacheHandlers } from "../../cache.ts"; import { Channel } from "../../types/channels/channel.ts"; import { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import { snowflakeToBigint } from "../../util/bigint.ts"; diff --git a/src/handlers/channels/THREAD_LIST_SYNC.ts b/src/handlers/channels/THREAD_LIST_SYNC.ts index 407b2e4b5..7ef76ed53 100644 --- a/src/handlers/channels/THREAD_LIST_SYNC.ts +++ b/src/handlers/channels/THREAD_LIST_SYNC.ts @@ -1,11 +1,7 @@ -// import { eventHandlers } from "../../bot.ts"; -// import { cacheHandlers } from "../../cache.ts"; import { ThreadListSync } from "../../types/channels/threads/threadListSync.ts"; import { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import { snowflakeToBigint } from "../../util/bigint.ts"; -// import { channelToThread } from "../../util/transformers/channel_to_thread.ts"; import { Collection } from "../../util/collection.ts"; -// import { threadMemberModified } from "../../util/transformers/thread_member_modified.ts"; export async function handleThreadListSync(data: DiscordGatewayPayload) { // const payload = data.d as ThreadListSync; diff --git a/src/handlers/channels/THREAD_MEMBERS_UPDATE.ts b/src/handlers/channels/THREAD_MEMBERS_UPDATE.ts index ef2a7a139..bb6649510 100644 --- a/src/handlers/channels/THREAD_MEMBERS_UPDATE.ts +++ b/src/handlers/channels/THREAD_MEMBERS_UPDATE.ts @@ -1,9 +1,6 @@ -// import { eventHandlers } from "../../bot.ts"; -// import { cacheHandlers } from "../../cache.ts"; import { ThreadMembersUpdate } from "../../types/channels/threads/threadMembersUpdate.ts"; import { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import { snowflakeToBigint } from "../../util/bigint.ts"; -// import { threadMembersUpdateModified } from "../../util/transformers/thread_members_update_modified.ts"; export async function handleThreadMembersUpdate(data: DiscordGatewayPayload) { // const payload = data.d as ThreadMembersUpdate; diff --git a/src/handlers/channels/THREAD_MEMBER_UPDATE.ts b/src/handlers/channels/THREAD_MEMBER_UPDATE.ts index be696bb7e..52dd7877c 100644 --- a/src/handlers/channels/THREAD_MEMBER_UPDATE.ts +++ b/src/handlers/channels/THREAD_MEMBER_UPDATE.ts @@ -1,5 +1,3 @@ -// import { eventHandlers } from "../../bot.ts"; -// import { cacheHandlers } from "../../cache.ts"; import { ThreadMember } from "../../types/channels/threads/threadMember.ts"; import { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import { snowflakeToBigint } from "../../util/bigint.ts"; diff --git a/src/handlers/channels/THREAD_UPDATE.ts b/src/handlers/channels/THREAD_UPDATE.ts index 41a61727a..d46e98153 100644 --- a/src/handlers/channels/THREAD_UPDATE.ts +++ b/src/handlers/channels/THREAD_UPDATE.ts @@ -1,9 +1,6 @@ -// import { eventHandlers } from "../../bot.ts"; -// import { cacheHandlers } from "../../cache.ts"; import { Channel } from "../../types/channels/channel.ts"; import { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import { snowflakeToBigint } from "../../util/bigint.ts"; -// import { channelToThread } from "../../util/transformers/channel_to_thread.ts"; export async function handleThreadUpdate(data: DiscordGatewayPayload) { // const payload = data.d as Channel; diff --git a/src/handlers/members/GUILD_MEMBER_ADD.ts b/src/handlers/members/GUILD_MEMBER_ADD.ts index 68e5aa38d..7ef44c978 100644 --- a/src/handlers/members/GUILD_MEMBER_ADD.ts +++ b/src/handlers/members/GUILD_MEMBER_ADD.ts @@ -8,12 +8,5 @@ export async function handleGuildMemberAdd(bot: Bot, data: DiscordGatewayPayload const guildId = bot.transformers.snowflake(payload.guild_id); const user = bot.transformers.user(bot, payload.user); const member = bot.transformers.member(bot, payload, guildId, user.id); - - await Promise.all([ - bot.cache.members.set(member.id, member), - bot.cache.users.set(user.id, user), - bot.cache.execute("GUILD_MEMBER_COUNT_INCREMENT", { guildId }), - ]); - bot.events.guildMemberAdd(bot, member, user); } diff --git a/src/handlers/members/GUILD_MEMBER_REMOVE.ts b/src/handlers/members/GUILD_MEMBER_REMOVE.ts index 3d1b6e39d..9d1eab357 100644 --- a/src/handlers/members/GUILD_MEMBER_REMOVE.ts +++ b/src/handlers/members/GUILD_MEMBER_REMOVE.ts @@ -2,17 +2,11 @@ import { Bot } from "../../bot.ts"; import type { DiscordGatewayPayload } from "../../types/gateway/gatewayPayload.ts"; import type { GuildMemberRemove } from "../../types/members/guildMemberRemove.ts"; import { SnakeCasedPropertiesDeep } from "../../types/util.ts"; -import { snowflakeToBigint } from "../../util/bigint.ts"; export async function handleGuildMemberRemove(bot: Bot, data: DiscordGatewayPayload) { const payload = data.d as SnakeCasedPropertiesDeep; const guildId = bot.transformers.snowflake(payload.guild_id); const user = bot.transformers.user(bot, payload.user); - await Promise.all([ - bot.cache.members.delete(user.id), - bot.cache.execute("GUILD_MEMBER_COUNT_DECREMENT", { guildId }), - ]); - bot.events.guildMemberRemove(bot, user, guildId); } diff --git a/src/helpers/channels/channelOverwriteHasPermission.ts b/src/helpers/channels/channelOverwriteHasPermission.ts deleted file mode 100644 index 429d6aeb0..000000000 --- a/src/helpers/channels/channelOverwriteHasPermission.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { separateOverwrites } from "../../transformers/channel.ts"; -import type { DiscordOverwrite } from "../../types/channels/overwrite.ts"; -import { BitwisePermissionFlags } from "../../types/permissions/bitwisePermissionFlags.ts"; -import type { PermissionStrings } from "../../types/permissions/permissionStrings.ts"; - -/** Checks if a channel overwrite for a user id or a role id has permission in this channel */ -export function channelOverwriteHasPermission( - guildId: bigint, - id: bigint, - overwrites: bigint[], - permissions: PermissionStrings[] -) { - const overwrite = - overwrites.find((perm) => { - const [_, bitID] = separateOverwrites(perm); - return id === bitID; - }) || - overwrites.find((perm) => { - const [_, bitID] = separateOverwrites(perm); - return bitID === guildId; - }); - - if (!overwrite) return false; - - return permissions.every((perm) => { - const [type, id, allowBits, denyBits] = separateOverwrites(overwrite); - if (BigInt(denyBits) & BigInt(BitwisePermissionFlags[perm])) { - return false; - } - if (BigInt(allowBits) & BigInt(BitwisePermissionFlags[perm])) { - return true; - } - }); -} diff --git a/src/helpers/channels/cloneChannel.ts b/src/helpers/channels/cloneChannel.ts deleted file mode 100644 index 5eb14825a..000000000 --- a/src/helpers/channels/cloneChannel.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { Bot } from "../../bot.ts"; -import { DiscordenoChannel, separateOverwrites } from "../../transformers/channel.ts"; -import type { CreateGuildChannel } from "../../types/guilds/createGuildChannel.ts"; - -/** Create a copy of a channel */ -export async function cloneChannel(bot: Bot, channel: DiscordenoChannel, reason?: string) { - if (!channel.guildId) throw new Error(`Cannot clone a channel outside a guild`); - - const createChannelOptions: CreateGuildChannel = { - type: channel.type, - bitrate: channel.bitrate, - userLimit: channel.userLimit, - rateLimitPerUser: channel.rateLimitPerUser, - position: channel.position, - parentId: channel.parentId, - nsfw: channel.nsfw, - name: channel.name!, - topic: channel.topic || undefined, - permissionOverwrites: channel.permissionOverwrites.map((overwrite) => { - const [type, id, allow, deny] = separateOverwrites(overwrite); - - return { - id, - type, - allow: bot.utils.calculatePermissions(BigInt(allow)), - deny: bot.utils.calculatePermissions(BigInt(deny)), - }; - }), - }; - - //Create the channel (also handles permissions) - return await bot.helpers.createChannel(channel.guildId!, createChannelOptions, reason); -} diff --git a/src/helpers/channels/createStageInstance.ts b/src/helpers/channels/createStageInstance.ts index abbedc881..685d6249b 100644 --- a/src/helpers/channels/createStageInstance.ts +++ b/src/helpers/channels/createStageInstance.ts @@ -4,10 +4,6 @@ import { PrivacyLevel } from "../../types/channels/privacyLevel.ts"; /** Creates a new Stage instance associated to a Stage channel. Requires the user to be a moderator of the Stage channel. */ export async function createStageInstance(bot: Bot, channelId: bigint, topic: string, privacyLevel?: PrivacyLevel) { - if (!bot.utils.validateLength(topic, { max: 120, min: 1 })) { - throw new Error(bot.constants.Errors.INVALID_TOPIC_LENGTH); - } - const result = await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.STAGE_INSTANCES, { channel_id: channelId.toString(), topic, diff --git a/src/helpers/channels/deleteChannel.ts b/src/helpers/channels/deleteChannel.ts index 3766d4e80..d047582f4 100644 --- a/src/helpers/channels/deleteChannel.ts +++ b/src/helpers/channels/deleteChannel.ts @@ -1,8 +1,14 @@ import type { Bot } from "../../bot.ts"; +import { Channel } from "../../types/channels/channel.ts"; -/** Delete a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ +/** Delete a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. Bot needs MANAGE_THREADS permissions in the server if deleting thread. */ export async function deleteChannel(bot: Bot, channelId: bigint, reason?: string) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.CHANNEL_BASE(channelId), { - reason, - }); + await bot.rest.runMethod( + bot.rest, + "delete", + bot.constants.endpoints.CHANNEL_BASE(channelId), + { + reason, + } + ); } diff --git a/src/helpers/channels/deleteChannelOverwrite.ts b/src/helpers/channels/deleteChannelOverwrite.ts index e3adb4ffa..fa0298d85 100644 --- a/src/helpers/channels/deleteChannelOverwrite.ts +++ b/src/helpers/channels/deleteChannelOverwrite.ts @@ -1,8 +1,8 @@ import type { Bot } from "../../bot.ts"; /** Delete the channel permission overwrites for a user or role in this channel. Requires `MANAGE_ROLES` permission. */ -export async function deleteChannelOverwrite(bot: Bot, channelId: bigint, overwriteId: bigint): Promise { - return await bot.rest.runMethod( +export async function deleteChannelOverwrite(bot: Bot, channelId: bigint, overwriteId: bigint) { + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.CHANNEL_OVERWRITE(channelId, overwriteId) diff --git a/src/helpers/channels/deleteStageInstance.ts b/src/helpers/channels/deleteStageInstance.ts index b2594e572..f359f2088 100644 --- a/src/helpers/channels/deleteStageInstance.ts +++ b/src/helpers/channels/deleteStageInstance.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Deletes the Stage instance. Requires the user to be a moderator of the Stage channel. */ export async function deleteStageInstance(bot: Bot, channelId: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.STAGE_INSTANCE(channelId)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.STAGE_INSTANCE(channelId)); } diff --git a/src/helpers/channels/editChannel.ts b/src/helpers/channels/editChannel.ts index 68b3fc494..063d8ae94 100644 --- a/src/helpers/channels/editChannel.ts +++ b/src/helpers/channels/editChannel.ts @@ -35,13 +35,17 @@ export async function editChannel(bot: Bot, channelId: bigint, options: ModifyCh name: options.name, topic: options.topic, bitrate: options.bitrate, - userLimit: options.userLimit, - rateLimitPerUser: options.rateLimitPerUser, + user_limit: options.userLimit, + rate_limit_per_user: options.rateLimitPerUser, position: options.position, - parentId: options.parentId, + parent_id: options.parentId === null ? null : options.parentId?.toString(), nsfw: options.nsfw, type: options.type, - permissionOverwrites: options.permissionOverwrites + archived: options.archived, + auto_archive_duration: options.autoArchiveDuration, + locked: options.locked, + invitable: options.invitable, + permission_overwrites: options.permissionOverwrites ? options.permissionOverwrites?.map((overwrite) => { return { ...overwrite, diff --git a/src/helpers/channels/editChannelOverwrite.ts b/src/helpers/channels/editChannelOverwrite.ts index 3fac722c7..b44bdb259 100644 --- a/src/helpers/channels/editChannelOverwrite.ts +++ b/src/helpers/channels/editChannelOverwrite.ts @@ -7,8 +7,8 @@ export async function editChannelOverwrite( channelId: bigint, overwriteId: bigint, options: Omit -): Promise { - return await bot.rest.runMethod( +) { + await bot.rest.runMethod( bot.rest, "put", bot.constants.endpoints.CHANNEL_OVERWRITE(channelId, overwriteId), diff --git a/src/helpers/channels/getChannel.ts b/src/helpers/channels/getChannel.ts index ea671e630..23f025acb 100644 --- a/src/helpers/channels/getChannel.ts +++ b/src/helpers/channels/getChannel.ts @@ -1,10 +1,7 @@ import type { Bot } from "../../bot.ts"; import type { Channel } from "../../types/channels/channel.ts"; -/** Fetches a single channel object from the api. - * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.** - */ +/** Fetches a single channel object from the api. */ export async function getChannel(bot: Bot, channelId: bigint) { const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.CHANNEL_BASE(channelId)); diff --git a/src/helpers/channels/getChannels.ts b/src/helpers/channels/getChannels.ts index 518ef6eb9..d196083c7 100644 --- a/src/helpers/channels/getChannels.ts +++ b/src/helpers/channels/getChannels.ts @@ -2,10 +2,7 @@ import type { Channel } from "../../types/channels/channel.ts"; import { Collection } from "../../util/collection.ts"; import type { Bot } from "../../bot.ts"; -/** Returns a list of guild channel objects. - * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.** - */ +/** Returns a list of guild channel objects. */ export async function getChannels(bot: Bot, guildId: bigint) { const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_CHANNELS(guildId)); diff --git a/src/helpers/channels/getStageInstance.ts b/src/helpers/channels/getStageInstance.ts index 6cded1faa..4f9d41c92 100644 --- a/src/helpers/channels/getStageInstance.ts +++ b/src/helpers/channels/getStageInstance.ts @@ -3,5 +3,16 @@ import type { Bot } from "../../bot.ts"; /** Gets the stage instance associated with the Stage channel, if it exists. */ export async function getStageInstance(bot: Bot, channelId: bigint) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.STAGE_INSTANCE(channelId)); + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.STAGE_INSTANCE(channelId) + ); + + return { + id: bot.transformers.snowflake(result.id), + guildId: bot.transformers.snowflake(result.guild_id), + channelId: bot.transformers.snowflake(result.channel_id), + topic: result.topic, + }; } diff --git a/src/helpers/channels/startTyping.ts b/src/helpers/channels/startTyping.ts index b33a21563..688c7b0eb 100644 --- a/src/helpers/channels/startTyping.ts +++ b/src/helpers/channels/startTyping.ts @@ -7,5 +7,5 @@ import type { Bot } from "../../bot.ts"; * this endpoint may be called to let the user know that the bot is processing their message. */ export async function startTyping(bot: Bot, channelId: bigint) { - return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.CHANNEL_TYPING(channelId)); + await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.CHANNEL_TYPING(channelId)); } diff --git a/src/helpers/channels/swapChannels.ts b/src/helpers/channels/swapChannels.ts index d6725d39d..aec44f2ea 100644 --- a/src/helpers/channels/swapChannels.ts +++ b/src/helpers/channels/swapChannels.ts @@ -1,13 +1,13 @@ import type { ModifyGuildChannelPositions } from "../../types/guilds/modifyGuildChannelPosition.ts"; import type { Bot } from "../../bot.ts"; -/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permission. */ +/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permission. Only channels to be modified are required. */ export async function swapChannels(bot: Bot, guildId: bigint, channelPositions: ModifyGuildChannelPositions[]) { if (!channelPositions.length) { throw "You must provide at least one channels to be moved."; } - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "patch", bot.constants.endpoints.GUILD_CHANNELS(guildId), diff --git a/src/helpers/channels/threads/addToThread.ts b/src/helpers/channels/threads/addToThread.ts index 05bc89af4..a146e48bc 100644 --- a/src/helpers/channels/threads/addToThread.ts +++ b/src/helpers/channels/threads/addToThread.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../../bot.ts"; /** Adds a user to a thread. Requires the ability to send messages in the thread. Requires the thread is not archived. */ export async function addToThread(bot: Bot, threadId: bigint, userId: bigint) { - return await bot.rest.runMethod(bot.rest, "put", bot.constants.endpoints.THREAD_USER(threadId, userId)); + await bot.rest.runMethod(bot.rest, "put", bot.constants.endpoints.THREAD_USER(threadId, userId)); } diff --git a/src/helpers/channels/threads/deleteThread.ts b/src/helpers/channels/threads/deleteThread.ts deleted file mode 100644 index d1876251f..000000000 --- a/src/helpers/channels/threads/deleteThread.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Bot } from "../../../bot.ts"; - -/** Delete a thread in your server. Bot needs MANAGE_THREADS permissions in the server. */ -export async function deleteThread(bot: Bot, threadId: bigint, reason?: string) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.CHANNEL_BASE(threadId), { - reason, - }); -} diff --git a/src/helpers/channels/threads/editThread.ts b/src/helpers/channels/threads/editThread.ts deleted file mode 100644 index 72c93811c..000000000 --- a/src/helpers/channels/threads/editThread.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ModifyThread } from "../../../types/channels/threads/modifyThread.ts"; -import type { Bot } from "../../../bot.ts"; -import { Channel } from "../../../types/channels/channel.ts"; -// import { channelToThread } from "../../../util/transformers/channel_to_thread.ts"; - -/** Update a thread's settings. Requires the `MANAGE_CHANNELS` permission for the guild. */ -export async function editThread(bot: Bot, threadId: bigint, options: ModifyThread, reason?: string) { - const result = await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.CHANNEL_BASE(threadId), { - name: options.name, - archived: options.archived, - auto_archive_duration: options.autoArchiveDuration, - locked: options.locked, - rate_limit_per_user: options.rateLimitPerUser, - reason, - }); - - return bot.transformers.channel(bot, { - channel: result, - guildId: result.guild_id ? bot.transformers.snowflake(result.guild_id) : undefined, - }); -} diff --git a/src/helpers/channels/threads/joinThread.ts b/src/helpers/channels/threads/joinThread.ts index 0a8ec5967..17429e042 100644 --- a/src/helpers/channels/threads/joinThread.ts +++ b/src/helpers/channels/threads/joinThread.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../../bot.ts"; /** Adds the bot to the thread. Cannot join an archived thread. */ export async function joinThread(bot: Bot, threadId: bigint) { - return await bot.rest.runMethod(bot.rest, "put", bot.constants.endpoints.THREAD_ME(threadId)); + await bot.rest.runMethod(bot.rest, "put", bot.constants.endpoints.THREAD_ME(threadId)); } diff --git a/src/helpers/channels/threads/leaveThread.ts b/src/helpers/channels/threads/leaveThread.ts index 6792dd31b..1fe3a611a 100644 --- a/src/helpers/channels/threads/leaveThread.ts +++ b/src/helpers/channels/threads/leaveThread.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../../bot.ts"; /** Removes the bot from a thread. Requires the thread is not archived. */ export async function leaveThread(bot: Bot, threadId: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.THREAD_ME(threadId)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.THREAD_ME(threadId)); } diff --git a/src/helpers/channels/threads/mod.ts b/src/helpers/channels/threads/mod.ts index 741b603d6..e348276f4 100644 --- a/src/helpers/channels/threads/mod.ts +++ b/src/helpers/channels/threads/mod.ts @@ -1,6 +1,4 @@ export * from "./addToThread.ts"; -export * from "./deleteThread.ts"; -export * from "./editThread.ts"; export * from "./getActiveThreads.ts"; export * from "./getArchivedThreads.ts"; export * from "./getThreadMembers.ts"; diff --git a/src/helpers/channels/threads/removeThreadMember.ts b/src/helpers/channels/threads/removeThreadMember.ts index 1dc30c5e8..3cd128a00 100644 --- a/src/helpers/channels/threads/removeThreadMember.ts +++ b/src/helpers/channels/threads/removeThreadMember.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../../bot.ts"; /** Removes a user from a thread. Requires the MANAGE_THREADS permission or that you are the creator of the thread. Also requires the thread is not archived. */ export async function removeThreadMember(bot: Bot, threadId: bigint, userId: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.THREAD_USER(threadId, userId)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.THREAD_USER(threadId, userId)); } diff --git a/src/helpers/channels/threads/startThreadWithoutMessage.ts b/src/helpers/channels/threads/startThreadWithoutMessage.ts index 8498239a0..94584f5ae 100644 --- a/src/helpers/channels/threads/startThreadWithoutMessage.ts +++ b/src/helpers/channels/threads/startThreadWithoutMessage.ts @@ -4,8 +4,18 @@ import type { Bot } from "../../../bot.ts"; /** Creates a new private thread. Returns a thread channel. */ export async function startThreadWithoutMessage(bot: Bot, channelId: bigint, options: StartThreadWithoutMessage) { - return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.THREAD_START_PRIVATE(channelId), { - name: options.name, - auto_archive_duration: options.autoArchiveDuration, + const result = await bot.rest.runMethod( + bot.rest, + "post", + bot.constants.endpoints.THREAD_START_PRIVATE(channelId), + { + name: options.name, + auto_archive_duration: options.autoArchiveDuration, + } + ); + + return bot.transformers.channel(bot, { + channel: result, + guildId: result.guild_id ? bot.transformers.snowflake(result.guild_id) : undefined, }); } diff --git a/src/helpers/channels/updateBotVoiceState.ts b/src/helpers/channels/updateBotVoiceState.ts deleted file mode 100644 index c03756206..000000000 --- a/src/helpers/channels/updateBotVoiceState.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { UpdateOthersVoiceState } from "../../types/guilds/updateOthersVoiceState.ts"; -import type { UpdateSelfVoiceState } from "../../types/guilds/updateSelfVoiceState.ts"; -import type { Bot } from "../../bot.ts"; - -/** - * Updates the a user's voice state, defaults to the current user - * Caveats: - * - `channel_id` must currently point to a stage channel. - * - User must already have joined `channel_id`. - * - You must have the `MUTE_MEMBERS` permission. But can always suppress yourself. - * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. - * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. - * - You are able to set `request_to_speak_timestamp` to any present or future time. - * - When suppressed, the user will have their `request_to_speak_timestamp` removed. - */ -export async function updateBotVoiceState( - bot: Bot, - guildId: bigint, - options: UpdateSelfVoiceState | ({ userId: bigint } & UpdateOthersVoiceState) -) { - return await bot.rest.runMethod( - bot.rest, - "patch", - bot.constants.endpoints.UPDATE_VOICE_STATE( - guildId, - bot.utils.hasProperty(options, "userId") ? options.userId : undefined - ), - { - channel_id: options.channelId, - suppress: options.suppress, - request_to_speak_timestamp: bot.utils.hasProperty(options, "requestToSpeakTimestamp") - ? options.requestToSpeakTimestamp - : undefined, - user_id: bot.utils.hasProperty(options, "userId") ? options.userId : undefined, - } - ); -} diff --git a/src/helpers/channels/updateStageInstance.ts b/src/helpers/channels/updateStageInstance.ts index ecd330794..9559840c9 100644 --- a/src/helpers/channels/updateStageInstance.ts +++ b/src/helpers/channels/updateStageInstance.ts @@ -4,16 +4,6 @@ import { AtLeastOne } from "../../types/util.ts"; /** Updates fields of an existing Stage instance. Requires the user to be a moderator of the Stage channel. */ export async function updateStageInstance(bot: Bot, channelId: bigint, data: AtLeastOne>) { - if ( - data.topic && - !bot.utils.validateLength(data.topic, { - min: 1, - max: 120, - }) - ) { - throw new Error(bot.constants.Errors.INVALID_TOPIC_LENGTH); - } - const result = await bot.rest.runMethod( bot.rest, "patch", diff --git a/src/helpers/channels/updateVoiceState.ts b/src/helpers/channels/updateVoiceState.ts new file mode 100644 index 000000000..55fb15f3b --- /dev/null +++ b/src/helpers/channels/updateVoiceState.ts @@ -0,0 +1,47 @@ +import type { UpdateOthersVoiceState } from "../../types/guilds/updateOthersVoiceState.ts"; +import type { UpdateSelfVoiceState } from "../../types/guilds/updateSelfVoiceState.ts"; +import type { Bot } from "../../bot.ts"; + +/** + * Updates the bot's voice state + * Caveats: + * - `channel_id` must currently point to a stage channel. + * - Bot must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission. But can always suppress yourself. + * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. + * - You are able to set `request_to_speak_timestamp` to any present or future time. + * - When suppressed, the user will have their `request_to_speak_timestamp` removed. + */ +export async function updateBotVoiceState(bot: Bot, guildId: bigint, options: UpdateSelfVoiceState) { + await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.UPDATE_VOICE_STATE(guildId), { + channel_id: options.channelId, + suppress: options.suppress, + request_to_speak_timestamp: options.requestToSpeakTimestamp + ? new Date(options.requestToSpeakTimestamp).toISOString() + : options.requestToSpeakTimestamp, + }); +} + +/** + * Updates the a user's voice state + * Caveats: + * - `channel_id` must currently point to a stage channel. + * - User must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission. But can always suppress yourself. + * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. + * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. + * - You are able to set `request_to_speak_timestamp` to any present or future time. + * - When suppressed, the user will have their `request_to_speak_timestamp` removed. + */ +export async function updateUserVoiceState(bot: Bot, guildId: bigint, options: UpdateOthersVoiceState) { + await bot.rest.runMethod( + bot.rest, + "patch", + bot.constants.endpoints.UPDATE_VOICE_STATE(guildId, options.userId), + { + channel_id: options.channelId, + suppress: options.suppress, + user_id: options.userId, + } + ); +} diff --git a/src/helpers/discovery/addDiscoverySubcategory.ts b/src/helpers/discovery/addDiscoverySubcategory.ts index 226bb6b57..0107c863b 100644 --- a/src/helpers/discovery/addDiscoverySubcategory.ts +++ b/src/helpers/discovery/addDiscoverySubcategory.ts @@ -3,7 +3,7 @@ import type { Bot } from "../../bot.ts"; /** Add a discovery subcategory to the guild. Requires the `MANAGE_GUILD` permission. */ export async function addDiscoverySubcategory(bot: Bot, guildId: bigint, categoryId: number) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "post", bot.constants.endpoints.DISCOVERY_SUBCATEGORY(guildId, categoryId) diff --git a/src/helpers/discovery/editDiscovery.ts b/src/helpers/discovery/editDiscovery.ts index 1480f29d6..f00027f24 100644 --- a/src/helpers/discovery/editDiscovery.ts +++ b/src/helpers/discovery/editDiscovery.ts @@ -4,7 +4,7 @@ import type { Bot } from "../../bot.ts"; /** Modify the discovery metadata for the guild. Requires the MANAGE_GUILD permission. Returns the updated discovery metadata object on success. */ export async function editDiscovery(bot: Bot, guildId: bigint, data: ModifyGuildDiscoveryMetadata) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "patch", bot.constants.endpoints.DISCOVERY_METADATA(guildId), @@ -14,4 +14,18 @@ export async function editDiscovery(bot: Bot, guildId: bigint, data: ModifyGuild emoji_discoverability_enabled: data.emojiDiscoverabilityEnabled, } ); + + return { + guildId, + primaryCategoryId: result.primary_category_id, + keywords: result.keywords ?? undefined, + emojiDiscoverabilityEnabled: result.emoji_discoverability_enabled, + partnerActionedTimestamp: result.partner_actioned_timestamp + ? Date.parse(result.partner_actioned_timestamp) + : undefined, + partnerApplicationTimestamp: result.partner_application_timestamp + ? Date.parse(result.partner_application_timestamp) + : undefined, + categoryIds: result.category_ids, + }; } diff --git a/src/helpers/discovery/getDiscovery.ts b/src/helpers/discovery/getDiscovery.ts new file mode 100644 index 000000000..3bcce91db --- /dev/null +++ b/src/helpers/discovery/getDiscovery.ts @@ -0,0 +1,25 @@ +import type { DiscoveryMetadata } from "../../types/discovery/discoveryMetadata.ts"; +import type { Bot } from "../../bot.ts"; + +/** Returns the discovery metadata object for the guild. Requires the `MANAGE_GUILD` permission. */ +export async function getDiscovery(bot: Bot, guildId: bigint) { + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.DISCOVERY_METADATA(guildId) + ); + + return { + guildId, + primaryCategoryId: result.primary_category_id, + keywords: result.keywords ?? undefined, + emojiDiscoverabilityEnabled: result.emoji_discoverability_enabled, + partnerActionedTimestamp: result.partner_actioned_timestamp + ? Date.parse(result.partner_actioned_timestamp) + : undefined, + partnerApplicationTimestamp: result.partner_application_timestamp + ? Date.parse(result.partner_application_timestamp) + : undefined, + categoryIds: result.category_ids, + }; +} diff --git a/src/helpers/discovery/get_discovery.ts b/src/helpers/discovery/get_discovery.ts deleted file mode 100644 index 224837df3..000000000 --- a/src/helpers/discovery/get_discovery.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { DiscoveryMetadata } from "../../types/discovery/discoveryMetadata.ts"; -import type { Bot } from "../../bot.ts"; - -/** Returns the discovery metadata object for the guild. Requires the `MANAGE_GUILD` permission. */ -export async function getDiscovery(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod( - bot.rest, - "get", - bot.constants.endpoints.DISCOVERY_METADATA(guildId) - ); -} diff --git a/src/helpers/discovery/removeDiscoverySubcategory.ts b/src/helpers/discovery/removeDiscoverySubcategory.ts index 7aec290c4..f25b35d90 100644 --- a/src/helpers/discovery/removeDiscoverySubcategory.ts +++ b/src/helpers/discovery/removeDiscoverySubcategory.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; /** Removes a discovery subcategory from the guild. Requires the MANAGE_GUILD permission. Returns a 204 No Content on success. */ export async function removeDiscoverySubcategory(bot: Bot, guildId: bigint, categoryId: number) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.DISCOVERY_SUBCATEGORY(guildId, categoryId) diff --git a/src/helpers/emojis/deleteEmoji.ts b/src/helpers/emojis/deleteEmoji.ts index e9c63446f..cc77e6c88 100644 --- a/src/helpers/emojis/deleteEmoji.ts +++ b/src/helpers/emojis/deleteEmoji.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ export async function deleteEmoji(bot: Bot, guildId: bigint, id: bigint, reason?: string) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_EMOJI(guildId, id), { + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_EMOJI(guildId, id), { reason, }); } diff --git a/src/helpers/emojis/editEmoji.ts b/src/helpers/emojis/editEmoji.ts index b08ecf300..c371ba16d 100644 --- a/src/helpers/emojis/editEmoji.ts +++ b/src/helpers/emojis/editEmoji.ts @@ -4,5 +4,12 @@ import type { Bot } from "../../bot.ts"; /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ export async function editEmoji(bot: Bot, guildId: bigint, id: bigint, options: ModifyGuildEmoji) { - return await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.GUILD_EMOJI(guildId, id), options); + const result = await bot.rest.runMethod( + bot.rest, + "patch", + bot.constants.endpoints.GUILD_EMOJI(guildId, id), + options + ); + + return bot.transformers.emoji(bot, result); } diff --git a/src/helpers/emojis/getEmoji.ts b/src/helpers/emojis/getEmoji.ts index 4b7b66e64..17287b174 100644 --- a/src/helpers/emojis/getEmoji.ts +++ b/src/helpers/emojis/getEmoji.ts @@ -4,9 +4,13 @@ import type { Bot } from "../../bot.ts"; /** * Returns an emoji for the given guild and emoji Id. - * - * ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get()?.emojis */ -export async function getEmoji(bot: Bot, guildId: bigint, emojiId: bigint, addToCache = true) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_EMOJI(guildId, emojiId)); +export async function getEmoji(bot: Bot, guildId: bigint, emojiId: bigint) { + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_EMOJI(guildId, emojiId) + ); + + return bot.transformers.emoji(bot, result); } diff --git a/src/helpers/emojis/getEmojis.ts b/src/helpers/emojis/getEmojis.ts index 6e229869e..2e0274476 100644 --- a/src/helpers/emojis/getEmojis.ts +++ b/src/helpers/emojis/getEmojis.ts @@ -5,10 +5,8 @@ import { Collection } from "../../util/collection.ts"; /** * Returns a list of emojis for the given guild. - * - * ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get()?.emojis */ -export async function getEmojis(bot: Bot, guildId: bigint, addToCache = true) { +export async function getEmojis(bot: Bot, guildId: bigint) { const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_EMOJIS(guildId)); return new Collection(result.map((e) => [bot.transformers.snowflake(e.id!), bot.transformers.emoji(bot, e)])); diff --git a/src/helpers/guilds/deleteGuild.ts b/src/helpers/guilds/deleteGuild.ts index c1c4d359b..45b0e1fb5 100644 --- a/src/helpers/guilds/deleteGuild.ts +++ b/src/helpers/guilds/deleteGuild.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event. */ export async function deleteGuild(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILDS_BASE(guildId)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILDS_BASE(guildId)); } diff --git a/src/helpers/guilds/editWelcomeScreen.ts b/src/helpers/guilds/editWelcomeScreen.ts index 0508738ff..9d7601fc7 100644 --- a/src/helpers/guilds/editWelcomeScreen.ts +++ b/src/helpers/guilds/editWelcomeScreen.ts @@ -3,21 +3,21 @@ import type { WelcomeScreen } from "../../types/guilds/welcomeScreen.ts"; import type { Bot } from "../../bot.ts"; export async function editWelcomeScreen(bot: Bot, guildId: bigint, options: ModifyGuildWelcomeScreen) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "patch", bot.constants.endpoints.GUILD_WELCOME_SCREEN(guildId), { enabled: options.enabled, - welcomeScreen: options.welcomeScreen?.map((welcomeScreen) => { - return { - channel_id: welcomeScreen.channelId, - description: welcomeScreen.description, - emoji_id: welcomeScreen.emojiId, - emoji_name: welcomeScreen.emojiName, - }; - }), + welcome_screen: options.welcomeScreen?.map((welcomeScreen) => ({ + channel_id: welcomeScreen.channelId, + description: welcomeScreen.description, + emoji_id: welcomeScreen.emojiId, + emoji_name: welcomeScreen.emojiName, + })), description: options.description, } ); + + return bot.transformers.welcomeScreen(bot, result); } diff --git a/src/helpers/guilds/editWidget.ts b/src/helpers/guilds/editWidget.ts index ec99f7d03..0135f2722 100644 --- a/src/helpers/guilds/editWidget.ts +++ b/src/helpers/guilds/editWidget.ts @@ -3,8 +3,10 @@ import type { Bot } from "../../bot.ts"; /** Modify a guild widget object for the guild. Requires the MANAGE_GUILD permission. */ export async function editWidget(bot: Bot, guildId: bigint, enabled: boolean, channelId?: string | null) { - return await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.GUILD_WIDGET(guildId), { + const result = await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.GUILD_WIDGET(guildId), { enabled, channel_id: channelId, }); + + return bot.transformers.widget(bot, result); } diff --git a/src/helpers/guilds/getAuditLogs.ts b/src/helpers/guilds/getAuditLogs.ts index ed4788956..d1ee34f13 100644 --- a/src/helpers/guilds/getAuditLogs.ts +++ b/src/helpers/guilds/getAuditLogs.ts @@ -48,5 +48,7 @@ export async function getAuditLogs(bot: Bot, guildId: bigint, options?: GetGuild } : undefined, })), + threads: auditlog.threads.map((thread) => bot.transformers.channel(bot, { channel: thread, guildId })), + scheduledEvents: auditlog.scheduled_events.map((event) => bot.transformers.scheduledEvent(bot, event)), }; } diff --git a/src/helpers/guilds/getAvailableVoiceRegions.ts b/src/helpers/guilds/getAvailableVoiceRegions.ts index 781742b02..d1541582f 100644 --- a/src/helpers/guilds/getAvailableVoiceRegions.ts +++ b/src/helpers/guilds/getAvailableVoiceRegions.ts @@ -1,7 +1,15 @@ import type { VoiceRegion } from "../../types/voice/voiceRegion.ts"; import type { Bot } from "../../bot.ts"; +import { Collection } from "../../util/collection.ts"; /** Returns an array of voice regions that can be used when creating servers. */ export async function getAvailableVoiceRegions(bot: Bot) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.VOICE_REGIONS); + const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.VOICE_REGIONS); + + return new Collection( + result.map((region) => { + const voiceRegion = bot.transformers.voiceRegion(bot, region); + return [voiceRegion.id, voiceRegion]; + }) + ); } diff --git a/src/helpers/guilds/getBans.ts b/src/helpers/guilds/getBans.ts index 2b1b2af56..ef4dcbab9 100644 --- a/src/helpers/guilds/getBans.ts +++ b/src/helpers/guilds/getBans.ts @@ -1,10 +1,19 @@ import type { Ban } from "../../types/guilds/ban.ts"; import type { Bot } from "../../bot.ts"; import { Collection } from "../../util/collection.ts"; +import { DiscordenoUser } from "../../transformers/member.ts"; /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ export async function getBans(bot: Bot, guildId: bigint) { const results = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_BANS(guildId)); - return new Collection(results.map((res) => [bot.transformers.snowflake(res.user.id), res])); + return new Collection( + results.map((res) => [ + bot.transformers.snowflake(res.user.id), + { + reason: res.reason ?? undefined, + user: bot.transformers.user(bot, res.user), + }, + ]) + ); } diff --git a/src/helpers/guilds/getGuild.ts b/src/helpers/guilds/getGuild.ts index 2ab3ac409..d377319f2 100644 --- a/src/helpers/guilds/getGuild.ts +++ b/src/helpers/guilds/getGuild.ts @@ -2,16 +2,13 @@ import type { Guild } from "../../types/guilds/guild.ts"; import type { Bot } from "../../bot.ts"; /** - * ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get() - * - * Advanced Devs: * This function fetches a guild's data. This is not the same data as a GUILD_CREATE. * So it does not cache the guild, you must do it manually. * */ export async function getGuild( bot: Bot, guildId: bigint, - options: { counts?: boolean; addToCache?: boolean } = { + options: { counts?: boolean } = { counts: true, } ) { @@ -19,10 +16,8 @@ export async function getGuild( with_counts: options.counts, }); - const guild = bot.transformers.guild(bot, { + return bot.transformers.guild(bot, { guild: result, shardId: bot.utils.calculateShardId(bot.gateway, guildId), }); - - return guild; } diff --git a/src/helpers/guilds/getGuildPreview.ts b/src/helpers/guilds/getGuildPreview.ts index d552d6720..90905f8b8 100644 --- a/src/helpers/guilds/getGuildPreview.ts +++ b/src/helpers/guilds/getGuildPreview.ts @@ -3,5 +3,22 @@ import type { Bot } from "../../bot.ts"; /** Returns the guild preview object for the given id. If the bot is not in the guild, then the guild must be Discoverable. */ export async function getGuildPreview(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_PREVIEW(guildId)); + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_PREVIEW(guildId) + ); + + return { + id: bot.transformers.snowflake(result.id), + name: result.name, + icon: result.icon ?? undefined, + splash: result.splash ?? undefined, + discoverySplash: result.discovery_splash ?? undefined, + emojis: result.emojis.map((emoji) => bot.transformers.emoji(bot, emoji)), + features: result.features, + approximateMemberCount: result.approximate_member_count, + approximatePresenceCount: result.approximate_presence_count, + description: result.description ?? undefined, + }; } diff --git a/src/helpers/guilds/getVanityUrl.ts b/src/helpers/guilds/getVanityUrl.ts index 3edba9272..1ee1e0b86 100644 --- a/src/helpers/guilds/getVanityUrl.ts +++ b/src/helpers/guilds/getVanityUrl.ts @@ -2,11 +2,15 @@ import type { InviteMetadata } from "../../types/invites/inviteMetadata.ts"; import type { Bot } from "../../bot.ts"; /** Returns the code and uses of the vanity url for this server if it is enabled else `code` will be null. Requires the `MANAGE_GUILD` permission. */ -export async function getVanityURL(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod< - | (Partial & Pick) - | { - code: null; - } - >(bot.rest, "get", bot.constants.endpoints.GUILD_VANITY_URL(guildId)); +export async function getVanityUrl(bot: Bot, guildId: bigint) { + const result = await bot.rest.runMethod>( + bot.rest, + "get", + bot.constants.endpoints.GUILD_VANITY_URL(guildId) + ); + + return { + uses: result.uses, + code: result.code, + }; } diff --git a/src/helpers/guilds/getVoiceRegions.ts b/src/helpers/guilds/getVoiceRegions.ts index 53b090266..36f016665 100644 --- a/src/helpers/guilds/getVoiceRegions.ts +++ b/src/helpers/guilds/getVoiceRegions.ts @@ -10,5 +10,10 @@ export async function getVoiceRegions(bot: Bot, guildId: bigint) { bot.constants.endpoints.GUILD_REGIONS(guildId) ); - return new Collection(result.map((region) => [region.id, region])); + return new Collection( + result.map((reg) => { + const region = bot.transformers.voiceRegion(bot, reg); + return [region.id, region]; + }) + ); } diff --git a/src/helpers/guilds/getWelcomeScreen.ts b/src/helpers/guilds/getWelcomeScreen.ts index d40145d1b..06e6e0fdc 100644 --- a/src/helpers/guilds/getWelcomeScreen.ts +++ b/src/helpers/guilds/getWelcomeScreen.ts @@ -2,9 +2,11 @@ import type { WelcomeScreen } from "../../types/guilds/welcomeScreen.ts"; import type { Bot } from "../../bot.ts"; export async function getWelcomeScreen(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "get", bot.constants.endpoints.GUILD_WELCOME_SCREEN(guildId) ); + + return bot.transformers.welcomeScreen(bot, result); } diff --git a/src/helpers/guilds/getWidget.ts b/src/helpers/guilds/getWidget.ts index deb1b1e34..c51cc0a58 100644 --- a/src/helpers/guilds/getWidget.ts +++ b/src/helpers/guilds/getWidget.ts @@ -3,9 +3,28 @@ import type { Bot } from "../../bot.ts"; /** Returns the widget for the guild. */ export async function getWidget(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "get", `${bot.constants.endpoints.GUILD_WIDGET(guildId)}.json` ); + + return { + id: bot.transformers.snowflake(result.id), + name: result.name, + instantInvite: result.instant_invite, + channels: result.channels.map((channel) => ({ + id: bot.transformers.snowflake(channel.id), + name: channel.name, + position: channel.position, + })), + members: result.members.map((member) => ({ + id: bot.transformers.snowflake(member.id), + username: member.username, + discriminator: Number(member.discriminator), + avatar: member.avatar ? bot.utils.iconHashToBigInt(member.avatar) : undefined, + status: member.status, + })), + presenceCount: result.presence_count, + }; } diff --git a/src/helpers/guilds/getWidgetSettings.ts b/src/helpers/guilds/getWidgetSettings.ts index 4131916b1..5d6c49bf5 100644 --- a/src/helpers/guilds/getWidgetSettings.ts +++ b/src/helpers/guilds/getWidgetSettings.ts @@ -3,5 +3,7 @@ import type { Bot } from "../../bot.ts"; /** Returns the guild widget object. Requires the MANAGE_GUILD permission. */ export async function getWidgetSettings(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_WIDGET(guildId)); + const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_WIDGET(guildId)); + + return bot.transformers.widget(bot, result); } diff --git a/src/helpers/guilds/guildIconUrl.ts b/src/helpers/guilds/guildIconUrl.ts index ca7a29c76..fcd4d89c5 100644 --- a/src/helpers/guilds/guildIconUrl.ts +++ b/src/helpers/guilds/guildIconUrl.ts @@ -6,20 +6,20 @@ import type { Bot } from "../../bot.ts"; export function guildIconURL( bot: Bot, id: bigint, - options: { - icon?: string | bigint; + icon: bigint | undefined, + options?: { size?: ImageSize; format?: ImageFormat; } ) { - return options.icon + return icon ? bot.utils.formatImageURL( bot.constants.endpoints.GUILD_ICON( id, - typeof options.icon === "string" ? options.icon : bot.utils.iconBigintToHash(options.icon) + typeof icon === "string" ? icon : bot.utils.iconBigintToHash(icon) ), - options.size || 128, - options.format + options?.size || 128, + options?.format ) : undefined; } diff --git a/src/helpers/guilds/guildSplashUrl.ts b/src/helpers/guilds/guildSplashUrl.ts index d9f28209b..defaa4930 100644 --- a/src/helpers/guilds/guildSplashUrl.ts +++ b/src/helpers/guilds/guildSplashUrl.ts @@ -6,20 +6,20 @@ import type { Bot } from "../../bot.ts"; export function guildSplashURL( bot: Bot, id: bigint, - options: { - splash?: string | bigint; + splash: bigint | undefined, + options?: { size?: ImageSize; format?: ImageFormat; } ) { - return options.splash + return splash ? bot.utils.formatImageURL( bot.constants.endpoints.GUILD_SPLASH( id, - typeof options.splash === "string" ? options.splash : bot.utils.iconBigintToHash(options.splash) + typeof splash === "string" ? splash : bot.utils.iconBigintToHash(splash) ), - options.size || 128, - options.format + options?.size || 128, + options?.format ) : undefined; } diff --git a/src/helpers/guilds/leaveGuild.ts b/src/helpers/guilds/leaveGuild.ts index 06d6f1756..fade59ee2 100644 --- a/src/helpers/guilds/leaveGuild.ts +++ b/src/helpers/guilds/leaveGuild.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Leave a guild */ export async function leaveGuild(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_LEAVE(guildId)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_LEAVE(guildId)); } diff --git a/src/helpers/integrations/deleteIntegration.ts b/src/helpers/integrations/deleteIntegration.ts index 10d420230..5be3de2af 100644 --- a/src/helpers/integrations/deleteIntegration.ts +++ b/src/helpers/integrations/deleteIntegration.ts @@ -2,9 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ export async function deleteIntegration(bot: Bot, guildId: bigint, id: bigint) { - return await bot.rest.runMethod( - bot.rest, - "delete", - bot.constants.endpoints.GUILD_INTEGRATION(guildId, id) - ); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_INTEGRATION(guildId, id)); } diff --git a/src/helpers/integrations/getIntegrations.ts b/src/helpers/integrations/getIntegrations.ts index fb0d2fec2..4736d3d0e 100644 --- a/src/helpers/integrations/getIntegrations.ts +++ b/src/helpers/integrations/getIntegrations.ts @@ -1,7 +1,36 @@ import type { Integration } from "../../types/integrations/integration.ts"; import type { Bot } from "../../bot.ts"; +import { Collection } from "../../util/collection.ts"; /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ export async function getIntegrations(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.GUILD_INTEGRATIONS(guildId)); + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_INTEGRATIONS(guildId) + ); + + return new Collection( + result.map((res) => { + const integration = bot.transformers.integration(bot, { + guild_id: guildId.toString(), + id: res.id, + name: res.name, + type: res.type, + enabled: res.enabled, + syncing: res.syncing, + role_id: res.role_id, + enable_emoticons: res.enable_emoticons, + expire_behavior: res.expire_behavior, + expire_grace_period: res.expire_grace_period, + user: res.user, + account: res.account, + synced_at: res.synced_at, + subscriber_count: res.subscriber_count, + revoked: res.revoked, + application: res.application, + }); + return [integration.id, integration]; + }) + ); } diff --git a/src/helpers/interactions/commands/createApplicationCommand.ts b/src/helpers/interactions/commands/createApplicationCommand.ts index 7697739ca..fea31422c 100644 --- a/src/helpers/interactions/commands/createApplicationCommand.ts +++ b/src/helpers/interactions/commands/createApplicationCommand.ts @@ -1,5 +1,5 @@ import type { ApplicationCommand } from "../../../types/interactions/commands/applicationCommand.ts"; -import type { CreateGlobalApplicationCommand } from "../../../types/interactions/commands/createGlobalApplicationCommand.ts"; +import type { CreateApplicationCommand } from "../../../types/interactions/commands/createGlobalApplicationCommand.ts"; import type { Bot } from "../../../bot.ts"; import { ApplicationCommandOption } from "../../../types/interactions/commands/applicationCommandOption.ts"; @@ -14,8 +14,8 @@ import { ApplicationCommandOption } from "../../../types/interactions/commands/a * Global commands are cached for **1 hour**. That means that new global commands will fan out slowly across all guilds, and will be guaranteed to be updated in an hour. * Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. */ -export async function createApplicationCommand(bot: Bot, options: CreateGlobalApplicationCommand, guildId?: bigint) { - return await bot.rest.runMethod( +export async function createApplicationCommand(bot: Bot, options: CreateApplicationCommand, guildId?: bigint) { + const result = await bot.rest.runMethod( bot.rest, "post", guildId @@ -28,6 +28,8 @@ export async function createApplicationCommand(bot: Bot, options: CreateGlobalAp options: options.options ? makeOptionsForCommand(options.options) : undefined, } ); + + return bot.transformers.applicationCommand(bot, result); } // @ts-ignore TODO: see if we can make this not circular @@ -40,5 +42,8 @@ export function makeOptionsForCommand(options: ApplicationCommandOption[]) { choices: option.choices, options: option.options ? makeOptionsForCommand(option.options) : undefined, channel_types: option.channelTypes, + autocomplete: option.autocomplete, + min_value: option.minValue, + max_value: option.maxValue, })); } diff --git a/src/helpers/interactions/commands/deleteApplicationCommand.ts b/src/helpers/interactions/commands/deleteApplicationCommand.ts index d72b7cb68..073727b2d 100644 --- a/src/helpers/interactions/commands/deleteApplicationCommand.ts +++ b/src/helpers/interactions/commands/deleteApplicationCommand.ts @@ -1,8 +1,8 @@ import type { Bot } from "../../../bot.ts"; -/** Deletes a slash command. */ +/** Deletes a application command. */ export async function deleteApplicationCommand(bot: Bot, id: bigint, guildId?: bigint) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", guildId diff --git a/src/helpers/interactions/commands/deleteInteractionResponse.ts b/src/helpers/interactions/commands/deleteInteractionResponse.ts index 65b8b1a93..353cdd58a 100644 --- a/src/helpers/interactions/commands/deleteInteractionResponse.ts +++ b/src/helpers/interactions/commands/deleteInteractionResponse.ts @@ -1,8 +1,8 @@ import type { Bot } from "../../../bot.ts"; -/** To delete your response to a slash command. If a message id is not provided, it will default to deleting the original response. */ +/** To delete your response to a application command. If a message id is not provided, it will default to deleting the original response. */ export async function deleteInteractionResponse(bot: Bot, token: string, messageId?: bigint) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", messageId diff --git a/src/helpers/interactions/commands/editInteractionResponse.ts b/src/helpers/interactions/commands/editInteractionResponse.ts index 6c688dde1..b4c32c347 100644 --- a/src/helpers/interactions/commands/editInteractionResponse.ts +++ b/src/helpers/interactions/commands/editInteractionResponse.ts @@ -3,38 +3,8 @@ import type { Bot } from "../../../bot.ts"; import { AllowedMentionsTypes } from "../../../types/messages/allowedMentionsTypes.ts"; import { MessageComponentTypes } from "../../../types/messages/components/messageComponentTypes.ts"; -/** To edit your response to a slash command. If a messageId is not provided it will default to editing the original response. */ +/** To edit your response to a application command. If a messageId is not provided it will default to editing the original response. */ export async function editInteractionResponse(bot: Bot, token: string, options: DiscordenoEditWebhookMessage) { - if (options.content && options.content.length > 2000) { - throw Error(bot.constants.Errors.MESSAGE_MAX_LENGTH); - } - - if (options.embeds && options.embeds.length > 10) { - options.embeds.splice(10); - } - - if (options.allowedMentions) { - if (options.allowedMentions.users?.length) { - if (options.allowedMentions.parse?.includes(AllowedMentionsTypes.UserMentions)) { - options.allowedMentions.parse = options.allowedMentions.parse.filter((p) => p !== "users"); - } - - if (options.allowedMentions.users.length > 100) { - options.allowedMentions.users = options.allowedMentions.users.slice(0, 100); - } - } - - if (options.allowedMentions.roles?.length) { - if (options.allowedMentions.parse?.includes(AllowedMentionsTypes.RoleMentions)) { - options.allowedMentions.parse = options.allowedMentions.parse.filter((p) => p !== "roles"); - } - - if (options.allowedMentions.roles.length > 100) { - options.allowedMentions.roles = options.allowedMentions.roles.slice(0, 100); - } - } - } - const result = await bot.rest.runMethod( bot.rest, "patch", @@ -57,6 +27,18 @@ export async function editInteractionResponse(bot: Bot, token: string, options: components: options.components?.map((component) => ({ type: component.type, components: component.components.map((subcomponent) => { + if (subcomponent.type === MessageComponentTypes.InputText) { + return { + type: subcomponent.type, + style: subcomponent.style, + custom_id: subcomponent.customId, + label: subcomponent.label, + placeholder: subcomponent.placeholder, + min_length: subcomponent.minLength ?? subcomponent.required === false ? 0 : subcomponent.minLength, + max_length: subcomponent.maxLength, + }; + } + if (subcomponent.type === MessageComponentTypes.SelectMenu) return { type: subcomponent.type, @@ -84,15 +66,16 @@ export async function editInteractionResponse(bot: Bot, token: string, options: custom_id: subcomponent.customId, label: subcomponent.label, style: subcomponent.style, - emoji: subcomponent.emoji - ? { - id: subcomponent.emoji.id?.toString(), - name: subcomponent.emoji.name, - animated: subcomponent.emoji.animated, - } - : undefined, - url: subcomponent.url, - disabled: subcomponent.disabled, + emoji: + "emoji" in subcomponent && subcomponent.emoji + ? { + id: subcomponent.emoji.id?.toString(), + name: subcomponent.emoji.name, + animated: subcomponent.emoji.animated, + } + : undefined, + url: "url" in subcomponent ? subcomponent.url : undefined, + disabled: "disabled" in subcomponent ? subcomponent.disabled : undefined, }; }), })), diff --git a/src/helpers/interactions/commands/getApplicationCommand.ts b/src/helpers/interactions/commands/getApplicationCommand.ts index 5078307d5..6622a02cb 100644 --- a/src/helpers/interactions/commands/getApplicationCommand.ts +++ b/src/helpers/interactions/commands/getApplicationCommand.ts @@ -11,9 +11,5 @@ export async function getApplicationCommand(bot: Bot, commandId: bigint, guildId : bot.constants.endpoints.COMMANDS_ID(bot.applicationId, commandId) ); - return { - ...result, - id: bot.transformers.snowflake(result.id), - applicationId: bot.transformers.snowflake(result.application_id), - }; + return bot.transformers.applicationCommand(bot, result); } diff --git a/src/helpers/interactions/commands/getApplicationCommandPermission.ts b/src/helpers/interactions/commands/getApplicationCommandPermission.ts index 52374b2b3..86e2ed419 100644 --- a/src/helpers/interactions/commands/getApplicationCommandPermission.ts +++ b/src/helpers/interactions/commands/getApplicationCommandPermission.ts @@ -3,9 +3,11 @@ import type { Bot } from "../../../bot.ts"; /** Fetches command permissions for a specific command for your application in a guild. Returns a GuildApplicationCommandPermissions object. */ export async function getApplicationCommandPermission(bot: Bot, guildId: bigint, commandId: bigint) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "get", bot.constants.endpoints.COMMANDS_PERMISSION(bot.applicationId, guildId, commandId) ); + + return bot.transformers.applicationCommandPermission(bot, result); } diff --git a/src/helpers/interactions/commands/getApplicationCommandPermissions.ts b/src/helpers/interactions/commands/getApplicationCommandPermissions.ts index 2b9c4c015..adac57736 100644 --- a/src/helpers/interactions/commands/getApplicationCommandPermissions.ts +++ b/src/helpers/interactions/commands/getApplicationCommandPermissions.ts @@ -1,11 +1,19 @@ import type { Bot } from "../../../bot.ts"; import type { GuildApplicationCommandPermissions } from "../../../types/interactions/commands/guildApplicationCommandPermissions.ts"; +import { Collection } from "../../../util/collection.ts"; /** Fetches command permissions for all commands for your application in a guild. Returns an array of GuildApplicationCommandPermissions objects. */ export async function getApplicationCommandPermissions(bot: Bot, guildId: bigint) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "get", bot.constants.endpoints.COMMANDS_PERMISSIONS(bot.applicationId, guildId) ); + + return new Collection( + result.map((res) => { + const perms = bot.transformers.applicationCommandPermission(bot, res); + return [perms.id, perms]; + }) + ); } diff --git a/src/helpers/interactions/commands/getApplicationCommands.ts b/src/helpers/interactions/commands/getApplicationCommands.ts index 004873678..4af891a24 100644 --- a/src/helpers/interactions/commands/getApplicationCommands.ts +++ b/src/helpers/interactions/commands/getApplicationCommands.ts @@ -2,7 +2,7 @@ import type { ApplicationCommand } from "../../../types/interactions/commands/ap import { Collection } from "../../../util/collection.ts"; import type { Bot } from "../../../bot.ts"; -/** Fetch all the global commands for your application. */ +/** Fetch all the commands for your application. If a guild id is not provided, it will fetch global commands. */ export async function getApplicationCommands(bot: Bot, guildId?: bigint) { const result = await bot.rest.runMethod( bot.rest, @@ -13,13 +13,9 @@ export async function getApplicationCommands(bot: Bot, guildId?: bigint) { ); return new Collection( - result.map((command) => [ - command.name, - { - ...command, - id: bot.transformers.snowflake(command.id), - applicationId: bot.transformers.snowflake(command.application_id), - }, - ]) + result.map((res) => { + const command = bot.transformers.applicationCommand(bot, res); + return [command.id, command]; + }) ); } diff --git a/src/helpers/interactions/commands/upsertApplicationCommand.ts b/src/helpers/interactions/commands/upsertApplicationCommand.ts index d2f868533..0d00b715b 100644 --- a/src/helpers/interactions/commands/upsertApplicationCommand.ts +++ b/src/helpers/interactions/commands/upsertApplicationCommand.ts @@ -4,7 +4,7 @@ import type { Bot } from "../../../bot.ts"; import { makeOptionsForCommand } from "./createApplicationCommand.ts"; /** - * Edit an existing slash command. If this command did not exist, it will create it. + * Edit an existing application command. If this command did not exist, it will create it. */ export async function upsertApplicationCommand( bot: Bot, @@ -12,7 +12,7 @@ export async function upsertApplicationCommand( options: EditGlobalApplicationCommand, guildId?: bigint ) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "patch", guildId @@ -25,4 +25,6 @@ export async function upsertApplicationCommand( options: options.options ? makeOptionsForCommand(options.options) : undefined, } ); + + return bot.transformers.applicationCommand(bot, result); } diff --git a/src/helpers/interactions/commands/upsertApplicationCommands.ts b/src/helpers/interactions/commands/upsertApplicationCommands.ts index 0667890b4..b47f668e3 100644 --- a/src/helpers/interactions/commands/upsertApplicationCommands.ts +++ b/src/helpers/interactions/commands/upsertApplicationCommands.ts @@ -2,23 +2,39 @@ import type { ApplicationCommand } from "../../../types/interactions/commands/ap import type { EditGlobalApplicationCommand } from "../../../types/interactions/commands/editGlobalApplicationCommand.ts"; import type { MakeRequired } from "../../../types/util.ts"; import type { Bot } from "../../../bot.ts"; +import { Collection } from "../../../util/collection.ts"; +import { ApplicationCommandOption } from "../../../types/interactions/commands/applicationCommandOption.ts"; +import { makeOptionsForCommand } from "./createApplicationCommand.ts"; /** - * Bulk edit existing slash commands. If a command does not exist, it will create it. + * Bulk edit existing application commands. If a command does not exist, it will create it. * - * **NOTE:** Any slash commands that are not specified in this function will be **deleted**. If you don't provide the commandId and rename your command, the command gets a new Id. + * **NOTE:** Any application commands that are not specified in this function will be **deleted**. If you don't provide the commandId and rename your command, the command gets a new Id. */ export async function upsertApplicationCommands( bot: Bot, options: MakeRequired[], guildId?: bigint ) { - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "put", guildId ? bot.constants.endpoints.COMMANDS_GUILD(bot.applicationId, guildId) : bot.constants.endpoints.COMMANDS(bot.applicationId), - options + options.map((option) => ({ + name: option.name, + description: option.description, + type: option.type, + options: option.options ? makeOptionsForCommand(option.options) : undefined, + default_permission: option.defaultPermission, + })) + ); + + return new Collection( + result.map((res) => { + const command = bot.transformers.applicationCommand(bot, res); + return [command.id, command]; + }) ); } diff --git a/src/helpers/interactions/followups/editFollowupMessage.ts b/src/helpers/interactions/followups/editFollowupMessage.ts index f3dcdfd86..173e64927 100644 --- a/src/helpers/interactions/followups/editFollowupMessage.ts +++ b/src/helpers/interactions/followups/editFollowupMessage.ts @@ -11,36 +11,6 @@ export async function editFollowupMessage( messageId: bigint, options: EditWebhookMessage ) { - if (options.content && options.content.length > 2000) { - throw Error(bot.constants.Errors.MESSAGE_MAX_LENGTH); - } - - if (options.embeds && options.embeds.length > 10) { - options.embeds.splice(10); - } - - if (options.allowedMentions) { - if (options.allowedMentions.users?.length) { - if (options.allowedMentions.parse?.includes(AllowedMentionsTypes.UserMentions)) { - options.allowedMentions.parse = options.allowedMentions.parse.filter((p) => p !== "users"); - } - - if (options.allowedMentions.users.length > 100) { - options.allowedMentions.users = options.allowedMentions.users.slice(0, 100); - } - } - - if (options.allowedMentions.roles?.length) { - if (options.allowedMentions.parse?.includes(AllowedMentionsTypes.RoleMentions)) { - options.allowedMentions.parse = options.allowedMentions.parse.filter((p) => p !== "roles"); - } - - if (options.allowedMentions.roles.length > 100) { - options.allowedMentions.roles = options.allowedMentions.roles.slice(0, 100); - } - } - } - const result = await bot.rest.runMethod( bot.rest, "patch", @@ -61,6 +31,18 @@ export async function editFollowupMessage( components: options.components?.map((component) => ({ type: component.type, components: component.components.map((subcomponent) => { + if (subcomponent.type === MessageComponentTypes.InputText) { + return { + type: subcomponent.type, + style: subcomponent.style, + custom_id: subcomponent.customId, + label: subcomponent.label, + placeholder: subcomponent.placeholder, + min_length: subcomponent.minLength ?? subcomponent.required === false ? 0 : subcomponent.minLength, + max_length: subcomponent.maxLength, + }; + } + if (subcomponent.type === MessageComponentTypes.SelectMenu) return { type: subcomponent.type, diff --git a/src/helpers/interactions/sendInteractionResponse.ts b/src/helpers/interactions/sendInteractionResponse.ts index af99c741a..c81848bec 100644 --- a/src/helpers/interactions/sendInteractionResponse.ts +++ b/src/helpers/interactions/sendInteractionResponse.ts @@ -1,10 +1,10 @@ import type { DiscordenoInteractionResponse } from "../../types/discordeno/interactionResponse.ts"; import type { Bot } from "../../bot.ts"; -import { AllowedMentions } from "../../types/messages/allowedMentions.ts"; import { MessageComponentTypes } from "../../types/messages/components/messageComponentTypes.ts"; +import { Message } from "../../types/messages/message.ts"; /** - * Send a response to a users slash command. The command data will have the id and token necessary to respond. + * Send a response to a users application command. The command data will have the id and token necessary to respond. * Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages. * * NOTE: By default we will suppress mentions. To enable mentions, just pass any mentions object. @@ -85,9 +85,23 @@ export async function sendInteractionResponse( roles: options.data.allowedMentions!.roles?.map((id) => id.toString()), }, file: options.data.file, + custom_id: options.data.customId, + title: options.data.title, components: options.data.components?.map((component) => ({ type: component.type, components: component.components.map((subcomponent) => { + if (subcomponent.type === MessageComponentTypes.InputText) { + return { + type: subcomponent.type, + style: subcomponent.style, + custom_id: subcomponent.customId, + label: subcomponent.label, + placeholder: subcomponent.placeholder, + min_length: subcomponent.minLength ?? subcomponent.required === false ? 0 : subcomponent.minLength, + max_length: subcomponent.maxLength, + }; + } + if (subcomponent.type === MessageComponentTypes.SelectMenu) return { type: subcomponent.type, @@ -115,29 +129,33 @@ export async function sendInteractionResponse( custom_id: subcomponent.customId, label: subcomponent.label, style: subcomponent.style, - emoji: subcomponent.emoji - ? { - id: subcomponent.emoji.id?.toString(), - name: subcomponent.emoji.name, - animated: subcomponent.emoji.animated, - } - : undefined, - url: subcomponent.url, - disabled: subcomponent.disabled, + emoji: + "emoji" in subcomponent && subcomponent.emoji + ? { + id: subcomponent.emoji.id?.toString(), + name: subcomponent.emoji.name, + animated: subcomponent.emoji.animated, + } + : undefined, + url: "url" in subcomponent ? subcomponent.url : undefined, + disabled: "disabled" in subcomponent ? subcomponent.disabled : undefined, }; }), })), flags: options.data.flags, + choices: options.data.choices, }; // A reply has never been send if (bot.cache.unrepliedInteractions.delete(id)) { - return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.INTERACTION_ID_TOKEN(id, token), { + return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.INTERACTION_ID_TOKEN(id, token), { type: options.type, data, }); } // If its already been executed, we need to send a followup response - return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.WEBHOOK(bot.applicationId, token), data); + const result = await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.WEBHOOK(bot.applicationId, token), data); + + return bot.transformers.message(bot, result); } diff --git a/src/helpers/interactions/verifySignature.ts b/src/helpers/interactions/verifySignature.ts index f6f4a1806..f062d6be0 100644 --- a/src/helpers/interactions/verifySignature.ts +++ b/src/helpers/interactions/verifySignature.ts @@ -1,4 +1,4 @@ -import { verify } from "https://unpkg.com/@evan/wasm@0.0.87/target/ed25519/deno.js"; +import { verify } from "https://unpkg.com/@evan/wasm@0.0.93/target/ed25519/deno.js"; export function verifySignature({ publicKey, signature, timestamp, body }: VerifySignatureOptions): { isValid: boolean; diff --git a/src/helpers/invites/createInvite.ts b/src/helpers/invites/createInvite.ts index 077110979..7ba836210 100644 --- a/src/helpers/invites/createInvite.ts +++ b/src/helpers/invites/createInvite.ts @@ -6,14 +6,7 @@ import { SnakeCasedPropertiesDeep } from "../../types/util.ts"; /** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */ export async function createInvite(bot: Bot, channelId: bigint, options: CreateChannelInvite = {}) { - if (options.maxAge && (options.maxAge < 0 || options.maxAge > 604800)) { - throw new Error(Errors.INVITE_MAX_AGE_INVALID); - } - if (options.maxUses && (options.maxUses < 0 || options.maxUses > 100)) { - throw new Error(Errors.INVITE_MAX_USES_INVALID); - } - - return await bot.rest.runMethod( + const result = await bot.rest.runMethod( bot.rest, "post", bot.constants.endpoints.CHANNEL_INVITES(channelId), @@ -27,4 +20,12 @@ export async function createInvite(bot: Bot, channelId: bigint, options: CreateC target_application_id: options.targetUserId, } ); + + return { + uses: result.uses, + maxUses: result.max_uses, + maxAge: result.max_age, + temporary: result.temporary, + createdAt: result.created_at, + }; } diff --git a/src/helpers/invites/deleteInvite.ts b/src/helpers/invites/deleteInvite.ts index 3fa82a88b..76d923a29 100644 --- a/src/helpers/invites/deleteInvite.ts +++ b/src/helpers/invites/deleteInvite.ts @@ -3,5 +3,5 @@ import type { Bot } from "../../bot.ts"; /** Deletes an invite for the given code. Requires `MANAGE_CHANNELS` or `MANAGE_GUILD` permission */ export async function deleteInvite(bot: Bot, inviteCode: string) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.INVITE(inviteCode)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.INVITE(inviteCode)); } diff --git a/src/helpers/invites/getChannelInvites.ts b/src/helpers/invites/getChannelInvites.ts index 6ad0f7cd6..4341416ec 100644 --- a/src/helpers/invites/getChannelInvites.ts +++ b/src/helpers/invites/getChannelInvites.ts @@ -1,7 +1,6 @@ import type { InviteMetadata } from "../../types/invites/inviteMetadata.ts"; import { Collection } from "../../util/collection.ts"; import type { Bot } from "../../bot.ts"; -import { SnakeCasedPropertiesDeep } from "../../types/util.ts"; /** Gets the invites for this channel. Requires MANAGE_CHANNEL */ export async function getChannelInvites(bot: Bot, channelId: bigint) { @@ -11,5 +10,16 @@ export async function getChannelInvites(bot: Bot, channelId: bigint) { bot.constants.endpoints.CHANNEL_INVITES(channelId) ); - return new Collection(result.map((invite) => [invite.code, invite])); + return new Collection( + result.map((invite) => [ + invite.code, + { + uses: invite.uses, + maxUses: invite.max_uses, + maxAge: invite.max_age, + temporary: invite.temporary, + createdAt: Date.parse(invite.created_at), + }, + ]) + ); } diff --git a/src/helpers/invites/getInvite.ts b/src/helpers/invites/getInvite.ts index 654c6d7bd..5eb3b5caa 100644 --- a/src/helpers/invites/getInvite.ts +++ b/src/helpers/invites/getInvite.ts @@ -4,9 +4,17 @@ import type { Bot } from "../../bot.ts"; /** Returns an invite for the given code or throws an error if the invite doesn't exists. */ export async function getInvite(bot: Bot, inviteCode: string, options?: GetInvite) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.INVITE(inviteCode), { + const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.INVITE(inviteCode), { with_counts: options?.withCounts || false, with_expiration: options?.withExpiration || false, guild_scheduled_event_id: options?.scheduledEventId?.toString(), }); + + return { + uses: result.uses, + maxUses: result.max_uses, + maxAge: result.max_age, + temporary: result.temporary, + createdAt: result.created_at, + }; } diff --git a/src/helpers/invites/getInvites.ts b/src/helpers/invites/getInvites.ts index 07a64588b..a828fcde9 100644 --- a/src/helpers/invites/getInvites.ts +++ b/src/helpers/invites/getInvites.ts @@ -11,5 +11,16 @@ export async function getInvites(bot: Bot, guildId: bigint) { bot.constants.endpoints.GUILD_INVITES(guildId) ); - return new Collection(result.map((invite) => [invite.code, invite])); + return new Collection( + result.map((invite) => [ + invite.code, + { + uses: invite.uses, + maxUses: invite.max_uses, + maxAge: invite.max_age, + temporary: invite.temporary, + createdAt: Date.parse(invite.created_at), + }, + ]) + ); } diff --git a/src/helpers/members/avatarUrl.ts b/src/helpers/members/avatarUrl.ts index 0114d497a..4bbd15d87 100644 --- a/src/helpers/members/avatarUrl.ts +++ b/src/helpers/members/avatarUrl.ts @@ -7,20 +7,20 @@ export function avatarURL( bot: Bot, userId: bigint, discriminator: number, - options: { - avatar?: string | bigint; + options?: { + avatar: bigint | undefined; size?: ImageSize; format?: ImageFormat; } ) { - return options.avatar + return options?.avatar ? bot.utils.formatImageURL( bot.constants.endpoints.USER_AVATAR( userId, - typeof options.avatar === "string" ? options.avatar : bot.utils.iconBigintToHash(options.avatar) + typeof options?.avatar === "string" ? options.avatar : bot.utils.iconBigintToHash(options?.avatar) ), - options.size || 128, - options.format + options?.size || 128, + options?.format ) : bot.constants.endpoints.USER_DEFAULT_AVATAR(Number(discriminator) % 5); } diff --git a/src/helpers/members/banMember.ts b/src/helpers/members/banMember.ts index fe4bc9655..e0e7cad44 100644 --- a/src/helpers/members/banMember.ts +++ b/src/helpers/members/banMember.ts @@ -3,7 +3,7 @@ import type { Bot } from "../../bot.ts"; /** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */ export async function banMember(bot: Bot, guildId: bigint, id: bigint, options?: CreateGuildBan) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "put", bot.constants.endpoints.GUILD_BAN(guildId, id), diff --git a/src/helpers/members/disconnectMember.ts b/src/helpers/members/disconnectMember.ts deleted file mode 100644 index 3eadbc25a..000000000 --- a/src/helpers/members/disconnectMember.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** Kicks a member from a voice channel */ -export function disconnectMember(bot: Bot, guildId: bigint, memberId: bigint) { - return bot.helpers.editMember(guildId, memberId, { channelId: null }); -} diff --git a/src/helpers/members/editMember.ts b/src/helpers/members/editMember.ts index fc7b6d8e3..721bc1a4d 100644 --- a/src/helpers/members/editMember.ts +++ b/src/helpers/members/editMember.ts @@ -1,6 +1,5 @@ import type { ModifyGuildMember } from "../../types/guilds/modifyGuildMember.ts"; import type { GuildMemberWithUser } from "../../types/members/guildMember.ts"; -import type { PermissionStrings } from "../../types/permissions/permissionStrings.ts"; import type { Bot } from "../../bot.ts"; /** Edit the member */ diff --git a/src/helpers/members/fetchMembers.ts b/src/helpers/members/fetchMembers.ts index eaa38ef08..f941bdcdd 100644 --- a/src/helpers/members/fetchMembers.ts +++ b/src/helpers/members/fetchMembers.ts @@ -6,9 +6,6 @@ import { GatewayOpcodes } from "../../types/codes/gatewayOpcodes.ts"; import type { DiscordenoMember } from "../../transformers/member.ts"; /** - * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.get() - * - * ADVANCED: * Highly recommended to use this function to fetch members instead of getMember from REST. * REST: 50/s global(across all shards) rate limit with ALL requests this included * GW(this function): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is now 960/m. diff --git a/src/helpers/members/getDmChannel.ts b/src/helpers/members/getDmChannel.ts index 177e03d84..8411c8ea2 100644 --- a/src/helpers/members/getDmChannel.ts +++ b/src/helpers/members/getDmChannel.ts @@ -1,12 +1,13 @@ import type { Channel } from "../../types/channels/channel.ts"; import type { Bot } from "../../bot.ts"; -/** Get a user's dm channel. This is required in order to send a DM. */ +/** Get a user's dm channel. This is required in order to send a DM. */ export async function getDmChannel(bot: Bot, userId: bigint) { if (userId === bot.id) throw new Error(bot.constants.Errors.YOU_CAN_NOT_DM_THE_BOT_ITSELF); const dmChannelData = await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.USER_DM, { recipient_id: userId.toString(), }); + return bot.transformers.channel(bot, { channel: dmChannelData }); } diff --git a/src/helpers/members/getMembers.ts b/src/helpers/members/getMembers.ts index 0da8fc150..a4c14ea90 100644 --- a/src/helpers/members/getMembers.ts +++ b/src/helpers/members/getMembers.ts @@ -5,54 +5,25 @@ import { Collection } from "../../util/collection.ts"; import type { DiscordenoMember } from "../../transformers/member.ts"; /** - * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.get() - * - * ADVANCED: * Highly recommended to **NOT** use this function to get members instead use fetchMembers(). * REST(this function): 50/s global(across all shards) rate limit with ALL requests this included * GW(fetchMembers): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is 960/m. */ export async function getMembers(bot: Bot, guildId: bigint, options: ListGuildMembers & { memberCount: number }) { - const members = new Collection(); - - let membersLeft = options?.limit ?? options.memberCount; - let loops = 1; - while ((options?.limit ?? options.memberCount) > members.size && membersLeft > 0) { - bot.events.debug("Running while loop in getMembers function."); - - if (options?.limit && options.limit > 1000) { - console.log(`Paginating get members from REST. #${loops} / ${Math.ceil((options?.limit ?? 1) / 1000)}`); + const result = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_MEMBERS(guildId), + { + limit: options?.limit ?? options.memberCount, + after: options?.after, } + ); - const result = await bot.rest.runMethod( - bot.rest, - "get", - `${bot.constants.endpoints.GUILD_MEMBERS(guildId)}?limit=${membersLeft > 1000 ? 1000 : membersLeft}${ - options?.after ? `&after=${options.after}` : "" - }` - ); - - const discordenoMembers = result.map((member) => - bot.transformers.member(bot, member, guildId, bot.transformers.snowflake(member.user.id)) - ); - - if (!discordenoMembers.length) break; - - discordenoMembers.forEach((member) => { - bot.events.debug(`Running forEach loop in get_members file.`); - members.set(member.id, member); - }); - - options = { - limit: options?.limit, - after: discordenoMembers[discordenoMembers.length - 1].id.toString(), - memberCount: options.memberCount, - }; - - membersLeft -= 1000; - - loops++; - } - - return members; + return new Collection( + result.map((res) => { + const member = bot.transformers.member(bot, res, guildId, bot.transformers.snowflake(res.user.id)); + return [member.id, member]; + }) + ); } diff --git a/src/helpers/members/kickMember.ts b/src/helpers/members/kickMember.ts index c2dd61543..d6145521b 100644 --- a/src/helpers/members/kickMember.ts +++ b/src/helpers/members/kickMember.ts @@ -2,10 +2,7 @@ import { Bot } from "../../bot.ts"; /** Kick a member from the server */ export async function kickMember(bot: Bot, guildId: bigint, memberId: bigint, reason?: string) { - return await bot.rest.runMethod( - bot.rest, - "delete", - bot.constants.endpoints.GUILD_MEMBER(guildId, memberId), - { reason } - ); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_MEMBER(guildId, memberId), { + reason, + }); } diff --git a/src/helpers/members/moveMember.ts b/src/helpers/members/moveMember.ts deleted file mode 100644 index 55a5b0e47..000000000 --- a/src/helpers/members/moveMember.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Bot } from "../../bot.ts"; - -/** - * Move a member from a voice channel to another. - * @param bot the bot - * @param guildId the id of the guild which the channel exists in - * @param memberId the id of the member to move. - * @param channelId id of channel to move user to (if they are connected to voice) - */ -export function moveMember(bot: Bot, guildId: bigint, memberId: bigint, channelId: bigint) { - return bot.helpers.editMember(guildId, memberId, { channelId }); -} diff --git a/src/helpers/members/searchMembers.ts b/src/helpers/members/searchMembers.ts index 96d83bd1c..913f902a3 100644 --- a/src/helpers/members/searchMembers.ts +++ b/src/helpers/members/searchMembers.ts @@ -4,17 +4,13 @@ import { Collection } from "../../util/collection.ts"; import { Bot } from "../../bot.ts"; /** - * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.filter() - * @param bot - * @param guildId - * @param query Query string to match username(s) and nickname(s) against - * @param options + * Query string to match username(s) and nickname(s) against */ export async function searchMembers( bot: Bot, guildId: bigint, query: string, - options?: Omit & { cache?: boolean } + options?: Omit ) { if (options?.limit) { if (options.limit < 1) throw new Error(bot.constants.Errors.MEMBER_SEARCH_LIMIT_TOO_LOW); diff --git a/src/helpers/members/unbanMember.ts b/src/helpers/members/unbanMember.ts index 71bc569c5..2dd874620 100644 --- a/src/helpers/members/unbanMember.ts +++ b/src/helpers/members/unbanMember.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Remove the ban for a user. Requires BAN_MEMBERS permission */ export async function unbanMember(bot: Bot, guildId: bigint, id: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_BAN(guildId, id)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_BAN(guildId, id)); } diff --git a/src/helpers/messages/addReaction.ts b/src/helpers/messages/addReaction.ts index 1f4390a49..855f00e70 100644 --- a/src/helpers/messages/addReaction.ts +++ b/src/helpers/messages/addReaction.ts @@ -8,7 +8,7 @@ export async function addReaction(bot: Bot, channelId: bigint, messageId: bigint reaction = reaction.substring(3, reaction.length - 1); } - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "put", bot.constants.endpoints.CHANNEL_MESSAGE_REACTION_ME(channelId, messageId, encodeURIComponent(reaction)), diff --git a/src/helpers/messages/deleteMessage.ts b/src/helpers/messages/deleteMessage.ts index 043361767..875e404ea 100644 --- a/src/helpers/messages/deleteMessage.ts +++ b/src/helpers/messages/deleteMessage.ts @@ -1,4 +1,3 @@ -// import { cacheHandlers } from "../../cache.ts"; import type { Bot } from "../../bot.ts"; /** Delete a message with the channel id and message id only. */ @@ -11,7 +10,7 @@ export async function deleteMessage( ) { if (delayMilliseconds) await bot.utils.delay(delayMilliseconds); - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.CHANNEL_MESSAGE(channelId, messageId), diff --git a/src/helpers/messages/deleteMessages.ts b/src/helpers/messages/deleteMessages.ts index c7c8a9249..591ad79a8 100644 --- a/src/helpers/messages/deleteMessages.ts +++ b/src/helpers/messages/deleteMessages.ts @@ -10,7 +10,7 @@ export async function deleteMessages(bot: Bot, channelId: bigint, ids: bigint[], console.warn(`This endpoint only accepts a maximum of 100 messages. Deleting the first 100 message ids provided.`); } - return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.CHANNEL_BULK_DELETE(channelId), { + await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.CHANNEL_BULK_DELETE(channelId), { messages: ids.splice(0, 100).map((id) => id.toString()), reason, }); diff --git a/src/helpers/messages/editMessage.ts b/src/helpers/messages/editMessage.ts index f77efec96..05feb4360 100644 --- a/src/helpers/messages/editMessage.ts +++ b/src/helpers/messages/editMessage.ts @@ -88,6 +88,18 @@ export async function editMessage(bot: Bot, channelId: bigint, messageId: bigint components: content.components?.map((component) => ({ type: component.type, components: component.components.map((subcomponent) => { + if (subcomponent.type === MessageComponentTypes.InputText) { + return { + type: subcomponent.type, + style: subcomponent.style, + custom_id: subcomponent.customId, + label: subcomponent.label, + placeholder: subcomponent.placeholder, + min_length: subcomponent.minLength ?? subcomponent.required === false ? 0 : subcomponent.minLength, + max_length: subcomponent.maxLength, + }; + } + if (subcomponent.type === MessageComponentTypes.SelectMenu) return { type: subcomponent.type, @@ -115,15 +127,16 @@ export async function editMessage(bot: Bot, channelId: bigint, messageId: bigint custom_id: subcomponent.customId, label: subcomponent.label, style: subcomponent.style, - emoji: subcomponent.emoji - ? { - id: subcomponent.emoji.id?.toString(), - name: subcomponent.emoji.name, - animated: subcomponent.emoji.animated, - } - : undefined, - url: subcomponent.url, - disabled: subcomponent.disabled, + emoji: + "emoji" in subcomponent && subcomponent.emoji + ? { + id: subcomponent.emoji.id?.toString(), + name: subcomponent.emoji.name, + animated: subcomponent.emoji.animated, + } + : undefined, + url: "url" in subcomponent ? subcomponent.url : undefined, + disabled: "disabled" in subcomponent ? subcomponent.disabled : undefined, }; }), })), diff --git a/src/helpers/messages/pinMessage.ts b/src/helpers/messages/pinMessage.ts index cf5eff916..8421c2ebd 100644 --- a/src/helpers/messages/pinMessage.ts +++ b/src/helpers/messages/pinMessage.ts @@ -2,9 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */ export async function pinMessage(bot: Bot, channelId: bigint, messageId: bigint) { - return await bot.rest.runMethod( - bot.rest, - "put", - bot.constants.endpoints.CHANNEL_PIN(channelId, messageId) - ); + await bot.rest.runMethod(bot.rest, "put", bot.constants.endpoints.CHANNEL_PIN(channelId, messageId)); } diff --git a/src/helpers/messages/removeAllReactions.ts b/src/helpers/messages/removeAllReactions.ts index d4f18e13a..3a58c4729 100644 --- a/src/helpers/messages/removeAllReactions.ts +++ b/src/helpers/messages/removeAllReactions.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; /** Removes all reactions for all emojis on this message. */ export async function removeAllReactions(bot: Bot, channelId: bigint, messageId: bigint) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.CHANNEL_MESSAGE_REACTIONS(channelId, messageId) diff --git a/src/helpers/messages/removeReaction.ts b/src/helpers/messages/removeReaction.ts index e15274565..4dbe2b362 100644 --- a/src/helpers/messages/removeReaction.ts +++ b/src/helpers/messages/removeReaction.ts @@ -14,7 +14,7 @@ export async function removeReaction( reaction = reaction.substring(3, reaction.length - 1); } - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", options?.userId diff --git a/src/helpers/messages/removeReactionEmoji.ts b/src/helpers/messages/removeReactionEmoji.ts index 252b02d80..590876429 100644 --- a/src/helpers/messages/removeReactionEmoji.ts +++ b/src/helpers/messages/removeReactionEmoji.ts @@ -8,7 +8,7 @@ export async function removeReactionEmoji(bot: Bot, channelId: bigint, messageId reaction = reaction.substring(3, reaction.length - 1); } - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.CHANNEL_MESSAGE_REACTION(channelId, messageId, reaction) diff --git a/src/helpers/messages/sendMessage.ts b/src/helpers/messages/sendMessage.ts index 26b2e7eba..fbc498223 100644 --- a/src/helpers/messages/sendMessage.ts +++ b/src/helpers/messages/sendMessage.ts @@ -103,6 +103,18 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: string | components: content.components?.map((component) => ({ type: component.type, components: component.components.map((subcomponent) => { + if (subcomponent.type === MessageComponentTypes.InputText) { + return { + type: subcomponent.type, + style: subcomponent.style, + custom_id: subcomponent.customId, + label: subcomponent.label, + placeholder: subcomponent.placeholder, + min_length: subcomponent.minLength ?? subcomponent.required === false ? 0 : subcomponent.minLength, + max_length: subcomponent.maxLength, + }; + } + if (subcomponent.type === MessageComponentTypes.SelectMenu) return { type: subcomponent.type, @@ -130,15 +142,16 @@ export async function sendMessage(bot: Bot, channelId: bigint, content: string | custom_id: subcomponent.customId, label: subcomponent.label, style: subcomponent.style, - emoji: subcomponent.emoji - ? { - id: subcomponent.emoji.id?.toString(), - name: subcomponent.emoji.name, - animated: subcomponent.emoji.animated, - } - : undefined, - url: subcomponent.url, - disabled: subcomponent.disabled, + emoji: + "emoji" in subcomponent && subcomponent.emoji + ? { + id: subcomponent.emoji.id?.toString(), + name: subcomponent.emoji.name, + animated: subcomponent.emoji.animated, + } + : undefined, + url: "url" in subcomponent ? subcomponent.url : undefined, + disabled: "disabled" in subcomponent ? subcomponent.disabled : undefined, }; }), })), diff --git a/src/helpers/messages/unpinMessage.ts b/src/helpers/messages/unpinMessage.ts index 6962f6d70..8e9f628c7 100644 --- a/src/helpers/messages/unpinMessage.ts +++ b/src/helpers/messages/unpinMessage.ts @@ -1,10 +1,6 @@ /** Unpin a message in a channel. Requires MANAGE_MESSAGES. */ import type { Bot } from "../../bot.ts"; -export async function unpinMessage(bot: Bot, channelId: bigint, messageId: bigint): Promise { - return await bot.rest.runMethod( - bot.rest, - "delete", - bot.constants.endpoints.CHANNEL_PIN(channelId, messageId) - ); +export async function unpinMessage(bot: Bot, channelId: bigint, messageId: bigint) { + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.CHANNEL_PIN(channelId, messageId)); } diff --git a/src/helpers/misc/editBotProfile.ts b/src/helpers/misc/editBotProfile.ts index 8e26d42a7..eb55b1a2e 100644 --- a/src/helpers/misc/editBotProfile.ts +++ b/src/helpers/misc/editBotProfile.ts @@ -26,8 +26,10 @@ export async function editBotProfile(bot: Bot, options: { username?: string; bot const avatar = options?.botAvatarURL ? await bot.utils.urlToBase64(options?.botAvatarURL) : options?.botAvatarURL; - return await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.USER_BOT, { + const result = await bot.rest.runMethod(bot.rest, "patch", bot.constants.endpoints.USER_BOT, { username: options.username?.trim(), avatar, }); + + return bot.transformers.user(bot, result); } diff --git a/src/helpers/misc/editBotStatus.ts b/src/helpers/misc/editBotStatus.ts index c45edfc38..b2c38fb12 100644 --- a/src/helpers/misc/editBotStatus.ts +++ b/src/helpers/misc/editBotStatus.ts @@ -7,11 +7,50 @@ export function editBotStatus(bot: Bot, data: Omit ({ + name: activity.name, + type: activity.type, + url: activity.url, + created_at: activity.createdAt, + timestamps: activity.timestamps + ? { + start: activity.timestamps.start, + end: activity.timestamps.end, + } + : undefined, + applicationId: activity.applicationId?.toString(), + details: activity.details, + state: activity.state, + emoji: activity.emoji + ? { + name: activity.emoji.name, + id: activity.emoji.id?.toString(), + animated: activity.emoji.animated, + } + : undefined, + party: activity.party + ? { + id: activity.party.id?.toString(), + size: activity.party.size, + } + : undefined, + assets: activity.assets + ? { + large_image: activity.assets.largeImage, + large_text: activity.assets.largeText, + small_image: activity.assets.smallImage, + small_text: activity.assets.smallText, + } + : undefined, + secrets: activity.secrets, + instance: activity.instance, + flags: activity.flags, + buttons: activity.buttons, + })), status: data.status, }, }); diff --git a/src/helpers/misc/getUser.ts b/src/helpers/misc/getUser.ts index 8b6a170c6..c56638fc0 100644 --- a/src/helpers/misc/getUser.ts +++ b/src/helpers/misc/getUser.ts @@ -4,5 +4,7 @@ import { SnakeCasedPropertiesDeep } from "../../types/util.ts"; /** This function will return the raw user payload in the rare cases you need to fetch a user directly from the API. */ export async function getUser(bot: Bot, userId: bigint) { - return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.USER(userId)); + const result = await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.USER(userId)); + + return bot.transformers.user(bot, result); } diff --git a/src/helpers/mod.ts b/src/helpers/mod.ts index 523aa4b60..25d374eef 100644 --- a/src/helpers/mod.ts +++ b/src/helpers/mod.ts @@ -1,5 +1,4 @@ //channels -export * from "./channels/channelOverwriteHasPermission.ts"; export * from "./channels/createChannel.ts"; export * from "./channels/deleteChannel.ts"; export * from "./channels/deleteChannelOverwrite.ts"; @@ -12,11 +11,12 @@ export * from "./channels/getChannelWebhooks.ts"; export * from "./channels/getPins.ts"; export * from "./channels/startTyping.ts"; export * from "./channels/swapChannels.ts"; -export * from "./channels/updateBotVoiceState.ts"; +export * from "./channels/updateVoiceState.ts"; //discovery export * from "./discovery/addDiscoverySubcategory.ts"; export * from "./discovery/editDiscovery.ts"; +export * from "./discovery/getDiscovery.ts"; export * from "./discovery/getDiscoveryCategories.ts"; export * from "./discovery/removeDiscoverySubcategory.ts"; export * from "./discovery/validDiscoveryTerm.ts"; @@ -70,6 +70,9 @@ export * from "./interactions/commands/getApplicationCommandPermission.ts"; export * from "./interactions/commands/getApplicationCommandPermissions.ts"; export * from "./interactions/commands/upsertApplicationCommand.ts"; export * from "./interactions/commands/upsertApplicationCommands.ts"; +export * from "./interactions/followups/deleteFollowupMessage.ts"; +export * from "./interactions/followups/editFollowupMessage.ts"; +export * from "./interactions/followups/getFollowupMessage.ts"; export * from "./interactions/getOriginalInteractionResponse.ts"; export * from "./interactions/sendInteractionResponse.ts"; @@ -83,14 +86,12 @@ export * from "./invites/getInvites.ts"; //members export * from "./members/avatarUrl.ts"; export * from "./members/banMember.ts"; -export * from "./members/disconnectMember.ts"; export * from "./members/editBotNickname.ts"; export * from "./members/editMember.ts"; export * from "./members/fetchMembers.ts"; export * from "./members/getMember.ts"; export * from "./members/getMembers.ts"; export * from "./members/kickMember.ts"; -export * from "./members/moveMember.ts"; export * from "./members/pruneMembers.ts"; export * from "./members/getDmChannel.ts"; export * from "./members/unbanMember.ts"; @@ -159,8 +160,6 @@ export * from "./channels/getStageInstance.ts"; export * from "./channels/deleteStageInstance.ts"; export * from "./voice/connectToVoiceChannel.ts"; export * from "./channels/threads/addToThread.ts"; -export * from "./channels/threads/deleteThread.ts"; -export * from "./channels/threads/editThread.ts"; export * from "./channels/threads/getActiveThreads.ts"; export * from "./channels/threads/getArchivedThreads.ts"; export * from "./channels/threads/getThreadMember.ts"; @@ -170,7 +169,6 @@ export * from "./channels/threads/leaveThread.ts"; export * from "./channels/threads/removeThreadMember.ts"; export * from "./channels/threads/startThreadWithMessage.ts"; export * from "./channels/threads/startThreadWithoutMessage.ts"; -export * from "./channels/cloneChannel.ts"; //guilds export * from "./guilds/scheduledEvents/createScheduledEvent.ts"; diff --git a/src/helpers/roles/addRole.ts b/src/helpers/roles/addRole.ts index 2e1a618f2..e668c2b66 100644 --- a/src/helpers/roles/addRole.ts +++ b/src/helpers/roles/addRole.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; /** Add a role to the member */ export async function addRole(bot: Bot, guildId: bigint, memberId: bigint, roleId: bigint, reason?: string) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "put", bot.constants.endpoints.GUILD_MEMBER_ROLE(guildId, memberId, roleId), diff --git a/src/helpers/roles/deleteRole.ts b/src/helpers/roles/deleteRole.ts index af217256a..7cb8cb24b 100644 --- a/src/helpers/roles/deleteRole.ts +++ b/src/helpers/roles/deleteRole.ts @@ -2,5 +2,5 @@ import type { Bot } from "../../bot.ts"; /** Delete a guild role. Requires the MANAGE_ROLES permission. */ export async function deleteRole(bot: Bot, guildId: bigint, id: bigint) { - return await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_ROLE(guildId, id)); + await bot.rest.runMethod(bot.rest, "delete", bot.constants.endpoints.GUILD_ROLE(guildId, id)); } diff --git a/src/helpers/roles/removeRole.ts b/src/helpers/roles/removeRole.ts index b45a7a788..f724691ae 100644 --- a/src/helpers/roles/removeRole.ts +++ b/src/helpers/roles/removeRole.ts @@ -2,7 +2,7 @@ import type { Bot } from "../../bot.ts"; /** Remove a role from the member */ export async function removeRole(bot: Bot, guildId: bigint, memberId: bigint, roleId: bigint, reason?: string) { - return await bot.rest.runMethod( + await bot.rest.runMethod( bot.rest, "delete", bot.constants.endpoints.GUILD_MEMBER_ROLE(guildId, memberId, roleId), diff --git a/src/helpers/templates/deleteGuildTemplate.ts b/src/helpers/templates/deleteGuildTemplate.ts index 720b34c27..ac297ecee 100644 --- a/src/helpers/templates/deleteGuildTemplate.ts +++ b/src/helpers/templates/deleteGuildTemplate.ts @@ -6,7 +6,7 @@ import type { Bot } from "../../bot.ts"; * Requires the `MANAGE_GUILD` permission. */ export async function deleteGuildTemplate(bot: Bot, guildId: bigint, templateCode: string) { - return await bot.rest.runMethod