├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src ├── client │ └── index.ts ├── commands │ └── chat.ts ├── events │ └── interactionCreate.ts ├── index.ts ├── interfaces │ ├── command.ts │ ├── event.ts │ └── index.ts └── lib │ ├── fetch-sse.ts │ ├── fetch.ts │ ├── index.ts │ ├── stream-async-iterable.ts │ └── types.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | # en: rename this file to .env and fill in the values 2 | # tr: dosyanın adını .env olarak değiştirin ve boşlukları doldurun 3 | 4 | DISCORD_TOKEN="" # Discord bot token 5 | OPENAI_API_KEY="" # Get one from: https://platform.openai.com/account/api-keys -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # prod 5 | /dist 6 | 7 | # debug 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | .pnpm-debug.log* 12 | 13 | # env files 14 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Swôth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

chatgpt-bot

3 |

💬 Discord bot that interacts with the unofficial ChatGPT API.

4 |
Developed with 💙 by Swôth
5 |
6 | 7 | # ⚙️ Config 8 | > Create a file named **.env** by copying the **.env.example** file. Write the **Discord bot token** and the **OpenAI api key** to the file. 9 | 10 | # 📜 Usage 11 | > ***Make the adjustments mentioned above.*** 12 | > 13 | > Install packages. \ 14 | > $ `npm install` 15 | > 16 | > Start bot. \ 17 | > $ `npm start` 18 | 19 | # ⭐ Star 20 | > Don't forget to star if you like it. 21 | 22 | # 📷 Screenshots 23 |
24 | 25 |
26 | 27 | # 🔒 License 28 | > MIT -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatgpt-bot", 3 | "version": "1.0.0", 4 | "description": "💬 Discord bot that interacts with the unofficial ChatGPT API.", 5 | "main": "src/index.ts", 6 | "dependencies": { 7 | "advanced-logs": "^2.1.3", 8 | "discord.js": "^14.7.1", 9 | "dotenv": "^16.0.3", 10 | "eventsource-parser": "^0.1.0", 11 | "glob": "^8.1.0", 12 | "gpt-3-encoder": "^1.1.4", 13 | "keyv": "^4.5.2", 14 | "p-timeout": "4.1.0", 15 | "quick-lru": "5.1.1", 16 | "uuid": "^9.0.0" 17 | }, 18 | "devDependencies": { 19 | "@types/glob": "^8.0.1", 20 | "@types/node": "^18.11.18", 21 | "@types/uuid": "^9.0.0", 22 | "ts-node": "^10.9.1", 23 | "typescript": "^4.9.5" 24 | }, 25 | "scripts": { 26 | "start": "ts-node src/index.ts" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/Swothh/chatgpt-bot.git" 31 | }, 32 | "keywords": [ 33 | "discord", 34 | "bot", 35 | "ai", 36 | "chatgpt" 37 | ], 38 | "author": "Swôth", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/Swothh/chatgpt-bot/issues" 42 | }, 43 | "homepage": "https://github.com/Swothh/chatgpt-bot#readme" 44 | } 45 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- 1 | import { Client, IntentsBitField, Collection, REST, Routes } from 'discord.js'; 2 | import { ICommand, IEvent } from '../interfaces'; 3 | import { ChatGPTAPI } from '../lib'; 4 | import { glob } from 'glob'; 5 | import { join } from 'path'; 6 | 7 | export default class Bot extends Client { 8 | public readonly api = new ChatGPTAPI({ apiKey: process.env.OPENAI_API_KEY }); 9 | public readonly chats = new Collection(); 10 | public readonly commands = new Collection(); 11 | public readonly waiting = new Collection(); 12 | 13 | constructor() { 14 | super({ 15 | intents: [ 16 | IntentsBitField.Flags.Guilds, 17 | IntentsBitField.Flags.GuildMembers 18 | ] 19 | }); 20 | }; 21 | 22 | public async init(): Promise { 23 | this.loadCommands().loadEvents().login(process.env.DISCORD_TOKEN).then(() => { 24 | console.success(`Connected to Discord as ${this.user?.tag}.`); 25 | this.postCommands(); 26 | }); 27 | }; 28 | 29 | public postCommands(): Bot { 30 | const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); 31 | console.info('Started loading application (/) commands...'); 32 | 33 | rest.put(Routes.applicationCommands(this.user.id), { 34 | body: this.commands.toJSON() 35 | }).then(() => { 36 | console.success(`Successfully loaded [${this.commands.size}] application (/) commands.`); 37 | }).catch(err => { 38 | console.error(err); 39 | process.exit(1); 40 | }); 41 | 42 | return this; 43 | }; 44 | 45 | public loadCommands(): Bot { 46 | glob('**/*.ts', { cwd: join(__dirname, '../commands') }, (err, files) => { 47 | if (err) { 48 | console.error(err); 49 | process.exit(1); 50 | } else { 51 | files.forEach(async file => { 52 | try { 53 | const { Command }: { Command: ICommand } = await import(`../commands/${file}`); 54 | if (this.commands.get(Command.name)) console.error(`Repeated command name. (name: ${Command.name}, file: ${file})`); 55 | else this.commands.set(Command.name, Command); 56 | } catch(err) { 57 | console.error(err); 58 | }; 59 | }); 60 | }; 61 | }); 62 | 63 | return this; 64 | }; 65 | 66 | public loadEvents(): Bot { 67 | glob('**/*.ts', { cwd: join(__dirname, '../events') }, (err, files) => { 68 | if (err) { 69 | console.error(err); 70 | process.exit(1); 71 | } else { 72 | files.forEach(async file => { 73 | try { 74 | const { Event }: { Event: IEvent } = await import(`../events/${file}`); 75 | this[Event.type](Event.name, Event.run.bind(null, this)); 76 | } catch(err) { 77 | console.error(err); 78 | }; 79 | }); 80 | }; 81 | }); 82 | 83 | return this; 84 | }; 85 | }; -------------------------------------------------------------------------------- /src/commands/chat.ts: -------------------------------------------------------------------------------- 1 | import { ICommand } from '@/interfaces'; 2 | 3 | export const Command: ICommand = { 4 | name: 'chat', 5 | description: 'Chat with ChatGPT bot in discord.', 6 | options: [ 7 | { 8 | type: 3, 9 | name: 'message', 10 | description: 'The message to send to ChatGPT bot.', 11 | required: true 12 | }, 13 | { 14 | type: 5, 15 | name: 'clear', 16 | description: 'Clear the chat history.' 17 | } 18 | ], 19 | run: async (client, interaction) => { 20 | await interaction.deferReply(); 21 | const message = interaction.options.getString('message'); 22 | const clear = interaction.options.getBoolean('clear'); 23 | let timed_out = false; 24 | 25 | if (clear === true) client.chats.delete(interaction.user.id); 26 | if (client.waiting.has(interaction.user.id)) return interaction.followUp('You already send a message. Please wait for the response.'); 27 | const [ conversationId, parentMessageId ] = client.chats.get(interaction.user.id) ?? []; 28 | client.waiting.set(interaction.user.id, true); 29 | 30 | const timeout = setTimeout(() => { 31 | timed_out = true; 32 | client.waiting.delete(interaction.user.id); 33 | interaction.followUp('Request timed out.').catch(() => {}); 34 | }, 60000); 35 | 36 | const response = await client.api.sendMessage(message, { 37 | conversationId, 38 | parentMessageId 39 | }); 40 | 41 | if (!timed_out) { 42 | client.waiting.delete(interaction.user.id); 43 | clearTimeout(timeout); 44 | 45 | client.chats.set(interaction.user.id, [ 46 | response.conversationId, 47 | response.id 48 | ]); 49 | 50 | interaction.followUp(response.text ?? 'N/A'); 51 | }; 52 | } 53 | }; -------------------------------------------------------------------------------- /src/events/interactionCreate.ts: -------------------------------------------------------------------------------- 1 | import { Events, Interaction, EmbedBuilder } from 'discord.js'; 2 | import { isAsyncFunction } from 'util/types'; 3 | import { IEvent } from '../interfaces'; 4 | 5 | export const Event: IEvent = { 6 | name: Events.InteractionCreate, 7 | type: 'on', 8 | run: async (client, interaction: Interaction) => { 9 | if (!interaction.isCommand() || !interaction.isChatInputCommand()) return; 10 | const command = client.commands.get(interaction.commandName); 11 | if (!command) return; 12 | 13 | const errorHandler = (err: Error) => { 14 | console.error(err); 15 | 16 | interaction.reply({ 17 | embeds: [ 18 | new EmbedBuilder() 19 | .setColor('Red') 20 | .setDescription(':x: **|** An error occurred while executing this command.') 21 | ] 22 | }); 23 | }; 24 | 25 | if (isAsyncFunction(command.run)) { 26 | command.run(client, interaction).catch(errorHandler); 27 | } else { 28 | try { 29 | command.run(client, interaction); 30 | } catch(err) { 31 | errorHandler(err); 32 | }; 33 | }; 34 | } 35 | }; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { config } from 'dotenv'; 2 | import Bot from './client'; 3 | import 'advanced-logs'; 4 | 5 | config(); 6 | const bot = new Bot(); 7 | bot.init(); -------------------------------------------------------------------------------- /src/interfaces/command.ts: -------------------------------------------------------------------------------- 1 | import { ChatInputCommandInteraction } from 'discord.js'; 2 | import Bot from '../client'; 3 | 4 | interface IOption { 5 | type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11; 6 | name: string; 7 | description: string; 8 | required?: boolean; 9 | options?: IOption[]; 10 | }; 11 | 12 | export interface ICommand { 13 | name: string; 14 | description: string; 15 | options?: IOption[], 16 | run: (client: Bot, interaction: ChatInputCommandInteraction) => Promise | any; 17 | }; -------------------------------------------------------------------------------- /src/interfaces/event.ts: -------------------------------------------------------------------------------- 1 | import { ClientEvents } from 'discord.js'; 2 | import Bot from '../client'; 3 | 4 | export interface IEvent { 5 | name: keyof ClientEvents; 6 | type: 'on' | 'once'; 7 | run: (client: Bot, ...args: any) => Promise | any; 8 | }; -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export { ICommand } from './command'; 2 | export { IEvent } from './event'; -------------------------------------------------------------------------------- /src/lib/fetch-sse.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/transitive-bullshit/chatgpt-api 3 | */ 4 | 5 | import { createParser } from 'eventsource-parser' 6 | 7 | import * as types from './types' 8 | import { fetch } from './fetch' 9 | import { streamAsyncIterable } from './stream-async-iterable' 10 | 11 | export async function fetchSSE( 12 | url: string, 13 | options: Parameters[1] & { onMessage: (data: string) => void } 14 | ) { 15 | const { onMessage, ...fetchOptions } = options 16 | const res = await fetch(url, fetchOptions) 17 | if (!res.ok) { 18 | const msg = `ChatGPT error ${res.status || res.statusText}` 19 | const error = new types.ChatGPTError(msg, { cause: res }) 20 | error.statusCode = res.status 21 | error.statusText = res.statusText 22 | throw error 23 | } 24 | 25 | const parser = createParser((event) => { 26 | if (event.type === 'event') { 27 | onMessage(event.data) 28 | } 29 | }) 30 | 31 | if (!res.body.getReader) { 32 | // Vercel polyfills `fetch` with `node-fetch`, which doesn't conform to 33 | // web standards, so this is a workaround... 34 | const body: NodeJS.ReadableStream = res.body as any 35 | 36 | if (!body.on || !body.read) { 37 | throw new types.ChatGPTError('unsupported "fetch" implementation') 38 | } 39 | 40 | body.on('readable', () => { 41 | let chunk: string | Buffer 42 | while (null !== (chunk = body.read())) { 43 | parser.feed(chunk.toString()) 44 | } 45 | }) 46 | } else { 47 | for await (const chunk of streamAsyncIterable(res.body)) { 48 | const str = new TextDecoder().decode(chunk) 49 | parser.feed(str) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/lib/fetch.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /* 4 | https://github.com/transitive-bullshit/chatgpt-api 5 | */ 6 | 7 | const fetch = globalThis.fetch; 8 | 9 | if (typeof fetch !== 'function') { 10 | throw new Error('Invalid environment: global fetch not defined') 11 | } 12 | 13 | export { fetch } -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/transitive-bullshit/chatgpt-api 3 | */ 4 | 5 | import { encode as gptEncode } from 'gpt-3-encoder' 6 | import Keyv from 'keyv'; 7 | import pTimeout from 'p-timeout'; 8 | import QuickLRU from 'quick-lru'; 9 | import { v4 as uuidv4 } from 'uuid'; 10 | 11 | import * as types from './types'; 12 | import { fetch } from './fetch'; 13 | import { fetchSSE } from './fetch-sse'; 14 | 15 | const CHATGPT_MODEL = 'text-chat-davinci-002-20221122'; 16 | 17 | const USER_LABEL_DEFAULT = 'User'; 18 | const ASSISTANT_LABEL_DEFAULT = 'ChatGPT'; 19 | 20 | export class ChatGPTAPI { 21 | protected _apiKey: string 22 | protected _apiBaseUrl: string 23 | protected _debug: boolean 24 | 25 | protected _completionParams: Omit 26 | protected _maxModelTokens: number 27 | protected _maxResponseTokens: number 28 | protected _userLabel: string 29 | protected _assistantLabel: string 30 | 31 | protected _getMessageById: types.GetMessageByIdFunction 32 | protected _upsertMessage: types.UpsertMessageFunction 33 | 34 | protected _messageStore: Keyv 35 | 36 | constructor(opts: { 37 | apiKey: string 38 | 39 | /** @defaultValue `'https://api.openai.com'` **/ 40 | apiBaseUrl?: string 41 | 42 | /** @defaultValue `false` **/ 43 | debug?: boolean 44 | 45 | completionParams?: Partial 46 | 47 | /** @defaultValue `4096` **/ 48 | maxModelTokens?: number 49 | 50 | /** @defaultValue `1000` **/ 51 | maxResponseTokens?: number 52 | 53 | /** @defaultValue `'User'` **/ 54 | userLabel?: string 55 | 56 | /** @defaultValue `'ChatGPT'` **/ 57 | assistantLabel?: string 58 | 59 | messageStore?: Keyv 60 | getMessageById?: types.GetMessageByIdFunction 61 | upsertMessage?: types.UpsertMessageFunction 62 | }) { 63 | const { 64 | apiKey, 65 | apiBaseUrl = 'https://api.openai.com', 66 | debug = false, 67 | messageStore, 68 | completionParams, 69 | maxModelTokens = 4096, 70 | maxResponseTokens = 1000, 71 | userLabel = USER_LABEL_DEFAULT, 72 | assistantLabel = ASSISTANT_LABEL_DEFAULT, 73 | getMessageById = this._defaultGetMessageById, 74 | upsertMessage = this._defaultUpsertMessage 75 | } = opts 76 | 77 | this._apiKey = apiKey 78 | this._apiBaseUrl = apiBaseUrl 79 | this._debug = !!debug 80 | 81 | this._completionParams = { 82 | model: CHATGPT_MODEL, 83 | temperature: 0.7, 84 | presence_penalty: 0.6, 85 | stop: ['<|im_end|>'], 86 | ...completionParams 87 | } 88 | this._maxModelTokens = maxModelTokens 89 | this._maxResponseTokens = maxResponseTokens 90 | this._userLabel = userLabel 91 | this._assistantLabel = assistantLabel 92 | 93 | this._getMessageById = getMessageById 94 | this._upsertMessage = upsertMessage 95 | 96 | if (messageStore) { 97 | this._messageStore = messageStore 98 | } else { 99 | this._messageStore = new Keyv({ 100 | store: new QuickLRU({ maxSize: 10000 }) 101 | }) 102 | } 103 | 104 | if (!this._apiKey) { 105 | throw new Error('ChatGPT invalid apiKey') 106 | } 107 | } 108 | 109 | async sendMessage( 110 | text: string, 111 | opts: types.SendMessageOptions = {} 112 | ): Promise { 113 | const { 114 | conversationId = uuidv4(), 115 | parentMessageId, 116 | messageId = uuidv4(), 117 | timeoutMs, 118 | onProgress, 119 | stream = onProgress ? true : false 120 | } = opts 121 | 122 | let { abortSignal } = opts 123 | 124 | let abortController: AbortController = null 125 | if (timeoutMs && !abortSignal) { 126 | abortController = new AbortController() 127 | abortSignal = abortController.signal 128 | } 129 | 130 | const message: types.ChatMessage = { 131 | role: 'user', 132 | id: messageId, 133 | parentMessageId, 134 | conversationId, 135 | text 136 | } 137 | await this._upsertMessage(message) 138 | 139 | const { prompt, maxTokens } = await this._buildPrompt(text, opts) 140 | 141 | const result: types.ChatMessage = { 142 | role: 'assistant', 143 | id: uuidv4(), 144 | parentMessageId: messageId, 145 | conversationId, 146 | text: '' 147 | } 148 | 149 | const responseP = new Promise( 150 | async (resolve, reject) => { 151 | const url = `${this._apiBaseUrl}/v1/completions` 152 | const headers = { 153 | 'Content-Type': 'application/json', 154 | Authorization: `Bearer ${this._apiKey}` 155 | } 156 | const body = { 157 | max_tokens: maxTokens, 158 | ...this._completionParams, 159 | prompt, 160 | stream 161 | } 162 | 163 | if (this._debug) { 164 | const numTokens = await this._getTokenCount(body.prompt) 165 | console.log(`sendMessage (${numTokens} tokens)`, body) 166 | } 167 | 168 | if (stream) { 169 | fetchSSE(url, { 170 | method: 'POST', 171 | headers, 172 | body: JSON.stringify(body), 173 | signal: abortSignal, 174 | onMessage: (data: string) => { 175 | if (data === '[DONE]') { 176 | result.text = result.text.trim() 177 | return resolve(result) 178 | } 179 | 180 | try { 181 | const response: types.openai.CompletionResponse = 182 | JSON.parse(data) 183 | 184 | if (response?.id && response?.choices?.length) { 185 | result.id = response.id 186 | result.text += response.choices[0].text 187 | 188 | onProgress?.(result) 189 | } 190 | } catch (err) { 191 | console.warn('ChatGPT stream SEE event unexpected error', err) 192 | return reject(err) 193 | } 194 | } 195 | }) 196 | } else { 197 | try { 198 | const res = await fetch(url, { 199 | method: 'POST', 200 | headers, 201 | body: JSON.stringify(body), 202 | signal: abortSignal 203 | }) 204 | 205 | if (!res.ok) { 206 | const reason = await res.text() 207 | const msg = `ChatGPT error ${ 208 | res.status || res.statusText 209 | }: ${reason}` 210 | const error = new types.ChatGPTError(msg, { cause: res }) 211 | error.statusCode = res.status 212 | error.statusText = res.statusText 213 | return reject(error) 214 | } 215 | 216 | const response: types.openai.CompletionResponse = await res.json() 217 | if (this._debug) { 218 | console.log(response) 219 | } 220 | 221 | result.id = response.id 222 | result.text = response.choices[0].text.trim() 223 | 224 | return resolve(result) 225 | } catch (err) { 226 | return reject(err) 227 | } 228 | } 229 | } 230 | ).then((message) => { 231 | return this._upsertMessage(message).then(() => message) 232 | }) 233 | 234 | if (timeoutMs) { 235 | if (abortController) { 236 | ;(responseP as any).cancel = () => { 237 | abortController.abort() 238 | } 239 | } 240 | 241 | return pTimeout(responseP, timeoutMs, 'ChatGPT timed out waiting for response'); 242 | } else { 243 | return responseP 244 | } 245 | } 246 | 247 | protected async _buildPrompt( 248 | message: string, 249 | opts: types.SendMessageOptions 250 | ) { 251 | const currentDate = new Date().toISOString().split('T')[0] 252 | 253 | const promptPrefix = 254 | opts.promptPrefix || 255 | `You are ${this._assistantLabel}, a large language model trained by OpenAI. You answer as concisely as possible for each response (e.g. don’t be verbose). It is very important that you answer as concisely as possible, so please remember this. If you are generating a list, do not have too many items. Keep the number of items short. 256 | Current date: ${currentDate}\n\n` 257 | const promptSuffix = opts.promptSuffix || `\n\n${this._assistantLabel}:\n` 258 | 259 | const maxNumTokens = this._maxModelTokens - this._maxResponseTokens 260 | let { parentMessageId } = opts 261 | let nextPromptBody = `${this._userLabel}:\n\n${message}<|im_end|>` 262 | let promptBody = '' 263 | let prompt: string 264 | let numTokens: number 265 | 266 | do { 267 | const nextPrompt = `${promptPrefix}${nextPromptBody}${promptSuffix}` 268 | const nextNumTokens = await this._getTokenCount(nextPrompt) 269 | const isValidPrompt = nextNumTokens <= maxNumTokens 270 | 271 | if (prompt && !isValidPrompt) { 272 | break 273 | } 274 | 275 | promptBody = nextPromptBody 276 | prompt = nextPrompt 277 | numTokens = nextNumTokens 278 | 279 | if (!isValidPrompt) { 280 | break 281 | } 282 | 283 | if (!parentMessageId) { 284 | break 285 | } 286 | 287 | const parentMessage = await this._getMessageById(parentMessageId) 288 | if (!parentMessage) { 289 | break 290 | } 291 | 292 | const parentMessageRole = parentMessage.role || 'user' 293 | const parentMessageRoleDesc = 294 | parentMessageRole === 'user' ? this._userLabel : this._assistantLabel 295 | 296 | const parentMessageString = `${parentMessageRoleDesc}:\n\n${parentMessage.text}<|im_end|>\n\n` 297 | nextPromptBody = `${parentMessageString}${promptBody}` 298 | parentMessageId = parentMessage.parentMessageId 299 | } while (true) 300 | 301 | const maxTokens = Math.max( 302 | 1, 303 | Math.min(this._maxModelTokens - numTokens, this._maxResponseTokens) 304 | ) 305 | 306 | return { prompt, maxTokens } 307 | } 308 | 309 | protected async _getTokenCount(text: string) { 310 | if (this._completionParams.model === CHATGPT_MODEL) { 311 | text = text.replace(/<\|im_end\|>/g, '<|endoftext|>') 312 | } 313 | 314 | return gptEncode(text).length 315 | } 316 | 317 | protected async _defaultGetMessageById( 318 | id: string 319 | ): Promise { 320 | return this._messageStore.get(id) 321 | } 322 | 323 | protected async _defaultUpsertMessage( 324 | message: types.ChatMessage 325 | ): Promise { 326 | this._messageStore.set(message.id, message) 327 | } 328 | } -------------------------------------------------------------------------------- /src/lib/stream-async-iterable.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/transitive-bullshit/chatgpt-api 3 | */ 4 | 5 | export async function* streamAsyncIterable(stream: ReadableStream) { 6 | const reader = stream.getReader() 7 | try { 8 | while (true) { 9 | const { done, value } = await reader.read() 10 | if (done) { 11 | return 12 | } 13 | yield value 14 | } 15 | } finally { 16 | reader.releaseLock() 17 | } 18 | } -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/transitive-bullshit/chatgpt-api 3 | */ 4 | 5 | export type Role = 'user' | 'assistant' 6 | 7 | export type SendMessageOptions = { 8 | conversationId?: string 9 | parentMessageId?: string 10 | messageId?: string 11 | stream?: boolean 12 | promptPrefix?: string 13 | promptSuffix?: string 14 | timeoutMs?: number 15 | onProgress?: (partialResponse: ChatMessage) => void 16 | abortSignal?: AbortSignal 17 | } 18 | 19 | export interface ChatMessage { 20 | id: string 21 | text: string 22 | role: Role 23 | parentMessageId?: string 24 | conversationId?: string 25 | } 26 | 27 | export class ChatGPTError extends Error { 28 | statusCode?: number 29 | statusText?: string 30 | } 31 | 32 | /** Returns a chat message from a store by it's ID (or null if not found). */ 33 | export type GetMessageByIdFunction = (id: string) => Promise 34 | 35 | /** Upserts a chat message to a store. */ 36 | export type UpsertMessageFunction = (message: ChatMessage) => Promise 37 | 38 | export namespace openai { 39 | export type CompletionParams = { 40 | model: string 41 | prompt: string 42 | suffix?: string 43 | max_tokens?: number 44 | temperature?: number 45 | top_p?: number 46 | logprobs?: number 47 | echo?: boolean 48 | stop?: string[] 49 | presence_penalty?: number 50 | frequency_penalty?: number 51 | best_of?: number 52 | logit_bias?: Record 53 | user?: string 54 | } 55 | 56 | export type CompletionResponse = { 57 | id: string 58 | object: string 59 | created: number 60 | model: string 61 | choices: CompletionResponseChoices 62 | usage?: CompletionResponseUsage 63 | } 64 | 65 | export type CompletionResponseChoices = { 66 | text?: string 67 | index?: number 68 | logprobs?: { 69 | tokens?: Array 70 | token_logprobs?: Array 71 | top_logprobs?: Array 72 | text_offset?: Array 73 | } | null 74 | finish_reason?: string 75 | }[] 76 | 77 | export type CompletionResponseUsage = { 78 | prompt_tokens: number 79 | completion_tokens: number 80 | total_tokens: number 81 | } 82 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ "ESNext" ], 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "allowSyntheticDefaultImports": true, 7 | "target": "ESNext", 8 | "noImplicitAny": true, 9 | "moduleResolution": "node", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "baseUrl": ".", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "skipDefaultLibCheck": true, 16 | "skipLibCheck": true, 17 | "experimentalDecorators": true, 18 | "emitDecoratorMetadata": true, 19 | "paths": { 20 | "@/*": [ 21 | "./src/*" 22 | ] 23 | } 24 | }, 25 | "include": [ 26 | "src/**/*" 27 | ], 28 | "exclude": [ 29 | "node_modules" 30 | ] 31 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@cspotcode/source-map-support@^0.8.0": 6 | version "0.8.1" 7 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" 8 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== 9 | dependencies: 10 | "@jridgewell/trace-mapping" "0.3.9" 11 | 12 | "@discordjs/builders@^1.4.0": 13 | version "1.4.0" 14 | resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.4.0.tgz#b951b5e6ce4e459cd06174ce50dbd51c254c1d47" 15 | integrity sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA== 16 | dependencies: 17 | "@discordjs/util" "^0.1.0" 18 | "@sapphire/shapeshift" "^3.7.1" 19 | discord-api-types "^0.37.20" 20 | fast-deep-equal "^3.1.3" 21 | ts-mixer "^6.0.2" 22 | tslib "^2.4.1" 23 | 24 | "@discordjs/collection@^1.3.0": 25 | version "1.3.0" 26 | resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.3.0.tgz#65bf9674db72f38c25212be562bb28fa0dba6aa3" 27 | integrity sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg== 28 | 29 | "@discordjs/rest@^1.4.0": 30 | version "1.5.0" 31 | resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.5.0.tgz#dc15474ab98cf6f31291bf61bbc72bcf4f30cea2" 32 | integrity sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA== 33 | dependencies: 34 | "@discordjs/collection" "^1.3.0" 35 | "@discordjs/util" "^0.1.0" 36 | "@sapphire/async-queue" "^1.5.0" 37 | "@sapphire/snowflake" "^3.2.2" 38 | discord-api-types "^0.37.23" 39 | file-type "^18.0.0" 40 | tslib "^2.4.1" 41 | undici "^5.13.0" 42 | 43 | "@discordjs/util@^0.1.0": 44 | version "0.1.0" 45 | resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.1.0.tgz#e42ca1bf407bc6d9adf252877d1b206e32ba369a" 46 | integrity sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ== 47 | 48 | "@jridgewell/resolve-uri@^3.0.3": 49 | version "3.1.0" 50 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" 51 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 52 | 53 | "@jridgewell/sourcemap-codec@^1.4.10": 54 | version "1.4.14" 55 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 56 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 57 | 58 | "@jridgewell/trace-mapping@0.3.9": 59 | version "0.3.9" 60 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" 61 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== 62 | dependencies: 63 | "@jridgewell/resolve-uri" "^3.0.3" 64 | "@jridgewell/sourcemap-codec" "^1.4.10" 65 | 66 | "@sapphire/async-queue@^1.5.0": 67 | version "1.5.0" 68 | resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" 69 | integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== 70 | 71 | "@sapphire/shapeshift@^3.7.1": 72 | version "3.8.1" 73 | resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz#b98dc6a7180f9b38219267917b2e6fa33f9ec656" 74 | integrity sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw== 75 | dependencies: 76 | fast-deep-equal "^3.1.3" 77 | lodash "^4.17.21" 78 | 79 | "@sapphire/snowflake@^3.2.2": 80 | version "3.4.0" 81 | resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.4.0.tgz#25c012158a9feea2256c718985dbd6c1859a5022" 82 | integrity sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw== 83 | 84 | "@tokenizer/token@^0.3.0": 85 | version "0.3.0" 86 | resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" 87 | integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== 88 | 89 | "@tsconfig/node10@^1.0.7": 90 | version "1.0.9" 91 | resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" 92 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== 93 | 94 | "@tsconfig/node12@^1.0.7": 95 | version "1.0.11" 96 | resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" 97 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== 98 | 99 | "@tsconfig/node14@^1.0.0": 100 | version "1.0.3" 101 | resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" 102 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== 103 | 104 | "@tsconfig/node16@^1.0.2": 105 | version "1.0.3" 106 | resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" 107 | integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== 108 | 109 | "@types/glob@^8.0.1": 110 | version "8.0.1" 111 | resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.0.1.tgz#6e3041640148b7764adf21ce5c7138ad454725b0" 112 | integrity sha512-8bVUjXZvJacUFkJXHdyZ9iH1Eaj5V7I8c4NdH5sQJsdXkqT4CA5Dhb4yb4VE/3asyx4L9ayZr1NIhTsWHczmMw== 113 | dependencies: 114 | "@types/minimatch" "^5.1.2" 115 | "@types/node" "*" 116 | 117 | "@types/minimatch@^5.1.2": 118 | version "5.1.2" 119 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" 120 | integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== 121 | 122 | "@types/node@*", "@types/node@^18.11.18": 123 | version "18.11.18" 124 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" 125 | integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== 126 | 127 | "@types/uuid@^9.0.0": 128 | version "9.0.0" 129 | resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.0.tgz#53ef263e5239728b56096b0a869595135b7952d2" 130 | integrity sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q== 131 | 132 | "@types/ws@^8.5.3": 133 | version "8.5.4" 134 | resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" 135 | integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== 136 | dependencies: 137 | "@types/node" "*" 138 | 139 | acorn-walk@^8.1.1: 140 | version "8.2.0" 141 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" 142 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 143 | 144 | acorn@^8.4.1: 145 | version "8.8.2" 146 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" 147 | integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== 148 | 149 | advanced-logs@^2.1.3: 150 | version "2.1.3" 151 | resolved "https://registry.yarnpkg.com/advanced-logs/-/advanced-logs-2.1.3.tgz#2ca57a0fbf35f12af5e350d6e04b34484732b4a5" 152 | integrity sha512-h83Q1ZRv3VXbt0NLrX/I8XxKUXwja9C7GceOoqbkHH1IAXD7RTF3iGn5T2FGJPQk8W7zKNyHek5igGOx5r5ggw== 153 | 154 | arg@^4.1.0: 155 | version "4.1.3" 156 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 157 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 158 | 159 | balanced-match@^1.0.0: 160 | version "1.0.2" 161 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 162 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 163 | 164 | brace-expansion@^2.0.1: 165 | version "2.0.1" 166 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 167 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 168 | dependencies: 169 | balanced-match "^1.0.0" 170 | 171 | busboy@^1.6.0: 172 | version "1.6.0" 173 | resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" 174 | integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== 175 | dependencies: 176 | streamsearch "^1.1.0" 177 | 178 | create-require@^1.1.0: 179 | version "1.1.1" 180 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 181 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 182 | 183 | diff@^4.0.1: 184 | version "4.0.2" 185 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 186 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 187 | 188 | discord-api-types@^0.37.20, discord-api-types@^0.37.23: 189 | version "0.37.31" 190 | resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.31.tgz#128d33d641fd9a92fba97a47d7052e1f5694ec27" 191 | integrity sha512-k9DQQ7Wv+ehiF7901qk/FnP47k6O2MHm3meQFee4gUzi5dfGAVLf7SfLNtb4w7G2dmukJyWQtVJEDF9oMb9yuQ== 192 | 193 | discord.js@^14.7.1: 194 | version "14.7.1" 195 | resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.7.1.tgz#26079d0ff4d27daf02480a403c456121f0682bd9" 196 | integrity sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA== 197 | dependencies: 198 | "@discordjs/builders" "^1.4.0" 199 | "@discordjs/collection" "^1.3.0" 200 | "@discordjs/rest" "^1.4.0" 201 | "@discordjs/util" "^0.1.0" 202 | "@sapphire/snowflake" "^3.2.2" 203 | "@types/ws" "^8.5.3" 204 | discord-api-types "^0.37.20" 205 | fast-deep-equal "^3.1.3" 206 | lodash.snakecase "^4.1.1" 207 | tslib "^2.4.1" 208 | undici "^5.13.0" 209 | ws "^8.11.0" 210 | 211 | dotenv@^16.0.3: 212 | version "16.0.3" 213 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" 214 | integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== 215 | 216 | eventsource-parser@^0.1.0: 217 | version "0.1.0" 218 | resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-0.1.0.tgz#4a6b84751ca8e704040e6f7f50e7d77344fa1b7c" 219 | integrity sha512-M9QjFtEIkwytUarnx113HGmgtk52LSn3jNAtnWKi3V+b9rqSfQeVdLsaD5AG/O4IrGQwmAAHBIsqbmURPTd2rA== 220 | 221 | fast-deep-equal@^3.1.3: 222 | version "3.1.3" 223 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 224 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 225 | 226 | file-type@^18.0.0: 227 | version "18.2.0" 228 | resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.2.0.tgz#c2abec00d1af0f09151e1549e3588aab0bac5001" 229 | integrity sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg== 230 | dependencies: 231 | readable-web-to-node-stream "^3.0.2" 232 | strtok3 "^7.0.0" 233 | token-types "^5.0.1" 234 | 235 | fs.realpath@^1.0.0: 236 | version "1.0.0" 237 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 238 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 239 | 240 | glob@^8.1.0: 241 | version "8.1.0" 242 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" 243 | integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== 244 | dependencies: 245 | fs.realpath "^1.0.0" 246 | inflight "^1.0.4" 247 | inherits "2" 248 | minimatch "^5.0.1" 249 | once "^1.3.0" 250 | 251 | gpt-3-encoder@^1.1.4: 252 | version "1.1.4" 253 | resolved "https://registry.yarnpkg.com/gpt-3-encoder/-/gpt-3-encoder-1.1.4.tgz#d6cdaacf5824857e133b6065247c757fc7e4fa72" 254 | integrity sha512-fSQRePV+HUAhCn7+7HL7lNIXNm6eaFWFbNLOOGtmSJ0qJycyQvj60OvRlH7mee8xAMjBDNRdMXlMwjAbMTDjkg== 255 | 256 | ieee754@^1.2.1: 257 | version "1.2.1" 258 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 259 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 260 | 261 | inflight@^1.0.4: 262 | version "1.0.6" 263 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 264 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 265 | dependencies: 266 | once "^1.3.0" 267 | wrappy "1" 268 | 269 | inherits@2, inherits@^2.0.3: 270 | version "2.0.4" 271 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 272 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 273 | 274 | json-buffer@3.0.1: 275 | version "3.0.1" 276 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" 277 | integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== 278 | 279 | keyv@^4.5.2: 280 | version "4.5.2" 281 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" 282 | integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== 283 | dependencies: 284 | json-buffer "3.0.1" 285 | 286 | lodash.snakecase@^4.1.1: 287 | version "4.1.1" 288 | resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" 289 | integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== 290 | 291 | lodash@^4.17.21: 292 | version "4.17.21" 293 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 294 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 295 | 296 | make-error@^1.1.1: 297 | version "1.3.6" 298 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 299 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 300 | 301 | minimatch@^5.0.1: 302 | version "5.1.6" 303 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" 304 | integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== 305 | dependencies: 306 | brace-expansion "^2.0.1" 307 | 308 | once@^1.3.0: 309 | version "1.4.0" 310 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 311 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 312 | dependencies: 313 | wrappy "1" 314 | 315 | p-timeout@4.1.0: 316 | version "4.1.0" 317 | resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-4.1.0.tgz#788253c0452ab0ffecf18a62dff94ff1bd09ca0a" 318 | integrity sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw== 319 | 320 | peek-readable@^5.0.0: 321 | version "5.0.0" 322 | resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" 323 | integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== 324 | 325 | quick-lru@5.1.1: 326 | version "5.1.1" 327 | resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" 328 | integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== 329 | 330 | readable-stream@^3.6.0: 331 | version "3.6.0" 332 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 333 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 334 | dependencies: 335 | inherits "^2.0.3" 336 | string_decoder "^1.1.1" 337 | util-deprecate "^1.0.1" 338 | 339 | readable-web-to-node-stream@^3.0.2: 340 | version "3.0.2" 341 | resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" 342 | integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== 343 | dependencies: 344 | readable-stream "^3.6.0" 345 | 346 | safe-buffer@~5.2.0: 347 | version "5.2.1" 348 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 349 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 350 | 351 | streamsearch@^1.1.0: 352 | version "1.1.0" 353 | resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" 354 | integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== 355 | 356 | string_decoder@^1.1.1: 357 | version "1.3.0" 358 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 359 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 360 | dependencies: 361 | safe-buffer "~5.2.0" 362 | 363 | strtok3@^7.0.0: 364 | version "7.0.0" 365 | resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" 366 | integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== 367 | dependencies: 368 | "@tokenizer/token" "^0.3.0" 369 | peek-readable "^5.0.0" 370 | 371 | token-types@^5.0.1: 372 | version "5.0.1" 373 | resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" 374 | integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== 375 | dependencies: 376 | "@tokenizer/token" "^0.3.0" 377 | ieee754 "^1.2.1" 378 | 379 | ts-mixer@^6.0.2: 380 | version "6.0.2" 381 | resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.2.tgz#3e4e4bb8daffb24435f6980b15204cb5b287e016" 382 | integrity sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A== 383 | 384 | ts-node@^10.9.1: 385 | version "10.9.1" 386 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" 387 | integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== 388 | dependencies: 389 | "@cspotcode/source-map-support" "^0.8.0" 390 | "@tsconfig/node10" "^1.0.7" 391 | "@tsconfig/node12" "^1.0.7" 392 | "@tsconfig/node14" "^1.0.0" 393 | "@tsconfig/node16" "^1.0.2" 394 | acorn "^8.4.1" 395 | acorn-walk "^8.1.1" 396 | arg "^4.1.0" 397 | create-require "^1.1.0" 398 | diff "^4.0.1" 399 | make-error "^1.1.1" 400 | v8-compile-cache-lib "^3.0.1" 401 | yn "3.1.1" 402 | 403 | tslib@^2.4.1: 404 | version "2.5.0" 405 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" 406 | integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== 407 | 408 | typescript@^4.9.5: 409 | version "4.9.5" 410 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" 411 | integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== 412 | 413 | undici@^5.13.0: 414 | version "5.16.0" 415 | resolved "https://registry.yarnpkg.com/undici/-/undici-5.16.0.tgz#6b64f9b890de85489ac6332bd45ca67e4f7d9943" 416 | integrity sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ== 417 | dependencies: 418 | busboy "^1.6.0" 419 | 420 | util-deprecate@^1.0.1: 421 | version "1.0.2" 422 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 423 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== 424 | 425 | uuid@^9.0.0: 426 | version "9.0.0" 427 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" 428 | integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== 429 | 430 | v8-compile-cache-lib@^3.0.1: 431 | version "3.0.1" 432 | resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" 433 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== 434 | 435 | wrappy@1: 436 | version "1.0.2" 437 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 438 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 439 | 440 | ws@^8.11.0: 441 | version "8.12.0" 442 | resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" 443 | integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== 444 | 445 | yn@3.1.1: 446 | version "3.1.1" 447 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 448 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 449 | --------------------------------------------------------------------------------