← back to blog

cloud phone integration with Telegram bots in 2026

May 07, 2026

cloud phone integration with Telegram bots in 2026

cloud phone Telegram bot integration in 2026 is the easiest way to put fleet control in your team’s pocket. you type /screenshot dev_4f2a from your phone on the train, the bot replies with a JPEG, and you have just verified that a customer’s session looks healthy without opening a laptop. the same pattern handles crash alerts, daily digests, and one-tap reboots from your group chat.

this guide builds a working Telegram bot end to end with inline commands, callback buttons, signed-URL screenshots, and a webhook receiver wired to cloudf.one. if you have already set up a Discord bot, this is the same shape with different APIs.

why Telegram for ops

three reasons teams pick Telegram.

the downside is no real role system. anyone in the chat can run any command unless you gate by user ID. plan for that.

creating your bot

open Telegram, search for @BotFather, send /newbot, follow the prompts. it gives you an HTTP API token like 7891234567:AAH.... add the bot to your group chat and promote it to admin so it can read messages and post images.

export TELEGRAM_TOKEN="7891234567:AAH..."
export TELEGRAM_CHAT_ID="-1001234567890"
export CLOUDFONE_TOKEN="cf_live_..."

to find the chat ID, send any message in the group then visit https://api.telegram.org/bot<TOKEN>/getUpdates. the chat ID for groups is negative.

the minimal Python bot

python-telegram-bot 21+ is the standard. install with pip install python-telegram-bot requests.

import os, requests
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes

CF_API = "https://api.cloudf.one/v1"
CF_H = {"Authorization": f"Bearer {os.environ['CLOUDFONE_TOKEN']}"}
ALLOWED = set(map(int, os.environ.get("TELEGRAM_ALLOWED_USERS", "").split(",")))

def authed(update: Update) -> bool:
    return update.effective_user.id in ALLOWED

async def devices(update: Update, _: ContextTypes.DEFAULT_TYPE):
    if not authed(update): return
    r = requests.get(f"{CF_API}/devices", headers=CF_H).json()
    lines = [f"<code>{d['id']}</code> {d['model']} - {d['state']}" for d in r["devices"]]
    await update.message.reply_text("\n".join(lines)[:3500], parse_mode="HTML")

async def screenshot(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    if not authed(update): return
    if not ctx.args:
        await update.message.reply_text("usage: /screenshot <device_id>")
        return
    device_id = ctx.args[0]
    r = requests.post(f"{CF_API}/devices/{device_id}/screenshot", headers=CF_H).json()
    await update.message.reply_photo(photo=r["url"], caption=device_id)

async def lock(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    if not authed(update): return
    minutes = int(ctx.args[0]) if ctx.args else 10
    r = requests.post(f"{CF_API}/devices/lock", headers=CF_H, json={
        "tag": "pool-default", "duration": minutes * 60
    }).json()
    kb = [[InlineKeyboardButton("unlock now", callback_data=f"unlock:{r['device_id']}")]]
    await update.message.reply_text(
        f"locked <code>{r['device_id']}</code> for {minutes}m\n"
        f"adb: <code>{r['adb_endpoint']}</code>",
        parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup(kb)
    )

async def callback(update: Update, _: ContextTypes.DEFAULT_TYPE):
    q = update.callback_query
    if q.data.startswith("unlock:"):
        device_id = q.data.split(":", 1)[1]
        requests.post(f"{CF_API}/devices/{device_id}/unlock", headers=CF_H)
        await q.edit_message_text(f"unlocked {device_id}")

app = Application.builder().token(os.environ["TELEGRAM_TOKEN"]).build()
app.add_handler(CommandHandler("devices", devices))
app.add_handler(CommandHandler("screenshot", screenshot))
app.add_handler(CommandHandler("lock", lock))
app.add_handler(CallbackQueryHandler(callback))
app.run_polling()

run this on any host. Telegram’s long-polling mode means no public HTTPS endpoint needed for the bot itself.

inline keyboards for one-tap actions

the lock command above shows an “unlock now” button. that pattern extends well. for example, after a crash alert, attach a button that reboots the device.

kb = [[
    InlineKeyboardButton("reboot", callback_data=f"reboot:{device_id}"),
    InlineKeyboardButton("screenshot", callback_data=f"shot:{device_id}"),
]]

teams use this constantly. you get a notification that something is wrong, and three taps later you have a screenshot and a reboot in flight.

crash alerts via the cloudf.one webhook

the polling bot above handles inbound. for outbound (push notifications), wire your webhook receiver to send Telegram messages.

def post_to_telegram(text: str):
    requests.post(
        f"https://api.telegram.org/bot{os.environ['TELEGRAM_TOKEN']}/sendMessage",
        json={
            "chat_id": int(os.environ["TELEGRAM_CHAT_ID"]),
            "text": text,
            "parse_mode": "HTML",
        }
    )

def on_crash(event):
    d = event["data"]
    post_to_telegram(
        f"<b>phone crashed</b>\n"
        f"device: <code>{d['device_id']}</code>\n"
        f"last apk: {d.get('last_apk', '?')}\n"
        f"uptime: {d.get('uptime_s', '?')}s"
    )

now device.crashed events from cloudf.one land in your group chat with full context. the Telegram Bot API supports HTML formatting, photos, documents, and inline keyboards on these notifications too.

scheduled digests

a cron at 09:00 SGT runs a script that pulls last-24h events and posts the summary.

def daily_digest():
    events = requests.get(f"{CF_API}/events?since=24h", headers=CF_H).json()["events"]
    locks = sum(1 for e in events if e["type"] == "device.locked")
    crashes = sum(1 for e in events if e["type"] == "device.crashed")
    post_to_telegram(
        f"<b>daily digest</b>\n"
        f"locks: {locks}\nrashes: {crashes}\n"
        f"window: 24h"
    )

run via cron, GitHub Actions, or a scheduled cloud function. one message per day, predictable, ignorable on quiet days.

securing the bot

three rules.

for higher-security teams, switch from polling to webhook mode and put the receiver behind your IP allowlist plus the bot token check.

hosting

three good options.

most teams pick the VPS. setup time is 15 minutes; cost is rounding error.

frequently asked questions

can a Telegram bot do per-user permissions like Discord roles?

not natively. the workaround is to maintain your own user ID allow-list (and optionally an admin sub-list) inside the bot code. it is less elegant than Discord roles but functionally equivalent.

what is the file size limit on screenshots?

Telegram caps photos at 10 MB on the Bot API. cloud phone screenshots are typically 200-800 KB so this is not an issue. for screen recordings, send as a document instead and the limit jumps to 50 MB on the standard API.

can the bot post into multiple chats?

yes, just call sendMessage with different chat_id values. teams often use one chat for crashes, another for billing, another for the daily digest.

how do I handle a deleted bot or revoked token?

@BotFather lets you regenerate the token. update the env var on your host, restart the bot, you are back. all chat history and the bot’s group memberships survive a token rotation.

does this work with Telegram channels (broadcast) instead of groups?

yes. add the bot as an admin of the channel, set chat_id to the channel username (e.g. @yourchannel) or numeric ID, and sendMessage works the same way. inline buttons still work; commands do not, because channels are broadcast-only.

ready to put cloud phone control in your group chat? start a cloudf.one trial, drop the bot above on a $5 VPS, and have crash alerts pinging your phone within an hour.