├── LICENSE ├── README.md └── games.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 TelegramMessenger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Library for communication between Telegram Apps and Games 2 | 3 | More info on the [Telegram Gaming Platform](https://core.telegram.org/bots/games) 4 | 5 | [Gaming Platform announcement](https://telegram.org/blog/games) 6 | -------------------------------------------------------------------------------- /games.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var eventHandlers = {}; 3 | 4 | // Parse init params from location hash: for Android < 5.0, TDesktop 5 | var locationHash = ''; 6 | try { 7 | locationHash = location.hash.toString(); 8 | } catch (e) {} 9 | 10 | var initParams = urlParseHashParams(locationHash); 11 | 12 | var isIframe = false; 13 | try { 14 | isIframe = (window.parent != null && window != window.parent); 15 | } catch (e) {} 16 | 17 | 18 | function urlSafeDecode(urlencoded) { 19 | try { 20 | return decodeURIComponent(urlencoded); 21 | } catch (e) { 22 | return urlencoded; 23 | } 24 | } 25 | 26 | function urlParseHashParams(locationHash) { 27 | locationHash = locationHash.replace(/^#/, ''); 28 | var params = {}; 29 | if (!locationHash.length) { 30 | return params; 31 | } 32 | if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) { 33 | params._path = urlSafeDecode(locationHash); 34 | return params; 35 | } 36 | var qIndex = locationHash.indexOf('?'); 37 | if (qIndex >= 0) { 38 | var pathParam = locationHash.substr(0, qIndex); 39 | params._path = urlSafeDecode(pathParam); 40 | locationHash = locationHash.substr(qIndex + 1); 41 | } 42 | var locationHashParams = locationHash.split('&'); 43 | var i, param, paramName, paramValue; 44 | for (i = 0; i < locationHashParams.length; i++) { 45 | param = locationHashParams[i].split('='); 46 | paramName = urlSafeDecode(param[0]); 47 | paramValue = param[1] == null ? null : urlSafeDecode(param[1]); 48 | params[paramName] = paramValue; 49 | } 50 | return params; 51 | } 52 | 53 | // Telegram apps will implement this logic to add service params (e.g. tgShareScoreUrl) to game URL 54 | function urlAppendHashParams(url, addHash) { 55 | // url looks like 'https://game.com/path?query=1#hash' 56 | // addHash looks like 'tgShareScoreUrl=' + encodeURIComponent('tgb://share_game_score?hash=very_long_hash123') 57 | 58 | var ind = url.indexOf('#'); 59 | if (ind < 0) { 60 | // https://game.com/path -> https://game.com/path#tgShareScoreUrl=etc 61 | return url + '#' + addHash; 62 | } 63 | var curHash = url.substr(ind + 1); 64 | if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) { 65 | // https://game.com/#hash=1 -> https://game.com/#hash=1&tgShareScoreUrl=etc 66 | // https://game.com/#path?query -> https://game.com/#path?query&tgShareScoreUrl=etc 67 | return url + '&' + addHash; 68 | } 69 | // https://game.com/#hash -> https://game.com/#hash?tgShareScoreUrl=etc 70 | if (curHash.length > 0) { 71 | return url + '?' + addHash; 72 | } 73 | // https://game.com/# -> https://game.com/#tgShareScoreUrl=etc 74 | return url + addHash; 75 | } 76 | 77 | 78 | function postEvent (eventType, callback, eventData) { 79 | if (!callback) { 80 | callback = function () {}; 81 | } 82 | if (eventData === undefined) { 83 | eventData = ''; 84 | } 85 | 86 | if (window.TelegramWebviewProxy !== undefined) { 87 | TelegramWebviewProxy.postEvent(eventType, eventData); 88 | callback(); 89 | } 90 | else if (window.external && 'notify' in window.external) { 91 | window.external.notify(JSON.stringify({eventType: eventType, eventData: eventData})); 92 | callback(); 93 | } 94 | else if (isIframe) { 95 | try { 96 | var trustedTarget = 'https://web.telegram.org'; 97 | // For now we don't restrict target, for testing purposes 98 | trustedTarget = '*'; 99 | window.parent.postMessage(JSON.stringify({eventType: eventType, eventData: eventData}), trustedTarget); 100 | } catch (e) { 101 | callback(e); 102 | } 103 | } 104 | else { 105 | callback({notAvailable: true}); 106 | } 107 | }; 108 | 109 | function receiveEvent(eventType, eventData) { 110 | var curEventHandlers = eventHandlers[eventType]; 111 | if (curEventHandlers === undefined || 112 | !curEventHandlers.length) { 113 | return; 114 | } 115 | for (var i = 0; i < curEventHandlers.length; i++) { 116 | try { 117 | curEventHandlers[i](eventType, eventData); 118 | } catch (e) {} 119 | } 120 | } 121 | 122 | function onEvent (eventType, callback) { 123 | if (eventHandlers[eventType] === undefined) { 124 | eventHandlers[eventType] = []; 125 | } 126 | var index = eventHandlers[eventType].indexOf(callback); 127 | if (index === -1) { 128 | eventHandlers[eventType].push(callback); 129 | } 130 | }; 131 | 132 | function offEvent (eventType, callback) { 133 | if (eventHandlers[eventType] === undefined) { 134 | return; 135 | } 136 | var index = eventHandlers[eventType].indexOf(callback); 137 | if (index === -1) { 138 | return; 139 | } 140 | eventHandlers[eventType].splice(index, 1); 141 | }; 142 | 143 | function openProtoUrl(url) { 144 | if (!url.match(/^(web\+)?tgb?:\/\/./)) { 145 | return false; 146 | } 147 | var wnd = false; 148 | try { 149 | wnd = window.open(url, '_blank'); 150 | } catch (e) { 151 | wnd = false; 152 | } 153 | if (!wnd) { 154 | location.href = url; 155 | } 156 | return true; 157 | } 158 | 159 | // For Windows Phone app 160 | window.TelegramGameProxy_receiveEvent = receiveEvent; 161 | 162 | window.TelegramGameProxy = { 163 | initParams: initParams, 164 | receiveEvent: receiveEvent, 165 | onEvent: onEvent, 166 | shareScore: function () { 167 | postEvent('share_score', function (error) { 168 | if (error) { 169 | var shareScoreUrl = initParams.tgShareScoreUrl; 170 | if (shareScoreUrl) { 171 | openProtoUrl(shareScoreUrl); 172 | } 173 | } 174 | }); 175 | } 176 | }; 177 | 178 | })(); 179 | --------------------------------------------------------------------------------