From 8eed071c21863696d77c92b5ba21aa851a0f2890 Mon Sep 17 00:00:00 2001 From: Austin Peterson Date: Tue, 10 May 2016 16:04:51 -0500 Subject: [PATCH] First commit. --- .gitignore | 42 ++++++++++++++ .npmignore | 42 ++++++++++++++ README.md | 33 +++++++++++ main.js | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 30 ++++++++++ plugin.json | 6 ++ 6 files changed, 313 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 README.md create mode 100644 main.js create mode 100644 package.json create mode 100644 plugin.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d620ef1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# Logs +!logs +logs/*.log +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# VS Code +.vscode + +config.json +certs +*.crt +*.key diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..d620ef1 --- /dev/null +++ b/.npmignore @@ -0,0 +1,42 @@ +# Logs +!logs +logs/*.log +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# VS Code +.vscode + +config.json +certs +*.crt +*.key diff --git a/README.md b/README.md new file mode 100644 index 0000000..16e1443 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +This plugin allows AKP48Squared to connect to Twitch chat servers. + +# Installation + +This plugin is included by default on new installations of AKP48Squared. No further installation is needed. + +# Config + +You'll need to add a new server to your configuration file for each Twitch bot you want to run. An example is shown below. + +``` +"servers": [ + { + "plugin": "twitch", + "config": { + "connection": { + "reconnect": true // should we reconnect automatically? + }, + "identity": { + "username": "twitch_bot", // See https://help.twitch.tv/customer/portal/articles/1302780-twitch-irc for more info. + "password": "oauth:we2sm49se21ln6tfkxzvbco7z8uv1s" + }, + "channels": [ + "#mytwitchchannel" + ] + } + } +] +``` + +# Issues + +If you come across any issues, you can report them on this GitHub repo [here](https://github.com/AKP48Squared/akp48-plugin-discord-server/issues). diff --git a/main.js b/main.js new file mode 100644 index 0000000..ea9db56 --- /dev/null +++ b/main.js @@ -0,0 +1,160 @@ +'use strict'; +const ServerConnectorPlugin = require('../../lib/ServerConnectorPlugin'); +const irc = require('tmi.js'); + +class Twitch extends ServerConnectorPlugin { + constructor(config, id, AKP48, persistentObjects) { + super('Twitch', AKP48); + this._id = id; + this._config = config; + this._defaultCommandDelimiters = ['!', '.']; + var self = this; + if(!config || !config.channels) { + global.logger.error(`${self._pluginName}|${self._id}: Required channels option missing from config!`); + this._error = true; + return; + } + + if(!config.identity || !config.identity.username || !config.identity.password) { + global.logger.warn(`${self._pluginName}|${self._id}: No complete identity found in config! Are you sure this is what you want?`); + } + + if(persistentObjects) { + this._client = persistentObjects.client; + this._client.removeAllListeners('chat'); + this._client.removeAllListeners('whisper'); + this._client.removeAllListeners('connecting'); + this._client.removeAllListeners('connected'); + this._client.removeAllListeners('disconnected'); + this._connected = true; + } else { + this._client = new irc.client(config); + } + + this._client.on('chat', function(to, user, text, sentFromSelf) { + if(to === self._client.getUsername()) { to = user.username; } + if(!sentFromSelf) { + self._AKP48.onMessage(text, self.createContexts(to, user, text)); + } + }); + + this._client.on('whisper', function(user, text) { + var to = user.username; + self._AKP48.onMessage(text, self.createContexts(to, user, text, true)); + }); + + this._client.on('connecting', function(address, port) { + global.logger.verbose(`${self._pluginName}|${self._id}: Connecting to ${address}:${port}.`); + }); + + this._client.on('connected', function(address, port) { + global.logger.debug(`${self._pluginName}|${self._id}: Connected to ${address}:${port}.`); + self._AKP48.emit('serverConnect', self._id, self); + }); + + this._client.on('disconnected', function(reason) { + global.logger.debug(`${self._pluginName}|${self._id}: Disconnected from server for "${reason}".`); + }); + + this._client.on('error', function(message) { + global.logger.error(`${self._pluginName}|${self._id}: Error received from ${message.server}! ${message.command}: ${message.args}`); + }); + + this._AKP48.on('msg_'+this._id, function(to, message, context) { + if(!context.noPrefix) {message = `@${context.nick}: ${message}`;} + if(context.isWhisper) { + self._client.whisper(to, message); + } else { + self._client.say(to, message); + } + self._AKP48.sentMessage(to, message, context); + }); + + this._AKP48.on('emote_'+this._id, function(to, message, context) { + self._client.action(to, message); + self._AKP48.sentMessage(to, message, context); + }); + } + + connect() { + if(this._error) { + global.logger.error(`${this._pluginName}|${this._id}: Cannot connect. Check log for errors.`); + return; + } + if(this._connected) { + global.logger.debug(`${this._pluginName}|${this._id}: Using previous connection.`); + this._connected = false; + } else { + this._client.connect(); + } + } + + disconnect(msg) { + if(this._error) { + global.logger.error(`${this._pluginName}|${this._id}: Cannot disconnect. Check log for errors.`); + return; + } + this._client.disconnect(msg || 'Goodbye.'); + } +} + +Twitch.prototype.createContexts = function (to, user, text, isWhisper) { + var textArray = text.split(/[^\\]\|/); + var ctxs = []; + + for (var i = 0; i < textArray.length; i++) { + textArray[i] = textArray[i].trim(); + var delimiterLength = this.isTextACommand(textArray[i], to); + if(delimiterLength) { + textArray[i] = textArray[i].slice(delimiterLength).trim(); + } + + var ctx = { + rawMessage: text, + nick: user['display-name'], + user: user.username, + rawText: text, + text: textArray[i].trim(), + to: to, + myNick: this._client.getUsername(), + instanceId: this._id, + instanceType: 'twitch', + instance: this, + isCmd: delimiterLength ? true : false + }; + + if(isWhisper) { + ctx.isWhisper = true; + } + + ctxs.push(ctx); + } + + ctxs[ctxs.length-1].last = true; + + return ctxs; +}; + +Twitch.prototype.getChannelConfig = function (channel) { + if(!this._config.chanConfig) {return {};} + return this._config.chanConfig[channel] || {}; +}; + +Twitch.prototype.isTextACommand = function (text, channel) { + var delimit = this.getChannelConfig(channel).commandDelimiters || this._config.commandDelimiters || this._defaultCommandDelimiters; + for (var i = 0; i < delimit.length; i++) { + if(text.toLowerCase().startsWith(delimit[i].toLowerCase())) { + return delimit[i].length; + } + } + + return false; +}; + +Twitch.prototype.getPersistentObjects = function () { + return { + client: this._client + }; +}; + +module.exports = Twitch; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a3e593e --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "akp48-plugin-twitch", + "version": "1.0.0", + "description": "Twitch plugin for AKP48.", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "preversion": "git checkout master && git pull && npm ls", + "publish-patch": "npm run preversion && npm version patch && git push origin master --tags && npm publish", + "publish-minor": "npm run preversion && npm version minor && git push origin master --tags && npm publish", + "publish-major": "npm run preversion && npm version major && git push origin master --tags && npm publish" + }, + "author": "Austin Peterson (http://akpwebdesign.com)", + "contributors": [ + "Alan H (Feildmaster) (http://feildmaster.com/)", + "Austin Peterson (http://akpwebdesign.com/)" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/AKP48Squared/twitch-server.git" + }, + "bugs": { + "url": "https://github.com/AKP48Squared/twitch-server/issues" + }, + "homepage": "https://github.com/AKP48Squared/twitch-server", + "dependencies": { + "tmi.js": "0.0.29" + } +} diff --git a/plugin.json b/plugin.json new file mode 100644 index 0000000..d8ecd70 --- /dev/null +++ b/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "twitch", + "type": "ServerConnector", + "description": "Used to connect AKP48 to Twitch IRC.", + "main": "main.js" +}