<commit_before>var later  = require('later');
var hash   = require('json-hash');
var moment = require('moment');

var bot;

var minimumInterval = 0; 
var noCommands = false;

//FIXME: this is gross vv
var schedules = [];
var timers = {};

function writeSchedule(data) {
  bot.writeDataFile("later.json", 
      JSON.stringify({'data': data}), function(err) {
        if(err) console.log("Error writing command file: " + err);
  });
}

function newSchedule(data) {
  var s;

  try {
    s = later.parse.text(data['schedule']);
  } catch (ex) {
    return ex;
  }

  // FIXME: change to > -1, @ char #
  if( s == 0 )
    return "Provided schedule query doesn't parse.";
  
  data['schedule'] = s;

  // check that command is valid
  //   - check if commands are disabled
  //   - doesn't call schedule
  //   - doesn't call a dumbcommand which calls schedule
  //     * checked via perceived caller
  // check frequency is below some minimum (config)
  
  if(noCommands && data.command.substring(0, bot.config.commandPrefix.length) 
      == bot.config.commandPrefix) 
    return "Commands are disabled";

  if(data['command'].match(/^!schedule/))
    return "Fuck you"; // FIXME: personalize

  if(data['blame'] == bot.client.nick)
    return "Cannot call scheduler recursively.";
  
  if(minimumInterval > 0 && schedules.length > 0 
      && moment().diff(schedules.slice(-1)[0].created, 'seconds') <= minimumInterval)
    return "Schedule creation is rate limited. Please wait at least " + minimumInterval
           + " seconds between schedule creation.";

  registerCommand(data);

  schedules.push(data);
  writeSchedule(schedules);
}

function registerCommand(data) {
  var command;

  //If it's a command, emulate being sent a command. Otherwise say it.
  command = function() {
    //FIXME: Properly emulate raw.
    bot.sayTo(data['channel'], data['command']); // say to channel even for own commands
    bot.client.emit('message', bot.client.nick, data['channel'], 
      data['command'], data['command']);
  };

  timers[hash.digest(data)] = later.setInterval(command, data['schedule']);
}

module.exports.init = function(b) {
  bot = b;
  bot.readDataFile("later.json", function(err, data) {
    console.log("Read data file w/ error '" + err + "'");
    if(err) {
      console.log("Initializing later.json");
      schedules = [];
      writeSchedule(schedules);
    } else {
      try {
        console.log("Parsing later.json...");
        schedules = JSON.parse(data)['data'];

        // process schedules
        schedules.forEach(function(e, i, d) {
          e['created'] = moment(e['created']);
          registerCommand(e);
        });
      } catch(ex) {
        console.log("Error parsing: " + ex);
        console.log("Corrupted later.json for schedule! Resetting file...");
        schedules = [];
        writeSchedule(schedules);
      }
    }
  });

  bot.getConfig("schedule.json", function(err, conf) {
    if(!err) {
      minimumInterval = conf.minimum_interval;
      noCommands = conf.no_commands;
    }
  });
};

module.exports.commands = {
  schedule: {
    _default: function(x,y,reply) {
      reply("Usage: !schedule [<add>|<remove>|<list>|<help>] [arguments]");
      reply("       Also see !schedule help <subcommand> for more details");
    },
    help: {
      _default: null, // to be aliased later to ^
      add: function(x,y,reply) {
        reply("Usage: !schedule add \"timeframe\" \"command or text\"");
        reply("       Timeframe syntax is here: http://bunkat.github.io/later/parsers.html#text");
        reply("       If a valid command isn't specified, it is treated as text to print.");
      },
      remove: function(x,y,reply) {
        reply("Usage: !schedule remove [hash|LAST]");
        reply("       Removes the schedule specified by the hash or the LAST one added.");
      },
      list: function(x,y,reply) {
        reply("Usage: !schedule list [offset=0]");
        reply("       Provides a list of schedules, in pages, from the specified offset of 0.");
      },
    },
    add: function(r, parts, reply, command, from, to, text, raw) {
      if(parts.length !== 2) return reply("add must have *exactly* two arguments");

      // check and add schedule
      var err = newSchedule({
        'blame': from,
        'created': new moment(),
        'schedule': parts[0],
        'command': parts[1],
        'channel': to
      });

      if(err)
        reply(err);
      else
        reply("Added");
    },
    list: function(x, parts, reply, command, from, to) {
      var offset = 0;
      var oi = offset;
      var count  = 5;
      var ci     = count;

      if(parts.length >= 1)
        oi = offset = parseInt(parts[0]);

      schedules.forEach( function(e,i,d) {
        if(oi != 0)
          oi -= 1;
        else if(ci > 0)
        {
          var message = hash.digest(e).substr(0,8);
          message += "     " + e["blame"] + "     " + e["created"];
          message += "     " + e["channel"];
          bot.sayTo(from, message);
<commit_msg>!schedule list now shows dates properly.<commit_after>var later  = require('later');
var hash   = require('json-hash');
var moment = require('moment');

var bot;

var minimumInterval = 0; 
var noCommands = false;

//FIXME: this is gross vv
var schedules = [];
var timers = {};

function writeSchedule(data) {
  bot.writeDataFile("later.json", 
      JSON.stringify({'data': data}), function(err) {
        if(err) console.log("Error writing command file: " + err);
  });
}

function newSchedule(data) {
  var s;

  try {
    s = later.parse.text(data['schedule']);
  } catch (ex) {
    return ex;
  }

  // FIXME: change to > -1, @ char #
  if( s == 0 )
    return "Provided schedule query doesn't parse.";
  
  data['schedule'] = s;

  // check that command is valid
  //   - check if commands are disabled
  //   - doesn't call schedule
  //   - doesn't call a dumbcommand which calls schedule
  //     * checked via perceived caller
  // check frequency is below some minimum (config)
  
  if(noCommands && data.command.substring(0, bot.config.commandPrefix.length) 
      == bot.config.commandPrefix) 
    return "Commands are disabled";

  if(data['command'].match(/^!schedule/))
    return "Fuck you"; // FIXME: personalize

  if(data['blame'] == bot.client.nick)
    return "Cannot call scheduler recursively.";
  
  if(minimumInterval > 0 && schedules.length > 0 
      && moment().diff(schedules.slice(-1)[0].created, 'seconds') <= minimumInterval)
    return "Schedule creation is rate limited. Please wait at least " + minimumInterval
           + " seconds between schedule creation.";

  registerCommand(data);

  schedules.push(data);
  writeSchedule(schedules);
}

function registerCommand(data) {
  var command;

  //If it's a command, emulate being sent a command. Otherwise say it.
  command = function() {
    //FIXME: Properly emulate raw.
    bot.sayTo(data['channel'], data['command']); // say to channel even for own commands
    bot.client.emit('message', bot.client.nick, data['channel'], 
      data['command'], data['command']);
  };

  timers[hash.digest(data)] = later.setInterval(command, data['schedule']);
}

module.exports.init = function(b) {
  bot = b;
  bot.readDataFile("later.json", function(err, data) {
    console.log("Read data file w/ error '" + err + "'");
    if(err) {
      console.log("Initializing later.json");
      schedules = [];
      writeSchedule(schedules);
    } else {
      try {
        console.log("Parsing later.json...");
        schedules = JSON.parse(data)['data'];

        // process schedules
        schedules.forEach(function(e, i, d) {
          e['created'] = moment(e['created']);
          registerCommand(e);
        });
      } catch(ex) {
        console.log("Error parsing: " + ex);
        console.log("Corrupted later.json for schedule! Resetting file...");
        schedules = [];
        writeSchedule(schedules);
      }
    }
  });

  bot.getConfig("schedule.json", function(err, conf) {
    if(!err) {
      minimumInterval = conf.minimum_interval;
      noCommands = conf.no_commands;
    }
  });
};

module.exports.commands = {
  schedule: {
    _default: function(x,y,reply) {
      reply("Usage: !schedule [<add>|<remove>|<list>|<help>] [arguments]");
      reply("       Also see !schedule help <subcommand> for more details");
    },
    help: {
      _default: null, // to be aliased later to ^
      add: function(x,y,reply) {
        reply("Usage: !schedule add \"timeframe\" \"command or text\"");
        reply("       Timeframe syntax is here: http://bunkat.github.io/later/parsers.html#text");
        reply("       If a valid command isn't specified, it is treated as text to print.");
      },
      remove: function(x,y,reply) {
        reply("Usage: !schedule remove [hash|LAST]");
        reply("       Removes the schedule specified by the hash or the LAST one added.");
      },
      list: function(x,y,reply) {
        reply("Usage: !schedule list [offset=0]");
        reply("       Provides a list of schedules, in pages, from the specified offset of 0.");
      },
    },
    add: function(r, parts, reply, command, from, to, text, raw) {
      if(parts.length !== 2) return reply("add must have *exactly* two arguments");

      // check and add schedule
      var err = newSchedule({
        'blame': from,
        'created': new moment(),
        'schedule': parts[0],
        'command': parts[1],
        'channel': to
      });

      if(err)
        reply(err);
      else
        reply("Added");
    },
    list: function(x, parts, reply, command, from, to) {
      var offset = 0;
      var oi = offset;
      var count  = 5;
      var ci     = count;

      if(parts.length >= 1)
        oi = offset = parseInt(parts[0]);

      schedules.forEach( function(e,i,d) {
        if(oi != 0)
          oi -= 1;
        else if(ci > 0)
        {
          var message = hash.digest(e).substr(0,8);
          message += "     " + e["blame"] + "     " 
                  + e["created"].format("ddd MM/DD/YY HH:mm:ss Z");
                  + "     " + e["channel"];
          bot.sayTo(from, message);

{'fraction_non_alphanumeric': 0.1539674716609167, 'fraction_numerical': 0.004337111877772301, 'mean_word_length': 2.4998275267333563, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3679389', 'n_tokens_mistral': 3143, 'n_tokens_neox': 3039, 'n_words': 827}