DChat Bots

DChat Bots

A bot is a small program that chats with people in DChat: it answers questions, shows buttons, posts to a channel, helps a group. You write it in any language, you run it on your own computer, and DChat hosts nothing.

What is a bot?

A bot is an ordinary DChat account whose username ends in bot, for example weatherbot. DChat's bot registry marks it as a bot, so the apps know to show its buttons and its command menu.

The program behind the account is yours. You run it on your own computer. It holds the bot's keys, receives what people send the bot, and gives your code a small local web address to talk to. Because of that, DChat hosts nothing for you, and DChat cannot read your bot's chats.

Good to know

Your bot works while your computer and the dchat-bot program are running. Messages sent while it was off wait and arrive when it starts again.

What bots can do today

  • Answer in private chats. People write to the bot, the bot answers. A bot can write to someone only after that person has written to it first.
  • Show buttons. Buttons under a message, and a keyboard of quick replies. A tap comes back to your code, and you answer with a short notice.
  • Offer commands. A command menu and / suggestions in the apps, such as /start and /help.
  • Join groups. A group admin adds the bot like a person. By default the bot sees only commands, mentions and replies to it.
  • Post in channels. A channel owner makes the bot an admin, and it can post, edit and delete its posts.
  • Edit and delete its own messages.
  • Answer inline. People type @yourbot and a few words in any chat, and your bot offers results to pick from. See Inline mode.
  • Open a mini app. A web page of yours inside DChat, from the Menu button or a button, that knows which account opened it. See Mini apps.
  • Have a profile page with a name, a short line, a longer description and its commands.

Privacy and safety for users

Bots are built so that people stay in control.

  • The person running the bot can read what you send it. Your message is encrypted between you and the bot, and the bot is its developer's program, so what you send arrives there as readable text. DChat cannot read it. The app tells you this before you write to a bot for the first time.
  • Inline bots see what you type after their name. When you type @somebot and a few words, those words go to that bot. It never learns which chat you are in, and it never sees which result you pick. The app tells you this before the first time. Nothing is sent to a chat until you tap a result.
  • A bot cannot message you first. It can only answer after you wrote to it. A text you send in a group does not open a private chat with a bot.
  • In a group, a bot sees only what is meant for it unless its owner turns that off: commands, messages that mention it, and replies to it. Media and everything else stay out.
  • Buttons and links are checked. The apps draw buttons only for registered, active bots. A link on a button always asks you before it opens, and only https and dchat links are allowed.
  • A bot cannot take over a channel. It can only post, edit, delete and pin. It can never add admins, change the channel's info or read its direct messages.
  • If something goes wrong, it can be switched off. The owner can turn a bot off at any time, and the apps stop drawing its buttons. A group admin can remove a bot like any member.

Create a bot in 5 steps

You need the DChat app and a computer that can stay on while the bot runs. The exact commands are in the command line reference.

  1. Pick a name and a username in the app

    Open My bots in DChat. Choose a name and a username of 5 to 32 letters, digits or underscores that ends in bot. The app gives you a claim code such as K7QW-M2PX-9RTA. It works for 15 minutes.

  2. Choose a passphrase on your computer

    It locks the bot's keys on your disk, and every command asks for it. Set it in DCHAT_BOT_PASSPHRASE and keep it somewhere safe.

  3. Create the bot

    Run dchat-bot init --claim K7QW-M2PX-9RTA. It makes the bot's recovery phrase on your computer, shows you the owner and the username, and asks you to confirm. At the end it prints your local API token once. Copy it now.

  4. Start it

    Run dchat-bot run and leave it running. It serves a local address, http://127.0.0.1:8081, that only your computer can reach.

  5. Write the bot

    Your code asks that address for new messages and sends answers back, in any language that can make web requests. Start from the echo bot example, then add a profile with dchat-bot set-profile when you are ready.

Keep three things secret

The bot's recovery phrase (sealed in vault.json), the passphrase, and the API token. If one leaks, turn the bot off in My bots and make a new one. Read how they work in Authorizing your bot.

Inline mode

Inline mode lets people use your bot from any chat without opening it. A person types your bot's name and a few words, such as @yourbot cats. The app lists what your bot offers above the message field. The person taps one, and its text is sent into the chat as their own message, marked "via @yourbot".

  1. Turn it on

    Call setMyInlinePlaceholder with a short hint, such as Type something to echo. People see it in grey after @yourbot while they have typed nothing yet. An empty hint turns inline mode off.

  2. Read the query

    What the person typed arrives from getUpdates as an inline_query update. It holds the words and who typed them, and nothing about the chat they are in.

  3. Answer it

    Call answerInlineQuery with up to 50 results. Each result has a title, a short description and the text to send. You get one answer for each query, within 30 seconds, so answer quickly.

Inline mode in the echo bot style
# Once, when the bot starts: turn inline mode on.
call("setMyInlinePlaceholder", placeholder="Type something to echo")


def on_inline_query(query):
    text = query["query"].strip()
    if not text:
        results = [{
            "type": "article", "id": "hello", "title": "Say hello",
            "description": "Type something after my name to echo it",
            "input_message_content": {"message_text": "Hello from the echo bot!"},
        }]
    else:
        results = [
            {"type": "article", "id": "echo", "title": f"Echo: {text[:100]}",
             "description": "Send it as it is", "input_message_content": {"message_text": text}},
            {"type": "article", "id": "upper", "title": "SHOUT IT",
             "description": "Send it in capitals", "input_message_content": {"message_text": text.upper()}},
        ]
    call("answerInlineQuery", inline_query_id=query["id"], results=results, cache_time=0)


# In the update loop, next to the message and callback_query cases:
elif "inline_query" in update:
    on_inline_query(update["inline_query"])

What you should know. Results are plain text articles: no photos, no files, no thumbnails. Your bot is never told which result was picked or where it was sent, and a query never lets the bot write to the person first. The app waits a moment after the last key press before it asks, so you get one query for a short burst of typing. A message sent from a result shows "via @yourbot" and no link preview, and it never counts as a command typed to another bot.

Every field, refusal and limit is in the API reference.

Mini apps

A mini app is a web page of yours that opens inside DChat when someone taps the bot's Menu button or a button you send. It is the way to give a bot a real screen: a form, a shop, a game. The page runs on your own https server. DChat hosts nothing.

  1. Register the page

    Call setChatMenuButton with the link and a label for the Menu button. That link fixes the one origin your bot may use.

  2. Include the page library

    Add https://dchat.online/js/dchat-web-app.js to your page. It gives you window.DChat.WebApp.

  3. Read the init data and send it to your backend

    Read DChat.WebApp.initData and post it to your own server. Do not trust anything else the page says about the user.

  4. Validate it

    Your backend calls validateWebAppData on the gateway. A good answer names the account that opened the page. Make your own short session from it.

1. Register it, with curl
curl -s http://127.0.0.1:8081/setChatMenuButton \
  -H "Authorization: Bearer $DCHAT_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"url":"https://app.example.com/start","text":"Open"}'
2. The page
<script src="https://dchat.online/js/dchat-web-app.js"></script>
<script>
  var app = window.DChat.WebApp;
  app.ready();
  fetch("/api/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ init_data: app.initData })
  })
    .then(function (r) { return r.json(); })
    .then(function (me) { document.body.textContent = "Hello, " + me.hello; });
</script>
3. Your backend, in Python (standard library only, like the echo bot)
import json, os, urllib.error, urllib.request
from http.server import BaseHTTPRequestHandler, HTTPServer

GATEWAY = os.environ.get("DCHAT_BOT_URL", "http://127.0.0.1:8081")
TOKEN = os.environ["DCHAT_BOT_TOKEN"]


def validate(init_data):
    request = urllib.request.Request(
        f"{GATEWAY}/validateWebAppData",
        data=json.dumps({"init_data": init_data, "max_age": 300}).encode("utf-8"),
        headers={"Content-Type": "application/json", "Authorization": f"Bearer {TOKEN}"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            answer = json.load(response)
    except urllib.error.HTTPError as e:
        answer = json.load(e)
    return answer["result"] if answer.get("ok") else None


class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        body = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
        launch = validate(body.get("init_data", ""))
        if launch is None:
            self.send_response(403)
            self.end_headers()
            return
        # The user is proven. Make your own short session here.
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps({"hello": launch["user"]["username"]}).encode("utf-8"))


HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
Always validate on your backend

A page is just a web page. Anyone can open it in a browser and invent any user. Believe a user only after validateWebAppData said so. Init data is good for 5 minutes by default (up to 24 hours with max_age) and each launch is accepted once.

What a page can do in this version. Read who opened it (after you validate), use the app's colors, show a main button and a back button, ask before closing, show a native popup, alert or confirm, give haptic feedback, open an https link after a tap, send one piece of data back to the bot, and close. A page opened from a reply keyboard button can send that data; one opened from the Menu button or an inline button cannot.

What it cannot do yet. No camera, location, clipboard, downloads, file picking, QR scanning or payments. Every permission the page asks for is denied.

One origin for each bot. You register one link, and every mini app button you send must be on that same origin. A page that navigates somewhere else leaves the mini app. Another bot cannot register your origin.

What users see. The first time someone opens your mini app, the app shows a notice that names what the page receives, before the page opens. The bot's name and host always stay visible above your page.

Every field, refusal and limit is in the API reference.

Coming later

These are planned and not available yet.

PaymentsComingLet a bot take a payment in the chat.

Where to go next