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
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MTProto
4
- module Type
5
- module RPC
6
- module Updates
7
- class GetState
8
- CONSTRUCTOR = 0xedd4882a
9
-
10
- def self.build
11
- [CONSTRUCTOR].pack('L<')
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../gzip_packed'
4
- require_relative '../../rpc_error'
5
-
6
- module MTProto
7
- module Type
8
- module RPC
9
- module Updates
10
- class State
11
- CONSTRUCTOR = 0xa56c2a3e
12
-
13
- def self.parse(response)
14
- offset = 0
15
-
16
- constructor = response[offset, 4].unpack1('L<')
17
- offset += 4
18
-
19
- if constructor == Type::GzipPacked::CONSTRUCTOR
20
- response = Type::GzipPacked.unpack(response)
21
- constructor = response[0, 4].unpack1('L<')
22
- offset = 4
23
- end
24
-
25
- if constructor == Type::RpcError::CONSTRUCTOR
26
- error = Type::RpcError.deserialize(response)
27
- raise MTProto::RpcError.new(error.error_code, error.error_message)
28
- end
29
-
30
- raise "Expected State constructor 0x#{CONSTRUCTOR.to_s(16)}, got 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
31
-
32
- # updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
33
- pts = response[offset, 4].unpack1('L<')
34
- offset += 4
35
-
36
- qts = response[offset, 4].unpack1('L<')
37
- offset += 4
38
-
39
- date = response[offset, 4].unpack1('L<')
40
- offset += 4
41
-
42
- seq = response[offset, 4].unpack1('L<')
43
- offset += 4
44
-
45
- unread_count = response[offset, 4].unpack1('L<')
46
-
47
- {
48
- pts: pts,
49
- qts: qts,
50
- date: date,
51
- seq: seq,
52
- unread_count: unread_count
53
- }
54
- end
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MTProto
4
- module Type
5
- module RPC
6
- module Users
7
- class GetUsers
8
- CONSTRUCTOR = 0x0d91a548
9
- INPUT_USER_SELF = 0xf7c1b13f
10
-
11
- def self.build
12
- query = [CONSTRUCTOR].pack('L<')
13
-
14
- # Vector of InputUser with one element: inputUserSelf
15
- query += [0x1cb5c415].pack('L<') # vector constructor
16
- query += [1].pack('L<') # count = 1
17
- query += [INPUT_USER_SELF].pack('L<') # inputUserSelf
18
-
19
- query
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../gzip_packed'
4
- require_relative '../../rpc_error'
5
-
6
- module MTProto
7
- module Type
8
- module RPC
9
- module Users
10
- class Users
11
- VECTOR_CONSTRUCTOR = 0x1cb5c415
12
- USER_CONSTRUCTOR = 0x020b1422
13
-
14
- def self.parse(response)
15
- offset = 0
16
-
17
- constructor = response[offset, 4].unpack1('L<')
18
- offset += 4
19
-
20
- if constructor == Type::GzipPacked::CONSTRUCTOR
21
- response = Type::GzipPacked.unpack(response)
22
- constructor = response[0, 4].unpack1('L<')
23
- offset = 4
24
- end
25
-
26
- if constructor == Type::RpcError::CONSTRUCTOR
27
- error = Type::RpcError.deserialize(response)
28
- raise MTProto::RpcError.new(error.error_code, error.error_message)
29
- end
30
-
31
- # Expect Vector<User>
32
- raise "Expected Vector constructor, got 0x#{constructor.to_s(16)}" unless constructor == VECTOR_CONSTRUCTOR
33
-
34
- count = response[offset, 4].unpack1('L<')
35
- offset += 4
36
-
37
- users = []
38
- count.times do
39
- user_constructor = response[offset, 4].unpack1('L<')
40
- offset += 4
41
-
42
- next unless user_constructor == USER_CONSTRUCTOR
43
-
44
- # Parse User: flags:# flags2:# id:long access_hash:flags.0?long first_name:flags.1?string ...
45
- flags = response[offset, 4].unpack1('L<')
46
- offset += 4
47
-
48
- flags2 = response[offset, 4].unpack1('L<')
49
- offset += 4
50
-
51
- id = response[offset, 8].unpack1('Q<')
52
- offset += 8
53
-
54
- access_hash = nil
55
- if (flags & (1 << 0)) != 0
56
- access_hash = response[offset, 8].unpack1('Q<')
57
- offset += 8
58
- end
59
-
60
- first_name = nil
61
- if (flags & (1 << 1)) != 0
62
- len = response[offset].unpack1('C')
63
- offset += 1
64
- first_name = response[offset, len]
65
- offset += len
66
- padding = (4 - ((1 + len) % 4)) % 4
67
- offset += padding
68
- end
69
-
70
- last_name = nil
71
- if (flags & (1 << 2)) != 0
72
- len = response[offset].unpack1('C')
73
- offset += 1
74
- last_name = response[offset, len]
75
- offset += len
76
- padding = (4 - ((1 + len) % 4)) % 4
77
- offset += padding
78
- end
79
-
80
- users << {
81
- id: id,
82
- access_hash: access_hash,
83
- first_name: first_name,
84
- last_name: last_name,
85
- flags: flags,
86
- flags2: flags2
87
- }
88
-
89
- # Skip remaining fields - we don't need them for this test
90
- break
91
- end
92
-
93
- users
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MTProto
4
- module Type
5
- class SentCode
6
- CONSTRUCTOR = 0x5e002502
7
-
8
- attr_reader :flags, :type, :phone_code_hash, :next_type, :timeout
9
-
10
- def self.deserialize(data)
11
- offset = 4
12
-
13
- flags = data[offset, 4].unpack1('L<')
14
- offset += 4
15
-
16
- type, type_bytes_read = SentCodeType.deserialize_from(data[offset..])
17
- offset += type_bytes_read
18
-
19
- phone_code_hash_length = data[offset].ord
20
- offset += 1
21
-
22
- phone_code_hash = data[offset, phone_code_hash_length]
23
- offset += phone_code_hash_length
24
-
25
- padding = (4 - ((phone_code_hash_length + 1) % 4)) % 4
26
- offset += padding
27
-
28
- next_type = nil
29
- if (flags & (1 << 1)) != 0
30
- next_type, next_type_bytes_read = CodeType.deserialize_from(data[offset..])
31
- offset += next_type_bytes_read
32
- end
33
-
34
- timeout = nil
35
- if (flags & (1 << 2)) != 0
36
- timeout = data[offset, 4].unpack1('L<')
37
- end
38
-
39
- new(
40
- flags: flags,
41
- type: type,
42
- phone_code_hash: phone_code_hash,
43
- next_type: next_type,
44
- timeout: timeout
45
- )
46
- end
47
-
48
- def initialize(flags:, type:, phone_code_hash:, next_type: nil, timeout: nil)
49
- @flags = flags
50
- @type = type
51
- @phone_code_hash = phone_code_hash
52
- @next_type = next_type
53
- @timeout = timeout
54
- end
55
-
56
- def to_h
57
- {
58
- phone_code_hash: @phone_code_hash,
59
- type: @type,
60
- next_type: @next_type,
61
- timeout: @timeout
62
- }.compact
63
- end
64
- end
65
-
66
- module SentCodeType
67
- def self.deserialize_from(data)
68
- constructor = data[0, 4].unpack1('L<')
69
- offset = 4
70
-
71
- case constructor
72
- when 0x3dbb5986 # auth.sentCodeTypeApp
73
- length = data[offset, 4].unpack1('L<')
74
- offset += 4
75
- [{ _: :sent_code_type_app, length: length }, offset]
76
- when 0xc000bba2 # auth.sentCodeTypeSms
77
- length = data[offset, 4].unpack1('L<')
78
- offset += 4
79
- [{ _: :sent_code_type_sms, length: length }, offset]
80
- when 0x5353e5a7 # auth.sentCodeTypeCall
81
- length = data[offset, 4].unpack1('L<')
82
- offset += 4
83
- [{ _: :sent_code_type_call, length: length }, offset]
84
- when 0xab03c6d9 # auth.sentCodeTypeFlashCall
85
- pattern_length = data[offset].ord
86
- offset += 1
87
- pattern = data[offset, pattern_length]
88
- offset += pattern_length
89
- padding = (4 - ((pattern_length + 1) % 4)) % 4
90
- offset += padding
91
- [{ _: :sent_code_type_flash_call, pattern: pattern }, offset]
92
- when 0x82006484 # auth.sentCodeTypeMissedCall
93
- prefix_length = data[offset].ord
94
- offset += 1
95
- prefix = data[offset, prefix_length]
96
- offset += prefix_length
97
- padding = (4 - ((prefix_length + 1) % 4)) % 4
98
- offset += padding
99
- length = data[offset, 4].unpack1('L<')
100
- offset += 4
101
- [{ _: :sent_code_type_missed_call, prefix: prefix, length: length }, offset]
102
- else
103
- raise "Unknown SentCodeType constructor: 0x#{constructor.to_s(16)}"
104
- end
105
- end
106
- end
107
-
108
- module CodeType
109
- def self.deserialize_from(data)
110
- constructor = data[0, 4].unpack1('L<')
111
- offset = 4
112
-
113
- case constructor
114
- when 0x72a3158c # codeTypeSms
115
- [{ _: :code_type_sms }, offset]
116
- when 0x741cd3e3 # codeTypeCall
117
- [{ _: :code_type_call }, offset]
118
- when 0x226ccefb # codeTypeFlashCall
119
- [{ _: :code_type_flash_call }, offset]
120
- when 0xd61ad6ee # codeTypeMissedCall
121
- [{ _: :code_type_missed_call }, offset]
122
- else
123
- raise "Unknown CodeType constructor: 0x#{constructor.to_s(16)}"
124
- end
125
- end
126
- end
127
- end
128
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MTProto
4
- module Type
5
- module Serializer
6
- module_function
7
-
8
- def serialize_int(value)
9
- [value].pack('L<')
10
- end
11
-
12
- def serialize_long(value)
13
- [value].pack('Q<')
14
- end
15
-
16
- def serialize_int128(value)
17
- value.is_a?(String) ? value : [value].pack('Q<Q<')
18
- end
19
-
20
- def serialize_int256(value)
21
- value.is_a?(String) ? value : [value].pack('Q<Q<Q<Q<')
22
- end
23
-
24
- def serialize_bytes(bytes)
25
- length = bytes.bytesize
26
-
27
- if length <= 253
28
- [length].pack('C').b + bytes.b + padding(length + 1)
29
- else
30
- [254].pack('C').b + [length].pack('L<')[0, 3].b + bytes.b + padding(length + 4)
31
- end
32
- end
33
-
34
- def serialize_string(str)
35
- serialize_bytes(str)
36
- end
37
-
38
- def padding(current_length)
39
- pad_length = (4 - (current_length % 4)) % 4
40
- ("\x00" * pad_length).b
41
- end
42
-
43
- def integer_to_bytes(int)
44
- return "\x00" if int.zero?
45
-
46
- bytes = []
47
- while int > 0
48
- bytes.unshift(int & 0xff)
49
- int >>= 8
50
- end
51
- bytes.pack('C*')
52
- end
53
- end
54
- end
55
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MTProto
4
- module Type
5
- class ServerDHInnerData
6
- CONSTRUCTOR = 0xb5890dba
7
-
8
- attr_reader :nonce, :server_nonce, :g, :dh_prime, :g_a, :server_time
9
-
10
- def self.deserialize(data)
11
- constructor = data[0, 4].unpack1('L<')
12
- raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
13
-
14
- offset = 4
15
-
16
- nonce = data[offset, 16]
17
- offset += 16
18
-
19
- server_nonce = data[offset, 16]
20
- offset += 16
21
-
22
- g = data[offset, 4].unpack1('L<')
23
- offset += 4
24
-
25
- dh_prime_length_byte = data[offset].ord
26
- offset += 1
27
-
28
- if dh_prime_length_byte == 254
29
- length_bytes = data[offset, 3].bytes
30
- dh_prime_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
31
- offset += 3
32
- else
33
- dh_prime_length = dh_prime_length_byte
34
- end
35
-
36
- dh_prime = data[offset, dh_prime_length]
37
- offset += dh_prime_length
38
- offset += padding_length(dh_prime_length_byte == 254 ? dh_prime_length + 4 : dh_prime_length + 1)
39
-
40
- g_a_length_byte = data[offset].ord
41
- offset += 1
42
-
43
- if g_a_length_byte == 254
44
- length_bytes = data[offset, 3].bytes
45
- g_a_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
46
- offset += 3
47
- else
48
- g_a_length = g_a_length_byte
49
- end
50
-
51
- g_a = data[offset, g_a_length]
52
- offset += g_a_length
53
- offset += padding_length(g_a_length_byte == 254 ? g_a_length + 4 : g_a_length + 1)
54
-
55
- server_time = data[offset, 4].unpack1('L<')
56
-
57
- new(
58
- nonce: nonce,
59
- server_nonce: server_nonce,
60
- g: g,
61
- dh_prime: dh_prime,
62
- g_a: g_a,
63
- server_time: server_time
64
- )
65
- end
66
-
67
- def initialize(nonce:, server_nonce:, g:, dh_prime:, g_a:, server_time:)
68
- @nonce = nonce
69
- @server_nonce = server_nonce
70
- @g = g
71
- @dh_prime = dh_prime
72
- @g_a = g_a
73
- @server_time = server_time
74
- end
75
-
76
- def self.padding_length(length)
77
- (4 - (length % 4)) % 4
78
- end
79
-
80
- def padding_length(length)
81
- self.class.padding_length(length)
82
- end
83
- end
84
- end
85
- end