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,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
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ # messages.getMessages#63c66506 id:Vector<InputMessage> — fetch messages by id
6
+ # from a basic group or private chat (the non-channel counterpart of
7
+ # channels.getMessages). Returns messages.messages / messagesSlice.
8
+ class MessagesGetMessages
9
+ include Binary
10
+
11
+ CONSTRUCTOR = 0x63c66506
12
+ INPUT_MESSAGE_ID = 0xa676a322
13
+
14
+ def initialize(ids:)
15
+ @ids = ids
16
+ end
17
+
18
+ def serialize
19
+ result = u32_b(CONSTRUCTOR)
20
+ result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
21
+ @ids.each { |id| result += u32_b(INPUT_MESSAGE_ID) + u32_b(id) }
22
+ result
23
+ end
24
+ end
25
+ end
26
+ end
@@ -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
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ReplyKeyboardMarkup
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x85dd99d1
9
+ KEYBOARD_BUTTON_ROW = 0x77608b83
10
+
11
+ # buttons: a single row of button objects (each responds to #serialize)
12
+ def initialize(buttons:, resize: true, single_use: false)
13
+ @buttons = buttons
14
+ @resize = resize
15
+ @single_use = single_use
16
+ end
17
+
18
+ def serialize
19
+ flags = 0
20
+ flags |= (1 << 0) if @resize
21
+ flags |= (1 << 1) if @single_use
22
+
23
+ result = u32_b(CONSTRUCTOR)
24
+ result += u32_b(flags)
25
+ result += u32_b(Constructors::VECTOR)
26
+ result += u32_b(1)
27
+ result += u32_b(KEYBOARD_BUTTON_ROW)
28
+ result += u32_b(Constructors::VECTOR)
29
+ result += u32_b(@buttons.size)
30
+ @buttons.each { |button| result += button.serialize }
31
+ result
32
+ end
33
+ end
34
+ end
35
+ 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
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class RequestPeerTypeCreateBot
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x3e81e078
9
+
10
+ def initialize(bot_managed: true, suggested_name: nil, suggested_username: nil)
11
+ @bot_managed = bot_managed
12
+ @suggested_name = suggested_name
13
+ @suggested_username = suggested_username
14
+ end
15
+
16
+ def serialize
17
+ flags = 0
18
+ flags |= (1 << 0) if @bot_managed
19
+ flags |= (1 << 1) if @suggested_name
20
+ flags |= (1 << 2) if @suggested_username
21
+
22
+ result = u32_b(CONSTRUCTOR)
23
+ result += u32_b(flags)
24
+ result += serialize_tl_string(@suggested_name) if @suggested_name
25
+ result += serialize_tl_string(@suggested_username) if @suggested_username
26
+ result
27
+ end
28
+
29
+ private
30
+
31
+ def serialize_tl_string(str)
32
+ bytes = str.encode('UTF-8').bytes
33
+ length = bytes.length
34
+ if length <= 253
35
+ [length] + bytes + padding(length + 1)
36
+ else
37
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
38
+ end
39
+ end
40
+
41
+ def padding(current_length)
42
+ pad_length = (4 - (current_length % 4)) % 4
43
+ [0] * pad_length
44
+ end
45
+ end
46
+ end
47
+ 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
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SendBotRequestedPeer
6
+ include Binary
7
+
8
+ CONSTRUCTOR = 0x6c5cf2a7
9
+
10
+ # peer / requested_peers entries: { type:, id:, access_hash: } or { type: :self }
11
+ def initialize(peer:, button_id:, msg_id: nil, requested_peers: [])
12
+ @peer = peer
13
+ @button_id = button_id
14
+ @msg_id = msg_id
15
+ @requested_peers = requested_peers
16
+ end
17
+
18
+ def serialize
19
+ flags = 0
20
+ flags |= (1 << 0) if @msg_id
21
+
22
+ result = u32_b(CONSTRUCTOR)
23
+ result += u32_b(flags)
24
+ result += serialize_input_peer(@peer)
25
+ result += u32_b(@msg_id) if @msg_id
26
+ result += u32_b(@button_id)
27
+ result += u32_b(Constructors::VECTOR)
28
+ result += u32_b(@requested_peers.size)
29
+ @requested_peers.each { |peer| result += serialize_input_peer(peer) }
30
+ result
31
+ end
32
+
33
+ private
34
+
35
+ def serialize_input_peer(peer)
36
+ case peer[:type]
37
+ when :self
38
+ u32_b(Constructors::INPUT_PEER_SELF)
39
+ when :user
40
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
41
+ when :chat
42
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(peer[:id])
43
+ when :channel
44
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
45
+ else
46
+ raise "Unknown peer type: #{peer[:type]}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ 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,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module MTProto
6
+ module TL
7
+ class SendMedia
8
+ include Binary
9
+
10
+ INPUT_REPLY_TO_MESSAGE = 0x869fbe10
11
+
12
+ def initialize(peer:, media:, message: '', random_id: nil, reply_to: nil)
13
+ @peer = peer
14
+ @media = media
15
+ @message = message
16
+ @random_id = random_id || SecureRandom.random_number(2**63)
17
+ @reply_to = reply_to
18
+ end
19
+
20
+ def serialize
21
+ flags = 0
22
+ flags |= (1 << 0) if @reply_to
23
+
24
+ result = u32_b(Constructors::MESSAGES_SEND_MEDIA)
25
+ result += u32_b(flags)
26
+ result += serialize_input_peer
27
+ result += serialize_reply_to if @reply_to
28
+ result += serialize_input_media
29
+ result += serialize_tl_string(@message)
30
+ result += u64_b(@random_id)
31
+ result
32
+ end
33
+
34
+ private
35
+
36
+ def serialize_reply_to
37
+ u32_b(INPUT_REPLY_TO_MESSAGE) + u32_b(0) + u32_b(@reply_to)
38
+ end
39
+
40
+ def serialize_input_peer
41
+ case @peer[:type]
42
+ when :self
43
+ u32_b(Constructors::INPUT_PEER_SELF)
44
+ when :user
45
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
46
+ when :chat
47
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
48
+ when :channel
49
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
50
+ else
51
+ raise "Unknown peer type: #{@peer[:type]}"
52
+ end
53
+ end
54
+
55
+ def serialize_input_media
56
+ case @media[:type]
57
+ when :uploaded_photo
58
+ serialize_input_media_uploaded_photo(@media[:file])
59
+ else
60
+ raise "Unknown media type: #{@media[:type]}"
61
+ end
62
+ end
63
+
64
+ def serialize_input_media_uploaded_photo(file)
65
+ result = u32_b(Constructors::INPUT_MEDIA_UPLOADED_PHOTO)
66
+ result += u32_b(0) # flags
67
+ result += serialize_input_file(file)
68
+ result
69
+ end
70
+
71
+ def serialize_input_file(file)
72
+ u32_b(Constructors::INPUT_FILE) +
73
+ u64_b(file[:id]) +
74
+ u32_b(file[:parts]) +
75
+ serialize_tl_string(file[:name].to_s) +
76
+ serialize_tl_string(file[:md5_checksum].to_s)
77
+ end
78
+
79
+ def serialize_tl_string(str)
80
+ bytes = str.encode('UTF-8').bytes
81
+ length = bytes.length
82
+
83
+ if length <= 253
84
+ [length] + bytes + padding(length + 1)
85
+ else
86
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
87
+ end
88
+ end
89
+
90
+ def padding(current_length)
91
+ pad_length = (4 - (current_length % 4)) % 4
92
+ [0] * pad_length
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,73 @@
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, reply_markup: nil)
13
+ @peer = peer
14
+ @message = message
15
+ @random_id = random_id || SecureRandom.random_number(2**63)
16
+ @reply_to = reply_to
17
+ @reply_markup = reply_markup
18
+ end
19
+
20
+ def serialize
21
+ flags = 0
22
+ flags |= (1 << 0) if @reply_to
23
+ flags |= (1 << 2) if @reply_markup
24
+
25
+ result = u32_b(Constructors::MESSAGES_SEND_MESSAGE)
26
+ result += u32_b(flags)
27
+ result += serialize_input_peer
28
+ result += serialize_reply_to if @reply_to
29
+ result += serialize_tl_string(@message)
30
+ result += u64_b(@random_id)
31
+ result += @reply_markup.serialize if @reply_markup
32
+ result
33
+ end
34
+
35
+ private
36
+
37
+ def serialize_reply_to
38
+ u32_b(INPUT_REPLY_TO_MESSAGE) + u32_b(0) + u32_b(@reply_to)
39
+ end
40
+
41
+ def serialize_input_peer
42
+ case @peer[:type]
43
+ when :self
44
+ u32_b(Constructors::INPUT_PEER_SELF)
45
+ when :user
46
+ u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
47
+ when :chat
48
+ u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
49
+ when :channel
50
+ u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
51
+ else
52
+ raise "Unknown peer type: #{@peer[:type]}"
53
+ end
54
+ end
55
+
56
+ def serialize_tl_string(str)
57
+ bytes = str.encode('UTF-8').bytes
58
+ length = bytes.length
59
+
60
+ if length <= 253
61
+ [length] + bytes + padding(length + 1)
62
+ else
63
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
64
+ end
65
+ end
66
+
67
+ def padding(current_length)
68
+ pad_length = (4 - (current_length % 4)) % 4
69
+ [0] * pad_length
70
+ end
71
+ end
72
+ end
73
+ 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
@@ -13,7 +13,7 @@ module MTProto
13
13
  @timeout = timeout
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::AUTH_SENT_CODE
19
19
 
@@ -5,7 +5,7 @@ module MTProto
5
5
  class ServerDHInnerData
6
6
  attr_reader :nonce, :server_nonce, :g, :dh_prime, :g_a, :server_time
7
7
 
8
- def self.parse(bytes)
8
+ def self.deserialize(bytes)
9
9
  constructor = bytes[0, 4].unpack1('L<')
10
10
  unless constructor == Constructors::SERVER_DH_INNER_DATA
11
11
  raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
@@ -7,7 +7,7 @@ module MTProto
7
7
 
8
8
  attr_reader :nonce, :server_nonce, :encrypted_answer
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
  unless constructor == Constructors::SERVER_DH_PARAMS_OK