Widget trong tin nhắn — các khối tương tác được tạo từ HTML, CSS và JavaScript ngay trong cuộc trò chuyện — đã có mặt trên các bản beta của Telegram dành cho Android và iOS. Vào ngày 25 tháng 10, chúng xuất hiện trong Android 13.2.0 (7254) và iOS 13.2 (35726), và trên Android, một widget tự tạo đã có thể thực thi JavaScript. Bản build Android 13.2.0 (7260) từ ngày 27 tháng 10 cho thấy cách thức hoạt động của chúng: không sử dụng công cụ trình duyệt, mà dựa trên trình thông dịch riêng với hạn ngạch CPU, cùng một bộ phận giám sát riêng để quyết định liệu một lần chạm có phải là thật hay không. Bot mà widget gửi yêu cầu sẽ biết ID của người đã chạm. Trong Telegram Desktop và API công khai, widget vẫn chưa có mặt.

Cơ chế yêu cầu đến bot trên bản beta dành cho macOS đã được phân tích trong bài viết ngày 27 tháng 10; phần này sẽ bổ sung những gì bản build dành cho Android mang lại.

Widget có mặt ở đâu

ClientBuildNhững gì có thể thấy
macOS, beta13.1.0sandbox, yêu cầu đến bot, các trang thử nghiệm — từ giữa tháng 10
Android, beta13.2.0 (7254, 7260)sơ đồ, sandbox, văn bản giao diện; widget hoạt động
iOS, beta13.2 (35726)widget trong tin nhắn và công cụ để kiểm tra chúng
Telegram Desktop7.3.3không có: cả trong mã nguồn lẫn sơ đồ layer 232
Bot API10.3không có

Bản beta Android được phân phối qua kênh chính thức và liên kết trực tiếp từ trang web của Telegram. Giao thức client trong đó là layer 233, mới hơn một layer so với bản công khai.

Widget nằm trong tin nhắn như thế nào

Widget là một loại media mới, messageMediaWidget. Ngoài mã nguồn (payload) và kích thước, nó còn chứa hai bộ dữ liệu: chat_data — dữ liệu của tin nhắn này, và public_data kèm hash — dữ liệu chung của widget mà bot có thể thay đổi sau khi gửi (messages.editMessageWidgetData, updateMessageWidgetData). Các cờ can_query và background_query cho biết liệu widget có thể gửi yêu cầu đến bot khi chạm hoặc không chạm, is_copy đánh dấu một bản sao được chuyển tiếp. Widget cũng có thể đến dưới dạng phản hồi inline của bot — botInlineMessageWidget.

Người gửi đặt các quyền. Trong các thông báo lỗi từ phần native của client, các cờ server allow_queries và fwd_allow_queries được nhắc đến: nếu không có cờ đầu tiên, widget sẽ không gửi yêu cầu đến bot; nếu không có cờ thứ hai, bản sao được chuyển tiếp của nó sẽ im lặng. Yêu cầu nền cần thêm background_query và chỉ được gửi khi widget đang hiển thị trên màn hình.

Bot nhận được gì

Một lần chạm sẽ biến thành messages.getBotWidgetQueryAnswer với các trường chat, tin nhắn và data — tối đa 64 byte. Bot nhận được cập nhật updateBotWidgetQuery và phản hồi bằng phương thức messages.setBotWidgetQueryAnswer với một chuỗi result.

Trong bản cập nhật dành cho bot, trường user_id là bắt buộc, không có cờ: bot luôn biết ai đã chạm. Kèm theo đó là chat_type, chat_instance — cùng một định danh chat ẩn danh như các nút inline thông thường — số tin nhắn và widget_id. Đối với các bản sao được chuyển tiếp và tin nhắn inline, có các cập nhật riêng — updateBotWidgetCopyQuery và updateInlineBotWidgetQuery.

Điều này tương tự như việc chạm vào một nút inline có callback, chỉ khác là nút được vẽ bởi script, và dữ liệu cũng do script đó thu thập.

Mã widget được thực thi như thế nào

Trong Android, không có WebView hay Chromium bên dưới widget. Mã được thực thi bởi thư viện native twidget bên trong thư viện chính của client — cùng tên này được sử dụng cho các kiểu Twidget* trong bản beta dành cho macOS và kho lưu trữ riêng tư được kết nối với mã nguồn của nó. Các script sandbox nằm trong client hai lần: dưới dạng mã nguồn và bytecode .qbc, được chạy bởi trình thông dịch JavaScript tích hợp. Hình ảnh được vẽ bởi trình kết xuất riêng trực tiếp vào cửa sổ Android — không có bố cục trình duyệt, mà có phân tích cú pháp của nó: một gói validator riêng kiểm tra HTML và CSS.

Các giới hạn được ghi trong mã, chứ không phải trong các quy tắc dành cho nhà phát triển:

  • CPU — widget có hạn ngạch thời gian (cpuQuotaMs), vượt quá sẽ dẫn đến lỗi «CPU limit exceeded»;
  • Bộ đếm thời gian — số lượng đồng thời bị giới hạn, công cụ sẽ từ chối các bộ đếm thời gian thừa;
  • Mạng — fetch, XMLHttpRequest, WebSocket và navigator.sendBeacon bị chặn, navigator.onLine luôn là false, userAgent — TelegramWidget/1.0;
  • Bộ nhớ — mỗi widget bắt đầu với trạng thái trống;
  • Truy cập bên ngoài — openLink chỉ chấp nhận địa chỉ https tối đa 256 ký tự và hỏi: «This widget wants to open:», copyText — tối đa 256 ký tự;
  • Cầu nối đến client — sau khi khởi chạy, các hàm dịch vụ __host, __hostOps và các hàm lân cận sẽ bị xóa khỏi đối tượng toàn cục, và mã widget không thể truy cập chúng.

Client so sánh các tệp sandbox với tổng kiểm tra từ manifest.txt, sự khác biệt sẽ gây ra lỗi riêng «Bundle checksum mismatch».

Client phân biệt chạm thật và chạm giả như thế nào

Yêu cầu đến bot mà không có quyền nền chỉ được gửi từ trình xử lý chạm — "một lệnh gọi cho một lần chạm trên widget hiển thị", như được nêu trong thông báo lỗi. Người quyết định liệu một lần chạm có phải là thật hay không không phải là script, mà là bộ phận giám sát chạm native: các hàm arbiterDown, arbiterMove, arbiterUp và arbiterHoldDeadline nhận tọa độ ngón tay và thời gian giữ. Theo thiết kế, một cú nhấp chuột tổng hợp từ JavaScript, bộ đếm thời gian hoặc kéo thả sẽ không gửi yêu cầu.

Trong bản beta cũng có các công cụ để kiểm tra giới hạn này: màn hình tuân thủ (WidgetConformanceActivity) và chạy một bộ tấn công (attackScan, WidgetAttacks). Bộ trang kiểm tra tấn công dành cho macOS — 28 trang cố gắng gửi tin nhắn mà không cần chạm hoặc chuyển hướng bằng liên kết — nằm trong mã nguồn mở của client.

Người dùng nhìn thấy gì

Các văn bản giao diện đã có trong từ điển chuỗi của client: «Loading widget…», «Widget failed to load», «Widget stopped», «Preview of the widget» và cảnh báo «This widget can send data to its bot's server when you tap it.»

Không có công tắc riêng để tắt widget trong cài đặt bản beta — điều này được các tester lưu ý. Hiện tại không phải ai cũng có thể gửi widget, và trong Bot API không có phương thức nào dành cho chúng.