All workDiscord bot
052024

GPT Oracle Discord Bot

GPT and DALL·E inside Discord, built Lite for small servers and Ultra to scale.

A dual-tier Discord AI bot built for fast, clean, command-driven UX: Lite keeps setup dead simple with local key storage, Ultra moves keys to a remote SQL database for secure multi-user scale.

Year
2024
Role
Development
Built with
PythonDiscordOpenAI APISQL
A friendly robot on a glowing platform between server racks, beside chat and Discord icons
About the bot

Two builds, one clean command line

GPT Oracle is a dual-tier Discord AI bot built for fast, clean, command-driven UX. Lite keeps setup dead simple with locally stored API keys for solo users and small servers. Ultra moves key management to a remote SQL database, for secure, concurrent use and smooth performance on large servers.

2
Builds
Lite and Ultra
3
Commands
!save_key, !ask, !draw
16k
Context
gpt-3.5-turbo-16k

The commands

01

!save_key

Stores your OpenAI key once, locally in Lite or in your own SQL database in Ultra, so it is never pasted again.

02

!ask

Puts any question to GPT and answers in the channel, split cleanly across Discord's message limit.

03

!draw

Turns a prompt into an image with DALL·E, straight into the conversation.

04

Never blocks

Calls run off the event loop with a typing indicator, so the bot stays responsive while it thinks.

main.pyPython
@bot.command()async def ask(ctx, *, question):    # Get the API key for the author of the message    api_key = await get_api_key(ctx.message.author.id)    if not api_key:        await ctx.send(f"{ctx.author.mention}, No API key found for your account. Please save your API key using the !save_key command.")        return    if bot.is_first_message:        await ctx.send("Attention all! This is a fresh instance of the bot, so previous conversations have been forgotten!")        bot.is_first_message = False    loop = asyncio.get_event_loop()    # Create a task for showing the typing indicator    typing_task = bot.loop.create_task(typing_indicator(ctx))    try:        # Run receive_input in the executor to avoid blocking the event loop        replies = await loop.run_in_executor(executor, receive_input, api_key, question)        for reply in replies:            mention = f"{ctx.author.mention}, "            # Split the reply into chunks and send each one separately            for part in get_chunks(reply, limit=2000-len(mention)):                await ctx.send(mention + part)    except Exception as e:        await ctx.send(f"{ctx.author.mention}, An error occurred: {str(e)}")    finally:        # Ensure the typing indicator is cancelled even if an error occurs        typing_task.cancel()

The !ask command: look up the caller's key, show typing, answer off the event loop, and chunk the reply under Discord's limit.

Read the code

Both builds, with setup notes, are on GitHub.