Build an uncensored Telegram bot
All guides

Guide · 25 min read

Build an uncensored Telegram bot

Receive a photo, run a job, and send the CDN URL back. Polling works without a public HTTPS server.

Telegram bots rarely have a public HTTPS server on day one. Poll getUpdates, resolve the photo with getFile, ingest it with POST /v2/files, then client.run(). Sandbox keys return example media so you can wire sendPhoto without spending.

If the Telegram file URL expires, the uploaded file url is stable for 24 hours. Switch to a live key when the loop works. Attach webhook_url only if this bot already serves HTTPS.

A chat that takes a photo and replies with a generated still.

Steps

  1. 1

    Create a Telegram bot token and a Goonify sandbox key.

  2. 2

    Poll getUpdates and resolve the user photo with getFile.

  3. 3

    Ingest the file with POST /v2/files, then run goonify/cnidia.

  4. 4

    Reply with sendPhoto using the CDN url.

Install the SDK

pip install "goonify @ git+https://github.com/Goonify/goonify.git#subdirectory=sdks/python"

Modelgoonify/cnidia

import json, os, time, urllib.parse, urllib.request
from goonify import Goonify

BOT = os.environ["TELEGRAM_BOT_TOKEN"]
client = Goonify()  # GOONIFY_API_KEY=sk_test_...

def tg(method, payload=None, query=None):
    url = f"https://api.telegram.org/bot{BOT}/{method}"
    if query:
        url += "?" + urllib.parse.urlencode(query)
    data = None if payload is None else json.dumps(payload).encode()
    req = urllib.request.Request(
        url,
        data=data,
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req) as resp:
        return json.load(resp)["result"]

def file_url(file_id):
    path = tg("getFile", query={"file_id": file_id})["file_path"]
    return f"https://api.telegram.org/file/bot{BOT}/{path}"

# Poll updates. Attach webhook_url only if this bot
# already serves public HTTPS.
offset = 0
while True:
    updates = tg("getUpdates", query={"timeout": 20, "offset": offset})
    for update in updates:
        offset = int(update["update_id"]) + 1
        msg = update.get("message") or {}
        chat_id = (msg.get("chat") or {}).get("id")
        photos = msg.get("photo") or []
        if not chat_id or not photos:
            continue
        source = file_url(photos[-1]["file_id"])
        uploaded = client.files.create(url=source)
        job = client.run(
            "goonify/cnidia",
            input={
                "prompt": "cinematic studio portrait, film still",
                "image_url": uploaded.url,
            },
        )
        tg("sendPhoto", {"chat_id": chat_id, "photo": job.output["url"]})
    time.sleep(0.2)

What it looks like

Run it with a sandbox key

Sandbox keys return example media and spend nothing. Switch to a live key when you are ready.

Next recipes