From 207f05fa74db5354133e851dea5a4fa2a33673b8 Mon Sep 17 00:00:00 2001 From: Space_Inteprise Date: Sun, 22 Nov 2020 22:23:08 -0300 Subject: [PATCH] Prepare Release --- src/client/Client.js | 109 +++++++++++++++++++++++++++++++- src/sleept/SLEEPTMethods.js | 123 +++++++++++++++++++++++++++--------- 2 files changed, 200 insertions(+), 32 deletions(-) diff --git a/src/client/Client.js b/src/client/Client.js index f3a7fe1..cf65ac9 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -3,7 +3,8 @@ const SLEEPTManager = require('../sleept/SLEEPTMananger'); const { autoEndLog, constants, logger, Util } = require('../utils'); /** - * @TODO fanMode (Anonymous mode) + * @TODO FanMode (Anonymous mode). + * @TODO Organize annotations. */ /** @@ -31,6 +32,10 @@ class Client extends EventEmmiter { option: this.options } }; + global.twitchApis.client.methods = { + joinQueueTimeout: [], + leaveQueueTimeout: [], + }; /** * Active Debug if Debug have to be activate @@ -129,6 +134,108 @@ class Client extends EventEmmiter { return this.sleept.methods.leave(channelName); } + /** + * emit a event from client level + * @param {string} event the name of the event than will be sended + * @param {any} args the args of the event + * @example + * client.eventEmmiter('event', Args) + */ + eventEmmiter(event, ...args) { + switch (event) { + case 'message': + var responseMessage = { + /** + * @returns {string} text content of message + */ + toString: () => { + return args[0].params[1].toString(); + }, + /** + * @type {string} The string of context text of message + */ + content: args[0].params[1].toString(), + /** + * responds the author of message + * @param {string} [message] the message than will be sended as reply of original message + * @return {promise} The message sended metadata + */ + reply: (message) => { + return this.sleept.methods.sendMessage(args[0].params[0], `@${args[0].prefix.slice(0,args[0].prefix.indexOf('!'))} ${message}`); + }, + channel: { + /** + * @type {string} the channel name without the hashtag + */ + name: args[0].params[0].slice(0), + /** + * send a message on the same channel who send it + * @param {string} [message] the message than will be sended on the channel + * @return {promise} The message sended metadata + */ + send: (...message) => { + return this.sleept.methods.sendMessage(args[0].params[0], message); + } + }, + author: { + /** + * @type {string} the name of the sender of message (channelname without hashtag) + */ + username: args[0].prefix.slice(0,args[0].prefix.indexOf('!')), + /** + * @type {string} the display name of the sender of message (can includes spaces symbols and captal letters) + */ + displayName: args[0].tags['display-name'], + /** + * @type {boolean} if the sender of message is the bot itself + */ + self: args[0].prefix.slice(0,args[0].prefix.indexOf('!')) === this.options.userName, + /** + * @type {string} id of author (on twitch? maybe) + */ + id: args[0].tags.id, + /** + * @type {boolean} if the user who send the message have mod on that channel + */ + mod: args[0].tags.mod === '1', + /** + * @type {boolean} if the user who send the message is subscribed on the channel + */ + subscriber: args[0].tags.subscriber >= '1', + /** + * @type {boolean} if the user who send the message have Twitch turbo + */ + turbo: args[0].tags.turbo >= '1', + /** + * @type {string} the id of user (on the chat? maybe) + */ + userId: args[0].tags['user-id'], + /** + * @type {string} the user color on the chat (on hex) + */ + color: args[0].tags.color, + /** + * @type {boolean} if the user have any badge on this channel + */ + containsBadge: args[0].tags['badge-info'], + /** + * @type {string} all badges the user have in this channel (not parsed, maybe in future) + */ + badges: args[0].tags.badges, + /** + * @type {boolean} if the user who send the message is the broadcaster + */ + broadcaster: args[0].tags['badge-info'] ? args[0].tags.badges.includes('broadcaster') : false, + } + }; + this.emit(event, responseMessage); + break; + default: + this.emit(event, args); + break; + } + } + /** * Validates the client options. * @param {ClientOptions} [options=this.options] Options to validate diff --git a/src/sleept/SLEEPTMethods.js b/src/sleept/SLEEPTMethods.js index c032f9f..7f06bb0 100644 --- a/src/sleept/SLEEPTMethods.js +++ b/src/sleept/SLEEPTMethods.js @@ -20,19 +20,19 @@ class SLEEPTMethods { return this.ws !== null && this.ws.readyState === 1; } - login(UserName, token) { + login(userName, token) { // eslint-disable-next-line no-unused-vars return new Promise((resolve, reject) => { if (!token || typeof token !== 'string' || !token.startsWith('oauth:') || token.includes(' ')) { reject(constants.errors.INVALID_TOKEN); logger.fatal(constants.errors.INVALID_TOKEN); } - if (!UserName || typeof UserName !== 'string' || UserName.includes(' ')) { + if (!userName || typeof userName !== 'string' || userName.includes(' ')) { reject(constants.errors.INVALID_USERNAME); logger.fatal(constants.errors.INVALID_USERNAME); } this.client.token = token; - this.UserName = UserName; + this.userName = userName; this.server = global.twitchApis.client.option.http.host; this.ws = new WebSocket(`wss://${this.server}:443`); this.ws.onmessage = this.onMessage.bind(this); @@ -71,17 +71,17 @@ class SLEEPTMethods { logger.debug('Sending Password...'); this.ws.send(`PASS ${token}`); logger.debug('Sending Nickname...'); - this.ws.send(`NICK ${this.UserName.toLowerCase()}`); + this.ws.send(`NICK ${this.userName.toLowerCase()}`); } - handlerMessage(MessageObject) { - if (MessageObject === null) { + handlerMessage(messageObject) { + if (messageObject === null) { return; } // Message Without prefix - if (MessageObject.prefix === null) { - switch (MessageObject.command) { + if (messageObject.prefix === null) { + switch (messageObject.command) { // Ping case 'PING': this.ws.send('PONG'); @@ -95,8 +95,8 @@ class SLEEPTMethods { default: break; } - } else if (MessageObject.prefix === 'tmi.twitch.tv') { - switch (MessageObject.command) { + } else if (messageObject.prefix === 'tmi.twitch.tv') { + switch (messageObject.command) { case '002': case '003': case '004': @@ -106,7 +106,7 @@ class SLEEPTMethods { break; case '001': - this.UserName = MessageObject.params[0]; + this.userName = messageObject.params[0]; break; case '372': @@ -129,13 +129,24 @@ class SLEEPTMethods { }, 9999); }, 60000); break; + default: break; } + } else if (messageObject.prefix === this.userName + '.tmi.twitch.tv') { + switch (messageObject.command) { + case '353': + this.client.eventEmmiter('Method.Joined.' + messageObject.params[2]); + break; + } } else { - switch (MessageObject.command) { + switch (messageObject.command) { + case 'PART': + this.client.eventEmmiter('Method.Leaved.' + messageObject.params[0]); + break; case 'PRIVMSG': - logger.debug(MessageObject.params[0] + '| ' + MessageObject.prefix.slice(0,MessageObject.prefix.indexOf('!')) + ': ' + MessageObject.params[1]); + this.client.eventEmmiter('message', messageObject); + logger.debug(messageObject.params[0] + '| ' + messageObject.prefix.slice(0,messageObject.prefix.indexOf('!')) + ': ' + messageObject.params[1]); break; } } @@ -144,30 +155,80 @@ class SLEEPTMethods { onConnected() { // Once connected connect the user to the servers he parsed on client inicialization global.twitchApis.client.option.channels.forEach((element, index) => { - this.join(element, index); + setTimeout(()=>{ + this.join(element, index); + }, index * 100); }); } join(channel, index) { - if (channel.includes(' ')) { - return logger.error('Channel name cannot include spaces: ' + channel + (index ? ', on channels list index: ' + index : '')); - } - if (!channel.startsWith('#')) { - channel = '#' + channel; - } - this.ws.send(`JOIN ${channel.toLowerCase()}`); - logger.info('Entering on: ' + channel.toLowerCase()); + return new Promise((resolve, reject) => { + if (channel.includes(' ')) { + logger.error('Channel name cannot include spaces: ' + channel + (index ? ', on channels list index: ' + index : '')); + return reject('Channel name cannot include spaces: ' + channel + (index ? ', on channels list index: ' + index : '')); + } + if (!channel.startsWith('#')) { + channel = '#' + channel; + } + this.ws.send(`JOIN ${channel.toLowerCase()}`); + logger.debug('Connecting to: ' + channel.toLowerCase()); + this.client.on('Method.Joined.' + channel.toLowerCase(), listener); + global.twitchApis.client.methods.joinQueueTimeout.push([setTimeout(()=> { + reject('Couldn\'t connect with twitch'); + }, 10000), channel.toLowerCase()]); + function listener() { + logger.debug('Connected to: ' + channel.toLowerCase()); + global.twitchApis.client.methods.joinQueueTimeout.forEach((element) => { + if (element[1] === channel.toLowerCase()) { + clearTimeout(element[0]); + return; + } + }); + this.removeListener('Method.Joined.' + channel.toLowerCase(), listener); + resolve(); + } + }); } - leave(channel, index) { - if (channel.includes(' ')) { - return logger.error('Channel name cannot include spaces: ' + channel + (index ? ', on channels list index: ' + index : '')); - } - if (!channel.startsWith('#')) { - channel = '#' + channel; - } - this.ws.send(`PART ${channel.toLowerCase()}`); - logger.info('Exiting of: ' + channel.toLowerCase()); + leave(channel) { + return new Promise((resolve, reject) => { + if (channel.includes(' ')) { + logger.error('Channel name cannot include spaces: ' + channel); + return reject('Channel name cannot include spaces: ' + channel); + } + if (!channel.startsWith('#')) { + channel = '#' + channel; + } + this.ws.send(`PART ${channel.toLowerCase()}`); + logger.debug('Disconnecting from: ' + channel.toLowerCase()); + this.client.on('Method.Leaved.' + channel.toLowerCase(), listener); + global.twitchApis.client.methods.leaveQueueTimeout.push([setTimeout(()=> { + logger.fatal('Couldn\'t connect with twitch'); + reject('Couldn\'t connect with twitch'); + }, 10000), channel.toLowerCase()]); + function listener() { + logger.debug('Disconnected from: ' + channel.toLowerCase()); + global.twitchApis.client.methods.leaveQueueTimeout.forEach((element) => { + if (element[1] === channel.toLowerCase()) { + clearTimeout(element[0]); + return; + } + }); + this.removeListener('Method.Leaved.' + channel.toLowerCase(), listener); + resolve(); + } + }); + } + + sendMessage(channel, ...message) { + return new Promise((resolve, reject) => { + if (!message || message === null || (typeof message === 'object' && message[0] === null)) { + logger.warn('Cannot send empty messages'); + reject('Cannot send empty messages'); + } + message = message.join(' '); + resolve(this.ws.send(`PRIVMSG ${channel} :${message}`)); + }); } /*