Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[api] Working channels synchronization.
  • Loading branch information
jfhbrook committed Jun 27, 2012
1 parent 0a41187 commit 3d2cc1c
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 33 deletions.
7 changes: 2 additions & 5 deletions bin/kohai
Expand Up @@ -5,14 +5,11 @@ var kohai = require('../lib/kohai'),
utile = require('utile'),
port = kohai.config.get('http-port') || 9000;

kohai.start(port, function (err) {
kohai.start(function (err) {

if (err) {
kohai.log.error('there was an error.');
kohai.log.error('err.stack');
console.log(err);
}

var address = kohai.server.address();

kohai.log.info(utile.format('http server chilling on %s:%d', address.address, address.port));
});
67 changes: 67 additions & 0 deletions lib/core/channels.js
@@ -0,0 +1,67 @@
var util = require('utile');
Channel = require('./irc/channel').Channel;

exports.name = 'irc-channels';

exports.attach = function () {

var self = this,
irc = this.irc,
channels = {};

irc.on('self::joined', join);
irc.on('self::parted', part);

this.channels = {
join: join,
part: part,
synchronize: synchronize
};

function join(data) {
if (!channels[data.channel]) {
channels[data.channel] = new Channel(data);
}
channels[data.channel].join();
}

function part(data) {
channels[data.channel].part();
}

function synchronize(cb) {
irc.channels(function (err, reply) {
if (err) {
return cb(err);
}

var chans = reply.channels;

Object.keys(chans).forEach(function (chan) {
chans[chan].channel = chan;
join(chans[chan]);
});
cb();
});
};

this.hook.on('**::irc::connected', function (data) {
if (data.type == 'irc') {
self.channels.synchronize(function (err) {
if (err) {
self.emit('error', err);
}
});
}
});

};

exports.init = function (done) {
var self = this,
hook = this.hook;

hook.on('hook::ready', function () {
self.channels.synchronize(done);
});
};
15 changes: 15 additions & 0 deletions lib/core/hook.js
@@ -0,0 +1,15 @@
// Flatiron plugin that adds a vanilla hook.
exports.name = 'hook.io';

var Hook = require('hook.io').Hook;

exports.attach = function attachHook (options) {
this.hook = new Hook(options);
};

exports.init = function initHook (done) {
this.hook.on('hook::ready', function (data) {
done(null);
});
this.hook.start();
};
11 changes: 11 additions & 0 deletions lib/core/index.js
@@ -0,0 +1,11 @@
exports.name = 'kohai-base';

var hook = require('./hook'),
irc = require('./irc-hook'),
channels = require('./channels');

exports.attach = function (opts) {
this.use(hook, {});
this.use(irc, {});
this.use(channels, {});
}
68 changes: 68 additions & 0 deletions lib/core/irc-hook.js
@@ -0,0 +1,68 @@
exports.name = 'irc-hooks';

var util = require('utile'),
EventEmitter2 = require('eventemitter2').EventEmitter2;

// Core irc management functionality. Basically unpacks the api.
var IRC = exports.IRC = function (hook) {
EventEmitter2.call(this, {
wildcard: true,
delimiter: '::'
});

this.hook = hook;

this.hook.on('**::irc::joined', function (data) {
if (data.nick) {
this.emit('joined', data);
}
else {
this.emit('self::joined', data);
}
});

this.hook.on('**::irc::parted', function (data) {
if (data.nick) {
this.emit('parted', data);
}
else {
this.emit('self::parted', data);
}
});
};
util.inherits(IRC, EventEmitter2);

IRC.prototype.join = function (channel) {
this.hook.emit('irc::join', channel);
};

IRC.prototype.part = function (channel) {
this.hook.emit('irc::part', channel);
};

IRC.prototype.say = function (to, msg) {
this.hook.emit('irc::say', {
to: to,
msg: msg
});
};

IRC.prototype.channels = function (cb) {
try {
this.hook.emit('irc::channels', {}, function (channels) {
cb(null, channels);
});
}
catch (err) {
cb(err);
}
};

// broadway attachments
exports.attach = function (options) {
this.irc = new IRC(this.hook);
};

exports.init = function (done) {
done();
};
97 changes: 97 additions & 0 deletions lib/core/irc/channel.js
@@ -0,0 +1,97 @@
/*
*
* channel.js - an object to hold various channel data
*
* (c) 2011 Nodejitsu Inc.
*
*/

var Channel = exports.Channel = function (options) {
for (var o in options) {
this[o] = options[o];
}
};

Channel.prototype.part = function () {
this.active = false;
};

Channel.prototype.join = function () {
this.active = true;
};

Channel.prototype.startVolume = function () {
this._ircRate();
this._twitRate();
};

Channel.prototype.stopVolume = function () {
clearInterval(this.ircInterval);
clearInterval(this.twitInterval);
};

Channel.prototype.config = function (key, value) {
if (typeof value === 'undefined') {
return this[key];
}
if (typeof key === 'string') {
this[key] = value;
return key + ' has been set to ' + value;
}
else {
return false;
}
};

Channel.prototype._ircRate = function () {
var self = this,
timespan = 60;
self.rateValues = [];
self.ircInterval = setInterval(function () {
var sum = 0;
if (self.rateValues.length > timespan) {
self.rateValues.shift();
}

self.rateValues.push(self.messageCount);
self.messageCount = 0;

self.rateValues.forEach(function (value) {
sum += Number(value);
});
self.rate = sum;
self._volumetrics();
}, 1000);

};

Channel.prototype._volumetrics = function () {
var self = this;

if (self.autoVolume) {
if (self.rate > 10) {
self.rate = 10;
}
if ((self.volume < 0) || (typeof self.volume === 'undefined')) {
self.volume = 0;
}
if ((10 - self.rate) <= self.volume) {
self.volume = 10 - self.rate;
self.lastVolume = self.volume;
}
else if ((10 - self.rate) > self.volume) {
self.lastVolume += self.lastVolume +0.05;
self.volume = Math.round(self.lastVolume);
}
}
};

Channel.prototype._twitRate = function () {
var self = this;

self.twitInterval = setInterval(function () {
if (self.currentTweetCount > 0) {
self.currentTweetCount--;
}
}, self.twitPeriod * 1000);
};
92 changes: 92 additions & 0 deletions lib/core/router.js
@@ -0,0 +1,92 @@
/*
*
* router.js - This should contain a director router or similar for handling
* "triggers."
*
* (c) 2012 Nodejitsu Inc.
*
*/

// Mostly copy-pasted from the cli router from director core.
var utile = require('utile'),
director = require('director');

var Router = exports.Router = function (routes) {
director.Router.call(this, routes);
this.recurse = 'backward';
};

//
// Inherit from `director.Router`.
//
utile.inherits(Router, director.Router);

//
// ### function configure (options)
// #### @options {Object} **Optional** Options to configure this instance with
// Configures this instance with the specified `options`.
//
Router.prototype.configure = function (options) {
options = options || {};
director.Router.prototype.configure.call(this, options);

//
// Our delimiter here is a space.
// e.g. `!foo bar baz`
//
this.delimiter = ' ';

// This way, you can use a custom trigger symbol.
// e.g. `@foo bar baz`
this.trigger = options.trigger || '!';

return this;
};

//
// ### function dispatch (method, path)
// #### @method {string} Method to dispatch
// #### @path {string} Path to dispatch
// Finds a set of functions on the traversal towards
// `method` and `path` in the core routing table then
// invokes them based on settings in this instance.
//
Router.prototype.dispatch = function (method, data, app, callback) {

var path = data.text,
trig = new RegExp('^' + this.trigger);

//
// Replace the trigger with a space so that the traversal
// algorithm will recognize it. This is because we always assume
// that the `path` begins with `this.delimiter`.
//
path = (path || '').replace(trig, this.delimiter);

var fns = this.traverse(method, path, this.routes, '');

if (!fns || fns.length === 0) {

if (callback) {

callback(new Error(
utile.format('IRC command router could not find cmd for: "%s"', path)
));
}

return false;
}

if (this.recurse === 'forward') {
fns = fns.reverse();
}

// Second arg is set to "this" in the route handler.
// Needs some helper fxns
this.invoke(this.runlist(fns), {
cmd: path.substring(1),
data: data,
app: app
}, callback);
return true;
};

0 comments on commit 3d2cc1c

Please sign in to comment.