/* * Possible strategies for updating the topic: * * o Set the topic unconditionally when the feed changes. This makes * it possible for users to change the topic and it won't be * overridden until the feed changes. * * o Set the topic on any topic change (making the feed control the * entire topic) * * o Support a delimiter so it can control only a part of the topic, * like "<>". Example * * Next meeting, sat 1900 <> DATA FROM FEED. * * A regexp selecting the are to be updated might conver it. * * o If the bot changed the topic the last time, it's probably safe to * just update it. */ require('tinycolor'); var node_irc = require('../node_modules/node-irc/IRC.js') , cron = require('cron').CronJob , parser = require('blindparser') , events = require('events') , _ = require('underscore'); var parserOptions = {}; var config; var cronJobs = []; var state = { channelTopic: undefined, irc: undefined, updatingFeed: false, feeds: [], newest: { timestamp: 0, text: undefined } }; module.exports.state = state; var eventEmitter = new events.EventEmitter; function startIrc() { irc = new node_irc.IRC(config.host, config.port); irc.on('raw', function(data) { console.log(data) }); irc.on('connected', function(server) { console.log('Connected to ' + server); irc.join(config.channel, function(error) { irc.notice(config.channel, 'well hello yall'); }); }); irc.topic = function(channel, topic) { irc._socket.write('topic ' + channel + ' :' + topic + '\r\n'); }; irc.on('topic', function(channel, topic) { console.log("new topic: " + topic); /* If we're not storing this, it is possible for people to set * the topic after the bot has set it and it will persist (until * next update from the feed). topic = t; */ state.channelTopic = topic; }); } function updateFeed(feedState) { console.log("Fetching " + feedState.url); if(feedState.updatingFeed) console.log("Already working"); feedState.updatingFeed = true; parser.parseURL(feedState.url, parserOptions, function(err, feed) { // console.log("Fetched " + feedState.url + ", status=" + (err ? "failure" : "success")); if(err) { console.log(err); return; } var newest = processFeed(feed); if(typeof newest == "object") { eventEmitter.emit("feedChanged", feedState.url, newest); } feedState.updatingFeed = false; }); } function processFeed(feed) { // Extracts the username from the feed. // TODO: Use something better than blindparser to parse atom so that // each entry has an author too to get the full name. if(typeof feed.items[0] == "undefined") { console.log("feed does not contain any items", feed); return undefined; } var match = /^http:\/\/twitter.com\/([a-zA-Z0-9_]+)\/.*$/.exec(feed.items[0].link) if(match.length != 2) { return undefined; } return { text: feed.items[0].title, author: match[1], timestamp: feed.items[0].date }; } module.exports.processFeed = processFeed; eventEmitter.on("feedChanged", function(url, newest) { state.feeds[url] = newest; if(state.newest.timestamp >= newest.timestamp) { // console.log("oold: " + newest.text); return; } var text = newest.author + ": " + newest.text; console.log("New topic", newest.timestamp, url, text); state.newest = newest; if(config.connect && state.topic != text) { irc.topic(config.channel, text); } }); eventEmitter.on("configUpdated", function(c) { setup(); }); function setup() { console.log("Stopping " + cronJobs.length + " cron jobs"); _.each(cronJobs, function(job) { job.stop(); }); cronJobs = []; _.each(config.feeds, function(feed) { var state = { url: feed, updatingFeed: false, last: undefined }; var job = new cron("*/10 * * * *", function() { updateFeed(state); }, function() {}, true); cronJobs.push(job); }); } function start(c) { config = c; startIrc(); setup(); if(config.connect) { console.log('Connecting to ' + config.host + ':' + config.port); irc.connect(config.nick); } else { console.log('Not connecting to IRC'); } } module.exports.start = start; module.exports.emit = function(config) { eventEmitter.emit("configUpdated", config); }