<|start_filename|>commands/kanji.js<|end_filename|>
//Grabs a definition from Jisho.org
module.exports = function command(bot, info)
{
  'use strict';
  const utility = info.utility;
  const request = require('request');
  const urlencode = require('urlencode');

  return{
    name: 'Kanji',
    alias: ['k'],
    description: '<kanji> ,Looks up kanji information.',
    permissions: 'public',
    action: function(details)
    {
      const searchKanji = function(kanji)
      {
        let options = {};
        options.url = `https://skurt.me/api/kanji/find/${urlencode(kanji)}`;
        options.json = true;
        options.headers = {Authorization: info.config.api.kanji};
        request(options, (err, response, body) =>
        {
          if(!err && response.statusCode === 200)
          {
            bot.sendMessage(details.channelID, {
              embed: prettyDisplay(body)
            }).catch((err) =>
            {
              console.log(`In kanji: ${err}`);
            });
          }
        });
      };
      const prettyDisplay = function(body)
      {
        let emb = {};
        if(body)
        {
          if(body._id != undefined)
          {
            emb.title = body._id;
            if(body.jlpt == undefined && body.grade == undefined)
            {
              emb.description = `\n _ _`;
            }
            else if(body.jlpt == undefined && body.grade != undefined)
            {
              emb.description = `Grade ${body.grade}`;
            }
            else if(body.jlpt != undefined && body.grade == undefined)
            {
              emb.description = `JLPT N${body.jlpt}`;
            }
            else if(body.jlpt != undefined && body.grade != undefined)
            {
              emb.description = `JLPT N${body.jlpt}, Grade ${body.grade}`;
            }
            
            let fields = [];
            let strokes = {name: 'Strokes', value: body.stroke_count[0], inline: true};
            fields.push(strokes);
            let radical = {name: 'Radical', value: body.radical.literal, inline: true};
            fields.push(radical);
            if(body.parents)
            {
              let parents = {name: 'Elements', value: body.parents.join('\n'), inline: true};
              fields.push(parents);
            }
            if(body.children)
            {
              let related = {name: 'Related Kanji', value: body.children.join(', '), inline: true};
              fields.push(related);
            }
            if(body.nanori)
            {
              let related = {name: 'Nanori', value: body.nanori.join(', '), inline: true};
              fields.push(related);
            }
            let readings = {name: 'Readings', value: `${body.reading.ja_on.join('\n')}\n${body.reading.ja_kun.join('\n')}`, inline: true};
            fields.push(readings);
            let meanings = {name: 'Meanings', value: body.meanings.join('\n'), inline: true};
            fields.push(meanings);
            emb.fields = fields;
            return emb;
          }
          else
          {
            emb.title = 'Error';
            emb.description = 'There was no result for what you searched. If this is an error of the command, please contact CyberRonin';
            return emb;
          }
        }
        else
        {
          emb.title = 'Error';
          emb.description = 'There was no result for what you searched. If this is an error of the command, please contact CyberRonin';
          return emb;
        }
      }

      if(details.input === '') {return;}
      else
      {
        searchKanji(details.args[1]);
      }
    }
  };
};

<|start_filename|>commands/loader.js<|end_filename|>
//Loads the commands in from ./commands and builds the help menu
//the help menu displays only the commands that a user can use
module.exports = function commandLoad(bot,info)
{
  'use strict';
  const commands = {};
  const config = info.config;
  const utility = info.utility;
  const fs = require('fs');

  fs.readdir(__dirname, (err, items) =>
  {
    if(err)
    {
      console.log(err);
    }
    else
    {
      items.sort();
      for(let i = 0; i < items.length; i ++)
      {   
        if(items[i] !== 'loader.js' && items[i].endsWith('.js'))
        {
          const commandName = items[i].slice(0,-3);
          commands[commandName] = require(__dirname + '/' + items[i])(bot,info);

          if(!commands[commandName])
          {
            delete commands[commandName];
          }
        }
      }
    }
  });

  commands['help'] = 
  {
    inline: true,
    alias: ['?'],
    description: '(Shows help menu)',
    permissions: 'public',
    action: function(details)
    {    
      //concatenates an array to one line
      const concatArr = function(arr)
      {
        let s = '';
        for(let i = 0; i < arr.length; i++)
        {
          if(arr[i] != null)
          {
            if(i == (arr.length - 1))
            {
              s += details.prefix + arr[i];
            }
            else
            {
              s += details.prefix + arr[i] + ', ';
            }
          }
        }
        return s;
      };

      let lines = [];
      const filter = function(filterArr,key)
      {

        let aliasStr = '';
        let field = {};
        if(utility.searchArr(filterArr,commands[key]['permissions']))
        {
          if(typeof commands[key]['alias'] === 'object')
          {
            aliasStr= ' ' + concatArr(commands[key]['alias']);
          }
          field.name = details.prefix + key + ', ' + aliasStr;
          field.value = commands[key]['description'];
          field.inline = commands[key].inline;
          return field;
        }
        else
        {
          return false;
        }
      };
      let filterArr =[];
      if(details.isAdministrator)
      {
        filterArr = ['public','mod','elevated','private'];
      }
      else if(details.isMod)
      {
        filterArr = ['public','mod','elevated'];
      }
      else if(details.isElevated)
      {
        filterArr = ['public','elevated'];
      }
      else
      {
        filterArr = ['public'];
      }
      let count = 0;
      Object.keys(commands).forEach(function(key)
      { 
        let filtered = filter(filterArr,key);
        if(filtered != false)
        {
          lines.push(filtered);
        }
        
        count ++;
        if(count == Object.keys(commands).length)
        {
          bot.sendMessage(details.channelID, {
            embed: {
              title: 'Help',
              description: 'You can PM the bot :heart:',
              fields: lines
            } 
          }).catch((err) =>
          {
            console.log(`In loader(help): ${err}`);
          });
        }
      });

    }
  };
  return commands;
};

<|start_filename|>lib/utility.js<|end_filename|>
module.exports = function utility(bot, info)
{
  'use strict';
  const utilities = {};
  const config = info.config;

  //Loads a feather if there is one by the name
  utilities.useSource = function(feather)
  {
    if(info.feathers[feather])
    {
      return info.feathers[feather];
    }
    else
    {
      return false;
    }
  };
  //Wraps a string in 3 backticks to make a code block
  utilities.codeBlock = function(str,style)
  {
    return `\`\`\`${style || ''}\n${str}\n\`\`\``;
  };
  //wraps a string to add one backtick to each side to make an inline code block
  utilities.codeInline = function(str)
  {
    return `\`${str}\``;
  };
  //Prints all of the roles in a server
  utilities.printRoles = function(sid)
  {
    let serv = '';
    let r = '';
    let roleStr = '';
    for(r in bot.servers[sid].roles)
    {
      roleStr += ('Name: ' + bot.servers[sid].roles[r].name + ' ID: ' + bot.servers[sid].roles[r].id + '\n');
    }
    return roleStr;
  };
  //Searches for an item in an array
  utilities.searchArr = function(array, item)
  {
    for(let i = 0; i < array.length; i ++)
    {
      if(item == array[i])
      {
        return true;
      }
    }
    return false;
  };
  //Checks to see if a message would be a command (Starts with the prefix)
  utilities.isCommandForm = function(message)
  {
    if(message.startsWith(config.prefix))
    {
      return true;
    }
    else
    {
      return false;
    }
  };
  utilities.isServerCommandForm = function(message, serverID)
  {
    return new Promise((resolve, reject) =>
    {
      info.db.findServer(serverID).then((serverInfo) =>
      {
        if(message.startsWith(serverInfo.prefix))
        {
          //console.log('true');
          resolve({'isCommand': true, 'prefix': serverInfo.prefix});
        }
        else
        {
          //console.log('false');
          reject('Not a prefix');
        }
      });
    });
  };
  //Removes the prefix from the message
  utilities.stripPrefix = function(message)
  {
    if(message.startsWith(config.prefix))
    {
      message = message.replace(config.prefix,'');
    }
    return message;
  };
  utilities.stripServerPrefix = function(message, prefix)
  {
    if(message.startsWith(prefix))
    {
      message = message.replace(prefix,'');
    }
    return message;
  };
  utilities.formatBytes = function(bytes,decimals)
  {
    if (bytes === 0)
    {
      return '0 Byte';
    }
    const k = 1000;
    const dm = decimals + 1 || 3;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  };
  //checks to see if the user that called a command is an Admin
  utilities.isAdministrator = function(userID)
  {
    return config.administrators.indexOf(userID) > -1;
  };
  //returns the userID of a mention
  utilities.stripUID = function(userString)
  {
    let userID = false;
    if (userString !== '')
    {
            //did we provide a mention?
      let regexp = /<\@!*([a-zA-Z0-9]+)>/g;
      let match = regexp.exec(userString);
      if (match)
      {
        userID = match[1];
      }
      else
      {
        userID = false;
      }
    }
    return userID;
  };
  //gets the role(s) of a userID, using sid for the serverID just in case the bot is on more than one
  utilities.getRole = function (uid, sid)
  {
    let roles = [];
    roles = bot.servers[sid].members[uid].roles;
    return roles;
  };
  //gets serverID
  utilities.getServerID = function(channelID)
  {
    return bot.channels[channelID].guild_id;
  };
  //checks to see if you have elevated priveleges
  utilities.checkModPerm = function (uid, sid)
  {
    //Admin, Moderator,  Developer
    const commandRoles = config.mod_roles;
    let userRoles = utilities.getRole(uid,sid);
    for(let i = 0; i < userRoles.length; i++)
    {
      if(utilities.searchArr(commandRoles,userRoles[i]))
      {
        return true;
      }
    }
    return false;
  };
  //checks to see if you have slightly elevated priveleges
  utilities.checkCommandPerm = function (uid, sid)
  {
    //Admin, Moderator, Mentor, Developer, for testing LEADER ROLE FROM PERSONAL SERVER
    const commandRoles = config.elevated_roles.concat(config.mod_roles);
    let userRoles = utilities.getRole(uid,sid);
    for(let i = 0; i < userRoles.length; i++)
    {        
      if(utilities.searchArr(commandRoles,userRoles[i]))
      {
        return true;
      }
    }
    return false;
  };
  //split message into 3 parts, [username, identifier, reason]
  utilities.splitMessage = function(str)
  {
    const patt = /.+#[0-9]{4}/g;
    var result = patt.exec(str);
    let resultArr = [];
    if(result === null)
    {
      return false;
    }
    else
    {
      resultArr = result[0].split('#');
      resultArr.push(str.replace(result[0],'').replace(' ',''));
      return resultArr;
    }
  };
  //replaces %prefix with the prefix from config
  utilities.filter = function(str)
  {
    return str.replace('%prefix', config.prefix);
  };
  return utilities;
};

<|start_filename|>commands/avatar.js<|end_filename|>
//Gets the avatar of either the person calling, or the person that is mentioned.
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Avatar',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['a'],
    //description that shows up in the help menu
    description: '[<mention user>]Gets the avatar for a user that is mentioned. If there is no mention, then it will get the avatar of the person who used the command.',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      const getAvatar = function(uid)
      {
        let ava = undefined;
        let userAva = bot.users[uid].avatar;
        if(userAva === null) return `https://cdn.discordapp.com/embed/avatars/${parseInt(bot.users[uid].discriminator, 10) % 5}.png`
        if(bot.users[uid].avatar.startsWith('a_'))
        {
          ava = 'https://cdn.discordapp.com/avatars/' +uid+'/'+bot.users[uid].avatar+'.gif';
        }
        else
        {
          ava = 'https://cdn.discordapp.com/avatars/' +uid+'/'+bot.users[uid].avatar+'.webp';
        }
        return ava;
      };
      console.log(details.input);
      if(details.input === '')
      {
        bot.sendMessage(details.channelID, {
          embed: {
            title: bot.users[details.userID].username +"'s Avatar",
            image: {
              url: getAvatar(details.userID)
            },
            color: parseInt('808080',16)
          }
        }).catch((err) =>
        {
          console.log(`In avatar: ${err}`);
        });
      }
      else if(details.args.length == 2)
      {
        let uid = info.utility.stripUID(details.args[1]);
        if(uid)
        {
          let link = getAvatar(uid);
          bot.sendMessage(details.channelID, {
            embed: {
              title: bot.users[uid].username+"'s Avatar",
              image: {
                url: getAvatar(uid)
              }
            }
          }).catch((err) =>
          {
            console.log(`In avatar: ${err}`);
          });
        }
      }
      else
      {
        bot.sendMessage(details.channelID, {
          message: 'Please look at the help menu to see how to properly use the command.'
        }).catch((err) =>
        {
          console.log(`In avatar: ${err}`);
        });
      }
    }
  };
};

<|start_filename|>commands/isbn.js<|end_filename|>
//Gets information from a book title/isbn number
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'ISBN',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['book'],
    //description that shows up in the help menu
    description: '[<book title/ isbn>, <book title/ isbn> --list, <book title/ isbn> --<number from --list> Looks up a book from Google Books, you may use isbn <book title/ isbn> -- list to get a list of books returned by the search. Then use isbn <book title/ isbn> --<number> to get that book info to display.]',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      const searchBook = function(w,n)
      {
        //do something here
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'https://www.googleapis.com/books/v1/volumes?q=';

        url += urlencode(w);
        request({url: url, json: false}, function(error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            let bookResponse = JSON.parse(body);
            //console.log(body);
            if(bookResponse.items.length != 0)
            {
              bot.sendMessage(details.channelID, {
                embed: getInfo(bookResponse.items,n)
              }).catch((err) =>
              {
                console.log(`In ISBN: ${err}`);
              });
            }
            else
            {
              bot.sendMessage(details.channelID, {
                embed: {
                  title: 'Error',
                  description: 'An error has occured with that lookup, please try a different name. If the error persists, contact CyberRonin'
                }
              }).catch((err) =>
              {
                console.log(`In ISBN: ${err}`);
              });
            }
          }
        });
      };
      const listBooks = function(w)
      {
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'https://www.googleapis.com/books/v1/volumes?q=';

        url += urlencode(w);
        request({url: url, json: false}, function(error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            let bookResponse = JSON.parse(body);
            if(bookResponse.items.length != 0)
            {
              bot.sendMessage(details.channelID, {
                embed: getList(bookResponse)
              }).catch((err) =>
              {
                console.log(`In ISBN: ${err}`);
              });
            }
            else
            {
              bot.sendMessage(details.channelID, {
                embed: {
                  title: 'Error',
                  description: 'An error has occured with that lookup, please try a different name. If the error persists, contact CyberRonin'
                }
              }).catch((err) =>
              {
                console.log(`In ISBN: ${err}`);
              });
            }

          }
        });
      };
      const getList = function(body)
      {
        let emb = {};
        emb.title = 'List Results';
        emb.description = 'Add --<number> where the number is on this list. e.g (--1 for the first, etc) to the original search.\n\n' +  getTitles(body.items) + '\n _ _';
        emb.footer = { text: 'Results provided by Google'};
        //emb.color = 0xf75239;
        return emb;
      };
      const getTitles = function(arr)
      {
        let str = '';
        let stop = arr.length;
        if(arr.length > 10)
        {
          stop = 10;
        }
        for(let i = 0; i < stop; i ++)
        {
          str += `${i+1}. ${arr[i].volumeInfo.title}`;
          if(arr[i].volumeInfo.subtitle != undefined)
          {
            str += `: ${arr[i].volumeInfo.subtitle}\n`;
          }
          else
          {
            str += '\n';
          }
        }
        return str;
      };
      //gets info for a specific book
      const getInfo = function(body, n)
      {
        const maxLength = 1014;
        //main embed
        let emb = {};
        let tv = true;
        if(body[n].volumeInfo.maturityRating == 'MATURE')
        {
          emb.title = 'Error';
          emb.description = 'The content requested had mature content';
          return emb;
        }
        let fields = [];
        if(body[n].volumeInfo.imageLinks != undefined)
        {
          let thumb = {url:body[n].volumeInfo.imageLinks.thumbnail};
          emb.thumbnail = thumb;
        }
        //Title
        emb.title = body[n].volumeInfo.title;
        if(body[n].volumeInfo.subtitle != undefined)
        {
          emb.title += `: ${body[n].volumeInfo.subtitle}`;
        }
        emb.description = '\n _ _';
        //authors
        let authors = {name: 'Author(s)', inline: true};
        if(body[n].volumeInfo.authors != undefined)
        {
          authors.value = concatArr(body[n].volumeInfo.authors);
        }
        else
        {
          authors.value = 'None provided.';
        }
        fields.push(authors);
        //publish date
        let published = {name: 'Published', inline: true};
        published.value = body[n].volumeInfo.publishedDate;
        fields.push(published);
        //identifiers
        let ident = {name: 'Identifiers', inline: false};
        if(body[n].volumeInfo.industryIdentifiers != undefined)
        {
          ident.value = concatIdentifiers(body[n].volumeInfo.industryIdentifiers);
        }
        else
        {
          ident.value = 'None provided.';
        }
        fields.push(ident);
        //description
        let desc = {name: 'Description', inline: false};
        if(body[n].volumeInfo.description != undefined)
        {
          if(body[n].volumeInfo.description.length > 1014)
          {
            desc.value = `${body[n].volumeInfo.description.slice(0, maxLength - 6)} [...]\n _ _`;
          }
          else
          {
            desc.value = body[n].volumeInfo.description + '\n _ _';
          }
        }
        else
        {
          desc.value = 'No description provided\n _ _';
        }
        fields.push(desc);
        emb.fields = fields;
        emb.footer = { text: 'Results provided by Google'};
        //emb.color = 0xf75239;
        return emb;
      };
      //concatenates an array to one line
      const concatArr = function(arr)
      {
        let s = '';
        for(let i = 0; i < arr.length; i++)
        {
          if(arr[i] != null)
          {
            if(i == (arr.length - 1))
            {
              s += arr[i];
            }
            else
            {
              s += arr[i] + ', ';
            }
          }
        }
        return s;
      };
      //concatenates an array to one line
      const concatIdentifiers = function(arr)
      {
        let s = '';
        for(let i = 0; i < arr.length; i++)
        {
          if(arr[i] != null)
          {
            if(i == (arr.length - 1))
            {
              s += `${arr[i].type}: ${arr[i].identifier}`;
            }
            else
            {
              s += `${arr[i].type}: ${arr[i].identifier}\n`;
            }
          }
        }
        return s;
      };
      //processes the command
      //to better understand this part, take a look at the parameters at the top of the page
      try{
        if(details.input === "") {return;}
        else if(details.input.search(/.+\s(--list)/g) != -1)
        {
          listBooks(details.input.replace(" --list", ''));
          return;
        }
        else if(details.input.search(/^.+\s--[1-9][0-9]*$/g) != -1)
        {
          let patt = /[1-9][0-9]*$/g;
          let num = parseInt(patt.exec(details.input),10);
          searchBook(details.input.replace(/\s--[1-9][0-9]*/g, ''),num - 1);
          return;
        }
        else
        {
          searchBook(details.input, 0);
          return;
        }
      }
      catch(err)
      {
        bot.sendMessage(details.channelID, {
          embed:{
            title: 'Error',
            description: 'Error occured, if you are using a number in a title try to put it in parenthesis. If the error continues, contact CyberRonin.'
          }
        }).catch((err) =>
        {
          console.log(`In ISBN: ${err}`);
        });
        console.log(err);
      }
    }
  };
};

<|start_filename|>commands/serverinfo.js<|end_filename|>
//Gets the info for a server
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Server Info',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['si'],
    //description that shows up in the help menu
    description: 'Gets information on the current server.',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {

      const getInfo = function(uid)
      {
        
        let emb = {};
        let server = bot.servers[details.serverID];
        let safe = false;
        if(server != undefined)
        {
          emb.title = server.name + '\'s Info';
          emb.description = '\n _ _';
          let thumbnail = {url: getIcon(details.serverID)};
          emb.thumbnail = thumbnail;
          let fields = [];
          emb.color = server.members[server.owner_id].color;
          //owner
          let owner = {name: 'Owner', value: `${bot.users[server.owner_id].username}#${bot.users[server.owner_id].discriminator}`};
          fields.push(owner);
          //reagion
          let region = {name: 'Region', value: server.region};
          fields.push(region);
          //creation time
          let created = {name: 'Created', value: getCreatedTime(server.id)};
          fields.push(created);
          //channel info
          let channels = {name: 'Channels', value: getChannelInfo(server.channels) };
          fields.push(channels);
          //member count
          let members = {name: 'Members', value: `${server.member_count} Members, ${getNormies()} Normies`};
          fields.push(members);
          //roles
          let serverRoles = {name: 'Roles', value: getRoles(bot.servers[details.serverID])};
          fields.push(serverRoles);
          emb.fields = fields;

          return emb;
        }
        else
        {
          emb.title = 'Error';
          emb.description = 'You are either not in a server, or there was an error looking up server info.';
          emb.color = 0xFF0000;
          return emb;
        }
      };
      const getChannelInfo = function(channels)
      {
        let voice = 0;
        let text = 0;
        let cat = 0;
        Object.keys(channels).forEach((key) =>
        {
          switch(channels[key].type)
          {
            case 0:
              text ++;
              break;
            case 2:
              voice ++;
              break;
            case 4:
              cat ++;
              break;
          }
        });
        return `${text} Text, ${voice} Voice, ${cat} Categories`;
      };
      const getRoles = function(server)
      {
        const maxLength = 1014;
        const roles = Object.keys(server.roles).map(x => server.roles[x].name).join(', ');
        let numRoles = undefined;
        if(roles.length > maxLength)
        {
          numRoles = Object.keys(server.roles).length;
          return `${numRoles} Roles`;
        }
        else
        {
          return roles;
        }
      };
      const getCreatedTime = function(uid)
      {
        let t = (uid / 4194304) + 1420070400000;
        let created = new Date(t);
        return `${created.toUTCString()}`;
      };
      const getJoinedTime = function(uid)
      {
        let d = new Date(bot.servers[details.serverID].members[details.userID].joined_at);
        let localOffset = 5 * 60000;
        let utc = d.getTime() + localOffset;
        let dUTC = new Date(utc);
        return `${d.toUTCString()}`;

      };
      const getNormies = function()
      {
        let normies = 0;
        Object.keys(bot.servers[details.serverID].members).forEach((memberID) =>
        {
          bot.servers[details.serverID].members[memberID].roles.length === 0 && bot.servers[details.serverID].members[memberID].bot == false ? normies ++ : '';
        });
        return normies;
      };
      const getIcon = function(sid)
      {
        return `https://cdn.discordapp.com/icons/${sid}/${bot.servers[sid].icon}.jpg`;
      };
      //console.log(details.input);
      if(details.input === '')
      {
        bot.sendMessage(details.channelID, {
          embed: getInfo(details.userID)
        }).catch((err) =>
        {
          console.log(`In server info: ${err}`);
        });
      }
      else if(details.args.length == 2)
      {
        let uid = info.utility.stripUID(details.args[1]);
        if(uid)
        {
          let link = getIcon(uid);
          if(link.includes('null.jpg'))
          {
            bot.sendMessage(details.channelID, {
              message: 'The user has a default avatar.'
            }).catch((err) =>
            {
              console.log(`In server info: ${err}`);
            });
          }
          else
          {
            bot.sendMessage(details.channelID, {
              embed: getInfo(uid)
            }).catch((err) =>
            {
              console.log(`In server info: ${err}`);
            });
          }
        }
      }
      else
      {
        bot.sendMessage(details.channelID, {
          message: 'Please look at the help menu to see how to properly use the command.'
        }).catch((err) =>
        {
          console.log(`In server info: ${err}`);
        });
      }
    }
  };
};

<|start_filename|>lib/db.js<|end_filename|>
module.exports = function utility(bot, info)
{
  'use strict';
  const Datastore = require('nedb');
  const db = {};
  db.servers = new Datastore('./lib/databases/servers.db');
  db.servers.loadDatabase();
  //set autocompaction to compact every 10 minutes
  db.servers.persistence.setAutocompactionInterval(600000);
  function loadCommands()
  {
    return new Promise((resolve, reject) =>
    {
      let commandsArr = [];
      let commands = Object.keys(info.commands).filter((command) =>
      {
        if(info.commands[command].name != undefined)
        {
          return command;
        }
      });
      for(let i = 0; i < commands.length; i++)
      {
        let commandObj = {key: commands[i], name: info.commands[commands[i]].name, enabled: true};
        commandsArr.push(commandObj);

      }
      resolve(commandsArr);
    });
    
  }

  bot.on('guildCreate', (server) =>
  {
    console.log(server.name);
    db.newServer(server.id);
  });
  bot.on('guildDelete', (server) =>
  {
    console.log(`Left ${server.name}`);
    db.deleteServer(server.id);
  })
  db.newServer = function (serverID)
  {
    loadCommands().then((commands) =>
    {
      let doc = {
        '_id': serverID,
        'prefix': info.config.prefix,
        'commands': commands
      };

      db.servers.insert(doc, (err, newDoc) =>
      {
        //console.log(newDoc);
        if(!err)
        {
          info.botList.updateBotLists();
        }
      });
    });

  };
  db.findServer = function(serverID)
  {
    return new Promise((resolve, reject) =>
    {
      db.servers.findOne({_id: serverID}, (err, doc) =>
      {
        //console.log(doc);
        resolve(doc);
      });
    });
  };
  db.getSettings = function(serverID)
  {
    return new Promise((resolve, reject) =>
    {
      db.servers.findOne({_id: serverID}, (err, doc) =>
      {
        if(err)
        {
          reject(err);
        }
        else
        {
          if(doc === null)
          {
            resolve(null);
          }
          else
          {
            resolve({prefix: doc.prefix});
          }
        }
      });
    });
  };
  db.updateServer = function(serverID, changes)
  {
    db.servers.update({_id: serverID}, {$set: changes}, (err, numReplaced) =>
    {
      if(err)
      {
        console.log(err);
      }
      else
      {
        console.log('hear');
        console.log(numReplaced);
      }
    });
  };
  db.checkEnabled = function(serverID, command)
  {
    return new Promise((resolve, reject) =>
    {
      db.servers.findOne({_id: serverID}, (err, doc) =>
      {
        if(err)
        {
          console.log(err);
        }
        else
        {
          //this should be whether or not a command is enabled or not SHOULD
          let commands = doc.commands;
          for(let i = 0; i < commands.length; i ++)
          {
            if(commands[i].key === command)
            {
              resolve(commands[i].enabled);
            }
          }
          resolve(false);
        }
      });
    });
  };
  db.addCommand = function(commandName, currentSize)
  {
    return new Promise((resolve, reject) =>
    {
      let commandObj = {key: commandName, name: info.commands[commandName].name, enabled: true};
      db.servers.update({commands: {$size: currentSize}}, {$push: {commands: commandObj}}, {multi: true}, (err, numAffected) =>
      { 
        if(err)
        {
          reject(err);
        }
        resolve(numAffected);
      });
    });
  };
  db.deleteServer = function(serverID)
  {
    db.servers.remove({_id: serverID}, {}, (err, numRemoved) =>
    {
      if(err)
      {
        console.log(err)
      }
      else
      {
        info.botList.updateBotLists();
      }
    });
  };
  return db;
};


<|start_filename|>commands/userinfo.js<|end_filename|>
//gets the avatar of either the person calling, or the person that is mentioned
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'User Info',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['ui'],
    //description that shows up in the help menu
    description: '[<mention user>]Gets information for a user that is mentioned, or the person that used the command. If there is no mention, then it will get the avatar of the person who used the command.',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {

      const getInfo = function(uid)
      {
        let emb = {};
        emb.title = bot.users[uid].username + '\'s Info';
        emb.description = '\n _ _';
        let server = bot.servers[details.serverID];
        let extras = false;
        if(server != undefined)
        {
          extras = true;
        }
        let thumbnail = {url: getAvatar(uid)};
        emb.thumbnail = thumbnail;

        let fields = [];


        if(extras)
        {
          emb.color = bot.servers[details.serverID].members[uid].color;
          if(bot.servers[details.serverID].members[uid].nick)
          {
            let nickname = {name: 'Nickname:', value: bot.servers[details.serverID].members[uid].nick};
            fields.push(nickname);
          }
          
          let joined = {name: 'Joined', value: getJoinedTime(uid)};
          fields.push(joined);
        }

        let created = {name: 'Created:', value: getCreatedTime(uid)};
        fields.push(created);
        if(bot.users[uid].game != null)
        {
          let playing = {name: 'Playing:', value: bot.users[uid].game.name};
          fields.push(playing);
        }

        
        emb.fields = fields;

        return emb;

      };
      const getCreatedTime = function(uid)
      {
        let t = (uid / 4194304) + 1420070400000;
        let created = new Date(t);
        return `${created.toUTCString()}`;
      };
      const getJoinedTime = function(uid)
      {
        let d = new Date(bot.servers[details.serverID].members[uid].joined_at);
        let dUTC = new Date(d.getTime());
        return `${dUTC.toUTCString()}`;

      };
      const getAvatar = function(uid)
      {
        let ava = undefined;
        let userAva = bot.users[uid].avatar;
        if(userAva === null) return `https://cdn.discordapp.com/embed/avatars/${parseInt(bot.users[uid].discriminator, 10) % 5}.png`
        if(bot.users[uid].avatar.startsWith('a_'))
        {
          ava = 'https://cdn.discordapp.com/avatars/' +uid+'/'+bot.users[uid].avatar+'.gif';
        }
        else
        {
          ava = 'https://cdn.discordapp.com/avatars/' +uid+'/'+bot.users[uid].avatar+'.webp';
        }
        return ava;
      };
      if(details.input === '')
      {
        bot.sendMessage(details.channelID, {
          embed: getInfo(details.userID)
        }).catch((err) =>
        {
          console.log(`In userinfo: ${err}`);
        });
      }
      else if(details.args.length == 2)
      {
        let uid = info.utility.stripUID(details.args[1]);
        if(uid)
        {
          let link = getAvatar(uid);
          if(link.includes('null.jpg'))
          {
            bot.sendMessage(details.channelID, {
              message: 'The user has a default avatar.'
            }).catch((err) =>
            {
              console.log(`In userinfo: ${err}`);
            });
          }
          else
          {
            bot.sendMessage(details.channelID, {
              embed: getInfo(uid)
            }).catch((err) =>
            {
              console.log(`In userinfo: ${err}`);
            });
          }
        }
      }
      else
      {
        bot.sendMessage(details.channelID, {
          message: 'Please look at the help menu to see how to properly use the command.'
        }).catch((err) =>
        {
          console.log(`In userinfo: ${err}`);
        });
      }
    }
  };
};

<|start_filename|>lib/web/css/custom.css<|end_filename|>
@import "https://fonts.googleapis.com/icon?family=Material+Icons";
footer.page-footer {
  margin: 0;
}
footer.page-footer.mint
{
  background-color: #A7FCD0;
}
.mint
{
  background-color: #A7FCD0;
}
.mint-text
{
  color: #A7FCD0 !important;
}
.collection .collection-item
{
  color: #424242;
  background-color: #9e9e9e;
  border-color:#424242;
}
.collection a.collection-item
{
  color: #424242;
}
.dropdown-content li>a, .dropdown-content li>span {
    font-size: 16px;
    color: #A7FCD0;
    display: block;
    line-height: 22px;
    padding: 14px 16px;
}
[type="checkbox"]:checked+label:before
{
  border-right: 2px solid #424242;
  border-bottom: 2px solid #424242 ;
}
input[type=text]
{
  border: 1px solid #ccc;
  border-radius: 4px;
  width: inherit;
  background-color: white;
  padding-left: 5px;
  transition: inherit;
  -webkit-transition: inherit;
}
input[type=text]:focus:not([readonly])
{
  border: 2px solid #424242;
  border-bottom-color: #424242;
  border-bottom: 2px solid #424242;
  box-shadow: none;
}
label
{
  font-size: inherit;
  color:#424242 ;
  display: block;
}
.mintbtn:hover
{
  background-color: #A7FCD0;
  color: #424242;
}
.mintbtn
{
  color: #A7FCD0;
  background-color: #424242;
}
.submit-padding
{
  padding-top: 5px;
}
.collection
{
  box-shadow: 0 2px 2px 0 #212121, 0 1px 5px 0 #212121, 0 3px 1px -2px #212121;
  border: none;
}
.card .card-action a:not(.btn):not(.btn-large):not(.btn-floating)
{
    color: grey;
    margin-right: 20px;
    transition: color .3s ease;
    text-transform: uppercase;
    cursor: pointer;
}
.card .card-action a:not(.btn):not(.btn-large):not(.btn-floating):hover
{
  color:#212121 ;
  cursor: pointer;
}
h5
{
  border-bottom: #212121 ;
  border-bottom-style: solid;
  border-bottom-width: 2px;
}
.card-panel span.mint-text h5
{
  border-bottom: #A7FCD0;
  border-bottom-style: solid;
  border-bottom-width: 2px;
}
.card .card-reveal
{
  overflow-y: hidden;
}
.darkgrey
{
  color: #333;
}
.activator
{
  color: grey;
  margin-right: 20px;
  transition: color .3s ease;
  text-transform: uppercase;
  cursor: pointer;
}
.activator:hover
{
  color:black;
  cursor: pointer;
}

body
{
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}
main
{
  flex: 1 0 auto;
}
.mint-logo:hover
{
  fill: #424242;
}
.mint-logo-stroke:hover
{
  stroke: #424242;
}
.logo-text:hover
{
  cursor: default;
}
.custom-card .card-action
{
  border-top: 1px solid #212121;
}
.custom-card
{
  box-shadow: 0 2px 2px 0 #212121, 0 1px 5px 0 #212121, 0 3px 1px -2px #212121;
}
.custom-card .card-action a
{
  color: #A7FCD0 !important;
}
.custom-card .card-action a:hover
{
  color: #212121 !important;
}
.center
{
  text-align: center;
}
label.custom
{
  text-align: left;
}
h5.grey-underline
{
  border-bottom: #424242 !important;
  border-bottom-style: solid !important;
  border-bottom-width: 2px !important;
}
footer.page-footer
{
  padding-top: 5px;
}
.page-footer .container .row
{
  margin-bottom: 0px;
}
.page-footer p
{
  margin-bottom: 0px;
}
.mint-divider 
{
  height: 1px;
  overflow: hidden;
  background-color: #A7FCD0;
}
nav .button-collapse
{
  margin: 0;
}
.reg-cursor
{
  cursor: default;
}
.side-nav .userView .email
{
  font-size: 12px;
  top: -10px !important;
  position: relative;
}

<|start_filename|>template.js<|end_filename|>
//template command
//when calling the commands, loader.js will create the command based on what the
//name of the file is.
module.exports = function command(bot, info)
{
  'use strict';
  return {
    //the alias can be set by putting what you'd like in that array
    alias: [''],
    //description that shows up in the help menu
    description: '(Description)',
    //set the permissions [public, elevated, mod, private]
    permissions: '',
    action: function(details)
    {
      //do something here
    }
  };
};

<|start_filename|>commands/dog.js<|end_filename|>
//Gets a random dog picture/gif.
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Dog',
    inline: true,
    //the alias can be set by putting what you'd like in that array
    alias: ['d', 'inu', '犬'],
    //description that shows up in the help menu
    description: '(Gets random dog pictures)',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      const sendDog = function()
      {
        const request = require('request');
        const url = 'http://random.dog/woof';
        request({
          url: url,
          json: false
        }, function (error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            bot.sendMessage(details.channelID, {
              embed:{
                title: '',
                description: '',
                image:{
                  url: `http://random.dog/${body}`
                }
              }
            }).catch((err) =>
            {
              console.log(`In dog: ${err}`);
            });
          }
        });
      };
      if(details.input === '')
      {
        sendDog();
      }
    }
  };
};

<|start_filename|>commands/strokeorder.js<|end_filename|>
//stroke order boi
module.exports = function command(bot, info)
{
  'use strict';
  const request = require('request');
  const urlencode = require('urlencode');

  return {
    name: 'Stroke Order',
    inline: true,
    alias: ['so'],
    description: 'Stroke order of a kanji.',
    permissions: 'public',
    action: function(details)
    {
      const searchKanji = (k) =>
      {
        kanjiReq(k).then((file) =>
        {
          bot.uploadFile(details.channelID, file, {
            fileName: 'strokes.gif',
          }).catch((err) => {
            console.log(err);
          });
        }).catch((err) =>
        {
          bot.sendMessage(details.channelID, {
            embed: {
              title: 'Error',
              description: 'Kanji not found.',
              color: info.utility.red
            }
          });
        });
      };

      const kanjiReq = function(kanji)
      {
        return new Promise((resolve, reject) =>
        {
          let options = {};
          options.url = 'https://skurt.me/api/kanji/stroke/' + urlencode(kanji);
          options.headers = {Authorization: info.config.api.kanji};
          options.json = true;
          options.encoding = null;
          request(options, (err, response, body) => {
            if(!err && response.statusCode == 200) {
              resolve(response.body);
            }
            else {
              reject({err, body});
            }
          });
        });
      };
    

      if(details.input === '') {return;}
      else
      {
        searchKanji(details.args[1]);
      }
    }
  };
};

<|start_filename|>commands/kill.js<|end_filename|>
//Kills the bot, just in case it's acting up ;)
module.exports = function command(bot, info)
{
  'use strict';
  return {
    inline: true,
    alias: ['ki'],
    description: 'Kills the bot',
    permissions: 'private',
    action: function(details)
    {
      info.manualKill = true;
      bot.disconnect();
    }
  };
};

<|start_filename|>lib/web/js/custom.js<|end_filename|>
$( document ).ready(function()
{
    console.log( "ready!" );
    $('.dropdown-button').dropdown({
      gutter: 0, // Spacing from edge
      belowOrigin: true, // Displays dropdown below the button
      alignment: 'left', // Displays dropdown with edge aligned to the left of button
      stopPropagation: false // Stops event propagation
    }
  );
  $(".button-collapse").sideNav({
    edge: "right",
    draggable: true
  });
  $('#server_submit').click( function() {
    if($('#prefix').val() === '')
    {
      Materialize.toast('You must enter a prefix', 4000);
    }
    else
    {
      $.post( '/form', $('#server_form').serialize(), function(data)
      {
        Materialize.toast('Info submitted', 4000);
      },
        'json' // I expect a JSON response
      );
    }
    
  });
});

<|start_filename|>lib/permissions.js<|end_filename|>
//Permission stuffs
module.exports = function permissions()
{
	//this code has been graciously provided by izy521, it is from the Discord.io lib
  const perms = {};
  perms.permissionBits = {
    GENERAL_CREATE_INSTANT_INVITE: 0,
    GENERAL_KICK_MEMBERS: 1,
    GENERAL_BAN_MEMBERS: 2,
    GENERAL_ADMINISTRATOR: 3,
    GENERAL_MANAGE_CHANNELS: 4,
    GENERAL_MANAGE_GUILD: 5,
    GENERAL_MANAGE_ROLES: 28,
    GENERAL_MANAGE_NICKNAMES: 27,
    GENERAL_CHANGE_NICKNAME: 26,
    GENERAL_MANAGE_WEBHOOKS: 29,
    GENERAL_MANAGE_EMOJIS: 30,

    TEXT_ADD_REACTIONS: 6,
    TEXT_READ_MESSAGES: 10,
    TEXT_SEND_MESSAGES: 11,
    TEXT_SEND_TTS_MESSAGE: 12,
    TEXT_MANAGE_MESSAGES: 13,
    TEXT_EMBED_LINKS: 14,
    TEXT_ATTACH_FILES: 15,
    TEXT_READ_MESSAGE_HISTORY: 16,
    TEXT_MENTION_EVERYONE: 17,
    TEXT_EXTERNAL_EMOJIS: 18,

    VOICE_CONNECT: 20,
    VOICE_SPEAK: 21,
    VOICE_MUTE_MEMBERS: 22,
    VOICE_DEAFEN_MEMBERS: 23,
    VOICE_MOVE_MEMBERS: 24,
    VOICE_USE_VAD: 25,
  };

  perms.hasPermission = function(bit, permissions)
  {
    if(((permissions >> bit) & 1) === 1)
    {
      return true;
    }
    else
    {
      return false;
    }
  };
  
  return perms;
};


<|start_filename|>commands/time.js<|end_filename|>
//command to get the time
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Time',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['t'],
    //description that shows up in the help menu
    description: `Gets the time in UTC. ${info.config.prefix}time <time zone abbreviation> for a specific time zone. ${info.config.prefix}time --list for a list of the time zones `,
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      //do something here
      //ms in an hour
      const msInHour = 3600000;
      const getTime = function(abbr)
      {

        if(info.time[abbr] != undefined)
        {
          let d = new Date();
          let localTime = d.getTime();
          let localOffset = d.getTimezoneOffset() * 60000;
          let utc = localTime + localOffset;
          let target = utc + (info.time[abbr].offset * 3600000)
          let dStr = new Date(target).toLocaleString();
          //let dStr = // `${dAdj.toDateString()} ${dAdj.getHours()}:${dAdj.getMinutes()} (${info.time[abbr].value})`
          return dStr + ' ' + info.time[abbr].text;
        }
        return undefined;
      };
      const getZones = function(page)
      {
        let p1 = '';
        let p2 = '';
        let arr = Object.keys(info.time);
        for(let i = 0; i < arr.length; i ++)
        {
          if(i < arr.length/2)
          {
            p1 += `${arr[i]}: ${info.time[arr[i]].text}\n`;
          }
          else
          {
            p2 += `${arr[i]}: ${info.time[arr[i]].text}\n`;
          }
        }
        if(page == '1')
        {
          return p1;
        }
        else
        {
          return p2;
        }
        /*
        Object.keys(info.time).forEach((key) =>
        {
          s += `${key}\n`//: ${info.time[key].value}\n`;
        });*/

        return s;
      };
      if(details.input === '')
      {
        let d = new Date();
        let localTime = d.getTime();
        let localOffset = d.getTimezoneOffset() * 60000;
        let utc = localTime + localOffset;
        let dUTC = new Date(utc);
        bot.sendMessage(details.channelID, {
          embed: {
            title: 'Time',
            description: `${dUTC.toLocaleString()} (UTC)`
          }
        }).catch((err) =>
        {
          console.log(`In time: ${err}`);
        });
      }
      else if(details.args[1] != undefined)
      {
        //let num = parseInt(patt.exec(details.input),10);
        //console.log("Num is " + num + " details is " + details.input);
        //console.log("Searching for " + details.input.replace(/\s[1-9][0-9]*/g, ''));
        if(details.args[1] === '--list')
        {
          console.log('getting it');
          if(details.args[2] != undefined)
          {
            if(details.args[2] == '2')
            {
              bot.sendMessage({
                to: details.channelID,
                embed: {
                  title: 'Time Zone List',
                  description: getZones(2),
                  footer: {text: 'Page 2'}
                }
              }).catch((err) =>
              {
                console.log(`In time: ${err}`);
              });
            }
          }
          else
          {
            bot.sendMessage(details.channelID, {
              embed: {
                title: 'Time Zone List',
                description: getZones(1),
                footer: {text: `Page 1: ${info.config.prefix}time --list 2 for Page 2`}
              }
            }).catch((err) =>
            {
              console.log(`In time: ${err}`);
            });
          }
          return;
        }
        let t = getTime(details.args[1].toUpperCase());
        if(t != undefined)
        {
          bot.sendMessage(details.channelID, {
            embed:{
              title: 'Time',
              description: t
            } 
          }).catch((err) =>
          {
            console.log(`In time: ${err}`);
          });
        }
        return;
      }
    }
  };
};

<|start_filename|>plugins/botlist.js<|end_filename|>
const request = require('request');

module.exports = function plugin(bot, info)
{
  'use strict';
  const plugin = {};
  function submit(options)
  {
    request(options, (err, response, body) =>
    {
      if(err)
      {
        console.log(err)
      }
      else
      {
        console.log(body); 
      }
    });
  }
  function getInfo()
  {
    return new Promise((resolve, reject) =>
    {
      resolve({"server_count": Object.keys(bot.servers).length});
    });
  }
  plugin.updateBotLists = function()
  {
    getInfo().then((status) =>
    {
      let botsPW = {
        url: 'https://bots.discord.pw/api/bots/193403332046487552/stats',
        method: 'POST',
        headers: {
          'Authorization': info.config.api.botsPW,
          'content-type': 'application/json'
          },
        body: JSON.stringify(status)
      };
      submit(botsPW);
    });
  };
  return plugin;
}


<|start_filename|>commands/invite.js<|end_filename|>
//Gives a link to the source code on github
module.exports = function command(bot, info)
{
  'use strict';
  const pack = require('../package.json');
  return {
    name: 'Invite',
    inline: true,
    alias: ['i'],
    description: "(Gets the bot's invite link)",
    permissions: 'public',
    action: function(details)
    {
      bot.sendMessage(details.channelID, {
        content: '<https://discordapp.com/oauth2/authorize?client_id=193403294419255297&scope=bot&permissions=0>'
      }).catch((err) =>
      {
        console.log(`In invite: ${err}`);
      });
    }
  };
};

<|start_filename|>commands/normies.js<|end_filename|>
//Lists the amount of normies in a server
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Normies',
    inline: true,
    alias: ['n'],
    description: 'Gets a list of people without a role',
    permissions: 'public',
    action: function(details)
    {
      const getNormies = function()
      {
        let normies = [];
        Object.keys(bot.servers[details.serverID].members).forEach((memberID) =>
        {
          if(bot.servers[details.serverID].members[memberID].roles.length === 0 && bot.servers[details.serverID].members[memberID].bot == false)
          {
            normies.push(`<@${memberID}>`);
          }
        });
        return normies;
      };
      if(bot.servers[details.serverID])
      {
        let normies = getNormies().join('\n');
        if(normies.length > 2000)
        {
          bot.sendMessage(details.channelID, {embed: {title: 'Error', description: 'This server is full of normies.'}});
        }
        else
        {
          let normieEmbed = {
            title: 'Normies',
            description: normies.length < 1 ? 'You have no normies here.' : normies
          };
          bot.sendMessage(details.channelID, {embed: normieEmbed});
        }
      }
      else
      {
        bot.sendMessage(details.channelID, {embed: {title: 'Error', description: `Uh oh, you're not in a server or the command is borked.`}});
      }
    }
  };
};

<|start_filename|>lib/portal.js<|end_filename|>
module.exports = function portal(bot, info)
{
  const express = require('express');
  const app = express();
  const request = require('request');
  const fs = require('fs');
  const Handlebars = require('handlebars');
  const session = require('express-session');
  const bodyParser = require('body-parser');
  const perms = require('./permissions')();
  const https = require('https');
  const helmet = require('helmet');
  //const multer = require('multer');
  //const upload = multer();
  const db = info.db;
  //keeps track of the guilds that sessions have access to
  const guildsWithPerms = {};
  //holds the tokens, keys are session IDs
  const tokens = {};
  //config for testing purposes
  const config = info.config;
  let privateKey = fs.readFileSync(__dirname + '/web/privkey1.pem');
  let certificate = fs.readFileSync(__dirname + '/web/fullchain1.pem');
  
  //use bodyParser to parse post shtuff\
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));
  //security shtuff
  app.use(helmet());
  app.use(helmet.referrerPolicy({ policy: 'no-referrer'}));
  app.use(helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", 'https://fonts.googleapis.com', "'unsafe-inline'"],
      imgSrc: ["'self'", 'https://cdn.discordapp.com', 'https://www.google-analytics.com'],
      scriptSrc: ["'self'", 'https://www.google-analytics.com', "'unsafe-inline'"],
      fontSrc: ["'self'", 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://fonts.googleapis.com']
    }
  }));
  //Actually use session info.
  app.use(session({
    secret: config.web.client_secret,
    resave: false,
    saveUninitialized: true
  }));
  //get info from the users and guilds endpoints
  //Promise that resolves an object with user and guilds as keys.
  function getAllInfo (sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      let userUrl = `https://discordapp.com/api/users/@me`;
      let guildsUrl = 'https://discordapp.com/api/users/@me/guilds';
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token).then((newToken) =>
        {
          token = newToken;
        }).then((err) =>
        {
          console.log(err);
        });
      }
      //console.log(tokens[sessionId]);
      let info = {};
      let userPromise = requestPage(userUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      let guildsPromise = requestPage(guildsUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      Promise.all([userPromise, guildsPromise]).then((values) =>
      {
        info.user = values[0];
        info.guilds = values[1];
        resolve(info);
      });
    });
  };
  //gets info for the servers a user is connected to
  //promise resolves an array of guild objects
  function getServerInfo(sessionId)
  {
    let token = tokens[sessionId];
    if(Date.now() - token.time >= 604800)
    {
      refreshToken(token).then((newToken) =>
      {
        token = newToken;
      });
    }
    let url = 'https://discordapp.com/api/users/@me/guilds';
    return requestPage(url, {json: true, headers: {Authorization: `Bearer ${tokens[sessionId].token}`}});
  }
  //gets info for a user (the user that is authenticated via Oauth)
  //promise resolves user obj
  function getUserInfo(sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token);
        token = tokens[sessionId];
      }
      let url = 'https://discordapp.com/api/users/@me';
      requestPage(url, {json: true, headers: {Authorization: `Bearer ${token.token}`}}).then((user) =>
      {
        resolve(user);
      });
    });
  }
  //the keys are sessionId's, which could be used in guildsWithPerms as well
  //what we'll do here is find any sessions that are older than 2 days and
  //clear them out of tokens and guildsWithPerms if they are present
  setInterval(() =>
  {
    Object.keys(tokens).forEach((key) =>
    {
      if(Date.now() - tokens[key].time >= 172800000)
      {
        delete tokens[key];
        if(guildsWithPerms[key] != undefined)
        {
          delete guildsWithPerms[key];
        }
      }
    });
  }, 172800000);
  //requests a url with the selected options
  //promise resolves the body of the url, or rejects with the error
  function requestPage(url, options)
  {
    options.url = url;
    return new Promise((resolve, reject) =>
    {
      request(options, (err, response, body) =>
      {
        if(!err && response.statusCode === 200)
        {
          //console.log(body);
          resolve(body);
        }
        else
        {
          reject(err);
        }
      });
    });
  }
  function filterSearch(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      filterGuilds(guilds).then((filtered) =>
      {
        //console.log(filtered);
        searchServers(filtered).then((searched) =>
        {
          resolve(searched);
        });
      });
    });

  }
  //filters the guilds that you don't have access to
  //promise resolves the guilds that you have Manage Server perms
  function filterGuilds(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      if(guilds == null)
      {
        resolve(null);
      }
      let filteredGuilds = guilds.filter((guild) =>
      {
        return perms.hasPermission(perms.permissionBits['GENERAL_MANAGE_GUILD'], guild.permissions);
      });
      resolve(filteredGuilds);
    });
  }
  //gets the prefix, nsfw setting and possibly add buttons to add HawkBot to the server
  function searchServers(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      if(guilds.length == 0)
      {
        resolve(null);
      }
      //console.log(`In searchServers: ${JSON.stringify(guilds)}`);
      let len = Object.keys(guilds).length -1;
      let counter = 0;
      Object.keys(guilds).forEach((key) =>
      {
        //console.log(guilds[key].id);
        db.getSettings(guilds[key].id).then((settings) =>
        {
          //console.log(settings);
          if(settings != null)
          {
            guilds[key].settings = settings;
          }
          if(counter == len)
          {
            resolve(guilds);
          }
          counter++;
        });
      });
    });

  }
  //Hopefully don't need this bs
  function getServerContext()
  {
    return new Promise((resolve, reject) =>
    {
      if(guildsWithPerms[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
        {
          resolve(serverID);
        }
        else
        {
          resolve(null);
        }
      }
    });
  }
  //handles the refreshing of tokens
  function refreshToken(token)
  {
    return new Promise((resolve, reject) =>
    {
      let data = {
        refresh_token: token.refresh,
        grant_type: 'refresh_token'
      };

      let base64 = new Buffer(`${config.web.client_id}:${config.web.client_secret}`).toString('base64');
      request.post({
        url: 'https://discordapp.com/api/oauth2/token',
        headers: { Authorization: `Basic ${base64}` },
        form: data
      }, (err, httpResponse, body) =>
      {
        
        if(err)
        {
          reject(err);
        }
        else
        {
          body = JSON.parse(body);
          tokens[token.session] = {
            token: body.access_token,
            session: token.session,
            time: Date.now(),
            refresh: body.refresh_token
          };
          resolve(tokens[token.session]);
        }
      });
    });
    
  }
  app.use(express.static(__dirname+'/web'));
  //handle the root of the site
  app.get('/', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        context.user = info;
        //console.log(info);
        let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  //handles login
  app.get('/login', (req, res, next) =>
  {
    let data = {
      client_id: config.web.client_id,
      client_secret: config.web.client_secret,
      code: req.query.code,
      redirect_uri: config.web.redirect_uri,
      grant_type: 'authorization_code'
    };
    request.post({
      url: 'https://discordapp.com/api/oauth2/token',
      form: data
    }, (err, httpResponse, body) =>
    {
      if(err)
      {
        console.log(err);
      }
      else
      {
        body = JSON.parse(body);
        tokens[req.sessionID] = {
          token: body.access_token,
          session: req.sessionID,
          time: Date.now(),
          refresh: body.refresh_token
        };
        //console.log(tokens);
        //console.log(body);
      }
      res.redirect('https://bot.cyberhawk.co/');
    });
  });
  app.get('/servers', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getAllInfo(req.sessionID).then((info) =>
      {
        context.user = info.user;
        let guilds = info.guilds;
        return filterSearch(guilds);
      }).catch((err) =>
      {
        console.log(err);
      }).then((guilds) =>
      {
        context.guilds = guilds;
        //console.log(`In SERVERS: ${JSON.stringify(guilds)}`);
        //database query for guild info (parameter and nsfw flag)
        if(guilds != null)
        {
          guildsWithPerms[req.sessionID] = guilds.map((guild) =>
          {
            return guild.id;
          });
        }
        
        //console.log(guildsWithPerms[req.sessionID]);
        let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
    }
  });
  app.get('/server', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        if(req.query.serverID != undefined)
        {
          //console.log('query is defined');
          let serverID = req.query.serverID;
          if(guildsWithPerms[req.sessionID])
          {
            if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
            {
              context.user = info;
              //query db for document(nedb lingo)
              return db.findServer(serverID);
            }
          }
        }
        context.user = info;
        
      }).then((server) =>
      {
        if(server != null)
        {
          context.server = server;
          try{
            context.serverName = bot.servers[server._id].name;
          }
          catch(err)
          {
            console.log(err);
          }
        }
        
        //console.log(server);
        let userPage = fs.readFileSync('./lib/web/server.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  app.post('/form',(req, res, next) =>
  {
    if(req.body.id)
    {
      if(tokens[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID])
        {
          if(guildsWithPerms[req.sessionID].indexOf(req.body.id) > -1)
          {
            let postData = req.body;
            //you can do stuff cuz you have perms.
            //if it has id, it will have at least prefix as well
            //console.log("You actually have perms to do this, nice");
            db.findServer(postData.id).then((oldInfo) =>
            {
              if(oldInfo !== null)
              {
                let done = oldInfo.commands.length - 1;
                let count = 0;
                for(let i = 0; i < oldInfo.commands.length; i ++)
                {
                  if(postData[oldInfo.commands[i].key])
                  {
                    //this means that the checkmard was on, so it'll be enabled
                    //console.log(`${postData[oldInfo.commands[i].key]} exists, going to enabled it`);
                    oldInfo.commands[i].enabled = true;
                  }
                  else
                  {
                    //this means that the command wasn't checked, so it's disabled
                    //console.log(`${postData[oldInfo.commands[i].key]} doesn't exist, going to disable it`);
                    oldInfo.commands[i].enabled = false;
                  }
                  if(count === done)
                  {
                    let newData = {};
                    newData.prefix = postData.prefix;
                    if(postData.nsfw)
                    {
                      newData.nsfw = true;
                    }
                    else
                    {
                      newData.nsfw = false;
                    }
                    newData.commands = oldInfo.commands;
                    //console.log(newData);
                    db.updateServer(postData.id, newData);
                  }
                  count++;
                }
              }
            });
          }
        }
      }
    }
    //console.log(req.body);
    res.json(req.body);
  });
  app.get('/logout', (req, res, next) =>
  {
    if(tokens[req.sessionID])
    {
      delete tokens[req.sessionID];
      delete guildsWithPerms[req.sessionID];
    }
    res.redirect("http://hawkbot.cyberhawk.co");
  });
  https.createServer({
    key: privateKey,
    cert: certificate
  }, app).listen(443);
  // Redirect from http port 80 to https
  const http = require('http');
  http.createServer(function (req, res)
  {
    res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url });
    res.end();
  }).listen(80);
};



<|start_filename|>commands/jisho.js<|end_filename|>
//Grabs a definition from Jisho.org
module.exports = function command(bot, info)
{
  'use strict';
  const utility = info.utility;
  return{
    name: 'Jisho',
    inline: false,
    alias: ['j'],
    description: '[<word/sentence>, <word/sentence> --list, <word/sentence> <number from --list>] Looks up a word from Jisho.org, you may use jisho <word> --list to get a list of definitions. Then use jisho <word> <number> and that will display the definition',
    permissions: 'public',
    action: function(details)
    {
      const out = {};
      //searches Jisho.org for a definition
      const searchJisho = function(w,n)
      {
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'http://jisho.org/api/v1/search/words?keyword=';
        //encode the URL to be used for the request (in order to work with JP), then make the API call
        url += urlencode(w);
        request({
          url: url,
          json: true
        }, function (error, response, body)
        {
          if (!error && response.statusCode === 200)
          {
            bot.sendMessage(details.channelID, {
              embed:{
                title: '',
                description: '',
                fields: prettyDisplay(body,n),
                color: parseInt('a7fcd0', 16)
              }
            }).catch((err) =>
            {
              console.log(`In jisho: ${err}`);
            });
          }
        });
      };
      //lists all of the different readings/definitions returned by Jisho.org
      //if there are more than 10, it will be sent to the user via PM
      const listJisho = function(w)
      {
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'http://jisho.org/api/v1/search/words?keyword=';
        url += urlencode(w);
        request({
          url: url,
          json: true
        }, function (error, response, body)
        {
          if (!error && response.statusCode === 200)
          {
            if(body.data.length > 10 && details.isDirectMessage)
            {
              bot.sendMessage(details.userID, {
                embed: {
                  title: 'Reading List:',
                  description: listJapanese(body) + '\nUse jisho(j) <word> <number on list> to get that definition',
                  color: parseInt('a7fcd0', 16)
                }
              }).catch((err) =>
              {
                console.log(`In jisho: ${err}`);
              });
            }
            else if(body.data.length > 10 && !details.isDirectMessage)
            {
              bot.sendMessage(details.channelID, {
                message: 'The word returned more than 10 items, sending a PM of the list!'
              }).catch((err) =>
              {
                console.log(`In jisho: ${err}`);
              });
              bot.sendMessage(details.userID, {
                embed: {
                  title: 'Reading List:',
                  description: listJapanese(body) + '\nUse jisho(j) <word> <number on list> to get that definition',
                  color: parseInt('a7fcd0', 16)
                }
              }).catch((err) =>
              {
                console.log(`In jisho: ${err}`);
              });
            }
            else
            {
              bot.sendMessage(details.channelID, {
                embed: {
                  title: 'Reading List:',
                  description: listJapanese(body) + '\nUse jisho(j) <word> <number on list> to get that definition',
                  color: parseInt('a7fcd0', 16)
                }
              }).catch((err) =>
              {
                console.log(`In jisho: ${err}`);
              });
            }
          }
        });
      };
      //list the japanese readings
      const listJapanese = function(api)
      {
        let list = '';
        for(let i = 0; i < api.data.length; i ++)
        {
          let line = '';
          line += (i+1) + '.' + getJapanese(api.data[i].japanese);
          if(i != (api.data.length - 1))
          {
            line += '\n';
          }
          list += line;
        }
        return list;
      };
      //grab the japanese readings
      const getJapanese = function(japaneseArr)
      {
        let ret = '';
        for(let i = 0; i < japaneseArr.length; i ++)
        {
          let line = '';
          if(japaneseArr[i].word == undefined)
          {
            line += ' (' + japaneseArr[i].reading + ')';
          }
          else if(japaneseArr[i].reading == undefined)
          {
            line += ' ' + japaneseArr[i].word;
          }
          else
          {
            line += ' ' + japaneseArr[i].word + ' (' + japaneseArr[i].reading + ')';
          }
          if(i != japaneseArr.length - 1)
          {
            line += ',';
          }
          ret += line;
        }
        return ret;
      };
      //gets the definitions
      const getDefinitions = function(sensesArr)
      {
        let definitions = '';
        let numDefs = 0;
        for(var i = 0; i < sensesArr.length; i ++)
        {
          if(sensesArr[i].english_definitions !== undefined)
          {
            definitions += (numDefs+1) + '. ';
            numDefs ++;
            if(concatArr(sensesArr[i].parts_of_speech) == '')
            {
              if(concatArr(sensesArr[i].tags) == '')
              {
                definitions += concatArr(sensesArr[i].english_definitions) +'\n';
              }
              else
              {
                definitions+= concatArr(sensesArr[i].english_definitions) + ' - ' + concatArr(sensesArr[i].tags) + '. ' + concatArr(sensesArr[i].info) + '\n';
              }
            }
            else
            {
              definitions += '*' + concatArr(sensesArr[i].parts_of_speech) + '*: ';
              if(concatArr(sensesArr[i].tags) == '')
              {
                definitions += concatArr(sensesArr[i].english_definitions) + '\n';
              }
              else
              {
                definitions+= concatArr(sensesArr[i].english_definitions) + ' - ' + concatArr(sensesArr[i].tags) + '. '+ concatArr(sensesArr[i].info) + '\n';
              }
            }
          }
          
          
        }
        return definitions;
      };
      //concatenates an array to one line
      const concatArr = function(arr)
      {
        let s = '';
        if(arr !== undefined)
        {
          for(let i = 0; i < arr.length; i++)
          {
            if(arr[i] != null)
            {
              if(i == (arr.length - 1))
              {
                s += arr[i];
              }
              else
              {
                s += arr[i] + ', ';
              }
            }
          }
        }
        
        return s;
      };
      //takes all of the data found and displays it nicely
      const prettyDisplay = function(api, num)
      {
        let readField = {name: 'Reading(s):',inline: true};
        let tagField = {name: 'Tag(s):',inline: true};
        let defField = {name: 'Definition(s):', inline: false};
        let fields = [];
        try
        {
          if(api.data[num] == undefined)
          {
            throw 'api.data is undefined';
          }
          else
          {
            const tags = getTags(api.data[num]);
            readField.value = getJapanese(api.data[num].japanese);
            fields.push(readField);
            if(tags != '')
            {
              tagField.value = tags;
              fields.push(tagField);
            }
            defField.value = getDefinitions(api.data[num].senses);
            if(api.data.length > 10)
            {
              defField.value += 'The lookup has more than 10 items from Jisho. Try jisho(j) <word> --list for the list.';
            }
            fields.push(defField);
          }
        }
        catch(err)
        {
          console.log( 'Error occured: Error Code ' + err +' - something was looked up that would break bot. Writing to dump file.' + new Date());
          fields = [{name:'Error',value: 'Error occured with that lookup. Try the command again. If that command doesn\'t work, please contact CyberRonin',inline:true}];
        }
        return fields;
      };
      //grabs the tags from the call
      const getTags = function(dataArr)
      {
        let tags = '';
        if(dataArr.is_common == true)
        {
          tags += '`Common` ';
        }
        tags += concatTags(dataArr.tags);
        return tags;
      };
      //lists the tags a little nicer
      const concatTags = function(arr)
      {
        let s = '';
        for(let i = 0; i < arr.length; i++)
        {
          if(i == (arr.length - 1))
          {
            s += utility.codeInline(arr[i]);
            s += ' ';
          }
          else
          {
            s += utility.codeInline(arr[i]);
            s += ' ';
          }
        }
        return s;
      };
      //processes the command
      //to better understand this part, take a look at the parameters at the top of the page
      if(details.input === '') {return;}
      else if(details.input.search(/.+\s(--list)/g) != -1)
      {
        listJisho(details.input.replace(' --list', ''));
        return;
      }
      else if(details.input.search(/^.+\s[1-9][0-9]*$/g) != -1)
      {
        let patt = /[1-9][0-9]*$/g;
        let num = parseInt(patt.exec(details.input),10);
        console.log('Num is ' + num + ' details is ' + details.input);
        console.log('Searching for ' + details.input.replace(/\s[1-9][0-9]*/g, ''));
        searchJisho(details.input.replace(/\s[1-9][0-9]*/g, ''),num - 1);
        return;
      }
      else
      {
        searchJisho(details.input, 0);
        return;
      }
    }
  };
};

<|start_filename|>commands/anime.js<|end_filename|>
//Gets information on an anime, or a list of anime based on a search.
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Anime',
    inline: false,
    //the alias can be set by putting what you'd like in that array
    alias: ['an'],
    //description that shows up in the help menu
    description: '[<anime>, <anime> --list, <anime> --<number from --list> Looks up an anime from Kitsu.io, you may use anime <anime> -- list to get a list of animes returned by the search. Then use anime <anime> --<number> to get that anime info to display.]',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      const searchAnime = function(w,n)
      {
        //do something here
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'https://kitsu.io/api/edge/anime?filter[text]=';

        url += urlencode(w);
        request({url: url, json: false}, function(error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            let animeResponse = JSON.parse(body);
            //console.log(body);
            if(animeResponse.data.length != 0)
            {
              bot.sendMessage(details.channelID, {
                embed: getInfo(animeResponse.data,n)
              }).catch((err) =>
              {
                console.log(`In anime: ${err}`);
              });
            }
            else
            {
              bot.sendMessage(details.channelID, {
                embed: {
                  title: 'Error',
                  description: 'An error has occured with that lookup, please try a different name. If the error persists, contact CyberRonin'
                }
              }).catch((err) =>
              {
                console.log(`In anime: ${err}`);
              });
            }

          }
        });
      };
      
      const listAnime = function(w)
      {
        const request = require('request');
        const urlencode = require('urlencode');
        var url = 'https://kitsu.io/api/edge/anime?filter[text]=';

        url += urlencode(w);
        request({url: url, json: false}, function(error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            let animeResponse = JSON.parse(body);
            if(animeResponse.data.length != 0)
            {
              bot.sendMessage(details.channelID, {
                embed: getList(animeResponse)
              }).catch((err) =>
              {
                console.log(`In anime: ${err}`);
              });
            }
            else
            {
              bot.sendMessage(details.channelID, {
                embed: {
                  title: 'Error',
                  description: 'An error has occured with that lookup, please try a different name. If the error persists, contact CyberRonin'
                }
              }).catch((err) =>
              {
                console.log(`In anime: ${err}`);
              });
            }

          }
        });
      };
      const getList = function(body)
      {
        let emb = {};
        emb.title = 'List Results';
        emb.description = 'Add --<number> where the number is on this list. e.g (--1 for the first, etc) to the original search.\n\n' +  getNames(body.data) + '\n _ _';
        emb.footer = { text: 'Results provided by Kitsu.io', icon_url: 'http://www.cyberhawk.co/kitsu.png'};
        emb.thumbnail = {url:'http://www.cyberhawk.co/kitsu.png' };
        emb.color = 0xf75239;
        return emb;
      };

      const getNames = function(arr)
      {
        let str = '';
        for(let i = 0; i < arr.length; i ++)
        {
          str += `${i+1}. ${arr[i].attributes.titles.en_jp}`;
          if(arr[i].attributes.titles.ja_jp != null && arr[i].attributes.titles.ja_jp != '')
          {
            str += ` (${arr[i].attributes.titles.ja_jp})\n`;
          }
          else
          {
            str += '\n';
          }
        }
        return str;
      };

      const getInfo = function(body, n)
      {
        const maxLength = 1014;
        //console.log(body);
        let tv = true;
        if(body[n].attributes.showType == 'Movie')
        {
          tv = false;
        }
        //main embed
        let emb = {};
        if(body[n].attributes.titles.en != null && body[n].attributes.titles.en != '')
        {
          emb.title = body[n].attributes.titles.en;
          emb.description = body[n].attributes.titles.en_jp;
          if(body[n].attributes.titles.ja_jp)
          {
            emb.description += '\n' + body[n].attributes.titles.ja_jp;
          }
        }
        else
        {
          emb.title = body[n].attributes.titles.en_jp;
          if(body[n].attributes.titles.ja_jp)
          {
            emb.description = body[n].attributes.titles.ja_jp;
          }
          else
          {
            emb.description = '\n _ _';
          }
        }
        emb.url = `https://kitsu.io/anime/${body[n].attributes.slug}`;
        //thumbnail
        let thumb = {};
        thumb.url = body[n].attributes.posterImage.medium;
        //fields embeds
        let fields = [];
        //Air Dates
        let airDates = {name: 'Air Date(s):', inline: true};
        if(body[n].attributes.startDate != null)
        {
          if(!tv)
          {
            airDates.value = `Aired: ${body[n].attributes.startDate}`;
          }
          else
          {
            airDates.value = `Start: ${body[n].attributes.startDate}`;
          }
        }
        if(body[n].attributes.endDate != null)
        {
          if(tv)
          {
            airDates.value += `\nFinish: ${body[n].attributes.endDate}`;
          }      
        }
        if(body[n].attributes.endDate != null || body[n].attributes.startDate != null)
        {
          fields.push(airDates);
        }
        //Episodes
        let eps = {name: 'Episodes:', inline:true};
        if(body[n].attributes.episodeCount == null)
        {
          eps.value = 'Unknown.';
        }
        else
        {
          eps.value = body[n].attributes.episodeCount;
        }
        if(tv)
        {
          fields.push(eps);
        }        
        //ep runtime
        let epRun = {name: 'Episode Runtime:', inline: true};
        if(!tv)
        {
          epRun.name = 'Movie Runtime:';
        }
        if(body[n].attributes.episodeLength!= null)
        {
          epRun.value = `${body[n].attributes.episodeLength}m`;
          fields.push(epRun);
        }
        //age Rating
        if(body[n].attributes.ageRating != null)
        {
          let age = {name: 'Age Rating', value: body[n].attributes.ageRating, inline: true};
          fields.push(age);
        }
        if(body[n].attributes.youtubeVideoId != '' && body[n].attributes.youtubeVideoId != null)
        {
          let youtube = {name: 'Trailer Link', value: `https://www.youtube.com/watch?v=${body[n].attributes.youtubeVideoId}`, inline: true};
          fields.push(youtube);
        }
        //synopsis
        let synopsis = {name: 'Synopsis:', inline: false};
        if(body[n].attributes.synopsis == '')
        {
          synopsis.value = 'No synopsis yet 😭';
        }
        else
        {
          if(body[n].attributes.synopsis.length > 1014)
          {
            synopsis.value = `${body[n].attributes.synopsis.slice(0, maxLength - 6)} [...]\n _ _`;
          }
          else
          {
            synopsis.value = body[n].attributes.synopsis + '\n _ _';
          }
        }
        fields.push(synopsis);
        //Rating
        emb.thumbnail = thumb;
        emb.fields = fields;
        emb.footer = { text: 'Results provided by Kitsu.io', icon_url: 'http://www.cyberhawk.co/kitsu.png'};
        emb.color = 0xf75239;
        return emb;
      };

      //concatenates an array to one line
      const concatArr = function(arr)
      {
        let s = '';
        for(let i = 0; i < arr.length; i++)
        {
          if(arr[i].name != null)
          {
            if(i == (arr.length - 1))
            {
              s += arr[i].name;
            }
            else
            {
              s += arr[i].name + ', ';
            }
          }
        }
        return s;
      };
      //processes the command
      //to better understand this part, take a look at the parameters at the top of the page
      try{
        if(details.input === '') {return;}
        else if(details.input.search(/.+\s(--list)/g) != -1)
        {
          listAnime(details.input.replace(' --list', ''));
          return;
        }
        else if(details.input.search(/^.+\s--[1-9][0-9]*$/g) != -1)
        {
          let patt = /[1-9][0-9]*$/g;
          let num = parseInt(patt.exec(details.input),10);
          console.log('Num is ' + num + ' details is ' + details.input);
          console.log('Searching for ' + details.input.replace(/\s[1-9][0-9]*/g, ''));
          searchAnime(details.input.replace(/\s[1-9][0-9]*/g, ''),num - 1);
          return;
        }
        else
        {
          searchAnime(details.input, 0);
          return;
        }
      }
      catch(err)
      {
        bot.sendMessage(details.channelID,
          {
          embed:{
            title: 'Error',
            description: 'Error occured, if you are using a number in a title try to put it in parenthesis. If the error continues, contact CyberRonin.'
          }
        }).catch((err) =>
        {
          console.log(`In anime: ${err}`);
        });
        console.log(err);
      }
    }
  };
};

<|start_filename|>commands/suggestion.js<|end_filename|>
//Suggestion command
module.exports = function command(bot, info)
{
  'use strict';
  return {
    inline: true,
    name: 'Suggestion',
    alias: ['su'],
    description: '<suggestion> Submits a suggestion to the developer of HawkBot :)',
    //public, elevated, mod, or private
    permissions: 'public',
    action: function(details)
    {
      bot.sendMessage(info.config.suggestion_channel, {
        embed: {
          title: 'Suggestion',
          description: details.input,
          footer: {text: `${details.user}#${bot.users[details.userID].discriminator}` || details.userID}
        }
      }).catch((err) =>
      {
        console.log(`In suggestion: ${err}`);
      });
    }
  };
};

<|start_filename|>commands/source.js<|end_filename|>
//Gives a link to the source code on github
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Source',
    inline: true,
    alias: ['git'],
    description: 'Gets the bot\'s source',
    permissions: 'public',
    action: function(details)
    {
      bot.sendMessage(details.channelID, {
        content: 'https://github.com/CyberHawkSoftware/HawkBot'
      }).catch((err) =>
      {
        console.log(`In source: ${err}`);
      });
    }
  };
};

<|start_filename|>commands/writingprompt.js<|end_filename|>
//template command
//when calling the commands, loader.js will create the command based on what the
//name of the file is.
module.exports = function command(bot, info)
{
  'use strict';
  return {
    name: 'Writing Prompt',
    inline: true,
    //the alias can be set by putting what you'd like in that array
    alias: ['wp'],
    //description that shows up in the help menu
    description: '(Gets random pictures)',
    //set the permissions [public, elevated, mod, private]
    permissions: 'public',
    action: function(details)
    {
      const sendImage = function()
      {
        const request = require('request');
        const url = 'http://www.splashbase.co/api/v1/images/random';
        request({
          url: url,
          json: true
        }, function (error, response, body)
        {
          if(!error && response.statusCode === 200)
          {
            //let catResponse = JSON.parse(body);
            bot.sendMessage(details.channelID, {
              embed:{
                title: '',
                description: '',
                image:{
                  url: body.url
                }
              }
            }).catch((err) =>
            {
              console.log(`In writingprompt: ${err}`);
            });
          }
        });
      };
      if(details.input === '')
      {
        sendImage();
      }
    }
  };
};

<|start_filename|>plugins/trello.js<|end_filename|>
//template feather
module.exports = function plugin(bot, info)
{
  'use strict';
  //feather obj
  const plugin = {};
  //set variable for config
  const config = info.config;
  //requires
  const request = require('request');
  //plugin functions
  function createCard(content)
  {
    request.post({
      url: `https://api.trello.com/1/cards?key=${config.api.trello.api_key}&token=${config.api.trello.token}`,
      json: true,
      form: content
    }, (err, response, body) =>
    {
      if(err)
      {
        console.log(err);
      }
    });
  };
  bot.on('messageReactionAdd', (reaction) =>
  {
    if(reaction.user_id === '190175637271478273' && reaction.channel_id === info.config.suggestion_channel && reaction.emoji.name === '👍')
    {
      bot.getMessage(info.config.suggestion_channel,reaction.message_id).then((message) =>
      {
        let newCard = {
          name: `${message.embeds[0].description}\n${message.embeds[0].footer.text}`,
          idList: '58fe3b0d4e2f17d3cddb5f11',
          pos: 'top',
          idLabels: '58fe3a14ced82109ff526fbf'
        };
        createCard(newCard);
      }).catch((err) => 
      {
        console.log(err);
      });
    }
  });
  return plugin;
};
