mtproto 0.0.8 → 0.0.10

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/data/tl-schema.json +42686 -0
  3. data/ext/aes_ige/extconf.rb +3 -9
  4. data/ext/factorization/extconf.rb +2 -0
  5. data/lib/mtproto/auth_key_generator.rb +68 -105
  6. data/lib/mtproto/binary.rb +21 -0
  7. data/lib/mtproto/client/api/check_password.rb +41 -0
  8. data/lib/mtproto/client/api/export_login_token.rb +27 -0
  9. data/lib/mtproto/client/api/get_dialogs.rb +21 -0
  10. data/lib/mtproto/client/api/get_history.rb +20 -0
  11. data/lib/mtproto/client/api/get_updates_difference.rb +21 -0
  12. data/lib/mtproto/client/api/get_updates_state.rb +14 -0
  13. data/lib/mtproto/client/api/get_users.rb +14 -0
  14. data/lib/mtproto/client/api/import_login_token.rb +23 -0
  15. data/lib/mtproto/client/api/send_code.rb +21 -0
  16. data/lib/mtproto/client/api/sign_in.rb +27 -0
  17. data/lib/mtproto/client/api.rb +36 -0
  18. data/lib/mtproto/client/rpc/response.rb +63 -0
  19. data/lib/mtproto/client/rpc.rb +60 -127
  20. data/lib/mtproto/client.rb +143 -32
  21. data/lib/mtproto/crypto/dh_key_exchange.rb +1 -2
  22. data/lib/mtproto/crypto/dh_validator.rb +17 -19
  23. data/lib/mtproto/crypto/factorization.rb +1 -1
  24. data/lib/mtproto/crypto/rsa_key.rb +2 -2
  25. data/lib/mtproto/crypto/srp.rb +117 -0
  26. data/lib/mtproto/delegate_methods.rb +11 -0
  27. data/lib/mtproto/errors.rb +8 -0
  28. data/lib/mtproto/message/message.rb +85 -0
  29. data/lib/mtproto/session.rb +1 -1
  30. data/lib/mtproto/tl/constructor_names.rb +2271 -0
  31. data/lib/mtproto/tl/constructors.rb +99 -0
  32. data/lib/mtproto/tl/object.rb +25 -0
  33. data/lib/mtproto/tl/objects/account_password.rb +69 -0
  34. data/lib/mtproto/tl/objects/authorization.rb +70 -0
  35. data/lib/mtproto/tl/objects/check_password.rb +43 -0
  36. data/lib/mtproto/tl/objects/client_dh_inner_data.rb +45 -0
  37. data/lib/mtproto/tl/objects/dh_gen_response.rb +46 -0
  38. data/lib/mtproto/tl/objects/dialogs.rb +453 -0
  39. data/lib/mtproto/tl/objects/export_login_token.rb +48 -0
  40. data/lib/mtproto/tl/objects/get_config.rb +13 -0
  41. data/lib/mtproto/tl/objects/get_dialogs.rb +51 -0
  42. data/lib/mtproto/tl/objects/get_difference.rb +34 -0
  43. data/lib/mtproto/tl/objects/get_history.rb +49 -0
  44. data/lib/mtproto/tl/objects/get_password.rb +13 -0
  45. data/lib/mtproto/tl/objects/get_state.rb +13 -0
  46. data/lib/mtproto/tl/objects/get_users.rb +16 -0
  47. data/lib/mtproto/{type → tl/objects}/gzip_packed.rb +6 -6
  48. data/lib/mtproto/tl/objects/help_config.rb +76 -0
  49. data/lib/mtproto/tl/objects/import_login_token.rb +37 -0
  50. data/lib/mtproto/tl/objects/init_connection.rb +57 -0
  51. data/lib/mtproto/tl/objects/invoke_with_layer.rb +20 -0
  52. data/lib/mtproto/tl/objects/login_token.rb +78 -0
  53. data/lib/mtproto/{type → tl/objects}/message.rb +3 -3
  54. data/lib/mtproto/tl/objects/messages.rb +162 -0
  55. data/lib/mtproto/{type → tl/objects}/msg_container.rb +1 -3
  56. data/lib/mtproto/{type → tl/objects}/new_session_created.rb +1 -3
  57. data/lib/mtproto/tl/objects/pq_inner_data.rb +66 -0
  58. data/lib/mtproto/tl/objects/req_dh_params.rb +63 -0
  59. data/lib/mtproto/tl/objects/req_pq_multi.rb +21 -0
  60. data/lib/mtproto/tl/objects/res_pq.rb +73 -0
  61. data/lib/mtproto/{type → tl/objects}/rpc_error.rb +1 -4
  62. data/lib/mtproto/tl/objects/send_code.rb +47 -0
  63. data/lib/mtproto/tl/objects/sent_code.rb +79 -0
  64. data/lib/mtproto/tl/objects/server_dh_inner_data.rb +74 -0
  65. data/lib/mtproto/tl/objects/server_dh_params.rb +53 -0
  66. data/lib/mtproto/tl/objects/set_client_dh_params.rb +46 -0
  67. data/lib/mtproto/tl/objects/sign_in.rb +45 -0
  68. data/lib/mtproto/tl/objects/update.rb +77 -0
  69. data/lib/mtproto/tl/objects/update_short.rb +20 -0
  70. data/lib/mtproto/tl/objects/update_short_message.rb +65 -0
  71. data/lib/mtproto/tl/objects/updates_difference.rb +152 -0
  72. data/lib/mtproto/tl/objects/updates_state.rb +35 -0
  73. data/lib/mtproto/tl/objects/users.rb +83 -0
  74. data/lib/mtproto/tl/schema.rb +102 -0
  75. data/lib/mtproto/transport/abridged_packet_codec.rb +35 -12
  76. data/lib/mtproto/transport/connection.rb +23 -0
  77. data/lib/mtproto/transport/errors.rb +11 -0
  78. data/lib/mtproto/transport/packet.rb +19 -0
  79. data/lib/mtproto/transport/tcp_connection.rb +57 -46
  80. data/lib/mtproto/updates_poller.rb +37 -33
  81. data/lib/mtproto/version.rb +1 -1
  82. data/lib/mtproto.rb +17 -27
  83. data/scripts/generate_constructors.rb +65 -0
  84. metadata +76 -61
  85. data/lib/mtproto/async/middleware/base.rb +0 -17
  86. data/lib/mtproto/async/middleware/flood_wait.rb +0 -42
  87. data/lib/mtproto/async/request.rb +0 -18
  88. data/lib/mtproto/async/request_queue.rb +0 -63
  89. data/lib/mtproto/async_client.rb +0 -201
  90. data/lib/mtproto/rpc/get_config.rb +0 -34
  91. data/lib/mtproto/rpc/get_contacts.rb +0 -29
  92. data/lib/mtproto/rpc/get_updates_difference.rb +0 -51
  93. data/lib/mtproto/rpc/get_updates_state.rb +0 -29
  94. data/lib/mtproto/rpc/get_users.rb +0 -29
  95. data/lib/mtproto/rpc/ping.rb +0 -33
  96. data/lib/mtproto/rpc/send_code.rb +0 -41
  97. data/lib/mtproto/rpc/send_message.rb +0 -47
  98. data/lib/mtproto/rpc/sign_in.rb +0 -48
  99. data/lib/mtproto/type/auth_key/dh_gen_response.rb +0 -37
  100. data/lib/mtproto/type/auth_key/req_dh_params.rb +0 -31
  101. data/lib/mtproto/type/auth_key/req_pq_multi.rb +0 -18
  102. data/lib/mtproto/type/auth_key/res_pq.rb +0 -62
  103. data/lib/mtproto/type/auth_key/server_dh_params.rb +0 -43
  104. data/lib/mtproto/type/auth_key/set_client_dh_params.rb +0 -25
  105. data/lib/mtproto/type/bad_msg_notification.rb +0 -46
  106. data/lib/mtproto/type/client_dh_inner_data.rb +0 -29
  107. data/lib/mtproto/type/code_settings.rb +0 -25
  108. data/lib/mtproto/type/config.rb +0 -124
  109. data/lib/mtproto/type/pq_inner_data.rb +0 -41
  110. data/lib/mtproto/type/rpc/auth/authorization.rb +0 -107
  111. data/lib/mtproto/type/rpc/auth/send_code.rb +0 -28
  112. data/lib/mtproto/type/rpc/auth/sent_code.rb +0 -36
  113. data/lib/mtproto/type/rpc/auth/sign_in.rb +0 -32
  114. data/lib/mtproto/type/rpc/contacts/contacts.rb +0 -155
  115. data/lib/mtproto/type/rpc/contacts/get_contacts.rb +0 -18
  116. data/lib/mtproto/type/rpc/help/config.rb +0 -35
  117. data/lib/mtproto/type/rpc/help/get_config.rb +0 -17
  118. data/lib/mtproto/type/rpc/init_connection.rb +0 -28
  119. data/lib/mtproto/type/rpc/invoke_with_layer.rb +0 -19
  120. data/lib/mtproto/type/rpc/messages/send_message.rb +0 -43
  121. data/lib/mtproto/type/rpc/messages/updates.rb +0 -87
  122. data/lib/mtproto/type/rpc/ping.rb +0 -18
  123. data/lib/mtproto/type/rpc/pong.rb +0 -46
  124. data/lib/mtproto/type/rpc/updates/difference.rb +0 -332
  125. data/lib/mtproto/type/rpc/updates/get_difference.rb +0 -42
  126. data/lib/mtproto/type/rpc/updates/get_state.rb +0 -17
  127. data/lib/mtproto/type/rpc/updates/state.rb +0 -59
  128. data/lib/mtproto/type/rpc/users/get_users.rb +0 -25
  129. data/lib/mtproto/type/rpc/users/users.rb +0 -99
  130. data/lib/mtproto/type/sent_code.rb +0 -128
  131. data/lib/mtproto/type/serializer.rb +0 -55
  132. data/lib/mtproto/type/server_dh_inner_data.rb +0 -85
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ResPq
6
+ extend Binary
7
+
8
+ attr_reader :nonce, :server_nonce, :pq, :fingerprints
9
+
10
+ def self.parse(message)
11
+ data = message.body
12
+ constructor = b_u32(data[0, 4])
13
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::RES_PQ
14
+
15
+ offset = 4
16
+
17
+ nonce = data[offset, 16].pack('C*')
18
+ offset += 16
19
+
20
+ server_nonce = data[offset, 16].pack('C*')
21
+ offset += 16
22
+
23
+ pq_length_byte = data[offset]
24
+ offset += 1
25
+
26
+ pq_length = if pq_length_byte == 254
27
+ b_u32(data[offset, 3] + [0]) & 0xffffff
28
+ offset += 3
29
+ else
30
+ pq_length_byte
31
+ end
32
+
33
+ pq = data[offset, pq_length].pack('C*')
34
+ offset += pq_length
35
+ offset += padding_length(pq_length + 1)
36
+
37
+ vector_constructor = b_u32(data[offset, 4])
38
+ offset += 4
39
+ raise 'Expected vector constructor' unless vector_constructor == Constructors::VECTOR
40
+
41
+ fingerprints_count = b_u32(data[offset, 4])
42
+ offset += 4
43
+
44
+ fingerprints = []
45
+ fingerprints_count.times do
46
+ fingerprints << b_u64(data[offset, 8])
47
+ offset += 8
48
+ end
49
+
50
+ new(
51
+ nonce: nonce,
52
+ server_nonce: server_nonce,
53
+ pq: pq,
54
+ fingerprints: fingerprints
55
+ )
56
+ end
57
+
58
+ def self.padding_length(length)
59
+ (4 - (length % 4)) % 4
60
+ end
61
+ private_class_method :padding_length
62
+
63
+ private
64
+
65
+ def initialize(nonce:, server_nonce:, pq:, fingerprints:)
66
+ @nonce = nonce
67
+ @server_nonce = server_nonce
68
+ @pq = pq
69
+ @fingerprints = fingerprints
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MTProto
4
- module Type
4
+ module TL
5
5
  class RpcError
6
- CONSTRUCTOR = 0x2144ca19
7
-
8
6
  attr_reader :error_code, :error_message
9
7
 
10
8
  def self.deserialize(data)
@@ -16,7 +14,6 @@ module MTProto
16
14
  offset += 1
17
15
 
18
16
  error_message = data[offset, message_length]
19
- offset += message_length
20
17
 
21
18
  new(error_code: error_code, error_message: error_message)
22
19
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SendCode
6
+ include Binary
7
+
8
+ attr_reader :phone_number, :api_id, :api_hash
9
+
10
+ def initialize(phone_number:, api_id:, api_hash:)
11
+ @phone_number = phone_number
12
+ @api_id = api_id
13
+ @api_hash = api_hash
14
+ end
15
+
16
+ def body
17
+ u32_b(Constructors::AUTH_SEND_CODE) +
18
+ serialize_tl_string(@phone_number) +
19
+ u32_b(@api_id) +
20
+ serialize_tl_string(@api_hash) +
21
+ code_settings
22
+ end
23
+
24
+ private
25
+
26
+ def code_settings
27
+ u32_b(Constructors::CODE_SETTINGS) + u32_b(0)
28
+ end
29
+
30
+ def serialize_tl_string(str)
31
+ bytes = str.encode('UTF-8').bytes
32
+ length = bytes.length
33
+
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
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SentCode
6
+ attr_reader :flags, :type, :phone_code_hash, :next_type, :timeout
7
+
8
+ def initialize(flags:, type:, phone_code_hash:, next_type: nil, timeout: nil)
9
+ @flags = flags
10
+ @type = type
11
+ @phone_code_hash = phone_code_hash
12
+ @next_type = next_type
13
+ @timeout = timeout
14
+ end
15
+
16
+ def self.parse(data)
17
+ constructor = data[0, 4].unpack1('L<')
18
+ raise UnexpectedConstructorError, constructor unless constructor == Constructors::AUTH_SENT_CODE
19
+
20
+ offset = 4
21
+ flags = data[offset, 4].unpack1('L<')
22
+ offset += 4
23
+
24
+ type, offset = parse_sent_code_type(data, offset)
25
+
26
+ phone_code_hash, offset = read_tl_string(data, offset)
27
+
28
+ next_type = nil
29
+ next_type, offset = parse_code_type(data, offset) if flags.anybits?(1 << 1)
30
+
31
+ timeout = nil
32
+ timeout = data[offset, 4].unpack1('L<') if flags.anybits?(1 << 2)
33
+
34
+ new(flags: flags, type: type, phone_code_hash: phone_code_hash, next_type: next_type, timeout: timeout)
35
+ end
36
+
37
+ class << self
38
+ private
39
+
40
+ def parse_sent_code_type(data, offset)
41
+ constructor = data[offset, 4].unpack1('L<')
42
+ offset += 4
43
+
44
+ case constructor
45
+ when Constructors::AUTH_SENT_CODE_TYPE_APP,
46
+ Constructors::AUTH_SENT_CODE_TYPE_SMS,
47
+ Constructors::AUTH_SENT_CODE_TYPE_CALL
48
+ length = data[offset, 4].unpack1('L<')
49
+ offset += 4
50
+ [{ length: length }, offset]
51
+ else
52
+ [{}, offset]
53
+ end
54
+ end
55
+
56
+ def parse_code_type(data, offset)
57
+ constructor = data[offset, 4].unpack1('L<')
58
+ offset += 4
59
+ [constructor, offset]
60
+ end
61
+
62
+ def read_tl_string(data, offset)
63
+ first_byte = data.getbyte(offset)
64
+ if first_byte == 254
65
+ len = data.getbyte(offset + 1) |
66
+ (data.getbyte(offset + 2) << 8) |
67
+ (data.getbyte(offset + 3) << 16)
68
+ total = 4 + len
69
+ else
70
+ len = first_byte
71
+ total = 1 + len
72
+ end
73
+ padding = (4 - (total % 4)) % 4
74
+ [data[offset + (total - len), len], offset + total + padding]
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ServerDHInnerData
6
+ attr_reader :nonce, :server_nonce, :g, :dh_prime, :g_a, :server_time
7
+
8
+ def self.parse(bytes)
9
+ constructor = bytes[0, 4].unpack1('L<')
10
+ unless constructor == Constructors::SERVER_DH_INNER_DATA
11
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
12
+ end
13
+
14
+ offset = 4
15
+
16
+ nonce = bytes[offset, 16]
17
+ offset += 16
18
+
19
+ server_nonce = bytes[offset, 16]
20
+ offset += 16
21
+
22
+ g = bytes[offset, 4].unpack1('L<')
23
+ offset += 4
24
+
25
+ dh_prime, offset = read_tl_bytes(bytes, offset)
26
+ g_a, offset = read_tl_bytes(bytes, offset)
27
+
28
+ server_time = bytes[offset, 4].unpack1('L<')
29
+
30
+ new(
31
+ nonce: nonce,
32
+ server_nonce: server_nonce,
33
+ g: g,
34
+ dh_prime: dh_prime,
35
+ g_a: g_a,
36
+ server_time: server_time
37
+ )
38
+ end
39
+
40
+ def self.read_tl_bytes(bytes, offset)
41
+ length_byte = bytes[offset].ord
42
+ offset += 1
43
+
44
+ if length_byte == 254
45
+ length_bytes = bytes[offset, 3].bytes
46
+ length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
47
+ offset += 3
48
+ header_size = 4
49
+ else
50
+ length = length_byte
51
+ header_size = 1
52
+ end
53
+
54
+ value = bytes[offset, length]
55
+ offset += length
56
+ offset += (4 - ((header_size + length) % 4)) % 4
57
+
58
+ [value, offset]
59
+ end
60
+ private_class_method :read_tl_bytes
61
+
62
+ private
63
+
64
+ def initialize(nonce:, server_nonce:, g:, dh_prime:, g_a:, server_time:)
65
+ @nonce = nonce
66
+ @server_nonce = server_nonce
67
+ @g = g
68
+ @dh_prime = dh_prime
69
+ @g_a = g_a
70
+ @server_time = server_time
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ServerDHParams
6
+ extend Binary
7
+
8
+ attr_reader :nonce, :server_nonce, :encrypted_answer
9
+
10
+ def self.parse(message)
11
+ data = message.body
12
+ constructor = b_u32(data[0, 4])
13
+ unless constructor == Constructors::SERVER_DH_PARAMS_OK
14
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
15
+ end
16
+
17
+ offset = 4
18
+
19
+ nonce = data[offset, 16].pack('C*')
20
+ offset += 16
21
+
22
+ server_nonce = data[offset, 16].pack('C*')
23
+ offset += 16
24
+
25
+ length_byte = data[offset]
26
+ offset += 1
27
+
28
+ if length_byte == 254
29
+ encrypted_answer_length = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16)
30
+ offset += 3
31
+ else
32
+ encrypted_answer_length = length_byte
33
+ end
34
+
35
+ encrypted_answer = data[offset, encrypted_answer_length].pack('C*')
36
+
37
+ new(
38
+ nonce: nonce,
39
+ server_nonce: server_nonce,
40
+ encrypted_answer: encrypted_answer
41
+ )
42
+ end
43
+
44
+ private
45
+
46
+ def initialize(nonce:, server_nonce:, encrypted_answer:)
47
+ @nonce = nonce
48
+ @server_nonce = server_nonce
49
+ @encrypted_answer = encrypted_answer
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SetClientDHParams
6
+ include Binary
7
+
8
+ attr_reader :nonce, :server_nonce, :encrypted_data
9
+
10
+ def initialize(nonce:, server_nonce:, encrypted_data:)
11
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
12
+ raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
13
+
14
+ @nonce = nonce
15
+ @server_nonce = server_nonce
16
+ @encrypted_data = encrypted_data
17
+ end
18
+
19
+ def body
20
+ result = u32_b(Constructors::SET_CLIENT_DH_PARAMS)
21
+ result += @nonce.bytes
22
+ result += @server_nonce.bytes
23
+ result += serialize_tl_bytes(@encrypted_data.bytes)
24
+
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def serialize_tl_bytes(bytes)
31
+ length = bytes.length
32
+
33
+ if length <= 253
34
+ [length] + bytes + padding(length + 1)
35
+ else
36
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
37
+ end
38
+ end
39
+
40
+ def padding(current_length)
41
+ pad_length = (4 - (current_length % 4)) % 4
42
+ [0] * pad_length
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class SignIn
6
+ include Binary
7
+
8
+ attr_reader :phone_number, :phone_code_hash, :phone_code
9
+
10
+ def initialize(phone_number:, phone_code_hash:, phone_code:)
11
+ @phone_number = phone_number
12
+ @phone_code_hash = phone_code_hash
13
+ @phone_code = phone_code
14
+ end
15
+
16
+ def body
17
+ flags = 1 << 0
18
+
19
+ u32_b(Constructors::AUTH_SIGN_IN) +
20
+ u32_b(flags) +
21
+ serialize_tl_string(@phone_number) +
22
+ serialize_tl_string(@phone_code_hash) +
23
+ serialize_tl_string(@phone_code)
24
+ end
25
+
26
+ private
27
+
28
+ def serialize_tl_string(str)
29
+ bytes = str.encode('UTF-8').bytes
30
+ length = bytes.length
31
+
32
+ if length <= 253
33
+ [length] + bytes + padding(length + 1)
34
+ else
35
+ [254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
36
+ end
37
+ end
38
+
39
+ def padding(current_length)
40
+ pad_length = (4 - (current_length % 4)) % 4
41
+ [0] * pad_length
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class Update
6
+ attr_reader :type, :text
7
+
8
+ def initialize(type, text: nil)
9
+ @type = type
10
+ @text = text
11
+ end
12
+
13
+ def self.parse(bytes)
14
+ constructor_id = bytes[0, 4].unpack1('L<')
15
+ type = type_name(constructor_id)
16
+
17
+ case constructor_id
18
+ when Constructors::UPDATE_NEW_MESSAGE
19
+ text = extract_new_message_text(bytes)
20
+ new(type, text: text)
21
+ else
22
+ new(type)
23
+ end
24
+ end
25
+
26
+ class << self
27
+ private
28
+
29
+ def type_name(constructor_id)
30
+ name = ConstructorNames::NAMES.fetch(constructor_id)
31
+ name.sub(/^update/, '')
32
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
33
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
34
+ .downcase
35
+ end
36
+
37
+ def extract_new_message_text(bytes)
38
+ offset = 4 # skip updateNewMessage constructor
39
+
40
+ msg_constructor = bytes[offset, 4].unpack1('L<')
41
+ return nil unless msg_constructor == Constructors::MESSAGE
42
+
43
+ offset += 4
44
+ flags = bytes[offset, 4].unpack1('L<')
45
+ offset += 4
46
+ flags2 = bytes[offset, 4].unpack1('L<')
47
+ offset += 4
48
+ offset += 4 # id
49
+
50
+ offset += 12 if flags & (1 << 8) != 0 # from_id: Peer
51
+ offset += 12 # peer_id: Peer
52
+ offset += 12 if flags & (1 << 28) != 0 # saved_peer_id: Peer
53
+ return nil if flags & (1 << 2) != 0 # fwd_from: variable length
54
+
55
+ offset += 8 if flags & (1 << 11) != 0 # via_bot_id: long
56
+ offset += 8 if flags2 & 1 != 0 # via_business_bot_id: long
57
+ return nil if flags & (1 << 3) != 0 # reply_to: variable length
58
+
59
+ offset += 4 # date
60
+ read_tl_string(bytes, offset)
61
+ end
62
+
63
+ def read_tl_string(bytes, offset)
64
+ first_byte = bytes.getbyte(offset)
65
+ if first_byte == 254
66
+ len = bytes.getbyte(offset + 1) |
67
+ (bytes.getbyte(offset + 2) << 8) |
68
+ (bytes.getbyte(offset + 3) << 16)
69
+ bytes[offset + 4, len]
70
+ else
71
+ bytes[offset + 1, first_byte]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class UpdateShort
6
+ attr_reader :update, :date
7
+
8
+ def initialize(update, date)
9
+ @update = update
10
+ @date = date
11
+ end
12
+
13
+ def self.parse(bytes)
14
+ date = bytes[-4, 4].unpack1('l<')
15
+ update = Update.parse(bytes[4...-4])
16
+ new(update, date)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class UpdateShortMessage
6
+ attr_reader :flags, :id, :user_id, :text, :pts, :pts_count, :date
7
+
8
+ def initialize(flags:, id:, user_id:, text:, pts:, pts_count:, date:)
9
+ @flags = flags
10
+ @id = id
11
+ @user_id = user_id
12
+ @text = text
13
+ @pts = pts
14
+ @pts_count = pts_count
15
+ @date = date
16
+ end
17
+
18
+ def out?
19
+ flags & (1 << 1) != 0
20
+ end
21
+
22
+ def mentioned?
23
+ flags & (1 << 4) != 0
24
+ end
25
+
26
+ def self.parse(bytes)
27
+ offset = 4 # skip constructor
28
+ flags = bytes[offset, 4].unpack1('L<')
29
+ offset += 4
30
+ id = bytes[offset, 4].unpack1('l<')
31
+ offset += 4
32
+ user_id = bytes[offset, 8].unpack1('q<')
33
+ offset += 8
34
+ text, offset = read_tl_string(bytes, offset)
35
+ pts = bytes[offset, 4].unpack1('l<')
36
+ offset += 4
37
+ pts_count = bytes[offset, 4].unpack1('l<')
38
+ offset += 4
39
+ date = bytes[offset, 4].unpack1('l<')
40
+
41
+ new(flags: flags, id: id, user_id: user_id, text: text,
42
+ pts: pts, pts_count: pts_count, date: date)
43
+ end
44
+
45
+ class << self
46
+ private
47
+
48
+ def read_tl_string(bytes, offset)
49
+ first_byte = bytes.getbyte(offset)
50
+ if first_byte == 254
51
+ len = bytes.getbyte(offset + 1) |
52
+ (bytes.getbyte(offset + 2) << 8) |
53
+ (bytes.getbyte(offset + 3) << 16)
54
+ total = 4 + len
55
+ else
56
+ len = first_byte
57
+ total = 1 + len
58
+ end
59
+ padding = (4 - (total % 4)) % 4
60
+ [bytes[offset + (total - len), len], offset + total + padding]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end