From 8f926d24d474da2ba5c7bc98090014278e87d4c0 Mon Sep 17 00:00:00 2001
From: Trygve Laugstøl <trygvis@inamo.no>
Date: Thu, 28 Jun 2012 02:56:41 +0200
Subject: o Making the atom bot useful.

---
 atom-bot/index.js | 217 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 173 insertions(+), 44 deletions(-)

(limited to 'atom-bot')

diff --git a/atom-bot/index.js b/atom-bot/index.js
index 7de99bc..18eaf14 100644
--- a/atom-bot/index.js
+++ b/atom-bot/index.js
@@ -21,57 +21,55 @@
 
 require('tinycolor');
 var cron = require('cron').CronJob
-  , parser = require('blindparser')
   , events = require('events')
+  , fs = require('fs')
+  , parser = require('blindparser')
+  , url = require('url')
   , _ = require('underscore');
 
-var irc = function() {
-  var Proxy = require('../node_modules/dynobot/proxy');
-  var Channel = require('../node_modules/dynobot/channel');
-  var IrcClient = require('../irc-client.js').IrcClient;
+var Proxy = require('../node_modules/dynobot/proxy');
+var Channel = require('../node_modules/dynobot/channel');
 
-  var channel = new Channel();
-  return new Proxy(IrcClient.prototype, 'irc', channel);
-}();
+var irc = new Proxy(require('../irc-client.js').IrcClient.prototype, 'irc', new Channel());
 
 function log(message) {
   console.log(('log ' + message).green);
 }
 
-var parserOptions = {};
-
-// Config
-var channel = '#dynobot';
-var feeds = [
-//  "http://search.twitter.com/search.atom?q=from:AgileBorat",
-//  "http://search.twitter.com/search.atom?q=from:KongenDin",
-//  "http://search.twitter.com/search.atom?q=from:NestenSivJensen",
-  "http://search.twitter.com/search.atom?q=ndc2012"
-//  "http://search.twitter.com/search.atom?q=awesome",
-//  "http://search.twitter.com/search.atom?q=eurovision",
-];
-var cronJobs = [];
+var config;
 
 var state = {
   channelTopic: undefined,
   topic: undefined,
-  updatingFeed: false,
-  feeds: [],
   newest: {
     timestamp: 0,
     text: undefined
   }
 };
 
+var feeds = {};
+
+function FeedState(url) {
+  this.url = url;
+  this.updatingFeed = false;
+  this.last = undefined;
+  this.job = undefined;
+}
+
 var eventEmitter = new events.EventEmitter;
 
+function parseFeed(url, cb) {
+  var parserOptions = {};
+  parser.parseURL(url, parserOptions, cb);
+}
+
 function updateFeed(feedState) {
   log("Fetching " + feedState.url);
   if(feedState.updatingFeed)
     log("Already working");
   feedState.updatingFeed = true;
 
-  parser.parseURL(feedState.url, parserOptions, function(err, feed) {
+  parseFeed(feedState.url, function(err, feed) {
     log("Fetched " + feedState.url + ", status=" + (err ? "failure" : "success"));
     if(err) {
       log(err);
@@ -106,7 +104,7 @@ function processFeed(feed) {
 }
 
 eventEmitter.on("feedChanged", function(url, newest) {
-  state.feeds[url] = newest;
+  feeds[url].newest = newest;
 
   if(state.newest.timestamp >= newest.timestamp) {
     log("oold: " + newest.text);
@@ -120,33 +118,55 @@ eventEmitter.on("feedChanged", function(url, newest) {
   state.newest = newest;
   /*
   if(state.channelTopic != text) {
-    irc.topic(channel, text);
+    irc.topic(config.channel, text);
   }
   */
-  irc.notice(channel, text);
+  log(config.channel);
+  irc.notice(config.channel, text);
 });
 
+function appendFeed(feedUrl) {
+  var feedState = new FeedState(feedUrl);
+  log("Job starting for " + feedUrl);
+  feedState.job = new cron("*/10 * * * *", function() {
+    updateFeed(feedState);
+  }, function() {
+    log("Job stopping for " + feedUrl);
+  }, true);
+  feeds[feedUrl] = feedState;
+  log("appendFeed: keys=" + _.keys(feeds));
+}
+
+function removeFeed(feedUrl) {
+  log("removeFeed: feedUrl=" + feedUrl);
+  log("removeFeed: keys=" + _.keys(feeds));
+  var feedState = feeds[feedUrl];
+  log("removeFeed: feedState=" + typeof feedState);
+  if(_.isObject(feedState)) {
+    log("removeFeed: keys feedState=" + _.keys(feedState));
+    feedState.job.stop();
+  }
+  else {
+    log("removeFeed: feed not found");
+  }
+  delete feeds[feedUrl];
+  log("removeFeed: keys=" + _.keys(feeds));
+}
+
 function setup() {
-  log("Stopping " + cronJobs.length + " cron jobs");
-  _.each(cronJobs, function(job) { job.stop(); });
-  cronJobs = [];
-  _.each(feeds, function(feed) {
-    var state = {
-      url: feed,
-      updatingFeed: false,
-      last: undefined
-    };
-    var job = new cron("*/10 * * * *", function() {
-      updateFeed(state);
-    }, function() {}, true);
-    cronJobs.push(job);
+  log('Starting..');
+
+  loadConfig(function(err, config) {
+    log('config: ' + JSON.stringify(config));
+    irc.join(config.channel, function(c) {
+      irc.notice(config.channel, 'Atom plugin online. Monitoring ' + config.feeds.length + ' feeds.');
+    });
+    _.each(config.feeds, function(feed) {
+      appendFeed(feed);
+    });
   });
 }
 
-irc.join(channel, function(c) {
-  irc.notice(channel, 'well hello yall: ' + c);
-});
-
 irc.on('topic', function(channel, topic) {
   log("new topic: " + topic);
   /* If we're not storing this, it is possible for people to set
@@ -158,4 +178,113 @@ irc.on('topic', function(channel, topic) {
   state.channelTopic = topic;
 });
 
+irc.on('privmsg', function(nick, channel, message) {
+  irc.whoami(function(whoami) {
+    // Hm, what happens if a nick contain funny characters like '.
+    var reg = new RegExp('^' + whoami + ': atom ');
+    if(!reg.test(message)) {
+      return;
+    }
+    var args = message.substring(whoami.length + 7).split(' ');
+
+    if(args.length < 1) {
+      usage(channel);
+      return;
+    }
+    var rest = _.rest(args);
+    switch(args[0]) {
+    case 'list-feeds': onListFeeds(channel); break;
+    case 'add-feed': onAddFeed(channel, rest); break;
+    case 'remove-feed': onRemoveFeed(channel, rest); break;
+    case 'load-config': onLoadConfig(channel); break;
+    }
+  });
+});
+
+function usage(channel) {
+}
+
+function onListFeeds(channel) {
+  _.each(config.feeds, function(feed, i) {
+    irc.notice(channel, 'feed #' + i + ': ' + feed);
+  });
+}
+
+function onAddFeed(channel, urls) {
+  if(urls.length != 1) {
+    return;
+  }
+  var u = urls[0];
+  log('Adding feed: ' + u);
+  url.parse(u); // TODO: Add error handling
+  parseFeed(u, function(err, feed) {
+    if(err) {
+      log('Unable to fetch feed');
+      log(util.format(err));
+      return;
+    }
+    config.feeds.push(u);
+    appendFeed(u);
+    saveConfig(function(err) {
+      if(err) throw err;
+      irc.notice(channel, 'Feed added');
+    });
+  });
+}
+
+function onRemoveFeed(channel, args) {
+  if(args.length != 1) {
+    return;
+  }
+  log("onRemoveFeed: args[0]=" + args[0]);
+  var i = parseInt(args[0]);
+  log("onRemoveFeed: i=" + i);
+  var feedUrl = config.feeds[i];
+  log("onRemoveFeed: config=" + JSON.stringify(config));
+  config.feeds.splice(i, 1);
+  log("onRemoveFeed: config=" + JSON.stringify(config));
+  log("onRemoveFeed: feedUrl=" + feedUrl);
+  removeFeed(feedUrl);
+  saveConfig(function(err) {
+    if(err) {
+      log(err);
+      irc.notice(channel, 'Error removing feed');
+      return;
+    }
+    irc.notice(channel, 'Feed removed');
+  });
+}
+
+function onLoadConfig(channel) {
+  loadConfig(function(err, config) {
+    if(err) {
+      irc.notice(channel, 'Unable to load config');
+    }
+    else {
+      irc.notice(channel, 'Loaded configuration, has ' + config.feeds.length + ' feeds.');
+    }
+  });
+}
+
+function loadConfig(cb) {
+  fs.readFile('config.json', function(err, data) {
+    if(err) return cb(err, undefined);
+
+    try {
+      config = JSON.parse(data);
+    } catch(e) {
+      log('Unable to parse config.json, using defaults.');
+    }
+
+    config.channel = _.isString(config.channel) ? config.channel : '#dynobot';
+    config.feeds = _.isArray(config.feeds) ? config.feeds : [];
+    cb(undefined, config);
+  });
+}
+
+function saveConfig(cb) {
+  log('Saving config: ' + JSON.stringify(config));
+  fs.writeFile('config.json', JSON.stringify(config), cb);
+}
+
 setup();
-- 
cgit v1.2.3