Skip to content

Commit

Permalink
feat: add extra commands (#17)
Browse files Browse the repository at this point in the history
Adds the extra commands used during SH8
  • Loading branch information
amishshah authored Jun 2, 2020
1 parent 822e03b commit 2653763
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [12.x]
node-version: [12.3.0]

steps:
- uses: actions/checkout@v2
Expand Down
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@
},
"dependencies": {
"@types/node-fetch": "^2.5.5",
"@unicsmcr/hs_discord_bot_api_client": "^0.0.2",
"@unicsmcr/hs_discord_bot_api_client": "^0.0.4",
"bad-words": "^3.0.3",
"canvas": "^2.6.1",
"discord-akairo": "^8.0.0",
"discord.js": "^12.2.0",
"dotenv": "^8.2.0",
"form-data": "^3.0.0",
"humanize-duration": "^3.22.0",
"node-fetch": "^2.6.0",
"pino": "^5.17.0"
}
Expand Down
9 changes: 8 additions & 1 deletion src/HackathonClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { AkairoClient, CommandHandler, ListenerHandler } from 'discord-akairo';
import { AkairoClient, CommandHandler, ListenerHandler, Command } from 'discord-akairo';
import { join } from 'path';
import { Logger } from 'pino';
import MuteTracker from './util/MuteTracker';
import { Image } from 'canvas';
import { Message } from 'discord.js';

export interface ApplicationConfig {
discord: {
Expand Down Expand Up @@ -54,6 +55,12 @@ export class HackathonClient extends AkairoClient {
directory: join(__dirname, 'commands')
});

this.commandHandler.on('cooldown', (message: Message, command: Command, remaining: number) => {
message
.reply(`You can't use that command for another ${Math.ceil(remaining / 1000)} seconds.`)
.catch(err => this.loggers.bot.warn(err));
});

const listenerHandler = new ListenerHandler(this, {
directory: join(__dirname, 'listeners')
});
Expand Down
31 changes: 31 additions & 0 deletions src/commands/id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Message, TextChannel, DMChannel, User } from 'discord.js';
import { Command } from 'discord-akairo';
import { Task, TaskStatus } from '../util/task';
import { HackathonClient } from '../HackathonClient';

export default class IdCommand extends Command {
public constructor() {
super('id', {
aliases: ['id'],
args: [
{
'id': 'target',
'type': 'user',
'default': (message: Message) => message.author
}
]
});
}

public async exec(message: Message, args: { target: User }) {
const task = new Task({
title: 'User ID',
issuer: message.author,
description: `${args.target.tag} - your ID is ${message.author.id}`,
status: TaskStatus.Completed
});
await task.sendTo(message.channel as TextChannel | DMChannel).catch(error => {
(this.client as HackathonClient).config.loggers.bot.warn(error);
});
}
}
13 changes: 8 additions & 5 deletions src/commands/mentor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Message, TextChannel, DMChannel } from 'discord.js';
import { Command } from 'discord-akairo';
import { modifyUserRoles, getUser, AuthLevel } from '@unicsmcr/hs_discord_bot_api_client';
import { Task, TaskStatus } from '../util/task';
import { HackathonClient } from '../HackathonClient';

const MentorMappings = {
'python': 'role.languages.python',
Expand Down Expand Up @@ -32,23 +33,24 @@ export default class MentorCommand extends Command {
args: [
{
id: 'roles',
type: 'lowercase',
match: 'content'
match: 'separate',
type: 'lowercase'
}
],
channel: 'guild'
});
}

public async exec(message: Message, args: { roles: string }) {
public async exec(message: Message, args: { roles: string[] }) {
const client = this.client as HackathonClient;
const task = new Task({
title: 'Update Mentor Roles',
issuer: message.author,
description: 'Updating your mentoring status...'
});
await task.sendTo(message.channel as TextChannel | DMChannel);

const roles = args.roles.split(' ');
const roles = args.roles || [];
const langRoles = [];
for (const [roleName, resourceName] of Object.entries(MentorMappings)) {
if (roles.includes(roleName)) {
Expand All @@ -72,11 +74,12 @@ export default class MentorCommand extends Command {
roles: existingRoles.concat(langRoles)
});

task.update({
await task.update({
status: TaskStatus.Completed,
description: 'Your new mentor roles have been set!'
});
} catch (err) {
client.loggers.bot.warn(err);
task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Please try again later.`
Expand Down
4 changes: 2 additions & 2 deletions src/commands/mute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export default class MuteCommand extends Command {
type: 'member',
prompt: {
start: 'Who would you like to mute?',
retry: 'That\'s not a valid member! Try again.',
optional: true
retry: 'That\'s not a valid member! Try again.'
}
}
],
Expand Down Expand Up @@ -62,6 +61,7 @@ export default class MuteCommand extends Command {
description: `Muted **${args.target.user.tag}** (${args.target.id})`
});
} catch (err) {
client.loggers.bot.warn(err);
task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Please try again later.`
Expand Down
17 changes: 14 additions & 3 deletions src/commands/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Message, TextChannel, DMChannel } from 'discord.js';
import { Command } from 'discord-akairo';
import { Task, TaskStatus } from '../util/task';
import { HackathonClient } from '../HackathonClient';
import { getTeams, getUsers } from '@unicsmcr/hs_discord_bot_api_client';
import { getTeams, getUsers, AuthLevel } from '@unicsmcr/hs_discord_bot_api_client';
import humanizeDuration from 'humanize-duration';

export default class StatsCommand extends Command {
public constructor() {
Expand Down Expand Up @@ -31,23 +32,33 @@ export default class StatsCommand extends Command {
task.status = TaskStatus.Completed;
task.description = '';
const [users, teams] = await Promise.all([getUsers(), getTeams()]);
const participants = users.filter(user => user.authLevel === AuthLevel.Attendee).length;
const volunteers = users.filter(user => user.authLevel === AuthLevel.Volunteer).length;
task.addFields(
{
name: 'Participants in Discord server',
value: users.length
value: participants
},
{
name: 'Volunteers in Discord server',
value: volunteers
},
{
name: 'Teams',
value: teams.length
},
{
name: 'Uptime',
value: client.uptime ? humanizeDuration(client.uptime) : 'Undefined'
}
);
task.update({});
} catch (error) {
client.loggers.bot.warn(error);
task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Please try again later.`
});
console.log(error);
}
}
}
50 changes: 50 additions & 0 deletions src/commands/sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Message, TextChannel, DMChannel, User } from 'discord.js';
import { Command } from 'discord-akairo';
import { Task, TaskStatus } from '../util/task';
import { syncAccount, getUser, AuthLevel } from '@unicsmcr/hs_discord_bot_api_client';

export default class SyncCommand extends Command {
public constructor() {
super('sync', {
aliases: ['sync'],
args: [
{
'id': 'target',
'type': 'user',
'default': (message: Message) => message.author
}
],
// One use per minute to stop abuse of API
cooldown: 60e3,
ratelimit: 1
});
}

public async exec(message: Message, args: { target: User }) {
const task = new Task({
title: 'User sync',
issuer: message.author,
description: `Syncing account state`
});
await task.sendTo(message.channel as TextChannel | DMChannel);
try {
let target = args.target;
if (target.id !== message.author.id) {
const issuer = await getUser(message.author.id);
if (issuer.authLevel < AuthLevel.Volunteer) {
target = message.author;
}
}
await syncAccount(target.id);
await task.update({
status: TaskStatus.Completed,
description: `Synced state for ${target.tag}!`
});
} catch (error) {
await task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Try again later.\n\n${error.message}`
});
}
}
}
4 changes: 2 additions & 2 deletions src/commands/unmute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export default class MuteCommand extends Command {
type: 'member',
prompt: {
start: 'Who would you like to unmute?',
retry: 'That\'s not a valid member! Try again.',
optional: true
retry: 'That\'s not a valid member! Try again.'
}
}
],
Expand Down Expand Up @@ -64,6 +63,7 @@ export default class MuteCommand extends Command {
description: `Unmuted **${args.target.user.tag}** (${args.target.id})`
});
} catch (err) {
client.loggers.bot.warn(err);
task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Please try again later.`
Expand Down
106 changes: 106 additions & 0 deletions src/commands/whois.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Message, TextChannel, DMChannel, User } from 'discord.js';
import { Command } from 'discord-akairo';
import { Task, TaskStatus } from '../util/task';
import { getUser, getTeam, APITeam, AuthLevel } from '@unicsmcr/hs_discord_bot_api_client';
import { HackathonClient } from '../HackathonClient';

export default class WhoIsCommand extends Command {
public constructor() {
super('whois', {
aliases: ['whois'],
args: [
{
id: 'target',
type: 'user',
prompt: {
start: 'Who would you like to get the details of?',
retry: 'That\'s not a valid member! Try again.'
}
}
]
});
}

public async exec(message: Message, args: { target: User }) {
const client = this.client as HackathonClient;
const task = new Task({
title: 'User info',
issuer: message.author,
description: 'Fetching the info now...'
});
await task.sendTo(message.channel as TextChannel | DMChannel);
try {
const issuer = await getUser(message.author.id);
if (issuer.authLevel < AuthLevel.Volunteer) {
return task.update({
status: TaskStatus.Failed,
description: 'Sorry, you need to be a volunteer or organiser to use this command.'
});
}

if (message.guild) {
const channel = message.channel as TextChannel;
if (channel.permissionsFor(message.guild.id)?.has('VIEW_CHANNEL')) {
return task.update({
status: TaskStatus.Failed,
description: 'Sorry, you need to run this in a private channel.'
});
}
}

task.status = TaskStatus.Completed;
task.description = '';
const user = await getUser(args.target.id);
task.addFields(
{
name: 'Name',
value: user.name
},
{
name: 'Auth ID',
value: user.authId
}
);
let team: APITeam | undefined;
if (user.team) {
try {
team = await getTeam(user.team);
task.addFields(
{
name: 'Team Name',
value: team.name
},
{
name: 'Team Auth ID',
value: team.authId
},
{
name: 'Team Number',
value: team.teamNumber
}
);
} catch (error) {
task.addFields({
name: 'Team',
value: `Auth ID: ${user.team} (error fetching more info than this)`
});
client.config.loggers.bot.warn(`Error fetching team ${user.team}`);
client.config.loggers.bot.warn(error);
}
}
task.update({});
} catch (error) {
if (error.res?.statusCode === 404) {
task.update({
status: TaskStatus.Failed,
description: `This account is not linked.`
});
} else {
task.update({
status: TaskStatus.Failed,
description: `An error occurred processing your request. Try again later.`
});
}
}
}
}
Loading

0 comments on commit 2653763

Please sign in to comment.