- Introduction
- The Source Code
- Package.json
- Index.js
- Commands
- Docker File
- GitHub Actions
- Usage Via Node
- Usage Via Docker
Adding ChatGPT and OpenAI To Your Discord Server
I love writing Discord bots, my favorite one was the Discord Twitter Bot. Now that ChatGPT API is available to developers I decided to create an OpenAI ChatGPT discord bot in Node.js and share the source code with you. This bot adds a slash command to your server called /generate-prompt that takes in a prompt string and the bot returns a result using the Text Completion API.
The Source Code
This is a dockerized node.js application that uses GitHub actions to deploy the Docker container to Docker Hub and GitHub Container Registry. The index.js file loads an instance of OpenAI and Discord.js, loads the slash commands from a commands
directory and registers them with Discord. It then listens for interactions i.e. a user using the slash command and then calls the generate
method to use the gpt-3.5-turbo
OpenAI language model to generate a response and reply to that message in Discord.
Listen To Some Hacker Music While You Code
Package.json
Below is an example of how you might want your package.json file to look like.
{
"name": "discord-gpt-bot",
"version": "1.1.0",
"description": "Add ChatGPT to your Discord server. Responds with a ChatGPT generated text when @.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mastashake08/discord-gpt-bot.git"
},
"keywords": [
"OpenAI",
"ChatGPT",
"gpt",
"discord",
"bots",
"discord"
],
"author": "Mastashake08",
"license": "GPL3",
"bugs": {
"url": "https://github.com/mastashake08/discord-gpt-bot/issues"
},
"homepage": "https://github.com/mastashake08/discord-gpt-bot#readme",
"dependencies": {
"discord.js": "^14.7.1",
"dotenv": "^16.0.3",
"openai": "^3.2.1"
}
}
Index File
This is where most of the magic happens, the index.js file loads our slash commands starts OpenAI, and starts the Discord.js instance. All secret keys and tokens are loaded using the dotenv package from a .env file. The generate
function makes a call to the OpenAI.createCompletion()
function which returns our text completion.
require('dotenv').config()
const { Client, Events, Collection, REST, Routes } = require('discord.js');
const fs = require('node:fs');
const path = require('node:path');
const { Configuration, OpenAIApi } = require("openai")
const client = new Client({ intents: 2048 })
client.commands = new Collection()
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
async function generate(prompt, model="gpt-3.5-turbo") {
const completion = await openai.createCompletion({
model: model,
prompt: prompt
});
const text = completion.data.choices[0].text
return text;
}
// start the discord client and listen for messages
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
const commands = []
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
// Construct and prepare an instance of the REST module
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(process.env.DISCORD_CLIENT_ID, process.env.DISCORD_GUILD_ID),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
client.login(process.env.DISCORD_TOKEN)
client.on(Events.InteractionCreate, async interaction => {
if (interaction.commandName === 'generate-prompt') {
const res = await generate(interaction.options.getString('prompt'))
await interaction.reply({ content: res });
}
});
Commands
I created a commands
directory and inside created a prompt.js
file. This file is responsible for using the SlashCommandBuilder
class from DIscord.js to create our command and options.
require('dotenv').config()
const { Configuration, OpenAIApi } = require("openai")
const { SlashCommandBuilder } = require('discord.js');
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
async function generate(prompt, model="gpt-3.5-turbo") {
const completion = await openai.createCompletion({
model: model,
prompt: prompt
});
const text = completion.data.choices[0].text
return text;
}
module.exports = {
data: new SlashCommandBuilder()
.setName('generate-prompt')
.setDescription('Generate a ChatGPT reponse!')
.addStringOption(option =>
option
.setName('prompt')
.setDescription('The prompt to generate')
.setRequired(true)
),
async execute(interaction) {
const res = await generate(interaction.options.getString('prompt'))
await interaction.reply(res);
},
};
Dockerfile
I created a Dockerfile so that anyone can run this application without having to build from source. It creates a node 16 image, copies the code files over, runs npm install
, then runs the command. By passing in an --env-file
flag to the docker run
command will pass in a .env file to the script.
# syntax=docker/dockerfile:1
FROM node:16.15.0
ENV NODE_ENV=production
WORKDIR /
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production
COPY . .
CMD [ "node", "index.js" ]
GitHub Actions
Automating the build process is the final part of the project. Whenever I release a new tagged version of the code, the GitHub action packages the Docker Image and publishes it to Docker Hub as well as Github Container Registry. From there I either run the docker image locally on my Raspberry Pi or I will run it in the cloud.
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.
name: Publish Docker image to Docker Hub
on:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.
name: Create and publish a Docker image to Github Packages
on:
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Usage
Via Node
cp .env.example .env
# Set variables
DISCORD_TOKEN=
DISCORD_CHANNEL_ID=
DISCORD_CLIENT_ID=
DISCORD_GUILD_ID=
OPENAI_API_KEY=
# Call program
node index.js
Via Docker
docker run --env-file=<PATH TO .ENV> -d --name=<NAME> mastashake08/discord-gpt-bot:latest
Follow Me On Social Media
Follow Me On Youtube!
Get Your Next Domain Cheap & Support The Channel
I use Namecheap for all of my domains! Whenever I need a cheap solution for a proof-of-concept project I grab a domain name for as little as $1! When you sign up and buy your first domain with Namecheap I get a commission, it’s a great way to get a quality service and support this platform!
Get Your Next Domain Cheap
CLICK HERE
Become A Sponsor
Open-source work is free to use but it is not free to develop. If you enjoy my content and would like to see more please consider becoming a sponsor on Github or Patreon! Not only do you support me but you are funding tech programs for at risk youth in Louisville, Kentucky.
Join The Newsletter
By joining the newsletter, you get first access to all of my blogs, events, and other brand-related content delivered directly to your inbox. It’s 100% free and you can opt out at any time!
Check The Shop
You can also consider visiting the official #CodeLife shop! I have my own clothing/accessory line for techies as well as courses designed by me covering a range of software engineering topics.
-
Product on saleGPT Genie: A Solo Developer’s Guide to Mastering ChatGPT$1.99
-
These Fingers Unisex t-shirt$23.55 – $31.55
-
#CodeLife AirPods case$15.00
-
Embroidered #CodeLife Champion Backpack$44.50
-
#CodeLife Laptop Sleeve$25.00 – $28.00
-
#CodeLife Unisex T-Shirt$20.00 – $22.00
-
#CodeLife Unisex Joggers$31.50 – $33.50
-
Cuffed #CodeLife Beanie$20.00
-
Unisex #CodeLife Hoodie$36.50 – $38.50