This commit is contained in:
Yusur 2025-09-06 16:53:33 +02:00
parent 1a4339eb9b
commit 0bd9960c01
7 changed files with 161 additions and 14 deletions

11
CHANGELOG.md Normal file
View file

@ -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

11
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "csinsol", "name": "csinsol",
"version": "0.1.0", "version": "0.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "csinsol", "name": "csinsol",
"version": "0.1.0", "version": "0.2.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@types/node": "^24.3.0", "@types/node": "^24.3.0",
@ -14,6 +14,7 @@
"discord.js": "^14.21.0", "discord.js": "^14.21.0",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"luxon": "^3.7.1", "luxon": "^3.7.1",
"toml": "^3.0.0",
"tsx": "^4.20.4" "tsx": "^4.20.4"
} }
}, },
@ -790,6 +791,12 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" "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": { "node_modules/ts-mixer": {
"version": "6.0.4", "version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "csinsol", "name": "csinsol",
"version": "0.1.0", "version": "0.2.0",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"author": "Sakuragasaki46", "author": "Sakuragasaki46",
@ -17,6 +17,7 @@
"discord.js": "^14.21.0", "discord.js": "^14.21.0",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"luxon": "^3.7.1", "luxon": "^3.7.1",
"toml": "^3.0.0",
"tsx": "^4.20.4" "tsx": "^4.20.4"
} }
} }

View file

@ -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 { MyClient } from './client';
import commands from './commands'; import commands from './commands';
import chalk from 'chalk'; import chalk from 'chalk';
@ -13,6 +13,8 @@ for (let command of commands) {
client.addCommand(command); client.addCommand(command);
} }
const guildDetail = !!process.env.GUILD_DETAIL_SHOW;
client.on(Events.InteractionCreate, async (interaction: Interaction) => { client.on(Events.InteractionCreate, async (interaction: Interaction) => {
if (interaction instanceof ChatInputCommandInteraction) { if (interaction instanceof ChatInputCommandInteraction) {
const { commandName } = interaction; const { commandName } = interaction;
@ -40,16 +42,75 @@ client.on(Events.GuildMemberAdd, async (member: GuildMember) => {
const { joinedAt } = member; const { joinedAt } = member;
if (joinedAt === null) return; if (joinedAt === null) return;
const rules = client.config.getRules(member.guild.id) || [];
const hours = (joinedAt.getUTCHours() * 60 + joinedAt.getUTCMinutes() + 120) % 1440; const hours = (joinedAt.getUTCHours() * 60 + joinedAt.getUTCMinutes() + 120) % 1440;
if (hours < 540) { const age = Math.floor((joinedAt.getTime() - createdAt.getTime()) / 10800);
console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} kicked: Join at night not allowed`); const ops: Record<string, (x: number) => boolean> = {
await member.kick(); "age <": x => age < x,
return; "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`));
}
} }
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;
} }
console.info(`Member: ${chalk.green(member.user.id)} ${chalk.grey(`(@${member.user.username})`)} accepted`); 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) => { client.once(Events.ClientReady, (c) => {
console.log(`Logged in as ${c.user.tag}`); 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'));
}
}); });

View file

@ -1,17 +1,27 @@
import { Client, ClientOptions, Collection } from "discord.js"; import { Client, ClientOptions, Collection } from "discord.js";
import { CommandInfo } from "./commands"; import { CommandInfo } from "./commands";
import { ConfigFile } from "./configFile";
export class MyClient extends Client { export class MyClient extends Client {
commands: Collection<string, CommandInfo>; commands: Collection<string, CommandInfo>;
config: ConfigFile;
constructor (opts: ClientOptions) { constructor (opts: ClientOptions) {
super(opts); super(opts);
this.commands = new Collection(); this.commands = new Collection();
this.config = new ConfigFile();
}
override async login(token?: string): Promise<string> {
await this.config.load();
return super.login(token);
} }
addCommand (command: CommandInfo) { addCommand (command: CommandInfo) {
this.commands.set(command.data.name, command); this.commands.set(command.data.name, command);
} }
} }

View file

@ -7,7 +7,7 @@ const nodejsVersion = process.versions.node;
const data = new SlashCommandBuilder() const data = new SlashCommandBuilder()
.setName('version') .setName('version')
.setDescription('Informaz. sul bot'); .setDescription('Informazioni sul bot');
async function execute (interaction: ChatInputCommandInteraction) { async function execute (interaction: ChatInputCommandInteraction) {
await interaction.deferReply(); await interaction.deferReply();

53
src/configFile.ts Normal file
View file

@ -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<string, string>,
rules?: Record<string, ConfigGuildRule[]>
}
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;
}
};