diff --git a/example.config.json b/example.config.json index 084894e..051d754 100644 --- a/example.config.json +++ b/example.config.json @@ -6,5 +6,7 @@ "ignoreSpink": "nepovinné, boolean - veškerá funkcionalita spojená se spinkáčkem nebude zavedena", "adminChannel": "nepovinné, string - ID kanálu, odkud půjde spustit komand update a pokud je bot spuštěn přes start.sh a bot spadne po 24 hodinách, pošle sem zprávu, která o tom informuje", "adminID": "nepovinné, string - ID uživatele, který bude moci spustit komand update a pokud je bot spuštěn přes start.sh a bot spadne po 24 hodinách, pošle mu zprávu, která o tom informuje", - "dieOnError": "nepovinné, boolean - pokud nastane chyba při exekutování komandu, která by bota normálně shodila, bot se vypne" + "dieOnError": "nepovinné, boolean - pokud nastane chyba při exekutování komandu, která by bota normálně shodila, bot se vypne", + "DBUser": "povinné, string - uživatelské jméno pro připojení do databáze", + "DBPwd": "povinné, string - heslo pro připojení do databáze" } diff --git a/package-lock.json b/package-lock.json index c0bb2fd..cffbd83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@discordjs/voice": "^0.16.0", "discord.js": "^14.11.0", "js-levenshtein": "^1.1.6", + "mysql": "^2.18.1", "node-fetch": "^2.6.1", "opusscript": "^0.1.0", "play-dl": "^1.9.6", @@ -21,6 +22,7 @@ "devDependencies": { "@types/jest": "^29.5.3", "@types/js-levenshtein": "^1.1.1", + "@types/mysql": "^2.15.21", "@types/node-fetch": "^2.6.4", "jest": "^29.6.1" } @@ -1232,6 +1234,15 @@ "integrity": "sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==", "dev": true }, + "node_modules/@types/mysql": { + "version": "2.15.21", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.21.tgz", + "integrity": "sha512-NPotx5CVful7yB+qZbWtXL2fA4e7aEHkihHLjklc6ID8aq7bhguHgeIoC1EmSNTAuCgI6ZXrjt2ZSaXnYX0EUg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", @@ -1455,6 +1466,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1691,6 +1710,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2236,6 +2260,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3100,6 +3129,47 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "dependencies": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mysql/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/mysql/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/mysql/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3382,6 +3452,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -3596,6 +3671,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", diff --git a/package.json b/package.json index 85352d0..8b67f9a 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@discordjs/voice": "^0.16.0", "discord.js": "^14.11.0", "js-levenshtein": "^1.1.6", + "mysql": "^2.18.1", "node-fetch": "^2.6.1", "opusscript": "^0.1.0", "play-dl": "^1.9.6", @@ -26,6 +27,7 @@ "devDependencies": { "@types/jest": "^29.5.3", "@types/js-levenshtein": "^1.1.1", + "@types/mysql": "^2.15.21", "@types/node-fetch": "^2.6.4", "jest": "^29.6.1" } diff --git a/src/app.ts b/src/app.ts index 734e99c..7cba00f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -303,6 +303,8 @@ client.on("messageCreate", async mes => { 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); diff --git a/src/modules/custom.ts b/src/modules/custom.ts index 63af466..1afd8f1 100644 --- a/src/modules/custom.ts +++ b/src/modules/custom.ts @@ -256,14 +256,20 @@ const exp: Modul = { const cmdName = client.aliasy[name] ?? name; let celkovadylka = 0; - const stranky: string[][] = []; + const stranky: string[][] = [[]]; + let aktualniStranka = 0; for (const key in client.aliasy) { const val = client.aliasy[key]; if (val != cmdName) continue; - celkovadylka += key.length + 3; - if (stranky[Math.floor(celkovadylka / 1024)]?.length == 20) celkovadylka += 1024 - (celkovadylka % 1024); - const stranka = stranky[Math.floor(celkovadylka / 1024)] ??= []; + const dylka = key.length + 3; + celkovadylka += dylka; + if (celkovadylka > 1024 || stranky[aktualniStranka].length == 20) { + aktualniStranka++; + celkovadylka = dylka; + } + + const stranka = stranky[aktualniStranka] ??= []; stranka.push(key); } diff --git a/src/modules/registerSlashCommands.ts b/src/modules/registerSlashCommands.ts index cf367bb..a7206e9 100644 --- a/src/modules/registerSlashCommands.ts +++ b/src/modules/registerSlashCommands.ts @@ -1,6 +1,6 @@ import { ApplicationCommandOptionType, REST, RESTPostAPIChatInputApplicationCommandsJSONBody, Routes } from 'discord.js'; import { CClient, Modul, SRecord } from '../utils/types'; -import { prefix } from '../utils/utils'; +import { log, prefix } from '../utils/utils'; const komandy: RESTPostAPIChatInputApplicationCommandsJSONBody[] = [ { @@ -32,6 +32,71 @@ const komandy: RESTPostAPIChatInputApplicationCommandsJSONBody[] = [ ] }, + { + name: `${prefix}vyhledej`, + description: "vihledavani tak dobri ze je dyckscord muze jenom zavydet", + dm_permission: false, + options: [ + { + name: "kontent", + description: "nejaki pysmenka kteri ve spravje jsou v tutom poradi", + type: ApplicationCommandOptionType.String, + required: false + }, + { + name: "regex", + description: "regulerni exprese ktera se aplykuje na obsach spravy (hestly nevys co to je pouzij radsi 'kontent')", + type: ApplicationCommandOptionType.String, + required: false + }, + { + name: "uzivatel", + description: "kerej kokot spavu posral", + type: ApplicationCommandOptionType.User, + required: false + }, + { + name: "od", + description: "vihledat zpravy mladsy nez tuten datum\npotporuju vsechni formati jako new Date() v jávaskryptech", + type: ApplicationCommandOptionType.String, + required: false + }, + { + name: "do", + description: "vihledat zpravy starsi nez tuten datum\npotporuju vsechni formati jako new Date() v jávaskryptech", + type: ApplicationCommandOptionType.String, + required: false + }, + { + name: "obrasek", + description: "estli sprava ma nejakej obrasek (a jakoze fakt vobrasek, ne otkas na gif)", + type: ApplicationCommandOptionType.Boolean, + required: false + }, + { + name: "link", + description: "estli sprava ma link", + type: ApplicationCommandOptionType.Boolean, + required: false + }, + { + name: "seradit", + description: "jak visletki poskladat", + type: ApplicationCommandOptionType.String, + choices: [{ + name: "ot polen co nejlip horej", + value: "ASC" + }, + { + name: "ot tech nejvic fres", + value: "DESC" + } + ], + required: false + } + ] + }, + { name: `${prefix}jehokomandy`, description: "napysu ti koamndy kteri udelal kkt kteriho viberes", diff --git a/src/modules/vordinace.ts b/src/modules/vordinace.ts index bcbe23d..59b2113 100644 --- a/src/modules/vordinace.ts +++ b/src/modules/vordinace.ts @@ -53,11 +53,11 @@ async function urobit() { channel: "891067481651839037" }); - const heneral2 = guilda.channels.cache.get("555779161067749448") as TextChannel; + const general = guilda.channels.cache.get("555779161067749448") as TextChannel; - if (!heneral2) return log(new Error("heneral nenalezen")); + if (!general) return log(new Error("general nenalezen")); - heneral2.send(`<@1123186280843444274> ${event.url}`); + general.send(`<@&1123186280843444274> ${event.url}`); } const exp: Modul = { diff --git a/src/modules/vyhledavac.ts b/src/modules/vyhledavac.ts new file mode 100644 index 0000000..0e3fcc0 --- /dev/null +++ b/src/modules/vyhledavac.ts @@ -0,0 +1,175 @@ +import { APIEmbed, CommandInteractionOption, Message, TextBasedChannel } from "discord.js"; +import { CClient, Modul } from "../utils/types"; +import { createPool } from "mysql"; +import { strankovani } from "../utils/utils"; + +// Připojení do databáze +const pool = createPool({ + host: "127.0.0.1", + user: process.env.DBUser, + password: process.env.DBPwd, + database: "zalohaGeneralu", + charset: "utf8mb4_unicode_ci" +}); + +const zpracovatZpravu = (mes: Message) => new Promise(async (res, rej) => { + console.log("zpracovavam", mes.id); + if ([6, 7, 8, 9, 10, 11, 18, 23].includes(mes.type)) return res(); + if (mes.type != 0 && mes.type != 20 && mes.type != 19) throw new Error("Neznámej message type " + mes.type.toString()); + + const reference = mes.reference?.messageId; + let attachments: { name: string; file: string; }[] = []; + + if (mes.attachments.size != 0) { + + for (const [_, attachment] of mes.attachments) { + attachments.push({ name: attachment.name, file: attachment.id }); + } + } + + const sql = `INSERT INTO Zpravy (ID, Content, Author ${reference ? ", Reference" : ""} , Attachments) VALUES (${mes.id}, ?, ${mes.author.id} ${reference ? `,${reference}` : ""} , ?)`; + + pool.query({ sql, values: [mes.content, JSON.stringify(attachments)] }, err => { + if (err) rej(err); + res(); + }); +}); + +const porovnat = (filtr: CommandInteractionOption): string[] => { + switch (filtr.name) { + case "kontent": return [`Content LIKE ?`, `%${filtr.value}%`]; + case "regex": return [`Content REGEXP ?`, `${filtr.value}`]; + case "uzivatel": return [`Author=?`, `${filtr.user!.id}`]; + case "od": + case "do": { + const datum = new Date(filtr.value as string); + const cislo = (Number(datum) - 1420070400000) * 4194304; + return [`ID ${filtr.name == "od" ? ">" : "<"} ${cislo}`]; + } + case "obrasek": return [`${filtr.value ? "" : " NOT"}Attachments REGEXP "\.(png|jpe?g|gif)"`]; + case "obrazekOdkaz": return [`${filtr.value ? "" : " NOT"}Content REGEXP "\.(png|jpe?g|gif)"`]; + case "link": return [`${filtr.value ? "" : " NOT"}Content REGEXP "https?://"`]; + } + return [""]; +}; + +const exp: Modul = { + more_komandy: { + vyhledej: { + run: () => { + const client: CClient = module.exports.client; + + return `tuto je novej komand a histmaj mrdal aby to slo i takhle proste pouzij `; + }, + + slashRun: async int => { + const moznosti = int.options; + const filtry = moznosti.data; + console.log(filtry); + if (!filtry.length) return "co takhle tam alespon jeden filtr"; + + // Nelgel kombinace a tak + if (moznosti.get("kontent") && moznosti.get("regex")) return "kontent a regex nejde kombynovat"; + const od = moznosti.get("od"); + if (od && isNaN(Number(new Date(od.value as string)))) return "od neny platni datum"; + const dod = moznosti.get("do"); + if (dod && isNaN(Number(new Date(dod.value as string)))) return "do neny platni datum"; + + const parsedFiltry: string[] = []; + const mista: string[] = []; + let seradit = "DESC"; + + for (const filtr of filtry) { + if (filtr.name != "seradit") { + const [parsed, misto] = porovnat(filtr); + parsedFiltry.push(parsed); + if (misto) mista.push(misto); + } else { + seradit = filtr.value as string; + } + } + + const odpoved = await int.reply("hledam"); + + const sql = `SELECT CONVERT(ID, CHAR) as idcko, SUBSTRING(Content, 1, 20) AS kontent FROM Zpravy WHERE ${parsedFiltry.join(" AND ")} ORDER BY ID ${seradit}`; + console.log(sql, mista); + pool.query({ sql, values: mista }, (err, res?: { idcko: string; kontent: string; }[]) => { + if (err) return odpoved.edit(`chyba: ${err}`); + + if (!res || !res.length) return odpoved.edit("nic takoviho sem nenasel"); + + odpoved.edit("nasel sem tuto:"); + + console.log(res); + + let celkovadylka = 0; + const stranky: string[][] = [[]]; + let aktualniStranka = 0; + for (const row of res) { + const vlozka = `[${row.kontent}] https://discord.com/channels/555779161067749446/555779161067749448/${row.idcko}`; + + const dylka = vlozka.length + 3; + celkovadylka += dylka; + if (celkovadylka > 1024 || stranky[aktualniStranka].length == 20) { + aktualniStranka++; + celkovadylka = dylka; + } + + const stranka = stranky[aktualniStranka] ??= []; + + stranka.push(vlozka); + } + + const zacatekNazvu = "visltki"; + const embed: APIEmbed = { + color: Math.random() * 16777215, + fields: [{ name: zacatekNazvu, value: `• ${stranky[0].join("\n• ")}` }] + }; + + if (stranky.length == 1) return int.channel?.send({ embeds: [embed] }); + + embed.fields![0].name += ` (1/${stranky.length})`; + + strankovani(int.channel!, embed, zacatekNazvu, stranky); + }); + } + } + }, + + on_ready: () => { + const client = module.exports.client as CClient; + + client.channels.fetch("555779161067749448") + .then(async ch => { + const channel = ch as TextBasedChannel; + let posledni: string | undefined; + + while (true) { + const msgs = await (channel.messages.fetch(posledni ? { before: posledni } : undefined)); + + for (const [_, mes] of msgs) { + + try { + await zpracovatZpravu(mes); + } catch { + console.log("zkonciclii sme"); + return; + } + } + + posledni = msgs.last()!.id; + + // Radši + await new Promise(res => setTimeout(res, 500)); + } + + }); + }, + + on_messageCreate: (mes: Message) => { + if (mes.channelId == "555779161067749448") + zpracovatZpravu(mes); + } +}; + +module.exports = exp; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 629e399..4700e57 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -172,6 +172,7 @@ export const strankovani = (channel: TextBasedChannel, embed: APIEmbed, zacatekN // Odeslání i.update({ embeds: [embed], components: [radek] }); }, - onEnd: () => { } + konecnaZprava: "", + stejne: true }); };