import { ChannelType, Client, CommandInteraction, GatewayIntentBits, InteractionReplyOptions, Message, Partials } from "discord.js"; import { readdirSync } from "fs"; import { CUser, KomandNaExport, Komand, ListenerFunkce, Modul, SRecord, SuperListenerFunkce, CClient, HelpServer, KomandRaw } from "./utils/types"; import { adminLog, formatCas, oddiakritikovat, log, rand, prefix, nabidni } from "./utils/utils.js"; import levenshtein from "js-levenshtein"; import { emouty } from "./utils/emotes"; import { lidiCoMajDenimPremium, setClient } from "./utils/denim-Spravce"; 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 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 = {}; setClient(client); 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; for (const klic in modul) { const prefix = /^(?super_)?on_(?.+)/.exec(klic); 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)); } // Bohužel typescript je tupá píča a musíme mu vysvětlit, že to vlastně jde const n = modul[klic as Exclude]!; 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 (klic == "more_komandy") { const n = modul[klic]!; 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, premium: value.premium }; 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}`; } } function handle(e: unknown, mesOrInt: Message | CommandInteraction) { 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 void mesOrInt.channel.send(txt); mesOrInt.reply(txt); } 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)}`); } if (cmd.premium && !lidiCoMajDenimPremium.includes((mesOrInt instanceof Message) ? mesOrInt.author.id : mesOrInt.member?.user.id || "")) { return void mesOrInt.channel?.send("sorka bracho tuto je koamnd jenom pro curaki co platy"); } const akce = jeMes ? cmd.run : cmd.slashRun!; if (typeof akce == "string") { const res = renderMessage(akce, arg?.split(" ") || []); if (mesOrInt instanceof Message) return void mesOrInt.channel.send(res); return void mesOrInt.reply(res); } try { // Je to absolutní fekálie, ale alespoň to je kompilovatelný narozdíl od předchozí verze (commit 08fde76 a předchozí) const result = await akce(mesOrInt as never, arg || ""); if (!result) return; if (jeMes) return void mesOrInt.channel.send(result) .catch(e => handle(e, mesOrInt)); // další feklo mesOrInt.reply(result as InteractionReplyOptions); } catch (e) { handle(e, mesOrInt); } } client.on("messageCreate", async mes => { if (process.env.ignoreMess) return; const [mesPrefix, komandSDiakritikou, ...args] = mes.content.split(" "); const prefixDiff = levenshtein(oddiakritikovat(mesPrefix.toLowerCase()), prefix); if (prefixDiff > 0) { if (!runEvent("messageCreate", [mes]) && prefixDiff <= 1 && mes.author.id != client.user!.id) mes.channel.send(`${prefix}*`); return; } 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); nabidni({ nabidka, channel: mes.channel, zpravec: { content: "nemnel sy na misli:" }, onCollect: (i, lookupId, radek) => { if (i.member?.user.id != mes.author.id) return; 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 (!cmd) return void mes.channel.send("bohuzel negdo sy ze mi dela prel"); if (mes.channel.type == ChannelType.DM && cmd?.DMUnsafe) return void mes.channel.send("tuten komand bohuzel v sz nefunkuje"); runKomand(mes, cmd, cmdName, celArgs); }, konecnaZprava: "pozde" }); }); // 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(prefix.length); const command = client.komandy[cmdName]; if (!command) return void int.reply(`komand nenalezen ${emouty.lukiw}`); runKomand(int, command, cmdName); }); client.login(process.env.token);