mtproto 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mtproto/auth_key_generator.rb +16 -6
- data/lib/mtproto/client/rpc.rb +141 -0
- data/lib/mtproto/client.rb +36 -181
- data/lib/mtproto/crypto/aes_ige.rb +1 -1
- data/lib/mtproto/crypto/factorization.rb +1 -1
- data/lib/mtproto/message_id.rb +13 -0
- data/lib/mtproto/rpc/get_config.rb +37 -0
- data/lib/mtproto/rpc/get_contacts.rb +22 -0
- data/lib/mtproto/rpc/get_updates_difference.rb +33 -0
- data/lib/mtproto/rpc/get_updates_state.rb +22 -0
- data/lib/mtproto/rpc/get_users.rb +22 -0
- data/lib/mtproto/rpc/ping.rb +26 -0
- data/lib/mtproto/rpc/send_code.rb +44 -0
- data/lib/mtproto/rpc/send_message.rb +31 -0
- data/lib/mtproto/rpc/sign_in.rb +52 -0
- data/lib/mtproto/tl/auth_key/dh_gen_response.rb +37 -0
- data/lib/mtproto/tl/auth_key/req_dh_params.rb +31 -0
- data/lib/mtproto/tl/auth_key/req_pq_multi.rb +18 -0
- data/lib/mtproto/tl/auth_key/res_pq.rb +62 -0
- data/lib/mtproto/tl/auth_key/server_dh_params.rb +43 -0
- data/lib/mtproto/tl/auth_key/set_client_dh_params.rb +25 -0
- data/lib/mtproto/tl/message.rb +2 -216
- data/lib/mtproto/tl/method_builder.rb +29 -0
- data/lib/mtproto/tl/rpc/auth/authorization.rb +107 -0
- data/lib/mtproto/tl/rpc/auth/send_code.rb +28 -0
- data/lib/mtproto/tl/rpc/auth/sent_code.rb +36 -0
- data/lib/mtproto/tl/rpc/auth/sign_in.rb +32 -0
- data/lib/mtproto/tl/rpc/contacts/contacts.rb +155 -0
- data/lib/mtproto/tl/rpc/contacts/get_contacts.rb +18 -0
- data/lib/mtproto/tl/rpc/help/config.rb +35 -0
- data/lib/mtproto/tl/rpc/help/get_config.rb +17 -0
- data/lib/mtproto/tl/rpc/messages/send_message.rb +43 -0
- data/lib/mtproto/tl/rpc/messages/updates.rb +87 -0
- data/lib/mtproto/tl/rpc/ping.rb +18 -0
- data/lib/mtproto/tl/rpc/pong.rb +46 -0
- data/lib/mtproto/tl/rpc/updates/difference.rb +332 -0
- data/lib/mtproto/tl/rpc/updates/get_difference.rb +42 -0
- data/lib/mtproto/tl/rpc/updates/get_state.rb +17 -0
- data/lib/mtproto/tl/rpc/updates/state.rb +59 -0
- data/lib/mtproto/tl/rpc/users/get_users.rb +25 -0
- data/lib/mtproto/tl/rpc/users/users.rb +99 -0
- data/lib/mtproto/updates_poller.rb +111 -0
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +10 -1
- metadata +53 -5
- data/ext/aes_ige/Makefile +0 -273
- data/ext/factorization/Makefile +0 -273
- data/lib/mtproto/connection.rb +0 -103
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2744b720c59f90c3e9d18021ce6096a1c8e559dae949fbd6a9b53948c14f8b5c
|
|
4
|
+
data.tar.gz: 3b9000cabc247db305244b279bc58474466e1549cca3efa07ca1202eaa2d7874
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 874a17be8f75bb305057d0a0cb2c136441f9e8da883c44a99169ad0975c5b745b4323abc84ab6edb96b72f2c8f500ec40536a1a6724f585730501514a2e7475b
|
|
7
|
+
data.tar.gz: 6a693436a7443f789cb73a6fe6ecebf523ee288bb174b297fdfac4274340c9a5a00544299d08f0558028645e4cf80dbfb0c70c844ec47d2bb6e413589a45a5b2
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
require 'digest'
|
|
5
|
+
require_relative 'message_id'
|
|
6
|
+
require_relative 'tl/auth_key/req_pq_multi'
|
|
7
|
+
require_relative 'tl/auth_key/res_pq'
|
|
8
|
+
require_relative 'tl/auth_key/req_dh_params'
|
|
9
|
+
require_relative 'tl/auth_key/server_dh_params'
|
|
10
|
+
require_relative 'tl/auth_key/set_client_dh_params'
|
|
11
|
+
require_relative 'tl/auth_key/dh_gen_response'
|
|
5
12
|
|
|
6
13
|
module MTProto
|
|
7
14
|
class AuthKeyGenerator
|
|
@@ -80,12 +87,13 @@ module MTProto
|
|
|
80
87
|
|
|
81
88
|
def req_pq_multi
|
|
82
89
|
nonce = SecureRandom.random_bytes(16)
|
|
83
|
-
|
|
90
|
+
body = TL::AuthKey::ReqPqMulti.build(nonce)
|
|
91
|
+
message = TL::Message.new(auth_key_id: 0, msg_id: MessageId.generate, body: body)
|
|
84
92
|
@connection.send(message.serialize)
|
|
85
93
|
|
|
86
94
|
response_data = @connection.recv(timeout: @timeout)
|
|
87
95
|
response_message = TL::Message.deserialize(response_data)
|
|
88
|
-
res_pq = response_message.
|
|
96
|
+
res_pq = TL::AuthKey::ResPq.parse(response_message.body)
|
|
89
97
|
|
|
90
98
|
raise 'Nonce mismatch!' unless res_pq[:nonce] == nonce
|
|
91
99
|
|
|
@@ -120,7 +128,7 @@ module MTProto
|
|
|
120
128
|
end
|
|
121
129
|
|
|
122
130
|
def send_req_dh_params(res_pq, p, q, server_key, encrypted_data)
|
|
123
|
-
|
|
131
|
+
body = TL::AuthKey::ReqDHParams.build(
|
|
124
132
|
nonce: res_pq[:nonce],
|
|
125
133
|
server_nonce: res_pq[:server_nonce],
|
|
126
134
|
p: p,
|
|
@@ -128,12 +136,13 @@ module MTProto
|
|
|
128
136
|
public_key_fingerprint: server_key.fingerprint,
|
|
129
137
|
encrypted_data: encrypted_data
|
|
130
138
|
)
|
|
139
|
+
message = TL::Message.new(auth_key_id: 0, msg_id: MessageId.generate, body: body)
|
|
131
140
|
|
|
132
141
|
@connection.send(message.serialize)
|
|
133
142
|
|
|
134
143
|
response_data = @connection.recv(timeout: @timeout)
|
|
135
144
|
response_message = TL::Message.deserialize(response_data)
|
|
136
|
-
server_dh_params = response_message.
|
|
145
|
+
server_dh_params = TL::AuthKey::ServerDHParams.parse(response_message.body)
|
|
137
146
|
|
|
138
147
|
raise 'Nonce mismatch!' unless server_dh_params[:nonce] == res_pq[:nonce]
|
|
139
148
|
raise 'Server nonce mismatch!' unless server_dh_params[:server_nonce] == res_pq[:server_nonce]
|
|
@@ -191,17 +200,18 @@ module MTProto
|
|
|
191
200
|
tmp_aes_iv
|
|
192
201
|
)
|
|
193
202
|
|
|
194
|
-
|
|
203
|
+
body = TL::AuthKey::SetClientDHParams.build(
|
|
195
204
|
nonce: res_pq[:nonce],
|
|
196
205
|
server_nonce: res_pq[:server_nonce],
|
|
197
206
|
encrypted_data: client_dh_encrypted
|
|
198
207
|
)
|
|
208
|
+
message = TL::Message.new(auth_key_id: 0, msg_id: MessageId.generate, body: body)
|
|
199
209
|
|
|
200
210
|
@connection.send(message.serialize)
|
|
201
211
|
|
|
202
212
|
response_data = @connection.recv(timeout: @timeout)
|
|
203
213
|
response_message = TL::Message.deserialize(response_data)
|
|
204
|
-
response_message.
|
|
214
|
+
TL::AuthKey::DHGenResponse.parse(response_message.body)
|
|
205
215
|
end
|
|
206
216
|
|
|
207
217
|
def verify_dh_gen_response(dh_gen_response, res_pq, new_nonce, auth_key)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../message_id'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
class Client
|
|
7
|
+
class RPC
|
|
8
|
+
CONSTRUCTOR_RPC_RESULT = 0xf35c6d01
|
|
9
|
+
CONSTRUCTOR_MSGS_ACK = 0x62d6b459
|
|
10
|
+
CONSTRUCTOR_BAD_SERVER_SALT = 0xedab447b
|
|
11
|
+
|
|
12
|
+
def initialize(client)
|
|
13
|
+
@client = client
|
|
14
|
+
@connection_initialized = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(body, content_related: true)
|
|
18
|
+
raise 'Auth key not generated' unless @client.auth_key
|
|
19
|
+
raise 'Session not initialized' unless @client.session
|
|
20
|
+
|
|
21
|
+
msg_id = MessageId.generate(time_offset: @client.time_offset)
|
|
22
|
+
seq_no = @client.session.next_seq_no(content_related: content_related)
|
|
23
|
+
|
|
24
|
+
encrypted_msg = EncryptedMessage.encrypt(
|
|
25
|
+
auth_key: @client.auth_key,
|
|
26
|
+
server_salt: @client.server_salt,
|
|
27
|
+
session_id: @client.session.session_id,
|
|
28
|
+
msg_id: msg_id,
|
|
29
|
+
seq_no: seq_no,
|
|
30
|
+
body: body
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@client.connection.send(encrypted_msg.serialize)
|
|
34
|
+
|
|
35
|
+
response_data = @client.connection.recv(timeout: @client.timeout)
|
|
36
|
+
|
|
37
|
+
decrypted = EncryptedMessage.decrypt(
|
|
38
|
+
auth_key: @client.auth_key,
|
|
39
|
+
encrypted_message_data: response_data,
|
|
40
|
+
sender: :server
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
response_body = decrypted[:body]
|
|
44
|
+
|
|
45
|
+
constructor = response_body[0, 4].unpack1('L<')
|
|
46
|
+
|
|
47
|
+
# Handle bad_server_salt
|
|
48
|
+
if constructor == CONSTRUCTOR_BAD_SERVER_SALT
|
|
49
|
+
# bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long
|
|
50
|
+
offset = 4
|
|
51
|
+
bad_msg_id = response_body[offset, 8].unpack1('Q<')
|
|
52
|
+
offset += 8
|
|
53
|
+
bad_msg_seqno = response_body[offset, 4].unpack1('L<')
|
|
54
|
+
offset += 4
|
|
55
|
+
error_code = response_body[offset, 4].unpack1('L<')
|
|
56
|
+
offset += 4
|
|
57
|
+
new_server_salt = response_body[offset, 8].unpack1('Q<')
|
|
58
|
+
|
|
59
|
+
# Update server salt and retry
|
|
60
|
+
update_server_salt(new_server_salt)
|
|
61
|
+
return call(body, content_related: content_related)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if constructor == TL::NewSessionCreated::CONSTRUCTOR
|
|
65
|
+
session_info = TL::NewSessionCreated.deserialize(response_body)
|
|
66
|
+
update_server_salt(session_info.server_salt)
|
|
67
|
+
|
|
68
|
+
response_data = @client.connection.recv(timeout: @client.timeout)
|
|
69
|
+
decrypted = EncryptedMessage.decrypt(
|
|
70
|
+
auth_key: @client.auth_key,
|
|
71
|
+
encrypted_message_data: response_data,
|
|
72
|
+
sender: :server
|
|
73
|
+
)
|
|
74
|
+
response_body = decrypted[:body]
|
|
75
|
+
constructor = response_body[0, 4].unpack1('L<')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if constructor == TL::MsgContainer::CONSTRUCTOR
|
|
79
|
+
container = TL::MsgContainer.deserialize(response_body)
|
|
80
|
+
|
|
81
|
+
rpc_result = container.messages.find do |msg|
|
|
82
|
+
msg[:body][0, 4].unpack1('L<') == CONSTRUCTOR_RPC_RESULT
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return rpc_result[:body][12..] if rpc_result
|
|
86
|
+
|
|
87
|
+
new_session = container.messages.find do |msg|
|
|
88
|
+
msg[:body][0, 4].unpack1('L<') == TL::NewSessionCreated::CONSTRUCTOR
|
|
89
|
+
end
|
|
90
|
+
if new_session
|
|
91
|
+
session_info = TL::NewSessionCreated.deserialize(new_session[:body])
|
|
92
|
+
update_server_salt(session_info.server_salt)
|
|
93
|
+
|
|
94
|
+
other_messages = container.messages.reject do |msg|
|
|
95
|
+
constructor = msg[:body][0, 4].unpack1('L<')
|
|
96
|
+
(
|
|
97
|
+
constructor == TL::NewSessionCreated::CONSTRUCTOR ||
|
|
98
|
+
constructor == CONSTRUCTOR_MSGS_ACK
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
return other_messages.first[:body] unless other_messages.empty?
|
|
103
|
+
|
|
104
|
+
response_data = @client.connection.recv(timeout: @client.timeout)
|
|
105
|
+
decrypted = EncryptedMessage.decrypt(
|
|
106
|
+
auth_key: @client.auth_key,
|
|
107
|
+
encrypted_message_data: response_data,
|
|
108
|
+
sender: :server
|
|
109
|
+
)
|
|
110
|
+
response_body = decrypted[:body]
|
|
111
|
+
constructor = response_body[0, 4].unpack1('L<')
|
|
112
|
+
|
|
113
|
+
return response_body[12..] if constructor == CONSTRUCTOR_RPC_RESULT
|
|
114
|
+
|
|
115
|
+
if constructor == TL::MsgContainer::CONSTRUCTOR
|
|
116
|
+
container = TL::MsgContainer.deserialize(response_body)
|
|
117
|
+
rpc_result = container.messages.find do |msg|
|
|
118
|
+
msg[:body][0, 4].unpack1('L<') == CONSTRUCTOR_RPC_RESULT
|
|
119
|
+
end
|
|
120
|
+
return rpc_result[:body][12..] if rpc_result
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
return response_body
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return container.messages.first[:body]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
return response_body[12..] if constructor == CONSTRUCTOR_RPC_RESULT
|
|
130
|
+
|
|
131
|
+
response_body
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
def update_server_salt(server_salt)
|
|
137
|
+
@client.instance_variable_set(:@server_salt, server_salt)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/mtproto/client.rb
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
require 'digest'
|
|
5
|
+
require 'base64'
|
|
5
6
|
require_relative 'transport/tcp_connection'
|
|
6
7
|
require_relative 'transport/abridged_packet_codec'
|
|
7
8
|
require_relative 'tl/message'
|
|
9
|
+
require_relative 'client/rpc'
|
|
10
|
+
require_relative 'tl/method_builder'
|
|
8
11
|
|
|
9
12
|
module MTProto
|
|
10
13
|
class Client
|
|
11
|
-
|
|
12
|
-
attr_accessor :api_id, :api_hash, :device_model, :system_version, :app_version, :system_lang_code, :lang_pack, :lang_code
|
|
14
|
+
include TL::MethodBuilder
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
attr_reader :connection, :server_key, :auth_key, :server_salt, :time_offset, :session, :timeout, :user_id, :access_hash, :dc_number
|
|
17
|
+
attr_accessor :api_id, :api_hash, :device_model, :system_version, :app_version, :system_lang_code, :lang_pack, :lang_code
|
|
15
18
|
|
|
16
19
|
def initialize(api_id: nil, api_hash: nil, host:, port: 443, public_key: nil, dc_number: nil, test_mode: false, timeout: 10)
|
|
17
20
|
raise ArgumentError, "host is required" if host.nil? || host.empty?
|
|
@@ -29,6 +32,8 @@ module MTProto
|
|
|
29
32
|
@time_offset = 0
|
|
30
33
|
@session = nil
|
|
31
34
|
@connection_initialized = false
|
|
35
|
+
@user_id = nil
|
|
36
|
+
@access_hash = nil
|
|
32
37
|
|
|
33
38
|
# Client configuration defaults
|
|
34
39
|
@api_id = api_id || 0
|
|
@@ -49,25 +54,7 @@ module MTProto
|
|
|
49
54
|
@connection.close if @connection
|
|
50
55
|
end
|
|
51
56
|
|
|
52
|
-
def
|
|
53
|
-
nonce = SecureRandom.random_bytes(16)
|
|
54
|
-
|
|
55
|
-
message = TL::Message.req_pq_multi(nonce)
|
|
56
|
-
payload = message.serialize
|
|
57
|
-
|
|
58
|
-
@connection.send(payload)
|
|
59
|
-
|
|
60
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
61
|
-
response_message = TL::Message.deserialize(response_data)
|
|
62
|
-
|
|
63
|
-
res_pq = response_message.parse_res_pq
|
|
64
|
-
|
|
65
|
-
raise 'Nonce mismatch!' unless res_pq[:nonce] == nonce
|
|
66
|
-
|
|
67
|
-
res_pq
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def make_auth_key
|
|
57
|
+
def exchange_keys!
|
|
71
58
|
raise ArgumentError, "public_key is required for auth key generation" if @public_key.nil? || @public_key.empty?
|
|
72
59
|
|
|
73
60
|
generator = AuthKeyGenerator.new(@connection, @public_key, @dc_number, test_mode: @test_mode, timeout: @timeout)
|
|
@@ -81,175 +68,43 @@ module MTProto
|
|
|
81
68
|
result
|
|
82
69
|
end
|
|
83
70
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
msg_id = (time * (2**32)).to_i
|
|
87
|
-
(msg_id / 4) * 4
|
|
71
|
+
def rpc
|
|
72
|
+
@rpc ||= RPC.new(self)
|
|
88
73
|
end
|
|
89
74
|
|
|
90
|
-
def
|
|
91
|
-
raise '
|
|
92
|
-
raise 'Session not initialized' unless @session
|
|
93
|
-
|
|
94
|
-
msg_id = generate_msg_id
|
|
95
|
-
seq_no = @session.next_seq_no(content_related: content_related)
|
|
75
|
+
def save_session
|
|
76
|
+
raise 'Cannot save session: auth_key not set' unless @auth_key
|
|
96
77
|
|
|
97
|
-
|
|
98
|
-
auth_key: @auth_key,
|
|
78
|
+
{
|
|
79
|
+
auth_key: Base64.strict_encode64(@auth_key),
|
|
99
80
|
server_salt: @server_salt,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@connection.send(encrypted_msg.serialize)
|
|
107
|
-
|
|
108
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
109
|
-
|
|
110
|
-
decrypted = EncryptedMessage.decrypt(
|
|
111
|
-
auth_key: @auth_key,
|
|
112
|
-
encrypted_message_data: response_data,
|
|
113
|
-
sender: :server
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
response_body = decrypted[:body]
|
|
117
|
-
|
|
118
|
-
constructor = response_body[0, 4].unpack1('L<')
|
|
119
|
-
|
|
120
|
-
if constructor == TL::NewSessionCreated::CONSTRUCTOR
|
|
121
|
-
session_info = TL::NewSessionCreated.deserialize(response_body)
|
|
122
|
-
@server_salt = session_info.server_salt
|
|
123
|
-
|
|
124
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
125
|
-
decrypted = EncryptedMessage.decrypt(
|
|
126
|
-
auth_key: @auth_key,
|
|
127
|
-
encrypted_message_data: response_data,
|
|
128
|
-
sender: :server
|
|
129
|
-
)
|
|
130
|
-
response_body = decrypted[:body]
|
|
131
|
-
constructor = response_body[0, 4].unpack1('L<')
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
if constructor == TL::Message::CONSTRUCTOR_MSG_CONTAINER
|
|
135
|
-
container = TL::MsgContainer.deserialize(response_body)
|
|
136
|
-
|
|
137
|
-
rpc_result = container.messages.find do |msg|
|
|
138
|
-
msg[:body][0, 4].unpack1('L<') == 0xf35c6d01
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
return rpc_result[:body][12..] if rpc_result
|
|
142
|
-
|
|
143
|
-
new_session = container.messages.find { |msg| msg[:body][0, 4].unpack1('L<') == TL::NewSessionCreated::CONSTRUCTOR }
|
|
144
|
-
if new_session
|
|
145
|
-
session_info = TL::NewSessionCreated.deserialize(new_session[:body])
|
|
146
|
-
@server_salt = session_info.server_salt
|
|
147
|
-
|
|
148
|
-
other_messages = container.messages.reject do |msg|
|
|
149
|
-
constructor = msg[:body][0, 4].unpack1('L<')
|
|
150
|
-
constructor == TL::NewSessionCreated::CONSTRUCTOR || constructor == CONSTRUCTOR_MSGS_ACK
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
if !other_messages.empty?
|
|
154
|
-
return other_messages.first[:body]
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
158
|
-
decrypted = EncryptedMessage.decrypt(
|
|
159
|
-
auth_key: @auth_key,
|
|
160
|
-
encrypted_message_data: response_data,
|
|
161
|
-
sender: :server
|
|
162
|
-
)
|
|
163
|
-
response_body = decrypted[:body]
|
|
164
|
-
constructor = response_body[0, 4].unpack1('L<')
|
|
165
|
-
|
|
166
|
-
return response_body[12..] if constructor == 0xf35c6d01
|
|
167
|
-
|
|
168
|
-
if constructor == TL::Message::CONSTRUCTOR_MSG_CONTAINER
|
|
169
|
-
container = TL::MsgContainer.deserialize(response_body)
|
|
170
|
-
rpc_result = container.messages.find { |msg| msg[:body][0, 4].unpack1('L<') == 0xf35c6d01 }
|
|
171
|
-
return rpc_result[:body][12..] if rpc_result
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
return response_body
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
return container.messages.first[:body]
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
return response_body[12..] if constructor == 0xf35c6d01
|
|
181
|
-
|
|
182
|
-
response_body
|
|
81
|
+
user_id: @user_id,
|
|
82
|
+
access_hash: @access_hash,
|
|
83
|
+
dc_number: @dc_number,
|
|
84
|
+
time_offset: @time_offset
|
|
85
|
+
}
|
|
183
86
|
end
|
|
184
87
|
|
|
185
|
-
def
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
body
|
|
190
|
-
end
|
|
88
|
+
def load_session(session_data)
|
|
89
|
+
raise ArgumentError, 'session_data must be a Hash' unless session_data.is_a?(Hash)
|
|
90
|
+
raise ArgumentError, 'auth_key is required' unless session_data[:auth_key]
|
|
91
|
+
raise ArgumentError, 'server_salt is required' unless session_data[:server_salt]
|
|
191
92
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
body += TL::Serializer.serialize_string(system_lang_code)
|
|
201
|
-
body += TL::Serializer.serialize_string(lang_pack)
|
|
202
|
-
body += TL::Serializer.serialize_string(lang_code)
|
|
203
|
-
body += query
|
|
204
|
-
body
|
|
205
|
-
end
|
|
93
|
+
@auth_key = Base64.strict_decode64(session_data[:auth_key])
|
|
94
|
+
@server_salt = session_data[:server_salt]
|
|
95
|
+
@time_offset = session_data[:time_offset] || 0
|
|
96
|
+
@user_id = session_data[:user_id]
|
|
97
|
+
@access_hash = session_data[:access_hash]
|
|
98
|
+
|
|
99
|
+
# Create a fresh session for the new connection
|
|
100
|
+
@session = Session.new
|
|
206
101
|
|
|
207
|
-
|
|
208
|
-
query = [0xc4f9186b].pack('L<')
|
|
209
|
-
|
|
210
|
-
unless @connection_initialized
|
|
211
|
-
query = init_connection(
|
|
212
|
-
api_id: @api_id,
|
|
213
|
-
device_model: @device_model,
|
|
214
|
-
system_version: @system_version,
|
|
215
|
-
app_version: @app_version,
|
|
216
|
-
system_lang_code: @system_lang_code,
|
|
217
|
-
lang_pack: @lang_pack,
|
|
218
|
-
lang_code: @lang_code,
|
|
219
|
-
query: query
|
|
220
|
-
)
|
|
221
|
-
query = invoke_with_layer(214, query)
|
|
222
|
-
@connection_initialized = true
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
rpc_call(query)
|
|
102
|
+
true
|
|
226
103
|
end
|
|
227
104
|
|
|
228
|
-
def
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
query = [0xa677244f].pack('L<')
|
|
232
|
-
query += TL::Serializer.serialize_string(phone_number)
|
|
233
|
-
query += TL::Serializer.serialize_int(@api_id)
|
|
234
|
-
query += TL::Serializer.serialize_string(@api_hash)
|
|
235
|
-
query += TL::CodeSettings.serialize(code_settings)
|
|
236
|
-
|
|
237
|
-
unless @connection_initialized
|
|
238
|
-
query = init_connection(
|
|
239
|
-
api_id: @api_id,
|
|
240
|
-
device_model: @device_model,
|
|
241
|
-
system_version: @system_version,
|
|
242
|
-
app_version: @app_version,
|
|
243
|
-
system_lang_code: @system_lang_code,
|
|
244
|
-
lang_pack: @lang_pack,
|
|
245
|
-
lang_code: @lang_code,
|
|
246
|
-
query: query
|
|
247
|
-
)
|
|
248
|
-
query = invoke_with_layer(214, query)
|
|
249
|
-
@connection_initialized = true
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
rpc_call(query)
|
|
105
|
+
def update_user(user_id:, access_hash: nil)
|
|
106
|
+
@user_id = user_id
|
|
107
|
+
@access_hash = access_hash
|
|
253
108
|
end
|
|
254
109
|
end
|
|
255
110
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tl/rpc/help/get_config'
|
|
4
|
+
require_relative '../tl/rpc/help/config'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
module RPC
|
|
8
|
+
class GetConfig
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
query = TL::RPC::Help::GetConfig.build
|
|
15
|
+
|
|
16
|
+
unless @client.instance_variable_get(:@connection_initialized)
|
|
17
|
+
query = @client.init_connection(
|
|
18
|
+
api_id: @client.api_id,
|
|
19
|
+
device_model: @client.device_model,
|
|
20
|
+
system_version: @client.system_version,
|
|
21
|
+
app_version: @client.app_version,
|
|
22
|
+
system_lang_code: @client.system_lang_code,
|
|
23
|
+
lang_pack: @client.lang_pack,
|
|
24
|
+
lang_code: @client.lang_code,
|
|
25
|
+
query: query
|
|
26
|
+
)
|
|
27
|
+
query = @client.invoke_with_layer(214, query)
|
|
28
|
+
@client.instance_variable_set(:@connection_initialized, true)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
response = @client.rpc.call(query)
|
|
32
|
+
|
|
33
|
+
TL::RPC::Help::Config.parse(response)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tl/rpc/contacts/get_contacts'
|
|
4
|
+
require_relative '../tl/rpc/contacts/contacts'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
module RPC
|
|
8
|
+
class GetContacts
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(hash: 0)
|
|
14
|
+
raise 'Auth key not generated' unless @client.auth_key
|
|
15
|
+
|
|
16
|
+
query = TL::RPC::Contacts::GetContacts.build(hash: hash)
|
|
17
|
+
response = @client.rpc.call(query)
|
|
18
|
+
TL::RPC::Contacts::Contacts.parse(response)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tl/rpc/updates/get_difference'
|
|
4
|
+
require_relative '../tl/rpc/updates/difference'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
module RPC
|
|
8
|
+
class GetUpdatesDifference
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(pts:, date:, qts:, pts_limit: 100, qts_limit: 100, pts_total_limit: nil)
|
|
14
|
+
raise 'Auth key not generated' unless @client.auth_key
|
|
15
|
+
raise ArgumentError, 'pts is required' if pts.nil?
|
|
16
|
+
raise ArgumentError, 'date is required' if date.nil?
|
|
17
|
+
raise ArgumentError, 'qts is required' if qts.nil?
|
|
18
|
+
|
|
19
|
+
query = TL::RPC::Updates::GetDifference.build(
|
|
20
|
+
pts: pts,
|
|
21
|
+
pts_limit: pts_limit,
|
|
22
|
+
pts_total_limit: pts_total_limit,
|
|
23
|
+
date: date,
|
|
24
|
+
qts: qts,
|
|
25
|
+
qts_limit: qts_limit
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
response = @client.rpc.call(query)
|
|
29
|
+
TL::RPC::Updates::Difference.parse(response)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tl/rpc/updates/get_state'
|
|
4
|
+
require_relative '../tl/rpc/updates/state'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
module RPC
|
|
8
|
+
class GetUpdatesState
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
raise 'Auth key not generated' unless @client.auth_key
|
|
15
|
+
|
|
16
|
+
query = TL::RPC::Updates::GetState.build
|
|
17
|
+
response = @client.rpc.call(query)
|
|
18
|
+
TL::RPC::Updates::State.parse(response)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tl/rpc/users/get_users'
|
|
4
|
+
require_relative '../tl/rpc/users/users'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
module RPC
|
|
8
|
+
class GetUsers
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
raise 'Auth key not generated' unless @client.auth_key
|
|
15
|
+
|
|
16
|
+
query = TL::RPC::Users::GetUsers.build
|
|
17
|
+
response = @client.rpc.call(query)
|
|
18
|
+
TL::RPC::Users::Users.parse(response)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|