Trishna is a modular personal bot platform written in Go. It runs independent adapters in one process where possible, so one platform can fail without stopping the others. It currently includes Discord and Telegram adapters plus a separate Discord chat bot:
- Trishna — Discord slash commands, Telegram owner commands, host
/status, YouTube webhook notifications - shawnb — Ollama AI chat bot with a gitignored
SOUL.mdpersonality file, DMs and allowed channels, chat logs, reminders, and owner alerts
- Discord gateway startup with
disgo - Environment-only config
- Slash command sync
- Module registry
- Discord
/pingcommand - Telegram owner-only
/pingcommand through long polling /statuscommand (bot uptime, version, process and host CPU/RAM/disk stats, services)- YouTube RSS poller with Discord webhook notifications
- YouTube live chat stream bot with basic
!commands - Podman Linux image with Trishna and shawnb binaries
- Separate Discord bot process
- Replies to allowed users in DMs and configured channels
- Local Ollama inference (
gemma4:e2bby default) - Personality from gitignored
SOUL.md(seeSOUL.md.example) - Natural-language reminders persisted to JSON and delivered on schedule
- Owner DM alerts when users send urgent or important messages
- Append-only JSONL chat logs under
data/shawnb/chats/ /resetslash command to clear in-memory history for a conversation
Copy .env.example values into your runtime environment:
DISCORD_TRISHNA_TOKEN=your-trishna-bot-token
DISCORD_GUILD_ID=optional-dev-guild-id
LOG_LEVEL=info
TELEGRAM_TRISHNA_TOKEN=your-telegram-bot-token
TELEGRAM_OWNER_USER_IDS=your-telegram-user-id
TELEGRAM_ALLOWED_CHAT_IDS=optional-chat-id
TELEGRAM_TGNET_REVEAL_IPS=false
TELEGRAM_TRANSPORT=botapi
TELEGRAM_MTPROTO_APP_ID=your-telegram-api-id
TELEGRAM_MTPROTO_APP_HASH=your-telegram-api-hash
TELEGRAM_MTPROTO_SESSION_PATH=data/telegram/mtproto-session.json
TELEGRAM_MTPROTO_PROXY_ENABLED=true
TELEGRAM_MTPROTO_PROXY_HOST=your-mtproxy-host
TELEGRAM_MTPROTO_PROXY_PORT=443
TELEGRAM_MTPROTO_PROXY_SECRET=your-mtproxy-secret-hex
TELEGRAM_API_BASE_URL=
DISCORD_WEBHOOK_SHNKPLAYS=https://discord.com/api/webhooks/...
YOUTUBE_CHAT_ENABLED=false
YOUTUBE_CLIENT_ID=your-google-oauth-client-id
YOUTUBE_CLIENT_SECRET=your-google-oauth-client-secret
YOUTUBE_TOKEN_PATH=data/youtube-token.json
YOUTUBE_OWNER_CHANNEL_IDS=your-youtube-channel-id
YOUTUBE_LIVE_VIDEO_ID=optional-live-video-id
STREAMBOT_STATE_PATH=data/streambot/state.json
STREAMBOT_RESPONSES_DIR=data/streambot/responses
STATUS_ALLOWED_USER_IDS=
DISCORD_SHAWNB_TOKEN=your-shawnb-bot-token
DISCORD_SHAWNB_GUILD_ID=optional-dev-guild-id
SHAWNB_ALLOWED_USER_IDS=allowed-user-discord-id
SHAWNB_OWNER_USER_ID=owner-discord-user-id
SHAWNB_ALLOWED_CHANNEL_IDS=private-channel-id
OLLAMA_BASE_URL=http://127.0.0.1:11434
OLLAMA_MODEL=gemma4:e2b
SOUL_MD_PATH=./SOUL.md
SHAWNB_CHAT_LOG_DIR=./data/shawnb/chats
SHAWNB_HEARTBEAT_PATH=data/shawnb/heartbeat.json
SHAWNB_HISTORY_LIMIT=20DISCORD_TRISHNA_TOKEN enables Trishna's Discord adapter. DISCORD_TOKEN still works as a legacy fallback. If Discord config is missing or invalid, the process can still run other enabled adapters such as YouTube chat.
TELEGRAM_TRISHNA_TOKEN enables Trishna's Telegram adapter. TELEGRAM_OWNER_USER_IDS is required when the Telegram token is set; only those numeric Telegram user IDs can run commands in DMs or groups. TELEGRAM_ALLOWED_CHAT_IDS optionally restricts owner commands to specific Telegram chats; leave it empty to preserve owner-only behavior everywhere.
TELEGRAM_TRANSPORT=botapi is the default, so MTProto stays disabled unless it is explicitly enabled. Set TELEGRAM_TRANSPORT=mtproto to enable MTProto. It uses Telegram MTProto through MTProxy by default, so TELEGRAM_MTPROTO_APP_ID, TELEGRAM_MTPROTO_APP_HASH, TELEGRAM_MTPROTO_PROXY_HOST, TELEGRAM_MTPROTO_PROXY_PORT, and TELEGRAM_MTPROTO_PROXY_SECRET are required. Use secured (dd/ee) or TLS cloak MTProxy secrets; simple 16-byte MTProxy secrets are rejected. The MTProto session is stored at TELEGRAM_MTPROTO_SESSION_PATH with 0600 file permissions, and the parent directory is kept at 0700.
Set TELEGRAM_MTPROTO_PROXY_ENABLED=false only when you intentionally want direct MTProto traffic, such as routing Telegram through WireGuard outside Trishna. When using the default Bot API transport, if TELEGRAM_API_BASE_URL uses plain HTTP, it must point to loopback or a private local address such as http://127.0.0.1:8081.
Telegram bot chats are not end-to-end encrypted. MTProxy improves network-path privacy and censorship resistance, but Telegram servers and this bot process can still read bot chat contents. /tgnet hides local/public IP values by default; set TELEGRAM_TGNET_REVEAL_IPS=true only while debugging routing.
Trishna /status includes a shawnb section (Discord connection via heartbeat file). shawnb writes data/shawnb/heartbeat.json every 10 seconds while connected.
DISCORD_GUILD_ID is optional. When set, Trishna slash commands sync to that guild for faster development. When unset, commands sync globally.
DISCORD_SHAWNB_GUILD_ID works the same way for shawnb's /reset command.
DISCORD_WEBHOOK_SHNKPLAYS is optional. When set, Trishna polls the hardcoded shnk YouTube channel every 5 seconds and posts new uploads or live streams to that Discord webhook. The bot does not post these updates itself; Discord receives them through the webhook URL.
YOUTUBE_CHAT_ENABLED=true enables the YouTube stream bot. Run go run ./cmd/trishna auth youtube once after setting YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET, and YOUTUBE_TOKEN_PATH; the command prints a Google consent URL and saves the OAuth token after the browser callback. The OAuth client must allow a loopback redirect URI for desktop apps.
YOUTUBE_OWNER_CHANNEL_IDS is a comma-separated list of YouTube channel IDs allowed to run owner-only stream commands such as !setgame. Trishna also uses these channel IDs to auto-detect the active public live stream via /channel/{id}/live when OAuth's owned-broadcast lookup cannot see the stream.
YOUTUBE_LIVE_VIDEO_ID is optional. Set it only when auto-detection cannot find the stream, such as some unlisted/private broadcasts.
Trishna auto-detects the current stream game from the broadcast title and tags. Supported game buckets are sky, valorant, and generic; owner-only !setgame can override detection during a stream.
STREAMBOT_RESPONSES_DIR points at editable text replies. These optional files override built-in fallback replies:
socials.txtvalorant.txtsky.txt
STATUS_ALLOWED_USER_IDS is required when the Discord adapter is enabled. Only those Discord user IDs can run /status.
SHAWNB_ALLOWED_USER_IDS is required for shawnb. Only those users can chat with the bot.
SHAWNB_OWNER_USER_ID is required for shawnb. That Discord user receives DM alerts when an allowed user sends something urgent (needs owner, contact request, emotional, security, etc.).
SHAWNB_ALLOWED_CHANNEL_IDS is optional. When set, shawnb also replies in those guild channels (in addition to DMs). Enable Message Content Intent and Direct Messages in the shawnb Discord application settings.
- Create a second Discord application/bot for shawnb.
- Enable Message Content Intent and Direct Messages under Bot settings.
- Copy
SOUL.md.exampletoSOUL.mdand customize personality (keepSOUL.mdgitignored). - Pull an Ollama model:
ollama pull gemma4:e2b - Set shawnb env vars in
.envand run./cmd/shawnbor use the container deployment below.
Read chat logs later:
podman exec shawnb sh -c 'tail -f "/app/data/shawnb/chats/$(date +%F).jsonl"'Each line is JSON with role, content, user_id, channel_id, is_dm, and ts.
- In Discord, open the target channel's settings.
- Go to Integrations → Webhooks → New Webhook.
- Name the webhook
shnkplays, choose the channel, and copy the webhook URL. - Set
DISCORD_WEBHOOK_SHNKPLAYSin your environment. - Start Trishna.
On first run, Trishna records the newest feed entry as a baseline and does not post older videos. After that, only new uploads or live streams are sent. State is stored in data/youtube-state.json.
- Enable YouTube Data API v3 in Google Cloud.
- Create an OAuth client for a desktop app.
- Set
YOUTUBE_CLIENT_ID,YOUTUBE_CLIENT_SECRET,YOUTUBE_TOKEN_PATH, andYOUTUBE_OWNER_CHANNEL_IDS. - Run:
go run ./cmd/trishna auth youtube- Open the printed URL, approve access, and wait for the terminal to save the token.
- Set
YOUTUBE_CHAT_ENABLED=trueand start Trishna.
Supported live chat commands:
!commands!game!specs!crosshair!isekai!valorant!sky!generic!socials!ping(owner only)!status(owner only)!uptime(owner only)!setgame valorant,!setgame sky, or!setgame generic(owner only)
When the current game is valorant, sky, or generic, Trishna posts a compact welcome/promo message on connect and every 30 minutes. This keeps YouTube API usage low: one chat poll per YouTube's returned interval plus at most two scheduled promo messages per hour.
Trishna:
go run ./cmd/trishnashawnb (requires Ollama running and SOUL.md):
go run ./cmd/shawnbThe supported deployment uses rootless Podman and Quadlet. The Linux image contains both bot binaries, persists runtime state in a named volume, and is published for AMD64 and ARM64. Quadlet lets systemd manage startup, restarts, logs, and image updates without a separate orchestration tool.
Install the Trishna service for the current user:
cp .env.example .env
# Fill in .env before continuing.
mkdir -p "$HOME/.config/trishna" "$HOME/.config/containers/systemd"
install -m 600 .env "$HOME/.config/trishna/trishna.env"
install -m 644 deploy/podman/trishna-data.volume deploy/podman/trishna.container \
"$HOME/.config/containers/systemd/"
systemctl --user daemon-reload
systemctl --user start trishna.serviceInstall the optional shawnb service:
cp SOUL.md.example SOUL.md
# Customize SOUL.md before continuing.
install -m 600 SOUL.md "$HOME/.config/trishna/SOUL.md"
install -m 644 deploy/podman/shawnb.container "$HOME/.config/containers/systemd/"
systemctl --user daemon-reload
systemctl --user start shawnb.serviceAllow the rootless user services to start during boot, even when the user is not logged in:
loginctl enable-linger "$USER"Useful service commands:
systemctl --user status trishna.service shawnb.service
journalctl --user -u trishna.service -f
systemctl --user restart trishna.service
podman auto-updateBuild the image locally when needed:
podman build -t localhost/trishna-go:dev -f Containerfile .Pushes to main, version tags such as v1.2.3, and manually dispatched runs
publish AMD64 and ARM64 images to ghcr.io/radcolor/trishna-go.
go test ./...Enable the repository's Git hooks once after cloning:
git config core.hooksPath .githooksBefore each commit, the hook checks gofmt, module tidiness, go vet,
golangci-lint when it is installed, and the full test suite. Commit messages
must follow Conventional Commits, for
example feat(reminders): add recurring schedules or fix: handle empty IDs.
GitHub Actions repeats the complete validation on every push and pull request.
Modules own their commands and interaction handlers. Future service adapters should call shared module logic instead of embedding platform behavior in module internals.