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

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

回答

收藏

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

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

该软件包提供在 服务器端处理 Telegram 迷你应用程序初始化数据的实用程序。 要了解有关初始化数据及其用法的更多信息,请参阅 the documentation.安装
* c: z9 o! I6 b; z/ ~' O. B/ r6 k* ?- }pnpm :
2 ]0 |  v0 h8 D
  1. pnpm i @telegram-apps/init-data-node
复制代码
npm :
+ f# i4 S( ^, Y: K7 T$ S# y% B; x
  1. npm i @telegram-apps/init-data-node
复制代码
yarn :# _( _; g2 s1 m  z& j' ~
  1. yarn add @telegram-apps/init-data-node
复制代码
解析

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

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

  1. import { parse } from '@telegram-apps/init-data-node';9 o, Q! O1 L( Y9 a
  2. . c# ?+ q) V! l
  3. try {
    3 t* ]* E% @; z3 e# Y( x6 j9 T
  4.   const initData = parse('...');
    6 h& M" a1 ]$ z5 ~
  5.   // {
    ! P% C: h0 b: i$ _2 Y
  6.   //   canSendAfter: 10000,
    . g8 u; |9 F5 k( }
  7.   //   chat: {/ K, {' x) @. {& x7 K( {* j
  8.   //     id: 1,* o. Y) O; ~' j
  9.   //     type: 'group',/ L1 c9 J  |! {0 }4 K
  10.   //     username: 'my-chat',
    , t& [9 T6 L9 j( F) P( f; u
  11.   //     title: 'chat-title',
    ' i. R/ L! ?& _4 N  y
  12.   //     photoUrl: 'chat-photo',& `5 P/ ~6 B0 Y9 o. |) M
  13.   //   },
    9 g% z; U  L1 p3 F  l9 i
  14.   //   chatInstance: '888',
    4 u& z) e/ w8 [/ d) |: |
  15.   //   chatType: 'sender',
    5 c- ]" @) o$ [, Z5 C
  16.   //   queryId: 'QUERY',
    7 w: q5 b% p, I" f; q" W
  17.   //   receiver: {) W; I( s; \* U* N4 }
  18.   //     addedToAttachmentMenu: false,: v3 l) C" R( h$ |+ J8 X4 W
  19.   //     allowsWriteToPm: true,
    " }( o- ^/ O6 E) B  @6 i
  20.   //     firstName: 'receiver-first-name',
    . O6 p! m: v# F& _# a0 |
  21.   //     id: 991,* v0 c8 f8 X6 H' ^# m
  22.   //     isBot: false,
    ( v  x) r+ I) g3 M
  23.   //     isPremium: true,
    / ~/ b2 \( [& R; w  T
  24.   //     languageCode: 'ru',  n5 h) c, g8 d' t* K7 Z
  25.   //     lastName: 'receiver-last-name',. d' X7 t$ O: `
  26.   //     photoUrl: 'receiver-photo',
    9 u! [7 d! m+ O8 p9 ?6 K9 e$ D
  27.   //     username: 'receiver-username',
    . m1 a# j, L. }' A! T$ s) D' o0 h+ x
  28.   //   },  G6 [1 Q* \$ e9 W& E3 q: d, G$ y
  29.   //   startParam: 'debug',/ j! q8 c" r0 p
  30.   //   user: {9 ^) o; Y+ N+ w
  31.   //     addedToAttachmentMenu: false,
    1 Y. _% I1 s4 F- D5 U
  32.   //     allowsWriteToPm: false,& X- ]) c* z! I
  33.   //     firstName: 'user-first-name',2 A( i, E4 k) A9 ~( U
  34.   //     id: 222,2 f3 w' F% q" Y9 |" p1 f
  35.   //     isBot: true,- E+ R; ^% P; \7 m& S
  36.   //     isPremium: false,; w7 e: ]( x. Y) E1 ]  I$ c
  37.   //     languageCode: 'en',5 o$ K" D) L$ k0 W# ?0 B) b+ {
  38.   //     lastName: 'user-last-name',
    : F0 s# U8 R8 c
  39.   //     photoUrl: 'user-photo',) X0 Y# ?+ V( K3 N, D
  40.   //     username: 'user-username',4 x$ x2 h) x- E3 P6 g& l
  41.   //   },$ \, D& `" A( `8 v0 t
  42.   // }
    1 f" Y* Y! n) w& Q, r0 B
  43. } catch (e) {; D8 D9 g3 J& u2 J; c
  44.   console.error('Something is wrong', e);* L# A2 L5 e) p. A
  45. }
复制代码
验证validate

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

  1. import { validate } from '@telegram-apps/init-data-node';
    # o2 n' _* h  {8 s# V1 L

  2. . x2 V+ f% |( X# h/ {
  3. const secretToken = '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8';4 q& \# g/ o2 A0 F; ^$ o
  4. const initData =
    . h7 m1 R' S7 g9 \4 m; {# [
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +
    % v  N( y7 t6 \/ l1 S! |5 {
  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' +
    5 w) t, ~( b  z) q* J  j  V2 y2 k
  7.   '&auth_date=1662771648' +
      ~& I! f( Z* o; D3 q4 y$ ^6 |
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';& s% \2 I9 v! k; T5 P. z

  9. 1 F- F8 B7 ?0 r! K8 _% q
  10. validate(initData, secretToken);
复制代码

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

  • ERR_AUTH_DATE_INVALID:auth_date 为空或未找到
  • ERR_HASH_INVALID:hash 为空或未找到
  • ERR_SIGN_INVALID:签名无效
  • ERR_EXPIRED:初始数据已过期/ h6 X$ [5 b& q3 u, M% c

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

  1. import { validate, isErrorOfType } from '@telegram-apps/init-data-node';
    8 b2 N5 P( p/ B. O. W% R

  2. * I6 U7 z6 \3 {
  3. try {
    2 N- ]3 v, y: Q7 C" m
  4.   validate('init-data', 'token');
    : h& }1 Y3 l+ i) ^
  5. } catch (e) {
    5 T8 g: |# V7 O! k. C; B9 n- u
  6.   if (isErrorOfType('ERR_SIGN_INVALID')) {1 R$ T1 c0 J" Q# ?# D9 k$ a( k
  7.     console.log('Sign is invalid');
    # K1 T$ c; Y, K0 \3 W$ v8 S
  8.   }7 M4 E, I5 @2 _2 A6 z0 J! Y
  9. }
复制代码

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

isValid

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

  1. import { isValid } from '@telegram-apps/init-data-node';
    / `  \8 D2 ]# \6 J

  2. ( u  E1 i* J! D& }( B0 c
  3. if (isValid('init-data')) {
    4 t9 k# M5 U. a. E, e5 s9 l) {
  4.   console.log('Init data is fine');) X- S* Q3 W( F% i* Q5 j
  5. }
复制代码
签名

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

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

Signing :

  1. import { sign } from '@telegram-apps/init-data-node';8 s0 T6 {; _4 R6 C
  2. * |+ P+ z/ N) h. R: Y2 l2 ~4 b' d
  3. sign(' N( ^- i6 W' U( E4 o
  4.   {
    . Q9 \  T# N6 V% z9 `3 r
  5.     canSendAfter: 10000,+ ]. S6 c$ Q9 ]5 g1 p: T! H! w
  6.     chat: {  |5 \8 G+ @$ X+ u. O
  7.       id: 1,
    ! A5 R2 E5 U3 K# }) e6 l
  8.       type: 'group',
    # x; S+ [$ r) a$ F
  9.       username: 'my-chat',
    ) E/ Z& _" u; _% g; j1 X7 j  v, {9 o
  10.       title: 'chat-title',% h5 @# J, Q9 ~" B/ s. Y: @9 r
  11.       photoUrl: 'chat-photo',0 o! q6 X9 l7 t$ |1 U, H
  12.     },! p7 l3 e6 P/ E
  13.     chatInstance: '888',* A' @- F) T) h
  14.     chatType: 'sender',+ m0 K. P( U7 s
  15.     queryId: 'QUERY',1 ^& ^4 Z0 c+ t% j2 p' N
  16.     receiver: {
    2 m7 J# B" R* V+ o+ ~  R6 D
  17.       addedToAttachmentMenu: false,) q7 Y9 o+ q2 B" G
  18.       allowsWriteToPm: true,
    0 X8 @2 L4 ?: a( b, N5 B* f/ n
  19.       firstName: 'receiver-first-name',7 E# f8 Y) a1 G3 q7 q
  20.       id: 991,
    4 s/ c' e! Z/ p: t2 Y
  21.       isBot: false,) t2 v/ b4 {9 \$ e2 T1 Y5 \, _7 n
  22.       isPremium: true,
    $ i+ H6 Y7 K: F" _  i
  23.       languageCode: 'ru',
    ' c* E" B- \' x, [! S3 R7 [
  24.       lastName: 'receiver-last-name',0 P" }" _" }2 F$ {( J" v! e
  25.       photoUrl: 'receiver-photo',
    0 J+ C0 l- q* Q& B6 Q4 s
  26.       username: 'receiver-username',
    9 w7 h! R' [8 f- E6 r* _2 [  [
  27.     },
    ' ~" f6 B. n' U5 I
  28.     startParam: 'debug',
    4 K8 m* O, ?$ Y. J! }. {8 F
  29.     user: {1 l- @1 J6 M1 g$ _' @! r
  30.       addedToAttachmentMenu: false,( B4 @2 Z$ r2 k) D* \2 k' [
  31.       allowsWriteToPm: false,
      d' L4 }  i% Q: i1 A
  32.       firstName: 'user-first-name',
    + C  {: E" e9 C- Z& v) ^
  33.       id: 222,
    * O) f! J/ U) W: M% T6 U* x/ E
  34.       isBot: true,/ @7 N9 M) U  {7 h. D
  35.       isPremium: false,
    4 Z$ R. c; `; n3 m6 F  }2 [
  36.       languageCode: 'en',! Z- F, q$ D/ J( h/ n; T
  37.       lastName: 'user-last-name',! d& ?' v* j* R3 h. }5 _
  38.       photoUrl: 'user-photo',1 F* t  Y6 H% \- a1 v! p3 D
  39.       username: 'user-username',& o: H# B0 v. E5 d- x
  40.     },6 \* P9 I( o, K0 P& Y" {
  41.   },$ T, T! T" L+ o4 s+ B
  42.   '5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8',+ ]  g" Q* H; F. ]1 X) z
  43.   new Date(1000),
    7 d" K: S7 x2 d5 q' y
  44. );
复制代码

Expected Result :

  1. 'auth_date=1' +
    " z8 W+ I8 x9 m# A2 a, Y; z! D
  2. '&can_send_after=10000' +. c; X" G+ x6 o( C+ A6 h# T
  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' +
    # U3 c# W% c7 A( z) ]7 r8 g
  4. '&chat_instance=888' +
    + E0 k( B8 f# l: @$ }
  5. '&chat_type=sender' +) r3 y* v( z8 _+ E# C, q$ R
  6. '&query_id=QUERY' +
    . [9 g; X; f, p. r' h
  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' +
    , C7 |7 ~# A$ L( v& j9 A% C9 W
  8. '&start_param=debug' +' P% @: W1 b* X8 z) z
  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' +* j# d% {" g$ u" f1 t) p+ r8 a, z
  10. '&hash=47cfa22e72b887cba90c9cb833c5ea0f599975b6ce7193741844b5c4a4228b40'
复制代码

该函数接受三个参数:

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

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

网络加密应用程序接口

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

  1. import {
    ( e- z1 ^& x& S
  2.   validate,% B8 s4 G/ a: {
  3.   sign,
    : g) }7 Q+ e8 Z% X, X0 W
  4.   signData,
    & F' t+ k5 S  H; X" k$ K
  5.   isValid,
    7 B% P& s  |8 Q$ |  O3 g3 p: ?5 K
  6. } from '@telegram-apps/init-data-node/web';: e# V1 T! r7 y9 Q% g
  7. % U/ s* e3 J; j3 y
  8. await validate(...);" S! N( {# f4 b( W
  9. await sign(...);; N' ?% [5 ]. ~9 }  Q2 \) {
  10. await signData(...);
    7 y" _: C" c; I
  11. await isValid(...);
复制代码
传递散列令牌 - Hashed Token

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

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

下面是一些例子:

  1. import { validate, sign } from '@telegram-apps/init-data-node';
    % d' Y1 b# ]9 E+ U3 O

  2. - }; ?" l. f! U4 e" T( a
  3. const secretTokenHashed = 'a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f';
    ! T6 f* H! \2 C4 |
  4. const initData =" ]% j, u" W2 z
  5.   'query_id=AAHdF6IQAAAAAN0XohDhrOrc' +
    4 T6 p0 Y/ ?2 m$ ^  O5 x4 ]1 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' +7 r& q' B2 U+ W* F/ w
  7.   '&auth_date=1662771648' +
    & Y2 D, p% ]' ^6 r: a2 L! T: ]
  8.   '&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2';: b% ?. \' r0 t8 ^- k. a7 Q( w- T

  9. 5 H3 a" u& n, F8 i' B; k( V
  10. // Validating.$ x; \/ r) {1 I2 g  d
  11. validate(initData, secretTokenHashed, { tokenHashed: true });
    & K- ^+ X+ O, i7 x

  12. 5 Y3 C6 C  F( l# B: U
  13. // Signing.
    + t& I2 G9 j1 N; i
  14. sign(6 ~8 M5 M' f& u1 t/ }& s/ s
  15.   {
    ; ~; w% b; i/ W  s
  16.     canSendAfter: 10000,
    $ j( ~6 {1 r3 v( n
  17.     chat: {
    $ E# @- q, F: o
  18.       id: 1,- ]0 a8 _4 r# M' C6 t3 A) K* X8 t
  19.       type: 'group',
    4 T0 G: N6 V  A( ^) J
  20.       username: 'my-chat',
    ' r9 n1 }" [$ ~+ l7 d
  21.       title: 'chat-title',. C8 S! f' x5 R: O4 B
  22.       photoUrl: 'chat-photo',
    / F+ N4 E# N- I: Z4 r
  23.     },  I* g9 s7 o: n: ]
  24.     chatInstance: '888',
    ; A) R) U8 A: [- h/ ]8 d
  25.     chatType: 'sender',
    8 Z( J" j, p4 m5 ]
  26.     queryId: 'QUERY',$ ]$ @" J5 I3 R7 K3 U
  27.     receiver: {
    $ b8 h. L0 c9 R
  28.       addedToAttachmentMenu: false,
    / E5 V# n/ b7 y7 P) U
  29.       allowsWriteToPm: true,( a+ g  o/ P$ A; n- g3 C$ \3 K! h
  30.       firstName: 'receiver-first-name',  x3 Y0 f2 h: F* a
  31.       id: 991,' D* G8 k# Y( H
  32.       isBot: false,, j; A& T$ J6 ?  X9 m1 u
  33.       isPremium: true,
    ) S5 J, {+ [- `0 H0 }9 K
  34.       languageCode: 'ru',
    6 T# g9 i3 j; p* v# Z4 W3 B
  35.       lastName: 'receiver-last-name',8 G! p1 e# F$ \3 V! I. Z
  36.       photoUrl: 'receiver-photo',1 _" E$ D7 t! r
  37.       username: 'receiver-username',, e3 N7 H) y; _. q) r  C
  38.     },
    # P0 u* K) J/ t
  39.     startParam: 'debug',5 F; S! \1 L% Z/ S! N, F
  40.     user: {
    6 {% Z# g: I+ z: m7 V6 a
  41.       addedToAttachmentMenu: false,% ?+ i/ v# x: P" X
  42.       allowsWriteToPm: false,+ c5 \9 V$ }2 d3 o: \
  43.       firstName: 'user-first-name',+ S6 k/ \! W5 d
  44.       id: 222,* F. f7 H1 M+ f+ b7 {" ]7 U. i
  45.       isBot: true,3 k# O# T/ A8 ~, V; c
  46.       isPremium: false,
    ! Q! i! K! y& d: M0 E/ |: u  g
  47.       languageCode: 'en',
    9 z) L6 G, J6 @  f4 n' J4 A+ Q( j" B
  48.       lastName: 'user-last-name',9 }9 `9 b/ R: |) _% E( X- R
  49.       photoUrl: 'user-photo'," g, F' \4 i0 X/ w9 M/ g5 ?
  50.       username: 'user-username',
    0 J) P+ h: c, Y, {, `
  51.     },5 \$ o) g9 D6 ]4 z6 j3 u
  52.   },  c/ ?" k& n! n/ a! y1 E
  53.   secretTokenHashed,6 R. e0 s  ?( r, l' _$ [
  54.   new Date(1000),
    * Z3 n( X3 P0 I; U$ ]
  55.   { tokenHashed: true }- ]+ c4 u2 }* t* r, C; Y
  56. );
复制代码

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


9 `! l$ B$ l5 w* c! S; W: V/ X& M+ @0 R8 x) t

9 _$ u4 e7 P! P+ p# O; E* d' Q) y

: d+ q& L. w! r4 h' J* X- G+ ?: M+ p! _0 D8 F+ Z
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则