├── .gitignore ├── context.example.js ├── .env.example ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .DS_Store 4 | context.js -------------------------------------------------------------------------------- /context.example.js: -------------------------------------------------------------------------------- 1 | module.exports = 'You are a friendly chatbot in Discord.'; 2 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = 2 | OPENAI_ORGANIZATION = 3 | OPENAI_API_KEY = 4 | CHAT_BOT_CHANNEL = -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon", 8 | "start": "node ." 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "discord.js": "^14.7.1", 15 | "dotenv": "^16.0.3", 16 | "openai": "^3.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GPT 3.5 Discord Chat Bot 2 | 3 | ### How to use this bot 4 | 5 | 1. Clone the Github repo in your current directory 6 | 7 | ```bash 8 | git clone https://github.com/notunderctrl/discord-chat-bot.git . 9 | ``` 10 | 11 | --- 12 | 13 | 2. Install all the packages 14 | 15 | - If you're using npm 16 | 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | - If you're using yarn 22 | 23 | ```bash 24 | yarn 25 | ``` 26 | 27 | --- 28 | 29 | 3. Run the bot 30 | 31 | For development 32 | 33 | - Using npm 34 | 35 | ```bash 36 | npm run dev 37 | ``` 38 | 39 | - Using yarn 40 | 41 | ```bash 42 | yarn dev 43 | ``` 44 | 45 | For production 46 | 47 | - Using npm 48 | 49 | ```bash 50 | npm run start 51 | ``` 52 | 53 | - Using yarn 54 | 55 | ```bash 56 | yarn start 57 | ``` 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField, AttachmentBuilder } = require('discord.js'); 3 | const { Configuration, OpenAIApi } = require('openai'); 4 | const context = require('./context'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.MessageContent, 12 | ], 13 | }); 14 | 15 | const configuration = new Configuration({ 16 | organization: process.env.OPENAI_ORGANIZATION, 17 | apiKey: process.env.OPENAI_API_KEY, 18 | }); 19 | const openai = new OpenAIApi(configuration); 20 | 21 | // EVENTS 22 | client.on('ready', (c) => { 23 | console.log(`Logged in as ${c.user.tag}!`); 24 | }); 25 | 26 | const msgLengthLimit = 2000; 27 | client.on('messageCreate', async (message) => { 28 | try { 29 | if (message.author.bot) return; 30 | if (message.channel.id !== process.env.CHAT_BOT_CHANNEL) return; 31 | if (message.content.startsWith('!')) return; 32 | 33 | await message.channel.sendTyping(); 34 | 35 | if (message.content.length > msgLengthLimit) { 36 | message.reply("Whoa now, I'm not going to read all that. Maybe summarize?"); 37 | return; 38 | } 39 | 40 | let prevMessages = await message.channel.messages.fetch({ limit: 15 }); 41 | prevMessages = prevMessages.sort((a, b) => a - b); 42 | 43 | let conversationLog = [{ role: 'system', content: context }]; 44 | 45 | prevMessages.forEach((msg) => { 46 | if (msg.content.startsWith('!')) return; 47 | if (msg.content.length > msgLengthLimit) return; 48 | if (msg.author.id !== client.user.id && message.author.bot) return; 49 | 50 | // If msg is from the bot (client) itself 51 | if (msg.author.id === client.user.id) { 52 | conversationLog.push({ 53 | role: 'assistant', 54 | content: `${msg.content}`, 55 | }); 56 | } 57 | 58 | // If msg is from a regular user 59 | else { 60 | if (msg.author.id !== message.author.id) return; 61 | 62 | conversationLog.push({ 63 | role: 'user', 64 | content: `${msg.content}`, 65 | }); 66 | } 67 | }); 68 | 69 | const res = await openai.createChatCompletion({ 70 | model: 'gpt-3.5-turbo', 71 | messages: conversationLog, 72 | }); 73 | 74 | let reply = res.data.choices[0].message?.content; 75 | 76 | if (reply?.length > 2000) { 77 | // If the reply length is over 2000 characters, send a txt file. 78 | const buffer = Buffer.from(reply, 'utf8'); 79 | const txtFile = new AttachmentBuilder(buffer, { name: `${message.author.tag}_response.txt` }); 80 | 81 | message.reply({ files: [txtFile] }).catch(() => { 82 | message.channel.send({ content: `${message.author}`, files: [txtFile] }); 83 | }); 84 | } else { 85 | message.reply(reply).catch(() => { 86 | message.channel.send(`${message.author} ${reply}`); 87 | }); 88 | } 89 | } catch (error) { 90 | message.reply(`Something went wrong. Try again in a bit.`).then((msg) => { 91 | setTimeout(async () => { 92 | await msg.delete().catch(() => null); 93 | }, 5000); 94 | }); 95 | 96 | console.log(`Error: ${error}`); 97 | } 98 | }); 99 | 100 | client.login(process.env.TOKEN); 101 | --------------------------------------------------------------------------------