mtproto 0.0.19 → 0.0.20
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/client/api/get_bot_callback_answer.rb +22 -0
- data/lib/mtproto/client/api/get_channel_difference.rb +22 -0
- data/lib/mtproto/client/api/import_bot_authorization.rb +27 -0
- data/lib/mtproto/client/api/send_message.rb +18 -3
- data/lib/mtproto/client/api.rb +21 -9
- data/lib/mtproto/client/rpc.rb +13 -1
- data/lib/mtproto/client.rb +129 -34
- data/lib/mtproto/dc.rb +31 -0
- data/lib/mtproto/markdown.rb +439 -0
- data/lib/mtproto/tl/constructors.rb +24 -0
- data/lib/mtproto/tl/message_entity.rb +154 -0
- data/lib/mtproto/tl/objects/bot_callback_answer.rb +75 -0
- data/lib/mtproto/tl/objects/channel_difference.rb +163 -0
- data/lib/mtproto/tl/objects/channels_edit_banned.rb +23 -6
- data/lib/mtproto/tl/objects/get_bot_info.rb +58 -0
- data/lib/mtproto/tl/objects/message.rb +26 -6
- data/lib/mtproto/tl/objects/messages_add_chat_user.rb +27 -0
- data/lib/mtproto/tl/objects/messages_create_chat.rb +47 -0
- data/lib/mtproto/tl/objects/reply_keyboard_markup.rb +26 -1
- data/lib/mtproto/tl/objects/send_message.rb +37 -3
- data/lib/mtproto/tl/objects/update.rb +15 -0
- data/lib/mtproto/tl/reader.rb +35 -8
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +3 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c0529af4d0277b14c02e0a618a865e67234aaf956873d749579f0aae5d2b4c8
|
|
4
|
+
data.tar.gz: c519588e92e960402dfe515e9b202e8f9c83a05ca6a21952a05d339b031c8878
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 782793b348cdd81fd8bfb43ceedd459bef20c0c2033c5b36068a392db9b3cc796273c0debfe9ddf0a1c2965b6b7aebe4a591f8b6afc35fed180e116c40d24a34
|
|
7
|
+
data.tar.gz: e2221dcca9e4e15f92b88140ef5eeb523e838a1a88687497e87a0241b2c196f2a6c70b841fbfecca36fccbb2ac6f68fa2be912dbe6cdc94f2c32a7175cec9cc5
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_bot_callback_answer'
|
|
4
|
+
require_relative '../../tl/objects/bot_callback_answer'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
# messages.getBotCallbackAnswer — press the inline callback button carrying
|
|
10
|
+
# `data` on message `msg_id` in `peer`. The server delivers an
|
|
11
|
+
# updateBotCallbackQuery to the bot and blocks until the bot answers (or the
|
|
12
|
+
# ~5s timeout), then returns the bot's botCallbackAnswer. This is the client
|
|
13
|
+
# half of "bots manage bots": the caller drives another bot's inline keyboard.
|
|
14
|
+
def get_bot_callback_answer(peer:, msg_id:, data: nil)
|
|
15
|
+
rpc_call(
|
|
16
|
+
TL::GetBotCallbackAnswer.new(peer: peer, msg_id: msg_id, data: data),
|
|
17
|
+
TL::BotCallbackAnswer
|
|
18
|
+
).body
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_channel_difference'
|
|
4
|
+
require_relative '../../tl/objects/channel_difference'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
# updates.getChannelDifference — pull a channel/supergroup's backlog since
|
|
10
|
+
# `pts` (seed it from the dialog's pts). Returns a parsed ChannelDifference.
|
|
11
|
+
# `force` re-fetches even without a pending gap.
|
|
12
|
+
def get_channel_difference(channel:, pts:, limit: 100, force: false)
|
|
13
|
+
rpc_call(
|
|
14
|
+
TL::GetChannelDifference.new(
|
|
15
|
+
channel: channel, pts: pts, limit: limit, force: force
|
|
16
|
+
),
|
|
17
|
+
TL::ChannelDifference
|
|
18
|
+
).body
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/import_bot_authorization'
|
|
4
|
+
require_relative '../../tl/objects/authorization'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def import_bot_authorization(bot_auth_token)
|
|
10
|
+
result = rpc_call(
|
|
11
|
+
TL::ImportBotAuthorization.new(
|
|
12
|
+
api_id: @client.api_id,
|
|
13
|
+
api_hash: @client.api_hash,
|
|
14
|
+
bot_auth_token: bot_auth_token
|
|
15
|
+
),
|
|
16
|
+
TL::Authorization
|
|
17
|
+
).body
|
|
18
|
+
|
|
19
|
+
if result.authorization? && result.user_id
|
|
20
|
+
@client.update_user(user_id: result.user_id, access_hash: result.access_hash)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -2,13 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../../tl/objects/send_message'
|
|
4
4
|
require_relative '../../tl/objects/update_short_sent_message'
|
|
5
|
+
require_relative '../../markdown'
|
|
5
6
|
|
|
6
7
|
module MTProto
|
|
7
8
|
class Client
|
|
8
9
|
class API
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# Send a text message. Formatting can be supplied three ways:
|
|
11
|
+
# - `entities:` — explicit formatting spans;
|
|
12
|
+
# - `parse_mode: :markdown` — parse Markdown out of `message` on the client
|
|
13
|
+
# (the plain text + derived entities are sent);
|
|
14
|
+
# - `rich_markdown:` — Telegram's server-side "Rich Formatting": the raw
|
|
15
|
+
# Markdown string is sent as inputRichMessageMarkdown and the SERVER formats
|
|
16
|
+
# it (`message`/`entities` are ignored). Note: server-gated (premium).
|
|
17
|
+
# A `reply_to` (with optional `quote_text`/`quote_offset`) replies to a message,
|
|
18
|
+
# optionally pinning the quoted fragment.
|
|
19
|
+
def send_message(peer:, message: '', random_id: nil, reply_to: nil, entities: nil,
|
|
20
|
+
parse_mode: nil, rich_markdown: nil, quote_text: nil, quote_offset: nil)
|
|
21
|
+
message, entities = Markdown.parse(message) if parse_mode && entities.nil? && rich_markdown.nil?
|
|
22
|
+
rpc_call(
|
|
23
|
+
TL::SendMessage.new(peer: peer, message: message, random_id: random_id, reply_to: reply_to,
|
|
24
|
+
entities: entities, rich_markdown: rich_markdown, quote_text: quote_text, quote_offset: quote_offset),
|
|
25
|
+
TL::UpdateShortSentMessage
|
|
26
|
+
).body
|
|
12
27
|
end
|
|
13
28
|
end
|
|
14
29
|
end
|
data/lib/mtproto/client/api.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative 'api/send_code'
|
|
|
4
4
|
require_relative 'api/sign_in'
|
|
5
5
|
require_relative 'api/export_authorization'
|
|
6
6
|
require_relative 'api/import_authorization'
|
|
7
|
+
require_relative 'api/import_bot_authorization'
|
|
7
8
|
require_relative 'api/export_login_token'
|
|
8
9
|
require_relative 'api/import_login_token'
|
|
9
10
|
require_relative 'api/check_password'
|
|
@@ -12,6 +13,8 @@ require_relative 'api/get_dialogs'
|
|
|
12
13
|
require_relative 'api/get_history'
|
|
13
14
|
require_relative 'api/get_updates_state'
|
|
14
15
|
require_relative 'api/get_updates_difference'
|
|
16
|
+
require_relative 'api/get_channel_difference'
|
|
17
|
+
require_relative 'api/get_bot_callback_answer'
|
|
15
18
|
require_relative 'api/send_message'
|
|
16
19
|
require_relative 'api/get_contacts'
|
|
17
20
|
|
|
@@ -25,15 +28,24 @@ module MTProto
|
|
|
25
28
|
private
|
|
26
29
|
|
|
27
30
|
def rpc_call(request, response_class)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
reinited = false
|
|
32
|
+
begin
|
|
33
|
+
response = @client.rpc.call(request, response_class)
|
|
34
|
+
response.wait!(@client.timeout)
|
|
35
|
+
response
|
|
36
|
+
rescue RpcError => e
|
|
37
|
+
if e.flood_wait?
|
|
38
|
+
warn "[MTProto] FLOOD_WAIT: retrying in #{e.flood_wait_seconds}s..."
|
|
39
|
+
sleep(e.flood_wait_seconds)
|
|
40
|
+
retry
|
|
41
|
+
elsif e.error_message == 'CONNECTION_NOT_INITED' && !reinited
|
|
42
|
+
reinited = true
|
|
43
|
+
@client.reinit_connection!
|
|
44
|
+
retry
|
|
45
|
+
else
|
|
46
|
+
raise
|
|
47
|
+
end
|
|
48
|
+
end
|
|
37
49
|
end
|
|
38
50
|
end
|
|
39
51
|
end
|
data/lib/mtproto/client/rpc.rb
CHANGED
|
@@ -36,8 +36,14 @@ module MTProto
|
|
|
36
36
|
new_server_salt = response_body[20, 8].unpack1('Q<')
|
|
37
37
|
|
|
38
38
|
client.update_server_salt(new_server_salt)
|
|
39
|
+
resend(bad_msg_id)
|
|
40
|
+
end
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
# Re-send an already-sent request (still tracked in @pending_requests) under
|
|
43
|
+
# a fresh msg_id, keeping the same Response so its caller's wait! resolves on
|
|
44
|
+
# the new reply. Used after bad_server_salt / bad_msg_notification.
|
|
45
|
+
def resend(msg_id)
|
|
46
|
+
response = @pending_requests.delete(msg_id)
|
|
41
47
|
return unless response
|
|
42
48
|
|
|
43
49
|
new_msg_id = send_encrypted(response.body_bytes)
|
|
@@ -45,6 +51,12 @@ module MTProto
|
|
|
45
51
|
@pending_requests[new_msg_id] = response
|
|
46
52
|
end
|
|
47
53
|
|
|
54
|
+
# Fail a pending request with an error so its caller stops waiting.
|
|
55
|
+
def fail_request(msg_id, error)
|
|
56
|
+
response = @pending_requests.delete(msg_id)
|
|
57
|
+
response&.signal_error(error)
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
def handle_rpc_result(response_body)
|
|
49
61
|
req_msg_id = response_body[4, 8].unpack1('Q<')
|
|
50
62
|
result = response_body[12..]
|
data/lib/mtproto/client.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'digest'
|
|
|
5
5
|
require 'base64'
|
|
6
6
|
require 'async'
|
|
7
7
|
require 'async/condition'
|
|
8
|
+
require_relative 'tl/constructors'
|
|
8
9
|
require_relative 'transport/tcp_connection'
|
|
9
10
|
require_relative 'transport/connection'
|
|
10
11
|
require_relative 'transport/abridged_packet_codec'
|
|
@@ -88,6 +89,11 @@ module MTProto
|
|
|
88
89
|
ACK_INTERVAL = 5
|
|
89
90
|
PING_INTERVAL = 30
|
|
90
91
|
|
|
92
|
+
# Reconnect backoff: wait RECONNECT_BACKOFF_BASE seconds after the first failed
|
|
93
|
+
# attempt, doubling each retry, capped at RECONNECT_BACKOFF_MAX.
|
|
94
|
+
RECONNECT_BACKOFF_BASE = 1
|
|
95
|
+
RECONNECT_BACKOFF_MAX = 30
|
|
96
|
+
|
|
91
97
|
def on_update(&block)
|
|
92
98
|
@on_update_callbacks << block
|
|
93
99
|
end
|
|
@@ -119,23 +125,7 @@ module MTProto
|
|
|
119
125
|
|
|
120
126
|
@running = true
|
|
121
127
|
@ack_ids = []
|
|
122
|
-
@receiver_task = Async
|
|
123
|
-
@connection.receive do |packet, error|
|
|
124
|
-
if error
|
|
125
|
-
warn "[MTProto] Packet read error: #{error.message}"
|
|
126
|
-
next
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
decrypted = EncryptedMessage.decrypt(
|
|
130
|
-
auth_key: @auth_key,
|
|
131
|
-
encrypted_message_data: packet.data.pack('C*'),
|
|
132
|
-
sender: :server
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
collect_ack(decrypted[:msg_id], decrypted[:seq_no], decrypted[:body])
|
|
136
|
-
process_message(decrypted[:body])
|
|
137
|
-
end
|
|
138
|
-
end
|
|
128
|
+
@receiver_task = Async { receiver_loop }
|
|
139
129
|
@keepalive_task = Async { keepalive_loop }
|
|
140
130
|
end
|
|
141
131
|
|
|
@@ -205,6 +195,14 @@ module MTProto
|
|
|
205
195
|
response.body
|
|
206
196
|
end
|
|
207
197
|
|
|
198
|
+
# Force a fresh initConnection on the current connection. Used to recover
|
|
199
|
+
# when the server reports CONNECTION_NOT_INITED (it lost our connection
|
|
200
|
+
# state): reset the guard and run init again.
|
|
201
|
+
def reinit_connection!
|
|
202
|
+
@connection_initialized = false
|
|
203
|
+
init_connection!
|
|
204
|
+
end
|
|
205
|
+
|
|
208
206
|
def save_auth_data
|
|
209
207
|
raise 'Cannot save auth_data: auth_key not set' unless @auth_key
|
|
210
208
|
|
|
@@ -245,22 +243,89 @@ module MTProto
|
|
|
245
243
|
|
|
246
244
|
private
|
|
247
245
|
|
|
248
|
-
#
|
|
249
|
-
# drop
|
|
250
|
-
#
|
|
246
|
+
# Read and dispatch incoming messages for as long as the client runs. On a
|
|
247
|
+
# transport drop, reconnect and resume; the post-reconnect re-init + replay run
|
|
248
|
+
# in a child task so this loop can read the responses those calls wait on.
|
|
249
|
+
def receiver_loop
|
|
250
|
+
while @running
|
|
251
|
+
begin
|
|
252
|
+
@connection.receive { |packet, error| handle_incoming(packet, error) }
|
|
253
|
+
rescue Transport::ConnectionError => e
|
|
254
|
+
break unless @running
|
|
255
|
+
|
|
256
|
+
reconnect_after(e)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def handle_incoming(packet, error)
|
|
262
|
+
if error
|
|
263
|
+
warn "[MTProto] Packet read error: #{error.message}"
|
|
264
|
+
return
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
decrypted = EncryptedMessage.decrypt(
|
|
268
|
+
auth_key: @auth_key,
|
|
269
|
+
encrypted_message_data: packet.data.pack('C*'),
|
|
270
|
+
sender: :server
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
collect_ack(decrypted[:msg_id], decrypted[:seq_no], decrypted[:body])
|
|
274
|
+
process_message(decrypted[:body], decrypted[:msg_id])
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Re-establish the transport and re-run initConnection. The re-init runs in a
|
|
278
|
+
# child task because it waits on a response that only this (resumed) receiver
|
|
279
|
+
# loop can read.
|
|
280
|
+
def reconnect_after(error)
|
|
281
|
+
warn "[MTProto] connection lost (#{error.message}); reconnecting"
|
|
282
|
+
# Fail in-flight requests instead of resending them across the drop: a
|
|
283
|
+
# resend could double-execute a side-effecting request, so callers retry at
|
|
284
|
+
# a higher level. Matches mtproto-rust's reconnect behaviour.
|
|
285
|
+
rpc.signal_all_error(error)
|
|
286
|
+
@connection_initialized = false
|
|
287
|
+
reestablish_connection
|
|
288
|
+
return unless @running
|
|
289
|
+
|
|
290
|
+
Async { init_connection! }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Reconnect the transport, retrying with exponential backoff until it succeeds
|
|
294
|
+
# or the client is shut down.
|
|
295
|
+
def reestablish_connection
|
|
296
|
+
attempt = 0
|
|
297
|
+
while @running
|
|
298
|
+
@connection.disconnect!
|
|
299
|
+
begin
|
|
300
|
+
@connection.connect!
|
|
301
|
+
return
|
|
302
|
+
rescue Transport::ConnectionError, SystemCallError => e
|
|
303
|
+
attempt += 1
|
|
304
|
+
warn "[MTProto] reconnect attempt #{attempt} failed: #{e.message}"
|
|
305
|
+
sleep [RECONNECT_BACKOFF_BASE * (2**(attempt - 1)), RECONNECT_BACKOFF_MAX].min
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Periodically acknowledge received messages and ping so the server keeps the
|
|
311
|
+
# connection. Survives transport drops (a failed tick is skipped; the receiver
|
|
312
|
+
# loop reconnects) and ends when the client is shut down.
|
|
251
313
|
def keepalive_loop
|
|
252
314
|
elapsed = 0
|
|
253
|
-
|
|
315
|
+
while @running
|
|
254
316
|
sleep ACK_INTERVAL
|
|
255
|
-
flush_acks
|
|
256
317
|
elapsed += ACK_INTERVAL
|
|
257
|
-
next if elapsed < PING_INTERVAL
|
|
258
318
|
|
|
259
|
-
|
|
260
|
-
|
|
319
|
+
begin
|
|
320
|
+
flush_acks
|
|
321
|
+
next if elapsed < PING_INTERVAL
|
|
322
|
+
|
|
323
|
+
elapsed = 0
|
|
324
|
+
rpc.send_ping
|
|
325
|
+
rescue Transport::ConnectionError
|
|
326
|
+
nil # connection down/reconnecting — skip this tick
|
|
327
|
+
end
|
|
261
328
|
end
|
|
262
|
-
rescue StandardError
|
|
263
|
-
nil
|
|
264
329
|
end
|
|
265
330
|
|
|
266
331
|
# Queue msg_ids of received content-related server messages for msgs_ack.
|
|
@@ -285,20 +350,50 @@ module MTProto
|
|
|
285
350
|
rpc.send_ack(ids)
|
|
286
351
|
end
|
|
287
352
|
|
|
288
|
-
def process_message(response_body)
|
|
353
|
+
def process_message(response_body, server_msg_id)
|
|
289
354
|
constructor = response_body[0, 4].unpack1('L<')
|
|
290
355
|
|
|
291
356
|
case constructor
|
|
292
|
-
when TL::Constructors::BAD_SERVER_SALT
|
|
293
|
-
rpc.handle_bad_server_salt(response_body, self)
|
|
294
|
-
when TL::Constructors::NEW_SESSION_CREATED
|
|
295
|
-
handle_new_session(response_body)
|
|
296
357
|
when TL::Constructors::MSG_CONTAINER
|
|
297
358
|
handle_container(response_body)
|
|
298
359
|
when TL::Constructors::RPC_RESULT
|
|
299
360
|
rpc.handle_rpc_result(response_body)
|
|
361
|
+
when TL::Constructors::BAD_SERVER_SALT
|
|
362
|
+
rpc.handle_bad_server_salt(response_body, self)
|
|
363
|
+
when TL::Constructors::BAD_MSG_NOTIFICATION
|
|
364
|
+
handle_bad_msg_notification(response_body, server_msg_id)
|
|
365
|
+
when TL::Constructors::NEW_SESSION_CREATED
|
|
366
|
+
handle_new_session(response_body)
|
|
367
|
+
when *TL::Constructors::SERVICE_MESSAGES
|
|
368
|
+
nil # transport/service messages — no app-level action
|
|
369
|
+
else
|
|
370
|
+
dispatch_update(constructor, response_body)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def dispatch_update(constructor, response_body)
|
|
375
|
+
unless TL::Constructors::UPDATE_CONSTRUCTORS.include?(constructor)
|
|
376
|
+
warn "[MTProto] Ignoring unknown server message 0x#{constructor.to_s(16).rjust(8, '0')}"
|
|
377
|
+
return
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
@on_update_callbacks.each { |cb| cb.call(constructor, response_body) }
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int.
|
|
384
|
+
# Codes 16/17 mean our clock is off (msg_id too low/high): resync time_offset
|
|
385
|
+
# from the server msg_id and resend the rejected request. Other codes can't be
|
|
386
|
+
# auto-recovered here, so fail the request loudly instead of leaving it to hang.
|
|
387
|
+
def handle_bad_msg_notification(response_body, server_msg_id)
|
|
388
|
+
bad_msg_id = response_body[4, 8].unpack1('Q<')
|
|
389
|
+
error_code = response_body[16, 4].unpack1('l<')
|
|
390
|
+
|
|
391
|
+
if [16, 17].include?(error_code)
|
|
392
|
+
@time_offset = (server_msg_id >> 32) - Time.now.to_i
|
|
393
|
+
rpc.resend(bad_msg_id)
|
|
300
394
|
else
|
|
301
|
-
|
|
395
|
+
warn "[MTProto] bad_msg_notification code=#{error_code} for msg #{bad_msg_id}"
|
|
396
|
+
rpc.fail_request(bad_msg_id, RpcError.new(0, "BAD_MSG_NOTIFICATION_#{error_code}"))
|
|
302
397
|
end
|
|
303
398
|
end
|
|
304
399
|
|
|
@@ -311,7 +406,7 @@ module MTProto
|
|
|
311
406
|
container = TL::MsgContainer.deserialize(response_body)
|
|
312
407
|
|
|
313
408
|
container.messages.each do |msg|
|
|
314
|
-
process_message(msg[:body])
|
|
409
|
+
process_message(msg[:body], msg[:msg_id])
|
|
315
410
|
end
|
|
316
411
|
end
|
|
317
412
|
end
|
data/lib/mtproto/dc.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
# Datacenter IP addresses by number. An auth key is bound to the DC that
|
|
5
|
+
# issued it, so a saved session must reconnect to that same DC — resolve the
|
|
6
|
+
# host from auth_data[:dc_number] rather than assuming a single endpoint.
|
|
7
|
+
module DC
|
|
8
|
+
PRODUCTION = {
|
|
9
|
+
1 => '149.154.175.53',
|
|
10
|
+
2 => '149.154.167.51',
|
|
11
|
+
3 => '149.154.175.100',
|
|
12
|
+
4 => '149.154.167.91',
|
|
13
|
+
5 => '91.108.56.130'
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
TEST = {
|
|
17
|
+
1 => '149.154.175.40',
|
|
18
|
+
2 => '149.154.167.40',
|
|
19
|
+
3 => '149.154.175.117'
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
DEFAULT_PORT = 443
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def address(dc_number, test:)
|
|
27
|
+
map = test ? TEST : PRODUCTION
|
|
28
|
+
{ host: map[dc_number] || map.fetch(2), port: DEFAULT_PORT }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|