Skip to content

Commit e783074

Browse files
feat: add support for user-installable apps (#782)
* refactor: update discord.js Signed-off-by: Seren_Modz 21 <seren@kings-world.net> * fix: correct the ordering of the use external apps permission Signed-off-by: Seren_Modz 21 <seren@kings-world.net> * feat: add in compute difference for new fields * chore: failed my own code writing --------- Signed-off-by: Seren_Modz 21 <seren@kings-world.net> Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
1 parent 40967b3 commit e783074

File tree

9 files changed

+249
-52
lines changed

9 files changed

+249
-52
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
"prepack:esm": "rollup-type-bundler -d dist/esm -t .mts"
3434
},
3535
"dependencies": {
36-
"@discordjs/builders": "^1.8.2",
37-
"@sapphire/discord-utilities": "^3.4.0",
36+
"@discordjs/builders": "^1.9.0",
37+
"@sapphire/discord-utilities": "^3.4.1",
3838
"@sapphire/discord.js-utilities": "^7.3.0",
3939
"@sapphire/lexure": "^1.1.7",
4040
"@sapphire/pieces": "^4.3.1",
@@ -60,7 +60,7 @@
6060
"@vitest/coverage-v8": "^2.0.5",
6161
"concurrently": "^8.2.2",
6262
"cz-conventional-changelog": "^3.3.0",
63-
"discord.js": "^14.15.3",
63+
"discord.js": "^14.16.1",
6464
"esbuild-plugin-file-path-extensions": "^2.1.2",
6565
"esbuild-plugin-version-injector": "^1.2.1",
6666
"eslint": "^8.57.0",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { InteractionContextType } from 'discord.js';
2+
import type { CommandDifference } from './_shared';
3+
4+
export function* checkInteractionContextTypes(
5+
existingContexts?: InteractionContextType[],
6+
newContexts?: InteractionContextType[]
7+
): Generator<CommandDifference> {
8+
// 0. No existing contexts and now we have contexts
9+
if (!existingContexts && newContexts?.length) {
10+
yield {
11+
key: 'contexts',
12+
original: 'no contexts present',
13+
expected: 'contexts present'
14+
};
15+
}
16+
// 1. Existing contexts and now we have no contexts
17+
else if (existingContexts?.length && !newContexts?.length) {
18+
yield {
19+
key: 'contexts',
20+
original: 'contexts present',
21+
expected: 'no contexts present'
22+
};
23+
}
24+
// 2. Maybe changes in order or additions, log
25+
else if (newContexts?.length) {
26+
let index = 0;
27+
28+
for (const newContext of newContexts) {
29+
const currentIndex = index++;
30+
31+
if (existingContexts![currentIndex] !== newContext) {
32+
yield {
33+
key: `contexts[${currentIndex}]`,
34+
original: `contexts type ${existingContexts?.[currentIndex]}`,
35+
expected: `contexts type ${newContext}`
36+
};
37+
}
38+
}
39+
40+
if (index < existingContexts!.length) {
41+
let type: InteractionContextType;
42+
43+
while ((type = existingContexts![index]) !== undefined) {
44+
yield {
45+
key: `contexts[${index}]`,
46+
original: `context ${type} present`,
47+
expected: `no context present`
48+
};
49+
50+
index++;
51+
}
52+
}
53+
}
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { ApplicationIntegrationType } from 'discord.js';
2+
import type { CommandDifference } from './_shared';
3+
4+
export function* checkIntegrationTypes(
5+
existingIntegrationTypes?: ApplicationIntegrationType[],
6+
newIntegrationTypes?: ApplicationIntegrationType[]
7+
): Generator<CommandDifference> {
8+
// 0. No existing integration types and now we have integration types
9+
if (!existingIntegrationTypes?.length && newIntegrationTypes?.length) {
10+
yield {
11+
key: 'integrationTypes',
12+
original: 'no integration types present',
13+
expected: 'integration types present'
14+
};
15+
}
16+
// 1. Existing integration types and now we have no integration types
17+
else if (existingIntegrationTypes?.length && !newIntegrationTypes?.length) {
18+
yield {
19+
key: 'integrationTypes',
20+
original: 'integration types present',
21+
expected: 'no integration types present'
22+
};
23+
}
24+
// 2. Maybe changes in order or additions, log
25+
else if (newIntegrationTypes?.length) {
26+
let index = 0;
27+
28+
for (const newIntegrationType of newIntegrationTypes) {
29+
const currentIndex = index++;
30+
31+
if (existingIntegrationTypes![currentIndex] !== newIntegrationType) {
32+
yield {
33+
key: `integrationTypes[${currentIndex}]`,
34+
original: `integration type ${existingIntegrationTypes?.[currentIndex]}`,
35+
expected: `integration type ${newIntegrationType}`
36+
};
37+
}
38+
}
39+
40+
if (index < existingIntegrationTypes!.length) {
41+
let type: ApplicationIntegrationType;
42+
43+
while ((type = existingIntegrationTypes![index]) !== undefined) {
44+
yield {
45+
key: `integrationTypes[${index}]`,
46+
original: `integration type ${type} present`,
47+
expected: 'no integration type present'
48+
};
49+
50+
index++;
51+
}
52+
}
53+
}
54+
}

src/lib/utils/application-commands/computeDifferences.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { checkDMPermission } from './compute-differences/dm_permission';
1212
import { checkLocalizations } from './compute-differences/localizations';
1313
import { checkName } from './compute-differences/name';
1414
import { checkOptions } from './compute-differences/options';
15+
import { checkIntegrationTypes } from './compute-differences/integration_types';
16+
import { checkInteractionContextTypes } from './compute-differences/contexts';
1517

1618
/**
1719
* @returns `true` if there are differences, `false` otherwise
@@ -61,6 +63,12 @@ export function* getCommandDifferences(
6163
originalLocalizedDescriptions: originalLocalizedNames,
6264
expectedLocalizedDescriptions: expectedLocalizedNames
6365
});
66+
67+
// Check integration types
68+
yield* checkIntegrationTypes(existingCommand.integration_types, casted.integration_types);
69+
70+
// Check contexts
71+
yield* checkInteractionContextTypes(existingCommand.contexts, casted.contexts);
6472
}
6573

6674
return;
@@ -106,5 +114,11 @@ export function* getCommandDifferences(
106114
expectedLocalizedDescriptions
107115
});
108116

117+
// Check integration types
118+
yield* checkIntegrationTypes(existingCommand.integration_types, casted.integration_types);
119+
120+
// Check contexts
121+
yield* checkInteractionContextTypes(existingCommand.contexts, casted.contexts);
122+
109123
yield* checkOptions(existingCommand.options, casted.options);
110124
}

src/lib/utils/application-commands/normalizeInputs.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import {
1515
import {
1616
ApplicationCommand,
1717
PermissionsBitField,
18+
type ApplicationIntegrationType,
1819
type ChatInputApplicationCommandData,
20+
type InteractionContextType,
1921
type MessageApplicationCommandData,
2022
type UserApplicationCommandData
2123
} from 'discord.js';
@@ -71,7 +73,9 @@ export function normalizeChatInputCommand(
7173
description_localizations: command.descriptionLocalizations,
7274
type: ApplicationCommandType.ChatInput,
7375
dm_permission: command.dmPermission,
74-
nsfw: command.nsfw
76+
nsfw: command.nsfw,
77+
integration_types: command.integrationTypes as ApplicationIntegrationType[] | undefined,
78+
contexts: command.contexts as InteractionContextType[] | undefined
7579
};
7680

7781
if (typeof command.defaultMemberPermissions !== 'undefined') {
@@ -108,7 +112,9 @@ export function normalizeContextMenuCommand(
108112
name_localizations: command.nameLocalizations,
109113
type: command.type,
110114
dm_permission: command.dmPermission,
111-
nsfw: command.nsfw
115+
nsfw: command.nsfw,
116+
integration_types: command.integrationTypes as ApplicationIntegrationType[] | undefined,
117+
contexts: command.contexts as InteractionContextType[] | undefined
112118
};
113119

114120
if (typeof command.defaultMemberPermissions !== 'undefined') {
@@ -125,7 +131,9 @@ export function convertApplicationCommandToApiData(command: ApplicationCommand):
125131
name_localizations: command.nameLocalizations,
126132
dm_permission: command.dmPermission,
127133
nsfw: command.nsfw,
128-
default_member_permissions: command.defaultMemberPermissions?.bitfield.toString() ?? null
134+
default_member_permissions: command.defaultMemberPermissions?.bitfield.toString() ?? null,
135+
integration_types: command.integrationTypes,
136+
contexts: command.contexts
129137
} as RESTPostAPIApplicationCommandsJSONBody;
130138

131139
if (command.type === ApplicationCommandType.ChatInput) {

src/optional-listeners/message-command-listeners/CoreMessageCommandTyping.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isStageChannel } from '@sapphire/discord.js-utilities';
2-
import type { Message } from 'discord.js';
2+
import { ChannelType, type Message } from 'discord.js';
33
import { Listener } from '../../lib/structures/Listener';
44
import type { MessageCommand } from '../../lib/types/CommandTypes';
55
import { Events, type MessageCommandRunPayload } from '../../lib/types/Events';
@@ -15,6 +15,10 @@ export class CoreListener extends Listener<typeof Events.MessageCommandRun> {
1515
return;
1616
}
1717

18+
if (message.channel.type === ChannelType.GroupDM) {
19+
return;
20+
}
21+
1822
try {
1923
await message.channel.sendTyping();
2024
} catch (error) {

src/optional-listeners/message-command-listeners/CorePreMessageParser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isDMChannel } from '@sapphire/discord.js-utilities';
2-
import { PermissionFlagsBits, PermissionsBitField, type Message } from 'discord.js';
2+
import { ChannelType, PermissionFlagsBits, PermissionsBitField, type Message } from 'discord.js';
33
import { Listener } from '../../lib/structures/Listener';
44
import { Events } from '../../lib/types/Events';
55

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

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

4546
const me = await message.guild?.members.fetchMe();

src/preconditions/ClientPermissions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export class CorePrecondition extends AllFlowsPrecondition {
154154
Stream: 'Stream',
155155
UseApplicationCommands: 'Use Application Commands',
156156
UseEmbeddedActivities: 'Start Activities',
157+
UseExternalApps: 'Use External Apps',
157158
UseExternalEmojis: 'Use External Emojis',
158159
UseExternalSounds: 'Use External Sounds',
159160
UseExternalStickers: 'Use External Stickers',

0 commit comments

Comments
 (0)