mtproto 0.0.14 → 0.0.17

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/data/tl-schema.json +47158 -42684
  3. data/lib/mtproto/auth_key_generator.rb +2 -2
  4. data/lib/mtproto/client/api/export_authorization.rb +17 -0
  5. data/lib/mtproto/client/api/import_authorization.rb +23 -0
  6. data/lib/mtproto/client/api.rb +2 -0
  7. data/lib/mtproto/client/rpc.rb +33 -0
  8. data/lib/mtproto/client.rb +54 -1
  9. data/lib/mtproto/file_downloader.rb +122 -0
  10. data/lib/mtproto/tl/constructor_names.rb +324 -109
  11. data/lib/mtproto/tl/constructors.rb +16 -8
  12. data/lib/mtproto/tl/objects/bot_command_scope.rb +81 -0
  13. data/lib/mtproto/tl/objects/channels_join_channel.rb +1 -1
  14. data/lib/mtproto/tl/objects/channels_update_username.rb +42 -0
  15. data/lib/mtproto/tl/objects/contacts.rb +1 -1
  16. data/lib/mtproto/tl/objects/create_bot.rb +54 -0
  17. data/lib/mtproto/tl/objects/dialogs.rb +25 -10
  18. data/lib/mtproto/tl/objects/edit_access_settings.rb +46 -0
  19. data/lib/mtproto/tl/objects/export_authorization.rb +17 -0
  20. data/lib/mtproto/tl/objects/export_bot_token.rb +32 -0
  21. data/lib/mtproto/tl/objects/exported_authorization.rb +32 -0
  22. data/lib/mtproto/tl/objects/exported_bot_token.rb +27 -0
  23. data/lib/mtproto/tl/objects/forward_messages.rb +12 -3
  24. data/lib/mtproto/tl/objects/get_access_settings.rb +28 -0
  25. data/lib/mtproto/tl/objects/get_bot_callback_answer.rb +67 -0
  26. data/lib/mtproto/tl/objects/get_bot_commands.rb +46 -0
  27. data/lib/mtproto/tl/objects/get_file.rb +10 -2
  28. data/lib/mtproto/tl/objects/import_authorization.rb +38 -0
  29. data/lib/mtproto/tl/objects/input_keyboard_button_request_peer.rb +54 -0
  30. data/lib/mtproto/tl/objects/keyboard_button_callback.rb +50 -0
  31. data/lib/mtproto/tl/objects/message.rb +97 -21
  32. data/lib/mtproto/tl/objects/messages.rb +7 -74
  33. data/lib/mtproto/tl/objects/messages_get_messages.rb +26 -0
  34. data/lib/mtproto/tl/objects/reply_inline_markup.rb +30 -0
  35. data/lib/mtproto/tl/objects/reply_keyboard_markup.rb +35 -0
  36. data/lib/mtproto/tl/objects/request_peer_type_create_bot.rb +47 -0
  37. data/lib/mtproto/tl/objects/reset_bot_commands.rb +45 -0
  38. data/lib/mtproto/tl/objects/send_bot_requested_peer.rb +51 -0
  39. data/lib/mtproto/tl/objects/send_media.rb +87 -4
  40. data/lib/mtproto/tl/objects/send_message.rb +7 -2
  41. data/lib/mtproto/tl/objects/send_message_action.rb +48 -0
  42. data/lib/mtproto/tl/objects/set_bot_callback_answer.rb +54 -0
  43. data/lib/mtproto/tl/objects/set_bot_commands.rb +53 -0
  44. data/lib/mtproto/tl/objects/set_bot_guest_chat_result.rb +84 -0
  45. data/lib/mtproto/tl/objects/set_bot_info.rb +64 -0
  46. data/lib/mtproto/tl/objects/set_typing.rb +49 -0
  47. data/lib/mtproto/tl/objects/update_status.rb +24 -0
  48. data/lib/mtproto/tl/objects/updates.rb +117 -0
  49. data/lib/mtproto/tl/objects/updates_difference.rb +45 -69
  50. data/lib/mtproto/tl/objects/upload_profile_photo.rb +65 -0
  51. data/lib/mtproto/tl/reader.rb +188 -0
  52. data/lib/mtproto/transport/abridged_packet_codec.rb +5 -1
  53. data/lib/mtproto/unencrypted_message.rb +39 -0
  54. data/lib/mtproto/version.rb +1 -1
  55. data/lib/mtproto.rb +4 -1
  56. data/scripts/gen_constructor_names.rb +72 -0
  57. data/scripts/tl_to_json.rb +72 -0
  58. data/scripts/verify_ids.rb +33 -0
  59. metadata +38 -2
  60. data/lib/mtproto/message/message.rb +0 -85
@@ -56,6 +56,9 @@ module MTProto
56
56
  AUTH_SIGN_IN = 0x8d52a951
57
57
  AUTH_AUTHORIZATION = 0x2ea2c0d4
58
58
  AUTH_AUTHORIZATION_SIGN_UP_REQUIRED = 0x44747e9a
59
+ AUTH_EXPORT_AUTHORIZATION = 0xe5bfffcd
60
+ AUTH_EXPORTED_AUTHORIZATION = 0xb434e2b8
61
+ AUTH_IMPORT_AUTHORIZATION = 0xa57a7dad
59
62
  AUTH_CHECK_PASSWORD = 0xd18b4d16
60
63
  AUTH_EXPORT_LOGIN_TOKEN = 0xb7e085fe
61
64
  AUTH_IMPORT_LOGIN_TOKEN = 0x95ac5ce4
@@ -70,7 +73,7 @@ module MTProto
70
73
 
71
74
  # Users
72
75
  USERS_GET_USERS = 0x0d91a548
73
- USER = 0x020b1422
76
+ USER = 0x31774388
74
77
 
75
78
  # Contacts
76
79
  CONTACTS_GET_CONTACTS = 0x5dd69e12
@@ -81,25 +84,30 @@ module MTProto
81
84
  # Messages
82
85
  MESSAGES_GET_DIALOGS = 0xa0f4cb4f
83
86
  MESSAGES_GET_HISTORY = 0x4423e6c5
84
- MESSAGES_SEND_MESSAGE = 0xfe05dc9a
85
- MESSAGES_SEND_MEDIA = 0xac55d9c1
86
- MESSAGES_EDIT_MESSAGE = 0xdfd14005
87
+ MESSAGES_SEND_MESSAGE = 0xfef48f62
88
+ MESSAGES_SEND_MEDIA = 0x0330e77f
89
+ MESSAGES_EDIT_MESSAGE = 0xb106e66c
87
90
  MESSAGES_DELETE_MESSAGES = 0xe58e95d2
88
91
 
89
92
  # Upload + media
90
93
  UPLOAD_SAVE_FILE_PART = 0xb304a621
91
94
  INPUT_FILE = 0xf52ff27f
92
- INPUT_MEDIA_UPLOADED_PHOTO = 0x1e287d04
95
+ INPUT_MEDIA_UPLOADED_PHOTO = 0x7d8375da
96
+ INPUT_MEDIA_UPLOADED_DOCUMENT = 0x037c9330
97
+ DOCUMENT_ATTRIBUTE_FILENAME = 0x15590068
98
+ DOCUMENT_ATTRIBUTE_AUDIO = 0x9852f9c6
93
99
  MESSAGES_DIALOGS = 0x15ba6c40
94
100
  MESSAGES_DIALOGS_SLICE = 0x71e094f3
95
- MESSAGE = 0x9815cec8
96
- DIALOG = 0xd58a08c6
101
+ MESSAGE = 0x7600b9d3
102
+ DIALOG = 0xfc89f7f3
97
103
  CHAT = 0x41cbf256
98
104
  CHAT_FORBIDDEN = 0x6592a1a7
99
- CHANNEL = 0xfe685355
105
+ CHANNEL = 0x1c32b11c
100
106
  CHANNEL_FORBIDDEN = 0x17d493d5
101
107
 
102
108
  # Updates
109
+ UPDATES = 0x74ae4240
110
+ UPDATES_COMBINED = 0x725b04c3
103
111
  UPDATE_NEW_MESSAGE = 0x1f2b0afd
104
112
  UPDATE_SHORT = 0x78d4dec1
105
113
  UPDATE_SHORT_MESSAGE = 0x313bc7f8
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ # Serializes a BotCommandScope — the `scope:` field shared by
6
+ # bots.setBotCommands / bots.resetBotCommands / bots.getBotCommands.
7
+ #
8
+ # scope is a hash:
9
+ # { type: :default }
10
+ # { type: :users }
11
+ # { type: :chats }
12
+ # { type: :chat_admins }
13
+ # { type: :peer, peer: <input_peer> }
14
+ # { type: :peer_admins, peer: <input_peer> }
15
+ # { type: :peer_user, peer: <input_peer>, user: <input_user> }
16
+ #
17
+ # input_peer is the same shape used elsewhere (e.g. SendMessage):
18
+ # { type: :self | :user | :chat | :channel, id:, access_hash: }
19
+ # input_user:
20
+ # { type: :self } or { id:, access_hash: }
21
+ module BotCommandScope
22
+ extend Binary
23
+
24
+ module_function
25
+
26
+ DEFAULT = 0x2f6cb2ab
27
+ USERS = 0x3c4f04d8
28
+ CHATS = 0x6fe1a881
29
+ CHAT_ADMINS = 0xb9aa606a
30
+ PEER = 0xdb9d897d
31
+ PEER_ADMINS = 0x3fd863d1
32
+ PEER_USER = 0x0a1321f3
33
+
34
+ INPUT_USER = 0xf21158c6
35
+ INPUT_USER_SELF = 0xf7c1b13f
36
+
37
+ def serialize(scope)
38
+ scope ||= { type: :default }
39
+ case scope[:type]
40
+ when :default then u32_b(DEFAULT)
41
+ when :users then u32_b(USERS)
42
+ when :chats then u32_b(CHATS)
43
+ when :chat_admins then u32_b(CHAT_ADMINS)
44
+ when :peer then u32_b(PEER) + serialize_input_peer(scope[:peer])
45
+ when :peer_admins then u32_b(PEER_ADMINS) + serialize_input_peer(scope[:peer])
46
+ when :peer_user
47
+ u32_b(PEER_USER) + serialize_input_peer(scope[:peer]) + serialize_input_user(scope[:user])
48
+ else
49
+ raise ArgumentError, "Unknown bot command scope: #{scope[:type].inspect}"
50
+ end
51
+ end
52
+
53
+ def serialize_input_peer(peer)
54
+ raise ArgumentError, 'scope requires peer:' if peer.nil?
55
+
56
+ case peer[:type]
57
+ when :self
58
+ u32_b(Constructors::INPUT_PEER_SELF)
59
+ when :user
60
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
61
+ when :chat
62
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(peer[:id])
63
+ when :channel
64
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
65
+ else
66
+ raise ArgumentError, "Unknown peer type: #{peer[:type].inspect}"
67
+ end
68
+ end
69
+
70
+ def serialize_input_user(user)
71
+ raise ArgumentError, 'peer_user scope requires user:' if user.nil?
72
+
73
+ if user[:type] == :self
74
+ u32_b(INPUT_USER_SELF)
75
+ else
76
+ u32_b(INPUT_USER) + u64_b(user[:id]) + u64_b(user[:access_hash] || 0)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -5,7 +5,7 @@ module MTProto
5
5
  class ChannelsJoinChannel
6
6
  include Binary
7
7
 
8
- CONSTRUCTOR = 0x24b524c5
8
+ CONSTRUCTOR = 0x7f6a1e22
9
9
 
10
10
  def initialize(channel:)
11
11
  @channel = channel
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ # channels.updateUsername — assign a public username to a channel/supergroup.
6
+ # Returns Bool.
7
+ class ChannelsUpdateUsername
8
+ include Binary
9
+
10
+ CONSTRUCTOR = 0x3514b3de
11
+
12
+ def initialize(channel:, username:)
13
+ @channel = channel
14
+ @username = username
15
+ end
16
+
17
+ def serialize
18
+ result = u32_b(CONSTRUCTOR)
19
+ result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
20
+ result += serialize_tl_string(@username)
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def serialize_tl_string(str)
27
+ bytes = str.encode('UTF-8').bytes
28
+ length = bytes.length
29
+ if length <= 253
30
+ [length] + bytes + padding(length + 1)
31
+ else
32
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
33
+ end
34
+ end
35
+
36
+ def padding(current_length)
37
+ pad_length = (4 - (current_length % 4)) % 4
38
+ [0] * pad_length
39
+ end
40
+ end
41
+ end
42
+ end
@@ -68,7 +68,7 @@ module MTProto
68
68
  [contacts, offset]
69
69
  end
70
70
 
71
- # User parsing mirrors TL::Dialogs.parse_user (user#020b1422). Kept private here
71
+ # User parsing mirrors TL::Dialogs.parse_user (user#31774388). Kept private here
72
72
  # so this class can be used without instantiating Dialogs.
73
73
  def parse_users_vector(data, offset)
74
74
  offset += 4 # vector tag
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class CreateBot
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xe5b17f2b
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+
12
+ # manager: { id:, access_hash: } or { type: :self }
13
+ def initialize(name:, username:, manager:, via_deeplink: false)
14
+ @name = name
15
+ @username = username
16
+ @manager = manager
17
+ @via_deeplink = via_deeplink
18
+ end
19
+
20
+ def serialize
21
+ flags = 0
22
+ flags |= (1 << 0) if @via_deeplink
23
+
24
+ result = u32_b(CONSTRUCTOR)
25
+ result += u32_b(flags)
26
+ result += serialize_tl_string(@name)
27
+ result += serialize_tl_string(@username)
28
+ result += if @manager[:type] == :self
29
+ u32_b(INPUT_USER_SELF)
30
+ else
31
+ u32_b(INPUT_USER) + u64_b(@manager[:id]) + u64_b(@manager[:access_hash] || 0)
32
+ end
33
+ result
34
+ end
35
+
36
+ private
37
+
38
+ def serialize_tl_string(str)
39
+ bytes = str.encode('UTF-8').bytes
40
+ length = bytes.length
41
+ if length <= 253
42
+ [length] + bytes + padding(length + 1)
43
+ else
44
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
45
+ end
46
+ end
47
+
48
+ def padding(current_length)
49
+ pad_length = (4 - (current_length % 4)) % 4
50
+ [0] * pad_length
51
+ end
52
+ end
53
+ end
54
+ end
@@ -51,6 +51,16 @@ module MTProto
51
51
  new(dialogs: dialogs, chats: chats, users: users, count: count, message_dates: message_dates)
52
52
  end
53
53
 
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
+
54
64
  class << self
55
65
  private
56
66
 
@@ -94,6 +104,7 @@ module MTProto
94
104
 
95
105
  offset += 4 # unread_mentions_count
96
106
  offset += 4 # unread_reactions_count
107
+ offset += 4 # unread_poll_votes_count (added in layer 227)
97
108
 
98
109
  offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
99
110
 
@@ -128,12 +139,12 @@ module MTProto
128
139
 
129
140
  # --- Messages vector ---
130
141
 
131
- # message#9815cec8 flags:# flags2:#
142
+ # message#7600b9d3 flags:# flags2:#
132
143
  # id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int
133
- # peer_id:Peer saved_peer_id:flags.28?Peer
144
+ # from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer
134
145
  # fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long
135
- # via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader
136
- # date:int ...
146
+ # via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer
147
+ # reply_to:flags.3?MessageReplyHeader date:int ...
137
148
  #
138
149
  # messageService#2b085862 flags:#
139
150
  # id:int from_id:flags.8?Peer peer_id:Peer
@@ -179,11 +190,13 @@ module MTProto
179
190
 
180
191
  offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
181
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)
182
194
  offset = schema.skip(data, offset) # peer_id (Peer, always present)
183
195
  offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
184
196
  offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
185
197
  offset += 8 if flags.anybits?(1 << 11) # via_bot_id
186
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)
187
200
  offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
188
201
 
189
202
  date = data[offset, 4].unpack1('L<')
@@ -268,7 +281,7 @@ module MTProto
268
281
  [Chat.new(id: id, title: title, type: :chat), offset]
269
282
  end
270
283
 
271
- # channel#fe685355 flags:# flags2:#
284
+ # channel#1c32b11c flags:# flags2:#
272
285
  # id:long access_hash:flags.13?long title:string
273
286
  # username:flags.6?string photo:ChatPhoto date:int
274
287
  # restriction_reason:flags.9?Vector<RestrictionReason>
@@ -310,7 +323,7 @@ module MTProto
310
323
  if flags2.anybits?(1 << 0) # usernames Vector<Username>
311
324
  offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
312
325
  end
313
- offset += 4 if flags2.anybits?(1 << 4) # stories_max_id
326
+ offset = schema.skip(data, offset) if flags2.anybits?(1 << 4) # stories_max_id (layer 227: RecentStory)
314
327
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 7) # color (PeerColor)
315
328
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # profile_color (PeerColor)
316
329
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # emoji_status
@@ -363,7 +376,7 @@ module MTProto
363
376
 
364
377
  USER_EMPTY = 0xd3bc4b7a
365
378
 
366
- # user#020b1422 flags:# flags2:#
379
+ # user#31774388 flags:# flags2:#
367
380
  # id:long access_hash:flags.0?long
368
381
  # first_name:flags.1?string last_name:flags.2?string
369
382
  # username:flags.3?string phone:flags.4?string
@@ -408,7 +421,9 @@ module MTProto
408
421
  last_name = nil
409
422
  last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
410
423
 
411
- offset = skip_tl_string(data, offset) if flags.anybits?(1 << 3) # username
424
+ username = nil
425
+ username, offset = read_tl_string(data, offset) if flags.anybits?(1 << 3) # username
426
+
412
427
  offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
413
428
  offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
414
429
  offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
@@ -420,14 +435,14 @@ module MTProto
420
435
  offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
421
436
  offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
422
437
  offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0) # usernames
423
- offset += 4 if flags2.anybits?(1 << 5) # stories_max_id
438
+ offset = schema.skip(data, offset) if flags2.anybits?(1 << 5) # stories_max_id (layer 227: RecentStory)
424
439
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
425
440
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
426
441
  offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
427
442
  offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
428
443
  offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
429
444
 
430
- [{ id: id, first_name: first_name, last_name: last_name, access_hash: access_hash }, offset]
445
+ [{ id: id, first_name: first_name, last_name: last_name, username: username, access_hash: access_hash }, offset]
431
446
  end
432
447
 
433
448
  # --- TL string ---
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class EditAccessSettings
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x31813cd8
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+
12
+ # bot / add_users entries: { id:, access_hash: } or { type: :self }
13
+ def initialize(bot:, restricted: false, add_users: [])
14
+ @bot = bot
15
+ @restricted = restricted
16
+ @add_users = add_users
17
+ end
18
+
19
+ def serialize
20
+ flags = 0
21
+ flags |= (1 << 0) if @restricted
22
+ flags |= (1 << 1) unless @add_users.empty?
23
+
24
+ result = u32_b(CONSTRUCTOR)
25
+ result += u32_b(flags)
26
+ result += serialize_input_user(@bot)
27
+ unless @add_users.empty?
28
+ result += u32_b(Constructors::VECTOR)
29
+ result += u32_b(@add_users.size)
30
+ @add_users.each { |u| result += serialize_input_user(u) }
31
+ end
32
+ result
33
+ end
34
+
35
+ private
36
+
37
+ def serialize_input_user(user)
38
+ if user[:type] == :self
39
+ u32_b(INPUT_USER_SELF)
40
+ else
41
+ u32_b(INPUT_USER) + u64_b(user[:id]) + u64_b(user[:access_hash] || 0)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ExportAuthorization
6
+ include Binary
7
+
8
+ def initialize(dc_id:)
9
+ @dc_id = dc_id
10
+ end
11
+
12
+ def serialize
13
+ u32_b(Constructors::AUTH_EXPORT_AUTHORIZATION) + u32_b(@dc_id)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ExportBotToken
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xbd0d99eb
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+ BOOL_TRUE = 0x997275b5
12
+ BOOL_FALSE = 0xbc799737
13
+
14
+ # bot: { id:, access_hash: } or { type: :self }
15
+ def initialize(bot:, revoke: false)
16
+ @bot = bot
17
+ @revoke = revoke
18
+ end
19
+
20
+ def serialize
21
+ result = u32_b(CONSTRUCTOR)
22
+ result += if @bot[:type] == :self
23
+ u32_b(INPUT_USER_SELF)
24
+ else
25
+ u32_b(INPUT_USER) + u64_b(@bot[:id]) + u64_b(@bot[:access_hash] || 0)
26
+ end
27
+ result += u32_b(@revoke ? BOOL_TRUE : BOOL_FALSE)
28
+ result
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ExportedAuthorization
6
+ attr_reader :id, :bytes
7
+
8
+ def initialize(id:, bytes:)
9
+ @id = id
10
+ @bytes = bytes
11
+ end
12
+
13
+ def self.deserialize(data)
14
+ constructor = data[0, 4].unpack1('L<')
15
+ raise UnexpectedConstructorError, constructor unless constructor == Constructors::AUTH_EXPORTED_AUTHORIZATION
16
+
17
+ id = data[4, 8].unpack1('Q<')
18
+ new(id: id, bytes: read_tl_bytes(data, 12))
19
+ end
20
+
21
+ def self.read_tl_bytes(data, offset)
22
+ first = data.getbyte(offset)
23
+ if first == 254
24
+ len = data.getbyte(offset + 1) | (data.getbyte(offset + 2) << 8) | (data.getbyte(offset + 3) << 16)
25
+ data[offset + 4, len]
26
+ else
27
+ data[offset + 1, first]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ExportedBotToken
6
+ CONSTRUCTOR = 0x3c60b621
7
+
8
+ attr_reader :token
9
+
10
+ def initialize(token:)
11
+ @token = token
12
+ end
13
+
14
+ def self.deserialize(bytes)
15
+ body = bytes[4..]
16
+ first = body.getbyte(0)
17
+ token = if first == 254
18
+ len = body.getbyte(1) | (body.getbyte(2) << 8) | (body.getbyte(3) << 16)
19
+ body[4, len]
20
+ else
21
+ body[1, first]
22
+ end
23
+ new(token: token.force_encoding('UTF-8'))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -7,18 +7,27 @@ module MTProto
7
7
  class ForwardMessages
8
8
  include Binary
9
9
 
10
- CONSTRUCTOR = 0x978928ca
10
+ CONSTRUCTOR = 0x13704a7c
11
11
 
12
- def initialize(from_peer:, to_peer:, ids:, random_ids: nil)
12
+ def initialize(from_peer:, to_peer:, ids:, random_ids: nil, drop_author: false, drop_media_captions: false,
13
+ silent: false)
13
14
  @from_peer = from_peer
14
15
  @to_peer = to_peer
15
16
  @ids = ids
16
17
  @random_ids = random_ids || Array.new(ids.length) { SecureRandom.random_number(2**63) }
18
+ @drop_author = drop_author
19
+ @drop_media_captions = drop_media_captions
20
+ @silent = silent
17
21
  end
18
22
 
19
23
  def serialize
24
+ flags = 0
25
+ flags |= (1 << 5) if @silent
26
+ flags |= (1 << 11) if @drop_author
27
+ flags |= (1 << 12) if @drop_media_captions
28
+
20
29
  result = u32_b(CONSTRUCTOR)
21
- result += u32_b(0) # flags
30
+ result += u32_b(flags)
22
31
  result += serialize_input_peer(@from_peer)
23
32
  result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
24
33
  @ids.each { |id| result += u32_b(id) }
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetAccessSettings
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x213853a3
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+
12
+ # bot: { id:, access_hash: } or { type: :self }
13
+ def initialize(bot:)
14
+ @bot = bot
15
+ end
16
+
17
+ def serialize
18
+ result = u32_b(CONSTRUCTOR)
19
+ result += if @bot[:type] == :self
20
+ u32_b(INPUT_USER_SELF)
21
+ else
22
+ u32_b(INPUT_USER) + u64_b(@bot[:id]) + u64_b(@bot[:access_hash] || 0)
23
+ end
24
+ result
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ # messages.getBotCallbackAnswer — the press itself: the user client invokes
6
+ # this with the button's callback data; the server delivers an
7
+ # updateBotCallbackQuery to the bot and blocks until the bot answers (or
8
+ # times out), then returns messages.botCallbackAnswer.
9
+ class GetBotCallbackAnswer
10
+ include Binary
11
+
12
+ CONSTRUCTOR = 0x9342ca07
13
+
14
+ def initialize(peer:, msg_id:, data: nil, game: false)
15
+ @peer = peer
16
+ @msg_id = msg_id
17
+ @data = data
18
+ @game = game
19
+ end
20
+
21
+ def serialize
22
+ flags = 0
23
+ flags |= (1 << 0) if @data
24
+ flags |= (1 << 1) if @game
25
+
26
+ result = u32_b(CONSTRUCTOR)
27
+ result += u32_b(flags)
28
+ result += serialize_input_peer
29
+ result += u32_b(@msg_id)
30
+ result += serialize_tl_bytes(@data) if @data
31
+ result
32
+ end
33
+
34
+ private
35
+
36
+ def serialize_input_peer
37
+ case @peer[:type]
38
+ when :self
39
+ u32_b(Constructors::INPUT_PEER_SELF)
40
+ when :user
41
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
42
+ when :chat
43
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
44
+ when :channel
45
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
46
+ else
47
+ raise "Unknown peer type: #{@peer[:type]}"
48
+ end
49
+ end
50
+
51
+ def serialize_tl_bytes(bytes_str)
52
+ bytes = bytes_str.to_s.b.bytes
53
+ length = bytes.length
54
+ if length <= 253
55
+ [length] + bytes + padding(length + 1)
56
+ else
57
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
58
+ end
59
+ end
60
+
61
+ def padding(current_length)
62
+ pad_length = (4 - (current_length % 4)) % 4
63
+ [0] * pad_length
64
+ end
65
+ end
66
+ end
67
+ end