import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, CommandInteraction, ComponentType, GatewayIntentBits, Message, Partials } from "discord.js"; import { readdirSync } from "fs"; import { CUser, EventSOn, KomandNaExport, Komand, ListenerFunkce, Modul, SRecord, SuperListenerFunkce, CClient, HelpServer, KomandRaw } from "./utils/types"; import { adminLog, formatCas, oddiakritikovat, log, rand } from "./utils/utils.js"; import levenshtein from "js-levenshtein"; import { emouty } from "./utils/emotes"; import { existsSync } from "fs"; if (!existsSync("config.json")) throw new Error("config.json neexistuje"); process.env = { ...process.env, ...require("../config.json") }; const ints = GatewayIntentBits; const client = new Client({ partials: [Partials.Channel], intents: [ints.Guilds, ints.GuildVoiceStates, ints.GuildPresences, ints.GuildMessages, ints.DirectMessages, ints.MessageContent] }) as CClient; const prefix = process.env.prefix || "more"; const DJ_PREFIX = "dj:"; const modulFolder = `${__dirname}/modules/`; const eventy: SRecord = {}; const superEventy: SRecord = {}; const kuldan_log: SRecord> = {}; const komandyNaPoslani: KomandNaExport[] = []; const helpServer: HelpServer = require("./utils/helpServer"); client.komandy = {}; client.aliasy = {}; function getFirstArg(fn: KomandRaw["run"]) { if (typeof fn != "function") return; return fn.toString() .match(/(?:function\s.*?)?\(([^)]*)\)|\w+ =>/)![1] ?.split(",")[1] ?.trim(); } const runEvent = (name: string, args: unknown[]) => { for (const listener of superEventy[name] || []) { if (!listener) continue; // [after] pozice v superEventy arrayi se dají nastavovat a teoreticky může nastat mezera try { if (listener(...args)) return true; // if listener returns true, it means, we shouldn't continue with execustion } catch (e) { if (process.env.dieOnError) throw e; log("error pri spusteni super listeneru", e as Error); adminLog(client, "error pri supereventu"); } } eventy[name]?.forEach(listener => { try { listener(...args); } catch (e) { if (process.env.dieOnError) throw e; log("error pri spousteni eventu", e as Error); adminLog(client, "error pri eventu"); } }); }; // Loading of modules readdirSync(modulFolder).forEach(soubor => { if (!soubor.endsWith(".js")) return; const modul: Modul = require(`${modulFolder}${soubor}`); log(`Loaded: ${modulFolder}${soubor}`); modul.client = client; Object.keys(modul).forEach(name => { const prefix = /^(?super_)?on_(?.+)/.exec(name); if (prefix) { const groups = prefix.groups!; const ev = groups.s ? superEventy : eventy; if (!ev[groups.h]) { ev[groups.h] = []; if (!["messageCreate", "userPresenceUpdate"].includes(groups.h)) client.on(groups.h, (...args) => void runEvent(groups.h, args)); } const n = modul[name as EventSOn]!; if (typeof n == "object") { const prev = ev[groups.h][n.pos]; ev[groups.h][n.pos] = n.fun; if (prev) ev[groups.h].push(prev); } else ev[groups.h].push(n); } else if (name === "more_komandy") { const n = modul[name]!; Object.keys(n).forEach(cmdName => { const value = n[cmdName]; const toCoExportuju: KomandNaExport = { name: cmdName }; let hide = false; if (typeof value !== "object") { client.komandy[cmdName] = { run: value }; toCoExportuju.arg = getFirstArg(value); } else { client.komandy[cmdName] = { run: value.run, slashRun: value.slashRun, cd: value.cd, DMUnsafe: value.DMUnsafe }; Object.assign(toCoExportuju, { als: value.als, arg: value.arg ?? getFirstArg(value.run) }); hide = !!value.hidden; value.als?.forEach(al => client.aliasy[al] = cmdName); } if (!hide) komandyNaPoslani.push(toCoExportuju); }); } }); }); helpServer.komandy = komandyNaPoslani; const maKuldan = (id: string, komand: string, kuldan_komandu: number) => { if (!kuldan_log[komand]) kuldan_log[komand] = {}; const cas_ted = Date.now() / 1000; const rozdil = cas_ted - kuldan_log[komand][id]; if (rozdil < kuldan_komandu) return kuldan_komandu - rozdil; kuldan_log[komand][id] = cas_ted; return 0; }; function assume(cond: boolean, msg: string) { if (!cond) throw msg; } function prase(expression: string, i: number, terminators: string[], args: string[]): [string, number, string?] { let ret = ""; let escaped = false; while (i < expression.length) { const char = expression[i]; i++; if (escaped) { escaped = false; ret += char; continue; } switch (char) { case "~": // eskejp escaped = true; break; case "[": { // RNG vyber let option: string, term: string | undefined; const options: string[] = []; do { [option, i, term] = prase(expression, i, ["|", "]"], args); options.push(option); } while (term == "|"); if (options.length > 0) ret += options[rand(options.length)]; break; } case "<": { // RNG cislo let min: string, max: string; [min, i] = prase(expression, i, [","], args); [max, i] = prase(expression, i, [">"], args); const imin = parseInt(min); const imax = parseInt(max); assume(!Number.isNaN(min), "RNG cislo: Min neni integer"); assume(!Number.isNaN(max), "RNG cislo: Max neni integer"); ret += rand(imax - imin + 1) + imin; break; } case "{": {// ARG let argId: string; [argId, i] = prase(expression, i, ["}"], args); if (argId === "*") { // join args ret += args.join(" "); } else if (argId === "#") { // number of args ret += args.length; } else { // arg id # const numArgId = parseInt(argId); assume(!Number.isNaN(numArgId) && numArgId > 0 && numArgId <= args.length, "ARG: Neplatnej index argumentu"); ret += args[numArgId - 1]; } break; } default: for (const terminator of terminators) { if (terminator == char) return [ret, i, char]; } ret += char; break; } } assume(terminators.length == 0, "Neocekavanej konec vejrazu"); return [ret, expression.length - 1]; } function renderMessage(expression: string, args: string[]) { if (!expression.toLowerCase().startsWith(DJ_PREFIX)) return expression; expression = expression.substring(DJ_PREFIX.length); try { return prase(expression, 0, [], args)[0]; } catch (ex) { return `Chyba: ${ex}`; } } async function runKomand(mes: Message, cmd: Komand, cmdName: string, arg: string): Promise; async function runKomand(interaction: CommandInteraction, cmd: Komand, cmdName: string): Promise; async function runKomand(mesOrInt: Message | CommandInteraction, cmd: Komand, cmdName: string, arg?: string) { const jeMes = mesOrInt instanceof Message; if (cmd.cd && jeMes) { const zbyva = Math.round(maKuldan(mesOrInt.author.id, cmdName, cmd.cd)); if (zbyva) return void mesOrInt.channel.send(`si kkt vole maz kuldan jeste ${formatCas(zbyva)}`); } const akce = jeMes ? cmd.run : cmd.slashRun!; if (typeof akce == "string") { const res = renderMessage(akce, arg?.split(" ") || []); if (mesOrInt instanceof Message) return mesOrInt.channel.send(res); return mesOrInt.reply(res); } try { // @ts-ignore todle prostě musí fungovat a jestli náhodou ne, stejně je tu catch const result = await akce(mesOrInt, arg); if (!result) return; if (mesOrInt instanceof Message) return mesOrInt.channel.send(result); // @ts-ignore a zas ksd mesOrInt.reply(result); } catch (e) { if (process.env.dieOnError) throw e; log("error pri spousteni akce", e as Error); const admin = process.env.adminID; const txt = `pri spousteni thohoto komandu nastala chyba ${admin ? `<@${admin}> uz?` : ""}`; if (mesOrInt instanceof Message) return mesOrInt.channel.send(txt); mesOrInt.reply(txt); } } client.on("messageCreate", async mes => { if (process.env.ignoreMess || mes.author.id == client.user!.id) return; const [mesPrefix, komandSDiakritikou, ...args] = mes.content.split(" "); if (oddiakritikovat(mesPrefix.toLowerCase()) != prefix) return void runEvent("messageCreate", [mes]); if (!komandSDiakritikou) { if (!runEvent("messageCreate", [mes])) mes.channel.send("coe voe"); return; } const celArgs = args.join(" "); const komandBez = oddiakritikovat(komandSDiakritikou).toLowerCase(); const cmdName = client.aliasy[komandBez] ?? komandBez; if (runEvent("messageCreate", [mes, cmdName])) return; const komand = client.komandy[cmdName]; if (mes.channel.type == ChannelType.DM && komand?.DMUnsafe) return void mes.channel.send("tuten komand bohuzel v sz nefunkuje"); if (komand) return void runKomand(mes, komand, cmdName, celArgs); // neměl jsi na mysli? const slova: string[] = []; [...Object.keys(client.komandy), ...Object.keys(client.aliasy)].forEach(cmd => { const distance = levenshtein(cmd, cmdName); if (distance <= Math.ceil(cmdName.length * 0.3)) slova.push(cmd); }); if (!slova.length) return void mes.channel.send("co to znamena ti gadzovko"); const nabidka = slova.sort().slice(0, 5); const radek = new ActionRowBuilder(); nabidka.forEach((cmd, i) => { const nazev = cmd.length > 80 ? `${cmd.slice(0, 77)}...` : cmd; radek.addComponents(new ButtonBuilder({ customId: `${i}`, label: nazev, style: ButtonStyle.Primary })); }); const zprava = await mes.channel.send({ content: "nemnel sy na misli:", components: [radek] }); const collector = mes.channel.createMessageComponentCollector({ filter: i => i.message.id == zprava.id && mes.author.id == i.user.id, time: 18e4 }); collector.on("collect", i => { const lookupId = Number(i.customId); if (isNaN(lookupId)) return void mes.channel.send("neco nefunguje idk"); const komand = nabidka[lookupId]; const cmdName = client.aliasy[komand] ?? komand; const cmd = client.komandy[cmdName]; radek.components.forEach(btn => btn.setDisabled()); i.update({ content: `ok vole ${emouty.d3k}`, components: [radek] }); if (mes.channel.type == ChannelType.DM && cmd?.DMUnsafe) return void mes.channel.send("tuten komand bohuzel v sz nefunkuje"); runKomand(mes, cmd, cmdName, celArgs); }); collector.on("end", c => { if (c.size) return; radek.components.forEach(btn => btn.setDisabled()); zprava.edit({ content: "pozde", components: [radek] }); }); }); // Simulation of userPresenceUpdate event client.on("presenceUpdate", (bef, aft) => { const user = aft.user as CUser | null; if (!user) return; if (!user.presence) user.presence = {}; if (JSON.stringify(aft.clientStatus) == JSON.stringify(user.presence)) return; user.presence = aft.clientStatus || {}; runEvent("userPresenceUpdate", [bef, aft]); }); client.on("interactionCreate", async int => { if (process.env.ignoreMess || !int.isCommand()) return; const cmdName = int.commandName.slice(4); const command = client.komandy[cmdName]; if (!command) int.reply(`komand nenalezen ${emouty.lukiw}`); runKomand(int, command, cmdName); }); client.login(process.env.token);