diff --git a/README.md b/README.md index 0897e66ec..647b82908 100644 --- a/README.md +++ b/README.md @@ -1,203 +1,148 @@ -Your support means the world to me! :heart: +# ChatGPT Twitch Bot Documentation + +**Important Notice: Cyclic is no longer supported for deployment. Please use Render for deploying this bot.** + +Your support means the world to me! ❤️ ☕ [Buy me a coffee to support me](https://www.buymeacoffee.com/osetinhas) ☕ -# ChatGPT Twitch Bot +Join our Discord community: + +[https://discord.gg/pcxybrpDx6](https://discord.gg/pcxybrpDx6) -This is a simple Node.js Chat bot with ChatGPT Integration based on the simplest possible nodejs api using express that responds to any request with: Yo! +--- -It considers a context file for every message so you can give it background information about your stream, your socials, your stream rewards, stream currency and so on. +## Overview -You can choose if you want it to run in prompt mode (without context of previous messages) or in chat mode (with context of previous messages). +This is a simple Node.js chatbot with ChatGPT integration, designed to work with Twitch streams. It uses the Express framework and can operate in two modes: chat mode (with context of previous messages) or prompt mode (without context of previous messages). -# How to use +## Features -## 1. Fork this on Github -Login with your github account and fork this repository to get your own copy of it. +- Responds to Twitch chat commands with ChatGPT-generated responses. +- Can operate in chat mode with context or prompt mode without context. +- Supports Text-to-Speech (TTS) for responses. +- Customizable via environment variables. +- Deployed on Render for 24/7 availability. --- -## 2. Fill out your context file -Open the file file_context.txt and write down all your background information for GPT. +## Setup Instructions -The contents of this file will be sent every time somebody enters your chat command. This will increase the cost of every request with every word you put in, but can still be pretty lengthy. +### 1. Fork the Repository ---- +Login to GitHub and fork this repository to get your own copy. -## 3. Create an openAI account -Go to https://platform.openai.com and create an account. +### 2. Fill Out Your Context File -Initially you will get a free contingent to use but later on you will have to pay for the server usage. +Open `file_context.txt` and write down all your background information for GPT. This content will be included in every request. -You can set a spending limit here: https://platform.openai.com/account/billing/limits +### 3. Create an OpenAI Account ---- +Create an account on [OpenAI](https://platform.openai.com) and set up billing limits if necessary. -## 4. Get your openAI Secure Key -Go to https://platform.openai.com/account/api-keys +### 4. Get Your OpenAI API Key -Press "Create new secret key" +Generate an API key on the [API keys page](https://platform.openai.com/account/api-keys) and store it securely. -Save that secret key somewhere safe. +### 5. Deploy on Render -You will use this key to authorize your version of this chatbot with openai. -Treat this as the key to your wallet. +Render allows you to run your bot 24/7 for free. Follow these steps: ---- +#### 5.1. Deploy to Render +Click the button below to deploy: -## 5. Deploy this repo on Cylic or Render for free +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy) -Render is the latest addition to our repo. With Render you can have a server running 24 hours a day for free. -You **WON'T** need to use NIGHTBOT or STREAMELEMENTS to host your bot anymore! -By default the messages will be answered by a bot called *oSetinhasbot* but you can change it to your own bot name. -Check +#### 5.2. Login with GitHub -Cyclic is our old approach with certain limitations. You can still use it if you want to. -You will have to use NIGHTBOT or STREAMELEMENTS to host your bot, and they have a **10seconds timeout**. -Also, Twitch has a **400 character limit** on chat messages, so you will have to use the !continue command to continue a conversation. +Log in with your GitHub account and select your forked repository for deployment. -Press the button to deploy this on Cyclic or Render. +### 6. Set Environment Variables -### 5.1. Deploy to Render +Go to the variables/environment tab in your Render deployment and set the following variables: -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy) +#### 6.1. Required Variables -### 5.2. Deploy to Cyclic - -[![Deploy to Cyclic](https://deploy.cyclic.app/button.svg)](https://deploy.cyclic.app/) +- `OPENAI_API_KEY`: Your OpenAI API key. -### 5.3. Login with Github +#### 6.2. Optional Variables -Log in with your Github account there and select your twitch-chatgpt repository for deployment. +##### 6.2.1. Nightbot/Streamelements Integration Variable +- `GPT_MODE`: (default: `CHAT`) Mode of operation, can be `CHAT` or `PROMPT`. -These services are free to use and only very big streams will run into issues with their request contingent. +##### 6.2.2. All Modes Variables +- `HISTORY_LENGTH`: (default: `5`) Number of previous messages to include in context. +- `MODEL_NAME`: (default: `gpt-3.5-turbo`) The OpenAI model to use. You can check the available models [here](https://platform.openai.com/docs/models/). +- `COMMAND_NAME`: (default: `!gpt`) The command that triggers the bot. You can set more than one command by separating them with a comma (e.g. `!gpt,!chatbot`). +- `CHANNELS`: List of Twitch channels the bot will join (comma-separated). (e.g. `channel1,channel2`; do not include www.twitch.tv) +- `SEND_USERNAME`: (default: `true`) Whether to include the username in the message sent to OpenAI. +- `ENABLE_TTS`: (default: `false`) Whether to enable Text-to-Speech. +- `ENABLE_CHANNEL_POINTS`: (default: `false`) Whether to enable channel points integration. +- `COOLDOWN_DURATION`: (default: `10`) Cooldown duration in seconds between responses. ---- +#### 6.3. Twitch Integration Variables -## 6. Set your environment variables -Go to the variables/environment tab in your deployment. - -Create 3 new variables. The exact spelling of these variables is important: - -### 6.1. Required Variables for both Render and Cyclic - -1. _**OPENAI_API_KEY**_ - - This is where you paste your openAI Secure Key. - -### 6.2 Required Variables only for Render - -2. _**CHANNELS**_ - - This is where you put your twitch channel name. - - If you have more than one channel, separate them with a comma. (e.g. channel1,channel2,channel3) - - If the channel has FOLLOWERS-ONLY mode enabled: - - create a GitHub issue requesting "oSetinhasBot" account to follow your channel. - - connect your own account, see variable 7. and 8. - -### 6.2. Optional Variables for both Render and Cyclic - -3. _**GPT_MODE**_ - - (default: CHAT) - - Accepts one of 2 values: - - "CHAT" - Chat mode with history, cheaper than prompt mode but also faster. Uses gpt-3.5-turbo as model. - - "PROMPT" - Prompt mode, no history. Uses text-davinci-003 as model. - -4. _**HISTORY_LENGTH**_ - - (default: 3) - - Accepts a number. - - Only works when GPT_MODE is CHAT - - Defines how many bot-user conversations will be saved and sent together with the most recent user message. - - This gives ChatGPT the ability to remember things and allow conversations instead of static prompts. - -5. _**MODEL_NAME**_ - - (default: gpt-3.5-turbo) - - Accepts one of 2 values: - - "gpt-3.5-turbo" - The default model. This is the fastest and cheapest model. It is also the least accurate. - - "text-davinci-003" - Most expensive model. - - "gpt-4" - Most accurate model and if you have the plan for it! - -6.3. Optional Variables only for Render - -6. _**COMMAND_NAME**_ - - (default: chat) (so the command is !chat) - - Accepts a string. - - Defines the command that will be used to trigger the bot. - -7. _**TWITCH_USER**_ -This step can be complicated. - - To get the necessary twitch user: - - Go to https://dev.twitch.tv/console - - Register your application - - Fill a name for your application (this can be anything) - - Set OAuth Redirect URL to https://twitchapps.com/tokengen/ - - Set Category to Chat Bot - - Set Description to anything you want - - Fill the TWITCH_USER variable with the name of your application - -8. _**TWITCH_AUTH**_ +- `TWITCH_AUTH`: OAuth token for your Twitch bot. - Go to https://twitchapps.com/tmi/ and click on Connect with Twitch - Copy the token from the page and paste it in the TWITCH_AUTH variable + - ⚠️ THIS TOKEN MIGHT EXPIRE AFTER A FEW DAYS, SO YOU MIGHT HAVE TO REPEAT THIS STEP EVERY FEW DAYS ⚠️ -Save the Changes. +### 7. Text-To-Speech (TTS) Setup -## 7. Text-To-Speech (TTS) Connection +Your Render URL (e.g., `https://your-twitch-bot.onrender.com/`) can be added as a widget to your stream for TTS integration. -Your render url, for example: -- https://your-twitch-bot.onrender.com/ +--- -You will see the following web page, and it is directly connected to your twitch chat. +## Usage -You can add it to your stream as a widget :) +### Commands -![img.png](imgs/img.png) +You can interact with the bot using Twitch chat commands. By default, the command is `!gpt`. You can change this in the environment variables. ---- +### Example -If you used RENDER, you can stop here :) +To use the `!gpt` command: ---- +```twitch +!gpt What is the weather today? +``` -## 7. Get your API Link from Cyclic -Copy the link cyclic provides for you. +The bot will respond with an OpenAI-generated message. -You can find it in the top left corner below the title of your deployed repository. +### Streamelements and Nightbot Integration ---- +#### Streamelements -## 8. Add your API Command to your Chatbot -Now it is time to build your Chat-Command. +Create a custom command with the response: +```twitch +$(urlfetch https://your-render-url.onrender.com/gpt/"${user}:${queryescape ${1:}}") +``` -### Streamelements -Go to your Streamelements Dashboard -> Chatbot -> Commands -> Custom Commands. +#### Nightbot -Create a new command. +Create a custom command with the response: -Enter the following in the response field: +```twitch +!addcom !gptcmd $(urlfetch https://twitch-chatgpt-bot.onrender.com/gpt/$(user):$(querystring)) ``` -$(urlfetch https://your-cyclic-url.app/gpt/"${user}:${queryescape ${1:}}") -``` -Replace "your-cyclic-url.app" with the url you copied in step 7 -Enjoy your command. +Replace `your-render-url.onrender.com` with your actual Render URL. +Replace `gptcmd` with your desired command name. +Remove `$(user):` if you don't want to include the username in the message sent to OpenAI. +--- +## Support -### Nightbot -Go to your commands page and add a new command. +For any issues or questions, please join our [Discord community](https://discord.gg/pcxybrpDx6). -Enter the following in the message field: +Thank you for using the ChatGPT Twitch Bot! Your support is greatly appreciated. ☕ [Buy me a coffee](https://www.buymeacoffee.com/osetinhas) ☕ -```bash -$(eval "$(urlfetch https://your-cyclic-url.app/gpt/"$(user):$(querystring)")"; ' ') -``` -Replace "your-cyclic-url.app" with the url you copied in step 7 - -Enjoy your command. +--- -## Extra commands +### Important Notice -### !continue -Twitch has a 400 character limit on chat messages. -If you want to continue a conversation, you can use the !continue command. +**Cyclic is no longer supported for deployment. Please use Render for deploying this bot.** -$(urlfetch https://your-cyclic-url.app/continue") \ No newline at end of file +--- \ No newline at end of file diff --git a/file_backup b/file_backup deleted file mode 100644 index 5abeb1165..000000000 --- a/file_backup +++ /dev/null @@ -1,20 +0,0 @@ -You are a twitch chatbot and are answering to prompts from multiple viewers. -You are part of the community and take action as an independent viewer. -Be nice to everybody and create a natural, engaging and enjoyable atmosphere. -Never begin your sentences with "!" or "/". -Try to make a joke here and there. -Dont engage into talks about politics or religion. Be respectful towards everybody. - -Your name is: -The name of the stream is: -The streamer is called: -The stream community members are called: -The stream currency is called: -The streamer can be found under the following socials and links: -TikTok: -Youtube: -Twitter: -Facebook: -Instagram: - -Please answer now the prompt of the viewer: diff --git a/file_context.txt b/file_context.txt index 214726afb..306883945 100644 --- a/file_context.txt +++ b/file_context.txt @@ -5,14 +5,14 @@ Never begin your sentences with "!" or "/". Try to make a joke here and there. Dont engage into talks about politics or religion. Be respectful towards everybody. -Your name is: -The name of the stream is: -The streamer is called: -The stream community members are called: -The stream currency is called: +Your name is: XTAKA +The name of the stream is: KEBAB CODE +The streamer is called:TAKA +The stream community members are called:CHAT +The stream currency is called:JUST IS LEAGUE The streamer can be found under the following socials and links: TikTok: -Youtube: +Youtube:https://www.youtube.com/@NORTHST4R Twitter: Facebook: Instagram: diff --git a/index.js b/index.js index b767bca14..76d16ba96 100644 --- a/index.js +++ b/index.js @@ -1,241 +1,202 @@ import express from 'express'; import fs from 'fs'; +import ws from 'ws'; +import expressWs from 'express-ws'; +import {job} from './keep_alive.js'; import {OpenAIOperations} from './openai_operations.js'; import {TwitchBot} from './twitch_bot.js'; -import {job} from './keep_alive.js'; -import expressWs from 'express-ws'; -import ws from 'ws'; -// start keep alive cron job +// Start keep alive cron job job.start(); -console.log(process.env) +console.log(process.env); -// setup express app +// Setup express app const app = express(); const expressWsInstance = expressWs(app); -// set the view engine to ejs +// Set the view engine to ejs app.set('view engine', 'ejs'); -// load env variables -let GPT_MODE = process.env.GPT_MODE -let HISTORY_LENGTH = process.env.HISTORY_LENGTH -let OPENAI_API_KEY = process.env.OPENAI_API_KEY -let MODEL_NAME = process.env.MODEL_NAME -let TWITCH_USER = process.env.TWITCH_USER -let TWITCH_AUTH = process.env.TWITCH_AUTH -let COMMAND_NAME = process.env.COMMAND_NAME -let CHANNELS = process.env.CHANNELS - -if (!GPT_MODE) { - GPT_MODE = "CHAT" -} -if (!HISTORY_LENGTH) { - HISTORY_LENGTH = 5 -} +// Load environment variables +const GPT_MODE = process.env.GPT_MODE || 'CHAT'; +const HISTORY_LENGTH = process.env.HISTORY_LENGTH || 5; +const OPENAI_API_KEY = process.env.OPENAI_API_KEY || ''; +const MODEL_NAME = process.env.MODEL_NAME || 'gpt-3.5-turbo'; +const TWITCH_USER = process.env.TWITCH_USER || 'oSetinhasBot'; +const TWITCH_AUTH = process.env.TWITCH_AUTH || 'oauth:vgvx55j6qzz1lkt3cwggxki1lv53c2'; +const COMMAND_NAME = process.env.COMMAND_NAME || '!gpt'; +const CHANNELS = process.env.CHANNELS || 'oSetinhas,jones88'; +const SEND_USERNAME = process.env.SEND_USERNAME || 'true'; +const ENABLE_TTS = process.env.ENABLE_TTS || 'false'; +const ENABLE_CHANNEL_POINTS = process.env.ENABLE_CHANNEL_POINTS || 'false'; +const COOLDOWN_DURATION = parseInt(process.env.COOLDOWN_DURATION, 10) || 10; // Cooldown duration in seconds + if (!OPENAI_API_KEY) { - console.log("No OPENAI_API_KEY found. Please set it as environment variable.") -} -if (!MODEL_NAME) { - MODEL_NAME = "gpt-3.5-turbo" -} -if (!TWITCH_USER) { - TWITCH_USER = "oSetinhasBot" - console.log("No TWITCH_USER found. Using oSetinhasBot as default.") -} -if (!TWITCH_AUTH) { - // https://dev.twitch.tv/console - // https://twitchapps.com/tmi/ - TWITCH_AUTH = "oauth:vgvx55j6qzz1lkt3cwggxki1lv53c2" - console.log("No TWITCH_AUTH found. Using oSetinhasBot auth as default.") -} -if (!COMMAND_NAME) { - COMMAND_NAME = "chat" -} -if (!CHANNELS) { - CHANNELS = ["oSetinhas", "jones88"] -} else { - // split channels by comma into array - CHANNELS = CHANNELS.split(",") + console.error('No OPENAI_API_KEY found. Please set it as an environment variable.'); } -// init global variables -const MAX_LENGTH = 399 -let file_context = "You are a helpful Twitch Chatbot." -let last_user_message = "" +const commandNames = COMMAND_NAME.split(',').map(cmd => cmd.trim().toLowerCase()); +const channels = CHANNELS.split(',').map(channel => channel.trim()); +const maxLength = 399; +let fileContext = 'You are a helpful Twitch Chatbot.'; +let lastUserMessage = ''; +let lastResponseTime = 0; // Track the last response time -// setup twitch bot -const channels = CHANNELS; -const channel = channels[0]; -console.log("Channels: " + channels) +// Setup Twitch bot +console.log('Channels: ', channels); +const bot = new TwitchBot(TWITCH_USER, TWITCH_AUTH, channels, OPENAI_API_KEY, ENABLE_TTS); -const bot = new TwitchBot(TWITCH_USER, TWITCH_AUTH, channels); +// Setup OpenAI operations +fileContext = fs.readFileSync('./file_context.txt', 'utf8'); +const openaiOps = new OpenAIOperations(fileContext, OPENAI_API_KEY, MODEL_NAME, HISTORY_LENGTH); -// setup openai operations -file_context = fs.readFileSync("./file_context.txt", 'utf8'); -const openai_ops = new OpenAIOperations(file_context, OPENAI_API_KEY, MODEL_NAME, HISTORY_LENGTH); - -// setup twitch bot callbacks +// Setup Twitch bot callbacks bot.onConnected((addr, port) => { console.log(`* Connected to ${addr}:${port}`); - - // join channels channels.forEach(channel => { console.log(`* Joining ${channel}`); - console.log(`* Saying hello in ${channel}`) + console.log(`* Saying hello in ${channel}`); }); }); -bot.onDisconnected((reason) => { +bot.onDisconnected(reason => { console.log(`Disconnected: ${reason}`); }); -// connect bot +// Connect bot bot.connect( () => { - console.log("Bot connected!"); + console.log('Bot connected!'); }, - (error) => { - console.log("Bot couldn't connect!"); - console.log(error); + error => { + console.error('Bot couldn\'t connect!', error); } ); bot.onMessage(async (channel, user, message, self) => { if (self) return; - // check if message is a command started with !COMMAND_NAME (e.g. !gpt) - if (message.startsWith("!" + COMMAND_NAME)) { - // get text - const text = message.slice(COMMAND_NAME.length + 1); + const currentTime = Date.now(); + const elapsedTime = (currentTime - lastResponseTime) / 1000; // Time in seconds - // make openai call - const response = await openai_ops.make_openai_call(text); + if (ENABLE_CHANNEL_POINTS === 'true' && user['msg-id'] === 'highlighted-message') { + console.log(`Highlighted message: ${message}`); + if (elapsedTime < COOLDOWN_DURATION) { + bot.say(channel, `Cooldown active. Please wait ${COOLDOWN_DURATION - elapsedTime.toFixed(1)} seconds before sending another message.`); + return; + } + lastResponseTime = currentTime; // Update the last response time + + const response = await openaiOps.make_openai_call(message); + bot.say(channel, response); + } - // split response if it exceeds twitch chat message length limit - // send multiples messages with a delay in between - if (response.length > MAX_LENGTH) { - const messages = response.match(new RegExp(`.{1,${MAX_LENGTH}}`, "g")); - messages.forEach((message, index) => { + const command = commandNames.find(cmd => message.toLowerCase().startsWith(cmd)); + if (command) { + if (elapsedTime < COOLDOWN_DURATION) { + bot.say(channel, `Cooldown active. Please wait ${COOLDOWN_DURATION - elapsedTime.toFixed(1)} seconds before sending another message.`); + return; + } + lastResponseTime = currentTime; // Update the last response time + + let text = message.slice(command.length).trim(); + if (SEND_USERNAME === 'true') { + text = `Message from user ${user.username}: ${text}`; + } + + const response = await openaiOps.make_openai_call(text); + if (response.length > maxLength) { + const messages = response.match(new RegExp(`.{1,${maxLength}}`, 'g')); + messages.forEach((msg, index) => { setTimeout(() => { - bot.say(channel, message); + bot.say(channel, msg); }, 1000 * index); }); } else { bot.say(channel, response); + } + + if (ENABLE_TTS === 'true') { try { - console.log(user.username + ' - ' + user.userstate); - const ttsAudioUrl = await bot.sayTTS(channel, response, user.userstate); - // Notify clients about the file change + const ttsAudioUrl = await bot.sayTTS(channel, response, user['userstate']); notifyFileChange(ttsAudioUrl); } catch (error) { - console.error(error); + console.error('TTS Error:', error); } } } }); app.ws('/check-for-updates', (ws, req) => { - ws.on('message', (message) => { - // Handle WebSocket messages (if needed) - }); + ws.on('message', message => { + // Handle WebSocket messages (if needed) + }); }); -// setup bot -const messages = [ - {role: "system", content: "You are a helpful Twitch Chatbot."} -]; - -console.log("GPT_MODE is " + GPT_MODE) -console.log("History length is " + HISTORY_LENGTH) -console.log("OpenAI API Key:" + OPENAI_API_KEY) -console.log("Model Name:" + MODEL_NAME) +const messages = [{role: 'system', content: 'You are a helpful Twitch Chatbot.'}]; +console.log('GPT_MODE:', GPT_MODE); +console.log('History length:', HISTORY_LENGTH); +console.log('OpenAI API Key:', OPENAI_API_KEY); +console.log('Model Name:', MODEL_NAME); -app.use(express.json({extended: true, limit: '1mb'})) -app.use('/public', express.static('public')) +app.use(express.json({extended: true, limit: '1mb'})); +app.use('/public', express.static('public')); app.all('/', (req, res) => { - console.log("Just got a request!") + console.log('Received a request!'); res.render('pages/index'); - //res.sendFile(process.env.RENDER_SRC_ROOT + '/index.ejs') - //res.send('Yo!') -}) +}); -if (process.env.GPT_MODE === "CHAT"){ - fs.readFile("./file_context.txt", 'utf8', function(err, data) { +if (GPT_MODE === 'CHAT') { + fs.readFile('./file_context.txt', 'utf8', (err, data) => { if (err) throw err; - console.log("Reading context file and adding it as system level message for the agent.") + console.log('Reading context file and adding it as system-level message for the agent.'); messages[0].content = data; }); } else { - fs.readFile("./file_context.txt", 'utf8', function(err, data) { + fs.readFile('./file_context.txt', 'utf8', (err, data) => { if (err) throw err; - console.log("Reading context file and adding it in front of user prompts:") - file_context = data; - console.log(file_context); + console.log('Reading context file and adding it in front of user prompts:'); + fileContext = data; }); } app.get('/gpt/:text', async (req, res) => { - - //The agent should receive Username:Message in the text to identify conversations with different users in his history. - const text = req.params.text - - // define function to check history length and perform bot response - const answer_question = async (answer) => { - if (answer.length > MAX_LENGTH) { - const messages = answer.match(new RegExp(`.{1,${MAX_LENGTH}}`, "g")); - messages.forEach((message, index) => { - setTimeout(() => { - bot.say(channel, message); - }, 1000 * index); - }); + const text = req.params.text; + + let answer = ''; + try { + if (GPT_MODE === 'CHAT') { + answer = await openaiOps.make_openai_call(text); + } else if (GPT_MODE === 'PROMPT') { + const prompt = `${fileContext}\n\nUser: ${text}\nAgent:`; + answer = await openaiOps.make_openai_call_completion(prompt); } else { - bot.say(channel, answer); + throw new Error('GPT_MODE is not set to CHAT or PROMPT. Please set it as an environment variable.'); } - } - let answer = "" - if (GPT_MODE === "CHAT") { - //CHAT MODE EXECUTION - answer = await openai_ops.make_openai_call(text); - } else if(GPT_MODE === "PROMPT") { - //PROMPT MODE EXECUTION - - // create prompt based on file_context and the user prompt - let prompt = file_context; - prompt += "\n\nUser: " + text + "\nAgent:" - answer = await openai_ops.make_openai_call_completion(prompt); - } else { - //ERROR MODE EXECUTION - console.log("ERROR: GPT_MODE is not set to CHAT or PROMPT. Please set it as environment variable.") + res.send(answer); + } catch (error) { + console.error('Error generating response:', error); + res.status(500).send('An error occurred while generating the response.'); } +}); - // send response - await answer_question(answer) - - res.send(answer) -}) - -// make app always listening to twitch chat and get new messages starting with !gpt on port 3000 const server = app.listen(3000, () => { - console.log('Server running on port 3000'); + console.log('Server running on port 3000'); }); const wss = expressWsInstance.getWss(); -// const wss = appWithWebSocket.ws - -wss.on('connection', (ws) => { - ws.on('message', (message) => { +wss.on('connection', ws => { + ws.on('message', message => { // Handle client messages (if needed) }); }); -// Notify clients when the file changes function notifyFileChange() { - wss.clients.forEach((client) => { + wss.clients.forEach(client => { if (client.readyState === ws.OPEN) { - client.send(JSON.stringify({ updated: true })); + client.send(JSON.stringify({updated: true})); } }); } diff --git a/keep_alive.js b/keep_alive.js index 6e869655e..25e3661e6 100644 --- a/keep_alive.js +++ b/keep_alive.js @@ -1,4 +1,5 @@ -import cron from 'cron'; +import { CronJob } from 'cron'; + import https from 'https'; // get env RENDER_EXTERNAL_URL @@ -8,7 +9,7 @@ if (!render_url) { console.log("No RENDER_EXTERNAL_URL found. Please set it as environment variable.") } -const job = new cron.CronJob('*/14 * * * *', function() { +const job = new CronJob('*/14 * * * *', function() { console.log('Making keep alive call'); https.get(render_url, (resp) => { diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d5aedc678..000000000 --- a/package-lock.json +++ /dev/null @@ -1,1151 +0,0 @@ -{ - "name": "starter-express-api", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/luxon": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.3.tgz", - "integrity": "sha512-/BJF3NT0pRMuxrenr42emRUF67sXwcZCd+S1ksG/Fcf9O7C3kKCY4uJSbKBE4KDUIYr3WMsvfmWD8hRjXExBJQ==" - }, - "@types/node": { - "version": "18.19.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.2.tgz", - "integrity": "sha512-6wzfBdbWpe8QykUkXBjtmO3zITA0A3FIjoy+in0Y2K4KrCiRhNYJIdwAPDffZ3G6GnaKaSLSEa9ZuORLfEoiwg==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", - "requires": { - "@types/node": "*", - "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, - "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "cron": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.1.tgz", - "integrity": "sha512-P8rDShXt2IZa16KwuYpbd1w7NjU4BnXeUEfkesBXT36JXhUsQ1P6i6ctsi5H82FQmR4tn/MOz05aB/3vn6dSkA==", - "requires": { - "@types/luxon": "~3.3.0", - "luxon": "~3.3.0" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "digest-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", - "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", - "requires": { - "base-64": "^0.1.0", - "md5": "^2.3.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "requires": { - "jake": "^10.8.5" - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "express-ws": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", - "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", - "requires": { - "ws": "^7.4.6" - }, - "dependencies": { - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "requires": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "dependencies": { - "web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" - } - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" - }, - "md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, - "node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha512-qAMrwuk2xLEutlASoiPiAMW3EN3K96Ka/ilSXYr6qR1zSVXw2j7+yDSqGTC4T9apfLYxM3tLLjKvgPdAUK7kYQ==" - }, - "openai": { - "version": "4.20.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.20.1.tgz", - "integrity": "sha512-Dd3q8EvINfganZFtg6V36HjrMaihqRgIcKiHua4Nq9aw/PxOP48dhbsk8x5klrxajt5Lpnc1KTOG5i1S6BKAJA==", - "requires": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "promisify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/promisify/-/promisify-0.0.3.tgz", - "integrity": "sha512-CcBGsRhhq466fsZVyHfptuKqon6eih0CqMsJE0kWIIjbpVNEyDoaKLELm2WVs//W/WXRBHip+6xhTExTkHUwtA==", - "requires": { - "when": "^3.7.8" - }, - "dependencies": { - "when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==" - } - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "say": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/say/-/say-0.16.0.tgz", - "integrity": "sha512-yEfncNu3I6lcZ6RIrXgE9DqbrEmvV5uQQ8ReM14u/DodlvJYpveqNphO55RLMSj77b06ZKNif/FLmhzQxcuUXg==", - "requires": { - "one-time": "0.0.4" - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "sound-play": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/sound-play/-/sound-play-1.1.0.tgz", - "integrity": "sha512-Bd/L0AoCwITFeOnpNLMsfPXrV5GG5NhrC/T6odveahYbhPZkdTnrFXRia9FCC5WBWdUTw1d+yvLBvi4wnD1xOA==" - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "tmi.js": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/tmi.js/-/tmi.js-1.8.5.tgz", - "integrity": "sha512-A9qrydfe1e0VWM9MViVhhxVgvLpnk7pFShVUWePsSTtoi+A1X+Zjdoa7OJd7/YsgHXGj3GkNEvnWop/1WwZuew==", - "requires": { - "node-fetch": "^2.6.1", - "ws": "^8.2.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==" - } - } -} diff --git a/package.json b/package.json index 7ac682161..31b7bca5e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "starter-express-api", - "version": "1.0.0", - "description": "", + "name": "Twitch Chat GPT", + "version": "1.1.0", + "description": "The Twitch Chat GPT is a bot that uses OpenAI's GPT-3.5 to generate messages based on the chat of a Twitch channel. It can be used to generate messages for a chatbot, or to generate, for example, a chatbot for a game.", "main": "index.js", "type": "module", "scripts": { @@ -9,26 +9,22 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cyclic-software/starter-express-api.git" + "url": "git+https://github.com/pedrojlazevedo/twitch-chatgpt.git" }, - "author": "", + "author": "Pedro Azevedo", "license": "ISC", "bugs": { - "url": "https://github.com/cyclic-software/starter-express-api/issues" + "url": "https://github.com/pedrojlazevedo/twitch-chatgpt/issues" }, - "homepage": "https://github.com/cyclic-software/starter-express-api#readme", + "homepage": "https://github.com/pedrojlazevedo/twitch-chatgpt#readme", "dependencies": { "cron": "^3.1.1", "ejs": "^3.1.9", "express": "^4.18.2", "express-ws": "^5.0.2", - "fs": "^0.0.1-security", - "https": "^1.0.0", "openai": "^4.20.1", "promisify": "^0.0.3", "request": "^2.88.2", - "say": "^0.16.0", - "sound-play": "^1.1.0", "tmi.js": "^1.8.5", "ws": "^8.14.2" } diff --git a/render.yaml b/render.yaml index a64189a78..1b65601c9 100644 --- a/render.yaml +++ b/render.yaml @@ -22,4 +22,12 @@ services: - key: TWITCH_USER # https://dev.twitch.tv/console value: "oSetinhasBot" - key: TWITCH_AUTH # https://twitchapps.com/tmi/ - value: "oauth:vgvx55j6qzz1lkt3cwggxki1lv53c2" + value: "oauth:9wh6ujbu2mro28zhbl8z6z921iuyxz" + - key: SEND_USERNAME + value: "true" + - key: ENABLE_TTS + value: "false" + - key: ENABLE_CHANNEL_POINTS + value: "false" + - key: COOLDOWN_DURATION + value: "10" diff --git a/tmi.min.js b/tmi.min.js deleted file mode 100644 index cfe8d1566..000000000 --- a/tmi.min.js +++ /dev/null @@ -1 +0,0 @@ -!function s(o,i,r){function a(t,e){if(!i[t]){if(!o[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(c)return c(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=i[t]={exports:{}},o[t][0].call(n.exports,function(e){return a(o[t][1][e]||e)},n,n.exports,s,o,i,r)}return i[t].exports}for(var c="function"==typeof require&&require,e=0;ee.length)&&(t=e.length);for(var n=0,s=new Array(t);n: ").concat(h)),V.hasOwn(t.tags,"username")||(t.tags.username=J),t.tags["message-type"]="whisper";J=V.channel(t.tags.username);this.emits(["whisper","message"],[[J,t.tags,h,!1]]);break;case"PRIVMSG":t.tags.username=t.prefix.split("!")[0],"jtv"===t.tags.username?(c=V.username(h.split(" ")[0]),u=h.includes("auto"),h.includes("hosting you for")?(a=V.extractNumber(h),this.emit("hosted",m,c,a,u)):h.includes("hosting you")&&this.emit("hosted",m,c,0,u)):(a=V.get(this.opts.options.messagesLogLevel,"info"),c=V.actionMessage(h),t.tags["message-type"]=c?"action":"chat",h=c?c[1]:h,V.hasOwn(t.tags,"bits")?this.emit("cheer",m,t.tags,h):(V.hasOwn(t.tags,"msg-id")?"highlighted-message"===t.tags["msg-id"]?(u=t.tags["msg-id"],this.emit("redeem",m,t.tags.username,u,t.tags,h)):"skip-subs-mode-message"===t.tags["msg-id"]&&(l=t.tags["msg-id"],this.emit("redeem",m,t.tags.username,l,t.tags,h)):V.hasOwn(t.tags,"custom-reward-id")&&(l=t.tags["custom-reward-id"],this.emit("redeem",m,t.tags.username,l,t.tags,h)),c?(this.log[a]("[".concat(m,"] *<").concat(t.tags.username,">: ").concat(h)),this.emits(["action","message"],[[m,t.tags,h,!1]])):(this.log[a]("[".concat(m,"] <").concat(t.tags.username,">: ").concat(h)),this.emits(["chat","message"],[[m,t.tags,h,!1]]))));break;default:this.log.warn("Could not parse message:\n".concat(JSON.stringify(t,null,4)))}}},n.prototype.connect=function(){var s=this;return new Promise(function(t,n){s.server=V.get(s.opts.connection.server,"irc-ws.chat.twitch.tv"),s.port=V.get(s.opts.connection.port,80),s.secure&&(s.port=443),443===s.port&&(s.secure=!0),s.reconnectTimer=s.reconnectTimer*s.reconnectDecay,s.reconnectTimer>=s.maxReconnectInterval&&(s.reconnectTimer=s.maxReconnectInterval),s._openConnection(),s.once("_promiseConnect",function(e){e?n(e):t([s.server,~~s.port])})})},n.prototype._openConnection=function(){var e="".concat(this.secure?"wss":"ws","://").concat(this.server,":").concat(this.port,"/"),t={};"agent"in this.opts.connection&&(t.agent=this.opts.connection.agent),this.ws=new r(e,"irc",t),this.ws.onmessage=this._onMessage.bind(this),this.ws.onerror=this._onError.bind(this),this.ws.onclose=this._onClose.bind(this),this.ws.onopen=this._onOpen.bind(this)},n.prototype._onOpen=function(){var n=this;this._isConnected()&&(this.log.info("Connecting to ".concat(this.server," on port ").concat(this.port,"..")),this.emit("connecting",this.server,~~this.port),this.username=V.get(this.opts.identity.username,V.justinfan()),this._getToken().then(function(e){var t=V.password(e);n.log.info("Sending authentication to server.."),n.emit("logon");e="twitch.tv/tags twitch.tv/commands";n._skipMembership||(e+=" twitch.tv/membership"),n.ws.send("CAP REQ :"+e),t?n.ws.send("PASS ".concat(t)):V.isJustinfan(n.username)&&n.ws.send("PASS SCHMOOPIIE"),n.ws.send("NICK ".concat(n.username))}).catch(function(e){n.emits(["_promiseConnect","disconnected"],[[e],["Could not get a token."]])}))},n.prototype._getToken=function(){var e,t=this.opts.identity.password;return"function"==typeof t?(e=t())instanceof Promise?e:Promise.resolve(e):Promise.resolve(t)},n.prototype._onMessage=function(e){var t=this;e.data.trim().split("\r\n").forEach(function(e){e=q.msg(e);e&&t.handleMessage(e)})},n.prototype._onError=function(){var t=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),clearTimeout(this._updateEmotesetsTimer),this.reason=null===this.ws?"Connection closed.":"Unable to connect.",this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout(function(){t.reconnecting=!1,t.connect().catch(function(e){return t.log.error(e)})},this.reconnectTimer)),this.ws=null},n.prototype._onClose=function(){var t=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),clearTimeout(this._updateEmotesetsTimer),this.wasCloseCalled?(this.wasCloseCalled=!1,this.reason="Connection closed.",this.log.info(this.reason),this.emits(["_promiseConnect","_promiseDisconnect","disconnected"],[[this.reason],[null],[this.reason]])):(this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Could not connect to server. Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout(function(){t.reconnecting=!1,t.connect().catch(function(e){return t.log.error(e)})},this.reconnectTimer))),this.ws=null},n.prototype._getPromiseDelay=function(){return this.currentLatency<=600?600:this.currentLatency+100},n.prototype._sendCommand=function(s,o,i,r){var a=this;return new Promise(function(e,t){if(!a._isConnected())return t("Not connected to server.");var n;null!==s&&"number"!=typeof s||(null===s&&(s=a._getPromiseDelay()),V.promiseDelay(s).then(function(){return t("No response from Twitch.")})),null!==o?(n=V.channel(o),a.log.info("[".concat(n,"] Executing command: ").concat(i)),a.ws.send("PRIVMSG ".concat(n," :").concat(i))):(a.log.info("Executing command: ".concat(i)),a.ws.send(i)),"function"==typeof r?r(e,t):e()})},n.prototype._sendMessage=function(c,u,l,m){var h=this;return new Promise(function(e,t){if(!h._isConnected())return t("Not connected to server.");if(V.isJustinfan(h.getUsername()))return t("Cannot send anonymous messages.");var n,s=V.channel(u);h.userstate[s]||(h.userstate[s]={}),500<=l.length&&(n=V.splitLine(l,500),l=n[0],setTimeout(function(){h._sendMessage(c,u,n[1],function(){})},350)),h.ws.send("PRIVMSG ".concat(s," :").concat(l));var o={};Object.keys(h.emotesets).forEach(function(e){return h.emotesets[e].forEach(function(e){return(V.isRegex(e.code)?q.emoteRegex:q.emoteString)(l,e.code,e.id,o)})});var i=Object.assign(h.userstate[s],q.emotes({emotes:q.transformEmotes(o)||null})),r=V.get(h.opts.options.messagesLogLevel,"info"),a=V.actionMessage(l);a?(i["message-type"]="action",h.log[r]("[".concat(s,"] *<").concat(h.getUsername(),">: ").concat(a[1])),h.emits(["action","message"],[[s,i,a[1],!0]])):(i["message-type"]="chat",h.log[r]("[".concat(s,"] <").concat(h.getUsername(),">: ").concat(l)),h.emits(["chat","message"],[[s,i,l,!0]])),"function"==typeof m?m(e,t):e()})},n.prototype._updateEmoteset=function(s){var t,o=this,e=void 0!==s;e&&(s===this.emotes?e=!1:this.emotes=s),this._skipUpdatingEmotesets?e&&this.emit("emotesets",s,{}):(t=function(){0n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},o.prototype.once=function(e,t){if(!c(t))throw TypeError("listener must be a function");var n=!1;if(this._events.hasOwnProperty(e)&&"_"===e.charAt(0)){var s,o=1,i=e;for(s in this._events)this._events.hasOwnProperty(s)&&s.startsWith(i)&&o++;e+=o}function r(){"_"!==e.charAt(0)||isNaN(e.substr(e.length-1))||(e=e.substring(0,e.length-1)),this.removeListener(e,r),n||(n=!0,t.apply(this,arguments))}return r.listener=t,this.on(e,r),this},o.prototype.removeListener=function(e,t){var n,s,o,i;if(!c(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(o=(n=this._events[e]).length,s=-1,n===t||c(n.listener)&&n.listener===t){if(delete this._events[e],this._events.hasOwnProperty(e+"2")&&"_"===e.charAt(0)){var r,a=e;for(r in this._events)this._events.hasOwnProperty(r)&&r.startsWith(a)&&(isNaN(parseInt(r.substr(r.length-1)))||(this._events[e+parseInt(r.substr(r.length-1)-1)]=this._events[r],delete this._events[r]));this._events[e]=this._events[e+"1"],delete this._events[e+"1"]}this._events.removeListener&&this.emit("removeListener",e,t)}else if(u(n)){for(i=o;0n?(t.command=e.slice(n),t):null;for(t.command=e.slice(n,s),n=s+1;32===e.charCodeAt(n);)n++;for(;n").replace(/\\"\\;/g,'"').replace(/\\'\\;/g,"'")},unescapeIRC:function(e){return e&&"string"==typeof e&&e.includes("\\")?e.replace(i,function(e,t){return t in a?a[t]:t}):e},escapeIRC:function(e){return e&&"string"==typeof e?e.replace(r,function(e,t){return t in c?"\\".concat(c[t]):t}):e},addWord:function(e,t){return e.length?e+" "+t:e+t},splitLine:function(e,t){var n=e.substring(0,t).lastIndexOf(" ");return[e.substring(0,n=-1===n?t-1:n),e.substring(n+1)]},extractNumber:function(e){for(var t=e.split(" "),n=0;n { - is_enabled = cb_enabled.checked; - if(is_enabled) check_pendientes( ); -} ); - -btn_cancel.addEventListener('click', e => { - speechSynthesis.cancel( ); -} ); - -function talk( chat_message, voz = 'm', estado = 'default' ){ - speech_pendientes.push( { - msg: chat_message, - voice: voz, - config: estado - } ); - check_pendientes( ); -} - -function check_pendientes( ){ - if( is_enabled && speech_pendientes.length ){ - const pendiente = speech_pendientes.shift( ); - send_voice( pendiente.msg, pendiente.voice, pendiente.config ); - } -} - -function send_voice( chat_message, voz = 'm', animo = 'default' ){ - const locucion = new SpeechSynthesisUtterance( chat_message ); - const estado = estados[animo] ?? estados['default']; - locucion.rate = estado.rate; //velocidad - locucion.pitch = estado.pitch; //tono - locucion.voice = voz == 'm' ? - voces[ select_voces_m.value ]: - voces[ select_voces_f.value ]; - - speechSynthesis.speak( locucion ); - locucion.onend = function( ){ - check_pendientes( ); - } -} - -function getVoices( ){ - voces = speechSynthesis.getVoices( ); - let voces_guardadas = localStorage.getItem('tts_voces') ? - JSON.parse( localStorage.getItem('tts_voces') ) : - { "m": "", "f": "" }; - - - voces.map( (v, index) => { - //cargar el combo para elegir voz masculina - const option = document.createElement('option'); - option.value = index; - if( `${v.name} (${v.lang})` == voces_guardadas.m ){ - option.selected = true; - } - option.innerHTML = `${v.name} (${v.lang})`; - select_voces_m.appendChild(option); - - //cargar combo para elegir voz femenina - const option2 = document.createElement('option'); - option2.value = index; - if( `${v.name} (${v.lang})` == voces_guardadas.f ){ - option2.selected = true; - } - option2.innerHTML = `${v.name} (${v.lang})`; - select_voces_f.appendChild(option2); - } ); -} - -getVoices( ); -speechSynthesis.onvoiceschanged = getVoices; - -export { talk }; \ No newline at end of file