diff --git a/src/bot.ts b/src/bot.ts index 3ee562630..7d2b12a17 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -91,6 +91,7 @@ import { transformWebhook } from "./transformers/webhook.ts"; import { transformAuditlogEntry } from "./transformers/auditlogEntry.ts"; import { transformApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; import { StatusUpdate } from "./types/gateway/status_update.ts"; +import { calculateBits, calculatePermissions } from "./util/permissions.ts"; type CacheOptions = | { @@ -307,6 +308,8 @@ export function createUtils(options: Partial) { urlToBase64, formatImageURL, validateSlashCommands, + calculateBits, + calculatePermissions, }; } @@ -325,6 +328,8 @@ export interface HelperUtils { urlToBase64: typeof urlToBase64; formatImageURL: typeof formatImageURL; validateSlashCommands: typeof validateSlashCommands; + calculateBits: typeof calculateBits; + calculatePermissions: typeof calculatePermissions; } export function createGatewayManager( diff --git a/src/helpers/interactions/commands/create_slash_command.ts b/src/helpers/interactions/commands/create_slash_command.ts index a722f2db4..1fc7e0b78 100644 --- a/src/helpers/interactions/commands/create_slash_command.ts +++ b/src/helpers/interactions/commands/create_slash_command.ts @@ -1,6 +1,7 @@ import type { ApplicationCommand } from "../../../types/interactions/commands/application_command.ts"; import type { CreateGlobalApplicationCommand } from "../../../types/interactions/commands/create_global_application_command.ts"; import type { Bot } from "../../../bot.ts"; +import { ApplicationCommandOption } from "../../../types/interactions/commands/application_command_option.ts"; /** * There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means: @@ -22,6 +23,24 @@ export async function createSlashCommand(bot: Bot, options: CreateGlobalApplicat guildId ? bot.constants.endpoints.COMMANDS_GUILD(bot.applicationId, guildId) : bot.constants.endpoints.COMMANDS(bot.applicationId), - options + { + name: options.name, + description: options.description, + type: options.type, + options: options.options ? makeOptionsForCommand(options.options) : undefined, + } ); } + +// @ts-ignore TODO: see if we can make this not circular +export function makeOptionsForCommand(options: ApplicationCommandOption[]) { + return options.map((option) => ({ + type: option.type, + name: option.name, + description: option.description, + required: option.required, + choices: option.choices, + options: option.options ? makeOptionsForCommand(option.options) : undefined, + channel_types: option.channelTypes, + })); +} diff --git a/src/helpers/interactions/commands/upsert_slash_command.ts b/src/helpers/interactions/commands/upsert_slash_command.ts index cb7b2ddf9..5b8ec6869 100644 --- a/src/helpers/interactions/commands/upsert_slash_command.ts +++ b/src/helpers/interactions/commands/upsert_slash_command.ts @@ -1,6 +1,7 @@ import type { ApplicationCommand } from "../../../types/interactions/commands/application_command.ts"; import type { EditGlobalApplicationCommand } from "../../../types/interactions/commands/edit_global_application_command.ts"; import type { Bot } from "../../../bot.ts"; +import { makeOptionsForCommand } from "./create_slash_command.ts"; /** * Edit an existing slash command. If this command did not exist, it will create it. @@ -19,6 +20,11 @@ export async function upsertSlashCommand( guildId ? bot.constants.endpoints.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId) : bot.constants.endpoints.COMMANDS_ID(bot.applicationId, commandId), - options + { + name: options.name, + description: options.description, + type: options.type, + options: options.options ? makeOptionsForCommand(options.options) : undefined, + } ); } diff --git a/src/helpers/members/edit_bot_nickname.ts b/src/helpers/members/edit_bot_nickname.ts index e8bf82546..348f51f14 100644 --- a/src/helpers/members/edit_bot_nickname.ts +++ b/src/helpers/members/edit_bot_nickname.ts @@ -1,14 +1,16 @@ import type { Bot } from "../../bot.ts"; /** Edit the nickname of the bot in this guild */ -export async function editBotNickname(bot: Bot, guildId: bigint, nickname: string | null) { +export async function editBotNickname( + bot: Bot, + guildId: bigint, + options: { nick: string | null; reason?: string } +) { const response = await bot.rest.runMethod<{ nick: string }>( bot.rest, "patch", bot.constants.endpoints.USER_NICK(guildId), - { - nick: nickname, - } + options ); return response.nick; diff --git a/src/transformers/member.ts b/src/transformers/member.ts index 6ad17e644..3d9ad7798 100644 --- a/src/transformers/member.ts +++ b/src/transformers/member.ts @@ -56,6 +56,7 @@ export function transformMember( mute: payload.mute, pending: payload.pending, cachedAt: Date.now(), + avatar: payload.avatar ? bot.utils.iconHashToBigInt(payload.avatar) : undefined, }; } @@ -80,4 +81,6 @@ export interface DiscordenoMember { mute?: boolean; /** Whether or not this member is pending in server verification. */ pending?: boolean; + /** The members avatar for this server. */ + avatar?: bigint; } diff --git a/src/transformers/role.ts b/src/transformers/role.ts index 9c6250fd0..b9ef69900 100644 --- a/src/transformers/role.ts +++ b/src/transformers/role.ts @@ -27,10 +27,12 @@ export function transformRole( ? bot.transformers.snowflake(payload.role.tags.integration_id) : undefined, permissions: bot.transformers.snowflake(payload.role.permissions), + icon: payload.role.icon ? bot.utils.iconHashToBigInt(payload.role.icon) : undefined, + unicodeEmoji: payload.role.unicode_emoji, }; } -export interface DiscordenoRole extends Omit { +export interface DiscordenoRole extends Omit { /** The role id */ id: bigint; /** The bot id that is associated with this role. */ @@ -43,4 +45,8 @@ export interface DiscordenoRole extends Omit `${baseEndpoints.CDN_URL}/embed/avatars/${icon}.png`, USER_DM: `${baseEndpoints.BASE_URL}/users/@me/channels`, USER_CONNECTIONS: `${baseEndpoints.BASE_URL}/users/@me/connections`, - USER_NICK: (guildId: bigint) => `${GUILDS_BASE(guildId)}/members/@me/nick`, + USER_NICK: (guildId: bigint) => `${GUILDS_BASE(guildId)}/members/@me`, // Discovery Endpoints DISCOVERY_CATEGORIES: `${baseEndpoints.BASE_URL}/discovery/categories`, diff --git a/src/util/permissions.ts b/src/util/permissions.ts new file mode 100644 index 000000000..5018f97be --- /dev/null +++ b/src/util/permissions.ts @@ -0,0 +1,22 @@ +import { DiscordBitwisePermissionFlags } from "../types/permissions/bitwise_permission_flags.ts"; +import { PermissionStrings } from "../types/permissions/permission_strings.ts"; + +/** This function converts a bitwise string to permission strings */ +export function calculatePermissions(permissionBits: bigint) { + return Object.keys(DiscordBitwisePermissionFlags).filter((permission) => { + // Since Object.keys() not only returns the permission names but also the bit values we need to return false if it is a Number + if (Number(permission)) return false; + // Check if permissionBits has this permission + return permissionBits & BigInt(DiscordBitwisePermissionFlags[permission as PermissionStrings]); + }) as PermissionStrings[]; +} + +/** This function converts an array of permissions into the bitwise string. */ +export function calculateBits(permissions: PermissionStrings[]) { + return permissions + .reduce((bits, perm) => { + bits |= BigInt(DiscordBitwisePermissionFlags[perm]); + return bits; + }, 0n) + .toString(); +}