// Modul dedikovaný funkci spinkáček 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"; const enum Spinky { vstavacek, spinkacek } const budouciSpinky: SRecord = {}; const autoSpinky: SRecord<[boolean, TextBasedChannel]> = {}; const contactSpinkServer = async (druh: Spinky, user: User) => { handleSpink(druh, user); const sql = druh == Spinky.spinkacek ? "INSERT INTO spinkacky (discord_id) VALUE (?)" : "UPDATE spinkacky SET vstavacek = curtime() WHERE discord_id = ? ORDER BY id DESC LIMIT 1"; const odpoved = await safeQuery(sql, [user.id]); return odpoved.err; }; const ifUzRemove = (id: string) => { const uzPgn = budouciSpinky[id]; if (uzPgn) { clearTimeout(uzPgn); delete budouciSpinky[id]; return true; } return false; }; async function getLastEntry(id: string) { const odpoved = await safeQuery<{ spinkacek: string; vstavacek: string | undefined; }>(`SELECT spinkacek, vstavacek FROM spinkacky WHERE discord_id = ? ORDER BY spinkacek DESC LIMIT 1; `, [id]); if (odpoved.err || !odpoved.data.length) return false; return odpoved.data[0]; } /* R.I.P. async tovjemamvolepreceroliky(roliky: User, rictCas?: boolean) * 20. 11. 2021 ✝ 19. 11. 2023 Vzpomínáme! */ const sendGmMessage = async (user: User, rictCas?: boolean) => { const odpoved = await contactSpinkServer(Spinky.vstavacek, user); if (odpoved) return "uz jsi vzhuru ty hajzle"; const zapis = await getLastEntry(user.id); const zacatek = `dobry rano hajzle ${user}`; if (!rictCas || zapis === false) return zacatek; const cas = (Number(new Date(zapis.vstavacek ?? Date.now())) - Number(new Date(zapis.spinkacek))) / 1000; const formatedCas = formatCas(cas); let zpr = ""; if (cas >= 57600) zpr = "mas dat more vstavacek uz kdyz vstanes retarde"; else if (cas >= 43200) zpr = "extrémní bídák"; else if (cas >= 36000) zpr = "fakt bídák"; else if (cas < 30) zpr = "dobrej mikrospánek debile"; else if (cas < 10800) zpr = "dobrej fake spink debile"; return `${zacatek}\nspal sy ${formatedCas}\n${zpr}`; }; function handleSpink(stav: Spinky, uzivatel: User) { for (const [, guild] of uzivatel.client.guilds.cache) { const member = guild.members.cache.get(uzivatel.id); if (!member) continue; const c = guild.roles.cache; const spinkRole = c.find(r => r.name == "spink"); const adminRole = c.find(r => r.name == "admin"); const make = (akce: "a" | "r", role: Role) => { const handle = (e: Error) => { if (["Missing Permissions", "Missing Access"].includes(e.message)) return; log("chyba pri davani/odebirani role", e); }; if (akce == "a") member.roles.add(role).catch(handle); else member.roles.remove(role).catch(handle); }; if (stav == Spinky.spinkacek) { if (spinkRole) make("a", spinkRole); if (adminRole) make("r", adminRole); } else { if (spinkRole) make("r", spinkRole); if (adminRole) make("a", adminRole); continue; } for (const [, channel] of guild.channels.cache) { if (channel.type != ChannelType.GuildVoice) continue; for (const [, member] of channel.members) { if (member.id != uzivatel.id) continue; member.voice.disconnect("spinkacek"); return false; } } } } function strToDate(str: string) { const cas = new Date(); const dnes = new Date(); let neco: RegExpExecArray | null; // Relativní čas neco = /^(?:(?\d+)h)?(?:(?\d+)m)?(?:(?\d+)s?)?$/.exec(str.toLowerCase()); if (neco) { if (str == "") return []; const g = neco.groups!; return ["rel", ((Number(g.h) * 3600 || 0) + (Number(g.m) * 60 || 0) + (Number(g.s) || 0)) * 1000] as const; } // Absolutní čas neco = /^((?\d+)\. ?((?\d+)\.)? ?)?(?\d+)(:(?\d+)(:(?\d+))?)? ?(?

am|pm)?$/.exec(str.toLowerCase()); if (neco) { const g = neco.groups!; if (g.d) cas.setDate(Number(g.d)); if (g.mo) cas.setMonth(Number(g.mo) - 1); const h = Number(g.h); cas.setHours(g.p == "pm" && h < 12 ? h + 12 : h); cas.setMinutes(Number(g.m) || 0); cas.setSeconds(Number(g.s) || 0); if (Number(cas) < Number(dnes)) cas.setDate(dnes.getDate() + 1); return ["abs", Number(cas) - Number(dnes)] as const; } return []; } const exp: Modul = { more_komandy: { spinkacek: { als: ["spink", "spoink", "spinkake", "spoinkacek", "gn", emouty.spinkacek, emouty.gn], run: async mes => { if (mes.author.bot) return `až někdy${emouty.kapp}`; const odpoved = await contactSpinkServer(Spinky.spinkacek, mes.author); if (!odpoved) { mes.react(emouty.spinkacek); ifUzRemove(mes.author.id); } else return 'nespis uz?????'; } }, vstavacek: { als: ["vstavcacek", "gm", "unspinkacek"], arg: "fejk (nepovinné)", run: async (mes, arg) => { if (mes.author.bot) return emouty.sjeta; return sendGmMessage(mes.author, arg.toLowerCase() != "fejk"); } }, pgn: { als: [`p${emouty.gn}`], run: (mes, kdy) => { if (mes.author.bot) return emouty.sjeta; // Random výplod GitHub Copilota if (kdy == "fejk") { mes.react(emouty.lukiw); return "fejk spinkacek"; } let druh = ""; let spinkZa: number; let random = false; if (kdy.startsWith("random")) { random = true; let rozdil = 540_000; let min = 60_000; if (kdy.length > 7) { const str = kdy.toLowerCase().slice(7).replace(/ /g, "").split("-"); if (str.length != 2) return "cos to napsal kkte"; const [from, to] = [strToDate(str[0]), strToDate(str[1])]; if (!from.length || !to.length) return "cos to napsal kkte"; if (to[1] <= from[1]) return "ja teda nevim jestli ti vys jak funguje cas ael vym ze tam mas chibu"; rozdil = to[1] - from[1]; min = from[1]; } spinkZa = (Math.random() * rozdil + min); log(`random spink pro ${oddiakritikovat(mes.member?.displayName || "neznamejmeno")} byl nastaven na ${formatter(Date.now() + spinkZa)}`); } else { [druh, spinkZa] = strToDate(kdy); if (typeof spinkZa != "number") return "cos to napsal kkte"; } ifUzRemove(mes.author.id); budouciSpinky[mes.author.id] = setTimeout(() => { contactSpinkServer(Spinky.spinkacek, mes.author); mes.react(emouty.spinkacek); }, spinkZa); if (!random) { if (druh == "abs") return `tvuj spinkacek byl naplanovan na za ${formatCas(spinkZa / 1000)}`; else return `tvuj spinkacek byl naplanovan na ${formatter(Date.now() + spinkZa)}`; } mes.react(emouty.lajk); } }, ps: { als: ["poslednispink"], arg: "kdo (nepovinné)", run: async (mes, arg) => { const prazdno = arg == ""; if (!ping.test(arg) && !prazdno) return "sikkt"; const id = prazdno ? mes.author.id : mes.mentions.members?.first()?.id ?? ""; const entry = await getLastEntry(id); if (entry === false) return `tuten kkt jeste nespal ${emouty.lukiw}`; if (!entry.vstavacek) return "ten kkt jiz ickon spi"; const cas = Number(new Date()) - Number(new Date(entry.vstavacek)); return `uz ${prazdno ? "jsi" : "je"} vzhuru ${formatCas(cas / 1000)}`; } }, zruspgn: { als: ["zpgn", "cancelpgn", "cpgn", "unpgn", `unp${emouty.gn}`], run: mes => { if (!ifUzRemove(mes.author.id)) return `spinkacek ale nemas naplanovanej ty kkte ${mes.author}`; return "to bylo teda trapny debile"; } }, autospink: mes => `autospink je ${autoSpinky[mes.author.id] ? (delete autoSpinky[mes.author.id], "vypnut") : (autoSpinky[mes.author.id] = [true, mes.channel], "zapnut")}`, autoautospink: { als: ["🚗🚗spink"], run: mes => `autoautospink je ${autoSpinky[mes.author.id] ? (delete autoSpinky[mes.author.id], "vypnut") : (autoSpinky[mes.author.id] = [false, mes.channel], "zapnut")}` } }, on_voiceStateUpdate: async (bef, aft) => { if (!aft.channel || bef.channel) return; const entry = await getLastEntry(aft.id); if (entry && !entry.vstavacek) aft.disconnect("spinkacek") .catch(err => log("spinkacek odpojit se nepovedlo nebo co:", err)); }, super_on_messageCreate: async (mes, cmd?: string) => { const entry = await getLastEntry(mes.author.id); if ((entry && !entry.vstavacek) && cmd != "vstavacek") { if (mes.channel.type == ChannelType.DM) { mes.channel.send("drz hubu"); return true; } mes.delete().catch((e: Error) => { if (e.name == "DiscordAPIError[5OO13]") return; log("chyba pri mazani", e); }); sendDM(mes.author, "spis tak nepis"); return true; } return false; }, // autoSpink handeler on_userPresenceUpdate: async (bef, aft) => { if (!bef) return; const befoff = bef.status == "offline"; const aftoff = aft.status == "offline"; const prop = autoSpinky[aft.userId]; if (!prop) return; if (!befoff && aftoff) { return contactSpinkServer(Spinky.spinkacek, aft.member!.user); } if (befoff && !aftoff) { // autospink, ne autoautospink if (prop[0]) delete autoSpinky[aft.userId]; const kanel = prop[1]; if (kanel?.type == ChannelType.GuildText) kanel.send(await sendGmMessage(aft.member!.user)); } } }; if (!process.env.ignoreSpink) module.exports = exp;