[chore]: Initial commit
This commit is contained in:
commit
b23e51cfdc
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
*.js
|
40
.eslintrc.js
Normal file
40
.eslintrc.js
Normal file
@ -0,0 +1,40 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
};
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
config.json
|
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Breakn't-bot
|
||||
|
||||
Welcome to the repository for breakn't-bot. Breakn't-bot is supposed to be a reliable music player for discord servers.
|
||||
|
||||
## Try it yourself!
|
||||
|
||||
Firstly, clone this repo and install all dependencies (discord.js does not work on devices other than x86 processors):
|
||||
```bash
|
||||
git clone <git link> && cd breakn\'t-bot
|
||||
npm i
|
||||
```
|
||||
|
||||
Then create `config.json` in the root of the breakn't-bot folder. It should contain this:
|
||||
```json
|
||||
{
|
||||
"prefix": "}",
|
||||
"token": "Discord token",
|
||||
"api_key": "Youtube data api v3 token"
|
||||
}
|
||||
```
|
||||
|
||||
To start the bot run: `npm start`;
|
1786
package-lock.json
generated
Normal file
1786
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
Normal file
27
package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "breaknt-bot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "eslint src/* && tsc src/index.ts && node src/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/opus": "^0.1.0",
|
||||
"discord.js": "^12.0.2",
|
||||
"ffmpeg": "0.0.4",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"youtube-search": "^1.1.4",
|
||||
"ytdl-core": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.12.31",
|
||||
"@typescript-eslint/eslint-plugin": "^2.25.0",
|
||||
"@typescript-eslint/parser": "^2.25.0",
|
||||
"eslint": "^6.8.0",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
264
src/index.js
Normal file
264
src/index.js
Normal file
@ -0,0 +1,264 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
exports.__esModule = true;
|
||||
var Discord = require("discord.js");
|
||||
var _a = require("../config.json"), prefix = _a.prefix, token = _a.token, api_key = _a.api_key, debug_channel = _a.debug_channel;
|
||||
var ytdl = require("ytdl-core");
|
||||
var search = require("youtube-search");
|
||||
function debug(message, something) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var channel;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, client.channels.fetch(debug_channel)];
|
||||
case 1:
|
||||
channel = _a.sent();
|
||||
channel.send("[" + new Date().toUTCString() + "] Reply on " + message.content + ":\n" + something);
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
var opts = {
|
||||
maxResults: 1,
|
||||
key: api_key,
|
||||
type: "video"
|
||||
};
|
||||
var client = new Discord.Client();
|
||||
var queue = new Map();
|
||||
client.once("ready", function () {
|
||||
console.log("Ready!");
|
||||
});
|
||||
client.once("reconnecting", function () {
|
||||
console.log("Reconnecting!");
|
||||
});
|
||||
client.once("disconnect", function () {
|
||||
console.log("Disconnect!");
|
||||
});
|
||||
client.on("message", function (message) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var serverQueue, toBeSent_1, toBeSent, currentSong, songInfo;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (message.author.bot)
|
||||
return [2 /*return*/];
|
||||
if (!message.content.startsWith(prefix))
|
||||
return [2 /*return*/];
|
||||
debug(message, "Received message: " + message.content);
|
||||
serverQueue = queue.get(message.guild.id);
|
||||
if (!message.content.startsWith(prefix + "play")) return [3 /*break*/, 1];
|
||||
execute(message, serverQueue);
|
||||
return [2 /*return*/];
|
||||
case 1:
|
||||
if (!message.content.startsWith(prefix + "skip")) return [3 /*break*/, 2];
|
||||
skip(message, serverQueue);
|
||||
return [2 /*return*/];
|
||||
case 2:
|
||||
if (!message.content.startsWith(prefix + "stop")) return [3 /*break*/, 3];
|
||||
stop(message, serverQueue);
|
||||
return [2 /*return*/];
|
||||
case 3:
|
||||
if (!message.content.startsWith(prefix + "queue")) return [3 /*break*/, 4];
|
||||
toBeSent_1 = ">>> **Queue for breakn't-bot:**\n";
|
||||
serverQueue.songs.forEach(function (song, index) {
|
||||
toBeSent_1 += index === 0 ? "_Currently playing:_ " + song.title + "\n" : index + ". " + song.title + "\n";
|
||||
});
|
||||
message.channel.send(toBeSent_1);
|
||||
return [2 /*return*/];
|
||||
case 4:
|
||||
if (!message.content.startsWith(prefix + "help")) return [3 /*break*/, 5];
|
||||
toBeSent = ">>> **Help for breakn't-bot:**\n - `}play <song name>`: Searches for a song on youtube and plays it or adds it to the queue.\n - `}skip`: Skips the currently playing song.\n - `}stop`: Forces the breakn't-bot to leave the channel. Poor bot.\n - `}queue`: Shows the queue.\n - `}remove <index>`: Removes the song with the specified index from the queue.\n - `}pause`: Pause the playback.\n - `}resume`: Resume the playback.\n - `}np`: Shows info about the currently playing song.";
|
||||
message.channel.send(toBeSent);
|
||||
return [2 /*return*/];
|
||||
case 5:
|
||||
if (!message.content.startsWith(prefix + "remove")) return [3 /*break*/, 6];
|
||||
remove(message, serverQueue);
|
||||
return [2 /*return*/];
|
||||
case 6:
|
||||
if (!message.content.startsWith(prefix + "np")) return [3 /*break*/, 8];
|
||||
currentSong = serverQueue.songs[0];
|
||||
return [4 /*yield*/, ytdl.getInfo(currentSong.url)];
|
||||
case 7:
|
||||
songInfo = _a.sent();
|
||||
message.channel.send(">>> _Currently playing:_ " + currentSong.title + "\n " + (serverQueue.connection.dispatcher.streamTime / 1000).toFixed(0) + "/" + songInfo.length_seconds);
|
||||
return [2 /*return*/];
|
||||
case 8:
|
||||
if (message.content.startsWith(prefix + "pause")) {
|
||||
serverQueue.connection.dispatcher.pause();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
else if (message.content.startsWith(prefix + "resume")) {
|
||||
serverQueue.connection.dispatcher.resume();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
else {
|
||||
message.channel.send("command validn't!");
|
||||
}
|
||||
_a.label = 9;
|
||||
case 9: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); });
|
||||
function execute(message, serverQueue) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var _a, args, voiceChannel, permissions;
|
||||
var _this = this;
|
||||
return __generator(this, function (_b) {
|
||||
_a = message.content.split(" "), args = _a.slice(1);
|
||||
debug(message, "Trying to find song: " + args.join(" "));
|
||||
voiceChannel = message.member.voice.channel;
|
||||
if (!voiceChannel)
|
||||
return [2 /*return*/, message.channel.send("You need to be in a voice channel to play music!")];
|
||||
permissions = voiceChannel.permissionsFor(message.client.user);
|
||||
if (!permissions.has("CONNECT") || !permissions.has("SPEAK"))
|
||||
return [2 /*return*/, message.channel.send("I need the permissions to join and speak in your voice channel!")];
|
||||
search(args.join(" "), opts, function (err, results) { return __awaiter(_this, void 0, void 0, function () {
|
||||
var song, queueContruct, connection, err_1;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (err)
|
||||
return [2 /*return*/, debug(message, "Error! " + err.name + ": " + err.message + ". Stacktrace: " + err.stack)];
|
||||
song = {
|
||||
title: results[0].title,
|
||||
url: results[0].link
|
||||
};
|
||||
if (!!serverQueue) return [3 /*break*/, 5];
|
||||
queueContruct = {
|
||||
textChannel: message.channel,
|
||||
voiceChannel: voiceChannel,
|
||||
connection: null,
|
||||
songs: [],
|
||||
volume: 5,
|
||||
playing: true
|
||||
};
|
||||
queue.set(message.guild.id, queueContruct);
|
||||
queueContruct.songs.push(song);
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, voiceChannel.join()];
|
||||
case 2:
|
||||
connection = _a.sent();
|
||||
queueContruct.connection = connection;
|
||||
play(message.guild, queueContruct.songs[0], message);
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_1 = _a.sent();
|
||||
debug(message, "Error! " + err_1.name + ": " + err_1.message + ". Stacktrace: " + err_1.stack);
|
||||
queue["delete"](message.guild.id);
|
||||
return [2 /*return*/, message.channel.send(err_1)];
|
||||
case 4: return [3 /*break*/, 6];
|
||||
case 5:
|
||||
debug(message, "Playing song " + song.title);
|
||||
serverQueue.songs.push(song);
|
||||
return [2 /*return*/, message.channel.send("**" + song.title + "** has been added to the queue!")];
|
||||
case 6: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); });
|
||||
return [2 /*return*/];
|
||||
});
|
||||
});
|
||||
}
|
||||
function remove(message, serverQueue) {
|
||||
var _a = message.content.split(" "), args = _a.slice(1);
|
||||
if (/^\d+$/.test(args[0])) {
|
||||
var index = Number(args[0]);
|
||||
if (index < serverQueue.songs.length) {
|
||||
var deleted = serverQueue.songs.splice(index, 1)[0];
|
||||
message.channel.send("Removed **" + deleted.title + "** from the queue");
|
||||
}
|
||||
else {
|
||||
debug(message, "The entered value is outside the queue's range.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug(message, "The entered value is not a valid number!");
|
||||
}
|
||||
}
|
||||
function skip(message, serverQueue) {
|
||||
if (!message.member.voice.channel) {
|
||||
debug(message, "No song to skip!");
|
||||
message.channel.send("You have to be in a voice channel to stop the music!");
|
||||
return;
|
||||
}
|
||||
if (!serverQueue) {
|
||||
message.channel.send("There is no song that I could skip!");
|
||||
debug(message, "No song to skip!");
|
||||
return;
|
||||
}
|
||||
serverQueue.connection.dispatcher.end();
|
||||
}
|
||||
function stop(message, serverQueue) {
|
||||
if (!message.member.voice.channel)
|
||||
return message.channel.send("You have to be in a voice channel to stop the music!");
|
||||
if (serverQueue.songs.length) {
|
||||
serverQueue.songs = [];
|
||||
}
|
||||
serverQueue.connection.dispatcher.end();
|
||||
serverQueue.voiceChannel.leave();
|
||||
}
|
||||
function play(guild, song, message) {
|
||||
var serverQueue = queue.get(guild.id);
|
||||
if (!song) {
|
||||
serverQueue.voiceChannel.leave();
|
||||
queue["delete"](guild.id);
|
||||
return;
|
||||
}
|
||||
var dispatcher;
|
||||
function attemptPlay() {
|
||||
debug(message, "Attempting to play song!");
|
||||
dispatcher = serverQueue.connection
|
||||
.play(ytdl(song.url))
|
||||
.on("finish", function () {
|
||||
serverQueue.songs.shift();
|
||||
play(guild, serverQueue.songs[0], message);
|
||||
})
|
||||
.on("error", function (err) {
|
||||
debug(message, "Error playing song!");
|
||||
debug(message, "Error! " + err.name + ": " + err.message + ". Stacktrace: " + err.stack);
|
||||
attemptPlay();
|
||||
});
|
||||
debug(message, "Successfully avoided errors!");
|
||||
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
|
||||
serverQueue.textChannel.send("Started playing: **" + song.title + "**");
|
||||
}
|
||||
attemptPlay();
|
||||
}
|
||||
client.login(token);
|
252
src/index.ts
Normal file
252
src/index.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import * as Discord from "discord.js";
|
||||
const { prefix, token, api_key, debug_channel } = require("../config.json");
|
||||
import * as ytdl from "ytdl-core";
|
||||
import * as search from "youtube-search";
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { Song, QueueConstruct } from "./model";
|
||||
|
||||
async function debug(message: Discord.Message | Discord.PartialMessage, something: string) {
|
||||
const channel = await client.channels.fetch(debug_channel) as Discord.TextChannel;
|
||||
channel.send(`[${new Date().toUTCString()}] Reply on ${message.content}:\n${something}`);
|
||||
}
|
||||
|
||||
const opts: search.YouTubeSearchOptions = {
|
||||
maxResults: 1,
|
||||
key: api_key,
|
||||
type: "video"
|
||||
};
|
||||
|
||||
const client = new Discord.Client();
|
||||
|
||||
const queue = new Map();
|
||||
|
||||
client.once("ready", () => {
|
||||
console.log("Ready!");
|
||||
});
|
||||
|
||||
client.once("reconnecting", () => {
|
||||
console.log("Reconnecting!");
|
||||
});
|
||||
|
||||
client.once("disconnect", () => {
|
||||
console.log("Disconnect!");
|
||||
});
|
||||
|
||||
client.on("message", async message => {
|
||||
if (message.author.bot) return;
|
||||
if (!message.content.startsWith(prefix)) return;
|
||||
|
||||
debug(message, `Received message: ${message.content}`);
|
||||
|
||||
const serverQueue: QueueConstruct = queue.get(message.guild.id);
|
||||
|
||||
if (message.content.startsWith(`${prefix}play`)) {
|
||||
|
||||
execute(message, serverQueue);
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}skip`)) {
|
||||
|
||||
skip(message, serverQueue);
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}stop`)) {
|
||||
|
||||
stop(message, serverQueue);
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}queue`)) {
|
||||
|
||||
let toBeSent = ">>> **Queue for breakn't-bot:**\n";
|
||||
serverQueue.songs.forEach((song, index) => {
|
||||
toBeSent += index === 0 ? `_Currently playing:_ ${song.title}\n` : `${index}. ${song.title}\n`;
|
||||
});
|
||||
message.channel.send(toBeSent);
|
||||
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}help`)) {
|
||||
|
||||
let toBeSent = `>>> **Help for breakn't-bot:**
|
||||
- \`}play <song name>\`: Searches for a song on youtube and plays it or adds it to the queue.
|
||||
- \`}skip\`: Skips the currently playing song.
|
||||
- \`}stop\`: Forces the breakn't-bot to leave the channel. Poor bot.
|
||||
- \`}queue\`: Shows the queue.
|
||||
- \`}remove <index>\`: Removes the song with the specified index from the queue.
|
||||
- \`}pause\`: Pause the playback.
|
||||
- \`}resume\`: Resume the playback.
|
||||
- \`}np\`: Shows info about the currently playing song.`;
|
||||
|
||||
message.channel.send(toBeSent);
|
||||
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}remove`)) {
|
||||
|
||||
remove(message, serverQueue);
|
||||
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}np`)) {
|
||||
|
||||
const currentSong = serverQueue.songs[0];
|
||||
const songInfo = await ytdl.getInfo(currentSong.url);
|
||||
message.channel.send(`>>> _Currently playing:_ ${currentSong.title}
|
||||
${(serverQueue.connection.dispatcher.streamTime/1000).toFixed(0)}/${songInfo.length_seconds}`);
|
||||
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}pause`)) {
|
||||
|
||||
serverQueue.connection.dispatcher.pause();
|
||||
|
||||
return;
|
||||
|
||||
} else if (message.content.startsWith(`${prefix}resume`)) {
|
||||
|
||||
serverQueue.connection.dispatcher.resume();
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
message.channel.send("command validn't!");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
async function execute(message: Discord.Message | Discord.PartialMessage, serverQueue: QueueConstruct) {
|
||||
const [, ...args] = message.content.split(" ");
|
||||
|
||||
debug(message, `Trying to find song: ${args.join(" ")}`);
|
||||
|
||||
const voiceChannel = message.member.voice.channel;
|
||||
|
||||
if (!voiceChannel)
|
||||
return message.channel.send(
|
||||
"You need to be in a voice channel to play music!"
|
||||
);
|
||||
|
||||
const permissions = voiceChannel.permissionsFor(message.client.user);
|
||||
if (!permissions.has("CONNECT") || !permissions.has("SPEAK"))
|
||||
return message.channel.send(
|
||||
"I need the permissions to join and speak in your voice channel!"
|
||||
);
|
||||
|
||||
search(args.join(" "), opts, async (err, results) => {
|
||||
if(err) return debug(message, "Error! " + err.name + ": " + err.message + ". Stacktrace: " + err.stack);
|
||||
|
||||
const song: Song = {
|
||||
title: results[0].title,
|
||||
url: results[0].link
|
||||
};
|
||||
|
||||
if (!serverQueue) {
|
||||
const queueContruct: QueueConstruct = {
|
||||
textChannel: message.channel,
|
||||
voiceChannel: voiceChannel,
|
||||
connection: null,
|
||||
songs: [],
|
||||
volume: 5,
|
||||
playing: true
|
||||
};
|
||||
|
||||
queue.set(message.guild.id, queueContruct);
|
||||
|
||||
queueContruct.songs.push(song);
|
||||
|
||||
try {
|
||||
var connection = await voiceChannel.join();
|
||||
queueContruct.connection = connection;
|
||||
play(message.guild, queueContruct.songs[0], message);
|
||||
} catch (err) {
|
||||
debug(message, "Error! " + err.name + ": " + err.message + ". Stacktrace: " + err.stack);
|
||||
queue.delete(message.guild.id);
|
||||
return message.channel.send(err);
|
||||
}
|
||||
} else {
|
||||
debug(message, `Playing song ${song.title}`);
|
||||
serverQueue.songs.push(song);
|
||||
return message.channel.send(`**${song.title}** has been added to the queue!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove(message: Discord.Message | Discord.PartialMessage, serverQueue: QueueConstruct) {
|
||||
const [, ...args] = message.content.split(" ");
|
||||
if (/^\d+$/.test(args[0])) {
|
||||
const index = Number(args[0]);
|
||||
if (index < serverQueue.songs.length) {
|
||||
const deleted = serverQueue.songs.splice(index, 1)[0];
|
||||
message.channel.send(`Removed **${deleted.title}** from the queue`);
|
||||
} else {
|
||||
debug(message, "The entered value is outside the queue's range.");
|
||||
}
|
||||
} else {
|
||||
debug(message, "The entered value is not a valid number!");
|
||||
}
|
||||
}
|
||||
|
||||
function skip(message: Discord.Message | Discord.PartialMessage, serverQueue: QueueConstruct) {
|
||||
if (!message.member.voice.channel) {
|
||||
debug(message, "No song to skip!");
|
||||
message.channel.send("You have to be in a voice channel to stop the music!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!serverQueue) {
|
||||
message.channel.send("There is no song that I could skip!");
|
||||
debug(message, "No song to skip!");
|
||||
return;
|
||||
}
|
||||
serverQueue.connection.dispatcher.end();
|
||||
}
|
||||
|
||||
function stop(message: Discord.Message | Discord.PartialMessage, serverQueue: QueueConstruct) {
|
||||
if (!message.member.voice.channel)
|
||||
return message.channel.send(
|
||||
"You have to be in a voice channel to stop the music!"
|
||||
);
|
||||
if (serverQueue.songs.length) {
|
||||
serverQueue.songs = [];
|
||||
}
|
||||
|
||||
serverQueue.connection.dispatcher.end();
|
||||
serverQueue.voiceChannel.leave();
|
||||
}
|
||||
|
||||
function play(guild: Discord.Guild, song: Song, message: Discord.Message | Discord.PartialMessage) {
|
||||
const serverQueue: QueueConstruct = queue.get(guild.id);
|
||||
if (!song) {
|
||||
serverQueue.voiceChannel.leave();
|
||||
queue.delete(guild.id);
|
||||
return;
|
||||
}
|
||||
|
||||
let dispatcher: Discord.StreamDispatcher;
|
||||
|
||||
function attemptPlay() {
|
||||
debug(message, "Attempting to play song!");
|
||||
|
||||
dispatcher = serverQueue.connection
|
||||
.play(ytdl(song.url))
|
||||
.on("finish", () => {
|
||||
serverQueue.songs.shift();
|
||||
play(guild, serverQueue.songs[0], message);
|
||||
})
|
||||
.on("error", err => {
|
||||
debug(message, "Error playing song!");
|
||||
debug(message, "Error! " + err.name + ": " + err.message + ". Stacktrace: " + err.stack);
|
||||
attemptPlay();
|
||||
});
|
||||
|
||||
debug(message, "Successfully avoided errors!");
|
||||
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
|
||||
serverQueue.textChannel.send(`Started playing: **${song.title}**`);
|
||||
}
|
||||
|
||||
attemptPlay();
|
||||
|
||||
}
|
||||
|
||||
client.login(token);
|
2
src/model.js
Normal file
2
src/model.js
Normal file
@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
13
src/model.ts
Normal file
13
src/model.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import * as Discord from "discord.js";
|
||||
|
||||
export interface Song {title: string, url: string}
|
||||
|
||||
export interface QueueConstruct {
|
||||
textChannel: Discord.TextChannel | Discord.DMChannel,
|
||||
voiceChannel: Discord.VoiceChannel,
|
||||
connection: null | Discord.VoiceConnection,
|
||||
songs: Song[],
|
||||
volume: number,
|
||||
playing: boolean
|
||||
}
|
Loading…
Reference in New Issue
Block a user