feat(04-04): /start carries the araharah logo (static-asset URL, resilient text fallback) + repo icon assets

This commit is contained in:
araharah-executor
2026-10-24 02:57:39 +08:00
parent 2c5fff1899
commit 135621f007
3 changed files with 36 additions and 8 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ export function createBot(env: Env, opts: { baseFetch?: typeof fetch } = {}): Bo
const bot = opts.baseFetch
? new Bot(env.BOT_TOKEN, { client: { fetch: opts.baseFetch } })
: new Bot(env.BOT_TOKEN);
registerStart(bot); // /start + /mula (D-04)
registerStart(bot, env.MINI_APP_URL); // /start + /mula (D-04) — carries the logo asset
registerHelp(bot); // /help
registerPrivacy(bot); // /privacy + /privasi (D-04)
registerLocationFallback(bot, env.chat_sessions); // GEO-02 location reply fallback
+35 -7
View File
@@ -6,8 +6,14 @@
*
* D-02 (structural, pinned by test/commands.test.mjs): the reply is plain
* text with link previews disabled — no reply menus, no inline button
* payloads, no callback routes anywhere in this tree. Telegram commands
* are the interface until the Mini App launch path lands (Phase 4).
* payloads, no callback routes anywhere in this tree.
*
* Device-gate feedback (2026-10-24): /start now carries the app logo —
* sendPhoto by its STATIC-ASSET public URL (<MINI_APP_URL>/logo_arah2.png,
* deployed from web/public/). Telegram fetches it server-side; the repo
* asset stays the single source (assets/logo_arah2.png). Photo send is
* resilient: if the fetch/send fails (e.g. asset not deployed yet), the
* text reply still goes out — the greeting is never lost.
*
* All copy comes from src/i18n/strings.ts (D-05) — nothing inline here.
*/
@@ -20,13 +26,35 @@ export function startReplyText(locale: Locale): string {
return strings[locale].startIntro;
}
async function handleStart(ctx: Context): Promise<void> {
/** The logo's public URL — derived from the same configured origin the
* deep link uses (single origin of truth, D-11). Empty when unconfigured
* (tests/dev without vars): the caller then skips the photo send. */
export function startLogoUrl(origin: string | undefined): string | null {
if (!origin) return null;
try {
return `${new URL(origin).origin}/logo_arah2.png`;
} catch {
return null;
}
}
async function handleStart(ctx: Context, origin: string | undefined): Promise<void> {
const locale = pickLocale(ctx.from?.language_code);
await ctx.reply(startReplyText(locale), { link_preview_options: { is_disabled: true } });
const text = startReplyText(locale);
const logoUrl = startLogoUrl(origin);
if (logoUrl !== null) {
try {
await ctx.replyWithPhoto(logoUrl, { caption: text });
return;
} catch {
// fall through to the text reply — the greeting survives asset loss
}
}
await ctx.reply(text, { link_preview_options: { is_disabled: true } });
}
/** Register the command stack entry: /start and its Malay alias /mula. */
export function registerStart(bot: Bot): void {
bot.command("start", handleStart);
bot.command("mula", handleStart);
export function registerStart(bot: Bot, origin?: string): void {
bot.command("start", (ctx) => handleStart(ctx, origin));
bot.command("mula", (ctx) => handleStart(ctx, origin));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB