Skip to content

Latest commit

Β 

History

91 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Astolfo

A Telegram bot that behaves like a member of your group chat β€” not an assistant.

CI CodeQL Audit Release Python 3.10+ License: MIT

Quick start Β· How it works Β· Owner's panel Β· Providers Β· Docs


It decides for itself whether a message deserves an instant reply, real reasoning, or a web search. It reads photos, stickers, GIFs, videos and voice messages. It stacks thirteen API providers behind one client and fails over between them without the chat noticing. And when every last one of them is out of allowance, it still answers the things that need no model at all β€” honestly, rather than by guessing.

Text in, text out. The bot never generates images or audio.

flowchart LR
    M([message]) --> P{participation}
    P -->|addressed, or<br/>manual/auto/smart| B{budget}
    P -.->|stay quiet| X([no reply])
    B -->|caps, per-chat<br/>and per-person| C{cache}
    C -->|hit| R([reply])
    C -->|miss| RT{router}
    RT -->|fast Β· think<br/>search Β· serious| L[[LLM client]]
    L -->|13 services,<br/>failover + rotation| R
    L -.->|nothing reachable| O[offline answers]
    O --> R
Loading

Features

🎭 Layered persona The system prompt is built from ordered layers β€” narrative identity, voice markers, canon anchors, group behaviour, language mirroring, banned assistant-isms, an in-character answer to "are you a bot?", and a highest-priority truthfulness layer. Traits are written as identity rather than commands, with few-shot examples in four moods and a slim reminder re-injected periodically so the voice does not flatten in long chats.
🧭 Adaptive routing Regex heuristics classify most messages for free β€” small talk goes to fast, technical questions to think, time-sensitive facts to search, distress to serious. Only ambiguous messages reach a small LLM dispatcher, and its verdicts are cached. The mode drives the model, temperature, token ceiling, reasoning budget and whether web search runs.
πŸ”Ž Grounded answers Search-mode replies run at low temperature over live web results and cite their sources. Canon anchors keep the persona from inventing its own lore, and the truthfulness layer makes "I don't know" the in-character answer rather than a failure.
πŸ–Ό Multimodal input Photos and stickers are downscaled and encoded, GIFs, videos and video notes are sampled into frames with ffmpeg (falling back to Telegram thumbnails), and voice messages are transcoded to mono mp3.
πŸ”Œ Thirteen providers, one client Services are tried in order and fail over on refusal, quota and rate limits. Each holds several keys; a refused key rests and the next takes over mid-conversation. Health survives a restart, so a quota that runs until tomorrow is still known tomorrow.
🧠 Models you pick, not models you hardcode The free catalog changes weekly. The panel reads it from the service, lists what is on offer with its context window, modalities and price, and a press assigns one to a job β€” no .env, no restart. Tokens spent are counted per model, which is the only number that separates one free model from another.
πŸ’Έ Credit controls Per-call cost is recorded and persisted. As spend approaches the daily cap the bot degrades instead of dying: cheap model only, then replies only when addressed, then a polite stop. Per-chat and per-person daily call limits sit on top.
πŸ’€ Offline answers With every service resting, greetings, thanks, "who are you", the time, the date and plain arithmetic are still answered in character. Anything needing actual knowledge gets an honest "my brain is offline" β€” never a guess.
πŸŽ› Run it from Telegram A private-chat control panel for the owner: services and keys, every setting, groups, people, limits, server health, update and restart. Changes take effect immediately β€” no editing .env, no restart.
🧹 It cleans up after itself The audit trail, the per-day counters, groups it was removed from and people nobody has seen since are dropped after RETAIN_DAYS and the file is compacted, so a small host does not fill up. A block, a limit or the owner is never forgotten β€” those were decisions.
πŸ” Built to be exposed Keys are encrypted at rest and only ever shown masked. Message text is never stored β€” a test checks the database file and its write-ahead log. The bot runs unprivileged and can ask a root helper for exactly two things.

Quick start

git clone https://github.com/hami9/Astolfo && cd Astolfo
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env      # add your bot token and one API key
python main.py

Two values are required: TELEGRAM_BOT_TOKEN from @BotFather, and one API key from any provider in the table below. Everything else has a default.

Install ffmpeg for voice and video analysis (apt install ffmpeg / brew install ffmpeg).

Or take it as a package or an image instead of a clone:

pip install "astolfo-bot @ git+https://github.com/hami9/Astolfo@v2.5.0"
astolfo                                  # same bot, on your PATH

docker run -d --env-file .env -v astolfo-data:/data ghcr.io/hami9/astolfo:latest
docker compose up -d                     # or with the bundled compose file

Important

Turn off Group Privacy in BotFather so the bot can see normal group messages: /mybots β†’ your bot β†’ Bot Settings β†’ Group Privacy β†’ Turn off, then remove and re-add the bot to the group.

Deployment on a VPS, Docker or Replit is covered in docs/DEPLOYMENT.md; running the bot from Telegram afterwards in docs/ADMIN.md.

How it works

Every message walks the same path, and most of it costs nothing:

  1. Participation β€” a reply, a mention or the bot's name always gets an answer. Otherwise the chat's mode decides: manual stays quiet, auto joins at the chance you set, smart does both depending on how busy the chat is, how much budget is left, and whether any service is actually reachable.
  2. Budget β€” the daily cap and the per-chat and per-person call limits return an allowance that can strip capabilities from the turn or block it outright.
  3. Cache β€” an identical recent message in the same chat is answered with no model call.
  4. Routing β€” heuristics decide the mode for free; the LLM dispatcher is consulted only when they are unsure.
  5. Model β€” the first working service and key answer; refusals, quotas and rate limits move to the next one without the chat noticing.
  6. Polish β€” markdown, name prefixes and assistant-isms are stripped, sources appended, long replies split to Telegram's limit.

Full detail in docs/ARCHITECTURE.md.

Commands

Command Description
/start, /help introduction and usage
/about channel, creator, the source, and what the bot can and cannot do
/source the repository and the capability list on their own
/chance 0-100 how often the bot joins conversations unprompted (admin)
/mode auto|fast|think|search pin a response mode (admin)
/usage today's cost, tokens, cache hits and budget state
/status current settings; the model and service it runs on only for admins
/reset clear this chat's history and notes (admin)
/mute, /unmute silence the bot or bring it back (admin)
/donate send Telegram Stars towards the API bill
/panel the owner's control panel, private chat only

The owner's panel

/panel, in a private chat with the bot, from the account named by MASTER_ID:

Screen What it is for
services Every service: keys, health, today's calls, order, on/off, and adding your own
models Pick which model does which job from the live catalog, and see the tokens each one spent
settings Any setting by name, plus switches for the common ones
groups Every group: activity, mute, leave, how talkative it is, daily limit
people Who has spoken to it, where, blocking, and per-person limits
server Health, log, update, restart
data Row counts, the audit trail, a backup of the database

Keys are added by message and the message is deleted straight away; they are stored encrypted and only ever shown masked. Destructive actions take a second press. Nothing here needs a restart. See docs/ADMIN.md.

Providers

Any OpenAI-compatible endpoint works. Thirteen are known out of the box, and more can be added from the panel β€” name, URL, models β€” with no code change.

Service Endpoint Notes
openrouter openrouter.ai/api/v1 discovers zero-cost models, web search plugin, usage accounting
google generativelanguage.googleapis.com Gemini, free tier
groq api.groq.com/openai/v1 fast Llama inference, free tier
github models.github.ai/inference GitHub Models
cerebras api.cerebras.ai/v1 free tier
mistral api.mistral.ai/v1 free tier, Pixtral for vision
cohere api.cohere.ai/compatibility/v1 trial keys are free and rate limited
huggingface router.huggingface.co/v1 monthly credit, then paid
sambanova api.sambanova.ai/v1 free tier
deepinfra api.deepinfra.com/v1/openai pay as you go after the signup credit
deepseek api.deepseek.com/v1 pay as you go, cheap
openai api.openai.com/v1 pay as you go
aimlapi api.aimlapi.com/v1 small free allowance, then paid

Note

Several keys per service is for keys you already hold β€” rotating one without downtime, or a work key beside a personal one. It is not a way to hold several accounts at one provider to get past its free quota: that breaks every provider's terms and ends with all of them closed.

Configuration

Every setting is an environment variable, and every one of them can be overridden from the panel at runtime. See .env.example for the full list with defaults. The ones worth knowing:

Variable Default Purpose
TELEGRAM_BOT_TOKEN β€” required
MASTER_ID β€” the numeric Telegram id that owns the panel
MODEL_FAST google/gemini-2.5-flash everyday chatter
MODEL_THINK google/gemini-2.5-pro reasoning-heavy turns
MODEL_ROUTER google/gemini-2.5-flash-lite the dispatcher
REPLY_MODE smart manual, auto or smart participation
GROUP_REPLY_CHANCE 0.30 unprompted participation
DAILY_BUDGET_USD 0 (off) spend cap with graceful degradation
CHAT_DAILY_CALL_LIMIT 0 (off) model calls per chat per day
USER_DAILY_CALL_LIMIT 0 (off) model calls per person per day
FREE_MODE 0 zero-cost models only
RETAIN_DAYS 90 how long the audit trail and counters are kept
RESPONSE_CACHE 1 reuse answers to identical recent messages
WEB_SEARCH 1 ground factual answers in live results
BOT_LANG en language of command replies (en or fa)

Unknown model ids are detected at startup and replaced from FALLBACK_MODELS.

Documentation

Document What is in it
docs/ARCHITECTURE.md request lifecycle, prompt structure, memory, failure behaviour
docs/ADMIN.md the owner's panel, services, modes, limits, updating the server
docs/DEPLOYMENT.md VPS with systemd, Docker, Replit
docs/COST.md what each turn costs and every lever that lowers it
SECURITY.md what this protects, what it does not, and how to report a problem
CONTRIBUTING.md how to set up, what the tests expect, house style
CHANGELOG.md what changed and when

Development

make install       # virtualenv + dev dependencies
make check         # ruff and the full suite, what CI runs
make build         # sdist, wheel, and the check that no package is missing
make help          # every target

Or without make:

pip install -r requirements-dev.txt
pytest -q          # 450 tests, fully offline
ruff check .

Tests use a mocked HTTP transport, so the exact request body sent to each provider is asserted without any network access or credentials. CI runs the suite on Python 3.10, 3.12 and 3.13, with CodeQL and a dependency audit alongside it, and pre-commit install runs the same checks before each commit.

Releases

The version in astolfo/__init__.py is the single source of truth; pyproject.toml reads it, and a tag that disagrees with it fails the release build. Tagging vX.Y.Z runs the suite, builds the sdist and wheel, verifies the wheel carries every package, publishes a GitHub release with that version's changelog section attached, and pushes ghcr.io/hami9/astolfo:X.Y.Z. See CONTRIBUTING.md and the changelog.

Layout

main.py                    entry point
astolfo/config.py          environment-driven settings
astolfo/persona.py         layered prompt, few-shot examples, locale detection
astolfo/routing.py         fast / think / search / serious dispatcher
astolfo/interest.py        whether an unprompted message is worth answering
astolfo/attention.py       which chat has its attention right now
astolfo/tuning.py          how long the next reply may be, from cost and reception
astolfo/roles.py           who runs each group, and what the bot is in it
astolfo/providers.py       the known services and their keys
astolfo/catalog.py         the models a service offers, as records
astolfo/llm.py             the client: failover, key rotation, retries
astolfo/services.py        stored services, keys and their health
astolfo/participation.py   manual, auto and smart: how talkative the bot is
astolfo/offline.py         the answers that need no model at all
astolfo/budget.py          cost accounting and the degradation ladder
astolfo/cache.py           TTL + LRU caches
astolfo/media.py           images, stickers, GIFs, video, audio
astolfo/memory.py          history, long-term notes, persistence
astolfo/learning.py        the speaking style it picks up, per chat and per person
astolfo/text.py            addressing, normalising, polishing and splitting
astolfo/db.py              SQLite: chats, people, settings, keys, audit
astolfo/settings_store.py  settings and keys that change without a restart
astolfo/crypto.py          encryption for the stored keys
astolfo/master.py          who owns the bot
astolfo/membership.py      joining and leaving groups
astolfo/admin/             the owner's panel
astolfo/server_ops.py      machine health, and asking the root helper for a job
astolfo/branding.py        the project's own links and identity
astolfo/donate.py          Telegram Stars
astolfo/strings.py         every user-facing string, in English and Persian
astolfo/runtime.py         the shared services, assembled once
astolfo/chat.py            the message pipeline
astolfo/commands.py        command handlers
astolfo/app.py             wiring, lifecycle, keepalive server
deploy/                    VPS installer and the privileged helper
docs/                      architecture, deployment, cost control, admin
tests/                     the offline suite
.github/                   CI, CodeQL, audit, release, issue templates

Contributing

Issues and pull requests are welcome β€” see CONTRIBUTING.md for the setup, what CI will check, and the house style. Everyone taking part is expected to follow the Code of Conduct.

Notes

Astolfo and the Fate series belong to TYPE-MOON. This is a non-commercial fan persona, and it is worth saying so in the bot's Telegram bio.

Keep your keys in a git-ignored .env or the panel's encrypted store, never in the repository.

Author

Built by @ham1235i β€” hami9.ir, channel @hami294, and a Discord.

License

MIT.

About

A Telegram bot that behaves like a member of your group chat instead of an assistant. It decides for itself whether a message deserves an instant reply, real reasoning, or a web search, it reads photos, stickers, GIFs, videos and voice messages, and it keeps its own spending under control.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages