cloud phone integration with Discord bots
cloud phone integration with Discord bots
cloud phone Discord bot integration in 2026 turns a community server into a control panel. instead of asking your team to log into the cloudf.one dashboard, they type /screenshot dev_4f2a in a Discord channel and the bot replies with the image. crash alerts arrive in #ops. nightly reports land in #qa. invoice failures ping the billing role. it is the closest thing to ChatOps that mobile testing teams actually use.
this guide walks through building a working Discord bot with slash commands, embeds, and webhook receivers, all wired to the cloudf.one API. if you have done the API basics tour, you have everything you need.
why Discord and not Slack
most teams already use both. Slack wins for enterprise, Discord wins for indie devs, agencies, communities, gaming companies, and anyone who already runs a public server. the cloud phone integration story is identical (lock, screenshot, reboot, alert) but Discord’s slash commands and embeds give you a slightly nicer UX out of the box and the bot is free up to a high message rate.
if your team lives in Slack, see the Slack notifications guide. everything below has a one-to-one Slack equivalent.
what your bot will do
three feature buckets. start with one, add the rest as you need them.
- slash commands:
/devices,/lock,/unlock,/screenshot,/reboot - alerts: post crash, abandoned-session, and billing events to a channel via webhooks
- daily digest: morning summary of last-24-hour usage, posted at 09:00 SGT
a 200-line bot covers all three with room to spare.
creating the Discord application
go to the Discord developer portal, click new application, name it (e.g. “cloudfone-ops”), then go to bot, click reset token, and copy the secret. add the bot to your server with the applications.commands and bot scopes, plus the send messages and embed links permissions. the Discord developer docs cover the OAuth flow.
stash the bot token in your env.
export DISCORD_BOT_TOKEN="..."
export DISCORD_GUILD_ID="..."
export CLOUDFONE_TOKEN="..."
the minimal Python bot
discord.py 2.x is the cleanest library. install with pip install -U discord.py requests.
import os, requests, discord
from discord import app_commands
CF_API = "https://api.cloudf.one/v1"
CF_HEADERS = {"Authorization": f"Bearer {os.environ['CLOUDFONE_TOKEN']}"}
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
GUILD = discord.Object(id=int(os.environ["DISCORD_GUILD_ID"]))
@tree.command(name="devices", description="list cloud phones", guild=GUILD)
async def devices(interaction: discord.Interaction):
r = requests.get(f"{CF_API}/devices", headers=CF_HEADERS).json()
lines = [f"`{d['id']}` {d['model']} — {d['state']}" for d in r["devices"]]
await interaction.response.send_message("\n".join(lines)[:1900])
@tree.command(name="screenshot", description="grab a screenshot", guild=GUILD)
async def screenshot(interaction: discord.Interaction, device_id: str):
await interaction.response.defer()
r = requests.post(f"{CF_API}/devices/{device_id}/screenshot",
headers=CF_HEADERS).json()
embed = discord.Embed(title=f"screenshot {device_id}")
embed.set_image(url=r["url"])
await interaction.followup.send(embed=embed)
@tree.command(name="lock", description="lock a device", guild=GUILD)
async def lock(interaction: discord.Interaction, tag: str = "pool-default", minutes: int = 10):
r = requests.post(f"{CF_API}/devices/lock", headers=CF_HEADERS, json={
"tag": tag, "duration": minutes * 60
}).json()
await interaction.response.send_message(
f"locked `{r['device_id']}` for {minutes}m\nadb: `{r['adb_endpoint']}`"
)
@client.event
async def on_ready():
await tree.sync(guild=GUILD)
print(f"logged in as {client.user}")
client.run(os.environ["DISCORD_BOT_TOKEN"])
run this on any machine with internet. the slash commands appear in your server within seconds of tree.sync.
wiring crash alerts via webhooks
slash commands are pull. alerts are push. for push, use a Discord webhook URL plus the cloudf.one webhook receiver from the webhook automation guide.
create a Discord webhook in your #ops channel (channel settings, integrations, webhooks, new). copy the URL. then in your receiver:
import os, requests
DISCORD_WEBHOOK = os.environ["DISCORD_OPS_WEBHOOK"]
def post_crash(event):
requests.post(DISCORD_WEBHOOK, json={
"embeds": [{
"title": "phone crashed",
"description": f"device {event['data']['device_id']}",
"color": 0xC62828,
"fields": [
{"name": "last apk", "value": event["data"].get("last_apk", "?"), "inline": True},
{"name": "uptime", "value": str(event["data"].get("uptime_s", "?")), "inline": True},
]
}]
})
now every device.crashed event from cloudf.one lands in #ops as a red embed with the relevant fields. mean time to notice drops from minutes to seconds.
permissions and roles
not every Discord member should be able to reboot a phone. gate dangerous commands by role.
def require_role(role_name: str):
async def predicate(interaction: discord.Interaction) -> bool:
return any(r.name == role_name for r in interaction.user.roles)
return app_commands.check(predicate)
@tree.command(name="reboot", description="reboot a device", guild=GUILD)
@require_role("ops")
async def reboot(interaction: discord.Interaction, device_id: str):
requests.post(f"{CF_API}/devices/{device_id}/reboot", headers=CF_HEADERS)
await interaction.response.send_message(f"rebooting `{device_id}`")
create an “ops” role in your server, assign it to trusted members, and only they can fire the reboot.
daily digest pattern
a cron job runs at 09:00 SGT, queries the last 24 hours of events, and posts a summary embed.
def daily_digest():
events = requests.get(f"{CF_API}/events?since=24h", headers=CF_HEADERS).json()
locks = sum(1 for e in events["events"] if e["type"] == "device.locked")
crashes = sum(1 for e in events["events"] if e["type"] == "device.crashed")
requests.post(DISCORD_WEBHOOK, json={
"embeds": [{
"title": "cloud phone daily digest",
"color": 0x2E7D32,
"fields": [
{"name": "locks", "value": str(locks), "inline": True},
{"name": "crashes", "value": str(crashes), "inline": True},
]
}]
})
schedule via cron, GitHub Actions, or any scheduler. the team gets the same number every morning at the same time.
hosting the bot
three good options.
- a $5 VPS (DigitalOcean, Hetzner, Vultr). simple, cheap, you own it.
- a free Railway or Fly.io tier. push to git, autodeploys.
- a serverless function for the webhook receiver only, plus a separate VPS for the persistent slash command bot (because Discord requires a long-lived gateway connection).
most teams pick the VPS. it costs less than a coffee per month.
frequently asked questions
can the bot run in multiple servers?
yes. drop the guild=GUILD parameter on tree.command and call tree.sync() without a guild to register commands globally. global commands take up to an hour to propagate; guild commands appear instantly.
how do I avoid leaking the cloudf.one token through Discord?
never echo the token in any message. store it only in env vars on the bot host. if a slash command needs to print connection details, only print the ADB endpoint, not the bearer token.
what if Discord rate-limits the webhook?
Discord webhooks rate-limit at 30 messages per minute per channel. for higher throughput, use the bot’s gateway connection rather than the webhook URL. for crash storms, debounce alerts by device ID over a 60-second window.
can users in DMs run the slash commands?
yes if you set dm_permission=True on the command, but most teams keep them server-only so the audit trail (channel history) is preserved.
does this work with Discord threads?
yes, you can post webhook messages directly into a thread by appending ?thread_id=... to the webhook URL. useful for keeping crash alerts grouped per device.
ready to put cloud phone control inside Discord? grab a cloudf.one trial token, run the bot above, and ship /screenshot to your server in 30 minutes.