如何使用Telegram API实现授权?

我想要使​​用PHP来实现与电报REST API交互的多重授权。

我想要解决什么任务? 好吧,这很简单:几十个用户(他们都有一个像这里(+10,-2,+1000等)与相关组分类:网站管理员和客户)在我的网站上有一个用户配置文件。 当他们达到一定数量的carma,并且由于他们在他们的个人资料被授权,他们被加入基于自动生成的电报私人聊天。

经过一番调查,我发现这很复杂,因为:

  1. 我从来没有过硬件绑定社交网络的API实现经验。
  2. 我查看了https://core.telegram.org/api/auth ,但是如何使用PHP或任何其他语言来实现这些功能(例如auth.sendCode)是完全不明显的。 如果这些命令应该作为JSON发送到服务器,那么它看起来不像JSON:

    auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode; 

    它是什么? 它写的是哪种语言?

    UPD:它是用TL(类型语言)编写的: https : //core.telegram.org/mtproto/TL

  3. 我已经探索了几个客户端的源代码(网络图,Telegram-Cli(tg),tdesktop),并且发现了https://core.telegram.org/mtproto

不幸的是,他们中没有一个支持多方授权,经过一点研究,我不知道要在哪里深入挖掘以获取更多信息。

另外,这些实现看起来很笨重和复杂(例如https://github.com/vysheng/tg ): 在这里输入图像描述

在那里我看到一堆服务器(./tg/tgl/tgl.h):

 #define TG_SERVER_1 "149.154.175.50" #define TG_SERVER_2 "149.154.167.51" #define TG_SERVER_3 "149.154.175.100" #define TG_SERVER_4 "149.154.167.91" #define TG_SERVER_5 "149.154.171.5" 

我发现了几个可能适当的函数(./tg/tgl/queries.c):

 void empty_auth_file (void) { if (TLS->test_mode) { bl_do_dc_option (TLS, 1, "", 0, TG_SERVER_TEST_1, strlen (TG_SERVER_TEST_1), 443); bl_do_dc_option (TLS, 2, "", 0, TG_SERVER_TEST_2, strlen (TG_SERVER_TEST_2), 443); bl_do_dc_option (TLS, 3, "", 0, TG_SERVER_TEST_3, strlen (TG_SERVER_TEST_3), 443); bl_do_set_working_dc (TLS, TG_SERVER_TEST_DEFAULT); } else { bl_do_dc_option (TLS, 1, "", 0, TG_SERVER_1, strlen (TG_SERVER_1), 443); bl_do_dc_option (TLS, 2, "", 0, TG_SERVER_2, strlen (TG_SERVER_2), 443); bl_do_dc_option (TLS, 3, "", 0, TG_SERVER_3, strlen (TG_SERVER_3), 443); bl_do_dc_option (TLS, 4, "", 0, TG_SERVER_4, strlen (TG_SERVER_4), 443); bl_do_dc_option (TLS, 5, "", 0, TG_SERVER_5, strlen (TG_SERVER_5), 443); bl_do_set_working_dc (TLS, TG_SERVER_DEFAULT); } } void bl_do_dc_option (struct tgl_state *TLS, int id, const char *name, int l1, const char *ip, int l2, int port) { struct tgl_dc *DC = TLS->DC_list[id]; if (DC && !strncmp (ip, DC->ip, l2)) { return; } clear_packet (); out_int (CODE_binlog_dc_option); out_int (id); out_cstring (name, l1); out_cstring (ip, l2); out_int (port); add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); } 

等等

我应该将哪些文件传输到PHP以实现多用户认证? 你能帮我知道从哪里开始,以及如何使它比目前更容易?

先谢谢你!

到目前为止,我已经能够完全实现电报授权,但不能以您所要求的语言 – PHP,我使用vb.Net。 不过,我相信也应该采用同样的逻辑。

创建一个电报授权密钥

电报API是不是在公园散步。 学习现有的src代码可能是相当艰巨的(恕我直言)。 因此,我的方法是研究在线API文档,并实现下面链接中概述的sample-auth_key。

https://core.telegram.org/mtproto/auth_key

https://core.telegram.org/mtproto/samples-auth_key

这种方法将给你更好的理解和介绍电报API中使用的原语,并可能帮助你开始组织你自己的一套函数和例程,以处理下一步你将需要的 – 实现API的其他功能,因为生成AuthKey只是一个开始。

步骤1

所有的通信都是通过TCP – 一旦你获得了唯一的api_id( https://core.telegram.org/api/obtaining_api_id#obtaining-api-id ),你会发现以下IP广告用于测试: 149.154.167.40: 433此时,不需要api_id来生成AuthKey

设置发送/接收TCP处理循环的首选方法

我拥有的是一个私人的SendData,只是发送一个字节到一个活的套接字连接到上面给的IP地址

 Private Sub SendData(b() As Byte, Optional read As Boolean = False) If Not IsConnected() Then Log("Connection Closed!", ConsoleColor.DarkRed) RaiseEvent Disconneted() Exit Sub End If b = TCPPack(b) Dim arg = New SocketAsyncEventArgs With {.RemoteEndPoint = ep} AddHandler arg.Completed, AddressOf IO_Handler arg.SetBuffer(b, 0, b.Length) Try If Not soc.SendAsync(arg) Then IO_Handler(soc, arg) End If If read Then ReadData() End If Catch ex As Exception Log("SendData: " & ex.ToString, ConsoleColor.Red) End Try End Sub Private Sub ReadData(Optional wait As Integer = 0) If Not IsConnected() Then Log("Connection Closed!", ConsoleColor.DarkRed) RaiseEvent Disconneted() Exit Sub End If Dim arg = New SocketAsyncEventArgs With {.RemoteEndPoint = ep} AddHandler arg.Completed, AddressOf IO_Handler Dim b(BUFFER_SIZE - 1) As Byte arg.SetBuffer(b, 0, BUFFER_SIZE) Try If Not soc.ReceiveAsync(arg) Then IO_Handler(soc, arg) End If Catch ex As Exception Log("ReadMessages: " & ex.ToString, ConsoleColor.Red) End Try End Sub Private Sub IO_Handler(sender As Object, e As SocketAsyncEventArgs) Log($"{e.LastOperation}:{e.SocketError}:{e.BytesTransferred}", ConsoleColor.Cyan) Select Case e.SocketError Case SocketError.Success Select Case e.LastOperation Case SocketAsyncOperation.Connect 'A socket Connect operation. Log("Connected to " & e.ConnectSocket.RemoteEndPoint.ToString, ConsoleColor.Green) are.Set() Case SocketAsyncOperation.Disconnect, SocketAsyncOperation.Connect RaiseEvent Disconneted() Case SocketAsyncOperation.Receive 'A socket Receive operation. HandleData(e) End Select Case SocketError.ConnectionAborted RaiseEvent Disconneted() End Select End Sub Private Sub HandleData(e As SocketAsyncEventArgs) If e.BytesTransferred = 0 Then --no pending data Log("The remote end has closed the connection.") Exit Sub End If Dim len As Integer = e.Buffer(0) Dim start = 1 If len = &H7F Then len = e.Buffer(1) len += e.Buffer(2) << 8 len += e.Buffer(3) << 16 start = 4 End If len = 4 * len Dim data(len - 1) As Byte Array.Copy(e.Buffer, start, data, 0, len) ProcessResponse(data) ReadData() End Sub 

最后,为了实现这一步,我们需要一个TcpPack()方法,它可以帮助我们以Telegram期望的格式填充数据 – 请参阅下面的代码

 Private Function TCPPack(b As Byte()) As Byte() Dim a = New List(Of Byte) Dim len = CByte(b.Length / 4) If efSent = False Then --TCP abridged version efSent = True a.Add(&HEF) End If If len >= &H7F Then a.Add(&H7F) a.AddRange(BitConverter.GetBytes(len)) Else a.Add(len) End If a.AddRange(b) --only data, no sequence number, no CRC32 Return a.ToArray End Function 

第2步

通过基本的TCP发送/接收例程设置,我们可以开始准备数据包发送到电报,并有子例程处理收到的具体响应 – ProcessResponse(数据)

接下来我们需要了解的是,Telegram处理两大类消息 –

未加密https://core.telegram.org/mtproto/description#unencrypted-message

这些纯文本消息的auth_key_id =0生成一个AuthKey使用这种类型的消息

加密https://core.telegram.org/mtproto/description#encrypted-message-encrypted-data

所有与电报服务器的进一步通信将通过加密的消息

我选择有两个类来封装这两种消息类型。 然后我可以有两个Send(m)方法来处理每个类型

 Private Sub Send(m As UnencryptedMessage) Log(m.ToString, ConsoleColor.DarkYellow, logTime:=False) SendData(m.data, True) End Sub Private Sub Send(m As EncryptedMessage) Log(m.ToString, ConsoleColor.DarkYellow, logTime:=False) SendData(m.data, True) End Sub 

现在只需要UnencryptedMessage

 Public Class UnencryptedMessage Public Property auth_key_id As Int64 Public Property message_id As Int64 Public Property data_length As Int32 Public Property message_data As Byte() Public Property message_type As String Public Property data As Byte() = {} Sub New(auth_key As Int64, message_id As Int64, data As Byte()) _auth_key_id = auth_key _message_id = message_id _data_length = data.Length _message_data = data message_type = B2Hr(data, 0, 4) Dim a = New List(Of Byte) a.AddRange(BitConverter.GetBytes(auth_key_id)) --{0, 0, 0, 0, 0, 0, 0, 0} a.AddRange(BitConverter.GetBytes(message_id)) a.AddRange(BitConverter.GetBytes(data_length)) a.AddRange(message_data) Me.data = a.ToArray End Sub Sub New(b As Byte()) data = b Dim skip = 0 _auth_key_id = BitConverter.ToInt64(b, skip) : skip += 8 _message_id = BitConverter.ToInt64(b, skip) : skip += 8 _data_length = BitConverter.ToInt32(b, skip) : skip += 4 ReDim _message_data(_data_length - 1) Array.Copy(b, skip, _message_data, 0, b.Length - skip) message_type = B2Hr(_message_data, 0, 4) End Sub Public Overrides Function ToString() As String Return $" raw_data: {B2H(data)} auth_key_id: {i2H(auth_key_id)} {auth_key_id} message_id: {i2H(message_id)} {message_id} data_length: {i2H(data_length)} {data_length} message_data: {B2H(message_data)} message_type: {message_type} " End Function End Class 

第3步

现在我们按照https://core.telegram.org/mtproto/auth_key中列出的一系列步骤进行操作;

1)客户端发送查询到服务器

req_pq#60469778 nonce:int128 = ResPQ nonce的值由客户端(随机数)随机选择,并标识此通信中的客户端。 在这一步之后,众所周知。

2)服务器发送表单的响应

resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ

我的做法很简单:

 Sub RequestPQAuthorization() Send(MTProto.req_pq) End Sub 

在一个名为MTProto的类中,我实现了交换每一步所需的一组共享函数。 每种方法都只是建立一个加密的数据结构,将根据需要发送上述数据结构

我们从: req_pq开始

 Shared Function req_pq(Optional nonce As Byte() = Nothing) As UnencryptedMessage --req_pq#60469778 --nonce:int128 If nonce Is Nothing Then ReDim nonce(15) RND.NextBytes(nonce) End If Dim d = New List(Of Byte) d.AddRange({120, 151, 70, 96}) --60469778 d.AddRange(nonce) Return New UnencryptedMessage(0, CreateMessageId, d.ToArray) End Function Private Shared Function CreateMessageId() As Int64 Return CLng((Date.UtcNow.Ticks - ZERO_TICK) * 429.4967296) End Function Public Const ZERO_TICK = 621355968000000000 -- ie 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC) 

现在我们从第1步开始的流程响应方法

 Private Sub ProcessResponse(data As Byte()) Try Dim r = New UnencryptedMessage(data) Log(r.ToString, ConsoleColor.Yellow, logTime:=False) Select Case r.message_type Case resPQ.Classid RequestDHKeyExchange(New resPQ(r.message_data)) Case server_DH_params_ok.Classid RequestSetDH_params(New server_DH_params_ok(r.message_data), new_nonce) Case server_DH_params_fail.Classid Log(New server_DH_params_fail(r.message_data).ToString, ConsoleColor.DarkMagenta) Case dh_gen_ok.Classid Log(New dh_gen_ok(r.message_data).ToString, ConsoleColor.Green) Case dh_gen_retry.Classid Log(New dh_gen_retry(r.message_data).ToString, ConsoleColor.DarkMagenta) Case dh_gen_fail.Classid Log(New dh_gen_fail(r.message_data).ToString, ConsoleColor.DarkMagenta) Case Else Log($"Unhandled type: {r.message_type}", ConsoleColor.Magenta) End Select Catch ex As Exception Log($"Error: {ex.ToString}", ConsoleColor.Red) Log(B2H(data), ConsoleColor.DarkRed, logTime:=False) End Try End Sub 

迄今为止,收到的每个响应都有一个message_type代码。

我们可以打开它,并确定如何处理。 我们现在需要处理resPQ。

我所做的是创建一组类,每个处理特定的响应类型

 ''' <summary> ''' resPQ#05162463 ''' nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ ''' </summary> Public NotInheritable Class resPQ : Inherits TLObject Public Shared Shadows ReadOnly Property Classid As String = "05162463" Public Property nonce As Byte() Public Property server_nonce As Byte() Public Property pq As Byte() Public Property fingerprints As List(Of UInt64) Public Property count As Int32 Sub New(data As Byte()) Dim skip = 4 nonce = Slice(data, skip, 16) : skip += 16 server_nonce = Slice(data, skip, 16) : skip += 16 skip += 1 'length of pq:string pq = Slice(data, skip, 8) : skip += 8 skip += 3 'padding to complete the 4-bytes skip += 4 '%(Vector long) 1cb5c415 count = i32r(data, skip) : skip += 4 fingerprints = New List(Of UInt64) For i = 0 To count - 1 fingerprints.Add(u64r(data, skip)) Next End Sub Public Overrides Function ToString() As String Return $" classid: {NameOf(resPQ)}#{Classid} nonce: {B2H(nonce)} server_nonce: {B2H(server_nonce)} pq: {B2H(pq)} {u64(pq)} count: {i2H(count)} {count} fingerprints: {i2H(fingerprints(0))} {fingerprints(0)} " End Function End Class 

每个都基于这个简单的对象

 Public MustInherit Class TLObject Public Shared Property ClassId As String Public MustOverride Overrides Function ToString() As String End Class 

在此基础上,我们解决步骤3和4

工作证明3)客户将pq分解为素因子,使得p <q。

这开始了一轮Diffie-Hellman密钥交换。

提交工作证明; 服务器认证4)客户端发送查询到服务器

req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params

这是在我的ProcessResponse例程中的RequestDHKeyExchange(New resPQ(r.message_data))这里启动的

 Sub RequestDHKeyExchange(r As resPQ) Log(r.ToString, ConsoleColor.Gray, logTime:=False) 'decompose prime cofactors Dim pp = New PrimeProduct(r.pq) Log(pp.ToString, ConsoleColor.Gray, logTime:=False) 'encrypted_data Generation Dim pq = New P_Q_inner_data(r.pq, pp.p, pp.q, r.nonce, r.server_nonce) new_nonce = pq.new_nonce 'The serialization Of P_Q_inner_data produces some String data. This Is followed by encrypted_data 'data_with_hash := SHA1(data) + data + (any random bytes); such that the length equal 255 Dim data_with_hash = New List(Of Byte) 'SHA1(data) = xxx- 40 =20 bytes Using sha1 = New SHA1Managed Dim b = pq.ToBytes data_with_hash.AddRange(sha1.ComputeHash(b)) data_with_hash.AddRange(b) End Using If data_with_hash.Count < 255 Then Dim pad(255 - data_with_hash.Count - 1) As Byte RND.NextBytes(pad) data_with_hash.AddRange(pad) End If 'RSA(data_with_hash, server_public_key) = xxx - 512 = 256 bytes Dim key = i2H(r.fingerprints(0)) 'c3b42b026ce86b21 Dim zb = Crypto.rsaEncrypt(data_with_hash.ToArray, key) Send(MTProto.req_DH_params(r.nonce, r.server_nonce, pp.p, pp.q, r.fingerprints(0), zb)) End Sub 

您可以使用您自己的Prime-Decomposition实现来替换此行Dim pp = New PrimeProduct(r.pq)

这里是我如何学习使用PollardBrent (波拉德Rho布伦特整数因子分解)的示例https://stackoverflow.com/a/31978350/44080

好吧,如果你可以跟进到这一点,你有一个大致的想法,我如何分解一步一步的AuthKey实施。

你应该能够通过剩下的步骤5-9。 打字对我来说很重要

如果这个答案到目前为止对任何人都有帮助,那么我会抽出时间组织和发布剩下的部分。

我相信,沿途建立的例行程序和知识将为您提供所需的工具,从头开始理解和实现您自己的独立Telegram API代码。

完成交互转储

 03:33:26.591 Connect:Success:0 03:33:26.593 Connected to 149.154.167.40:443 03:33:26.598 raw_data: 000000000000000000DC799836FE075614000000789746604479257F6C01C039A3DEAD031BC2D6A4 auth_key_id: 0000000000000000 0 message_id: 5607FE369879DC00 6199202922538589184 data_length: 00000014 20 message_data: 789746604479257F6C01C039A3DEAD031BC2D6A4 message_type: 60469778 03:33:26.600 Send:Success:42 03:33:26.735 Receive:Success:85 03:33:26.737 raw_data: 0000000000000000015CF64539FE075640000000632416054479257F6C01C039A3DEAD031BC2D6A44F9DB065B36308CF4D9965725DD7153D0818DDCAF407B7CDCD00000015C4B51C01000000216BE86C022BB4C3 auth_key_id: 0000000000000000 0 message_id: 5607FE3945F65C01 6199202934039141377 data_length: 00000040 64 message_data: 632416054479257F6C01C039A3DEAD031BC2D6A44F9DB065B36308CF4D9965725DD7153D0818DDCAF407B7CDCD00000015C4B51C01000000216BE86C022BB4C3 message_type: 05162463 03:33:26.743 classid: resPQ#05162463 nonce: 4479257F6C01C039A3DEAD031BC2D6A4 server_nonce: 4F9DB065B36308CF4D9965725DD7153D pq: 18DDCAF407B7CDCD 1791811376213642701 count: 00000001 1 fingerprints: C3B42B026CE86B21 14101943622620965665 03:33:26.810 PQ: 18DDCAF407B7CDCD 1791811376213642701 P: 45F57B87 1173715847 Q: 5AFE490B 1526614283 03:33:26.930 raw_data: auth_key_id: 0000000000000000 0 message_id: 5607FE36EE3C4000 6199202923977392128 data_length: 00000140 320 message_data: BEE message_type: D712E4BE 03:33:26.933 Send:Success:341 03:33:27.217 Receive:Success:656 03:33:27.217 raw_data: auth_key_id: 0000000000000000 0 message_id: 5607FE399F9A1C01 6199202935543045121 data_length: 00000278 632 message_data: message_type: D0E8075C 03:33:27.240 classid: server_DH_params_ok#D0E8075C nonce: 4479257F6C01C039A3DEAD031BC2D6A4 server_nonce: 4F9DB065B36308CF4D9965725DD7153D enc_answer: tmp_aes_key: 297CB750FF0052B67515B3F11B45F11F15D106BC25ED0027570D5B9D83102BFA tmp_aes_iv: CBDCF40A77B6A1C7CE74A1F8EC8E091A49FAD3B9A2499BFFFD084D537A53B36D answer: BA classid: Server_DH_inner_data#B5890DBA nonce: 4479257F6C01C039A3DEAD031BC2D6A4 server_nonce: 4F9DB065B36308CF4D9965725DD7153D g: 00000003 3 dh_prime: C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B g_a: C49E858CA0107FF9B51DC88236370866BE4A69DDC2193930769C11722D2884CE5017AF60712B6BAC17F79DBA8701A25AAA901FDCB483C56A246C1CA7705FAA87F0AFD68EAC8FC5EC88307298DAF7252DD6D8630BF819D65F9E4B5624B6A05149B35B8509A63C2F2D05417F38DD0A90727F5D12CC4D213B5974C732FB261F6AC01426F2B7269C17230442AA8C9AFCCD927463C4EC8465F841D969F0C47FC270D8EC23B1F5D861EB6A5602CF6F87A02A56A4094E06509503CACE935461086668AC32E8C69A90EB19C3232B20635DFADFC6E4EDC11FA34A3E2E2BBA28DDCEF422120077D3A171A6A5B65744113AF0D0A1FC566D31DBBDB43F5DE35A7CE5F0BB0ECD server_time: 5607FE39 1443364409 client_time: 5607FE37 1443364407 padding: 646CF781176C3EAC DH_Prime is Safe? = True classid: Client_DH_Inner_Data#6643B654 nonce: 4479257F6C01C039A3DEAD031BC2D6A4 server_nonce: 4F9DB065B36308CF4D9965725DD7153D retry_id: 0000000000000000 0 g_b: 923A21384FE0318D569B2F2BEA667D1A999050A0A1B5AFDA39F2B890DEE45F9ED08E319C8243CD1496269CCF956DFA6C98633BDC2E26B1675C15D7904417EC2A74C687E682ED14182178BC0BD189F6E020131C87FD42A24798FCCD2416348EE0AAF534B652175BAC33E89C82874A8C3E8562815DDA213610167B10153EFC1BD1A0CFBACFEA22E3E8D80917F262D2C67BF1327A245CF7FE0E299F7517EE6A2F65568630A6191FEB0C1254F260A6554ED2BEE19E94AAB693E58DD032C26B9CAFEB0482F12DE2573B6E6D2816AC37ADDF3B99525FDBAF94690926320CC67ABF35D3EA6EC6CC7211BAF11FBDD6897959F6F1E3D4335B89B3024C1B3C0066246B5DCD 03:33:27.590 enc_data: 03:33:27.810 auth_Key: 87A801A14AD6426E6AD56B638B315DF9F5B66F77333DC8C0FAADB77A1D51E71B68F5BB9B21DB275F2C4CA495E6440DDEACBDB199C52C327F7E2E9D78921E0D632CCA63DB6384FAF387E9D41717899EE5D54609C2F88573BBE8128FB5864CB62BC7F0ED250CBB57929AA5198FE568FC76FB846262A505B42D04BCB87C9EB24007CE9F9BDEB79391E7E9425F3A3D5028410E129C078EB8644EAB770F8705D8228CFAEAA4478A0D8E326971C7C2223074C4302C1F1DE5D08AC00CBEBEE41981B57A4248B517386DE68A51D01087F0E58D75A4C0FD2D031BC5BFC08651C4133494B572150EDD1C486153E8F51F99771DD57F55B3A5BBAE1874F25E69150C4E3C1397 03:33:27.813 raw_data: auth_key_id: 0000000000000000 0 message_id: 5607FE37D0489C00 6199202927769852928 data_length: 00000178 376 message_data: message_type: F5045F1F 03:33:27.823 Send:Success:397 03:33:27.983 Receive:Success:73 03:33:27.985 

….

问候。

我已经写了一个mtproto的PHP实现,到目前为止我已经设法实现了TL序列化/反序列化,tcp abridged / intermediate / full连接,http / https连接。

我已经实现了所有mtproto方法的OOP包装,基于Python / wolfram alpha / php的主要生成模块,HTML / Markdown解析支持提及,bot API文件ID支持,更新处理回调或getupdates,bot API < – > MTProto对象转换,上传/下载包装,僵尸/用户的登录包装(支持2FA),简单的错误处理,内部对等管理(你可以提供一个简单的机器人API聊天ID,用户名或tg-cli格式的对等ID发送消息或调用其他mtproto方法),我目前正在使用一个漂亮的Lua包装器,以允许使用td-cli / tg-cli机器人/ MadelineProto。

我还写了一个类来生成所有mtproto方法/构造函数/类型的文档(可在https://daniil.it/MadelineProto/API_docs上找到; )。 MadelineProto可以被序列化为一个文件,以允许简单的会话存储。

如果任何人有兴趣,或想贡献,这里是链接到github回购: https : //github.com/danog/MadelineProto

作为我原来的文章的后续,一旦你完成了auth_key步骤,以下将有助于快速了解电报的其他部分。

1)下载并研究Webogram的源代码,基本都是JavaScript。

2)运行一个用console.logs修改的网络图的本地拷贝,清楚地向你展示“正在工作的电报客户端”和电报服务器之间发生的交互。 这将给你一个电报客户和电报如何真正工作的工作流程。

3)使用任何您最喜欢的开发语言来构建自己的协议解析器的实现,以将二进制流转换为TL类型,反之亦然

编码这一步其实很有趣。 我刚刚在Elixir中构建了一个简单的Protocol Parser

4)这里是一个从网络图的修改副本的示例日志。

它显示了成功生成Auth_key后要执行的步骤

你甚至可以看到它在哪里生成一个新的Auth_key在飞行中,因为它将验证的你切换到适当的DataCenter,重新创建他的会话,新的server_salts等(未显示)

问候。

 ID time info log 121 12:26:35.318 new Auth Auth successfull! dc= 2 mtproto.js:479:15 127 12:26:35.325 CALL =>> API call help.getNearestDc Object { } Object { dcID: 2, createNetworker: true } mtproto.js:749:4 128 12:26:35.327 enc-START [invokeWithLayer] 45 mtproto.js:753:7 168 12:26:35.551 SEND POST XHR http://149.154.167.51/apiw1 [HTTP/1.1 200 OK 708ms] 189 12:26:36.522 msg-r <<=m [msg_container] Object { _: "msg_container", messages: Array[2] } mtproto.js:1452:5 190 12:26:36.523 msg-r <<=m [message] Object { _: "message", msg_id: "6242200678614200321", seqno: 1, bytes: 28, body: Object } mtproto.js:1452:5 191 12:26:36.525 msg-r <<=m [new_session_created] Object { _: "new_session_created", first_msg_id: "6242200671500883244", unique_id: "645797764649391412", server_salt: "17212767594123551779" } mtproto.js:1452:5 192 12:26:36.526 msg-r <<=m [message] Object { _: "message", msg_id: "6242200678630513665", seqno: 3, bytes: 28, body: Object } mtproto.js:1452:5 193 12:26:36.528 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200671500883244", result: Object } mtproto.js:1452:5 257 12:26:39.239 new Auth Auth successfull! dc= 4 mtproto.js:479:15 270 12:26:59.606 CALL =>> API call auth.sendCode Object { phone_number: "2348022002298", sms_type: 5, api_id: 38665, api_hash: "880c7847a517fc455d7d54731e90ad4e", lang_code: "en-US" } Object { dcID: 2, createNetworker: true } mtproto.js:749:4 321 12:27:00.048 SEND POST XHR http://149.154.167.51/apiw1 [HTTP/1.1 200 OK 422ms] 328 12:27:00.981 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200779464550396", result: Object } mtproto.js:1452:5 331 12:27:00.986 CALL =>> API call auth.sendCode Object { phone_number: "2348022002298", sms_type: 5, api_id: 38665, api_hash: "880c7847a517fc455d7d54731e90ad4e", lang_code: "en-US" } Object { dcID: "4", createNetworker: true, resultType: "auth.SentCode", messageID: "6242200779464550396" } mtproto.js:749:4 332 12:27:00.987 enc-START [invokeWithLayer] 45 mtproto.js:753:7 377 12:27:01.288 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 321ms] 398 12:27:02.170 msg-r <<=m [msg_container] Object { _: "msg_container", messages: Array[2] } mtproto.js:1452:5 399 12:27:02.171 msg-r <<=m [message] Object { _: "message", msg_id: "6242200787684582401", seqno: 1, bytes: 28, body: Object } mtproto.js:1452:5 400 12:27:02.172 msg-r <<=m [new_session_created] Object { _: "new_session_created", first_msg_id: "6242200784556418516", unique_id: "13396961033572361155", server_salt: "7076519506215495914" } mtproto.js:1452:5 401 12:27:02.173 msg-r <<=m [message] Object { _: "message", msg_id: "6242200787775901697", seqno: 3, bytes: 48, body: Object } mtproto.js:1452:5 402 12:27:02.174 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200784556418516", result: Object } mtproto.js:1452:5 407 12:27:02.344 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 25154ms] 414 12:27:03.068 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200779464550396" } mtproto.js:1452:5 420 12:27:15.639 CALL =>> API call auth.sendSms Object { phone_number: "2348022002298", phone_code_hash: "696b5befd8e809de81" } Object { dcID: "4", createNetworker: true, resultType: "auth.SentCode", messageID: "6242200784556418516" } mtproto.js:749:4 468 12:27:15.924 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 457ms] 475 12:27:17.154 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200848242727644", result: true } mtproto.js:1452:5 482 12:27:27.511 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200848242727644" } mtproto.js:1452:5 505 12:27:31.003 SEND POST XHR http://149.154.167.51/apiw1 [HTTP/1.1 200 OK 25161ms] 511 12:27:40.133 CALL =>> API call auth.signIn Object { phone_number: "2348022002298", phone_code_hash: "696b5befd8e809de81", phone_code: "26914" } Object { dcID: "4", createNetworker: true, resultType: "Bool", messageID: "6242200848242727644" } mtproto.js:749:4 560 12:27:40.454 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 756ms] 567 12:27:41.223 msg-r <<=m [msg_container] Object { _: "msg_container", messages: Array[2] } mtproto.js:1452:5 568 12:27:41.224 msg-r <<=m [message] Object { _: "message", msg_id: "6242200956100910081", seqno: 7, bytes: 80, body: Object } mtproto.js:1452:5 569 12:27:41.225 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200954555850604", result: Object } mtproto.js:1452:5 570 12:27:41.226 msg-r <<=m [message] Object { _: "message", msg_id: "6242200956751929345", seqno: 9, bytes: 88, body: Object } mtproto.js:1452:5 571 12:27:41.227 msg-r <<=m [updates] Object { _: "updates", updates: Array[1], users: Array[0], chats: Array[0], date: 1453375666, seq: 2 } mtproto.js:1452:5 579 12:27:41.758 CALL =>> API call updates.getState Object { } Object { noErrorBox: true } mtproto.js:749:4 586 12:27:41.842 CALL =>> API call account.updateStatus Object { offline: false } Object { noErrorBox: true } mtproto.js:749:4 594 12:27:41.850 CALL =>> API call messages.getDialogs Object { offset_date: 0, offset_id: 0, offset_peer: Object, limit: 20 } Object { timeout: 300 } mtproto.js:749:4 655 12:27:42.965 CALL =>> API call messages.getAllStickers Object { hash: "" } Object { } mtproto.js:749:4 696 12:27:43.920 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 908ms] 712 12:27:44.613 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 303ms] 727 12:27:46.488 msg-r <<=m [msg_container] Object { _: "msg_container", messages: Array[3] } mtproto.js:1452:5 728 12:27:46.489 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970862778369", seqno: 11, bytes: 36, body: Object } mtproto.js:1452:5 729 12:27:46.489 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960159395644", result: Object } mtproto.js:1452:5 730 12:27:46.490 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970891641857", seqno: 13, bytes: 776, body: Object } mtproto.js:1452:5 731 12:27:46.492 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960356118244", result: Object } mtproto.js:1452:5 732 12:27:46.494 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970897958913", seqno: 15, bytes: 16, body: Object } mtproto.js:1452:5 733 12:27:46.495 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960337591900", result: true } mtproto.js:1452:5 740 12:27:46.567 msg-r <<=m [msg_container] Object { _: "msg_container", messages: Array[4] } mtproto.js:1452:5 741 12:27:46.568 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970862778369", seqno: 11, bytes: 36, body: Object } mtproto.js:1452:5 742 12:27:46.569 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960159395644" } mtproto.js:1452:5 743 12:27:46.569 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970891641857", seqno: 13, bytes: 776, body: Object } mtproto.js:1452:5 744 12:27:46.570 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960356118244" } mtproto.js:1452:5 745 12:27:46.571 msg-r <<=m [message] Object { _: "message", msg_id: "6242200970897958913", seqno: 15, bytes: 16, body: Object } mtproto.js:1452:5 746 12:27:46.572 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200960337591900" } mtproto.js:1452:5 747 12:27:46.573 msg-r <<=m [message] Object { _: "message", msg_id: "6242200973791209473", seqno: 17, bytes: 76, body: Object } mtproto.js:1452:5 748 12:27:46.574 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200964892679524", result: Object } mtproto.js:1452:5 754 12:27:46.584 CALL =>> API call messages.getStickerSet Object { stickerset: Object } Object { } mtproto.js:749:4 811 12:27:47.346 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 539ms] 818 12:27:48.175 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242200981275507620", result: Object } mtproto.js:1452:5 857 12:27:49.351 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 25141ms] 864 12:27:57.219 msg-r <<=m [msgs_ack] Object { _: "msgs_ack", msg_ids: Array[0] } mtproto.js:1452:5 870 12:28:04.673 CALL =>> API call messages.readHistory Object { peer: Object, max_id: 0 } Object { } mtproto.js:749:4 908 12:28:04.860 CALL =>> API call messages.getHistory Object { peer: Object, offset_id: 16, add_offset: 0, limit: 20 } Object { timeout: 300, noErrorBox: true } mtproto.js:749:4 922 12:28:05.713 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 367ms] 929 12:28:06.096 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242201058769372356", result: Object } mtproto.js:1452:5 936 12:28:15.702 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242201058769372356" } mtproto.js:1452:5 977 12:28:16.122 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 25170ms] 983 12:28:26.637 CALL =>> API call account.updateStatus Object { offline: true } Object { noErrorBox: true } mtproto.js:749:4 1016 12:28:26.660 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 302ms] 1023 12:28:27.062 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242201153183272948", result: true } mtproto.js:1452:5 1030 12:28:41.985 msg-r <<=m [rpc_result] Object { _: "rpc_result", req_msg_id: "6242201153183272948" } mtproto.js:1452:5 1071 12:28:42.421 SEND POST XHR http://149.154.167.91/apiw1 [HTTP/1.1 200 OK 25165ms]