tmi.js
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -8,2 +8,3 @@ var bunyan = require("bunyan"); | ||
var utils = require("./utils"); | ||
var vow = require("vow"); | ||
var webSocket = require("ws"); | ||
@@ -34,3 +35,5 @@ | ||
// Client instance.. | ||
function client(opts) { | ||
var client = function client(opts) { | ||
this.setMaxListeners(0); | ||
this.opts = (typeof opts !== "undefined") ? opts : {}; | ||
@@ -72,2 +75,23 @@ this.opts.channels = opts.channels || []; | ||
// Parse emotes.. | ||
if (typeof message.tags['emotes'] === 'string') { | ||
var emoticons = message.tags['emotes'].split('/'); | ||
var emotes = {}; | ||
for (var i = 0; i < emoticons.length; i++) { | ||
var parts = emoticons[i].split(':'); | ||
emotes[parts[0]] = parts[1].split(','); | ||
} | ||
message.tags['emotes'] = emotes; | ||
} else { message.tags['emotes'] = null; } | ||
// Transform the IRCv3 tags.. | ||
if (message.tags) { | ||
for(var key in message.tags) { | ||
if (typeof message.tags[key] === "boolean") { message.tags[key] = null; } | ||
else if (message.tags[key] === "1") { message.tags[key] = true; } | ||
else if (message.tags[key] === "0") { message.tags[key] = false; } | ||
} | ||
} | ||
// Messages with no prefix.. | ||
@@ -139,2 +163,3 @@ if (message.prefix === null) { | ||
self.log.info("[" + message.params[0] + "] This room is now in subscribers-only mode."); | ||
self.emit("subscriber", message.params[0], true); | ||
self.emit("subscribers", message.params[0], true); | ||
@@ -146,2 +171,3 @@ break; | ||
self.log.info("[" + message.params[0] + "] This room is no longer in subscribers-only mode."); | ||
self.emit("subscriber", message.params[0], false); | ||
self.emit("subscribers", message.params[0], false); | ||
@@ -155,2 +181,3 @@ break; | ||
self.emit("slow", message.params[0], true); | ||
self.emit("slowmode", message.params[0], true); | ||
break; | ||
@@ -162,2 +189,3 @@ | ||
self.emit("slow", message.params[0], false); | ||
self.emit("slowmode", message.params[0], false); | ||
break; | ||
@@ -175,2 +203,3 @@ | ||
self.emit("r9kmode", message.params[0], false); | ||
self.emit("r9kbeta", message.params[0], false); | ||
break; | ||
@@ -242,2 +271,6 @@ | ||
break; | ||
case "ROOMSTATE": | ||
self.emit("roomstate", message.params[0], message.tags); | ||
break; | ||
@@ -255,2 +288,8 @@ default: | ||
case "MODE": | ||
if (message.params[1] === "+o") { | ||
self.emit("mod", message.params[0], message.params[2]); | ||
} | ||
else if (message.params[1] === "-o") { | ||
self.emit("unmod", message.params[0], message.params[2]); | ||
} | ||
break; | ||
@@ -303,2 +342,41 @@ | ||
} | ||
if (message.tags.username === "twitchnotify") { | ||
// Someone subscribed to a hosted channel. Who cares. | ||
if (message.params[1].indexOf("subscribed to") >= 0) { | ||
// | ||
} | ||
// New subscriber.. | ||
else if (message.params[1].indexOf("just subscribed") >= 0) { | ||
self.emit('subscription', message.params[0], message.params[1].split(' ')[0]); | ||
} | ||
// Subanniversary.. | ||
else if (message.params[1].indexOf("subscribed") >= 0 && message.params[1].indexOf("in a row") >= 0) { | ||
var splitted = message.params[1].split(' '); | ||
var length = splitted[splitted.length - 5]; | ||
self.emit('subanniversary', message.params[0], splitted[0], length); | ||
} | ||
} | ||
else if (message.tags.username === "jtv") { | ||
// Client sent /mods command to channel.. | ||
if (message.params[1].indexOf("moderators of this room are") >= 0) { | ||
var splitted = message.params[1].split(':'); | ||
var mods = splitted[1].replace(/,/g, '').split(':').toString().toLowerCase().split(' '); | ||
for(var i = mods.length - 1; i >= 0; i--) { | ||
if(mods[i] === '') { | ||
mods.splice(i, 1); | ||
} | ||
} | ||
self.emit('mods', message.params[0], mods); | ||
self.emit('modspromises', message.params[0], mods); | ||
} | ||
// Someone is hosting my channel.. | ||
else if (message.params[1].indexOf("is now hosting you") >= 0) { | ||
self.emit('hosted', message.params[0], utils.normalizeUsername(message.params[1].split(' ')[0])); | ||
} | ||
} | ||
break; | ||
@@ -317,3 +395,4 @@ | ||
var self = this; | ||
var deferred = vow.defer(); | ||
this.reconnect = typeof this.opts.connection.reconnect !== "undefined" ? this.opts.connection.reconnect : false; | ||
@@ -333,2 +412,3 @@ this.server = typeof this.opts.connection.server !== "undefined" ? this.opts.connection.server : "RANDOM"; | ||
self._openConnection(); | ||
deferred.resolve(); | ||
}); | ||
@@ -339,3 +419,6 @@ } | ||
this._openConnection(); | ||
deferred.resolve(); | ||
} | ||
return deferred.promise(); | ||
}; | ||
@@ -362,2 +445,3 @@ | ||
self.log.error("Server is not accepting WebSocket connections. Reconnecting in 10 seconds.."); | ||
self.emit("reconnect"); | ||
setTimeout(function() { self.connect(self.server + ":" + self.port); }, 10000); | ||
@@ -415,2 +499,3 @@ } else { | ||
var self = this; | ||
// User called .disconnect(); | ||
@@ -426,4 +511,5 @@ if (this.wasCloseCalled) { | ||
if (self.reconnect) { | ||
if (this.reconnect) { | ||
this.log.error("Sorry, we were unable to connect to chat. Reconnecting in 10 seconds.."); | ||
this.emit("reconnect"); | ||
setTimeout(function() { self.connect(self.server + ":" + self.port); }, 10000); | ||
@@ -436,4 +522,72 @@ } else { | ||
// Send a command to the server.. | ||
client.prototype._sendCommand = function _sendCommand(channel, command) { | ||
var self = this; | ||
// Promise a result.. | ||
return new vow.Promise(function(resolve, reject, notify) { | ||
if (self.usingWebSocket && self.ws !== null && self.ws.readyState !== 2 && self.ws.readyState !== 3 && self.getUsername().indexOf("justinfan") < 0) { | ||
switch (command.toLowerCase()) { | ||
case "/mods": | ||
case ".mods": | ||
self.log.info("Executing command: " + command); | ||
self.once("modspromises", function (channel, mods) { | ||
resolve(mods); | ||
}); | ||
self.ws.send("PRIVMSG " + utils.normalizeChannel(channel) + " :" + command); | ||
break; | ||
default: | ||
if (channel !== null) { | ||
self.log.info("[" + utils.normalizeChannel(channel) + "] Executing command: " + command); | ||
self.ws.send("PRIVMSG " + utils.normalizeChannel(channel) + " :" + command); | ||
} else { | ||
self.log.info("Executing command: " + command); | ||
self.ws.send(command); | ||
} | ||
resolve(); | ||
break; | ||
} | ||
} else { | ||
reject(); | ||
} | ||
}); | ||
} | ||
// Send a message to the server.. | ||
client.prototype._sendMessage = function _sendMessage(channel, message) { | ||
var self = this; | ||
// Promise a result.. | ||
return new vow.Promise(function(resolve, reject, notify) { | ||
if (self.usingWebSocket && self.ws !== null && self.ws.readyState !== 2 && self.ws.readyState !== 3 && self.getUsername().indexOf("justinfan") < 0) { | ||
self.ws.send("PRIVMSG " + utils.normalizeChannel(channel) + " :" + message); | ||
if (message.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)) { | ||
self.log.info("[" + utils.normalizeChannel(channel) + "] *<" + self.getUsername() + ">: " + message.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1]); | ||
self.emit("action", utils.normalizeChannel(channel), self.userstate[utils.normalizeChannel(channel)], message.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1]); | ||
} else { | ||
self.log.info("[" + utils.normalizeChannel(channel) + "] <" + self.getUsername() + ">: " + message); | ||
self.emit("chat", utils.normalizeChannel(channel), self.userstate[utils.normalizeChannel(channel)], message); | ||
} | ||
resolve(); | ||
} else { | ||
reject(); | ||
} | ||
}); | ||
} | ||
// Get current username.. | ||
client.prototype.getUsername = function getUsername() { | ||
return this.username; | ||
}; | ||
// Get current options.. | ||
client.prototype.getOptions = function getOptions() { | ||
return this.opts; | ||
}; | ||
// Disconnect from server.. | ||
client.prototype.disconnect = function disconnect() { | ||
var deferred = vow.defer(); | ||
if (this.usingWebSocket && this.ws !== null && this.ws.readyState !== 3) { | ||
@@ -443,10 +597,116 @@ this.wasCloseCalled = true; | ||
this.ws.close(); | ||
deferred.resolve(); | ||
} else { | ||
this.log.error("Cannot disconnect from server. Socket is not opened or connection is already closing."); | ||
deferred.reject(); | ||
} | ||
return deferred.promise(); | ||
}; | ||
client.prototype.action = function action(channel, message) { | ||
message = "\u0001ACTION " + message + "\u0001"; | ||
return this._sendMessage(channel, message); | ||
}; | ||
client.prototype.ban = function ban(channel, username) { | ||
return this._sendCommand(channel, "/ban " + utils.normalizeUsername(username)); | ||
}; | ||
client.prototype.clear = function clear(channel) { | ||
return this._sendCommand(channel, "/clear"); | ||
}; | ||
client.prototype.color = function color(channel, color) { | ||
return this._sendCommand(channel, "/color " + color); | ||
}; | ||
client.prototype.commercial = function commercial(channel, seconds) { | ||
seconds = typeof seconds !== 'undefined' ? seconds : 30; | ||
return this._sendCommand(channel, "/commercial " + seconds); | ||
}; | ||
client.prototype.host = function host(channel, target) { | ||
return this._sendCommand(channel, "/host " + utils.normalizeUsername(target)); | ||
}; | ||
client.prototype.join = function join(channel) { | ||
return this._sendCommand(null, "JOIN " + utils.normalizeChannel(channel)); | ||
}; | ||
client.prototype.mod = function mod(channel, username) { | ||
return this._sendCommand(channel, "/mod " + utils.normalizeUsername(username)); | ||
}; | ||
client.prototype.mods = function mods(channel) { | ||
return this._sendCommand(channel, "/mods"); | ||
}; | ||
client.prototype.part = client.prototype.leave = function part(channel) { | ||
return this._sendCommand(null, "PART " + utils.normalizeChannel(channel)); | ||
}; | ||
client.prototype.ping = function ping() { | ||
return this._sendCommand(null, "PING"); | ||
}; | ||
client.prototype.r9kbeta = client.prototype.r9kmode = function r9kbeta(channel) { | ||
return this._sendCommand(channel, "/r9kbeta"); | ||
}; | ||
client.prototype.r9kbetaoff = client.prototype.r9kmodeoff = function r9kbetaoff(channel) { | ||
return this._sendCommand(channel, "/r9kbetaoff"); | ||
}; | ||
client.prototype.raw = function raw(message) { | ||
return this._sendCommand(null, message); | ||
}; | ||
client.prototype.say = function say(channel, message) { | ||
return this._sendMessage(channel, message); | ||
}; | ||
client.prototype.slow = client.prototype.slowmode = function slow(channel, seconds) { | ||
seconds = typeof seconds !== 'undefined' ? seconds : 300; | ||
return this._sendCommand(channel, "/seconds " + seconds); | ||
}; | ||
client.prototype.slowoff = client.prototype.slowmodeoff = function slowoff(channel) { | ||
return this._sendCommand(channel, "/slowoff"); | ||
}; | ||
client.prototype.subscribers = function subscribers(channel) { | ||
return this._sendCommand(channel, "/subscribers"); | ||
}; | ||
client.prototype.subscribersoff = function subscribersoff(channel) { | ||
return this._sendCommand(channel, "/subscribersoff"); | ||
}; | ||
client.prototype.timeout = function timeout(channel, username, seconds) { | ||
seconds = typeof seconds !== 'undefined' ? seconds : 300; | ||
username = typeof username !== 'undefined' ? username : "Kappa"; | ||
return this._sendCommand(channel, "/timeout " + username + " " + seconds); | ||
}; | ||
client.prototype.unban = function unban(channel, username) { | ||
return this._sendCommand(channel, "/unban " + utils.normalizeUsername(username)); | ||
}; | ||
client.prototype.unhost = function unhost(channel) { | ||
return this._sendCommand(channel, "/unhost"); | ||
}; | ||
client.prototype.unmod = function unmod(channel, username) { | ||
return this._sendCommand(channel, "/unmod " + utils.normalizeUsername(username)); | ||
}; | ||
// Expose everything, for browser and Node.js / io.js | ||
if (typeof window !== "undefined") { | ||
window.client = client; | ||
window.irc = {}; | ||
window.irc.client = client; | ||
} else { | ||
module.exports = client; | ||
} |
@@ -7,3 +7,8 @@ var webSocket = require("ws"); | ||
if (type === "event" || type === "events") { | ||
var servers = [ | ||
"192.16.64.143:80" | ||
]; | ||
if (servers.indexOf(ignore) >= 0) { servers.splice(servers.indexOf(ignore), 1); } | ||
return callback(servers[Math.floor(Math.random()*servers.length)]); | ||
} | ||
@@ -18,3 +23,3 @@ else if (type === "group" || type === "groups") { | ||
]; | ||
if (servers.indexOf(ignore) >=0) { servers.splice(servers.indexOf(ignore), 1); } | ||
if (servers.indexOf(ignore) >= 0) { servers.splice(servers.indexOf(ignore), 1); } | ||
@@ -21,0 +26,0 @@ return callback(servers[Math.floor(Math.random()*servers.length)]); |
@@ -12,3 +12,3 @@ // Generate a random justinfan username.. | ||
// Normalize channel name for Twitch.. | ||
// Normalize channel name.. | ||
function normalizeChannel(name) { | ||
@@ -19,4 +19,10 @@ if (name.indexOf("#") !== 0) { name = "#" + name; } | ||
// Normalize username.. | ||
function normalizeUsername(username) { | ||
return username.replace("#", "").toLowerCase(); | ||
} | ||
exports.generateJustinfan = generateJustinfan; | ||
exports.isInteger = isInteger; | ||
exports.normalizeChannel = normalizeChannel; | ||
exports.normalizeChannel = normalizeChannel; | ||
exports.normalizeUsername = normalizeUsername; |
{ | ||
"name": "tmi.js", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Javascript library for the Twitch Messaging Interface.", | ||
@@ -14,2 +14,3 @@ "main": "index.js", | ||
"irc-message": "^3.0.1", | ||
"vow": "^0.4.10", | ||
"ws": "^0.7.2" | ||
@@ -16,0 +17,0 @@ }, |
# tmi.js - WORK IN PROGRESS | ||
[![Build Status](https://secure.travis-ci.org/Schmoopiie/tmi.js.png?branch=master)](https://travis-ci.org/Schmoopiie/tmi.js) [![Downloads](http://img.shields.io/npm/dm/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) [![Npm Version](http://img.shields.io/npm/v/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) [![Node Version](https://img.shields.io/node/v/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) [![Issues](http://img.shields.io/github/issues/Schmoopiie/tmi.js.svg?style=flat)](https://github.com/Schmoopiie/tmi.js/tmi) | ||
![](http://i.imgur.com/Fb1mUDJ.png) | ||
![](http://i.imgur.com/vsdO7N5.png) | ||
Twitch is a trademark or registered trademark of Twitch Interactive, Inc. in the U.S. and/or other countries. "tmi.js" is not operated by, sponsored by, or affiliated with Twitch Interactive, Inc. in any way. | ||
@@ -6,0 +7,0 @@ |
36461
12
714
43
4
+ Addedvow@^0.4.10
+ Addednan@2.19.0(transitive)
+ Addedvow@0.4.20(transitive)
- Removednan@2.20.0(transitive)