├── .gitignore ├── example-config.json ├── .eslintrc.cjs ├── package.json ├── src ├── client.js └── utils.js ├── README.md ├── index.js └── colors.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | 5 | config.json 6 | /node_modules -------------------------------------------------------------------------------- /example-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "123456789", 3 | "username": "your_username", 4 | "access_token": "https://twitchtokengenerator.com/", 5 | "helix_id": "https://twitchtokengenerator.com/", 6 | "channels": [ 7 | "your_channel", 8 | "forsen", 9 | "xqc" 10 | ], 11 | "whitelist_channels": [ 12 | "your_channel" 13 | ], 14 | "color_set": "twitch_basic" 15 | } -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | commonjs: false, 7 | es2021: true 8 | }, 9 | extends: 'airbnb-base/legacy', 10 | overrides: [], 11 | parser: "@babel/eslint-parser", 12 | parserOptions: { 13 | requireConfigFile: false, 14 | babelOptions: { 15 | plugins: [ 16 | '@babel/plugin-syntax-import-assertions' 17 | ], 18 | }, 19 | ecmaVersion: 2023, 20 | sourceType: 'module' 21 | }, 22 | rules: { 23 | "no-console": "off", 24 | "await-in-loop": "off", 25 | "no-restricted-syntax": "off", 26 | "consistent-return": "off", 27 | "no-promise-executor-return": "off", 28 | "no-await-in-loop": "off", 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colorchanger", 3 | "type": "module", 4 | "scripts": { 5 | "start": "node --no-warnings index.js", 6 | "dev": "eslint ./ & nodemon index.js", 7 | "lint": "eslint ./ & npx depcheck", 8 | "lint:fix": "eslint ./ --fix" 9 | }, 10 | "version": "1.0.0", 11 | "description": "Change twitch chat color using Twitch API", 12 | "main": "index.js", 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@babel/eslint-parser": "^7.22.15", 17 | "@kararty/dank-twitch-irc": "^6.0.0", 18 | "eslint": "^8.50.0", 19 | "eslint-config-airbnb-base": "^15.0.0", 20 | "eslint-plugin-import": "^2.28.1" 21 | }, 22 | "devDependencies": { 23 | "@babel/plugin-syntax-import-assertions": "^7.26.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import config from '../config.json' with { type: 'json' }; 2 | import { 3 | ChatClient, 4 | AlternateMessageModifier, 5 | SlowModeRateLimiter 6 | } from '@kararty/dank-twitch-irc'; 7 | import { sleep } from './utils.js'; 8 | 9 | if (!config.username || !config.access_token) { 10 | throw new Error('Missing username or access_token in config.json'); 11 | } 12 | 13 | /** 14 | * @typedef {import('@kararty/dank-twitch-irc').ChatClient} ChatClient 15 | */ 16 | const client = new ChatClient({ 17 | username: config.username.toLowerCase(), 18 | password: config.access_token, 19 | ignoreUnhandledPromiseRejections: true, 20 | rateLimits: 'default' // 'default or 'verifiedBot' 21 | }); 22 | 23 | client.use(new AlternateMessageModifier(client)); 24 | client.use(new SlowModeRateLimiter(client, 10)); 25 | client.connect(); 26 | 27 | client.on('error', (err) => console.error(err.message)); 28 | 29 | /** 30 | * Notifications of JOINs sent to any connected client, upon successful 31 | * joining of a channel. 32 | * 33 | * @typedef {import('@kararty/dank-twitch-irc').JoinMessage} JoinMessage 34 | */ 35 | client.on('JOIN', (msg) => console.log(`Joined #${msg.channelName}`)); 36 | 37 | /** 38 | * Notifications of PARTs sent to any connected client, upon parting 39 | * of a channel. 40 | * 41 | * @typedef {import('@kararty/dank-twitch-irc').PartMessage} PartMessage 42 | */ 43 | client.on('PART', (msg) => console.log(`Parted #${msg.channelName}`)); 44 | 45 | async function joinChannels() { 46 | if (!config.channels?.length) { 47 | console.log('No channels to join in config.json'); 48 | } 49 | 50 | for (const channel of config.channels) { 51 | await sleep(500).then(() => client.join(channel.toLowerCase())); 52 | } 53 | } 54 | 55 | client.on('ready', () => { 56 | console.log('Connected to Twitch'); 57 | return joinChannels(); 58 | }); 59 | 60 | export { client }; 61 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import config from '../config.json' with { type: 'json' }; 2 | import colors from '../colors.json' with { type: 'json' }; 3 | 4 | const hexToColor = { 5 | '#0000FF': 'blue', 6 | '#8A2BE2': 'blue_violet', 7 | '#5F9EA0': 'cadet_blue', 8 | '#D2691E': 'chocolate', 9 | '#FF7F50': 'coral', 10 | '#1E90FF': 'dodger_blue', 11 | '#B22222': 'firebrick', 12 | '#DAA520': 'golden_rod', 13 | '#008000': 'green', 14 | '#FF69B4': 'hot_pink', 15 | '#FF4500': 'orange_red', 16 | '#FF0000': 'red', 17 | '#2E8B57': 'sea_green', 18 | '#00FF7F': 'spring_green', 19 | '#9ACD32': 'yellow_green' 20 | }; 21 | 22 | let cooldown = false; 23 | // listen for your messages to trigger next color change 24 | export const changeColor = async (colorRaw) => { 25 | if (cooldown) return; 26 | cooldown = true; 27 | const 28 | hexColor = config.color_set === 'twitch_basic' 29 | ? hexToColor[colorRaw] || config.color_set[0] 30 | : colorRaw; 31 | const colorArray = colors[config.color_set]; 32 | const colorIndex = colorArray.findIndex((setColor) => setColor === hexColor); 33 | const finalIndex = colorIndex !== -1 ? colorIndex : 0; 34 | const newColor = colorArray[(finalIndex + 1) % colorArray.length]; 35 | 36 | try { 37 | const 38 | encodedColor = encodeURIComponent(newColor === null ? '#FF69B4' : newColor); 39 | const options = { 40 | method: 'PUT', 41 | headers: { 42 | 'Client-ID': config.helix_id, 43 | authorization: `Bearer ${config.access_token}` 44 | } 45 | }; 46 | const url = `chat/color?user_id=${config.id}&color=${encodedColor}`; 47 | const response = await fetch('https://api.twitch.tv/helix/' + url, options); 48 | 49 | // Return error to console if unsuccessful 50 | if (response.status !== 204) { 51 | console.error(await response.json()); 52 | } 53 | } catch (e) { 54 | console.error(e); 55 | } finally { 56 | cooldown = false; 57 | } 58 | }; 59 | 60 | export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # basicTwitchBot 2 | 3 | A tiny personal chatbot to change your color every message. 4 | 5 | Something of a tutorial for using Javascript with @kararty/dank-twitch-irc 6 | 7 | ## Prerequisites 8 | 9 | Before you begin, ensure you have the following installed: 10 | 11 | - [Node.js](https://nodejs.org/) (LTS version recommended, >16.14.0 required) 12 | - [git](https://github.com/git-guides/install-git) 13 | 14 | ## Installation 15 | 16 | Open Command Prompt, Windows Powershell, or a similar terminal to enter the following commands to install the bot: 17 | 18 | 1. **Clone the Repository**: Start by cloning the bot's repository to your local machine. 19 | 20 | ```bash 21 | git clone https://github.com/RyanPotat/basicTwitchBot.git 22 | ``` 23 | 24 | 2. **Install Dependencies**: Install the required Node.js packages by running: 25 | 26 | ```bash 27 | npm install 28 | ``` 29 | 30 | ## Configuration 31 | 32 | You'll need to configure the bot before you can use it. Open `example-config.json` and then enter the following: 33 | 34 | - `id` - Your Twitch user ID. 35 | - `username` - Your Twitch user login. 36 | - `helix_id` and `access_token` - [Twitch Helix API Access](https://twitchtokengenerator.com/) to generate your Twitch Helix API access token and client ID. 37 | - `channels` - A list of channels to join. 38 | - `whitelist_channels` - A list of channels whitelisted to use commands. 39 | - `color_set` - Choose from the `colors.json` list to find a color set to your liking, or create your own unique set of colors. Defaults to the standard Twitch colors. Keep in mind you must have Turbo or Prime to use anything other than the standard Twitch colors. 40 | 41 | Available color sets: 42 | 1. twitch_basic 43 | 2. pastels 44 | 3. longPastels 45 | 4. rainbow 46 | 5. longRainbow 47 | 6. earthTones 48 | 7. neon 49 | 8. grayscale 50 | 9. coolBlues 51 | 10. warmReds 52 | 11. vibrantGreens 53 | 12. deepPurples 54 | 13. candyShop 55 | 14. sunsetShades 56 | 15. autumnTones 57 | 16. oceanBlues 58 | 17. burger 59 | 18. christmas 60 | 19. halloween 61 | 62 | Now, just rename `example-config.json` to `config.json` 63 | 64 | You could do this from command line with: 65 | 66 | Windows: 67 | ```bash 68 | copy example-config.json config.json 69 | ``` 70 | 71 | Linux: 72 | ```bash 73 | cp example-config.json config.json 74 | ``` 75 | 76 | ## Usage 77 | 78 | Once the installation and configuration are complete, you can start using the bot. Run the following command: 79 | 80 | ```bash 81 | npm start 82 | ``` 83 | Optionally you can use PM2 to run this process in the background, here's some basic instructions to set it up. First, run this code to install: 84 | 85 | ```bash 86 | npm install -g pm2 87 | ``` 88 | 89 | Now if you are still in the same directory as the repository you just cloned, simply run: 90 | 91 | ```bash 92 | pm2 start index.js 93 | ``` 94 | 95 | You can check the status of the application by running: 96 | 97 | ```bash 98 | pm2 list 99 | ``` 100 | 101 | And check logs with: 102 | 103 | ```bash 104 | pm2 logs 105 | ``` 106 | 107 | To stop the application: 108 | 109 | ```bash 110 | pm2 stop index.js 111 | ``` 112 | 113 | ## Formatting: 114 | 115 | If you've made changes and want to reformat the code, you can run lint with: 116 | 117 | ```bash 118 | npm run lint:fix 119 | ``` 120 | 121 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { client } from './src/client.js'; 2 | import { changeColor } from './src/utils.js'; 3 | import config from './config.json' with { type: 'json' }; 4 | 5 | /** 6 | * @typedef {import('@kararty/dank-twitch-irc').PrivmsgMessage} PrivmsgMessage 7 | * 8 | * Reference of common properties you might want to use, not all. 9 | * @property {string} msg.channelID - - Twitch id where the message was sent. 10 | * @property {string} msg.channelName - Twitch login where the message was sent. 11 | * @property {string} msg.colorRaw - The chat color of the user. 12 | * @property {string} msg.displayName - Twitch user's display name. 13 | * @property {string} msg.messageID - The message id, used for reply threads. 14 | * @property {string} msg.messageText - The actual message. 15 | * @property {string} msg.senderUserID - Twitch id of message sender. 16 | * @property {string} msg.senderUsername - Twitch login of message sender. 17 | * 18 | * Further documentation: 19 | * https://kararty.github.io/dank-twitch-irc/ 20 | */ 21 | 22 | /** 23 | * Twitch event listener for standard chat messages in joined chats. 24 | * @param {PrivmsgMessage} msg - The message object. 25 | */ 26 | client.on('PRIVMSG', async (msg) => { 27 | // Change colors when you send a message 28 | if (msg.senderUserID === config.id) changeColor(msg.colorRaw); 29 | 30 | // Basic chatbot response example 31 | if ( 32 | msg.channelName === 'channel_to_farm_in' && 33 | msg.senderUsername === 'DeepDankDungeonBot' 34 | ) { 35 | return client.say(msg.channelName, '+join'); 36 | } 37 | 38 | // Ignore messages from non-whitelisted channels, to run commands 39 | if (!config.whitelist_channels.includes(msg.senderUsername.toLowerCase())) { 40 | return; 41 | } 42 | 43 | // Example possible usage for a command 44 | if (msg.messageText.startsWith('!info')) { 45 | // Get arguments from message (all words split by spaces) 46 | const args = msg.messageText 47 | .slice('!info'.length) // <- slice() removes the first n characters from a string 48 | .trim() // <- trim() removes whitespace from both ends of a string 49 | .split(' '); // <- split() splits a string into an array of substrings 50 | 51 | // If there are no arguments, (no values in the args array), return an error message 52 | if (!args.length) { 53 | return client.say( 54 | msg.channelName, 55 | 'No channel provided, please provide a channel to get info for.' 56 | ); 57 | } 58 | 59 | // Perform some action on those args, such as: 60 | const response = await fetch( 61 | `https://api.ivr.fi/v2/twitch/user?login=${encodeURIComponent(args[0])}` 62 | ); 63 | 64 | // Return error to console if unsuccessful 65 | if (!response.ok) { 66 | console.error(`HTTP error! status: ${response.status}`); 67 | } 68 | 69 | // Parse the response as JSON 70 | const data = await response.json(); 71 | 72 | // If the response is empty, return an error message 73 | if (!data.length) { 74 | return client.say( 75 | msg.channelName, 76 | `No data found for channel: ${args[0]}` 77 | ); 78 | } 79 | 80 | // Build a nice message from the response 81 | const message = `ID: ${data[0].id}, Followers: ${data[0].followers.toLocaleString()}`; 82 | 83 | // Send the very cool response to chat 84 | return client.say( 85 | msg.channelName, 86 | `OpieOP Clap ${message}` 87 | ); 88 | } 89 | }); 90 | 91 | // Basic loop for farming chatbots 92 | setInterval(() => { 93 | client.say('', ''); 94 | }, 24 * 60 * 60 * 1000); // interval: hours, minutes, seconds, milliseconds 95 | 96 | // Example: farm cookies every 2 hours 97 | setInterval(() => { 98 | client.say('', '!cookie'); 99 | }, 2 * 60 * 60 * 1000); 100 | -------------------------------------------------------------------------------- /colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "twitch_basic": [ 3 | "blue", 4 | "blue_violet", 5 | "cadet_blue", 6 | "chocolate", 7 | "coral", 8 | "dodger_blue", 9 | "firebrick", 10 | "golden_rod", 11 | "green", 12 | "hot_pink", 13 | "orange_red", 14 | "red", 15 | "sea_green", 16 | "spring_green", 17 | "yellow_green" 18 | ], 19 | "pastels": [ 20 | "#FF8F8F", 21 | "#FFB278", 22 | "#FFD889", 23 | "#C7FFA0", 24 | "#9EE6FF", 25 | "#B3BFFF", 26 | "#D8C3FF", 27 | "#FFBBFF" 28 | ], 29 | "longPastels": [ 30 | "#FF8F8F", 31 | "#FFAA8F", 32 | "#FFC48F", 33 | "#FFDE8F", 34 | "#FFF88F", 35 | "#C7FFA0", 36 | "#A0FFA0", 37 | "#80FFA0", 38 | "#60FFA0", 39 | "#40FFA0", 40 | "#9EE6FF", 41 | "#A0C1FF", 42 | "#A0A6FF", 43 | "#A085FF", 44 | "#A067FF", 45 | "#B3BFFF", 46 | "#C1BFFF", 47 | "#D0BFFF", 48 | "#E0BFFF", 49 | "#F0BFFF", 50 | "#D8C3FF", 51 | "#DAC3FF", 52 | "#DDC3FF", 53 | "#DFC3FF", 54 | "#E1C3FF", 55 | "#FFBBFF", 56 | "#FFBFF3", 57 | "#FFBFE1", 58 | "#FFBFDA", 59 | "#FFBFD2" 60 | ], 61 | "rainbow": [ 62 | "#FF0000", 63 | "#FF7F00", 64 | "#FFFF00", 65 | "#00FF00", 66 | "#0000FF", 67 | "#4B0082", 68 | "#8B00FF" 69 | ], 70 | "longRainbow": [ 71 | "#FF0000", 72 | "#FF3300", 73 | "#FF6600", 74 | "#FF9900", 75 | "#FFCC00", 76 | "#FFFF00", 77 | "#CCFF00", 78 | "#99FF00", 79 | "#66FF00", 80 | "#33FF00", 81 | "#00FF00", 82 | "#00FF33", 83 | "#00FF66", 84 | "#00FF99", 85 | "#00FFCC", 86 | "#00FFFF", 87 | "#00CCFF", 88 | "#0099FF", 89 | "#0066FF", 90 | "#0033FF", 91 | "#0000FF", 92 | "#3300FF", 93 | "#6600FF", 94 | "#9900FF", 95 | "#CC00FF", 96 | "#FF00FF", 97 | "#FF00CC", 98 | "#FF0099", 99 | "#FF0066", 100 | "#FF0033" 101 | ], 102 | "earthTones": [ 103 | "#8B4513", 104 | "#CD853F", 105 | "#D2691E", 106 | "#8B0000", 107 | "#556B2F", 108 | "#228B22", 109 | "#2E8B57", 110 | "#B8860B", 111 | "#A0522D", 112 | "#DAA520", 113 | "#FFD700", 114 | "#D2B48C" 115 | ], 116 | "neon": [ 117 | "#FF1493", 118 | "#00FF00", 119 | "#00FFFF", 120 | "#FFFF00", 121 | "#FF00FF", 122 | "#8A2BE2", 123 | "#FF69B4", 124 | "#FFD700", 125 | "#00FF7F", 126 | "#FF4500" 127 | ], 128 | "grayscale": [ 129 | "#000000", 130 | "#333333", 131 | "#666666", 132 | "#999999", 133 | "#CCCCCC", 134 | "#E0E0E0", 135 | "#F5F5F5", 136 | "#FFFFFF", 137 | "#202020", 138 | "#505050", 139 | "#808080", 140 | "#A0A0A0", 141 | "#D0D0D0", 142 | "#F0F0F0" 143 | ], 144 | "coolBlues": [ 145 | "#0000FF", 146 | "#3399FF", 147 | "#66CCFF", 148 | "#99CCFF", 149 | "#00FFFF", 150 | "#00CCFF", 151 | "#0099FF", 152 | "#0033CC", 153 | "#0066CC", 154 | "#0099CC" 155 | ], 156 | "warmReds": [ 157 | "#FF0000", 158 | "#FF3300", 159 | "#FF6600", 160 | "#FF9900", 161 | "#FFCC00", 162 | "#FF0033", 163 | "#FF3366", 164 | "#FF6666", 165 | "#FF9966", 166 | "#FFCC66" 167 | ], 168 | "vibrantGreens": [ 169 | "#00FF00", 170 | "#33FF00", 171 | "#66FF00", 172 | "#99FF00", 173 | "#CCFF00", 174 | "#00FF33", 175 | "#00FF66", 176 | "#00FF99", 177 | "#33FF33", 178 | "#66FF66", 179 | "#99FF99", 180 | "#CCFFCC" 181 | ], 182 | "deepPurples": [ 183 | "#660066", 184 | "#990099", 185 | "#CC00CC", 186 | "#6600CC", 187 | "#9900CC", 188 | "#9900FF", 189 | "#6633CC", 190 | "#9966CC", 191 | "#9933FF", 192 | "#6600FF", 193 | "#9966FF", 194 | "#660033", 195 | "#993333", 196 | "#CC3333", 197 | "#996666" 198 | ], 199 | "candyShop": [ 200 | "#FF6B6B", 201 | "#FFDD94", 202 | "#A0CED9", 203 | "#FFC3A0", 204 | "#E2D58B", 205 | "#B5EAD7", 206 | "#FFAAA6", 207 | "#FFBCBC", 208 | "#D7FFAB", 209 | "#9DF5CF" 210 | ], 211 | "sunsetShades": [ 212 | "#FF5733", 213 | "#FF8C19", 214 | "#FFC300", 215 | "#FFDB58", 216 | "#FFAB40", 217 | "#FF5733", 218 | "#C70039", 219 | "#900C3F", 220 | "#581845", 221 | "#512E5F" 222 | ], 223 | "autumnTones": [ 224 | "#FF4500", 225 | "#FF6B00", 226 | "#FF8833", 227 | "#FFA500", 228 | "#FFCC66", 229 | "#FFD700", 230 | "#E6B800", 231 | "#CC9900", 232 | "#996633", 233 | "#663300" 234 | ], 235 | "oceanBlues": [ 236 | "#0077B6", 237 | "#0096C7", 238 | "#00A0D3", 239 | "#00A8E0", 240 | "#00BCF2", 241 | "#0099CC", 242 | "#0055A4", 243 | "#003366", 244 | "#003049", 245 | "#00171F" 246 | ], 247 | "burger": [ 248 | "#F2D16B", 249 | "#8B4513", 250 | "#228B22", 251 | "#FF6347", 252 | "#D2691E", 253 | "#FFD700" 254 | ], 255 | "christmas": [ 256 | "#FF0000", 257 | "#00FF00", 258 | "#FFD700", 259 | "#228B22", 260 | "#8B4513", 261 | "#FFFFFF", 262 | "#FF1493", 263 | "#006400", 264 | "#B22222", 265 | "#2E8B57" 266 | ], 267 | "halloween": [ 268 | "#FF6600", 269 | "#000000", 270 | "#FFA500", 271 | "#8B4513", 272 | "#FF6347", 273 | "#0000FF", 274 | "#4B0082", 275 | "#8B0000", 276 | "#006400", 277 | "#483D8B" 278 | ] 279 | } --------------------------------------------------------------------------------