play from arbitrary files
This commit is contained in:
parent
44c1e5ec70
commit
b2cbe50d74
|
@ -5,6 +5,7 @@
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "pnpm build && node .",
|
"start": "pnpm build && node .",
|
||||||
|
"watch": "pnpm build watch",
|
||||||
"build": "node build.mjs",
|
"build": "node build.mjs",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
|
@ -12,12 +13,14 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/voice": "^0.18.0",
|
"@discordjs/voice": "^0.17.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
|
"libsodium-wrappers": "^0.7.15",
|
||||||
"oceanic.js": "^1.11.2"
|
"oceanic.js": "^1.11.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a",
|
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
"esbuild": "0.24.2"
|
"esbuild": "0.24.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,21 @@ importers:
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@discordjs/voice':
|
'@discordjs/voice':
|
||||||
specifier: ^0.18.0
|
specifier: ^0.17.0
|
||||||
version: 0.18.0
|
version: 0.17.0
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.7
|
specifier: ^16.4.7
|
||||||
version: 16.4.7
|
version: 16.4.7
|
||||||
|
libsodium-wrappers:
|
||||||
|
specifier: ^0.7.15
|
||||||
|
version: 0.7.15
|
||||||
oceanic.js:
|
oceanic.js:
|
||||||
specifier: ^1.11.2
|
specifier: ^1.11.2
|
||||||
version: 1.11.2
|
version: 1.11.2
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@eslint/js':
|
||||||
|
specifier: ^9.18.0
|
||||||
|
version: 9.18.0
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.10.5
|
specifier: ^22.10.5
|
||||||
version: 22.10.5
|
version: 22.10.5
|
||||||
|
@ -32,10 +38,6 @@ packages:
|
||||||
engines: {node: '>=16.11.0'}
|
engines: {node: '>=16.11.0'}
|
||||||
deprecated: This version uses deprecated encryption modes. Please use a newer version.
|
deprecated: This version uses deprecated encryption modes. Please use a newer version.
|
||||||
|
|
||||||
'@discordjs/voice@0.18.0':
|
|
||||||
resolution: {integrity: sha512-BvX6+VJE5/vhD9azV9vrZEt9hL1G+GlOdsQaVl5iv9n87fkXjf3cSwllhR3GdaUC8m6dqT8umXIWtn3yCu4afg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.24.2':
|
'@esbuild/aix-ppc64@0.24.2':
|
||||||
resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
|
resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
@ -186,15 +188,16 @@ packages:
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@eslint/js@9.18.0':
|
||||||
|
resolution: {integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==}
|
||||||
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@types/node@22.10.5':
|
'@types/node@22.10.5':
|
||||||
resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==}
|
resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==}
|
||||||
|
|
||||||
'@types/ws@8.5.13':
|
'@types/ws@8.5.13':
|
||||||
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
|
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
|
||||||
|
|
||||||
discord-api-types@0.37.115:
|
|
||||||
resolution: {integrity: sha512-ivPnJotSMrXW8HLjFu+0iCVs8zP6KSliMelhr7HgcB2ki1QzpORkb26m71l1pzSnnGfm7gb5n/VtRTtpw8kXFA==}
|
|
||||||
|
|
||||||
discord-api-types@0.37.83:
|
discord-api-types@0.37.83:
|
||||||
resolution: {integrity: sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==}
|
resolution: {integrity: sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==}
|
||||||
|
|
||||||
|
@ -207,6 +210,12 @@ packages:
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
libsodium-wrappers@0.7.15:
|
||||||
|
resolution: {integrity: sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ==}
|
||||||
|
|
||||||
|
libsodium@0.7.15:
|
||||||
|
resolution: {integrity: sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw==}
|
||||||
|
|
||||||
oceanic.js@1.11.2:
|
oceanic.js@1.11.2:
|
||||||
resolution: {integrity: sha512-kXMoZiIrIFq0QCfsZGJ+eOK+1IFkVsTpvwcQ3nfY6ioDKGVdHQjVIXNJAYvJb5c5la//2OU8WGpqsmp/bAP8aw==}
|
resolution: {integrity: sha512-kXMoZiIrIFq0QCfsZGJ+eOK+1IFkVsTpvwcQ3nfY6ioDKGVdHQjVIXNJAYvJb5c5la//2OU8WGpqsmp/bAP8aw==}
|
||||||
engines: {node: '>=18.13.0'}
|
engines: {node: '>=18.13.0'}
|
||||||
|
@ -262,22 +271,6 @@ snapshots:
|
||||||
- node-opus
|
- node-opus
|
||||||
- opusscript
|
- opusscript
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@discordjs/voice@0.18.0':
|
|
||||||
dependencies:
|
|
||||||
'@types/ws': 8.5.13
|
|
||||||
discord-api-types: 0.37.115
|
|
||||||
prism-media: 1.3.5
|
|
||||||
tslib: 2.8.1
|
|
||||||
ws: 8.18.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@discordjs/opus'
|
|
||||||
- bufferutil
|
|
||||||
- ffmpeg-static
|
|
||||||
- node-opus
|
|
||||||
- opusscript
|
|
||||||
- utf-8-validate
|
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.24.2':
|
'@esbuild/aix-ppc64@0.24.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -354,6 +347,8 @@ snapshots:
|
||||||
'@esbuild/win32-x64@0.24.2':
|
'@esbuild/win32-x64@0.24.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@eslint/js@9.18.0': {}
|
||||||
|
|
||||||
'@types/node@22.10.5':
|
'@types/node@22.10.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.20.0
|
undici-types: 6.20.0
|
||||||
|
@ -362,10 +357,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.10.5
|
'@types/node': 22.10.5
|
||||||
|
|
||||||
discord-api-types@0.37.115: {}
|
discord-api-types@0.37.83: {}
|
||||||
|
|
||||||
discord-api-types@0.37.83:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
dotenv@16.4.7: {}
|
dotenv@16.4.7: {}
|
||||||
|
|
||||||
|
@ -397,6 +389,12 @@ snapshots:
|
||||||
'@esbuild/win32-ia32': 0.24.2
|
'@esbuild/win32-ia32': 0.24.2
|
||||||
'@esbuild/win32-x64': 0.24.2
|
'@esbuild/win32-x64': 0.24.2
|
||||||
|
|
||||||
|
libsodium-wrappers@0.7.15:
|
||||||
|
dependencies:
|
||||||
|
libsodium: 0.7.15
|
||||||
|
|
||||||
|
libsodium@0.7.15: {}
|
||||||
|
|
||||||
oceanic.js@1.11.2:
|
oceanic.js@1.11.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import commands, { Command } from ".";
|
||||||
|
import { filterAsync } from "../util";
|
||||||
|
|
||||||
|
const cmdHelp: Command = {
|
||||||
|
names: ["help", "cmds"],
|
||||||
|
desc: "list out commands you have access to",
|
||||||
|
checkPerm: async () => true,
|
||||||
|
async handler(msg, { reply }) {
|
||||||
|
const availCmds = await filterAsync(
|
||||||
|
commands,
|
||||||
|
async (cmd) => await cmd.checkPerm(msg)
|
||||||
|
);
|
||||||
|
|
||||||
|
let text = `// List of commands (${availCmds.length} count)\n`;
|
||||||
|
|
||||||
|
for (let cmd of availCmds) {
|
||||||
|
text += `${cmd.names[0]} `;
|
||||||
|
if (cmd.names.length > 1) {
|
||||||
|
text += `(aka: ${cmd.names.slice(1).join(", ")}) `;
|
||||||
|
}
|
||||||
|
text += `- ${cmd.desc}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = "```\n" + text + "\n```";
|
||||||
|
await reply(text);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmdHelp;
|
|
@ -1,19 +1,15 @@
|
||||||
import { Message } from "oceanic.js";
|
import { Message } from "oceanic.js";
|
||||||
import { Command } from ".";
|
import { Command } from ".";
|
||||||
|
|
||||||
const hi: Command = {
|
const cmdHi: Command = {
|
||||||
name: "hi",
|
names: ["hi", "hello"],
|
||||||
desc: "test command",
|
desc: "test command",
|
||||||
async checkPerm(msg: Message): Promise<boolean> {
|
async checkPerm(msg: Message) {
|
||||||
if (msg.member === undefined) return false;
|
return msg.member?.roles.includes("1327065762031992924");
|
||||||
|
|
||||||
return msg.member.roles.includes("1327065762031992924");
|
|
||||||
},
|
},
|
||||||
async handler(msg: Message) {
|
async handler(msg: Message, { say }) {
|
||||||
msg.channel?.createMessage({
|
await say("Test message");
|
||||||
content: "Test message",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default hi;
|
export default cmdHi;
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
import { Message } from "oceanic.js";
|
import { Message } from "oceanic.js";
|
||||||
import hi from "./hi";
|
import cmdHelp from "./help";
|
||||||
|
import cmdHi from "./hi";
|
||||||
|
import cmdPlay from "./play";
|
||||||
|
|
||||||
export type Command = {
|
export type Command = {
|
||||||
name: string;
|
names: string[];
|
||||||
desc?: string;
|
desc: string;
|
||||||
checkPerm(msg: Message): Promise<boolean>;
|
checkPerm(msg: Message): Promise<boolean | undefined>;
|
||||||
handler(msg: Message): Promise<void>;
|
handler(
|
||||||
|
msg: Message,
|
||||||
|
replies: {
|
||||||
|
reply: (text: string) => Promise<void>;
|
||||||
|
say: (text: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const commands: Command[] = [hi];
|
const commands: Command[] = [cmdHelp, cmdHi, cmdPlay];
|
||||||
|
|
||||||
export default commands;
|
export default commands;
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import {
|
||||||
|
AudioPlayerStatus,
|
||||||
|
createAudioPlayer,
|
||||||
|
createAudioResource,
|
||||||
|
VoiceConnectionStatus,
|
||||||
|
} from "@discordjs/voice";
|
||||||
|
import { Command } from ".";
|
||||||
|
import { existsSync } from "node:fs";
|
||||||
|
|
||||||
|
const cmdPlay: Command = {
|
||||||
|
names: ["play", "soundtest"],
|
||||||
|
desc: "test voice features",
|
||||||
|
async checkPerm(msg) {
|
||||||
|
return msg.author.id === "778441081883983893";
|
||||||
|
},
|
||||||
|
async handler(msg, { reply, say }) {
|
||||||
|
const voiceState = msg.member?.voiceState;
|
||||||
|
if (!voiceState?.channelID)
|
||||||
|
return await reply("You are not in a voice channel");
|
||||||
|
|
||||||
|
if (msg.guildID !== null && msg.client.getVoiceConnection(msg.guildID)) {
|
||||||
|
return await reply("I am already connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn = voiceState.channel?.join({
|
||||||
|
selfMute: false,
|
||||||
|
});
|
||||||
|
if (!conn) return await reply("Failed to join channel");
|
||||||
|
|
||||||
|
conn.on(VoiceConnectionStatus.Disconnected, () => {
|
||||||
|
console.log("Rejoining from disconnection!!");
|
||||||
|
conn.rejoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
const player = createAudioPlayer();
|
||||||
|
conn.subscribe(player);
|
||||||
|
|
||||||
|
conn.on("stateChange", (oldSt, newSt) => {
|
||||||
|
console.log(`Moving from ${oldSt.status} to ${newSt.status}`);
|
||||||
|
});
|
||||||
|
conn.on("error", (err) => {
|
||||||
|
console.error("Connection experienced error!", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
player.once(AudioPlayerStatus.Playing, async () => {
|
||||||
|
await say("Playing audio");
|
||||||
|
});
|
||||||
|
player.on(AudioPlayerStatus.Idle, async () => {
|
||||||
|
await say("Audio finished playing");
|
||||||
|
});
|
||||||
|
|
||||||
|
const name = msg.content.match(/\s(.*)$/)?.[1];
|
||||||
|
if (!name || !existsSync(name)) {
|
||||||
|
return await reply("Audio file not found");
|
||||||
|
}
|
||||||
|
console.log(name);
|
||||||
|
|
||||||
|
const audio = createAudioResource(name);
|
||||||
|
player.play(audio);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmdPlay;
|
|
@ -0,0 +1 @@
|
||||||
|
export const PREFIX = "!!";
|
51
src/index.ts
51
src/index.ts
|
@ -1,15 +1,20 @@
|
||||||
import { Client } from "oceanic.js";
|
import { Client } from "oceanic.js";
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import commands from "./commands";
|
import commands from "./commands";
|
||||||
|
import { PREFIX } from "./constants";
|
||||||
|
|
||||||
const token = process.env.DISCORD_TOKEN;
|
function token() {
|
||||||
if (!token) {
|
return (
|
||||||
console.error("No `DISCORD_TOKEN` was specified!!");
|
process.env.DISCORD_TOKEN ??
|
||||||
process.exit(1);
|
(() => {
|
||||||
|
console.error("No `DISCORD_TOKEN` was specified!!");
|
||||||
|
process.exit(1);
|
||||||
|
})()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
auth: `Bot ${token}`,
|
auth: `Bot ${token()}`,
|
||||||
gateway: {
|
gateway: {
|
||||||
intents: ["ALL"],
|
intents: ["ALL"],
|
||||||
},
|
},
|
||||||
|
@ -21,25 +26,37 @@ client.on("ready", async () => {
|
||||||
console.log("Ready as", client.user.tag);
|
console.log("Ready as", client.user.tag);
|
||||||
});
|
});
|
||||||
|
|
||||||
const PREFIX = "!!";
|
|
||||||
|
|
||||||
client.on("messageCreate", async (msg) => {
|
client.on("messageCreate", async (msg) => {
|
||||||
if (msg.author.bot) return;
|
if (msg.author.bot) return;
|
||||||
|
|
||||||
let first = msg.content.split(/\s/).shift();
|
const content = msg.content.toLowerCase().trim();
|
||||||
if (first?.startsWith(PREFIX) === false) return;
|
if (!content.startsWith(PREFIX)) return;
|
||||||
let cmdName = first!.slice(PREFIX.length);
|
|
||||||
|
|
||||||
let command = commands.find((c) => c.name === cmdName);
|
const cmdName = content.split(/\s/)[0]?.slice(PREFIX.length) ?? "";
|
||||||
|
const cmd = commands.find((cmd) => cmd.names.includes(cmdName));
|
||||||
|
|
||||||
if (command === undefined) return;
|
if (cmd === undefined) return;
|
||||||
|
|
||||||
if ((await command.checkPerm(msg)) === false) {
|
if (await cmd.checkPerm(msg)) {
|
||||||
await msg.createReaction("❌");
|
const reply = async (text: string) => {
|
||||||
return;
|
await msg.channel?.createMessage({
|
||||||
}
|
content: text,
|
||||||
|
allowedMentions: {
|
||||||
|
repliedUser: false,
|
||||||
|
},
|
||||||
|
messageReference: {
|
||||||
|
messageID: msg.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const say = async (text: string) => {
|
||||||
|
await msg.channel?.createMessage({
|
||||||
|
content: text,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
await command?.handler(msg);
|
await cmd?.handler(msg, { reply, say });
|
||||||
|
} else await msg.createReaction("❌");
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on("error", (err) => {
|
client.on("error", (err) => {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export async function filterAsync<T>(
|
||||||
|
arr: T[],
|
||||||
|
predicate: (v) => Promise<boolean>
|
||||||
|
): Promise<T[]> {
|
||||||
|
const results = await Promise.all(arr.map(predicate));
|
||||||
|
return arr.filter((_v, i) => results[i]);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"isolatedModules": true,
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
"module": "NodeNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
"sourceMap": true,
|
||||||
|
|
||||||
|
"lib": ["ESNext"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue