UNPKG

telegram-sdk

Version:

Telegram Web App - (Uses July 7, 2024, Bot API 7.7)

1,592 lines (1,591 loc) 70.9 kB
"use strict"; // src/js/telegram-web-app.js (function() { var eventHandlers = {}; var locationHash = ""; try { locationHash = location.hash.toString(); } catch (e) { } var initParams = urlParseHashParams(locationHash); var storedParams = sessionStorageGet("initParams"); if (storedParams) { for (var key in storedParams) { if (typeof initParams[key] === "undefined") { initParams[key] = storedParams[key]; } } } sessionStorageSet("initParams", initParams); var isIframe = false, iFrameStyle; try { isIframe = window.parent != null && window != window.parent; if (isIframe) { window.addEventListener("message", function(event) { if (event.source !== window.parent) return; try { var dataParsed = JSON.parse(event.data); } catch (e) { return; } if (!dataParsed || !dataParsed.eventType) { return; } if (dataParsed.eventType == "set_custom_style") { if (event.origin === "https://web.telegram.org") { iFrameStyle.innerHTML = dataParsed.eventData; } } else if (dataParsed.eventType == "reload_iframe") { try { window.parent.postMessage( JSON.stringify({ eventType: "iframe_will_reload" }), "*" ); } catch (e) { } location.reload(); } else { receiveEvent(dataParsed.eventType, dataParsed.eventData); } }); iFrameStyle = document.createElement("style"); document.head.appendChild(iFrameStyle); try { window.parent.postMessage( JSON.stringify({ eventType: "iframe_ready", eventData: { reload_supported: true } }), "*" ); } catch (e) { } } } catch (e) { } function urlSafeDecode(urlencoded) { try { urlencoded = urlencoded.replace(/\+/g, "%20"); return decodeURIComponent(urlencoded); } catch (e) { return urlencoded; } } function urlParseHashParams(locationHash2) { locationHash2 = locationHash2.replace(/^#/, ""); var params = {}; if (!locationHash2.length) { return params; } if (locationHash2.indexOf("=") < 0 && locationHash2.indexOf("?") < 0) { params._path = urlSafeDecode(locationHash2); return params; } var qIndex = locationHash2.indexOf("?"); if (qIndex >= 0) { var pathParam = locationHash2.substr(0, qIndex); params._path = urlSafeDecode(pathParam); locationHash2 = locationHash2.substr(qIndex + 1); } var query_params = urlParseQueryString(locationHash2); for (var k in query_params) { params[k] = query_params[k]; } return params; } function urlParseQueryString(queryString) { var params = {}; if (!queryString.length) { return params; } var queryStringParams = queryString.split("&"); var i, param, paramName, paramValue; for (i = 0; i < queryStringParams.length; i++) { param = queryStringParams[i].split("="); paramName = urlSafeDecode(param[0]); paramValue = param[1] == null ? null : urlSafeDecode(param[1]); params[paramName] = paramValue; } return params; } function urlAppendHashParams(url2, addHash) { var ind = url2.indexOf("#"); if (ind < 0) { return url2 + "#" + addHash; } var curHash = url2.substr(ind + 1); if (curHash.indexOf("=") >= 0 || curHash.indexOf("?") >= 0) { return url2 + "&" + addHash; } if (curHash.length > 0) { return url2 + "?" + addHash; } return url2 + addHash; } function postEvent(eventType, callback, eventData) { if (!callback) { callback = function() { }; } if (eventData === void 0) { eventData = ""; } console.log("[Telegram.WebView] > postEvent", eventType, eventData); if (window.TelegramWebviewProxy !== void 0) { TelegramWebviewProxy.postEvent(eventType, JSON.stringify(eventData)); callback(); } else if (window.external && "notify" in window.external) { window.external.notify( JSON.stringify({ eventType, eventData }) ); callback(); } else if (isIframe) { try { var trustedTarget = "https://web.telegram.org"; trustedTarget = "*"; window.parent.postMessage( JSON.stringify({ eventType, eventData }), trustedTarget ); callback(); } catch (e) { callback(e); } } else { callback({ notAvailable: true }); } } function receiveEvent(eventType, eventData) { console.log("[Telegram.WebView] < receiveEvent", eventType, eventData); callEventCallbacks(eventType, function(callback) { callback(eventType, eventData); }); } function callEventCallbacks(eventType, func) { var curEventHandlers = eventHandlers[eventType]; if (curEventHandlers === void 0 || !curEventHandlers.length) { return; } for (var i = 0; i < curEventHandlers.length; i++) { try { func(curEventHandlers[i]); } catch (e) { } } } function onEvent(eventType, callback) { if (eventHandlers[eventType] === void 0) { eventHandlers[eventType] = []; } var index = eventHandlers[eventType].indexOf(callback); if (index === -1) { eventHandlers[eventType].push(callback); } } function offEvent(eventType, callback) { if (eventHandlers[eventType] === void 0) { return; } var index = eventHandlers[eventType].indexOf(callback); if (index === -1) { return; } eventHandlers[eventType].splice(index, 1); } function openProtoUrl(url2) { if (!url2.match(/^(web\+)?tgb?:\/\/./)) { return false; } var useIframe = navigator.userAgent.match(/iOS|iPhone OS|iPhone|iPod|iPad/i) ? true : false; if (useIframe) { var iframeContEl = document.getElementById("tgme_frame_cont") || document.body; var iframeEl = document.createElement("iframe"); iframeContEl.appendChild(iframeEl); var pageHidden = false; var enableHidden = function() { pageHidden = true; }; window.addEventListener("pagehide", enableHidden, false); window.addEventListener("blur", enableHidden, false); if (iframeEl !== null) { iframeEl.src = url2; } setTimeout(function() { if (!pageHidden) { window.location = url2; } window.removeEventListener("pagehide", enableHidden, false); window.removeEventListener("blur", enableHidden, false); }, 2e3); } else { window.location = url2; } return true; } function sessionStorageSet(key2, value) { try { window.sessionStorage.setItem("__telegram__" + key2, JSON.stringify(value)); return true; } catch (e) { } return false; } function sessionStorageGet(key2) { try { return JSON.parse(window.sessionStorage.getItem("__telegram__" + key2)); } catch (e) { } return null; } if (!window.Telegram) { window.Telegram = {}; } window.Telegram.WebView = { initParams, isIframe, onEvent, offEvent, postEvent, receiveEvent, callEventCallbacks }; window.Telegram.Utils = { urlSafeDecode, urlParseQueryString, urlParseHashParams, urlAppendHashParams, sessionStorageSet, sessionStorageGet }; window.TelegramGameProxy_receiveEvent = receiveEvent; window.TelegramGameProxy = { receiveEvent }; })(); (function() { var Utils = window.Telegram.Utils; var WebView = window.Telegram.WebView; var initParams = WebView.initParams; var isIframe = WebView.isIframe; var WebApp = {}; var webAppInitData = "", webAppInitDataUnsafe = {}; var themeParams = {}, colorScheme = "light"; var webAppVersion = "6.0"; var webAppPlatform = "unknown"; if (initParams.tgWebAppData && initParams.tgWebAppData.length) { webAppInitData = initParams.tgWebAppData; webAppInitDataUnsafe = Utils.urlParseQueryString(webAppInitData); for (var key in webAppInitDataUnsafe) { var val = webAppInitDataUnsafe[key]; try { if (val.substr(0, 1) == "{" && val.substr(-1) == "}" || val.substr(0, 1) == "[" && val.substr(-1) == "]") { webAppInitDataUnsafe[key] = JSON.parse(val); } } catch (e) { } } } if (initParams.tgWebAppThemeParams && initParams.tgWebAppThemeParams.length) { var themeParamsRaw = initParams.tgWebAppThemeParams; try { var theme_params = JSON.parse(themeParamsRaw); if (theme_params) { setThemeParams(theme_params); } } catch (e) { } } var theme_params = Utils.sessionStorageGet("themeParams"); if (theme_params) { setThemeParams(theme_params); } if (initParams.tgWebAppVersion) { webAppVersion = initParams.tgWebAppVersion; } if (initParams.tgWebAppPlatform) { webAppPlatform = initParams.tgWebAppPlatform; } function onThemeChanged(eventType, eventData) { if (eventData.theme_params) { setThemeParams(eventData.theme_params); window.Telegram.WebApp.MainButton.setParams({}); updateBackgroundColor(); receiveWebViewEvent("themeChanged"); } } var lastWindowHeight = window.innerHeight; function onViewportChanged(eventType, eventData) { if (eventData.height) { window.removeEventListener("resize", onWindowResize); setViewportHeight(eventData); } } function onWindowResize(e) { if (lastWindowHeight != window.innerHeight) { lastWindowHeight = window.innerHeight; receiveWebViewEvent("viewportChanged", { isStateStable: true }); } } function linkHandler(e) { if (e.metaKey || e.ctrlKey) return; var el = e.target; while (el.tagName != "A" && el.parentNode) { el = el.parentNode; } if (el.tagName == "A" && el.target != "_blank" && (el.protocol == "http:" || el.protocol == "https:") && el.hostname == "t.me") { WebApp.openTgLink(el.href); e.preventDefault(); } } function strTrim(str) { return str.toString().replace(/^\s+|\s+$/g, ""); } function receiveWebViewEvent(eventType) { var args = Array.prototype.slice.call(arguments); eventType = args.shift(); WebView.callEventCallbacks("webview:" + eventType, function(callback) { callback.apply(WebApp, args); }); } function onWebViewEvent(eventType, callback) { WebView.onEvent("webview:" + eventType, callback); } function offWebViewEvent(eventType, callback) { WebView.offEvent("webview:" + eventType, callback); } function setCssProperty(name, value) { var root = document.documentElement; if (root && root.style && root.style.setProperty) { root.style.setProperty("--tg-" + name, value); } } function setThemeParams(theme_params2) { if (theme_params2.bg_color == "#1c1c1d" && theme_params2.bg_color == theme_params2.secondary_bg_color) { theme_params2.secondary_bg_color = "#2c2c2e"; } var color; for (var key2 in theme_params2) { if (color = parseColorToHex(theme_params2[key2])) { themeParams[key2] = color; if (key2 == "bg_color") { colorScheme = isColorDark(color) ? "dark" : "light"; setCssProperty("color-scheme", colorScheme); } key2 = "theme-" + key2.split("_").join("-"); setCssProperty(key2, color); } } Utils.sessionStorageSet("themeParams", themeParams); } var webAppCallbacks = {}; function generateCallbackId(len) { var tries = 100; while (--tries) { var id = "", chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", chars_len = chars.length; for (var i = 0; i < len; i++) { id += chars[Math.floor(Math.random() * chars_len)]; } if (!webAppCallbacks[id]) { webAppCallbacks[id] = {}; return id; } } throw Error("WebAppCallbackIdGenerateFailed"); } var viewportHeight = false, viewportStableHeight = false, isExpanded = true; function setViewportHeight(data) { if (typeof data !== "undefined") { isExpanded = !!data.is_expanded; viewportHeight = data.height; if (data.is_state_stable) { viewportStableHeight = data.height; } receiveWebViewEvent("viewportChanged", { isStateStable: !!data.is_state_stable }); } var height, stable_height; if (viewportHeight !== false) { height = viewportHeight - mainButtonHeight + "px"; } else { height = mainButtonHeight ? "calc(100vh - " + mainButtonHeight + "px)" : "100vh"; } if (viewportStableHeight !== false) { stable_height = viewportStableHeight - mainButtonHeight + "px"; } else { stable_height = mainButtonHeight ? "calc(100vh - " + mainButtonHeight + "px)" : "100vh"; } setCssProperty("viewport-height", height); setCssProperty("viewport-stable-height", stable_height); } var isClosingConfirmationEnabled = false; function setClosingConfirmation(need_confirmation) { if (!versionAtLeast("6.2")) { console.warn( "[Telegram.WebApp] Closing confirmation is not supported in version " + webAppVersion ); return; } isClosingConfirmationEnabled = !!need_confirmation; WebView.postEvent("web_app_setup_closing_behavior", false, { need_confirmation: isClosingConfirmationEnabled }); } var isVerticalSwipesEnabled = true; function toggleVerticalSwipes(enable_swipes) { if (!versionAtLeast("7.6")) { console.warn( "[Telegram.WebApp] Changing swipes behavior is not supported in version " + webAppVersion ); return; } isVerticalSwipesEnabled = !!enable_swipes; WebView.postEvent("web_app_setup_swipe_behavior", false, { allow_vertical_swipe: isVerticalSwipesEnabled }); } var headerColorKey = "bg_color", headerColor = null; function getHeaderColor() { if (headerColorKey == "secondary_bg_color") { return themeParams.secondary_bg_color; } else if (headerColorKey == "bg_color") { return themeParams.bg_color; } return headerColor; } function setHeaderColor(color) { if (!versionAtLeast("6.1")) { console.warn( "[Telegram.WebApp] Header color is not supported in version " + webAppVersion ); return; } if (!versionAtLeast("6.9")) { if (themeParams.bg_color && themeParams.bg_color == color) { color = "bg_color"; } else if (themeParams.secondary_bg_color && themeParams.secondary_bg_color == color) { color = "secondary_bg_color"; } } var head_color = null, color_key = null; if (color == "bg_color" || color == "secondary_bg_color") { color_key = color; } else if (versionAtLeast("6.9")) { head_color = parseColorToHex(color); if (!head_color) { console.error("[Telegram.WebApp] Header color format is invalid", color); throw Error("WebAppHeaderColorInvalid"); } } if (!versionAtLeast("6.9") && color_key != "bg_color" && color_key != "secondary_bg_color") { console.error( "[Telegram.WebApp] Header color key should be one of Telegram.WebApp.themeParams.bg_color, Telegram.WebApp.themeParams.secondary_bg_color, 'bg_color', 'secondary_bg_color'", color ); throw Error("WebAppHeaderColorKeyInvalid"); } headerColorKey = color_key; headerColor = head_color; updateHeaderColor(); } var appHeaderColorKey = null, appHeaderColor = null; function updateHeaderColor() { if (appHeaderColorKey != headerColorKey || appHeaderColor != headerColor) { appHeaderColorKey = headerColorKey; appHeaderColor = headerColor; if (appHeaderColor) { WebView.postEvent("web_app_set_header_color", false, { color: headerColor }); } else { WebView.postEvent("web_app_set_header_color", false, { color_key: headerColorKey }); } } } var backgroundColor = "bg_color"; function getBackgroundColor() { if (backgroundColor == "secondary_bg_color") { return themeParams.secondary_bg_color; } else if (backgroundColor == "bg_color") { return themeParams.bg_color; } return backgroundColor; } function setBackgroundColor(color) { if (!versionAtLeast("6.1")) { console.warn( "[Telegram.WebApp] Background color is not supported in version " + webAppVersion ); return; } var bg_color; if (color == "bg_color" || color == "secondary_bg_color") { bg_color = color; } else { bg_color = parseColorToHex(color); if (!bg_color) { console.error( "[Telegram.WebApp] Background color format is invalid", color ); throw Error("WebAppBackgroundColorInvalid"); } } backgroundColor = bg_color; updateBackgroundColor(); } var appBackgroundColor = null; function updateBackgroundColor() { var color = getBackgroundColor(); if (appBackgroundColor != color) { appBackgroundColor = color; WebView.postEvent("web_app_set_background_color", false, { color }); } } function parseColorToHex(color) { color += ""; var match; if (match = /^\s*#([0-9a-f]{6})\s*$/i.exec(color)) { return "#" + match[1].toLowerCase(); } else if (match = /^\s*#([0-9a-f])([0-9a-f])([0-9a-f])\s*$/i.exec(color)) { return ("#" + match[1] + match[1] + match[2] + match[2] + match[3] + match[3]).toLowerCase(); } else if (match = /^\s*rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)\s*$/.exec( color )) { var r = parseInt(match[1]), g = parseInt(match[2]), b = parseInt(match[3]); r = (r < 16 ? "0" : "") + r.toString(16); g = (g < 16 ? "0" : "") + g.toString(16); b = (b < 16 ? "0" : "") + b.toString(16); return "#" + r + g + b; } return false; } function isColorDark(rgb) { rgb = rgb.replace(/[\s#]/g, ""); if (rgb.length == 3) { rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2]; } var r = parseInt(rgb.substr(0, 2), 16); var g = parseInt(rgb.substr(2, 2), 16); var b = parseInt(rgb.substr(4, 2), 16); var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); return hsp < 120; } function versionCompare(v1, v2) { if (typeof v1 !== "string") v1 = ""; if (typeof v2 !== "string") v2 = ""; v1 = v1.replace(/^\s+|\s+$/g, "").split("."); v2 = v2.replace(/^\s+|\s+$/g, "").split("."); var a = Math.max(v1.length, v2.length), i, p1, p2; for (i = 0; i < a; i++) { p1 = parseInt(v1[i]) || 0; p2 = parseInt(v2[i]) || 0; if (p1 == p2) continue; if (p1 > p2) return 1; return -1; } return 0; } function versionAtLeast(ver) { return versionCompare(webAppVersion, ver) >= 0; } function byteLength(str) { if (window.Blob) { try { return new Blob([str]).size; } catch (e) { } } var s = str.length; for (var i = str.length - 1; i >= 0; i--) { var code = str.charCodeAt(i); if (code > 127 && code <= 2047) s++; else if (code > 2047 && code <= 65535) s += 2; if (code >= 56320 && code <= 57343) i--; } return s; } var BackButton = function() { var isVisible = false; var backButton = {}; Object.defineProperty(backButton, "isVisible", { set: function(val2) { setParams({ is_visible: val2 }); }, get: function() { return isVisible; }, enumerable: true }); var curButtonState = null; WebView.onEvent("back_button_pressed", onBackButtonPressed); function onBackButtonPressed() { receiveWebViewEvent("backButtonClicked"); } function buttonParams() { return { is_visible: isVisible }; } function buttonState(btn_params) { if (typeof btn_params === "undefined") { btn_params = buttonParams(); } return JSON.stringify(btn_params); } function buttonCheckVersion() { if (!versionAtLeast("6.1")) { console.warn( "[Telegram.WebApp] BackButton is not supported in version " + webAppVersion ); return false; } return true; } function updateButton() { var btn_params = buttonParams(); var btn_state = buttonState(btn_params); if (curButtonState === btn_state) { return; } curButtonState = btn_state; WebView.postEvent("web_app_setup_back_button", false, btn_params); } function setParams(params) { if (!buttonCheckVersion()) { return backButton; } if (typeof params.is_visible !== "undefined") { isVisible = !!params.is_visible; } updateButton(); return backButton; } backButton.onClick = function(callback) { if (buttonCheckVersion()) { onWebViewEvent("backButtonClicked", callback); } return backButton; }; backButton.offClick = function(callback) { if (buttonCheckVersion()) { offWebViewEvent("backButtonClicked", callback); } return backButton; }; backButton.show = function() { return setParams({ is_visible: true }); }; backButton.hide = function() { return setParams({ is_visible: false }); }; return backButton; }(); var mainButtonHeight = 0; var MainButton = function() { var isVisible = false; var isActive = true; var isProgressVisible = false; var buttonText = "CONTINUE"; var buttonColor = false; var buttonTextColor = false; var mainButton = {}; Object.defineProperty(mainButton, "text", { set: function(val2) { mainButton.setParams({ text: val2 }); }, get: function() { return buttonText; }, enumerable: true }); Object.defineProperty(mainButton, "color", { set: function(val2) { mainButton.setParams({ color: val2 }); }, get: function() { return buttonColor || themeParams.button_color || "#2481cc"; }, enumerable: true }); Object.defineProperty(mainButton, "textColor", { set: function(val2) { mainButton.setParams({ text_color: val2 }); }, get: function() { return buttonTextColor || themeParams.button_text_color || "#ffffff"; }, enumerable: true }); Object.defineProperty(mainButton, "isVisible", { set: function(val2) { mainButton.setParams({ is_visible: val2 }); }, get: function() { return isVisible; }, enumerable: true }); Object.defineProperty(mainButton, "isProgressVisible", { get: function() { return isProgressVisible; }, enumerable: true }); Object.defineProperty(mainButton, "isActive", { set: function(val2) { mainButton.setParams({ is_active: val2 }); }, get: function() { return isActive; }, enumerable: true }); var curButtonState = null; WebView.onEvent("main_button_pressed", onMainButtonPressed); var debugBtn = null, debugBtnStyle = {}; if (initParams.tgWebAppDebug) { debugBtn = document.createElement("tg-main-button"); debugBtnStyle = { font: "600 14px/18px sans-serif", display: "none", width: "100%", height: "48px", borderRadius: "0", background: "no-repeat right center", position: "fixed", left: "0", right: "0", bottom: "0", margin: "0", padding: "15px 20px", textAlign: "center", boxSizing: "border-box", zIndex: "10000" }; for (var k in debugBtnStyle) { debugBtn.style[k] = debugBtnStyle[k]; } document.addEventListener( "DOMContentLoaded", function onDomLoaded(event) { document.removeEventListener("DOMContentLoaded", onDomLoaded); document.body.appendChild(debugBtn); debugBtn.addEventListener("click", onMainButtonPressed, false); } ); } function onMainButtonPressed() { if (isActive) { receiveWebViewEvent("mainButtonClicked"); } } function buttonParams() { var color = mainButton.color; var text_color = mainButton.textColor; return isVisible ? { is_visible: true, is_active: isActive, is_progress_visible: isProgressVisible, text: buttonText, color, text_color } : { is_visible: false }; } function buttonState(btn_params) { if (typeof btn_params === "undefined") { btn_params = buttonParams(); } return JSON.stringify(btn_params); } function updateButton() { var btn_params = buttonParams(); var btn_state = buttonState(btn_params); if (curButtonState === btn_state) { return; } curButtonState = btn_state; WebView.postEvent("web_app_setup_main_button", false, btn_params); if (initParams.tgWebAppDebug) { updateDebugButton(btn_params); } } function updateDebugButton(btn_params) { if (btn_params.is_visible) { debugBtn.style.display = "block"; mainButtonHeight = 48; debugBtn.style.opacity = btn_params.is_active ? "1" : "0.8"; debugBtn.style.cursor = btn_params.is_active ? "pointer" : "auto"; debugBtn.disabled = !btn_params.is_active; debugBtn.innerText = btn_params.text; debugBtn.style.backgroundImage = btn_params.is_progress_visible ? "url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewport%3D%220%200%2048%2048%22%20width%3D%2248px%22%20height%3D%2248px%22%3E%3Ccircle%20cx%3D%2250%25%22%20cy%3D%2250%25%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222.25%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%20stroke-dashoffset%3D%22106%22%20r%3D%229%22%20stroke-dasharray%3D%2256.52%22%20rotate%3D%22-90%22%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20attributeType%3D%22XML%22%20dur%3D%22360s%22%20from%3D%220%22%20to%3D%2212500%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3CanimateTransform%20attributeName%3D%22transform%22%20attributeType%3D%22XML%22%20type%3D%22rotate%22%20dur%3D%221s%22%20from%3D%22-90%2024%2024%22%20to%3D%22630%2024%2024%22%20repeatCount%3D%22indefinite%22%3E%3C%2FanimateTransform%3E%3C%2Fcircle%3E%3C%2Fsvg%3E')" : "none"; debugBtn.style.backgroundColor = btn_params.color; debugBtn.style.color = btn_params.text_color; } else { debugBtn.style.display = "none"; mainButtonHeight = 0; } if (document.documentElement) { document.documentElement.style.boxSizing = "border-box"; document.documentElement.style.paddingBottom = mainButtonHeight + "px"; } setViewportHeight(); } function setParams(params) { if (typeof params.text !== "undefined") { var text = strTrim(params.text); if (!text.length) { console.error( "[Telegram.WebApp] Main button text is required", params.text ); throw Error("WebAppMainButtonParamInvalid"); } if (text.length > 64) { console.error("[Telegram.WebApp] Main button text is too long", text); throw Error("WebAppMainButtonParamInvalid"); } buttonText = text; } if (typeof params.color !== "undefined") { if (params.color === false || params.color === null) { buttonColor = false; } else { var color = parseColorToHex(params.color); if (!color) { console.error( "[Telegram.WebApp] Main button color format is invalid", params.color ); throw Error("WebAppMainButtonParamInvalid"); } buttonColor = color; } } if (typeof params.text_color !== "undefined") { if (params.text_color === false || params.text_color === null) { buttonTextColor = false; } else { var text_color = parseColorToHex(params.text_color); if (!text_color) { console.error( "[Telegram.WebApp] Main button text color format is invalid", params.text_color ); throw Error("WebAppMainButtonParamInvalid"); } buttonTextColor = text_color; } } if (typeof params.is_visible !== "undefined") { if (params.is_visible && !mainButton.text.length) { console.error("[Telegram.WebApp] Main button text is required"); throw Error("WebAppMainButtonParamInvalid"); } isVisible = !!params.is_visible; } if (typeof params.is_active !== "undefined") { isActive = !!params.is_active; } updateButton(); return mainButton; } mainButton.setText = function(text) { return mainButton.setParams({ text }); }; mainButton.onClick = function(callback) { onWebViewEvent("mainButtonClicked", callback); return mainButton; }; mainButton.offClick = function(callback) { offWebViewEvent("mainButtonClicked", callback); return mainButton; }; mainButton.show = function() { return mainButton.setParams({ is_visible: true }); }; mainButton.hide = function() { return mainButton.setParams({ is_visible: false }); }; mainButton.enable = function() { return mainButton.setParams({ is_active: true }); }; mainButton.disable = function() { return mainButton.setParams({ is_active: false }); }; mainButton.showProgress = function(leaveActive) { isActive = !!leaveActive; isProgressVisible = true; updateButton(); return mainButton; }; mainButton.hideProgress = function() { if (!mainButton.isActive) { isActive = true; } isProgressVisible = false; updateButton(); return mainButton; }; mainButton.setParams = setParams; return mainButton; }(); var SettingsButton = function() { var isVisible = false; var settingsButton = {}; Object.defineProperty(settingsButton, "isVisible", { set: function(val2) { setParams({ is_visible: val2 }); }, get: function() { return isVisible; }, enumerable: true }); var curButtonState = null; WebView.onEvent("settings_button_pressed", onSettingsButtonPressed); function onSettingsButtonPressed() { receiveWebViewEvent("settingsButtonClicked"); } function buttonParams() { return { is_visible: isVisible }; } function buttonState(btn_params) { if (typeof btn_params === "undefined") { btn_params = buttonParams(); } return JSON.stringify(btn_params); } function buttonCheckVersion() { if (!versionAtLeast("6.10")) { console.warn( "[Telegram.WebApp] SettingsButton is not supported in version " + webAppVersion ); return false; } return true; } function updateButton() { var btn_params = buttonParams(); var btn_state = buttonState(btn_params); if (curButtonState === btn_state) { return; } curButtonState = btn_state; WebView.postEvent("web_app_setup_settings_button", false, btn_params); } function setParams(params) { if (!buttonCheckVersion()) { return settingsButton; } if (typeof params.is_visible !== "undefined") { isVisible = !!params.is_visible; } updateButton(); return settingsButton; } settingsButton.onClick = function(callback) { if (buttonCheckVersion()) { onWebViewEvent("settingsButtonClicked", callback); } return settingsButton; }; settingsButton.offClick = function(callback) { if (buttonCheckVersion()) { offWebViewEvent("settingsButtonClicked", callback); } return settingsButton; }; settingsButton.show = function() { return setParams({ is_visible: true }); }; settingsButton.hide = function() { return setParams({ is_visible: false }); }; return settingsButton; }(); var HapticFeedback = function() { var hapticFeedback = {}; function triggerFeedback(params) { if (!versionAtLeast("6.1")) { console.warn( "[Telegram.WebApp] HapticFeedback is not supported in version " + webAppVersion ); return hapticFeedback; } if (params.type == "impact") { if (params.impact_style != "light" && params.impact_style != "medium" && params.impact_style != "heavy" && params.impact_style != "rigid" && params.impact_style != "soft") { console.error( "[Telegram.WebApp] Haptic impact style is invalid", params.impact_style ); throw Error("WebAppHapticImpactStyleInvalid"); } } else if (params.type == "notification") { if (params.notification_type != "error" && params.notification_type != "success" && params.notification_type != "warning") { console.error( "[Telegram.WebApp] Haptic notification type is invalid", params.notification_type ); throw Error("WebAppHapticNotificationTypeInvalid"); } } else if (params.type == "selection_change") { } else { console.error( "[Telegram.WebApp] Haptic feedback type is invalid", params.type ); throw Error("WebAppHapticFeedbackTypeInvalid"); } WebView.postEvent("web_app_trigger_haptic_feedback", false, params); return hapticFeedback; } hapticFeedback.impactOccurred = function(style) { return triggerFeedback({ type: "impact", impact_style: style }); }; hapticFeedback.notificationOccurred = function(type) { return triggerFeedback({ type: "notification", notification_type: type }); }; hapticFeedback.selectionChanged = function() { return triggerFeedback({ type: "selection_change" }); }; return hapticFeedback; }(); var CloudStorage = function() { var cloudStorage = {}; function invokeStorageMethod(method, params, callback) { if (!versionAtLeast("6.9")) { console.error( "[Telegram.WebApp] CloudStorage is not supported in version " + webAppVersion ); throw Error("WebAppMethodUnsupported"); } invokeCustomMethod(method, params, callback); return cloudStorage; } cloudStorage.setItem = function(key2, value, callback) { return invokeStorageMethod( "saveStorageValue", { key: key2, value }, callback ); }; cloudStorage.getItem = function(key2, callback) { return cloudStorage.getItems( [key2], callback ? function(err, res) { if (err) callback(err); else callback(null, res[key2]); } : null ); }; cloudStorage.getItems = function(keys, callback) { return invokeStorageMethod("getStorageValues", { keys }, callback); }; cloudStorage.removeItem = function(key2, callback) { return cloudStorage.removeItems([key2], callback); }; cloudStorage.removeItems = function(keys, callback) { return invokeStorageMethod( "deleteStorageValues", { keys }, callback ); }; cloudStorage.getKeys = function(callback) { return invokeStorageMethod("getStorageKeys", {}, callback); }; return cloudStorage; }(); var BiometricManager = function() { var isInited = false; var isBiometricAvailable = false; var biometricType = "unknown"; var isAccessRequested = false; var isAccessGranted = false; var isBiometricTokenSaved = false; var deviceId = ""; var biometricManager = {}; Object.defineProperty(biometricManager, "isInited", { get: function() { return isInited; }, enumerable: true }); Object.defineProperty(biometricManager, "isBiometricAvailable", { get: function() { return isInited && isBiometricAvailable; }, enumerable: true }); Object.defineProperty(biometricManager, "biometricType", { get: function() { return biometricType || "unknown"; }, enumerable: true }); Object.defineProperty(biometricManager, "isAccessRequested", { get: function() { return isAccessRequested; }, enumerable: true }); Object.defineProperty(biometricManager, "isAccessGranted", { get: function() { return isAccessRequested && isAccessGranted; }, enumerable: true }); Object.defineProperty(biometricManager, "isBiometricTokenSaved", { get: function() { return isBiometricTokenSaved; }, enumerable: true }); Object.defineProperty(biometricManager, "deviceId", { get: function() { return deviceId || ""; }, enumerable: true }); var initRequestState = { callbacks: [] }; var accessRequestState = false; var authRequestState = false; var tokenRequestState = false; WebView.onEvent("biometry_info_received", onBiometryInfoReceived); WebView.onEvent("biometry_auth_requested", onBiometryAuthRequested); WebView.onEvent("biometry_token_updated", onBiometryTokenUpdated); function onBiometryInfoReceived(eventType, eventData) { isInited = true; if (eventData.available) { isBiometricAvailable = true; biometricType = eventData.type || "unknown"; if (eventData.access_requested) { isAccessRequested = true; isAccessGranted = !!eventData.access_granted; isBiometricTokenSaved = !!eventData.token_saved; } else { isAccessRequested = false; isAccessGranted = false; isBiometricTokenSaved = false; } } else { isBiometricAvailable = false; biometricType = "unknown"; isAccessRequested = false; isAccessGranted = false; isBiometricTokenSaved = false; } deviceId = eventData.device_id || ""; if (initRequestState.callbacks.length > 0) { for (var i = 0; i < initRequestState.callbacks.length; i++) { var callback = initRequestState.callbacks[i]; callback(); } } if (accessRequestState) { var state = accessRequestState; accessRequestState = false; if (state.callback) { state.callback(isAccessGranted); } } receiveWebViewEvent("biometricManagerUpdated"); } function onBiometryAuthRequested(eventType, eventData) { var isAuthenticated = eventData.status == "authorized", biometricToken = eventData.token || ""; if (authRequestState) { var state = authRequestState; authRequestState = false; if (state.callback) { state.callback( isAuthenticated, isAuthenticated ? biometricToken : null ); } } receiveWebViewEvent( "biometricAuthRequested", isAuthenticated ? { isAuthenticated: true, biometricToken } : { isAuthenticated: false } ); } function onBiometryTokenUpdated(eventType, eventData) { var applied = false; if (isBiometricAvailable && isAccessRequested) { if (eventData.status == "updated") { isBiometricTokenSaved = true; applied = true; } else if (eventData.status == "removed") { isBiometricTokenSaved = false; applied = true; } } if (tokenRequestState) { var state = tokenRequestState; tokenRequestState = false; if (state.callback) { state.callback(applied); } } receiveWebViewEvent("biometricTokenUpdated", { isUpdated: applied }); } function checkVersion() { if (!versionAtLeast("7.2")) { console.warn( "[Telegram.WebApp] BiometricManager is not supported in version " + webAppVersion ); return false; } return true; } function checkInit() { if (!isInited) { console.error( "[Telegram.WebApp] BiometricManager should be inited before using." ); throw Error("WebAppBiometricManagerNotInited"); } return true; } biometricManager.init = function(callback) { if (!checkVersion()) { return biometricManager; } if (isInited) { return biometricManager; } if (callback) { initRequestState.callbacks.push(callback); } WebView.postEvent("web_app_biometry_get_info", false); return biometricManager; }; biometricManager.requestAccess = function(params, callback) { if (!checkVersion()) { return biometricManager; } checkInit(); if (!isBiometricAvailable) { console.error( "[Telegram.WebApp] Biometrics is not available on this device." ); throw Error("WebAppBiometricManagerBiometricsNotAvailable"); } if (accessRequestState) { console.error("[Telegram.WebApp] Access is already requested"); throw Error("WebAppBiometricManagerAccessRequested"); } var popup_params = {}; if (typeof params.reason !== "undefined") { var reason = strTrim(params.reason); if (reason.length > 128) { console.error( "[Telegram.WebApp] Biometric reason is too long", reason ); throw Error("WebAppBiometricRequestAccessParamInvalid"); } if (reason.length > 0) { popup_params.reason = reason; } } accessRequestState = { callback }; WebView.postEvent("web_app_biometry_request_access", false, popup_params); return biometricManager; }; biometricManager.authenticate = function(params, callback) { if (!checkVersion()) { return biometricManager; } checkInit(); if (!isBiometricAvailable) { console.error( "[Telegram.WebApp] Biometrics is not available on this device." ); throw Error("WebAppBiometricManagerBiometricsNotAvailable"); } if (!isAccessGranted) { console.error( "[Telegram.WebApp] Biometric access was not granted by the user." ); throw Error("WebAppBiometricManagerBiometricAccessNotGranted"); } if (authRequestState) { console.error( "[Telegram.WebApp] Authentication request is already in progress." ); throw Error("WebAppBiometricManagerAuthenticationRequested"); } var popup_params = {}; if (typeof params.reason !== "undefined") { var reason = strTrim(params.reason); if (reason.length > 128) { console.error( "[Telegram.WebApp] Biometric reason is too long", reason ); throw Error("WebAppBiometricRequestAccessParamInvalid"); } if (reason.length > 0) { popup_params.reason = reason; } } authRequestState = { callback }; WebView.postEvent("web_app_biometry_request_auth", false, popup_params); return biometricManager; }; biometricManager.updateBiometricToken = function(token, callback) { if (!checkVersion()) { return biometricManager; } token = token || ""; if (token.length > 1024) { console.error("[Telegram.WebApp] Token is too long", token); throw Error("WebAppBiometricManagerTokenInvalid"); } checkInit(); if (!isBiometricAvailable) { console.error( "[Telegram.WebApp] Biometrics is not available on this device." ); throw Error("WebAppBiometricManagerBiometricsNotAvailable"); } if (!isAccessGranted) { console.error( "[Telegram.WebApp] Biometric access was not granted by the user." ); throw Error("WebAppBiometricManagerBiometricAccessNotGranted"); } if (tokenRequestState) { console.error("[Telegram.WebApp] Token request is already in progress."); throw Error("WebAppBiometricManagerTokenUpdateRequested"); } tokenRequestState = { callback }; WebView.postEvent("web_app_biometry_update_token", false, { token }); return biometricManager; }; biometricManager.openSettings = function() { if (!checkVersion()) { return biometricManager; } checkInit(); if (!isBiometricAvailable) { console.error( "[Telegram.WebApp] Biometrics is not available on this device." ); throw Error("WebAppBiometricManagerBiometricsNotAvailable"); } if (!isAccessRequested) { console.error( "[Telegram.WebApp] Biometric access was not requested yet." ); throw Error("WebAppBiometricManagerBiometricsAccessNotRequested"); } if (isAccessGranted) { console.warn( "[Telegram.WebApp] Biometric access was granted by the user, no need to go to settings." ); return biometricManager; } WebView.postEvent("web_app_biometry_open_settings", false); return biometricManager; }; return biometricManager; }(); var webAppInvoices = {}; function onInvoiceClosed(eventType, eventData) { if (eventData.slug && webAppInvoices[eventData.slug]) { var invoiceData = webAppInvoices[eventData.slug]; delete webAppInvoices[eventData.slug]; if (invoiceData.callback) { invoiceData.callback(eventData.status); } receiveWebViewEvent("invoiceClosed", { url: invoiceData.url, status: eventData.status }); } } var webAppPopupOpened = false; function onPopupClosed(eventType, eventData) { if (webAppPopupOpened) { var popupData = webAppPopupOpened; webAppPopupOpened = false; var button_id = null; if (typeof eventData.button_id !== "undefined") { button_id = eventData.button_id; } if (popupData.callback) { popupData.callback(button_id); } receiveWebViewEvent("popupClosed", { button_id }); } } var webAppScanQrPopupOpened = false; function onQrTextReceived(eventType, eventData) { if (webAppScanQrPopupOpened) { var popupData = webAppScanQrPopupOpened; var data = null; if (typeof eventData.data !== "undefined") { data = eventData.data; } if (popupData.callback) { if (popupData.callback(data)) { webAppScanQrPopupOpened = false; WebView.postEvent("web_app_close_scan_qr_popup", false); } } receiveWebViewEvent("qrTextReceived", { data }); } } function onScanQrPopupClosed(eventType, eventData) { webAppScanQrPopupOpened = false; receiveWebViewEvent("scanQrPopupClosed"); } function onClipboardTextReceived(eventType, eventData) { if (eventData.req_id && webAppCallbacks[eventData.req_id]) { var requestData = webAppCallbacks[eventData.req_id]; delete webAppCallbacks[eventData.req_id]; var data = null; if (typeof eventData.data !== "undefined") { data = eventData.data; } if (requestData.callback) { requestData.callback(data); } receiveWebViewEvent("clipboardTextReceived", { data }); } } var WebAppWriteAccessRequested = false; function onWriteAccessRequested(eventType, eventData) { if (WebAppWriteAccessRequested) { var requestData = WebAppWriteAccessRequested; WebAppWriteAccessRequested = false; if (requestData.callback) { requestData.callback(eventData.status == "allowed"); } receiveWebViewEvent("writeAccessRequested", { status: eventData.status }); } } function getRequestedContact(callback, timeout) { var reqTo, fallbackTo, reqDelay = 0; var reqInvoke = function() { invokeCustomMethod("getRequestedContact", {}, function(err, res) { if (res && res.length) { clearTimeout(fallbackTo); callback(res); } else { reqDelay += 50; reqTo = setTimeout(reqInvoke, reqDelay); } }); }; var fallbackInvoke = function() { clearTimeout(reqTo); callback(""); }; fallbackTo = setTimeout(fallbackInvoke, timeout); reqInvoke(); } var WebAppContactRequested = false; function onPhoneRequested(eventType, eventData) { if (WebAppContactRequested) { var requestData = WebAppContactRequested; WebAppContactRequested = false; var requestSent = eventData.status == "sent"; var webViewEvent = { status: eventData.statu