From 676a42425950af45b0eee1113454f389494893fe Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:23:37 +0000 Subject: [PATCH 01/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/integrations/supabase/types.ts | 6 ++++++ ...20260426202333_f3fab87d-8ee9-4cd5-ab4d-e23c9d55f881.sql | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 supabase/migrations/20260426202333_f3fab87d-8ee9-4cd5-ab4d-e23c9d55f881.sql diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index e08f5ef..23a8b39 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -21,6 +21,8 @@ export type Database = { created_at: string id: string last_health_check_at: string | null + managed_bot_pending: boolean + managed_bot_suggested_username: string | null model_config: Json onboarding_completed: boolean provisioned_at: string | null @@ -48,6 +50,8 @@ export type Database = { created_at?: string id?: string last_health_check_at?: string | null + managed_bot_pending?: boolean + managed_bot_suggested_username?: string | null model_config?: Json onboarding_completed?: boolean provisioned_at?: string | null @@ -75,6 +79,8 @@ export type Database = { created_at?: string id?: string last_health_check_at?: string | null + managed_bot_pending?: boolean + managed_bot_suggested_username?: string | null model_config?: Json onboarding_completed?: boolean provisioned_at?: string | null diff --git a/supabase/migrations/20260426202333_f3fab87d-8ee9-4cd5-ab4d-e23c9d55f881.sql b/supabase/migrations/20260426202333_f3fab87d-8ee9-4cd5-ab4d-e23c9d55f881.sql new file mode 100644 index 0000000..82f799d --- /dev/null +++ b/supabase/migrations/20260426202333_f3fab87d-8ee9-4cd5-ab4d-e23c9d55f881.sql @@ -0,0 +1,7 @@ +ALTER TABLE public.agent_instances + ADD COLUMN IF NOT EXISTS managed_bot_pending boolean NOT NULL DEFAULT false, + ADD COLUMN IF NOT EXISTS managed_bot_suggested_username text; + +CREATE INDEX IF NOT EXISTS idx_agent_instances_managed_bot_pending + ON public.agent_instances (managed_bot_pending) + WHERE managed_bot_pending = true; \ No newline at end of file From a8ed0f79e8f010d89ce6c513002d6661830934be Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:24:43 +0000 Subject: [PATCH 02/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- .../functions/create-managed-bot/index.ts | 134 +++++++++++++ .../functions/managed-bot-webhook/index.ts | 180 ++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 supabase/functions/create-managed-bot/index.ts create mode 100644 supabase/functions/managed-bot-webhook/index.ts diff --git a/supabase/functions/create-managed-bot/index.ts b/supabase/functions/create-managed-bot/index.ts new file mode 100644 index 0000000..a281e75 --- /dev/null +++ b/supabase/functions/create-managed-bot/index.ts @@ -0,0 +1,134 @@ +// create-managed-bot +// Recebe { agent_instance_id?, agent_name } do usuário autenticado. +// Gera username sugerido, marca o agent_instance como managed_bot_pending +// e retorna a URL de deep-link para o @mika_managerbot criar o bot em 1 toque. + +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; +import { corsHeaders } from "../_shared/cors.ts"; + +function jsonResponse(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { + status, + headers: { ...corsHeaders, "Content-Type": "application/json" }, + }); +} + +function generateUsername(name: string): string { + const base = (name || "mika") + .toLowerCase() + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") // remove acentos + .replace(/[^a-z0-9]/g, "") // só letras e números + .substring(0, 28); + const safe = base.length >= 3 ? base : `mika${base}`; + return `${safe.substring(0, 28)}bot`; +} + +async function usernameAvailable( + admin: ReturnType, + username: string, +): Promise { + const { data } = await admin + .from("agent_instances") + .select("id") + .or(`telegram_bot_username.eq.${username},managed_bot_suggested_username.eq.${username}`) + .limit(1) + .maybeSingle(); + return !data; +} + +Deno.serve(async (req) => { + if (req.method === "OPTIONS") { + return new Response(null, { headers: corsHeaders }); + } + + try { + const supabaseUrl = Deno.env.get("SUPABASE_URL")!; + const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; + const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!; + const managerUsername = + Deno.env.get("TELEGRAM_MANAGER_BOT_USERNAME") || "mika_managerbot"; + + const authHeader = req.headers.get("Authorization") ?? ""; + const userClient = createClient(supabaseUrl, anonKey, { + global: { headers: { Authorization: authHeader } }, + }); + const { data: userData, error: userErr } = await userClient.auth.getUser(); + if (userErr || !userData.user) { + return jsonResponse({ error: "Não autenticado" }, 401); + } + const userId = userData.user.id; + + const body = await req.json().catch(() => ({})); + const agentName = (body?.agent_name ?? "").toString().trim(); + const explicitAgentId = (body?.agent_instance_id ?? "").toString().trim(); + + if (!agentName || agentName.length < 2) { + return jsonResponse({ error: "Nome do agente inválido." }, 400); + } + + const admin = createClient(supabaseUrl, serviceKey); + + // Localiza agent_instance do usuário + const query = admin + .from("agent_instances") + .select("id, user_id, agent_name, managed_bot_suggested_username") + .eq("user_id", userId); + const { data: agent, error: agentErr } = explicitAgentId + ? await query.eq("id", explicitAgentId).maybeSingle() + : await query.maybeSingle(); + + if (agentErr) { + console.error("agent lookup error", agentErr); + return jsonResponse({ error: "Falha ao localizar agente." }, 500); + } + if (!agent) { + return jsonResponse( + { error: "Agente não encontrado. Aguarde o provisionamento." }, + 404, + ); + } + + // Gera username único (até 5 tentativas com sufixo numérico) + let suggestedUsername = generateUsername(agentName); + let attempts = 0; + while (attempts < 5 && !(await usernameAvailable(admin, suggestedUsername))) { + attempts++; + const suffix = Math.floor(Math.random() * 9000 + 1000); + const baseNoBot = suggestedUsername.replace(/bot$/, ""); + suggestedUsername = `${baseNoBot.substring(0, 24)}${suffix}bot`; + } + + // Persiste estado pendente + const { error: updErr } = await admin + .from("agent_instances") + .update({ + managed_bot_pending: true, + managed_bot_suggested_username: suggestedUsername, + agent_name: agent.agent_name || agentName, + updated_at: new Date().toISOString(), + }) + .eq("id", agent.id); + + if (updErr) { + console.error("agent update error", updErr); + return jsonResponse({ error: "Falha ao iniciar criação do bot." }, 500); + } + + const url = + `https://t.me/newbot/${managerUsername}/${suggestedUsername}` + + `?name=${encodeURIComponent(agentName)}`; + + return jsonResponse({ + url, + suggested_username: suggestedUsername, + manager_username: managerUsername, + }); + } catch (err) { + console.error("create-managed-bot fatal", err); + return jsonResponse( + { error: err instanceof Error ? err.message : "Erro inesperado" }, + 500, + ); + } +}); diff --git a/supabase/functions/managed-bot-webhook/index.ts b/supabase/functions/managed-bot-webhook/index.ts new file mode 100644 index 0000000..195727f --- /dev/null +++ b/supabase/functions/managed-bot-webhook/index.ts @@ -0,0 +1,180 @@ +// managed-bot-webhook +// Recebe updates do @mika_managerbot. Quando o update tem o campo "managed_bot", +// busca o token do novo bot via getManagedBotToken, salva no Vault, atualiza +// o agent_instance correspondente e dispara provisionamento automático. +// +// Setup do webhook: GET ?setup=true configura o webhook do manager bot. +// +// IMPORTANTE: Esta função usa endpoints experimentais do BotFather Bot +// Management Mode que NÃO são parte da Bot API pública oficial. + +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; + +function ack(): Response { + // Sempre 200 — Telegram não deve retentar. + return new Response("ok", { status: 200 }); +} + +function json(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { + status, + headers: { "Content-Type": "application/json" }, + }); +} + +Deno.serve(async (req) => { + const url = new URL(req.url); + const supabaseUrl = Deno.env.get("SUPABASE_URL")!; + const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; + const managerToken = Deno.env.get("TELEGRAM_MANAGER_BOT_TOKEN"); + + // Setup: configura webhook + if (url.searchParams.get("setup") === "true") { + if (!managerToken) { + return json({ error: "TELEGRAM_MANAGER_BOT_TOKEN não configurado" }, 400); + } + const webhookUrl = `${supabaseUrl}/functions/v1/managed-bot-webhook`; + const res = await fetch( + `https://api.telegram.org/bot${managerToken}/setWebhook`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + url: webhookUrl, + allowed_updates: ["message", "managed_bot"], + }), + }, + ); + const data = await res.json().catch(() => ({})); + return json({ webhook_set: webhookUrl, telegram_response: data }); + } + + if (req.method !== "POST") { + return ack(); + } + + if (!managerToken) { + console.error("TELEGRAM_MANAGER_BOT_TOKEN ausente"); + return ack(); + } + + let update: Record; + try { + update = await req.json(); + } catch { + return ack(); + } + + // Só processamos eventos de managed_bot + // deno-lint-ignore no-explicit-any + const managed = (update as any).managed_bot; + if (!managed || !managed.bot) { + return ack(); + } + + try { + const admin = createClient(supabaseUrl, serviceKey); + + const botId = managed.bot.id; + const botUsername: string = managed.bot.username; + // deno-lint-ignore no-explicit-any + const tgUser = (managed as any).user ?? {}; + + // 1) Busca token do bot recém-criado + const tokenRes = await fetch( + `https://api.telegram.org/bot${managerToken}/getManagedBotToken`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ bot_id: botId }), + }, + ); + const tokenJson = await tokenRes.json().catch(() => ({})); + // deno-lint-ignore no-explicit-any + const newBotToken: string | undefined = (tokenJson as any)?.result?.token; + if (!newBotToken) { + console.error("getManagedBotToken falhou", tokenJson); + return ack(); + } + + // 2) Localiza agent_instance pendente correspondente + const filter = tgUser?.id + ? `telegram_user_chat_id.eq.${tgUser.id},managed_bot_suggested_username.eq.${botUsername}` + : `managed_bot_suggested_username.eq.${botUsername}`; + + const { data: agentInstance, error: lookupErr } = await admin + .from("agent_instances") + .select("id, user_id") + .eq("managed_bot_pending", true) + .or(filter) + .order("created_at", { ascending: false }) + .limit(1) + .maybeSingle(); + + if (lookupErr || !agentInstance) { + console.error( + "Nenhum agent_instance pendente encontrado para managed bot", + { botUsername, tgUserId: tgUser?.id, lookupErr }, + ); + return ack(); + } + + // 3) Salva token no Vault (reutiliza vault_create_secret) + const secretName = `telegram_bot_token_${agentInstance.id}_${Math.floor( + Date.now() / 1000, + )}`; + const { data: vaultData, error: vaultErr } = await admin + .rpc("vault_create_secret", { + secret_value: newBotToken, + secret_name: secretName, + secret_description: `Managed bot token for agent ${agentInstance.id}`, + }) + .single(); + + if (vaultErr || !vaultData) { + console.error("vault_create_secret falhou", vaultErr); + return ack(); + } + // deno-lint-ignore no-explicit-any + const secretId = (vaultData as any).secret_id ?? vaultData; + + // 4) Atualiza agent_instance + const { error: updErr } = await admin + .from("agent_instances") + .update({ + telegram_bot_token_vault_id: secretId, + telegram_bot_username: botUsername, + telegram_user_chat_id: tgUser?.id ? Number(tgUser.id) : null, + telegram_connected_at: new Date().toISOString(), + telegram_onboarding_completed: true, + telegram_token_invalid: false, + managed_bot_pending: false, + onboarding_completed: true, + updated_at: new Date().toISOString(), + }) + .eq("id", agentInstance.id); + + if (updErr) { + console.error("agent update falhou", updErr); + return ack(); + } + + // 5) Dispara provisionamento (fire-and-forget) + fetch(`${supabaseUrl}/functions/v1/provision-agent`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${serviceKey}`, + }, + body: JSON.stringify({ + agent_instance_id: agentInstance.id, + user_id: agentInstance.user_id, + }), + }).catch((err) => console.error("Auto-provision error:", err)); + + return ack(); + } catch (err) { + console.error("managed-bot-webhook fatal", err); + return ack(); + } +}); From 1e5343e089ad7a7067805cef5b92398517b66586 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:24:55 +0000 Subject: [PATCH 03/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- supabase/config.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/supabase/config.toml b/supabase/config.toml index 296ee11..187b0d3 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -53,3 +53,6 @@ verify_jwt = false [functions.keep-alive-agents] verify_jwt = false + +[functions.managed-bot-webhook] +verify_jwt = false From 2a2550e0cf6bee69d798b728a0a57ec44c160316 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:25:12 +0000 Subject: [PATCH 04/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/hooks/use-agent-instance.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/use-agent-instance.ts b/src/hooks/use-agent-instance.ts index a9dbb52..d3b16b6 100644 --- a/src/hooks/use-agent-instance.ts +++ b/src/hooks/use-agent-instance.ts @@ -18,6 +18,8 @@ export interface AgentInstance { telegram_onboarding_completed: boolean; onboarding_completed: boolean; agent_name: string | null; + managed_bot_pending: boolean; + managed_bot_suggested_username: string | null; created_at: string; } @@ -33,7 +35,7 @@ export function useAgentInstance() { const { data, error } = await supabase .from("agent_instances") .select( - "id, user_id, uuid_tenant, status, telegram_bot_username, telegram_bot_token_vault_id, telegram_webhook_configured, telegram_token_invalid, telegram_first_message_received_at, telegram_connected_at, telegram_onboarding_completed, onboarding_completed, agent_name, created_at", + "id, user_id, uuid_tenant, status, telegram_bot_username, telegram_bot_token_vault_id, telegram_webhook_configured, telegram_token_invalid, telegram_first_message_received_at, telegram_connected_at, telegram_onboarding_completed, onboarding_completed, agent_name, managed_bot_pending, managed_bot_suggested_username, created_at", ) .eq("user_id", user!.id) .maybeSingle(); From fa7b85b43031a731b8aeea1d626fa3335ff77d32 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:25:25 +0000 Subject: [PATCH 05/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/bem-vindo.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx index defcfc6..5fbe1fc 100644 --- a/src/routes/bem-vindo.tsx +++ b/src/routes/bem-vindo.tsx @@ -1,13 +1,14 @@ "use client"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { ArrowRight, CheckCircle2, ExternalLink, Loader2, + RefreshCcw, Sparkles, } from "lucide-react"; import { useQueryClient } from "@tanstack/react-query"; From 54add0c24379c3b2a5a6074fd8bfd95e6f3111a9 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:25:41 +0000 Subject: [PATCH 06/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/bem-vindo.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx index 5fbe1fc..83d9bc0 100644 --- a/src/routes/bem-vindo.tsx +++ b/src/routes/bem-vindo.tsx @@ -43,6 +43,13 @@ function WelcomePage() { const [saving, setSaving] = useState(false); const [wizardOpen, setWizardOpen] = useState(false); + // Managed bot state + const [creatingBot, setCreatingBot] = useState(false); + const [waitingConfirm, setWaitingConfirm] = useState(false); + const [waitTimedOut, setWaitTimedOut] = useState(false); + const [previewUsername, setPreviewUsername] = useState(null); + const waitStartedAt = useRef(null); + const fullName = (profile?.full_name || "").trim(); const firstName = useMemo( () => (fullName.split(" ")[0] || "você").trim(), From e0a60f149e2e3184367b2d3d3e9078ffecfffe14 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:26:01 +0000 Subject: [PATCH 07/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/bem-vindo.tsx | 81 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx index 83d9bc0..094a659 100644 --- a/src/routes/bem-vindo.tsx +++ b/src/routes/bem-vindo.tsx @@ -129,8 +129,9 @@ function WelcomePage() { } } - async function handleConnectTelegram() { + async function handleOpenManualWizard() { await markWelcomeDone(); + setWaitingConfirm(false); setWizardOpen(true); } @@ -139,6 +140,84 @@ function WelcomePage() { navigate({ to: "/painel", search: {} }); } + // Sugere username localmente (apenas preview visual antes do clique) + useEffect(() => { + if (step !== 3) return; + if (agent?.managed_bot_suggested_username) { + setPreviewUsername(agent.managed_bot_suggested_username); + return; + } + const base = (agentName || "mika") + .toLowerCase() + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^a-z0-9]/g, "") + .substring(0, 28); + const safe = base.length >= 3 ? base : `mika${base}`; + setPreviewUsername(`${safe}bot`); + }, [step, agentName, agent?.managed_bot_suggested_username]); + + async function handleCreateManagedBot() { + if (!agent) { + toast.error("Aguarde, ainda estamos preparando seu agente…"); + return; + } + setCreatingBot(true); + setWaitTimedOut(false); + try { + const { data, error } = await supabase.functions.invoke<{ + url: string; + suggested_username: string; + manager_username: string; + }>("create-managed-bot", { + body: { + agent_instance_id: agent.id, + agent_name: agentName.trim(), + }, + }); + if (error || !data?.url) { + throw error ?? new Error("Resposta inválida"); + } + setPreviewUsername(data.suggested_username); + window.open(data.url, "_blank", "noopener,noreferrer"); + await markWelcomeDone(); + waitStartedAt.current = Date.now(); + setWaitingConfirm(true); + } catch (err) { + console.error(err); + toast.error( + "Não foi possível iniciar a criação do bot. Tente novamente ou use o modo manual.", + ); + } finally { + setCreatingBot(false); + } + } + + // Polling: enquanto aguardamos confirmação, useAgentInstance já refetch a cada 10s. + // Aceleramos o refetch a cada 3s e detectamos sucesso. + useEffect(() => { + if (!waitingConfirm) return; + if (!user) return; + const interval = setInterval(() => { + queryClient.invalidateQueries({ queryKey: ["agent-instance", user.id] }); + if (waitStartedAt.current && Date.now() - waitStartedAt.current > 5 * 60_000) { + setWaitingConfirm(false); + setWaitTimedOut(true); + } + }, 3000); + return () => clearInterval(interval); + }, [waitingConfirm, user, queryClient]); + + // Detecta sucesso da confirmação via webhook + useEffect(() => { + if (!waitingConfirm) return; + if (agent?.telegram_onboarding_completed && !agent.managed_bot_pending) { + setWaitingConfirm(false); + toast.success("🎉 Bot criado! Seu agente está sendo ativado."); + navigate({ to: "/painel", search: {} }); + } + }, [waitingConfirm, agent, navigate]); + if (authLoading || !user) { return (
From 2010809e3f3fb93a2cc757a32be50a50e0cd268f Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:26:28 +0000 Subject: [PATCH 08/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/bem-vindo.tsx | 137 ++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 51 deletions(-) diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx index 094a659..857b51b 100644 --- a/src/routes/bem-vindo.tsx +++ b/src/routes/bem-vindo.tsx @@ -369,68 +369,103 @@ function WelcomePage() { className="text-center" >

- Quase lá! Conecte seu Telegram. + Crie seu bot em 1 toque 🚀

- Para conversar com{" "} - - {agentName || "seu agente"} - - , você precisa conectar seu Telegram. Leva menos de 2 minutos. + Preparamos tudo para você. Basta confirmar no Telegram.

- - - + {/* Card de preview do bot */} +
+
+
+ +
+
+

+ {agentName || "Seu agente"} +

+

+ @{previewUsername || "carregando…"} +

+
+
+
-
    - - - Abrir BotFather - - - } - /> - - -
+ {/* Estado: aguardando confirmação */} + {waitingConfirm && ( +
+
+ + + Aguardando confirmação no Telegram… + +
+

+ Confirme as informações na conversa com @mika_managerbot. +

+
+ )} + {/* Estado: timeout */} + {waitTimedOut && ( +
+

+ Não recebemos a confirmação ainda. Tente novamente ou faça + o processo manualmente. +

+
+ )} + + {/* Ícones explicativos — só antes de iniciar */} + {!waitingConfirm && !waitTimedOut && ( +
+ + + +
+ )} + + {/* Ações */}
- + )} + + {waitTimedOut && ( + + )} + + + Prefiro configurar manualmente + From 8925f871b068f8b71bafd2aa77adcbad9c64ff97 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:26:37 +0000 Subject: [PATCH 09/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- src/routes/bem-vindo.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx index 857b51b..4efd6c3 100644 --- a/src/routes/bem-vindo.tsx +++ b/src/routes/bem-vindo.tsx @@ -520,6 +520,15 @@ function StepRow({ ); } +function ExplainIcon({ emoji, label }: { emoji: string; label: string }) { + return ( +
+
{emoji}
+

{label}

+
+ ); +} + // Marker no componente para cumprir contrato de "marcar como visitada" // (também usado pelo redirect do /painel) export function isWelcomeDone(): boolean { From d5f717249352b58579b3580d880dd9c0fd81b456 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:27:11 +0000 Subject: [PATCH 10/10] Changes Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com> --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index b141701..359a062 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,21 @@ Já configuradas via `.env` (gerado automaticamente pelo Lovable Cloud): - `RAILWAY_API_TOKEN` — token da Railway Public API, usado por `provision-agent`/`suspend-agent`/`resume-agent` para criar e gerenciar containers Hermes. - `OLLAMA_API_KEY` — chave do Ollama Cloud, injetada como env var no container Hermes (imagem custom `ghcr.io/domfelipe/hermes-agent-custom:latest`). - `HERMES_API_SERVER_KEY` — token usado pelo API server interno do Hermes (`API_SERVER_KEY`). Valor atual: `HermesRailwayKey2026SecureToken123456`. +- `TELEGRAM_MANAGER_BOT_TOKEN` — token do bot manager (`@mika_managerbot`) usado para criar bots dos clientes em 1 toque via Bot Management Mode do BotFather. +- `TELEGRAM_MANAGER_BOT_USERNAME` — username do bot manager, padrão `mika_managerbot` (sem `@`). + +### Setup do Managed Bot (one-tap onboarding) + +> ⚠️ **Experimental**: o "Bot Management Mode" do BotFather (endpoint `getManagedBotToken` e deep-link `t.me/newbot//`) **não faz parte da Bot API pública oficial do Telegram**. Funcionalidade pode mudar/quebrar sem aviso. Mantemos o fluxo manual via `/newbot` no BotFather como fallback na página `/bem-vindo`. + +1. **Criar o bot manager** no [@BotFather](https://t.me/BotFather): `/newbot` → nome: `Mika Manager` → username: `mika_managerbot`. +2. **Habilitar Bot Management Mode**: abrir [https://t.me/Botfather?startapp](https://t.me/Botfather?startapp) → selecionar o bot `Mika Manager` → **Bot Settings → Bot Management Mode → Enable**. +3. **Adicionar secrets** no Lovable Cloud: `TELEGRAM_MANAGER_BOT_TOKEN` (token recebido do BotFather) e `TELEGRAM_MANAGER_BOT_USERNAME=mika_managerbot`. +4. **Configurar webhook** uma vez após o deploy: + ``` + GET https:///functions/v1/managed-bot-webhook?setup=true + ``` + Resposta esperada: `{ "webhook_set": "...", "telegram_response": { "ok": true } }`. > **Imagem Docker custom**: o container roda `ghcr.io/domfelipe/hermes-agent-custom:latest`, que já contém o `SOUL.md` embutido. Por isso não passamos mais `HERMES_SOUL_OVERRIDE` via env var, e o Dockerfile define o `CMD` (sem `startCommand` no Railway). Secrets removidos do projeto: `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `HERMES_SOUL_OVERRIDE`.