What you are building
The bot has one job: take a text message from a Telegram user, send it with that user's recent history to the chat completions endpoint, and send the answer back. You will:
- Create a Telegram bot and copy its token.
- Get an API key for the LLM.
- Run a Python script that handles messages.
- Add a daily cap per user and a balance log.
The model behind the endpoint is an open-weight model tuned to answer without content refusals for lawful adult use, so it is right for companion, fiction and roleplay bots aimed at adults. Sexual content involving minors is a hard limit. If you run the bot in public chats, state in the bot description that it is for adults.
Step 1: the bot token and the key
In Telegram, open a chat with BotFather, send /newbot, pick a name and a username, and copy the token it gives you. Treat the token like a password.
For the LLM, sign up on the API key page with Google or with email and password, then claim the $0.50 trial credit with one click. It is valid for 7 days and needs no payment details. Install the libraries with pip install python-telegram-bot openai and export both secrets as TELEGRAM_TOKEN and API_KEY.
Step 2: the bot code
This version uses long polling, an in-memory history per user, a daily message cap and a log line with your remaining credit from the X-Balance-USD response header.
import logging
import os
from collections import defaultdict, deque
from datetime import date
from openai import AsyncOpenAI, RateLimitError
from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, MessageHandler, filters
logging.basicConfig(level=logging.INFO)
ai = AsyncOpenAI(base_url="https://api.botchataiunfiltered.com/v1", api_key=os.environ["API_KEY"])
SYSTEM = "You are Rook, a blunt and funny chat companion for adults. Keep replies under 120 words."
DAILY_LIMIT = 60 # messages per user per day
history = defaultdict(lambda: deque(maxlen=20))
used = defaultdict(int)
today = {"day": date.today()}
async def handle(update: Update, context: ContextTypes.DEFAULT_TYPE):
if today["day"] < date.today():
used.clear()
today["day"] = date.today()
uid = update.effective_user.id
if used[uid] >= DAILY_LIMIT:
await update.message.reply_text("Daily limit reached. Come back tomorrow.")
return
used[uid] += 1
history[uid].append({"role": "user", "content": update.message.text})
messages = [{"role": "system", "content": SYSTEM}] + list(history[uid])
try:
raw = await ai.chat.completions.with_raw_response.create(
model="uncensored", messages=messages, max_tokens=300
)
except RateLimitError:
history[uid].pop()
await update.message.reply_text("Busy right now. Try again in a few seconds.")
return
logging.info("balance: %s", raw.headers.get("X-Balance-USD"))
text = raw.parse().choices[0].message.content
history[uid].append({"role": "assistant", "content": text})
await update.message.reply_text(text[:4096])
app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle))
app.run_polling()Run it with python bot.py, open your bot in Telegram and send a message. The text[:4096] slice matters because Telegram rejects messages longer than 4096 characters.
Polling, webhook and hosting
Polling or webhook
Polling, which the code above uses, asks Telegram for new messages in a loop. It needs no public address, so it works from a laptop or a small VPS and is the right choice while you build.
A webhook flips the direction: Telegram calls your server when a message arrives. It needs a public HTTPS URL, but it suits hosting platforms that scale to zero. In python-telegram-bot you swap run_polling() for run_webhook() and pass your URL. Pick one mode at a time, because a bot with a webhook set will not receive polled updates. Whichever you choose, handlers should return quickly, and one slow completion should not block others, which is why the code uses the async client.
Hosting the bot
A laptop is fine for testing, but a bot that people rely on needs a machine that stays on. A small VPS is enough, because the heavy work happens on the API side and your process only passes text. Keep both secrets out of the code, in environment variables or a secrets file with tight permissions, and never commit them. Run the script under a service manager so it restarts after a crash or reboot, and write logs to a file you can read the next morning.
Plan for the day the credit runs low. Read X-Balance-USD in the handler as shown, and message yourself when it drops under a number you choose, so you can top up before users see errors. Top-ups run from $20 to $500, and prepaid credit does not expire. Trial credit is valid for 7 days, so move to a paid top-up before launching a public bot. The docs list the balance endpoints.
History, commands and onboarding
Per-user history and restarts
The dictionary above is lost when the process restarts. For a bot with real users, store the last messages per user in SQLite or Redis, keyed by the Telegram user ID, and load them on each message. Keep the same cap of about 20 messages. Add a /reset command that clears a user's history, since people like a fresh start and it cuts their cost.
If you outgrow a simple cap, summarize old messages into one short note and put it in the system message. The context window is 100,000 tokens for prompt plus reply, far more than a Telegram chat usually needs. For an operations view, the production checklist for uncensored AI chat covers logging, moderation hooks and monitoring.
Commands and the first message
Users judge a bot by its first ten seconds, so add a few commands. A /start handler should say what the bot is, state that it is for adults and give one example to try. A /reset handler clears that user's history, and a /help handler lists the commands. In python-telegram-bot you register each with a CommandHandler next to the text handler shown above.
Show that the bot is working. Telegram lets a bot send a "typing" chat action while it waits, and a reply from an LLM can take a few seconds. Call send_chat_action with the typing action before the API request and the chat feels responsive. If you want streaming, edit one message as chunks arrive, but keep the edit rate modest and check Telegram's current limits.
Cost control
Cost follows input tokens, and input follows history length. With a 20-message cap at about 120 tokens each plus a 300-token system message, input is about 2,700 tokens. A reply of 200 tokens is output. One message then costs 2,700 x $0.15 / 1M = $0.000405 plus 200 x $0.50 / 1M = $0.0001, which is $0.000505.
A user who sends 40 messages a day for 30 days sends 1,200 messages, or $0.61. A hundred such users cost about $60.60 a month. The daily cap bounds the worst case: 60 messages a day with full 300-token replies costs about $1.00 per user per month. You can read the balance any time with GET /v1/balance, as well as in the header, and top-ups on the pricing page start at $20 and are paid in crypto. Failed or refused requests are not charged.
Groups, limits and Telegram quirks
- Group chats. Telegram bots have privacy mode on by default in groups, so they see commands and replies to them, not every message. Change this in BotFather if you want the bot to read the room, and key history by chat ID plus user.
- Telegram limits. Telegram also limits how fast a bot can send messages. Check its current Bot API limits if you broadcast.
- API limits. A full key allows 8 concurrent requests and 300 per minute. A trial key allows 2 and 60. Over a limit you get HTTP 429 with
rate_limitedorconcurrency. The code above answers with a polite message instead of crashing. A queue per bot is the next step for busy bots. - Abuse. A per-user cap, as above, is your first defence against one person draining your credit. See the API docs for the headers that report your limits.
