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

Update to djs@14 #276

Merged
merged 3 commits into from
Jul 27, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"typescript"
],
"dependencies": {
"discord.js": "^13.6.0"
"discord.js": "^14.0.3"
},
"devDependencies": {
"@types/jest": "^28.1.2",
Expand Down
38 changes: 22 additions & 16 deletions src/__tests__/GameBoardButtonBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import GameBoardButtonBuilder from '@bot/builder/GameBoardButtonBuilder';
import localize from '@i18n/localize';
import AI from '@tictactoe/AI';
import { Player } from '@tictactoe/Player';
import { MessageButton } from 'discord.js';
import { ActionRow, ButtonComponent } from 'discord.js';

jest.mock('@tictactoe/AI');

Expand All @@ -26,13 +26,15 @@ describe('GameBoardButtonBuilder', () => {
.withBoard(2, [Player.First, Player.None, Player.None, Player.Second])
.toMessageOptions();

expect(options.components).toHaveLength(2);
expect(options.components![0].components).toHaveLength(2);
expect(options.components![1].components).toHaveLength(2);
expect((options.components![0].components[0] as MessageButton).label).toBe('X');
expect((options.components![0].components[1] as MessageButton).label).toBe(' ');
expect((options.components![1].components[0] as MessageButton).label).toBe(' ');
expect((options.components![1].components[1] as MessageButton).label).toBe('O');
const components = <ActionRow<ButtonComponent>[]>options.components;

expect(components).toHaveLength(2);
expect(components[0].toJSON().components).toHaveLength(2);
expect(components[1].toJSON().components).toHaveLength(2);
expect(components[0].toJSON().components[0].label).toBe('X');
expect(components[0].toJSON().components[1].label).toBe(' ');
expect(components[1].toJSON().components[0].label).toBe(' ');
expect(components[1].toJSON().components[1].label).toBe('O');
});

it('should compute board using custom emojies', () => {
Expand All @@ -41,10 +43,12 @@ describe('GameBoardButtonBuilder', () => {
.withBoard(2, [Player.First, Player.Second, Player.Second, Player.First])
.toMessageOptions();

expect((options.components![0].components[0] as MessageButton).emoji?.name).toBe('dog');
expect((options.components![0].components[1] as MessageButton).emoji?.name).toBe('cat');
expect((options.components![1].components[0] as MessageButton).emoji?.name).toBe('cat');
expect((options.components![1].components[1] as MessageButton).emoji?.name).toBe('dog');
const components = <ActionRow<ButtonComponent>[]>options.components;

expect(components[0].toJSON().components[0].emoji?.name).toBe('dog');
expect(components[0].toJSON().components[1].emoji?.name).toBe('cat');
expect(components[1].toJSON().components[0].emoji?.name).toBe('cat');
expect(components[1].toJSON().components[1].emoji?.name).toBe('dog');
});

it.each`
Expand All @@ -63,9 +67,11 @@ describe('GameBoardButtonBuilder', () => {
.withBoard(2, [Player.First, Player.Second, Player.None, Player.None])
.toMessageOptions();

expect((options.components![0].components[0] as MessageButton).disabled).toBeTruthy();
expect((options.components![0].components[1] as MessageButton).disabled).toBeTruthy();
expect((options.components![1].components[0] as MessageButton).disabled).toBeFalsy();
expect((options.components![1].components[1] as MessageButton).disabled).toBeFalsy();
const components = <ActionRow<ButtonComponent>[]>options.components;

expect(components[0].toJSON().components[0].disabled).toBeTruthy();
expect(components[0].toJSON().components[1].disabled).toBeTruthy();
expect(components[1].toJSON().components[0].disabled).toBeFalsy();
expect(components[1].toJSON().components[1].disabled).toBeFalsy();
});
});
23 changes: 12 additions & 11 deletions src/__tests__/GameStateValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import {
Collection,
Guild,
GuildMember,
GuildMemberManager,
GuildMemberRoleManager,
Permissions,
PermissionString,
Role,
PermissionsBitField, PermissionsString, Role,
TextChannel
} from 'discord.js';

Expand All @@ -27,8 +26,10 @@ describe('GameStateValidator', () => {
channel: <TextChannel>{
id: 'TC1',
guild: <Guild>{
me: <GuildMember>{
permissionsIn: _c => <Readonly<Permissions>>{ has: _p => true }
members: <GuildMemberManager>{
me: <GuildMember>{
permissionsIn: _c => <Readonly<PermissionsBitField>>{ has: _p => true }
}
}
}
},
Expand All @@ -37,7 +38,7 @@ describe('GameStateValidator', () => {
roles: <GuildMemberRoleManager>{
cache: new Collection()
},
permissions: <Readonly<Permissions>>{ has: _ => true }
permissions: <Readonly<PermissionsBitField>>{ has: _ => true }
}
};
manager = <GameStateManager>{
Expand All @@ -59,11 +60,11 @@ describe('GameStateValidator', () => {
${GameStateValidator['PERM_LIST']} | ${true}
`('should check for member permissions $permissions', ({ permissions, expected }) => {
const spyError = jest.spyOn(global.console, 'error').mockImplementation();
jest.spyOn(tunnel.channel.guild.me!, 'permissionsIn').mockReturnValue(<
Readonly<Permissions>
>{
has: list => (list as Array<PermissionString>).every(k => permissions.includes(k))
});
jest.spyOn(tunnel.channel.guild.members.me!, 'permissionsIn').mockReturnValue(<
Readonly<PermissionsBitField>
>{
has: list => (list as Array<PermissionsString>).every(k => permissions.includes(k))
});

expect(validator.isInteractionValid(tunnel)).toBe(expected);
expect(spyError).toHaveBeenCalledTimes(expected ? 0 : 1);
Expand Down
4 changes: 2 additions & 2 deletions src/bot/TicTacToeBot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import GameCommand from '@bot/command/GameCommand';
import EventHandler from '@bot/EventHandler';
import GameStateManager from '@bot/state/GameStateManager';
import Config from '@config/Config';
import { Client, CommandInteraction, Message } from 'discord.js';
import { ChatInputCommandInteraction, Client, Message } from 'discord.js';

/**
* Manages all interactions with the Discord bot.
Expand Down Expand Up @@ -100,7 +100,7 @@ export default class TicTacToeBot {
*
* @param interaction Discord.js interaction object
*/
public handleInteraction(interaction: CommandInteraction): void {
public handleInteraction(interaction: ChatInputCommandInteraction): void {
this.command.handleInteraction(interaction, true);
}
}
14 changes: 7 additions & 7 deletions src/bot/builder/GameBoardButtonBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import GameBoardBuilder from '@bot/builder/GameBoardBuilder';
import Entity from '@tictactoe/Entity';
import { Player } from '@tictactoe/Player';
import {
MessageActionRow,
MessageButton,
MessageButtonStyleResolvable,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
MessageOptions
} from 'discord.js';

Expand All @@ -25,7 +25,7 @@ export default class GameBoardButtonBuilder extends GameBoardBuilder {
* Button styles used for representing the two players.
* @private
*/
private buttonStyles: MessageButtonStyleResolvable[] = ['SECONDARY', 'PRIMARY', 'DANGER'];
private buttonStyles: ButtonStyle[] = [ButtonStyle.Secondary, ButtonStyle.Primary, ButtonStyle.Danger];
/**
* Stores if emojies have been customized.
* @private
Expand Down Expand Up @@ -77,7 +77,7 @@ export default class GameBoardButtonBuilder extends GameBoardBuilder {
return {
content: this.title + this.state,
components: [...Array(this.boardSize).keys()].map(row =>
new MessageActionRow().addComponents(
new ActionRowBuilder<ButtonBuilder>().addComponents(
[...Array(this.boardSize).keys()].map(col => this.createButton(row, col))
)
)
Expand All @@ -91,8 +91,8 @@ export default class GameBoardButtonBuilder extends GameBoardBuilder {
* @param col button placement column
* @returns button discord.js object
*/
private createButton(row: number, col: number): MessageButton {
const button = new MessageButton();
private createButton(row: number, col: number): ButtonBuilder {
const button = new ButtonBuilder();
const buttonIndex = row * this.boardSize + col;
const buttonData = this.boardData[buttonIndex];

Expand Down
6 changes: 3 additions & 3 deletions src/bot/command/AppCommandRegister.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import localize from '@i18n/localize';
import { ApplicationCommandManager, Message } from 'discord.js';
import { ApplicationCommandManager, ApplicationCommandOptionType, Message } from 'discord.js';

/**
* Manages application command used by the module.
Expand Down Expand Up @@ -44,7 +44,7 @@ export default class AppCommandRegister {
* @param message discord.js message object
*/
public async handleDeployMessage(message: Message): Promise<void> {
if (message.guild && message.member && message.member.permissions.has('ADMINISTRATOR')) {
if (message.guild && message.member && message.member.permissions.has('Administrator')) {
if (message.content === '?tttdeploy') {
await this.registerInGuild(message.guild.id);
await message.reply(`Command /${this.name} has been registered.`);
Expand Down Expand Up @@ -72,7 +72,7 @@ export default class AppCommandRegister {
description: localize.__('command.description'),
options: [
{
type: 'USER',
type: ApplicationCommandOptionType.User,
name: this.optionName,
description: localize.__('command.option-user')
}
Expand Down
10 changes: 5 additions & 5 deletions src/bot/command/GameCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default class GameCommand {
if (
message.member &&
!message.author.bot &&
message.channel.isText() &&
message.channel.isTextBased() &&
(noTrigger ||
(this.config.textCommand && message.content.startsWith(this.config.textCommand)))
) {
Expand All @@ -63,9 +63,9 @@ export default class GameCommand {
*/
public async handleInteraction(interaction: Interaction, noTrigger = false): Promise<void> {
if (
interaction?.isCommand() &&
interaction?.isChatInputCommand() &&
interaction.inCachedGuild() &&
interaction.channel?.isText() &&
interaction.channel?.isTextBased() &&
(noTrigger || interaction.commandName === this.config.command)
) {
// Retrieve the inviter and create an interaction tunnel
Expand All @@ -74,7 +74,7 @@ export default class GameCommand {
// Retrieve invited user from options if provided
const member = await interaction.member.fetch();
const mentionned =
interaction.options.getMember(this.config.commandOptionName ?? 'opponent', false) ??
interaction.options.getMember(this.config.commandOptionName ?? 'opponent') ??
undefined;

return this.handleInvitation(tunnel, member, mentionned);
Expand All @@ -98,7 +98,7 @@ export default class GameCommand {
if (!invited.user.bot) {
if (
inviter.user.id !== invited.user.id &&
invited.permissionsIn(tunnel.channel).has('VIEW_CHANNEL')
invited.permissionsIn(tunnel.channel).has('ViewChannel')
) {
await this.manager.requestDuel(tunnel, invited);
} else {
Expand Down
15 changes: 8 additions & 7 deletions src/bot/entity/DuelRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import MessagingTunnel from '@bot/messaging/MessagingTunnel';
import GameStateManager from '@bot/state/GameStateManager';
import localize from '@i18n/localize';
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
Collection,
GuildMember,
Message,
MessageActionRow,
MessageButton,
MessageComponentInteraction,
MessageOptions,
MessageReaction,
Expand Down Expand Up @@ -85,14 +86,14 @@ export default class DuelRequest {
allowedMentions: { parse: ['users'] },
components: !this.useReactions
? [
new MessageActionRow().addComponents(
new MessageButton({
style: 'SUCCESS',
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder({
style: ButtonStyle.Success,
customId: 'yes',
label: localize.__('duel.button.accept')
}),
new MessageButton({
style: 'DANGER',
new ButtonBuilder({
style: ButtonStyle.Danger,
customId: 'no',
label: localize.__('duel.button.decline')
})
Expand Down
3 changes: 2 additions & 1 deletion src/bot/entity/GameBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Game from '@tictactoe/Game';
import {
ButtonInteraction,
Collection,
InteractionResponse,
Message,
MessageReaction,
Snowflake,
Expand Down Expand Up @@ -184,7 +185,7 @@ export default class GameBoard {
*
* @param interaction interaction to update if action was triggered by it
*/
public async update(interaction?: ButtonInteraction): Promise<void> {
public async update(interaction?: ButtonInteraction): Promise<InteractionResponse<boolean> | void> {
if (interaction) {
return interaction.update(this.content);
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/bot/messaging/CommandInteractionMessagingTunnel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MessagingTunnel, { MessagingAnswer } from '@bot/messaging/MessagingTunnel';
import { CommandInteraction, GuildMember, Message, TextChannel } from 'discord.js';
import { ChatInputCommandInteraction, GuildMember, Message, TextChannel } from 'discord.js';

/**
* Represents an interaction messaging channel
Expand All @@ -13,7 +13,7 @@ export default class CommandInteractionMessagingTunnel extends MessagingTunnel {
* Interaction object retrieved from the Discord API
* @private
*/
private readonly interaction: CommandInteraction;
private readonly interaction: ChatInputCommandInteraction;
/**
* Last reply sent into the tunnnel
* @private
Expand All @@ -26,7 +26,7 @@ export default class CommandInteractionMessagingTunnel extends MessagingTunnel {
*
* @param interaction interaction generic object
*/
constructor(interaction: CommandInteraction) {
constructor(interaction: ChatInputCommandInteraction) {
super();
this.interaction = interaction;
}
Expand Down
16 changes: 8 additions & 8 deletions src/bot/state/GameStateValidator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import MessagingTunnel from '@bot/messaging/MessagingTunnel';
import GameStateManager from '@bot/state/GameStateManager';
import InteractionConfig from '@config/InteractionConfig';
import { GuildMember, PermissionString } from 'discord.js';
import { GuildMember, PermissionsString } from 'discord.js';

/**
* Validates state of a messaging tunnel
Expand All @@ -15,11 +15,11 @@ export default class GameStateValidator {
* List with all permissions that the bot needs to work properly.
* @private
*/
private static readonly PERM_LIST: PermissionString[] = [
'ADD_REACTIONS',
'READ_MESSAGE_HISTORY',
'SEND_MESSAGES',
'VIEW_CHANNEL'
private static readonly PERM_LIST: PermissionsString[] = [
'AddReactions',
'ReadMessageHistory',
'SendMessages',
'ViewChannel'
]; // bot doesn't need manage message to delete its own message

/**
Expand Down Expand Up @@ -102,7 +102,7 @@ export default class GameStateValidator {
*/
private hasPermissionsInChannel(tunnel: MessagingTunnel): boolean {
const allowed =
tunnel.channel.guild.me
tunnel.channel.guild.members.me
?.permissionsIn(tunnel.channel)
?.has(GameStateValidator.PERM_LIST) ?? false;

Expand Down Expand Up @@ -139,7 +139,7 @@ export default class GameStateValidator {
return (
!this.config.allowedRoleIds ||
this.config.allowedRoleIds.length == 0 ||
member.permissions.has('ADMINISTRATOR') ||
member.permissions.has('Administrator') ||
member.roles.cache.some(role => this.config.allowedRoleIds!.includes(role.id))
);
}
Expand Down