Skip to content

Commit cba4b67

Browse files
Configure prettier and lint (#28)
* Configure prettier and lint * Add workflow to check linting * Spelled GitHub action wrong * Named the branch main instead of master * Attempt to install dependencies * fix syntax * Add eslint-plugin-prettier * Add documentation for yml file
1 parent de39b21 commit cba4b67

27 files changed

+1250
-1096
lines changed

.eslintrc.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ module.exports = {
33
es6: true,
44
node: true,
55
},
6-
extends: [
7-
'airbnb-base',
8-
],
6+
extends: ['airbnb-base', 'prettier'],
97
globals: {
108
Atomics: 'readonly',
119
SharedArrayBuffer: 'readonly',
@@ -15,13 +13,12 @@ module.exports = {
1513
ecmaVersion: 2018,
1614
sourceType: 'module',
1715
},
18-
plugins: [
19-
'@typescript-eslint',
20-
],
16+
plugins: ['@typescript-eslint', 'prettier'],
2117
rules: {
2218
'import/extensions': 'off',
23-
'no-unused-vars': 'off', /* because ESLint is broken, we need to disable the js version of this */
19+
'no-unused-vars': 'off' /* because ESLint is broken, we need to disable the js version of this */,
2420
'@typescript-eslint/no-unused-vars': ['error'],
21+
'prettier/prettier': 'error',
2522
},
2623
settings: {
2724
'import/resolver': {

.github/workflows/pr.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: pr
2+
3+
on:
4+
pull_request:
5+
branches: [master]
6+
jobs:
7+
Linux:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Clone
11+
uses: actions/checkout@v2
12+
# We need to run the following commands so yarn can successfully install dependencies.
13+
# Because node-canvas depends on cairo and pango to compile on the system, we install them here.
14+
- name: Install dependencies for node-canvas
15+
run: |
16+
sudo apt update
17+
sudo apt install -y libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev librsvg2-dev
18+
- name: Install dependencies
19+
run: 'yarn install --frozen-lockfile'
20+
- name: Lint
21+
run: yarn lint

.prettierrc.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"quoteProps": "as-needed",
3+
"singleQuote": true,
4+
"arrowParens": "avoid",
5+
"tabWidth": 2,
6+
"useTabs": false,
7+
"printWidth": 120,
8+
"proseWrap": "always",
9+
"bracketSpacing": true,
10+
"trailingComma": "es5",
11+
"semi": true,
12+
"endOfLine": "auto"
13+
}

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
"@typescript-eslint/parser": "^4.17.0",
5757
"eslint": "^7.21.0",
5858
"eslint-config-airbnb-base": "^14.2.1",
59-
"eslint-plugin-import": "^2.22.1"
59+
"eslint-config-prettier": "^8.8.0",
60+
"eslint-plugin-import": "^2.22.1",
61+
"eslint-plugin-prettier": "^4.2.1",
62+
"prettier": "^2.8.7"
6063
}
6164
}

src/Client.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ export default class Client extends DiscordClient implements BotClient {
4141
* @param actionManager An ActionManager class to run. Injected by TypeDI.
4242
* @param portalAPIManager A PortalAPIManager class to run. Injected by TypeDI
4343
*/
44-
constructor(
45-
private actionManager: ActionManager,
46-
private portalAPIManager: PortalAPIManager,
47-
) {
44+
constructor(private actionManager: ActionManager, private portalAPIManager: PortalAPIManager) {
4845
super(
4946
configuration.clientOptions || {
5047
intents: [
@@ -56,7 +53,7 @@ export default class Client extends DiscordClient implements BotClient {
5653
'GUILD_MESSAGE_REACTIONS',
5754
'DIRECT_MESSAGE_REACTIONS',
5855
],
59-
},
56+
}
6057
);
6158
this.settings = configuration;
6259
// We absolutely need some envvars, so if they're not in our .env file, nuke the initialization.
@@ -95,9 +92,7 @@ export default class Client extends DiscordClient implements BotClient {
9592
this.settings.acmurl.password = process.env.ACMURL_PASSWORD;
9693
this.settings.portalAPI.username = process.env.MEMBERSHIP_PORTAL_API_USERNAME;
9794
this.settings.portalAPI.password = process.env.MEMBERSHIP_PORTAL_API_PASSWORD;
98-
this.settings.discordGuildIDs = JSON.parse(
99-
process.env.DISCORD_GUILD_IDS,
100-
) as Array<string>;
95+
this.settings.discordGuildIDs = JSON.parse(process.env.DISCORD_GUILD_IDS) as Array<string>;
10196
this.initialize().then();
10297
}
10398

src/Command.ts

+103-111
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { SlashCommandBuilder } from '@discordjs/builders';
2-
import {
3-
CommandInteraction,
4-
} from 'discord.js';
5-
import {
6-
BotClient, CommandOptions, InteractionPayload,
7-
} from './types';
2+
import { CommandInteraction } from 'discord.js';
3+
import { BotClient, CommandOptions, InteractionPayload } from './types';
84
import Logger from './utils/Logger';
95

106
/**
@@ -19,122 +15,118 @@ import Logger from './utils/Logger';
1915
* The Command class should also interpret all the logic necessary to run the Slash Command.
2016
*/
2117
export default abstract class Command {
22-
/**
23-
* The command options for the bot.
24-
*/
25-
public conf: CommandOptions;
18+
/**
19+
* The command options for the bot.
20+
*/
21+
public conf: CommandOptions;
2622

27-
/**
28-
* The Slash Command definition to upload to the Discord Gateway.
29-
* This can be used to register a Slash Command.
30-
*/
31-
public definition: SlashCommandBuilder;
23+
/**
24+
* The Slash Command definition to upload to the Discord Gateway.
25+
* This can be used to register a Slash Command.
26+
*/
27+
public definition: SlashCommandBuilder;
3228

33-
/**
34-
* The default constructor for Commands.
35-
*
36-
* By default, all required arguments are passed by CommandOptions. Other optional arguments
37-
* are given sensible defaults here.
38-
*
39-
* @param client The client receiving the Command.
40-
* @param options Any options to set for the Command.
41-
*/
42-
constructor(protected client: BotClient,
43-
options: CommandOptions,
44-
definition: SlashCommandBuilder) {
45-
this.conf = {
46-
enabled: options.enabled,
47-
name: options.name,
48-
boardRequired: options.boardRequired || false,
49-
description: options.description || 'No information specified.',
50-
usage: options.usage || 'No usage specified.',
51-
category: options.category || 'Uncategorized',
52-
requiredPermissions: options.requiredPermissions || ['VIEW_CHANNEL', 'READ_MESSAGE_HISTORY'],
53-
};
54-
this.definition = definition;
55-
}
29+
/**
30+
* The default constructor for Commands.
31+
*
32+
* By default, all required arguments are passed by CommandOptions. Other optional arguments
33+
* are given sensible defaults here.
34+
*
35+
* @param client The client receiving the Command.
36+
* @param options Any options to set for the Command.
37+
*/
38+
constructor(protected client: BotClient, options: CommandOptions, definition: SlashCommandBuilder) {
39+
this.conf = {
40+
enabled: options.enabled,
41+
name: options.name,
42+
boardRequired: options.boardRequired || false,
43+
description: options.description || 'No information specified.',
44+
usage: options.usage || 'No usage specified.',
45+
category: options.category || 'Uncategorized',
46+
requiredPermissions: options.requiredPermissions || ['VIEW_CHANNEL', 'READ_MESSAGE_HISTORY'],
47+
};
48+
this.definition = definition;
49+
}
5650

57-
/**
58-
* Checks if the user has permission to run the command.
59-
* @param {Interaction} interaction The Interaction made when calling the Command.
60-
* @returns {boolean} Whether the user can run the command.
61-
*/
62-
public canRun(interaction: CommandInteraction): boolean {
63-
// Check whether user has the Board role.
64-
//
65-
// They either have a role cache (they have at least one Role) that includes "Board"
66-
// or they don't.
67-
const memberRoles = interaction.member ? interaction.member.roles : undefined;
68-
if (!memberRoles) {
69-
return false;
70-
}
71-
const validRole = (role: string) => role.toLowerCase().includes('staff') || role.toLowerCase().includes('board');
51+
/**
52+
* Checks if the user has permission to run the command.
53+
* @param {Interaction} interaction The Interaction made when calling the Command.
54+
* @returns {boolean} Whether the user can run the command.
55+
*/
56+
public canRun(interaction: CommandInteraction): boolean {
57+
// Check whether user has the Board role.
58+
//
59+
// They either have a role cache (they have at least one Role) that includes "Board"
60+
// or they don't.
61+
const memberRoles = interaction.member ? interaction.member.roles : undefined;
62+
if (!memberRoles) {
63+
return false;
64+
}
65+
const validRole = (role: string) => role.toLowerCase().includes('staff') || role.toLowerCase().includes('board');
7266

73-
const isBoard = Array.isArray(memberRoles)
74-
? memberRoles.some((role) => validRole(role))
75-
: memberRoles.cache.some((r) => validRole(r.name));
67+
const isBoard = Array.isArray(memberRoles)
68+
? memberRoles.some(role => validRole(role))
69+
: memberRoles.cache.some(r => validRole(r.name));
7670

77-
if (this.conf.boardRequired && !isBoard) {
78-
interaction.reply(
79-
'You must be a Board member to use this command!',
80-
).then(() => {
81-
Logger.warn(`Member ${interaction.member?.toString()} attempted to use a Board command without permission!`, {
82-
eventType: 'rolesError',
83-
author: interaction.member?.toString(),
84-
requiredRole: 'Board',
85-
});
71+
if (this.conf.boardRequired && !isBoard) {
72+
interaction.reply('You must be a Board member to use this command!').then(() => {
73+
Logger.warn(`Member ${interaction.member?.toString()} attempted to use a Board command without permission!`, {
74+
eventType: 'rolesError',
75+
author: interaction.member?.toString(),
76+
requiredRole: 'Board',
8677
});
87-
return false;
88-
}
89-
90-
return true;
78+
});
79+
return false;
9180
}
9281

93-
/**
94-
* The abstract run method for every command.
95-
* @param {Message} message The original message object that triggered the command.
96-
* @param {string[]} args The arguments that got sent with the message.
97-
*/
98-
public abstract run(interaction: CommandInteraction): Promise<void>;
82+
return true;
83+
}
9984

100-
/**
101-
* Replies to a Command Interaction with a message of any kind.
102-
* @param {CommandInteraction} interaction The Command Interaction instance to respond to.
103-
* @param {InteractionPayload} message The message that will be sent.
104-
* @returns {Promise<Command>} The original command, supports method chaining.
105-
*/
106-
public async respond(interaction: CommandInteraction, message: InteractionPayload) {
107-
await interaction.reply(message);
108-
return this;
109-
}
85+
/**
86+
* The abstract run method for every command.
87+
* @param {Message} message The original message object that triggered the command.
88+
* @param {string[]} args The arguments that got sent with the message.
89+
*/
90+
public abstract run(interaction: CommandInteraction): Promise<void>;
11091

111-
/**
112-
* Defers the reply to the Command Interaction.
113-
*
114-
* The time limit for an Interaction is 3 seconds, unless you defer beforehand.
115-
*
116-
* @param interaction The Command Interaction to defer for later.
117-
* @returns The original command, supports method chaining.
118-
*/
119-
public async defer(interaction: CommandInteraction, ephemeral?: boolean) {
120-
if (ephemeral) {
121-
await interaction.deferReply({ ephemeral: true });
122-
return this;
123-
}
124-
await interaction.deferReply();
125-
return this;
126-
}
92+
/**
93+
* Replies to a Command Interaction with a message of any kind.
94+
* @param {CommandInteraction} interaction The Command Interaction instance to respond to.
95+
* @param {InteractionPayload} message The message that will be sent.
96+
* @returns {Promise<Command>} The original command, supports method chaining.
97+
*/
98+
public async respond(interaction: CommandInteraction, message: InteractionPayload) {
99+
await interaction.reply(message);
100+
return this;
101+
}
127102

128-
/**
129-
* Edits reply to the Command Interaction.
130-
*
131-
* This is required after deferring a reply, but not necessarily otherwise.
132-
*
133-
* @param interaction The Command Interaction to defer for later.
134-
* @returns The original command, supports method chaining.
135-
*/
136-
public async edit(interaction: CommandInteraction, message: InteractionPayload) {
137-
await interaction.editReply(message);
103+
/**
104+
* Defers the reply to the Command Interaction.
105+
*
106+
* The time limit for an Interaction is 3 seconds, unless you defer beforehand.
107+
*
108+
* @param interaction The Command Interaction to defer for later.
109+
* @returns The original command, supports method chaining.
110+
*/
111+
public async defer(interaction: CommandInteraction, ephemeral?: boolean) {
112+
if (ephemeral) {
113+
await interaction.deferReply({ ephemeral: true });
138114
return this;
139115
}
116+
await interaction.deferReply();
117+
return this;
118+
}
119+
120+
/**
121+
* Edits reply to the Command Interaction.
122+
*
123+
* This is required after deferring a reply, but not necessarily otherwise.
124+
*
125+
* @param interaction The Command Interaction to defer for later.
126+
* @returns The original command, supports method chaining.
127+
*/
128+
public async edit(interaction: CommandInteraction, message: InteractionPayload) {
129+
await interaction.editReply(message);
130+
return this;
131+
}
140132
}

0 commit comments

Comments
 (0)