[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