<commit_before>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) =>
    {
      //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;
        //database query for guild info (parameter and nsfw flag)
        //console.log(guilds);
        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);
};

<commit_msg>debug<commit_after>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) =>
    {
      //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;
        //database query for guild info (parameter and nsfw flag)
        console.log(guilds);
        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);
};

<|endoftext|>


<commit_before>/*global _, require, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXECUTEJSON, AQL_EXPLAIN */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var errors = require("internal").errors;
var helper = require("org/arangodb/aql-helper");
var isEqual = helper.isEqual;
var db = require("org/arangodb").db;
var _ = require("underscore");

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function optimizerRuleTestSuite () {
  var ruleName = "use-index-range";
  var cn = "UnitTestsAhuacatlRange";
  var c;

  // various choices to control the optimizer:
  var paramNone     = { optimizer: { rules: [ "-all" ] } };
  var paramEnabled  = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
  var paramDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } };

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      db._drop(cn);
      c = db._create(cn);

      var i;

      for (i = 0; i < 100; ++i) {
        c.save({ value1: "test" + i, value2: i });
      }

      c.ensureSkiplist("value1");
      c.ensureSkiplist("value2");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
//      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////

    testRanges : function () {
      var queries = [
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i SORT j.value2 RETURN j.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i <= j.value2 FILTER i >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 < i + 1 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER i.value2 == j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER j.value2 == i.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 || i.value2 == 3 FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i.value2 FILTER j.value2 <= i.value2 + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 <= j.value2 FILTER i.value2 + 1 >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 2, 3 ], false ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 97, 98, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 >= i.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER i.value2 <= j.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ], true ]
      ];

      var opts = _.clone(paramEnabled);
      opts.allPlans = true;
      opts.verbosePlans = true;

      queries.forEach(function(query) {
        var resultDisabled = AQL_EXECUTE(query[0], { }, paramDisabled).json;
        var resultEnabled  = AQL_EXECUTE(query[0], { }, paramEnabled).json;

        if (query[2]) {
          assertNotEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }
        else {
          assertEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }

        assertTrue(isEqual(query[1], resultDisabled), query[0]);
        assertTrue(isEqual(query[1], resultEnabled), query[0]);

        var plans = AQL_EXPLAIN(query[0], { }, opts).plans;
        plans.forEach(function(plan) {
          var result = AQL_EXECUTEJSON(plan, { optimizer: { rules: [ "-all" ] } }).json;
          assertEqual(query[1], result, query[0]);
        });
      });
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(optimizerRuleTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<commit_msg>added bounds tests<commit_after>/*global _, require, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXECUTEJSON, AQL_EXPLAIN */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var errors = require("internal").errors;
var helper = require("org/arangodb/aql-helper");
var isEqual = helper.isEqual;
var db = require("org/arangodb").db;
var _ = require("underscore");

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function singleAttributeTestSuite () {
  var ruleName = "use-index-range";
  var cn = "UnitTestsAhuacatlRange";
  var c;

  // various choices to control the optimizer:
  var paramNone     = { optimizer: { rules: [ "-all" ] } };
  var paramEnabled  = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
  var paramDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } };

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      db._drop(cn);
      c = db._create(cn);

      var i;

      for (i = 0; i < 100; ++i) {
        c.save({ value1: "test" + i, value2: i });
      }

      c.ensureSkiplist("value1");
      c.ensureSkiplist("value2");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////

    testRanges : function () {
      var queries = [
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i SORT j.value2 RETURN j.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i <= j.value2 FILTER i >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 < i + 1 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER i.value2 == j.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER j.value2 == i.value2 RETURN j.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 || i.value2 == 3 FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i.value2 FILTER j.value2 <= i.value2 + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 <= j.value2 FILTER i.value2 + 1 >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 2, 3 ], false ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 97, 98, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 >= i.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER i.value2 <= j.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ], true ]
      ];

      var opts = _.clone(paramEnabled);
      opts.allPlans = true;
      opts.verbosePlans = true;

      queries.forEach(function(query) {
        var resultDisabled = AQL_EXECUTE(query[0], { }, paramDisabled).json;
        var resultEnabled  = AQL_EXECUTE(query[0], { }, paramEnabled).json;

        if (query[2]) {
          assertNotEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }
        else {
          assertEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }

        assertTrue(isEqual(query[1], resultDisabled), query[0]);
        assertTrue(isEqual(query[1], resultEnabled), query[0]);

        var plans = AQL_EXPLAIN(query[0], { }, opts).plans;
        plans.forEach(function(plan) {
          var result = AQL_EXECUTEJSON(plan, { optimizer: { rules: [ "-all" ] } }).json;
          assertEqual(query[1], result, query[0]);
        });
      });
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function nonIndexedAttributeTestSuite () {
  var ruleName = "use-index-range";
  var cn = "UnitTestsAhuacatlRange";
  var c;

  // various choices to control the optimizer:
  var paramNone     = { optimizer: { rules: [ "-all" ] } };
  var paramEnabled  = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
  var paramDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } };

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      db._drop(cn);
      c = db._create(cn);

      var i;

      for (i = 0; i < 100; ++i) {
        c.save({ value1: i * 10, value2: i });
      }

      c.ensureSkiplist("value1", "value2");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////

    testRanges : function () {
      var queries = [
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2 ] ],
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i <= j.value2 FILTER i >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 < i + 1 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i FILTER j.value2 <= i + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER i.value2 == j.value2 RETURN j.value2", [ 2 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 FOR j IN " + cn + " FILTER j.value2 == i.value2 RETURN j.value2", [ 2 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 == 2 || i.value2 == 3 FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 == i.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 == j.value2 SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 >= i.value2 FILTER j.value2 <= i.value2 + 1 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value2 <= j.value2 FILTER i.value2 + 1 >= j.value2 SORT j.value2 RETURN j.value2", [ 2, 3, 3, 4 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 2, 3 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 IN [ i.value2 ] SORT j.value2 RETURN j.value2", [ 97, 98, 99 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER j.value2 >= i.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ] ],
        [ "FOR i IN " + cn + " FILTER i.value2 >= 97 FOR j IN " + cn + " FILTER i.value2 <= j.value2 SORT j.value2 RETURN j.value2", [ 97, 98, 98, 99, 99, 99 ] ]
      ];

      var opts = _.clone(paramEnabled);
      opts.allPlans = true;
      opts.verbosePlans = true;

      queries.forEach(function(query) {
        var resultDisabled = AQL_EXECUTE(query[0], { }, paramDisabled).json;
        var resultEnabled  = AQL_EXECUTE(query[0], { }, paramEnabled).json;

        assertEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);

        assertTrue(isEqual(query[1], resultDisabled), query[0]);
        assertTrue(isEqual(query[1], resultEnabled), query[0]);

        var plans = AQL_EXPLAIN(query[0], { }, opts).plans;
        plans.forEach(function(plan) {
          var result = AQL_EXECUTEJSON(plan, { optimizer: { rules: [ "-all" ] } }).json;
          assertEqual(query[1], result, query[0]);
        });
      });
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function nestedAttributeTestSuite () {
  var ruleName = "use-index-range";
  var cn = "UnitTestsAhuacatlRange";
  var c;

  // various choices to control the optimizer:
  var paramNone     = { optimizer: { rules: [ "-all" ] } };
  var paramEnabled  = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
  var paramDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } };

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      db._drop(cn);
      c = db._create(cn);

      var i;

      for (i = 0; i < 100; ++i) {
        c.save({ value1: { value2: i } });
      }

      c.ensureSkiplist("value1.value2");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////

    testRanges : function () {
      var queries = [
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER j.value1.value2 == i SORT j.value1.value2 RETURN j.value1.value2", [ 2 ], true ],
        [ "FOR i IN [ 2 ] FOR j IN " + cn + " FILTER i == j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i == j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 == i SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 >= i FILTER j.value1.value2 <= i SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER i <= j.value1.value2 FILTER i >= j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ] , true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 >= i FILTER j.value1.value2 < i + 1 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 >= i FILTER j.value1.value2 <= i + 1 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 == 2 FOR j IN " + cn + " FILTER i.value1.value2 == j.value1.value2 RETURN j.value1.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 == 2 FOR j IN " + cn + " FILTER j.value1.value2 == i.value1.value2 RETURN j.value1.value2", [ 2 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 == 2 || i.value1.value2 == 3 FOR j IN " + cn + " FILTER j.value1.value2 == i.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 == i.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value1.value2 == j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 >= i.value1.value2 FILTER j.value1.value2 <= i.value1.value2 + 1 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER i.value1.value2 <= j.value1.value2 FILTER i.value1.value2 + 1 >= j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3, 3, 4 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 IN [ 2, 3 ] FOR j IN " + cn + " FILTER j.value1.value2 IN [ i.value1.value2 ] SORT j.value1.value2 RETURN j.value1.value2", [ 2, 3 ], false ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 >= 97 FOR j IN " + cn + " FILTER j.value1.value2 IN [ i.value1.value2 ] SORT j.value1.value2 RETURN j.value1.value2", [ 97, 98, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 >= 97 FOR j IN " + cn + " FILTER j.value1.value2 >= i.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 97, 98, 98, 99, 99, 99 ], true ],
        [ "FOR i IN " + cn + " FILTER i.value1.value2 >= 97 FOR j IN " + cn + " FILTER i.value1.value2 <= j.value1.value2 SORT j.value1.value2 RETURN j.value1.value2", [ 97, 98, 98, 99, 99, 99 ], true ]
      ];

      var opts = _.clone(paramEnabled);
      opts.allPlans = true;
      opts.verbosePlans = true;

      queries.forEach(function(query) {
        var resultDisabled = AQL_EXECUTE(query[0], { }, paramDisabled).json;
        var resultEnabled  = AQL_EXECUTE(query[0], { }, paramEnabled).json;

        if (query[2]) {
          assertNotEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }
        else {
          assertEqual(-1, AQL_EXPLAIN(query[0], { }, paramEnabled).plan.rules.indexOf(ruleName), query[0]);
        }

        assertTrue(isEqual(query[1], resultDisabled), query[0]);
        assertTrue(isEqual(query[1], resultEnabled), query[0]);

        var plans = AQL_EXPLAIN(query[0], { }, opts).plans;
        plans.forEach(function(plan) {
          var result = AQL_EXECUTEJSON(plan, { optimizer: { rules: [ "-all" ] } }).json;
          assertEqual(query[1], result, query[0]);
        });
      });
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(singleAttributeTestSuite);
jsunity.run(nonIndexedAttributeTestSuite);
jsunity.run(nestedAttributeTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<|endoftext|>


<commit_before>function _assertValidEvent(event) {
  if (typeof event !== 'string' || event.length === 0) {
    throw new TypeError('Event must be a non empty string');
  }
}

function _assertValidEventAndHandler(event, handler) {
  _assertValidEvent(event);
  if (typeof handler !== 'function') {
    throw new TypeError('Handler must be a function.');
  }
}

function _assertSymbol(symbol) {
  if (typeof symbol !== 'symbol') {
    throw new TypeError('Argument must be a symbol');
  }
}

/**
 * Class representing a subscription instance.
 */
class Subscription {
  constructor({ symbol, handler, duration }) {
    if (typeof duration !== 'number' || duration < 1) {
      throw new TypeError('Duration must by a number > 0');
    }

    this.symbol = symbol;
    this.duration = duration;
    this.handler = handler;
  }

  isExpired() {
    return this.duration === 0;
  }
}

// symbol used to access the map of events to their subscriptions
const eventToSubscriptionsMap = Symbol('eventToSubscriptionsMap');

class PubSub {
  constructor() {
    // map of event name to arrays of subscriptions for that event
    this[eventToSubscriptionsMap] = new Map();
  }

  /**
   * Subscribes the given handler for the given event and the optional duration.
   * Default duration is Infinity.
   * @param  {String} event   Event name for which to subscribe the given handler
   * @param  {Function} handler Function to be called when the given event is published by another subscriber
   * @param  {Number} duration Optional integer denoting how many subscriptions should happen before automatically unsubscribing
   * @return {[type]}         [description]
   */
  subscribe(event, handler, duration = Infinity) {
    _assertValidEventAndHandler(event, handler);

    // initialize empty array of subscriptions for the given event, if necessary
    if (!this[eventToSubscriptionsMap].has(event)) {
      this[eventToSubscriptionsMap].set(event, []);
    }

    // unique symbol identifying this subscription
    const symbol = Symbol(event);

    // create the new subscription object
    const subscription = new Subscription({ symbol, handler, duration });

    // add the new subscription object
    this[eventToSubscriptionsMap]
      .get(event)
      .push(subscription);

    // return symbol representing this subscription
    return symbol;
  }

  /**
   * Method to publish data to all subscribers for the given event.
   *
   * @param  {String} event Event for
   * @param  {Array}  args  Array of arguments to send to all subscribers for this event
   * @return {Boolean}      Boolean that is true if publishing succeeded, false otherwise
   */
  publish(event, ...args) {
    _assertValidEvent(event);

    // obtain all subscriptions for a particular event
    const subscriptions = this[eventToSubscriptionsMap].get(event);

    // if nobody registered to this event, return false
    if (!subscriptions || subscriptions.length === 0) {
      return false;
    }

    // publish all subscriptions
    // after each individual publish, cancel the subscription if it expired.
    subscriptions.forEach(sub => {
      if (sub.duration > 0) {
        sub.handler(...args);
        sub.duration -= 1;
      } else {
        this.unsubscribeHandler(event, sub.handler);
      }
    });

    // if publish succeeded, return true
    return true;
  }

  /**
   * Delegates unsubscribing to appropriate method based on argument count.
   */
  unsubscribe(...args) {
    if (args.length === 1) {
      return this.unsubscribeSymbol(args[0]);
    } else if (args.length === 2) {
      return this.unsubscribeHandler(args[0], args[1]);
    } else {
      throw new TypeError('Must pass 1 or 2 arguments');
    }
  }

  /**
   * Cancel a subscription using the subscription symbol
   * @param  {Symbol} symbol subscription symbol
   * @return {Boolean} boolean that is true if subscription was cancelled, false otherwise
   */
  unsubscribeSymbol(symbol) {
    _assertSymbol(symbol);

    // iterate through all event subscriptions
    for (const [event, subscriptions] of this[eventToSubscriptionsMap].entries()) {
      // iterate through all handler for particular event
      for (const [idx, subscription] of subscriptions.entries()) {
        // if symbol represents an existing subscription, remove it
        if (subscription.symbol === symbol) {
          subscriptions.splice(idx, 1);
          return true;
        }
      }
    }

    // return false if a subscription matching given symbol couldn't be found.
    return false;
  }

  /**
   * Cancel a subscription using the same event name and handler that created it.
   * @param  {String} event Event name from which to unsubscribe the given handler
   * @param  {Function} handler Function representing the handler that will be unsubscribed
   * @return {Boolean} boolean that is true if subscription was cancelled, false otherwise
   */
  unsubscribeHandler(event, handler) {
    _assertValidEventAndHandler(event, handler);

    // shortcircuit if nobody subscribed to the given event
    if (!this[eventToSubscriptionsMap].has(event)) {
      return false;
    }

    // extract all subscriptions for current event
    const subscriptions = this[eventToSubscriptionsMap].get(event);

    // if a matching handler is found, remove it and return true
    for (const [idx, subscription] of subscriptions.entries()) {
      console.info('COMPARING:', subscription.handler === handler);
      if (subscription.handler === handler) {
        subscriptions.splice(idx, 1);
        return true;
      }
    }

    // return false if a subscription matching given event&handler pair couldn't be found.
    return false;
  }
}

export default PubSub;
<commit_msg>Cleanup<commit_after>function _assertValidEvent(event) {
  if (typeof event !== 'string' || event.length === 0) {
    throw new TypeError('Event must be a non empty string');
  }
}

function _assertValidEventAndHandler(event, handler) {
  _assertValidEvent(event);
  if (typeof handler !== 'function') {
    throw new TypeError('Handler must be a function.');
  }
}

function _assertSymbol(symbol) {
  if (typeof symbol !== 'symbol') {
    throw new TypeError('Argument must be a symbol');
  }
}

/**
 * Class representing a subscription instance.
 */
class Subscription {
  constructor({ symbol, handler, duration }) {
    if (typeof duration !== 'number' || duration < 1) {
      throw new TypeError('Duration must by a number > 0');
    }

    this.symbol = symbol;
    this.duration = duration;
    this.handler = handler;
  }
}

// symbol used to access the map of events to their subscriptions
const eventToSubscriptionsMap = Symbol('eventToSubscriptionsMap');

class PubSub {
  constructor() {
    // map of event name to arrays of subscriptions for that event
    this[eventToSubscriptionsMap] = new Map();
  }

  /**
   * Subscribes the given handler for the given event and the optional duration.
   * Default duration is Infinity.
   * @param  {String} event   Event name for which to subscribe the given handler
   * @param  {Function} handler Function to be called when the given event is published by another subscriber
   * @param  {Number} duration Optional integer denoting how many subscriptions should happen before automatically unsubscribing
   * @return {[type]}         [description]
   */
  subscribe(event, handler, duration = Infinity) {
    _assertValidEventAndHandler(event, handler);

    // initialize empty array of subscriptions for the given event, if necessary
    if (!this[eventToSubscriptionsMap].has(event)) {
      this[eventToSubscriptionsMap].set(event, []);
    }

    // unique symbol identifying this subscription
    const symbol = Symbol(event);

    // create the new subscription object
    const subscription = new Subscription({ symbol, handler, duration });

    // add the new subscription object
    this[eventToSubscriptionsMap]
      .get(event)
      .push(subscription);

    // return symbol representing this subscription
    return symbol;
  }

  /**
   * Method to publish data to all subscribers for the given event.
   *
   * @param  {String} event Event for
   * @param  {Array}  args  Array of arguments to send to all subscribers for this event
   * @return {Boolean}      Boolean that is true if publishing succeeded, false otherwise
   */
  publish(event, ...args) {
    _assertValidEvent(event);

    // obtain all subscriptions for a particular event
    const subscriptions = this[eventToSubscriptionsMap].get(event);

    // if nobody registered to this event, return false
    if (!subscriptions || subscriptions.length === 0) {
      return false;
    }

    // publish all subscriptions
    // after each individual publish, cancel the subscription if it expired.
    subscriptions.forEach(sub => {
      if (sub.duration > 0) {
        sub.handler(...args);
        sub.duration -= 1;
      } else {
        this.unsubscribeHandler(event, sub.handler);
      }
    });

    // if publish succeeded, return true
    return true;
  }

  /**
   * Delegates unsubscribing to appropriate method based on argument count.
   */
  unsubscribe(...args) {
    if (args.length === 1) {
      return this.unsubscribeSymbol(args[0]);
    } else if (args.length === 2) {
      return this.unsubscribeHandler(args[0], args[1]);
    } else {
      throw new TypeError('Must pass 1 or 2 arguments');
    }
  }

  /**
   * Cancel a subscription using the subscription symbol
   * @param  {Symbol} symbol subscription symbol
   * @return {Boolean} boolean that is true if subscription was cancelled, false otherwise
   */
  unsubscribeSymbol(symbol) {
    _assertSymbol(symbol);

    // iterate through all event subscriptions
    for (const [event, subscriptions] of this[eventToSubscriptionsMap].entries()) {
      // iterate through all handler for particular event
      for (const [idx, subscription] of subscriptions.entries()) {
        // if symbol represents an existing subscription, remove it
        if (subscription.symbol === symbol) {
          subscriptions.splice(idx, 1);
          return true;
        }
      }
    }

    // return false if a subscription matching given symbol couldn't be found.
    return false;
  }

  /**
   * Cancel a subscription using the same event name and handler that created it.
   * @param  {String} event Event name from which to unsubscribe the given handler
   * @param  {Function} handler Function representing the handler that will be unsubscribed
   * @return {Boolean} boolean that is true if subscription was cancelled, false otherwise
   */
  unsubscribeHandler(event, handler) {
    _assertValidEventAndHandler(event, handler);

    // shortcircuit if nobody subscribed to the given event
    if (!this[eventToSubscriptionsMap].has(event)) {
      return false;
    }

    // extract all subscriptions for current event
    const subscriptions = this[eventToSubscriptionsMap].get(event);

    // if a matching handler is found, remove it and return true
    for (const [idx, subscription] of subscriptions.entries()) {
      console.info('COMPARING:', subscription.handler === handler);
      if (subscription.handler === handler) {
        subscriptions.splice(idx, 1);
        return true;
      }
    }

    // return false if a subscription matching given event&handler pair couldn't be found.
    return false;
  }
}

export default PubSub;
<|endoftext|>


<commit_before>/*jshint bitwise: false*/
'use strict';

angular.module('wardenOAuth')

    .factory('UrlLocationService', function ($location, $state) {
        return {

            parseQueryParams: function () {
                return $location.search();
            },

            deleteQueryParam: function (key) {
                console.log("Attempting to delete query param " + key );
                $location.search(key, null);
            },

            /**
            * Returns an absolute URL with the given state/params
            *
            * This method is more robust than $state.href(s,p,{absolute : true})
            */
            getAbsoluteStateUrl : function(state, params) {

              var absUrl = this._absUrlTillHash();
              var stateUrl = $state.href(state, params);
              var angularRoute = this._trimUntilHash(stateUrl);

              return absUrl + "#" + angularRoute;
            },

            /**
            * Returns the current absolute url including the path,
            * but without the hash-bang part.
            *
            * given:  'http://server.any/thing?asdf#/after/the/bang'
            * =>
            * result: 'http://server.any/thing?asdf'
            *
            */
            _absUrlTillHash: function (){
              return window.location.href.split('#',1)[0];
            },

            /**
            * Trims a given url (part) until a hash-bang:
            * given: 'any/thing?asdf#/after/the/bang'
            * =>
            * result: '/after/the/bang'
            */
            _trimUntilHash : function (url){
               var parts = url.split('#',2);
               return parts.length == 1 ? parts[0] : parts[1];
            }

        };
    });
<commit_msg>improved documentation<commit_after>/*jshint bitwise: false*/
'use strict';

angular.module('wardenOAuth')

    .factory('UrlLocationService', function ($location, $state) {
        return {

            /**
            * Returns a map with all query params
            *
            * var params = UrlLocationService.parseQueryParams();
            * var p = params['token'];
            *
            */
            parseQueryParams: function () {
                return $location.search();
            },

            /**
            * Deletes the query param with the given key from the browser address bar.
            * (Works only with params after #-hash-bang)
            */
            deleteQueryParam: function (key) {
                console.log("Attempting to delete query param " + key );
                $location.search(key, null);
            },

            /**
            * Returns an absolute URL with the given state/params
            *
            * This method is more robust than $state.href(s,p,{absolute : true})
            */
            getAbsoluteStateUrl : function(state, params) {

              var absUrl = this._absUrlTillHash();
              var stateUrl = $state.href(state, params);
              var angularRoute = this._trimUntilHash(stateUrl);

              return absUrl + "#" + angularRoute;
            },

            /**
            * Returns the current absolute url including the path,
            * but without the hash-bang part.
            *
            * given:  'http://server.any/thing?asdf#/after/the/bang'
            * =>
            * result: 'http://server.any/thing?asdf'
            *
            */
            _absUrlTillHash: function (){
              return window.location.href.split('#',1)[0];
            },

            /**
            * Trims a given url (part) until a hash-bang:
            * given: 'any/thing?asdf#/after/the/bang'
            * =>
            * result: '/after/the/bang'
            */
            _trimUntilHash : function (url){
               var parts = url.split('#',2);
               return parts.length == 1 ? parts[0] : parts[1];
            }

        };
    });
<|endoftext|>


<commit_before>//*==========  initapp.js  ==========*/

(function() {
    "use strict";
    /*
    NOTE: The Trello client library has been included as a Managed Resource.  To include the client library in your own code, you would include jQuery and then


<commit_before>exports.run = (client) => {
  Promise.all(
    client.cfg.activeRepos.reduce((all, repo) => {
      const repoOwner = repo.split("/")[0];
      const repoName = repo.split("/")[1];
      const func = client.pullRequests.getAll({
        owner: repoOwner, repo: repoName, per_page: 100
      });
      return all.concat(client.getAll(client, [], func));
    }, [])
  ).then(async(array) => {
    const pullRequests = array.reduce((a, element) => {
      return a.concat(element);
    }, []);
    await scrapeInactivePullRequests(client, pullRequests);
  });
};

async function scrapeInactivePullRequests(client, pullRequests) {
  const references = new Map();
  const ims = client.cfg.inactivityTimeLimit * 86400000;
  pullRequests.forEach(async(pullRequest, index, array) => {
    setTimeout(async() => {
      const time = Date.parse(pullRequest.updated_at);
      const body = pullRequest.body;
      const number = pullRequest.number;
      const author = pullRequest.user.login;
      const repoName = pullRequest.base.repo.name;
      const repoOwner = pullRequest.base.repo.owner.login;
      if (time + ims <= Date.now()) {
        const labels = await client.issues.getIssueLabels({
          owner: repoOwner, repo: repoName, number: number
        });
        const inactiveLabel = labels.data.find(l => l.name === client.cfg.inactiveLabel);
        const reviewedLabel = labels.data.find(l => l.name === client.cfg.reviewedLabel);
        if (inactiveLabel || !reviewedLabel) return;
        const comment = client.templates.get("updateWarning").replace("[author]", author)
        .replace("[days]", client.cfg.inactivityTimeLimit);
        const comments = await client.issues.getComments({
          owner: repoOwner, repo: repoName, number: number, per_page: 100
        });
        const com = comments.data.slice(-1).pop();
        const lastComment = com ? com.body.includes(comment) && com.user.login === client.cfg.username : false;
        if (reviewedLabel && !lastComment) {
          client.newComment(pullRequest, pullRequest.base.repo, comment);
        }
      }
      const commits = await client.pullRequests.getCommits({
        owner: repoOwner, repo: repoName, number: number
      });
      const refIssues = commits.data.filter((c) => {
        return c.commit.message.match(/#([0-9]+)/);
      }).map(c => c.commit.message);
      if (body.match(/#([0-9]+)/) || refIssues.length) {
        const commitRef = refIssues[0] ? refIssues[0].match(/#([0-9]+)/)[1] : null;
        const ref = commitRef || body.match(/#([0-9]+)/)[1];
        references.set(`${repoName}/${ref}`, time);
      }
      if (index !== array.length - 1) return;
      const func = client.issues.getAll({
        filter: "all", labels: client.cfg.inProgressLabel, per_page: 100
      });
      const issues = await client.getAll(client, [], func);
      await scrapeInactiveIssues(client, references, issues);
    }, index * 500);
  });
}

async function scrapeInactiveIssues(client, references, issues) {
  const ms = client.cfg.autoAbandonTimeLimit * 86400000;
  const ims = client.cfg.inactivityTimeLimit * 86400000;
  issues.forEach(async(issue, index) => {
    setTimeout(async() => {
      const inactiveLabel = issue.labels.find(label => label.name === client.cfg.inactiveLabel);
      if (inactiveLabel) return;
      let time = Date.parse(issue.updated_at);
      const issueNumber = issue.number;
      const repoName = issue.repository.name;
      const repoOwner = issue.repository.owner.login;
      if (time < references.get(`${repoName}/${issueNumber}`)) {
        time = references.get(`${repoName}/${issueNumber}`);
      }
      if (time + ms >= Date.now() || !client.cfg.activeRepos.includes(`${repoOwner}/${repoName}`)) return;
      const aString = issue.assignees.map(assignee => assignee.login).join(", @");
      if (!aString) {
        const comment = "**ERROR:** This issue is in progress, but has no assignee.";
        return client.newComment(issue, issue.repository, comment);
      }
      const c = client.templates.get("inactiveWarning")
      .replace("[assignee]", aString)
      .replace("[inactive]", client.cfg.inactivityTimeLimit)
      .replace("[abandon]", client.cfg.autoAbandonTimeLimit)
      .replace("[username]", client.cfg.username);
      const issueComments = await client.issues.getComments({
        owner: repoOwner, repo: repoName, number: issueNumber, per_page: 100
      });
      const com = issueComments.data.slice(-1).pop();
      const labelComment = com ? com.body.includes(c) && com.user.login === client.cfg.username : false;
      if (labelComment) {
        const assignees = JSON.stringify({
          assignees: aString.split(", @")
        });
        client.issues.removeAssigneesFromIssue({
          owner: repoOwner, repo: repoName, number: issueNumber, body: assignees
        });
        const warning = client.templates.get("abandonWarning")
        .replace("[assignee]", aString)
        .replace("[total]", client.cfg.autoAbandonTimeLimit + client.cfg.inactivityTimeLimit)
        .replace("[username]", client.cfg.username);
        client.issues.editComment({
          owner: repoOwner, repo: repoName, id: com.id, body: warning
        });
      } else if (!labelComment && time + ims <= Date.now()) {
        client.newComment(issue, issue.repository, c);
      }
    }, index * 500);
  });
}
<commit_msg>pull requests: Add parameters to fetching PRs for inactivity checks.<commit_after>exports.run = (client) => {
  Promise.all(
    client.cfg.activeRepos.reduce((all, repo) => {
      const repoOwner = repo.split("/")[0];
      const repoName = repo.split("/")[1];
      const func = client.pullRequests.getAll({
        owner: repoOwner, repo: repoName, per_page: 100, sort: "long-running", direction: "desc"
      });
      return all.concat(client.getAll(client, [], func));
    }, [])
  ).then(async(array) => {
    const pullRequests = array.reduce((a, element) => {
      return a.concat(element);
    }, []);
    await scrapeInactivePullRequests(client, pullRequests);
  });
};

async function scrapeInactivePullRequests(client, pullRequests) {
  const references = new Map();
  const ims = client.cfg.inactivityTimeLimit * 86400000;
  pullRequests.forEach(async(pullRequest, index, array) => {
    setTimeout(async() => {
      const time = Date.parse(pullRequest.updated_at);
      const body = pullRequest.body;
      const number = pullRequest.number;
      const author = pullRequest.user.login;
      const repoName = pullRequest.base.repo.name;
      const repoOwner = pullRequest.base.repo.owner.login;
      if (time + ims <= Date.now()) {
        const labels = await client.issues.getIssueLabels({
          owner: repoOwner, repo: repoName, number: number
        });
        const inactiveLabel = labels.data.find(l => l.name === client.cfg.inactiveLabel);
        const reviewedLabel = labels.data.find(l => l.name === client.cfg.reviewedLabel);
        if (inactiveLabel || !reviewedLabel) return;
        const comment = client.templates.get("updateWarning").replace("[author]", author)
        .replace("[days]", client.cfg.inactivityTimeLimit);
        const comments = await client.issues.getComments({
          owner: repoOwner, repo: repoName, number: number, per_page: 100
        });
        const com = comments.data.slice(-1).pop();
        const lastComment = com ? com.body.includes(comment) && com.user.login === client.cfg.username : false;
        if (reviewedLabel && !lastComment) {
          client.newComment(pullRequest, pullRequest.base.repo, comment);
        }
      }
      const commits = await client.pullRequests.getCommits({
        owner: repoOwner, repo: repoName, number: number
      });
      const refIssues = commits.data.filter((c) => {
        return c.commit.message.match(/#([0-9]+)/);
      }).map(c => c.commit.message);
      if (body.match(/#([0-9]+)/) || refIssues.length) {
        const commitRef = refIssues[0] ? refIssues[0].match(/#([0-9]+)/)[1] : null;
        const ref = commitRef || body.match(/#([0-9]+)/)[1];
        references.set(`${repoName}/${ref}`, time);
      }
      if (index !== array.length - 1) return;
      const func = client.issues.getAll({
        filter: "all", labels: client.cfg.inProgressLabel, per_page: 100
      });
      const issues = await client.getAll(client, [], func);
      await scrapeInactiveIssues(client, references, issues);
    }, index * 500);
  });
}

async function scrapeInactiveIssues(client, references, issues) {
  const ms = client.cfg.autoAbandonTimeLimit * 86400000;
  const ims = client.cfg.inactivityTimeLimit * 86400000;
  issues.forEach(async(issue, index) => {
    setTimeout(async() => {
      const inactiveLabel = issue.labels.find(label => label.name === client.cfg.inactiveLabel);
      if (inactiveLabel) return;
      let time = Date.parse(issue.updated_at);
      const issueNumber = issue.number;
      const repoName = issue.repository.name;
      const repoOwner = issue.repository.owner.login;
      if (time < references.get(`${repoName}/${issueNumber}`)) {
        time = references.get(`${repoName}/${issueNumber}`);
      }
      if (time + ms >= Date.now() || !client.cfg.activeRepos.includes(`${repoOwner}/${repoName}`)) return;
      const aString = issue.assignees.map(assignee => assignee.login).join(", @");
      if (!aString) {
        const comment = "**ERROR:** This issue is in progress, but has no assignee.";
        return client.newComment(issue, issue.repository, comment);
      }
      const c = client.templates.get("inactiveWarning")
      .replace("[assignee]", aString)
      .replace("[inactive]", client.cfg.inactivityTimeLimit)
      .replace("[abandon]", client.cfg.autoAbandonTimeLimit)
      .replace("[username]", client.cfg.username);
      const issueComments = await client.issues.getComments({
        owner: repoOwner, repo: repoName, number: issueNumber, per_page: 100
      });
      const com = issueComments.data.slice(-1).pop();
      const labelComment = com ? com.body.includes(c) && com.user.login === client.cfg.username : false;
      if (labelComment) {
        const assignees = JSON.stringify({
          assignees: aString.split(", @")
        });
        client.issues.removeAssigneesFromIssue({
          owner: repoOwner, repo: repoName, number: issueNumber, body: assignees
        });
        const warning = client.templates.get("abandonWarning")
        .replace("[assignee]", aString)
        .replace("[total]", client.cfg.autoAbandonTimeLimit + client.cfg.inactivityTimeLimit)
        .replace("[username]", client.cfg.username);
        client.issues.editComment({
          owner: repoOwner, repo: repoName, id: com.id, body: warning
        });
      } else if (!labelComment && time + ims <= Date.now()) {
        client.newComment(issue, issue.repository, c);
      }
    }, index * 500);
  });
}
<|endoftext|>