gramio-pick-username
Pick a Telegram Bot Username
You help the user find a short, memorable, available Telegram bot username that satisfies BotFather's rules.
Arguments
The user provides free-form context, typically a topic and audience. Examples:
/gramio-pick-username weather bot for Russian users/gramio-pick-username крипто-трекер, русскоязычная аудитория/gramio-pick-username is @weatherly_bot free?
If the user only gives a topic, proceed without asking — generate candidates, check availability, report back. Ask only when the topic is genuinely ambiguous (e.g. single word with multiple meanings) or the target audience is impossible to guess.
Rules BotFather enforces
Validate every candidate against all of these before wasting a fetch on it:
- Length: 5–32 characters total.
- Charset:
a-z,A-Z,0-9,_only (case-insensitive at lookup time, but store lowercase). - Suffix: must end in
bot(case-insensitive —FooBot,foo_bot,fooBOTall legal). Exception: collectible usernames on Fragment skip thebotsuffix but require a paid upgrade (1000+ TON) — do not suggest these unless the user explicitly asks for short/premium names. - Underscores: cannot start or end with
_, cannot contain consecutive__. - First character: must be a letter (cannot start with a digit or underscore).
- Reserved / profane: Telegram silently rejects some words; if a legal-looking name is refused in BotFather, drop it and move on.
Any candidate violating 1–5 is filtered out locally before any network check.
Candidate-generation strategy
Given topic T and audience language L:
- Obvious direct forms —
{T}bot,{T}_bot,the{T}bot,my{T}bot,{T}ai_bot,{T}hub_bot. Almost always taken for popular topics, but you must rule them out explicitly. - Prefixes —
my,the,get,ask,hey,now,ok,tap,go,it,ai,hub,box,pal,peek,lens. - Suffix variants before
bot—ly,ify,io,hq,lab,kit,zen,wise,mate,pal. - Transliteration for non-English audiences — e.g. for Russian weather bot:
pogoda,kakpogoda,chopogoda,pogoday,pogodnik,pogodushka,gradus,nebo. Always include both English and transliterated forms whenLis not English. - Domain synonyms — pull from the topic's jargon. Weather →
meteo,forecast,sky,cloud,rain,climate,nimbus,breeze. Crypto →chain,block,token,ledger,hodl. Tasks →todo,task,doit,tick,check. - Soft mutations — drop a vowel (
weathr), addr/y/q(climatiq,weathery), swaps→z. Keep readable. - Two-word merges that read as one —
weatherit_bot✓ (reads cleanly) ;weather_one_bot✗ (two words plusbot= visually noisy). Prefer the merge that letsbotfuse into the tail.
Generate ~20 candidates before any network call. More = wasted fetches; fewer = likely all taken.
Availability check (t.me button inspection)
The truth signal is the main CTA button on https://t.me/<username>:
| Button text | og:title pattern | Verdict |
|---|---|---|
Start Bot |
Telegram: Launch @xxx |
taken — live bot |
View Bot |
Telegram: Contact @xxx |
taken — bot without /start handler, still reserved |
Send Message |
generic "Telegram: Contact" | free — username not registered as bot or user |
View Channel / View Group |
— | taken by non-bot (rare for *bot suffix, but possible) |
Additional taken-signals to double-check: presence of avatar image, tgme_page_description block, Subscribe button.
Critical caveats (spell these out to the user in the final report)
- t.me always returns HTTP 200 and shows the boilerplate "If you have Telegram, you can contact @..." page. The boilerplate text says nothing about availability — only the button text does.
- "Free on t.me" ≠ "creatable in BotFather". If a bot owner deletes their bot via
/deletebot, the username often stays reserved and BotFather refuses/newboton it. The only ground truth is attempting/newbotin @BotFather. Always tell the user to do this final check before committing. - Case doesn't matter.
@WeatherBotand@weatherbotresolve to the same account. Lowercase your candidates before fetching. - Rate limits exist. If you batch too aggressively and start seeing 429s or rate-limit pages, back off and slow the batch.
Availability check — use the bundled script, not WebFetch
This skill ships with check-usernames.mjs in its own directory. Always use it for availability checks — do not fall back to WebFetch per-URL.
Why:
- One subprocess invocation returns a compact JSON verdict per username instead of 20+ KB of raw HTML per fetch. Your context stays clean.
- It validates each name locally (length, charset, suffix, leading digit, consecutive/trailing underscores) before fetching, so invalid candidates cost zero requests.
- Parallelism is handled in-process with a bounded worker pool (default 8) — no wall-of-tool-calls.
- Runs on any machine with Node ≥18, Bun, or Deno (all ship a native
fetch). Zero dependencies. No bash / no Windowsshquirks — portable across Claude Code, Cursor, Copilot, and any other agent runtime that can spawnnode.
How to invoke
# Pass candidates as CLI args (preferred for small batches)
node skills/gramio-pick-username/check-usernames.mjs --json \
weatherbot weatheritbot meteobot kakpogodabot nebobot
# Or pipe from stdin (useful for larger lists)
printf 'weatherbot\nweatheritbot\nmeteobot\n' | \
node skills/gramio-pick-username/check-usernames.mjs --json
# Tuning knobs
node … --concurrency 4 --timeout 10000 foo_bot bar_bot
From inside the user's project, the script lives under whatever path they installed the skills to — usually ./skills/gramio-pick-username/check-usernames.mjs. Check the cwd before running, and if the script isn't where you expect, fall back to bun or copy the script to a known location.
JSON output schema
The --json flag emits an array of result objects:
[
{
"username": "weatheritbot",
"verdict": "free",
"kind": "unclaimed",
"button": "Send Message",
"ogTitle": "Telegram: Contact @weatheritbot",
"hasAvatar": false,
"status": 200
},
{
"username": "weatherbot",
"verdict": "taken",
"kind": "bot_live",
"button": "Start Bot",
"hasAvatar": true,
"status": 200
},
{
"username": "1foo",
"verdict": "invalid",
"reasons": ["MUST_START_WITH_LETTER", "MISSING_BOT_SUFFIX"]
}
]
Verdicts: free · taken · invalid · rate_limited · error · unknown.
Kinds (when taken): bot_live · bot_no_start · user · channel · group.
Handling non-clean results
rate_limited→ back off: reduce--concurrencyto 2–3, wait ~60s, retry only those names. Do not spam the endpoint.error→ retry once with a higher--timeout. If it still fails, mark the name as "unknown, verify manually" in the final report — do not silently treat it as free.unknown→ the HTML didn't match known patterns (rare). Fetch the page manually once via WebFetch to inspect, then update the classifier if you've found a new pattern.
Final ranking (criteria for the shortlist)
Rank surviving free candidates by:
- Length — prefer ≤16 chars (comfortable inline mention, fits in bio/ads).
- Readability in target language — a native reader should parse it at first glance, no syllable backtracking.
- No digits / no underscores if a clean alternative exists.
weatherit_botbeatsweather_1_bot. botfuses into the word —weatheritbot>weatheronebot. Two-word roots +botlook like three tokens.- Pronounceable aloud — useful for podcasts, demos, referrals.
- No trademark risk — flag candidates that collide with a known brand in the topic (e.g.
chatgpt_botis a legal minefield).
Output format
Deliver a compact report the user can act on:
Topic: <topic> · Audience: <language/region>
✅ FREE (ranked)
1. @weatheritbot — 12 chars · reads clean in EN · "it" nod to imperative mood
2. @kakpogodabot — 11 chars · RU-native · "how's the weather" question form
3. @nebobot — 6 chars · RU · "sky" · very short, memorable
⚠️ FREE ON T.ME BUT VERIFY IN BOTFATHER
4. @breezybot — maybe deleted-and-reserved · try `/newbot` first
❌ TAKEN (checked)
- @weatherbot (Start Bot)
- @pogodabot (View Bot)
- @meteobot (Start Bot)
- ... (condensed list, grouped)
NEXT STEP
Open @BotFather → /newbot → paste "@weatheritbot" when asked for the username.
If BotFather rejects it, try the next one down the list.
Anti-patterns to avoid
- Don't judge availability from the "If you have Telegram" boilerplate text — it's identical for free and taken names.
- Don't rely on HTTP status codes — they're always 200.
- Don't suggest names with consecutive
__, trailing_, or leading digit — BotFather rejects them and you'll have wasted the user's time. - Don't skip the "verify in BotFather" reminder —
t.mefree ≠ creatable. - Don't propose
gpt/openai/telegram-prefixed names without flagging the trademark risk. - Don't burn fetches on candidates that fail local validation. Validate, then fetch.
- Don't over-ask. If the topic is clear, just generate and check — deliver a shortlist, not a questionnaire.
After the user picks a name
Once the user picks a finalist from the shortlist and confirms BotFather accepted it, remind them to copy the bot's @username and the token BotFather returned into .env as BOT_TOKEN=... before starting the bot.
More from gramiojs/documentation
gramio
Invoke for ANY Telegram bot code — `gramio`/`@gramio/*` imports, `new Bot()`, `bot.command`/`bot.callbackQuery`/`bot.on`/`bot.updates.on`, scenes/FSM, inline & reply keyboards, message entities, file uploads, sessions, plugins, webhooks vs long-polling, Telegram Stars, Mini Apps (TMA), broadcasting, Docker. Type-safe TypeScript framework (Node.js/Bun/Deno) with full Bot API coverage. Also covers migrations from Telegraf/grammY/puregram/node-telegram-bot-api. When delegating to a subagent, pass relevant reference files (callback-data, scenes, formatting, context, middleware-routing, ux-patterns) inline — this skill does not auto-load in subagent sessions.
164add-doc-page
Create a new documentation page in both EN and RU with proper frontmatter, sidebar registration, and automatic Russian translation.
1translate-page
Translate a GramIO documentation page from English to Russian (or vice versa), preserving code blocks, twoslash annotations, and frontmatter.
1enrich-api-page
Research and fill user-maintained content for Telegram API reference pages — SEO meta, GramIO usage examples, errors with context annotations, tips & gotchas, and see-also links. Supports multiple page names at once.
1gramio-help
Internal knowledge skill — provides Claude with GramIO framework context by reading documentation files dynamically.
1add-plugin-doc
Create documentation for a new official GramIO plugin with proper template, badges, installation instructions, and sidebar registration.
1