Comparing version 1.6.0 to 1.7.0
@@ -0,4 +1,5 @@ | ||
var client = require("./lib/client"); | ||
module.exports = { | ||
client: require("./lib/client"), | ||
Client: require("./lib/client") | ||
client, | ||
Client: client | ||
}; |
@@ -1,2 +0,2 @@ | ||
var request = require("request"); | ||
var fetch = require("node-fetch"); | ||
var _ = require("./utils"); | ||
@@ -13,15 +13,34 @@ | ||
// We are inside a Node application, so we can use the request module.. | ||
// We are inside a Node application, so we can use the node-fetch module.. | ||
if(_.isNode()) { | ||
var opts = _.merge({ method: "GET", json: true }, options, { url }); | ||
request(opts, callback); | ||
var url = opts.url; | ||
if(opts.qs) { | ||
var qs = new URLSearchParams(opts.qs); | ||
url += `?${qs}`; | ||
} | ||
var response = {}; | ||
/** @type {ReturnType<import('node-fetch')['default']>} */ | ||
const fetchPromise = fetch(url, { | ||
method: opts.method, | ||
headers: opts.headers, | ||
body: opts.body | ||
}); | ||
fetchPromise.then(res => { | ||
response = { statusCode: res.status, headers: res.headers }; | ||
return opts.json ? res.json() : res.text(); | ||
}) | ||
.then( | ||
data => callback(null, response, data), | ||
err => callback(err, response, null) | ||
); | ||
} | ||
// Inside an extension -> we cannot use jsonp! | ||
else if(_.isExtension() || _.isReactNative()) { | ||
options = _.merge({ url, method: "GET", headers: {} }, options); | ||
// Web application, extension, React Native etc. | ||
else { | ||
var opts = _.merge({ method: "GET", headers: {} }, options, { url }); | ||
// prepare request | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open(options.method, options.url, true); | ||
for(var name in options.headers) { | ||
xhr.setRequestHeader(name, options.headers[name]); | ||
xhr.open(opts.method, opts.url, true); | ||
for(var name in opts.headers) { | ||
xhr.setRequestHeader(name, opts.headers[name]); | ||
} | ||
@@ -43,19 +62,4 @@ xhr.responseType = "json"; | ||
} | ||
// Inside a web application, use jsonp.. | ||
else { | ||
var script = document.createElement("script"); | ||
// Callbacks must match the regex [a-zA-Z_$][\w$]*(\.[a-zA-Z_$][\w$]*)* | ||
var callbackName = `jsonp_callback_${Math.round(100000 * Math.random())}`; | ||
window[callbackName] = function(data) { | ||
delete window[callbackName]; | ||
document.body.removeChild(script); | ||
callback(null, null, data); | ||
}; | ||
// Inject the script in the document.. | ||
script.src = `${url}${url.includes("?") ? "&" : "?"}callback=${callbackName}`; | ||
document.body.appendChild(script); | ||
} | ||
} | ||
module.exports = api; |
var api = require("./api"); | ||
var commands = require("./commands"); | ||
var eventEmitter = require("./events").EventEmitter; | ||
var EventEmitter = require("./events").EventEmitter; | ||
var logger = require("./logger"); | ||
var parse = require("./parser"); | ||
var timer = require("./timer"); | ||
var ws = global.WebSocket || global.MozWebSocket || require("ws"); | ||
var _global = typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : {}; | ||
var _WebSocket = _global.WebSocket || require("ws"); | ||
var _ = require("./utils"); | ||
@@ -13,3 +14,2 @@ | ||
if(this instanceof client === false) { return new client(opts); } | ||
this.setMaxListeners(0); | ||
@@ -23,2 +23,3 @@ this.opts = _.get(opts, {}); | ||
this.clientId = _.get(this.opts.options.clientId, null); | ||
this._globalDefaultChannel = _.channel(_.get(this.opts.options.globalDefaultChannel, "#tmijs")); | ||
@@ -35,3 +36,6 @@ this.maxReconnectAttempts = _.get(this.opts.connection.maxReconnectAttempts, Infinity); | ||
this.secure = _.get(this.opts.connection.secure, false); | ||
this.secure = _.get( | ||
this.opts.connection.secure, | ||
!this.opts.connection.server && !this.opts.connection.port | ||
); | ||
@@ -68,7 +72,18 @@ // Raw data and object for emote-sets.. | ||
eventEmitter.call(this); | ||
EventEmitter.call(this); | ||
this.setMaxListeners(0); | ||
} | ||
_.inherits(client, eventEmitter); | ||
_.inherits(client, EventEmitter); | ||
// Emit multiple events.. | ||
client.prototype.emits = function emits(types, values) { | ||
for (var i = 0; i < types.length; i++) { | ||
var val = i < values.length ? values[i] : values[values.length - 1]; | ||
this.emit.apply(this, [types[i]].concat(val)); | ||
} | ||
}; | ||
client.prototype.off = client.prototype.removeListener; | ||
client.prototype.api = api; | ||
@@ -87,3 +102,5 @@ | ||
this.emit("raw_message", JSON.parse(JSON.stringify(message)), message); | ||
if(this.listenerCount("raw_message")) { | ||
this.emit("raw_message", JSON.parse(JSON.stringify(message)), message); | ||
} | ||
@@ -95,17 +112,15 @@ var channel = _.channel(_.get(message.params[0], null)); | ||
// Parse badges, badge-info and emotes.. | ||
message.tags = parse.badges(parse.badgeInfo(parse.emotes(message.tags))); | ||
var tags = message.tags = parse.badges(parse.badgeInfo(parse.emotes(message.tags))); | ||
// Transform IRCv3 tags.. | ||
if(message.tags) { | ||
var tags = message.tags; | ||
for(var key in tags) { | ||
if(key !== "emote-sets" && key !== "ban-duration" && key !== "bits") { | ||
var value = tags[key]; | ||
if(_.isBoolean(value)) { value = null; } | ||
else if(value === "1") { value = true; } | ||
else if(value === "0") { value = false; } | ||
else if(_.isString(value)) { value = _.unescapeIRC(value); } | ||
tags[key] = value; | ||
} | ||
for(var key in tags) { | ||
if(key === "emote-sets" || key === "ban-duration" || key === "bits") { | ||
continue; | ||
} | ||
var value = tags[key]; | ||
if(_.isBoolean(value)) { value = null; } | ||
else if(value === "1") { value = true; } | ||
else if(value === "0") { value = false; } | ||
else if(_.isString(value)) { value = _.unescapeIRC(value); } | ||
tags[key] = value; | ||
} | ||
@@ -119,3 +134,3 @@ | ||
this.emit("ping"); | ||
if(!_.isNull(this.ws) && this.ws.readyState === 1) { | ||
if(this._isConnected()) { | ||
this.ws.send("PONG"); | ||
@@ -159,3 +174,3 @@ } | ||
this.log.info("Connected to server."); | ||
this.userstate["#tmijs"] = {}; | ||
this.userstate[this._globalDefaultChannel] = {}; | ||
this.emits(["connected", "_promiseConnect"], [[this.server, this.port], [null]]); | ||
@@ -168,3 +183,3 @@ this.reconnections = 0; | ||
// Make sure the connection is opened before sending the message.. | ||
if(!_.isNull(this.ws) && this.ws.readyState === 1) { | ||
if(this._isConnected()) { | ||
this.ws.send("PING"); | ||
@@ -185,4 +200,6 @@ } | ||
// Join all the channels from configuration with a 2 seconds interval.. | ||
var joinQueue = new timer.queue(2000); | ||
// Join all the channels from the config with an interval.. | ||
var joinInterval = _.get(this.opts.options.joinInterval, 2000); | ||
if(joinInterval < 300) joinInterval = 300; | ||
var joinQueue = new timer.queue(joinInterval); | ||
var joinChannels = _.union(this.opts.channels, this.channels); | ||
@@ -194,3 +211,3 @@ this.channels = []; | ||
joinQueue.add(() => { | ||
if(!_.isNull(this.ws) && this.ws.readyState === 1) { | ||
if(this._isConnected()) { | ||
this.join(channel).catch(err => this.log.error(err)); | ||
@@ -339,2 +356,3 @@ } | ||
case "bad_vip_grantee_already_vip": | ||
case "bad_vip_max_vips_reached": | ||
case "bad_vip_achievement_incomplete": | ||
@@ -539,2 +557,3 @@ this.log.info(basicLog); | ||
case "whisper_limit_per_sec": | ||
case "whisper_restricted": | ||
case "whisper_restricted_recipient": | ||
@@ -551,2 +570,3 @@ this.log.info(basicLog); | ||
case "tos_ban": | ||
case "invalid_user": | ||
this.log.info(basicLog); | ||
@@ -580,3 +600,4 @@ this.emits([ | ||
"_promiseEmoteonly", | ||
"_promiseEmoteonlyoff" | ||
"_promiseEmoteonlyoff", | ||
"_promiseWhisper" | ||
], [noticeArr, [msgid, channel]]); | ||
@@ -620,2 +641,3 @@ break; | ||
case "usage_me": | ||
case "unavailable_command": | ||
this.log.info(basicLog); | ||
@@ -654,2 +676,3 @@ this.emit("notice", channel, msgid, msg); | ||
this.log.warn(`Could not parse NOTICE from tmi.twitch.tv:\n${JSON.stringify(message, null, 4)}`); | ||
this.emit("notice", channel, msgid, msg); | ||
} | ||
@@ -662,12 +685,11 @@ break; | ||
case "USERNOTICE": | ||
var username = message.tags["display-name"] || message.tags["login"]; | ||
var plan = message.tags["msg-param-sub-plan"] || ""; | ||
var planName = _.unescapeIRC(_.get(message.tags["msg-param-sub-plan-name"], "")) || null; | ||
var username = tags["display-name"] || tags["login"]; | ||
var plan = tags["msg-param-sub-plan"] || ""; | ||
var planName = _.unescapeIRC(_.get(tags["msg-param-sub-plan-name"], "")) || null; | ||
var prime = plan.includes("Prime"); | ||
var methods = { prime, plan, planName }; | ||
var userstate = message.tags; | ||
var streakMonths = ~~(message.tags["msg-param-streak-months"] || 0); | ||
var recipient = message.tags["msg-param-recipient-display-name"] || message.tags["msg-param-recipient-user-name"]; | ||
var giftSubCount = ~~message.tags["msg-param-mass-gift-count"]; | ||
userstate["message-type"] = msgid; | ||
var streakMonths = ~~(tags["msg-param-streak-months"] || 0); | ||
var recipient = tags["msg-param-recipient-display-name"] || tags["msg-param-recipient-user-name"]; | ||
var giftSubCount = ~~tags["msg-param-mass-gift-count"]; | ||
tags["message-type"] = msgid; | ||
@@ -678,3 +700,3 @@ switch(msgid) { | ||
this.emits(["resub", "subanniversary"], [ | ||
[channel, username, streakMonths, msg, userstate, methods] | ||
[channel, username, streakMonths, msg, tags, methods] | ||
]); | ||
@@ -685,3 +707,3 @@ break; | ||
case "sub": | ||
this.emit("subscription", channel, username, methods, msg, userstate); | ||
this.emit("subscription", channel, username, methods, msg, tags); | ||
break; | ||
@@ -691,3 +713,3 @@ | ||
case "subgift": | ||
this.emit("subgift", channel, username, streakMonths, recipient, methods, userstate); | ||
this.emit("subgift", channel, username, streakMonths, recipient, methods, tags); | ||
break; | ||
@@ -698,3 +720,3 @@ | ||
case "anonsubgift": | ||
this.emit("anonsubgift", channel, streakMonths, recipient, methods, userstate); | ||
this.emit("anonsubgift", channel, streakMonths, recipient, methods, tags); | ||
break; | ||
@@ -704,3 +726,3 @@ | ||
case "submysterygift": | ||
this.emit("submysterygift", channel, username, giftSubCount, methods, userstate); | ||
this.emit("submysterygift", channel, username, giftSubCount, methods, tags); | ||
break; | ||
@@ -711,3 +733,3 @@ | ||
case "anonsubmysterygift": | ||
this.emit("anonsubmysterygift", channel, giftSubCount, methods, userstate); | ||
this.emit("anonsubmysterygift", channel, giftSubCount, methods, tags); | ||
break; | ||
@@ -717,3 +739,3 @@ | ||
case "primepaidupgrade": | ||
this.emit("primepaidupgrade", channel, username, methods, userstate); | ||
this.emit("primepaidupgrade", channel, username, methods, tags); | ||
break; | ||
@@ -723,4 +745,4 @@ | ||
case "giftpaidupgrade": | ||
var sender = message.tags["msg-param-sender-name"] || message.tags["msg-param-sender-login"]; | ||
this.emit("giftpaidupgrade", channel, username, sender, userstate); | ||
var sender = tags["msg-param-sender-name"] || tags["msg-param-sender-login"]; | ||
this.emit("giftpaidupgrade", channel, username, sender, tags); | ||
break; | ||
@@ -730,3 +752,3 @@ | ||
case "anongiftpaidupgrade": | ||
this.emit("anongiftpaidupgrade", channel, username, userstate); | ||
this.emit("anongiftpaidupgrade", channel, username, tags); | ||
break; | ||
@@ -736,6 +758,25 @@ | ||
case "raid": | ||
var username = message.tags["msg-param-displayName"] || message.tags["msg-param-login"]; | ||
var viewers = message.tags["msg-param-viewerCount"]; | ||
this.emit("raided", channel, username, viewers); | ||
var username = tags["msg-param-displayName"] || tags["msg-param-login"]; | ||
var viewers = +tags["msg-param-viewerCount"]; | ||
this.emit("raided", channel, username, viewers, tags); | ||
break; | ||
// Handle ritual | ||
case "ritual": | ||
var ritualName = tags["msg-param-ritual-name"]; | ||
switch(ritualName) { | ||
// Handle new chatter ritual | ||
case "new_chatter": | ||
this.emit("newchatter", channel, username, tags, msg); | ||
break; | ||
// All unknown rituals should be passed through | ||
default: | ||
this.emit("ritual", ritualName, channel, username, tags, msg); | ||
break; | ||
} | ||
break; | ||
// All other msgid events should be emitted under a usernotice event | ||
// until it comes up and needs to be added.. | ||
default: | ||
this.emit("usernotice", msgid, channel, tags, msg); | ||
break; | ||
} | ||
@@ -787,9 +828,8 @@ | ||
if(message.params.length > 1) { | ||
var username = message.tags["login"]; | ||
var deletedMessage = msg; | ||
var userstate = message.tags; | ||
userstate["message-type"] = "messagedeleted"; | ||
var username = tags["login"]; | ||
tags["message-type"] = "messagedeleted"; | ||
this.log.info(`[${channel}] ${username}'s message has been deleted.`); | ||
this.emit("messagedeleted", channel, username, deletedMessage, userstate); | ||
this.emit("messagedeleted", channel, username, deletedMessage, tags); | ||
} | ||
@@ -802,4 +842,4 @@ break; | ||
this.log.info(`Disconnecting and reconnecting in ${Math.round(this.reconnectTimer / 1000)} seconds..`); | ||
this.disconnect(); | ||
setTimeout(() => this.connect(), this.reconnectTimer); | ||
this.disconnect().catch(err => this.log.error(err)); | ||
setTimeout(() => this.connect().catch(err => this.log.error(err)), this.reconnectTimer); | ||
break; | ||
@@ -813,7 +853,7 @@ | ||
if(message.tags["user-type"] === "mod") { | ||
if(!this.moderators[this.lastJoined]) { | ||
this.moderators[this.lastJoined] = []; | ||
if(!this.moderators[channel]) { | ||
this.moderators[channel] = []; | ||
} | ||
if(!this.moderators[this.lastJoined].includes(this.username)) { | ||
this.moderators[this.lastJoined].push(this.username); | ||
if(!this.moderators[channel].includes(this.username)) { | ||
this.moderators[channel].push(this.username); | ||
} | ||
@@ -824,3 +864,3 @@ } | ||
if(!_.isJustinfan(this.getUsername()) && !this.userstate[channel]) { | ||
this.userstate[channel] = message.tags; | ||
this.userstate[channel] = tags; | ||
this.lastJoined = channel; | ||
@@ -837,3 +877,3 @@ this.channels.push(channel); | ||
this.userstate[channel] = message.tags; | ||
this.userstate[channel] = tags; | ||
break; | ||
@@ -843,3 +883,3 @@ | ||
case "GLOBALUSERSTATE": | ||
this.globaluserstate = message.tags; | ||
this.globaluserstate = tags; | ||
@@ -872,4 +912,4 @@ // Received emote-sets.. | ||
else { | ||
var minutes = ~~message.tags.slow; | ||
var enabled = [channel, true, minutes]; | ||
var seconds = ~~message.tags.slow; | ||
var enabled = [channel, true, seconds]; | ||
this.log.info(`[${channel}] This room is now in slow mode.`); | ||
@@ -1035,21 +1075,37 @@ this.emits(["slow", "slowmode", "_promiseSlow"], [enabled, enabled, [null]]); | ||
else { | ||
var messagesLogLevel = _.get(this.opts.options.messagesLogLevel, "info") | ||
// Message is an action (/me <message>).. | ||
var actionMessage = _.actionMessage(msg); | ||
if(actionMessage) { | ||
message.tags["message-type"] = "action"; | ||
this.log.info(`[${channel}] *<${message.tags.username}>: ${actionMessage[1]}`); | ||
this.emits(["action", "message"], [ | ||
[channel, message.tags, actionMessage[1], false] | ||
]); | ||
message.tags["message-type"] = actionMessage ? "action" : "chat"; | ||
msg = actionMessage ? actionMessage[1] : msg; | ||
// Check for Bits prior to actions message | ||
if (message.tags.hasOwnProperty("bits")) { | ||
this.emit("cheer", channel, message.tags, msg); | ||
} | ||
else { | ||
if(message.tags.hasOwnProperty("bits")) { | ||
this.emit("cheer", channel, message.tags, msg); | ||
//Handle Channel Point Redemptions (Require's Text Input) | ||
if (message.tags.hasOwnProperty("msg-id")) { | ||
if (message.tags["msg-id"] === "highlighted-message") { | ||
var rewardtype = message.tags["msg-id"]; | ||
this.emit("redeem", channel, message.tags.username, rewardtype, message.tags, msg); | ||
} | ||
else if (message.tags["msg-id"] === "skip-subs-mode-message") { | ||
var rewardtype = message.tags["msg-id"]; | ||
this.emit("redeem", channel, message.tags.username, rewardtype, message.tags, msg); | ||
} | ||
} | ||
else if (message.tags.hasOwnProperty("custom-reward-id")) { | ||
var rewardtype = message.tags["custom-reward-id"]; | ||
this.emit("redeem", channel, message.tags.username, rewardtype, message.tags, msg); | ||
} | ||
if(actionMessage) { | ||
this.log[messagesLogLevel](`[${channel}] *<${message.tags.username}>: ${msg}`); | ||
this.emits(["action", "message"], [ | ||
[channel, message.tags, msg, false] | ||
]); | ||
} | ||
// Message is a regular chat message.. | ||
else { | ||
message.tags["message-type"] = "chat"; | ||
this.log.info(`[${channel}] <${message.tags.username}>: ${msg}`); | ||
this.log[messagesLogLevel](`[${channel}] <${message.tags.username}>: ${msg}`); | ||
this.emits(["chat", "message"], [ | ||
@@ -1096,3 +1152,3 @@ [channel, message.tags, msg, false] | ||
client.prototype._openConnection = function _openConnection() { | ||
this.ws = new ws(`${this.secure ? "wss" : "ws"}://${this.server}:${this.port}/`, "irc"); | ||
this.ws = new _WebSocket(`${this.secure ? "wss" : "ws"}://${this.server}:${this.port}/`, "irc"); | ||
@@ -1130,2 +1186,5 @@ this.ws.onmessage = this._onMessage.bind(this); | ||
} | ||
else if(_.isJustinfan(this.username)) { | ||
this.ws.send("PASS SCHMOOPIIE"); | ||
} | ||
this.ws.send(`NICK ${this.username}`); | ||
@@ -1187,3 +1246,3 @@ }) | ||
this.reconnecting = false; | ||
this.connect(); | ||
this.connect().catch(err => this.log.error(err)); | ||
}, this.reconnectTimer); | ||
@@ -1228,3 +1287,3 @@ } | ||
this.reconnecting = false; | ||
this.connect(); | ||
this.connect().catch(err => this.log.error(err)); | ||
}, this.reconnectTimer); | ||
@@ -1268,3 +1327,8 @@ } | ||
} | ||
fn(resolve, reject); | ||
if(typeof fn === 'function') { | ||
fn(resolve, reject); | ||
} | ||
else { | ||
resolve(); | ||
} | ||
}); | ||
@@ -1315,2 +1379,4 @@ }; | ||
var messagesLogLevel = _.get(this.opts.options.messagesLogLevel, "info") | ||
// Message is an action (/me <message>).. | ||
@@ -1320,3 +1386,3 @@ var actionMessage = _.actionMessage(message); | ||
userstate["message-type"] = "action"; | ||
this.log.info(`[${chan}] *<${this.getUsername()}>: ${actionMessage[1]}`); | ||
this.log[messagesLogLevel](`[${chan}] *<${this.getUsername()}>: ${actionMessage[1]}`); | ||
this.emits(["action", "message"], [ | ||
@@ -1330,3 +1396,3 @@ [chan, userstate, actionMessage[1], true] | ||
userstate["message-type"] = "chat"; | ||
this.log.info(`[${chan}] <${this.getUsername()}>: ${message}`); | ||
this.log[messagesLogLevel](`[${chan}] <${this.getUsername()}>: ${message}`); | ||
this.emits(["chat", "message"], [ | ||
@@ -1336,3 +1402,8 @@ [chan, userstate, message, true] | ||
} | ||
fn(resolve, reject); | ||
if(typeof fn === 'function') { | ||
fn(resolve, reject); | ||
} | ||
else { | ||
resolve(); | ||
} | ||
}); | ||
@@ -1392,2 +1463,8 @@ }; | ||
// Determine if the client has a WebSocket and it's open.. | ||
client.prototype._isConnected = function _isConnected() { | ||
return this.ws !== null && this.ws.readyState === 1; | ||
} | ||
// Disconnect from server.. | ||
@@ -1394,0 +1471,0 @@ client.prototype.disconnect = function disconnect() { |
@@ -105,3 +105,3 @@ var _ = require("./utils"); | ||
// Send action message (/me <message>) on a channel.. | ||
action: function action(channel, message) { | ||
action(channel, message) { | ||
channel = _.channel(channel); | ||
@@ -119,3 +119,3 @@ message = `\u0001ACTION ${message}\u0001`; | ||
// Ban username on channel.. | ||
ban: function ban(channel, username, reason) { | ||
ban(channel, username, reason) { | ||
channel = _.channel(channel); | ||
@@ -136,3 +136,3 @@ username = _.username(username); | ||
// Clear all messages on a channel.. | ||
clear: function clear(channel) { | ||
clear(channel) { | ||
channel = _.channel(channel); | ||
@@ -151,3 +151,3 @@ | ||
// Change the color of your username.. | ||
color: function color(channel, newColor) { | ||
color(channel, newColor) { | ||
newColor = _.get(newColor, channel); | ||
@@ -166,3 +166,3 @@ | ||
// Run commercial on a channel for X seconds.. | ||
commercial: function commercial(channel, seconds) { | ||
commercial(channel, seconds) { | ||
channel = _.channel(channel); | ||
@@ -181,5 +181,4 @@ seconds = _.get(seconds, 30); | ||
// Delete a specific message on a channel | ||
deletemessage: function deletemessage(channel, messageUUID) { | ||
deletemessage(channel, messageUUID) { | ||
channel = _.channel(channel); | ||
@@ -198,3 +197,3 @@ | ||
// Enable emote-only mode on a channel.. | ||
emoteonly: function emoteonly(channel) { | ||
emoteonly(channel) { | ||
channel = _.channel(channel); | ||
@@ -213,3 +212,3 @@ | ||
// Disable emote-only mode on a channel.. | ||
emoteonlyoff: function emoteonlyoff(channel) { | ||
emoteonlyoff(channel) { | ||
channel = _.channel(channel); | ||
@@ -240,3 +239,3 @@ | ||
// Host a channel.. | ||
host: function host(channel, target) { | ||
host(channel, target) { | ||
channel = _.channel(channel); | ||
@@ -256,3 +255,3 @@ target = _.username(target); | ||
// Join a channel.. | ||
join: function join(channel) { | ||
join(channel) { | ||
channel = _.channel(channel); | ||
@@ -285,3 +284,3 @@ | ||
// Mod username on channel.. | ||
mod: function mod(channel, username) { | ||
mod(channel, username) { | ||
channel = _.channel(channel); | ||
@@ -301,3 +300,3 @@ username = _.username(username); | ||
// Get list of mods on a channel.. | ||
mods: function mods(channel) { | ||
mods(channel) { | ||
channel = _.channel(channel); | ||
@@ -329,3 +328,3 @@ | ||
// Send a ping to the server.. | ||
ping: function ping() { | ||
ping() { | ||
// Send the command to the server and race the Promise against a delay.. | ||
@@ -364,3 +363,3 @@ return this._sendCommand(this._getPromiseDelay(), null, "PING", (resolve, reject) => { | ||
// Send a raw message to the server.. | ||
raw: function raw(message) { | ||
raw(message) { | ||
// Send the command to the server and race the Promise against a delay.. | ||
@@ -373,3 +372,3 @@ return this._sendCommand(this._getPromiseDelay(), null, message, (resolve, reject) => { | ||
// Send a message on a channel.. | ||
say: function say(channel, message) { | ||
say(channel, message) { | ||
channel = _.channel(channel); | ||
@@ -413,3 +412,3 @@ | ||
// Enable subscribers mode on a channel.. | ||
subscribers: function subscribers(channel) { | ||
subscribers(channel) { | ||
channel = _.channel(channel); | ||
@@ -428,3 +427,3 @@ | ||
// Disable subscribers mode on a channel.. | ||
subscribersoff: function subscribersoff(channel) { | ||
subscribersoff(channel) { | ||
channel = _.channel(channel); | ||
@@ -443,3 +442,3 @@ | ||
// Timeout username on channel for X seconds.. | ||
timeout: function timeout(channel, username, seconds, reason) { | ||
timeout(channel, username, seconds, reason) { | ||
channel = _.channel(channel); | ||
@@ -467,3 +466,3 @@ username = _.username(username); | ||
// Unban username on channel.. | ||
unban: function unban(channel, username) { | ||
unban(channel, username) { | ||
channel = _.channel(channel); | ||
@@ -483,3 +482,3 @@ username = _.username(username); | ||
// End the current hosting.. | ||
unhost: function unhost(channel) { | ||
unhost(channel) { | ||
channel = _.channel(channel); | ||
@@ -498,3 +497,3 @@ | ||
// Unmod username on channel.. | ||
unmod: function unmod(channel, username) { | ||
unmod(channel, username) { | ||
channel = _.channel(channel); | ||
@@ -514,3 +513,3 @@ username = _.username(username); | ||
// Unvip username on channel.. | ||
unvip: function unvip(channel, username) { | ||
unvip(channel, username) { | ||
channel = _.channel(channel); | ||
@@ -530,3 +529,3 @@ username = _.username(username); | ||
// Add username to VIP list on channel.. | ||
vip: function vip(channel, username) { | ||
vip(channel, username) { | ||
channel = _.channel(channel); | ||
@@ -546,3 +545,3 @@ username = _.username(username); | ||
// Get list of VIPs on a channel.. | ||
vips: function vips(channel) { | ||
vips(channel) { | ||
channel = _.channel(channel); | ||
@@ -561,3 +560,3 @@ | ||
// Send an whisper message to a user.. | ||
whisper: function whisper(username, message) { | ||
whisper(username, message) { | ||
username = _.username(username); | ||
@@ -572,2 +571,12 @@ | ||
return this._sendCommand(this._getPromiseDelay(), "#tmijs", `/w ${username} ${message}`, (resolve, reject) => { | ||
this.once("_promiseWhisper", (err) => { | ||
if (err) { reject(err); } | ||
}); | ||
}).catch((err) => { | ||
// Either an "actual" error occured or the timeout triggered | ||
// the latter means no errors have occured and we can resolve | ||
// else just elevate the error | ||
if(err && typeof err === 'string' && err.indexOf("No response from Twitch.") !== 0) { | ||
throw err; | ||
} | ||
var from = _.channel(username), | ||
@@ -586,8 +595,5 @@ userstate = _.merge({ | ||
]); | ||
// At this time, there is no possible way to detect if a message has been sent has been eaten | ||
// by the server, so we can only resolve the Promise. | ||
resolve([username, message]); | ||
return [username, message]; | ||
}); | ||
} | ||
} |
@@ -24,9 +24,2 @@ /* | ||
if (!String.prototype.startsWith) { | ||
String.prototype.startsWith = function(searchString, position) { | ||
position = position || 0; | ||
return this.indexOf(searchString, position) === position; | ||
}; | ||
} | ||
function EventEmitter() { | ||
@@ -61,10 +54,2 @@ this._events = this._events || {}; | ||
// Emit multiple events.. | ||
EventEmitter.prototype.emits = function(types, values) { | ||
for (var i = 0; i < types.length; i++) { | ||
var val = i < values.length ? values[i] : values[values.length - 1]; | ||
this.emit.apply(this, [types[i]].concat(val)); | ||
} | ||
} | ||
EventEmitter.prototype.emit = function(type) { | ||
@@ -71,0 +56,0 @@ var er, handler, len, args, i, listeners; |
@@ -9,3 +9,3 @@ var _ = require("./utils"); | ||
// Return a console message depending on the logging level.. | ||
return function (message) { | ||
return function(message) { | ||
if(levels[level] >= levels[currentLevel]) { | ||
@@ -19,3 +19,3 @@ console.log(`[${_.formatDate(new Date())}] ${level}: ${message}`); | ||
// Change the current logging level.. | ||
setLevel: function(level) { | ||
setLevel(level) { | ||
currentLevel = level; | ||
@@ -22,0 +22,0 @@ }, |
@@ -62,3 +62,3 @@ /* | ||
// Parse Twitch badges.. | ||
badges: function badges(tags) { | ||
badges(tags) { | ||
return parseComplexTag(tags, "badges"); | ||
@@ -68,3 +68,3 @@ }, | ||
// Parse Twitch badge-info.. | ||
badgeInfo: function badgeInfo(tags) { | ||
badgeInfo(tags) { | ||
return parseComplexTag(tags, "badge-info"); | ||
@@ -74,3 +74,3 @@ }, | ||
// Parse Twitch emotes.. | ||
emotes: function emotes(tags) { | ||
emotes(tags) { | ||
return parseComplexTag(tags, "emotes", "/", ":", ","); | ||
@@ -80,3 +80,3 @@ }, | ||
// Parse regex emotes.. | ||
emoteRegex: function emoteRegex(msg, code, id, obj) { | ||
emoteRegex(msg, code, id, obj) { | ||
nonspaceRegex.lastIndex = 0; | ||
@@ -96,3 +96,3 @@ var regex = new RegExp("(\\b|^|\s)" + _.unescapeHtml(code) + "(\\b|$|\s)"); | ||
// Parse string emotes.. | ||
emoteString: function emoteString(msg, code, id, obj) { | ||
emoteString(msg, code, id, obj) { | ||
nonspaceRegex.lastIndex = 0; | ||
@@ -112,3 +112,3 @@ var match; | ||
// emote_id:first_index-last_index,another_first-another_last/another_emote_id:first_index-last_index | ||
transformEmotes: function transformEmotes(emotes) { | ||
transformEmotes(emotes) { | ||
var transformed = ""; | ||
@@ -127,4 +127,13 @@ | ||
formTags(tags) { | ||
var result = []; | ||
for(var key in tags) { | ||
var value = _.escapeIRC(tags[key]); | ||
result.push(`${key}=${value}`); | ||
} | ||
return `@${result.join(';')}`; | ||
}, | ||
// Parse Twitch messages.. | ||
msg: function msg(data) { | ||
msg(data) { | ||
var message = { | ||
@@ -131,0 +140,0 @@ raw: data, |
var actionMessageRegex = /^\u0001ACTION ([^\u0001]+)\u0001$/; | ||
var justinFanRegex = /^(justinfan)(\d+$)/; | ||
var unescapeIRCRegex = /\\([sn:r\\])/g; | ||
var escapeIRCRegex = /([ \n;\r\\])/g; | ||
var ircEscapedChars = { s: ' ', n: '', ':': ';', r: '' }; | ||
var ircUnescapedChars = { ' ': 's', '\n': 'n', ';': ':', '\r': 'r' }; | ||
var self = module.exports = { | ||
@@ -74,2 +76,8 @@ // Return the second value if the first value is undefined.. | ||
), | ||
escapeIRC: msg => !msg ? msg : | ||
msg.replace( | ||
escapeIRCRegex, | ||
(m, p) => p in ircUnescapedChars ? `\\${ircUnescapedChars[p]}` : p | ||
), | ||
@@ -127,18 +135,2 @@ actionMessage: msg => msg.match(actionMessageRegex), | ||
// Return whether inside a Chrome extension or not.. | ||
isExtension: () => { | ||
try { | ||
return window.chrome && chrome.runtime && chrome.runtime.id; | ||
} catch(e) {} | ||
return false; | ||
}, | ||
// Return whether inside a React Native app.. | ||
isReactNative: () => { | ||
try { | ||
return navigator && navigator.product == "ReactNative"; | ||
} catch(e) {} | ||
return false; | ||
}, | ||
// Merge two objects.. | ||
@@ -145,0 +137,0 @@ merge: Object.assign, |
{ | ||
"name": "tmi.js", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "Javascript library for the Twitch Messaging Interface.", | ||
"keywords": [ | ||
"tmi", | ||
"tmijs", | ||
"twitch", | ||
"twitch.tv", | ||
"stream", | ||
"broadcast", | ||
"chat", | ||
"message", | ||
"messaging", | ||
"interface", | ||
"subscriber", | ||
"websocket", | ||
"ws", | ||
"bot", | ||
"robot" | ||
"bot" | ||
], | ||
@@ -31,3 +30,3 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "istanbul cover node_modules/mocha/bin/_mocha -- --require should --exit", | ||
"test": "nyc mocha -- --require should --exit", | ||
"build": "npm-run-all --sequential build:*", | ||
@@ -38,3 +37,3 @@ "build:rimraf": "rimraf ./build", | ||
"build:browserify": "browserify index.js -o ./build/tmi.js", | ||
"build:uglify": "uglifyjs --compress --mangle --output ./build/tmi.min.js --source-map ./build/tmi.js.map ./build/tmi.js", | ||
"build:uglify": "uglifyjs --compress --mangle --output ./build/tmi.min.js --source-map \"filename='./build/tmi.js.map'\" ./build/tmi.js", | ||
"build:sri": "node sri.js ./build/tmi.js", | ||
@@ -52,21 +51,22 @@ "build:sri-min": "node sri.js ./build/tmi.min.js" | ||
"dependencies": { | ||
"request": "2.88.0", | ||
"ws": "6.1.3" | ||
"node-fetch": "2.6.1", | ||
"ws": "7.4.0" | ||
}, | ||
"devDependencies": { | ||
"babel-preset-es2015": "6.6.0", | ||
"babelify": "7.2.0", | ||
"browserify": "13.0.0", | ||
"hook-std": "0.2.0", | ||
"istanbul": "0.4.5", | ||
"mkdirp": "0.5.1", | ||
"mocha": "5.2.0", | ||
"npm-run-all": "1.7.0", | ||
"rimraf": "2.5.2", | ||
"should": "7.0.4", | ||
"uglify-js": "2.6.2" | ||
"@types/node-fetch": "2.5.7", | ||
"babel-preset-env": "1.7.0", | ||
"babelify": "10.0.0", | ||
"browserify": "17.0.0", | ||
"hook-std": "2.0.0", | ||
"mkdirp": "1.0.4", | ||
"mocha": "8.2.1", | ||
"npm-run-all": "4.1.5", | ||
"nyc": "15.1.0", | ||
"rimraf": "3.0.2", | ||
"should": "13.2.3", | ||
"uglify-js": "3.11.6" | ||
}, | ||
"browser": { | ||
"ws": false, | ||
"request": false | ||
"node-fetch": false | ||
}, | ||
@@ -79,3 +79,3 @@ "browserify": { | ||
"presets": [ | ||
"es2015" | ||
"env" | ||
] | ||
@@ -82,0 +82,0 @@ } |
# tmi.js | ||
[![Build Status](https://secure.travis-ci.org/tmijs/tmi.js.png?branch=master)](https://travis-ci.org/tmijs/tmi.js) | ||
![Test Workflow Status](https://github.com/tmijs/tmi.js/workflows/Test/badge.svg) | ||
[![Npm Version](https://img.shields.io/npm/v/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) | ||
@@ -13,3 +13,3 @@ [![Downloads](https://img.shields.io/npm/dm/tmi.js.svg?style=flat)](https://www.npmjs.org/package/tmi.js) | ||
[Documentation currently at tmijs/docs](https://github.com/tmijs/docs/tree/gh-pages/_posts) | | ||
Changelog on the [release page](https://github.com/tmijs/tmi.js/releases) | ||
[Changelog on the release page](https://github.com/tmijs/tmi.js/releases) | ||
@@ -21,3 +21,3 @@ ## Install | ||
```bash | ||
npm i tmi.js | ||
$ npm i tmi.js | ||
``` | ||
@@ -28,4 +28,4 @@ | ||
const client = new tmi.Client({ | ||
options: { debug: true }, | ||
connections: { | ||
options: { debug: true, messagesLogLevel: "info" }, | ||
connection: { | ||
reconnect: true, | ||
@@ -40,3 +40,3 @@ secure: true | ||
}); | ||
client.connect(); | ||
client.connect().catch(console.error); | ||
client.on('message', (channel, tags, message, self) => { | ||
@@ -57,2 +57,8 @@ if(self) return; | ||
``` | ||
```html | ||
<script> | ||
const client = new tmi.Client({ /* ... */ }); | ||
client.connect().catch(console.error); | ||
</script> | ||
``` | ||
@@ -74,3 +80,3 @@ #### Prebuilt Browser Releases | ||
```bash | ||
npm i -D @types/tmi.js | ||
$ npm i -D @types/tmi.js | ||
``` | ||
@@ -80,3 +86,3 @@ | ||
- Follow [@AlcaMagic on Twitter](https://twitter.com/AlcaMagic), [Alca on Twitch](https://www.twitch.tv/alca). | ||
- Follow [@AlcaMagic on Twitter](https://twitter.com/AlcaMagic), [Alca on Twitch](https://twitch.tv/alca). | ||
- Follow [@Schmoopiie on Twitter](https://twitter.com/Schmoopiie). | ||
@@ -83,0 +89,0 @@ - Found a bug: [submit an issue.](https://github.com/tmijs/tmi.js/issues/new) |
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
Network access
Supply chain riskThis module accesses the network.
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
93760
2454
0
93
12
1
+ Addednode-fetch@2.6.1
+ Addednode-fetch@2.6.1(transitive)
+ Addedws@7.4.0(transitive)
- Removedrequest@2.88.0
- Removedajv@6.12.6(transitive)
- Removedasn1@0.2.6(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedasync-limiter@1.0.1(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedaws-sign2@0.7.0(transitive)
- Removedaws4@1.13.2(transitive)
- Removedbcrypt-pbkdf@1.0.2(transitive)
- Removedcaseless@0.12.0(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcore-util-is@1.0.2(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedecc-jsbn@0.1.2(transitive)
- Removedextend@3.0.2(transitive)
- Removedextsprintf@1.3.0(transitive)
- Removedfast-deep-equal@3.1.3(transitive)
- Removedfast-json-stable-stringify@2.1.0(transitive)
- Removedforever-agent@0.6.1(transitive)
- Removedform-data@2.3.3(transitive)
- Removedgetpass@0.1.7(transitive)
- Removedhar-schema@2.0.0(transitive)
- Removedhar-validator@5.1.5(transitive)
- Removedhttp-signature@1.2.0(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedisstream@0.1.2(transitive)
- Removedjsbn@0.1.1(transitive)
- Removedjson-schema@0.4.0(transitive)
- Removedjson-schema-traverse@0.4.1(transitive)
- Removedjson-stringify-safe@5.0.1(transitive)
- Removedjsprim@1.4.2(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedoauth-sign@0.9.0(transitive)
- Removedperformance-now@2.1.0(transitive)
- Removedpsl@1.10.0(transitive)
- Removedpunycode@1.4.12.3.1(transitive)
- Removedqs@6.5.3(transitive)
- Removedrequest@2.88.0(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsshpk@1.18.0(transitive)
- Removedtough-cookie@2.4.3(transitive)
- Removedtunnel-agent@0.6.0(transitive)
- Removedtweetnacl@0.14.5(transitive)
- Removeduri-js@4.4.1(transitive)
- Removeduuid@3.4.0(transitive)
- Removedverror@1.10.0(transitive)
- Removedws@6.1.3(transitive)
Updatedws@7.4.0