mtproto 0.0.32 → 0.0.33

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.
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../schema'
3
+ require_relative '../reader'
4
+ require_relative '../peers'
5
+ require_relative 'message'
6
+ require_relative 'message_service'
4
7
 
5
8
  module MTProto
6
9
  module TL
7
10
  class Dialogs
8
11
  Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, :pts, keyword_init: true)
9
- Chat = Struct.new(:id, :title, :type, :access_hash, :username, keyword_init: true)
10
-
11
- SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
12
12
 
13
13
  attr_reader :dialogs, :chats, :users, :count, :message_dates
14
14
 
@@ -24,10 +24,6 @@ module MTProto
24
24
  !@count.nil?
25
25
  end
26
26
 
27
- def self.schema
28
- @schema ||= Schema.new(SCHEMA_PATH)
29
- end
30
-
31
27
  def self.deserialize(data)
32
28
  constructor = data[0, 4].unpack1('L<')
33
29
 
@@ -45,22 +41,12 @@ module MTProto
45
41
 
46
42
  dialogs, offset = parse_dialogs_vector(data, offset)
47
43
  message_dates, offset = parse_messages_vector(data, offset)
48
- chats, offset = parse_chats_vector(data, offset)
49
- users, = parse_users_vector(data, offset)
44
+ chats, offset = Peers.chats_at(data, offset)
45
+ users, = Peers.users_at(data, offset)
50
46
 
51
47
  new(dialogs: dialogs, chats: chats, users: users, count: count, message_dates: message_dates)
52
48
  end
53
49
 
54
- # Parse a chats:Vector<Chat> / users:Vector<User> at an arbitrary offset.
55
- # Reused by UpdatesDifference — the same vectors appear in updates.difference.
56
- def self.chats_at(data, offset)
57
- parse_chats_vector(data, offset)
58
- end
59
-
60
- def self.users_at(data, offset)
61
- parse_users_vector(data, offset)
62
- end
63
-
64
50
  class << self
65
51
  private
66
52
 
@@ -84,14 +70,14 @@ module MTProto
84
70
  offset += 4
85
71
 
86
72
  unless constructor == Constructors::DIALOG
87
- offset = schema.skip_params(data, offset, constructor)
73
+ offset = Reader.schema.skip_params(data, offset, constructor)
88
74
  return [nil, offset]
89
75
  end
90
76
 
91
77
  flags = data[offset, 4].unpack1('L<')
92
78
  offset += 4
93
79
 
94
- peer_type, peer_id, offset = parse_peer(data, offset)
80
+ peer_type, peer_id, offset = Reader.parse_peer(data, offset)
95
81
 
96
82
  top_message = data[offset, 4].unpack1('L<')
97
83
  offset += 4
@@ -106,14 +92,14 @@ module MTProto
106
92
  offset += 4 # unread_reactions_count
107
93
  offset += 4 # unread_poll_votes_count (added in layer 227)
108
94
 
109
- offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
95
+ offset = Reader.schema.skip(data, offset) # notify_settings (PeerNotifySettings)
110
96
 
111
97
  pts = nil
112
98
  if flags.anybits?(1 << 0)
113
99
  pts = data[offset, 4].unpack1('L<')
114
100
  offset += 4
115
101
  end
116
- offset = schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
102
+ offset = Reader.schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
117
103
  offset += 4 if flags.anybits?(1 << 4) # folder_id
118
104
  offset += 4 if flags.anybits?(1 << 5) # ttl_period
119
105
 
@@ -121,39 +107,8 @@ module MTProto
121
107
  top_message: top_message, unread_count: unread_count, pts: pts), offset]
122
108
  end
123
109
 
124
- def parse_peer(data, offset)
125
- constructor = data[offset, 4].unpack1('L<')
126
- offset += 4
127
-
128
- case constructor
129
- when Constructors::PEER_USER
130
- [:user, data[offset, 8].unpack1('Q<'), offset + 8]
131
- when Constructors::PEER_CHAT
132
- [:chat, data[offset, 8].unpack1('Q<'), offset + 8]
133
- when Constructors::PEER_CHANNEL
134
- [:channel, data[offset, 8].unpack1('Q<'), offset + 8]
135
- else
136
- raise "Unknown peer constructor: 0x#{constructor.to_s(16)}"
137
- end
138
- end
139
-
140
110
  # --- Messages vector ---
141
111
 
142
- # message#7600b9d3 flags:# flags2:#
143
- # id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int
144
- # from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer
145
- # fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long
146
- # via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer
147
- # reply_to:flags.3?MessageReplyHeader date:int ...
148
- #
149
- # messageService#2b085862 flags:#
150
- # id:int from_id:flags.8?Peer peer_id:Peer
151
- # saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader
152
- # date:int ...
153
- MESSAGE_CONSTRUCTOR = Constructors::MESSAGE
154
- MESSAGE_SERVICE = 0x7a800e0a
155
- MESSAGE_EMPTY = 0x90a6ca84
156
-
157
112
  def parse_messages_vector(data, offset)
158
113
  offset += 4 # vector constructor
159
114
  count = data[offset, 4].unpack1('L<')
@@ -165,308 +120,15 @@ module MTProto
165
120
  id_date = extract_message_id_date(data, offset, constructor)
166
121
  dates[id_date[0]] = id_date[1] if id_date
167
122
 
168
- offset = schema.skip(data, offset)
123
+ offset = Reader.schema.skip(data, offset)
169
124
  end
170
125
  [dates, offset]
171
126
  end
172
127
 
173
128
  def extract_message_id_date(data, offset, constructor)
174
- case constructor
175
- when MESSAGE_CONSTRUCTOR
176
- extract_message_date(data, offset)
177
- when MESSAGE_SERVICE
178
- extract_message_service_date(data, offset)
179
- end
180
- end
181
-
182
- def extract_message_date(data, offset)
183
- offset += 4 # constructor
184
- flags = data[offset, 4].unpack1('L<')
185
- offset += 4
186
- flags2 = data[offset, 4].unpack1('L<')
187
- offset += 4
188
- id = data[offset, 4].unpack1('l<')
189
- offset += 4
190
-
191
- offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
192
- offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
193
- _, offset = read_tl_string(data, offset) if flags2.anybits?(1 << 12) # from_rank (layer 227)
194
- offset = schema.skip(data, offset) # peer_id (Peer, always present)
195
- offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
196
- offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
197
- offset += 8 if flags.anybits?(1 << 11) # via_bot_id
198
- offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
199
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 19) # guestchat_via_from (layer 227, Peer)
200
- offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
201
-
202
- date = data[offset, 4].unpack1('L<')
203
- [id, date]
204
- end
205
-
206
- def extract_message_service_date(data, offset)
207
- offset += 4 # constructor
208
- flags = data[offset, 4].unpack1('L<')
209
- offset += 4
210
- id = data[offset, 4].unpack1('l<')
211
- offset += 4
212
-
213
- offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
214
- offset = schema.skip(data, offset) # peer_id (Peer, always present)
215
- offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
216
- offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
217
-
218
- date = data[offset, 4].unpack1('L<')
219
- [id, date]
220
- end
221
-
222
- # --- Chats vector ---
223
-
224
- def parse_chats_vector(data, offset)
225
- offset += 4 # vector constructor
226
- count = data[offset, 4].unpack1('L<')
227
- offset += 4
228
-
229
- chats = []
230
- count.times do
231
- chat, offset = parse_chat(data, offset)
232
- chats << chat if chat
233
- end
234
- [chats, offset]
235
- end
236
-
237
- def parse_chat(data, offset)
238
- constructor = data[offset, 4].unpack1('L<')
239
- offset += 4
240
-
241
- case constructor
242
- when Constructors::CHAT
243
- parse_basic_chat(data, offset)
244
- when Constructors::CHANNEL
245
- parse_channel(data, offset)
246
- when Constructors::CHAT_FORBIDDEN
247
- parse_chat_forbidden(data, offset)
248
- when Constructors::CHANNEL_FORBIDDEN
249
- parse_channel_forbidden(data, offset)
250
- when CHAT_EMPTY
251
- id = data[offset, 8].unpack1('Q<')
252
- offset += 8
253
- [Chat.new(id: id, title: '(empty)', type: :chat_empty), offset]
254
- else
255
- raise "Unknown Chat constructor: 0x#{constructor.to_s(16).rjust(8, '0')}"
256
- end
257
- end
258
-
259
- CHAT_EMPTY = 0x29562865
260
-
261
- # chat#41cbf256 flags:#
262
- # id:long title:string photo:ChatPhoto
263
- # participants_count:int date:int version:int
264
- # migrated_to:flags.6?InputChannel
265
- # admin_rights:flags.14?ChatAdminRights
266
- # default_banned_rights:flags.18?ChatBannedRights
267
- def parse_basic_chat(data, offset)
268
- flags = data[offset, 4].unpack1('L<')
269
- offset += 4
270
- id = data[offset, 8].unpack1('Q<')
271
- offset += 8
272
- title, offset = read_tl_string(data, offset)
273
- offset = schema.skip(data, offset) # photo (ChatPhoto)
274
- offset += 4 # participants_count
275
- offset += 4 # date
276
- offset += 4 # version
277
- offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # migrated_to (InputChannel)
278
- offset = schema.skip(data, offset) if flags.anybits?(1 << 14) # admin_rights
279
- offset = schema.skip(data, offset) if flags.anybits?(1 << 18) # default_banned_rights
280
-
281
- [Chat.new(id: id, title: title, type: :chat), offset]
282
- end
283
-
284
- # channel#1c32b11c flags:# flags2:#
285
- # id:long access_hash:flags.13?long title:string
286
- # username:flags.6?string photo:ChatPhoto date:int
287
- # restriction_reason:flags.9?Vector<RestrictionReason>
288
- # admin_rights:flags.14?ChatAdminRights
289
- # banned_rights:flags.15?ChatBannedRights
290
- # default_banned_rights:flags.18?ChatBannedRights
291
- # participants_count:flags.17?int
292
- # usernames:flags2.0?Vector<Username>
293
- # stories_max_id:flags2.4?int
294
- # color:flags2.7?PeerColor profile_color:flags2.8?PeerColor
295
- # emoji_status:flags2.9?EmojiStatus
296
- # level:flags2.10?int subscription_until_date:flags2.11?int
297
- # bot_verification_icon:flags2.13?long
298
- # send_paid_messages_stars:flags2.14?long
299
- # linked_monoforum_id:flags2.18?long
300
- def parse_channel(data, offset)
301
- flags = data[offset, 4].unpack1('L<')
302
- offset += 4
303
- flags2 = data[offset, 4].unpack1('L<')
304
- offset += 4
305
- id = data[offset, 8].unpack1('Q<')
306
- offset += 8
307
- access_hash = nil
308
- if flags.anybits?(1 << 13)
309
- access_hash = data[offset, 8].unpack1('Q<')
310
- offset += 8
311
- end
312
- title, offset = read_tl_string(data, offset)
313
- username = nil
314
- username, offset = read_tl_string(data, offset) if flags.anybits?(1 << 6) # username
315
- offset = schema.skip(data, offset) # photo (ChatPhoto)
316
- offset += 4 # date
317
- if flags.anybits?(1 << 9) # restriction_reason Vector<RestrictionReason>
318
- offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
319
- end
320
- offset = schema.skip(data, offset) if flags.anybits?(1 << 14) # admin_rights
321
- offset = schema.skip(data, offset) if flags.anybits?(1 << 15) # banned_rights
322
- offset = schema.skip(data, offset) if flags.anybits?(1 << 18) # default_banned_rights
323
- offset += 4 if flags.anybits?(1 << 17) # participants_count
324
- if flags2.anybits?(1 << 0) # usernames Vector<Username>
325
- offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
326
- end
327
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 4) # stories_max_id (layer 227: RecentStory)
328
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 7) # color (PeerColor)
329
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # profile_color (PeerColor)
330
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # emoji_status
331
- offset += 4 if flags2.anybits?(1 << 10) # level
332
- offset += 4 if flags2.anybits?(1 << 11) # subscription_until_date
333
- offset += 8 if flags2.anybits?(1 << 13) # bot_verification_icon (long)
334
- offset += 8 if flags2.anybits?(1 << 14) # send_paid_messages_stars (long)
335
- offset += 8 if flags2.anybits?(1 << 18) # linked_monoforum_id (long)
336
-
337
- type = flags.anybits?(1 << 8) ? :supergroup : :channel
338
- [Chat.new(id: id, title: title, type: type, access_hash: access_hash, username: username), offset]
339
- end
340
-
341
- # chatForbidden#6592a1a7 id:long title:string
342
- def parse_chat_forbidden(data, offset)
343
- id = data[offset, 8].unpack1('Q<')
344
- offset += 8
345
- title, offset = read_tl_string(data, offset)
346
- [Chat.new(id: id, title: title, type: :chat_forbidden), offset]
347
- end
348
-
349
- # channelForbidden#17d493d5 flags:#
350
- # id:long access_hash:long title:string until_date:flags.16?int
351
- def parse_channel_forbidden(data, offset)
352
- flags = data[offset, 4].unpack1('L<')
353
- offset += 4
354
- id = data[offset, 8].unpack1('Q<')
355
- offset += 8
356
- access_hash = data[offset, 8].unpack1('Q<')
357
- offset += 8
358
- title, offset = read_tl_string(data, offset)
359
- offset += 4 if flags.anybits?(1 << 16) # until_date
360
- [Chat.new(id: id, title: title, type: :channel_forbidden, access_hash: access_hash), offset]
361
- end
362
-
363
- # --- Users vector ---
364
-
365
- def parse_users_vector(data, offset)
366
- offset += 4 # vector constructor
367
- count = data[offset, 4].unpack1('L<')
368
- offset += 4
369
-
370
- users = []
371
- count.times do
372
- user, offset = parse_user(data, offset)
373
- users << user if user
374
- end
375
- [users, offset]
376
- end
377
-
378
- USER_EMPTY = 0xd3bc4b7a
379
-
380
- # user#31774388 flags:# flags2:#
381
- # id:long access_hash:flags.0?long
382
- # first_name:flags.1?string last_name:flags.2?string
383
- # username:flags.3?string phone:flags.4?string
384
- # photo:flags.5?UserProfilePhoto status:flags.6?UserStatus
385
- # bot_info_version:flags.14?int
386
- # restriction_reason:flags.18?Vector<RestrictionReason>
387
- # bot_inline_placeholder:flags.19?string
388
- # lang_code:flags.22?string
389
- # emoji_status:flags.30?EmojiStatus
390
- # usernames:flags2.0?Vector<Username>
391
- # stories_max_id:flags2.5?int
392
- # color:flags2.8?PeerColor profile_color:flags2.9?PeerColor
393
- # bot_active_users:flags2.12?int
394
- # bot_verification_icon:flags2.14?long
395
- # send_paid_messages_stars:flags2.15?long
396
- def parse_user(data, offset)
397
- constructor = data[offset, 4].unpack1('L<')
398
- offset += 4
399
-
400
- if constructor == USER_EMPTY
401
- offset += 8 # id (long)
402
- return [nil, offset]
403
- end
404
-
405
- raise "Unknown User constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::USER
406
-
407
- flags = data[offset, 4].unpack1('L<')
408
- offset += 4
409
- flags2 = data[offset, 4].unpack1('L<')
410
- offset += 4
411
- id = data[offset, 8].unpack1('Q<')
412
- offset += 8
413
- access_hash = nil
414
- if flags.anybits?(1 << 0)
415
- access_hash = data[offset, 8].unpack1('Q<')
416
- offset += 8
417
- end
418
-
419
- first_name = nil
420
- first_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 1)
421
-
422
- last_name = nil
423
- last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
424
-
425
- username = nil
426
- username, offset = read_tl_string(data, offset) if flags.anybits?(1 << 3) # username
427
-
428
- offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
429
- offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
430
- offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
431
- offset += 4 if flags.anybits?(1 << 14) # bot_info_version
432
- if flags.anybits?(1 << 18) # restriction_reason
433
- offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
434
- end
435
- offset = skip_tl_string(data, offset) if flags.anybits?(1 << 19) # bot_inline_placeholder
436
- offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
437
- offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
438
- offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0) # usernames
439
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 5) # stories_max_id (layer 227: RecentStory)
440
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
441
- offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
442
- offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
443
- offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
444
- offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
445
-
446
- [{ id: id, first_name: first_name, last_name: last_name, username: username,
447
- is_bot: flags.anybits?(1 << 14), access_hash: access_hash }, offset]
448
- end
449
-
450
- # --- TL string ---
451
-
452
- def read_tl_string(data, offset)
453
- first_byte = data.getbyte(offset)
454
- if first_byte == 254
455
- len = data.getbyte(offset + 1) |
456
- (data.getbyte(offset + 2) << 8) |
457
- (data.getbyte(offset + 3) << 16)
458
- total = 4 + len
459
- else
460
- len = first_byte
461
- total = 1 + len
462
- end
463
- padding = (4 - (total % 4)) % 4
464
- [data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
465
- end
466
-
467
- def skip_tl_string(data, offset)
468
- _, new_offset = read_tl_string(data, offset)
469
- new_offset
129
+ msg = Message.deserialize(data, offset, constructor) ||
130
+ MessageService.deserialize(data, offset, constructor)
131
+ [msg.id, msg.date] if msg
470
132
  end
471
133
  end
472
134
  end
@@ -35,7 +35,7 @@ module MTProto
35
35
  private
36
36
 
37
37
  def entities?
38
- @entities && !@entities.empty?
38
+ !@entities.nil?
39
39
  end
40
40
 
41
41
  def serialize_input_peer
@@ -92,6 +92,20 @@ module MTProto
92
92
  fwd_channel_post: fwd_channel_post)
93
93
  end
94
94
 
95
+ def vector_at(data, offset)
96
+ offset += 4 # vector constructor
97
+ count = data[offset, 4].unpack1('L<')
98
+ offset += 4
99
+
100
+ messages = []
101
+ count.times do
102
+ msg = deserialize(data, offset, data[offset, 4].unpack1('L<'))
103
+ messages << msg if msg
104
+ offset = Reader.schema.skip(data, offset)
105
+ end
106
+ [messages, offset]
107
+ end
108
+
95
109
  private
96
110
 
97
111
  # entities (flags.7) sit past media (flags.9) and reply_markup (flags.6) in the
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../reader'
4
+
5
+ module MTProto
6
+ module TL
7
+ # The header of `messageService#7a800e0a` — the chat event (a join, a leave, a
8
+ # title change) that rides the same Vector<Message> as a plain `message`. The
9
+ # walk stops at the MessageAction, whose meaning belongs to the caller: it reads
10
+ # the action itself, from `action_offset`.
11
+ module MessageService
12
+ module_function
13
+
14
+ MESSAGE_SERVICE = 0x7a800e0a
15
+
16
+ Header = Struct.new(:id, :date, :from_id, :peer_type, :peer_id, :action_offset, keyword_init: true)
17
+
18
+ def deserialize(data, offset, constructor)
19
+ return unless constructor == MESSAGE_SERVICE
20
+
21
+ offset += 4 # constructor
22
+ flags = data[offset, 4].unpack1('L<')
23
+ offset += 4
24
+ id = data[offset, 4].unpack1('l<')
25
+ offset += 4
26
+
27
+ from_id = nil
28
+ _from_type, from_id, offset = Reader.parse_peer(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
29
+ peer_type, peer_id, offset = Reader.parse_peer(data, offset)
30
+ offset = Reader.schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
31
+ offset = Reader.schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
32
+
33
+ date = data[offset, 4].unpack1('L<')
34
+
35
+ Header.new(id: id, date: date, from_id: from_id,
36
+ peer_type: peer_type, peer_id: peer_id, action_offset: offset + 4)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../schema'
3
+ require_relative '../reader'
4
+ require_relative '../peers'
4
5
  require_relative 'message'
5
- require_relative 'dialogs'
6
6
 
7
7
  module MTProto
8
8
  module TL
@@ -29,12 +29,6 @@ module MTProto
29
29
  !@count.nil?
30
30
  end
31
31
 
32
- def self.schema
33
- @schema ||= Schema.new(
34
- File.expand_path('../../../../data/tl-schema.json', __dir__)
35
- )
36
- end
37
-
38
32
  def self.deserialize(data)
39
33
  constructor = data[0, 4].unpack1('L<')
40
34
  raise UnexpectedConstructorError, constructor unless VALID_CONSTRUCTORS.include?(constructor)
@@ -54,7 +48,7 @@ module MTProto
54
48
  offset += 4
55
49
  offset += 4 if flags.anybits?(1 << 0) # next_rate
56
50
  offset += 4 if flags.anybits?(1 << 2) # offset_id_offset
57
- offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # search_flood
51
+ offset = Reader.schema.skip(data, offset) if flags.anybits?(1 << 3) # search_flood
58
52
  when MESSAGES_CHANNEL
59
53
  # flags:# pts:int count:int offset_id_offset:flags.2?int
60
54
  # messages:Vector<Message> topics:Vector<ForumTopic> ...
@@ -66,35 +60,16 @@ module MTProto
66
60
  offset += 4 if flags.anybits?(1 << 2) # offset_id_offset
67
61
  end
68
62
 
69
- messages, offset = parse_messages_vector(data, offset)
63
+ messages, offset = Message.vector_at(data, offset)
70
64
 
71
65
  # topics:Vector<ForumTopic> precedes chats/users in all three constructors
72
- offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
66
+ offset = Reader.schema.skip_vector(data, offset)
73
67
 
74
- chats, offset = Dialogs.chats_at(data, offset)
75
- users, = Dialogs.users_at(data, offset)
68
+ chats, offset = Peers.chats_at(data, offset)
69
+ users, = Peers.users_at(data, offset)
76
70
 
77
71
  new(messages: messages, count: count, users: users, chats: chats)
78
72
  end
79
-
80
- class << self
81
- private
82
-
83
- def parse_messages_vector(data, offset)
84
- offset += 4 # vector constructor
85
- count = data[offset, 4].unpack1('L<')
86
- offset += 4
87
-
88
- messages = []
89
- count.times do
90
- constructor = data[offset, 4].unpack1('L<')
91
- msg = Message.deserialize(data, offset, constructor)
92
- messages << msg if msg
93
- offset = schema.skip(data, offset)
94
- end
95
- [messages, offset]
96
- end
97
- end
98
73
  end
99
74
  end
100
75
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../reader'
4
+ require_relative '../peers'
5
+
6
+ module MTProto
7
+ module TL
8
+ class ResolvedPeer
9
+ def self.deserialize(data)
10
+ constructor = data[0, 4].unpack1('L<')
11
+ raise UnexpectedConstructorError, constructor unless constructor == Constructors::CONTACTS_RESOLVED_PEER
12
+
13
+ type, id, offset = Reader.parse_peer(data, 4)
14
+ chats, offset = Peers.chats_at(data, offset)
15
+ users, = Peers.users_at(data, offset)
16
+
17
+ { type: type, id: id, access_hash: access_hash_for(type, id, chats, users) }
18
+ end
19
+
20
+ def self.access_hash_for(type, id, chats, users)
21
+ return chats.find { |chat| chat.id == id }&.access_hash unless type == :user
22
+
23
+ user = users.find { |candidate| candidate[:id] == id }
24
+ user && user[:access_hash]
25
+ end
26
+ private_class_method :access_hash_for
27
+ end
28
+ end
29
+ end
@@ -52,7 +52,7 @@ module MTProto
52
52
  private
53
53
 
54
54
  def entities?
55
- @entities && !@entities.empty? && !@rich_markdown
55
+ !@entities.nil? && !@rich_markdown
56
56
  end
57
57
 
58
58
  # inputRichMessageMarkdown#004b572c flags:# rtl:flags.0?true noautolink:flags.1?true
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'message'
4
+
3
5
  module MTProto
4
6
  module TL
5
7
  class Update
@@ -50,41 +52,7 @@ module MTProto
50
52
  end
51
53
 
52
54
  def extract_new_message_text(bytes)
53
- offset = 4 # skip updateNewMessage constructor
54
-
55
- msg_constructor = bytes[offset, 4].unpack1('L<')
56
- return nil unless msg_constructor == Constructors::MESSAGE
57
-
58
- offset += 4
59
- flags = bytes[offset, 4].unpack1('L<')
60
- offset += 4
61
- flags2 = bytes[offset, 4].unpack1('L<')
62
- offset += 4
63
- offset += 4 # id
64
-
65
- offset += 12 if flags & (1 << 8) != 0 # from_id: Peer
66
- offset += 12 # peer_id: Peer
67
- offset += 12 if flags & (1 << 28) != 0 # saved_peer_id: Peer
68
- return nil if flags & (1 << 2) != 0 # fwd_from: variable length
69
-
70
- offset += 8 if flags & (1 << 11) != 0 # via_bot_id: long
71
- offset += 8 if flags2 & 1 != 0 # via_business_bot_id: long
72
- return nil if flags & (1 << 3) != 0 # reply_to: variable length
73
-
74
- offset += 4 # date
75
- read_tl_string(bytes, offset)
76
- end
77
-
78
- def read_tl_string(bytes, offset)
79
- first_byte = bytes.getbyte(offset)
80
- if first_byte == 254
81
- len = bytes.getbyte(offset + 1) |
82
- (bytes.getbyte(offset + 2) << 8) |
83
- (bytes.getbyte(offset + 3) << 16)
84
- bytes[offset + 4, len]
85
- else
86
- bytes[offset + 1, first_byte]
87
- end
55
+ Message.deserialize(bytes, 4, bytes[4, 4].unpack1('L<'))&.text
88
56
  end
89
57
  end
90
58
  end