Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tmi.js

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tmi.js - npm Package Compare versions

Comparing version 1.0.0-rc1 to 1.0.0

4

lib/api.js

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc