mtproto 0.0.13 → 0.0.14

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/FUTURE.md +9 -0
  3. data/docs/test_architecture_level1.md +237 -0
  4. data/lib/mtproto/auth_key_generator.rb +5 -5
  5. data/lib/mtproto/client/api/get_contacts.rb +14 -0
  6. data/lib/mtproto/client/api/send_message.rb +15 -0
  7. data/lib/mtproto/client/api.rb +2 -0
  8. data/lib/mtproto/client/rpc/response.rb +1 -1
  9. data/lib/mtproto/client/rpc.rb +2 -2
  10. data/lib/mtproto/client.rb +28 -18
  11. data/lib/mtproto/tl/constructors.rb +26 -0
  12. data/lib/mtproto/tl/object.rb +1 -1
  13. data/lib/mtproto/tl/objects/account_password.rb +1 -1
  14. data/lib/mtproto/tl/objects/authorization.rb +1 -1
  15. data/lib/mtproto/tl/objects/channels_create_channel.rb +48 -0
  16. data/lib/mtproto/tl/objects/channels_delete_messages.rb +32 -0
  17. data/lib/mtproto/tl/objects/channels_delete_participant_history.rb +34 -0
  18. data/lib/mtproto/tl/objects/channels_edit_banned.rb +54 -0
  19. data/lib/mtproto/tl/objects/channels_get_messages.rb +33 -0
  20. data/lib/mtproto/tl/objects/channels_get_participant.rb +36 -0
  21. data/lib/mtproto/tl/objects/channels_join_channel.rb +22 -0
  22. data/lib/mtproto/tl/objects/channels_leave_channel.rb +22 -0
  23. data/lib/mtproto/tl/objects/channels_report_spam.rb +37 -0
  24. data/lib/mtproto/tl/objects/check_password.rb +1 -1
  25. data/lib/mtproto/tl/objects/client_dh_inner_data.rb +1 -1
  26. data/lib/mtproto/tl/objects/contacts.rb +156 -0
  27. data/lib/mtproto/tl/objects/delete_messages.rb +25 -0
  28. data/lib/mtproto/tl/objects/dh_gen_response.rb +1 -1
  29. data/lib/mtproto/tl/objects/dialogs.rb +8 -4
  30. data/lib/mtproto/tl/objects/edit_message.rb +58 -0
  31. data/lib/mtproto/tl/objects/export_login_token.rb +1 -1
  32. data/lib/mtproto/tl/objects/forward_messages.rb +49 -0
  33. data/lib/mtproto/tl/objects/get_channel_difference.rb +41 -0
  34. data/lib/mtproto/tl/objects/get_config.rb +1 -1
  35. data/lib/mtproto/tl/objects/get_contacts.rb +17 -0
  36. data/lib/mtproto/tl/objects/get_dialogs.rb +1 -1
  37. data/lib/mtproto/tl/objects/get_difference.rb +1 -1
  38. data/lib/mtproto/tl/objects/get_file.rb +54 -0
  39. data/lib/mtproto/tl/objects/get_full_channel.rb +29 -0
  40. data/lib/mtproto/tl/objects/get_full_user.rb +28 -0
  41. data/lib/mtproto/tl/objects/get_history.rb +1 -1
  42. data/lib/mtproto/tl/objects/get_messages_reactions.rb +39 -0
  43. data/lib/mtproto/tl/objects/get_password.rb +1 -1
  44. data/lib/mtproto/tl/objects/get_state.rb +1 -1
  45. data/lib/mtproto/tl/objects/get_users.rb +1 -1
  46. data/lib/mtproto/tl/objects/help_config.rb +1 -1
  47. data/lib/mtproto/tl/objects/import_bot_authorization.rb +44 -0
  48. data/lib/mtproto/tl/objects/import_login_token.rb +1 -1
  49. data/lib/mtproto/tl/objects/init_connection.rb +2 -2
  50. data/lib/mtproto/tl/objects/invite_to_channel.rb +35 -0
  51. data/lib/mtproto/tl/objects/invoke_with_layer.rb +2 -2
  52. data/lib/mtproto/tl/objects/login_token.rb +2 -2
  53. data/lib/mtproto/tl/objects/messages.rb +1 -1
  54. data/lib/mtproto/tl/objects/pq_inner_data.rb +1 -1
  55. data/lib/mtproto/tl/objects/raw_response.rb +29 -0
  56. data/lib/mtproto/tl/objects/req_dh_params.rb +1 -1
  57. data/lib/mtproto/tl/objects/req_pq_multi.rb +1 -1
  58. data/lib/mtproto/tl/objects/res_pq.rb +1 -1
  59. data/lib/mtproto/tl/objects/resolve_username.rb +40 -0
  60. data/lib/mtproto/tl/objects/save_file_part.rb +40 -0
  61. data/lib/mtproto/tl/objects/send_code.rb +1 -1
  62. data/lib/mtproto/tl/objects/send_media.rb +85 -0
  63. data/lib/mtproto/tl/objects/send_message.rb +70 -0
  64. data/lib/mtproto/tl/objects/send_reaction.rb +68 -0
  65. data/lib/mtproto/tl/objects/sent_code.rb +1 -1
  66. data/lib/mtproto/tl/objects/server_dh_inner_data.rb +1 -1
  67. data/lib/mtproto/tl/objects/server_dh_params.rb +1 -1
  68. data/lib/mtproto/tl/objects/set_client_dh_params.rb +1 -1
  69. data/lib/mtproto/tl/objects/sign_in.rb +1 -1
  70. data/lib/mtproto/tl/objects/update.rb +1 -1
  71. data/lib/mtproto/tl/objects/update_short.rb +2 -2
  72. data/lib/mtproto/tl/objects/update_short_message.rb +1 -1
  73. data/lib/mtproto/tl/objects/update_short_sent_message.rb +36 -0
  74. data/lib/mtproto/tl/objects/update_username.rb +39 -0
  75. data/lib/mtproto/tl/objects/updates_difference.rb +2 -2
  76. data/lib/mtproto/tl/objects/updates_state.rb +1 -1
  77. data/lib/mtproto/tl/objects/users.rb +1 -1
  78. data/lib/mtproto/version.rb +1 -1
  79. metadata +36 -1
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ChannelsGetMessages
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xad8c9a23
9
+ INPUT_MESSAGE_ID = 0xa676a322
10
+
11
+ def initialize(channel:, ids:)
12
+ @channel = channel
13
+ @ids = ids
14
+ end
15
+
16
+ def serialize
17
+ result = u32_b(CONSTRUCTOR)
18
+ result += serialize_input_channel
19
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
20
+ @ids.each { |id| result += u32_b(INPUT_MESSAGE_ID) + u32_b(id) }
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def serialize_input_channel
27
+ u32_b(Constructors::INPUT_CHANNEL) +
28
+ u64_b(@channel[:id]) +
29
+ u64_b(@channel[:access_hash])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ChannelsGetParticipant
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xa0ab6cc6
9
+
10
+ def initialize(channel:, participant:)
11
+ @channel = channel
12
+ @participant = participant
13
+ end
14
+
15
+ def serialize
16
+ result = u32_b(CONSTRUCTOR)
17
+ result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
18
+ result += serialize_input_peer(@participant)
19
+ result
20
+ end
21
+
22
+ private
23
+
24
+ def serialize_input_peer(peer)
25
+ case peer[:type]
26
+ when :user
27
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
28
+ when :self
29
+ u32_b(Constructors::INPUT_PEER_SELF)
30
+ else
31
+ raise "Unsupported participant peer type: #{peer[:type]}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ChannelsJoinChannel
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x24b524c5
9
+
10
+ def initialize(channel:)
11
+ @channel = channel
12
+ end
13
+
14
+ def serialize
15
+ u32_b(CONSTRUCTOR) +
16
+ u32_b(Constructors::INPUT_CHANNEL) +
17
+ u64_b(@channel[:id]) +
18
+ u64_b(@channel[:access_hash])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ChannelsLeaveChannel
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xf836aa95
9
+
10
+ def initialize(channel:)
11
+ @channel = channel
12
+ end
13
+
14
+ def serialize
15
+ u32_b(CONSTRUCTOR) +
16
+ u32_b(Constructors::INPUT_CHANNEL) +
17
+ u64_b(@channel[:id]) +
18
+ u64_b(@channel[:access_hash])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ChannelsReportSpam
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xf44a8315
9
+
10
+ def initialize(channel:, participant:, ids:)
11
+ @channel = channel
12
+ @participant = participant
13
+ @ids = ids
14
+ end
15
+
16
+ def serialize
17
+ result = u32_b(CONSTRUCTOR)
18
+ result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
19
+ result += serialize_input_peer(@participant)
20
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
21
+ @ids.each { |id| result += u32_b(id) }
22
+ result
23
+ end
24
+
25
+ private
26
+
27
+ def serialize_input_peer(peer)
28
+ case peer[:type]
29
+ when :user
30
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
31
+ else
32
+ raise "Unsupported participant peer type: #{peer[:type]}"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -13,7 +13,7 @@ module MTProto
13
13
  @m1 = m1
14
14
  end
15
15
 
16
- def body
16
+ def serialize
17
17
  u32_b(Constructors::AUTH_CHECK_PASSWORD) +
18
18
  u32_b(Constructors::INPUT_CHECK_PASSWORD_SRP) +
19
19
  u64_b(@srp_id) +
@@ -14,7 +14,7 @@ module MTProto
14
14
  @g_b = g_b
15
15
  end
16
16
 
17
- def body
17
+ def serialize
18
18
  result = u32_b(Constructors::CLIENT_DH_INNER_DATA)
19
19
  result += @nonce.bytes
20
20
  result += @server_nonce.bytes
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../schema'
4
+
5
+ module MTProto
6
+ module TL
7
+ class Contacts
8
+ Contact = Struct.new(:user_id, :mutual, keyword_init: true)
9
+ User = Struct.new(:id, :first_name, :last_name, :access_hash, keyword_init: true)
10
+
11
+ SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
12
+
13
+ attr_reader :contacts, :saved_count, :users, :not_modified
14
+
15
+ def initialize(contacts: [], saved_count: 0, users: [], not_modified: false)
16
+ @contacts = contacts
17
+ @saved_count = saved_count
18
+ @users = users
19
+ @not_modified = not_modified
20
+ end
21
+
22
+ def not_modified?
23
+ @not_modified
24
+ end
25
+
26
+ def self.schema
27
+ @schema ||= Schema.new(SCHEMA_PATH)
28
+ end
29
+
30
+ def self.deserialize(data)
31
+ constructor = data[0, 4].unpack1('L<')
32
+ return new(not_modified: true) if constructor == Constructors::CONTACTS_CONTACTS_NOT_MODIFIED
33
+ raise UnexpectedConstructorError, constructor unless constructor == Constructors::CONTACTS_CONTACTS
34
+
35
+ offset = 4
36
+ contacts, offset = parse_contacts_vector(data, offset)
37
+ saved_count = data[offset, 4].unpack1('l<')
38
+ offset += 4
39
+ users, = parse_users_vector(data, offset)
40
+
41
+ new(contacts: contacts, saved_count: saved_count, users: users)
42
+ end
43
+
44
+ USER_EMPTY = 0xd3bc4b7a
45
+ private_constant :USER_EMPTY
46
+
47
+ class << self
48
+ private
49
+
50
+ def parse_contacts_vector(data, offset)
51
+ offset += 4 # vector tag
52
+ count = data[offset, 4].unpack1('L<')
53
+ offset += 4
54
+
55
+ contacts = []
56
+ count.times do
57
+ constructor = data[offset, 4].unpack1('L<')
58
+ offset += 4
59
+ raise "Unknown Contact constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::CONTACT
60
+
61
+ user_id = data[offset, 8].unpack1('Q<')
62
+ offset += 8
63
+ mutual_tag = data[offset, 4].unpack1('L<')
64
+ offset += 4
65
+ mutual = mutual_tag == Constructors::BOOL_TRUE
66
+ contacts << Contact.new(user_id: user_id, mutual: mutual)
67
+ end
68
+ [contacts, offset]
69
+ end
70
+
71
+ # User parsing mirrors TL::Dialogs.parse_user (user#020b1422). Kept private here
72
+ # so this class can be used without instantiating Dialogs.
73
+ def parse_users_vector(data, offset)
74
+ offset += 4 # vector tag
75
+ count = data[offset, 4].unpack1('L<')
76
+ offset += 4
77
+
78
+ users = []
79
+ count.times do
80
+ user, offset = parse_user(data, offset)
81
+ users << user if user
82
+ end
83
+ [users, offset]
84
+ end
85
+
86
+ def parse_user(data, offset)
87
+ constructor = data[offset, 4].unpack1('L<')
88
+ offset += 4
89
+
90
+ if constructor == USER_EMPTY
91
+ offset += 8
92
+ return [nil, offset]
93
+ end
94
+
95
+ raise "Unknown User constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::USER
96
+
97
+ flags = data[offset, 4].unpack1('L<')
98
+ offset += 4
99
+ flags2 = data[offset, 4].unpack1('L<')
100
+ offset += 4
101
+ id = data[offset, 8].unpack1('Q<')
102
+ offset += 8
103
+ access_hash = nil
104
+ if flags.anybits?(1 << 0)
105
+ access_hash = data[offset, 8].unpack1('Q<')
106
+ offset += 8
107
+ end
108
+
109
+ first_name = nil
110
+ first_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 1)
111
+ last_name = nil
112
+ last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
113
+
114
+ offset = skip_tl_string(data, offset) if flags.anybits?(1 << 3) # username
115
+ offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
116
+ offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
117
+ offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
118
+ offset += 4 if flags.anybits?(1 << 14) # bot_info_version
119
+ offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags.anybits?(1 << 18)
120
+ offset = skip_tl_string(data, offset) if flags.anybits?(1 << 19) # bot_inline_placeholder
121
+ offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
122
+ offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
123
+ offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0)
124
+ offset += 4 if flags2.anybits?(1 << 5) # stories_max_id
125
+ offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
126
+ offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
127
+ offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
128
+ offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
129
+ offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
130
+
131
+ [User.new(id: id, first_name: first_name, last_name: last_name, access_hash: access_hash), offset]
132
+ end
133
+
134
+ def read_tl_string(data, offset)
135
+ first_byte = data.getbyte(offset)
136
+ if first_byte == 254
137
+ len = data.getbyte(offset + 1) |
138
+ (data.getbyte(offset + 2) << 8) |
139
+ (data.getbyte(offset + 3) << 16)
140
+ total = 4 + len
141
+ else
142
+ len = first_byte
143
+ total = 1 + len
144
+ end
145
+ padding = (4 - (total % 4)) % 4
146
+ [data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
147
+ end
148
+
149
+ def skip_tl_string(data, offset)
150
+ _, new_offset = read_tl_string(data, offset)
151
+ new_offset
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class DeleteMessages
6
+ include Binary
7
+
8
+ def initialize(ids:, revoke: false)
9
+ @ids = ids
10
+ @revoke = revoke
11
+ end
12
+
13
+ def serialize
14
+ flags = 0
15
+ flags |= (1 << 0) if @revoke
16
+
17
+ result = u32_b(Constructors::MESSAGES_DELETE_MESSAGES)
18
+ result += u32_b(flags)
19
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
20
+ @ids.each { |id| result += u32_b(id) }
21
+ result
22
+ end
23
+ end
24
+ end
25
+ end
@@ -7,7 +7,7 @@ module MTProto
7
7
 
8
8
  attr_reader :status, :nonce, :server_nonce, :new_nonce_hash
9
9
 
10
- def self.parse(message)
10
+ def self.deserialize(message)
11
11
  data = message.body
12
12
  constructor = b_u32(data[0, 4])
13
13
 
@@ -5,7 +5,7 @@ require_relative '../schema'
5
5
  module MTProto
6
6
  module TL
7
7
  class Dialogs
8
- Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, keyword_init: true)
8
+ Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, :pts, keyword_init: true)
9
9
  Chat = Struct.new(:id, :title, :type, :access_hash, keyword_init: true)
10
10
 
11
11
  SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
@@ -28,7 +28,7 @@ module MTProto
28
28
  @schema ||= Schema.new(SCHEMA_PATH)
29
29
  end
30
30
 
31
- def self.parse(data)
31
+ def self.deserialize(data)
32
32
  constructor = data[0, 4].unpack1('L<')
33
33
 
34
34
  unless [Constructors::MESSAGES_DIALOGS, Constructors::MESSAGES_DIALOGS_SLICE].include?(constructor)
@@ -97,13 +97,17 @@ module MTProto
97
97
 
98
98
  offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
99
99
 
100
- offset += 4 if flags.anybits?(1 << 0) # pts
100
+ pts = nil
101
+ if flags.anybits?(1 << 0)
102
+ pts = data[offset, 4].unpack1('L<')
103
+ offset += 4
104
+ end
101
105
  offset = schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
102
106
  offset += 4 if flags.anybits?(1 << 4) # folder_id
103
107
  offset += 4 if flags.anybits?(1 << 5) # ttl_period
104
108
 
105
109
  [Dialog.new(peer_type: peer_type, peer_id: peer_id,
106
- top_message: top_message, unread_count: unread_count), offset]
110
+ top_message: top_message, unread_count: unread_count, pts: pts), offset]
107
111
  end
108
112
 
109
113
  def parse_peer(data, offset)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class EditMessage
6
+ include Binary
7
+
8
+ def initialize(peer:, id:, message:)
9
+ @peer = peer
10
+ @id = id
11
+ @message = message
12
+ end
13
+
14
+ def serialize
15
+ flags = 1 << 11 # message field present
16
+ result = u32_b(Constructors::MESSAGES_EDIT_MESSAGE)
17
+ result += u32_b(flags)
18
+ result += serialize_input_peer
19
+ result += u32_b(@id)
20
+ result += serialize_tl_string(@message)
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def serialize_input_peer
27
+ case @peer[:type]
28
+ when :self
29
+ u32_b(Constructors::INPUT_PEER_SELF)
30
+ when :user
31
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
32
+ when :chat
33
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
34
+ when :channel
35
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
36
+ else
37
+ raise "Unknown peer type: #{@peer[:type]}"
38
+ end
39
+ end
40
+
41
+ def serialize_tl_string(str)
42
+ bytes = str.encode('UTF-8').bytes
43
+ length = bytes.length
44
+
45
+ if length <= 253
46
+ [length] + bytes + padding(length + 1)
47
+ else
48
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
49
+ end
50
+ end
51
+
52
+ def padding(current_length)
53
+ pad_length = (4 - (current_length % 4)) % 4
54
+ [0] * pad_length
55
+ end
56
+ end
57
+ end
58
+ end
@@ -13,7 +13,7 @@ module MTProto
13
13
  @except_ids = except_ids
14
14
  end
15
15
 
16
- def body
16
+ def serialize
17
17
  u32_b(Constructors::AUTH_EXPORT_LOGIN_TOKEN) +
18
18
  u32_b(@api_id) +
19
19
  serialize_tl_string(@api_hash) +
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module MTProto
6
+ module TL
7
+ class ForwardMessages
8
+ include Binary
9
+
10
+ CONSTRUCTOR = 0x978928ca
11
+
12
+ def initialize(from_peer:, to_peer:, ids:, random_ids: nil)
13
+ @from_peer = from_peer
14
+ @to_peer = to_peer
15
+ @ids = ids
16
+ @random_ids = random_ids || Array.new(ids.length) { SecureRandom.random_number(2**63) }
17
+ end
18
+
19
+ def serialize
20
+ result = u32_b(CONSTRUCTOR)
21
+ result += u32_b(0) # flags
22
+ result += serialize_input_peer(@from_peer)
23
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
24
+ @ids.each { |id| result += u32_b(id) }
25
+ result += u32_b(Constructors::VECTOR) + u32_b(@random_ids.length)
26
+ @random_ids.each { |rid| result += u64_b(rid) }
27
+ result += serialize_input_peer(@to_peer)
28
+ result
29
+ end
30
+
31
+ private
32
+
33
+ def serialize_input_peer(peer)
34
+ case peer[:type]
35
+ when :self
36
+ u32_b(Constructors::INPUT_PEER_SELF)
37
+ when :user
38
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
39
+ when :chat
40
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(peer[:id])
41
+ when :channel
42
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
43
+ else
44
+ raise "Unknown peer type: #{peer[:type]}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetChannelDifference
6
+ include Binary
7
+
8
+ def initialize(channel:, pts:, limit: 100, force: false)
9
+ @channel = channel
10
+ @pts = pts
11
+ @limit = limit
12
+ @force = force
13
+ end
14
+
15
+ def serialize
16
+ flags = 0
17
+ flags |= (1 << 0) if @force
18
+
19
+ result = u32_b(Constructors::UPDATES_GET_CHANNEL_DIFFERENCE)
20
+ result += u32_b(flags)
21
+ result += serialize_input_channel
22
+ result += u32_b(Constructors::CHANNEL_MESSAGES_FILTER_EMPTY)
23
+ result += u32_b(@pts)
24
+ result += u32_b(@limit)
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def serialize_input_channel
31
+ if @channel.nil? || @channel[:type] == :empty
32
+ u32_b(Constructors::INPUT_CHANNEL_EMPTY)
33
+ else
34
+ u32_b(Constructors::INPUT_CHANNEL) +
35
+ u64_b(@channel[:id]) +
36
+ u64_b(@channel[:access_hash])
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -5,7 +5,7 @@ module MTProto
5
5
  class GetConfig
6
6
  include Binary
7
7
 
8
- def body
8
+ def serialize
9
9
  u32_b(Constructors::HELP_GET_CONFIG)
10
10
  end
11
11
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetContacts
6
+ include Binary
7
+
8
+ def initialize(hash: 0)
9
+ @hash = hash
10
+ end
11
+
12
+ def serialize
13
+ u32_b(Constructors::CONTACTS_GET_CONTACTS) + u64_b(@hash)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -16,7 +16,7 @@ module MTProto
16
16
  @folder_id = folder_id
17
17
  end
18
18
 
19
- def body
19
+ def serialize
20
20
  flags = 0
21
21
  flags |= (1 << 0) if @exclude_pinned
22
22
  flags |= (1 << 1) if @folder_id
@@ -16,7 +16,7 @@ module MTProto
16
16
  @qts_limit = qts_limit
17
17
  end
18
18
 
19
- def body
19
+ def serialize
20
20
  flags = 0
21
21
  flags |= (1 << 0) if @pts_limit
22
22
  flags |= (1 << 1) if @pts_total_limit
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetFile
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xbe5335be
9
+ INPUT_PHOTO_FILE_LOCATION = 0x40181ffe
10
+
11
+ # location: { id:, access_hash:, file_reference: (binary String), thumb_size: (String) }
12
+ def initialize(location:, offset: 0, limit: 1024 * 1024)
13
+ @location = location
14
+ @offset = offset
15
+ @limit = limit
16
+ end
17
+
18
+ def serialize
19
+ result = u32_b(CONSTRUCTOR)
20
+ result += u32_b(0) # flags
21
+ result += serialize_location
22
+ result += u64_b(@offset)
23
+ result += u32_b(@limit)
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def serialize_location
30
+ u32_b(INPUT_PHOTO_FILE_LOCATION) +
31
+ u64_b(@location[:id]) +
32
+ u64_b(@location[:access_hash]) +
33
+ serialize_tl_bytes(@location[:file_reference]) +
34
+ serialize_tl_bytes(@location[:thumb_size])
35
+ end
36
+
37
+ def serialize_tl_bytes(value)
38
+ bytes = value.to_s.b.bytes
39
+ length = bytes.length
40
+
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