English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE คนไทย Français русский

回答

收藏

Telegram 小程序 | Node @telegram-apps/init-data-node

开源社区 开源社区 7011 人阅读 | 0 人回复 | 2025-02-28

该软件包提供在 服务器端处理 Telegram 迷你应用程序初始化数据的实用程序。 要了解有关初始化数据及其用法的更多信息,请参阅 the documentation.安装
/ s+ z  g% Z! D) xpnpm :
* f9 g; ]  j  m
  1. pnpm i @telegram-apps/init-data-node
复制代码
npm :
( k; Z9 T$ ^2 d# S3 D5 n7 _
  1. npm i @telegram-apps/init-data-node
复制代码
yarn :# W+ z/ ^. {) |7 A4 j+ P
  1. yarn add @telegram-apps/init-data-node
复制代码
解析

要将一个值解析为 init 数据,请使用 parse 方法。

该方法接受以 string 或 URLSearchParams 形式呈现的 init 数据。

  1. import { parse } from '@telegram-apps/init-data-node';, H# \1 b. j! d$ W  Z. x7 Z
  2. ; a' g# B1 N' o- D$ K6 A
  3. try {
    # |6 N3 v3 I2 `; k% S- v
  4.   const initData = parse('...');  e) ]( q7 V: ?, {. H5 |; L# j
  5.   // {
    " A8 e8 o1 ?% o3 G
  6.   //   canSendAfter: 10000,
    % `3 c5 M$ V, Q2 a' G4 J) L
  7.   //   chat: {
    : B8 s0 p( j. r1 ?3 s
  8.   //     id: 1,; l/ j2 u- N' M( I9 C6 f. {* i
  9.   //     type: 'group',- G, |' l$ Z$ U, k3 F& `9 z0 E1 q; q
  10.   //     username: 'my-chat',
    7 \+ @! L: r+ F3 Y! i( n5 n
  11.   //     title: 'chat-title',
    5 h& }" E: b; Q: t
  12.   //     photoUrl: 'chat-photo',
    " P5 b7 v6 P' |3 K: v
  13.   //   },  r( K- f" |8 d
  14.   //   chatInstance: '888',) x+ I; \1 Y; R/ D! c% L3 Y" o: P, X$ K
  15.   //   chatType: 'sender',
    ; x3 u4 `' X0 s1 S" G2 A( ^% b
  16.   //   queryId: 'QUERY',
    / j0 E! j) q; p
  17.   //   receiver: {* f9 l4 B- D6 m1 R7 p
  18.   //     addedToAttachmentMenu: false,
      W7 a% K# _& c( M: Y
  19.   //     allowsWriteToPm: true,: N+ {$ d+ d2 Q$ s- ?$ n
  20.   //     firstName: 'receiver-first-name',
    3 m: u6 b- m9 A/ G
  21.   //     id: 991,
    : U  q* T5 v2 n/ L* b. k
  22.   //     isBot: false,; Y' ~/ `% V: ^" \- }
  23.   //     isPremium: true,
    - }7 p+ U! I# y7 \* \# y' B0 l
  24.   //     languageCode: 'ru',
    , i. j+ U, m) ~' ^& P; e) v: b
  25.   //     lastName: 'receiver-last-name',' r* K8 l4 J: ?/ ?: C; Y0 g
  26.   //     photoUrl: 'receiver-photo',
    8 ^/ A! C7 H* r7 X
  27.   //     username: 'receiver-username',6 H3 h5 E3 a8 P
  28.   //   },
    - |" C& t2 h$ t, C
  29.   //   startParam: 'debug',) F$ Q6 q3 s* S- H
  30.   //   user: {
    ; L8 F7 P9 H; K% f/ o9 y( z
  31.   //     addedToAttachmentMenu: false,; n1 z( h7 m1 c; b. M
  32.   //     allowsWriteToPm: false,' ^- G0 g* b. q1 [( n" W$ e- w5 h
  33.   //     firstName: 'user-first-name',
    1 _/ n( p' f' h' C2 p% I
  34.   //     id: 222,
    3 `$ ~. q4 Q: G% \- X) [
  35.   //     isBot: true,
    ' T/ p6 i, \% Q+ M5 L! ~  _
  36.   //     isPremium: false,3 L, O( {$ R7 `( L, @% j1 A9 _8 ]
  37.   //     languageCode: 'en',! i- W9 ?2 U# Q/ A) y
  38.   //     lastName: 'user-last-name',
    : x8 s) O; V* b, S
  39.   //     photoUrl: 'user-photo',
    * r& ?; R! v& X. g
  40.   //     username: 'user-username',. V6 b$ a, ]" j# Y" k) J  ^
  41.   //   },
    * A* G1 I9 L8 c# d
  42.   // }
    9 ~5 h) G* V/ Q/ x, A! v
  43. } catch (e) {
    5 G+ ^. {9 R% |! `  c% I1 s7 A
  44.   console.error('Something is wrong', e);
    7 _6 \* `8 W/ R4 `; n  \( h
  45. }
复制代码
验证validate

要验证初始化数据的签名,需要使用validate函数。 它希望 以原始格式(搜索参数)传递初始化数据,并在 某些情况下出错。

  1. import { validate } from '@telegram-apps/init-data-node';$ I( ?0 }! ?3 c' m

  2. % S% ]" c# {# s) w5 z2 T
  3. const secretToken = '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8';! M2 \+ l5 Q$ j' p! s  {( V
  4. const initData =, {! c4 x" G4 }+ {; U
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +; {7 c9 @/ e  O/ l  s& ~
  6.   '&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
  7.   '&auth_date=1662771648' +* J- s: G* H& @
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';
    7 V; ]0 {: }/ g3 K
  9. * r9 z4 k1 S! g7 p% p  M
  10. 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

以下是您可以用来检查错误类型的代码:

  1. import { validate, isErrorOfType } from '@telegram-apps/init-data-node';
    + v7 t7 v; K3 \

  2. # X" m& D, r) [+ l+ e0 d
  3. try {- K5 M0 L. a) B4 ]- [- U( \7 B
  4.   validate('init-data', 'token');
    - g- n) ^! {2 }) w' T* p
  5. } catch (e) {
    ( |2 F& u# \0 K7 f5 t9 g
  6.   if (isErrorOfType('ERR_SIGN_INVALID')) {
    . c) Y% k4 s9 @6 f, z# A' y
  7.     console.log('Sign is invalid');/ C9 \/ [) R0 y7 d, [9 L4 M
  8.   }; }1 e3 `* ?, i4 h
  9. }
复制代码

默认情况下,函数会检查初始化数据是否过期。 的默认过期时间设置为 1 天(86,400 秒)。 建议始终检查 初始化数据的有效期,因为它可能被盗但仍然有效。 要禁用此功能,将 { expiresIn: 0 } 作为第三个参数。

isValid

或者,开发人员可以使用 isValid 函数来检查初始数据的有效性。 它不会引发错误,但会返回一个布尔值,表明初始数据的有效性。

  1. import { isValid } from '@telegram-apps/init-data-node';
    * G2 T) @, J! g' @/ S2 P- C
  2. - r9 m  R0 X( h
  3. if (isValid('init-data')) {/ ^( e2 t- O' ?, e$ L
  4.   console.log('Init data is fine');0 M: j* j" D1 A
  5. }
复制代码
签名

在某些情况下,开发人员可能需要创建自己的初始数据。 例如,如果您使用 KeyboardButton 或 InlineKeyboardButton, Telegram 不会自动发送这些数据。 Telegram 无法做到这一点,因为它不知道应该使用哪个 Telegram Bot 令牌 。

要实现这一过程,需要使用 sign 方法。 下面是完整的示例:

Signing :

  1. import { sign } from '@telegram-apps/init-data-node';
    - D( y: w, S5 R% g& N3 _
  2. ( E6 o& h2 S3 g7 V9 O% V3 T& @
  3. sign(
    - J4 t& R( |7 M% A0 E2 e
  4.   {
    # h8 y  p) ?. B
  5.     canSendAfter: 10000,
    / R% {+ e5 R$ p1 K6 F- B2 D
  6.     chat: {
    4 x+ E' _; X5 I& ?
  7.       id: 1,
    " Y- x* T% C* `/ F6 {  C
  8.       type: 'group',
    " l/ [; }4 T1 G0 ~1 c" ~8 y% I
  9.       username: 'my-chat',
    ( j0 O" E: x6 K
  10.       title: 'chat-title',
    9 ~5 [% x- Q$ M3 B1 Z
  11.       photoUrl: 'chat-photo',( G6 y9 R, g6 y  }  e& G
  12.     },. ~" f7 ~) `, M% X" f) w) Q
  13.     chatInstance: '888',( y1 o6 \# G% m) L/ H& x& D
  14.     chatType: 'sender',8 U4 s7 e+ f/ W: X
  15.     queryId: 'QUERY',- U4 B& U7 a' v
  16.     receiver: {2 l/ f1 \1 u) i2 e' o' z4 X
  17.       addedToAttachmentMenu: false,6 }+ R3 b9 Q7 N/ q5 ?5 A- n
  18.       allowsWriteToPm: true,  a7 Q* ]+ ^% f* D" y: f1 L5 @0 C
  19.       firstName: 'receiver-first-name',
    8 T  d$ K$ c0 m) j
  20.       id: 991,6 K( u% f: O9 }; d* |4 b
  21.       isBot: false,/ b1 c0 a) i! S. }. }' i7 n' ]( B
  22.       isPremium: true,' P3 _, q7 C! O$ I9 I
  23.       languageCode: 'ru',1 j0 F5 f+ o/ _+ ^, }* Q, Y' j
  24.       lastName: 'receiver-last-name',
    ' P- E' d) L8 t9 ~" l1 b2 y
  25.       photoUrl: 'receiver-photo',3 }7 P7 f9 {4 p: K  s( C* n
  26.       username: 'receiver-username',
    ( G9 K. Q' @% u( `6 M0 f+ ]
  27.     },( t  p' L" z. ~6 I% I0 E0 h$ U" C6 L
  28.     startParam: 'debug',
    - L- i9 _# \8 X9 m. y/ u4 e
  29.     user: {0 M  d! S" O+ }4 L
  30.       addedToAttachmentMenu: false,
    , Y' X) z! g0 |/ H  D. h3 l+ c
  31.       allowsWriteToPm: false,! }8 }. |- W  D: I
  32.       firstName: 'user-first-name',
    / v: X& ]  o! Y: d3 S
  33.       id: 222,
    : U+ ~% v3 @9 W9 B& _
  34.       isBot: true,
    2 e" O& U5 k9 u  w; `- J
  35.       isPremium: false,- D+ E+ S# I9 D0 I! `) n% A0 x
  36.       languageCode: 'en',$ |) c5 ^  I2 y! t# ~, i4 x+ @# T; ~
  37.       lastName: 'user-last-name',
    , M# {/ v/ h! h8 y* A
  38.       photoUrl: 'user-photo',
    " W; g4 N" @7 g3 f
  39.       username: 'user-username',6 R# ]% W9 g/ l7 Q5 K9 p0 r
  40.     },
    / z( ~- t4 T* Y8 k
  41.   },
    $ g4 [+ _- o' {" }0 |
  42.   '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8',1 Y, Z: z  @. n& h( m" X$ ]
  43.   new Date(1000),6 X. ~" m/ {3 b7 I
  44. );
复制代码

Expected Result :

  1. 'auth_date=1' +
    7 j5 d9 b4 E* F( K
  2. '&can_send_after=10000' +
    5 J; D$ ~" j# b2 ~( C
  3. '&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
  4. '&chat_instance=888' +% ?' `( W7 C/ g2 ^$ P; e4 F
  5. '&chat_type=sender' +' l# y# X  R$ _
  6. '&query_id=QUERY' +
    ! N) @+ ?4 j$ _5 v
  7. '&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
  8. '&start_param=debug' +
    3 U2 a- q" w1 l- G: w+ X2 J
  9. '&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
  10. '&hash=47cfa22e72b887cba90c9cb833c5ea0f599975b6ce7193741844b5c4a4228b40'
复制代码

该函数接受三个参数:

  • 要签名的数据:它表示经过解析的初始数据对象,不包括 authDate 和 hash 属性。
  • 机器人令牌:该令牌由 [@BotFather] (https://t.me/botfather) 接收。
  • 签署日期:此值将用作 authDate 属性的值。/ q+ D% i  f% v- L9 ]

因此,函数会返回带符号的 init 数据。

网络加密应用程序接口

如果要在 Node.js 以外的环境中使用此软件包,开发人员可以使用 web 子目录,该子目录导出的方法与上述方法相同,但会返回承诺。

  1. import {  h. p' z- r" A0 K/ i& e( {
  2.   validate,
    2 L- w0 n: n9 ^. U% j5 E
  3.   sign,
    2 n' S' M9 p! S* l% J2 U! B8 T
  4.   signData,
    3 Z7 j' w0 _" |4 D" I  T
  5.   isValid,1 m" h, D  x) m8 |5 q# u* E+ H
  6. } from '@telegram-apps/init-data-node/web';' v: }( J" r( w2 I% g0 i& z
  7. 6 z% {+ T, h- \- ~
  8. await validate(...);
    ' k! _- d( v7 m9 t# c' J
  9. await sign(...);
    * ^* d8 `  f0 _+ n* Y  ^
  10. await signData(...);0 s$ B8 d+ a! E9 |2 ]  s; |
  11. await isValid(...);
复制代码
传递散列令牌 - Hashed Token

所有软件包方法都允许开发人员使用散列令牌而不是原始令牌。

我们所说的 "散列令牌 "是指使用 HMAC-SHA-256 算法散列的令牌,其密钥来自 WebAppData,详见 文档的 validation 部分。

下面是一些例子:

  1. import { validate, sign } from '@telegram-apps/init-data-node';
    1 R0 i0 w& d4 Q+ }9 F8 A; S
  2. 2 {# |3 t$ D6 |0 g2 W) @7 x
  3. const secretTokenHashed = 'a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f';4 m/ ]# L! e) |  N
  4. const initData =2 W% i1 h5 w3 i' p' ~
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +7 b9 I! C; O3 i$ h, F
  6.   '&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
  7.   '&auth_date=1662771648' +
    0 R" @. z; n6 O. e9 o# ?  }
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';8 A4 l/ M# V( S( d' Q2 T# @

  9. 7 R0 x1 \" A) V/ e& g+ L3 ^8 E
  10. // Validating.
    ( O  i- b9 w  s* k8 C1 {! u0 m1 Q
  11. validate(initData, secretTokenHashed, { tokenHashed: true });$ H, \5 g& o( ~9 p! s

  12. / a( S% t, N0 ^5 V" N  t4 [
  13. // Signing.
    ; y, _& P4 [  z' g! g1 k
  14. sign(9 I! Z' m& h$ E
  15.   {
    " T' g) @5 J" O! S8 a; }- H
  16.     canSendAfter: 10000,
    $ `9 p7 K; y  j5 A$ O
  17.     chat: {
    5 _9 D" _$ _' Y' P9 A. J! {
  18.       id: 1,
    - v0 y6 ^3 f4 p; C" A1 G
  19.       type: 'group',1 j; {# k( H9 `6 U1 u
  20.       username: 'my-chat',/ q9 d' y1 @! q
  21.       title: 'chat-title',
    $ H9 V, a  h* l3 J, `6 q  i
  22.       photoUrl: 'chat-photo',1 E0 H* m+ c% G3 a2 v- R8 i! ^5 z
  23.     },
    6 v7 V' w' ^- @1 T$ [
  24.     chatInstance: '888',5 ~; _5 k% [) ?
  25.     chatType: 'sender',2 o+ G) Q, Q2 J& ^9 g7 x5 r
  26.     queryId: 'QUERY',
    & S( `; E# Q% j, h- S% F2 r
  27.     receiver: {6 G& l! ~- {! T; j, S. B3 b6 E
  28.       addedToAttachmentMenu: false,- o8 N9 f, T  B/ B$ ^* |* f
  29.       allowsWriteToPm: true,
    ) P0 X, k+ N9 u3 W4 X
  30.       firstName: 'receiver-first-name',
    - Z$ O' ]$ q: t5 G- t1 }. |
  31.       id: 991,
    7 s5 c: x& F, ?( g, Z: p* O' V* {( U
  32.       isBot: false,
    4 n, g% ~" V6 Y- u
  33.       isPremium: true,% k2 t  e' t0 @! k
  34.       languageCode: 'ru',1 V. V- c2 t1 Q* H8 U5 R' l
  35.       lastName: 'receiver-last-name',
    2 l' r2 M/ A+ l; p  G, _( D  S
  36.       photoUrl: 'receiver-photo',7 z8 Q8 k3 Y/ n/ b) s
  37.       username: 'receiver-username',
    ! J4 ^1 ?5 _+ a1 {5 e+ ?
  38.     },
    ( T" M; Y$ O' X+ m2 b
  39.     startParam: 'debug',2 ]0 _4 F+ g/ S& t% J4 l
  40.     user: {
    ) p' p. C5 R8 @- t' c% x- R
  41.       addedToAttachmentMenu: false,
    # Y& T- R# J& Q
  42.       allowsWriteToPm: false,- l' |2 J( B# j; [9 b  `& U
  43.       firstName: 'user-first-name',1 @/ c% y( X7 b% J/ Y* ^. h% z
  44.       id: 222,6 I! q$ @/ q: N
  45.       isBot: true,
    0 ~' F. c* u, t3 ~
  46.       isPremium: false,
    ' V- b8 O4 c  p2 y, v; h4 f+ R
  47.       languageCode: 'en',* T% ~! O" f& o) j7 h
  48.       lastName: 'user-last-name',
    ) w' S% c( F9 M" R. c
  49.       photoUrl: 'user-photo',
    & |. W9 M. Z9 U6 t
  50.       username: 'user-username',  |' Q% u7 U- @& U
  51.     },
    ( c5 C7 u) p8 z- {- P" l6 V
  52.   },
    " J0 f# T9 j2 ]8 c
  53.   secretTokenHashed,' D$ M2 z/ A. }7 z+ r: v. S
  54.   new Date(1000),/ o( \' E2 p" B2 p7 U; |* j  a: K
  55.   { tokenHashed: true }
    6 s/ D' J1 p. N* @. X
  56. );
复制代码

使用这种方法可以减少直接传递原始标记的次数。


, ~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( ~
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则