mirror of
https://github.com/veganhacktivists/arabot.git
synced 2025-10-30 14:07:40 +01:00
Merge pull request #68 from veganhacktivists/verification
feat(arabot): new verification system
This commit is contained in:
commit
57b17d6e28
@ -1,4 +1,4 @@
|
|||||||
FROM node:18-buster
|
FROM node:18
|
||||||
|
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
|
|
||||||
@ -11,6 +11,8 @@ RUN npm install
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
RUN npx prisma generate
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
RUN chown node:node /opt/app/
|
RUN chown node:node /opt/app/
|
||||||
|
|||||||
949
package-lock.json
generated
949
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -26,31 +26,31 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "^1.2.0",
|
"@discordjs/builders": "^1.3.0",
|
||||||
"@prisma/client": "^4.0.0",
|
"@prisma/client": "^4.0.0",
|
||||||
"@sapphire/discord.js-utilities": "^5.0.0",
|
"@sapphire/discord.js-utilities": "^5.0.0",
|
||||||
"@sapphire/framework": "^3.1.1",
|
"@sapphire/framework": "^3.1.3",
|
||||||
"@sapphire/plugin-scheduled-tasks": "^4.0.0",
|
"@sapphire/plugin-scheduled-tasks": "^4.0.0",
|
||||||
"@sapphire/plugin-subcommands": "^3.0.0",
|
"@sapphire/plugin-subcommands": "^3.0.0",
|
||||||
"@sapphire/stopwatch": "^1.4.1",
|
"@sapphire/stopwatch": "^1.4.1",
|
||||||
"@sapphire/ts-config": "^3.3.4",
|
|
||||||
"@sapphire/utilities": "^3.9.2",
|
"@sapphire/utilities": "^3.9.2",
|
||||||
"@types/node": "^18.0.3",
|
"@types/node": "^18.0.3",
|
||||||
"bullmq": "^1.89.1",
|
"bullmq": "^1.89.1",
|
||||||
"discord-api-types": "^0.33.3",
|
"discord-api-types": "^0.33.3",
|
||||||
"discord.js": "^13.10.3",
|
"discord.js": "^13.12.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"prisma": "^4.2.1",
|
|
||||||
"ts-node": "^10.8.2",
|
"ts-node": "^10.8.2",
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@sapphire/ts-config": "^3.3.4",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||||
"@typescript-eslint/parser": "^5.30.7",
|
"@typescript-eslint/parser": "^5.30.7",
|
||||||
"eslint": "8.22.0",
|
"eslint": "8.22.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
"eslint-plugin-import": "^2.26.0"
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"prisma": "^4.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
prisma/migrations/20220806153942_/migration.sql
Normal file
14
prisma/migrations/20220806153942_/migration.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `balance` on the `User` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `lastDaily` on the `User` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `level` on the `User` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `xp` on the `User` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" DROP COLUMN "balance",
|
||||||
|
DROP COLUMN "lastDaily",
|
||||||
|
DROP COLUMN "level",
|
||||||
|
DROP COLUMN "xp";
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `channelId` to the `VerifyUnblock` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" ADD COLUMN "channelId" TEXT NOT NULL;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `time` on the `VerifyUnblock` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" DROP COLUMN "time",
|
||||||
|
ADD COLUMN "finishTime" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "startTime" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" ADD COLUMN "joinTime" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "startTime" DROP NOT NULL;
|
||||||
14
prisma/migrations/20220806202004_change_id/migration.sql
Normal file
14
prisma/migrations/20220806202004_change_id/migration.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- The primary key for the `VerifyUnblock` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||||
|
- You are about to drop the column `channelId` on the `VerifyUnblock` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" DROP CONSTRAINT "Verify_pkey",
|
||||||
|
DROP COLUMN "channelId",
|
||||||
|
ALTER COLUMN "id" DROP DEFAULT,
|
||||||
|
ALTER COLUMN "id" SET DATA TYPE TEXT,
|
||||||
|
ADD CONSTRAINT "Verify_pkey" PRIMARY KEY ("id");
|
||||||
|
DROP SEQUENCE "Verify_id_seq";
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" ALTER COLUMN "startTime" DROP DEFAULT;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" ADD COLUMN "activist" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
ADD COLUMN "food" INTEGER,
|
||||||
|
ADD COLUMN "length" INTEGER,
|
||||||
|
ADD COLUMN "life" INTEGER,
|
||||||
|
ADD COLUMN "reason" INTEGER,
|
||||||
|
ADD COLUMN "reasoning" INTEGER,
|
||||||
|
ADD COLUMN "trusted" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
ADD COLUMN "vegCurious" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
ADD COLUMN "where" INTEGER;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Verify" ADD COLUMN "convinced" BOOLEAN NOT NULL DEFAULT false;
|
||||||
@ -27,10 +27,6 @@ datasource db {
|
|||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @db.VarChar(255)
|
id String @id @db.VarChar(255)
|
||||||
level Int @default(0)
|
|
||||||
xp Int @default(0)
|
|
||||||
balance Int @default(0)
|
|
||||||
lastDaily DateTime?
|
|
||||||
vegan Boolean @default(false)
|
vegan Boolean @default(false)
|
||||||
trusted Boolean @default(false)
|
trusted Boolean @default(false)
|
||||||
activist Boolean @default(false)
|
activist Boolean @default(false)
|
||||||
@ -52,16 +48,31 @@ model User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Verify {
|
model Verify {
|
||||||
id Int @id @default(autoincrement())
|
id String @id
|
||||||
user User @relation("verUser", fields: [userId], references: [id])
|
user User @relation("verUser", fields: [userId], references: [id])
|
||||||
userId String
|
userId String
|
||||||
verifier User? @relation("verVerifier", fields: [verifierId], references: [id])
|
verifier User? @relation("verVerifier", fields: [verifierId], references: [id])
|
||||||
verifierId String?
|
verifierId String?
|
||||||
time DateTime @default(now())
|
joinTime DateTime @default(now())
|
||||||
timedOut Boolean @default(false) // If they got kicked out of verification because they timed out
|
startTime DateTime?
|
||||||
vegan Boolean @default(false) // If they got verified as a vegan
|
finishTime DateTime?
|
||||||
text Boolean @default(false) // If they used text verification
|
timedOut Boolean @default(false) // If they got kicked out of verification because they timed out
|
||||||
serverVegan Boolean @default(false) // People that went vegan on the server
|
//complete Boolean @default(false) // If the verification was incomplete
|
||||||
|
// Roles they got from verification
|
||||||
|
vegan Boolean @default(false) // If they got verified as a vegan
|
||||||
|
activist Boolean @default(false) // If they got the activist role when they verified
|
||||||
|
trusted Boolean @default(false) // If they got the trusted role when they verified
|
||||||
|
vegCurious Boolean @default(false) // If they got the Veg Curious role
|
||||||
|
convinced Boolean @default(false)
|
||||||
|
text Boolean @default(false) // If they used text verification
|
||||||
|
serverVegan Boolean @default(false) // People that went vegan on the server
|
||||||
|
// Stats on verification
|
||||||
|
reason Int?
|
||||||
|
where Int?
|
||||||
|
length Int?
|
||||||
|
reasoning Int?
|
||||||
|
life Int?
|
||||||
|
food Int?
|
||||||
notes String?
|
notes String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,113 +21,19 @@ import { Command, RegisterBehavior, Args } from '@sapphire/framework';
|
|||||||
import {
|
import {
|
||||||
MessageEmbed, MessageActionRow, MessageButton, Constants, ButtonInteraction,
|
MessageEmbed, MessageActionRow, MessageButton, Constants, ButtonInteraction,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import type { Message, GuildMember } from 'discord.js';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
||||||
import { addExistingUser, userExists } from '../../utils/dbExistingUser';
|
import { addExistingUser, userExists } from '../../utils/database/dbExistingUser';
|
||||||
|
import {
|
||||||
|
addToDatabase,
|
||||||
|
findNotes,
|
||||||
|
getNote,
|
||||||
|
deactivateNote,
|
||||||
|
deactivateAllNotes,
|
||||||
|
} from '../../utils/database/sus';
|
||||||
import IDs from '../../utils/ids';
|
import IDs from '../../utils/ids';
|
||||||
|
|
||||||
// TODO add a check when they join the server to give the user the sus role again
|
// TODO add a check when they join the server to give the user the sus role again
|
||||||
|
|
||||||
async function addToDatabase(userId: string, modId: string, message: string) {
|
|
||||||
// Initialise the database connection
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Add the user to the database
|
|
||||||
await prisma.sus.create({
|
|
||||||
data: {
|
|
||||||
user: {
|
|
||||||
connect: {
|
|
||||||
id: userId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mod: {
|
|
||||||
connect: {
|
|
||||||
id: modId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
note: message,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of sus notes from the user
|
|
||||||
async function findNotes(userId: string, active: boolean) {
|
|
||||||
// Initialise the database connection
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Query to get the specific user's sus notes
|
|
||||||
const note = await prisma.sus.findMany({
|
|
||||||
where: {
|
|
||||||
userId,
|
|
||||||
active,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
return note;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get one note from the id
|
|
||||||
async function getNote(noteId: number) {
|
|
||||||
// Initialise the database connection
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Query to get the specific user's sus notes
|
|
||||||
const note = await prisma.sus.findUnique({
|
|
||||||
where: {
|
|
||||||
id: noteId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
return note;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deactivateNote(noteId: number) {
|
|
||||||
// Initialise the database connection
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Query to deactivate the specific sus note
|
|
||||||
await prisma.sus.update({
|
|
||||||
where: {
|
|
||||||
id: noteId,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deactivateAllNotes(userId: string) {
|
|
||||||
// Initialise the database connection
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Query to deactivate the specific user's sus notes
|
|
||||||
await prisma.sus.updateMany({
|
|
||||||
where: {
|
|
||||||
userId: {
|
|
||||||
contains: userId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main command
|
|
||||||
class SusCommand extends Command {
|
class SusCommand extends Command {
|
||||||
public constructor(context: Command.Context) {
|
public constructor(context: Command.Context) {
|
||||||
super(context, {
|
super(context, {
|
||||||
|
|||||||
50
src/listeners/verification/joinServer.ts
Normal file
50
src/listeners/verification/joinServer.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Listener } from '@sapphire/framework';
|
||||||
|
import type { GuildMember } from 'discord.js';
|
||||||
|
import { fetchRoles } from '../../utils/database/dbExistingUser';
|
||||||
|
import IDs from '../../utils/ids';
|
||||||
|
import { blockTime } from '../../utils/database/verification';
|
||||||
|
|
||||||
|
class VerificationReady extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
once: true,
|
||||||
|
event: 'guildMemberAdd',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(user: GuildMember) {
|
||||||
|
// Add basic roles
|
||||||
|
const roles = await fetchRoles(user.id);
|
||||||
|
|
||||||
|
// Check if the user has a verification block
|
||||||
|
const timeout = await blockTime(user.id);
|
||||||
|
if (timeout > 0) {
|
||||||
|
roles.push(IDs.roles.verifyBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add roles if they don't have verification block
|
||||||
|
await user.roles.add(roles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerificationReady;
|
||||||
720
src/listeners/verification/joinVC.ts
Normal file
720
src/listeners/verification/joinVC.ts
Normal file
@ -0,0 +1,720 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { container, Listener } from '@sapphire/framework';
|
||||||
|
import type {
|
||||||
|
CategoryChannel,
|
||||||
|
ColorResolvable,
|
||||||
|
TextChannel,
|
||||||
|
VoiceChannel,
|
||||||
|
VoiceState,
|
||||||
|
GuildMember,
|
||||||
|
Guild,
|
||||||
|
User,
|
||||||
|
} from 'discord.js';
|
||||||
|
import {
|
||||||
|
ButtonInteraction,
|
||||||
|
Constants,
|
||||||
|
MessageActionRow,
|
||||||
|
MessageButton,
|
||||||
|
MessageEmbed,
|
||||||
|
} from 'discord.js';
|
||||||
|
import { time } from '@discordjs/builders';
|
||||||
|
import { maxVCs, questionInfo, serverFind } from '../../utils/verificationConfig';
|
||||||
|
import { joinVerification, startVerification, finishVerification } from '../../utils/database/verification';
|
||||||
|
import { findNotes } from '../../utils/database/sus';
|
||||||
|
import { userExists, addExistingUser } from '../../utils/database/dbExistingUser';
|
||||||
|
import { rolesToString } from '../../utils/formatter';
|
||||||
|
import IDs from '../../utils/ids';
|
||||||
|
|
||||||
|
class VerificationJoinVCListener extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'voiceStateUpdate',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(oldState: VoiceState, newState: VoiceState) {
|
||||||
|
// If the event was not a user joining the channel
|
||||||
|
if (oldState.channel?.parent?.id === IDs.categories.verification
|
||||||
|
|| newState.channel?.parent?.id !== IDs.categories.verification
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable if this channel is a Verifiers only VC
|
||||||
|
let verifier = false;
|
||||||
|
|
||||||
|
// Checks for not null
|
||||||
|
const { channel } = newState;
|
||||||
|
const { member } = newState;
|
||||||
|
const { client } = container;
|
||||||
|
const guild = client.guilds.cache.get(newState.guild.id);
|
||||||
|
|
||||||
|
if (channel === null || member === null || guild === undefined) {
|
||||||
|
console.error('Verification channel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current category and channel
|
||||||
|
const categoryGuild = guild.channels.cache.get(IDs.categories.verification);
|
||||||
|
const currentChannelGuild = guild.channels.cache.get(channel.id);
|
||||||
|
if (currentChannelGuild === undefined || categoryGuild === undefined) {
|
||||||
|
console.error('Verification channel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentChannel = currentChannelGuild as VoiceChannel;
|
||||||
|
const category = categoryGuild as CategoryChannel;
|
||||||
|
|
||||||
|
const roles = rolesToString(member.roles.cache.map((r) => r.id));
|
||||||
|
|
||||||
|
// Checks if a verifier has joined
|
||||||
|
if (channel.members.size === 2) {
|
||||||
|
await newState.channel!.permissionOverwrites.set([
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
allow: ['SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a verifier joined a verification VC and update database
|
||||||
|
if (channel.members.size === 2) {
|
||||||
|
if (!channel.name.includes(' - Verification')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await startVerification(channel.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if there is more than one person who has joined or if the channel has members
|
||||||
|
if (channel.members.size !== 1
|
||||||
|
|| !channel.members.has(member.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has the verifiers role
|
||||||
|
if (member.roles.cache.has(IDs.roles.staff.verifier)
|
||||||
|
|| member.roles.cache.has(IDs.roles.staff.trialVerifier)) {
|
||||||
|
await channel.setName('Verifier Meeting');
|
||||||
|
verifier = true;
|
||||||
|
} else {
|
||||||
|
await channel.setName(`${member.displayName} - Verification`);
|
||||||
|
await currentChannel.send(`Hiya ${member.user}, please be patient as a verifier has been called out to verify you.\n\n`
|
||||||
|
+ 'If you leave this voice channel, you will automatically be given the non-vegan role where you gain access to this server and if you\'d like to verify as a vegan again, you\'d have to contact a Mod, which could be done via ModMail.');
|
||||||
|
// Adds to the database that the user joined verification
|
||||||
|
await joinVerification(channel.id, member);
|
||||||
|
|
||||||
|
// Remove all roles from the user
|
||||||
|
await member.roles.remove([
|
||||||
|
IDs.roles.vegan.vegan,
|
||||||
|
IDs.roles.trusted,
|
||||||
|
IDs.roles.nonvegan.nonvegan,
|
||||||
|
IDs.roles.nonvegan.convinced,
|
||||||
|
IDs.roles.nonvegan.vegCurious,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Start 15-minute timer if verifier does not join
|
||||||
|
// @ts-ignore
|
||||||
|
this.container.tasks.create('verifyTimeout', {
|
||||||
|
channelId: channel.id,
|
||||||
|
userId: member.id,
|
||||||
|
}, 900_000); // 15 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check how many voice channels there are
|
||||||
|
const listVoiceChannels = category.children.filter((c) => c.type === 'GUILD_VOICE');
|
||||||
|
|
||||||
|
// Create a text channel for verifiers only
|
||||||
|
// Checks if there are more than 10 voice channels
|
||||||
|
if (!verifier) {
|
||||||
|
const verificationText = await guild.channels.create(`✅┃${member.displayName}-verification`, {
|
||||||
|
type: 'GUILD_TEXT',
|
||||||
|
topic: `Channel for verifiers only. ${member.id} ${channel.id} (Please do not change this)`,
|
||||||
|
parent: category.id,
|
||||||
|
userLimit: 1,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.verifyBlock,
|
||||||
|
deny: ['VIEW_CHANNEL', 'SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.verifier,
|
||||||
|
allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a message that someone wants to be verified
|
||||||
|
const userInfoEmbed = await this.getUserInfo(member, roles);
|
||||||
|
const susNotes = await this.getSus(member, guild);
|
||||||
|
await verificationText.send({
|
||||||
|
content: `${member.user} wants to be verified in ${channel}
|
||||||
|
\n<@&${IDs.roles.staff.verifier}> <@&${IDs.roles.staff.trialVerifier}>`,
|
||||||
|
embeds: [userInfoEmbed, susNotes],
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.verificationProcess(verificationText, channel.id, member, guild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new channel for others to join
|
||||||
|
|
||||||
|
// Checks if there are more than 10 voice channels
|
||||||
|
if (listVoiceChannels.size > maxVCs - 1) {
|
||||||
|
await guild.channels.create('Verification', {
|
||||||
|
type: 'GUILD_VOICE',
|
||||||
|
parent: category.id,
|
||||||
|
userLimit: 1,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.verifyBlock,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT', 'SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
deny: ['CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
deny: ['CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.activist,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.verifier,
|
||||||
|
allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await guild.channels.create('Verification', {
|
||||||
|
type: 'GUILD_VOICE',
|
||||||
|
parent: category.id,
|
||||||
|
userLimit: 1,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.verifyBlock,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT', 'SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.activist,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.verifier,
|
||||||
|
allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change permissions to join the current channel
|
||||||
|
await currentChannel.permissionOverwrites.set([
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
deny: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
deny: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: member.id,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await currentChannel.setUserLimit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an embed for information about the user
|
||||||
|
private async getUserInfo(user: GuildMember, roles: string) {
|
||||||
|
const joinTime = time(user.joinedAt!);
|
||||||
|
const registerTime = time(user.user.createdAt);
|
||||||
|
|
||||||
|
const embed = new MessageEmbed()
|
||||||
|
.setColor(user.displayHexColor)
|
||||||
|
.setTitle(`Information on ${user.user.username}`)
|
||||||
|
.setThumbnail(user.user.avatarURL()!)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Joined:', value: `${joinTime}`, inline: true },
|
||||||
|
{ name: 'Created:', value: `${registerTime}`, inline: true },
|
||||||
|
{ name: 'Roles:', value: roles },
|
||||||
|
);
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the embed to display the sus note
|
||||||
|
private async getSus(user: GuildMember, guild: Guild) {
|
||||||
|
const notes = await findNotes(user.id, true);
|
||||||
|
|
||||||
|
const embed = new MessageEmbed()
|
||||||
|
.setColor(user.displayHexColor)
|
||||||
|
.setTitle(`${notes.length} sus notes for ${user.user.username}`);
|
||||||
|
|
||||||
|
// Add up to 10 of the latest sus notes to the embed
|
||||||
|
for (let i = notes.length > 10 ? notes.length - 10 : 0; i < notes.length; i += 1) {
|
||||||
|
// Get mod name
|
||||||
|
const modGuildMember = guild!.members.cache.get(notes[i].modId);
|
||||||
|
let mod = notes[i].modId;
|
||||||
|
if (modGuildMember !== undefined) {
|
||||||
|
mod = modGuildMember!.displayName;
|
||||||
|
}
|
||||||
|
// Add sus note to embed
|
||||||
|
embed.addFields({
|
||||||
|
name: `Sus ID: ${notes[i].id} | Moderator: ${mod} | Date: <t:${Math.floor(notes[i].time.getTime() / 1000)}>`,
|
||||||
|
value: notes[i].note,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async verificationProcess(
|
||||||
|
channel: TextChannel,
|
||||||
|
verId: string,
|
||||||
|
user: GuildMember,
|
||||||
|
guild: Guild,
|
||||||
|
) {
|
||||||
|
const embedColor = '#0099ff';
|
||||||
|
const info = {
|
||||||
|
page: 0,
|
||||||
|
find: {
|
||||||
|
reason: 0,
|
||||||
|
where: 0,
|
||||||
|
},
|
||||||
|
length: 0,
|
||||||
|
reasoning: 0,
|
||||||
|
life: 0,
|
||||||
|
food: 0,
|
||||||
|
roles: {
|
||||||
|
vegan: false,
|
||||||
|
activist: false,
|
||||||
|
trusted: false,
|
||||||
|
vegCurious: false,
|
||||||
|
convinced: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO add a variable that tells if each order has a reversed value, e.g. 0-3 or 3-0
|
||||||
|
const questionLength = questionInfo.length;
|
||||||
|
|
||||||
|
let embed = await this.createEmbed(questionInfo[0].question, embedColor);
|
||||||
|
let buttons = await this.createButtons(questionInfo[0].buttons);
|
||||||
|
|
||||||
|
// Sends the note to verify this note is to be deleted
|
||||||
|
const message = await channel.send({
|
||||||
|
embeds: [embed],
|
||||||
|
components: buttons,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for the button presses
|
||||||
|
const collector = channel.createMessageComponentCollector({
|
||||||
|
// max: 2, // Maximum of 1 button press
|
||||||
|
});
|
||||||
|
|
||||||
|
// Button pressed
|
||||||
|
collector.on('collect', async (button: ButtonInteraction) => {
|
||||||
|
// Select roles
|
||||||
|
if (button.customId.includes('button')) {
|
||||||
|
await button.deferUpdate();
|
||||||
|
// Get the button choice
|
||||||
|
const buttonChoice = this.getButtonValue(button.customId);
|
||||||
|
if (Number.isNaN(buttonChoice)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Set the value of the button choice to the page the question was on
|
||||||
|
switch (info.page) {
|
||||||
|
case 0: {
|
||||||
|
info.find.reason = buttonChoice;
|
||||||
|
if (buttonChoice !== 0 && info.find.reason === 0) {
|
||||||
|
embed = await this.createEmbed(serverFind[info.page].question, embedColor);
|
||||||
|
buttons = await this.createButtons(serverFind[info.page].buttons);
|
||||||
|
await message.edit({
|
||||||
|
embeds: [embed],
|
||||||
|
components: buttons,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info.find.reason !== 0) {
|
||||||
|
info.find.where = buttonChoice;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
info.length = buttonChoice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
info.reasoning = buttonChoice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
info.life = buttonChoice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
info.food = buttonChoice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If they are definitely vegan or not
|
||||||
|
case 5: {
|
||||||
|
if (buttonChoice === 0) {
|
||||||
|
info.roles.vegan = true;
|
||||||
|
info.roles.trusted = true;
|
||||||
|
} else {
|
||||||
|
info.page += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If they are vegan but should get activist role
|
||||||
|
case 6: {
|
||||||
|
if (buttonChoice === 0) {
|
||||||
|
info.roles.activist = true;
|
||||||
|
}
|
||||||
|
info.page += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If they should get vegan, convinced or non-vegan
|
||||||
|
case 7: {
|
||||||
|
if (buttonChoice === 0) {
|
||||||
|
info.roles.vegan = true;
|
||||||
|
} else if (buttonChoice === 1) {
|
||||||
|
info.roles.convinced = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
|
if (buttonChoice === 0) {
|
||||||
|
info.roles.vegCurious = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.error('Button clicked out of range');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.page += 1;
|
||||||
|
// Checks if finished all the questions
|
||||||
|
if (info.page < questionLength) {
|
||||||
|
embed = await this.createEmbed(questionInfo[info.page].question, embedColor);
|
||||||
|
buttons = await this.createButtons(questionInfo[info.page].buttons);
|
||||||
|
await message.edit({
|
||||||
|
embeds: [embed],
|
||||||
|
components: buttons,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Confirmation to give roles to the user being verified
|
||||||
|
if (info.page === questionLength) {
|
||||||
|
// Create embed with all the roles the user has
|
||||||
|
embed = new MessageEmbed()
|
||||||
|
.setColor(embedColor)
|
||||||
|
.setTitle(`Give these roles to ${user.displayName}?`)
|
||||||
|
.setThumbnail(user.avatarURL()!)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Roles:', value: this.getTextRoles(info.roles) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create buttons for input
|
||||||
|
buttons = [new MessageActionRow<MessageButton>()
|
||||||
|
.addComponents(
|
||||||
|
new MessageButton()
|
||||||
|
.setCustomId('confirm')
|
||||||
|
.setLabel('Yes')
|
||||||
|
.setStyle(Constants.MessageButtonStyles.SUCCESS),
|
||||||
|
new MessageButton()
|
||||||
|
.setCustomId('cancel')
|
||||||
|
.setLabel('No')
|
||||||
|
.setStyle(Constants.MessageButtonStyles.DANGER),
|
||||||
|
)];
|
||||||
|
await message.edit({
|
||||||
|
embeds: [embed],
|
||||||
|
components: buttons,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Confirming and finishing the verification
|
||||||
|
if (button.customId === 'confirm' && info.page >= questionLength) {
|
||||||
|
// Check verifier is on the database
|
||||||
|
const verifierGuildMember = await guild.members.cache.get(button.user.id);
|
||||||
|
if (verifierGuildMember === undefined) {
|
||||||
|
await message.edit({ content: 'Verifier not found!' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Add verifier to database if they're not on the database
|
||||||
|
if (!(await userExists(verifierGuildMember))) {
|
||||||
|
await addExistingUser(verifierGuildMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add verification data to database
|
||||||
|
await finishVerification(verId, button.user.id, info);
|
||||||
|
// Give roles on Discord
|
||||||
|
await this.giveRoles(user, info.roles);
|
||||||
|
// Add timeout if they do not have activist role
|
||||||
|
if (!info.roles.activist) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.container.tasks.create('verifyUnblock', {
|
||||||
|
userId: user.id,
|
||||||
|
guildId: guild.id,
|
||||||
|
}, (info.roles.vegan || info.roles.convinced) ? 604800000 : 1814400000);
|
||||||
|
}
|
||||||
|
// Add embed saying verification completed
|
||||||
|
embed = new MessageEmbed()
|
||||||
|
.setColor('#34c000')
|
||||||
|
.setTitle(`Successfully verified ${user.displayName}!`)
|
||||||
|
.setThumbnail(user.user.avatarURL()!)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Roles:', value: this.getTextRoles(info.roles) },
|
||||||
|
);
|
||||||
|
await message.edit({
|
||||||
|
embeds: [embed],
|
||||||
|
components: [],
|
||||||
|
});
|
||||||
|
// Send welcome message after verification
|
||||||
|
await this.finishMessages(user.user, info.roles);
|
||||||
|
}
|
||||||
|
if (button.customId === 'cancel' && info.page >= questionLength) {
|
||||||
|
info.page = 5;
|
||||||
|
info.roles.vegan = false;
|
||||||
|
info.roles.activist = false;
|
||||||
|
info.roles.trusted = false;
|
||||||
|
info.roles.vegCurious = false;
|
||||||
|
info.roles.convinced = false;
|
||||||
|
embed = await this.createEmbed(questionInfo[info.page].question, embedColor);
|
||||||
|
buttons = await this.createButtons(questionInfo[info.page].buttons);
|
||||||
|
await message.edit({
|
||||||
|
embeds: [embed],
|
||||||
|
components: buttons,
|
||||||
|
});
|
||||||
|
await button.deferUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createEmbed(title: string, color: ColorResolvable) {
|
||||||
|
return new MessageEmbed()
|
||||||
|
.setColor(color)
|
||||||
|
.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createButtons(buttons: string[]) {
|
||||||
|
const buttonActions = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < buttons.length; i += 1) {
|
||||||
|
// Check if it exceeds the maximum buttons in a ActionRow
|
||||||
|
if (i % 5 === 0) {
|
||||||
|
buttonActions.push(new MessageActionRow<MessageButton>());
|
||||||
|
}
|
||||||
|
buttonActions[Math.floor(i / 5)]
|
||||||
|
.addComponents(
|
||||||
|
new MessageButton()
|
||||||
|
.setCustomId(`button${i}`)
|
||||||
|
.setLabel(buttons[i])
|
||||||
|
.setStyle(Constants.MessageButtonStyles.SECONDARY),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttonActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the value of the choice in the button
|
||||||
|
private getButtonValue(button: string) {
|
||||||
|
const buttonChoice = button.at(button.length - 1);
|
||||||
|
if (buttonChoice === undefined) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
return parseInt(buttonChoice, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTextRoles(
|
||||||
|
roles: {
|
||||||
|
vegan: boolean,
|
||||||
|
activist: boolean,
|
||||||
|
trusted: boolean,
|
||||||
|
vegCurious: boolean,
|
||||||
|
convinced: boolean
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
let rolesText = '';
|
||||||
|
if (roles.convinced) {
|
||||||
|
rolesText += `<@&${IDs.roles.nonvegan.convinced}>`;
|
||||||
|
}
|
||||||
|
if (roles.vegan) {
|
||||||
|
rolesText += `<@&${IDs.roles.vegan.vegan}>`;
|
||||||
|
} else {
|
||||||
|
rolesText += `<@&${IDs.roles.nonvegan.nonvegan}>`;
|
||||||
|
}
|
||||||
|
if (roles.activist) {
|
||||||
|
rolesText += `<@&${IDs.roles.vegan.activist}>`;
|
||||||
|
}
|
||||||
|
if (roles.trusted) {
|
||||||
|
rolesText += `<@&${IDs.roles.trusted}>`;
|
||||||
|
}
|
||||||
|
if (roles.vegCurious) {
|
||||||
|
rolesText += `<@&${IDs.roles.nonvegan.vegCurious}>`;
|
||||||
|
}
|
||||||
|
return rolesText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async giveRoles(
|
||||||
|
user: GuildMember,
|
||||||
|
roles: {
|
||||||
|
vegan: boolean,
|
||||||
|
activist: boolean,
|
||||||
|
trusted: boolean,
|
||||||
|
vegCurious: boolean,
|
||||||
|
convinced: boolean
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const rolesAdd = [];
|
||||||
|
if (roles.convinced) {
|
||||||
|
rolesAdd.push(IDs.roles.nonvegan.convinced);
|
||||||
|
}
|
||||||
|
if (roles.vegan) {
|
||||||
|
rolesAdd.push(IDs.roles.vegan.vegan);
|
||||||
|
} else {
|
||||||
|
rolesAdd.push(IDs.roles.nonvegan.nonvegan);
|
||||||
|
}
|
||||||
|
if (roles.activist) {
|
||||||
|
rolesAdd.push(IDs.roles.vegan.activist);
|
||||||
|
} else {
|
||||||
|
rolesAdd.push(IDs.roles.verifyBlock);
|
||||||
|
}
|
||||||
|
if (roles.trusted) {
|
||||||
|
rolesAdd.push(IDs.roles.trusted);
|
||||||
|
}
|
||||||
|
if (roles.vegCurious) {
|
||||||
|
rolesAdd.push(IDs.roles.nonvegan.vegCurious);
|
||||||
|
}
|
||||||
|
await user.roles.add(rolesAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages after verifying the user
|
||||||
|
private async finishMessages(user: User, roles: {
|
||||||
|
vegan: boolean,
|
||||||
|
activist: boolean,
|
||||||
|
trusted: boolean,
|
||||||
|
vegCurious: boolean,
|
||||||
|
convinced: boolean
|
||||||
|
}) {
|
||||||
|
// Send a DM with when their verification is finished
|
||||||
|
await this.finishDM(user, roles)
|
||||||
|
.catch(() => console.error('Verification: Closed DMs'));
|
||||||
|
|
||||||
|
// Not vegan
|
||||||
|
if (!roles.vegan) {
|
||||||
|
const general = this.container.client.channels.cache.get(IDs.channels.nonVegan.general) as TextChannel | undefined;
|
||||||
|
if (general === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg = `${user}, you have been verified! Please check <#${IDs.channels.information.roles}> `
|
||||||
|
+ `and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussion and debates.`;
|
||||||
|
// Add extra info if the user got veg curious or convinced.
|
||||||
|
if (roles.vegCurious || roles.convinced) {
|
||||||
|
msg += `\n\nYou also have access to <#${IDs.channels.dietSupport.main}> for help on going vegan.`;
|
||||||
|
}
|
||||||
|
await general.send(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vegan
|
||||||
|
const general = this.container.client.channels.cache.get(IDs.channels.vegan.general) as TextChannel | undefined;
|
||||||
|
if (general === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = `Welcome ${user}! Please check out <#${IDs.channels.information.roles}> :)`;
|
||||||
|
await general.send(msg);
|
||||||
|
|
||||||
|
// Activist role
|
||||||
|
if (roles.activist) {
|
||||||
|
const activist = this.container.client.channels.cache.get(IDs.channels.activism.activism) as TextChannel | undefined;
|
||||||
|
if (activist === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const activistMsg = `${user} you have been given the activist role! This means that if you'd wish to engage with non-vegans in `
|
||||||
|
+ `<#${IDs.channels.nonVegan.general}>, you should follow these rules:\n\n`
|
||||||
|
+ '1. Try to move conversations with non-vegans towards veganism/animal ethics\n'
|
||||||
|
+ '2. Don\'t discuss social topics while activism is happening\n'
|
||||||
|
+ '3. Have evidence for claims you make. "I don\'t know" is an acceptable answer. Chances are someone here knows or you can take time to find out\n'
|
||||||
|
+ '4. Don\'t advocate for baby steps towards veganism. Participation in exploitation can stop today\n'
|
||||||
|
+ '5. Differences in opinion between activists should be resolved in vegan spaces, not in the chat with non-vegans';
|
||||||
|
await activist.send(activistMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages after verifying the user
|
||||||
|
private async finishDM(user: User, roles: {
|
||||||
|
vegan: boolean,
|
||||||
|
activist: boolean,
|
||||||
|
trusted: boolean,
|
||||||
|
vegCurious: boolean,
|
||||||
|
convinced: boolean
|
||||||
|
}) {
|
||||||
|
if (!roles.vegan && !roles.convinced) {
|
||||||
|
const message = 'You\'ve been verified as non-vegan!'
|
||||||
|
+ `\n\nYou can next verify on ${time(Math.round(Date.now() / 1000) + 1814400)}`;
|
||||||
|
|
||||||
|
await user.send(message);
|
||||||
|
} else if (roles.convinced) {
|
||||||
|
const message = 'You\'ve been verified as convinced!'
|
||||||
|
+ `\n\nYou can next verify on ${time(Math.round(Date.now() / 1000) + 604800)}`;
|
||||||
|
|
||||||
|
await user.send(message);
|
||||||
|
} else if (roles.vegan && !roles.activist) {
|
||||||
|
const message = 'You\'ve been verified as a vegan!'
|
||||||
|
+ `\n\nYou can next get verified on ${time(Math.round(Date.now() / 1000) + 604800)} if you would wish to have the activist role.`;
|
||||||
|
|
||||||
|
await user.send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerificationJoinVCListener;
|
||||||
183
src/listeners/verification/leaveVC.ts
Normal file
183
src/listeners/verification/leaveVC.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { container, Listener } from '@sapphire/framework';
|
||||||
|
import type {
|
||||||
|
VoiceState, CategoryChannel, VoiceChannel, TextChannel,
|
||||||
|
} from 'discord.js';
|
||||||
|
import { time } from '@discordjs/builders';
|
||||||
|
import { maxVCs, leaveBan } from '../../utils/verificationConfig';
|
||||||
|
import { getUser, checkFinish, countIncomplete } from '../../utils/database/verification';
|
||||||
|
import { fetchRoles } from '../../utils/database/dbExistingUser';
|
||||||
|
import { fibonacci } from '../../utils/mathsSeries';
|
||||||
|
import IDs from '../../utils/ids';
|
||||||
|
|
||||||
|
class VerificationLeaveVCListener extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'voiceStateUpdate',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(oldState: VoiceState, newState: VoiceState) {
|
||||||
|
// If the event was not a user joining the channel
|
||||||
|
if (oldState.channel?.parent?.id !== IDs.categories.verification
|
||||||
|
|| newState.channel?.parent?.id === IDs.categories.verification
|
||||||
|
|| oldState.channel.members.size > 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let verifier = false;
|
||||||
|
|
||||||
|
// Check for undefined variables
|
||||||
|
const { client } = container;
|
||||||
|
const { channel } = oldState;
|
||||||
|
const guild = client.guilds.cache.get(newState.guild.id);
|
||||||
|
|
||||||
|
if (channel === null || guild === undefined) {
|
||||||
|
console.error('Verification channel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the category
|
||||||
|
const categoryGuild = guild.channels.cache.get(IDs.categories.verification);
|
||||||
|
if (categoryGuild === null) {
|
||||||
|
console.error('Verification channel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const category = categoryGuild as CategoryChannel;
|
||||||
|
|
||||||
|
// Get the user that was being verified
|
||||||
|
const userSnowflake = await getUser(channel.id);
|
||||||
|
if (userSnowflake === null) {
|
||||||
|
verifier = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow more people to join VC if there are less than 10 VCs
|
||||||
|
|
||||||
|
if (!verifier) {
|
||||||
|
const user = guild.members.cache.get(userSnowflake!)!;
|
||||||
|
|
||||||
|
// Remove verify as vegan and give non vegan role
|
||||||
|
if (!await checkFinish(channel.id)) {
|
||||||
|
await user.roles.remove(IDs.roles.verifyingAsVegan);
|
||||||
|
|
||||||
|
// Get roles to give back to the user
|
||||||
|
const roles = await fetchRoles(user.id);
|
||||||
|
roles.push(IDs.roles.verifyBlock);
|
||||||
|
await user.roles.add(roles);
|
||||||
|
// Create timeout block for user
|
||||||
|
// Counts the recent times they have incomplete verifications
|
||||||
|
const incompleteCount = await countIncomplete(user.id) % (leaveBan + 1);
|
||||||
|
// Creates the length of the time for the ban
|
||||||
|
const banLength = fibonacci(incompleteCount) * 3600_000;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.container.tasks.create('verifyUnblock', {
|
||||||
|
userId: user.id,
|
||||||
|
guildId: guild.id,
|
||||||
|
}, banLength);
|
||||||
|
|
||||||
|
await user.user.send('You have been timed out as a verifier had not joined for 15 minutes or you disconnected from verification.\n\n'
|
||||||
|
+ `You can verify again at: ${time(Math.round(Date.now() / 1000) + (banLength / 1000))}`)
|
||||||
|
.catch(() => console.error('Verification: Closed DMs'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check how many voice channels there are
|
||||||
|
const listVoiceChannels = category.children.filter((c) => c.type === 'GUILD_VOICE');
|
||||||
|
|
||||||
|
// Check that it is not deleting the 'Verification' channel (in case bot crashes)
|
||||||
|
if (channel.name !== 'Verification') {
|
||||||
|
// Delete the channel
|
||||||
|
await channel.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete text channel
|
||||||
|
if (!verifier) {
|
||||||
|
// Gets a list of all the text channels in the verification category
|
||||||
|
const listTextChannels = category.children.filter((c) => c.type === 'GUILD_TEXT');
|
||||||
|
listTextChannels.forEach((c) => {
|
||||||
|
const textChannel = c as TextChannel;
|
||||||
|
// Checks if the channel topic has the user's snowflake
|
||||||
|
if (textChannel.topic!.includes(userSnowflake!)) {
|
||||||
|
textChannel.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no VCs left in verification after having the channel deleted
|
||||||
|
if (listVoiceChannels.size === 0) {
|
||||||
|
// Create a verification channel
|
||||||
|
await guild.channels.create('Verification', {
|
||||||
|
type: 'GUILD_VOICE',
|
||||||
|
parent: IDs.categories.verification,
|
||||||
|
userLimit: 1,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.verifyBlock,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT', 'SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.activist,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.verifier,
|
||||||
|
allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are less than 10, stop
|
||||||
|
if (listVoiceChannels.size < maxVCs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verification = listVoiceChannels.last() as VoiceChannel;
|
||||||
|
|
||||||
|
await verification!.permissionOverwrites.set([
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerificationLeaveVCListener;
|
||||||
111
src/listeners/verification/start.ts
Normal file
111
src/listeners/verification/start.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Listener } from '@sapphire/framework';
|
||||||
|
import type {
|
||||||
|
Client,
|
||||||
|
CategoryChannel,
|
||||||
|
TextChannel,
|
||||||
|
VoiceChannel,
|
||||||
|
} from 'discord.js';
|
||||||
|
import IDs from '../../utils/ids';
|
||||||
|
|
||||||
|
class VerificationReady extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
once: true,
|
||||||
|
event: 'ready',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(client: Client) {
|
||||||
|
// Get verification category
|
||||||
|
let category = client.channels.cache.get(IDs.categories.verification) as CategoryChannel | undefined;
|
||||||
|
if (category === undefined) {
|
||||||
|
category = await client.channels.fetch(IDs.categories.verification) as CategoryChannel | undefined;
|
||||||
|
if (category === undefined) {
|
||||||
|
console.error('verifyStart: Channel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check how many voice channels there are
|
||||||
|
let voiceChannels = category.children.filter((c) => c.type === 'GUILD_VOICE');
|
||||||
|
const emptyVC: string[] = [];
|
||||||
|
// Delete voice channels
|
||||||
|
voiceChannels.forEach((c) => {
|
||||||
|
const voiceChannel = c as VoiceChannel;
|
||||||
|
if (voiceChannel.members.size === 0) {
|
||||||
|
emptyVC.push(voiceChannel.id);
|
||||||
|
voiceChannel.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete text channels
|
||||||
|
const textChannels = category.children.filter((c) => c.type === 'GUILD_TEXT');
|
||||||
|
textChannels.forEach((c) => {
|
||||||
|
const textChannel = c as TextChannel;
|
||||||
|
// Checks if the channel topic has the user's snowflake
|
||||||
|
emptyVC.forEach((snowflake) => {
|
||||||
|
if (textChannel.topic!.includes(snowflake)) {
|
||||||
|
textChannel.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if there is no voice channels, create verification
|
||||||
|
voiceChannels = category.children.filter((c) => c.type === 'GUILD_VOICE');
|
||||||
|
if (voiceChannels.size === emptyVC.length) {
|
||||||
|
await category.guild.channels.create('Verification', {
|
||||||
|
type: 'GUILD_VOICE',
|
||||||
|
parent: IDs.categories.verification,
|
||||||
|
userLimit: 1,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: category.guild.roles.everyone,
|
||||||
|
deny: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.verifyBlock,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT', 'SEND_MESSAGES'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.nonvegan.nonvegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.vegan,
|
||||||
|
allow: ['VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.vegan.activist,
|
||||||
|
deny: ['VIEW_CHANNEL', 'CONNECT'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.verifier,
|
||||||
|
allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerificationReady;
|
||||||
56
src/scheduled-tasks/verifyTimeout.ts
Normal file
56
src/scheduled-tasks/verifyTimeout.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { VoiceChannel } from 'discord.js';
|
||||||
|
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
|
||||||
|
|
||||||
|
export class VerifyTimeout extends ScheduledTask {
|
||||||
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
|
super(context, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(payload: { channelId: string, userId: string }) {
|
||||||
|
// Get the guild where the user is in
|
||||||
|
let channel = this.container.client.channels.cache.get(payload.channelId) as VoiceChannel | undefined;
|
||||||
|
if (channel === undefined) {
|
||||||
|
channel = await this.container.client.channels.fetch(payload.channelId) as VoiceChannel | undefined;
|
||||||
|
if (channel === undefined) {
|
||||||
|
console.error('verifyTimeout: Channel not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel.members.size < 2 && channel.members.has(payload.userId)) {
|
||||||
|
const user = channel.members.get(payload.userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
console.error('verifyTimeout: GuildMember not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await user.voice.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
|
interface ScheduledTasks {
|
||||||
|
verifyUnblock: never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerifyTimeout;
|
||||||
60
src/scheduled-tasks/verifyUnblock.ts
Normal file
60
src/scheduled-tasks/verifyUnblock.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
|
||||||
|
import IDs from '../utils/ids';
|
||||||
|
|
||||||
|
export class VerifyUnblock extends ScheduledTask {
|
||||||
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
|
super(context, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(payload: { userId: string, guildId: string }) {
|
||||||
|
// Get the guild where the user is in
|
||||||
|
let guild = this.container.client.guilds.cache.get(payload.guildId);
|
||||||
|
if (guild === undefined) {
|
||||||
|
guild = await this.container.client.guilds.fetch(payload.guildId);
|
||||||
|
if (guild === undefined) {
|
||||||
|
console.error('verifyUnblock: Guild not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find GuildMember for the user
|
||||||
|
let user = guild.members.cache.get(payload.userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.members.fetch(payload.userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
console.error('verifyUnblock: GuildMember not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the 'verify block' role
|
||||||
|
await user.roles.remove(IDs.roles.verifyBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
|
interface ScheduledTasks {
|
||||||
|
verifyUnblock: never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerifyUnblock;
|
||||||
185
src/utils/database/dbExistingUser.ts
Normal file
185
src/utils/database/dbExistingUser.ts
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { GuildMember, GuildMemberRoleManager } from 'discord.js';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import IDs from '../ids';
|
||||||
|
|
||||||
|
// Checks if the user exists on the database
|
||||||
|
export async function userExists(user: GuildMember) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Counts if the user is on the database by their snowflake
|
||||||
|
const userQuery = await prisma.user.count({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
// If the user is found on the database, then return true, otherwise, false.
|
||||||
|
return userQuery > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRoles(roles: GuildMemberRoleManager) {
|
||||||
|
// Checks what roles the user has
|
||||||
|
const rolesDict = {
|
||||||
|
vegan: roles.cache.has(IDs.roles.vegan.vegan),
|
||||||
|
activist: roles.cache.has(IDs.roles.vegan.activist),
|
||||||
|
plus: roles.cache.has(IDs.roles.vegan.plus),
|
||||||
|
notVegan: roles.cache.has(IDs.roles.nonvegan.nonvegan),
|
||||||
|
vegCurious: roles.cache.has(IDs.roles.nonvegan.vegCurious),
|
||||||
|
convinced: roles.cache.has(IDs.roles.nonvegan.convinced),
|
||||||
|
trusted: roles.cache.has(IDs.roles.trusted),
|
||||||
|
muted: roles.cache.has(IDs.roles.restrictions.muted),
|
||||||
|
};
|
||||||
|
|
||||||
|
return rolesDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the user to the database if they were already on the server before the bot/database
|
||||||
|
export async function addExistingUser(user: GuildMember) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Counts if the user is on the database by their snowflake
|
||||||
|
const userQuery = await prisma.user.count({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the user is already in the database
|
||||||
|
if (userQuery > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse all the roles into a dictionary
|
||||||
|
const roles = getRoles(user.roles);
|
||||||
|
|
||||||
|
// Create the user in the database
|
||||||
|
await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
id: user.id,
|
||||||
|
vegan: roles.vegan,
|
||||||
|
trusted: roles.trusted,
|
||||||
|
activist: roles.activist,
|
||||||
|
plus: roles.plus,
|
||||||
|
notVegan: roles.notVegan,
|
||||||
|
vegCurious: roles.vegCurious,
|
||||||
|
convinced: roles.convinced,
|
||||||
|
muted: roles.muted,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(user: GuildMember) {
|
||||||
|
// Check if the user is already on the database
|
||||||
|
if (!(await userExists(user))) {
|
||||||
|
await addExistingUser(user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse all the roles into a dictionary
|
||||||
|
const roles = getRoles(user.roles);
|
||||||
|
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
id: user.id,
|
||||||
|
vegan: roles.vegan,
|
||||||
|
trusted: roles.trusted,
|
||||||
|
activist: roles.activist,
|
||||||
|
plus: roles.plus,
|
||||||
|
notVegan: roles.notVegan,
|
||||||
|
vegCurious: roles.vegCurious,
|
||||||
|
convinced: roles.convinced,
|
||||||
|
muted: roles.muted,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchRoles(user: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Get the user's roles
|
||||||
|
const roleQuery = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: user,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
vegan: true,
|
||||||
|
trusted: true,
|
||||||
|
activist: true,
|
||||||
|
plus: true,
|
||||||
|
notVegan: true,
|
||||||
|
vegCurious: true,
|
||||||
|
convinced: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
// Assign roles to role snowflakes
|
||||||
|
const roles = [];
|
||||||
|
|
||||||
|
if (roleQuery === null) {
|
||||||
|
roles.push('');
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
if (roleQuery.vegan) {
|
||||||
|
roles.push(IDs.roles.vegan.vegan);
|
||||||
|
}
|
||||||
|
if (roleQuery.trusted) {
|
||||||
|
roles.push(IDs.roles.trusted);
|
||||||
|
}
|
||||||
|
if (roleQuery.activist) {
|
||||||
|
roles.push(IDs.roles.vegan.activist);
|
||||||
|
}
|
||||||
|
if (roleQuery.plus) {
|
||||||
|
roles.push(IDs.roles.vegan.plus);
|
||||||
|
}
|
||||||
|
if (roleQuery.notVegan) {
|
||||||
|
roles.push(IDs.roles.nonvegan.nonvegan);
|
||||||
|
}
|
||||||
|
if (roleQuery.vegCurious) {
|
||||||
|
roles.push(IDs.roles.nonvegan.vegCurious);
|
||||||
|
}
|
||||||
|
if (roleQuery.convinced) {
|
||||||
|
roles.push(IDs.roles.nonvegan.convinced);
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
99
src/utils/database/sus.ts
Normal file
99
src/utils/database/sus.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
export async function addToDatabase(userId: string, modId: string, message: string) {
|
||||||
|
// Initialise the database connection
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Add the user to the database
|
||||||
|
await prisma.sus.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
note: message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of sus notes from the user
|
||||||
|
export async function findNotes(userId: string, active: boolean) {
|
||||||
|
// Initialise the database connection
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Query to get the specific user's sus notes
|
||||||
|
const note = await prisma.sus.findMany({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
active,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get one note from the id
|
||||||
|
export async function getNote(noteId: number) {
|
||||||
|
// Initialise the database connection
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Query to get the specific user's sus notes
|
||||||
|
const note = await prisma.sus.findUnique({
|
||||||
|
where: {
|
||||||
|
id: noteId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deactivateNote(noteId: number) {
|
||||||
|
// Initialise the database connection
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Query to deactivate the specific sus note
|
||||||
|
await prisma.sus.update({
|
||||||
|
where: {
|
||||||
|
id: noteId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deactivateAllNotes(userId: string) {
|
||||||
|
// Initialise the database connection
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Query to deactivate the specific user's sus notes
|
||||||
|
await prisma.sus.updateMany({
|
||||||
|
where: {
|
||||||
|
userId: {
|
||||||
|
contains: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
230
src/utils/database/verification.ts
Normal file
230
src/utils/database/verification.ts
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { GuildMember } from 'discord.js';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { updateUser } from './dbExistingUser';
|
||||||
|
import { leaveBan } from '../verificationConfig';
|
||||||
|
import { fibonacci } from '../mathsSeries';
|
||||||
|
|
||||||
|
export async function joinVerification(channelId: string, user: GuildMember) {
|
||||||
|
// Update the user on the database with the current roles they have
|
||||||
|
await updateUser(user);
|
||||||
|
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
await prisma.verify.create({
|
||||||
|
data: {
|
||||||
|
id: channelId,
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startVerification(channelId: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
await prisma.verify.update({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
startTime: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUser(channelId: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Get the snowflake of the user verifying
|
||||||
|
const user = await prisma.verify.findUnique({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
userId: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
// Check the user could be found
|
||||||
|
if (user === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the user's snowflake
|
||||||
|
return user.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function finishVerification(
|
||||||
|
channelId: string,
|
||||||
|
verifierId: string,
|
||||||
|
info: {
|
||||||
|
page: number,
|
||||||
|
find: {
|
||||||
|
reason: number,
|
||||||
|
where: number
|
||||||
|
},
|
||||||
|
length: number,
|
||||||
|
reasoning: number,
|
||||||
|
life: number,
|
||||||
|
food: number,
|
||||||
|
roles: {
|
||||||
|
vegan: boolean,
|
||||||
|
activist: boolean,
|
||||||
|
trusted: boolean,
|
||||||
|
vegCurious: boolean,
|
||||||
|
convinced: boolean
|
||||||
|
} },
|
||||||
|
) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// TODO potentially add an incomplete tracker?
|
||||||
|
await prisma.verify.update({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
verifier: {
|
||||||
|
connect: {
|
||||||
|
id: verifierId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
finishTime: new Date(),
|
||||||
|
// Roles
|
||||||
|
vegan: info.roles.vegan,
|
||||||
|
activist: info.roles.activist,
|
||||||
|
trusted: info.roles.trusted,
|
||||||
|
vegCurious: info.roles.vegCurious,
|
||||||
|
convinced: info.roles.convinced,
|
||||||
|
// Statistics
|
||||||
|
reason: info.find.reason,
|
||||||
|
where: info.find.where,
|
||||||
|
length: info.length,
|
||||||
|
reasoning: info.reasoning,
|
||||||
|
life: info.life,
|
||||||
|
food: info.food,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if verification was complete
|
||||||
|
export async function checkFinish(channelId: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Get the snowflake of the user verifying
|
||||||
|
const finish = await prisma.verify.findUnique({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
finishTime: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
// Checks if query returned is null
|
||||||
|
if (finish === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if a finish time has been set meaning verification is complete
|
||||||
|
return finish.finishTime !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counts how many times the user has not had a verifier join their VC before leaving
|
||||||
|
export async function countIncomplete(userId: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Count how many times the user has not completed a verification
|
||||||
|
const incompleteCount = await prisma.verify.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
finishTime: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
return incompleteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the amount of time left on the block
|
||||||
|
export async function blockTime(userId: string) {
|
||||||
|
// Initialises Prisma Client
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Count how many times the user has not completed a verification
|
||||||
|
const verification = await prisma.verify.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
|
||||||
|
if (verification === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user finished verification
|
||||||
|
if (verification.finishTime !== null) {
|
||||||
|
// Activist role
|
||||||
|
if (verification.activist) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const timeOff = new Date().getTime() - verification.finishTime.getTime();
|
||||||
|
return ((verification.vegan || verification.convinced) ? 604800000 : 1814400000) - timeOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeouts
|
||||||
|
const count = await countIncomplete(verification.userId) % (leaveBan + 1);
|
||||||
|
const timeOff = new Date().getTime() - verification.joinTime.getTime();
|
||||||
|
// Creates the length of the time for the ban
|
||||||
|
return (fibonacci(count) * 3600_000) - timeOff;
|
||||||
|
}
|
||||||
@ -1,87 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
/*
|
|
||||||
Animal Rights Advocates Discord Bot
|
|
||||||
Copyright (C) 2022 Anthony Berg
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { GuildMember } from 'discord.js';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
import IDs from './ids';
|
|
||||||
|
|
||||||
// Checks if the user exists on the database
|
|
||||||
export async function userExists(user: GuildMember) {
|
|
||||||
// Initialises Prisma Client
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Counts if the user is on the database by their snowflake
|
|
||||||
const userQuery = await prisma.user.count({
|
|
||||||
where: {
|
|
||||||
id: user.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
|
|
||||||
// If the user is found on the database, then return true, otherwise, false.
|
|
||||||
return userQuery > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds the user to the database if they were already on the server before the bot/database
|
|
||||||
export async function addExistingUser(user: GuildMember) {
|
|
||||||
// Initialises Prisma Client
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// Counts if the user is on the database by their snowflake
|
|
||||||
const userQuery = await prisma.user.count({
|
|
||||||
where: {
|
|
||||||
id: user.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the user is already in the database
|
|
||||||
if (userQuery > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks what roles the user has
|
|
||||||
const hasVegan = user.roles.cache.has(IDs.roles.vegan.vegan);
|
|
||||||
const hasActivist = user.roles.cache.has(IDs.roles.vegan.activist);
|
|
||||||
const hasPlus = user.roles.cache.has(IDs.roles.vegan.plus);
|
|
||||||
const hasNotVegan = user.roles.cache.has(IDs.roles.nonvegan.nonvegan);
|
|
||||||
const hasVegCurious = user.roles.cache.has(IDs.roles.nonvegan.vegCurious);
|
|
||||||
const hasConvinced = user.roles.cache.has(IDs.roles.nonvegan.convinced);
|
|
||||||
const hasTrusted = user.roles.cache.has(IDs.roles.trusted);
|
|
||||||
const hasMuted = user.roles.cache.has(IDs.roles.restrictions.muted);
|
|
||||||
|
|
||||||
// Create the user in the database
|
|
||||||
await prisma.user.create({
|
|
||||||
data: {
|
|
||||||
id: user.id,
|
|
||||||
vegan: hasVegan,
|
|
||||||
trusted: hasTrusted,
|
|
||||||
activist: hasActivist,
|
|
||||||
plus: hasPlus,
|
|
||||||
notVegan: hasNotVegan,
|
|
||||||
vegCurious: hasVegCurious,
|
|
||||||
convinced: hasConvinced,
|
|
||||||
muted: hasMuted,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the database connection
|
|
||||||
await prisma.$disconnect();
|
|
||||||
}
|
|
||||||
@ -60,15 +60,28 @@ const devIDs = {
|
|||||||
channels: {
|
channels: {
|
||||||
information: {
|
information: {
|
||||||
news: '999431676058927247',
|
news: '999431676058927247',
|
||||||
|
conduct: '999431676058927248',
|
||||||
|
roles: '999431676058927250',
|
||||||
},
|
},
|
||||||
staff: {
|
staff: {
|
||||||
coordinators: '999431676058927254',
|
coordinators: '999431676058927254',
|
||||||
standup: '999431676289622183',
|
standup: '999431676289622183',
|
||||||
verifiers: '999431677006860411',
|
verifiers: '999431677006860411',
|
||||||
},
|
},
|
||||||
|
dietSupport: {
|
||||||
|
info: '999431677006860417',
|
||||||
|
introduction: '999431677325615184',
|
||||||
|
main: '999431677325615185',
|
||||||
|
},
|
||||||
nonVegan: {
|
nonVegan: {
|
||||||
general: '999431677325615189',
|
general: '999431677325615189',
|
||||||
},
|
},
|
||||||
|
vegan: {
|
||||||
|
general: '999431677535338575',
|
||||||
|
},
|
||||||
|
activism: {
|
||||||
|
activism: '999431678214807604',
|
||||||
|
},
|
||||||
diversity: {
|
diversity: {
|
||||||
women: '999431679053660187',
|
women: '999431679053660187',
|
||||||
lgbtqia: '999431679053660188',
|
lgbtqia: '999431679053660188',
|
||||||
|
|||||||
34
src/utils/formatter.ts
Normal file
34
src/utils/formatter.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Snowflake } from 'discord-api-types/globals';
|
||||||
|
|
||||||
|
export function rolesToString(roles: Snowflake[]) {
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
roles.forEach((role) => {
|
||||||
|
output += `<@&${role}>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (output.length === 0) {
|
||||||
|
output = 'None';
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
@ -52,23 +52,39 @@ let IDs = {
|
|||||||
moderator: '826157475815489598',
|
moderator: '826157475815489598',
|
||||||
trialModerator: '982074555596152904',
|
trialModerator: '982074555596152904',
|
||||||
verifier: '871802735031373856',
|
verifier: '871802735031373856',
|
||||||
|
trialVerifier: '982635638010572850',
|
||||||
},
|
},
|
||||||
stageHost: '854893757593419786',
|
stageHost: '854893757593419786',
|
||||||
patron: '765370219207852055',
|
patron: '765370219207852055',
|
||||||
patreon: '993848684640997406',
|
patreon: '993848684640997406',
|
||||||
verifyingAsVegan: '854725899576279060',
|
verifyingAsVegan: '854725899576279060',
|
||||||
|
verifyBlock: '1032765019269640203',
|
||||||
},
|
},
|
||||||
channels: {
|
channels: {
|
||||||
information: {
|
information: {
|
||||||
news: '866000393259319306',
|
news: '866000393259319306',
|
||||||
|
conduct: '990728521531920385',
|
||||||
|
roles: '990761562199457813',
|
||||||
},
|
},
|
||||||
staff: {
|
staff: {
|
||||||
coordinators: '1006240682505142354',
|
coordinators: '1006240682505142354',
|
||||||
standup: '996009201237233684',
|
standup: '996009201237233684',
|
||||||
|
verifiers: '873215538627756072',
|
||||||
|
},
|
||||||
|
dietSupport: {
|
||||||
|
info: '993891104346873888',
|
||||||
|
introduction: '993272252743286874',
|
||||||
|
main: '822665615612837918',
|
||||||
},
|
},
|
||||||
nonVegan: {
|
nonVegan: {
|
||||||
general: '798967615636504657',
|
general: '798967615636504657',
|
||||||
},
|
},
|
||||||
|
vegan: {
|
||||||
|
general: '787738272616808509',
|
||||||
|
},
|
||||||
|
activism: {
|
||||||
|
activism: '730907954877956179',
|
||||||
|
},
|
||||||
diversity: {
|
diversity: {
|
||||||
women: '938808963544285324',
|
women: '938808963544285324',
|
||||||
lgbtqia: '956224226556272670',
|
lgbtqia: '956224226556272670',
|
||||||
@ -77,6 +93,7 @@ let IDs = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
|
verification: '797505409073676299',
|
||||||
diversity: '933078380394459146',
|
diversity: '933078380394459146',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
34
src/utils/mathsSeries.ts
Normal file
34
src/utils/mathsSeries.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Created because Stove loves Fibonacci sequences
|
||||||
|
// A fibonacci sequence where n = 0 => 1
|
||||||
|
export function fibonacci(position: number) {
|
||||||
|
let previous = 0;
|
||||||
|
let next = 1;
|
||||||
|
let tempNext;
|
||||||
|
|
||||||
|
for (let i = 0; i < position; i += 1) {
|
||||||
|
tempNext = next + previous;
|
||||||
|
previous = next;
|
||||||
|
next = tempNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
141
src/utils/verificationConfig.ts
Normal file
141
src/utils/verificationConfig.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2022 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The maximum amount of verification VCs there can be
|
||||||
|
export const maxVCs = 10;
|
||||||
|
|
||||||
|
// The maximum amount of leaving bans before time resets
|
||||||
|
export const leaveBan = 8;
|
||||||
|
|
||||||
|
export const questionInfo = [
|
||||||
|
{
|
||||||
|
question: 'Welcome to Animal Rights Advocates! How did you find the server?',
|
||||||
|
buttons: [
|
||||||
|
'Search',
|
||||||
|
'Friend',
|
||||||
|
'YouTube',
|
||||||
|
'Another Server',
|
||||||
|
'Vegan Org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'How long have you been vegan?',
|
||||||
|
buttons: [
|
||||||
|
'<1 month',
|
||||||
|
'1-2 months',
|
||||||
|
'3-6 months',
|
||||||
|
'6 months - 1 year',
|
||||||
|
'1-2 years',
|
||||||
|
'2+ years',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Ask the user why they went vegan and to define veganism.\n'
|
||||||
|
+ 'Do they cite ethical concerns and abstinence from at least meat, dairy, eggs, leather, and fur?',
|
||||||
|
buttons: [
|
||||||
|
'Yes',
|
||||||
|
'Yes with prompting',
|
||||||
|
'No',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Ask the user about their life as a vegan, including things like watching documentaries or social media content and interactions with family and friends. What are their stories like?',
|
||||||
|
buttons: [
|
||||||
|
'Believable',
|
||||||
|
'Unbelievable',
|
||||||
|
'Short',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Ask the user about food and nutrition. Do they seem to know how to live as a vegan?',
|
||||||
|
buttons: [
|
||||||
|
'Dietitian / Chef',
|
||||||
|
'Acceptable',
|
||||||
|
'Salads / Smoothies',
|
||||||
|
'No clue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Do you think this user is definitely vegan?',
|
||||||
|
buttons: [
|
||||||
|
'Yes',
|
||||||
|
'No',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Offer to ask questions for Activist. Do you think they should get it?',
|
||||||
|
buttons: [
|
||||||
|
'Yes',
|
||||||
|
'No',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Do some activism, asking Activist questions. Now which role should they get?',
|
||||||
|
buttons: [
|
||||||
|
'Vegan',
|
||||||
|
'Convinced',
|
||||||
|
'Non-vegan',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Should this user get Veg Curious?',
|
||||||
|
buttons: [
|
||||||
|
'Yes',
|
||||||
|
'No',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const serverFind = [
|
||||||
|
// From a friend
|
||||||
|
{
|
||||||
|
question: 'Ask for username and indicate',
|
||||||
|
buttons: [
|
||||||
|
'Vegan',
|
||||||
|
'Non-Vegan',
|
||||||
|
'Unknown',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// From a video
|
||||||
|
{
|
||||||
|
question: 'Ask what video',
|
||||||
|
buttons: [
|
||||||
|
'Troll video',
|
||||||
|
'Our content',
|
||||||
|
'Other',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// From another server
|
||||||
|
{
|
||||||
|
question: 'Ask which server',
|
||||||
|
buttons: [
|
||||||
|
'Vegan',
|
||||||
|
'Debate',
|
||||||
|
'Other',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// From a vegan organisation
|
||||||
|
{
|
||||||
|
question: 'Ask which one',
|
||||||
|
buttons: [
|
||||||
|
'Vegan Hacktivists',
|
||||||
|
'Other',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -12,7 +12,7 @@
|
|||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
"target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
@ -101,7 +101,7 @@
|
|||||||
|
|
||||||
/* Completeness */
|
/* Completeness */
|
||||||
// "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"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user