diff --git a/src/app.ts b/src/app.ts index c88cbcc..c89bc46 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,4 @@ -import { Client, Intents, Message, MessageActionRow, MessageButton } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GatewayIntentBits, Message, Partials } from "discord.js"; import { readdirSync } from "fs"; import { CUser, EventSOn, KomandNaExport, Komand, ListenerFunkce, Modul, SRecord, SuperListenerFunkce, CustomKomandy } from "./utils/types"; import { adminLog, getFirstArg, formatCas, loadEnv, oddiakritikovat, sortArr } from "./utils/utils.js"; @@ -10,10 +10,10 @@ const custom: CustomKomandy = require("./utils/customCommands"); loadEnv(); -const ints = Intents.FLAGS; +const ints = GatewayIntentBits; const client = new Client({ - partials: ["CHANNEL"], - intents: [ints.GUILDS, ints.GUILD_VOICE_STATES, ints.GUILD_PRESENCES, ints.GUILD_MESSAGES, ints.DIRECT_MESSAGES] + partials: [Partials.Channel], + intents: [ints.Guilds, ints.GuildVoiceStates, ints.GuildPresences, ints.GuildMessages, ints.DirectMessages, ints.MessageContent] }); const prefix = process.env.prefix || "more"; const modulFolder = `${__dirname}/modules/`; @@ -80,7 +80,7 @@ readdirSync(modulFolder).forEach(soubor => { const ev = groups.s ? superEventy : eventy; if (!ev[groups.h]) { ev[groups.h] = []; - if (!["message", "userPresenceUpdate"].includes(groups.h)) client.on(groups.h, (...args) => void runEvent(groups.h, args)); + if (!["messageCreate", "userPresenceUpdate"].includes(groups.h)) client.on(groups.h, (...args) => void runEvent(groups.h, args)); } const n = modul[name as EventSOn]!; @@ -154,9 +154,9 @@ client.on("messageCreate", async mes => { if (process.env.ignoreMess) return; const [mesPrefix, komandSDiakritikou, ...args] = mes.content.split(" "); - if (oddiakritikovat(mesPrefix.toLowerCase()) != prefix) return void runEvent("message", [mes]); + if (oddiakritikovat(mesPrefix.toLowerCase()) != prefix) return void runEvent("messageCreate", [mes]); if (!komandSDiakritikou) { - if (!runEvent("message", [mes])) mes.channel.send("coe voe"); + if (!runEvent("messageCreate", [mes])) mes.channel.send("coe voe"); return; } @@ -164,10 +164,10 @@ client.on("messageCreate", async mes => { const komandBez = oddiakritikovat(komandSDiakritikou).toLowerCase(); const cmdName = aliasy[komandBez] ?? customAliasy[komandBez] ?? komandBez; - if (runEvent("message", [mes, cmdName])) return; + if (runEvent("messageCreate", [mes, cmdName])) return; const komand = komandy[cmdName] ?? customKomandy[cmdName]; - if (mes.channel.type == "DM" && komand?.DMUnsafe) return void mes.channel.send("tuten komand bohuzel v sz nefunkuje"); + 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); @@ -180,10 +180,10 @@ client.on("messageCreate", async mes => { if (!slova.length) return void mes.channel.send("co to znamena ti gadzovko"); const nabidka = slova.sort().slice(0, 5); - const radek = new MessageActionRow(); + const radek = new ActionRowBuilder(); nabidka.forEach((cmd, i) => { const nazev = cmd.length > 80 ? `${cmd.slice(0, 77)}...` : cmd; - radek.addComponents(new MessageButton({ customId: `${i}`, label: nazev, style: "PRIMARY" })); + radek.addComponents(new ButtonBuilder({ customId: `${i}`, label: nazev, style: ButtonStyle.Primary })); }); const zprava = await mes.channel.send({ content: "nemnel sy na misli:", components: [radek] }); diff --git a/src/modules/anketa.ts b/src/modules/anketa.ts index 85b2cd7..5b67b5e 100644 --- a/src/modules/anketa.ts +++ b/src/modules/anketa.ts @@ -1,6 +1,6 @@ // Modul na komand "anketa" -import { Message, MessageActionRow, MessageButton, MessageComponentInteraction, MessageEmbedOptions } from "discord.js"; +import { ActionRowBuilder, APIEmbed, ButtonBuilder, ButtonStyle, Message, MessageComponentInteraction } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul } from "../utils/types"; import { formatter } from "../utils/utils"; @@ -35,12 +35,12 @@ const exp: Modul = { const konec = new Date(); konec.setSeconds(konec.getSeconds() + settings.time); - const embed: MessageEmbedOptions = { + const embed: APIEmbed = { title: "Hlasování", description: settings.immediateShow ? `Končí ${formatter(konec)}` : `Výsledky se zobrazí ${formatter(konec)}`, fields: [] }; - const radek = new MessageActionRow(); + const radek = new ActionRowBuilder(); const odpovedi: Record = {}; function prepocitat(): void; @@ -58,7 +58,7 @@ const exp: Modul = { moznosti.forEach((h, i) => { if (settings.immediateShow) embed.fields?.push({ name: h, value: "" }); - radek.addComponents(new MessageButton({ customId: `${i}`, label: h, style: "PRIMARY" })); + radek.addComponents(new ButtonBuilder({ customId: `${i}`, label: h, style: ButtonStyle.Primary })); }); prepocitat(); const zprava = await mes.channel.send({ embeds: [embed], components: [radek] }); @@ -68,9 +68,9 @@ const exp: Modul = { if (!(d.message instanceof Message)) return; const i = Number(d.customId); const prev = odpovedi[d.user.id]; - if (prev == i) return d.deferUpdate(); + if (prev == i) return void d.deferUpdate(); odpovedi[d.user.id] = i; - if (!settings.immediateShow) return d.deferUpdate(); + if (!settings.immediateShow) return void d.deferUpdate(); prepocitat(d); }); diff --git a/src/modules/komArgs.ts b/src/modules/komArgs.ts index cf4c596..c2cb802 100644 --- a/src/modules/komArgs.ts +++ b/src/modules/komArgs.ts @@ -1,10 +1,10 @@ // Komandy, který pošlou jenom celArgs a random hovno -import { Message } from "discord.js"; +import { ChannelType, Message } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul } from "../utils/types"; const delAndReturn = (mes: Message, co: string) => { - if (mes.channel.type != "DM") mes.delete(); + if (mes.channel.type != ChannelType.DM) mes.delete(); return co; }; diff --git a/src/modules/komComplex.ts b/src/modules/komComplex.ts index 8139e6b..3be0595 100644 --- a/src/modules/komComplex.ts +++ b/src/modules/komComplex.ts @@ -1,7 +1,7 @@ // Komandy, který buď nějakým způsobem mění funkci nebo "vzhled" bota // nebo donutí bota něco udělat (odeslání zprávy nebo smazání originální zprávy se nepočítá) -import { ActivityType, Message, MessageReaction, PresenceStatusData } from "discord.js"; +import { ActivityType, ChannelType, Message, MessageReaction, PresenceStatusData } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul, SRecord } from "../utils/types"; import { formatter, ping } from "../utils/utils"; @@ -11,8 +11,8 @@ const changeStatus = (mes: Message, status: PresenceStatusData) => { return "ano pane"; }; -const changeActivity = (mes: Message, activity: Exclude | undefined = undefined, txt: string = "") => { - mes.client.user?.setActivity({ name: txt, type: activity }); +const changeActivity = (mes: Message, activity: Exclude | undefined = undefined, txt: string = "") => { + mes.client.user?.setActivity(txt, { type: activity }); mes.react(emouty.d3k); return "ano pane"; }; @@ -39,10 +39,10 @@ const exp: Modul = { run: mes => changeStatus(mes, "invisible") }, - hraj: (mes, co) => changeActivity(mes, "PLAYING", co), - sleduj: (mes, co) => changeActivity(mes, "WATCHING", co), - poslouchej: (mes, co) => changeActivity(mes, "LISTENING", co), - soutez: (mes, vcem) => changeActivity(mes, "COMPETING", vcem), + hraj: (mes, co) => changeActivity(mes, ActivityType.Playing, co), + sleduj: (mes, co) => changeActivity(mes, ActivityType.Watching, co), + poslouchej: (mes, co) => changeActivity(mes, ActivityType.Listening, co), + soutez: (mes, vcem) => changeActivity(mes, ActivityType.Competing, vcem), nedelej: mes => changeActivity(mes), fight: { @@ -99,7 +99,7 @@ const exp: Modul = { } const reakce: Promise[] = []; - if (mes.channel.type != "DM") mes.delete(); + if (mes.channel.type != ChannelType.DM) mes.delete(); emouty.forEach(emout => { reakce.push(naCo.react(emout) .catch(() => { })); diff --git a/src/modules/muzika.ts b/src/modules/muzika.ts index 6cc73fd..14128d1 100644 --- a/src/modules/muzika.ts +++ b/src/modules/muzika.ts @@ -1,37 +1,128 @@ // Tady bude muzika, vole +import { AudioPlayerStatus, VoiceConnection } from "@discordjs/voice"; import { search, soundcloud, stream, validate, video_basic_info } from "play-dl"; -import { Modul } from "../utils/types"; -import { joinVoice, play } from "../utils/utils"; +import { emouty } from "../utils/emotes"; +import { Modul, SRecord } from "../utils/types"; +import { configureTimeAnouncment, getCurrentPlayer, joinVoice, play } from "../utils/utils"; + +const kjus: SRecord<{ name: string; url: string; }[]> = {}; + +async function zahrat(conn: VoiceConnection, url: string, seek?: number) { + const src = await stream(url, { seek }); + play(conn, { name: src.stream, volume: 1, type: src.type }); +} const exp: Modul = { more_komandy: { zahraj: { DMUnsafe: true, run: async (mes, txt) => { - let url: string; + const kanel = mes.member?.voice.channel; + if (!kanel) return "nejsi ve vojsu ty kkt"; + + const ajtem = { name: "", url: "" }; const druh = await validate(txt); if (druh && druh != "search") { if (druh != "yt_video" && druh != "so_track") return "tuto neumim zahrat"; - url = txt; + ajtem.url = txt; if (druh == "yt_video") { - video_basic_info(url).then(v => mes.channel.send(`hraju \`${v.video_details.title}\``)); + video_basic_info(txt).then(v => { + ajtem.name = v.video_details.title!; + mes.channel.send(`zahraju \`${ajtem.name}\``); + }); } else { - soundcloud(url).then(s => mes.channel.send(`hraju \`${s.name}\``)); + soundcloud(txt).then(s => { + ajtem.name = s.name; + mes.channel.send(`zahraju \`${s.name}\``); + }); } } else { const msg = mes.channel.send("hledam"); const hledani = await search(txt, { limit: 1 }); - url = hledani[0].url; - msg.then(m => m.edit(`hraju \`${hledani[0].title}\``)); + ajtem.url = hledani[0].url; + ajtem.name = hledani[0].title!; + msg.then(m => m.edit(`zahraju \`${ajtem.name}\``)); } - const kanel = mes.member?.voice.channel; - if (!kanel) return "nejsi ve vojsu ty kkt"; - const { conn } = await joinVoice(kanel); + const kju = (kjus[mes.guildId!] ??= []); + kju.push(ajtem); - const src = await stream(url); - play(conn, { name: src.stream, volume: 1, type: src.type }); + if (kju.length != 1) return; + + configureTimeAnouncment(mes.guildId!, false); + + const { conn } = await joinVoice(kanel); + const player = getCurrentPlayer(mes.guildId!)!; + + zahrat(conn, ajtem.url); + + player.on(AudioPlayerStatus.Idle, () => { + kju.splice(0, 1); + if (kju.length == 0) { + delete kjus[mes.guildId!]; + configureTimeAnouncment(mes.guildId!, true); + return; + } + zahrat(conn, kju[0].url); + }); + } + }, + + queue: { + als: ["q", "kju"], + DMUnsafe: true, + run: mes => { + const kju = kjus[mes.guildId!]; + if (!kju) return "nehraje nic"; + + return `tuto je kurentni repertoar:\n${kju.reduce((acc, cur, i) => { + return `${acc}\n${i ? `${i}. ${cur.name}` : `-- *${cur.name}* --`}`; + }, "")}`; + } + }, + + skip: { + DMUnsafe: true, + run: mes => { + const kju = kjus[mes.guildId!]; + if (!kju) return "nic nehraje"; + + const player = getCurrentPlayer(mes.guildId!); + player.stop(); + mes.react(emouty.d3k); + } + }, + + remove: { + DMUnsafe: true, + als: ["odebrat"], + run: (mes, arg) => { + const numero = Number(arg); + if (isNaN(numero)) return "cokundo?"; + + const kju = kjus[mes.guildId!]; + if (!kju) return "nic nehraje"; + + if (kju.length <= numero) return "tolik toho nehrae"; + + kju.splice(numero, 1); + mes.react(emouty.d3k); + } + }, + + seek: { + DMUnsafe: true, + run: (mes, arg) => { + const numero = Number(arg); + if (isNaN(numero)) return "huh?"; + + const kju = kjus[mes.guildId!]; + if (!kju) return "nehraje nic"; + + const conn = getCurrentPlayer(mes.guildId!)!.playable[0]; + zahrat(conn, kju[0].url, numero); + mes.react(emouty.d3k); } } } diff --git a/src/modules/novaOrdinacka.ts b/src/modules/novaOrdinacka.ts new file mode 100644 index 0000000..1020204 --- /dev/null +++ b/src/modules/novaOrdinacka.ts @@ -0,0 +1,62 @@ +import { Client, TextChannel } from "discord.js"; +import fetch from "node-fetch"; +import { emouty } from "../utils/emotes"; +import { Modul } from "../utils/types"; + +let posledniDatum = ""; +let client: Client; + +function naplanovat(): void; +function naplanovat(jesteNeska: true): void; +function naplanovat(jesteNeska?: true) { + const novyCas = new Date(); + const den = novyCas.getDay(); + + if (jesteNeska && den == 4) return hendelieren(); + + novyCas.setDate(novyCas.getDate() + ((den < 4 ? 4 : 11) - den)); + novyCas.setHours(15); + novyCas.setMinutes(0); + setTimeout(hendelieren, Number(novyCas) - Number(new Date())); +} + +const padNula = (n: number) => n > 9 ? n : `0${n}`; + +function hendelieren() { + fetch("https://sktorrent.eu/torrent/torrents_v2.php?search=ordinace") + .then(r => r.text()) + .then(txt => { + const tabule = txt.match(/(?.+)<\/table>/is); + if (!tabule) return console.log("nenašel jsem tabuleho"); + + const detaily = tabule.groups!.kontent.match(/\d\d\/\d\d\/\d{4})/is); + const datum = detaily?.groups!.datum!; + + if (posledniDatum == "") { + const d = new Date(); + if (`${padNula(d.getDate())}/${padNula(d.getMonth() + 1)}/${d.getFullYear()}` == datum) { + return naplanovat(); + } + + posledniDatum = datum; + return setTimeout(hendelieren, 18e4); + } + + if (posledniDatum == datum) return setTimeout(hendelieren, 18e4); + + (client.channels.cache.get("555779161067749448") as TextChannel).send(`<@&591306633196339261> vyšla nová vordinačka ${emouty.lajk}\nhttps://sktorrent.eu/${detaily?.groups?.odkaz}`); + naplanovat(); + return; + }); +} + +const exp: Modul = { + + on_ready: () => { + client = module.exports.client; + + naplanovat(true); + } +}; + +module.exports = exp; diff --git a/src/modules/spink.ts b/src/modules/spink.ts index 4bf4164..9546428 100644 --- a/src/modules/spink.ts +++ b/src/modules/spink.ts @@ -1,6 +1,6 @@ // Modul dedikovaný funkci spinkáček -import { Message, Presence, Role, TextBasedChannel, User, VoiceState } from "discord.js"; +import { ChannelType, Message, Presence, Role, TextBasedChannel, User, VoiceState } from "discord.js"; import fetch from "node-fetch"; import { emouty } from "../utils/emotes"; import { Modul, Spinkackar, SRecord } from "../utils/types"; @@ -174,7 +174,7 @@ const exp: Modul = { .catch(err => console.log("spinkacek odpojit se nepovedlo nebo co:", err)); }, - super_on_message: (mes: Message, cmd?: string) => { + super_on_messageCreate: (mes: Message, cmd?: string) => { if (mes.author.id === '831318260493844494') { syncSpink(); const uzivatel = mes.content.match(/(?<=discord_)\d+/)?.[0]; @@ -205,7 +205,7 @@ const exp: Modul = { continue; } for (const [, channel] of guild.channels.cache) { - if (channel.type !== "GUILD_VOICE") continue; + if (channel.type != ChannelType.GuildVoice) continue; for (const [, member] of channel.members) { if (member.id !== uzivatel) continue; member.voice.disconnect("spinkacek"); @@ -214,6 +214,8 @@ const exp: Modul = { } } } else if (spinkacky[mes.author.id]?.spinkacek && cmd != "vstavacek") { + if (mes.channel.type == ChannelType.DM) return mes.channel.send("drz hubu"); + mes.delete(); (async () => { const chanel = mes.author.dmChannel ?? await mes.author.createDM(); @@ -235,7 +237,7 @@ const exp: Modul = { if (befoff && !aftoff) { if (prop[0]) delete autoSpinky[aft.userId]; const kanel = prop[1]; - if (kanel?.isText()) kanel.send(await tovjemamvolepreceroliky(aft.member!.user)); + if (kanel?.type == ChannelType.GuildText) kanel.send(await tovjemamvolepreceroliky(aft.member!.user)); } } }; diff --git a/src/modules/vojs.ts b/src/modules/vojs.ts index 2274c4c..1b2aacf 100644 --- a/src/modules/vojs.ts +++ b/src/modules/vojs.ts @@ -1,14 +1,14 @@ // Cokoliv co má něco společnýho s vojsem import { getVoiceConnection, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice"; -import { GuildMember, VoiceChannel } from "discord.js"; +import { ChannelType, GuildMember, VoiceChannel } from "discord.js"; import { emouty } from "../utils/emotes"; import { Modul, MuzikaFace, SRecord } from "../utils/types"; -import { handlePrevVoice, joinVoice, play } from "../utils/utils"; +import { canAnounceTime, handlePrevVoice, joinVoice, play } from "../utils/utils"; const timeouty: SRecord = {}; -const vypocitatCas = (conn: VoiceConnection) => { +const vypocitatCas = (guild: string, conn: VoiceConnection) => { const c = new Date(); const d = new Date(); let hod = d.getHours(); @@ -28,17 +28,19 @@ const vypocitatCas = (conn: VoiceConnection) => { d.setMinutes(min); d.setSeconds(0); - timeouty[conn.joinConfig.guildId] = setTimeout(() => rekniCas(conn, `${nula(hod)}${nula(min)}`), Number(d) - Number(c)); + timeouty[conn.joinConfig.guildId] = setTimeout(() => rekniCas(guild, conn, `${nula(hod)}${nula(min)}`), Number(d) - Number(c)); }; const nula = (a: number) => a < 10 ? `0${a}` : a; -const rekniCas = (conn: VoiceConnection, cas: string) => { - play(conn, [{ name: "zvuky/intro.mp3", volume: 0.8 }, { name: `zvuky/${cas}.mp3`, volume: 2 }, { name: "zvuky/grg.mp3", volume: 0.5 }]) - .then(() => { - vypocitatCas(conn); - }) - .catch(err => { console.log("cas error:", err); }); +const rekniCas = (guild: string, conn: VoiceConnection, cas: string) => { + if (canAnounceTime(guild)) + play(conn, [{ name: "zvuky/intro.mp3", volume: 0.8 }, { name: `zvuky/${cas}.mp3`, volume: 2 }, { name: "zvuky/grg.mp3", volume: 0.5 }]) + .then(() => { + vypocitatCas(guild, conn); + }) + .catch(err => { console.log("cas error:", err); }); + else vypocitatCas(guild, conn); }; const vytahnout = (member: GuildMember, patro: number) => { @@ -50,7 +52,7 @@ const vytahnout = (member: GuildMember, patro: number) => { if (patro < aktPatro) dalsiPatro--; else if (patro > aktPatro) dalsiPatro++; else return; if (dalsiPatro === 0) dalsiPatro = "P"; - const dalsiVojs = member.guild.channels.cache.filter(channel => channel.type === "GUILD_VOICE" && channel.name === String(dalsiPatro)).first() as VoiceChannel | undefined; + const dalsiVojs = member.guild.channels.cache.filter(channel => channel.type == ChannelType.GuildVoice && channel.name === String(dalsiPatro)).first() as VoiceChannel | undefined; if (!dalsiVojs) return; member.voice.setChannel(dalsiVojs); @@ -77,8 +79,8 @@ const exp: Modul = { .then(obj => { const { prev, conn } = obj; if (!timeouty[mes.guildId!]) { - vypocitatCas(conn); - conn.on<"stateChange">("stateChange", (_, now) => { + vypocitatCas(mes.guildId!, conn); + conn.on("stateChange", (_, now) => { if (now.status !== VoiceConnectionStatus.Disconnected && now.status !== VoiceConnectionStatus.Destroyed) return; clearTimeout(timeouty[mes.guildId!]); delete timeouty[mes.guildId!]; diff --git a/src/modules/zbytek.ts b/src/modules/zbytek.ts index c136c27..76dc2a2 100644 --- a/src/modules/zbytek.ts +++ b/src/modules/zbytek.ts @@ -46,7 +46,7 @@ const exp: Modul = { } }, - super_on_message: { + super_on_messageCreate: { pos: 0, fun: (mes: Message) => { const cont = mes.content.toLowerCase(); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 7383e59..5cdb33c 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,11 +1,12 @@ import { AudioPlayerStatus, AudioResource, createAudioPlayer, createAudioResource, entersState, getVoiceConnection, joinVoiceChannel, PlayerSubscription, StreamType, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice"; -import { Client, Guild, StageChannel, VoiceChannel } from "discord.js"; +import { ChannelType, Client, Guild, StageChannel, VoiceChannel } from "discord.js"; import { once } from "events"; import { JoinHovna, KomandNaExport, MuzikaFace, RunFunkce, SRecord } from "./types"; import { existsSync } from "fs"; import { Readable } from "node:stream"; const pripojeni: SRecord = {}; +const timeAnouncability: Record = {}; export const oddiakritikovat = (a: string) => { return a @@ -183,7 +184,7 @@ export const ping = /^<@!?\d+>$/; export function adminLog(client: Client, text: string) { if (process.env.adminChannel) { const adminChannel = client.channels.cache.get(process.env.adminChannel); - if (adminChannel?.isText()) + if (adminChannel?.type == ChannelType.GuildText) adminChannel.send(text); } if (process.env.adminID) { @@ -202,3 +203,15 @@ export function getFirstArg(fn: RunFunkce | string) { ?.split(",")[1] ?.trim(); } + +export function getCurrentPlayer(guildId: string) { + return pripojeni[guildId]?.player; +} + +export function configureTimeAnouncment(guild: string, state: boolean) { + timeAnouncability[guild] = state; +} + +export function canAnounceTime(guild: string) { + return (timeAnouncability[guild] ?? true); +}