mtproto 0.0.15 → 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 (47) 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.rb +1 -1
  8. data/lib/mtproto/file_downloader.rb +122 -0
  9. data/lib/mtproto/tl/constructor_names.rb +314 -109
  10. data/lib/mtproto/tl/constructors.rb +16 -8
  11. data/lib/mtproto/tl/objects/bot_command_scope.rb +81 -0
  12. data/lib/mtproto/tl/objects/channels_join_channel.rb +1 -1
  13. data/lib/mtproto/tl/objects/channels_update_username.rb +42 -0
  14. data/lib/mtproto/tl/objects/contacts.rb +1 -1
  15. data/lib/mtproto/tl/objects/dialogs.rb +15 -10
  16. data/lib/mtproto/tl/objects/export_authorization.rb +17 -0
  17. data/lib/mtproto/tl/objects/exported_authorization.rb +32 -0
  18. data/lib/mtproto/tl/objects/forward_messages.rb +5 -2
  19. data/lib/mtproto/tl/objects/get_bot_callback_answer.rb +67 -0
  20. data/lib/mtproto/tl/objects/get_bot_commands.rb +46 -0
  21. data/lib/mtproto/tl/objects/get_file.rb +10 -2
  22. data/lib/mtproto/tl/objects/import_authorization.rb +38 -0
  23. data/lib/mtproto/tl/objects/keyboard_button_callback.rb +50 -0
  24. data/lib/mtproto/tl/objects/message.rb +97 -21
  25. data/lib/mtproto/tl/objects/messages.rb +7 -74
  26. data/lib/mtproto/tl/objects/reply_inline_markup.rb +30 -0
  27. data/lib/mtproto/tl/objects/reset_bot_commands.rb +45 -0
  28. data/lib/mtproto/tl/objects/send_media.rb +76 -4
  29. data/lib/mtproto/tl/objects/send_message.rb +4 -2
  30. data/lib/mtproto/tl/objects/send_message_action.rb +48 -0
  31. data/lib/mtproto/tl/objects/set_bot_callback_answer.rb +54 -0
  32. data/lib/mtproto/tl/objects/set_bot_commands.rb +6 -3
  33. data/lib/mtproto/tl/objects/set_bot_guest_chat_result.rb +84 -0
  34. data/lib/mtproto/tl/objects/set_typing.rb +49 -0
  35. data/lib/mtproto/tl/objects/update_status.rb +24 -0
  36. data/lib/mtproto/tl/objects/updates.rb +117 -0
  37. data/lib/mtproto/tl/objects/updates_difference.rb +16 -119
  38. data/lib/mtproto/tl/reader.rb +188 -0
  39. data/lib/mtproto/transport/abridged_packet_codec.rb +5 -1
  40. data/lib/mtproto/unencrypted_message.rb +39 -0
  41. data/lib/mtproto/version.rb +1 -1
  42. data/lib/mtproto.rb +4 -1
  43. data/scripts/gen_constructor_names.rb +72 -0
  44. data/scripts/tl_to_json.rb +72 -0
  45. data/scripts/verify_ids.rb +33 -0
  46. metadata +25 -2
  47. 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
@@ -104,6 +104,7 @@ module MTProto
104
104
 
105
105
  offset += 4 # unread_mentions_count
106
106
  offset += 4 # unread_reactions_count
107
+ offset += 4 # unread_poll_votes_count (added in layer 227)
107
108
 
108
109
  offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
109
110
 
@@ -138,12 +139,12 @@ module MTProto
138
139
 
139
140
  # --- Messages vector ---
140
141
 
141
- # message#9815cec8 flags:# flags2:#
142
+ # message#7600b9d3 flags:# flags2:#
142
143
  # id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int
143
- # peer_id:Peer saved_peer_id:flags.28?Peer
144
+ # from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer
144
145
  # fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long
145
- # via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader
146
- # date:int ...
146
+ # via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer
147
+ # reply_to:flags.3?MessageReplyHeader date:int ...
147
148
  #
148
149
  # messageService#2b085862 flags:#
149
150
  # id:int from_id:flags.8?Peer peer_id:Peer
@@ -189,11 +190,13 @@ module MTProto
189
190
 
190
191
  offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
191
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)
192
194
  offset = schema.skip(data, offset) # peer_id (Peer, always present)
193
195
  offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
194
196
  offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
195
197
  offset += 8 if flags.anybits?(1 << 11) # via_bot_id
196
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)
197
200
  offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
198
201
 
199
202
  date = data[offset, 4].unpack1('L<')
@@ -278,7 +281,7 @@ module MTProto
278
281
  [Chat.new(id: id, title: title, type: :chat), offset]
279
282
  end
280
283
 
281
- # channel#fe685355 flags:# flags2:#
284
+ # channel#1c32b11c flags:# flags2:#
282
285
  # id:long access_hash:flags.13?long title:string
283
286
  # username:flags.6?string photo:ChatPhoto date:int
284
287
  # restriction_reason:flags.9?Vector<RestrictionReason>
@@ -320,7 +323,7 @@ module MTProto
320
323
  if flags2.anybits?(1 << 0) # usernames Vector<Username>
321
324
  offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
322
325
  end
323
- 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)
324
327
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 7) # color (PeerColor)
325
328
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # profile_color (PeerColor)
326
329
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # emoji_status
@@ -373,7 +376,7 @@ module MTProto
373
376
 
374
377
  USER_EMPTY = 0xd3bc4b7a
375
378
 
376
- # user#020b1422 flags:# flags2:#
379
+ # user#31774388 flags:# flags2:#
377
380
  # id:long access_hash:flags.0?long
378
381
  # first_name:flags.1?string last_name:flags.2?string
379
382
  # username:flags.3?string phone:flags.4?string
@@ -418,7 +421,9 @@ module MTProto
418
421
  last_name = nil
419
422
  last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
420
423
 
421
- 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
+
422
427
  offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
423
428
  offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
424
429
  offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
@@ -430,14 +435,14 @@ module MTProto
430
435
  offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
431
436
  offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
432
437
  offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0) # usernames
433
- 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)
434
439
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
435
440
  offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
436
441
  offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
437
442
  offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
438
443
  offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
439
444
 
440
- [{ 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]
441
446
  end
442
447
 
443
448
  # --- TL string ---
@@ -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 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
@@ -7,19 +7,22 @@ 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, drop_author: false, drop_media_captions: false)
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) }
17
18
  @drop_author = drop_author
18
19
  @drop_media_captions = drop_media_captions
20
+ @silent = silent
19
21
  end
20
22
 
21
23
  def serialize
22
24
  flags = 0
25
+ flags |= (1 << 5) if @silent
23
26
  flags |= (1 << 11) if @drop_author
24
27
  flags |= (1 << 12) if @drop_media_captions
25
28
 
@@ -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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'bot_command_scope'
4
+
5
+ module MTProto
6
+ module TL
7
+ # bots.getBotCommands — fetch the bot's commands for a given scope/language.
8
+ # Returns Vector<BotCommand>. Result parsing belongs to a later layer; this
9
+ # object only builds the request.
10
+ class GetBotCommands
11
+ include Binary
12
+
13
+ CONSTRUCTOR = 0xe34c0dd6
14
+
15
+ # scope: BotCommandScope hash (see BotCommandScope); defaults to default scope.
16
+ def initialize(scope: { type: :default }, lang_code: '')
17
+ @scope = scope
18
+ @lang_code = lang_code
19
+ end
20
+
21
+ def serialize
22
+ result = u32_b(CONSTRUCTOR)
23
+ result += BotCommandScope.serialize(@scope)
24
+ result += serialize_tl_string(@lang_code)
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def serialize_tl_string(str)
31
+ bytes = str.to_s.encode('UTF-8').bytes
32
+ length = bytes.length
33
+ if length <= 253
34
+ [length] + bytes + padding(length + 1)
35
+ else
36
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
37
+ end
38
+ end
39
+
40
+ def padding(current_length)
41
+ pad_length = (4 - (current_length % 4)) % 4
42
+ [0] * pad_length
43
+ end
44
+ end
45
+ end
46
+ end
@@ -7,10 +7,18 @@ module MTProto
7
7
 
8
8
  CONSTRUCTOR = 0xbe5335be
9
9
  INPUT_PHOTO_FILE_LOCATION = 0x40181ffe
10
+ INPUT_DOCUMENT_FILE_LOCATION = 0xbad07584
11
+
12
+ LOCATION_CONSTRUCTORS = {
13
+ photo: INPUT_PHOTO_FILE_LOCATION,
14
+ document: INPUT_DOCUMENT_FILE_LOCATION
15
+ }.freeze
10
16
 
11
17
  # location: { id:, access_hash:, file_reference: (binary String), thumb_size: (String) }
12
- def initialize(location:, offset: 0, limit: 1024 * 1024)
18
+ # type: :photo for photos, :document for documents/voice/video/audio.
19
+ def initialize(location:, type: :photo, offset: 0, limit: 1024 * 1024)
13
20
  @location = location
21
+ @type = type
14
22
  @offset = offset
15
23
  @limit = limit
16
24
  end
@@ -27,7 +35,7 @@ module MTProto
27
35
  private
28
36
 
29
37
  def serialize_location
30
- u32_b(INPUT_PHOTO_FILE_LOCATION) +
38
+ u32_b(LOCATION_CONSTRUCTORS.fetch(@type)) +
31
39
  u64_b(@location[:id]) +
32
40
  u64_b(@location[:access_hash]) +
33
41
  serialize_tl_bytes(@location[:file_reference]) +
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ImportAuthorization
6
+ include Binary
7
+
8
+ def initialize(id:, bytes:)
9
+ @id = id
10
+ @bytes = bytes
11
+ end
12
+
13
+ def serialize
14
+ u32_b(Constructors::AUTH_IMPORT_AUTHORIZATION) +
15
+ u64_b(@id) +
16
+ serialize_tl_bytes(@bytes)
17
+ end
18
+
19
+ private
20
+
21
+ def serialize_tl_bytes(value)
22
+ bytes = value.to_s.b.bytes
23
+ length = bytes.length
24
+
25
+ if length <= 253
26
+ [length] + bytes + padding(length + 1)
27
+ else
28
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
29
+ end
30
+ end
31
+
32
+ def padding(current_length)
33
+ pad_length = (4 - (current_length % 4)) % 4
34
+ [0] * pad_length
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class KeyboardButtonCallback
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xe62bc960
9
+
10
+ # data: raw callback payload bytes echoed back to the bot on press.
11
+ def initialize(text:, data:, requires_password: false)
12
+ @text = text
13
+ @data = data
14
+ @requires_password = requires_password
15
+ end
16
+
17
+ def serialize
18
+ flags = 0
19
+ flags |= (1 << 0) if @requires_password
20
+
21
+ result = u32_b(CONSTRUCTOR)
22
+ result += u32_b(flags)
23
+ result += serialize_tl_string(@text)
24
+ result += serialize_tl_bytes(@data)
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def serialize_tl_string(str)
31
+ serialize_tl_bytes(str.to_s.encode('UTF-8').b)
32
+ end
33
+
34
+ def serialize_tl_bytes(bytes_str)
35
+ bytes = bytes_str.to_s.b.bytes
36
+ length = bytes.length
37
+ if length <= 253
38
+ [length] + bytes + padding(length + 1)
39
+ else
40
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
41
+ end
42
+ end
43
+
44
+ def padding(current_length)
45
+ pad_length = (4 - (current_length % 4)) % 4
46
+ [0] * pad_length
47
+ end
48
+ end
49
+ end
50
+ end