(rewrite): Bot & Events. This most definitely doesn't work yet. I'm just running out of time to work on this atm.
This commit is contained in:
@@ -1 +1,2 @@
|
||||
registry=https://npm.hep.gg
|
||||
@clynamic:registry=https://npm.clynamic.net
|
||||
|
||||
Generated
+88
-5
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "discordbot-ng",
|
||||
"name": "hexerade",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "discordbot-ng",
|
||||
"dependencies": {
|
||||
"@clynamic/dmark": "0.0.11",
|
||||
"@redis/client": "5.1.0",
|
||||
"athena-prime": "4.2.0",
|
||||
"body-parser": "2.2.2",
|
||||
"discord.js": "14.26.4",
|
||||
"dotenv": "16.5.0",
|
||||
"express": "5.1.0",
|
||||
"express-session": "1.19.0",
|
||||
"ioredis": "6.0.0",
|
||||
"markdown-it": "14.2.0",
|
||||
"memorystore": "1.6.7",
|
||||
"sqlite": "5.1.1",
|
||||
@@ -824,6 +825,12 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://npm.hep.gg/@ioredis/commands/-/commands-2.0.0.tgz",
|
||||
"integrity": "sha512-vrx0AE/T0h7cRZwfo1M39Cr+ZhZrkf0V8mQN75wucKCxCLD9l/VX6no3gFvrLqD1IlG/1LtzWovqEw3t0Vr9zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -1394,6 +1401,22 @@
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/athena-prime": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://npm.hep.gg/athena-prime/-/athena-prime-4.2.0.tgz",
|
||||
"integrity": "sha512-CbIPwdupVGfGRR/ZRcc9BAC/nBnRQS9sRalzWtONYQ4eNhCDJVykPLQM2N7abndvcpLctWd1cD3awpA9NDy30Q==",
|
||||
"license": "NOLICENSE",
|
||||
"dependencies": {
|
||||
"discord-api-types": "^0.38.48",
|
||||
"eventemitter3": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.15.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"ws": "^8.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1821,6 +1844,15 @@
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://npm.hep.gg/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@@ -1838,9 +1870,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.38.42",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.42.tgz",
|
||||
"integrity": "sha512-qs1kya7S84r5RR8m9kgttywGrmmoHaRifU1askAoi+wkoSefLpZP6aGXusjNw5b0jD3zOg3LTwUa3Tf2iHIceQ==",
|
||||
"version": "0.38.53",
|
||||
"resolved": "https://npm.hep.gg/discord-api-types/-/discord-api-types-0.38.53.tgz",
|
||||
"integrity": "sha512-HL1zz/UuZ+bbJjA/X8Kbxx9gk8v9rJAbTeWRNYKmIdjwJ7EovjlHgoJTxcLpATfNJ+AonOtMdy3Y5MVIJAAd/A==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"scripts/actions/documentation"
|
||||
@@ -2203,6 +2235,12 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://npm.hep.gg/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
@@ -2761,6 +2799,36 @@
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://npm.hep.gg/ioredis/-/ioredis-6.0.0.tgz",
|
||||
"integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "2.0.0",
|
||||
"cluster-key-slot": "1.1.1",
|
||||
"debug": "4.4.3",
|
||||
"denque": "2.1.0",
|
||||
"redis-errors": "1.2.0",
|
||||
"standard-as-callback": "2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis/node_modules/cluster-key-slot": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://npm.hep.gg/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
|
||||
"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@@ -3679,6 +3747,15 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://npm.hep.gg/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
@@ -4124,6 +4201,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://npm.hep.gg/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.32.1"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"@clynamic/dmark": "0.0.11",
|
||||
"@redis/client": "5.1.0",
|
||||
"ioredis": "6.0.0",
|
||||
"athena-prime": "4.2.0",
|
||||
"body-parser": "2.2.2",
|
||||
"discord.js": "14.26.4",
|
||||
"dotenv": "16.5.0",
|
||||
@@ -24,6 +27,7 @@
|
||||
"sqlite": "5.1.1",
|
||||
"sqlite3": "6.0.1"
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"dev": "tsx watch --enable-source-maps src/index.ts",
|
||||
"start": "node dist/index.js",
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// External Imports
|
||||
import { CommandClient, Constants, LRUCollection, Member, User } from "athena-prime";
|
||||
|
||||
// Internal Imports
|
||||
import config from "./config";
|
||||
|
||||
// ---------------
|
||||
|
||||
const client = new CommandClient({
|
||||
token: `Bot ${config.DISCORD_TOKEN}`,
|
||||
|
||||
options: {
|
||||
intents: [
|
||||
Constants.GatewayIntentBits.Guilds,
|
||||
Constants.GatewayIntentBits.GuildMembers,
|
||||
Constants.GatewayIntentBits.GuildMessages,
|
||||
Constants.GatewayIntentBits.GuildModeration,
|
||||
Constants.GatewayIntentBits.GuildVoiceStates,
|
||||
Constants.GatewayIntentBits.MessageContent,
|
||||
],
|
||||
|
||||
largeBotOptimizations: true,
|
||||
|
||||
// TODO: Move caching to Redis.
|
||||
cache: {
|
||||
users: () => new LRUCollection(User, 1024),
|
||||
members: (guild) => new LRUCollection(Member, 1024),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default client;
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
.addComponents(button);
|
||||
|
||||
await interaction.channel.send({ components: [row], content });
|
||||
await Database.updateGuildSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
|
||||
await Database.updateSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
|
||||
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
|
||||
}
|
||||
};
|
||||
|
||||
+14
-14
@@ -139,75 +139,75 @@ export default {
|
||||
|
||||
const generalChannel = interaction.options.getChannel('general-channel');
|
||||
if (generalChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
|
||||
response.push(`**general_chat_id** has been set to: ${generalChannel}.`);
|
||||
}
|
||||
|
||||
const ticketsChannel = interaction.options.getChannel('tickets-channel');
|
||||
if (ticketsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
|
||||
response.push(`**tickets_channel_id** has been set to: ${ticketsChannel}.`);
|
||||
}
|
||||
|
||||
const appealsChannel = interaction.options.getChannel('appeals-channel');
|
||||
|
||||
if (appealsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
|
||||
|
||||
response.push(`**appeals_channel_id** has been set to ${appealsChannel}.`);
|
||||
}
|
||||
|
||||
const eventLogsChannel = interaction.options.getChannel('event-logs-channel');
|
||||
if (eventLogsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
|
||||
response.push(`**event_logs_channel_id** has been set to: ${eventLogsChannel}.`);
|
||||
}
|
||||
|
||||
const discordLogsChannel = interaction.options.getChannel('discord-logs-channel');
|
||||
if (discordLogsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
|
||||
response.push(`**discord_logs_channel_id** has been set to: ${discordLogsChannel}.`);
|
||||
}
|
||||
|
||||
const auditLogsChannel = interaction.options.getChannel('audit-logs-channel');
|
||||
if (auditLogsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
|
||||
response.push(`**audit_logs_channel_id** has been set to: ${auditLogsChannel}.`);
|
||||
}
|
||||
|
||||
const voiceLogsChannel = interaction.options.getChannel('voice-logs-channel');
|
||||
if (voiceLogsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
|
||||
response.push(`**voice_logs_channel_id** has been set to: ${voiceLogsChannel}.`);
|
||||
}
|
||||
|
||||
const newMemberLogsChannel = interaction.options.getChannel('new-member-channel');
|
||||
if (newMemberLogsChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
|
||||
response.push(`**new_member_channel_id** has been set to: ${newMemberLogsChannel}.`);
|
||||
}
|
||||
|
||||
const moderatorChannel = interaction.options.getChannel('moderator-channel');
|
||||
if (moderatorChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
|
||||
response.push(`**moderator_channel_id** has been set to": ${moderatorChannel}.`);
|
||||
}
|
||||
|
||||
const adminRole = interaction.options.getRole('admin-role');
|
||||
if (adminRole) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'admin_role_id', adminRole.id);
|
||||
await Database.updateSettings(interaction.guildId, 'admin_role_id', adminRole.id);
|
||||
response.push(`**admin_role_id** has been set to: ${adminRole}.`);
|
||||
}
|
||||
|
||||
const privateHelperRole = interaction.options.getRole('private-helper-role');
|
||||
if (privateHelperRole) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
|
||||
await Database.updateSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
|
||||
response.push(`**private_help_role_id** has been set to: ${privateHelperRole}.`);
|
||||
}
|
||||
|
||||
const devWatchRole = interaction.options.getRole('devwatch-role');
|
||||
if (devWatchRole) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
|
||||
await Database.updateSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
|
||||
response.push(`**devwatch_role_id** has been set to ${devWatchRole}.`);
|
||||
}
|
||||
|
||||
@@ -264,13 +264,13 @@ export default {
|
||||
|
||||
const githubReleaseChannel = interaction.options.getChannel('github-release-channel');
|
||||
if (githubReleaseChannel) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
|
||||
await Database.updateSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
|
||||
response.push(`**github_release_channel** has been set to ${githubReleaseChannel}.`);
|
||||
}
|
||||
|
||||
const siteBreakerRole = interaction.options.getRole('site-breaker-role');
|
||||
if (siteBreakerRole) {
|
||||
await Database.updateGuildSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
|
||||
await Database.updateSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
|
||||
response.push(`**site_breaker_role_id** has been set to ${siteBreakerRole}.`);
|
||||
}
|
||||
|
||||
|
||||
+4
-9
@@ -1,10 +1,11 @@
|
||||
import dotenv from 'dotenv';
|
||||
// External Imports
|
||||
import 'dotenv/config';
|
||||
|
||||
dotenv.config();
|
||||
// ---------------
|
||||
|
||||
const { DISCORD_TOKEN, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_GUILD_ID, RELEASE_SECRET, DATABASE_SECRET, LINK_SECRET, E621_BASE_URL, E926_BASE_URL, PRIVACY_POLICY_URL, GIT_REPO_BASE_URL, REDIS_URL, PORT, DEBUG } = process.env;
|
||||
|
||||
export const config = {
|
||||
export default {
|
||||
DISCORD_TOKEN,
|
||||
DISCORD_CLIENT_ID,
|
||||
DISCORD_CLIENT_SECRET,
|
||||
@@ -21,9 +22,3 @@ export const config = {
|
||||
DEV_MODE: process.env.npm_lifecycle_event == 'dev',
|
||||
DEBUG: DEBUG == 'true'
|
||||
};
|
||||
|
||||
for (const [key, val] of Object.entries(config)) {
|
||||
if (val === undefined) {
|
||||
throw new Error(`${key} is undefined in config`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// External Imports
|
||||
import { CommandClient, Event, Guild, User } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
// ---------------
|
||||
|
||||
class GuildBanRemoveEvent extends Event<CommandClient> {
|
||||
event: string = 'guildBanRemove' as const;
|
||||
|
||||
async handle(context: CommandClient<any, any>, guild: Guild, user: User) {
|
||||
await Database.deleteBan(user.id);
|
||||
}
|
||||
}
|
||||
|
||||
export default new GuildBanRemoveEvent('guildBanRemove');
|
||||
@@ -0,0 +1,15 @@
|
||||
// External Imports
|
||||
import { CommandClient, Event, Guild } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
class GuildCreateEvent extends Event<CommandClient> {
|
||||
event: string = 'guildCreate' as const;
|
||||
|
||||
async handle(context: CommandClient<any, any>, guild: Guild) {
|
||||
await Database.GetOrCreateSettings(guild.id);
|
||||
}
|
||||
}
|
||||
|
||||
export default new GuildCreateEvent('guildCreate');
|
||||
@@ -0,0 +1,24 @@
|
||||
// External Imports
|
||||
import { CommandClient, Event, Guild, Member } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Database } from '../shared/Database';
|
||||
import { getE621Alts } from '../utils';
|
||||
|
||||
// ---------------
|
||||
|
||||
class GuildMemberAddEvent extends Event<CommandClient> {
|
||||
event: string = 'guildMemberAdd' as const;
|
||||
|
||||
async handle(context: CommandClient<any, any>, guild: Guild, member: Member) {
|
||||
const settings = await Database.GetOrCreateSettings(guild.id);
|
||||
|
||||
// TODO: Seperate this into its own function and create a handleWelcomeMessage() function.
|
||||
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
||||
|
||||
if (!settings.moderator_channel_id || !content.includes('[BANNED]')) return;
|
||||
context.createMessage(settings.moderator_channel_id, content);
|
||||
}
|
||||
}
|
||||
|
||||
export default new GuildMemberAddEvent('guildMemberAdd');
|
||||
@@ -1,6 +0,0 @@
|
||||
import { GuildBan } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleBanRemove(ban: GuildBan) {
|
||||
await Database.removeBan(ban.user.id);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Guild } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleGuildCreate(guild: Guild) {
|
||||
try {
|
||||
if (!await Database.getGuildSettings(guild.id)) await Database.putGuild(guild.id);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { GuildMember, GuildTextBasedChannel } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { getE621Alts } from '../utils';
|
||||
|
||||
export async function handleMemberJoin(member: GuildMember) {
|
||||
const guildSettings = await Database.getGuildSettings(member.guild.id);
|
||||
|
||||
if (guildSettings?.new_member_channel_id) {
|
||||
const channel = await member.guild.channels.fetch(guildSettings.new_member_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (channel) {
|
||||
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
||||
|
||||
channel.send(content).catch(console.error);
|
||||
|
||||
if (guildSettings.moderator_channel_id && content.includes('[BANNED]')) {
|
||||
const modChannel = await member.guild.channels.fetch(guildSettings.moderator_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (modChannel) modChannel.send(`Member joined with banned alts:\n${content}`).catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -1,7 +1,9 @@
|
||||
export * from './handle-audit-log-create';
|
||||
export * from './handle-ban-remove';
|
||||
export * from './handle-guild-create';
|
||||
export * from './handle-member-join';
|
||||
import guildBanRemove from './guildBanRemove';
|
||||
import guildCreate from './guildCreate';
|
||||
import guildMemberAdd from './guildMemberAdd';
|
||||
export * from './handle-message';
|
||||
export * from './handle-thread-create';
|
||||
export * from './handle-voice-state-update';
|
||||
|
||||
export default [guildBanRemove, guildCreate, guildMemberAdd];
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Client } from 'discord.js';
|
||||
// External Imports
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Task } from '../types';
|
||||
import ScheduledTasks from './tasks';
|
||||
|
||||
class Scheduler {
|
||||
private tasks = new Map<Task, NodeJS.Timeout>();
|
||||
private context: Client;
|
||||
private context: CommandClient;
|
||||
|
||||
constructor(context: Client) {
|
||||
constructor(context: CommandClient) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@@ -18,6 +21,4 @@ class Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ScheduledTasks, Scheduler
|
||||
};
|
||||
export { Scheduler, ScheduledTasks }
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Client } from 'discord.js';
|
||||
// External Imports
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Task } from '../../types';
|
||||
import { checkExpiredBans } from '../../utils';
|
||||
|
||||
// ---------------
|
||||
|
||||
class CheckExpiredBansTask implements Task {
|
||||
interval: number = 300000;
|
||||
firstRun: boolean = true;
|
||||
|
||||
async handle(context: Client): Promise<void> {
|
||||
async handle(context: CommandClient) {
|
||||
await checkExpiredBans(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Client } from 'discord.js';
|
||||
// External Imports
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Task } from '../../types';
|
||||
import { closeOldTickets } from '../../utils';
|
||||
|
||||
// ---------------
|
||||
|
||||
class CloseStaleTicketsTask implements Task {
|
||||
interval: number = 3.6e6;
|
||||
firstRun: boolean = true;
|
||||
|
||||
async handle(context: Client): Promise<void> {
|
||||
async handle(context: CommandClient) {
|
||||
await closeOldTickets(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// External Imports
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { Task } from '../../types';
|
||||
import { Database } from '../../shared/Database';
|
||||
|
||||
@@ -5,7 +9,7 @@ class PruneOldMessagesTask implements Task {
|
||||
interval: number = 3.6e6;
|
||||
firstRun: boolean = true;
|
||||
|
||||
async handle(): Promise<void> {
|
||||
async handle(context: CommandClient) {
|
||||
await Database.pruneOldMessages();
|
||||
}
|
||||
}
|
||||
|
||||
+15
-12
@@ -2,7 +2,7 @@ import path from 'path';
|
||||
import { open, Database as SqliteDatabase } from 'sqlite';
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { Message } from '../events';
|
||||
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
|
||||
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
|
||||
import { deserializeMessage, serializeMessage, wait } from '../utils';
|
||||
import { readFileSync } from 'fs';
|
||||
import { Encrypter } from './Encrypter';
|
||||
@@ -13,7 +13,7 @@ export const enum PrivateHelpTicketStatus {
|
||||
}
|
||||
|
||||
export class Database {
|
||||
private static db: SqliteDatabase;
|
||||
static db: SqliteDatabase;
|
||||
|
||||
static async open(file: string): Promise<void> {
|
||||
if (Database.db) return;
|
||||
@@ -28,7 +28,6 @@ export class Database {
|
||||
console.log('SQLite database opened');
|
||||
|
||||
await Database.ensure();
|
||||
|
||||
await Database.migrate();
|
||||
}
|
||||
|
||||
@@ -75,15 +74,19 @@ export class Database {
|
||||
|
||||
//#region Guild Settings
|
||||
|
||||
static async getGuildSettings(guildId: string): Promise<GuildSettings> {
|
||||
return await Database.db.get<GuildSettings>('SELECT * FROM settings WHERE guild_id = ?', guildId) as GuildSettings;
|
||||
}
|
||||
static async GetOrCreateSettings(guildId: string): Promise<GuildSettings> {
|
||||
await Database.db.run(
|
||||
'INSERT OR IGNORE INTO settings (guild_id) VALUES (?)',
|
||||
guildId
|
||||
);
|
||||
|
||||
static async putGuild(guildId: string) {
|
||||
await Database.db.run('INSERT INTO settings(guild_id) VALUES (?)', guildId);
|
||||
}
|
||||
return await Database.db.get<GuildSettings>(
|
||||
'SELECT * FROM settings WHERE guild_id = ?',
|
||||
guildId
|
||||
) as GuildSettings;
|
||||
}
|
||||
|
||||
static async updateGuildSettings(guildId: string, key: GuildSetting, value: string) {
|
||||
static async updateSettings(guildId: string, key: Exclude<keyof GuildSettings, 'guild_id'>, value: string) {
|
||||
await Database.db.run(`UPDATE settings SET ${key} = ? WHERE guild_id = ?`, value, guildId);
|
||||
}
|
||||
|
||||
@@ -332,10 +335,10 @@ export class Database {
|
||||
}
|
||||
|
||||
static async pruneExpiredBans(date: Date) {
|
||||
await Database.db.all<Ban[]>('DELETE from bans WHERE expires = 1 AND expires_at <= ?', date);
|
||||
|
||||
}
|
||||
|
||||
static async removeBan(userId: string) {
|
||||
static async deleteBan(userId: string) {
|
||||
await Database.db.run('DELETE from bans WHERE user_id_hash = ?', Encrypter.hash(userId));
|
||||
}
|
||||
|
||||
|
||||
Vendored
-1
@@ -33,7 +33,6 @@ export type GuildSettings = {
|
||||
site_breaker_role_id?: string
|
||||
}
|
||||
|
||||
type GuildSetting = Exclude<keyof GuildSettings, 'guild_id'>
|
||||
export type GuildArraySetting = 'staff_categories' | 'safe_channels' | 'link_skip_channels';
|
||||
|
||||
export type TicketMessage = {
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { Client } from 'discord.js';
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
export interface Task {
|
||||
interval: number;
|
||||
firstRun: boolean;
|
||||
|
||||
handle(context: Client): Promise<void>;
|
||||
handle(context: CommandClient): Promise<void>;
|
||||
};
|
||||
|
||||
+10
-18
@@ -1,7 +1,10 @@
|
||||
import { Guild } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
// External Imports
|
||||
import { Guild } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import { userIsBanned } from './e621-utils';
|
||||
import { config } from '../config';
|
||||
import config from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export type AltData = {
|
||||
type: 'e621' | 'discord'
|
||||
@@ -13,7 +16,6 @@ export type AltData = {
|
||||
|
||||
export async function getE621Alts(discordId: string, guild: Guild, depth = 1, ignore: number[] = []): Promise<string> {
|
||||
const e621UserIds = await Database.getE621Ids(discordId);
|
||||
|
||||
const toIgnore = ignore.concat(e621UserIds);
|
||||
|
||||
let content = '';
|
||||
@@ -22,7 +24,6 @@ export async function getE621Alts(discordId: string, guild: Guild, depth = 1, ig
|
||||
if (ignore.includes(e621Id)) continue;
|
||||
|
||||
const alts = await getDiscordAlts(e621Id, guild, depth + 1, toIgnore);
|
||||
|
||||
const banned = await userIsBanned(e621Id);
|
||||
|
||||
content += `${' '.repeat((depth - 1) * 2)}- ${config.E621_BASE_URL}/users/${e621Id}${banned ? ' [BANNED]' : ''}\n${alts}`;
|
||||
@@ -38,13 +39,10 @@ export async function getDiscordAlts(e621Id: number, guild: Guild, depth = 1, ig
|
||||
|
||||
for (const discordId of discordIds) {
|
||||
const alts = await getE621Alts(discordId, guild, depth + 1, ignore);
|
||||
|
||||
let banned = false;
|
||||
|
||||
// It's either this or fetch all the bans and sift through them for every discord alt.
|
||||
try {
|
||||
banned = !!(await guild.bans.fetch(discordId));
|
||||
} catch (e) { }
|
||||
banned = !!(await guild.getBan(discordId));
|
||||
|
||||
content += `${' '.repeat((depth - 1) * 2)}- <@${discordId}> (${discordId})${banned ? ' [BANNED]' : ''}\n${alts}`;
|
||||
}
|
||||
@@ -69,14 +67,13 @@ async function getE621AltData(discordId: string, guild: Guild | null, depth = 1,
|
||||
|
||||
// It's either this or fetch all the bans and sift through them for every discord alt.
|
||||
try {
|
||||
banned = guild ? !!(await guild.bans.fetch(discordId)) : false;
|
||||
banned = guild ? !!(await guild.getBan(discordId)) : false;
|
||||
} catch (e) { }
|
||||
|
||||
const data: AltData = { type: 'discord', thisId: discordId, banned, alts: [] };
|
||||
|
||||
for (const e621Id of e621UserIds) {
|
||||
if (ignore.includes(e621Id)) continue;
|
||||
|
||||
data.alts.push(await getDiscordAltData(e621Id, guild, depth + 1, toIgnore));
|
||||
}
|
||||
|
||||
@@ -85,12 +82,9 @@ async function getE621AltData(discordId: string, guild: Guild | null, depth = 1,
|
||||
|
||||
async function getDiscordAltData(e621Id: number, guild: Guild | null, depth = 1, ignore: number[] = []): Promise<AltData> {
|
||||
const discordIds = await Database.getDiscordIds(e621Id);
|
||||
|
||||
const data: AltData = { type: 'e621', thisId: e621Id, banned: await userIsBanned(e621Id), alts: [] };
|
||||
|
||||
for (const discordId of discordIds) {
|
||||
data.alts.push(await getE621AltData(discordId, guild, depth + 1, ignore));
|
||||
}
|
||||
for (const discordId of discordIds) data.alts.push(await getE621AltData(discordId, guild, depth + 1, ignore));
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -98,9 +92,7 @@ async function getDiscordAltData(e621Id: number, guild: Guild | null, depth = 1,
|
||||
export function e621IdsFromAltData(altData: AltData, data: number[] = []) {
|
||||
if (altData.type == 'e621' && !data.includes(altData.thisId as number)) data.push(altData.thisId as number);
|
||||
|
||||
for (const alt of altData.alts) {
|
||||
e621IdsFromAltData(alt, data);
|
||||
}
|
||||
for (const alt of altData.alts) e621IdsFromAltData(alt, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
+11
-11
@@ -1,22 +1,22 @@
|
||||
import { Client } from 'discord.js';
|
||||
// External Imports
|
||||
import { CommandClient } from 'athena-prime';
|
||||
|
||||
// Internal Imports
|
||||
import config from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
import { config } from '../config';
|
||||
import { Ban } from '../types';
|
||||
|
||||
export async function checkExpiredBans(client: Client) {
|
||||
const guild = await client.guilds.fetch(config.DISCORD_GUILD_ID!);
|
||||
// ---------------
|
||||
|
||||
export async function checkExpiredBans(client: CommandClient) {
|
||||
const guild = client.guilds.get(config.DISCORD_GUILD_ID!);
|
||||
if (!guild) return;
|
||||
|
||||
const date = new Date();
|
||||
|
||||
for (const ban of await Database.getExpiredBans(date)) {
|
||||
try {
|
||||
await guild.bans.remove(ban.user_id);
|
||||
} catch (e) {
|
||||
console.error(`Error unbanning user: ${ban.user_id}`);
|
||||
console.error(e);
|
||||
}
|
||||
await guild.unbanMember(ban.user_id, 'Ban Expired.');
|
||||
}
|
||||
|
||||
await Database.pruneExpiredBans(date);
|
||||
await Database.db.all<Ban[]>('DELETE from bans WHERE expires = 1 AND expires_at <= ?', date);
|
||||
}
|
||||
@@ -1,18 +1,15 @@
|
||||
import { APIEmbed, Client } from 'discord.js';
|
||||
import { CommandClient, Constants } from 'athena-prime';
|
||||
|
||||
type EmbedSeverity =
|
||||
| 'default'
|
||||
| 'info'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'success'
|
||||
type Severity = 'info' | 'warning' | 'error' | 'success';
|
||||
|
||||
export function CreateDefaultEmbed(context: Client): APIEmbed {
|
||||
export function CreateDefaultEmbed(context: CommandClient): Constants.APIEmbed {
|
||||
if (!context.user) return {};
|
||||
|
||||
return {
|
||||
color: 0x014995,
|
||||
|
||||
footer: {
|
||||
icon_url: context.user.avatarURL()!,
|
||||
icon_url: context.user.avatarURL,
|
||||
text: context.user.username
|
||||
},
|
||||
|
||||
@@ -20,9 +17,8 @@ export function CreateDefaultEmbed(context: Client): APIEmbed {
|
||||
};
|
||||
}
|
||||
|
||||
export function SetSeverity(severity: EmbedSeverity): APIEmbed {
|
||||
export function SetSeverity(severity: Severity): Constants.APIEmbed {
|
||||
switch (severity) {
|
||||
case 'default': return { color: 0x014995 };
|
||||
case 'info': return { color: 0x5865F2 };
|
||||
case 'warning': return { color: 0xFEE75C };
|
||||
case 'error': return { color: 0xED4245 };
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, ThreadChannel, UserContextMenuCommandInteraction } from 'discord.js';
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, UserContextMenuCommandInteraction } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { createTextInput, createYesNoMenu } from './modal-utils';
|
||||
|
||||
export async function closeOldTickets(client: Client) {
|
||||
import { CommandClient, ThreadChannel } from 'athena-prime';
|
||||
|
||||
// TODO: Handle Modmail Errors.
|
||||
export async function closeOldTickets(client: CommandClient) {
|
||||
for (const ticket of await Database.getAllOpenPrivateHelpTickets()) {
|
||||
try {
|
||||
const thread = await client.channels.fetch(ticket.thread_id) as ThreadChannel;
|
||||
const latestMessage = (await thread.messages.fetch({ limit: 1 })).at(0);
|
||||
if (latestMessage && latestMessage.createdTimestamp <= Date.now() - 432e6) {
|
||||
const thread = client.getChannel(ticket.thread_id) as ThreadChannel;
|
||||
const latestMessage = (await (thread.getMessages({ limit: 1 }))).at(0);
|
||||
|
||||
if (latestMessage && latestMessage.timestamp <= Date.now() - 432e6) {
|
||||
await Database.closePrivateHelpTicket(thread.id);
|
||||
|
||||
await thread.send('This ticket has been closed due to inactivity.');
|
||||
await thread.createMessage('This ticket has been closed due to inactivity.');
|
||||
|
||||
thread.edit({
|
||||
archived: true,
|
||||
locked: true
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error closing ticket due to inactivity:');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user