Skip to content
This repository
Browse code

Version updates. See the changelog. All tests passing.

  • Loading branch information...
commit 38de58ea9bbbcd4147a09602773607e06bdd24c1 1 parent 95b049c
Ryan Scheel authored March 11, 2013
35  doc/Interfaces.txt
... ...
@@ -1,21 +1,21 @@
1 1
 Interfaces
2 2
 
3  
-IRCCommandHandler implements EventSubscriber, EventEmitter
4  
-    parseMessage(IRCMessage)
5  
-    emits(single-arbitrary-word, IRCCommand, NRC)
  3
+IrcCommandHandler implements EventSubscriber, EventEmitter
  4
+    parseMessage(IrcMessage)
  5
+    emits(single-arbitrary-word, IrcCommand, NRC)
6 6
 
7  
-IRCCommand
  7
+IrcCommand
8 8
     get sender
9 9
     get args
10 10
     get channel
11 11
     get name
12 12
     get isQuery
13 13
 
14  
-IRCMessage
  14
+IrcMessage
15 15
     get receiver:NRC
16 16
        Receiver of the message. The NRC object in most cases.
17 17
     get prefix
18  
-      If an IRC message starts with a :, the first word is called the prefix.
  18
+      If an Irc message starts with a :, the first word is called the prefix.
19 19
     get sender
20 20
       Sender of the message. Usually a Hostmask.
21 21
     get type
@@ -32,27 +32,27 @@ Actor
32 32
 Channel
33 33
     get channel: String
34 34
 
35  
-IRCUserMessage implements IRCMessage, Actor
36  
-IRCChannelUserMessage implements IRCMessage, Channel, Actor
37  
-IRCChannelMessage implements IRCMessage, Channel
38  
-IRCJoinPartMessage implements IRCChannelUserMessage
  35
+IrcUserMessage implements IrcMessage, Actor
  36
+IrcChannelUserMessage implements IrcMessage, Channel, Actor
  37
+IrcChannelMessage implements IrcMessage, Channel
  38
+IrcJoinPartMessage implements IrcChannelUserMessage
39 39
 
40  
-IRCPrivMsgMessage implements IRCChannelUserMessage
  40
+IrcPrivMsgMessage implements IrcChannelUserMessage
41 41
     get isQuery: boolean
42 42
         True if message sent in a query.
43 43
 
44  
-IRCQuitMessage implements IRCUserMessage
  44
+IrcQuitMessage implements IrcUserMessage
45 45
     get reason: String
46 46
 
47  
-IRCNickMessage implements IRCUserMessage
  47
+IrcNickMessage implements IrcUserMessage
48 48
     get newNick: String
49 49
         New nick for the user changing nick.
50 50
 
51  
-IRCChannelNamesMessage implements IRCChannelMessage
  51
+IrcChannelNamesMessage implements IrcChannelMessage
52 52
     get users: List[String]
53 53
         List of users in channel.
54 54
 
55  
-NRC implements ModuleSubscriber, ModuleStorage, BiEventSubscriber, ConfigurationStorage, IRCClient
  55
+NRC implements ModuleSubscriber, ModuleStorage, BiEventSubscriber, ConfigurationStorage, IrcOutputClient
56 56
 
57 57
 ModuleSubscriber
58 58
     require(Module)
@@ -75,8 +75,10 @@ SimpleReadSocket extends EventSubscriber, EventEmitter
75 75
     end()
76 76
     emits('data', String)
77 77
 
78  
-IRCSocket extends SimpleReadSocket
  78
+IrcSocket extends SimpleReadSocket
79 79
     raw(String)
  80
+    start aliases connect
  81
+    disconnect aliases end
80 82
     onConnect(Callback[])
81 83
     onDisconnect(Callback[])
82 84
 
@@ -84,3 +86,4 @@ Logger
84 86
     input
85 87
     output
86 88
     error
  89
+    debug
24  doc/changelog.txt
@@ -26,6 +26,24 @@ Changelog
26 26
         + dependencies(undefined U ModuleExports): List[String] U undefined
27 27
     * Only the server module works.
28 28
 
  29
+0.4.1:
  30
+    * Added a new test for Message Handlers.
  31
+
  32
+0.4.2: (Unreleased)
  33
+    * Renamed classes.
  34
+    * Added an IrcOutputSocket class. NRC absorbs it of course.
  35
+    * Added everything to the index.
  36
+    * Got Dependency Injection for all non-structure objects.
  37
+    * Added `userhost` method to IrcOutputSocket/NRC.
  38
+
  39
+0.4.3:
  40
+    * Nrc class renamed to Client.
  41
+    * `whois` method added to IrcOutputSocket/Client
  42
+    * Moved startup stuff from IrcMessageHandler to Client.
  43
+    ** Removed config parameter from its constructor. :)
  44
+    * Testing of 'say', 'part', and 'quit' of the IrcOutputSocket.
  45
+    ** Feel free to add more tests!
  46
+
29 47
 Future:
30 48
     * Executable tool to start a new NRC bot.
31 49
       Ex:  nrc ./my-server-config.json
@@ -34,5 +52,7 @@ Future:
34 52
       optional 'modules' field. Absolute or relative URLs to modules that
35 53
       are required.
36 54
     * Get modules rewritten properly.
37  
-    * Move startup code out of Message Handler. [Why did I put it there?]
38  
-    ** Refactor the IRC Socket to be multi-stage.
  55
+    * Refactor the IRC Socket to have a Startup class.
  56
+    * ChunkedMessageParser to chunk MessageParser parsed messagse when
  57
+      applicable.
  58
+    * Whatever is in the Contributors section of readme.md
57  lib/command-parser.js
... ...
@@ -0,0 +1,57 @@
  1
+/**
  2
+ *
  3
+ * Command Handler
  4
+ *
  5
+ */
  6
+
  7
+var events = require('events');
  8
+var util = require('util');
  9
+
  10
+var Command = require('./structures/command');
  11
+
  12
+/**
  13
+ * ctx: NRC interface
  14
+ */
  15
+var Commander = function (name, config) {
  16
+    events.EventEmitter.call(this);
  17
+    var that = this;
  18
+    this._name = name;
  19
+    this.trigger = config.trigger || "!";
  20
+};
  21
+
  22
+Commander.prototype = new events.EventEmitter();
  23
+Commander.prototype.constructor = Commander;
  24
+
  25
+Commander.prototype.parseMessage = function (msg) {
  26
+    var command = this.getCommandString(msg);
  27
+
  28
+    if (command) {
  29
+        command = Object.freeze(new Command(msg.actor, command,
  30
+            msg. channel, msg.isQuery));
  31
+        this.emit(command.name, command);
  32
+    }
  33
+};
  34
+
  35
+Commander.prototype.getCommandString = function (privmsg) {
  36
+    if (privmsg.message[0] === this.trigger[0]) {
  37
+        return privmsg.message.substr(1);
  38
+    }
  39
+
  40
+    if (privmsg.isQuery) {
  41
+        return privmsg.message;
  42
+    }
  43
+
  44
+    if (privmsg.message.indexOf(this._name()) === 0) {
  45
+        var msg = privmsg.message.substr(privmsg.message.indexOf(" ") + 1);
  46
+
  47
+        if (msg[0] === this.trigger[0]) {
  48
+            return msg.substr(1);
  49
+        } else {
  50
+            return msg;
  51
+        }
  52
+    }
  53
+
  54
+    return false;
  55
+};
  56
+
  57
+module.exports = Commander;
61  lib/commander.js
... ...
@@ -1,28 +1,52 @@
1 1
 /**
2 2
  *
3  
- * Command Handler
  3
+ * Command Handlers parse incoming messages and emit commands for valid
  4
+ * command messages. Valid commands are any of the following:
4 5
  *
  6
+ * 1. Starts with the trigger character.
  7
+ * 2. Starts with the receiver's name as determined by the name function.
  8
+ * 3. Is a private message.
  9
+ *
  10
+ * The constructor takes two arguments:
  11
+ *   1. A no-arg function that returns the name of the receiver
  12
+ *   2. Trigger configuration: Either a one character string or an object with
  13
+ *      a property trigger which has a one character string.
5 14
  */
6 15
 
  16
+/*
  17
+This needs a unit test suite. But since its only job is to implement an
  18
+interface, maybe I can find an interface test suite?
  19
+*/
  20
+
7 21
 var events = require('events');
8 22
 var util = require('util');
9 23
 
10 24
 var Command = require('./structures/command');
11 25
 
12  
-/**
13  
- * ctx: NRC interface
14  
- */
15  
-var Commander = function (name, config) {
  26
+var CommandParser = function (name, config) {
16 27
     events.EventEmitter.call(this);
17 28
     var that = this;
18 29
     this._name = name;
  30
+
  31
+    switch (typeof config) {
  32
+        case "string":
  33
+        this.trigger = config;
  34
+        break;
  35
+        case "object":
  36
+        this.trigger = (typeof config.trigger === "string" ?
  37
+            config.trigger : '!');
  38
+
  39
+        break;
  40
+        default:
  41
+        this.trigger = '!';
  42
+    }
19 43
     this.trigger = config.trigger || "!";
20 44
 };
21 45
 
22  
-Commander.prototype = new events.EventEmitter();
23  
-Commander.prototype.constructor = Commander;
  46
+CommandParser.prototype = new events.EventEmitter();
  47
+CommandParser.prototype.constructor = CommandParser;
24 48
 
25  
-Commander.prototype.parseMessage = function (msg) {
  49
+CommandParser.prototype.parse = function (msg) {
26 50
     var command = this.getCommandString(msg);
27 51
 
28 52
     if (command) {
@@ -32,15 +56,8 @@ Commander.prototype.parseMessage = function (msg) {
32 56
     }
33 57
 };
34 58
 
35  
-Commander.prototype.getCommandString = function (privmsg) {
36  
-    if (privmsg.message[0] === this.trigger[0]) {
37  
-        return privmsg.message.substr(1);
38  
-    }
39  
-
40  
-    if (privmsg.isQuery) {
41  
-        return privmsg.message;
42  
-    }
43  
-
  59
+// Returns the command string with the name and trigger removed or false.
  60
+CommandParser.prototype.getCommandString = function (privmsg) {
44 61
     if (privmsg.message.indexOf(this._name()) === 0) {
45 62
         var msg = privmsg.message.substr(privmsg.message.indexOf(" ") + 1);
46 63
 
@@ -51,7 +68,15 @@ Commander.prototype.getCommandString = function (privmsg) {
51 68
         }
52 69
     }
53 70
 
  71
+    if (privmsg.message[0] === this.trigger[0]) {
  72
+        return privmsg.message.substr(1);
  73
+    }
  74
+
  75
+    if (privmsg.isQuery) {
  76
+        return privmsg.message;
  77
+    }
  78
+
54 79
     return false;
55 80
 };
56 81
 
57  
-module.exports = Commander;
  82
+module.exports = CommandParser;
29  lib/index.js
... ...
@@ -1,9 +1,22 @@
1  
-module.exports = {
2  
-  NRC : require('./nrc'),
3  
-  Message : require('./structures/message'),
4  
-  Command : require('./structures/command'),
5  
-  Socket : require('./socket'),
6  
-  MessageListener : require('./irc-message-emitter'),
7  
-  Commander : require('./commander'),
8  
-  Modules : require('./modules')
  1
+var index = {
  2
+    NRC : "nrc",
  3
+    nrc : "nrc",
  4
+    Nrc : "nrc",
  5
+    Client: "nrc",
  6
+    Message : 'structures/message',
  7
+    Command : 'structures/command',
  8
+    Hostmask : 'structures/hostmask',
  9
+    Socket : 'socket',
  10
+    OutputSocket : 'output-socket',
  11
+    MessageParser : 'irc-message-emitter',
  12
+    // ChunkedMessageParser: 'chunked-message-parser',
  13
+    CommandParser : 'commander',
  14
+    Modules : 'modules',
  15
+    Bisubscriber : 'bisubscriber'
9 16
 };
  17
+
  18
+for (var m in index) {
  19
+    index[m] = require('./' + index[m]);
  20
+}
  21
+
  22
+module.exports = index;
61  lib/irc-message-emitter.js
... ...
@@ -1,61 +0,0 @@
1  
-/**
2  
- * This event emitter listens to an IRC Socket, emitting parsed messages.
3  
- * Here are some example messages:
4  
- *  join
5  
- *  error (when quitting/forced to quit)
6  
- *  005
7  
- *
8  
- * This one also joins all channels and identifies to nickserv.
9  
- * It probably shouldn't...
10  
- *
11  
- * The constructor takes three arguments:
12  
- * IRCSocket - implementing the IRCSocket interface.
13  
- * Config - A data object with the following fields:
14  
- *   password
15  
- *   nickserv
16  
- *   channels
17  
- *   (It shouldn't need to know any of these!)
18  
- * IRCClient - An object that abstracts the sending of messages to the socket.
19  
- * Should be - Object that `receives` the message.
20  
- */
21  
-
22  
- var events = require('events');
23  
- var util = require('util');
24  
-
25  
- var Message = require('./structures/message');
26  
-
27  
- var IRCMessageHandler = function (IRCMessageEmitter, config, client) {
28  
-    this.nrc = client;
29  
-    IRCMessageEmitter.on('data', this._onData.bind(this));
30  
-    IRCMessageEmitter.on('ready', this._onReady(config.password, config.nickserv, config.channels).bind(this));
31  
-};
32  
-
33  
-IRCMessageHandler.prototype = new events.EventEmitter();
34  
-IRCMessageHandler.prototype.constructor = IRCMessageHandler;
35  
-
36  
-IRCMessageHandler.prototype._onData = function (raw) {
37  
-    var message = Object.freeze(new Message(raw, this.nrc));
38  
-    this.emit(message.name, message);
39  
-};
40  
-
41  
-// This should be handled by the Socket.
42  
-IRCMessageHandler.prototype._onReady = function (password, nickserv, channels) {
43  
-    var that = this;
44  
-    return function () {
45  
-        if (password) {
46  
-            this.nrc.say(nickserv, "identify " + password);
47  
-        }
48  
-
49  
-        for (var ix = 0; ix < channels.length; ix++) {
50  
-            this.nrc.join(channels[ix]);
51  
-        }
52  
-
53  
-        that.emit("ready");
54  
-    };
55  
-};
56  
-
57  
-IRCMessageHandler.prototype.toString = function () {
58  
-    return "[Object IRCMessageHandler]";
59  
-};
60  
-
61  
-module.exports = IRCMessageHandler;
42  lib/message-parser.js
... ...
@@ -0,0 +1,42 @@
  1
+/**
  2
+ * This event emitter listens to an IRC Socket and converts all messages to
  3
+ * Message objects which it emits under events of the Message.name field.
  4
+ * Some example events include:
  5
+ *  join
  6
+ *  error (when quitting/forced to quit)
  7
+ *  005
  8
+ *
  9
+ * The constructor takes two arguments:
  10
+ *   1. socket   - Implementing the IrcSocket interface.
  11
+ *   2. receiver - The nominal receiver of the object.
  12
+ */
  13
+
  14
+/*
  15
+This is very close to a pure actor.
  16
+Just need class syntax and fat arrow syntax and this will look beautiful.
  17
+*/
  18
+
  19
+var events = require('events');
  20
+var util = require('util');
  21
+
  22
+var Message = require('./structures/message');
  23
+
  24
+var MessageParser = function (receiver, socket) {
  25
+    this.receiver = receiver;
  26
+    socket.on('data', this._onData.bind(this));
  27
+};
  28
+
  29
+MessageParser.prototype = new events.EventEmitter();
  30
+MessageParser.prototype.constructor = MessageParser;
  31
+
  32
+MessageParser.prototype._onData = function (raw) {
  33
+    var message = Object.freeze(new Message(raw, this.receiver));
  34
+    this.emit(message.name, message);
  35
+    this.emit("_message", message);
  36
+};
  37
+
  38
+MessageParser.prototype.toString = function () {
  39
+    return "[Object MessageParser]";
  40
+};
  41
+
  42
+module.exports = MessageParser;
178  lib/nrc.js
... ...
@@ -1,19 +1,33 @@
1 1
 /**
2 2
  *
3  
- * This is mostly documented by readme.md.
  3
+ * The public contract of the Client object is documented by readme.md.
4 4
  *
  5
+ * Public Methods:
  6
+ *   connect() -> void
  7
+ *   disconnect() -> void
  8
+ *   Configuration Storage iface
  9
+ *   IrcOutputSocket iface
  10
+ *   Bisubscriber iface
  11
+ *   Modules iface
5 12
  */
6 13
 
7  
-var events = require('events');
8  
-var util = require('util');
  14
+/*
  15
+I should see if there's a better way to make this class than
  16
+all of these wrapped methods.
  17
+*/
9 18
 
10  
-var IrcSocket = require('./socket');
11  
-var IrcMessageEmitter = require('./irc-message-emitter');
12  
-var Commander = require('./commander');
13  
-var Modules = require('./modules');
14  
-var Subscriber = require('./bisubscriber');
  19
+var defaultFactoryConfiguration = {
  20
+    'Socket' : require('net').Socket,
  21
+    'IrcSocket' : require('./socket'),
  22
+    'IrcOutputSocket' : require('./output-socket'),
  23
+    'MessageParser' : require('./message-parser'),
  24
+    // 'ChunkedMessageParser' : require('./chunked-message-parser'),
  25
+    'CommandParser' : require('./command-parser'),
  26
+    'Modules' : require('./modules'),
  27
+    'BiSubscriber' : require('./bisubscriber')
  28
+};
15 29
 
16  
-var defaultNRCConfiguration = {
  30
+var defaultClientConfiguration = {
17 31
     channels: [],
18 32
     nickserv: "nickserv",
19 33
     password: undefined,
@@ -42,44 +56,58 @@ var defaults = function (o, d) {
42 56
 /** Fields
43 57
  * _config
44 58
  * _socket
45  
- * _messageParser
46  
- * _commander
47  
- * _nick
  59
+ * _outputSocket
48 60
  * _subscriber
49 61
  * _modules
50 62
  */
51  
-var NRC = function (config, opt) {
  63
+ var Client = function (config, di) {
52 64
     var that = this;
53 65
 
54  
-    // Parse the configuration object.
55  
-    this._config = config = defaults(config, defaultNRCConfiguration);
  66
+    // Parse the configuration object. Make it immutable.
  67
+    this._config = Object.freeze(defaults(config, defaultClientConfiguration));
  68
+    di = defaults(di, defaultFactoryConfiguration);
56 69
 
57 70
     // Create a socket.
58 71
     // The socket reads and sends messages from/to the IRC server.
59  
-    this._socket = new IrcSocket(config, opt);
  72
+    // The output socket wraps the `raw` method.
  73
+    this._socket = new di.IrcSocket(this._config, di);
  74
+    this._outputSocket = new di.IrcOutputSocket(this._socket, config.nick);
  75
+
  76
+    // Find a new place for this.
  77
+    // Maybe as part of the IrcSocket refactored for accepting a
  78
+    // startup sequence.
  79
+    this._socket.on('ready', function () {
  80
+        if (that._config.password) {
  81
+            that.say(that._config.nickserv, "identify " + that._config.password);
  82
+        }
  83
+
  84
+        that._config.channels.forEach(function (channel) {
  85
+            this.join(channel);
  86
+        }.bind(that));
  87
+    });
60 88
 
61 89
     // Create the listener to the socket.
62 90
     // This listener will parse the raw messages of the socket, and
63 91
     // emits specific events to listen to.
64  
-    this._messageParser = new IrcMessageEmitter(this._socket, config, this);
  92
+    var messageParser = new di.MessageParser(this, this._socket);
65 93
 
66 94
     // Create the listener to private messages from the IRCMessageEmitter
67 95
     // The commander will parse these private messages for commands, and
68 96
     // emit those commands, also parsed.
69  
-    this._commander = new Commander(this.nick.bind(this), this, config, opt);
70  
-    this._nick = config.nick;
  97
+    var commandParser = new di.CommandParser(this.nick.bind(this), this._config);
71 98
 
72  
-    // The subscriber handles event subscriptions to the NRC object,
  99
+    // The subscriber handles event subscriptions to the Client object,
73 100
     // determining whether they should be handled by the IrcMessageEmitter
74  
-    // or the Commander.
75  
-    this._subscriber = new Subscriber(this._messageParser, this._commander);
76  
-    this._subscriber.on("privmsg", this._commander.parseMessage.bind(this._commander));
  101
+    // or the Command Parser.
  102
+    this._subscriber = new di.BiSubscriber(messageParser, commandParser);
  103
+    this._subscriber.on("privmsg", commandParser.parseMessage.bind(commandParser));
77 104
 
78 105
     // And finally, the module system.
79  
-    this._modules = new Modules(this._subscriber);
  106
+    this._modules = new di.Modules(this._subscriber);
80 107
     this.require(require("../modules/server"));
81 108
     //this.require(require("../modules/help"));
82 109
     //this.require(require("../modules/user"));
  110
+    //this.require(require("../modules/channel"));
83 111
 
84 112
     // Standard event for IRC quitting.
85 113
     this._subscriber.on("error", function (event) {
@@ -89,116 +117,106 @@ var NRC = function (config, opt) {
89 117
 
90 118
 // implements ConfigurationStorage
91 119
 
92  
-NRC.prototype.config = function (param) {
  120
+Client.prototype.config = function (param) {
93 121
     return this._config[param];
94 122
 };
95 123
 
96  
-// implements IRCClient
  124
+// implements ???
97 125
 
98  
-NRC.prototype.connect = function () {
  126
+Client.prototype.connect = function () {
99 127
     this._socket.connect();
100 128
     return this;
101 129
 };
102 130
 
103  
-NRC.prototype.disconnect = function () {
104  
-    this._socket.end();
  131
+Client.prototype.start = function () {
  132
+    this._socket.connect();
105 133
     return this;
106 134
 };
107 135
 
108  
-NRC.prototype.say = function (location, message) {
109  
-    if (util.isArray(message)) {
110  
-        for (var ix = 0; ix < message.length; ix++) {
111  
-            this.say(message[ix]);
112  
-        }
113  
-        return this;
114  
-    }
  136
+Client.prototype.disconnect = function () {
  137
+    this._socket.end();
  138
+    return this;
  139
+};
115 140
 
116  
-    //Log.output(this.log, location + " " + message);
117  
-    this._socket.raw(["PRIVMSG", location, ":" + message].join(" "));
  141
+Client.prototype.end = function () {
  142
+    this._socket.end();
118 143
     return this;
119 144
 };
120 145
 
121  
-NRC.prototype.act = function(location, message) {
122  
-    if (util.isArray(message)) {
123  
-        for (var ix = 0; ix < message.length; ix++) {
124  
-            this.act(message[ix]);
125  
-        }
126  
-        return this;
127  
-    }
  146
+// implements IRC Output Socket
128 147
 
129  
-    //Log.output(this.log, location + " " + message);
130  
-    this._socket.raw([
131  
-        "PRIVMSG",
132  
-        location,
133  
-        ":\u0001ACTION" + message + "\u0001"
134  
-    ].join(" "));
  148
+Client.prototype.say = function () {
  149
+    this._outputSocket.say.apply(this._outputSocket, arguments);
135 150
     return this;
136 151
 };
137 152
 
138  
-NRC.prototype.join = function (channel) {
139  
-    this._socket.raw(["JOIN", channel]);
  153
+Client.prototype.act = function() {
  154
+    this._outputSocket.act.apply(this._outputSocket, arguments);
140 155
     return this;
141 156
 };
142 157
 
143  
-NRC.prototype.part = function (channel, reason) {
144  
-    this._socket.raw(function () {
145  
-        var part = ["PART", channel];
146  
-        if (reason) {
147  
-            part.push(":" + reason);
148  
-        }
149  
-        return part;
150  
-    }());
  158
+Client.prototype.join = function () {
  159
+    this._outputSocket.join.apply(this._outputSocket, arguments);
  160
+    return this;
  161
+};
  162
+
  163
+Client.prototype.part = function () {
  164
+    this._outputSocket.part.apply(this._outputSocket, arguments);
151 165
 
152 166
     return this;
153 167
 };
154 168
 
155  
-NRC.prototype.quit = function (reason) {
156  
-    this._socket.raw(["QUIT", reason ? ":" + reason : ""].join(" ").trim());
  169
+Client.prototype.quit = function () {
  170
+    this._outputSocket.quit.apply(this._outputSocket, arguments);
157 171
     return this;
158 172
 };
159 173
 
160  
-NRC.prototype.nick = function (newNick) {
161  
-    if (newNick) {
162  
-        //Log.output(this.log, "nick " + newNick);
163  
-        this._socket.raw("NICK " + newNick);
164  
-        this._nick = newNick;
165  
-        return this;
166  
-    } else {
167  
-        return this._nick;
168  
-    }
  174
+Client.prototype.nick = function () {
  175
+    var nick = this._outputSocket.nick.apply(this._outputSocket, arguments);
  176
+    return nick || this;
  177
+};
  178
+
  179
+Client.prototype.userhost = function () {
  180
+    this._outputSocket.userhost.apply(this._outputSocket, arguments);
  181
+};
  182
+
  183
+Client.prototype.whois = function () {
  184
+    this._outputSocket.whois.apply(this._outputSocket, arguments);
169 185
 };
170 186
 
171 187
 // implements BiSubscriber
172 188
 
173  
-NRC.prototype.on = function (a1, a2) {
  189
+Client.prototype.on = function (a1, a2) {
174 190
     this._subscriber.on(a1, a2);
  191
+    return this;
175 192
 };
176 193
 
177  
-NRC.prototype.once = function (a1, a2) {
  194
+Client.prototype.once = function (a1, a2) {
178 195
     this._subscriber.once(a1, a2);
  196
+    return this;
179 197
 };
180 198
 
181 199
 // implements ModuleSubscriber
182 200
 
183  
-NRC.prototype.require = function (module) {
  201
+Client.prototype.require = function (module) {
184 202
     return this._modules.require(module);
185 203
 };
186 204
 
187  
-NRC.prototype.use = function (name) {
  205
+Client.prototype.use = function (name) {
188 206
     return this._modules.use(name);
189 207
 };
190 208
 
191  
-NRC.prototype.isModule = function (name) {
  209
+Client.prototype.isModule = function (name) {
192 210
   return this._modules.isModule(name);
193 211
 };
194 212
 
195  
-NRC.prototype.getAllModuleNames = function () {
  213
+Client.prototype.getAllModuleNames = function () {
196 214
   return this._modules.getAllModuleNames();
197 215
 };
198 216
 
199  
-NRC.prototype.getAllModuleExports = function () {
  217
+Client.prototype.getAllModuleExports = function () {
200 218
   return this._modules.getAllModuleExports();
201 219
 };
202 220
 
203 221
 // Export the class.
204  
-module.exports = NRC;
  222
+module.exports = Client;
111  lib/output-socket.js
... ...
@@ -0,0 +1,111 @@
  1
+/**
  2
+ * The OutputSocket is a facade for the IrcSocket's `raw` method.
  3
+ *
  4
+ * Though it doesn't have all commands possible, it hsa
  5
+ *
  6
+ * Public Methods
  7
+ *   say
  8
+ *   act
  9
+ *   join
  10
+ *   part
  11
+ *   quit
  12
+ *   nick
  13
+ *   userhost
  14
+ *   whois
  15
+ */
  16
+
  17
+/*
  18
+The fact that the output socket keeps track of the user's nick is both wrong
  19
+and stupid, and needs another refactoring to move out.
  20
+*/
  21
+
  22
+var util = require('util');
  23
+
  24
+// Calls fn for each element in the array of the second parameter.
  25
+// If no array, treat as an array of one element. ;)
  26
+var invokeGroupTwo = function (fn) {
  27
+    return function (_, group) {
  28
+        if (util.isArray(group)) {
  29
+            var args = Array.prototype.slice.call(arguments);
  30
+            for (var ix = 0; ix < group.length; ix++) {
  31
+                args[1] = group[ix];
  32
+                fn.apply(this, args);
  33
+            }
  34
+        } else {
  35
+            fn.apply(this, arguments);
  36
+        }
  37
+    };
  38
+};
  39
+
  40
+var OutputSocket = function (socket, nick) {
  41
+    this._socket = socket;
  42
+    this._nick = nick;
  43
+};
  44
+
  45
+OutputSocket.prototype.say = invokeGroupTwo(function (location, message) {
  46
+    this._raw(["PRIVMSG", location, ":" + message].join(" "));
  47
+});
  48
+
  49
+OutputSocket.prototype.ctcp = invokeGroupTwo(function (location, message) {
  50
+    this.say(location, '\u0001' + message + '\u0001');
  51
+});
  52
+
  53
+OutputSocket.prototype.act = invokeGroupTwo(function (location, message) {
  54
+    this.ctcp(location, "ACTION " + message);
  55
+});
  56
+
  57
+OutputSocket.prototype.join = function (channel) {
  58
+    this._raw(["JOIN", channel]);
  59
+};
  60
+
  61
+OutputSocket.prototype.part = function (channel, reason) {
  62
+    this._raw("PART "+ channel + (reason ? " :" + reason : ''));
  63
+};
  64
+
  65
+OutputSocket.prototype.nick = function (newNick) {
  66
+    if (newNick) {
  67
+        this._raw("NICK " + newNick);
  68
+        this._nick = newNick;
  69
+        return;
  70
+    } else {
  71
+        return this._nick;
  72
+    }
  73
+};
  74
+
  75
+OutputSocket.prototype.quit = function (reason) {
  76
+    this._raw("QUIT" + (reason ? " :" + reason : ""));
  77
+};
  78
+
  79
+OutputSocket.prototype.userhost = function (users) {
  80
+    switch (typeof users) {
  81
+        case "string":
  82
+            this._raw("USERHOST " + users);
  83
+            break;
  84
+        case "array":
  85
+            while (users.length !== 0) {
  86
+                this.userhost(users.splice(0, 5).join(' '));
  87
+            }
  88
+        break;
  89
+            default:
  90
+            throw new Error("Userhost command takes either a string (a" +
  91
+                " single nick) or an array (of string nicks)");
  92
+    }
  93
+};
  94
+
  95
+OutputSocket.prototype.whois = function (users, server) {
  96
+    if (typeof users === "array") {
  97
+        while (users.length > 15) {
  98
+            this.whois(users.splice(0, 15), server);
  99
+        }
  100
+
  101
+        users = users.join(',');
  102
+    }
  103
+
  104
+    this._raw("WHOIS " + server ? server + " " : "" + users);
  105
+};
  106
+
  107
+OutputSocket.prototype._raw = function (command) {
  108
+    this._socket.raw(command);
  109
+};
  110
+
  111
+module.exports = OutputSocket;
27  lib/socket.js
... ...
@@ -1,7 +1,8 @@
1 1
 /*
2 2
  * IRC Socket - handles communication between an IRC server and a Node.js script.
3 3
  *
4  
- * The constructor takes a network configuration object and an optional object.
  4
+ * The constructor takes a network configuration object and a dependency 
  5
+ * injection object.
5 6
  *
6 7
  * This socket exposes the connect and end methods of the socket Interface.
7 8
  *
@@ -12,11 +13,11 @@
12 13
  *   realname - Realname for the bot.
13 14
  *   port     - Port to connect to. Defaults to 6667.
14 15
  *
15  
- * The optional object has one field: socket, which is the socket to use for
16  
- * the impl. Must use the net.Socket interface.
  16
+ * The dependency injection object has one field: Socket, which is the 
  17
+ * socket class to use for the impl. Defaults to require('net').Socket.
17 18
  *
18  
- * Note that the only default value from the perspective of the socket is
19  
- * the port. All other information must be passed to it.
  19
+ * From the perspective of the socket, the only default value is the port.
  20
+ * The rest of the defaults are added by the NRC object.
20 21
  *
21 22
  * ## Writing to the Server ##
22 23
  * To send messages to the server, use socket.raw(). It accepts either a
@@ -47,7 +48,9 @@
47 48
  * ## Public methods ##
48 49
  *   - new(network: Object[, opt: Object]): IRCSocket
49 50
  *   - connect(): undefined
  51
+ *   - start(): undefined
50 52
  *   - end(): undefined
  53
+ *   - disconnect(): undefined
51 54
  *   - raw(message: List[String]): undefined
52 55
  *   - raw(message: String): undefined
53 56
  *   - isConnected(): boolean
@@ -68,15 +71,13 @@
68 71
  var events = require('events');
69 72
  var util = require('util');
70 73
 
71  
- var Socket = function (network, opt) {
  74
+ var Socket = function (network, di) {
72 75
     that = this;
73  
-    events.EventEmitter.call(this);
  76
+    var Socket = (di && di.Socket) || net.Socket;
74 77
 
75 78
     this.port = network.port || 6667;
76 79
     this.netname = network.server;
77  
-
78  
-    this.server = (opt && opt.socket) ? opt.socket : new net.Socket();
79  
-    this.log = (opt && opt.logger) || {};
  80
+    this.server = new Socket();
80 81
     this.connected = false;
81 82
 
82 83
     /**
@@ -149,7 +150,7 @@
149 150
 };
150 151
 
151 152
 Socket.prototype = new events.EventEmitter();
152  
-
  153
+Socket.prototype.constructor = Socket;
153 154
 
154 155
 Socket.prototype.connect = function () {
155 156
     if (this.isConnected()) {
@@ -159,6 +160,8 @@ Socket.prototype.connect = function () {
159 160
     this.server.connect(this.port, this.netname);
160 161
 };
161 162
 
  163
+Socket.prototype.start = Socket.prototype.connect;
  164
+
162 165
 Socket.prototype.end = function () {
163 166
     if (!this.isConnected()) {
164 167
         return;
@@ -167,6 +170,8 @@ Socket.prototype.end = function () {
167 170
     this.server.end();
168 171
 };
169 172
 
  173
+Socket.prototype.disconnect = Socket.prototype.end;
  174
+
170 175
 Socket.prototype.raw = function (message) {
171 176
     if (!this.connected) {
172 177
         return;
2  lib/structures/message.js
... ...
@@ -1,3 +1,5 @@
  1
+var util = require('util');
  2
+
1 3
 var Hostmask = require('./hostmask');
2 4
 var mircColors = /\u0003\d?\d?,?\d?\d?/g;
3 5
 
4  package.json
... ...
@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "nrc",
3  
-  "version": "0.4.1",
  3
+  "version": "0.4.3",
4 4
   "description": "Node.js IRC API",
5 5
   "maintainers": [{
6 6
     "name": "Ryan Scheel",
@@ -15,7 +15,7 @@
15 15
   "main": "lib",
16 16
   "directories": ["lib", "config"],
17 17
   "engines": {
18  
-    "node": ">=0.6.7"
  18
+    "node": ">=0.6.0"
19 19
   },
20 20
   "keywords": ["irc"],
21 21
   "dependencies": {
104  readme.md
Source Rendered
@@ -11,12 +11,11 @@ API documenation will be here at some point.
11 11
 ```javascript
12 12
 var nrc = require('nrc');
13 13
 var network = require('../config/myNetwork.json');
14  
-var myLogger = new (require('myLogger').Logger)()
15  
-var myNetwork = new nrc.NRC(network, {log: myLogger});
  14
+var myNetwork = new nrc.Client(network);
16 15
 myNetwork.connect();
17 16
 ```
18 17
 
19  
-Before connecting, add listeners to events from irc & users, or load a module.
  18
+Before connecting, add listeners to events from irc & users, or load modules.
20 19
 
21 20
 ```javascript
22 21
 
@@ -44,17 +43,24 @@ It is suggested that your static network configuration objects go in _/config/%N
44 43
 
45 44
 A network configuration object has the following properties:
46 45
 
47  
-* server   - IRC server to connect to. _Example:_ _irc.mibbit.net_
48  
-* nick     - Nickname the bot will use. Defaults to "nrcbot"
49  
-* user     - Username the bot will use. Defaults to "user"
50  
-* realname - Realname for the bot. Defaults to "nrc v0.3"
51  
-* port     - Port to connect to. Defaults to 6667.
52  
-* password - Password for identifying to services.
53  
-* nickserv - Nickname for nickserv service. Defaults to "nickserv".
54  
-* trigger  - Command character to trigger commands with. By default, '!'.
55  
-* channels - Array of channels to autojoin. _Example:_ ["#help", "#nrc"]
56  
-
57  
-Other modules may require or use more options.
  46
+* server      - IRC server to connect to. _Example:_ _irc.mibbit.net_
  47
+* nick        - Nickname the bot will use. Defaults to "nrcbot"
  48
+* user        - Username the bot will use. Defaults to "user"
  49
+* realname    - Realname for the bot. Defaults to "nrc v0.3"
  50
+* port        - Port to connect to. Defaults to 6667.
  51
+* password    - Password for identifying to services.
  52
+* nickserv    - Nickname for nickserv service. Defaults to "nickserv".
  53
+* trigger     - Command character to trigger commands with. By default, '!'.
  54
+* channels    - Array of channels to autojoin. _Example:_ ["#help", "#nrc"]
  55
+* modules     - An array of strings or objects.
  56
+** string     - The file location of the Node module that exports an NRC module.
  57
+** object     - Has the following fields. All optional other than 'file'
  58
+*** file      - The file location of the Node module that exports an NRC module.
  59
+*** config    - Configuration object for the module. Defaults to {}.
  60
+*** usepath   - Boolean whether to append module-path to file. Defaults to true.
  61
+* module-path - Prefix added to module file locations.
  62
+
  63
+Other modules may require or use more options. Such options will be in
58 64
 
59 65
 -------------
60 66
 
@@ -163,12 +169,30 @@ nrc.act('#example', "does something!");
163 169
 
164 170
 Quits the server with the given reason.
165 171
 
  172
+
  173
+### whois(users, server) ###
  174
+
  175
+Server is optional, and you'll probably not need it. Look at RFC 1459 for
  176
+what benefit it gives you.
  177
+
  178
+users is either a string or an array of strings.
  179
+
  180
+### userhost(users) ###
  181
+
  182
+Retrieves the userhost of the user. 
  183
+
  184
+### _raw(message) ###
  185
+
  186
+Our IrcOutputSocket class does not have all commands. If you need to use one
  187
+that is not listed here, you can use the internal _raw method, which takes
  188
+the entire message as is as a string.
  189
+
166 190
 --------
167 191
 
168 192
 ## Modules ##
169 193
 
170  
-NRC has its own module system, loosely based off of Node's. Modules are implemented
171  
-using the following object structure:
  194
+NRC has its own module system, loosely based off of Node's. Modules are
  195
+implemented using the following object structure:
172 196
 
173 197
 ```javascript
174 198
 {
@@ -248,15 +272,17 @@ Assume the module's name is 'example'. Then these will all work and return
248 272
 
249 273
 #### channels ####
250 274
 
251  
-___Unofficial:___ This module will be official in 0.3.
  275
+___Unofficial:___ This module will be official before the 1.0.0 release.
  276
+
  277
+This module is currently disabed.
252 278
 
253 279
 This module handles keeping track of channel-specific data.
254 280
 
255 281
 #### users ####
256 282
 
257  
-___Unofficial:___ This module will be official in 0.3.
  283
+___Unofficial:___ This module will be official before the 1.0.0 release.
258 284
 
259  
-Though unofficial, and untested, this module should hopefully work.
  285
+This module is currently disabled.
260 286
 
261 287
 This module handles keeping track of user-specific data.
262 288
 
@@ -306,3 +332,43 @@ The capabilities object looks like this for the Mibbit network.
306 332
   INVEX: true
307 333
 }
308 334
 ```
  335
+
  336
+## Contributions ##
  337
+
  338
+There's a lot of work that can be done.
  339
+
  340
+### Module System ###
  341
+
  342
+The parameters for the modules in the config don't actually do anything. They
  343
+need to be implemented.
  344
+
  345
+### Startup ###
  346
+
  347
+As part of an IrcSocket, I want them to take a Startup object (an object that
  348
+implements the startup interface, whatever that may be). Right now startup
  349
+code is all over the place, and this sucks.
  350
+
  351
+I want this to be able to change (say we have one that implements IRC 3's
  352
+CAPABILITIES protocol or one that does WEBIRC) pretty easily.
  353
+
  354
+### Testing ###
  355
+
  356
+I would like to be testing each class in isolation.
  357
+
  358
+With exception to the Client class, which should be tested as integration.
  359
+
  360
+### Built In Modules ###
  361
+
  362
+I don't want to write these, so I've been putting them off. If you want to,
  363
+please feel free to write them.
  364
+
  365
+### ChunkedIrcMessageHandler ###
  366
+
  367
+Listens to the IrcMessageHandler, it chunks together list-like replies such
  368
+as whois and isupport numerics. For messages that aren't chunked, just pass
  369
+them through normally.
  370
+
  371
+### IrcOutputSocket Commands ###
  372
+
  373
+I'm missing most of the commands from IRC. If you want to add them, please
  374
+do so. Just make sure to also add the method wrapper to the client class.
33  spec/commander.spec.js
... ...
@@ -1,33 +0,0 @@
1  
-var util = require('util');
2  
-
3  
-var Commander = require('../lib/commander');
4  
-var EE = require('events').EventEmitter;
5  
-var mockMessage = Object.freeze({
6  
-  actor : 'sender',
7  
-  channel : 'sender',
8  
-  isQuery : 'true',
9  
-  message : 'event'
10  
-});
11  
-
12  
-// Does not work this way anymore. Commander doesn't listen to the ctx
13  
-// anymore. Instead, it's up to the ctx to set this up.
14  
-xdescribe("binding", function () {
15  
-  it('binds callbacks to its context', function () {
16  
-    var ctx = new EE();
17  
-    var commander = new Commander(ctx, {}, {});
18  
-
19  
-    runs(function () {
20  
-      commander.on('event', function () {
21  
-        this.success = true;
22  
-      });
23  
-
24  
-      ctx.emit('privmsg', mockMessage);
25  
-    });
26  
-
27  
-    waits(10);
28  
-
29  
-    runs(function () {
30  
-      expect(ctx.success).toBeTruthy();
31  
-    });
32  
-  });
33  
-});
33  spec/ircsocket.spec.js
... ...
@@ -1,14 +1,9 @@
1  
-/**
2  
- * @author havvy
3  
- * This document does not respect the 80 char length limit.
4  
- * Waits total max: 1.1 seconds.
5  
- */
6  
-
7 1
 /*
8  
- * The MockSocket mocks a net.socket. The IRC socket is the socket type found
9  
- * in nrc. socket is a variable to hold the IRCSocket. mocksocket is a variable
10  
- * for holding mock sockets.
11  
- */
  2
+The MockSocket mocks a net.socket. The IRC socket is the socket type found
  3
+in nrc. socket is a variable to hold the IRCSocket. mocksocket is a variable
  4
+for holding mock sockets.
  5
+  */
  6
+
12 7
 var MockSocket = require('./mocksocket');
13 8
 var IRCSocket = require('../lib/socket.js');
14 9
 
@@ -19,12 +14,17 @@ var network = Object.freeze({
19 14
   realname: 'realbot'
20 15
 });
21 16
 
  17
+var closure = function (value) {
  18
+  return function () {
  19
+    return value;
  20
+  };
  21
+};
  22
+
22 23
 describe("connecting to a network", function connectingToANetwork () {
23 24
   var mocksocket, socket;
24 25
 
25 26
   it('knows whether or not it is connected.', function () {
26  
-    mocksocket = new MockSocket();
27  
-    socket = new IRCSocket(network, {socket : mocksocket});
  27
+    socket = new IRCSocket(network, {Socket : MockSocket});
28 28
 
29 29
     expect(socket.isConnected()).toBeFalsy();
30 30
   });
@@ -40,8 +40,8 @@ describe("connecting to a network", function connectingToANetwork () {
40 40
   });
41 41
 
42 42
   it('declares NICK and USER to the server on connection', function () {
43  
-    mocksocket = new MockSocket();
44  
-    socket = new IRCSocket(network, {socket : mocksocket});
  43
+    var mocksocket = new MockSocket();
  44
+    socket = new IRCSocket(network, {Socket : closure(mocksocket)});
45 45
     socket.connect();
46 46
     socket.end();
47 47
     expect(mocksocket.write).toHaveBeenCalledWith('NICK testbot\n', 'ascii');
@@ -52,8 +52,7 @@ describe("connecting to a network", function connectingToANetwork () {
52 52
   it('declares when ready to send commands', function () {
53 53
     var readyIsCalled = false;
54 54
     runs(function () {
55  
-      mocksocket = new MockSocket();
56  
-      socket = new IRCSocket(network, {socket : mocksocket});
  55
+      socket = new IRCSocket(network, {Socket : MockSocket});
57 56
       socket.on('ready', function () {
58 57
         readyIsCalled = true;
59 58
       });
@@ -76,7 +75,7 @@ describe('maintaining connection to a server', function () {
76 75
 
77 76
   beforeEach(function () {
78 77
     mocksocket = new MockSocket();
79  
-    socket = new IRCSocket(network, {socket : mocksocket});
  78
+    socket = new IRCSocket(network, {Socket : closure(mocksocket)});
80 79
   });
81 80
 
82 81
   afterEach(function () {
27  spec/irc-message-emitter.spec.js → spec/message-parser.spec.js
... ...
@@ -1,6 +1,6 @@
1 1
 var events = require('events');
2 2
 
3  
-var MessageHandler = require('../lib/irc-message-emitter');
  3
+var MessageHandler = require('../lib/message-parser');
4 4
 var Message = require('../lib/structures/message');
5 5
 
6 6
 describe('Message Handlers', function () {
@@ -8,8 +8,8 @@ describe('Message Handlers', function () {
8 8
 
9 9
     beforeEach(function () {
10 10
         emitter = new events.EventEmitter();
11  
-        receiver = {};
12  
-        mh = new MessageHandler(emitter, {}, receiver);
  11
+        receiver = {_:1};
  12
+        mh = new MessageHandler(receiver, emitter);
13 13
     });
14 14
 
15 15
     it('convert IRC to Messages', function () {
@@ -34,4 +34,23 @@ describe('Message Handlers', function () {
34 34
             expect(msg.receiver).toBe(receiver);
35 35
         });
36 36
     });