mtproto 0.0.13 → 0.0.15

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 (93) 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 +35 -2
  10. data/lib/mtproto/client.rb +81 -18
  11. data/lib/mtproto/tl/constructor_names.rb +10 -0
  12. data/lib/mtproto/tl/constructors.rb +26 -0
  13. data/lib/mtproto/tl/object.rb +1 -1
  14. data/lib/mtproto/tl/objects/account_password.rb +1 -1
  15. data/lib/mtproto/tl/objects/authorization.rb +1 -1
  16. data/lib/mtproto/tl/objects/channels_create_channel.rb +48 -0
  17. data/lib/mtproto/tl/objects/channels_delete_messages.rb +32 -0
  18. data/lib/mtproto/tl/objects/channels_delete_participant_history.rb +34 -0
  19. data/lib/mtproto/tl/objects/channels_edit_banned.rb +54 -0
  20. data/lib/mtproto/tl/objects/channels_get_messages.rb +33 -0
  21. data/lib/mtproto/tl/objects/channels_get_participant.rb +36 -0
  22. data/lib/mtproto/tl/objects/channels_join_channel.rb +22 -0
  23. data/lib/mtproto/tl/objects/channels_leave_channel.rb +22 -0
  24. data/lib/mtproto/tl/objects/channels_report_spam.rb +37 -0
  25. data/lib/mtproto/tl/objects/check_password.rb +1 -1
  26. data/lib/mtproto/tl/objects/client_dh_inner_data.rb +1 -1
  27. data/lib/mtproto/tl/objects/contacts.rb +156 -0
  28. data/lib/mtproto/tl/objects/create_bot.rb +54 -0
  29. data/lib/mtproto/tl/objects/delete_messages.rb +25 -0
  30. data/lib/mtproto/tl/objects/dh_gen_response.rb +1 -1
  31. data/lib/mtproto/tl/objects/dialogs.rb +18 -4
  32. data/lib/mtproto/tl/objects/edit_access_settings.rb +46 -0
  33. data/lib/mtproto/tl/objects/edit_message.rb +58 -0
  34. data/lib/mtproto/tl/objects/export_bot_token.rb +32 -0
  35. data/lib/mtproto/tl/objects/export_login_token.rb +1 -1
  36. data/lib/mtproto/tl/objects/exported_bot_token.rb +27 -0
  37. data/lib/mtproto/tl/objects/forward_messages.rb +55 -0
  38. data/lib/mtproto/tl/objects/get_access_settings.rb +28 -0
  39. data/lib/mtproto/tl/objects/get_channel_difference.rb +41 -0
  40. data/lib/mtproto/tl/objects/get_config.rb +1 -1
  41. data/lib/mtproto/tl/objects/get_contacts.rb +17 -0
  42. data/lib/mtproto/tl/objects/get_dialogs.rb +1 -1
  43. data/lib/mtproto/tl/objects/get_difference.rb +1 -1
  44. data/lib/mtproto/tl/objects/get_file.rb +54 -0
  45. data/lib/mtproto/tl/objects/get_full_channel.rb +29 -0
  46. data/lib/mtproto/tl/objects/get_full_user.rb +28 -0
  47. data/lib/mtproto/tl/objects/get_history.rb +1 -1
  48. data/lib/mtproto/tl/objects/get_messages_reactions.rb +39 -0
  49. data/lib/mtproto/tl/objects/get_password.rb +1 -1
  50. data/lib/mtproto/tl/objects/get_state.rb +1 -1
  51. data/lib/mtproto/tl/objects/get_users.rb +1 -1
  52. data/lib/mtproto/tl/objects/help_config.rb +1 -1
  53. data/lib/mtproto/tl/objects/import_bot_authorization.rb +44 -0
  54. data/lib/mtproto/tl/objects/import_login_token.rb +1 -1
  55. data/lib/mtproto/tl/objects/init_connection.rb +2 -2
  56. data/lib/mtproto/tl/objects/input_keyboard_button_request_peer.rb +54 -0
  57. data/lib/mtproto/tl/objects/invite_to_channel.rb +35 -0
  58. data/lib/mtproto/tl/objects/invoke_with_layer.rb +2 -2
  59. data/lib/mtproto/tl/objects/login_token.rb +2 -2
  60. data/lib/mtproto/tl/objects/messages.rb +1 -1
  61. data/lib/mtproto/tl/objects/messages_get_messages.rb +26 -0
  62. data/lib/mtproto/tl/objects/pq_inner_data.rb +1 -1
  63. data/lib/mtproto/tl/objects/raw_response.rb +29 -0
  64. data/lib/mtproto/tl/objects/reply_keyboard_markup.rb +35 -0
  65. data/lib/mtproto/tl/objects/req_dh_params.rb +1 -1
  66. data/lib/mtproto/tl/objects/req_pq_multi.rb +1 -1
  67. data/lib/mtproto/tl/objects/request_peer_type_create_bot.rb +47 -0
  68. data/lib/mtproto/tl/objects/res_pq.rb +1 -1
  69. data/lib/mtproto/tl/objects/resolve_username.rb +40 -0
  70. data/lib/mtproto/tl/objects/save_file_part.rb +40 -0
  71. data/lib/mtproto/tl/objects/send_bot_requested_peer.rb +51 -0
  72. data/lib/mtproto/tl/objects/send_code.rb +1 -1
  73. data/lib/mtproto/tl/objects/send_media.rb +96 -0
  74. data/lib/mtproto/tl/objects/send_message.rb +73 -0
  75. data/lib/mtproto/tl/objects/send_reaction.rb +68 -0
  76. data/lib/mtproto/tl/objects/sent_code.rb +1 -1
  77. data/lib/mtproto/tl/objects/server_dh_inner_data.rb +1 -1
  78. data/lib/mtproto/tl/objects/server_dh_params.rb +1 -1
  79. data/lib/mtproto/tl/objects/set_bot_commands.rb +50 -0
  80. data/lib/mtproto/tl/objects/set_bot_info.rb +64 -0
  81. data/lib/mtproto/tl/objects/set_client_dh_params.rb +1 -1
  82. data/lib/mtproto/tl/objects/sign_in.rb +1 -1
  83. data/lib/mtproto/tl/objects/update.rb +1 -1
  84. data/lib/mtproto/tl/objects/update_short.rb +2 -2
  85. data/lib/mtproto/tl/objects/update_short_message.rb +1 -1
  86. data/lib/mtproto/tl/objects/update_short_sent_message.rb +36 -0
  87. data/lib/mtproto/tl/objects/update_username.rb +39 -0
  88. data/lib/mtproto/tl/objects/updates_difference.rb +93 -14
  89. data/lib/mtproto/tl/objects/updates_state.rb +1 -1
  90. data/lib/mtproto/tl/objects/upload_profile_photo.rb +65 -0
  91. data/lib/mtproto/tl/objects/users.rb +1 -1
  92. data/lib/mtproto/version.rb +1 -1
  93. metadata +49 -1
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SetBotCommands
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x0517165a
9
+ BOT_COMMAND_SCOPE_DEFAULT = 0x2f6cb2ab
10
+ BOT_COMMAND = 0xc27ac8c7
11
+
12
+ # commands: array of { command:, description: }
13
+ def initialize(commands:, lang_code: '')
14
+ @commands = commands
15
+ @lang_code = lang_code
16
+ end
17
+
18
+ def serialize
19
+ result = u32_b(CONSTRUCTOR)
20
+ result += u32_b(BOT_COMMAND_SCOPE_DEFAULT)
21
+ result += serialize_tl_string(@lang_code)
22
+ result += u32_b(Constructors::VECTOR)
23
+ result += u32_b(@commands.size)
24
+ @commands.each do |cmd|
25
+ result += u32_b(BOT_COMMAND)
26
+ result += serialize_tl_string(cmd[:command])
27
+ result += serialize_tl_string(cmd[:description])
28
+ end
29
+ result
30
+ end
31
+
32
+ private
33
+
34
+ def serialize_tl_string(str)
35
+ bytes = str.to_s.encode('UTF-8').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
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SetBotInfo
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x10cf3123
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+
12
+ # bot: { id:, access_hash: } or { type: :self } (nil = the calling bot)
13
+ def initialize(bot: nil, lang_code: '', name: nil, about: nil, description: nil)
14
+ @bot = bot
15
+ @lang_code = lang_code
16
+ @name = name
17
+ @about = about
18
+ @description = description
19
+ end
20
+
21
+ def serialize
22
+ flags = 0
23
+ flags |= (1 << 2) if @bot
24
+ flags |= (1 << 3) if @name
25
+ flags |= (1 << 0) if @about
26
+ flags |= (1 << 1) if @description
27
+
28
+ result = u32_b(CONSTRUCTOR)
29
+ result += u32_b(flags)
30
+ result += serialize_input_user(@bot) if @bot
31
+ result += serialize_tl_string(@lang_code)
32
+ result += serialize_tl_string(@name) if @name
33
+ result += serialize_tl_string(@about) if @about
34
+ result += serialize_tl_string(@description) if @description
35
+ result
36
+ end
37
+
38
+ private
39
+
40
+ def serialize_input_user(user)
41
+ if user[:type] == :self
42
+ u32_b(INPUT_USER_SELF)
43
+ else
44
+ u32_b(INPUT_USER) + u64_b(user[:id]) + u64_b(user[:access_hash] || 0)
45
+ end
46
+ end
47
+
48
+ def serialize_tl_string(str)
49
+ bytes = str.to_s.encode('UTF-8').bytes
50
+ length = bytes.length
51
+ if length <= 253
52
+ [length] + bytes + padding(length + 1)
53
+ else
54
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
55
+ end
56
+ end
57
+
58
+ def padding(current_length)
59
+ pad_length = (4 - (current_length % 4)) % 4
60
+ [0] * pad_length
61
+ end
62
+ end
63
+ end
64
+ end
@@ -16,7 +16,7 @@ module MTProto
16
16
  @encrypted_data = encrypted_data
17
17
  end
18
18
 
19
- def body
19
+ def serialize
20
20
  result = u32_b(Constructors::SET_CLIENT_DH_PARAMS)
21
21
  result += @nonce.bytes
22
22
  result += @server_nonce.bytes
@@ -13,7 +13,7 @@ module MTProto
13
13
  @phone_code = phone_code
14
14
  end
15
15
 
16
- def body
16
+ def serialize
17
17
  flags = 1 << 0
18
18
 
19
19
  u32_b(Constructors::AUTH_SIGN_IN) +
@@ -10,7 +10,7 @@ module MTProto
10
10
  @text = text
11
11
  end
12
12
 
13
- def self.parse(bytes)
13
+ def self.deserialize(bytes)
14
14
  constructor_id = bytes[0, 4].unpack1('L<')
15
15
  type = type_name(constructor_id)
16
16
 
@@ -10,9 +10,9 @@ module MTProto
10
10
  @date = date
11
11
  end
12
12
 
13
- def self.parse(bytes)
13
+ def self.deserialize(bytes)
14
14
  date = bytes[-4, 4].unpack1('l<')
15
- update = Update.parse(bytes[4...-4])
15
+ update = Update.deserialize(bytes[4...-4])
16
16
  new(update, date)
17
17
  end
18
18
  end
@@ -23,7 +23,7 @@ module MTProto
23
23
  flags & (1 << 4) != 0
24
24
  end
25
25
 
26
- def self.parse(bytes)
26
+ def self.deserialize(bytes)
27
27
  offset = 4 # skip constructor
28
28
  flags = bytes[offset, 4].unpack1('L<')
29
29
  offset += 4
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class UpdateShortSentMessage
6
+ attr_reader :flags, :id, :pts, :pts_count, :date
7
+
8
+ def initialize(flags:, id:, pts:, pts_count:, date:)
9
+ @flags = flags
10
+ @id = id
11
+ @pts = pts
12
+ @pts_count = pts_count
13
+ @date = date
14
+ end
15
+
16
+ def out?
17
+ flags & (1 << 1) != 0
18
+ end
19
+
20
+ def self.deserialize(bytes)
21
+ offset = 4 # skip constructor
22
+ flags = bytes[offset, 4].unpack1('L<')
23
+ offset += 4
24
+ id = bytes[offset, 4].unpack1('l<')
25
+ offset += 4
26
+ pts = bytes[offset, 4].unpack1('l<')
27
+ offset += 4
28
+ pts_count = bytes[offset, 4].unpack1('l<')
29
+ offset += 4
30
+ date = bytes[offset, 4].unpack1('l<')
31
+
32
+ new(flags: flags, id: id, pts: pts, pts_count: pts_count, date: date)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class UpdateUsername
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x3e0bdd7c
9
+
10
+ def initialize(username:)
11
+ @username = username
12
+ end
13
+
14
+ def serialize
15
+ result = u32_b(CONSTRUCTOR)
16
+ result += serialize_tl_string(@username)
17
+ result
18
+ end
19
+
20
+ private
21
+
22
+ def serialize_tl_string(str)
23
+ bytes = str.encode('UTF-8').bytes
24
+ length = bytes.length
25
+
26
+ if length <= 253
27
+ [length] + bytes + padding(length + 1)
28
+ else
29
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
30
+ end
31
+ end
32
+
33
+ def padding(current_length)
34
+ pad_length = (4 - (current_length % 4)) % 4
35
+ [0] * pad_length
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,28 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../schema'
4
+ require_relative 'dialogs'
4
5
 
5
6
  module MTProto
6
7
  module TL
7
8
  class UpdatesDifference
8
- attr_reader :type, :date, :seq, :new_messages, :pts, :state
9
+ attr_reader :type, :date, :seq, :new_messages, :pts, :state, :chats, :users
9
10
 
10
11
  SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
11
12
 
12
- def initialize(type:, date: nil, seq: nil, new_messages: [], pts: nil, state: nil)
13
+ def initialize(type:, date: nil, seq: nil, new_messages: [], pts: nil, state: nil, chats: [], users: [])
13
14
  @type = type
14
15
  @date = date
15
16
  @seq = seq
16
17
  @new_messages = new_messages
17
18
  @pts = pts
18
19
  @state = state
20
+ @chats = chats
21
+ @users = users
19
22
  end
20
23
 
21
24
  def self.schema
22
25
  @schema ||= Schema.new(SCHEMA_PATH)
23
26
  end
24
27
 
25
- def self.parse(data)
28
+ def self.deserialize(data)
26
29
  constructor = data[0, 4].unpack1('L<')
27
30
 
28
31
  case constructor
@@ -41,6 +44,11 @@ module MTProto
41
44
  MESSAGE_CONSTRUCTOR = Constructors::MESSAGE
42
45
  MESSAGE_SERVICE = 0x7a800e0a
43
46
  MESSAGE_EMPTY = 0x90a6ca84
47
+ MESSAGE_REPLY_HEADER = 0x6917560b
48
+ MEDIA_PHOTO = 0x695150d7
49
+ MEDIA_DOCUMENT = 0x52d8ccd9
50
+ UPDATE_NEW_MESSAGE = 0x1f2b0afd
51
+ UPDATE_NEW_CHANNEL_MESSAGE = 0x62ba04d9
44
52
 
45
53
  class << self
46
54
  private
@@ -53,24 +61,48 @@ module MTProto
53
61
  )
54
62
  end
55
63
 
56
- # updates.difference: new_messages new_encrypted_messages other_updates chats users state
64
+ # updates.difference: new_messages new_encrypted_messages other_updates chats users state.
65
+ # Channel/supergroup messages arrive in other_updates as
66
+ # updateNewChannelMessage (not in new_messages), so we mine those too; and
67
+ # chats/users carry the peers' access_hashes for the bot's peer cache.
57
68
  def parse_difference(data, constructor)
58
69
  offset = 4
59
70
 
60
71
  messages, offset = parse_messages_vector(data, offset)
61
72
  offset = schema.skip_vector(data, offset) # new_encrypted_messages
62
- offset = schema.skip_vector(data, offset) # other_updates
63
- offset = schema.skip_vector(data, offset) # chats
64
- offset = schema.skip_vector(data, offset) # users
65
- state = UpdatesState.parse(data[offset..])
73
+ other_messages, offset = parse_other_updates(data, offset)
74
+ chats, offset = Dialogs.chats_at(data, offset)
75
+ users, offset = Dialogs.users_at(data, offset)
76
+ state = UpdatesState.deserialize(data[offset..])
66
77
 
67
78
  new(
68
79
  type: constructor == Constructors::UPDATES_DIFFERENCE ? :difference : :slice,
69
- new_messages: messages,
70
- state: state
80
+ new_messages: messages + other_messages,
81
+ chats: chats, users: users, state: state
71
82
  )
72
83
  end
73
84
 
85
+ # other_updates: Vector<Update>. Mine the message-bearing updates
86
+ # (updateNewMessage / updateNewChannelMessage — each is the update ctor,
87
+ # then an inner Message, then pts/pts_count); advance the rest via schema.
88
+ def parse_other_updates(data, offset)
89
+ offset += 4 # vector constructor
90
+ count = data[offset, 4].unpack1('L<')
91
+ offset += 4
92
+
93
+ messages = []
94
+ count.times do
95
+ ctor = data[offset, 4].unpack1('L<')
96
+ if [UPDATE_NEW_MESSAGE, UPDATE_NEW_CHANNEL_MESSAGE].include?(ctor)
97
+ inner_off = offset + 4
98
+ msg = extract_message(data, inner_off, data[inner_off, 4].unpack1('L<'))
99
+ messages << msg if msg
100
+ end
101
+ offset = schema.skip(data, offset)
102
+ end
103
+ [messages, offset]
104
+ end
105
+
74
106
  def parse_messages_vector(data, offset)
75
107
  offset += 4 # vector constructor
76
108
  count = data[offset, 4].unpack1('L<')
@@ -97,20 +129,30 @@ module MTProto
97
129
  id = data[offset, 4].unpack1('l<')
98
130
  offset += 4
99
131
 
100
- offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
132
+ from_type = from_id = nil
133
+ from_type, from_id, offset = parse_peer(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
101
134
  offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
102
135
  peer_type, peer_id, offset = parse_peer(data, offset)
103
136
  offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
104
137
  offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
105
138
  offset += 8 if flags.anybits?(1 << 11) # via_bot_id
106
139
  offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
107
- offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
140
+
141
+ is_reply = false
142
+ reply_to_msg_id = nil
143
+ if flags.anybits?(1 << 3) # reply_to
144
+ reply_to_msg_id, is_reply = parse_reply_to(data, offset)
145
+ offset = schema.skip(data, offset)
146
+ end
108
147
 
109
148
  date = data[offset, 4].unpack1('L<')
110
149
  offset += 4
111
- message_text, = read_tl_string(data, offset)
150
+ message_text, offset = read_tl_string(data, offset)
151
+ media = flags.anybits?(1 << 9) ? parse_media(data, offset) : nil
112
152
 
113
- { id: id, date: date, message: message_text, peer_type: peer_type, peer_id: peer_id }
153
+ { id: id, date: date, message: message_text, peer_type: peer_type, peer_id: peer_id,
154
+ from_type: from_type, from_id: from_id, out: flags.anybits?(1 << 1),
155
+ media: media, is_reply: is_reply, reply_to_msg_id: reply_to_msg_id }
114
156
  end
115
157
 
116
158
  def parse_peer(data, offset)
@@ -129,6 +171,43 @@ module MTProto
129
171
  end
130
172
  end
131
173
 
174
+ # messageReplyHeader: ctor, flags, reply_to_msg_id:flags.4?int (first payload
175
+ # field). A plain message in a forum topic carries this header (forum_topic
176
+ # flags.3) but is a reply only when it targets a message in the topic
177
+ # (reply_to_top_id flags.1). => [reply_to_msg_id, is_reply]
178
+ def parse_reply_to(data, offset)
179
+ return [nil, false] unless data[offset, 4].unpack1('L<') == MESSAGE_REPLY_HEADER
180
+
181
+ io = offset + 4
182
+ flags = data[io, 4].unpack1('L<')
183
+ io += 4
184
+ forum_topic = flags.anybits?(1 << 3)
185
+ msg_id = flags.anybits?(1 << 4) ? data[io, 4].unpack1('l<') : nil
186
+ [msg_id, forum_topic ? flags.anybits?(1 << 1) : true]
187
+ rescue StandardError
188
+ [nil, false]
189
+ end
190
+
191
+ # Classify message media into :photo / :video / :audio / :document, or nil.
192
+ def parse_media(data, offset)
193
+ case data[offset, 4].unpack1('L<')
194
+ when MEDIA_PHOTO
195
+ :photo
196
+ when MEDIA_DOCUMENT
197
+ document_kind(data[offset + 4, 4].unpack1('L<'))
198
+ end
199
+ rescue StandardError
200
+ nil
201
+ end
202
+
203
+ # messageMediaDocument flags: video flags.6 / round flags.7 / voice flags.8.
204
+ def document_kind(flags)
205
+ return :video if flags.anybits?(1 << 6) || flags.anybits?(1 << 7)
206
+ return :audio if flags.anybits?(1 << 8)
207
+
208
+ :document
209
+ end
210
+
132
211
  def read_tl_string(data, offset)
133
212
  first_byte = data.getbyte(offset)
134
213
  if first_byte == 254
@@ -13,7 +13,7 @@ module MTProto
13
13
  @unread_count = unread_count
14
14
  end
15
15
 
16
- def self.parse(data)
16
+ def self.deserialize(data)
17
17
  constructor = data[0, 4].unpack1('L<')
18
18
  raise UnexpectedConstructorError, constructor unless constructor == Constructors::UPDATES_STATE
19
19
 
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class UploadProfilePhoto
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x0388a3b5
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+ INPUT_FILE = 0xf52ff27f
12
+
13
+ # bot: { id:, access_hash: } or { type: :self }; nil targets the calling bot
14
+ # file: { id:, parts:, name:, md5_checksum: }
15
+ def initialize(file:, bot: nil)
16
+ @bot = bot
17
+ @file = file
18
+ end
19
+
20
+ def serialize
21
+ flags = (1 << 0) # file
22
+ flags |= (1 << 5) if @bot
23
+
24
+ result = u32_b(CONSTRUCTOR)
25
+ result += u32_b(flags)
26
+ result += serialize_input_user(@bot) if @bot
27
+ result += serialize_input_file(@file)
28
+ result
29
+ end
30
+
31
+ private
32
+
33
+ def serialize_input_user(user)
34
+ if user[:type] == :self
35
+ u32_b(INPUT_USER_SELF)
36
+ else
37
+ u32_b(INPUT_USER) + u64_b(user[:id]) + u64_b(user[:access_hash] || 0)
38
+ end
39
+ end
40
+
41
+ def serialize_input_file(file)
42
+ u32_b(INPUT_FILE) +
43
+ u64_b(file[:id]) +
44
+ u32_b(file[:parts]) +
45
+ serialize_tl_string(file[:name].to_s) +
46
+ serialize_tl_string(file[:md5_checksum].to_s)
47
+ end
48
+
49
+ def serialize_tl_string(str)
50
+ bytes = str.encode('UTF-8').bytes
51
+ length = bytes.length
52
+ if length <= 253
53
+ [length] + bytes + padding(length + 1)
54
+ else
55
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
56
+ end
57
+ end
58
+
59
+ def padding(current_length)
60
+ pad_length = (4 - (current_length % 4)) % 4
61
+ [0] * pad_length
62
+ end
63
+ end
64
+ end
65
+ end
@@ -6,7 +6,7 @@ module MTProto
6
6
  User = Struct.new(:id, :access_hash, :first_name, :last_name, :username, :phone, :flags, :flags2,
7
7
  keyword_init: true)
8
8
 
9
- def self.parse(data)
9
+ def self.deserialize(data)
10
10
  constructor = data[0, 4].unpack1('L<')
11
11
  raise "Expected Vector constructor, got 0x#{constructor.to_s(16)}" unless constructor == Constructors::VECTOR
12
12
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MTProto
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.15'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mtproto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Levenkov
@@ -49,8 +49,10 @@ extra_rdoc_files: []
49
49
  files:
50
50
  - ".env.example"
51
51
  - ".ruby-version"
52
+ - FUTURE.md
52
53
  - Rakefile
53
54
  - data/tl-schema.json
55
+ - docs/test_architecture_level1.md
54
56
  - ext/aes_ige/aes_ige.c
55
57
  - ext/aes_ige/extconf.rb
56
58
  - ext/factorization/extconf.rb
@@ -62,6 +64,7 @@ files:
62
64
  - lib/mtproto/client/api.rb
63
65
  - lib/mtproto/client/api/check_password.rb
64
66
  - lib/mtproto/client/api/export_login_token.rb
67
+ - lib/mtproto/client/api/get_contacts.rb
65
68
  - lib/mtproto/client/api/get_dialogs.rb
66
69
  - lib/mtproto/client/api/get_history.rb
67
70
  - lib/mtproto/client/api/get_updates_difference.rb
@@ -69,6 +72,7 @@ files:
69
72
  - lib/mtproto/client/api/get_users.rb
70
73
  - lib/mtproto/client/api/import_login_token.rb
71
74
  - lib/mtproto/client/api/send_code.rb
75
+ - lib/mtproto/client/api/send_message.rb
72
76
  - lib/mtproto/client/api/sign_in.rb
73
77
  - lib/mtproto/client/rpc.rb
74
78
  - lib/mtproto/client/rpc/response.rb
@@ -92,44 +96,86 @@ files:
92
96
  - lib/mtproto/tl/object.rb
93
97
  - lib/mtproto/tl/objects/account_password.rb
94
98
  - lib/mtproto/tl/objects/authorization.rb
99
+ - lib/mtproto/tl/objects/channels_create_channel.rb
100
+ - lib/mtproto/tl/objects/channels_delete_messages.rb
101
+ - lib/mtproto/tl/objects/channels_delete_participant_history.rb
102
+ - lib/mtproto/tl/objects/channels_edit_banned.rb
103
+ - lib/mtproto/tl/objects/channels_get_messages.rb
104
+ - lib/mtproto/tl/objects/channels_get_participant.rb
105
+ - lib/mtproto/tl/objects/channels_join_channel.rb
106
+ - lib/mtproto/tl/objects/channels_leave_channel.rb
107
+ - lib/mtproto/tl/objects/channels_report_spam.rb
95
108
  - lib/mtproto/tl/objects/check_password.rb
96
109
  - lib/mtproto/tl/objects/client_dh_inner_data.rb
110
+ - lib/mtproto/tl/objects/contacts.rb
111
+ - lib/mtproto/tl/objects/create_bot.rb
112
+ - lib/mtproto/tl/objects/delete_messages.rb
97
113
  - lib/mtproto/tl/objects/dh_gen_response.rb
98
114
  - lib/mtproto/tl/objects/dialogs.rb
115
+ - lib/mtproto/tl/objects/edit_access_settings.rb
116
+ - lib/mtproto/tl/objects/edit_message.rb
117
+ - lib/mtproto/tl/objects/export_bot_token.rb
99
118
  - lib/mtproto/tl/objects/export_login_token.rb
119
+ - lib/mtproto/tl/objects/exported_bot_token.rb
120
+ - lib/mtproto/tl/objects/forward_messages.rb
121
+ - lib/mtproto/tl/objects/get_access_settings.rb
122
+ - lib/mtproto/tl/objects/get_channel_difference.rb
100
123
  - lib/mtproto/tl/objects/get_config.rb
124
+ - lib/mtproto/tl/objects/get_contacts.rb
101
125
  - lib/mtproto/tl/objects/get_dialogs.rb
102
126
  - lib/mtproto/tl/objects/get_difference.rb
127
+ - lib/mtproto/tl/objects/get_file.rb
128
+ - lib/mtproto/tl/objects/get_full_channel.rb
129
+ - lib/mtproto/tl/objects/get_full_user.rb
103
130
  - lib/mtproto/tl/objects/get_history.rb
131
+ - lib/mtproto/tl/objects/get_messages_reactions.rb
104
132
  - lib/mtproto/tl/objects/get_password.rb
105
133
  - lib/mtproto/tl/objects/get_state.rb
106
134
  - lib/mtproto/tl/objects/get_users.rb
107
135
  - lib/mtproto/tl/objects/gzip_packed.rb
108
136
  - lib/mtproto/tl/objects/help_config.rb
137
+ - lib/mtproto/tl/objects/import_bot_authorization.rb
109
138
  - lib/mtproto/tl/objects/import_login_token.rb
110
139
  - lib/mtproto/tl/objects/init_connection.rb
140
+ - lib/mtproto/tl/objects/input_keyboard_button_request_peer.rb
141
+ - lib/mtproto/tl/objects/invite_to_channel.rb
111
142
  - lib/mtproto/tl/objects/invoke_with_layer.rb
112
143
  - lib/mtproto/tl/objects/login_token.rb
113
144
  - lib/mtproto/tl/objects/message.rb
114
145
  - lib/mtproto/tl/objects/messages.rb
146
+ - lib/mtproto/tl/objects/messages_get_messages.rb
115
147
  - lib/mtproto/tl/objects/msg_container.rb
116
148
  - lib/mtproto/tl/objects/new_session_created.rb
117
149
  - lib/mtproto/tl/objects/pq_inner_data.rb
150
+ - lib/mtproto/tl/objects/raw_response.rb
151
+ - lib/mtproto/tl/objects/reply_keyboard_markup.rb
118
152
  - lib/mtproto/tl/objects/req_dh_params.rb
119
153
  - lib/mtproto/tl/objects/req_pq_multi.rb
154
+ - lib/mtproto/tl/objects/request_peer_type_create_bot.rb
120
155
  - lib/mtproto/tl/objects/res_pq.rb
156
+ - lib/mtproto/tl/objects/resolve_username.rb
121
157
  - lib/mtproto/tl/objects/rpc_error.rb
158
+ - lib/mtproto/tl/objects/save_file_part.rb
159
+ - lib/mtproto/tl/objects/send_bot_requested_peer.rb
122
160
  - lib/mtproto/tl/objects/send_code.rb
161
+ - lib/mtproto/tl/objects/send_media.rb
162
+ - lib/mtproto/tl/objects/send_message.rb
163
+ - lib/mtproto/tl/objects/send_reaction.rb
123
164
  - lib/mtproto/tl/objects/sent_code.rb
124
165
  - lib/mtproto/tl/objects/server_dh_inner_data.rb
125
166
  - lib/mtproto/tl/objects/server_dh_params.rb
167
+ - lib/mtproto/tl/objects/set_bot_commands.rb
168
+ - lib/mtproto/tl/objects/set_bot_info.rb
126
169
  - lib/mtproto/tl/objects/set_client_dh_params.rb
127
170
  - lib/mtproto/tl/objects/sign_in.rb
128
171
  - lib/mtproto/tl/objects/update.rb
129
172
  - lib/mtproto/tl/objects/update_short.rb
130
173
  - lib/mtproto/tl/objects/update_short_message.rb
174
+ - lib/mtproto/tl/objects/update_short_sent_message.rb
175
+ - lib/mtproto/tl/objects/update_username.rb
131
176
  - lib/mtproto/tl/objects/updates_difference.rb
132
177
  - lib/mtproto/tl/objects/updates_state.rb
178
+ - lib/mtproto/tl/objects/upload_profile_photo.rb
133
179
  - lib/mtproto/tl/objects/users.rb
134
180
  - lib/mtproto/tl/schema.rb
135
181
  - lib/mtproto/transport/abridged_packet_codec.rb
@@ -151,6 +197,8 @@ metadata:
151
197
  rdoc_options: []
152
198
  require_paths:
153
199
  - lib
200
+ - ext/aes_ige
201
+ - ext/factorization
154
202
  required_ruby_version: !ruby/object:Gem::Requirement
155
203
  requirements:
156
204
  - - ">="