FoukoApi
A Rust framework for building cross-platform bots. Write a command once, run it on Telegram, Discord and anything you plug in next.
api.fouko.xyz · fouko.xyz · Discord · Telegram
[!NOTE] FoukoApi is approaching its first public release (
0.1.0-alpha.*). The public surface below is stable in spirit; feature-gated internals may still shift.
What is it
FoukoApi is a small, opinionated framework that lets you describe a bot once — its commands, handlers, state, linked-account UX — and run the same bot on multiple chat platforms without forking logic for each one.
No custom DSL, no macros you have to learn. Just Rust functions and a Bot builder.
Contents
Features
- One codebase, many platforms. Telegram and Discord shipped, more adapters possible behind the same
Platformtrait. Your handler doesn't care which one it's running on. - Unified buttons and keyboards. Build a
Keyboardonce, each adapter turns it into Telegram inline keyboards or Discord message components. - Unified embeds.
Embedwith title / description / fields / footer / color renders as a real Discord embed and as HTML on Telegram, from the same code. - Pluggable
Storagetrait. Any async key-value store works. Bundled: in-memory (great for tests) and SQLite (great for a self-hosted bot). - Built-in account linking.
Accounts::start_link/redeem_linklets users tie their Telegram and Discord identities with a 6-char code. Ships with a ready-made/linkcommand. - Built-in
/helpand/lang. Turn on with one builder call, localised per user. - Type-safe, async from the ground up. Built on
tokio. No unsafe, no build-time code generation, no custom DSL. - Pluggable adapters. Every platform is a trait impl. Rolling your own for a niche chat service is a weekend project.
Quick start
# Cargo.toml
[]
= "0.1"
= { = "1", = ["full"] }
use ;
async
That single /help command responds on every platform you added, and the Keyboard is rendered natively on each one (inline keyboard in Telegram, message components in Discord).
Mini-wiki
Cargo features
| Feature | Default | What it does |
|---|---|---|
telegram |
yes | Adds the Telegram adapter (teloxide). |
discord |
yes | Adds the Discord adapter (serenity). |
sqlite |
yes | Enables the bundled SQLite storage backend. |
full |
no | Turns on everything above. |
= { = "0.1", = false, = ["telegram", "sqlite"] }
Environment variables
bootstrap_env() creates a commented .env template on the first run. Defaults:
| Variable | What it is |
|---|---|
TG_TOKEN |
Telegram bot token from @BotFather. |
DISCORD_TOKEN |
Discord bot token (Developer Portal → Bot → Reset Token). |
FOUKO_DB |
Storage URL. sqlite:./foukobot.sqlite, memory:, or a custom scheme your own Storage understands. If unset, a SQLite file is placed next to the binary. |
RUST_LOG |
Standard tracing filter, e.g. info,foukoapi=debug. |
Core types
Bot — builder. Add platforms, commands, on_message hooks, defaults.
Platform — "here's a Telegram/Discord token". Plugged into Bot::add_platform.
Ctx — everything a handler needs: platform, user_id, text, args,
reply / reply_with / edit_reply, callback_data, is_dm, ...
Reply — what you send back. Reply::text(..), Reply::embed(..),
with optional .keyboard(kb).
Embed — rich reply card. .title / .description / .field(_inline) /
.footer / .image / .thumbnail / .url / .color.
Keyboard — rows of Buttons. Button::callback("label", "cb_data") or
Button::url("label", "https://...").
Storage — async KV store trait (get/set/del). AnyStorage is a boxed version.
Accounts — cross-platform user linking. Sits on top of Storage.
Built-in commands
One builder call each, all localised and DM-aware where it makes sense:
new
.with_accounts // needed by /help (i18n), /lang, /link
.with_default_help // lists every described command
.with_default_lang_command // picker with inline buttons
.with_default_link_command // 6-char code flow + unlink button
Minimal bot with state
use ;
async
Account linking flow
┌─ Telegram ─┐ ┌─ Discord ─┐
│ /link │ ── 6-char code ─▶ │
│ │ │ /link CODE │
│ │ ◀── linked ─────│ │
└────────────┘ └────────────┘
│ │
└──── pick primary (once) ───┘
│
primary keeps XP/coins/settings
the other side stays clean
┌─ later, anywhere ─┐
│ /link → Unlink │
│ │
│ primary keeps its │
│ profile, other │
│ side → fresh │
└───────────────────┘
Accounts::start_linkissues a 6-char code valid for 5 minutes.Accounts::redeem_linkon the other side ties them together.- The built-in
/linkcommand (Bot::with_default_link_command) adds an inline-button primary picker that locks in the choice exactly once. After that, the only way to pick a different primary is to/link→ Unlink and start over. - After
Accounts::unlink, the primary-ident keeps its data (XP/coins/settings/lang); the ex-partner platform falls back to its own ident and therefore starts from a clean slate.
Utility helpers (foukoapi::util)
Small, platform-agnostic helpers that you'll reach for in pretty much every bot:
use ;
assert_eq!;
assert_eq!;
assert_eq!;
capitalize(&str) -> String— uppercases the first char, UTF-8 safe.progress_bar(done, total, width) -> String—▰/▱bar of the given width.urlencode(&str) -> String— minimal percent-encoder for query-string args.
Status
| Platform | Status |
|---|---|
| Telegram | ✅ Working |
| Discord | ✅ Working |
| Feature | Status |
|---|---|
| Command router | ✅ Working |
| Keyboards / inline buttons | ✅ Working |
| Embeds (Discord + HTML TG) | ✅ Working |
| Storage (memory + sqlite) | ✅ Working |
| Account linking + primary | ✅ Working |
Built-in /help, /lang, /link |
✅ Working |
| Util helpers | ✅ Working |
| Argument parsing helpers | ⏳ Planned |
| Middleware chain | ⏳ Planned |
Examples
The examples/ directory is where ready-to-run sample bots live. A reference bot that uses every feature is also published as FoukoBot — its source is a good place to watch the API take shape.
# run the quickstart example (needs TG_TOKEN / DISCORD_TOKEN)
MSRV
Latest stable Rust. The crate pins an explicit MSRV once 0.1 ships.
Contributing
Issues and PRs are welcome. The repo follows the usual "fork, branch, PR" flow. Be kind in the tracker, keep diffs small, and run cargo fmt + cargo clippy --all-targets before opening a PR.
License
MIT - see LICENSE.
Part of the Fouko family.