HeyHeyBot is a simple Discord bot with soundboard functionality that enhances your voice chat experience. Whenever a user joins a voice chat, the bot plays a customizable greeting audio for that user, announcing their arrival to others in the channel.
- Personalized Greetings: Assign a unique audio file for each user. When they join the voice chat, their greeting is played.
- Soundboard Functionality: By sending
!playsoundto a chat on Discord server, you can request buttons with the names of available sounds. Clicking a button will play the corresponding sound in the voice chat. - Automatic Audio Playback: The bot joins the voice channel to play audio, ensuring a seamless experience for users.
- Webserver for Audio Uploads: Application includes a webserver that allows users to upload their audio files with ease if enabled.
- User-Specific Greetings: Through the web interface, you can:
- Upload custom greeting sounds for specific Discord users
- Set existing soundboard sounds as user greetings
- View and manage greeting history with automatic versioning
- HTTPS Support: Secure your web interface with SSL/TLS encryption for added security.
Get your Discord bot token from Discord Developer Portal. Token can be requested by creating a new application and clicking Reset Token button on a Bot page (link to this page looks like this: https://discord.com/developers/applications/{APPLICATION_ID}/bot).
Easiest way to run the bot is using Docker.
Change docker-compose.yml file to match your settings (example below):
services:
heyheybot:
build:
context: .
dockerfile: Dockerfile
container_name: HeyHeyBot
environment:
- DISCORD_TOKEN=PLACE_YOUR_DISCORD_TOKEN_HERE
- DISCORD_CONTINUE_PRESENCE=True
- DISCORD_MUTING_ANNOUNCE=False
# - WEBPAGE_USERNAME=YOUR_WEBPAGE_USERNAME
# - WEBPAGE_PASSWORD=YOUR_WEBPAGE_PASSWORD
# - WEBPAGE_HOST=localhost
# - WEBPAGE_PORT=5100
# - SSL_CERT=/path/to/cert.pem
# - SSL_KEY=/path/to/key.pem
volumes:
- ./data:/app/data
- ./logs:/app/logs
# For SSL certificates:
# - ./certs:/app/certs
# ports:
# - 5100:5100
restart: unless-stoppedYou can set environment variables in .env file in the root directory of the project. Or you can set them directly in docker-compose.yml file.
Possible environment variables:
DISCORD_TOKEN- Discord bot token (required)DISCORD_CONTINUE_PRESENCE- Whether bot will leave voice channel after playing audio (default:False)DISCORD_MUTING_ANNOUNCE- Whether to announce muting/unmuting (default:True)DISCORD_ARRIVAL_ANNOUNCE- Whether to announce user arrivals (default:True)DISCORD_LEAVE_ANNOUNCE- Whether to announce user departures (default:True,DISCORD_LEAVING_ANNOUNCEis accepted as an alias)DISCORD_ANNOUNCE_COOLDOWN- Seconds before the same user can trigger the same announcement again,0disables it (default:5)DISCORD_VOLUME- Playback volume,1.0is the file's own level (default:1.0). Keeping it at1.0is noticeably cheaper on CPU, see PerformanceDISCORD_LOGLEVEL- Logging level (default:WARNING)WEBPAGE_USERNAME- Username for a webpage where you can upload files (required for the webserver to start)WEBPAGE_PASSWORD- Password for this webpage (required for the webserver to start)WEBPAGE_HOST- Host for this webpage (defaultlocalhost, set to something like0.0.0.0if you want to access webpage from outside)WEBPAGE_PORT- Port for webserver to use (default5100, also uncommentportssection indocker-compose.yml)SSL_CERT- Path to SSL certificate file (optional, for HTTPS support)SSL_KEY- Path to SSL private key file (optional, for HTTPS support)SECRET_KEY- Fixed key for signing web sessions (optional; without it everyone is logged out on restart)MAX_UPLOAD_MB- Maximum upload size in megabytes (default:16)TRUSTED_PROXIES- Number of reverse proxies in front of the panel (default:0, set to1behind Traefik/nginx, see Behind a reverse proxy)SESSION_COOKIE_SECURE- Force theSecureflag on the session cookie (default: on when SSL orTRUSTED_PROXIESis set)UPLOAD_FOLDER/GREETINGS_FOLDER- Override where soundboard and greeting files live (optional)PROXY_URL- Proxy for the Discord API and gateway, e.g.http://host:3128orsocks5://host:1080(optional, see Using a proxy)PROXY_USERNAME/PROXY_PASSWORD- Credentials for that proxy (optional)
Then run the following command in the root directory of the project:
docker compose up -d- Soundboard: Store greeting audios in the
./data/audiodirectory. Only.wavfiles are supported. - Announcements: Store announcement audios in the
./data/greetings,./data/leavingsand./data/mutingsdirectories. Default files should behello.wav,bye.wavandmuted.wavrespectively. Custom greeting files are automatically versioned and stored as{discord_name}.wavfor current greetings and{discord_name}.{version}.wavfor previous versions. Only.wavfiles are supported. - Naming greetings: a greeting is matched by Discord user id first (
123456789012345678.wav), then by user name (cooldiscordname.wav). The user id never changes when someone renames themselves, so it is the more reliable option - right-click a user in Discord with Developer Mode on and pickCopy User ID. Non-ASCII names (Cyrillic, CJK) are supported.
Example directory structure:
data
βββ audio
β βββ fuze.wav
β βββ amogus.wav
β βββ pew.wav
β βββ ...
βββ greetings
β βββ hello.wav
β βββ cooldiscordname.wav
β βββ cooldiscordname.1.wav
β βββ cooldiscordname.2.wav
β βββ ...
βββ leavings
β βββ bye.wav
β βββ cooldiscordname.wav
β βββ ...
βββ mutings
βββ muted.wav
βββ cooldiscordname.wav
βββ ...
To enable HTTPS:
- Prepare your SSL certificate and private key files (e.g., using Let's Encrypt)
- Add the certificate files to your Docker volume by adding this to docker-compose.yml:
volumes: - ./certs:/app/certs
- Set the SSL environment variables in docker-compose.yml:
environment: - SSL_CERT=/app/certs/cert.pem - SSL_KEY=/app/certs/key.pem
- Restart the container for changes to take effect
- Go to Discord Developer Portal and select your application.
- Go to
OAuth2tab and selectURL Generator. - Select
botscope and set bot permissions. At leastRead Messages/View Channels,Send Messages in Threads,Connect,Speak,Use Voice ActivityandPriority Speakerpermissions are required. - Copy the generated link and paste it in your browser. Select the server you want to add the bot to and click
Authorize. - Bot should now be visible in the server's member list.
Logs are stored in the ./logs directory and rotated by size (1 MB). History of 5 logs is kept.
You can use web interface to manage audio files and user greetings. The interface provides:
-
Soundboard Management:
- Upload new sounds to the soundboard
- Play and delete existing sounds
- Set any soundboard sound as a user's greeting
-
User Greetings Management:
- Upload custom greeting sounds for specific Discord users
- View all users' greeting sounds with version history
- Play and delete greeting sounds
- Automatic versioning of greeting sounds (old versions are preserved)
When file is uploaded it automatically converts to .wav. Use !playsound in Discord chat to request for a new updated soundboard buttons.
WEBPAGE_USERNAME and WEBPAGE_PASSWORD is required for a webserver to start. When it starts you can access it via browser:
- HTTP:
http://{WEBPAGE_HOST}:{WEBPAGE_PORT}/ - HTTPS (if configured):
https://{WEBPAGE_HOST}:{WEBPAGE_PORT}/
Default location is http://localhost:5100/ or https://localhost:5100/ if HTTPS is enabled.
Uploaded files will be automatically converted to WAV and volume will be normalized to -16.
- Join a voice chat and experience personalized greetings!
- Trigger the soundboard by typing !playsound and click on the displayed buttons to play the sounds from
./data/audiodirectory. Soundboard will be send to chat as a message and will remain there. If you update the./data/audiodirectory, you need to request new soundboard by typing!playsoundagain. - Upload new audio to soundboard via webpage if you have set it up.
- Set custom greeting sounds for specific users through the web interface:
- Upload a new sound directly as a greeting
- Set an existing soundboard sound as a greeting
- View and manage greeting history for each user
The bot handles multiple servers independently - each server gets its own voice connection and its own playback queue. The audio files themselves are shared between servers.
To serve the panel on your own domain with automatic HTTPS, put it behind a reverse proxy instead of using
SSL_CERT/SSL_KEY. Two settings matter:
WEBPAGE_HOST=0.0.0.0- with the defaultlocalhostthe panel only listens inside the container and the proxy cannot reach it.TRUSTED_PROXIES=1- makes the panel readX-Forwarded-For/X-Forwarded-Proto. Without it every request appears to come from the proxy itself, so one failed-login lockout would lock out every user at once, and generated URLs would sayhttp.
Drop the ports: mapping once the proxy is in front, otherwise the panel stays reachable over plain HTTP on that
port, bypassing TLS.
Example with Traefik (assumes Traefik already runs with a webnet network and a
myresolver certificate resolver):
services:
heyheybot:
image: heyheybot:latest
container_name: HeyHeyBot
environment:
- DISCORD_TOKEN=${DISCORD_TOKEN}
- WEBPAGE_USERNAME=${WEBPAGE_USERNAME}
- WEBPAGE_PASSWORD=${WEBPAGE_PASSWORD}
# Must listen on all interfaces for Traefik to reach it
- WEBPAGE_HOST=0.0.0.0
- WEBPAGE_PORT=5100
# Traefik terminates TLS and is the only hop in front of us
- TRUSTED_PROXIES=1
# Keeps logins alive across restarts
- SECRET_KEY=${SECRET_KEY}
volumes:
- ./data:/app/data
- ./logs:/app/logs
# No ports: - Traefik reaches the container over the shared network
labels:
- "traefik.enable=true"
- "traefik.docker.network=webnet"
- "traefik.http.routers.heyheybot.rule=Host(`hey.example.com`)"
- "traefik.http.routers.heyheybot.entrypoints=websecure"
- "traefik.http.routers.heyheybot.tls.certresolver=myresolver"
- "traefik.http.services.heyheybot.loadbalancer.server.port=5100"
networks:
- webnet
restart: unless-stopped
networks:
webnet:
external: trueThe panel is now internet-facing, so use a long WEBPAGE_PASSWORD. The built-in lockout (5 failed attempts,
5 minute block) applies per client IP once TRUSTED_PROXIES is set.
If the Discord API is not reachable directly, set PROXY_URL to an HTTP or SOCKS proxy:
environment:
# HTTP proxy
- PROXY_URL=http://proxy.example.com:3128
# or a SOCKS5 proxy
# - PROXY_URL=socks5://proxy.example.com:1080
# Credentials can be given separately instead of inline in the URL
# - PROXY_USERNAME=user
# - PROXY_PASSWORD=secretSupported schemes are http, https, socks4, socks4a, socks5 and socks5h. SOCKS support comes from
aiohttp-socks, which is already in requirements.txt.
Important: the proxy covers the REST API and the gateway websocket only. Voice traffic stays direct, so the bot still needs outbound UDP access to play audio.
To be precise about why: SOCKS5 can proxy UDP - the protocol has a
UDP ASSOCIATEcommand (RFC 1928). It does not help here for two independent reasons:
discord.pyopens the voice socket itself, as a plainsocket.socket(AF_INET, SOCK_DGRAM)indiscord/voice_state.py. It never asks the HTTP connector, so no proxy setting can reach it.- The libraries in use are TCP-only anyway:
aiohttp_socks.ProxyConnectorsubclassesaiohttp.TCPConnector, andpython-socksdefinesUDP_ASSOCIATE = 0x03as a constant without any connector ever sending it.HTTP proxies cannot do it at all -
CONNECTis TCP-only (UDP would need CONNECT-UDP/MASQUE over HTTP/3).If UDP is blocked on your network, route the whole container through a VPN (WireGuard/OpenVPN) or a transparent
tun2socks-style layer instead of settingPROXY_URL.
- Audio is streamed straight from disk by FFmpeg; nothing is preloaded into memory.
- At
DISCORD_VOLUME=1.0FFmpeg encodes Opus itself, so Python never touches the audio stream. Any other volume forces per-packet re-encoding in Python, which costs noticeably more CPU. Prefer normalizing the files themselves (the web panel already does this on upload) over changing the volume. - Playback is serialized per server and waits for the real end of the sound, so announcements queue up instead of cutting each other off.
DISCORD_ANNOUNCE_COOLDOWNprevents a user who rejoins in a loop from making the bot spam a channel.- Announcements are skipped entirely when nobody is in the target channel to hear them.
- The web panel caches directory listings and serves audio with revalidation headers.
python3 -m unittest discover -s tests -vThe web panel tests need flask and ffmpeg installed; they are skipped when ffmpeg is missing.
You can use FFmpeg to convert audio files to WAV format.
Simpliest command:
ffmpeg -i "sound.mp3" "sound.wav"Changing volume to 70% of original:
ffmpeg -i "sound.mp3" -af "volume=0.7" "sound.wav"Trimming audio from 0 to 4 seconds:
ffmpeg -i "sound.mp3" -ss 0 -to 4 "sound.wav"If we also want it to fading out at the end (fade out starts at 3 seconds and lasts 1 second):
ffmpeg -i "sound.mp3" -ss 0 -to 4 -af "afade=t=out:st=3:d=1" "sound.wav"Learn more about FFmpeg here.
It is possible that some of your audio files will be louder than others. You can use volume_normalization.py script to normalize volume of all audio files in a given directory. It uses ffmpeg to do it, so you need to have it installed. Just run the script, it will ask you for the directory with audio files and then it will normalize them.
It works only with .wav files.