From 0bd9960c01c4eba0228f870abc4135f33d4f1262 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Sat, 6 Sep 2025 16:53:33 +0200 Subject: [PATCH] 0.2.0 --- CHANGELOG.md | 11 ++++++ package-lock.json | 11 +++++- package.json | 3 +- src/bot.ts | 85 ++++++++++++++++++++++++++++++++++++----- src/client.ts | 10 +++++ src/commands/version.ts | 2 +- src/configFile.ts | 53 +++++++++++++++++++++++++ 7 files changed, 161 insertions(+), 14 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 src/configFile.ts diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..65b98a6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ + +## 0.2.0 + +- Moved all configuration to `config/config.toml`, contains: + + join gate + + guild aliases +- Now you can see what guilds is your bot in + +## 0.1.0 + +- Initial commit \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d0c6978..4863dcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csinsol", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "csinsol", - "version": "0.1.0", + "version": "0.2.0", "license": "Apache-2.0", "dependencies": { "@types/node": "^24.3.0", @@ -14,6 +14,7 @@ "discord.js": "^14.21.0", "dotenv": "^17.2.1", "luxon": "^3.7.1", + "toml": "^3.0.0", "tsx": "^4.20.4" } }, @@ -790,6 +791,12 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", diff --git a/package.json b/package.json index ced225f..973aa5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csinsol", - "version": "0.1.0", + "version": "0.2.0", "description": "", "license": "Apache-2.0", "author": "Sakuragasaki46", @@ -17,6 +17,7 @@ "discord.js": "^14.21.0", "dotenv": "^17.2.1", "luxon": "^3.7.1", + "toml": "^3.0.0", "tsx": "^4.20.4" } } diff --git a/src/bot.ts b/src/bot.ts index 00f94b9..d78755a 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,6 +1,6 @@ -import { ChatInputCommandInteraction, Events, GatewayIntentBits, GuildMember, Interaction } from 'discord.js'; +import { ChatInputCommandInteraction, Events, GatewayIntentBits, Guild, GuildMember, Interaction } from 'discord.js'; import { MyClient } from './client'; import commands from './commands'; import chalk from 'chalk'; @@ -13,6 +13,8 @@ for (let command of commands) { client.addCommand(command); } +const guildDetail = !!process.env.GUILD_DETAIL_SHOW; + client.on(Events.InteractionCreate, async (interaction: Interaction) => { if (interaction instanceof ChatInputCommandInteraction) { const { commandName } = interaction; @@ -40,16 +42,75 @@ client.on(Events.GuildMemberAdd, async (member: GuildMember) => { const { joinedAt } = member; if (joinedAt === null) return; + const rules = client.config.getRules(member.guild.id) || []; const hours = (joinedAt.getUTCHours() * 60 + joinedAt.getUTCMinutes() + 120) % 1440; - if (hours < 540) { - console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} kicked: Join at night not allowed`); - await member.kick(); - return; - } - if (joinedAt.getTime() - createdAt.getTime() < 10800000) { - console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} kicked: Account too young`); - await member.kick(); - return; + const age = Math.floor((joinedAt.getTime() - createdAt.getTime()) / 10800); + const ops: Record boolean> = { + "age <": x => age < x, + "age >=": x => age >= x, + "now <": x => hours < x, + "now >=": x => hours >= x + }; + for (let rule of rules.filter(x => x.type === 'join')) { + let { action, where, message = void 0, private_message = null } = rule; + let privMessage = private_message || message; + + let result: boolean; + try { + let matches = where.match(/^([a-z]+) *([<=>]+) *([0-9]+) *$/); + if (!matches) continue; + let op = `${matches[1]} ${matches[2]}`; + let opf = ops[op]; + if (!opf) continue; + let rval = parseInt(matches[3]); + result = opf(rval); + } catch(ex) { + console.warn(`malformed rule skipped: '${rule}'`) + continue; + } + + if (result) { + const actionMessage = ({ + kick: 'kicked', ban:'banned', + '24h': 'muted for 24 hours', '3d': 'muted for 3 days','7d': 'muted for 7 days', + })[action]; + + // apply the rule + if (message) try { + // try messaging the user + + await member.user.send({ + content: `Sei statÉ™ espulsÉ™ da **${member.guild.name}**: ${message}` + }); + } catch(ex) { } + + try { + switch(action) { + case 'kick': + await member.kick(message); + break; + case 'ban': + await member.ban({ reason: message, deleteMessageSeconds: 0 }); + break; + case '24h': + await member.timeout(24 * 3600000, message); + break; + case '3d': + await member.timeout(3 * 24 * 3600000, message); + break; + case '7d': + await member.timeout(7 * 24 * 3600000, message); + break; + default: + console.warn(`unknown action: ${action}`); + break; + } + + console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} ${actionMessage}: ${privMessage}`); + } catch (ex) { + console.error(chalk.red(`Member: ${chalk.white(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} not ${actionMessage}: ${ex}\x1b[0m`)); + } + } } console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} accepted`); @@ -58,5 +119,9 @@ client.on(Events.GuildMemberAdd, async (member: GuildMember) => { client.once(Events.ClientReady, (c) => { console.log(`Logged in as ${c.user.tag}`); + console.log(`Currently in ${chalk.bold(c.guilds.cache.size)} guilds` + (guildDetail? ':' : `; rerun with GUILD_DETAIL_SHOW=1 for details`)); + if (guildDetail) { + console.log(c.guilds.cache.map((x: Guild) => `* ${chalk.bold(x.name)} ${chalk.grey('(ID: ')}${chalk.green(x.id)}${chalk.grey(`, ${x.memberCount} members)`)}`).join('\n')); + } }); diff --git a/src/client.ts b/src/client.ts index 7e01486..6aab6c8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,17 +1,27 @@ import { Client, ClientOptions, Collection } from "discord.js"; import { CommandInfo } from "./commands"; +import { ConfigFile } from "./configFile"; export class MyClient extends Client { commands: Collection; + config: ConfigFile; constructor (opts: ClientOptions) { super(opts); this.commands = new Collection(); + this.config = new ConfigFile(); + } + + override async login(token?: string): Promise { + await this.config.load(); + return super.login(token); } addCommand (command: CommandInfo) { this.commands.set(command.data.name, command); } + + } \ No newline at end of file diff --git a/src/commands/version.ts b/src/commands/version.ts index eff0fd9..0f67778 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -7,7 +7,7 @@ const nodejsVersion = process.versions.node; const data = new SlashCommandBuilder() .setName('version') - .setDescription('Informaz. sul bot'); + .setDescription('Informazioni sul bot'); async function execute (interaction: ChatInputCommandInteraction) { await interaction.deferReply(); diff --git a/src/configFile.ts b/src/configFile.ts new file mode 100644 index 0000000..64d9183 --- /dev/null +++ b/src/configFile.ts @@ -0,0 +1,53 @@ + +import fs from "node:fs/promises"; +import toml from "toml"; + +type ConfigGuildRule = { + type: "join", + action: "kick" | "ban" | "24h" | "3d" | "7d", + where: string, + message?: string, + private_message?: string +}; + +export class ConfigFile{ + guilds?: { + aliases?: Record, + rules?: Record + } + globals?: { + timezone?: string + } + + constructor() { + + } + + async load () { + let content = await fs.readFile("config/config.toml", "utf-8"); + + let data = toml.parse(content); + + this.globals = data.globals; + this.guilds = data.guilds; + } + + getRules (guildId: string) : ConfigGuildRule[] | null { + if(!this?.guilds?.rules) { + return null; + } + + let aliases = this?.guilds?.aliases || {}; + for(let key in this.guilds.rules) { + if (key.startsWith('@')) { + key = aliases[key.slice(1)]; + if (!key) continue; + } + if (key === guildId) { + return this.guilds.rules[key]; + } + } + return null; + } +}; +