该软件包提供在 服务器端处理 Telegram 迷你应用程序初始化数据的实用程序。 要了解有关初始化数据及其用法的更多信息,请参阅 the documentation.安装
/ s+ z g% Z! D) xpnpm :
* f9 g; ] j m- pnpm i @telegram-apps/init-data-node
复制代码 npm :
( k; Z9 T$ ^2 d# S3 D5 n7 _- npm i @telegram-apps/init-data-node
复制代码 yarn :# W+ z/ ^. {) |7 A4 j+ P
- yarn add @telegram-apps/init-data-node
复制代码 解析要将一个值解析为 init 数据,请使用 parse 方法。 该方法接受以 string 或 URLSearchParams 形式呈现的 init 数据。 - import { parse } from '@telegram-apps/init-data-node';, H# \1 b. j! d$ W Z. x7 Z
- ; a' g# B1 N' o- D$ K6 A
- try {
# |6 N3 v3 I2 `; k% S- v - const initData = parse('...'); e) ]( q7 V: ?, {. H5 |; L# j
- // {
" A8 e8 o1 ?% o3 G - // canSendAfter: 10000,
% `3 c5 M$ V, Q2 a' G4 J) L - // chat: {
: B8 s0 p( j. r1 ?3 s - // id: 1,; l/ j2 u- N' M( I9 C6 f. {* i
- // type: 'group',- G, |' l$ Z$ U, k3 F& `9 z0 E1 q; q
- // username: 'my-chat',
7 \+ @! L: r+ F3 Y! i( n5 n - // title: 'chat-title',
5 h& }" E: b; Q: t - // photoUrl: 'chat-photo',
" P5 b7 v6 P' |3 K: v - // }, r( K- f" |8 d
- // chatInstance: '888',) x+ I; \1 Y; R/ D! c% L3 Y" o: P, X$ K
- // chatType: 'sender',
; x3 u4 `' X0 s1 S" G2 A( ^% b - // queryId: 'QUERY',
/ j0 E! j) q; p - // receiver: {* f9 l4 B- D6 m1 R7 p
- // addedToAttachmentMenu: false,
W7 a% K# _& c( M: Y - // allowsWriteToPm: true,: N+ {$ d+ d2 Q$ s- ?$ n
- // firstName: 'receiver-first-name',
3 m: u6 b- m9 A/ G - // id: 991,
: U q* T5 v2 n/ L* b. k - // isBot: false,; Y' ~/ `% V: ^" \- }
- // isPremium: true,
- }7 p+ U! I# y7 \* \# y' B0 l - // languageCode: 'ru',
, i. j+ U, m) ~' ^& P; e) v: b - // lastName: 'receiver-last-name',' r* K8 l4 J: ?/ ?: C; Y0 g
- // photoUrl: 'receiver-photo',
8 ^/ A! C7 H* r7 X - // username: 'receiver-username',6 H3 h5 E3 a8 P
- // },
- |" C& t2 h$ t, C - // startParam: 'debug',) F$ Q6 q3 s* S- H
- // user: {
; L8 F7 P9 H; K% f/ o9 y( z - // addedToAttachmentMenu: false,; n1 z( h7 m1 c; b. M
- // allowsWriteToPm: false,' ^- G0 g* b. q1 [( n" W$ e- w5 h
- // firstName: 'user-first-name',
1 _/ n( p' f' h' C2 p% I - // id: 222,
3 `$ ~. q4 Q: G% \- X) [ - // isBot: true,
' T/ p6 i, \% Q+ M5 L! ~ _ - // isPremium: false,3 L, O( {$ R7 `( L, @% j1 A9 _8 ]
- // languageCode: 'en',! i- W9 ?2 U# Q/ A) y
- // lastName: 'user-last-name',
: x8 s) O; V* b, S - // photoUrl: 'user-photo',
* r& ?; R! v& X. g - // username: 'user-username',. V6 b$ a, ]" j# Y" k) J ^
- // },
* A* G1 I9 L8 c# d - // }
9 ~5 h) G* V/ Q/ x, A! v - } catch (e) {
5 G+ ^. {9 R% |! ` c% I1 s7 A - console.error('Something is wrong', e);
7 _6 \* `8 W/ R4 `; n \( h - }
复制代码 验证validate要验证初始化数据的签名,需要使用validate函数。 它希望 以原始格式(搜索参数)传递初始化数据,并在 某些情况下出错。 - import { validate } from '@telegram-apps/init-data-node';$ I( ?0 }! ?3 c' m
% S% ]" c# {# s) w5 z2 T- const secretToken = '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8';! M2 \+ l5 Q$ j' p! s {( V
- const initData =, {! c4 x" G4 }+ {; U
- 'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +; {7 c9 @/ e O/ l s& ~
- '&user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%7D' + W0 i% H: L: n* h/ D' {* f
- '&auth_date=1662771648' +* J- s: G* H& @
- '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';
7 V; ]0 {: }/ g3 K - * r9 z4 k1 S! g7 p% p M
- validate(initData, secretToken);
复制代码函数会在其中一种情况下出错: - ERR_AUTH_DATE_INVALID:auth_date 为空或未找到
- ERR_HASH_INVALID:hash 为空或未找到
- ERR_SIGN_INVALID:签名无效
- ERR_EXPIRED:初始数据已过期0 N: V: w% |6 d: e2 u
以下是您可以用来检查错误类型的代码: - import { validate, isErrorOfType } from '@telegram-apps/init-data-node';
+ v7 t7 v; K3 \
# X" m& D, r) [+ l+ e0 d- try {- K5 M0 L. a) B4 ]- [- U( \7 B
- validate('init-data', 'token');
- g- n) ^! {2 }) w' T* p - } catch (e) {
( |2 F& u# \0 K7 f5 t9 g - if (isErrorOfType('ERR_SIGN_INVALID')) {
. c) Y% k4 s9 @6 f, z# A' y - console.log('Sign is invalid');/ C9 \/ [) R0 y7 d, [9 L4 M
- }; }1 e3 `* ?, i4 h
- }
复制代码默认情况下,函数会检查初始化数据是否过期。 的默认过期时间设置为 1 天(86,400 秒)。 建议始终检查 初始化数据的有效期,因为它可能被盗但仍然有效。 要禁用此功能,将 { expiresIn: 0 } 作为第三个参数。 isValid或者,开发人员可以使用 isValid 函数来检查初始数据的有效性。 它不会引发错误,但会返回一个布尔值,表明初始数据的有效性。 - import { isValid } from '@telegram-apps/init-data-node';
* G2 T) @, J! g' @/ S2 P- C - - r9 m R0 X( h
- if (isValid('init-data')) {/ ^( e2 t- O' ?, e$ L
- console.log('Init data is fine');0 M: j* j" D1 A
- }
复制代码 签名在某些情况下,开发人员可能需要创建自己的初始数据。 例如,如果您使用 KeyboardButton 或 InlineKeyboardButton, Telegram 不会自动发送这些数据。 Telegram 无法做到这一点,因为它不知道应该使用哪个 Telegram Bot 令牌 。 要实现这一过程,需要使用 sign 方法。 下面是完整的示例: Signing : - import { sign } from '@telegram-apps/init-data-node';
- D( y: w, S5 R% g& N3 _ - ( E6 o& h2 S3 g7 V9 O% V3 T& @
- sign(
- J4 t& R( |7 M% A0 E2 e - {
# h8 y p) ?. B - canSendAfter: 10000,
/ R% {+ e5 R$ p1 K6 F- B2 D - chat: {
4 x+ E' _; X5 I& ? - id: 1,
" Y- x* T% C* `/ F6 { C - type: 'group',
" l/ [; }4 T1 G0 ~1 c" ~8 y% I - username: 'my-chat',
( j0 O" E: x6 K - title: 'chat-title',
9 ~5 [% x- Q$ M3 B1 Z - photoUrl: 'chat-photo',( G6 y9 R, g6 y } e& G
- },. ~" f7 ~) `, M% X" f) w) Q
- chatInstance: '888',( y1 o6 \# G% m) L/ H& x& D
- chatType: 'sender',8 U4 s7 e+ f/ W: X
- queryId: 'QUERY',- U4 B& U7 a' v
- receiver: {2 l/ f1 \1 u) i2 e' o' z4 X
- addedToAttachmentMenu: false,6 }+ R3 b9 Q7 N/ q5 ?5 A- n
- allowsWriteToPm: true, a7 Q* ]+ ^% f* D" y: f1 L5 @0 C
- firstName: 'receiver-first-name',
8 T d$ K$ c0 m) j - id: 991,6 K( u% f: O9 }; d* |4 b
- isBot: false,/ b1 c0 a) i! S. }. }' i7 n' ]( B
- isPremium: true,' P3 _, q7 C! O$ I9 I
- languageCode: 'ru',1 j0 F5 f+ o/ _+ ^, }* Q, Y' j
- lastName: 'receiver-last-name',
' P- E' d) L8 t9 ~" l1 b2 y - photoUrl: 'receiver-photo',3 }7 P7 f9 {4 p: K s( C* n
- username: 'receiver-username',
( G9 K. Q' @% u( `6 M0 f+ ] - },( t p' L" z. ~6 I% I0 E0 h$ U" C6 L
- startParam: 'debug',
- L- i9 _# \8 X9 m. y/ u4 e - user: {0 M d! S" O+ }4 L
- addedToAttachmentMenu: false,
, Y' X) z! g0 |/ H D. h3 l+ c - allowsWriteToPm: false,! }8 }. |- W D: I
- firstName: 'user-first-name',
/ v: X& ] o! Y: d3 S - id: 222,
: U+ ~% v3 @9 W9 B& _ - isBot: true,
2 e" O& U5 k9 u w; `- J - isPremium: false,- D+ E+ S# I9 D0 I! `) n% A0 x
- languageCode: 'en',$ |) c5 ^ I2 y! t# ~, i4 x+ @# T; ~
- lastName: 'user-last-name',
, M# {/ v/ h! h8 y* A - photoUrl: 'user-photo',
" W; g4 N" @7 g3 f - username: 'user-username',6 R# ]% W9 g/ l7 Q5 K9 p0 r
- },
/ z( ~- t4 T* Y8 k - },
$ g4 [+ _- o' {" }0 | - '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8',1 Y, Z: z @. n& h( m" X$ ]
- new Date(1000),6 X. ~" m/ {3 b7 I
- );
复制代码Expected Result : - 'auth_date=1' +
7 j5 d9 b4 E* F( K - '&can_send_after=10000' +
5 J; D$ ~" j# b2 ~( C - '&chat=%7B%22id%22%3A1%2C%22type%22%3A%22group%22%2C%22title%22%3A%22chat-title%22%2C%22photo_url%22%3A%22group%22%2C%22username%22%3A%22my-chat%22%7D' +9 Q& c1 m) y$ ?8 Z( c
- '&chat_instance=888' +% ?' `( W7 C/ g2 ^$ P; e4 F
- '&chat_type=sender' +' l# y# X R$ _
- '&query_id=QUERY' +
! N) @+ ?4 j$ _5 v - '&receiver=%7B%22added_to_attachment_menu%22%3Afalse%2C%22allows_write_to_pm%22%3Atrue%2C%22first_name%22%3A%22receiver-first-name%22%2C%22id%22%3A991%2C%22is_bot%22%3Afalse%2C%22is_premium%22%3Atrue%2C%22language_code%22%3A%22ru%22%2C%22last_name%22%3A%22receiver-last-name%22%2C%22photo_url%22%3A%22receiver-photo%22%2C%22username%22%3A%22receiver-username%22%7D' +
6 g2 c9 I; M' [' ~( x7 c - '&start_param=debug' +
3 U2 a- q" w1 l- G: w+ X2 J - '&user=%7B%22added_to_attachment_menu%22%3Afalse%2C%22allows_write_to_pm%22%3Afalse%2C%22first_name%22%3A%22user-first-name%22%2C%22id%22%3A222%2C%22is_bot%22%3Atrue%2C%22is_premium%22%3Afalse%2C%22language_code%22%3A%22en%22%2C%22last_name%22%3A%22user-last-name%22%2C%22photo_url%22%3A%22user-photo%22%2C%22username%22%3A%22user-username%22%7D' +0 [' y* ^/ s' i% f7 a: Y
- '&hash=47cfa22e72b887cba90c9cb833c5ea0f599975b6ce7193741844b5c4a4228b40'
复制代码该函数接受三个参数: - 要签名的数据:它表示经过解析的初始数据对象,不包括 authDate 和 hash 属性。
- 机器人令牌:该令牌由 [@BotFather] (https://t.me/botfather) 接收。
- 签署日期:此值将用作 authDate 属性的值。/ q+ D% i f% v- L9 ]
因此,函数会返回带符号的 init 数据。 网络加密应用程序接口如果要在 Node.js 以外的环境中使用此软件包,开发人员可以使用 web 子目录,该子目录导出的方法与上述方法相同,但会返回承诺。 - import { h. p' z- r" A0 K/ i& e( {
- validate,
2 L- w0 n: n9 ^. U% j5 E - sign,
2 n' S' M9 p! S* l% J2 U! B8 T - signData,
3 Z7 j' w0 _" |4 D" I T - isValid,1 m" h, D x) m8 |5 q# u* E+ H
- } from '@telegram-apps/init-data-node/web';' v: }( J" r( w2 I% g0 i& z
- 6 z% {+ T, h- \- ~
- await validate(...);
' k! _- d( v7 m9 t# c' J - await sign(...);
* ^* d8 ` f0 _+ n* Y ^ - await signData(...);0 s$ B8 d+ a! E9 |2 ] s; |
- await isValid(...);
复制代码 传递散列令牌 - Hashed Token所有软件包方法都允许开发人员使用散列令牌而不是原始令牌。 我们所说的 "散列令牌 "是指使用 HMAC-SHA-256 算法散列的令牌,其密钥来自 WebAppData,详见 文档的 validation 部分。 下面是一些例子: - import { validate, sign } from '@telegram-apps/init-data-node';
1 R0 i0 w& d4 Q+ }9 F8 A; S - 2 {# |3 t$ D6 |0 g2 W) @7 x
- const secretTokenHashed = 'a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f';4 m/ ]# L! e) | N
- const initData =2 W% i1 h5 w3 i' p' ~
- 'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +7 b9 I! C; O3 i$ h, F
- '&user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%7D' +
' g, m& p6 H2 w M - '&auth_date=1662771648' +
0 R" @. z; n6 O. e9 o# ? } - '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';8 A4 l/ M# V( S( d' Q2 T# @
7 R0 x1 \" A) V/ e& g+ L3 ^8 E- // Validating.
( O i- b9 w s* k8 C1 {! u0 m1 Q - validate(initData, secretTokenHashed, { tokenHashed: true });$ H, \5 g& o( ~9 p! s
/ a( S% t, N0 ^5 V" N t4 [- // Signing.
; y, _& P4 [ z' g! g1 k - sign(9 I! Z' m& h$ E
- {
" T' g) @5 J" O! S8 a; }- H - canSendAfter: 10000,
$ `9 p7 K; y j5 A$ O - chat: {
5 _9 D" _$ _' Y' P9 A. J! { - id: 1,
- v0 y6 ^3 f4 p; C" A1 G - type: 'group',1 j; {# k( H9 `6 U1 u
- username: 'my-chat',/ q9 d' y1 @! q
- title: 'chat-title',
$ H9 V, a h* l3 J, `6 q i - photoUrl: 'chat-photo',1 E0 H* m+ c% G3 a2 v- R8 i! ^5 z
- },
6 v7 V' w' ^- @1 T$ [ - chatInstance: '888',5 ~; _5 k% [) ?
- chatType: 'sender',2 o+ G) Q, Q2 J& ^9 g7 x5 r
- queryId: 'QUERY',
& S( `; E# Q% j, h- S% F2 r - receiver: {6 G& l! ~- {! T; j, S. B3 b6 E
- addedToAttachmentMenu: false,- o8 N9 f, T B/ B$ ^* |* f
- allowsWriteToPm: true,
) P0 X, k+ N9 u3 W4 X - firstName: 'receiver-first-name',
- Z$ O' ]$ q: t5 G- t1 }. | - id: 991,
7 s5 c: x& F, ?( g, Z: p* O' V* {( U - isBot: false,
4 n, g% ~" V6 Y- u - isPremium: true,% k2 t e' t0 @! k
- languageCode: 'ru',1 V. V- c2 t1 Q* H8 U5 R' l
- lastName: 'receiver-last-name',
2 l' r2 M/ A+ l; p G, _( D S - photoUrl: 'receiver-photo',7 z8 Q8 k3 Y/ n/ b) s
- username: 'receiver-username',
! J4 ^1 ?5 _+ a1 {5 e+ ? - },
( T" M; Y$ O' X+ m2 b - startParam: 'debug',2 ]0 _4 F+ g/ S& t% J4 l
- user: {
) p' p. C5 R8 @- t' c% x- R - addedToAttachmentMenu: false,
# Y& T- R# J& Q - allowsWriteToPm: false,- l' |2 J( B# j; [9 b `& U
- firstName: 'user-first-name',1 @/ c% y( X7 b% J/ Y* ^. h% z
- id: 222,6 I! q$ @/ q: N
- isBot: true,
0 ~' F. c* u, t3 ~ - isPremium: false,
' V- b8 O4 c p2 y, v; h4 f+ R - languageCode: 'en',* T% ~! O" f& o) j7 h
- lastName: 'user-last-name',
) w' S% c( F9 M" R. c - photoUrl: 'user-photo',
& |. W9 M. Z9 U6 t - username: 'user-username', |' Q% u7 U- @& U
- },
( c5 C7 u) p8 z- {- P" l6 V - },
" J0 f# T9 j2 ]8 c - secretTokenHashed,' D$ M2 z/ A. }7 z+ r: v. S
- new Date(1000),/ o( \' E2 p" B2 p7 U; |* j a: K
- { tokenHashed: true }
6 s/ D' J1 p. N* @. X - );
复制代码使用这种方法可以减少直接传递原始标记的次数。
, ~7 k8 T4 T! ~) `% M; n! B2 K- ], ~( b C: z8 F5 V
2 {- D& \/ i' u: X
' x& Q2 A; l3 r
( t* D& {' D6 K1 m
% R3 o5 F- R, G9 k. E( ~
|