diff --git a/src/app.ts b/src/app.ts index 43f178e..dab8466 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,7 @@ import { ButtonStyle, 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 { adminLog, formatCas, oddiakritikovat, log, rand, prefix, nabidni, send } from "./utils/utils.js"; import levenshtein from "js-levenshtein"; import { emouty } from "./utils/emotes"; import { lidiCoMajDenimPremium, setClient } from "./utils/denim-Spravce"; @@ -229,7 +229,7 @@ function handle(e: unknown, mesOrInt: Message | CommandInteraction) { 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); + if (mesOrInt instanceof Message) return void send(mesOrInt, txt); mesOrInt.reply(txt); } @@ -241,23 +241,23 @@ async function runKomand(mesOrInt: Message | CommandInteraction, cmd: Komand, cm if (jeMes && cmd.slashRun) { const nazev = `${prefix}${cmdName}`; const id = client.slashCommandy[nazev]; - return void mesOrInt.channel.send(`tuto ejenom sleh komand `); + return void send(mesOrInt, `tuto ejenom sleh komand `); } if (cmd.cd && jeMes) { const zbyva = Math.round(maKuldan(mesOrInt.author.id, cmdName, cmd.cd)); - if (zbyva) return mesOrInt.channel.send(`si kkt vole maz kuldan jeste ${formatCas(zbyva)}`); + if (zbyva) return send(mesOrInt, `si kkt vole maz kuldan jeste ${formatCas(zbyva)}`); } - if (cmd.premium && !lidiCoMajDenimPremium.includes((mesOrInt instanceof Message) ? mesOrInt.author.id : mesOrInt.member?.user.id || "")) { - return mesOrInt.channel?.send("sorka bracho tuto je koamnd jenom pro curaki co platy"); + if (cmd.premium && !lidiCoMajDenimPremium.includes(jeMes ? mesOrInt.author.id : mesOrInt.member?.user.id || "")) { + return send(mesOrInt as Message, "sorka bracho tuto je koamnd jenom pro curaki co platy"); } const akce = cmd.run ?? cmd.slashRun!; if (typeof akce == "string") { const res = renderMessage(akce, arg?.split(" ") || []); - if (mesOrInt instanceof Message) return mesOrInt.channel.send(res); + if (jeMes) return send(mesOrInt, res); return mesOrInt.reply(res); } @@ -266,7 +266,7 @@ async function runKomand(mesOrInt: Message | CommandInteraction, cmd: Komand, cm const result = await akce(mesOrInt as never, arg || ""); if (!result) return; - if (jeMes) return mesOrInt.channel.send(result) + if (jeMes) return send(mesOrInt, result) .catch(e => handle(e, mesOrInt)); // další feklo mesOrInt.reply(result as InteractionReplyOptions); @@ -282,12 +282,12 @@ client.on("messageCreate", async mes => { const prefixDiff = levenshtein(oddiakritikovat(mesPrefix.toLowerCase()), prefix); if (prefixDiff > 0) { - if (!await runEvent("messageCreate", [mes]) && prefixDiff <= 1 && mes.author.id != client.user!.id) mes.channel.send(`${prefix}*`); + if (!await runEvent("messageCreate", [mes]) && prefixDiff <= 1 && mes.author.id != client.user!.id) send(mes, `${prefix}*`); return; } if (!komandSDiakritikou) { - if (!await runEvent("messageCreate", [mes])) mes.channel.send("coe voe"); + if (!await runEvent("messageCreate", [mes])) send(mes, "coe voe"); return; } @@ -307,7 +307,7 @@ client.on("messageCreate", async mes => { if (await 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 (mes.channel.type == ChannelType.DM && komand?.DMUnsafe) return void send(mes, "tuten komand bohuzel v sz nefunkuje"); if (komand) return void runKomand(mes, komand, cmdName, celArgs); @@ -317,7 +317,7 @@ client.on("messageCreate", async mes => { 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"); + if (!slova.length) return void send(mes, "co to znamena ti gadzovko"); const nabidka = slova.sort().slice(0, 5); @@ -333,9 +333,9 @@ client.on("messageCreate", async mes => { radek.components.forEach((btn, i) => btn.setDisabled() && (i == lookupId && btn.setStyle(ButtonStyle.Success))); i.update({ content: `ok vole ${emouty.d3k}`, components: [radek] }); - if (!cmd) return void mes.channel.send("bohuzel negdo sy ze mi dela prel"); + if (!cmd) return void send(mes, "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"); + if (mes.channel.type == ChannelType.DM && cmd?.DMUnsafe) return void send(mes, "tuten komand bohuzel v sz nefunkuje"); runKomand(mes, cmd, cmdName, celArgs); }, diff --git a/src/modules/custom.ts b/src/modules/custom.ts index c4914a6..0b3c187 100644 --- a/src/modules/custom.ts +++ b/src/modules/custom.ts @@ -1,7 +1,7 @@ import { CClient, HelpServer, Komand, KomandNaExport, Modul, SRecord } from "../utils/types"; import { join } from "path"; import { existsSync, readFileSync, writeFileSync } from "fs"; -import { oddiakritikovat, strankovani } from "../utils/utils"; +import { oddiakritikovat, send, strankovani } from "../utils/utils"; import { APIEmbed, Message } from "discord.js"; let client: CClient; @@ -63,13 +63,13 @@ function zmrdovoAliasy(zacatek: string, owner: string, mes: Message) { let text = ""; arr.forEach(element => { if (text.length + element.length > 2000) { - mes.channel.send({ content: text, allowedMentions: { users: [] } }); + send(mes, { content: text, allowedMentions: { users: [] } }); text = ""; } text += `${element}\n`; }); - mes.channel.send({ content: text, allowedMentions: { users: [] } }); + send(mes, { content: text, allowedMentions: { users: [] } }); } const exp: Modul = { diff --git a/src/modules/denim-Dobroty.ts b/src/modules/denim-Dobroty.ts index 9bfc7b3..3a054ce 100644 --- a/src/modules/denim-Dobroty.ts +++ b/src/modules/denim-Dobroty.ts @@ -5,7 +5,7 @@ import { join } from "path"; import { Priority, novejPlay } from "../utils/voice"; import { lidiCoMajDenimPremium } from "../utils/denim-Spravce"; import { exec } from "child_process"; -import { log } from "../utils/utils"; +import { log, send } from "../utils/utils"; const kmenovaCesta = join(__dirname, `../../zvuky/priVstupu`); const formaty = ["mp3", "wav", "ogg"]; @@ -42,14 +42,14 @@ const exp: Modul = { if (error) { log("chyba pri ffprobe", error); - mes.channel.send("bohuzel chiba"); + send(mes, "bohuzel chiba"); vymaz(docasnaCesta); return; } // Maximum if (Number(stdout) > 13) { - mes.channel.send("13 se kund maks"); + send(mes, "13 se kund maks"); vymaz(docasnaCesta); return; } @@ -62,7 +62,7 @@ const exp: Modul = { renameSync(docasnaCesta, `${zaklad}.${typ}`); - mes.channel.send("ej tot am"); + send(mes, "ej tot am"); }); } } diff --git a/src/modules/komComplex.ts b/src/modules/komComplex.ts index 34d1fb7..8f9e33d 100644 --- a/src/modules/komComplex.ts +++ b/src/modules/komComplex.ts @@ -3,7 +3,7 @@ import { ChannelType, Message, MessageReaction } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul, SRecord } from "../utils/types"; -import { formatter, ping, sendDM } from "../utils/utils"; +import { formatter, ping, send, sendDM } from "../utils/utils"; const exp: Modul = { more_komandy: { @@ -67,7 +67,7 @@ const exp: Modul = { argument.splice(0, 1); if (argument.length) setTimeout(() => randomshit(mes, argument), 1000); }; - mes.channel.send(":stop_button:").then(mes => randomshit(mes, [":five:", ":four:", ":three:", ":two:", ":one:", ":ok:"])); + send(mes, ":stop_button:").then(mes => randomshit(mes, [":five:", ":four:", ":three:", ":two:", ":one:", ":ok:"])); }, pocasi: () => { diff --git a/src/modules/muzika.ts b/src/modules/muzika.ts index eb468e0..a5b7dc7 100644 --- a/src/modules/muzika.ts +++ b/src/modules/muzika.ts @@ -6,7 +6,7 @@ import { search, validate, video_basic_info } from "play-dl"; import ytdlko from "ytdl-core"; import { emouty } from "../utils/emotes"; import { Modul } from "../utils/types"; -import { adminLog, log } from "../utils/utils"; +import { adminLog, log, send } from "../utils/utils"; import { getConn, Hratelny, novejJoin, novejPlay, Priority, stopPlayer } from "../utils/voice"; import { array, record, safeParse, string } from "valibot"; @@ -73,16 +73,16 @@ const exp: Modul = { ajtem.name = soubor.name; ajtem.url = soubor.url; ajtem.special = true; - mes.channel.send(`zahraju \`${soubor.name}\``); + send(mes, `zahraju \`${soubor.name}\``); } else if (txt == "") return "co mam zahrat??"; else if (!druh) { ajtem.name = `nejaka picovina ot ${mes.author}`; ajtem.url = txt; ajtem.special = true; - mes.channel.send(`zkusim to zahrat`); + send(mes, `zkusim to zahrat`); } else if (druh == "search") { - const msg = mes.channel.send("hledam"); + const msg = send(mes, "hledam"); const hledani = await search(txt, { limit: 1 }); if (!hledani[0]) return "nic sem nenašel"; ajtem.url = hledani[0].url; @@ -100,7 +100,7 @@ const exp: Modul = { const client: Client = module.exports.client; adminLog(client, "video nemá název"); } - mes.channel.send(`zahraju \`${ajtem.name}\``); + send(mes, `zahraju \`${ajtem.name}\``); } else return "tuto neumim zahrat"; if (!guildy.has(guildId)) { diff --git a/src/modules/sachy.ts b/src/modules/sachy.ts index e8ca13f..ee3364b 100644 --- a/src/modules/sachy.ts +++ b/src/modules/sachy.ts @@ -4,6 +4,7 @@ import { APIEmbed, Message } from "discord.js"; import { Modul } from "../utils/types"; import { fokinLookupTable, klikance, pocetKeStrane } from "../utils/sachyEtc"; import { emiter, sachyDomena } from "../utils/sachyServer"; +import { send } from "../utils/utils"; const hry = new Map(); // gameID -> Hra const hraci = new Map(); // hrac -> gameID @@ -478,7 +479,7 @@ const exp: Modul = { if (hraci.has(player)) return "zakaz symultanki"; } - const message = await mes.channel.send("hraj zopiciva"); + const message = await send(mes, "hraj zopiciva"); const hra = createGame(++nextGameID, mes.author.id, druhej, message); hraci.set(mes.author.id, nextGameID); diff --git a/src/modules/spink.ts b/src/modules/spink.ts index 5c1c90d..fc65580 100644 --- a/src/modules/spink.ts +++ b/src/modules/spink.ts @@ -3,7 +3,7 @@ import { ChannelType, Role, TextBasedChannel, User } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul, SRecord } from "../utils/types"; -import { formatCas, formatter, log, oddiakritikovat, ping, safeQuery, sendDM } from "../utils/utils"; +import { formatCas, formatter, log, oddiakritikovat, ping, safeQuery, send, sendDM } from "../utils/utils"; const enum Spinky { spinkacek, @@ -283,7 +283,7 @@ const exp: Modul = { const entry = await getLastEntry(mes.author.id); if ((entry && !entry.vstavacek) && cmd != "vstavacek") { if (mes.channel.type == ChannelType.DM) { - mes.channel.send("drz hubu"); + send(mes, "drz hubu"); return true; } diff --git a/src/modules/ultimatniAntispam.ts b/src/modules/ultimatniAntispam.ts new file mode 100644 index 0000000..9d8b6df --- /dev/null +++ b/src/modules/ultimatniAntispam.ts @@ -0,0 +1,52 @@ +// "Ulimatní" kapp + +import { Modul } from "../utils/types"; +import { send, messageLinks, wait } from "../utils/utils"; + +const maxRecurseLength = 10; + +function getDepth(id: string, lvl = 0) { + const pre = messageLinks.get(id); + if (!pre || lvl >= maxRecurseLength) return lvl; + + return getDepth(pre, lvl + 1); +} + +function clear(id: string) { + const kam = messageLinks.get(id); + if (!kam) return; + + messageLinks.delete(id); + + return clear(kam); +} + +// Garbage collector +const garbageCollectDelay = 60_000; +setInterval(() => { + for (const klic of messageLinks.keys()) { + if (Number(BigInt.asUintN(64, BigInt(klic)) >> 22n) + 1420070400000 < Date.now() - garbageCollectDelay) + messageLinks.delete(klic); + } +}, garbageCollectDelay); + +const exp: Modul = { + super_on_messageCreate: { + pos: 2, + fun: async mes => { + if (mes.author.id != mes.client.user.id) return false; + + await wait(mes); + + if (getDepth(mes.id) == maxRecurseLength) { + clear(mes.id); + send(mes, "nemam rat sarandu"); + return true; + } + + return false; + } + } +}; + +module.exports = exp; diff --git a/src/modules/vojs.ts b/src/modules/vojs.ts index ae5f517..ff443b0 100644 --- a/src/modules/vojs.ts +++ b/src/modules/vojs.ts @@ -53,12 +53,13 @@ const exp: Modul = { if (!channel) return `di si tam sam ne ty gadzo ${mes.author}`; const nahlas = !arg.startsWith("potichu"); - if (nahlas) mes.channel.send("<@&591306633196339261> vojs"); novejJoin(mes.guild!, channel.id) .then(prev => { if (prev != true && nahlas) novejPlay(mes.guildId!, "zvuky/nazdar.ogg", Priority.Etc); }); + + if (nahlas) return "<@&591306633196339261> vojs"; } }, diff --git a/src/modules/zapniVypniSe.ts b/src/modules/zapniVypniSe.ts index 364be98..a0190d6 100644 --- a/src/modules/zapniVypniSe.ts +++ b/src/modules/zapniVypniSe.ts @@ -1,7 +1,7 @@ import { getVoiceConnections } from "@discordjs/voice"; import { emouty } from "../utils/emotes"; import { Modul } from "../utils/types"; -import { prefix } from "../utils/utils"; +import { prefix, send } from "../utils/utils"; import { novejLeave } from "../utils/voice"; let spim = false; @@ -15,9 +15,9 @@ const exp: Modul = { if (spim) { spim = false; mes.client.user?.setStatus("online"); - mes.channel.send("dobré ráno magoří"); + send(mes, "dobré ráno magoří"); } - else mes.channel.send("tak jsi kokot?"); + else send(mes, "tak jsi kokot?"); } else if (!spim) { if (cont == `${prefix} vypni se`) { mes.react(emouty.purfieRIP); diff --git a/src/modules/zbytek.ts b/src/modules/zbytek.ts index 8470464..0cbd592 100644 --- a/src/modules/zbytek.ts +++ b/src/modules/zbytek.ts @@ -4,7 +4,7 @@ import { getVoiceConnections } from "@discordjs/voice"; import { Client, TextChannel, } from "discord.js"; import { createServer } from "http"; import { Modul } from "../utils/types"; -import { adminLog, formatCas, log, nabidni, sendDM } from "../utils/utils"; +import { adminLog, formatCas, log, nabidni, send, sendDM } from "../utils/utils"; import { emouty } from "../utils/emotes"; import { readFileSync } from "fs"; @@ -110,7 +110,7 @@ const exp: Modul = { (async () => { if (mes.author.id == client.user?.id) - return mes.content != "uz nechci" ? mes.channel.send("uz nechci") : ""; + return mes.content != "uz nechci" ? send(mes, "uz nechci") : ""; const adminRole = mes.member?.roles.cache.find(r => r.name == "admin"); if (adminRole) { await mes.member?.roles.remove(adminRole).catch(e => log(mes.author.displayName, "nejde odebrat admin", e)); @@ -120,7 +120,7 @@ const exp: Modul = { if (adminRole) setTimeout(() => { mes.member?.roles.add(adminRole); }, 60_000); - mes.channel.send(emouty.fu); + send(mes, emouty.fu); })(); return true; diff --git a/src/utils/types.ts b/src/utils/types.ts index e3be018..d269f48 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -1,8 +1,8 @@ import { VoiceConnection } from "@discordjs/voice"; -import { Awaitable, BaseMessageOptions, Client, ClientEvents, ClientPresenceStatusData, CommandInteraction, Message, MessageCreateOptions, MessagePayload, Presence, User } from "discord.js"; +import { Awaitable, BaseMessageOptions, Client, ClientEvents, ClientPresenceStatusData, CommandInteraction, Message, MessageCreateOptions, Presence, User } from "discord.js"; import { EventEmitter } from "events"; -export type RunFunkce = (message: Message, argumenty: string) => Awaitable; +export type RunFunkce = (message: Message, argumenty: string) => Awaitable; export type InteractionRunFunkce = (interaction: CommandInteraction) => Awaitable; export type BaseKomandProps = { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 86d0360..cd57ea1 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,4 +1,6 @@ -import { APIEmbed, ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CacheType, ChannelType, Client, ComponentType, MessageCreateOptions, ReadonlyCollection, TextBasedChannel, User } from "discord.js"; +/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/ + +import { APIEmbed, ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CacheType, ChannelType, Client, ComponentType, Message, MessageCreateOptions, ReadonlyCollection, TextBasedChannel, User } from "discord.js"; import { existsSync } from "fs"; import { MysqlError, createPool } from "mysql"; @@ -93,7 +95,7 @@ export function log(...content: unknown[]) { }); const d = new Date(); - console.log(`[${d.getDate()}.${d.getMonth() + 1}. ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}]:`, ...jo); + console.log(`[${d.getDate()}.${d.getMonth() + 1}. ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}]:`, ...jo); } export async function sendDM(user: User, txt: string) { @@ -197,3 +199,25 @@ export const safeQuery = (query: string, parametry?: string[]) => new Promise res({ data: odpoved, err }); }); }); + +export const messageLinks = new Map(); + +/** + * Resolvne až zpráva s daným ID bude zařazena do messageLinks + * @param mes Zpráva na kterou se má počkat + */ +export async function wait(mes: Message) { + while (true) { + if (messageLinks.has(mes.id)) + return; + await new Promise(r => setTimeout(r, 0)); + } +} + +export async function send(mes: Message, co: string | MessageCreateOptions) { + const nova = await mes.channel.send(co); + + messageLinks.set(nova.id, mes.id); + + return nova; +}