mirror of
https://github.com/veganhacktivists/arabot.git
synced 2025-12-22 12:28:50 +01:00
Compare commits
90 Commits
feat/log-e
...
71f0ee9f01
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71f0ee9f01 | ||
|
|
881f9bfc24 | ||
|
|
98b9ac6fde | ||
|
|
1f92bf5d68 | ||
|
|
d9f04e8d49 | ||
|
|
b4c8f0785c | ||
|
|
7918f73e7d | ||
|
|
ea211a9111 | ||
|
|
32776a2311 | ||
|
|
d72b66f988 | ||
|
|
e03bd6e85e | ||
|
|
a400cf9507 | ||
|
|
2fbb6c9265 | ||
|
|
fc8c12b346 | ||
|
|
9ebf8a6938 | ||
|
|
bc7f2ffcfd | ||
|
|
86f391e131 | ||
|
|
63c3b14b1c | ||
|
|
a5187ec567 | ||
|
|
222c3cb81a | ||
|
|
8f8580398e | ||
|
|
fe88e9f87b | ||
|
|
4ad35f5b57 | ||
|
|
1c9f6612a3 | ||
|
|
88dd678bdc | ||
|
|
9c51be9ab6 | ||
|
|
128b15f18f | ||
|
|
dba9aa970e | ||
|
|
0ac0ff7f5c | ||
|
|
ae0afa02db | ||
|
|
3009a0f923 | ||
|
|
a09b007831 | ||
|
|
325dc0d0d0 | ||
|
|
71a065d3ca | ||
|
|
613f53491b | ||
|
|
19721c10ea | ||
|
|
bd87a8b6c6 | ||
|
|
46ef2fd8e2 | ||
|
|
d8c91fd39b | ||
|
|
fabd381051 | ||
|
|
a5758dc6ef | ||
|
|
9ff5b78aff | ||
|
|
f4655829e2 | ||
|
|
c82d256be4 | ||
|
|
a9039572d1 | ||
|
|
2cf7998cd9 | ||
|
|
63a4d651af | ||
|
|
a2dba859f2 | ||
|
|
a7b772f77a | ||
|
|
1fa87b8a4a | ||
|
|
4e99a5456f | ||
|
|
62d941dfcb | ||
|
|
e6b1463a1a | ||
|
|
5793bbb461 | ||
|
|
cf8142b86a | ||
|
|
502d5c5cdf | ||
|
|
8f071b8043 | ||
|
|
36ce086532 | ||
|
|
617834833a | ||
|
|
4d92250500 | ||
|
|
f898dada56 | ||
|
|
9e2f2c7558 | ||
|
|
e15e5da5aa | ||
|
|
e89f056b94 | ||
|
|
6acd012e7a | ||
|
|
75174f711d | ||
|
|
5e69ea6126 | ||
|
|
3d8aba5577 | ||
|
|
e7839552f8 | ||
|
|
3b0666e80d | ||
|
|
3d8a9be7f2 | ||
|
|
a7f608c1f0 | ||
|
|
cb457136d4 | ||
|
|
7e984c4857 | ||
|
|
0ea9ea3f64 | ||
|
|
19e70ebdbc | ||
|
|
5409be3d75 | ||
|
|
edd3caf9c0 | ||
|
|
5a87f97a74 | ||
|
|
feab05c1ea | ||
|
|
9b505fbece | ||
|
|
a5ba493372 | ||
|
|
98e514b5e9 | ||
|
|
172508c741 | ||
|
|
730f3e6a28 | ||
|
|
fbc2944b92 | ||
|
|
6172dc6ac6 | ||
|
|
b762ae3bc8 | ||
|
|
c8eb8299dd | ||
|
|
785e844da8 |
@@ -1,6 +1,6 @@
|
|||||||
.idea
|
.idea
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
tsconfig.tsbuildinfo
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
.env
|
.env
|
||||||
|
*.md
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ POSTGRES_USER=USERNAME
|
|||||||
POSTGRES_PASSWORD=PASSWORD
|
POSTGRES_PASSWORD=PASSWORD
|
||||||
POSTGRES_DB=DB
|
POSTGRES_DB=DB
|
||||||
|
|
||||||
# Redis
|
# Redis (if running everything within docker compose, use "redis" for the host and leave the rest empty)
|
||||||
REDIS_URL= # URL to redis database (if running everything within docker compose, use "redis")
|
REDIS_HOST= # URL to redis database
|
||||||
BULLMQ_URL # URL for redis database, but without redis:// and credentials
|
REDIS_USER= # redis database user
|
||||||
|
REDIS_PASSWORD= # redis database password
|
||||||
|
REDIS_PORT= # redis database port
|
||||||
|
|
||||||
# Database URL (designed for Postgres, but designed on Prisma)
|
# Database URL (designed for Postgres, but designed on Prisma)
|
||||||
DATABASE_URL= # "postgresql://USERNAME:PASSWORD@postgres:5432/DB?schema=ara&sslmode=prefer"
|
DATABASE_URL= # "postgresql://USERNAME:PASSWORD@postgres:5432/DB?schema=ara&sslmode=prefer"
|
||||||
|
|||||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node-linker=hoisted
|
||||||
|
shamefully-hoist=true
|
||||||
|
public-hoist-pattern[]=@sapphire/*
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
tsconfig.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|||||||
36
Dockerfile
36
Dockerfile
@@ -1,24 +1,26 @@
|
|||||||
FROM node:20
|
FROM node:22 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 .
|
FROM base AS build
|
||||||
COPY --chown=node:node package-lock.json .
|
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||||
COPY --chown=node:node tsconfig.json .
|
RUN pnpm exec prisma generate
|
||||||
COPY --chown=node:node prisma ./prisma/
|
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 chown node:node .
|
||||||
|
|
||||||
RUN npx prisma generate
|
|
||||||
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
RUN chown node:node /opt/app/
|
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
|
CMD [ "pnpm", "run", "start:migrate"]
|
||||||
CMD [ "npm", "run", "start:migrate"]
|
|
||||||
|
|||||||
5
nixpacks.toml
Normal file
5
nixpacks.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[phases.build]
|
||||||
|
cmds = ["pnpm prisma generate", "..."]
|
||||||
|
|
||||||
|
[start]
|
||||||
|
cmd = 'pnpm run start:migrate'
|
||||||
4556
package-lock.json
generated
4556
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
51
package.json
51
package.json
@@ -4,10 +4,11 @@
|
|||||||
"description": "A Discord bot for Animal Rights Advocates",
|
"description": "A Discord bot for Animal Rights Advocates",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"cleanBuild": "rm -rf ./dist && tsc",
|
"cleanBuild": "rm -rf ./dist && tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"start:migrate": "prisma migrate deploy && npm run start"
|
"start:migrate": "prisma migrate deploy && pnpm run start"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"#utils/*": "./dist/utils/*.js"
|
"#utils/*": "./dist/utils/*.js"
|
||||||
@@ -28,31 +29,35 @@
|
|||||||
"url": "https://github.com/veganhacktivists/arabot/issues"
|
"url": "https://github.com/veganhacktivists/arabot/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20",
|
||||||
|
"pnpm": ">=9"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.7.1",
|
"@prisma/client": "^5.22.0",
|
||||||
"@sapphire/discord.js-utilities": "^7.1.5",
|
"@sapphire/discord.js-utilities": "^7.3.2",
|
||||||
"@sapphire/framework": "^5.0.5",
|
"@sapphire/framework": "^5.3.2",
|
||||||
"@sapphire/plugin-logger": "^4.0.1",
|
"@sapphire/plugin-logger": "^4.0.2",
|
||||||
"@sapphire/plugin-scheduled-tasks": "^10.0.0",
|
"@sapphire/plugin-scheduled-tasks": "^10.0.2",
|
||||||
"@sapphire/plugin-subcommands": "^6.0.2",
|
"@sapphire/plugin-subcommands": "^6.0.3",
|
||||||
"@sapphire/stopwatch": "^1.5.1",
|
"@sapphire/stopwatch": "^1.5.4",
|
||||||
"@sapphire/time-utilities": "^1.7.11",
|
"@sapphire/time-utilities": "^1.7.14",
|
||||||
"@sapphire/ts-config": "^5.0.0",
|
"@sapphire/ts-config": "^5.0.1",
|
||||||
"@sapphire/utilities": "^3.15.1",
|
"@sapphire/utilities": "^3.18.1",
|
||||||
"@types/node": "^20.10.6",
|
"bullmq": "^5.34.10",
|
||||||
"bullmq": "^5.1.1",
|
"discord.js": "^14.17.3",
|
||||||
"discord.js": "^14.14.1",
|
"ioredis": "^5.4.2",
|
||||||
"redis": "^4.6.12",
|
"ts-node": "^10.9.2",
|
||||||
"ts-node": "^10.9.1",
|
"typescript": "~5.4.5"
|
||||||
"typescript": "^5.3.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ioredis": "^5.0.0",
|
"@types/node": "^20.17.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.17.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"prettier": "3.1.1",
|
"prettier": "3.2.4",
|
||||||
"prisma": "^5.7.1"
|
"prisma": "^5.22.0"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
|
||||||
}
|
}
|
||||||
|
|||||||
1899
pnpm-lock.yaml
generated
Normal file
1899
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `channelId` to the `StatRole` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "StatRole" ADD COLUMN "channelId" TEXT NOT NULL;
|
||||||
@@ -225,6 +225,7 @@ model StatRole {
|
|||||||
stat Stat @relation(fields: [statId], references: [id])
|
stat Stat @relation(fields: [statId], references: [id])
|
||||||
statId Int @id
|
statId Int @id
|
||||||
roleId String
|
roleId String
|
||||||
|
channelId String
|
||||||
}
|
}
|
||||||
|
|
||||||
model ParticipantStat {
|
model ParticipantStat {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { Message } from 'discord.js';
|
import { Message, MessageFlagsBitField } from 'discord.js';
|
||||||
import { ChannelType, TextChannel } from 'discord.js';
|
import { ChannelType, TextChannel } from 'discord.js';
|
||||||
|
|
||||||
export class AnonymousCommand extends Command {
|
export class AnonymousCommand extends Command {
|
||||||
@@ -67,8 +67,8 @@ export class AnonymousCommand extends Command {
|
|||||||
if (guild === null) {
|
if (guild === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching guild!',
|
content: 'Error fetching guild!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,8 +77,17 @@ export class AnonymousCommand extends Command {
|
|||||||
if (interaction.channel === null) {
|
if (interaction.channel === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error getting the channel!',
|
content: 'Error getting the channel!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!interaction.channel.isSendable()) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: `I do not have sufficient permissions to send a message in ${interaction.channel}!`,
|
||||||
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -86,8 +95,8 @@ export class AnonymousCommand extends Command {
|
|||||||
await interaction.channel.send(message);
|
await interaction.channel.send(message);
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Sent the message',
|
content: 'Sent the message',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -95,8 +104,8 @@ export class AnonymousCommand extends Command {
|
|||||||
if (channel.type !== ChannelType.GuildText) {
|
if (channel.type !== ChannelType.GuildText) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Could not send, unsupported text channel!',
|
content: 'Could not send, unsupported text channel!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +114,8 @@ export class AnonymousCommand extends Command {
|
|||||||
|
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Sent the message',
|
content: 'Sent the message',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +130,7 @@ export class AnonymousCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel.isTextBased()) {
|
if (channel.isSendable()) {
|
||||||
await channel.send(text);
|
await channel.send(text);
|
||||||
} else {
|
} else {
|
||||||
await message.react('❌');
|
await message.react('❌');
|
||||||
|
|||||||
@@ -356,6 +356,12 @@ export class PrivateCommand extends Subcommand {
|
|||||||
} else if (user.roles.cache.has(IDs.roles.staff.eventCoordinator)) {
|
} else if (user.roles.cache.has(IDs.roles.staff.eventCoordinator)) {
|
||||||
name = 'event';
|
name = 'event';
|
||||||
id = IDs.roles.staff.eventCoordinator;
|
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 {
|
} else {
|
||||||
name = 'coordinator';
|
name = 'coordinator';
|
||||||
id = IDs.roles.staff.coordinator;
|
id = IDs.roles.staff.coordinator;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { User, Guild, Message } from 'discord.js';
|
import type { User, Guild, Message } from 'discord.js';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { getBalance } from '#utils/database/economy';
|
import { getBalance } from '#utils/database/fun/economy';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
|
|
||||||
export class BalanceCommand extends Command {
|
export class BalanceCommand extends Command {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import { Time } from '@sapphire/time-utilities';
|
import { Time } from '@sapphire/time-utilities';
|
||||||
import type { User, Guild, GuildMember, Message } from 'discord.js';
|
import type { User, Guild, GuildMember, Message } from 'discord.js';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { daily, getLastDaily } from '#utils/database/economy';
|
import { daily, getLastDaily } from '#utils/database/fun/economy';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { User, Guild, Message } from 'discord.js';
|
import type { User, Guild, Message } from 'discord.js';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { getBalance, transfer } from '#utils/database/economy';
|
import { getBalance, transfer } from '#utils/database/fun/economy';
|
||||||
import { EmbedBuilder, TextChannel } from 'discord.js';
|
import { EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import { EmbedBuilder, GuildMember } from 'discord.js';
|
import { EmbedBuilder, GuildMember } from 'discord.js';
|
||||||
import { N1984 } from '#utils/gifs';
|
import { N1984 } from '#utils/gifs';
|
||||||
import { addFunLog, countTotal } from '#utils/database/fun';
|
import { addFunLog, countTotal } from '#utils/database/fun/fun';
|
||||||
|
|
||||||
export class N1984Command extends Command {
|
export class N1984Command extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
@@ -28,7 +28,7 @@ export class N1984Command extends Command {
|
|||||||
...options,
|
...options,
|
||||||
name: '1984',
|
name: '1984',
|
||||||
description: 'this is literally 1984',
|
description: 'this is literally 1984',
|
||||||
preconditions: ['CoordinatorOnly', 'ModOnly'],
|
preconditions: [['CoordinatorOnly', 'ModOnly']],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import { EmbedBuilder, GuildMember } from 'discord.js';
|
import { EmbedBuilder, GuildMember } from 'discord.js';
|
||||||
import { Cringe } from '#utils/gifs';
|
import { Cringe } from '#utils/gifs';
|
||||||
import { addFunLog, countTotal } from '#utils/database/fun';
|
import { addFunLog, countTotal } from '#utils/database/fun/fun';
|
||||||
|
|
||||||
export class CringeCommand extends Command {
|
export class CringeCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export class HappyCommand extends Command {
|
|||||||
...options,
|
...options,
|
||||||
name: 'happy',
|
name: 'happy',
|
||||||
description: 'Express your happiness',
|
description: 'Express your happiness',
|
||||||
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import { EmbedBuilder, GuildMember } from 'discord.js';
|
import { EmbedBuilder, GuildMember } from 'discord.js';
|
||||||
import { Hugs } from '#utils/gifs';
|
import { Hugs } from '#utils/gifs';
|
||||||
import { addFunLog, countTotal } from '#utils/database/fun';
|
import { addFunLog, countTotal } from '#utils/database/fun/fun';
|
||||||
|
|
||||||
export class HugCommand extends Command {
|
export class HugCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import { EmbedBuilder, GuildMember } from 'discord.js';
|
import { EmbedBuilder, GuildMember } from 'discord.js';
|
||||||
import { Kill } from '#utils/gifs';
|
import { Kill } from '#utils/gifs';
|
||||||
import { addFunLog, countTotal } from '#utils/database/fun';
|
import { addFunLog, countTotal } from '#utils/database/fun/fun';
|
||||||
|
|
||||||
export class KillCommand extends Command {
|
export class KillCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import { EmbedBuilder, GuildMember } from 'discord.js';
|
import { EmbedBuilder, GuildMember } from 'discord.js';
|
||||||
import { Poke } from '#utils/gifs';
|
import { Poke } from '#utils/gifs';
|
||||||
import { addFunLog, countTotal } from '#utils/database/fun';
|
import { addFunLog, countTotal } from '#utils/database/fun/fun';
|
||||||
|
|
||||||
export class PokeCommand extends Command {
|
export class PokeCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export class SadCommand extends Command {
|
|||||||
...options,
|
...options,
|
||||||
name: 'sad',
|
name: 'sad',
|
||||||
description: 'Express your sadness',
|
description: 'Express your sadness',
|
||||||
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export class ShrugCommand extends Command {
|
|||||||
...options,
|
...options,
|
||||||
name: 'shrug',
|
name: 'shrug',
|
||||||
description: 'Ugh... whatever... idk...',
|
description: 'Ugh... whatever... idk...',
|
||||||
preconditions: [['CoordinatorOnly', 'PatreonOnly']],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import type { User, Message, Snowflake, TextChannel, Guild } from 'discord.js';
|
import type { User, Message, Snowflake, TextChannel, Guild } from 'discord.js';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { addBan, checkBan } from '#utils/database/ban';
|
import { addBan, checkBan } from '#utils/database/moderation/ban';
|
||||||
import { addEmptyUser, updateUser } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
|
import {
|
||||||
|
checkTempBan,
|
||||||
|
removeTempBan,
|
||||||
|
} from '#utils/database/moderation/tempBan';
|
||||||
|
|
||||||
export class BanCommand extends Command {
|
export class BanCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { Duration, DurationFormatter } from '@sapphire/time-utilities';
|
|||||||
import type { User, Snowflake, TextChannel, Guild } from 'discord.js';
|
import type { User, Snowflake, TextChannel, Guild } from 'discord.js';
|
||||||
import { EmbedBuilder, Message } from 'discord.js';
|
import { EmbedBuilder, Message } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { addTempBan, checkTempBan } from '#utils/database/tempBan';
|
import { addTempBan, checkTempBan } from '#utils/database/moderation/tempBan';
|
||||||
import { addEmptyUser, updateUser } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, updateUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
export class TempBanCommand extends Command {
|
export class TempBanCommand extends Command {
|
||||||
|
|||||||
@@ -28,8 +28,11 @@ import type {
|
|||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { removeBan, checkBan, addBan } from '#utils/database/ban';
|
import { removeBan, checkBan, addBan } from '#utils/database/moderation/ban';
|
||||||
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
|
import {
|
||||||
|
checkTempBan,
|
||||||
|
removeTempBan,
|
||||||
|
} from '#utils/database/moderation/tempBan';
|
||||||
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
export class UnbanCommand extends Command {
|
export class UnbanCommand extends Command {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
updateUser,
|
updateUser,
|
||||||
fetchRoles,
|
fetchRoles,
|
||||||
} from '#utils/database/dbExistingUser';
|
} from '#utils/database/dbExistingUser';
|
||||||
import { restrict, checkActive } from '#utils/database/restriction';
|
import { restrict, checkActive } from '#utils/database/moderation/restriction';
|
||||||
import { randint } from '#utils/maths';
|
import { randint } from '#utils/maths';
|
||||||
import { blockedRolesAfterRestricted } from '#utils/blockedRoles';
|
import { blockedRolesAfterRestricted } from '#utils/blockedRoles';
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import { ChannelType, EmbedBuilder } from 'discord.js';
|
import { ChannelType, EmbedBuilder } from 'discord.js';
|
||||||
import type { Message, TextChannel, Guild, Snowflake } from 'discord.js';
|
import type { Message, TextChannel, Guild, Snowflake } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { getRestrictions } from '#utils/database/restriction';
|
import { getRestrictions } from '#utils/database/moderation/restriction';
|
||||||
import { checkStaff } from '#utils/checker';
|
import { checkStaff } from '#utils/checker';
|
||||||
|
|
||||||
export class RestrictLogsCommand extends Command {
|
export class RestrictLogsCommand extends Command {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
unRestrict,
|
unRestrict,
|
||||||
checkActive,
|
checkActive,
|
||||||
unRestrictLegacy,
|
unRestrictLegacy,
|
||||||
} from '#utils/database/restriction';
|
} from '#utils/database/moderation/restriction';
|
||||||
|
|
||||||
export class UnRestrictCommand extends Command {
|
export class UnRestrictCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
@@ -25,17 +25,21 @@ import {
|
|||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonInteraction,
|
ButtonInteraction,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
|
User,
|
||||||
|
Guild,
|
||||||
|
TextChannel,
|
||||||
|
GuildMember,
|
||||||
|
Snowflake,
|
||||||
|
MessageFlagsBitField,
|
||||||
} from 'discord.js';
|
} 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 {
|
import {
|
||||||
addToDatabase,
|
addSusNoteDB,
|
||||||
findNotes,
|
findNotes,
|
||||||
getNote,
|
getNote,
|
||||||
deactivateNote,
|
deactivateNote,
|
||||||
deactivateAllNotes,
|
deactivateAllNotes,
|
||||||
} from '#utils/database/sus';
|
} from '#utils/database/moderation/sus';
|
||||||
import { checkStaff } from '#utils/checker';
|
import { checkStaff } from '#utils/checker';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { createSusLogEmbed } from '#utils/embeds';
|
import { createSusLogEmbed } from '#utils/embeds';
|
||||||
@@ -54,8 +58,8 @@ export class SusCommand extends Subcommand {
|
|||||||
{
|
{
|
||||||
name: 'add',
|
name: 'add',
|
||||||
default: true,
|
default: true,
|
||||||
chatInputRun: 'addNote',
|
chatInputRun: 'addNoteChatInput',
|
||||||
messageRun: 'addMessage',
|
messageRun: 'addNoteMessage',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'view',
|
name: 'view',
|
||||||
@@ -143,7 +147,9 @@ export class SusCommand extends Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Subcommand to add sus note
|
// Subcommand to add sus note
|
||||||
public async addNote(interaction: Subcommand.ChatInputCommandInteraction) {
|
public async addNoteChatInput(
|
||||||
|
interaction: Subcommand.ChatInputCommandInteraction,
|
||||||
|
) {
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const user = interaction.options.getUser('user', true);
|
const user = interaction.options.getUser('user', true);
|
||||||
const note = interaction.options.getString('note', true);
|
const note = interaction.options.getString('note', true);
|
||||||
@@ -154,40 +160,127 @@ export class SusCommand extends Subcommand {
|
|||||||
if (guild === null) {
|
if (guild === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching guild!',
|
content: 'Error fetching guild!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const info = await this.addNote(user, mod, note, guild);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: info.message,
|
||||||
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = message.guild;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
// Add the data to the database
|
// Add the data to the database
|
||||||
|
await addSusNoteDB(user.id, mod.id, note);
|
||||||
|
|
||||||
// Check if the user exists on the database
|
// Gives the sus role to the user
|
||||||
const member = guild.members.cache.get(user.id);
|
await this.addSusRole(user, guild);
|
||||||
const modMember = guild.members.cache.get(mod.id);
|
|
||||||
|
|
||||||
if (member === undefined || modMember === undefined) {
|
info.message = `Added the sus note for ${user}: ${note}`;
|
||||||
await interaction.reply({
|
info.success = true;
|
||||||
content: 'Error fetching users!',
|
|
||||||
ephemeral: true,
|
// Log the sus note
|
||||||
});
|
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
|
||||||
return;
|
| 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user and mod are on the database
|
const message = new EmbedBuilder()
|
||||||
await addExistingUser(member);
|
.setColor('#0099ff')
|
||||||
await addExistingUser(modMember);
|
.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 addToDatabase(user.id, mod.id, note);
|
await logChannel.send({ embeds: [message] });
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addSusRole(user: User, guild: Guild) {
|
||||||
|
// 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).catch(() => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Give the user the sus role they don't already have the sus note
|
// Give the user the sus role they don't already have the sus note
|
||||||
if (!member.roles.cache.has(IDs.roles.restrictions.sus)) {
|
if (!member.roles.cache.has(IDs.roles.restrictions.sus)) {
|
||||||
await member.roles.add(IDs.roles.restrictions.sus);
|
await member.roles.add(IDs.roles.restrictions.sus);
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
content: `${user} note: ${note}`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async listNote(interaction: Subcommand.ChatInputCommandInteraction) {
|
public async listNote(interaction: Subcommand.ChatInputCommandInteraction) {
|
||||||
@@ -199,7 +292,7 @@ export class SusCommand extends Subcommand {
|
|||||||
if (guild == null) {
|
if (guild == null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching guild!',
|
content: 'Error fetching guild!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -213,8 +306,8 @@ export class SusCommand extends Subcommand {
|
|||||||
if (notes.length === 0) {
|
if (notes.length === 0) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: `${user} has no sus notes!`,
|
content: `${user} has no sus notes!`,
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -225,22 +318,23 @@ export class SusCommand extends Subcommand {
|
|||||||
// Sends the notes to the user
|
// Sends the notes to the user
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
embeds: [noteEmbed],
|
embeds: [noteEmbed],
|
||||||
ephemeral: !staffChannel,
|
flags: staffChannel ? undefined : MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async removeNote(interaction: Subcommand.ChatInputCommandInteraction) {
|
public async removeNote(interaction: Subcommand.ChatInputCommandInteraction) {
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const noteId = interaction.options.getInteger('id', true);
|
const noteId = interaction.options.getInteger('id', true);
|
||||||
|
const mod = interaction.user;
|
||||||
const { guild, channel } = interaction;
|
const { guild, channel } = interaction;
|
||||||
|
|
||||||
// Checks if all the variables are of the right type
|
// Checks if all the variables are of the right type
|
||||||
if (guild === null || channel === null) {
|
if (guild === null || channel === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching guild or channel!',
|
content: 'Error fetching guild or channel!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -252,45 +346,46 @@ export class SusCommand extends Subcommand {
|
|||||||
if (note === null) {
|
if (note === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching note from database!',
|
content: 'Error fetching note from database!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userId = note.userId;
|
||||||
|
const modId = note.modId;
|
||||||
|
|
||||||
// Get user GuildMembers for user and mod and person who ran command
|
// Get user GuildMembers for user and mod and person who ran command
|
||||||
const member = await guild.members.cache.get(note.userId);
|
let user = guild.client.users.cache.get(userId);
|
||||||
const mod = await guild.members.cache.get(note.modId);
|
if (!(user instanceof User)) {
|
||||||
|
user = await guild.client.users.fetch(userId).catch(() => undefined);
|
||||||
// TODO fix if user left the server
|
}
|
||||||
if (member === undefined || mod === undefined) {
|
if (user === undefined) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching users from Discord!',
|
content: 'Error fetching user!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user's name
|
let modCreator = guild.client.users.cache.get(modId);
|
||||||
let userName = note.userId;
|
if (!(modCreator instanceof User)) {
|
||||||
if (member !== undefined) {
|
modCreator = await guild.client.users.fetch(modId).catch(() => undefined);
|
||||||
userName = member.displayName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mod name
|
let modCreatorDisplay = modId;
|
||||||
let modName = note.modId;
|
if (modCreator instanceof User) {
|
||||||
if (mod !== undefined) {
|
modCreatorDisplay = modCreator.displayName;
|
||||||
modName = mod.displayName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an embed for the note
|
// Create an embed for the note
|
||||||
const noteEmbed = new EmbedBuilder()
|
const noteEmbed = new EmbedBuilder()
|
||||||
.setColor('#ff0000')
|
.setColor('#ff0000')
|
||||||
.setTitle(`Sus note for ${userName}`)
|
.setTitle(`Sus note for ${user.tag}`)
|
||||||
.setThumbnail(member.displayAvatarURL())
|
.setThumbnail(user.displayAvatarURL())
|
||||||
.addFields({
|
.addFields({
|
||||||
name: `ID: ${noteId} | Moderator: ${modName} | Date: <t:${Math.floor(
|
name: `ID: ${noteId} | Moderator: ${modCreatorDisplay} | Date: <t:${Math.floor(
|
||||||
note.time.getTime() / 1000,
|
note.time.getTime() / 1000,
|
||||||
)}>`,
|
)}>`,
|
||||||
value: note.note,
|
value: note.note,
|
||||||
@@ -312,16 +407,21 @@ export class SusCommand extends Subcommand {
|
|||||||
const message = await interaction.reply({
|
const message = await interaction.reply({
|
||||||
embeds: [noteEmbed],
|
embeds: [noteEmbed],
|
||||||
components: [buttons],
|
components: [buttons],
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Checks if the message is not an APIMessage
|
// Checks if the message is not an APIMessage
|
||||||
if (!isMessageInstance(message)) {
|
if (message.resource === null) {
|
||||||
await interaction.editReply('Failed to retrieve the message :(');
|
await interaction.editReply('Failed to retrieve the message :(');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!channel.isSendable()) {
|
||||||
|
await interaction.editReply('Cannot send messages in this channel!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for the button presses
|
// Listen for the button presses
|
||||||
const collector = channel.createMessageComponentCollector({
|
const collector = channel.createMessageComponentCollector({
|
||||||
max: 1, // Maximum of 1 button press
|
max: 1, // Maximum of 1 button press
|
||||||
@@ -333,19 +433,29 @@ export class SusCommand extends Subcommand {
|
|||||||
if (button.customId === `delete${noteId}`) {
|
if (button.customId === `delete${noteId}`) {
|
||||||
await deactivateNote(noteId);
|
await deactivateNote(noteId);
|
||||||
await interaction.editReply({
|
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: [],
|
embeds: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO create a new Prisma function to only count and not to get a whole list of sus notes
|
// 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
|
// 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
|
// Checks if there are no notes on the user and if there's none, remove the sus role
|
||||||
if (notes.length === 0) {
|
if (notes.length === 0) {
|
||||||
|
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);
|
await member.roles.remove(IDs.roles.restrictions.sus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs the removal of the sus note
|
||||||
|
await this.deleteNoteLogger(userId, mod, noteId, guild);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove the buttons after they have been clicked
|
// Remove the buttons after they have been clicked
|
||||||
@@ -356,19 +466,66 @@ 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 === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId).catch(() => undefined);
|
||||||
|
}
|
||||||
|
if (user === undefined) 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(
|
public async removeAllNotes(
|
||||||
interaction: Subcommand.ChatInputCommandInteraction,
|
interaction: Subcommand.ChatInputCommandInteraction,
|
||||||
) {
|
) {
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const user = interaction.options.getUser('user', true);
|
const user = interaction.options.getUser('user', true);
|
||||||
|
const mod = interaction.user;
|
||||||
const { guild, channel } = interaction;
|
const { guild, channel } = interaction;
|
||||||
|
|
||||||
// Checks if all the variables are of the right type
|
// Checks if all the variables are of the right type
|
||||||
if (guild === null || channel === null) {
|
if (guild === null || channel === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching guild or channel!',
|
content: 'Error fetching guild or channel!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -379,8 +536,8 @@ export class SusCommand extends Subcommand {
|
|||||||
if (member === undefined) {
|
if (member === undefined) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching user!',
|
content: 'Error fetching user!',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -393,8 +550,8 @@ export class SusCommand extends Subcommand {
|
|||||||
if (notes.length === 0) {
|
if (notes.length === 0) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: `${user} had no notes!`,
|
content: `${user} had no notes!`,
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -444,16 +601,21 @@ export class SusCommand extends Subcommand {
|
|||||||
const message = await interaction.reply({
|
const message = await interaction.reply({
|
||||||
embeds: [noteEmbed],
|
embeds: [noteEmbed],
|
||||||
components: [buttons],
|
components: [buttons],
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Checks if the message is not an APIMessage
|
// Checks if the message is not an APIMessage
|
||||||
if (!isMessageInstance(message)) {
|
if (message.resource === null) {
|
||||||
await interaction.editReply('Failed to retrieve the message :(');
|
await interaction.editReply('Failed to retrieve the message :(');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!channel.isSendable()) {
|
||||||
|
await interaction.editReply('Cannot send messages in this channel!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for the button presses
|
// Listen for the button presses
|
||||||
const collector = channel.createMessageComponentCollector({
|
const collector = channel.createMessageComponentCollector({
|
||||||
max: 1, // Maximum of 1 button press
|
max: 1, // Maximum of 1 button press
|
||||||
@@ -470,6 +632,8 @@ export class SusCommand extends Subcommand {
|
|||||||
embeds: [],
|
embeds: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.deleteAllNotesLogger(user, mod, guild);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove the buttons after they have been clicked
|
// Remove the buttons after they have been clicked
|
||||||
@@ -483,46 +647,36 @@ export class SusCommand extends Subcommand {
|
|||||||
await member.roles.remove(IDs.roles.restrictions.sus);
|
await member.roles.remove(IDs.roles.restrictions.sus);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non Application Command method of adding a sus note
|
// Logs removal of 1 sus note
|
||||||
// xlevra begged me to add this... so I guess here it is
|
private async deleteAllNotesLogger(user: User, mod: User, guild: Guild) {
|
||||||
public async addMessage(message: Message, args: Args) {
|
// Log the sus note
|
||||||
// Get arguments
|
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
|
||||||
let user: GuildMember;
|
| TextChannel
|
||||||
try {
|
| undefined;
|
||||||
user = await args.pick('member');
|
|
||||||
} catch {
|
if (logChannel === undefined) {
|
||||||
await message.react('❌');
|
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
|
||||||
await message.reply('User was not provided!');
|
| TextChannel
|
||||||
|
| undefined;
|
||||||
|
if (logChannel === undefined) {
|
||||||
|
this.container.logger.error('Sus Error: Could not fetch log channel');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const note = args.finished ? null : await args.rest('string');
|
|
||||||
const mod = message.member;
|
|
||||||
|
|
||||||
if (note === null) {
|
|
||||||
await message.react('❌');
|
|
||||||
await message.reply('No sus note was provided!');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod === null) {
|
const embed = new EmbedBuilder()
|
||||||
await message.react('❌');
|
.setColor('#28A745')
|
||||||
await message.reply(
|
.setAuthor({
|
||||||
'Moderator not found! Try again or contact a developer!',
|
name: `Purged all sus notes for ${user.tag}`,
|
||||||
);
|
iconURL: `${user.displayAvatarURL()}`,
|
||||||
return;
|
})
|
||||||
}
|
.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 logChannel.send({ embeds: [embed] });
|
||||||
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('✅');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@
|
|||||||
|
|
||||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { GuildMember, Message } from 'discord.js';
|
import type { GuildMember, Message } from 'discord.js';
|
||||||
import { addMute, removeMute, checkActive } from '#utils/database/vcMute';
|
import {
|
||||||
|
addMute,
|
||||||
|
removeMute,
|
||||||
|
checkActive,
|
||||||
|
} from '#utils/database/moderation/vcMute';
|
||||||
import { addExistingUser } from '#utils/database/dbExistingUser';
|
import { addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
export class VCMuteCommand extends Command {
|
export class VCMuteCommand extends Command {
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import { EmbedBuilder, TextChannel } from 'discord.js';
|
import { EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
import type { Message, Guild, User } from 'discord.js';
|
import type { Message, Guild, User } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { deleteWarning, fetchWarning } from '#utils/database/warnings';
|
import {
|
||||||
|
deleteWarning,
|
||||||
|
fetchWarning,
|
||||||
|
} from '#utils/database/moderation/warnings';
|
||||||
import { checkStaff } from '#utils/checker';
|
import { checkStaff } from '#utils/checker';
|
||||||
|
|
||||||
export class DeleteWarningCommand extends Command {
|
export class DeleteWarningCommand extends Command {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@sapphire/framework';
|
} from '@sapphire/framework';
|
||||||
import type { User, Message, Snowflake, Guild, TextChannel } from 'discord.js';
|
import type { User, Message, Snowflake, Guild, TextChannel } from 'discord.js';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { addWarn } from '#utils/database/warnings';
|
import { addWarn } from '#utils/database/moderation/warnings';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import { ChannelType, EmbedBuilder } from 'discord.js';
|
import { ChannelType, EmbedBuilder } from 'discord.js';
|
||||||
import type { Message, Guild, User } from 'discord.js';
|
import type { Message, Guild, User } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { fetchWarnings } from '#utils/database/warnings';
|
import { fetchWarnings } from '#utils/database/moderation/warnings';
|
||||||
import { checkStaff } from '#utils/checker';
|
import { checkStaff } from '#utils/checker';
|
||||||
import { createWarningsEmbed } from '#utils/embeds';
|
import { createWarningsEmbed } from '#utils/embeds';
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import { Subcommand } from '@sapphire/plugin-subcommands';
|
import { Subcommand } from '@sapphire/plugin-subcommands';
|
||||||
import { RegisterBehavior } from '@sapphire/framework';
|
import { RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { Snowflake } from 'discord.js';
|
import { ChannelType, PermissionsBitField, Snowflake } from 'discord.js';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import {
|
import {
|
||||||
addStatUser,
|
addStatUser,
|
||||||
@@ -66,7 +66,6 @@ export class OutreachCommand extends Subcommand {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
preconditions: ['ModOnly'],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,15 +199,15 @@ export class OutreachCommand extends Subcommand {
|
|||||||
|
|
||||||
if (mod === undefined) {
|
if (mod === undefined) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Mod was not found!',
|
content: 'Outreach Leader was not found!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mod.roles.cache.has(IDs.roles.staff.outreachCoordinator)) {
|
if (!mod.roles.cache.has(IDs.roles.staff.outreachLeader)) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'You need to be an Outreach Coordinator to run this command!',
|
content: 'You need to be an Outreach Leader to run this command!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -254,9 +253,9 @@ export class OutreachCommand extends Subcommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mod.roles.cache.has(IDs.roles.staff.outreachCoordinator)) {
|
if (!mod.roles.cache.has(IDs.roles.staff.outreachLeader)) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'You need to be an Outreach Coordinator to run this command!',
|
content: 'You need to be an Outreach Leader to run this command!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -275,7 +274,8 @@ export class OutreachCommand extends Subcommand {
|
|||||||
|
|
||||||
stat.forEach(({ role }) => {
|
stat.forEach(({ role }) => {
|
||||||
if (role !== null) {
|
if (role !== null) {
|
||||||
guild.roles.delete(role.roleId);
|
guild.roles.delete(role.roleId); // Delete role
|
||||||
|
guild.channels.delete(role.channelId); // Delete VC
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -388,14 +388,66 @@ export class OutreachCommand extends Subcommand {
|
|||||||
|
|
||||||
await updateUser(leaderMember);
|
await updateUser(leaderMember);
|
||||||
|
|
||||||
|
// Create role for group
|
||||||
const role = await guild.roles.create({
|
const role = await guild.roles.create({
|
||||||
name: `Outreach Group ${groupNo}`,
|
name: `Outreach Group ${groupNo}`,
|
||||||
|
mentionable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await createStat(event.id, leader.id, role.id);
|
// Create a voice channel for group
|
||||||
|
const channel = await guild.channels.create({
|
||||||
|
name: `Outreach Group ${groupNo}`,
|
||||||
|
type: ChannelType.GuildVoice,
|
||||||
|
parent: IDs.categories.activism,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: [
|
||||||
|
PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.Connect,
|
||||||
|
PermissionsBitField.Flags.ViewChannel,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.activist,
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: role.id, // Permissions for the specific group
|
||||||
|
allow: [
|
||||||
|
PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.Connect,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.outreachLeader,
|
||||||
|
allow: [
|
||||||
|
PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.Connect,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create stats in database
|
||||||
|
await createStat(event.id, leader.id, role.id, channel.id);
|
||||||
|
|
||||||
|
// Give group leader role
|
||||||
await leaderMember.roles.add(role);
|
await leaderMember.roles.add(role);
|
||||||
|
|
||||||
|
// Send message in VC with a welcome and reminder
|
||||||
|
await channel.send(
|
||||||
|
`Welcome ${role}, ${leaderMember} is going to be the leader of your group!\n\n` +
|
||||||
|
'Remember to keep track of stats during activism with `/outreach group update` and' +
|
||||||
|
'to have these questions in mind whilst doing activism:\n' +
|
||||||
|
'- How many said would go vegan?\n' +
|
||||||
|
'- How many seriously considered being vegan?\n' +
|
||||||
|
'- How many people had anti-vegan viewpoints?\n' +
|
||||||
|
'- How many thanked you for the conversation?\n' +
|
||||||
|
'- How many said they would watch a vegan documentary?\n' +
|
||||||
|
'- How many got educated on veganism or the animal industry?',
|
||||||
|
);
|
||||||
|
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: `Created a group with the leader being ${leader}`,
|
content: `Created a group with the leader being ${leader}`,
|
||||||
});
|
});
|
||||||
@@ -442,7 +494,7 @@ export class OutreachCommand extends Subcommand {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
leader.id !== stat.stat.leaderId &&
|
leader.id !== stat.stat.leaderId &&
|
||||||
!leaderMember.roles.cache.has(IDs.roles.staff.outreachCoordinator)
|
!leaderMember.roles.cache.has(IDs.roles.staff.outreachLeader)
|
||||||
) {
|
) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: `You are not the leader for ${group}`,
|
content: `You are not the leader for ${group}`,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class PlusCommand extends Command {
|
|||||||
name: 'plus',
|
name: 'plus',
|
||||||
aliases: ['+'],
|
aliases: ['+'],
|
||||||
description: 'Give/remove the plus role',
|
description: 'Give/remove the plus role',
|
||||||
preconditions: [['CoordinatorOnly', 'ModOnly']],
|
preconditions: [['CoordinatorOnly', 'VerifierOnly', 'ModOnly']],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +138,14 @@ export class PlusCommand extends Command {
|
|||||||
info.success = true;
|
info.success = true;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the user is vegan before giving the plus role
|
||||||
|
// If not, stop from giving the plus role
|
||||||
|
if (!member.roles.cache.has(IDs.roles.vegan.vegan)) {
|
||||||
|
info.message = `Can't give ${user} the vegan role as they are not vegan!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
// Add Plus role to the user
|
// Add Plus role to the user
|
||||||
await member.roles.add(plus);
|
await member.roles.add(plus);
|
||||||
await roleAddLog(user.id, mod.id, plus);
|
await roleAddLog(user.id, mod.id, plus);
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ export class TrustedCommand extends Command {
|
|||||||
.send(
|
.send(
|
||||||
`You have been given the ${trusted.name} role by ${mod}!` +
|
`You have been given the ${trusted.name} role by ${mod}!` +
|
||||||
'\n\nThis role allows you to post attachments to the server and stream in VCs.' +
|
'\n\nThis role allows you to post attachments to the server and stream in VCs.' +
|
||||||
"\nMake sure that you follow the rules, and don't post anything NSFW, anything objectifying animals and follow Discord's ToS." +
|
'\nMake sure that you follow the rules, especially by **not** posting anything **NSFW**, and **no animal products or consumption of animal products**.' +
|
||||||
`\nNot following these rules can result in the removal of the ${trusted.name} role.`,
|
`\n\nNot following these rules will result in the **immediate removal** of the ${trusted.name} role.`,
|
||||||
)
|
)
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
info.success = true;
|
info.success = true;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Command, RegisterBehavior } from '@sapphire/framework';
|
import { Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
export class InfoCommand extends Command {
|
export class InfoCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
@@ -94,7 +95,9 @@ export class InfoCommand extends Command {
|
|||||||
message =
|
message =
|
||||||
"If you want to have the vegan or activist role, you'll need to do a voice verification. " +
|
"If you want to have the vegan or activist role, you'll need to do a voice verification. " +
|
||||||
"To do this, hop into the 'Verification' voice channel." +
|
"To do this, hop into the 'Verification' voice channel." +
|
||||||
"\n\nIf there aren't any verifiers available, you'll be disconnected, and you can rejoin later.";
|
"\n\nIf there aren't any verifiers available, you'll be disconnected, and you can rejoin later." +
|
||||||
|
`\n\nAlternatively if you would like text verification, you can use \`/apply\` in <#${IDs.channels.nonVegan.vcText}> ` +
|
||||||
|
'to be able fill out a Vegan Verification form through the Appy Bot.';
|
||||||
break;
|
break;
|
||||||
case 'modMail':
|
case 'modMail':
|
||||||
message =
|
message =
|
||||||
|
|||||||
@@ -17,9 +17,8 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
|
||||||
import { Command } from '@sapphire/framework';
|
import { Command } from '@sapphire/framework';
|
||||||
import type { Message } from 'discord.js';
|
import { Message, MessageFlagsBitField } from 'discord.js';
|
||||||
|
|
||||||
export class PingCommand extends Command {
|
export class PingCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
@@ -41,12 +40,13 @@ export class PingCommand extends Command {
|
|||||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
const msg = await interaction.reply({
|
const msg = await interaction.reply({
|
||||||
content: 'Ping?',
|
content: 'Ping?',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
fetchReply: true,
|
withResponse: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isMessageInstance(msg)) {
|
if (msg.resource !== null && msg.resource.message !== null) {
|
||||||
const diff = msg.createdTimestamp - interaction.createdTimestamp;
|
const diff =
|
||||||
|
msg.resource.message.createdTimestamp - interaction.createdTimestamp;
|
||||||
const ping = Math.round(this.container.client.ws.ping);
|
const ping = Math.round(this.container.client.ws.ping);
|
||||||
return interaction.editReply(
|
return interaction.editReply(
|
||||||
`Pong 🏓! (Round trip took: ${diff}ms. Heartbeat: ${ping}ms.)`,
|
`Pong 🏓! (Round trip took: ${diff}ms. Heartbeat: ${ping}ms.)`,
|
||||||
@@ -57,6 +57,11 @@ export class PingCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async messageRun(message: Message) {
|
public async messageRun(message: Message) {
|
||||||
|
if (!message.channel.isSendable()) {
|
||||||
|
// TODO manage logging/errors properly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const msg = await message.channel.send('Ping?');
|
const msg = await message.channel.send('Ping?');
|
||||||
|
|
||||||
const diff = msg.createdTimestamp - message.createdTimestamp;
|
const diff = msg.createdTimestamp - message.createdTimestamp;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { User, Guild, Message } from 'discord.js';
|
import type { User, Guild, Message } from 'discord.js';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { getRank, xpToNextLevel } from '#utils/database/xp';
|
import { getRank, xpToNextLevel } from '#utils/database/fun/xp';
|
||||||
|
|
||||||
export class RankCommand extends Command {
|
export class RankCommand extends Command {
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
|||||||
25
src/index.ts
25
src/index.ts
@@ -25,8 +25,11 @@ import { LogLevel, SapphireClient, container } from '@sapphire/framework';
|
|||||||
import '@sapphire/plugin-scheduled-tasks/register';
|
import '@sapphire/plugin-scheduled-tasks/register';
|
||||||
import '@sapphire/plugin-logger/register';
|
import '@sapphire/plugin-logger/register';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import { createClient } from 'redis';
|
import { Redis } from 'ioredis';
|
||||||
import type { RedisClientType } from 'redis';
|
|
||||||
|
const REDIS_PORT = process.env.REDIS_PORT
|
||||||
|
? parseInt(process.env.REDIS_PORT)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
// Setting up the Sapphire client
|
// Setting up the Sapphire client
|
||||||
const client = new SapphireClient({
|
const client = new SapphireClient({
|
||||||
@@ -50,7 +53,10 @@ const client = new SapphireClient({
|
|||||||
tasks: {
|
tasks: {
|
||||||
bull: {
|
bull: {
|
||||||
connection: {
|
connection: {
|
||||||
host: process.env.BULLMQ_URL,
|
host: process.env.REDIS_HOST,
|
||||||
|
username: process.env.REDIS_USER,
|
||||||
|
password: process.env.REDIS_PASSWORD,
|
||||||
|
port: REDIS_PORT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -63,11 +69,14 @@ const main = async () => {
|
|||||||
client.logger.info('Logging in');
|
client.logger.info('Logging in');
|
||||||
|
|
||||||
// Create databases
|
// Create databases
|
||||||
container.database = await new PrismaClient();
|
container.database = new PrismaClient();
|
||||||
container.redis = createClient({
|
container.redis = new Redis({
|
||||||
url: process.env.REDIS_URL,
|
host: process.env.REDIS_HOST,
|
||||||
|
username: process.env.REDIS_USER,
|
||||||
|
password: process.env.REDIS_PASSWORD,
|
||||||
|
port: REDIS_PORT,
|
||||||
|
db: 1,
|
||||||
});
|
});
|
||||||
await container.redis.connect();
|
|
||||||
|
|
||||||
// Log the bot in to Discord
|
// Log the bot in to Discord
|
||||||
await client.login(token);
|
await client.login(token);
|
||||||
@@ -83,7 +92,7 @@ const main = async () => {
|
|||||||
declare module '@sapphire/pieces' {
|
declare module '@sapphire/pieces' {
|
||||||
interface Container {
|
interface Container {
|
||||||
database: PrismaClient;
|
database: PrismaClient;
|
||||||
redis: RedisClientType;
|
redis: Redis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,14 @@ import {
|
|||||||
InteractionHandler,
|
InteractionHandler,
|
||||||
InteractionHandlerTypes,
|
InteractionHandlerTypes,
|
||||||
} from '@sapphire/framework';
|
} from '@sapphire/framework';
|
||||||
import type { ButtonInteraction, GuildMember, TextChannel } from 'discord.js';
|
import {
|
||||||
|
ButtonInteraction,
|
||||||
|
GuildMember,
|
||||||
|
MessageFlagsBitField,
|
||||||
|
TextChannel,
|
||||||
|
} from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
import { checkActive } from '#utils/database/moderation/restriction';
|
||||||
|
|
||||||
export class WelcomeButtonHandler extends InteractionHandler {
|
export class WelcomeButtonHandler extends InteractionHandler {
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -54,7 +60,7 @@ export class WelcomeButtonHandler extends InteractionHandler {
|
|||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content:
|
content:
|
||||||
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
|
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -62,14 +68,25 @@ export class WelcomeButtonHandler extends InteractionHandler {
|
|||||||
try {
|
try {
|
||||||
member = member as GuildMember;
|
member = member as GuildMember;
|
||||||
|
|
||||||
|
// Checks if the user is currently restricted
|
||||||
|
if (await checkActive(member.id)) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: `You are currently restricted from this server! Contact the moderators by sending a DM to <@${IDs.modMail}>.`,
|
||||||
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Give non-vegan role
|
// Give non-vegan role
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await member.roles.add(IDs.roles.nonvegan.nonvegan);
|
await member.roles.add(IDs.roles.nonvegan.nonvegan);
|
||||||
|
|
||||||
await general.send(
|
await general.send(
|
||||||
`${member} Welcome to ARA! :D Please check <#${IDs.channels.information.roles}> ` +
|
`${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.",
|
`\n\nIf you are vegan, you can join the 'Verification' voice channel, or use \`/apply\` with the Appy bot in <#${IDs.channels.nonVegan.vcText}>, ` +
|
||||||
|
'to be verified and gain access to more channels.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,13 +94,13 @@ export class WelcomeButtonHandler extends InteractionHandler {
|
|||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content:
|
content:
|
||||||
"You're currently in a verification, you'll have to leave the verification or get verified before being able to access the server again.",
|
"You're currently in a verification, you'll have to leave the verification or get verified before being able to access the server again.",
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content:
|
content:
|
||||||
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
|
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
|
||||||
ephemeral: true,
|
flags: MessageFlagsBitField.Flags.Ephemeral,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { GuildBan } from 'discord.js';
|
import type { GuildBan } from 'discord.js';
|
||||||
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
|
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
import { addBan, checkBan } from '#utils/database/ban';
|
import { addBan, checkBan } from '#utils/database/moderation/ban';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { GuildMember } from 'discord.js';
|
import type { GuildMember } from 'discord.js';
|
||||||
import { checkBan, getBanReason } from '#utils/database/ban';
|
import { checkBan, getBanReason } from '#utils/database/moderation/ban';
|
||||||
import { checkTempBan } from '#utils/database/tempBan';
|
import { checkTempBan } from '#utils/database/moderation/tempBan';
|
||||||
|
|
||||||
export class BanJoinListener extends Listener {
|
export class BanJoinListener extends Listener {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { GuildBan } from 'discord.js';
|
import type { GuildBan } from 'discord.js';
|
||||||
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
|
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
import { addBan, checkBan, removeBan } from '#utils/database/ban';
|
import { addBan, checkBan, removeBan } from '#utils/database/moderation/ban';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { Message } from 'discord.js';
|
import type { Message } from 'discord.js';
|
||||||
import { getLastCount, addCount } from '#utils/database/counting';
|
import { getLastCount, addCount } from '#utils/database/fun/counting';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
export class XpListener extends Listener {
|
export class XpListener extends Listener {
|
||||||
@@ -49,6 +49,11 @@ export class XpListener extends Listener {
|
|||||||
// If no counts exist on the database, then create the first count from the bot
|
// If no counts exist on the database, then create the first count from the bot
|
||||||
if (lastCount === null) {
|
if (lastCount === null) {
|
||||||
if (this.container.client.id === null) {
|
if (this.container.client.id === null) {
|
||||||
|
if (!message.channel.isSendable()) {
|
||||||
|
// TODO manage logging/errors properly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
message.channel.send(
|
message.channel.send(
|
||||||
'An unexpected error occurred trying to set up the counting channel, please contact a developer!',
|
'An unexpected error occurred trying to set up the counting channel, please contact a developer!',
|
||||||
);
|
);
|
||||||
@@ -63,6 +68,11 @@ export class XpListener extends Listener {
|
|||||||
|
|
||||||
lastCount = await getLastCount();
|
lastCount = await getLastCount();
|
||||||
if (lastCount === null) {
|
if (lastCount === null) {
|
||||||
|
if (!message.channel.isSendable()) {
|
||||||
|
// TODO manage logging/errors properly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
message.channel.send(
|
message.channel.send(
|
||||||
'An unexpected error occurred, please contact a developer!',
|
'An unexpected error occurred, please contact a developer!',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
/*
|
|
||||||
Animal Rights Advocates Discord Bot
|
|
||||||
Copyright (C) 2024 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 { Listener, ListenerErrorPayload } from '@sapphire/framework';
|
|
||||||
|
|
||||||
export class ErrorListener extends Listener {
|
|
||||||
public constructor(
|
|
||||||
context: Listener.LoaderContext,
|
|
||||||
options: Listener.Options,
|
|
||||||
) {
|
|
||||||
super(context, {
|
|
||||||
...options,
|
|
||||||
event: 'listenerError',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(error: Error, payload: ListenerErrorPayload) {
|
|
||||||
this.container.logger.debug(
|
|
||||||
`TEST ERROR: ${error.stack}\n\nPAYLOAD: ${payload.piece.name}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
162
src/listeners/fixRoles.ts
Normal file
162
src/listeners/fixRoles.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2025 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/>.
|
||||||
|
|
||||||
|
I used the Sapphire documentation and parts of the code from the Sapphire CLI to
|
||||||
|
create this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Listener } from '@sapphire/framework';
|
||||||
|
import { DurationFormatter } from '@sapphire/time-utilities';
|
||||||
|
import type { Client } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { fetchRoles } from '#utils/database/dbExistingUser';
|
||||||
|
import { checkActive } from '#utils/database/moderation/restriction';
|
||||||
|
|
||||||
|
export class FixRolesOnReady extends Listener {
|
||||||
|
public constructor(
|
||||||
|
context: Listener.LoaderContext,
|
||||||
|
options: Listener.Options,
|
||||||
|
) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
once: true,
|
||||||
|
event: 'ready',
|
||||||
|
// !!!!!!!!!!!! WARNING !!!!!!!!!!!!
|
||||||
|
// THIS SHOULD BE DISABLED BY DEFAULT
|
||||||
|
// THIS IS ONLY USED FOR RESTORING ROLES TO THE SERVER!
|
||||||
|
// ENABLING THIS UNINTENTIONALLY WILL CAUSE SLOWDOWNS TO THE BOT DUE TO RATE LIMITING!
|
||||||
|
enabled: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(client: Client) {
|
||||||
|
this.container.logger.info(
|
||||||
|
'FixRolesOnReady: Preparation before starting to fix the roles for each user...',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fetching the Guild
|
||||||
|
const guild = await client.guilds.fetch(IDs.guild).catch(() => undefined);
|
||||||
|
|
||||||
|
if (guild === undefined) {
|
||||||
|
this.container.logger.error('FixRolesOnReady: Could not find the server');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetching the channel for the logs
|
||||||
|
// Leave the snowflake parameter empty for no logs
|
||||||
|
const logChannel = await client.channels.fetch('');
|
||||||
|
const sendLogs = logChannel !== null;
|
||||||
|
|
||||||
|
if (!sendLogs) {
|
||||||
|
this.container.logger.error(
|
||||||
|
'FixRolesOnReady: Could not find the channel for bot logs.',
|
||||||
|
);
|
||||||
|
} else if (sendLogs && !logChannel.isSendable()) {
|
||||||
|
this.container.logger.info(
|
||||||
|
'FixRolesOnReady: No permission to send in bots logs channel.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the current users
|
||||||
|
this.container.logger.info('FixRolesOnReady: Fetching all the members...');
|
||||||
|
if (sendLogs) {
|
||||||
|
logChannel.send('Fetching all the users in ARA!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const members = await guild.members.fetch().catch(() => undefined);
|
||||||
|
|
||||||
|
if (members === undefined) {
|
||||||
|
this.container.logger.error(
|
||||||
|
'FixRolesOnReady: Could fetch all the members, this function is stopping now.',
|
||||||
|
);
|
||||||
|
if (sendLogs) {
|
||||||
|
logChannel.send("Never mind, something went wrong :'(");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalMembers = members.size;
|
||||||
|
|
||||||
|
this.container.logger.info(
|
||||||
|
`FixRolesOnReady: Done fetching ${totalMembers} members!`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Giving the roles to each user
|
||||||
|
let count = 0;
|
||||||
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
|
this.container.logger.info(
|
||||||
|
'FixRolesOnReady: Starting the process of fixing the roles for every member...',
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [userId, member] of members) {
|
||||||
|
// Send a message with an update for every 50 completions
|
||||||
|
// Checks if `channelLog` has been set to null
|
||||||
|
// The RHS of the modulo should be around 100
|
||||||
|
if (sendLogs && count % 250 === 0) {
|
||||||
|
const currentTime = new Date().getTime();
|
||||||
|
const runningTime = currentTime - startTime;
|
||||||
|
|
||||||
|
const remaining = totalMembers - count;
|
||||||
|
// Basing this on the fact that
|
||||||
|
const eta = remaining * (runningTime / count);
|
||||||
|
const estimate = new DurationFormatter().format(eta);
|
||||||
|
|
||||||
|
logChannel.send(
|
||||||
|
`Given roles to ${count} out of ${totalMembers} members. Estimated time until completion: ${estimate}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the user is restricted, and skips over them if they are
|
||||||
|
const restricted = await checkActive(userId);
|
||||||
|
|
||||||
|
if (restricted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the roles for the member in the database
|
||||||
|
const dbRoles = await fetchRoles(userId);
|
||||||
|
|
||||||
|
// Filters out the roles that the member does not have
|
||||||
|
const roles = dbRoles.filter((role) => !member.roles.cache.has(role));
|
||||||
|
|
||||||
|
// Give the roles to the member
|
||||||
|
if (roles.length > 0) {
|
||||||
|
await member.roles.add(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the completion
|
||||||
|
count += 1;
|
||||||
|
this.container.logger.info(
|
||||||
|
`FixRolesOnReady: Given roles to ${count}/${totalMembers}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the logs that the fix has finished.
|
||||||
|
const endTime = new Date().getTime();
|
||||||
|
const totalTime = endTime - startTime;
|
||||||
|
const totalTimeWritten = new DurationFormatter().format(totalTime);
|
||||||
|
const finishMessage = `Finished fixing roles for all ${totalMembers} members! It took ${totalTimeWritten} to complete.`;
|
||||||
|
|
||||||
|
this.container.logger.info(`FixRolesOnReady: ${finishMessage}`);
|
||||||
|
if (sendLogs) {
|
||||||
|
logChannel.send(finishMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,14 +22,17 @@ import { ChannelType } from 'discord.js';
|
|||||||
import type { GuildChannel, EmbedBuilder } from 'discord.js';
|
import type { GuildChannel, EmbedBuilder } from 'discord.js';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { checkActive, getRestrictions } from '#utils/database/restriction';
|
import {
|
||||||
import { findNotes } from '#utils/database/sus';
|
checkActive,
|
||||||
|
getRestrictions,
|
||||||
|
} from '#utils/database/moderation/restriction';
|
||||||
|
import { findNotes } from '#utils/database/moderation/sus';
|
||||||
import {
|
import {
|
||||||
createRestrictLogEmbed,
|
createRestrictLogEmbed,
|
||||||
createSusLogEmbed,
|
createSusLogEmbed,
|
||||||
createWarningsEmbed,
|
createWarningsEmbed,
|
||||||
} from '#utils/embeds';
|
} from '#utils/embeds';
|
||||||
import { fetchWarnings } from '#utils/database/warnings';
|
import { fetchWarnings } from '#utils/database/moderation/warnings';
|
||||||
|
|
||||||
export class ModMailCreateListener extends Listener {
|
export class ModMailCreateListener extends Listener {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { Client } from 'discord.js';
|
import type { Client } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
export class ReadyListener extends Listener {
|
export class ReadyListener extends Listener {
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -35,8 +36,24 @@ export class ReadyListener extends Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public run(client: Client) {
|
public async run(client: Client) {
|
||||||
const { username, id } = client.user!;
|
const { username, id } = client.user!;
|
||||||
this.container.logger.info(`Successfully logged in as ${username} (${id})`);
|
this.container.logger.info(`Successfully logged in as ${username} (${id})`);
|
||||||
|
|
||||||
|
const botLogChannel = await client.channels.fetch(IDs.channels.logs.bot);
|
||||||
|
|
||||||
|
if (botLogChannel === null) {
|
||||||
|
this.container.logger.error(
|
||||||
|
'ReadyListener: Could not find the channel for bot logs.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else if (!botLogChannel.isSendable()) {
|
||||||
|
this.container.logger.info(
|
||||||
|
'ReadyListener: No permission to send in bots logs channel.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botLogChannel.send('The bot has started up!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ import type {
|
|||||||
import { ChannelType } from 'discord.js';
|
import { ChannelType } from 'discord.js';
|
||||||
import { fetchRoles, getLeaveRoles } from '#utils/database/dbExistingUser';
|
import { fetchRoles, getLeaveRoles } from '#utils/database/dbExistingUser';
|
||||||
import { blockTime } from '#utils/database/verification';
|
import { blockTime } from '#utils/database/verification';
|
||||||
import { checkActive, getSection } from '#utils/database/restriction';
|
import {
|
||||||
|
checkActive,
|
||||||
|
getSection,
|
||||||
|
} from '#utils/database/moderation/restriction';
|
||||||
import { blockedRoles, blockedRolesAfterRestricted } from '#utils/blockedRoles';
|
import { blockedRoles, blockedRolesAfterRestricted } from '#utils/blockedRoles';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ export class Suggestions extends Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mailbox.isSendable()) {
|
||||||
|
// TODO manage logging/errors properly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const sent = await mailbox.send({
|
const sent = await mailbox.send({
|
||||||
embeds: [suggestion],
|
embeds: [suggestion],
|
||||||
content: message.author.toString(),
|
content: message.author.toString(),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { VoiceState } from 'discord.js';
|
import type { VoiceState } from 'discord.js';
|
||||||
import { checkActive, removeMute } from '#utils/database/vcMute';
|
import { checkActive, removeMute } from '#utils/database/moderation/vcMute';
|
||||||
|
|
||||||
export class VCMuteListener extends Listener {
|
export class VCMuteListener extends Listener {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import {
|
|||||||
startVerification,
|
startVerification,
|
||||||
finishVerification,
|
finishVerification,
|
||||||
} from '#utils/database/verification';
|
} from '#utils/database/verification';
|
||||||
import { findNotes } from '#utils/database/sus';
|
import { findNotes } from '#utils/database/moderation/sus';
|
||||||
import { addExistingUser } from '#utils/database/dbExistingUser';
|
import { addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
import { rolesToString } from '#utils/formatter';
|
import { rolesToString } from '#utils/formatter';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|||||||
@@ -148,7 +148,10 @@ export class VerificationLeaveVCListener extends Listener {
|
|||||||
listTextChannels.forEach((c) => {
|
listTextChannels.forEach((c) => {
|
||||||
const textChannel = c as TextChannel;
|
const textChannel = c as TextChannel;
|
||||||
// Checks if the channel topic has the user's snowflake
|
// Checks if the channel topic has the user's snowflake
|
||||||
if (textChannel.topic!.includes(userSnowflake!)) {
|
if (
|
||||||
|
textChannel.topic !== null &&
|
||||||
|
textChannel.topic.includes(userSnowflake!)
|
||||||
|
) {
|
||||||
textChannel.delete();
|
textChannel.delete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ export class VerificationReady extends Listener {
|
|||||||
const textChannel = c as TextChannel;
|
const textChannel = c as TextChannel;
|
||||||
// Checks if the channel topic has the user's snowflake
|
// Checks if the channel topic has the user's snowflake
|
||||||
emptyVC.forEach((snowflake) => {
|
emptyVC.forEach((snowflake) => {
|
||||||
if (textChannel.topic!.includes(snowflake)) {
|
if (
|
||||||
|
textChannel.topic !== null &&
|
||||||
|
textChannel.topic.includes(snowflake)
|
||||||
|
) {
|
||||||
textChannel.delete();
|
textChannel.delete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
88
src/listeners/verification/trusted.ts
Normal file
88
src/listeners/verification/trusted.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2024 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 { Listener } from '@sapphire/framework';
|
||||||
|
import { GuildMember } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { noModHistory, userPreviouslyHadRole } from '#utils/database/memberMod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the trusted role to users who have levelled up to level 5
|
||||||
|
* and has not gotten any other warnings/restrictions prior.
|
||||||
|
*/
|
||||||
|
export class TrustedListener extends Listener {
|
||||||
|
public constructor(
|
||||||
|
context: Listener.LoaderContext,
|
||||||
|
options: Listener.Options,
|
||||||
|
) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'xpLevelUp',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(member: GuildMember, level: number) {
|
||||||
|
// Checks if the member has gotten level 7
|
||||||
|
// Has been nefred. Should take around 1.5 hours to get the trusted role now
|
||||||
|
if (level !== 7) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the user has been previously moderated
|
||||||
|
const noModerationHistory = await noModHistory(member.id);
|
||||||
|
if (!noModerationHistory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = member;
|
||||||
|
const trusted = guild.roles.cache.get(IDs.roles.trusted);
|
||||||
|
|
||||||
|
if (trusted === undefined) {
|
||||||
|
this.container.logger.error(
|
||||||
|
'TrustedXP Listener: the Trusted role could not be found in the guild.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the member has previously had the trusted role given/removed
|
||||||
|
const previouslyHadRole = await userPreviouslyHadRole(
|
||||||
|
member.id,
|
||||||
|
trusted.id,
|
||||||
|
);
|
||||||
|
if (previouslyHadRole) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the user already has the trusted role
|
||||||
|
if (member.roles.cache.has(trusted.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gives the trusted role to the member
|
||||||
|
await member.roles.add(trusted);
|
||||||
|
|
||||||
|
// Send a DM to inform the member that they have been given the trusted role
|
||||||
|
await member.user.send(
|
||||||
|
`Hi, you have been given the ${trusted.name} as you have been interacting in ARA for a long enough time!` +
|
||||||
|
'\n\nThis role allows you to post attachments to the server and stream in VCs.' +
|
||||||
|
'\nMake sure that you follow the rules, especially by **not** posting anything **NSFW**, and **no animal products or consumption of animal products**.' +
|
||||||
|
`\n\nNot following these rules will result in the **immediate removal** of the ${trusted.name} role.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { Message } from 'discord.js';
|
import type { Message } from 'discord.js';
|
||||||
import { addXp, checkCanAddXp } from '#utils/database/xp';
|
import { addXp, checkCanAddXp } from '#utils/database/fun/xp';
|
||||||
import { randint } from '#utils/maths';
|
import { randint } from '#utils/maths';
|
||||||
|
|
||||||
export class XpListener extends Listener {
|
export class XpListener extends Listener {
|
||||||
@@ -46,6 +46,12 @@ export class XpListener extends Listener {
|
|||||||
|
|
||||||
const xp = randint(15, 25);
|
const xp = randint(15, 25);
|
||||||
|
|
||||||
await addXp(user.id, xp);
|
const level = await addXp(user.id, xp);
|
||||||
|
|
||||||
|
// Emits that a user has leveled up
|
||||||
|
if (level !== null) {
|
||||||
|
this.container.logger.info('User is levelling up!');
|
||||||
|
this.container.client.emit('xpLevelUp', message.member, level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,34 +26,33 @@ import type {
|
|||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
export class PatreonOnlyPrecondition extends AllFlowsPrecondition {
|
export class OutreachLeaderOnlyPrecondition extends AllFlowsPrecondition {
|
||||||
public override async messageRun(message: Message) {
|
public override async messageRun(message: Message) {
|
||||||
// for message command
|
// for message command
|
||||||
return this.checkPatreon(message.member!);
|
return this.checkCoordinator(message.member!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async chatInputRun(interaction: CommandInteraction) {
|
public override async chatInputRun(interaction: CommandInteraction) {
|
||||||
// for slash command
|
// for slash command
|
||||||
return this.checkPatreon(interaction.member! as GuildMember);
|
return this.checkCoordinator(interaction.member! as GuildMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async contextMenuRun(
|
public override async contextMenuRun(
|
||||||
interaction: ContextMenuCommandInteraction,
|
interaction: ContextMenuCommandInteraction,
|
||||||
) {
|
) {
|
||||||
// for context menu command
|
// for context menu command
|
||||||
return this.checkPatreon(interaction.member! as GuildMember);
|
return this.checkCoordinator(interaction.member! as GuildMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkPatreon(user: GuildMember) {
|
private async checkCoordinator(user: GuildMember) {
|
||||||
return user.roles.cache.has(IDs.roles.patron) ||
|
return user.roles.cache.has(IDs.roles.staff.outreachLeader)
|
||||||
user.roles.cache.has(IDs.roles.patreon)
|
|
||||||
? this.ok()
|
? this.ok()
|
||||||
: this.error({ message: 'Only Patreon members can run this command!' });
|
: this.error({ message: 'Only outreach leaders can run this command!' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@sapphire/framework' {
|
declare module '@sapphire/framework' {
|
||||||
interface Preconditions {
|
interface Preconditions {
|
||||||
PatreonOnly: never;
|
OutreachLeaderOnly: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,9 @@ export class VerifyReminder extends ScheduledTask {
|
|||||||
await channel.send(
|
await channel.send(
|
||||||
"If you want to have the vegan or activist role, you'll need to do a voice verification. " +
|
"If you want to have the vegan or activist role, you'll need to do a voice verification. " +
|
||||||
"To do this, hop into the 'Verification' voice channel." +
|
"To do this, hop into the 'Verification' voice channel." +
|
||||||
"\n\nIf there aren't any verifiers available, you'll be disconnected, and you can rejoin later.",
|
"\n\nIf there aren't any verifiers available, you'll be disconnected, and you can rejoin later." +
|
||||||
|
`\nAlternatively if you would like text verification, you can use \`/apply\` in <#${IDs.channels.nonVegan.vcText}> ` +
|
||||||
|
'to be able fill out a Vegan Verification form through the Appy Bot.',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset the total message counter to 0
|
// Reset the total message counter to 0
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
|
|
||||||
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
|
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { TextChannel, EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { checkBan } from '#utils/database/ban';
|
import { checkBan } from '#utils/database/moderation/ban';
|
||||||
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
|
import {
|
||||||
|
checkTempBan,
|
||||||
|
removeTempBan,
|
||||||
|
} from '#utils/database/moderation/tempBan';
|
||||||
|
|
||||||
export class TempBan extends ScheduledTask {
|
export class TempBan extends ScheduledTask {
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -36,7 +39,9 @@ export class TempBan extends ScheduledTask {
|
|||||||
// Get the guild where the user is in
|
// Get the guild where the user is in
|
||||||
let guild = this.container.client.guilds.cache.get(payload.guildId);
|
let guild = this.container.client.guilds.cache.get(payload.guildId);
|
||||||
if (guild === undefined) {
|
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) {
|
if (guild === undefined) {
|
||||||
this.container.logger.error('Temp Unban Task: Guild not found!');
|
this.container.logger.error('Temp Unban Task: Guild not found!');
|
||||||
return;
|
return;
|
||||||
@@ -48,7 +53,7 @@ export class TempBan extends ScheduledTask {
|
|||||||
let user = guild.client.users.cache.get(userId);
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
if (user === undefined) {
|
if (user === undefined) {
|
||||||
user = await guild.client.users.fetch(userId);
|
user = await guild.client.users.fetch(userId).catch(() => undefined);
|
||||||
if (user === undefined) {
|
if (user === undefined) {
|
||||||
this.container.logger.error(
|
this.container.logger.error(
|
||||||
'Temp Unban Task: Could not fetch banned user!',
|
'Temp Unban Task: Could not fetch banned user!',
|
||||||
@@ -70,20 +75,27 @@ export class TempBan extends ScheduledTask {
|
|||||||
await removeTempBan(userId);
|
await removeTempBan(userId);
|
||||||
|
|
||||||
// Log unban
|
// Log unban
|
||||||
let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted) as
|
let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted);
|
||||||
| TextChannel
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (logChannel === undefined) {
|
if (logChannel === undefined) {
|
||||||
logChannel = (await guild.channels.fetch(
|
const logChannelFetch = await guild.channels
|
||||||
IDs.channels.logs.restricted,
|
.fetch(IDs.channels.logs.restricted)
|
||||||
)) as TextChannel | undefined;
|
.catch(() => null);
|
||||||
if (logChannel === undefined) {
|
if (logChannelFetch === null) {
|
||||||
this.container.logger.error(
|
this.container.logger.error(
|
||||||
`Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`,
|
`Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`,
|
||||||
);
|
);
|
||||||
return;
|
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()
|
const log = new EmbedBuilder()
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
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 { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
|
||||||
|
import { ChannelType } from 'discord.js';
|
||||||
|
|
||||||
export class VerifyTimeout extends ScheduledTask {
|
export class VerifyTimeout extends ScheduledTask {
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -30,17 +30,24 @@ export class VerifyTimeout extends ScheduledTask {
|
|||||||
|
|
||||||
public async run(payload: { channelId: string; userId: string }) {
|
public async run(payload: { channelId: string; userId: string }) {
|
||||||
// Get the guild where the user is in
|
// Get the guild where the user is in
|
||||||
let channel = this.container.client.channels.cache.get(
|
let channel = this.container.client.channels.cache.get(payload.channelId);
|
||||||
payload.channelId,
|
|
||||||
) as VoiceChannel | undefined;
|
|
||||||
if (channel === undefined) {
|
|
||||||
channel = (await this.container.client.channels.fetch(
|
|
||||||
payload.channelId,
|
|
||||||
)) as VoiceChannel | undefined;
|
|
||||||
if (channel === 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!');
|
this.container.logger.error('verifyTimeout: Channel not found!');
|
||||||
return;
|
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)) {
|
if (channel.members.size < 2 && channel.members.has(payload.userId)) {
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ export class VerifyUnblock extends ScheduledTask {
|
|||||||
// Get the guild where the user is in
|
// Get the guild where the user is in
|
||||||
let guild = this.container.client.guilds.cache.get(payload.guildId);
|
let guild = this.container.client.guilds.cache.get(payload.guildId);
|
||||||
if (guild === undefined) {
|
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) {
|
if (guild === undefined) {
|
||||||
this.container.logger.error('verifyUnblock: Guild not found!');
|
this.container.logger.error('verifyUnblock: Guild not found!');
|
||||||
return;
|
return;
|
||||||
@@ -42,7 +44,7 @@ export class VerifyUnblock extends ScheduledTask {
|
|||||||
// Find GuildMember for the user
|
// Find GuildMember for the user
|
||||||
let user = guild.members.cache.get(payload.userId);
|
let user = guild.members.cache.get(payload.userId);
|
||||||
if (user === undefined) {
|
if (user === undefined) {
|
||||||
user = await guild.members.fetch(payload.userId).catch(undefined);
|
user = await guild.members.fetch(payload.userId).catch(() => undefined);
|
||||||
if (user === undefined) {
|
if (user === undefined) {
|
||||||
this.container.logger.error('verifyUnblock: GuildMember not found!');
|
this.container.logger.error('verifyUnblock: GuildMember not found!');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const blockedRoles = [
|
|||||||
IDs.roles.staff.trialVerifier,
|
IDs.roles.staff.trialVerifier,
|
||||||
IDs.roles.staff.mentor,
|
IDs.roles.staff.mentor,
|
||||||
IDs.roles.stageHost,
|
IDs.roles.stageHost,
|
||||||
|
IDs.roles.booster,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const blockedRolesAfterRestricted = [
|
export const blockedRolesAfterRestricted = [
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export async function addXp(userId: Snowflake, xp: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await container.database.xp.upsert({
|
const info = await container.database.xp.upsert({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
@@ -63,6 +63,12 @@ export async function addXp(userId: Snowflake, xp: number) {
|
|||||||
xpForNextLevel: xp,
|
xpForNextLevel: xp,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (level === 1) {
|
||||||
|
return info.level;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkCanAddXp(userId: Snowflake) {
|
export async function checkCanAddXp(userId: Snowflake) {
|
||||||
55
src/utils/database/memberMod.ts
Normal file
55
src/utils/database/memberMod.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2024 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 { Snowflake } from 'discord.js';
|
||||||
|
import { countWarnings } from '#utils/database/moderation/warnings';
|
||||||
|
import { countRestrictions } from '#utils/database/moderation/restriction';
|
||||||
|
import { container } from '@sapphire/framework';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user has
|
||||||
|
* @param userId Discord Snowflake of the user to check
|
||||||
|
* @return Boolean true if no prior moderation action
|
||||||
|
*/
|
||||||
|
export async function noModHistory(userId: Snowflake) {
|
||||||
|
const warnCount = await countWarnings(userId);
|
||||||
|
const restrictCount = await countRestrictions(userId);
|
||||||
|
|
||||||
|
return warnCount === 0 && restrictCount === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user has previously had a role given or taken away by a moderator.
|
||||||
|
* @param userId Discord Snowflake of the user to check
|
||||||
|
* @param roleId Snowflake of the role being checked for the user
|
||||||
|
* @return Boolean true if the user has had a moderator give/remove the specified role
|
||||||
|
*/
|
||||||
|
export async function userPreviouslyHadRole(
|
||||||
|
userId: Snowflake,
|
||||||
|
roleId: Snowflake,
|
||||||
|
) {
|
||||||
|
const count = await container.database.roleLog.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
roleId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return count !== 0;
|
||||||
|
}
|
||||||
@@ -114,6 +114,19 @@ export async function getSection(userId: Snowflake) {
|
|||||||
return restriction.section;
|
return restriction.section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of restrictions a user has.
|
||||||
|
* @param userId Discord Snowflake of the user to check
|
||||||
|
* @return number The amount of restrictions the user has
|
||||||
|
*/
|
||||||
|
export async function countRestrictions(userId: Snowflake) {
|
||||||
|
return container.database.restrict.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// This is only for restrictions created with the old bot
|
// This is only for restrictions created with the old bot
|
||||||
export async function unRestrictLegacy(
|
export async function unRestrictLegacy(
|
||||||
userId: Snowflake,
|
userId: Snowflake,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
export async function addToDatabase(
|
export async function addSusNoteDB(
|
||||||
userId: string,
|
userId: string,
|
||||||
modId: string,
|
modId: string,
|
||||||
message: string,
|
message: string,
|
||||||
@@ -10,14 +10,24 @@ export async function addToDatabase(
|
|||||||
await container.database.sus.create({
|
await container.database.sus.create({
|
||||||
data: {
|
data: {
|
||||||
user: {
|
user: {
|
||||||
connect: {
|
connectOrCreate: {
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
id: userId,
|
id: userId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
mod: {
|
mod: {
|
||||||
connect: {
|
connectOrCreate: {
|
||||||
|
where: {
|
||||||
id: modId,
|
id: modId,
|
||||||
},
|
},
|
||||||
|
create: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
note: message,
|
note: message,
|
||||||
},
|
},
|
||||||
@@ -61,3 +61,16 @@ export async function deleteWarning(warningId: number) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of warnings a user has.
|
||||||
|
* @param userId Discord Snowflake of the user to check
|
||||||
|
* @return number The amount of warnings the user has
|
||||||
|
*/
|
||||||
|
export async function countWarnings(userId: Snowflake) {
|
||||||
|
return container.database.warning.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -85,6 +85,7 @@ export async function createStat(
|
|||||||
eventId: number,
|
eventId: number,
|
||||||
leaderId: Snowflake,
|
leaderId: Snowflake,
|
||||||
roleId: Snowflake,
|
roleId: Snowflake,
|
||||||
|
channelId: Snowflake,
|
||||||
) {
|
) {
|
||||||
await container.database.stat.create({
|
await container.database.stat.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -110,6 +111,7 @@ export async function createStat(
|
|||||||
role: {
|
role: {
|
||||||
create: {
|
create: {
|
||||||
roleId,
|
roleId,
|
||||||
|
channelId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,8 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const devIDs = {
|
const devIDs = {
|
||||||
|
guild: '999431674972618792',
|
||||||
roles: {
|
roles: {
|
||||||
trusted: '999431675081666599',
|
trusted: '999431675081666599',
|
||||||
|
booster: '',
|
||||||
nonvegan: {
|
nonvegan: {
|
||||||
nonvegan: '999431675081666598',
|
nonvegan: '999431675081666598',
|
||||||
vegCurious: '999431675098447932',
|
vegCurious: '999431675098447932',
|
||||||
@@ -57,6 +59,9 @@ const devIDs = {
|
|||||||
verifierCoordinator: '999431675140382810',
|
verifierCoordinator: '999431675140382810',
|
||||||
eventCoordinator: '999431675165556817',
|
eventCoordinator: '999431675165556817',
|
||||||
outreachCoordinator: '999431675140382807',
|
outreachCoordinator: '999431675140382807',
|
||||||
|
mediaCoordinator: '1204801056404676618',
|
||||||
|
hrCoordinator: '1204795893480431657',
|
||||||
|
outreachLeader: '999431675123597409',
|
||||||
restricted: '999431675123597407',
|
restricted: '999431675123597407',
|
||||||
moderator: '999431675123597408',
|
moderator: '999431675123597408',
|
||||||
trialModerator: '999431675123597404',
|
trialModerator: '999431675123597404',
|
||||||
@@ -96,6 +101,7 @@ const devIDs = {
|
|||||||
},
|
},
|
||||||
nonVegan: {
|
nonVegan: {
|
||||||
general: '999431677325615189',
|
general: '999431677325615189',
|
||||||
|
vcText: '999431677535338567',
|
||||||
},
|
},
|
||||||
vegan: {
|
vegan: {
|
||||||
general: '999431677535338575',
|
general: '999431677535338575',
|
||||||
@@ -121,6 +127,7 @@ const devIDs = {
|
|||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
restricted: '999431681217937513',
|
restricted: '999431681217937513',
|
||||||
|
bot: '999431681217937516',
|
||||||
economy: '999431681599623198',
|
economy: '999431681599623198',
|
||||||
sus: '999431681599623199',
|
sus: '999431681599623199',
|
||||||
},
|
},
|
||||||
@@ -129,10 +136,12 @@ const devIDs = {
|
|||||||
staff: '999431676058927253',
|
staff: '999431676058927253',
|
||||||
modMail: '1095453371411996762',
|
modMail: '1095453371411996762',
|
||||||
verification: '999431677006860409',
|
verification: '999431677006860409',
|
||||||
|
activism: '999431677795389549',
|
||||||
diversity: '999431679053660185',
|
diversity: '999431679053660185',
|
||||||
private: '999431679527628818',
|
private: '999431679527628818',
|
||||||
restricted: '999431679812845654',
|
restricted: '999431679812845654',
|
||||||
},
|
},
|
||||||
|
modMail: '575252669443211264',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default devIDs;
|
export default devIDs;
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
import type { Guild, User } from 'discord.js';
|
import type { Guild, User } from 'discord.js';
|
||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import type { SusNotes } from '#utils/database/sus';
|
import type { SusNotes } from '#utils/database/moderation/sus';
|
||||||
import { RestrictionLogs } from '#utils/database/restriction';
|
import { RestrictionLogs } from '#utils/database/moderation/restriction';
|
||||||
import { Warnings } from '#utils/database/warnings';
|
import { Warnings } from '#utils/database/moderation/warnings';
|
||||||
|
|
||||||
export function createSusLogEmbed(notes: SusNotes, user: User, guild: Guild) {
|
export function createSusLogEmbed(notes: SusNotes, user: User, guild: Guild) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
/*
|
|
||||||
Animal Rights Advocates Discord Bot
|
|
||||||
Copyright (C) 2024 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/>.
|
|
||||||
*/
|
|
||||||
@@ -20,33 +20,35 @@
|
|||||||
import devIDs from '#utils/devIDs';
|
import devIDs from '#utils/devIDs';
|
||||||
|
|
||||||
let IDs = {
|
let IDs = {
|
||||||
|
guild: '730907954345279591',
|
||||||
roles: {
|
roles: {
|
||||||
trusted: '731563158011117590',
|
trusted: '1329089675977035879',
|
||||||
|
booster: '731213264540795012',
|
||||||
nonvegan: {
|
nonvegan: {
|
||||||
nonvegan: '774763753308815400',
|
nonvegan: '1329093962153332848',
|
||||||
vegCurious: '832656046572961803',
|
vegCurious: '1329107984227369020',
|
||||||
convinced: '797132019166871612',
|
convinced: '797132019166871612',
|
||||||
},
|
},
|
||||||
vegan: {
|
vegan: {
|
||||||
vegan: '788114978020392982',
|
vegan: '788114978020392982',
|
||||||
activist: '730915638746546257',
|
activist: '1329112833115295815',
|
||||||
nvAccess: '1076857105648209971',
|
nvAccess: '1076857105648209971',
|
||||||
plus: '798682625619132428',
|
plus: '798682625619132428',
|
||||||
araVegan: '995394977658044506',
|
araVegan: '995394977658044506',
|
||||||
},
|
},
|
||||||
restrictions: {
|
restrictions: {
|
||||||
sus: '859145930640457729',
|
sus: '1329125130949103626',
|
||||||
muted: '730924813681688596',
|
muted: '730924813681688596',
|
||||||
softMute: '775934741139554335',
|
softMute: '775934741139554335',
|
||||||
restricted1: '809769217477050369',
|
restricted1: '809769217477050369',
|
||||||
restricted2: '872482843304001566',
|
restricted2: '872482843304001566',
|
||||||
restricted3: '856582673258774538',
|
restricted3: '1329126085207789658',
|
||||||
restricted4: '872472182888992858',
|
restricted4: '1329126181164945499',
|
||||||
restricted: [
|
restricted: [
|
||||||
'809769217477050369', // Restricted 1
|
'809769217477050369', // Restricted 1
|
||||||
'872482843304001566', // Restricted 2
|
'872482843304001566', // Restricted 2
|
||||||
'856582673258774538', // Restricted 3
|
'1329126085207789658', // Restricted 3
|
||||||
'872472182888992858', // Restricted 4
|
'1329126181164945499', // Restricted 4
|
||||||
'1075951477379567646', // Restricted Vegan
|
'1075951477379567646', // Restricted Vegan
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -59,6 +61,9 @@ let IDs = {
|
|||||||
verifierCoordinator: '940721280376778822',
|
verifierCoordinator: '940721280376778822',
|
||||||
eventCoordinator: '944732860554817586',
|
eventCoordinator: '944732860554817586',
|
||||||
outreachCoordinator: '954804769476730890',
|
outreachCoordinator: '954804769476730890',
|
||||||
|
mediaCoordinator: '1203778509449723914',
|
||||||
|
hrCoordinator: '1203802120180989993',
|
||||||
|
outreachLeader: '730915698544607232',
|
||||||
restricted: '851624392928264222',
|
restricted: '851624392928264222',
|
||||||
moderator: '826157475815489598',
|
moderator: '826157475815489598',
|
||||||
trialModerator: '982074555596152904',
|
trialModerator: '982074555596152904',
|
||||||
@@ -71,7 +76,7 @@ let IDs = {
|
|||||||
stageHost: '854893757593419786',
|
stageHost: '854893757593419786',
|
||||||
patron: '765370219207852055',
|
patron: '765370219207852055',
|
||||||
patreon: '993848684640997406',
|
patreon: '993848684640997406',
|
||||||
verifyBlock: '1032765019269640203',
|
verifyBlock: '1329107805130461247',
|
||||||
bookClub: '955516408249352212',
|
bookClub: '955516408249352212',
|
||||||
debateHost: '935508325615931443',
|
debateHost: '935508325615931443',
|
||||||
gameNightHost: '952779915701415966',
|
gameNightHost: '952779915701415966',
|
||||||
@@ -98,6 +103,7 @@ let IDs = {
|
|||||||
},
|
},
|
||||||
nonVegan: {
|
nonVegan: {
|
||||||
general: '798967615636504657',
|
general: '798967615636504657',
|
||||||
|
vcText: '808191982169096232',
|
||||||
},
|
},
|
||||||
vegan: {
|
vegan: {
|
||||||
general: '787738272616808509',
|
general: '787738272616808509',
|
||||||
@@ -123,6 +129,7 @@ let IDs = {
|
|||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
restricted: '920993034462715925',
|
restricted: '920993034462715925',
|
||||||
|
bot: '872126272015314966',
|
||||||
economy: '932050015034159174',
|
economy: '932050015034159174',
|
||||||
sus: '872884989950324826',
|
sus: '872884989950324826',
|
||||||
},
|
},
|
||||||
@@ -131,10 +138,12 @@ let IDs = {
|
|||||||
staff: '768685283583328257',
|
staff: '768685283583328257',
|
||||||
modMail: '867077297664426006',
|
modMail: '867077297664426006',
|
||||||
verification: '797505409073676299',
|
verification: '797505409073676299',
|
||||||
|
activism: '873918877019545640',
|
||||||
diversity: '933078380394459146',
|
diversity: '933078380394459146',
|
||||||
private: '992581296901599302',
|
private: '992581296901599302',
|
||||||
restricted: '809765577236283472',
|
restricted: '809765577236283472',
|
||||||
},
|
},
|
||||||
|
modMail: '575252669443211264',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the bot is in development mode
|
// Check if the bot is in development mode
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
// "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. */,
|
"baseUrl": "src" /* Specify the base directory to resolve non-relative module names. */,
|
||||||
"paths": {
|
"paths": {
|
||||||
"#utils/*": ["./utils/*"]
|
"#utils/*": ["./utils/*"],
|
||||||
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
|
} /* 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. */
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||||
|
|
||||||
/* Type Checking */
|
/* 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. */
|
// "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'. */
|
// "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. */
|
// "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. */
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
// "skipLibCheck": true /* Skip type checking all .d.ts files. */
|
// "skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user