telegram_webapp_sdk/core/
init.rs1use js_sys::Reflect;
5use serde_wasm_bindgen::from_value;
6use wasm_bindgen::JsValue;
7use web_sys::window;
8
9use crate::core::{
10 context::TelegramContext,
11 types::{
12 chat::TelegramChat, init_data::TelegramInitData,
13 init_data_internal::TelegramInitDataInternal, theme_params::TelegramThemeParams,
14 user::TelegramUser
15 }
16};
17
18#[derive(Debug, Clone, PartialEq)]
20pub enum InitError {
21 WindowUnavailable,
23 TelegramUnavailable,
25 WebAppUnavailable,
27 InitDataParseFailed(String),
29 ThemeParamsParseFailed(String),
31 ContextInitFailed(String)
33}
34
35impl std::fmt::Display for InitError {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 match self {
38 Self::WindowUnavailable => write!(f, "Browser window object is not available"),
39 Self::TelegramUnavailable => write!(f, "window.Telegram is undefined"),
40 Self::WebAppUnavailable => write!(f, "Telegram.WebApp is undefined"),
41 Self::InitDataParseFailed(msg) => write!(f, "Failed to parse initData: {msg}"),
42 Self::ThemeParamsParseFailed(msg) => {
43 write!(f, "Failed to parse theme parameters: {msg}")
44 }
45 Self::ContextInitFailed(msg) => write!(f, "Failed to initialize context: {msg}")
46 }
47 }
48}
49
50impl std::error::Error for InitError {}
51
52impl From<InitError> for JsValue {
53 fn from(err: InitError) -> Self {
54 JsValue::from_str(&err.to_string())
55 }
56}
57
58pub fn is_telegram_available() -> bool {
73 window()
74 .and_then(|w| Reflect::get(&w, &"Telegram".into()).ok())
75 .filter(|tg| !tg.is_undefined())
76 .and_then(|tg| Reflect::get(&tg, &"WebApp".into()).ok())
77 .filter(|webapp| !webapp.is_undefined())
78 .is_some()
79}
80
81pub fn try_init_sdk() -> Result<bool, InitError> {
105 if !is_telegram_available() {
106 return Ok(false);
107 }
108 init_sdk_typed().map(|_| true)
109}
110
111fn init_sdk_typed() -> Result<(), InitError> {
113 let win = window().ok_or(InitError::WindowUnavailable)?;
114 let telegram =
115 Reflect::get(&win, &"Telegram".into()).map_err(|_| InitError::TelegramUnavailable)?;
116
117 if telegram.is_undefined() {
118 return Err(InitError::TelegramUnavailable);
119 }
120
121 let webapp =
122 Reflect::get(&telegram, &"WebApp".into()).map_err(|_| InitError::WebAppUnavailable)?;
123
124 if webapp.is_undefined() {
125 return Err(InitError::WebAppUnavailable);
126 }
127
128 let init_data_str = Reflect::get(&webapp, &"initData".into())
130 .ok()
131 .and_then(|v| v.as_string())
132 .ok_or_else(|| InitError::InitDataParseFailed("initData is not a string".to_string()))?;
133
134 let raw: TelegramInitDataInternal = serde_urlencoded::from_str(&init_data_str)
135 .map_err(|e| InitError::InitDataParseFailed(e.to_string()))?;
136
137 let user: Option<TelegramUser> = raw
139 .user
140 .as_deref()
141 .map(serde_json::from_str)
142 .transpose()
143 .map_err(|e| InitError::InitDataParseFailed(format!("Failed to parse user: {e}")))?;
144
145 let receiver: Option<TelegramUser> = raw
146 .receiver
147 .as_deref()
148 .map(serde_json::from_str)
149 .transpose()
150 .map_err(|e| InitError::InitDataParseFailed(format!("Failed to parse receiver: {e}")))?;
151
152 let chat: Option<TelegramChat> = raw
153 .chat
154 .as_deref()
155 .map(serde_json::from_str)
156 .transpose()
157 .map_err(|e| InitError::InitDataParseFailed(format!("Failed to parse chat: {e}")))?;
158
159 let init_data = TelegramInitData {
161 query_id: raw.query_id,
162 user,
163 receiver,
164 chat,
165 chat_type: raw.chat_type,
166 chat_instance: raw.chat_instance,
167 start_param: raw.start_param,
168 can_send_after: raw.can_send_after,
169 auth_date: raw.auth_date,
170 hash: raw.hash,
171 signature: raw.signature
172 };
173
174 let theme_val = Reflect::get(&webapp, &"themeParams".into())
176 .map_err(|e| InitError::ThemeParamsParseFailed(format!("{e:?}")))?;
177 let theme_params: TelegramThemeParams =
178 from_value(theme_val).map_err(|e| InitError::ThemeParamsParseFailed(format!("{e:?}")))?;
179
180 TelegramContext::init(init_data, theme_params, init_data_str)
182 .map_err(|e| InitError::ContextInitFailed(format!("{e:?}")))?;
183
184 Ok(())
185}
186
187pub fn init_sdk() -> Result<(), JsValue> {
217 init_sdk_typed().map_err(Into::into)
218}