316 lines
10 KiB
TypeScript
316 lines
10 KiB
TypeScript
// 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<NodeJS.Timeout> = {};
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zpracuje komplexní string pro pgn a vrátí počet milisekund, o kolik v budoucnu autor zamýšlel
|
|
* @param str
|
|
* @returns array, kde první informace je, zda-li byla informace relativní nebo absolutní a druhá je počet milisekund v budoucnoti
|
|
*/
|
|
function strToDate(str: string): ["rel" | "abs", number] | never[] {
|
|
const cas = new Date();
|
|
const dnes = new Date();
|
|
let neco: RegExpExecArray | null;
|
|
|
|
if (str == "") return [];
|
|
|
|
// Relativní čas
|
|
neco = /^(?:(?<h>\d+)h)?(?:(?<m>\d+)m)?(?:(?<s>\d+)s?)?$/.exec(str.toLowerCase());
|
|
if (neco) {
|
|
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+)\. ?((?<mo>\d+)\.)? ?)?(?<h>\d+)(:(?<m>\d+)(:(?<s>\d+))?)? ?(?<p>am|pm)?$/.exec(str.toLowerCase());
|
|
if (neco) {
|
|
const g = neco.groups!;
|
|
if (typeof g.m == "undefined" && typeof g.p == "undefined") return [];
|
|
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 {
|
|
if (kdy == "")
|
|
[druh, spinkZa] = ["rel", 2 * 60 * 60 * 1000];
|
|
else {
|
|
const back = strToDate(kdy);
|
|
if (typeof back[1] != "number") return "cos to napsal kkte";
|
|
[druh, spinkZa] = back;
|
|
}
|
|
}
|
|
|
|
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;
|