From 2870c4da1aedf41926972dd60227c3d62cdaa123 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sat, 16 Jun 2012 14:24:33 +0200 Subject: o Polishing the dynobot a bit. --- FakeSocket.js | 44 +++++++++++++ Repl.js | 82 ++++++++++++++++++++++++ atom-bot/index.js | 25 +++++--- dynobot-irc.js | 183 ++++++++++++++---------------------------------------- echo-bot/index.js | 2 +- irc-client.js | 20 +++++- ngircd.conf | 5 ++ 7 files changed, 214 insertions(+), 147 deletions(-) create mode 100644 FakeSocket.js create mode 100644 Repl.js create mode 100644 ngircd.conf diff --git a/FakeSocket.js b/FakeSocket.js new file mode 100644 index 0000000..af3d74c --- /dev/null +++ b/FakeSocket.js @@ -0,0 +1,44 @@ +function FakeSocket(stdin, stdout, msgHandler) { + this.stdin = stdin; + this.stdout = stdout; + this.msgHandler = msgHandler; + this.connectHandler = undefined; + this.errorHandler = undefined; +} + +FakeSocket.prototype.setEncoding = function(encoding) { + this.stdin.setEncoding(encoding); +} + +FakeSocket.prototype.on = function(event, cb) { + switch(event) { + case 'data': + this.stdin.on('data', function(chunk) { + cb(chunk.trim() + '\r\n'); + }); + break; + case 'connect': + this.connectHandler = cb; + break; + case 'close': + this.stdin.on('exit', cb); + break; + case 'end': + case 'error': + break; + default: + process.exit(1); + break; + } +} + +FakeSocket.prototype.connect = function(server, port) { + this.connectHandler(); +} + +FakeSocket.prototype.write = function(chunk) { + this.stdout.write(chunk.red); + this.msgHandler && this.msgHandler(chunk); +} + +module.exports = FakeSocket; diff --git a/Repl.js b/Repl.js new file mode 100644 index 0000000..cce93d0 --- /dev/null +++ b/Repl.js @@ -0,0 +1,82 @@ +var rl = require('readline'); + +function quit() { + i.close(); + process.stdin.destroy(); + process.exit(0); +} + +var i; + +module.exports.init = function() { + process.stdin.resume(); + i = rl.createInterface(process.stdin, process.stdout, null); +} + +module.exports.setupRepl = function() { +} + +module.exports.setupSimulatorRepl = function(config) { + var from = 'alice!~alice@example.com'; + var channel = config.channel; + + function updatePrompt() { + var prefix = 'from=' + from; + prefix += channel ? ', channel=' + channel : ''; + prefix += '> '; + i.setPrompt(prefix, prefix.length); + i.prompt(); + } + + i.on('line', function (cmd) { + cmd = cmd.trim(); + if(cmd.length == 0) { + i.prompt(); + return; + } + var parts = cmd.split(' '); + switch(parts[0]) { + case '/quit': + case '/q': + quit(); + break; + case '/raw': + case '/r': + parts.shift(); + stdinPipe.send(parts.join(' ')); + updatePrompt(); + break; + case '/join': + case '/j': + channel = parts[1]; + updatePrompt(); + break; + case '/from': + case '/f': + from = parts[1]; + updatePrompt(); + break; + case '/notice': + case '/n': + if(channel) { + parts.shift(); + stdinPipe.send(':' + from + ' NOTICE ' + channel + ' :' + parts.join(' ')); + } + else { + info('You have to /j a channel first'); + } + break; + default: + stdinPipe.send(':' + from + ' PRIVMSG ' + channel + ' :' + parts.join(' ')); + updatePrompt(); + break; + } + }).on('close', function() { + quit(); + }); + updatePrompt(); + + setTimeout(function() { + stdinPipe.send(':irc.foo.bar 001 this :Welcome to Some Internet Relay Chat Network this'); + }, 1000); +} diff --git a/atom-bot/index.js b/atom-bot/index.js index 470786c..7de99bc 100644 --- a/atom-bot/index.js +++ b/atom-bot/index.js @@ -34,10 +34,14 @@ var irc = function() { return new Proxy(IrcClient.prototype, 'irc', channel); }(); +function log(message) { + console.log(('log ' + message).green); +} + var parserOptions = {}; // Config -var channel = '#bitraf2'; +var channel = '#dynobot'; var feeds = [ // "http://search.twitter.com/search.atom?q=from:AgileBorat", // "http://search.twitter.com/search.atom?q=from:KongenDin", @@ -62,15 +66,15 @@ var state = { var eventEmitter = new events.EventEmitter; function updateFeed(feedState) { - console.log("Fetching " + feedState.url); + log("Fetching " + feedState.url); if(feedState.updatingFeed) - console.log("Already working"); + log("Already working"); feedState.updatingFeed = true; parser.parseURL(feedState.url, parserOptions, function(err, feed) { - console.log("Fetched " + feedState.url + ", status=" + (err ? "failure" : "success")); + log("Fetched " + feedState.url + ", status=" + (err ? "failure" : "success")); if(err) { - console.log(err); + log(err); return; } var newest = processFeed(feed); @@ -86,7 +90,8 @@ function processFeed(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); + log("feed does not contain any items"); + log(feed); return undefined; } var match = /^http:\/\/twitter.com\/([a-zA-Z0-9_]+)\/.*$/.exec(feed.items[0].link) @@ -104,14 +109,14 @@ eventEmitter.on("feedChanged", function(url, newest) { state.feeds[url] = newest; if(state.newest.timestamp >= newest.timestamp) { - console.log("oold: " + newest.text); + log("oold: " + newest.text); return; } /* */ var text = newest.author + ": " + newest.text; - console.log("New topic", newest.timestamp, url, text); + log("New topic: " + newest.timestamp + ", url=" + url + ", text=" + text); state.newest = newest; /* if(state.channelTopic != text) { @@ -122,7 +127,7 @@ eventEmitter.on("feedChanged", function(url, newest) { }); function setup() { - console.log("Stopping " + cronJobs.length + " cron jobs"); + log("Stopping " + cronJobs.length + " cron jobs"); _.each(cronJobs, function(job) { job.stop(); }); cronJobs = []; _.each(feeds, function(feed) { @@ -143,7 +148,7 @@ irc.join(channel, function(c) { }); irc.on('topic', function(channel, topic) { - console.log("new topic: " + topic); + 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). diff --git a/dynobot-irc.js b/dynobot-irc.js index 8258930..9cae258 100644 --- a/dynobot-irc.js +++ b/dynobot-irc.js @@ -1,60 +1,46 @@ require('tinycolor'); -var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var Channel = require('dynobot/channel.js'); +var cmdopt = require('cmdopt') var cp = require('child_process'); +var EventEmitter = require('events').EventEmitter; +var FakeSocket = require('./FakeSocket.js'); var IrcClient = require('./irc-client').IrcClient; -var Service = require('dynobot/service.js'); -var Channel = require('dynobot/channel.js'); +var os = require('os') var _ = require('underscore'); -var rl = require('readline'); - -/************************************************************/ - -function FakeSocket(stdin, stdout, msgHandler) { - this.stdin = stdin; - this.stdout = stdout; - this.msgHandler = msgHandler; - this.connectHandler = undefined; - this.errorHandler = undefined; -} - -FakeSocket.prototype.setEncoding = function(encoding) { - this.stdin.setEncoding(encoding); -} +var Service = require('dynobot/service.js'); +var util = require('util'); -FakeSocket.prototype.on = function(event, cb) { - switch(event) { - case 'data': - this.stdin.on('data', function(chunk) { - cb(chunk.trim() + '\r\n'); - }); - break; - case 'connect': - this.connectHandler = cb; - break; - case 'close': - this.stdin.on('exit', cb); - break; - case 'end': - case 'error': - break; - default: - process.exit(1); - break; +var parser = new cmdopt.Parser(); +parser.option("-h, --help", "Show this help"); +parser.option("-s, --server=HOST", "Server to join"); +parser.option("-p, --port=PORT", "Port to use"); +parser.option("-n, --nick=NICK", "Nickname of the bot"); +parser.option("-c, --channel=CHANNEL", "IRC channel to join"); + +var args = process.argv.slice(2); +var defaults = { + server: "irc.freenode.org", + port: "6667", + nick: os.hostname(), + channel: "#dynobot" +}; + +try { + var config = parser.parse(args); + if (config.help) { + console.log(parser.help()); + return; } + config = _.defaults(config, defaults); + console.log("config", config); +} catch (ex) { + if (ex instanceof cmdopt.ParseError) { + process.stderr.write(ex.message + "\n"); + process.exit(1); + } + throw ex; } -FakeSocket.prototype.connect = function(server, port) { - this.connectHandler(); -} - -FakeSocket.prototype.write = function(chunk) { - this.stdout.write(chunk.red); - this.msgHandler && this.msgHandler(chunk); -} - -/************************************************************/ - var StdinPipe = function() { EventEmitter.call(this); } @@ -66,11 +52,8 @@ StdinPipe.prototype.send = function(msg) { stdinPipe.emit('data', msg + '\r\n'); } -/************************************************************/ - var stdinPipe = new StdinPipe(); var irc; -var nick = 'dynobot'; var realName = 'bots\'r\'us'; var ident = 'ident'; @@ -80,9 +63,7 @@ function info(a, b, c) { var reallyConnect = true; if(reallyConnect) { - var host = 'localhost'; - // host = 'irc.freenode.net'; - irc = new IrcClient(nick, realName, ident, host, 6667); + irc = new IrcClient(config.nick, realName, ident, config.server, config.port); } else { var msgHandler = function(line) { @@ -94,8 +75,7 @@ else { stdinPipe.send(prefix + ' JOIN ' + parts[1]); } } - irc = new IrcClient(nick, realName, ident,new FakeSocket(stdinPipe, process.stdout, msgHandler)); - process.stdin.resume(); + irc = new IrcClient(nick, realName, ident, new FakeSocket(stdinPipe, process.stdout, msgHandler)); } var ircService = new Service('irc', irc); @@ -109,8 +89,8 @@ function Plugin(name, script) { } var plugins = [ - new Plugin('echo', './echo-bot/index.js') -// new Plugin('atom', './atom-bot/index.js') +// new Plugin('echo', './echo-bot/index.js') + new Plugin('atom', './atom-bot/index.js') ]; _.each(plugins, function(plugin) { @@ -133,85 +113,18 @@ _.each(plugins, function(plugin) { // piping to process.stdout }); -process.stdin.resume(); -var i = rl.createInterface(process.stdin, process.stdout, null); - -function quit() { - i.close(); - process.stdin.destroy(); - process.exit(0); -} - -function setupRepl() { - var from = 'alice!~alice@example.com'; - var channel = '#bitraf2'; - - function updatePrompt() { - var prefix = 'from=' + from; - prefix += channel ? ', channel=' + channel : ''; - prefix += '> '; - i.setPrompt(prefix, prefix.length); - i.prompt(); - } - - i.on('line', function (cmd) { - cmd = cmd.trim(); - if(cmd.length == 0) { - i.prompt(); - return; - } - var parts = cmd.split(' '); - switch(parts[0]) { - case '/quit': - case '/q': - quit(); - break; - case '/raw': - case '/r': - parts.shift(); - stdinPipe.send(parts.join(' ')); - updatePrompt(); - break; - case '/join': - case '/j': - channel = parts[1]; - updatePrompt(); - break; - case '/from': - case '/f': - from = parts[1]; - updatePrompt(); - break; - case '/notice': - case '/n': - if(channel) { - parts.shift(); - stdinPipe.send(':' + from + ' NOTICE ' + channel + ' :' + parts.join(' ')); - } - else { - info('You have to /j a channel first'); - } - break; - default: - stdinPipe.send(':' + from + ' PRIVMSG ' + channel + ' :' + parts.join(' ')); - updatePrompt(); - break; - } - }).on('close', function() { - quit(); - }); - updatePrompt(); -} - -irc.join('#bitraf2', function(name) { +irc.join(config.channel, function(name) { info('joined ' + name + ' from dynobot.'); }); info('colors: ' + 'from bot'.red + ', ' + 'to bot'.blue); irc.connect(); -setupRepl(i); - -setTimeout(function() { - stdinPipe.send(':irc.foo.bar 001 this :Welcome to Some Internet Relay Chat Network this'); -}, 1000); +var repl = require('./Repl.js'); +repl.init(); +if(reallyConnect) { + repl.setupRepl(); +} +else { + repl.setupSimulatorRepl(); +} diff --git a/echo-bot/index.js b/echo-bot/index.js index 234e450..43240d4 100644 --- a/echo-bot/index.js +++ b/echo-bot/index.js @@ -22,7 +22,7 @@ irc.on('join', function(nick, channel) { notice(channel, 'on join: hello ' + nick + '!'); }); -irc.join('#bitraf2', function(channel) { +irc.join('#dynobot', function(channel) { notice(channel, 'join(): channel=' + channel); }); diff --git a/irc-client.js b/irc-client.js index 275ee72..1be71bf 100644 --- a/irc-client.js +++ b/irc-client.js @@ -1,4 +1,4 @@ -var IRC = require('./node_modules/node-irc/irc').IRC; +var IRC = require('irc.js'); var util = require('util'); var events = require('events'); var _ = require('underscore'); @@ -35,6 +35,10 @@ function IrcClient(nick, realName, ident, server, port, password) { this.connected = false; this.debugLevel = undefined; this.irc = undefined; + + this.nickCount = 0; + this.nickAdditions = [ '', '^', '-', '_', '\\', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; + console.log("server: " + server); } util.inherits(IrcClient, events.EventEmitter); @@ -68,6 +72,13 @@ IrcClient.prototype.init = function(irc) { info('JOINed ' + channelName); client.getChannel(channelName).fire(channelName); }); + irc.on('errorcode', function(code) { + if (code == 'ERR_NICKNAMEINUSE') { + var nick = client.findNick(); + info("nick: " + nick); + irc.nick(nick); + } + }); _.each(['privmsg', 'topic', 'join'], function(e) { irc.on(e, function() { var args = [e]; @@ -110,6 +121,13 @@ IrcClient.prototype.join = function(name, cb) { } } +IrcClient.prototype.findNick = function() { + if (this.nickCount == this.nickAdditions.length) { + this.nickCount = 0; + } + return this.nick + this.nickAdditions[this.nickCount++]; +} + // TODO: fix function dispatch(name) { this.irc[name].apply(this.irc, arguments); diff --git a/ngircd.conf b/ngircd.conf new file mode 100644 index 0000000..dae83b7 --- /dev/null +++ b/ngircd.conf @@ -0,0 +1,5 @@ +[Global] + MaxNickLength = 20 + Name = irc.example.net + Info = Server Info Text + MotdPhrase = "Hello world!" -- cgit v1.2.3