tak teď to je dokonalý. Už to nemůže bejt lepší

clueless
This commit is contained in:
Histmy 2023-07-27 14:11:50 +02:00
parent 88a82655af
commit f216d445dd
5 changed files with 104 additions and 81 deletions

View File

@ -1,38 +1,37 @@
// Tady bude muzika, vole
import { AudioPlayerStatus, getVoiceConnection } from "@discordjs/voice";
import { Client, VoiceBasedChannel } from "discord.js";
import { AudioPlayerStatus, VoiceConnectionStatus } from "@discordjs/voice";
import { Client } from "discord.js";
import { search, soundcloud, stream, validate, video_basic_info } from "play-dl";
import { emouty } from "../utils/emotes";
import { Modul, SRecord } from "../utils/types";
import { Modul } from "../utils/types";
import { adminLog, log } from "../utils/utils";
import { novejPlay, Priority, stopPlayer } from "../utils/voice";
import { getConn, novejJoin, novejPlay, Priority, stopPlayer } from "../utils/voice";
const kjus: SRecord<{ name: string; url: string; }[]> = {};
const guildy = new Map<string, { kju: { name: string; url: string; }[]; loop: boolean; }>();
async function playNext(channel: VoiceBasedChannel, seek?: number) {
async function playNext(guildId: string, seek?: number) {
try {
const kju = kjus[channel.guildId]!;
const guilda = guildy.get(guildId)!;
const kju = guilda.kju;
const item = kju[0];
const src = await stream(item.url, { seek });
novejPlay(channel, { src: src.stream, volume: 1, type: src.type }, Priority.Music)
novejPlay(guildId, { src: src.stream, volume: 1, type: src.type }, Priority.Music)
.then(state => {
if (state.status != AudioPlayerStatus.Idle) return;
kju.shift();
if (!guilda.loop) kju.shift();
if (kju.length)
return playNext(channel);
delete kjus[channel.guildId];
return playNext(guildId);
})
.catch(e => {
log("chyba v muziki", e);
});
} catch (e) {
if (e instanceof Error && e.message.startsWith("Seeking beyond limit"))
return stopPlayer(channel.guildId, Priority.Music);
return stopPlayer(guildId, Priority.Music);
log(e);
const client: Client = module.exports.client;
@ -45,9 +44,10 @@ const exp: Modul = {
zahraj: {
DMUnsafe: true,
run: async (mes, txt) => {
const kanel = mes.member?.voice.channel;
const kanel = mes.member?.voice.channelId;
if (!kanel) return "nejsi ve vojsu ty kkt";
const guildId = mes.guildId!;
const ajtem = { name: "", url: "" };
const druh = await validate(txt);
if (druh && druh != "search") {
@ -79,12 +79,19 @@ const exp: Modul = {
msg.then(m => void m.edit(`zahraju \`${ajtem.name}\``));
}
const kju = (kjus[mes.guildId!] ??= []);
if (!guildy.has(guildId)) {
guildy.set(guildId, { kju: [], loop: false });
}
const kju = guildy.get(guildId)!.kju;
kju.push(ajtem);
if (kju.length != 1) return;
playNext(mes.member.voice.channel);
await novejJoin(mes.guild!, kanel);
getConn(guildId)?.once(VoiceConnectionStatus.Destroyed, () => {
kju.length = 0;
});
playNext(guildId);
}
},
@ -92,8 +99,8 @@ const exp: Modul = {
als: ["q", "kju"],
DMUnsafe: true,
run: mes => {
const kju = kjus[mes.guildId!];
if (!kju) return "nehraje nic";
const kju = guildy.get(mes.guildId!)?.kju;
if (!kju?.length) return "nehraje nic";
return `tuto je kurentni repertoar:\n${kju.reduce((acc, cur, i) => {
const jmeno = `${cur.name} (<${cur.url}>)`;
@ -105,8 +112,8 @@ const exp: Modul = {
skip: {
DMUnsafe: true,
run: mes => {
const kju = kjus[mes.guildId!];
if (!kju) return "nic nehraje";
const kju = guildy.get(mes.guildId!)?.kju;
if (!kju?.length) return "nehraje nic";
stopPlayer(mes.guildId!, Priority.Music);
mes.react(emouty.d3k);
@ -120,8 +127,8 @@ const exp: Modul = {
const numero = Number(arg);
if (isNaN(numero)) return "cokundo?";
const kju = kjus[mes.guildId!];
if (!kju) return "nic nehraje";
const kju = guildy.get(mes.guildId!)?.kju;
if (!kju?.length) return "nehraje nic";
if (kju.length <= numero) return "tolik toho nehrae";
@ -136,14 +143,27 @@ const exp: Modul = {
const numero = Number(arg);
if (isNaN(numero)) return "huh?";
const kju = kjus[mes.guildId!];
if (!kju) return "nehraje nic";
const kju = guildy.get(mes.guildId!)?.kju;
if (!kju?.length) return "nehraje nic";
const channel = mes.client.channels.cache.get(getVoiceConnection(mes.guildId!)?.joinConfig.channelId ?? "") as VoiceBasedChannel;
if (!channel) return "neco nefunguje";
playNext(channel, numero);
playNext(mes.guildId!, numero);
mes.react(emouty.d3k);
}
},
loop: {
DMUnsafe: true,
run: mes => {
if (!guildy.has(mes.guildId!)) {
guildy.set(mes.guildId!, { kju: [], loop: false });
}
const guilda = guildy.get(mes.guildId!)!;
guilda.loop = !guilda.loop;
return `lup je ${guilda.loop ? "zapnutej" : "ukoncen"}`;
}
}
}
};

View File

@ -26,14 +26,18 @@ const vytahnout = (member: GuildMember, patro: number) => {
setTimeout(() => vytahnout(member, patro), 1e3);
};
async function playAndFukOff(mes: Message, jinak: string, zvuk: string) {
async function playAndFukOff(mes: Message, jinak: string, zvuk: string | Hratelny[]) {
const channel = mes.member?.voice.channel;
if (!channel) return jinak;
await novejPlay(channel, zvuk, Priority.Etc, true)
const prev = await novejJoin(mes.guild!, channel.id);
await novejPlay(mes.guildId!, zvuk, Priority.Etc)
.catch(e => {
log("Pičo chyba při fukofování", e);
});
handlePrevVoice(prev, mes.guild!);
}
const exp: Modul = {
@ -51,9 +55,9 @@ const exp: Modul = {
const nahlas = !arg.startsWith("potichu");
if (nahlas) mes.channel.send("<@&591306633196339261> vojs");
novejJoin(channel)
novejJoin(mes.guild!, channel.id)
.then(prev => {
if (prev != true && nahlas) novejPlay(channel, "zvuky/nazdar.ogg", Priority.Etc);
if (prev != true && nahlas) novejPlay(mes.guildId!, "zvuky/nazdar.ogg", Priority.Etc);
});
}
},
@ -110,7 +114,7 @@ const exp: Modul = {
const je = h > 1 && h < 5 ? "jsou" : "je";
const channel = mes.member?.voice.channel;
if (!channel) return `${je} ${h} ${hod} ${m} ${min}`;
if (!channel) return;
const zvuky: Hratelny[] = [{ src: `${pre}${je}.mp3`, volume: 2.5 }];
@ -121,12 +125,7 @@ const exp: Modul = {
zvuky.push({ src: `${pre}${min}.mp3`, volume: 2.5 });
}
const prev = await novejJoin(channel);
await novejPlay(channel, zvuky, Priority.Etc)
.catch(e => {
log("chyba tady, si to najdi", e);
});
handlePrevVoice(prev, mes.guildId!);
playAndFukOff(mes, `${je} ${h} ${hod} ${m} ${min}`, zvuky);
}
},
@ -158,11 +157,8 @@ const exp: Modul = {
const conn = getVoiceConnection(aft.guild.id);
if (!aft.channel || !conn || aft.member?.user.id == aft.client.user?.id) return;
const channel = aft.client.channels.cache.get(conn.joinConfig.channelId ?? "") as VoiceBasedChannel;
if (!channel) return log(new Error("kanela jsem nenasel"));
if (neodposlouchavani && aft.selfMute && !aft.deaf) {
novejPlay(channel, { src: "./zvuky/neodposlouchavej.ogg", volume: 0.38 }, Priority.Etc)
novejPlay(aft.channel.guildId, { src: "./zvuky/neodposlouchavej.ogg", volume: 0.38 }, Priority.Etc)
.then(() => {
if (!aft.selfMute) return;
aft.setDeaf(true, "otposlouchávala ta gadza");

View File

@ -74,12 +74,6 @@ export interface ZmenovejObjekt {
status?: string;
}
export interface MuzikaFace {
name: string | Readable;
volume: number;
type?: StreamType;
}
export type KomandNaExport = {
name: string;
custom?: true;

View File

@ -91,7 +91,7 @@ export function log(...content: unknown[]) {
}
export async function sendDM(user: User, txt: string) {
user.send(txt).catch(() => console.log(`dementovi ${user} nejde poslat DM`));
user.send(txt).catch(() => log(new Error(`dementovi ${user} nejde poslat DM`)));
}
export const rand = (max: number) => Math.floor(Math.random() * max);

View File

@ -1,5 +1,5 @@
import { AudioPlayer, AudioPlayerState, AudioPlayerStatus, AudioResource, createAudioPlayer, createAudioResource, entersState, joinVoiceChannel, StreamType, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice";
import { VoiceBasedChannel } from "discord.js";
import { Guild } from "discord.js";
import { log } from "./utils";
import { Readable } from "node:stream";
@ -10,7 +10,6 @@ export type Hratelny = string | Readable | {
};
type Vojska = {
channel: VoiceBasedChannel;
conn: VoiceConnection;
players: [AudioPlayer, AudioPlayer, AudioPlayer];
queues: [AudioResource[], AudioResource[], AudioResource[]];
@ -31,28 +30,46 @@ const getId = () => `Id:${lastId++}`;
* @returns Předchozí stav bota, před tímto příkazem
* `true` znamená, že byl bot do tohoto kanelu již připojen
* `false` znamená, že nebyl připojen nikam
* `VoiceBasedChannel` znamená, že v tutý gildě byl předtím připojenej do daného kanelu
* `channelId` znamená, že v tutý gildě byl předtím připojenej do kanelu s tímto ID
*/
export const novejJoin = (channel: VoiceBasedChannel) => new Promise<boolean | VoiceBasedChannel>(async (resolve, reject) => {
const guildId = channel.guildId;
let prev: boolean | VoiceBasedChannel = false;
export const novejJoin = (guild: Guild, channelId: string) => new Promise<boolean | string>(async (resolve, reject) => {
const guildId = guild.id;
let prev: false | string = false;
const gildina = vojsy.get(guildId);
if (gildina) {
if (gildina.channel.id == channel.id) {
if (gildina.conn.state.status != VoiceConnectionStatus.Ready) {
const con = gildina.conn;
if (con.joinConfig.channelId == channelId) {
if (con.state.status != VoiceConnectionStatus.Ready) {
log(`pri pripojovani do vojsu tady uz existovalo stejny pripojeni, ale status byl ${gildina.conn.state.status}, tak jsem to zahodil`);
novejLeave(guildId);
}
else return resolve(true);
}
else prev = gildina.channel;
else {
prev = con.joinConfig.channelId!;
if (!prev) {
log(new Error("fakt tam nic neni, jak je to mozny?"));
prev = false;
}
// Přesunout se
const state = guild.voiceStates.cache.get(guild.client.user!.id);
if (state) {
await state.setChannel(channelId);
resolve(prev);
return;
}
// Z nějakýho neznámího důvodu se nepodařilo přepojit, prostě vytvoříme noví připojení
log(new Error("jo tak tuto nechapu"));
novejLeave(guildId);
};
}
const conn = joinVoiceChannel({
channelId: channel.id,
channelId: channelId,
guildId,
adapterCreator: channel.guild.voiceAdapterCreator
adapterCreator: guild.voiceAdapterCreator
});
await entersState(conn, VoiceConnectionStatus.Ready, 1e4)
@ -76,7 +93,6 @@ export const novejJoin = (channel: VoiceBasedChannel) => new Promise<boolean | V
conn.subscribe(player);
vojsy.set(guildId, {
channel,
conn,
players: [createAudioPlayer(), createAudioPlayer(), createAudioPlayer()],
queues: [[], [], []]
@ -134,7 +150,7 @@ function handelieren(vojska: Vojska, priority: Priority) {
}
if (nejprioritnejsi != -1)
handelieren(vojska, nejprioritnejsi);
};
}
vojska.players[priority].on("stateChange", nasafunkca);
}
@ -155,14 +171,10 @@ function convert(resources: { id: string; res: AudioResource; }[]) {
return odpoved;
}
export const novejPlay = (channel: VoiceBasedChannel, co: Hratelny | Hratelny[], priority: Priority, handlePrev?: true) => new Promise<AudioPlayerState>(async (resolve, reject) => {
let prev: Awaited<ReturnType<typeof novejJoin>>;
if (!(vojsy.get(channel.guildId)?.channel.id == channel.id)) {
prev = await novejJoin(channel);
}
export const novejPlay = (guildId: string, co: Hratelny | Hratelny[], priority: Priority) => new Promise<AudioPlayerState>(async (resolve, reject) => {
const vojska = vojsy.get(channel.guildId);
if (!vojska) return reject("silenost");
const vojska = vojsy.get(guildId);
if (!vojska) return reject("nejsi pripojenej do vojsu debile");
const resources: { id: string; res: AudioResource; }[] = [];
if (!Array.isArray(co)) {
@ -175,7 +187,7 @@ export const novejPlay = (channel: VoiceBasedChannel, co: Hratelny | Hratelny[],
});
}
// Tuto se možná může někdy hodit, třeba už za 5 minut, třeba nikdy, kdovi
// Tuto se možná může někdy hodit, třeba nikdy, kdovi
// if (neprepisovat) {
// vojska.queues[priority].push(resource);
// } else {
@ -187,23 +199,22 @@ export const novejPlay = (channel: VoiceBasedChannel, co: Hratelny | Hratelny[],
const posledniId = resources.at(-1)?.id;
function nasafunkca(pred: AudioPlayerState, po: AudioPlayerState) {
log({ id: posledniId, pred: pred.status, po: po.status });
if (!(("resource" in pred && pred.resource.metadata == posledniId) && (("resource" in po && po.resource.metadata != posledniId) || !("resource" in po)))) return;
log("odebiram sa", { id: posledniId });
vojska?.players[priority].removeListener("stateChange", nasafunkca);
if (handlePrev) handlePrevVoice(prev, channel.guildId);
resolve(po);
};
}
vojska.players[priority].on("stateChange", nasafunkca);
handelieren(vojska, priority);
});
export const handlePrevVoice = (prev: boolean | VoiceBasedChannel, guildId: string) => {
export const handlePrevVoice = (prev: boolean | string, guild: Guild) => {
if (prev === true) return; // Byl jsem v tomdle vojsu
if (prev === false) return novejLeave(guildId); // Nebyl jsem ve vojsu vůbec
novejJoin(prev); // Byl jsem jinde
if (prev === false) return novejLeave(guild.id); // Nebyl jsem ve vojsu vůbec
novejJoin(guild, prev); // Byl jsem jinde
};
export function stopPlayer(guildId: string, priorita: Priority) {
@ -214,23 +225,25 @@ export function stopPlayer(guildId: string, priorita: Priority) {
return true;
}
export const getConn = (guildId: string) => vojsy.get(guildId)?.conn;
// Ohlašování času
const timeouty = new Map<string, NodeJS.Timeout>();
const nula = (a: number) => a < 10 ? `0${a}` : a;
const vypocitatCas = (channel: VoiceBasedChannel) => {
const vypocitatCas = (guildId: string) => {
const d = new Date();
d.setMinutes((d.getMinutes() >= 30 ? 60 : 30));
d.setSeconds(0);
timeouty.set(channel.guildId, setTimeout(() => rekniCas(channel, `${nula(d.getHours())}${nula(d.getMinutes())}`), Number(d) - Number(new Date())));
timeouty.set(guildId, setTimeout(() => rekniCas(guildId, `${nula(d.getHours())}${nula(d.getMinutes())}`), Number(d) - Number(new Date())));
};
const rekniCas = (channel: VoiceBasedChannel, cas: string) => {
novejPlay(channel, [{ src: "zvuky/intro.mp3", volume: 0.8 }, { src: `zvuky/${cas}.mp3`, volume: 2 }, { src: "zvuky/grg.mp3", volume: 0.5 }], Priority.Time)
const rekniCas = (guildId: string, cas: string) => {
novejPlay(guildId, [{ src: "zvuky/intro.mp3", volume: 0.8 }, { src: `zvuky/${cas}.mp3`, volume: 2 }, { src: "zvuky/grg.mp3", volume: 0.5 }], Priority.Time)
.then(() => {
vypocitatCas(channel);
vypocitatCas(guildId);
})
.catch(err => { log("cas error:", err); });
};