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,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetFullChannel
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x08736a09
9
+
10
+ def initialize(channel:)
11
+ @channel = channel
12
+ end
13
+
14
+ def serialize
15
+ result = u32_b(CONSTRUCTOR)
16
+ result += serialize_input_channel
17
+ result
18
+ end
19
+
20
+ private
21
+
22
+ def serialize_input_channel
23
+ u32_b(Constructors::INPUT_CHANNEL) +
24
+ u64_b(@channel[:id]) +
25
+ u64_b(@channel[:access_hash])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetFullUser
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xb60f5918
9
+ INPUT_USER = 0xf21158c6
10
+ INPUT_USER_SELF = 0xf7c1b13f
11
+
12
+ # user: { id:, access_hash: } or { type: :self }
13
+ def initialize(user:)
14
+ @user = user
15
+ end
16
+
17
+ def serialize
18
+ result = u32_b(CONSTRUCTOR)
19
+ result += if @user[:type] == :self
20
+ u32_b(INPUT_USER_SELF)
21
+ else
22
+ u32_b(INPUT_USER) + u64_b(@user[:id]) + u64_b(@user[:access_hash] || 0)
23
+ end
24
+ result
25
+ end
26
+ end
27
+ end
28
+ end
@@ -17,7 +17,7 @@ module MTProto
17
17
  @min_id = min_id
18
18
  end
19
19
 
20
- def body
20
+ def serialize
21
21
  result = u32_b(CONSTRUCTOR)
22
22
  result += serialize_input_peer
23
23
  result += u32_b(@offset_id)
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class GetMessagesReactions
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x8bba90e6
9
+
10
+ def initialize(peer:, ids:)
11
+ @peer = peer
12
+ @ids = ids
13
+ end
14
+
15
+ def serialize
16
+ result = u32_b(CONSTRUCTOR)
17
+ result += serialize_input_peer
18
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
19
+ @ids.each { |id| result += u32_b(id) }
20
+ result
21
+ end
22
+
23
+ private
24
+
25
+ def serialize_input_peer
26
+ case @peer[:type]
27
+ when :channel
28
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
29
+ when :user
30
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
31
+ when :chat
32
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
33
+ else
34
+ raise "Unknown peer type: #{@peer[:type]}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -5,7 +5,7 @@ module MTProto
5
5
  class GetPassword
6
6
  include Binary
7
7
 
8
- def body
8
+ def serialize
9
9
  u32_b(Constructors::ACCOUNT_GET_PASSWORD)
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module MTProto
5
5
  class GetState
6
6
  include Binary
7
7
 
8
- def body
8
+ def serialize
9
9
  u32_b(Constructors::UPDATES_GET_STATE)
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module MTProto
5
5
  class GetUsers
6
6
  include Binary
7
7
 
8
- def body
8
+ def serialize
9
9
  u32_b(Constructors::USERS_GET_USERS) +
10
10
  u32_b(Constructors::VECTOR) +
11
11
  u32_b(1) +
@@ -14,7 +14,7 @@ module MTProto
14
14
  @dc_options = dc_options
15
15
  end
16
16
 
17
- def self.parse(data)
17
+ def self.deserialize(data)
18
18
  constructor = data[0, 4].unpack1('L<')
19
19
  unless [Constructors::CONFIG, Constructors::GZIP_PACKED].include?(constructor)
20
20
  raise UnexpectedConstructorError, constructor
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ImportBotAuthorization
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x67a3ff2c
9
+
10
+ def initialize(api_id:, api_hash:, bot_auth_token:)
11
+ @api_id = api_id
12
+ @api_hash = api_hash
13
+ @bot_auth_token = bot_auth_token
14
+ end
15
+
16
+ def serialize
17
+ result = u32_b(CONSTRUCTOR)
18
+ result += u32_b(0) # flags (int, unused)
19
+ result += u32_b(@api_id)
20
+ result += serialize_tl_string(@api_hash)
21
+ result += serialize_tl_string(@bot_auth_token)
22
+ result
23
+ end
24
+
25
+ private
26
+
27
+ def serialize_tl_string(str)
28
+ bytes = str.to_s.b.bytes
29
+ length = bytes.length
30
+
31
+ if length <= 253
32
+ [length] + bytes + padding(length + 1)
33
+ else
34
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
35
+ end
36
+ end
37
+
38
+ def padding(current_length)
39
+ pad_length = (4 - (current_length % 4)) % 4
40
+ [0] * pad_length
41
+ end
42
+ end
43
+ end
44
+ end
@@ -11,7 +11,7 @@ module MTProto
11
11
  @token = token
12
12
  end
13
13
 
14
- def body
14
+ def serialize
15
15
  u32_b(Constructors::AUTH_IMPORT_LOGIN_TOKEN) + serialize_tl_bytes(@token)
16
16
  end
17
17
 
@@ -20,7 +20,7 @@ module MTProto
20
20
  @query = query
21
21
  end
22
22
 
23
- def body
23
+ def serialize
24
24
  flags = 0
25
25
 
26
26
  result = u32_b(Constructors::INIT_CONNECTION)
@@ -32,7 +32,7 @@ module MTProto
32
32
  result += serialize_tl_string(@system_lang_code)
33
33
  result += serialize_tl_string(@lang_pack)
34
34
  result += serialize_tl_string(@lang_code)
35
- result + @query.body
35
+ result + @query.serialize
36
36
  end
37
37
 
38
38
  private
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class InviteToChannel
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xc9e33d54
9
+ INPUT_USER = 0xf21158c6
10
+
11
+ def initialize(channel:, users:)
12
+ @channel = channel
13
+ @users = users
14
+ end
15
+
16
+ def serialize
17
+ result = u32_b(CONSTRUCTOR)
18
+ result += serialize_input_channel
19
+ result += u32_b(Constructors::VECTOR) + u32_b(@users.length)
20
+ @users.each do |u|
21
+ result += u32_b(INPUT_USER) + u64_b(u[:id]) + u64_b(u[:access_hash] || 0)
22
+ end
23
+ result
24
+ end
25
+
26
+ private
27
+
28
+ def serialize_input_channel
29
+ u32_b(Constructors::INPUT_CHANNEL) +
30
+ u64_b(@channel[:id]) +
31
+ u64_b(@channel[:access_hash])
32
+ end
33
+ end
34
+ end
35
+ end
@@ -12,8 +12,8 @@ module MTProto
12
12
  @query = query
13
13
  end
14
14
 
15
- def body
16
- u32_b(Constructors::INVOKE_WITH_LAYER) + u32_b(@layer) + @query.body
15
+ def serialize
16
+ u32_b(Constructors::INVOKE_WITH_LAYER) + u32_b(@layer) + @query.serialize
17
17
  end
18
18
  end
19
19
  end
@@ -27,7 +27,7 @@ module MTProto
27
27
  @type == :success
28
28
  end
29
29
 
30
- def self.parse(data)
30
+ def self.deserialize(data)
31
31
  constructor = data[0, 4].unpack1('L<')
32
32
 
33
33
  case constructor
@@ -36,7 +36,7 @@ module MTProto
36
36
  when Constructors::AUTH_LOGIN_TOKEN_MIGRATE_TO
37
37
  parse_migrate_to(data)
38
38
  when Constructors::AUTH_LOGIN_TOKEN_SUCCESS
39
- new(type: :success, authorization: Authorization.parse(data[4..]))
39
+ new(type: :success, authorization: Authorization.deserialize(data[4..]))
40
40
  else
41
41
  raise UnexpectedConstructorError, constructor
42
42
  end
@@ -34,7 +34,7 @@ module MTProto
34
34
  )
35
35
  end
36
36
 
37
- def self.parse(data)
37
+ def self.deserialize(data)
38
38
  constructor = data[0, 4].unpack1('L<')
39
39
  raise UnexpectedConstructorError, constructor unless VALID_CONSTRUCTORS.include?(constructor)
40
40
 
@@ -18,7 +18,7 @@ module MTProto
18
18
  @expires_in = expires_in
19
19
  end
20
20
 
21
- def body
21
+ def serialize
22
22
  constructor = @expires_in ? Constructors::PQ_INNER_DATA_TEMP_DC : Constructors::PQ_INNER_DATA_DC
23
23
 
24
24
  result = u32_b(constructor)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../constructor_names'
4
+
5
+ module MTProto
6
+ module TL
7
+ # Pseudo TL object used as response_class when we want rpc.call to hand
8
+ # back the raw bytes of whatever the server returns, without imposing a
9
+ # specific TL schema. Useful when a method can legally return one of
10
+ # several constructor variants (e.g. messages.sendMessage may return
11
+ # updateShortSentMessage, updates, or updatesCombined depending on
12
+ # context) and we just want to capture the wire bytes for fixtures.
13
+ class RawResponse
14
+ attr_reader :raw_bytes, :constructor_id, :constructor_name
15
+
16
+ def initialize(raw_bytes:, constructor_id:, constructor_name:)
17
+ @raw_bytes = raw_bytes
18
+ @constructor_id = constructor_id
19
+ @constructor_name = constructor_name
20
+ end
21
+
22
+ def self.deserialize(bytes)
23
+ constructor = bytes[0, 4].unpack1('L<')
24
+ name = ConstructorNames::NAMES[constructor] || "0x#{constructor.to_s(16)}"
25
+ new(raw_bytes: bytes, constructor_id: constructor, constructor_name: name)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -19,7 +19,7 @@ module MTProto
19
19
  @encrypted_data = encrypted_data
20
20
  end
21
21
 
22
- def body
22
+ def serialize
23
23
  result = u32_b(Constructors::REQ_DH_PARAMS)
24
24
  result += @nonce.bytes
25
25
  result += @server_nonce.bytes
@@ -13,7 +13,7 @@ module MTProto
13
13
  @nonce = nonce
14
14
  end
15
15
 
16
- def body
16
+ def serialize
17
17
  u32_b(Constructors::REQ_PQ_MULTI) + @nonce.bytes
18
18
  end
19
19
  end
@@ -7,7 +7,7 @@ module MTProto
7
7
 
8
8
  attr_reader :nonce, :server_nonce, :pq, :fingerprints
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
  raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::RES_PQ
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ResolveUsername
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x725afbbc
9
+
10
+ def initialize(username:)
11
+ @username = username
12
+ end
13
+
14
+ def serialize
15
+ result = u32_b(CONSTRUCTOR)
16
+ result += u32_b(0) # flags
17
+ result += serialize_tl_string(@username)
18
+ result
19
+ end
20
+
21
+ private
22
+
23
+ def serialize_tl_string(str)
24
+ bytes = str.encode('UTF-8').bytes
25
+ length = bytes.length
26
+
27
+ if length <= 253
28
+ [length] + bytes + padding(length + 1)
29
+ else
30
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
31
+ end
32
+ end
33
+
34
+ def padding(current_length)
35
+ pad_length = (4 - (current_length % 4)) % 4
36
+ [0] * pad_length
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SaveFilePart
6
+ include Binary
7
+
8
+ def initialize(file_id:, file_part:, bytes:)
9
+ @file_id = file_id
10
+ @file_part = file_part
11
+ @bytes = bytes
12
+ end
13
+
14
+ def serialize
15
+ u32_b(Constructors::UPLOAD_SAVE_FILE_PART) +
16
+ u64_b(@file_id) +
17
+ u32_b(@file_part) +
18
+ serialize_tl_bytes(@bytes)
19
+ end
20
+
21
+ private
22
+
23
+ def serialize_tl_bytes(bytes)
24
+ bytes = bytes.bytes if bytes.is_a?(String)
25
+ length = bytes.length
26
+
27
+ if length <= 253
28
+ [length] + bytes + padding(length + 1)
29
+ else
30
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
31
+ end
32
+ end
33
+
34
+ def padding(current_length)
35
+ pad_length = (4 - (current_length % 4)) % 4
36
+ [0] * pad_length
37
+ end
38
+ end
39
+ end
40
+ end
@@ -13,7 +13,7 @@ module MTProto
13
13
  @api_hash = api_hash
14
14
  end
15
15
 
16
- def body
16
+ def serialize
17
17
  u32_b(Constructors::AUTH_SEND_CODE) +
18
18
  serialize_tl_string(@phone_number) +
19
19
  u32_b(@api_id) +
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module MTProto
6
+ module TL
7
+ class SendMedia
8
+ include Binary
9
+
10
+ def initialize(peer:, media:, message: '', random_id: nil)
11
+ @peer = peer
12
+ @media = media
13
+ @message = message
14
+ @random_id = random_id || SecureRandom.random_number(2**63)
15
+ end
16
+
17
+ def serialize
18
+ result = u32_b(Constructors::MESSAGES_SEND_MEDIA)
19
+ result += u32_b(0) # flags
20
+ result += serialize_input_peer
21
+ result += serialize_input_media
22
+ result += serialize_tl_string(@message)
23
+ result += u64_b(@random_id)
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def serialize_input_peer
30
+ case @peer[:type]
31
+ when :self
32
+ u32_b(Constructors::INPUT_PEER_SELF)
33
+ when :user
34
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
35
+ when :chat
36
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
37
+ when :channel
38
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
39
+ else
40
+ raise "Unknown peer type: #{@peer[:type]}"
41
+ end
42
+ end
43
+
44
+ def serialize_input_media
45
+ case @media[:type]
46
+ when :uploaded_photo
47
+ serialize_input_media_uploaded_photo(@media[:file])
48
+ else
49
+ raise "Unknown media type: #{@media[:type]}"
50
+ end
51
+ end
52
+
53
+ def serialize_input_media_uploaded_photo(file)
54
+ result = u32_b(Constructors::INPUT_MEDIA_UPLOADED_PHOTO)
55
+ result += u32_b(0) # flags
56
+ result += serialize_input_file(file)
57
+ result
58
+ end
59
+
60
+ def serialize_input_file(file)
61
+ u32_b(Constructors::INPUT_FILE) +
62
+ u64_b(file[:id]) +
63
+ u32_b(file[:parts]) +
64
+ serialize_tl_string(file[:name].to_s) +
65
+ serialize_tl_string(file[:md5_checksum].to_s)
66
+ end
67
+
68
+ def serialize_tl_string(str)
69
+ bytes = str.encode('UTF-8').bytes
70
+ length = bytes.length
71
+
72
+ if length <= 253
73
+ [length] + bytes + padding(length + 1)
74
+ else
75
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
76
+ end
77
+ end
78
+
79
+ def padding(current_length)
80
+ pad_length = (4 - (current_length % 4)) % 4
81
+ [0] * pad_length
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module MTProto
6
+ module TL
7
+ class SendMessage
8
+ include Binary
9
+
10
+ INPUT_REPLY_TO_MESSAGE = 0x869fbe10
11
+
12
+ def initialize(peer:, message:, random_id: nil, reply_to: nil)
13
+ @peer = peer
14
+ @message = message
15
+ @random_id = random_id || SecureRandom.random_number(2**63)
16
+ @reply_to = reply_to
17
+ end
18
+
19
+ def serialize
20
+ flags = 0
21
+ flags |= (1 << 0) if @reply_to
22
+
23
+ result = u32_b(Constructors::MESSAGES_SEND_MESSAGE)
24
+ result += u32_b(flags)
25
+ result += serialize_input_peer
26
+ result += serialize_reply_to if @reply_to
27
+ result += serialize_tl_string(@message)
28
+ result += u64_b(@random_id)
29
+ result
30
+ end
31
+
32
+ private
33
+
34
+ def serialize_reply_to
35
+ u32_b(INPUT_REPLY_TO_MESSAGE) + u32_b(0) + u32_b(@reply_to)
36
+ end
37
+
38
+ def serialize_input_peer
39
+ case @peer[:type]
40
+ when :self
41
+ u32_b(Constructors::INPUT_PEER_SELF)
42
+ when :user
43
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
44
+ when :chat
45
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
46
+ when :channel
47
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
48
+ else
49
+ raise "Unknown peer type: #{@peer[:type]}"
50
+ end
51
+ end
52
+
53
+ def serialize_tl_string(str)
54
+ bytes = str.encode('UTF-8').bytes
55
+ length = bytes.length
56
+
57
+ if length <= 253
58
+ [length] + bytes + padding(length + 1)
59
+ else
60
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
61
+ end
62
+ end
63
+
64
+ def padding(current_length)
65
+ pad_length = (4 - (current_length % 4)) % 4
66
+ [0] * pad_length
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SendReaction
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0xd30d78d4
9
+ REACTION_EMOJI = 0x1b2286b8
10
+
11
+ # emoticons: array of emoji strings (a single-element array is the common case);
12
+ # an empty array clears the reaction.
13
+ def initialize(peer:, msg_id:, emoticons: [])
14
+ @peer = peer
15
+ @msg_id = msg_id
16
+ @emoticons = emoticons
17
+ end
18
+
19
+ def serialize
20
+ flags = 0
21
+ flags |= (1 << 0) unless @emoticons.empty? # reaction field present
22
+
23
+ result = u32_b(CONSTRUCTOR)
24
+ result += u32_b(flags)
25
+ result += serialize_input_peer
26
+ result += u32_b(@msg_id)
27
+ unless @emoticons.empty?
28
+ result += u32_b(Constructors::VECTOR) + u32_b(@emoticons.length)
29
+ @emoticons.each { |e| result += u32_b(REACTION_EMOJI) + serialize_tl_string(e) }
30
+ end
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_string(str)
52
+ bytes = str.to_s.b.bytes
53
+ length = bytes.length
54
+
55
+ if length <= 253
56
+ [length] + bytes + padding(length + 1)
57
+ else
58
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
59
+ end
60
+ end
61
+
62
+ def padding(current_length)
63
+ pad_length = (4 - (current_length % 4)) % 4
64
+ [0] * pad_length
65
+ end
66
+ end
67
+ end
68
+ end