Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for user-installable apps #782

Merged
merged 4 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"prepack:esm": "rollup-type-bundler -d dist/esm -t .mts"
},
"dependencies": {
"@discordjs/builders": "^1.8.2",
"@sapphire/discord-utilities": "^3.4.0",
"@discordjs/builders": "^1.9.0",
"@sapphire/discord-utilities": "^3.4.1",
"@sapphire/discord.js-utilities": "^7.3.0",
"@sapphire/lexure": "^1.1.7",
"@sapphire/pieces": "^4.3.1",
Expand All @@ -60,7 +60,7 @@
"@vitest/coverage-v8": "^2.0.5",
"concurrently": "^8.2.2",
"cz-conventional-changelog": "^3.3.0",
"discord.js": "^14.15.3",
"discord.js": "^14.16.1",
"esbuild-plugin-file-path-extensions": "^2.1.2",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { InteractionContextType } from 'discord.js';
import type { CommandDifference } from './_shared';

export function* checkInteractionContextTypes(
existingContexts?: InteractionContextType[],
newContexts?: InteractionContextType[]
): Generator<CommandDifference> {
// 0. No existing contexts and now we have contexts
if (!existingContexts && newContexts?.length) {
yield {
key: 'contexts',
original: 'no contexts present',
expected: 'contexts present'
};
}
// 1. Existing contexts and now we have no contexts
else if (existingContexts?.length && !newContexts?.length) {
yield {
key: 'contexts',
original: 'contexts present',
expected: 'no contexts present'
};
}
// 2. Maybe changes in order or additions, log
else if (newContexts?.length) {
let index = 0;

for (const newContext of newContexts) {
const currentIndex = index++;

if (existingContexts![currentIndex] !== newContext) {
yield {
key: `contexts[${currentIndex}]`,
original: `contexts type ${existingContexts?.[currentIndex]}`,
expected: `contexts type ${newContext}`
};
}
}

if (index < existingContexts!.length) {
let type: InteractionContextType;

while ((type = existingContexts![index]) !== undefined) {
yield {
key: `contexts[${index}]`,
original: `context ${type} present`,
expected: `no context present`
};

index++;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { ApplicationIntegrationType } from 'discord.js';
import type { CommandDifference } from './_shared';

export function* checkIntegrationTypes(
existingIntegrationTypes?: ApplicationIntegrationType[],
newIntegrationTypes?: ApplicationIntegrationType[]
): Generator<CommandDifference> {
// 0. No existing integration types and now we have integration types
if (!existingIntegrationTypes?.length && newIntegrationTypes?.length) {
yield {
key: 'integrationTypes',
original: 'no integration types present',
expected: 'integration types present'
};
}
// 1. Existing integration types and now we have no integration types
else if (existingIntegrationTypes?.length && !newIntegrationTypes?.length) {
yield {
key: 'integrationTypes',
original: 'integration types present',
expected: 'no integration types present'
};
}
// 2. Maybe changes in order or additions, log
else if (newIntegrationTypes?.length) {
let index = 0;

for (const newIntegrationType of newIntegrationTypes) {
const currentIndex = index++;

if (existingIntegrationTypes![currentIndex] !== newIntegrationType) {
yield {
key: `integrationTypes[${currentIndex}]`,
original: `integration type ${existingIntegrationTypes?.[currentIndex]}`,
expected: `integration type ${newIntegrationType}`
};
}
}

if (index < existingIntegrationTypes!.length) {
let type: ApplicationIntegrationType;

while ((type = existingIntegrationTypes![index]) !== undefined) {
yield {
key: `integrationTypes[${index}]`,
original: `integration type ${type} present`,
expected: 'no integration type present'
};

index++;
}
}
}
}
14 changes: 14 additions & 0 deletions src/lib/utils/application-commands/computeDifferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import { checkLocalizations } from './compute-differences/localizations';
import { checkName } from './compute-differences/name';
import { checkOptions } from './compute-differences/options';
import { checkIntegrationTypes } from './compute-differences/integration_types';
import { checkInteractionContextTypes } from './compute-differences/contexts';

/**
* @returns `true` if there are differences, `false` otherwise
Expand Down Expand Up @@ -44,7 +46,7 @@

// Check dmPermission only for non-guild commands
if (!guildCommand) {
yield* checkDMPermission(existingCommand.dm_permission, casted.dm_permission);

Check warning on line 49 in src/lib/utils/application-commands/computeDifferences.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead

Check warning on line 49 in src/lib/utils/application-commands/computeDifferences.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead
}

// Check defaultMemberPermissions
Expand All @@ -61,6 +63,12 @@
originalLocalizedDescriptions: originalLocalizedNames,
expectedLocalizedDescriptions: expectedLocalizedNames
});

// Check integration types
yield* checkIntegrationTypes(existingCommand.integration_types, casted.integration_types);

// Check contexts
yield* checkInteractionContextTypes(existingCommand.contexts, casted.contexts);
}

return;
Expand All @@ -85,7 +93,7 @@

// Check dmPermission
if (!guildCommand) {
yield* checkDMPermission(existingCommand.dm_permission, casted.dm_permission);

Check warning on line 96 in src/lib/utils/application-commands/computeDifferences.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead

Check warning on line 96 in src/lib/utils/application-commands/computeDifferences.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead
}

// Check defaultMemberPermissions
Expand All @@ -106,5 +114,11 @@
expectedLocalizedDescriptions
});

// Check integration types
yield* checkIntegrationTypes(existingCommand.integration_types, casted.integration_types);

// Check contexts
yield* checkInteractionContextTypes(existingCommand.contexts, casted.contexts);

yield* checkOptions(existingCommand.options, casted.options);
}
14 changes: 11 additions & 3 deletions src/lib/utils/application-commands/normalizeInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import {
ApplicationCommand,
PermissionsBitField,
type ApplicationIntegrationType,
type ChatInputApplicationCommandData,
type InteractionContextType,
type MessageApplicationCommandData,
type UserApplicationCommandData
} from 'discord.js';
Expand All @@ -31,7 +33,7 @@
}

function addDefaultsToChatInputJSON(data: RESTPostAPIChatInputApplicationCommandsJSONBody): RESTPostAPIChatInputApplicationCommandsJSONBody {
data.dm_permission ??= true;

Check warning on line 36 in src/lib/utils/application-commands/normalizeInputs.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead
data.type ??= ApplicationCommandType.ChatInput;
data.default_member_permissions ??= null;

Expand All @@ -39,7 +41,7 @@
}

function addDefaultsToContextMenuJSON(data: RESTPostAPIContextMenuApplicationCommandsJSONBody): RESTPostAPIContextMenuApplicationCommandsJSONBody {
data.dm_permission ??= true;

Check warning on line 44 in src/lib/utils/application-commands/normalizeInputs.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dm_permission' is deprecated. Use `contexts` instead
data.default_member_permissions ??= null;

return data;
Expand Down Expand Up @@ -71,7 +73,9 @@
description_localizations: command.descriptionLocalizations,
type: ApplicationCommandType.ChatInput,
dm_permission: command.dmPermission,
nsfw: command.nsfw
nsfw: command.nsfw,
integration_types: command.integrationTypes as ApplicationIntegrationType[] | undefined,
contexts: command.contexts as InteractionContextType[] | undefined
};

if (typeof command.defaultMemberPermissions !== 'undefined') {
Expand Down Expand Up @@ -108,7 +112,9 @@
name_localizations: command.nameLocalizations,
type: command.type,
dm_permission: command.dmPermission,
nsfw: command.nsfw
nsfw: command.nsfw,
integration_types: command.integrationTypes as ApplicationIntegrationType[] | undefined,
contexts: command.contexts as InteractionContextType[] | undefined
};

if (typeof command.defaultMemberPermissions !== 'undefined') {
Expand All @@ -123,9 +129,11 @@
const returnData = {
name: command.name,
name_localizations: command.nameLocalizations,
dm_permission: command.dmPermission,

Check warning on line 132 in src/lib/utils/application-commands/normalizeInputs.ts

View workflow job for this annotation

GitHub Actions / Linting / Linting / Run yarn job

'dmPermission' is deprecated. Use {@link ApplicationCommand.contexts } instead
nsfw: command.nsfw,
default_member_permissions: command.defaultMemberPermissions?.bitfield.toString() ?? null
default_member_permissions: command.defaultMemberPermissions?.bitfield.toString() ?? null,
integration_types: command.integrationTypes,
contexts: command.contexts
} as RESTPostAPIApplicationCommandsJSONBody;

if (command.type === ApplicationCommandType.ChatInput) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isStageChannel } from '@sapphire/discord.js-utilities';
import type { Message } from 'discord.js';
import { ChannelType, type Message } from 'discord.js';
import { Listener } from '../../lib/structures/Listener';
import type { MessageCommand } from '../../lib/types/CommandTypes';
import { Events, type MessageCommandRunPayload } from '../../lib/types/Events';
Expand All @@ -15,6 +15,10 @@ export class CoreListener extends Listener<typeof Events.MessageCommandRun> {
return;
}

if (message.channel.type === ChannelType.GroupDM) {
return;
}

try {
await message.channel.sendTyping();
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isDMChannel } from '@sapphire/discord.js-utilities';
import { PermissionFlagsBits, PermissionsBitField, type Message } from 'discord.js';
import { ChannelType, PermissionFlagsBits, PermissionsBitField, type Message } from 'discord.js';
import { Listener } from '../../lib/structures/Listener';
import { Events } from '../../lib/types/Events';

Expand Down Expand Up @@ -40,6 +40,7 @@ export class CoreListener extends Listener<typeof Events.PreMessageParsed> {
}

private async canRunInChannel(message: Message): Promise<boolean> {
if (message.channel.type === ChannelType.GroupDM) return false;
if (isDMChannel(message.channel)) return true;

const me = await message.guild?.members.fetchMe();
Expand Down
1 change: 1 addition & 0 deletions src/preconditions/ClientPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export class CorePrecondition extends AllFlowsPrecondition {
Stream: 'Stream',
UseApplicationCommands: 'Use Application Commands',
UseEmbeddedActivities: 'Start Activities',
UseExternalApps: 'Use External Apps',
UseExternalEmojis: 'Use External Emojis',
UseExternalSounds: 'Use External Sounds',
UseExternalStickers: 'Use External Stickers',
Expand Down
Loading
Loading