该软件包提供在 服务器端处理 Telegram 迷你应用程序初始化数据的实用程序。 要了解有关初始化数据及其用法的更多信息,请参阅 the documentation.安装+ E" F- D2 E6 q5 N
pnpm :+ X; ?* A2 T1 |+ J I, c/ g2 I
- pnpm i @telegram-apps/init-data-node
复制代码 npm :
* G& x- E" w1 \4 ?' R2 g$ h- npm i @telegram-apps/init-data-node
复制代码 yarn :
$ H: ?0 n! T+ j3 i. n4 u) Z0 n- yarn add @telegram-apps/init-data-node
复制代码 解析要将一个值解析为 init 数据,请使用 parse 方法。 该方法接受以 string 或 URLSearchParams 形式呈现的 init 数据。 - import { parse } from '@telegram-apps/init-data-node';1 g# f( {9 g* U1 U$ e( u
- ( e7 }1 a4 y: Y
- try {0 R# f+ ]3 t6 Z; b
- const initData = parse('...');
: H: ]1 l7 E' h+ h - // {3 p% g E4 Y5 C$ Z" i
- // canSendAfter: 10000,2 z0 C9 @5 b3 Y
- // chat: {: P$ D+ U7 k2 A
- // id: 1,
* a- G% ]! Q$ c9 e) p - // type: 'group',
& r. i4 z" W7 D; K - // username: 'my-chat',% M; Z2 n9 T* G: Z( v" n8 s
- // title: 'chat-title',0 x7 _& P& L" i: V
- // photoUrl: 'chat-photo',
! ?) a$ U* A. W. {" j& } - // },( s. A+ {6 B' Y/ \* J3 E$ U9 v$ W
- // chatInstance: '888',1 r- |6 I" J7 ?! `! E
- // chatType: 'sender',
% Q( Q, n& ]0 h - // queryId: 'QUERY',
7 I7 \) `- _+ a: _ - // receiver: {0 P5 l+ Y* K# P# m* Z
- // addedToAttachmentMenu: false,& ~& a! t& J3 a; {) h
- // allowsWriteToPm: true,! @5 ^* T; ^- i0 u' H5 z
- // firstName: 'receiver-first-name',7 D" O+ N0 ]* k
- // id: 991,
0 C* g; L1 c7 P# `5 X3 C$ ?: Y8 `6 A2 h - // isBot: false,# m. C: T9 U3 P" r1 H6 f% ?
- // isPremium: true,: k9 Y& u+ o4 ]; w+ y3 O
- // languageCode: 'ru',& c4 S" {$ J) k& v8 q% L+ ~
- // lastName: 'receiver-last-name',( J9 c6 q% T! ^* G
- // photoUrl: 'receiver-photo',
2 B1 i/ o8 G [5 c; B - // username: 'receiver-username',
3 b+ g2 r- D% \: P7 U( ^4 e( F - // },- U: E* N3 ~8 {3 b/ s" w e
- // startParam: 'debug',
0 J: s8 \6 ?# h. M3 l0 ?6 [ - // user: {
) j: Q6 l& A' Z - // addedToAttachmentMenu: false,
) \$ j3 a; z9 D! v9 K1 v- g' w5 r - // allowsWriteToPm: false,
9 B% t0 e: U- C# K$ a' H6 n - // firstName: 'user-first-name',3 ~2 T7 X s2 m4 }# [+ ]. E/ I% V
- // id: 222,% _& q1 J7 N* n4 z& ?$ k/ P8 `
- // isBot: true,. K( F5 Y( Z; b+ J) C
- // isPremium: false, i( z% G' `& u; a4 E( L
- // languageCode: 'en',: x+ s$ ~& d6 I- l ?
- // lastName: 'user-last-name',5 B! a* O& q5 M
- // photoUrl: 'user-photo',
9 \: c8 U5 m |7 ]. k' @! V, V - // username: 'user-username',; m& A2 N5 F( U* u
- // },% W9 W/ a+ `0 m" H2 O
- // }
$ f6 J+ `7 X6 v% R" @( i4 [ - } catch (e) { ^3 N8 g, ^4 y3 J2 R3 N- o1 k' @
- console.error('Something is wrong', e);$ Q& E$ u: q- W5 {# V
- }
复制代码 验证validate要验证初始化数据的签名,需要使用validate函数。 它希望 以原始格式(搜索参数)传递初始化数据,并在 某些情况下出错。 - import { validate } from '@telegram-apps/init-data-node';
_8 `: F9 D2 w. S - 3 O, \8 B* l7 N/ c9 _
- const secretToken = '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8';
2 t0 }, `) g8 k! S- ~) B - const initData =$ o( C7 }; e6 ~
- 'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +
6 G9 c }* z8 i& w* v - '&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' +" ?* e2 i$ o5 Z7 U6 \
- '&auth_date=1662771648' +
% z3 s: g+ g+ M+ L5 @ Y5 b+ r - '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';/ _! Z R# ?) v1 z
( j' I5 O s. r/ ?2 g: C7 S- validate(initData, secretToken);
复制代码函数会在其中一种情况下出错: - ERR_AUTH_DATE_INVALID:auth_date 为空或未找到
- ERR_HASH_INVALID:hash 为空或未找到
- ERR_SIGN_INVALID:签名无效
- ERR_EXPIRED:初始数据已过期
, z) R! \3 ^5 h3 w
以下是您可以用来检查错误类型的代码: - import { validate, isErrorOfType } from '@telegram-apps/init-data-node';
6 d. H& l6 o, h* u! ~7 \ - 8 h; m& C8 R' j1 V4 v+ A, R$ g% W
- try {
" l5 P# p( k) x - validate('init-data', 'token');$ G' j) z2 i! |1 o
- } catch (e) {
7 u' p2 F# t! z- P1 o! u3 M& u8 J% M - if (isErrorOfType('ERR_SIGN_INVALID')) {
$ g: W8 [! ~* H8 P/ I3 O/ N - console.log('Sign is invalid');
L, ^/ y2 f: c( J0 E) y) C: m2 W& e; L - }
0 k% ^' i7 s8 J7 j: B - }
复制代码默认情况下,函数会检查初始化数据是否过期。 的默认过期时间设置为 1 天(86,400 秒)。 建议始终检查 初始化数据的有效期,因为它可能被盗但仍然有效。 要禁用此功能,将 { expiresIn: 0 } 作为第三个参数。 isValid或者,开发人员可以使用 isValid 函数来检查初始数据的有效性。 它不会引发错误,但会返回一个布尔值,表明初始数据的有效性。 - import { isValid } from '@telegram-apps/init-data-node';( f- N1 |( w3 l
& f% t* P6 |/ R2 L B/ k- if (isValid('init-data')) {. C5 C/ L/ i; T& W: d0 F
- console.log('Init data is fine');
" x) K' `3 s+ p) j* C/ e - }
复制代码 签名在某些情况下,开发人员可能需要创建自己的初始数据。 例如,如果您使用 KeyboardButton 或 InlineKeyboardButton, Telegram 不会自动发送这些数据。 Telegram 无法做到这一点,因为它不知道应该使用哪个 Telegram Bot 令牌 。 要实现这一过程,需要使用 sign 方法。 下面是完整的示例: Signing : - import { sign } from '@telegram-apps/init-data-node';: u! C% \9 e' E) r5 N
- 1 f1 [" @8 m5 @ I- N" M' |
- sign(1 H k) Z: L) E" M" l
- {$ \! S* D+ F. S$ B L% K& \- Q
- canSendAfter: 10000,
/ S& H3 Z- V( n+ [ - chat: {
' t9 F8 ]+ |# | ^ - id: 1,
: D- C: C* n/ V4 `0 |! m, l0 V# E9 ]5 @ - type: 'group',: n4 K7 P" t' ^! t" t0 P
- username: 'my-chat',) |2 r2 d0 ~+ N
- title: 'chat-title',! P( p! z# j% _% b6 N
- photoUrl: 'chat-photo',4 x3 l4 g5 C( |8 z3 H
- }, _8 Y0 J1 u$ n% ]
- chatInstance: '888',
- K7 q/ V+ Z" f; {" f - chatType: 'sender',5 G, r9 I2 h9 j; }, c, K$ d2 I
- queryId: 'QUERY',- C3 n- p, h5 |: `, K$ z6 z# x3 q
- receiver: {
' x6 E1 U: t8 N* l& S7 [ - addedToAttachmentMenu: false,
; |. P% I$ `$ j+ Q; k; \ - allowsWriteToPm: true,: g1 i0 ~$ ]# D. B7 E& M! N9 w, k
- firstName: 'receiver-first-name',8 r! n& t" s. E" p& x& j
- id: 991,; f$ n( m4 I& x, J+ c/ l6 ~! S5 a
- isBot: false,, a% E! \5 |$ j& G: v
- isPremium: true,5 }9 S2 k9 f& i( j. v
- languageCode: 'ru',2 ^) X7 b) y! J- h( S2 ]4 k( D( U
- lastName: 'receiver-last-name',- P; j+ O+ J+ ~' C8 X
- photoUrl: 'receiver-photo',9 } _4 N/ q& H) `" u' x4 N
- username: 'receiver-username',4 R$ S! l+ T! G" H& F
- }," @- c/ D" `# o0 E) j! ? N+ i
- startParam: 'debug',
& I9 L$ K8 S* v4 a5 e- z - user: {, L3 l' b/ @& {# k0 i$ y
- addedToAttachmentMenu: false," A1 Y" E# H! X9 W. H
- allowsWriteToPm: false,3 a7 `6 h: a# z! m5 Y8 u: @
- firstName: 'user-first-name',
Y. b$ p( h# M# m& l; O4 K7 v - id: 222,
% G8 b2 _" s" S; Z - isBot: true,
! u& z. B& d0 N9 O8 u J - isPremium: false,
1 X/ d% k* V; R+ a* e: [. p) ^6 v3 C - languageCode: 'en', D1 L* m+ M& m5 y; O3 O9 G' s9 B
- lastName: 'user-last-name',
( F/ f% n& ^/ X R5 P: a - photoUrl: 'user-photo',0 O7 L/ h4 ~' n) t( z
- username: 'user-username',
1 e! z5 j& v, T" N1 T7 @! { - },9 ^5 d( g1 D8 H0 n6 k `9 j
- },8 k, U0 S( t7 w9 r, n+ o
- '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8',4 s# l9 n. m, R$ k: ], X5 b4 G
- new Date(1000),
; _6 {! l: d8 {2 N3 { U. H: u - );
复制代码Expected Result : - 'auth_date=1' +
% @9 A7 s. S0 i S' H. k0 { - '&can_send_after=10000' +
( I$ c8 Q7 G+ g% g5 o$ z/ U - '&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' +
. s2 F- ^: O7 w3 Z; M - '&chat_instance=888' +" p: A) i" H* b% z6 ~5 N0 Z5 q
- '&chat_type=sender' +) M- T7 \% ?# g
- '&query_id=QUERY' +) W! V9 y) C- D U5 I" F2 z
- '&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' +
: h: }2 a Y7 |0 K. g - '&start_param=debug' +: G6 A! K( B6 `2 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' +
8 r$ G" U6 x4 i6 T - '&hash=47cfa22e72b887cba90c9cb833c5ea0f599975b6ce7193741844b5c4a4228b40'
复制代码该函数接受三个参数: - 要签名的数据:它表示经过解析的初始数据对象,不包括 authDate 和 hash 属性。
- 机器人令牌:该令牌由 [@BotFather] (https://t.me/botfather) 接收。
- 签署日期:此值将用作 authDate 属性的值。
& I$ _8 k1 E4 O
因此,函数会返回带符号的 init 数据。 网络加密应用程序接口如果要在 Node.js 以外的环境中使用此软件包,开发人员可以使用 web 子目录,该子目录导出的方法与上述方法相同,但会返回承诺。 - import {
8 \" f. t" W7 N - validate,
$ j6 u; I# e4 v& |9 D9 y - sign,
) U4 f5 ]+ }0 t. S4 q - signData,( L/ P+ T* O# K; i- X+ s) P8 P
- isValid,
9 v1 c. z6 z; Q - } from '@telegram-apps/init-data-node/web';/ o6 w1 g1 E' I) {# e! }
- 0 S1 m* J8 p K" g6 ?3 k1 J
- await validate(...);
& P8 c1 Z, j+ U+ A7 ? - await sign(...);3 b/ }+ j: i/ W4 H# t) ?/ H
- await signData(...);7 O& d( Y+ }, Y4 L# E5 z
- await isValid(...);
复制代码 传递散列令牌 - Hashed Token所有软件包方法都允许开发人员使用散列令牌而不是原始令牌。 我们所说的 "散列令牌 "是指使用 HMAC-SHA-256 算法散列的令牌,其密钥来自 WebAppData,详见 文档的 validation 部分。 下面是一些例子: - import { validate, sign } from '@telegram-apps/init-data-node';/ T7 g6 g4 [: I, X' Y2 c. z; s/ Z7 M
$ v D6 P: @, @2 o- const secretTokenHashed = 'a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f';% s' L2 \: \$ Y9 K2 O" V
- const initData =1 r! F5 M9 R X5 ^9 H, J
- 'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +2 k, Q, g' R1 X$ w
- '&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* V; N3 x6 d$ l* d
- '&auth_date=1662771648' ++ b( c" b! O" k6 Q' u! ]+ p
- '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';
9 C8 Y; ?6 E4 q# H
+ O/ u$ C! n9 B: u2 Q" i" p$ j- // Validating. X: c) [7 Y8 {" W; g8 l. Y/ n
- validate(initData, secretTokenHashed, { tokenHashed: true });
( y4 g( F% c* s4 j& K
2 f) Y: y) z5 G/ \9 V) B- // Signing.
, t$ L' |8 W/ `" B9 w+ @ - sign(4 I& F3 G5 i, Z6 }
- {
5 [. y. O+ e b$ R9 m - canSendAfter: 10000, Z6 S/ H- |" t% r
- chat: {+ H0 b0 N6 p! q! P
- id: 1,
) s3 S/ D7 g) @; V! F4 s - type: 'group',/ B4 N/ a. K& o8 u! s/ E/ j4 h# c
- username: 'my-chat', N% @. x5 n6 `6 W
- title: 'chat-title',
' H1 z: Y3 H5 s% r - photoUrl: 'chat-photo',+ s- `1 _5 ?" @" d" c# h
- },# i: `8 j& l1 x
- chatInstance: '888',: G9 N; u* A L3 L) [9 ]
- chatType: 'sender',
- E; K6 L6 P3 k+ e6 X - queryId: 'QUERY',, g) e& O% \8 ]$ A
- receiver: {( t' f" z2 a& u2 C) P
- addedToAttachmentMenu: false,/ k r' C4 B4 J' Y+ Y2 m7 e# R
- allowsWriteToPm: true,$ a2 c5 `% m0 j0 n4 n
- firstName: 'receiver-first-name',7 x: r( R6 Q' U* j/ X6 C4 H
- id: 991,
\% G- V; t6 m4 _8 w, G - isBot: false,; |4 O& }: W8 W: Y/ C: Z; f9 B/ f
- isPremium: true,8 E9 z& k2 y i# S; N( l) x
- languageCode: 'ru',4 B9 Z7 C; D0 ^3 i" B
- lastName: 'receiver-last-name',% \1 B0 C6 J% e% o' [/ p
- photoUrl: 'receiver-photo',
1 Z1 m. i8 S+ d+ {1 _2 o - username: 'receiver-username',
9 ?8 N1 |7 F$ u7 [6 V5 S - },! y( i9 O5 }0 k2 P' z0 ?0 v7 n& q
- startParam: 'debug',
3 D( g) J1 F8 ]+ K: s( X- `& I - user: {* ]' E. x! f: E9 d; w
- addedToAttachmentMenu: false,
! A3 H2 Z2 h% P4 A0 o$ p% b - allowsWriteToPm: false,
0 m! Q2 H/ }- I+ V2 H9 b - firstName: 'user-first-name',
" G% F" W' Z; X - id: 222,0 K7 q/ Z0 x5 B* Z2 I
- isBot: true,
$ U% c4 J8 ^: X$ c+ q! i - isPremium: false,7 K4 h9 A# D/ ]" h
- languageCode: 'en',
7 y5 M+ @9 C Y, f2 V8 k7 z - lastName: 'user-last-name',
- y: d2 d& W) f' [/ f8 K - photoUrl: 'user-photo',1 [1 @& `$ o& k, i% C4 j
- username: 'user-username',
: f- D3 H u- Y2 a9 x5 n - },
4 c I% X( j( u8 c7 C; C+ x - },
( M7 P" W2 ]8 h- p2 H+ L. E - secretTokenHashed,1 p- n- Z0 u$ W4 I! h
- new Date(1000),
# a4 w& C' v5 l+ q - { tokenHashed: true }
; z: w4 g0 \4 `! h" F: o2 C+ f - );
复制代码使用这种方法可以减少直接传递原始标记的次数。 . M9 M( K; o3 v- M9 ^+ N$ x* M
. J, R9 d# C d; D" k" L) S4 W( U5 F' F6 z
, M: p4 w8 I& l; a" {( C& b
7 G" B0 w, m; J/ e$ K$ }& a t7 @+ G( T9 Z' f Q6 {- u2 B
|