Browse Source

adding back lots of semi-tested features. still crash when we end playback

tarfeef101 4 years ago
parent
commit
ea9d96be09
4 changed files with 302 additions and 45 deletions
  1. 2 1
      docker-compose.yaml
  2. 45 21
      src/Queue.js
  3. 1 0
      src/package.json
  4. 254 23
      src/viki.js

+ 2 - 1
docker-compose.yaml

@@ -4,7 +4,8 @@ services:
   viki:
     build: .
     image: tarfeef101/viki
-    restart: on-failure
+    #restart: on-failure
+    restart: "no"
     logging:
       driver: "json-file"
       options:

+ 45 - 21
src/Queue.js

@@ -3,47 +3,65 @@ class Queue
   // initializes the queue
   constructor()
   {
-    this.queue = [];
-    this.offset = 0;
+    this.arr = [];
+    this.pos = 0;
   }
-
-  // Returns the length of the queue.
-  getLength()
+  
+  // uses the array x to fill the queue
+  makeFilled(x)
   {
-    return (this.queue.length - this.offset);
+    this.arr = x;
+    this.offset = 0;
   }
 
   // Returns true if the queue is empty, and false otherwise.
   isEmpty()
   {
-    return (this.queue.length == 0);
+    return (this.arr.length == 0);
+  }
+  
+  getPos()
+  {
+    return this.pos;
   }
 
   // Enqueues x in the queue (to the end)
   enqueue(x)
   {
-    this.queue.push(x);
+    this.arr.push(x);
+  }
+  
+  // enqueues x to position i
+  insert(x, i)
+  {
+    this.arr.splice(i, 0, x);
+  }
+  
+  // removes item at index i
+  remove(i)
+  {
+    this.arr.splice(i, 1);
   }
 
   // Dequeues an item and returns it. If the queue is empty, throws an error
   dequeue()
   {
     // if the queue is empty, throw
-    if (this.queue.length == 0)
+    if (this.arr.length == 0)
     {
-      throw "Queue already empty!";
+      throw "Queue empty!";
     }
-
-    // store the item at the front of the queue
-    var item = this.queue[this.offset];
-
-    // increment the offset and refactor if necessary
-    if (++ this.offset * 2 >= this.queue.length)
+    
+    // if the queue is at the end already, return undefined
+    if (this.pos >= this.arr.length)
     {
-      this.queue = this.queue.slice(this.offset);
-      this.offset = 0;
+      return undefined;
     }
 
+    // store the item at the front of the queue
+    var item = this.arr[this.pos];
+    ++this.pos;
+
     // return the dequeued item
     return item;
   }
@@ -52,14 +70,20 @@ class Queue
   // queue is empty then undefined is returned.
   peek()
   {
-    return (this.queue.length > 0 ? this.queue[this.offset] : undefined);
+    return (this.arr.length > 0 ? this.arr[this.pos] : undefined);
+  }
+  
+  // returns an array of all items in the queue (again, without dequeuing) from the current pos.
+  read()
+  {
+    return this.arr.slice(this.pos);
   }
   
   // Deletes all the data, resets to as on construction
   reset()
   {
-    this.queue = [];
-    this.offset = 0;
+    this.arr = [];
+    this.pos = 0;
   }
 }
 

+ 1 - 0
src/package.json

@@ -11,6 +11,7 @@
     "erlpack": "discord/erlpack",
     "ffmpeg-static": "^4.2.7",
     "libsodium-wrappers": "^0.7.8",
+    "request": "^2.88.2",
     "utf-8-validate": "^5.0.3",
     "zlib-sync": "^0.1.7"
   }

+ 254 - 23
src/viki.js

@@ -9,8 +9,12 @@ const client = new Discord.Client();
 // this allows us to define a voice connection with global scope
 var connection;
 var dispatcher;
-// this is the playlist queue for music
+// playlist-related globals
 var playlist = new Queue();
+var played = []
+var cursong;
+var repeatall = false;
+var repeatone = false;
 // Array of music classes users can call (artist, track, etc)
 const musicTypes = ['track', 'title', 'song', 'artist', 'album'];
 const musicTypesString = "track, title, song, artist, album";
@@ -37,14 +41,29 @@ String.prototype.replaceAll = function(remove, replace)
 // This plays the next song in the queue, and logs that in the channel where it was requested.
 function play()
 {
-  let nextSong = playlist.dequeue();
+  var nextSong = cursong;
+  
+  // if we aren't repeating cursong, dequeue
+  if (!repeatone)
+  {
+    played.push(cursong);
+    nextSong = playlist.dequeue();
+  }
+  // if we set repeat, but had no songs played yet
+  // we should dequeue
+  else if (!nextSong)
+  {
+    nextSong = playlist.dequeue();
+  }
+
   dispatcher = connection.play(nextSong[0]);
   console.log(`Playing ${nextSong[2]}.`);
-  nextSong[1].reply(`Playing ${nextSong[2]}.`);
+  nextSong[1].channel.send(`Playing ${nextSong[2]}.`);
   dispatcher.setVolume(0.2);
   dispatcher.setBitrate(96);
+  cursong = nextSong;
 
-  dispatcher.on("finish", reason => 
+  var endHandler = function endHandler(reason)
   {
     if (!(playlist.isEmpty()))
     {
@@ -53,11 +72,72 @@ function play()
     }
     else
     {
-      console.log("Playlist exhausted, music playback stopped.");
+      if (repeatall)
+      {
+        playlist.makeFilled(played);
+        played = [];
+        play();
+        console.log("Repeat all encountered.");
+      }
+      else
+      {
+        console.log("Playlist exhausted, music playback stopped.");
+      }
     }
+  }
+
+  // what to do if it ends
+  dispatcher.on("finish", endHandler);
+  dispatcher.on("close", endHandler);
+}
+
+// riven stuff
+// Load up the request library
+var Request = require("request");
+var unrolledStats = new Map();
+var rolledStats = new Map();
+var cacheTime = 0;
+
+function updateRivens()
+{
+  Request.get("http://n9e5v4d8.ssl.hwcdn.net/repos/weeklyRivensPC.json", (error, response, body) =>
+  {
+    if (error) return console.dir(error);
+
+    var rivenArr = JSON.parse(body);
+
+    for (var i = 0; i < rivenArr.length; i++)
+    {
+      var info = Object.assign({}, rivenArr[i]);
+      delete info.itemType;
+      delete info.compatibility;
+      delete info.rerolled;
+
+      // veiled rivens
+      if (!(rivenArr[i].compatibility))
+      {
+        // set value in map to info
+        unrolledStats.set(rivenArr[i].itemType.toUpperCase(), info);
+      }
+      else // weapon-specific, so check if rolled or unrolled
+      {
+        if (rivenArr[i].rerolled === true)
+        {
+          rolledStats.set(rivenArr[i].compatibility, info);
+        }
+        else
+        {
+          unrolledStats.set(rivenArr[i].compatibility, info);
+        }
+      }
+    }  
   });
 }
 
+updateRivens();
+cacheTime = new Date().getTime() / 1000;
+
+
 client.on("ready", () =>
 {
   // This event will run if the bot starts, and logs in, successfully.
@@ -137,12 +217,60 @@ client.on('message', async msg => {
     }
   }
 
+  if (command === "prices")
+  {
+    // parse args 
+    var type = args[0];
+    args.splice(0, 1);
+    var query = args.join(' ');
+    query = query.toUpperCase();
+   
+    // check cache freshness
+    delta = (new Date().getTime() / 1000) - cacheTime;
+    if (delta > 3600) updateRivens();
+ 
+    if (type == "rolled")
+    {
+      var result = rolledStats.get(query);
+      if (!(result))
+      {
+        return msg.channel.send("Sorry, I couldn't find that weapon. Please check your message and try again.");
+      }
+      result = JSON.stringify(result, undefined, 2);
+      return msg.channel.send(result);
+    }
+    else if (type == "unrolled")
+    {
+      var result = unrolledStats.get(query);
+      if (!(result))
+      {
+        return msg.channel.send("Sorry, I couldn't find that weapon. Please check your message and try again.");
+      }
+      result = JSON.stringify(result, undefined, 2);
+      return msg.channel.send(result);
+    }
+    else
+    {
+      return msg.channel.send("Sorry, please enter a command in the form: prices unrolled/rolled [weapon_name]");
+    }
+  }
+
   if (command === "addmusic") // adds songs to queue, starts playback if none already
   {
+    if (!(config.whitelist.includes(msg.author.tag)))
+    {
+      return msg.channel.send("Sorry, you're not allowed to run this command. Please contact the server owner to acquire that permission.")
+    }
+
+    if (!connection)
+    {
+      return msg.channel.send("Please add me to a voice channel before adding music.")
+    }
+
     var type = args[0];
     if (!(musicTypes.includes(type)))
     {
-      return msg.reply("Sorry, that is not a valid command. Please enter something from: " + musicTypesString);
+      return msg.channel.send("Sorry, that is not a valid command. Please enter something from: " + musicTypesString);
     }
     if (type == 'song' || type == 'track') type = 'title'; // account for poor beets API
     args.splice(0, 1);
@@ -152,11 +280,11 @@ client.on('message', async msg => {
     {
       if (error)
       {
-        return msg.reply(`Sorry, I encountered an issue looking for that: ${error}`);
+        return msg.channel.send(`Sorry, I encountered an issue looking for that: ${error}`);
       }
       else if (stdout === '\n' || stdout === '' || stdout === undefined)
       {
-        return msg.reply(`There were no results that matched your search. Please give the type and name of your query (e.g. song songname, album albumname...)`);
+        return msg.channel.send(`There were no results that matched your search. Please give the type and name of your query (e.g. song songname, album albumname...)`);
       }
       else
       {  
@@ -164,7 +292,7 @@ client.on('message', async msg => {
         {
           if (error)
           {
-            return msg.reply(`Sorry, I encountered an issue looking for that: ${error}`);
+            return msg.channel.send(`Sorry, I encountered an issue looking for that: ${error}`);
           }
           else
           {
@@ -186,7 +314,7 @@ client.on('message', async msg => {
               {
                 if (error)
                 {
-                  return msg.reply(`Sorry, I encountered an issue looking for song ${i}: ${error}`);
+                  return msg.channel.send(`Sorry, I encountered an issue looking for song ${i}: ${error}`);
                 }
                 else
                 {
@@ -194,7 +322,7 @@ client.on('message', async msg => {
                   playlist.enqueue([filepathRaw, msg, stdouts]);
                   
                   // check if music is playing, if not start it
-                  if ((dispatcher === undefined || dispatcher.destroyed == true) && !(playlist.isEmpty()))
+                  if ((dispatcher === undefined || dispatcher.ended == true) && !(playlist.isEmpty()))
                   {
                     play();
                   }
@@ -206,47 +334,109 @@ client.on('message', async msg => {
       }
 
       let amt = stdout.trim();
-      msg.reply(`${amt} songs added!`);
+      msg.channel.send(`${amt} songs added!`);
     });
   }
   
   if (command === 'stop') // clears playlist, stops music
   {
     playlist.reset();
-    dispatcher.destroy();
-    console.log("Playback stopped, playlist cleared.")
+    repeatone = false;
+    repeatall = false;
+    played = [];
+    dispatcher.end();
+    console.log("Playback stopped, playlist cleared.");
+    msg.channel.send("Playback stopped, playlist cleared.");
   }
   
   if (command === 'next') // returns next song in playlist, or informs that there is none
   {
     if (playlist.isEmpty())
     {
-      msg.reply("The playlist is empty.");
+      msg.channel.send("The playlist is empty.");
     }
     else
     {
       const next = playlist.peek();
-      msg.reply(`Next song is: ${next[2]}.`);
+      msg.channel.send(`Next song is: ${next[2]}.`);
+    }
+  }
+
+  if (command === 'previous')
+  {
+    if (played.length <= 1)
+    {
+      msg.channel.send("No previous song.");
+    }
+    else
+    {
+      let temp = played.slice(-1).pop();
+      msg.channel.send(`Previous song was: ${temp[2]}`);
+    }
+  }
+
+  if (command === 'playlist')
+  {
+    if (playlist.isEmpty())
+    {
+      msg.channel.send("The playlist is empty.");
+    }
+    else
+    {
+      const list = playlist.read();
+      
+      for (var i = 0; i < list.length; i++)
+      {
+        msg.channel.send(`Song #${i + 1} is: ${list[i][2]}.`);
+      }
     }
   }
   
   if (command === 'pause') // pauses the dispatcher if playing, or does nothing
   {
-    dispatcher.pause();
-    msg.reply("Playback paused.");
+    dispatcher.pause(true);
+    msg.channel.send("Playback paused.");
   }
-  
+
   if (command === 'resume') // resumes the dispatcher, or does nothing
   {
     dispatcher.resume();
-    msg.reply("Playback resumed.");
+    msg.channel.send("Playback resumed.");
   }
-  
+
+  if (command === 'repeat')
+  {
+    var param = args[0];
+    
+    if (param === 'one' && cursong)
+    {
+      repeatone = true; // causes play function to repeat current cursong
+      repeatall = false;
+      msg.channel.send(`Repeating ${cursong[2]}.`);
+    }
+    else if (param === 'all') // track playlist, and repeat whole thing once empty
+    {
+      repeatone = false;
+      repeatall = true;
+      msg.channel.send("Repeating playlist.");
+    }
+    else if (param === 'off') // resets repeat variables
+    {
+      repeatone = false;
+      repeatall = false;
+      msg.channel.send("Repeat off.");
+    }
+    else
+    {
+      msg.channel.send("There was nothing to repeat, or an invalid option was given. Valid options are one, all, and off.");
+    }
+  }
+
   if (command === 'skip') // starts playing the next song in the queue if it exists
   {
     if (playlist.isEmpty())
     {
-      msg.reply("Sorry, the playlist is empty.");
+      msg.channel.send("Sorry, the playlist is empty.");
     }
     else
     {
@@ -254,7 +444,7 @@ client.on('message', async msg => {
       {
         return new Promise((success, fail) =>
         {
-          dispatcher.destroy();
+          dispatcher.end();
 
           dispatcher.on("finish", () =>
           {
@@ -271,6 +461,47 @@ client.on('message', async msg => {
       resolveEnd();
     }
   }
+
+  if (command === 'back') // if possible, adds cursong to queue at the front, starts playing last song
+  {
+    if (played.length == 0)
+    {
+      msg.channel.send("Sorry, there is no song to skip back to.");
+    }
+    else
+    {
+      function resolveEnd()
+      {
+        return new Promise((success, fail) =>
+        {
+          /*
+          playlist.reset();
+          repeatone = false;
+          repeatall = false;
+          played = [];
+          dispatcher.end();
+          */
+          playlist.cut(cursong); // put cursong back on
+          let tempsong = played[played.length - 1]; // captures the song to go back to
+          played = played.splice(played.length - 1, 1); // removes the last song from played
+          playlist.cut(tempsong); // put old song on the front
+          dispatcher.end(); // stop playing wrong song
+
+          dispatcher.on("finish", () =>
+          {
+            success('Track reversed!');
+          });
+
+          dispatcher.on("error", () =>
+          {
+            fail('Couldn\'t skip :(');
+          });
+        });
+      }
+
+      resolveEnd();
+    }
+  }
 });
 
 client.login(config.token);