Comparing version 1.0.0-rc1 to 1.0.0
@@ -20,3 +20,3 @@ var request = require("request"); | ||
// Callbacks must match the regex [a-zA-Z_$][\w$]*(\.[a-zA-Z_$][\w$]*)* | ||
var callbackName = "jsonp_callback_" + Math.round(100000 * Math.random()); | ||
var callbackName = `jsonp_callback_${Math.round(100000 * Math.random())}`; | ||
window[callbackName] = function(data) { | ||
@@ -30,3 +30,3 @@ delete window[callbackName]; | ||
var script = document.createElement("script"); | ||
script.src = url + (url.indexOf("?") >= 0 ? "&" : "?") + "callback=" + callbackName; | ||
script.src = `${url}${url.indexOf("?") >= 0 ? "&" : "?"}callback=${callbackName}`; | ||
document.body.appendChild(script); | ||
@@ -33,0 +33,0 @@ } |
@@ -5,3 +5,3 @@ var api = require("./api"); | ||
var logger = require("./logger"); | ||
var parser = require("./parser"); | ||
var parse = require("./parser"); | ||
var timer = require("./timer"); | ||
@@ -60,8 +60,2 @@ var ws = global.WebSocket || global.MozWebSocket || require("ws"); | ||
// Deprecation notice.. | ||
if (typeof this.opts.connection.random !== "undefined") { | ||
this.opts.connection.cluster = this.opts.connection.random; | ||
this.log.warn("connection.random is deprecated, please use connection.cluster instead."); | ||
} | ||
eventEmitter.call(this); | ||
@@ -91,41 +85,14 @@ } | ||
client.prototype.handleMessage = function handleMessage(message) { | ||
if (message !== null) { | ||
if (!_.isNull(message)) { | ||
var channel = _.channel(_.get(message.params[0], null)); | ||
var msg = _.get(message.params[1], null); | ||
var msgid = _.get(message.tags["msg-id"], null); | ||
// Parse emotes.. | ||
if (_.isString(message.tags["emotes"])) { | ||
var emoticons = message.tags["emotes"].split("/"); | ||
var emotes = {}; | ||
// Parse badges and emotes.. | ||
message.tags = parse.badges(parse.emotes(message.tags)); | ||
for (var i = 0; i < emoticons.length; i++) { | ||
var parts = emoticons[i].split(":"); | ||
if (!parts[1]) return; | ||
emotes[parts[0]] = parts[1].split(","); | ||
} | ||
message.tags["emotes-raw"] = message.tags["emotes"]; | ||
message.tags["emotes"] = emotes; | ||
} | ||
if (_.isBoolean(message.tags["emotes"])) { message.tags["emotes-raw"] = null; } | ||
// Parse badges.. | ||
if (_.isString(message.tags["badges"])) { | ||
var badges = {}; | ||
var explode = message.tags["badges"].split(","); | ||
for (var i = 0; i < explode.length; i++) { | ||
var parts = explode[i].split("/"); | ||
if (!parts[1]) return; | ||
if (!badges[parts[1]]) { badges[parts[1]] = []; } | ||
badges[parts[1]].push(parts[0]); | ||
} | ||
message.tags["badges-raw"] = message.tags["badges"]; | ||
message.tags["badges"] = badges; | ||
} | ||
if (_.isBoolean(message.tags["badges"])) { message.tags["badges-raw"] = null; } | ||
// Transform IRCv3 tags.. | ||
if (message.tags) { | ||
for(var key in message.tags) { | ||
if (key !== "emote-sets") { | ||
if (key !== "emote-sets" && key !== "ban-duration") { | ||
if (_.isBoolean(message.tags[key])) { message.tags[key] = null; } | ||
@@ -216,3 +183,3 @@ else if (message.tags[key] === "1") { message.tags[key] = true; } | ||
if (!_.isNull(self.ws) && self.ws.readyState !== 2 && self.ws.readyState !== 3) { | ||
self.ws.send("JOIN " + _.channel(joinChannels[i])); | ||
self.ws.send(`JOIN ${_.channel(joinChannels[i])}`); | ||
} | ||
@@ -227,4 +194,2 @@ }.bind(this, i)) | ||
case "NOTICE": | ||
var msgid = _.get(message.tags["msg-id"], null); | ||
switch(msgid) { | ||
@@ -274,3 +239,3 @@ // This room is now in subscribers-only mode. | ||
case "room_mods": | ||
var splitted = message.params[1].split(":"); | ||
var splitted = msg.split(":"); | ||
var mods = splitted[1].replace(/,/g, "").split(":").toString().toLowerCase().split(" "); | ||
@@ -300,4 +265,4 @@ | ||
case "usage_ban": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseBan"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseBan"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -307,4 +272,4 @@ | ||
case "ban_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseBan"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseBan"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -314,4 +279,4 @@ | ||
case "usage_clear": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseClear"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseClear"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -321,4 +286,4 @@ | ||
case "usage_mods": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseMods"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1], []]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseMods"], [[channel, msgid, msg], [msgid, []]]); | ||
break; | ||
@@ -328,4 +293,4 @@ | ||
case "mod_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseMod"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseMod"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -337,4 +302,4 @@ | ||
case "bad_mod_mod": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseMod"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseMod"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -344,4 +309,4 @@ | ||
case "unmod_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseUnmod"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseUnmod"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -352,4 +317,4 @@ | ||
case "bad_unmod_mod": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseUnmod"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseUnmod"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -359,4 +324,4 @@ | ||
case "color_changed": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseColor"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseColor"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -367,4 +332,4 @@ | ||
case "turbo_only_color": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseColor"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseColor"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -374,4 +339,4 @@ | ||
case "commercial_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseCommercial"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseCommercial"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -382,4 +347,4 @@ | ||
case "bad_commercial_error": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseCommercial"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseCommercial"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -389,5 +354,5 @@ | ||
case "hosts_remaining": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
var remainingHost = (!isNaN(message.params[1].charAt(0)) ? message.params[1].charAt(0) : 0); | ||
this.emits(["notice", "_promiseHost"], [[channel, message.tags["msg-id"], message.params[1]], [null, remainingHost]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
var remainingHost = (!isNaN(msg.charAt(0)) ? msg.charAt(0) : 0); | ||
this.emits(["notice", "_promiseHost"], [[channel, msgid, msg], [null, remainingHost]]); | ||
break; | ||
@@ -400,4 +365,4 @@ | ||
case "usage_host": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseHost"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1], null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseHost"], [[channel, msgid, msg], [msgid, null]]); | ||
break; | ||
@@ -408,4 +373,4 @@ | ||
case "usage_r9k_on": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseR9kbeta"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseR9kbeta"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -416,4 +381,4 @@ | ||
case "usage_r9k_off": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseR9kbetaoff"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseR9kbetaoff"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -423,4 +388,4 @@ | ||
case "timeout_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseTimeout"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseTimeout"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -431,4 +396,4 @@ | ||
case "usage_subs_off": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseSubscribersoff"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseSubscribersoff"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -439,4 +404,4 @@ | ||
case "usage_subs_on": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseSubscribers"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseSubscribers"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -447,4 +412,4 @@ | ||
case "usage_emote_only_off": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseEmoteonlyoff"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseEmoteonlyoff"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -455,4 +420,4 @@ | ||
case "usage_emote_only_on": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseEmoteonly"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseEmoteonly"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -462,4 +427,4 @@ | ||
case "usage_slow_on": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseSlow"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseSlow"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -469,4 +434,4 @@ | ||
case "usage_slow_off": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseSlowoff"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseSlowoff"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -478,7 +443,8 @@ | ||
case "bad_timeout_broadcaster": | ||
case "bad_timeout_duration": | ||
case "bad_timeout_global_mod": | ||
case "bad_timeout_self": | ||
case "bad_timeout_staff": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseTimeout"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseTimeout"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -488,4 +454,4 @@ | ||
case "unban_success": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseUnban"], [[channel, message.tags["msg-id"], message.params[1]], [null]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseUnban"], [[channel, msgid, msg], [null]]); | ||
break; | ||
@@ -496,4 +462,4 @@ | ||
case "bad_unban_no_ban": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseUnban"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseUnban"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -504,4 +470,4 @@ | ||
case "not_hosting": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseUnhost"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseUnhost"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -515,4 +481,4 @@ | ||
case "whisper_restricted_recipient": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emits(["notice", "_promiseWhisper"], [[channel, message.tags["msg-id"], message.params[1]], [message.params[1]]]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emits(["notice", "_promiseWhisper"], [[channel, msgid, msg], [msgid]]); | ||
break; | ||
@@ -523,3 +489,2 @@ | ||
case "msg_banned": | ||
var msg = message.params[1]; | ||
this.log.info(`[${channel}] ${msg}`); | ||
@@ -546,8 +511,8 @@ this.emits([ | ||
], [ | ||
[channel, message.tags["msg-id"], msg], | ||
[msg], [msg], [msg], [msg], | ||
[msg], [msg], [msg], [msg], | ||
[msg], [msg], [msg], [msg], | ||
[msg], [msg], [msg], [msg], | ||
[msg] | ||
[channel, msgid, msg], | ||
[msgid], [msgid], [msgid], [msgid], | ||
[msgid], [msgid], [msgid], [msgid], | ||
[msgid], [msgid], [msgid], [msgid], | ||
[msgid], [msgid], [msgid], [msgid], | ||
[msgid] | ||
]); | ||
@@ -558,6 +523,6 @@ break; | ||
case "unrecognized_cmd": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emit("notice", channel, message.tags["msg-id"], message.params[1]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emit("notice", channel, msgid, msg); | ||
if (message.params[1].split(" ").splice(-1)[0] === "/w") { | ||
if (msg.split(" ").splice(-1)[0] === "/w") { | ||
this.log.warn("You must be connected to a group server to send or receive whispers."); | ||
@@ -579,4 +544,4 @@ } | ||
case "usage_me": | ||
this.log.info(`[${channel}] ${message.params[1]}`); | ||
this.emit("notice", channel, message.tags["msg-id"], message.params[1]); | ||
this.log.info(`[${channel}] ${msg}`); | ||
this.emit("notice", channel, msgid, msg); | ||
break; | ||
@@ -591,17 +556,17 @@ | ||
default: | ||
if (message.params[1].includes("Login unsuccessful")) { | ||
if (msg.includes("Login unsuccessful") || msg.includes("Login authentication failed")) { | ||
this.wasCloseCalled = false; | ||
this.reconnect = false; | ||
this.reason = "Login unsuccessful."; | ||
this.reason = msg; | ||
this.log.error(this.reason); | ||
this.ws.close(); | ||
} | ||
else if (message.params[1].includes("Error logging in")) { | ||
else if (msg.includes("Error logging in") || msg.includes("Improperly formatted auth")) { | ||
this.wasCloseCalled = false; | ||
this.reconnect = false; | ||
this.reason = "Error logging in."; | ||
this.reason = msg; | ||
this.log.error(this.reason); | ||
this.ws.close(); | ||
} | ||
else if (message.params[1].includes("Invalid NICK")) { | ||
else if (msg.includes("Invalid NICK")) { | ||
this.wasCloseCalled = false; | ||
@@ -620,5 +585,13 @@ this.reconnect = false; | ||
// FIXME: New notice and still undocumented. | ||
// Handle subanniversary / resub.. | ||
case "USERNOTICE": | ||
// TODO: Handle this new notice. | ||
if (msgid === "resub") { | ||
var username = _.get(message.tags["login"], null); | ||
var months = _.get(message.tags["msg-param-months"], null); | ||
this.emits(["resub", "subanniversary"], [ | ||
[channel, username, months, msg], | ||
[channel, username, months, msg] | ||
]); | ||
} | ||
break; | ||
@@ -629,13 +602,13 @@ | ||
// Stopped hosting.. | ||
if (message.params[1].split(" ")[0] === "-") { | ||
if (msg.split(" ")[0] === "-") { | ||
this.log.info(`[${channel}] Exited host mode.`); | ||
this.emits(["unhost", "_promiseUnhost"], [[channel, message.params[1].split(" ")[1] || "0"], [null]]); | ||
this.emits(["unhost", "_promiseUnhost"], [[channel, msg.split(" ")[1] || "0"], [null]]); | ||
} | ||
// Now hosting.. | ||
else { | ||
var viewers = message.params[1].split(" ")[1] || 0; | ||
var viewers = msg.split(" ")[1] || 0; | ||
if (!_.isInteger(viewers)) { viewers = 0; } | ||
this.log.info(`[${channel}] Now hosting ${message.params[1].split(" ")[0]} for ${viewers} viewer(s).`); | ||
this.emit("hosting", channel, message.params[1].split(" ")[0], viewers); | ||
this.log.info(`[${channel}] Now hosting ${msg.split(" ")[0]} for ${viewers} viewer(s).`); | ||
this.emit("hosting", channel, msg.split(" ")[0], viewers); | ||
} | ||
@@ -661,7 +634,7 @@ break; | ||
if (_.isNull(duration)) { | ||
this.log.info(`[${channel}] ${message.params[1]} has been banned. Reason: ${reason || "n/a"}`); | ||
this.emit("ban", channel, message.params[1], reason); | ||
this.log.info(`[${channel}] ${msg} has been banned. Reason: ${reason || "n/a"}`); | ||
this.emit("ban", channel, msg, reason); | ||
} else { | ||
this.log.info(`[${channel}] ${message.params[1]} has been timed out for ${duration} seconds. Reason: ${reason || "n/a"}`); | ||
this.emit("timeout", channel, message.params[1], reason, duration); | ||
this.log.info(`[${channel}] ${msg} has been timed out for ${duration} seconds. Reason: ${reason || "n/a"}`); | ||
this.emit("timeout", channel, msg, reason, duration); | ||
} | ||
@@ -684,7 +657,5 @@ } | ||
// Joined a channel on the wrong cluster.. | ||
// Wrong cluster.. | ||
case "SERVERCHANGE": | ||
this.log.warn(`Channel ${channel} isn't or no longer located on this cluster.`); | ||
this.log.warn(`Read more: https://www.tmijs.org/forums/index.php?/topic/39-channel-specific-chat-server-clusters/`); | ||
this.emit("serverchange", channel); | ||
// | ||
break; | ||
@@ -707,4 +678,4 @@ | ||
this.channels.push(channel); | ||
this.log.info("Joined " + channel); | ||
this.emit("join", channel, _.username(this.getUsername())); | ||
this.log.info(`Joined ${channel}`); | ||
this.emit("join", channel, _.username(this.getUsername()), true); | ||
} | ||
@@ -757,3 +728,3 @@ | ||
case "MODE": | ||
if (message.params[1] === "+o") { | ||
if (msg === "+o") { | ||
// Add username to the moderators.. | ||
@@ -765,3 +736,3 @@ if (!this.moderators[channel]) { this.moderators[channel] = []; } | ||
} | ||
else if (message.params[1] === "-o") { | ||
else if (msg === "-o") { | ||
// Remove username from the moderators.. | ||
@@ -798,3 +769,3 @@ if (!this.moderators[channel]) { this.moderators[channel] = []; } | ||
this.log.info(`Joined ${channel}`); | ||
this.emit("join", channel, message.prefix.split("!")[0]); | ||
this.emit("join", channel, message.prefix.split("!")[0], true); | ||
} | ||
@@ -804,3 +775,3 @@ | ||
if (this.username !== message.prefix.split("!")[0]) { | ||
this.emit("join", channel, message.prefix.split("!")[0]); | ||
this.emit("join", channel, message.prefix.split("!")[0], false); | ||
} | ||
@@ -811,4 +782,6 @@ break; | ||
case "PART": | ||
var isSelf = false; | ||
// Client a channel.. | ||
if (this.username === message.prefix.split("!")[0]) { | ||
isSelf = true; | ||
if (this.userstate[channel]) { delete this.userstate[channel]; } | ||
@@ -827,3 +800,3 @@ | ||
// Client or someone else left the channel, emit the part event.. | ||
this.emit("part", channel, message.prefix.split("!")[0]); | ||
this.emit("part", channel, message.prefix.split("!")[0], isSelf); | ||
break; | ||
@@ -833,3 +806,3 @@ | ||
case "WHISPER": | ||
this.log.info(`[WHISPER] <${message.prefix.split("!")[0]}>: ${message.params[1]}`); | ||
this.log.info(`[WHISPER] <${message.prefix.split("!")[0]}>: ${msg}`); | ||
@@ -840,4 +813,8 @@ // Update the tags to provide the username.. | ||
var from = _.channel(message.tags.username); | ||
// Emit for both, whisper and message.. | ||
this.emits(["whisper", "message"], [[message.tags, message.params[1]], [null, message.tags, message.params[1], false]]); | ||
this.emits(["whisper", "message"], [ | ||
[from, message.tags, msg, false], | ||
[from, message.tags, msg, false] | ||
]); | ||
break; | ||
@@ -852,3 +829,3 @@ | ||
// Someone subscribed to a hosted channel. Who cares. | ||
if (message.params[1].includes("subscribed to")) { | ||
if (msg.includes("subscribed to")) { | ||
// Ignore this feature. | ||
@@ -858,11 +835,15 @@ } | ||
// New subscriber.. | ||
else if (message.params[1].includes("just subscribed")) { | ||
this.emit("subscription", channel, message.params[1].split(" ")[0]); | ||
else if (msg.includes("just subscribed")) { | ||
this.emit("subscription", channel, msg.split(" ")[0]); | ||
} | ||
// TODO: Remove this on June 16th. | ||
// Subanniversary.. | ||
else if (message.params[1].includes("subscribed") && message.params[1].includes("in a row")) { | ||
var count = _.get(_.extractNumber(message.params[1]), 0); | ||
else if (msg.includes("subscribed") && msg.includes("in a row")) { | ||
var count = _.get(_.extractNumber(msg), 0); | ||
this.emit("subanniversary", channel, message.params[1].split(" ")[0], count); | ||
this.emits(["resub", "subanniversary"], [ | ||
[channel, msg.split(" ")[0], count, null], | ||
[channel, msg.split(" ")[0], count, null] | ||
]); | ||
} | ||
@@ -874,11 +855,11 @@ } | ||
// Someone is hosting the channel and the message contains how many viewers.. | ||
if (message.params[1].includes("is now hosting you for")) { | ||
var count = _.get(_.extractNumber(message.params[1]), 0); | ||
if (msg.includes("is now hosting you for")) { | ||
var count = _.get(_.extractNumber(msg), 0); | ||
this.emit("hosted", channel, _.username(message.params[1].split(" ")[0]), count); | ||
this.emit("hosted", channel, _.username(msg.split(" ")[0]), count); | ||
} | ||
// Some is hosting the channel, but no viewer(s) count provided in the message.. | ||
else if (message.params[1].includes("is now hosting you")) { | ||
this.emit("hosted", channel, _.username(message.params[1].split(" ")[0]), 0); | ||
else if (msg.includes("is now hosting you")) { | ||
this.emit("hosted", channel, _.username(msg.split(" ")[0]), 0); | ||
} | ||
@@ -889,8 +870,8 @@ } | ||
// Message is an action (/me <message>).. | ||
if (message.params[1].match(/^\u0001ACTION ([^\u0001]+)\u0001$/)) { | ||
if (msg.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)) { | ||
message.tags["message-type"] = "action"; | ||
this.log.info(`[${channel}] *<${message.tags.username}>: ${message.params[1].match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1]}`); | ||
this.log.info(`[${channel}] *<${message.tags.username}>: ${msg.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1]}`); | ||
this.emits(["action", "message"], [ | ||
[channel, message.tags, message.params[1].match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1], false], | ||
[channel, message.tags, message.params[1].match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1], false] | ||
[channel, message.tags, msg.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1], false], | ||
[channel, message.tags, msg.match(/^\u0001ACTION ([^\u0001]+)\u0001$/)[1], false] | ||
]); | ||
@@ -902,6 +883,6 @@ } | ||
message.tags["message-type"] = "chat"; | ||
this.log.info(`[${channel}] <${message.tags.username}>: ${message.params[1]}`); | ||
this.log.info(`[${channel}] <${message.tags.username}>: ${msg}`); | ||
this.emits(["chat", "message"], [ | ||
[channel, message.tags, message.params[1], false], | ||
[channel, message.tags, message.params[1], false] | ||
[channel, message.tags, msg, false], | ||
[channel, message.tags, msg, false] | ||
]); | ||
@@ -923,3 +904,3 @@ } | ||
return new Promise((resolve, reject) => { | ||
this.server = _.get(this.opts.connection.server, "RANDOM"); | ||
this.server = _.get(this.opts.connection.server, "irc-ws.chat.twitch.tv"); | ||
this.port = _.get(this.opts.connection.port, 80); | ||
@@ -936,11 +917,2 @@ | ||
// Connect to a random server.. | ||
if (this.server === "RANDOM" || typeof this.opts.connection.cluster !== "undefined") { | ||
// Default type is "aws" server.. | ||
var cluster = _.get(this.opts.connection.cluster, "aws"); | ||
var address = _.server(cluster, this.secure).split(":"); | ||
this.server = address[0]; | ||
this.port = address[1]; | ||
} | ||
// Connect to server from configuration.. | ||
@@ -992,6 +964,4 @@ this._openConnection(); | ||
parts.forEach((line) => { | ||
if (line !== null) { | ||
this.handleMessage(parser(line)); | ||
} | ||
parts.forEach((str) => { | ||
if (!_.isNull(str)) { this.handleMessage(parse.msg(str)); } | ||
}); | ||
@@ -1119,3 +1089,3 @@ }; | ||
this.ws.send("PRIVMSG " + _.channel(channel) + " :" + message); | ||
this.ws.send(`PRIVMSG ${_.channel(channel)} :${message}`); | ||
@@ -1122,0 +1092,0 @@ // Message is an action (/me <message>).. |
@@ -89,11 +89,12 @@ var _ = require("./utils"); | ||
// Ban username on channel.. | ||
ban: function ban(channel, username) { | ||
ban: function ban(channel, username, reason) { | ||
channel = _.channel(channel); | ||
username = _.username(username); | ||
reason = _.get(reason, ""); | ||
// Send the command to the server and race the Promise against a delay.. | ||
return this._sendCommand(this._getPromiseDelay(), channel, `/ban ${username}`, (resolve, reject) => { | ||
return this._sendCommand(this._getPromiseDelay(), channel, `/ban ${username} ${reason}`, (resolve, reject) => { | ||
// Received _promiseBan event, resolve or reject.. | ||
this.once("_promiseBan", (err) => { | ||
if (!err) { resolve([channel, username]); } | ||
if (!err) { resolve([channel, username, reason]); } | ||
else { reject(err); } | ||
@@ -120,3 +121,3 @@ }); | ||
color: function color(channel, newColor) { | ||
if (typeof newColor === "undefined") { newColor = channel; } | ||
newColor = _.get(newColor, channel); | ||
@@ -356,12 +357,19 @@ // Send the command to the server and race the Promise against a delay.. | ||
// Timeout username on channel for X seconds.. | ||
timeout: function timeout(channel, username, seconds) { | ||
timeout: function timeout(channel, username, seconds, reason) { | ||
channel = _.channel(channel); | ||
username = _.username(username); | ||
if (!_.isNull(seconds) && !_.isInteger(seconds)) { | ||
reason = seconds; | ||
seconds = 300; | ||
} | ||
seconds = _.get(seconds, 300); | ||
reason = _.get(reason, ""); | ||
// Send the command to the server and race the Promise against a delay.. | ||
return this._sendCommand(this._getPromiseDelay(), channel, `/timeout ${username} ${seconds}`, (resolve, reject) => { | ||
return this._sendCommand(this._getPromiseDelay(), channel, `/timeout ${username} ${seconds} ${reason}`, (resolve, reject) => { | ||
// Received _promiseTimeout event, resolve or reject.. | ||
this.once("_promiseTimeout", (err) => { | ||
if (!err) { resolve([channel, username, seconds]); } | ||
if (!err) { resolve([channel, username, seconds, reason]); } | ||
else { reject(err); } | ||
@@ -419,5 +427,24 @@ }); | ||
username = _.username(username); | ||
// The server will not send a whisper to the account that sent it. | ||
if (username === this.getUsername()) { | ||
return Promise.reject("Cannot send a whisper to the same account."); | ||
} | ||
// Send the command to the server and race the Promise against a delay.. | ||
return this._sendCommand(this._getPromiseDelay(), "#jtv", `/w ${username} ${message}`, (resolve, reject) => { | ||
var from = _.channel(username), | ||
userstate = _.merge({ | ||
"message-type": "whisper", | ||
"message-id": null, | ||
"thread-id": null, | ||
username: this.getUsername() | ||
}, this.globaluserstate); | ||
// Emit for both, whisper and message.. | ||
this.emits(["whisper", "message"], [ | ||
[from, userstate, message, true], | ||
[from, userstate, message, true] | ||
]); | ||
// At this time, there is no possible way to detect if a message has been sent has been eaten | ||
@@ -424,0 +451,0 @@ // by the server, so we can only resolve the Promise. |
@@ -25,57 +25,83 @@ /* | ||
*/ | ||
var _ = require("./utils"); | ||
var parser = function(data) { | ||
var message = { | ||
raw: data, | ||
tags: {}, | ||
prefix: null, | ||
command: null, | ||
params: [] | ||
} | ||
module.exports = { | ||
// Parse Twitch badges.. | ||
badges: function badges(tags) { | ||
if (_.isString(tags["badges"])) { | ||
var badges = {}; | ||
var explode = tags["badges"].split(","); | ||
// Position and nextspace are used by the parser as a reference.. | ||
var position = 0; | ||
var nextspace = 0; | ||
for (var i = 0; i < explode.length; i++) { | ||
var parts = explode[i].split("/"); | ||
if (!parts[1]) return; | ||
badges[parts[0]] = parts[1]; | ||
} | ||
// The first thing we check for is IRCv3.2 message tags. | ||
// http://ircv3.atheme.org/specification/message-tags-3.2 | ||
if (data.charCodeAt(0) === 64) { | ||
var nextspace = data.indexOf(" "); | ||
tags["badges-raw"] = tags["badges"]; | ||
tags["badges"] = badges; | ||
} | ||
if (_.isBoolean(tags["badges"])) { tags["badges-raw"] = null; } | ||
// Malformed IRC message.. | ||
if (nextspace === -1) { | ||
return null; | ||
return tags; | ||
}, | ||
// Parse Twitch emotes.. | ||
emotes: function emotes(tags) { | ||
if (_.isString(tags["emotes"])) { | ||
var emoticons = tags["emotes"].split("/"); | ||
var emotes = {}; | ||
for (var i = 0; i < emoticons.length; i++) { | ||
var parts = emoticons[i].split(":"); | ||
if (!parts[1]) return; | ||
emotes[parts[0]] = parts[1].split(","); | ||
} | ||
tags["emotes-raw"] = tags["emotes"]; | ||
tags["emotes"] = emotes; | ||
} | ||
if (_.isBoolean(tags["emotes"])) { tags["emotes-raw"] = null; } | ||
// Tags are split by a semi colon.. | ||
var rawTags = data.slice(1, nextspace).split(";"); | ||
return tags; | ||
}, | ||
for (var i = 0; i < rawTags.length; i++) { | ||
// Tags delimited by an equals sign are key=value tags. | ||
// If there's no equals, we assign the tag a value of true. | ||
var tag = rawTags[i]; | ||
var pair = tag.split("="); | ||
message.tags[pair[0]] = tag.substring(tag.indexOf("=") + 1) || true; | ||
// Parse Twitch messages.. | ||
msg: function msg(data) { | ||
var message = { | ||
raw: data, | ||
tags: {}, | ||
prefix: null, | ||
command: null, | ||
params: [] | ||
} | ||
position = nextspace + 1; | ||
} | ||
// Position and nextspace are used by the parser as a reference.. | ||
var position = 0; | ||
var nextspace = 0; | ||
// Skip any trailing whitespace.. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
} | ||
// The first thing we check for is IRCv3.2 message tags. | ||
// http://ircv3.atheme.org/specification/message-tags-3.2 | ||
if (data.charCodeAt(0) === 64) { | ||
var nextspace = data.indexOf(" "); | ||
// Extract the message's prefix if present. Prefixes are prepended with a colon.. | ||
if (data.charCodeAt(position) === 58) { | ||
nextspace = data.indexOf(" ", position); | ||
// Malformed IRC message.. | ||
if (nextspace === -1) { | ||
return null; | ||
} | ||
// If there's nothing after the prefix, deem this message to be malformed. | ||
if (nextspace === -1) { | ||
return null; | ||
// Tags are split by a semi colon.. | ||
var rawTags = data.slice(1, nextspace).split(";"); | ||
for (var i = 0; i < rawTags.length; i++) { | ||
// Tags delimited by an equals sign are key=value tags. | ||
// If there's no equals, we assign the tag a value of true. | ||
var tag = rawTags[i]; | ||
var pair = tag.split("="); | ||
message.tags[pair[0]] = tag.substring(tag.indexOf("=") + 1) || true; | ||
} | ||
position = nextspace + 1; | ||
} | ||
message.prefix = data.slice(position + 1, nextspace); | ||
position = nextspace + 1; | ||
// Skip any trailing whitespace.. | ||
@@ -85,66 +111,82 @@ while (data.charCodeAt(position) === 32) { | ||
} | ||
} | ||
nextspace = data.indexOf(" ", position); | ||
// Extract the message's prefix if present. Prefixes are prepended with a colon.. | ||
if (data.charCodeAt(position) === 58) { | ||
nextspace = data.indexOf(" ", position); | ||
// If there's no more whitespace left, extract everything from the | ||
// current position to the end of the string as the command.. | ||
if (nextspace === -1) { | ||
if (data.length > position) { | ||
message.command = data.slice(position); | ||
return message; | ||
// If there's nothing after the prefix, deem this message to be malformed. | ||
if (nextspace === -1) { | ||
return null; | ||
} | ||
message.prefix = data.slice(position + 1, nextspace); | ||
position = nextspace + 1; | ||
// Skip any trailing whitespace.. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
} | ||
} | ||
return null; | ||
} | ||
nextspace = data.indexOf(" ", position); | ||
// Else, the command is the current position up to the next space. After | ||
// that, we expect some parameters. | ||
message.command = data.slice(position, nextspace); | ||
// If there's no more whitespace left, extract everything from the | ||
// current position to the end of the string as the command.. | ||
if (nextspace === -1) { | ||
if (data.length > position) { | ||
message.command = data.slice(position); | ||
return message; | ||
} | ||
position = nextspace + 1; | ||
return null; | ||
} | ||
// Skip any trailing whitespace.. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
} | ||
// Else, the command is the current position up to the next space. After | ||
// that, we expect some parameters. | ||
message.command = data.slice(position, nextspace); | ||
while (position < data.length) { | ||
nextspace = data.indexOf(" ", position); | ||
position = nextspace + 1; | ||
// If the character is a colon, we've got a trailing parameter. | ||
// At this point, there are no extra params, so we push everything | ||
// from after the colon to the end of the string, to the params array | ||
// and break out of the loop. | ||
if (data.charCodeAt(position) === 58) { | ||
message.params.push(data.slice(position + 1)); | ||
break; | ||
// Skip any trailing whitespace.. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
} | ||
// If we still have some whitespace... | ||
if (nextspace !== -1) { | ||
// Push whatever's between the current position and the next | ||
// space to the params array. | ||
message.params.push(data.slice(position, nextspace)); | ||
position = nextspace + 1; | ||
while (position < data.length) { | ||
nextspace = data.indexOf(" ", position); | ||
// Skip any trailing whitespace and continue looping. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
// If the character is a colon, we've got a trailing parameter. | ||
// At this point, there are no extra params, so we push everything | ||
// from after the colon to the end of the string, to the params array | ||
// and break out of the loop. | ||
if (data.charCodeAt(position) === 58) { | ||
message.params.push(data.slice(position + 1)); | ||
break; | ||
} | ||
continue; | ||
// If we still have some whitespace... | ||
if (nextspace !== -1) { | ||
// Push whatever's between the current position and the next | ||
// space to the params array. | ||
message.params.push(data.slice(position, nextspace)); | ||
position = nextspace + 1; | ||
// Skip any trailing whitespace and continue looping. | ||
while (data.charCodeAt(position) === 32) { | ||
position++; | ||
} | ||
continue; | ||
} | ||
// If we don't have any more whitespace and the param isn't trailing, | ||
// push everything remaining to the params array. | ||
if (nextspace === -1) { | ||
message.params.push(data.slice(position)); | ||
break; | ||
} | ||
} | ||
// If we don't have any more whitespace and the param isn't trailing, | ||
// push everything remaining to the params array. | ||
if (nextspace === -1) { | ||
message.params.push(data.slice(position)); | ||
break; | ||
} | ||
return message; | ||
} | ||
return message; | ||
} | ||
module.exports = parser; |
@@ -27,3 +27,3 @@ var self = module.exports = { | ||
// Return a random justinfan username.. | ||
justinfan: () => { return "justinfan" + Math.floor((Math.random() * 80000) + 1000); }, | ||
justinfan: () => { return `justinfan${Math.floor((Math.random() * 80000) + 1000)}`; }, | ||
@@ -73,3 +73,3 @@ // Return a valid password.. | ||
return hours + ":" + mins; | ||
return `${hours}:${mins}`; | ||
}, | ||
@@ -104,21 +104,2 @@ | ||
// Return a valid server.. | ||
server: (cluster, secure) => { | ||
var url = secure ? "irc-ws.chat.twitch.tv:443" : "irc-ws.chat.twitch.tv:80"; | ||
switch(cluster) { | ||
case "event": | ||
case "events": | ||
url = secure ? "event-ws.tmi.twitch.tv:443": "event.tmi.twitch.tv:80"; | ||
break; | ||
case "group": | ||
case "groups": | ||
url = secure ? "group-ws.tmi.twitch.tv:443": "group.tmi.twitch.tv:80"; | ||
break; | ||
case "main": | ||
url = secure ? "main-ws.tmi.twitch.tv:443": "main.tmi.twitch.tv:80"; | ||
break; | ||
} | ||
return url; | ||
}, | ||
// Split a line but don't cut a word in half.. | ||
@@ -125,0 +106,0 @@ splitLine: (input, length) => { |
{ | ||
"name": "tmi.js", | ||
"version": "1.0.0-rc1", | ||
"version": "1.0.0", | ||
"description": "Javascript library for the Twitch Messaging Interface.", | ||
@@ -50,6 +50,6 @@ "keywords": [ | ||
"type": "git", | ||
"url": "git://github.com/Schmoopiie/tmi.js.git" | ||
"url": "git://github.com/tmijs/tmi.js.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/Schmoopiie/tmi.js/issues" | ||
"url": "https://github.com/tmijs/tmi.js/issues" | ||
}, | ||
@@ -56,0 +56,0 @@ "dependencies": { |
# tmi.js | ||
[![Build Status](https://secure.travis-ci.org/Schmoopiie/tmi.js.png?branch=master)](https://travis-ci.org/Schmoopiie/tmi.js) [![Downloads](https://img.shields.io/npm/dm/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) [![Npm Version](https://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](https://img.shields.io/github/issues/Schmoopiie/tmi.js.svg?style=flat)](https://github.com/Schmoopiie/tmi.js/issues) | ||
[![Build Status](https://secure.travis-ci.org/tmijs/tmi.js.png?branch=master)](https://travis-ci.org/tmijs/tmi.js) [![Downloads](https://img.shields.io/npm/dm/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) [![Npm Version](https://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](https://img.shields.io/github/issues/tmijs/tmi.js.svg?style=flat)](https://github.com/tmijs/tmi.js/issues) | ||
@@ -34,7 +34,7 @@ ![](https://i.imgur.com/vsdO7N5.png) | ||
- Have a question that is not a bug report ? [Discuss on the tmi.js forum](http://www.tmijs.org/forums/). | ||
- Found a bug ? [Submit an issue](https://github.com/Schmoopiie/tmi.js/issues/new). | ||
- Found a bug ? [Submit an issue](https://github.com/tmijs/tmi.js/issues/new). | ||
## Contributors | ||
In order of the [most commits](https://github.com/Schmoopiie/tmi.js/graphs/contributors): | ||
In order of the [most commits](https://github.com/tmijs/tmi.js/graphs/contributors): | ||
@@ -53,2 +53,2 @@ - Schmoopiie - https://github.com/Schmoopiie | ||
Please review the [guidelines for contributing](https://github.com/Schmoopiie/tmi.js/blob/master/CONTRIBUTING.md) of the [tmi.js repository](https://github.com/Schmoopiie/tmi.js). We reserve the right to refuse a Pull Request if it does not meet the requirements. | ||
Please review the [guidelines for contributing](https://github.com/tmijs/tmi.js/blob/master/CONTRIBUTING.md) of the [tmi.js repository](https://github.com/tmijs/tmi.js). We reserve the right to refuse a Pull Request if it does not meet the requirements. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
2068
1
105040