45 Commits

Author SHA1 Message Date
Anthony
2cf7998cd9 feat(arabot): add hr and mentor coordinators private channels 2024-02-07 15:03:26 +00:00
Anthony Berg
63a4d651af build: change ioredis to dev dep 2024-02-03 22:45:39 +00:00
Anthony Berg
a2dba859f2 build: fix prisma generate in Dockerfile 2024-02-03 22:39:26 +00:00
Anthony Berg
a7b772f77a build: update deps 2024-02-03 22:18:11 +00:00
Anthony Berg
1fa87b8a4a Merge remote-tracking branch 'origin/main'
# Conflicts:
#	package-lock.json
#	package.json
2024-02-03 22:14:50 +00:00
Anthony Berg
4e99a5456f build: change to pnpm 2024-02-03 22:13:35 +00:00
Anthony
62d941dfcb feat(arabot): add better checks for types for temp bans 2024-01-27 19:46:31 +01:00
Anthony
e6b1463a1a refactor: run prettier 2024-01-27 19:46:17 +01:00
Anthony
5793bbb461 fix(arabot): catch conditions for fetching guild and user 2024-01-27 19:41:22 +01:00
Anthony
cf8142b86a fix(arabot): deleted channel errors 2024-01-27 19:38:59 +01:00
Anthony
502d5c5cdf ci: update deps 2024-01-27 19:27:19 +01:00
Anthony Berg
8f071b8043 Merge pull request #187 from veganhacktivists/renovate/node-20.x-lockfile
fix(deps): update dependency @types/node to v20.11.8
2024-01-27 18:04:39 +00:00
Anthony Berg
36ce086532 Merge pull request #188 from veganhacktivists/renovate/bullmq-5.x-lockfile
fix(deps): update dependency bullmq to v5.1.5
2024-01-27 18:04:24 +00:00
Anthony Berg
617834833a Merge pull request #189 from veganhacktivists/renovate/prisma-monorepo
fix(deps): update prisma monorepo to v5.8.1
2024-01-27 18:04:15 +00:00
renovate[bot]
4d92250500 fix(deps): update prisma monorepo to v5.8.1 2024-01-27 17:59:09 +00:00
renovate[bot]
f898dada56 fix(deps): update dependency @types/node to v20.11.8 2024-01-27 17:58:51 +00:00
renovate[bot]
9e2f2c7558 fix(deps): update dependency bullmq to v5.1.5 2024-01-27 14:03:25 +00:00
Anthony Berg
e15e5da5aa Merge pull request #186 from veganhacktivists/renovate/sapphire-time-utilities-1.x-lockfile
fix(deps): update dependency @sapphire/time-utilities to v1.7.12
2024-01-27 14:02:47 +00:00
Anthony Berg
e89f056b94 Merge pull request #185 from veganhacktivists/renovate/typescript-eslint-monorepo
chore(deps): update typescript-eslint monorepo to v6.19.1
2024-01-27 14:02:27 +00:00
renovate[bot]
6acd012e7a fix(deps): update dependency @sapphire/time-utilities to v1.7.12 2024-01-27 13:50:08 +00:00
renovate[bot]
75174f711d chore(deps): update typescript-eslint monorepo to v6.19.1 2024-01-27 13:49:56 +00:00
Anthony
5e69ea6126 fix(arabot): grammar in welcome message 2024-01-24 15:43:45 +00:00
Anthony Berg
3d8aba5577 Merge pull request #182 from veganhacktivists/renovate/sapphire-plugin-logger-4.x-lockfile
fix(deps): update dependency @sapphire/plugin-logger to v4.0.2
2024-01-21 23:36:36 +00:00
Anthony Berg
e7839552f8 Merge pull request #183 from veganhacktivists/renovate/sapphire-plugin-scheduled-tasks-10.x-lockfile
fix(deps): update dependency @sapphire/plugin-scheduled-tasks to v10.0.1
2024-01-21 23:36:26 +00:00
Anthony Berg
3b0666e80d Merge pull request #184 from veganhacktivists/renovate/sapphire-plugin-subcommands-6.x-lockfile
fix(deps): update dependency @sapphire/plugin-subcommands to v6.0.3
2024-01-21 23:36:17 +00:00
Anthony Berg
3d8a9be7f2 Merge pull request #180 from veganhacktivists/renovate/node-20.x-lockfile
fix(deps): update dependency @types/node to v20.11.5
2024-01-21 23:35:58 +00:00
Anthony Berg
a7f608c1f0 Merge pull request #179 from veganhacktivists/renovate/prettier-3.x
chore(deps): update dependency prettier to v3.2.4
2024-01-21 23:35:48 +00:00
Anthony Berg
cb457136d4 Merge pull request #178 from veganhacktivists/renovate/sapphire-framework-5.x-lockfile
fix(deps): update dependency @sapphire/framework to v5.0.7
2024-01-21 23:35:40 +00:00
Anthony Berg
7e984c4857 Merge pull request #177 from veganhacktivists/renovate/typescript-eslint-monorepo
chore(deps): update typescript-eslint monorepo to v6.19.0
2024-01-21 23:35:32 +00:00
Anthony Berg
0ea9ea3f64 feat(arabot): remove Patreon precondition 2024-01-21 20:11:21 +00:00
renovate[bot]
19e70ebdbc fix(deps): update dependency @sapphire/plugin-subcommands to v6.0.3 2024-01-20 16:17:44 +00:00
renovate[bot]
5409be3d75 fix(deps): update dependency @sapphire/plugin-scheduled-tasks to v10.0.1 2024-01-20 16:17:37 +00:00
renovate[bot]
edd3caf9c0 fix(deps): update dependency @sapphire/plugin-logger to v4.0.2 2024-01-20 14:29:26 +00:00
renovate[bot]
5a87f97a74 fix(deps): update dependency @sapphire/framework to v5.0.7 2024-01-19 21:34:24 +00:00
renovate[bot]
feab05c1ea chore(deps): update dependency prettier to v3.2.4 2024-01-17 11:41:50 +00:00
renovate[bot]
9b505fbece fix(deps): update dependency @types/node to v20.11.5 2024-01-17 07:25:22 +00:00
renovate[bot]
a5ba493372 chore(deps): update typescript-eslint monorepo to v6.19.0 2024-01-15 18:20:21 +00:00
Anthony Berg
98e514b5e9 fix(arabot): not giving roles back to server boosters 2024-01-13 02:06:25 +00:00
Anthony Berg
172508c741 feat(arabot): remove unholy fun command 2024-01-13 01:36:43 +00:00
Anthony Berg
730f3e6a28 feat(arabot): add fun listener for "bad" words 2024-01-13 01:17:34 +00:00
Anthony Berg
fbc2944b92 fix(arabot): 1984 preconditions 2024-01-13 01:07:20 +00:00
Anthony Berg
6172dc6ac6 feat(arabot): add logging for sus note purges 2024-01-13 00:57:14 +00:00
Anthony Berg
b762ae3bc8 feat(arabot): add logging for one sus note removal 2024-01-13 00:52:54 +00:00
Anthony Berg
c8eb8299dd refactor(arabot): change removing sus notes to have more checks for types 2024-01-13 00:29:57 +00:00
Anthony Berg
785e844da8 feat(arabot): sus note logging for added sus note 2024-01-13 00:11:06 +00:00
22 changed files with 2081 additions and 4765 deletions

View File

@@ -1,6 +1,6 @@
.idea
dist
node_modules
tsconfig.tsbuildinfo
npm-debug.log
.env
*.md

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
node-linker=hoisted
shamefully-hoist=true
public-hoist-pattern[]=@sapphire/*

View File

@@ -1,24 +1,26 @@
FROM node:20
FROM node:20 AS base
# PNPM
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /opt/app
COPY . /app
WORKDIR /app
ENV NODE_ENV=production
FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --chown=node:node package.json .
COPY --chown=node:node package-lock.json .
COPY --chown=node:node tsconfig.json .
COPY --chown=node:node prisma ./prisma/
FROM base AS build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm exec prisma generate
RUN pnpm run build
RUN npm install
FROM base
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/node_modules/.prisma /app/node_modules/.prisma
COPY --from=build /app/dist /app/dist
COPY . .
RUN npx prisma generate
RUN npm run build
RUN chown node:node /opt/app/
RUN chown node:node .
USER node
CMD [ "npm", "run", "start:migrate"]
CMD [ "pnpm", "run", "start:migrate"]

4556
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,10 +4,11 @@
"description": "A Discord bot for Animal Rights Advocates",
"main": "dist/index.js",
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "tsc",
"cleanBuild": "rm -rf ./dist && tsc",
"start": "node dist/index.js",
"start:migrate": "prisma migrate deploy && npm run start"
"start:migrate": "prisma migrate deploy && pnpm run start"
},
"imports": {
"#utils/*": "./dist/utils/*.js"
@@ -28,31 +29,35 @@
"url": "https://github.com/veganhacktivists/arabot/issues"
},
"homepage": "https://github.com/veganhacktivists/arabot#readme",
"engines": {
"node": ">=20",
"pnpm": ">=8"
},
"dependencies": {
"@prisma/client": "^5.7.1",
"@sapphire/discord.js-utilities": "^7.1.5",
"@sapphire/framework": "^5.0.5",
"@sapphire/plugin-logger": "^4.0.1",
"@sapphire/plugin-scheduled-tasks": "^10.0.0",
"@sapphire/plugin-subcommands": "^6.0.2",
"@sapphire/stopwatch": "^1.5.1",
"@sapphire/time-utilities": "^1.7.11",
"@prisma/client": "^5.9.1",
"@sapphire/discord.js-utilities": "^7.1.6",
"@sapphire/framework": "^5.0.7",
"@sapphire/plugin-logger": "^4.0.2",
"@sapphire/plugin-scheduled-tasks": "^10.0.1",
"@sapphire/plugin-subcommands": "^6.0.3",
"@sapphire/stopwatch": "^1.5.2",
"@sapphire/time-utilities": "^1.7.12",
"@sapphire/ts-config": "^5.0.0",
"@sapphire/utilities": "^3.15.1",
"@types/node": "^20.10.6",
"bullmq": "^5.1.1",
"@sapphire/utilities": "^3.15.3",
"@types/node": "^20.11.16",
"bullmq": "^5.1.8",
"discord.js": "^14.14.1",
"redis": "^4.6.12",
"ts-node": "^10.9.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/ioredis": "^5.0.0",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "8.56.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "3.1.1",
"prisma": "^5.7.1"
"ioredis": "^5.3.2",
"prettier": "3.2.4",
"prisma": "^5.9.1"
}
}

1744
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -356,6 +356,12 @@ export class PrivateCommand extends Subcommand {
} else if (user.roles.cache.has(IDs.roles.staff.eventCoordinator)) {
name = 'event';
id = IDs.roles.staff.eventCoordinator;
} else if (user.roles.cache.has(IDs.roles.staff.mediaCoordinator)) {
name = 'media';
id = IDs.roles.staff.mediaCoordinator;
} else if (user.roles.cache.has(IDs.roles.staff.hrCoordinator)) {
name = 'hr';
id = IDs.roles.staff.hrCoordinator;
} else {
name = 'coordinator';
id = IDs.roles.staff.coordinator;

View File

@@ -28,7 +28,7 @@ export class N1984Command extends Command {
...options,
name: '1984',
description: 'this is literally 1984',
preconditions: ['CoordinatorOnly', 'ModOnly'],
preconditions: [['CoordinatorOnly', 'ModOnly']],
});
}

View File

@@ -27,7 +27,6 @@ export class HappyCommand extends Command {
...options,
name: 'happy',
description: 'Express your happiness',
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
});
}

View File

@@ -27,7 +27,6 @@ export class SadCommand extends Command {
...options,
name: 'sad',
description: 'Express your sadness',
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
});
}

View File

@@ -27,7 +27,6 @@ export class ShrugCommand extends Command {
...options,
name: 'shrug',
description: 'Ugh... whatever... idk...',
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
});
}

View File

@@ -25,12 +25,16 @@ import {
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
User,
Guild,
TextChannel,
GuildMember,
Snowflake,
} from 'discord.js';
import type { Message, GuildMember } from 'discord.js';
import type { Message } from 'discord.js';
import { isMessageInstance } from '@sapphire/discord.js-utilities';
import { addExistingUser } from '#utils/database/dbExistingUser';
import {
addToDatabase,
addSusNoteDB,
findNotes,
getNote,
deactivateNote,
@@ -54,8 +58,8 @@ export class SusCommand extends Subcommand {
{
name: 'add',
default: true,
chatInputRun: 'addNote',
messageRun: 'addMessage',
chatInputRun: 'addNoteChatInput',
messageRun: 'addNoteMessage',
},
{
name: 'view',
@@ -143,7 +147,9 @@ export class SusCommand extends Subcommand {
}
// Subcommand to add sus note
public async addNote(interaction: Subcommand.ChatInputCommandInteraction) {
public async addNoteChatInput(
interaction: Subcommand.ChatInputCommandInteraction,
) {
// Get the arguments
const user = interaction.options.getUser('user', true);
const note = interaction.options.getString('note', true);
@@ -151,7 +157,7 @@ export class SusCommand extends Subcommand {
const { guild } = interaction;
// Checks if all the variables are of the right type
if (guild === null) {
if (!(guild instanceof Guild)) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
@@ -159,35 +165,117 @@ export class SusCommand extends Subcommand {
return;
}
// Add the data to the database
const info = await this.addNote(user, mod, note, guild);
// Check if the user exists on the database
const member = guild.members.cache.get(user.id);
const modMember = guild.members.cache.get(mod.id);
await interaction.reply({
content: info.message,
ephemeral: true,
});
}
if (member === undefined || modMember === undefined) {
await interaction.reply({
content: 'Error fetching users!',
ephemeral: true,
});
// Non Application Command method of adding a sus note
public async addNoteMessage(message: Message, args: Args) {
// Get arguments
let user: User;
try {
user = await args.pick('user');
} catch {
await message.react('❌');
await message.reply('User was not provided!');
return;
}
const note = args.finished ? null : await args.rest('string');
const mod = message.author;
if (note === null) {
await message.react('❌');
await message.reply('No sus note was provided!');
return;
}
// Check if user and mod are on the database
await addExistingUser(member);
await addExistingUser(modMember);
const guild = message.guild;
await addToDatabase(user.id, mod.id, note);
if (!(guild instanceof Guild)) {
await message.react('❌');
await message.reply(
'Could not find guild! Make sure you run this command in a server.',
);
return;
}
const info = await this.addNote(user, mod, note, guild);
if (!info.success) {
await message.react('❌');
return;
}
await message.react('✅');
}
private async addNote(user: User, mod: User, note: string, guild: Guild) {
const info = {
message: '',
success: false,
};
// Get GuildMember for user to add a sus note for
let member = guild.members.cache.get(user.id);
// Checks if Member was not found in cache
if (member === undefined) {
// Fetches Member from API call to Discord
member = await guild.members.fetch(user.id);
if (member === undefined) {
info.message = 'Error fetching user';
return info;
}
}
// Add the data to the database
await addSusNoteDB(user.id, mod.id, note);
// Give the user the sus role they don't already have the sus note
if (!member.roles.cache.has(IDs.roles.restrictions.sus)) {
await member.roles.add(IDs.roles.restrictions.sus);
}
await interaction.reply({
content: `${user} note: ${note}`,
ephemeral: true,
});
info.message = `Added the sus note for ${user}: ${note}`;
info.success = true;
// Log the sus note
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
| TextChannel
| undefined;
if (logChannel === undefined) {
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
| TextChannel
| undefined;
if (logChannel === undefined) {
this.container.logger.error('Sus Error: Could not fetch log channel');
info.message = `Added a sus note for ${user} but could not find the log channel. This has been logged to the database.`;
return info;
}
}
const message = new EmbedBuilder()
.setColor('#0099ff')
.setAuthor({
name: `Added sus note for ${user.tag}`,
iconURL: `${user.displayAvatarURL()}`,
})
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
{ name: 'Note', value: note },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [message] });
return info;
}
public async listNote(interaction: Subcommand.ChatInputCommandInteraction) {
@@ -233,6 +321,7 @@ export class SusCommand extends Subcommand {
public async removeNote(interaction: Subcommand.ChatInputCommandInteraction) {
// Get the arguments
const noteId = interaction.options.getInteger('id', true);
const mod = interaction.user;
const { guild, channel } = interaction;
// Checks if all the variables are of the right type
@@ -258,39 +347,40 @@ export class SusCommand extends Subcommand {
return;
}
// Get user GuildMembers for user and mod and person who ran command
const member = await guild.members.cache.get(note.userId);
const mod = await guild.members.cache.get(note.modId);
const userId = note.userId;
const modId = note.modId;
// TODO fix if user left the server
if (member === undefined || mod === undefined) {
// Get user GuildMembers for user and mod and person who ran command
let user = guild.client.users.cache.get(userId);
if (!(user instanceof User)) {
user = await guild.client.users.fetch(userId).catch(() => undefined);
}
if (user === undefined) {
await interaction.reply({
content: 'Error fetching users from Discord!',
content: 'Error fetching user!',
ephemeral: true,
fetchReply: true,
});
return;
}
// Get user's name
let userName = note.userId;
if (member !== undefined) {
userName = member.displayName;
let modCreator = guild.client.users.cache.get(modId);
if (!(modCreator instanceof User)) {
modCreator = await guild.client.users.fetch(modId).catch(() => undefined);
}
// Get mod name
let modName = note.modId;
if (mod !== undefined) {
modName = mod.displayName;
let modCreatorDisplay = modId;
if (modCreator instanceof User) {
modCreatorDisplay = modCreator.displayName;
}
// Create an embed for the note
const noteEmbed = new EmbedBuilder()
.setColor('#ff0000')
.setTitle(`Sus note for ${userName}`)
.setThumbnail(member.displayAvatarURL())
.setTitle(`Sus note for ${user.tag}`)
.setThumbnail(user.displayAvatarURL())
.addFields({
name: `ID: ${noteId} | Moderator: ${modName} | Date: <t:${Math.floor(
name: `ID: ${noteId} | Moderator: ${modCreatorDisplay} | Date: <t:${Math.floor(
note.time.getTime() / 1000,
)}>`,
value: note.note,
@@ -333,18 +423,28 @@ export class SusCommand extends Subcommand {
if (button.customId === `delete${noteId}`) {
await deactivateNote(noteId);
await interaction.editReply({
content: `${member}'s sus note (ID: ${noteId}) has been successfully removed`,
content: `${user}'s sus note (ID: ${noteId}) has been successfully removed`,
embeds: [],
});
// TODO create a new Prisma function to only count and not to get a whole list of sus notes
// Check how many notes the user has and if 0, then remove sus note
const notes = await findNotes(member.id, true);
const notes = await findNotes(userId, true);
// Checks if there are no notes on the user and if there's none, remove the sus role
if (notes.length === 0) {
await member.roles.remove(IDs.roles.restrictions.sus);
let member = guild.members.cache.get(userId);
if (!(member instanceof GuildMember)) {
member = await guild.members.fetch(userId).catch(() => undefined);
}
if (member instanceof GuildMember) {
await member.roles.remove(IDs.roles.restrictions.sus);
}
}
// Logs the removal of the sus note
await this.deleteNoteLogger(userId, mod, noteId, guild);
}
});
@@ -356,11 +456,58 @@ export class SusCommand extends Subcommand {
});
}
// Logs removal of 1 sus note
private async deleteNoteLogger(
userId: Snowflake,
mod: User,
noteId: number,
guild: Guild,
) {
// Find user
let user = guild.client.users.cache.get(userId);
if (!(user instanceof User)) {
user = await guild.client.users.fetch(userId).catch(() => undefined);
}
if (!(user instanceof User)) return;
// Log the sus note
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
| TextChannel
| undefined;
if (logChannel === undefined) {
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
| TextChannel
| undefined;
if (logChannel === undefined) {
this.container.logger.error('Sus Error: Could not fetch log channel');
return;
}
}
const embed = new EmbedBuilder()
.setColor('#28A745')
.setAuthor({
name: `Removed sus note for ${user.tag}`,
iconURL: `${user.displayAvatarURL()}`,
})
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
{ name: 'Note ID', value: `${noteId}`, inline: true },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [embed] });
}
public async removeAllNotes(
interaction: Subcommand.ChatInputCommandInteraction,
) {
// Get the arguments
const user = interaction.options.getUser('user', true);
const mod = interaction.user;
const { guild, channel } = interaction;
// Checks if all the variables are of the right type
@@ -470,6 +617,8 @@ export class SusCommand extends Subcommand {
embeds: [],
});
}
await this.deleteAllNotesLogger(user, mod, guild);
});
// Remove the buttons after they have been clicked
@@ -483,46 +632,36 @@ export class SusCommand extends Subcommand {
await member.roles.remove(IDs.roles.restrictions.sus);
}
// Non Application Command method of adding a sus note
// xlevra begged me to add this... so I guess here it is
public async addMessage(message: Message, args: Args) {
// Get arguments
let user: GuildMember;
try {
user = await args.pick('member');
} catch {
await message.react('❌');
await message.reply('User was not provided!');
return;
}
const note = args.finished ? null : await args.rest('string');
const mod = message.member;
// Logs removal of 1 sus note
private async deleteAllNotesLogger(user: User, mod: User, guild: Guild) {
// Log the sus note
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
| TextChannel
| undefined;
if (note === null) {
await message.react('❌');
await message.reply('No sus note was provided!');
return;
if (logChannel === undefined) {
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
| TextChannel
| undefined;
if (logChannel === undefined) {
this.container.logger.error('Sus Error: Could not fetch log channel');
return;
}
}
if (mod === null) {
await message.react('❌');
await message.reply(
'Moderator not found! Try again or contact a developer!',
);
return;
}
const embed = new EmbedBuilder()
.setColor('#28A745')
.setAuthor({
name: `Purged all sus notes for ${user.tag}`,
iconURL: `${user.displayAvatarURL()}`,
})
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
// Check if user and mod are on the database
await addExistingUser(user);
await addExistingUser(mod);
await addToDatabase(user.id, mod.id, note);
// Give the user the sus role they don't already have the sus note
if (!user.roles.cache.has(IDs.roles.restrictions.sus)) {
await user.roles.add(IDs.roles.restrictions.sus);
}
await message.react('✅');
await logChannel.send({ embeds: [embed] });
}
}

View File

@@ -68,7 +68,7 @@ export class WelcomeButtonHandler extends InteractionHandler {
await general.send(
`${member} Welcome to ARA! :D Please check <#${IDs.channels.information.roles}> ` +
`and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussion and debates.` +
`and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussions and debates.` +
"\n\nIf you would like to be verified as a vegan, join the 'Verification' voice channel.",
);
return;

View File

@@ -1,59 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2022 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { AllFlowsPrecondition } from '@sapphire/framework';
import type {
CommandInteraction,
ContextMenuCommandInteraction,
Message,
GuildMember,
} from 'discord.js';
import IDs from '#utils/ids';
export class PatreonOnlyPrecondition extends AllFlowsPrecondition {
public override async messageRun(message: Message) {
// for message command
return this.checkPatreon(message.member!);
}
public override async chatInputRun(interaction: CommandInteraction) {
// for slash command
return this.checkPatreon(interaction.member! as GuildMember);
}
public override async contextMenuRun(
interaction: ContextMenuCommandInteraction,
) {
// for context menu command
return this.checkPatreon(interaction.member! as GuildMember);
}
private async checkPatreon(user: GuildMember) {
return user.roles.cache.has(IDs.roles.patron) ||
user.roles.cache.has(IDs.roles.patreon)
? this.ok()
: this.error({ message: 'Only Patreon members can run this command!' });
}
}
declare module '@sapphire/framework' {
interface Preconditions {
PatreonOnly: never;
}
}

View File

@@ -19,7 +19,7 @@
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
import IDs from '#utils/ids';
import { TextChannel, EmbedBuilder } from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import { checkBan } from '#utils/database/ban';
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
@@ -36,7 +36,9 @@ export class TempBan extends ScheduledTask {
// Get the guild where the user is in
let guild = this.container.client.guilds.cache.get(payload.guildId);
if (guild === undefined) {
guild = await this.container.client.guilds.fetch(payload.guildId);
guild = await this.container.client.guilds
.fetch(payload.guildId)
.catch(() => undefined);
if (guild === undefined) {
this.container.logger.error('Temp Unban Task: Guild not found!');
return;
@@ -48,7 +50,7 @@ export class TempBan extends ScheduledTask {
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId);
user = await guild.client.users.fetch(userId).catch(() => undefined);
if (user === undefined) {
this.container.logger.error(
'Temp Unban Task: Could not fetch banned user!',
@@ -70,20 +72,27 @@ export class TempBan extends ScheduledTask {
await removeTempBan(userId);
// Log unban
let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted) as
| TextChannel
| undefined;
let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted);
if (logChannel === undefined) {
logChannel = (await guild.channels.fetch(
IDs.channels.logs.restricted,
)) as TextChannel | undefined;
if (logChannel === undefined) {
const logChannelFetch = await guild.channels
.fetch(IDs.channels.logs.restricted)
.catch(() => null);
if (logChannelFetch === null) {
this.container.logger.error(
`Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`,
);
return;
}
logChannel = logChannelFetch;
}
if (!logChannel.isTextBased()) {
this.container.logger.error(
'Temp Ban Listener: Log channel is not a text based channel!',
);
return;
}
const log = new EmbedBuilder()

View File

@@ -17,8 +17,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import type { VoiceChannel } from 'discord.js';
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
import { ChannelType } from 'discord.js';
export class VerifyTimeout extends ScheduledTask {
public constructor(
@@ -30,17 +30,24 @@ export class VerifyTimeout extends ScheduledTask {
public async run(payload: { channelId: string; userId: string }) {
// Get the guild where the user is in
let channel = this.container.client.channels.cache.get(
payload.channelId,
) as VoiceChannel | undefined;
let channel = this.container.client.channels.cache.get(payload.channelId);
if (channel === undefined) {
channel = (await this.container.client.channels.fetch(
payload.channelId,
)) as VoiceChannel | undefined;
if (channel === undefined) {
const channelFetch = await this.container.client.channels
.fetch(payload.channelId)
.catch(() => null);
if (channelFetch === null) {
this.container.logger.error('verifyTimeout: Channel not found!');
return;
}
channel = channelFetch;
}
if (channel.type !== ChannelType.GuildVoice) {
this.container.logger.error(
'verifyTimeout: Channel is not a voice channel!',
);
return;
}
if (channel.members.size < 2 && channel.members.has(payload.userId)) {

View File

@@ -32,7 +32,9 @@ export class VerifyUnblock extends ScheduledTask {
// Get the guild where the user is in
let guild = this.container.client.guilds.cache.get(payload.guildId);
if (guild === undefined) {
guild = await this.container.client.guilds.fetch(payload.guildId);
guild = await this.container.client.guilds
.fetch(payload.guildId)
.catch(() => undefined);
if (guild === undefined) {
this.container.logger.error('verifyUnblock: Guild not found!');
return;
@@ -42,7 +44,7 @@ export class VerifyUnblock extends ScheduledTask {
// Find GuildMember for the user
let user = guild.members.cache.get(payload.userId);
if (user === undefined) {
user = await guild.members.fetch(payload.userId).catch(undefined);
user = await guild.members.fetch(payload.userId).catch(() => undefined);
if (user === undefined) {
this.container.logger.error('verifyUnblock: GuildMember not found!');
return;

View File

@@ -29,6 +29,7 @@ export const blockedRoles = [
IDs.roles.staff.trialVerifier,
IDs.roles.staff.mentor,
IDs.roles.stageHost,
IDs.roles.booster,
];
export const blockedRolesAfterRestricted = [

View File

@@ -1,7 +1,7 @@
import { container } from '@sapphire/framework';
import { Prisma } from '@prisma/client';
export async function addToDatabase(
export async function addSusNoteDB(
userId: string,
modId: string,
message: string,
@@ -10,13 +10,23 @@ export async function addToDatabase(
await container.database.sus.create({
data: {
user: {
connect: {
id: userId,
connectOrCreate: {
where: {
id: userId,
},
create: {
id: userId,
},
},
},
mod: {
connect: {
id: modId,
connectOrCreate: {
where: {
id: modId,
},
create: {
id: modId,
},
},
},
note: message,

View File

@@ -20,6 +20,7 @@
const devIDs = {
roles: {
trusted: '999431675081666599',
booster: '',
nonvegan: {
nonvegan: '999431675081666598',
vegCurious: '999431675098447932',
@@ -57,6 +58,8 @@ const devIDs = {
verifierCoordinator: '999431675140382810',
eventCoordinator: '999431675165556817',
outreachCoordinator: '999431675140382807',
mediaCoordinator: '1204801056404676618',
hrCoordinator: '1204795893480431657',
restricted: '999431675123597407',
moderator: '999431675123597408',
trialModerator: '999431675123597404',

View File

@@ -22,6 +22,7 @@ import devIDs from '#utils/devIDs';
let IDs = {
roles: {
trusted: '731563158011117590',
booster: '731213264540795012',
nonvegan: {
nonvegan: '774763753308815400',
vegCurious: '832656046572961803',
@@ -59,6 +60,8 @@ let IDs = {
verifierCoordinator: '940721280376778822',
eventCoordinator: '944732860554817586',
outreachCoordinator: '954804769476730890',
mediaCoordinator: '1203778509449723914',
hrCoordinator: '1203802120180989993',
restricted: '851624392928264222',
moderator: '826157475815489598',
trialModerator: '982074555596152904',

View File

@@ -31,7 +31,7 @@
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "src" /* Specify the base directory to resolve non-relative module names. */,
"paths": {
"#utils/*": ["./utils/*"]
"#utils/*": ["./utils/*"],
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
@@ -79,7 +79,7 @@
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@@ -103,5 +103,5 @@
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
// "skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["src"]
"include": ["src"],
}