Widgets em mensagens — blocos interativos de HTML, CSS e JavaScript diretamente nas conversas — chegaram às versões beta do Telegram para Android e iOS. Em 25 de outubro, eles surgiram no Android 13.2.0 (7254) e iOS 13.2 (35726), e no Android, um widget feito por conta própria já executa JavaScript. A build do Android 13.2.0 (7260) de 27 de outubro mostra como isso funciona: sem um motor de navegador, usando seu próprio interpretador com uma cota de CPU, e com um árbitro separado que decide se o toque foi genuíno. O bot, para o qual o widget enviou uma requisição, descobre o id de quem tocou. No Telegram Desktop e na API pública, os widgets ainda não estão disponíveis.

A mecânica das requisições ao bot na versão beta para macOS foi analisada em um artigo de 27 de outubro; aqui, apresentamos o que a build para Android adiciona.

Onde os widgets estão disponíveis

ClienteBuildO que é visível
macOS, beta13.1.0sandbox, requisições ao bot, páginas de teste — desde meados de outubro
Android, beta13.2.0 (7254, 7260)esquema, sandbox, textos da interface; widgets funcionando
iOS, beta13.2 (35726)widgets em mensagens e ferramentas para sua verificação
Telegram Desktop7.3.3não: nem no código, nem no esquema da layer 232
Bot API10.3não

A versão beta do Android é distribuída pelo canal oficial e por link direto do site do Telegram. O protocolo do cliente nela é a layer 233, uma versão mais recente que a pública.

Como o widget está em uma mensagem

O widget é um novo tipo de mídia, messageMediaWidget. Além do próprio código (payload) e das dimensões, ele contém dois conjuntos de dados: chat_data — dados desta mensagem, e public_data com um hash — dados gerais do widget que o bot pode alterar após o envio (messages.editMessageWidgetData, updateMessageWidgetData). Os sinalizadores can_query e background_query indicam se é possível escrever para o bot a partir do widget com ou sem um toque, e is_copy marca uma cópia encaminhada. O widget também pode vir como uma resposta inline do bot — botInlineMessageWidget.

As permissões são definidas pelo remetente. Em mensagens de erro da parte nativa do cliente, são mencionados os sinalizadores de servidor allow_queries e fwd_allow_queries: sem o primeiro, o widget não escreve para o bot de forma alguma; sem o segundo, sua cópia encaminhada permanece em silêncio. Uma requisição em segundo plano exige também background_query e só é enviada enquanto o widget está na tela.

O que o bot recebe

Um toque se transforma em messages.getBotWidgetQueryAnswer com os campos de chat, mensagem e data — até 64 bytes. O bot recebe a atualização updateBotWidgetQuery e responde com o método messages.setBotWidgetQueryAnswer com uma única string result.

Na atualização para o bot, o campo user_id é obrigatório, sem sinalizador: o bot sempre sabe quem tocou. Junto, vêm chat_type, chat_instance — o mesmo identificador de chat anonimizado que as botões inline comuns — o número da mensagem e widget_id. Para cópias encaminhadas e mensagens inline, existem atualizações próprias — updateBotWidgetCopyQuery e updateInlineBotWidgetQuery.

Isso é semelhante ao toque de um botão inline com callback, mas o botão é desenhado pelo script, e os dados são coletados por ele.

Como o código do widget é executado

No Android, não há WebView nem Chromium sob o widget. O código é executado por uma biblioteca nativa twidget dentro da biblioteca principal do cliente — o mesmo nome é usado pelos tipos Twidget* na versão beta para macOS e pelo repositório privado conectado aos seus códigos-fonte. Os scripts da sandbox estão no cliente duas vezes: como código-fonte e como bytecode .qbc, que é executado por um interpretador JavaScript embutido. A imagem é desenhada por seu próprio renderer diretamente na janela do Android — não há layout de navegador, mas sim sua análise: um pacote validator separado verifica HTML e CSS.

As restrições estão escritas no código, e não nas regras para desenvolvedores:

  • CPU — o widget tem uma cota de tempo (cpuQuotaMs), e excedê-la resulta no erro «CPU limit exceeded»;
  • timers — o número de simultâneos é limitado, o motor rejeita os extras;
  • rede — fetch, XMLHttpRequest, WebSocket e navigator.sendBeacon são desativados, navigator.onLine é sempre falso, userAgent é TelegramWidget/1.0;
  • armazenamento — cada widget começa com um armazenamento vazio;
  • saídas externas — openLink aceita apenas endereços https de até 256 caracteres e pergunta: «This widget wants to open:», copyText — até 256 caracteres;
  • ponte para o cliente — após a inicialização, as funções de serviço __host, __hostOps e as adjacentes são removidas do objeto global, e o código do widget não consegue acessá-las.

O cliente compara os arquivos da sandbox com as somas de verificação de manifest.txt; uma divergência é um erro separado «Bundle checksum mismatch».

Como o cliente distingue um toque de uma imitação

Uma requisição ao bot sem permissão de segundo plano é enviada apenas a partir do manipulador de toque — «uma chamada por toque em um widget visível», como indicado na mensagem de erro. Quem decide se houve um toque não é o script, mas um árbitro de toques nativo: as funções arbiterDown, arbiterMove, arbiterUp e arbiterHoldDeadline recebem as coordenadas do dedo e o tempo de retenção. Por intenção, um clique sintético de JavaScript, um timer ou um arrastar não enviarão uma requisição.

Na versão beta, também existem ferramentas para verificar esse limite: a tela de conformidade (WidgetConformanceActivity) e a execução de um conjunto de ataques (attackScan, WidgetAttacks). O conjunto de páginas de teste de ataque para macOS — 28 páginas que tentam enviar uma mensagem sem um toque ou redirecionar para um link — está no código aberto do cliente.

O que o usuário vê

Os textos da interface já estão no dicionário de strings em inglês do cliente: «Loading widget…», «Widget failed to load», «Widget stopped», «Preview of the widget» e o aviso «This widget can send data to its bot's server when you tap it.»

Não há um interruptor separado nas configurações da versão beta para desativar os widgets — testadores chamam a atenção para isso. Nem todos podem enviar widgets ainda, e a Bot API não possui métodos para eles.