Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
6. Take all of the information from the page and enter it into the `config/keys.js` file, replacing the placeholders.
7. Navigate to the `config/plex.js` file and replace the placeholders with your Plex Server information
1. To get your token, following the instructions here: https://support.plex.tv/hc/en-us/articles/204059436-Finding-an-authentication-token-X-Plex-Token
2. To get your machineId or "machineIdentifier", follow the instructions here: https://support.plex.tv/hc/en-us/articles/201638786-Plex-Media-Server-URL-Commands
* In the first example under "Base Server Capabilities", you can see the information returned when you type `http://[PMS_IP_Address]:32400/?X-Plex-Token=YourTokenGoesHere` into your address bar of a web browser. Copy everything between the quotes for the parameter "machineIdentifier" and paste it into the "machineId" property in `config/plex.js`
3. The identifier, product, version, and deviceName can be anything you want
2. The identifier, product, version, and deviceName can be anything you want
8. Once you have the configs set up correctly, you'll need to authorize your bot on a server you have administrative access to. For documentation, you can read: https://discordapp.com/developers/docs/topics/oauth2#bots. The steps are as follows:
1. Go to `https://discordapp.com/api/oauth2/authorize?client_id=[CLIENT_ID]&scope=bot&permissions=1` where [CLIENT_ID] is the Discord App Client ID
2. Select **Add a bot to a server** and select the server to add it to
Expand Down Expand Up @@ -53,6 +51,7 @@ If I am missing any steps, feel free to reach out or open an issue/bug in the I
* `!skip` : skips the current song if one is playing and plays the next song in queue if it exists
* `!stop` : stops song if one is playing
* `!viewqueue` : displays current song queue
* `!bot prefix <prefix>` : changes the bot prefix for the guild

***
## Customization
Expand All @@ -61,10 +60,8 @@ Update the `config\keys.js` file with your information:

```javascript
module.exports = {
'clientId' : 'DISCORD_CLIENT_ID',
'clientSecret' : 'DISCORD_CLIENT_SECRET',
'username' : 'DISCORD_BOT_USERNAME',
'botToken' : 'DISCORD_BOT_TOKEN',
'defaultPrefix' : '!'
};
```

Expand All @@ -73,17 +70,15 @@ And update the `config\plex.js` file with your Plex information:
```javascript
module.exports= {
'hostname' : 'PLEX_LOCAL_IP',
'port' : 'PLEX_LOCAL_PORT'
'port' : 'PLEX_LOCAL_PORT',
'username' : 'PLEX_USERNAME',
'password' : 'PLEX_PASSWORD',
'token' : 'PLEX_TOKEN',
'machineId' : 'PLEX_MACHINEID',
'managedUser' : 'PLEX_MANAGED_USERNAME',
'options' : {
'identifier': 'APP_IDENTIFIER',
'product' : 'APP_PRODUCT_NAME',
'version' : 'APP_VERSION_NUMBER',
'deviceName': 'APP_DEVICE_NAME',
'identifier': 'Plex-Discord-Bot',
'product' : 'Node.js App',
'version' : '1.0.0',
'deviceName': 'Node.js App',
'platform' : 'Discord',
'device' : 'Discord'
}
Expand Down
185 changes: 176 additions & 9 deletions app/music.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,202 @@
module.exports = function(client) {
// plex commands -------------------------------------------------------------
var plexCommands = require('../commands/plex');
var plexCommands = require('../commands/plex.js');
var keys = require('../config/keys.js');

// Database for individual server settings as well as saving bot changes. This can be used later for even more advanced customizations like mod roles, log channels, etc.
const SQLite = require("better-sqlite3");
const sql = new SQLite('./config/database.sqlite');

// when bot is ready
client.on('ready', function() {
client.on('ready', async message => {
console.log('bot ready');
console.log('logged in as: ' + client.user.tag);
client.user.setActivity('music | ' + keys.defaultPrefix + 'help', { type: 'PLAYING' });

// Check if the table "guildSettings" exists.
const tableGuildSettings = sql.prepare("SELECT count(*) FROM sqlite_master WHERE type='table' AND name = 'guildSettings';").get();
if (!tableGuildSettings['count(*)']) {
// If the table isn't there, create it and setup the database correctly.
sql.prepare("CREATE TABLE guildSettings (id TEXT PRIMARY KEY, guild TEXT, prefix TEXT);").run();
// Ensure that the "id" row is always unique and indexed.
sql.prepare("CREATE UNIQUE INDEX idx_guildSettings_id ON guildSettings (id);").run();
sql.pragma("synchronous = 1");
sql.pragma("journal_mode = wal");
}

// And then we have prepared statements to get and set guildSettings data.
client.getGuildSettings = sql.prepare("SELECT * FROM guildSettings WHERE guild = ?");
client.setGuildSettings = sql.prepare("INSERT OR REPLACE INTO guildSettings (id, guild, prefix) VALUES (@id, @guild, @prefix);");


plexCommands['plexTest'].process();
});

// when message is sent to discord
client.on('message', function(message){
client.on('message', async message => {
if (message.author.bot) return; // If a bot sends a message, ignore it.
let guildSettings; // used for discord server settings

if (message.guild) {
// Sets default server settings if message occurs in a guild (not a dm)
guildSettings = client.getGuildSettings.get(message.guild.id);
if (!guildSettings) {
guildSettings = { id: `${message.guild.id}-${client.user.id}`, guild: message.guild.id, prefix: keys.defaultPrefix };
client.setGuildSettings.run(guildSettings);
guildSettings = client.getGuildSettings.get(message.guild.id);
}
}
var prefix = guildSettings.prefix;

var msg = message.content.toLowerCase();
if (msg.startsWith('!')){
var cmdTxt = msg.split(" ")[0].substring("-".length, msg.length);
if (msg.startsWith(prefix)){
// Used for bot settings
var args = message.content.slice(prefix.length).trim().split(/ +/g);
var command = args.shift().toLowerCase();

var cmdTxt = msg.split(" ")[0].substring(prefix.length, msg.length);
var query = msg.substring(msg.indexOf(' ')+1);
var cmd = plexCommands[cmdTxt];

if (cmd){
if (command === "bot") {
// This is where we change bot information
if (args.length > 0) {
command = args.shift().toLowerCase();
} else {
command = "help";
}

if (command === "prefix") {
if (args.length > 0) {
if (message.channel.guild.member(message.author).hasPermission('ADMINISTRATOR')) {
command = args.shift().toLowerCase();
guildSettings.prefix = command;
client.setGuildSettings.run(guildSettings);
guildSettings = client.getGuildSettings.get(message.guild.id);
message.channel.send("Prefix changed to `" + guildSettings.prefix + "`");
}
else {
return message.channel.send('You do not have permissions to use `' + prefix + 'bot prefix` in <#' + message.channel.id + '>!');
}
} else {
return message.channel.send("The current prefix is `" + guildSettings.prefix + "`\nTo change it type: `" + guildSettings.prefix + "bot prefix <" + keys.defaultPrefix + ">` (where **<" + keys.defaultPrefix + ">** is the prefix)");
}
}
else if (command === "help") {
// Help message for bot settings goes here
const help = {
"title": "The following are available for the command " + prefix + "bot <command>",
"description": "\n\u200b",
"color": 4025171,
"timestamp": new Date(),
"footer": {
"icon_url": client.user.avatarURL,
"text": "Fetched"
},
"thumbnail": {
"url": client.user.avatarURL
},
"author": {
"name": client.user.username,
"icon_url": client.user.avatarURL
},
"fields": [
{
"name": prefix + "bot prefix <prefix> :",
"value": "changes the bot prefix for the guild"
}
]
}

return message.channel.send({ embed: help });
}
else {
return message.channel.send("**Command not recognized!** Type `" + prefix + "bot help` for a list of bot settings.");
}
}
else if (command === "help") {
// Help message for available bot commands goes here
const help = {
"title": "The following commands are available for this bot:",
"description": "\n\u200b",
"color": 4025171,
"timestamp": new Date(),
"footer": {
"icon_url": client.user.avatarURL,
"text": "Fetched"
},
"thumbnail": {
"url": client.user.avatarURL
},
"author": {
"name": client.user.username,
"icon_url": client.user.avatarURL
},
"fields": [
{
"name": prefix + "plexTest :",
"value": "a test to see make sure your Plex server is connected properly"
},
{
"name": prefix + "clearqueue : ",
"value": "clears all songs in queue"
},
{
"name": prefix + "nextpage :",
"value": "get next page of songs if desired song is not listed"
},
{
"name": prefix + "pause : ",
"value": "pauses current song if one is playing"
},
{
"name": prefix + "play <song title or artist> :",
"value": "bot will join voice channel and play song if one song available. if more than one, bot will return a list to choose from"
},
{
"name": prefix + "playsong <song number> : ",
"value": "plays a song from the generated song list"
},
{
"name": prefix + "removesong <song queue number> :",
"value": "removes song by index from the song queue"
},
{
"name": prefix + "resume :",
"value": "resumes song if previously paused"
},
{
"name": prefix + "skip :",
"value": "skips the current song if one is playing and plays the next song in queue if it exists"
},
{
"name": prefix + "stop :",
"value": "stops song if one is playing"
},
{
"name": prefix + "viewqueue :",
"value": "displays current song queue"
},
{
"name": prefix + "bot prefix <prefix> :",
"value": "changes the bot prefix for the guild"
}
]
}

return message.channel.send({ embed: help });
}
else if (cmd){
try {
cmd.process(client, message, query);
cmd.process(client, message, query, prefix);
}
catch (e) {
console.log(e);
}
}
else {
message.reply('**Sorry, that\'s not a command.**');
return message.channel.send('I\'m sorry **' + message.author.username + '**, that\'s not a command!\nIf you need help, please type `' + prefix + "help` for a list of available commands.");
}

}
});
};
32 changes: 16 additions & 16 deletions commands/plex.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var plex = new PlexAPI({
password: plexConfig.password,
token: plexConfig.token,
options: {
identifier: 'PlexBot',
identifier: plexConfig.options.identifier,
product: plexConfig.options.identifier,
version: plexConfig.options.version,
deviceName: plexConfig.options.deviceName,
Expand Down Expand Up @@ -45,7 +45,7 @@ var conn = null;
// plex functions ------------------------------------------------------------

// find song when provided with query string, offset, pagesize, and message
function findSong(query, offset, pageSize, message) {
function findSong(query, offset, pageSize, message, prefix) {
plex.query('/search/?type=10&query=' + query + '&X-Plex-Container-Start=' + offset + '&X-Plex-Container-Size=' + pageSize).then(function(res) {
tracks = res.MediaContainer.Metadata;

Expand All @@ -59,7 +59,7 @@ function findSong(query, offset, pageSize, message) {
if (resultSize == 1 && offset == 0) {
songKey = 0;
// add song to queue
addToQueue(songKey, tracks, message);
addToQueue(songKey, tracks, message, prefix);
}
else if (resultSize > 1) {
for (var t = 0; t < tracks.length; t++) {
Expand All @@ -71,8 +71,8 @@ function findSong(query, offset, pageSize, message) {
}
messageLines += (t+1) + ' - ' + artist + ' - ' + tracks[t].title + '\n';
}
messageLines += '\n***!playsong (number)** to play your song.*';
messageLines += '\n***!nextpage** if the song you want isn\'t listed*';
messageLines += '\n***' + prefix + 'playsong (number)** to play your song.*';
messageLines += '\n***' + prefix + 'nextpage** if the song you want isn\'t listed*';
message.reply(messageLines);
}
else {
Expand All @@ -84,7 +84,7 @@ function findSong(query, offset, pageSize, message) {
}

// not sure if ill need this
function addToQueue(songNumber, tracks, message) {
function addToQueue(songNumber, tracks, message, prefix) {
if (songNumber > -1){
var key = tracks[songNumber].Media[0].Part[0].key;
var artist = '';
Expand All @@ -98,7 +98,7 @@ function addToQueue(songNumber, tracks, message) {

songQueue.push({'artist' : artist, 'title': title, 'key': key});
if (songQueue.length > 1) {
message.reply('You have added **' + artist + ' - ' + title + '** to the queue.\n\n***!viewqueue** to view the queue.*');
message.reply('You have added **' + artist + ' - ' + title + '** to the queue.\n\n***' + prefix + 'viewqueue** to view the queue.*');
}

if (!isPlaying) {
Expand Down Expand Up @@ -204,8 +204,8 @@ var commands = {
'nextpage' : {
usage: '',
description: 'get next page of songs if desired song not listed',
process: function(client, message, query) {
findSong(plexQuery, plexOffset, plexPageSize, message);
process: function(client, message, query, prefix) {
findSong(plexQuery, plexOffset, plexPageSize, message, prefix);
}
},
'pause' : {
Expand All @@ -231,13 +231,13 @@ var commands = {
'play' : {
usage: '<song title or artist>',
description: 'bot will join voice channel and play song if one song available. if more than one, bot will return a list to choose from',
process: function(client, message, query) {
process: function(client, message, query, prefix) {
// if song request exists
if (query.length > 0) {
plexOffset = 0; // reset paging
plexQuery = null; // reset query for !nextpage

findSong(query, plexOffset, plexPageSize, message);
findSong(query, plexOffset, plexPageSize, message, prefix);
}
else {
message.reply('**Please enter a song title**');
Expand All @@ -247,12 +247,12 @@ var commands = {
'playsong' : {
usage: '<song number>',
description: 'play a song from the generated song list',
process: function(client, message, query) {
process: function(client, message, query, prefix) {
var songNumber = query;
songNumber = parseInt(songNumber);
songNumber = songNumber - 1;

addToQueue(songNumber, tracks, message);
addToQueue(songNumber, tracks, message, prefix);
}
},
'removesong' : {
Expand Down Expand Up @@ -336,7 +336,7 @@ var commands = {
'viewqueue' : {
usage: '',
description: 'displays current song queue',
process: function(client, message) {
process: function(client, message, prefix) {
//var messageLines = '\n**Song Queue:**\n\n';

var messageLines = '';
Expand All @@ -346,8 +346,8 @@ var commands = {
messageLines += (t+1) + ' - ' + songQueue[t].artist + ' - ' + songQueue[t].title + '\n';
}

messageLines += '\n***!removesong (number)** to remove a song*';
messageLines += '\n***!skip** to skip the current song*';
messageLines += '\n***' + prefix + 'removesong (number)** to remove a song*';
messageLines += '\n***' + prefix + 'skip** to skip the current song*';

var embedObj = {
embed: {
Expand Down
4 changes: 1 addition & 3 deletions config/keys.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
module.exports = {
'clientId' : 'DISCORD_CLIENT_ID',
'clientSecret' : 'DISCORD_CLIENT_SECRET',
'username' : 'DISCORD_BOT_USERNAME',
'botToken' : 'DISCORD_BOT_TOKEN',
'defaultPrefix' : '!'
};
Loading