yipee it works

This commit is contained in:
Mars Niermann 2024-03-19 22:30:38 +01:00
parent e1da30687e
commit 36d373ef32
4 changed files with 3310 additions and 2555 deletions

5119
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,33 @@
{ {
"name": "kiku", "name": "kiku",
"main": "src/index.js", "main": "src/index.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"start": "node dist/index.js", "start": "node dist/index.js",
"watch": "tsc watch" "watch": "tsc watch",
}, "dev": "nodemon src/index.ts"
"devDependencies": { },
"@biomejs/biome": "1.6.0", "devDependencies": {
"@tsconfig/node18": "^18.2.2", "@biomejs/biome": "1.6.0",
"@types/node": "^18.7.18", "@tsconfig/node18": "^18.2.2",
"@typescript-eslint/parser": "^5.62.0", "@types/node": "^18.7.18",
"eslint": "^8.57.0", "@typescript-eslint/parser": "^5.62.0",
"prettier": "^3.2.5", "eslint": "^8.57.0",
"prettier-eslint": "^16.3.0", "prettier": "^3.2.5",
"typescript": "^4.9.5" "prettier-eslint": "^16.3.0",
}, "tsx": "^4.7.1",
"engines": { "typescript": "^4.9.5"
"node": ">=18" },
}, "engines": {
"dependencies": { "node": ">=18"
"discord.js": "^14.14.1", },
"dotenv": "^16.4.5", "dependencies": {
"kazagumo": "^3.0.1", "discord.js": "^14.14.1",
"kazagumo-spotify": "^2.0.1", "dotenv": "^16.4.5",
"pretty-ms": "^9.0.0", "kazagumo": "^3.0.1",
"shoukaku": "^4.0.1" "kazagumo-spotify": "^2.0.1",
} "pretty-ms": "^9.0.0",
} "shoukaku": "^4.0.1"
}
}

1
settings.json Normal file
View file

@ -0,0 +1 @@
{"requestChannels":{"777923126981558282":{"channelId":"1219720332101943296","messageId":"1219745004659015813"}}}

View file

@ -1,243 +1,534 @@
import { import {
type APIEmbed, type APIEmbed,
ActionRowBuilder, ActionRowBuilder,
ButtonBuilder, ButtonBuilder,
ButtonStyle, ButtonStyle,
ChannelType, ChannelType,
Client, Client,
EmbedBuilder, EmbedBuilder,
GatewayIntentBits, GatewayIntentBits,
type Message, type Message,
SlashCommandBuilder, SlashCommandBuilder,
Interaction,
} from "discord.js"; } from "discord.js";
import { Connectors, Track } from "shoukaku"; import { Connectors, Track } from "shoukaku";
import { Kazagumo, type KazagumoPlayer, type KazagumoTrack, type Payload } from "kazagumo"; import {
Kazagumo,
type KazagumoPlayer,
type KazagumoTrack,
type Payload,
} from "kazagumo";
import KazagumoPlugin from "kazagumo-spotify"; import KazagumoPlugin from "kazagumo-spotify";
import prettyMilliseconds from 'pretty-ms'; import prettyMilliseconds from "pretty-ms";
const { Guilds, GuildVoiceStates, GuildMessages, MessageContent } = GatewayIntentBits; const { Guilds, GuildVoiceStates, GuildMessages, MessageContent } =
GatewayIntentBits;
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import { readFileSync, writeFileSync } from "fs";
import { channel } from "process";
dotenv.config(); dotenv.config();
if (!process.env.SPOTIFY_ID || !process.env.SPOTIFY_SECRET) { if (!process.env.SPOTIFY_ID || !process.env.SPOTIFY_SECRET) {
throw "Spotfiy Credentials missing"; throw "Spotify Credentials missing";
}
let settings: settings;
try {
settings = JSON.parse(readFileSync("./settings.json", "utf8")); // Load data
} catch (e) {
settings = { requestChannels: {} }; // Init if no data found
} }
const Nodes = [ const Nodes = [
{ {
name: "lavalink", name: "lavalink",
url: "localhost:2333", url: "localhost:2333",
auth: "youshallnotpass", auth: "youshallnotpass",
secure: false, secure: false,
}, },
]; ];
const client = new Client({ const client = new Client({
intents: [Guilds, GuildVoiceStates, GuildMessages, MessageContent], intents: [Guilds, GuildVoiceStates, GuildMessages, MessageContent],
}); });
const kazagumo = new Kazagumo( const kazagumo = new Kazagumo(
{ {
defaultSearchEngine: "youtube", defaultSearchEngine: "youtube",
send: (guildId: string, payload:Payload) => { send: (guildId: string, payload: Payload) => {
const guild = client.guilds.cache.get(guildId); const guild = client.guilds.cache.get(guildId);
if (guild) guild.shard.send(payload); if (guild) guild.shard.send(payload);
}, },
plugins: [ plugins: [
new KazagumoPlugin({ new KazagumoPlugin({
clientId: process.env.SPOTIFY_ID, clientId: process.env.SPOTIFY_ID,
clientSecret: process.env.SPOTIFY_SECRET, clientSecret: process.env.SPOTIFY_SECRET,
playlistPageLimit: 1, // optional ( 100 tracks per page ) playlistPageLimit: 1, // optional ( 100 tracks per page )
albumPageLimit: 1, // optional ( 50 tracks per page ) albumPageLimit: 1, // optional ( 50 tracks per page )
searchLimit: 10, // optional ( track search limit. Max 50 ) searchLimit: 10, // optional ( track search limit. Max 50 )
searchMarket: "US", // optional || default: US ( Enter the country you live in. [ Can only be of 2 letters. For eg: US, IN, EN ] )// searchMarket: "US", // optional || default: US ( Enter the country you live in. [ Can only be of 2 letters. For eg: US, IN, EN ] )//
}), }),
], ],
}, },
new Connectors.DiscordJS(client), new Connectors.DiscordJS(client),
Nodes, Nodes
); );
client.on("ready", () => console.log(`${client.user!.tag}·Ready!`)); interface requestChannels {
[guildId: string]: {
channelId: string;
messageId: string;
};
}
interface settings {
requestChannels: requestChannels;
}
client.on("ready", () => {
kazagumo.shoukaku.on("ready", (name:string) => // console.log(client)
console.log(`Lavalink ${name}: Ready!`), console.log(`${client.user!.tag}·Ready!`);
);
kazagumo.shoukaku.on("error", (name:string, error:Error) =>
console.error(`Lavalink ${name}: Error Caught,`, error),
);
kazagumo.shoukaku.on("close", (name:string, code:unknown, reason:unknown) =>
console.warn(
`Lavalink ${name}: Closed, Code ${code}, Reason ${reason || "No reason"}`,
),
);
kazagumo.shoukaku.on("debug", (name:string, info:string) =>
console.debug(`Lavalink ${name}: Debug,`, info),
);
kazagumo.shoukaku.on("disconnect", (name:string, count:number) => {
// if (moved) return;
// players.map((player:any) => player.connection.disconnect());
console.warn(`Lavalink ${name}: Disconnected`);
}); });
kazagumo.on("playerStart", (player:KazagumoPlayer, track:KazagumoTrack) => { kazagumo.shoukaku.on("ready", (name: string) =>
if (!player.textId) return; console.log(`Lavalink ${name}: Ready!`)
const channel = client.channels.cache.get(player.textId) );
if (!channel) return; kazagumo.shoukaku.on("error", (name: string, error: Error) =>
if (channel.type === ChannelType.GuildText) console.error(`Lavalink ${name}: Error Caught,`, error)
{ channel.send({ content: `Now playing **${track.title}** by **${track.author}**` }) );
.then((x:any) => player.data.set("message", x));} kazagumo.shoukaku.on("close", (name: string, code: unknown, reason: unknown) =>
console.warn(
`Lavalink ${name}: Closed, Code ${code}, Reason ${reason || "No reason"}`
)
);
kazagumo.shoukaku.on("debug", (name: string, info: string) =>
console.debug(`Lavalink ${name}: Debug,`, info)
);
kazagumo.shoukaku.on("disconnect", (name: string, count: number) => {
// if (moved) return;
// players.map((player:any) => player.connection.disconnect());
console.warn(`Lavalink ${name}: Disconnected`);
}); });
kazagumo.on("playerEnd", (player:KazagumoPlayer) => { kazagumo.on("playerStart", (player: KazagumoPlayer, track: KazagumoTrack) => {
player.data.get("message")?.edit({ content: "Finished playing" }); if (!player.textId) return;
const channel = client.channels.cache.get(player.textId);
if (!channel) return;
if (channel.type === ChannelType.GuildText) {
channel
.send({
content: `Now playing **${track.title}** by **${track.author}**`,
})
.then((x: any) => {
player.data.set("message", x);
setTimeout(() => x.delete(), 3000);
});
}
}); });
kazagumo.on("playerEmpty", (player:KazagumoPlayer) => { kazagumo.on("playerEnd", (player: KazagumoPlayer) => {
if (!player.textId) return; player.data.get("message")?.edit({ content: "Finished playing" });
const channel = client.channels.cache.get(player.textId)
if (!channel) return;
if (channel.type === ChannelType.GuildText)
{ channel.send({ content: "Destroyed player due to inactivity." })
.then((x:any) => player.data.set("message", x));}
player.destroy();
}); });
client.on("messageCreate", async (msg:Message) => { kazagumo.on("playerEmpty", (player: KazagumoPlayer) => {
if (msg.author.bot) return; if (!player.textId) return;
if (!msg.guild) return; const channel = client.channels.cache.get(player.textId);
if (!msg.member) return; if (!channel) return;
if (msg.channel.type !== ChannelType.GuildText) return; if (channel.type === ChannelType.GuildText) {
if (msg.channel.name === "moe-song-requests") { channel
const query = msg.content; .send({ content: "Destroyed player due to inactivity." })
.then((x: any) => {
const { channel } = msg.member.voice; player.data.set("message", x);
if (!channel){ setTimeout(() => x.delete(), 3000);
msg.reply( });
"You need to be in a voice channel to use this command!", }
); player.destroy();
return; });
}
// if (channel.type !== ChannelType.GuildVoice) {return;}
const player = await kazagumo.createPlayer({ client.on("messageCreate", async (msg: Message) => {
guildId: msg.guild.id, // console.log(msg.content);
textId: msg.channel.id, if (msg.author.bot) return;
voiceId: channel.id, if (!msg.guild) return;
volume: 40, if (!msg.member) return;
}); if (msg.channel.type !== ChannelType.GuildText) return;
console.log("Player created") if (msg.content.startsWith(".")) {
const result = await kazagumo.search(query, { requester: msg.author }); let cmd = msg.content.split(".")[1];
if (!result.tracks.length) msg.reply("No results found!"); switch (cmd) {
case "init":
if (msg.guildId && msg.channelId) {
if (!settings.requestChannels[msg.guildId]) {
await msg.delete();
const player = await kazagumo.createPlayer({
guildId: msg.guild.id,
textId: msg.channel.id,
voiceId: msg.channel.id,
volume: 40,
});
const play = new ButtonBuilder()
.setCustomId("play")
.setLabel("Play/Pause")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943073793122355");
if (result.type === "PLAYLIST") const stop = new ButtonBuilder()
for (const track of result.tracks) player.queue.add(track); .setCustomId("stop")
else player.queue.add(result.tracks[0]); .setLabel("Stop Playing")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943074258694184");
const play = new ButtonBuilder() const skip = new ButtonBuilder()
.setCustomId("play") .setCustomId("skip")
.setLabel("Play/Pause") .setLabel("Skip Song")
.setStyle(ButtonStyle.Secondary) .setStyle(ButtonStyle.Secondary)
.setEmoji("889943073793122355"); .setEmoji("889943074233516072");
const stop = new ButtonBuilder() const loop = new ButtonBuilder()
.setCustomId("stop") .setCustomId("loop")
.setLabel("Stop Playing") .setLabel("Loop Song")
.setStyle(ButtonStyle.Secondary) .setStyle(ButtonStyle.Secondary)
.setEmoji("889943074258694184"); .setEmoji("889943073667289099");
const skip = new ButtonBuilder() const shuffle = new ButtonBuilder()
.setCustomId("skip") .setCustomId("shuffle")
.setLabel("Skip Song") .setLabel("Shuffle Queue")
.setStyle(ButtonStyle.Secondary) .setStyle(ButtonStyle.Secondary)
.setEmoji("889943074233516072"); .setEmoji("890325437962678352");
const loop = new ButtonBuilder() const seek = new ButtonBuilder()
.setCustomId("loop") .setCustomId("seek")
.setLabel("Loop Song") .setLabel("Seek Forward")
.setStyle(ButtonStyle.Secondary) .setStyle(ButtonStyle.Secondary)
.setEmoji("889943073667289099"); .setEmoji("890325511878889504");
const shuffle = new ButtonBuilder() const previous = new ButtonBuilder()
.setCustomId("shuffle") .setCustomId("previous")
.setLabel("Shuffle Queue") .setLabel("Previous Song")
.setStyle(ButtonStyle.Secondary) .setStyle(ButtonStyle.Secondary)
.setEmoji("890325437962678352"); .setEmoji("890325512071831562");
const seek = new ButtonBuilder() const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
.setCustomId("seek") play,
.setLabel("Seek Forward") stop,
.setStyle(ButtonStyle.Secondary) skip,
.setEmoji("890325511878889504"); loop
);
const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(
shuffle,
seek,
previous
);
const previous = new ButtonBuilder() let track_author: string;
.setCustomId("previous") track_author = "None";
.setLabel("Previous Song") let track_duration;
.setStyle(ButtonStyle.Secondary) track_duration = "NaN:NaN";
.setEmoji("890325512071831562");
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(play, stop, skip, loop); const embed = new EmbedBuilder()
const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(shuffle, seek, previous); .setAuthor({
name: "Moe",
let track_author:string iconURL: "https://cdn.m3.fyi/MoeLogo.gif",
if (result.tracks[0].author) { })
track_author = result.tracks[0].author .setTitle("Nothing is being played right now")
} else { .setDescription("Enter message to search")
track_author = "" .addFields(
} {
let track_duration:string name: "Author",
if (result.tracks[0].length) { value: track_author,
track_duration = prettyMilliseconds(result.tracks[0].length) inline: true,
} else { },
track_duration = "NaN:NaN" {
} name: "Duration",
let track_thumbnail:string value: track_duration,
if (result.tracks[0].thumbnail) { inline: true,
track_thumbnail = result.tracks[0].thumbnail.toString() }
} else { )
track_thumbnail = "NaN:NaN" .setColor("#ff0047");
}
const embed = new EmbedBuilder()
.setAuthor({
name: "Added to queue",
iconURL: "https://cdn.m3.fyi/MoeLogo.gif",
})
.setTitle(
result.type === "PLAYLIST"
? `Queued ${result.tracks.length} from ${result.playlistName}`
: `Queued ${result.tracks[0].title}`,
)
.setDescription(`[${result.tracks[0].title}](${result.tracks[0].uri})`)
.addFields(
{
name: "Author",
value: track_author,
inline: true,
},
{
name: "Duration",
value: track_duration,
inline: true,
},
)
.setThumbnail(track_thumbnail)
.setColor("#ff0047");
if (!player.playing && !player.paused) player.play(); const response = await msg.channel.send({
embeds: [embed],
components: [row, row2],
});
const response = msg.channel.send({ settings.requestChannels[msg.guildId] = {
embeds: [embed], channelId: msg.channelId,
components: [row, row2], messageId: response.id,
}) };
return; console.log(settings.requestChannels[msg.guildId]);
}
}) writeFileSync("./settings.json", JSON.stringify(settings)); // Save count to file
return;
} else {
let answer = await msg.reply("Already initialized");
console.log(answer);
setTimeout(() => msg.delete(), 3000);
setTimeout(() => answer.delete(), 3000);
}
}
break;
default:
break;
}
return;
}
if (msg.channel.name === "moe-song-requests") {
let controls;
if (!settings.requestChannels[msg.guild.id]) {
let answer = await msg.reply("Use .init first");
console.log(answer);
setTimeout(() => msg.delete(), 3000);
setTimeout(() => answer.delete(), 3000);
return;
} else {
let channelId = settings.requestChannels[msg.guild.id].channelId;
let messageId = settings.requestChannels[msg.guild.id].messageId;
let channel = msg.guild.channels.cache.get(channelId);
if (channel && channel.type === ChannelType.GuildText) {
controls = await channel.messages.fetch(messageId);
}
// let controls = msg.channel.guild
// get(requestChannels[msg.guild.id]);
// client.channels.fetch(requestChannels[msg.guild.id])
}
const query = msg.content;
const { channel } = msg.member.voice;
if (!channel) {
let answer = await msg.reply(
"You need to be in a voice channel to use this command!"
);
setTimeout(() => answer.delete(), 3000);
return;
}
const player = await kazagumo.createPlayer({
guildId: msg.guild.id,
textId: msg.channel.id,
voiceId: channel.id,
volume: 40,
});
console.log("Player created");
const result = await kazagumo.search(query, { requester: msg.author });
if (!result.tracks.length) {
let answer = await msg.reply("No results found!");
setTimeout(() => answer.delete(), 3000);
}
if (result.type === "PLAYLIST")
for (const track of result.tracks) player.queue.add(track);
else player.queue.add(result.tracks[0]);
const play = new ButtonBuilder()
.setCustomId("play")
.setLabel("Play/Pause")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943073793122355");
const stop = new ButtonBuilder()
.setCustomId("stop")
.setLabel("Stop Playing")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943074258694184");
const skip = new ButtonBuilder()
.setCustomId("skip")
.setLabel("Skip Song")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943074233516072");
const loop = new ButtonBuilder()
.setCustomId("loop")
.setLabel("Loop Song")
.setStyle(ButtonStyle.Secondary)
.setEmoji("889943073667289099");
const shuffle = new ButtonBuilder()
.setCustomId("shuffle")
.setLabel("Shuffle Queue")
.setStyle(ButtonStyle.Secondary)
.setEmoji("890325437962678352");
const seek = new ButtonBuilder()
.setCustomId("seek")
.setLabel("Seek Forward")
.setStyle(ButtonStyle.Secondary)
.setEmoji("890325511878889504");
const previous = new ButtonBuilder()
.setCustomId("previous")
.setLabel("Previous Song")
.setStyle(ButtonStyle.Secondary)
.setEmoji("890325512071831562");
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
play,
stop,
skip,
loop
);
const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(
shuffle,
seek,
previous
);
let track_author: string;
if (result.tracks[0].author) {
track_author = result.tracks[0].author;
} else {
track_author = "";
}
let track_duration: string;
if (result.tracks[0].length) {
track_duration = prettyMilliseconds(result.tracks[0].length);
} else {
track_duration = "NaN:NaN";
}
let track_thumbnail: string;
if (result.tracks[0].thumbnail) {
track_thumbnail = result.tracks[0].thumbnail.toString();
} else {
track_thumbnail = "NaN:NaN";
}
const embed = new EmbedBuilder()
.setAuthor({
name: "Now playing",
iconURL: "https://cdn.m3.fyi/MoeLogo.gif",
})
.setTitle(
result.type === "PLAYLIST"
? `Playing ${result.tracks.length} from ${result.playlistName}`
: `Playing ${result.tracks[0].title}`
)
.setDescription(`[${result.tracks[0].title}](${result.tracks[0].uri})`)
.addFields(
{
name: "Author",
value: track_author,
inline: true,
},
{
name: "Duration",
value: track_duration,
inline: true,
}
)
.setThumbnail(track_thumbnail)
.setColor("#ff0047");
if (!player.playing && !player.paused) player.play();
if (controls) {
const response = controls.edit({
embeds: [embed],
components: [row, row2],
});
}
setTimeout(() => msg.delete(), 3000);
return;
}
});
client.on("interactionCreate", async (interaction: Interaction) => {
console.log(interaction.id);
if (interaction.isButton()) {
const guild = client.guilds.cache.get(interaction.guildId!);
const member = guild?.members.cache.get(interaction.member!.user.id);
let voiceId = member?.voice.channel!.id;
let player = await kazagumo.createPlayer({
guildId: interaction.guild!.id,
textId: interaction.channel!.id,
voiceId: voiceId!,
});
// volume: 40
// })
switch (interaction.customId) {
case "play":
player.pause(!player.paused);
await interaction.reply({
ephemeral: true,
content: "Toggled Pause",
});
break;
case "stop":
// player.disconnect();
player.destroy();
await interaction.reply({
ephemeral: true,
content: "Stopped Playing",
});
break;
case "skip":
player.skip();
await interaction.reply({
ephemeral: true,
content: "Skipped Song",
});
break;
case "loop":
if (player.loop != "none") {
player.setLoop("none");
await interaction.reply({
ephemeral: true,
content: "Disabled Looping",
});
} else {
player.setLoop("track");
await interaction.reply({
ephemeral: true,
content: "Started Looping",
});
}
break;
case "shuffle":
player.queue.shuffle();
await interaction.reply({
ephemeral: true,
content: "Shuffled Queue",
});
break;
case "seek":
// let position = player.position / 1000;
// position = position | 0;
// console.log(position);
// player.seek(position + 5);
await interaction.reply({
ephemeral: true,
content: "Does not work :pensive:",
// content: "Skipped 5s forward",
});
break;
case "previous":
let track = player.getPrevious();
await interaction.reply({
ephemeral: true,
content: "Queued previous song",
});
player.play(track[0]);
break;
default:
break;
}
}
// interaction.id
// console.log(interaction.client.application.get(interaction.commandName))
// switch (interaction.customID) {
// case value:
// break;
// default:
// break;
// }
});
client.login(process.env.TOKEN); client.login(process.env.TOKEN);