From 432e7e451472e5cbc0c4249040e28e38090b050f Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:31:17 +0000 Subject: [PATCH 01/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- .../configure-telegram-webhook/index.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/supabase/functions/configure-telegram-webhook/index.ts b/supabase/functions/configure-telegram-webhook/index.ts index b173ba0..66cd8e7 100644 --- a/supabase/functions/configure-telegram-webhook/index.ts +++ b/supabase/functions/configure-telegram-webhook/index.ts @@ -113,6 +113,34 @@ Deno.serve(async (req) => { return jsonResponse({ error: "Webhook configurado, mas falha ao salvar." }, 500); } + // Auto-provisionamento: agora que temos token + webhook, disparamos provision-agent + // de forma assíncrona. Não bloqueamos a resposta — falhas são tratadas via retry/admin. + if (agent.status === "provisioning") { + try { + const provisionUrl = `${supabaseUrl}/functions/v1/provision-agent`; + console.log( + `auto-provision: disparando para agent ${agent.id} (user ${userId})`, + ); + // Fire-and-forget: não fazemos await para não bloquear o cliente + fetch(provisionUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${serviceKey}`, + }, + body: JSON.stringify({ + agent_instance_id: agent.id, + user_id: userId, + }), + }).catch((err) => + console.error("auto-provision fetch error:", err) + ); + } catch (err) { + console.error("auto-provision trigger error:", err); + // Não re-throw — wizard do Telegram deve retornar sucesso mesmo se provision falhar + } + } + return jsonResponse({ success: true }); } catch (err) { console.error("configure-telegram-webhook fatal", err); From 76cb26a927b8d81341b19909ac3f44e7d17cd109 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:31:27 +0000 Subject: [PATCH 02/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/provision-agent/index.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/supabase/functions/provision-agent/index.ts b/supabase/functions/provision-agent/index.ts index 2595e2e..7ee1c42 100644 --- a/supabase/functions/provision-agent/index.ts +++ b/supabase/functions/provision-agent/index.ts @@ -26,6 +26,28 @@ const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const RAILWAY_API_TOKEN = Deno.env.get("RAILWAY_API_TOKEN"); const OPENROUTER_API_KEY = Deno.env.get("OPENROUTER_API_KEY") ?? ""; +const ADMIN_TELEGRAM_BOT_TOKEN = Deno.env.get("ADMIN_TELEGRAM_BOT_TOKEN"); +const ADMIN_TELEGRAM_CHAT_ID = Deno.env.get("ADMIN_TELEGRAM_CHAT_ID"); + +async function notifyAdmin(message: string): Promise { + if (!ADMIN_TELEGRAM_BOT_TOKEN || !ADMIN_TELEGRAM_CHAT_ID) return; + try { + await fetch( + `https://api.telegram.org/bot${ADMIN_TELEGRAM_BOT_TOKEN}/sendMessage`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chat_id: ADMIN_TELEGRAM_CHAT_ID, + text: message, + parse_mode: "HTML", + }), + }, + ); + } catch (e) { + console.error("notifyAdmin failed:", e); + } +} Deno.serve(async (req) => { if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); From 467f3edfb97c4e12f87b6efb68994c24659286b7 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:32:15 +0000 Subject: [PATCH 03/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/provision-agent/index.ts | 41 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/supabase/functions/provision-agent/index.ts b/supabase/functions/provision-agent/index.ts index 7ee1c42..adcc97c 100644 --- a/supabase/functions/provision-agent/index.ts +++ b/supabase/functions/provision-agent/index.ts @@ -75,6 +75,8 @@ Deno.serve(async (req) => { auth: { persistSession: false, autoRefreshToken: false }, }); + console.log(`[provision-agent] início para agent_instance_id=${body.agent_instance_id}`); + // 1) Carregar agent_instance const { data: agent, error: agentErr } = await supabase .from("agent_instances") @@ -85,14 +87,17 @@ Deno.serve(async (req) => { .maybeSingle(); if (agentErr || !agent) { + console.error(`[provision-agent] agent_instance não encontrado: ${agentErr?.message}`); return jsonResponse(404, { error: "agent_instance not found", detail: agentErr?.message }); } if (agent.status !== "provisioning") { + console.log(`[provision-agent] status atual=${agent.status}, abortando`); return jsonResponse(409, { error: "agent_instance is not in provisioning status", status: agent.status }); } if (agent.railway_service_id) { + console.log(`[provision-agent] já tem railway_service_id=${agent.railway_service_id}, abortando`); return jsonResponse(409, { error: "agent_instance already has a railway_service_id", railway_service_id: agent.railway_service_id }); } @@ -106,6 +111,7 @@ Deno.serve(async (req) => { const fullName = (profile?.full_name?.trim() || "Usuário").toString(); const firstName = fullName.split(" ")[0] || "Usuário"; const agentName = body.agent_name?.trim() || `Mika de ${firstName}`; + console.log(`[provision-agent] profile carregado: ${fullName} → agent_name=${agentName}`); // 1c) Carregar subscription ativa (para definir modelo Pro vs Basic) const { data: subscription } = await supabase @@ -120,6 +126,7 @@ Deno.serve(async (req) => { // deno-lint-ignore no-explicit-any const planSlug = ((subscription as any)?.plans?.slug as string | undefined) ?? "basic"; const isPro = ["professional", "enterprise"].includes(planSlug); + console.log(`[provision-agent] plano=${planSlug} isPro=${isPro}`); // 2) Buscar pool disponível (com IDs Railway preenchidos e capacidade) const { data: pool, error: poolErr } = await supabase @@ -133,9 +140,17 @@ Deno.serve(async (req) => { .maybeSingle(); if (poolErr || !pool || !pool.railway_project_id || !pool.railway_environment_id) { + console.error(`[provision-agent] sem vps_pool disponível: ${poolErr?.message}`); await failJob(supabase, agent, null, "Nenhum vps_pool com Railway IDs configurados disponível"); + await notifyAdmin( + `❌ Falha no auto-provisionamento\n\n` + + `👤 Cliente: ${fullName}\n` + + `❗ Erro: Nenhum vps_pool disponível\n\n` + + `➡️ Resolver manualmente`, + ); return jsonResponse(503, { error: "no railway pool available" }); } + console.log(`[provision-agent] pool selecionado: ${pool.id} (railway_project=${pool.railway_project_id})`); // 3) Criar provisioning_job em status running const { data: job, error: jobErr } = await supabase @@ -158,12 +173,15 @@ Deno.serve(async (req) => { .single(); if (jobErr || !job) { + console.error(`[provision-agent] falha ao criar job: ${jobErr?.message}`); return jsonResponse(500, { error: "failed to create provisioning_job", detail: jobErr?.message }); } + console.log(`[provision-agent] provisioning_job criado: ${job.id}`); // 4) Decrypt do telegram_bot_token (se existir) let telegramBotToken = ""; if (agent.telegram_bot_token_vault_id) { + console.log(`[provision-agent] decifrando token do Vault: ${agent.telegram_bot_token_vault_id}`); const { data: secret } = await supabase.rpc("vault_decrypt_secret", { secret_id: agent.telegram_bot_token_vault_id, }); @@ -171,15 +189,18 @@ Deno.serve(async (req) => { } if (!telegramBotToken) { + console.error(`[provision-agent] telegram_bot_token ausente — usuário ainda não conectou bot`); await failJob(supabase, agent, job.id, "telegram_bot_token ausente no Vault — usuário precisa concluir onboarding antes"); return jsonResponse(412, { error: "telegram token missing" }); } + console.log(`[provision-agent] token Telegram OK (len=${telegramBotToken.length})`); // 5) Apagar webhook Telegram (Hermes vai usar polling) try { await deleteTelegramWebhook(telegramBotToken); + console.log(`[provision-agent] deleteTelegramWebhook OK`); } catch (e) { - console.warn("deleteTelegramWebhook failed (continuing):", String(e)); + console.warn("[provision-agent] deleteTelegramWebhook failed (continuing):", String(e)); } // 6) Montar variáveis de ambiente do container @@ -212,6 +233,7 @@ Deno.serve(async (req) => { // 7) Criar serviço no Railway const serviceName = `mika-${agent.uuid_tenant.replace(/-/g, "").slice(0, 8)}`; + console.log(`[provision-agent] criando serviço Railway: ${serviceName}`); let railwayServiceId: string; try { @@ -220,6 +242,7 @@ Deno.serve(async (req) => { projectId: pool.railway_project_id, name: serviceName, }); + console.log(`[provision-agent] serviço criado: ${railwayServiceId}`); await configureRailwayService({ token: RAILWAY_API_TOKEN, @@ -229,16 +252,26 @@ Deno.serve(async (req) => { startCommand: HERMES_START_COMMAND, variables: envVars, }); + console.log(`[provision-agent] serviço configurado com ${Object.keys(envVars).length} env vars`); await deployRailwayService({ token: RAILWAY_API_TOKEN, serviceId: railwayServiceId, environmentId: pool.railway_environment_id, }); + console.log(`[provision-agent] deploy disparado em Railway`); } catch (e) { const msg = e instanceof Error ? e.message : String(e); - console.error("Railway provisioning failed:", msg); - await scheduleRetry(supabase, agent, job.id, msg); + console.error("[provision-agent] Railway provisioning failed:", msg); + const reachedMax = await scheduleRetry(supabase, agent, job.id, msg); + if (reachedMax) { + await notifyAdmin( + `❌ Falha no auto-provisionamento\n\n` + + `👤 Cliente: ${fullName}\n` + + `❗ Erro: ${msg}\n\n` + + `➡️ Provisionar manualmente`, + ); + } return jsonResponse(500, { error: "railway provisioning failed", detail: msg }); } @@ -262,6 +295,8 @@ Deno.serve(async (req) => { .update({ railway_service_id: railwayServiceId, status: "running" }) .eq("id", job.id); + console.log(`[provision-agent] sucesso: agent=${agent.id} railway=${railwayServiceId} (aguardando deploy)`); + // status do agent permanece 'provisioning' — railway-webhook atualiza para 'active' quando deploy subir return jsonResponse(200, { success: true, From b650a0f11d14da957853b270142b0b3f6f637bf7 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:32:26 +0000 Subject: [PATCH 04/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/provision-agent/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supabase/functions/provision-agent/index.ts b/supabase/functions/provision-agent/index.ts index adcc97c..a3808de 100644 --- a/supabase/functions/provision-agent/index.ts +++ b/supabase/functions/provision-agent/index.ts @@ -335,7 +335,7 @@ async function scheduleRetry( agent: { id: string }, jobId: string, message: string, -) { +): Promise { const { data: job } = await supabase .from("provisioning_jobs") .select("attempt, max_attempts") @@ -347,7 +347,7 @@ async function scheduleRetry( if (attempt >= max) { await failJob(supabase, agent, jobId, `Max attempts reached. Last error: ${message}`); - return; + return true; } const nextDelayMs = Math.pow(attempt, 2) * 60_000; From c5dfae1482b2197a5acc9b70b8027509a49bc7a2 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:32:33 +0000 Subject: [PATCH 05/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/provision-agent/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/supabase/functions/provision-agent/index.ts b/supabase/functions/provision-agent/index.ts index a3808de..0791394 100644 --- a/supabase/functions/provision-agent/index.ts +++ b/supabase/functions/provision-agent/index.ts @@ -362,4 +362,5 @@ async function scheduleRetry( next_retry_at: nextRetryAt, }) .eq("id", jobId); + return false; } From 6b069037ff88631419b17396c26265465cbb036e Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:32:42 +0000 Subject: [PATCH 06/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/railway-webhook/index.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/supabase/functions/railway-webhook/index.ts b/supabase/functions/railway-webhook/index.ts index d891b0e..6943cfb 100644 --- a/supabase/functions/railway-webhook/index.ts +++ b/supabase/functions/railway-webhook/index.ts @@ -13,6 +13,28 @@ import { corsHeaders } from "../_shared/cors.ts"; const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; +const ADMIN_TELEGRAM_BOT_TOKEN = Deno.env.get("ADMIN_TELEGRAM_BOT_TOKEN"); +const ADMIN_TELEGRAM_CHAT_ID = Deno.env.get("ADMIN_TELEGRAM_CHAT_ID"); + +async function notifyAdmin(message: string): Promise { + if (!ADMIN_TELEGRAM_BOT_TOKEN || !ADMIN_TELEGRAM_CHAT_ID) return; + try { + await fetch( + `https://api.telegram.org/bot${ADMIN_TELEGRAM_BOT_TOKEN}/sendMessage`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chat_id: ADMIN_TELEGRAM_CHAT_ID, + text: message, + parse_mode: "HTML", + }), + }, + ); + } catch (e) { + console.error("notifyAdmin failed:", e); + } +} Deno.serve(async (req) => { if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); From 7218ed9885477001fb81a429cb227ffab6130aea Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:33:06 +0000 Subject: [PATCH 07/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/functions/railway-webhook/index.ts | 38 ++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/supabase/functions/railway-webhook/index.ts b/supabase/functions/railway-webhook/index.ts index 6943cfb..840669e 100644 --- a/supabase/functions/railway-webhook/index.ts +++ b/supabase/functions/railway-webhook/index.ts @@ -81,7 +81,7 @@ Deno.serve(async (req) => { const { data: agent } = await supabase .from("agent_instances") - .select("id, status") + .select("id, status, user_id, telegram_bot_username, railway_service_id") .eq("railway_service_id", serviceId) .maybeSingle(); @@ -92,6 +92,17 @@ Deno.serve(async (req) => { const now = new Date().toISOString(); const upper = status.toUpperCase(); + const wasProvisioning = agent.status === "provisioning"; + + // Carrega nome do cliente para a notificação (best-effort) + async function loadFullName(): Promise { + const { data } = await supabase + .from("profiles") + .select("full_name") + .eq("id", agent.user_id) + .maybeSingle(); + return (data?.full_name as string | undefined) || "—"; + } if (upper === "SUCCESS" || upper === "ACTIVE" || upper === "DEPLOYED") { await supabase @@ -110,6 +121,19 @@ Deno.serve(async (req) => { .in("status", ["running", "retrying", "pending"]); console.log(`railway-webhook: agent ${agent.id} marcado como active (status=${upper})`); + + // Notifica admin somente se era um auto-provisionamento (status anterior=provisioning) + if (wasProvisioning) { + const fullName = await loadFullName(); + await notifyAdmin( + `✅ Agente provisionado automaticamente!\n\n` + + `👤 Cliente: ${fullName}\n` + + `🤖 Bot: @${agent.telegram_bot_username || "—"}\n` + + `🚀 Railway: ${agent.railway_service_id}\n\n` + + `O cliente já pode conversar com a Mika no Telegram.`, + ); + } + return jsonResponse(200, { ok: true, agent_id: agent.id, new_status: "active" }); } @@ -130,6 +154,18 @@ Deno.serve(async (req) => { .in("status", ["running", "retrying", "pending"]); console.log(`railway-webhook: agent ${agent.id} marcado como error (status=${upper})`); + + if (wasProvisioning) { + const fullName = await loadFullName(); + await notifyAdmin( + `❌ Falha no deploy do agente\n\n` + + `👤 Cliente: ${fullName}\n` + + `🚀 Railway: ${agent.railway_service_id}\n` + + `❗ Status: ${upper}\n\n` + + `➡️ Investigar`, + ); + } + return jsonResponse(200, { ok: true, agent_id: agent.id, new_status: "error" }); } From 16c7baa8600db4db51694b4a040b01e1477a7c5e Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:33:14 +0000 Subject: [PATCH 08/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/painel.index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/painel.index.tsx b/src/routes/painel.index.tsx index c80bf71..d16e966 100644 --- a/src/routes/painel.index.tsx +++ b/src/routes/painel.index.tsx @@ -1,8 +1,8 @@ "use client"; import { createFileRoute, Link } from "@tanstack/react-router"; -import { useEffect, useState } from "react"; -import { ArrowRight, CheckCircle2, Loader2, Sparkles } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import { ArrowRight, CheckCircle2, Loader2, MessageCircle, Sparkles } from "lucide-react"; import { useSubscription } from "@/hooks/use-profile"; import { useProfile } from "@/hooks/use-profile"; import { useAgentInstance } from "@/hooks/use-agent-instance"; From a3c35c61fbaf83c149a2a339bae36296acb0692c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:33:35 +0000 Subject: [PATCH 09/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/painel.index.tsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/routes/painel.index.tsx b/src/routes/painel.index.tsx index d16e966..189abb8 100644 --- a/src/routes/painel.index.tsx +++ b/src/routes/painel.index.tsx @@ -33,6 +33,7 @@ function DashboardPage() { const search = Route.useSearch(); const navigate = Route.useNavigate(); const [wizardOpen, setWizardOpen] = useState(false); + const previousStatusRef = useRef(null); // Auto-open do wizard ao voltar com ?status=success useEffect(() => { @@ -51,6 +52,18 @@ function DashboardPage() { } }, [search.status, agent, isLoading]); + // Toast quando agente sair de provisioning → active (uma única vez) + useEffect(() => { + if (!agent) return; + const prev = previousStatusRef.current; + if (prev === "provisioning" && agent.status === "active") { + toast.success("Sua Mika está pronta! 🎉", { + description: "Abra o Telegram e mande uma mensagem para começar.", + }); + } + previousStatusRef.current = agent.status; + }, [agent]); + if (isLoading) { return (
@@ -79,10 +92,16 @@ function DashboardPage() { {subscription && (subscription.status === "incomplete" || subscription.status === "active") && - agent?.status === "provisioning" && } + agent?.status === "provisioning" && ( + + )} {subscription && subscription.status === "active" && agent?.status === "active" && ( <> + {agent.telegram_bot_username && }
From 66b087bdadb44809314b62d759106de7709b228e Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:33:59 +0000 Subject: [PATCH 10/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/painel.index.tsx | 91 ++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/src/routes/painel.index.tsx b/src/routes/painel.index.tsx index 189abb8..08f6171 100644 --- a/src/routes/painel.index.tsx +++ b/src/routes/painel.index.tsx @@ -140,13 +140,34 @@ function NoSubscriptionCard() { ); } -function ProvisioningCard() { - const steps = [ - { label: "Provisionar container na VPS", state: "active" as const }, - { label: "Configurar modelo de IA", state: "pending" as const }, - { label: "Conectar seu Telegram", state: "pending" as const }, - { label: "Personalizar seu agente", state: "pending" as const }, +type StepState = "done" | "active" | "pending"; + +function ProvisioningCard({ + telegramConnected, + railwayServiceCreated: _railwayServiceCreated, +}: { + telegramConnected: boolean; + railwayServiceCreated?: boolean; +}) { + // Etapas dinâmicas — sabemos: pagamento confirmado (sempre done aqui), + // Telegram conectado (vem do agent.telegram_bot_username), e o resto + // está em andamento até o railway-webhook chegar como SUCCESS. + const steps: { label: string; state: StepState }[] = [ + { label: "Pagamento confirmado", state: "done" }, + { + label: telegramConnected ? "Telegram conectado" : "Conecte seu Telegram", + state: telegramConnected ? "done" : "active", + }, + { + label: "Provisionando container", + state: telegramConnected ? "active" : "pending", + }, + { + label: "Mika quase pronta!", + state: "pending", + }, ]; + return (
@@ -154,9 +175,9 @@ function ProvisioningCard() {
-

Seu agente Mika está sendo provisionado

+

Estamos preparando seu agente Mika

- Você receberá um e-mail quando estiver pronto — geralmente em até 10 minutos. + Geralmente leva de 3 a 5 minutos. Esta página atualiza sozinha quando ficar pronta.

@@ -168,23 +189,25 @@ function ProvisioningCard() {
- {step.state === "active" ? ( - - ) : step.state === "pending" ? ( - i + 1 - ) : ( + {step.state === "done" ? ( + ) : step.state === "active" ? ( + + ) : ( + i + 1 )}

{step.label} @@ -196,3 +219,37 @@ function ProvisioningCard() {

); } + +function ActiveSuccessCard({ botUsername }: { botUsername: string }) { + return ( +
+
+
+
+ +
+
+

Sua Mika está no ar 🎉

+

+ Abra o Telegram e converse com{" "} + @{botUsername} para começar. +

+
+
+ +
+
+ ); +}