Leaves One

Alan Richard's Blog

Build a Poe Bot That Plays Bad Apple Using Django

In this tutorial, we’ll build a fun bot that plays ASCII art videos (Bad Apple) on Poe using Django.

Bad Apple Video Played by Poe Bot

Try it out!

Overview

The Poe platform allows developers to create bots that users can interact with in the app. Poe provides a specification that bots need to implement to communicate properly.

Here, I use Django to handle the HTTP requests from Poe server and stream back ASCII art video (specifically, Bad Apple) frames rendered from a source video file. The key ideas are:

  • Use the StreamingHttpResponse to send back server-sent events
  • Read the ASCII art video frames from a text file and send them back one by one
  • Use the replace_response event to update the displayed frames

Project Setup

First, install the dependencies:

pip install django sse-starlette

Decouple reads config values from a .env file. Sse-starlette helps generate server-sent events.

Then, you need a Django project. Run:

django-admin startproject badapplebot

Then create an app:

cd badapplebot
python manage.py startapp chat

Implementing the Bot Logic

In views.py, we’ll handle the API requests:

chat/views.py

# Import dependencies
import json
import os
import time

from django.http import StreamingHttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from sse_starlette import ServerSentEvent


# Config
VIDEO_PATH = "resources/badapple_poe.txt"

# View function to handle requests
@csrf_exempt
def stream(request):

# Stream response
def query_event_stream():
# Send initial metadata
yield ServerSentEvent(data=json.dumps(
{
"content_type": "text/markdown", # For code block rendering
"refetch_settings": True,
"linkify": True,
"suggested_replies": False,
}
), event="meta").encode()

# Main logic

return StreamingHttpResponse(query_event_stream(), content_type="text/event-stream")

The key points:

  • query_event_stream handles creation of response events
  • Use a generator function to lazily yield events
  • StreamingHttpResponse streams the events

Next, we open the video file and yield the frames:

chat/views.py

def query_event_stream():
# ...

# Main logic
with open(VIDEO_PATH, 'r') as f:
ascii_frame = ''
for line in f:
if line.strip() == "<END_FRAME>":
# If the frame is done, send it to the client
# Delay a bit
time.sleep(1/24)
# Send the frame with the replace_response event
yield ServerSentEvent(data=json.dumps({"text": "```badapple_poe\n" + ascii_frame + "```"}), event="replace_response").encode()
ascii_frame = '' # Empty the frame for the next one
else:
ascii_frame += line

The video file contains the pre-rendered ASCII frames. We sleep briefly between frames to maintain the video FPS. The replace_response event swaps the displayed frame.

Finally, send the done event to end streaming:

chat/views.py

def query_event_stream():
# ...

# Main logic
# ...

# End response
yield ServerSentEvent(data="{}", event="done").encode()

Creating the Video File

The video file contains the ASCII art frames. Use the following script to convert a video file to ASCII art frames:

https://github.com/richardhyy/badapple-poe-bot/blob/main/tools/video2ascii.py

Example usage:

python ./video2ascii.py -v bad_apple.mp4 -o bad_apple.txt

Put the video file in the resources folder.

Your project structure should look like this:

badapplebot
├── badapplebot
│   ├── ...
├── chat
│   ├── ...
├── resources
│   └── bad_apple.txt # Your video file
└── manage.py

Configuring Django

In settings.py:

INSTALLED_APPS = [
# 'django.contrib.staticfiles',
'ascii_video_bot',
]

...

ALLOWED_HOSTS = ['your_server_ip']

...

DEBUG = False # On production
SECRET_KEY = config('SECRET_KEY')

Add the bot’s URL to urls.py:

from django.urls import path
from . import views

urlpatterns = [
path('chat/poe', views.stream),
]

Full Code

The full code is available at this repo.

It also features the following improvements to code in this tutorial:

  • Access Key authentication
  • Use python-decouple to read config values from a .env file
  • Introduction message

Deploying the Bot

You can deploy the bot to any Django-compatible hosting.

Once deployed, you can add the bot to Poe by creating a server bot with URL https://your_server_ip/chat/poe.

Create a Server Bot on Poe

The bot will now respond to requests from Poe!

Test chat

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
If you checked “Remember me”, your email address and name will be stored in your browser for your convenience.