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

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

回答

收藏

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

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

该软件包提供在 服务器端处理 Telegram 迷你应用程序初始化数据的实用程序。 要了解有关初始化数据及其用法的更多信息,请参阅 the documentation.安装+ E" F- D2 E6 q5 N
pnpm :+ X; ?* A2 T1 |+ J  I, c/ g2 I
  1. pnpm i @telegram-apps/init-data-node
复制代码
npm :
* G& x- E" w1 \4 ?' R2 g$ h
  1. npm i @telegram-apps/init-data-node
复制代码
yarn :
$ H: ?0 n! T+ j3 i. n4 u) Z0 n
  1. yarn add @telegram-apps/init-data-node
复制代码
解析

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

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

  1. import { parse } from '@telegram-apps/init-data-node';1 g# f( {9 g* U1 U$ e( u
  2. ( e7 }1 a4 y: Y
  3. try {0 R# f+ ]3 t6 Z; b
  4.   const initData = parse('...');
    : H: ]1 l7 E' h+ h
  5.   // {3 p% g  E4 Y5 C$ Z" i
  6.   //   canSendAfter: 10000,2 z0 C9 @5 b3 Y
  7.   //   chat: {: P$ D+ U7 k2 A
  8.   //     id: 1,
    * a- G% ]! Q$ c9 e) p
  9.   //     type: 'group',
    & r. i4 z" W7 D; K
  10.   //     username: 'my-chat',% M; Z2 n9 T* G: Z( v" n8 s
  11.   //     title: 'chat-title',0 x7 _& P& L" i: V
  12.   //     photoUrl: 'chat-photo',
    ! ?) a$ U* A. W. {" j& }
  13.   //   },( s. A+ {6 B' Y/ \* J3 E$ U9 v$ W
  14.   //   chatInstance: '888',1 r- |6 I" J7 ?! `! E
  15.   //   chatType: 'sender',
    % Q( Q, n& ]0 h
  16.   //   queryId: 'QUERY',
    7 I7 \) `- _+ a: _
  17.   //   receiver: {0 P5 l+ Y* K# P# m* Z
  18.   //     addedToAttachmentMenu: false,& ~& a! t& J3 a; {) h
  19.   //     allowsWriteToPm: true,! @5 ^* T; ^- i0 u' H5 z
  20.   //     firstName: 'receiver-first-name',7 D" O+ N0 ]* k
  21.   //     id: 991,
    0 C* g; L1 c7 P# `5 X3 C$ ?: Y8 `6 A2 h
  22.   //     isBot: false,# m. C: T9 U3 P" r1 H6 f% ?
  23.   //     isPremium: true,: k9 Y& u+ o4 ]; w+ y3 O
  24.   //     languageCode: 'ru',& c4 S" {$ J) k& v8 q% L+ ~
  25.   //     lastName: 'receiver-last-name',( J9 c6 q% T! ^* G
  26.   //     photoUrl: 'receiver-photo',
    2 B1 i/ o8 G  [5 c; B
  27.   //     username: 'receiver-username',
    3 b+ g2 r- D% \: P7 U( ^4 e( F
  28.   //   },- U: E* N3 ~8 {3 b/ s" w  e
  29.   //   startParam: 'debug',
    0 J: s8 \6 ?# h. M3 l0 ?6 [
  30.   //   user: {
    ) j: Q6 l& A' Z
  31.   //     addedToAttachmentMenu: false,
    ) \$ j3 a; z9 D! v9 K1 v- g' w5 r
  32.   //     allowsWriteToPm: false,
    9 B% t0 e: U- C# K$ a' H6 n
  33.   //     firstName: 'user-first-name',3 ~2 T7 X  s2 m4 }# [+ ]. E/ I% V
  34.   //     id: 222,% _& q1 J7 N* n4 z& ?$ k/ P8 `
  35.   //     isBot: true,. K( F5 Y( Z; b+ J) C
  36.   //     isPremium: false,  i( z% G' `& u; a4 E( L
  37.   //     languageCode: 'en',: x+ s$ ~& d6 I- l  ?
  38.   //     lastName: 'user-last-name',5 B! a* O& q5 M
  39.   //     photoUrl: 'user-photo',
    9 \: c8 U5 m  |7 ]. k' @! V, V
  40.   //     username: 'user-username',; m& A2 N5 F( U* u
  41.   //   },% W9 W/ a+ `0 m" H2 O
  42.   // }
    $ f6 J+ `7 X6 v% R" @( i4 [
  43. } catch (e) {  ^3 N8 g, ^4 y3 J2 R3 N- o1 k' @
  44.   console.error('Something is wrong', e);$ Q& E$ u: q- W5 {# V
  45. }
复制代码
验证validate

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

  1. import { validate } from '@telegram-apps/init-data-node';
      _8 `: F9 D2 w. S
  2. 3 O, \8 B* l7 N/ c9 _
  3. const secretToken = '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8';
    2 t0 }, `) g8 k! S- ~) B
  4. const initData =$ o( C7 }; e6 ~
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +
    6 G9 c  }* z8 i& w* v
  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' +" ?* e2 i$ o5 Z7 U6 \
  7.   '&auth_date=1662771648' +
    % z3 s: g+ g+ M+ L5 @  Y5 b+ r
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';/ _! Z  R# ?) v1 z

  9. ( j' I5 O  s. r/ ?2 g: C7 S
  10. validate(initData, secretToken);
复制代码

函数会在其中一种情况下出错:

  • ERR_AUTH_DATE_INVALID:auth_date 为空或未找到
  • ERR_HASH_INVALID:hash 为空或未找到
  • ERR_SIGN_INVALID:签名无效
  • ERR_EXPIRED:初始数据已过期
    , z) R! \3 ^5 h3 w

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

  1. import { validate, isErrorOfType } from '@telegram-apps/init-data-node';
    6 d. H& l6 o, h* u! ~7 \
  2. 8 h; m& C8 R' j1 V4 v+ A, R$ g% W
  3. try {
    " l5 P# p( k) x
  4.   validate('init-data', 'token');$ G' j) z2 i! |1 o
  5. } catch (e) {
    7 u' p2 F# t! z- P1 o! u3 M& u8 J% M
  6.   if (isErrorOfType('ERR_SIGN_INVALID')) {
    $ g: W8 [! ~* H8 P/ I3 O/ N
  7.     console.log('Sign is invalid');
      L, ^/ y2 f: c( J0 E) y) C: m2 W& e; L
  8.   }
    0 k% ^' i7 s8 J7 j: B
  9. }
复制代码

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

isValid

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

  1. import { isValid } from '@telegram-apps/init-data-node';( f- N1 |( w3 l

  2. & f% t* P6 |/ R2 L  B/ k
  3. if (isValid('init-data')) {. C5 C/ L/ i; T& W: d0 F
  4.   console.log('Init data is fine');
    " x) K' `3 s+ p) j* C/ e
  5. }
复制代码
签名

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

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

Signing :

  1. import { sign } from '@telegram-apps/init-data-node';: u! C% \9 e' E) r5 N
  2. 1 f1 [" @8 m5 @  I- N" M' |
  3. sign(1 H  k) Z: L) E" M" l
  4.   {$ \! S* D+ F. S$ B  L% K& \- Q
  5.     canSendAfter: 10000,
    / S& H3 Z- V( n+ [
  6.     chat: {
    ' t9 F8 ]+ |# |  ^
  7.       id: 1,
    : D- C: C* n/ V4 `0 |! m, l0 V# E9 ]5 @
  8.       type: 'group',: n4 K7 P" t' ^! t" t0 P
  9.       username: 'my-chat',) |2 r2 d0 ~+ N
  10.       title: 'chat-title',! P( p! z# j% _% b6 N
  11.       photoUrl: 'chat-photo',4 x3 l4 g5 C( |8 z3 H
  12.     },  _8 Y0 J1 u$ n% ]
  13.     chatInstance: '888',
    - K7 q/ V+ Z" f; {" f
  14.     chatType: 'sender',5 G, r9 I2 h9 j; }, c, K$ d2 I
  15.     queryId: 'QUERY',- C3 n- p, h5 |: `, K$ z6 z# x3 q
  16.     receiver: {
    ' x6 E1 U: t8 N* l& S7 [
  17.       addedToAttachmentMenu: false,
    ; |. P% I$ `$ j+ Q; k; \
  18.       allowsWriteToPm: true,: g1 i0 ~$ ]# D. B7 E& M! N9 w, k
  19.       firstName: 'receiver-first-name',8 r! n& t" s. E" p& x& j
  20.       id: 991,; f$ n( m4 I& x, J+ c/ l6 ~! S5 a
  21.       isBot: false,, a% E! \5 |$ j& G: v
  22.       isPremium: true,5 }9 S2 k9 f& i( j. v
  23.       languageCode: 'ru',2 ^) X7 b) y! J- h( S2 ]4 k( D( U
  24.       lastName: 'receiver-last-name',- P; j+ O+ J+ ~' C8 X
  25.       photoUrl: 'receiver-photo',9 }  _4 N/ q& H) `" u' x4 N
  26.       username: 'receiver-username',4 R$ S! l+ T! G" H& F
  27.     }," @- c/ D" `# o0 E) j! ?  N+ i
  28.     startParam: 'debug',
    & I9 L$ K8 S* v4 a5 e- z
  29.     user: {, L3 l' b/ @& {# k0 i$ y
  30.       addedToAttachmentMenu: false," A1 Y" E# H! X9 W. H
  31.       allowsWriteToPm: false,3 a7 `6 h: a# z! m5 Y8 u: @
  32.       firstName: 'user-first-name',
      Y. b$ p( h# M# m& l; O4 K7 v
  33.       id: 222,
    % G8 b2 _" s" S; Z
  34.       isBot: true,
    ! u& z. B& d0 N9 O8 u  J
  35.       isPremium: false,
    1 X/ d% k* V; R+ a* e: [. p) ^6 v3 C
  36.       languageCode: 'en',  D1 L* m+ M& m5 y; O3 O9 G' s9 B
  37.       lastName: 'user-last-name',
    ( F/ f% n& ^/ X  R5 P: a
  38.       photoUrl: 'user-photo',0 O7 L/ h4 ~' n) t( z
  39.       username: 'user-username',
    1 e! z5 j& v, T" N1 T7 @! {
  40.     },9 ^5 d( g1 D8 H0 n6 k  `9 j
  41.   },8 k, U0 S( t7 w9 r, n+ o
  42.   '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8',4 s# l9 n. m, R$ k: ], X5 b4 G
  43.   new Date(1000),
    ; _6 {! l: d8 {2 N3 {  U. H: u
  44. );
复制代码

Expected Result :

  1. 'auth_date=1' +
    % @9 A7 s. S0 i  S' H. k0 {
  2. '&can_send_after=10000' +
    ( I$ c8 Q7 G+ g% g5 o$ z/ U
  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' +
    . s2 F- ^: O7 w3 Z; M
  4. '&chat_instance=888' +" p: A) i" H* b% z6 ~5 N0 Z5 q
  5. '&chat_type=sender' +) M- T7 \% ?# g
  6. '&query_id=QUERY' +) W! V9 y) C- D  U5 I" F2 z
  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' +
    : h: }2 a  Y7 |0 K. g
  8. '&start_param=debug' +: G6 A! K( B6 `2 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' +
    8 r$ G" U6 x4 i6 T
  10. '&hash=47cfa22e72b887cba90c9cb833c5ea0f599975b6ce7193741844b5c4a4228b40'
复制代码

该函数接受三个参数:

  • 要签名的数据:它表示经过解析的初始数据对象,不包括 authDate 和 hash 属性。
  • 机器人令牌:该令牌由 [@BotFather] (https://t.me/botfather) 接收。
  • 签署日期:此值将用作 authDate 属性的值。
    & I$ _8 k1 E4 O

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

网络加密应用程序接口

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

  1. import {
    8 \" f. t" W7 N
  2.   validate,
    $ j6 u; I# e4 v& |9 D9 y
  3.   sign,
    ) U4 f5 ]+ }0 t. S4 q
  4.   signData,( L/ P+ T* O# K; i- X+ s) P8 P
  5.   isValid,
    9 v1 c. z6 z; Q
  6. } from '@telegram-apps/init-data-node/web';/ o6 w1 g1 E' I) {# e! }
  7. 0 S1 m* J8 p  K" g6 ?3 k1 J
  8. await validate(...);
    & P8 c1 Z, j+ U+ A7 ?
  9. await sign(...);3 b/ }+ j: i/ W4 H# t) ?/ H
  10. await signData(...);7 O& d( Y+ }, Y4 L# E5 z
  11. await isValid(...);
复制代码
传递散列令牌 - Hashed Token

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

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

下面是一些例子:

  1. import { validate, sign } from '@telegram-apps/init-data-node';/ T7 g6 g4 [: I, X' Y2 c. z; s/ Z7 M

  2. $ v  D6 P: @, @2 o
  3. const secretTokenHashed = 'a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f';% s' L2 \: \$ Y9 K2 O" V
  4. const initData =1 r! F5 M9 R  X5 ^9 H, J
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +2 k, Q, g' R1 X$ w
  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* V; N3 x6 d$ l* d
  7.   '&auth_date=1662771648' ++ b( c" b! O" k6 Q' u! ]+ p
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';
    9 C8 Y; ?6 E4 q# H

  9. + O/ u$ C! n9 B: u2 Q" i" p$ j
  10. // Validating.  X: c) [7 Y8 {" W; g8 l. Y/ n
  11. validate(initData, secretTokenHashed, { tokenHashed: true });
    ( y4 g( F% c* s4 j& K

  12. 2 f) Y: y) z5 G/ \9 V) B
  13. // Signing.
    , t$ L' |8 W/ `" B9 w+ @
  14. sign(4 I& F3 G5 i, Z6 }
  15.   {
    5 [. y. O+ e  b$ R9 m
  16.     canSendAfter: 10000,  Z6 S/ H- |" t% r
  17.     chat: {+ H0 b0 N6 p! q! P
  18.       id: 1,
    ) s3 S/ D7 g) @; V! F4 s
  19.       type: 'group',/ B4 N/ a. K& o8 u! s/ E/ j4 h# c
  20.       username: 'my-chat',  N% @. x5 n6 `6 W
  21.       title: 'chat-title',
    ' H1 z: Y3 H5 s% r
  22.       photoUrl: 'chat-photo',+ s- `1 _5 ?" @" d" c# h
  23.     },# i: `8 j& l1 x
  24.     chatInstance: '888',: G9 N; u* A  L3 L) [9 ]
  25.     chatType: 'sender',
    - E; K6 L6 P3 k+ e6 X
  26.     queryId: 'QUERY',, g) e& O% \8 ]$ A
  27.     receiver: {( t' f" z2 a& u2 C) P
  28.       addedToAttachmentMenu: false,/ k  r' C4 B4 J' Y+ Y2 m7 e# R
  29.       allowsWriteToPm: true,$ a2 c5 `% m0 j0 n4 n
  30.       firstName: 'receiver-first-name',7 x: r( R6 Q' U* j/ X6 C4 H
  31.       id: 991,
      \% G- V; t6 m4 _8 w, G
  32.       isBot: false,; |4 O& }: W8 W: Y/ C: Z; f9 B/ f
  33.       isPremium: true,8 E9 z& k2 y  i# S; N( l) x
  34.       languageCode: 'ru',4 B9 Z7 C; D0 ^3 i" B
  35.       lastName: 'receiver-last-name',% \1 B0 C6 J% e% o' [/ p
  36.       photoUrl: 'receiver-photo',
    1 Z1 m. i8 S+ d+ {1 _2 o
  37.       username: 'receiver-username',
    9 ?8 N1 |7 F$ u7 [6 V5 S
  38.     },! y( i9 O5 }0 k2 P' z0 ?0 v7 n& q
  39.     startParam: 'debug',
    3 D( g) J1 F8 ]+ K: s( X- `& I
  40.     user: {* ]' E. x! f: E9 d; w
  41.       addedToAttachmentMenu: false,
    ! A3 H2 Z2 h% P4 A0 o$ p% b
  42.       allowsWriteToPm: false,
    0 m! Q2 H/ }- I+ V2 H9 b
  43.       firstName: 'user-first-name',
    " G% F" W' Z; X
  44.       id: 222,0 K7 q/ Z0 x5 B* Z2 I
  45.       isBot: true,
    $ U% c4 J8 ^: X$ c+ q! i
  46.       isPremium: false,7 K4 h9 A# D/ ]" h
  47.       languageCode: 'en',
    7 y5 M+ @9 C  Y, f2 V8 k7 z
  48.       lastName: 'user-last-name',
    - y: d2 d& W) f' [/ f8 K
  49.       photoUrl: 'user-photo',1 [1 @& `$ o& k, i% C4 j
  50.       username: 'user-username',
    : f- D3 H  u- Y2 a9 x5 n
  51.     },
    4 c  I% X( j( u8 c7 C; C+ x
  52.   },
    ( M7 P" W2 ]8 h- p2 H+ L. E
  53.   secretTokenHashed,1 p- n- Z0 u$ W4 I! h
  54.   new Date(1000),
    # a4 w& C' v5 l+ q
  55.   { tokenHashed: true }
    ; z: w4 g0 \4 `! h" F: o2 C+ f
  56. );
复制代码

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

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

本版积分规则