#!/usr/bin/env node //IRC bot that responds to !chat, queries the chatgpt api, and prints the response line by line const irc = require('irc'); const axios = require('axios'); const config = require('./config.json'); const client = new irc.Client(config.server, config.nick, { channels: config.channels, }); // listen for messages that start with !chat and call the chatgpt api with a callback that prints the response line by line client.addListener('message', async (from, to, message) => { if (message.startsWith('!chat')) { const query = message.slice(6); chatgpt(query, (line) => { client.say(to, line); }); } }); // function that calls the chatgpt streaming api (with server send events) and calls the callback function for each line function chatgpt(query, callback) { const apiUrl = 'https://api.openai.com/v1/chat/completions'; const response = await axios.post(apiUrl, { messages: [{ role: 'user', content: query }], model: 'gpt-3.5-turbo', stream: true, }, { headers: { Authorization: `Bearer ${config.openaiApiKey}`, 'Content-Type': 'application/json', }, responseType: 'stream', }); let line = ''; response.data.on('data', (event) => { let data = event.toString(); let parts = data.split('\n'); // parse if starts with data: for(part of parts) { if(part === 'data: [DONE]') { process.stdout.write('\n'); } else if(part.startsWith('data: ')) { let jsonString = part.slice(part.indexOf('{'), part.lastIndexOf('}') + 1); try { let json = JSON.parse(jsonString); let chunk = json.choices[0].delta.content; //split the chunk into lines leaving the delimiter in the array const lines = str.split(/\r?\n/); // split by new lines let hasStartNewline = str.startsWith("\n"); let hasEndNewline = str.endsWith("\n"); if(hasStartNewline) { callback(line); line = ''; } for (let i = 0; i < lines.length - 1; i++) { callback(lines[i]); line = ''; } line += lines[lines.length - 1]; if(hasEndNewline) { callback(line); line = ''; } } catch (e) { console.log(e); console.log(part); } } } }); }