mtproto 0.0.6 → 0.0.7

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mtproto/auth_key_generator.rb +16 -6
  3. data/lib/mtproto/client/rpc.rb +141 -0
  4. data/lib/mtproto/client.rb +36 -181
  5. data/lib/mtproto/crypto/aes_ige.rb +1 -1
  6. data/lib/mtproto/crypto/factorization.rb +1 -1
  7. data/lib/mtproto/message_id.rb +13 -0
  8. data/lib/mtproto/rpc/get_config.rb +37 -0
  9. data/lib/mtproto/rpc/get_contacts.rb +22 -0
  10. data/lib/mtproto/rpc/get_updates_difference.rb +33 -0
  11. data/lib/mtproto/rpc/get_updates_state.rb +22 -0
  12. data/lib/mtproto/rpc/get_users.rb +22 -0
  13. data/lib/mtproto/rpc/ping.rb +26 -0
  14. data/lib/mtproto/rpc/send_code.rb +44 -0
  15. data/lib/mtproto/rpc/send_message.rb +31 -0
  16. data/lib/mtproto/rpc/sign_in.rb +52 -0
  17. data/lib/mtproto/tl/auth_key/dh_gen_response.rb +37 -0
  18. data/lib/mtproto/tl/auth_key/req_dh_params.rb +31 -0
  19. data/lib/mtproto/tl/auth_key/req_pq_multi.rb +18 -0
  20. data/lib/mtproto/tl/auth_key/res_pq.rb +62 -0
  21. data/lib/mtproto/tl/auth_key/server_dh_params.rb +43 -0
  22. data/lib/mtproto/tl/auth_key/set_client_dh_params.rb +25 -0
  23. data/lib/mtproto/tl/message.rb +2 -216
  24. data/lib/mtproto/tl/method_builder.rb +29 -0
  25. data/lib/mtproto/tl/rpc/auth/authorization.rb +107 -0
  26. data/lib/mtproto/tl/rpc/auth/send_code.rb +28 -0
  27. data/lib/mtproto/tl/rpc/auth/sent_code.rb +36 -0
  28. data/lib/mtproto/tl/rpc/auth/sign_in.rb +32 -0
  29. data/lib/mtproto/tl/rpc/contacts/contacts.rb +155 -0
  30. data/lib/mtproto/tl/rpc/contacts/get_contacts.rb +18 -0
  31. data/lib/mtproto/tl/rpc/help/config.rb +35 -0
  32. data/lib/mtproto/tl/rpc/help/get_config.rb +17 -0
  33. data/lib/mtproto/tl/rpc/messages/send_message.rb +43 -0
  34. data/lib/mtproto/tl/rpc/messages/updates.rb +87 -0
  35. data/lib/mtproto/tl/rpc/ping.rb +18 -0
  36. data/lib/mtproto/tl/rpc/pong.rb +46 -0
  37. data/lib/mtproto/tl/rpc/updates/difference.rb +332 -0
  38. data/lib/mtproto/tl/rpc/updates/get_difference.rb +42 -0
  39. data/lib/mtproto/tl/rpc/updates/get_state.rb +17 -0
  40. data/lib/mtproto/tl/rpc/updates/state.rb +59 -0
  41. data/lib/mtproto/tl/rpc/users/get_users.rb +25 -0
  42. data/lib/mtproto/tl/rpc/users/users.rb +99 -0
  43. data/lib/mtproto/updates_poller.rb +111 -0
  44. data/lib/mtproto/version.rb +1 -1
  45. data/lib/mtproto.rb +10 -1
  46. metadata +53 -5
  47. data/ext/aes_ige/Makefile +0 -273
  48. data/ext/factorization/Makefile +0 -273
  49. data/lib/mtproto/connection.rb +0 -103
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tl/rpc/ping'
4
+ require_relative '../tl/rpc/pong'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class Ping
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(ping_id = nil)
14
+ ping_id ||= rand(2**63)
15
+ body = TL::RPC::Ping.build(ping_id)
16
+
17
+ response_body = @client.rpc.call(body)
18
+ pong = TL::RPC::Pong.parse(response_body)
19
+
20
+ raise PingMismatchError unless pong[:ping_id] == ping_id
21
+
22
+ true
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tl/rpc/auth/send_code'
4
+ require_relative '../tl/rpc/auth/sent_code'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class SendCode
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(phone_number, code_settings: {})
14
+ raise ArgumentError, 'phone_number is required' if phone_number.nil? || phone_number.empty?
15
+
16
+ query = TL::RPC::Auth::SendCode.build(
17
+ phone_number: phone_number,
18
+ api_id: @client.api_id,
19
+ api_hash: @client.api_hash,
20
+ code_settings: code_settings
21
+ )
22
+
23
+ unless @client.instance_variable_get(:@connection_initialized)
24
+ query = @client.init_connection(
25
+ api_id: @client.api_id,
26
+ device_model: @client.device_model,
27
+ system_version: @client.system_version,
28
+ app_version: @client.app_version,
29
+ system_lang_code: @client.system_lang_code,
30
+ lang_pack: @client.lang_pack,
31
+ lang_code: @client.lang_code,
32
+ query: query
33
+ )
34
+ query = @client.invoke_with_layer(214, query)
35
+ @client.instance_variable_set(:@connection_initialized, true)
36
+ end
37
+
38
+ response = @client.rpc.call(query)
39
+
40
+ TL::RPC::Auth::SentCode.parse(response)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tl/rpc/messages/send_message'
4
+ require_relative '../tl/rpc/messages/updates'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class SendMessage
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(user_id:, access_hash:, message:, random_id: nil)
14
+ raise 'Auth key not generated' unless @client.auth_key
15
+ raise ArgumentError, 'user_id is required' if user_id.nil?
16
+ raise ArgumentError, 'access_hash is required' if access_hash.nil?
17
+ raise ArgumentError, 'message is required and cannot be empty' if message.nil? || message.empty?
18
+
19
+ query = TL::RPC::Messages::SendMessage.build(
20
+ user_id: user_id,
21
+ access_hash: access_hash,
22
+ message: message,
23
+ random_id: random_id
24
+ )
25
+
26
+ response = @client.rpc.call(query)
27
+ TL::RPC::Messages::Updates.parse(response)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tl/rpc/auth/sign_in'
4
+ require_relative '../tl/rpc/auth/authorization'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class SignIn
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(phone_number:, phone_code_hash:, phone_code:)
14
+ raise ArgumentError, 'phone_number is required' if phone_number.nil? || phone_number.empty?
15
+ raise ArgumentError, 'phone_code_hash is required' if phone_code_hash.nil? || phone_code_hash.empty?
16
+ raise ArgumentError, 'phone_code is required' if phone_code.nil? || phone_code.empty?
17
+
18
+ query = TL::RPC::Auth::SignIn.build(
19
+ phone_number: phone_number,
20
+ phone_code_hash: phone_code_hash,
21
+ phone_code: phone_code
22
+ )
23
+
24
+ unless @client.instance_variable_get(:@connection_initialized)
25
+ query = @client.init_connection(
26
+ api_id: @client.api_id,
27
+ device_model: @client.device_model,
28
+ system_version: @client.system_version,
29
+ app_version: @client.app_version,
30
+ system_lang_code: @client.system_lang_code,
31
+ lang_pack: @client.lang_pack,
32
+ lang_code: @client.lang_code,
33
+ query: query
34
+ )
35
+ query = @client.invoke_with_layer(214, query)
36
+ @client.instance_variable_set(:@connection_initialized, true)
37
+ end
38
+
39
+ response = @client.rpc.call(query)
40
+
41
+ result = TL::RPC::Auth::Authorization.parse(response)
42
+
43
+ # Update client with user data if authorization was successful
44
+ if result[:authorization] && result[:user_id]
45
+ @client.update_user(user_id: result[:user_id], access_hash: result[:access_hash])
46
+ end
47
+
48
+ result
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ module AuthKey
6
+ class DHGenResponse
7
+ CONSTRUCTOR_DH_GEN_OK = 0x3bcbf734
8
+ CONSTRUCTOR_DH_GEN_RETRY = 0x46dc1fb9
9
+ CONSTRUCTOR_DH_GEN_FAIL = 0xa69dae02
10
+
11
+ def self.parse(body)
12
+ constructor = body[0, 4].unpack1('L<')
13
+
14
+ offset = 4
15
+ nonce = body[offset, 16]
16
+ offset += 16
17
+
18
+ server_nonce = body[offset, 16]
19
+ offset += 16
20
+
21
+ new_nonce_hash = body[offset, 16]
22
+
23
+ case constructor
24
+ when CONSTRUCTOR_DH_GEN_OK
25
+ { status: :ok, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
26
+ when CONSTRUCTOR_DH_GEN_RETRY
27
+ { status: :retry, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
28
+ when CONSTRUCTOR_DH_GEN_FAIL
29
+ { status: :fail, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
30
+ else
31
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../serializer'
4
+
5
+ module MTProto
6
+ module TL
7
+ module AuthKey
8
+ class ReqDHParams
9
+ CONSTRUCTOR = 0xd712e4be
10
+
11
+ def self.build(nonce:, server_nonce:, p:, q:, public_key_fingerprint:, encrypted_data:)
12
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
13
+ raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
14
+
15
+ p_bytes = Serializer.integer_to_bytes(p)
16
+ q_bytes = Serializer.integer_to_bytes(q)
17
+
18
+ body = Serializer.serialize_int(CONSTRUCTOR)
19
+ body += nonce
20
+ body += server_nonce
21
+ body += Serializer.serialize_bytes(p_bytes)
22
+ body += Serializer.serialize_bytes(q_bytes)
23
+ body += Serializer.serialize_long(public_key_fingerprint)
24
+ body += Serializer.serialize_bytes(encrypted_data)
25
+
26
+ body
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ module AuthKey
6
+ class ReqPqMulti
7
+ CONSTRUCTOR = 0xbe7e8ef1
8
+
9
+ def self.build(nonce)
10
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
11
+
12
+ constructor = [CONSTRUCTOR].pack('L<')
13
+ constructor + nonce
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ module AuthKey
6
+ class ResPq
7
+ CONSTRUCTOR = 0x05162463
8
+
9
+ def self.parse(body)
10
+ constructor = body[0, 4].unpack1('L<')
11
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
12
+
13
+ offset = 4
14
+
15
+ nonce = body[offset, 16]
16
+ offset += 16
17
+
18
+ server_nonce = body[offset, 16]
19
+ offset += 16
20
+
21
+ pq_length_byte = body[offset].unpack1('C')
22
+ offset += 1
23
+
24
+ pq_length = if pq_length_byte == 254
25
+ body[offset, 3].unpack1('L<') & 0xffffff
26
+ offset += 3
27
+ else
28
+ pq_length_byte
29
+ end
30
+
31
+ pq = body[offset, pq_length]
32
+ offset += pq_length
33
+ offset += padding_length(pq_length + 1)
34
+
35
+ vector_constructor = body[offset, 4].unpack1('L<')
36
+ offset += 4
37
+ raise 'Expected vector constructor' unless vector_constructor == 0x1cb5c415
38
+
39
+ fingerprints_count = body[offset, 4].unpack1('L<')
40
+ offset += 4
41
+
42
+ fingerprints = []
43
+ fingerprints_count.times do
44
+ fingerprints << body[offset, 8].unpack1('Q<')
45
+ offset += 8
46
+ end
47
+
48
+ {
49
+ nonce: nonce,
50
+ server_nonce: server_nonce,
51
+ pq: pq,
52
+ fingerprints: fingerprints
53
+ }
54
+ end
55
+
56
+ def self.padding_length(length)
57
+ (4 - (length % 4)) % 4
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ module AuthKey
6
+ class ServerDHParams
7
+ CONSTRUCTOR = 0xd0e8075c
8
+
9
+ def self.parse(body)
10
+ constructor = body[0, 4].unpack1('L<')
11
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
12
+
13
+ offset = 4
14
+
15
+ nonce = body[offset, 16]
16
+ offset += 16
17
+
18
+ server_nonce = body[offset, 16]
19
+ offset += 16
20
+
21
+ length_byte = body[offset].ord
22
+ offset += 1
23
+
24
+ if length_byte == 254
25
+ length_bytes = body[offset, 3].bytes
26
+ encrypted_answer_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
27
+ offset += 3
28
+ else
29
+ encrypted_answer_length = length_byte
30
+ end
31
+
32
+ encrypted_answer = body[offset, encrypted_answer_length]
33
+
34
+ {
35
+ nonce: nonce,
36
+ server_nonce: server_nonce,
37
+ encrypted_answer: encrypted_answer
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../serializer'
4
+
5
+ module MTProto
6
+ module TL
7
+ module AuthKey
8
+ class SetClientDHParams
9
+ CONSTRUCTOR = 0xf5045f1f
10
+
11
+ def self.build(nonce:, server_nonce:, encrypted_data:)
12
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
13
+ raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
14
+
15
+ body = Serializer.serialize_int(CONSTRUCTOR)
16
+ body += nonce
17
+ body += server_nonce
18
+ body += Serializer.serialize_bytes(encrypted_data)
19
+
20
+ body
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,28 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'serializer'
4
-
5
3
  module MTProto
6
4
  module TL
7
5
  class Message
8
- CONSTRUCTOR_REQ_PQ_MULTI = 0xbe7e8ef1
9
- CONSTRUCTOR_RES_PQ = 0x05162463
10
- CONSTRUCTOR_REQ_DH_PARAMS = 0xd712e4be
11
- CONSTRUCTOR_SERVER_DH_PARAMS_OK = 0xd0e8075c
12
- CONSTRUCTOR_SET_CLIENT_DH_PARAMS = 0xf5045f1f
13
- CONSTRUCTOR_DH_GEN_OK = 0x3bcbf734
14
- CONSTRUCTOR_DH_GEN_RETRY = 0x46dc1fb9
15
- CONSTRUCTOR_DH_GEN_FAIL = 0xa69dae02
16
- CONSTRUCTOR_PING = 0x7abe77ec
17
- CONSTRUCTOR_PONG = 0x347773c5
18
- CONSTRUCTOR_BAD_MSG_NOTIFICATION = 0xa7eff811
19
- CONSTRUCTOR_MSG_CONTAINER = 0x73f1f8dc
20
-
21
6
  attr_reader :auth_key_id, :msg_id, :body
22
7
 
23
- def initialize(auth_key_id: 0, msg_id: nil, body: '')
8
+ def initialize(auth_key_id:, msg_id:, body:)
24
9
  @auth_key_id = auth_key_id
25
- @msg_id = msg_id || generate_msg_id
10
+ @msg_id = msg_id
26
11
  @body = body
27
12
  end
28
13
 
@@ -34,45 +19,6 @@ module MTProto
34
19
  auth_key_id_bytes + msg_id_bytes + body_length + @body
35
20
  end
36
21
 
37
- def self.req_pq_multi(nonce)
38
- raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
39
-
40
- constructor = [CONSTRUCTOR_REQ_PQ_MULTI].pack('L<')
41
- body = constructor + nonce
42
-
43
- new(auth_key_id: 0, body: body)
44
- end
45
-
46
- def self.req_DH_params(nonce:, server_nonce:, p:, q:, public_key_fingerprint:, encrypted_data:)
47
- raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
48
- raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
49
-
50
- p_bytes = Serializer.integer_to_bytes(p)
51
- q_bytes = Serializer.integer_to_bytes(q)
52
-
53
- body = Serializer.serialize_int(CONSTRUCTOR_REQ_DH_PARAMS)
54
- body += nonce
55
- body += server_nonce
56
- body += Serializer.serialize_bytes(p_bytes)
57
- body += Serializer.serialize_bytes(q_bytes)
58
- body += Serializer.serialize_long(public_key_fingerprint)
59
- body += Serializer.serialize_bytes(encrypted_data)
60
-
61
- new(auth_key_id: 0, body: body)
62
- end
63
-
64
- def self.set_client_DH_params(nonce:, server_nonce:, encrypted_data:)
65
- raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
66
- raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
67
-
68
- body = Serializer.serialize_int(CONSTRUCTOR_SET_CLIENT_DH_PARAMS)
69
- body += nonce
70
- body += server_nonce
71
- body += Serializer.serialize_bytes(encrypted_data)
72
-
73
- new(auth_key_id: 0, body: body)
74
- end
75
-
76
22
  def self.deserialize(data)
77
23
  if data.bytesize < 20
78
24
  raise(ArgumentError,
@@ -87,166 +33,6 @@ module MTProto
87
33
 
88
34
  new(auth_key_id: auth_key_id, msg_id: msg_id, body: body)
89
35
  end
90
-
91
- def parse_res_pq
92
- constructor = @body[0, 4].unpack1('L<')
93
- raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR_RES_PQ
94
-
95
- offset = 4
96
-
97
- nonce = @body[offset, 16]
98
- offset += 16
99
-
100
- server_nonce = @body[offset, 16]
101
- offset += 16
102
-
103
- pq_length_byte = @body[offset].unpack1('C')
104
- offset += 1
105
-
106
- pq_length = if pq_length_byte == 254
107
- @body[offset, 3].unpack1('L<') & 0xffffff
108
- offset += 3
109
- else
110
- pq_length_byte
111
- end
112
-
113
- pq = @body[offset, pq_length]
114
- offset += pq_length
115
- offset += padding_length(pq_length + 1)
116
-
117
- vector_constructor = @body[offset, 4].unpack1('L<')
118
- offset += 4
119
- raise 'Expected vector constructor' unless vector_constructor == 0x1cb5c415
120
-
121
- fingerprints_count = @body[offset, 4].unpack1('L<')
122
- offset += 4
123
-
124
- fingerprints = []
125
- fingerprints_count.times do
126
- fingerprints << @body[offset, 8].unpack1('Q<')
127
- offset += 8
128
- end
129
-
130
- {
131
- nonce: nonce,
132
- server_nonce: server_nonce,
133
- pq: pq,
134
- fingerprints: fingerprints
135
- }
136
- end
137
-
138
- def parse_server_DH_params_ok
139
- constructor = @body[0, 4].unpack1('L<')
140
- raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR_SERVER_DH_PARAMS_OK
141
-
142
- offset = 4
143
-
144
- nonce = @body[offset, 16]
145
- offset += 16
146
-
147
- server_nonce = @body[offset, 16]
148
- offset += 16
149
-
150
- length_byte = @body[offset].ord
151
- offset += 1
152
-
153
- if length_byte == 254
154
- length_bytes = @body[offset, 3].bytes
155
- encrypted_answer_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
156
- offset += 3
157
- else
158
- encrypted_answer_length = length_byte
159
- end
160
-
161
- encrypted_answer = @body[offset, encrypted_answer_length]
162
-
163
- {
164
- nonce: nonce,
165
- server_nonce: server_nonce,
166
- encrypted_answer: encrypted_answer
167
- }
168
- end
169
-
170
- def parse_dh_gen_response
171
- constructor = @body[0, 4].unpack1('L<')
172
-
173
- offset = 4
174
- nonce = @body[offset, 16]
175
- offset += 16
176
-
177
- server_nonce = @body[offset, 16]
178
- offset += 16
179
-
180
- new_nonce_hash = @body[offset, 16]
181
-
182
- case constructor
183
- when CONSTRUCTOR_DH_GEN_OK
184
- { status: :ok, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
185
- when CONSTRUCTOR_DH_GEN_RETRY
186
- { status: :retry, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
187
- when CONSTRUCTOR_DH_GEN_FAIL
188
- { status: :fail, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
189
- else
190
- raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
191
- end
192
- end
193
-
194
- def self.ping(ping_id)
195
- body = Serializer.serialize_int(CONSTRUCTOR_PING)
196
- body += [ping_id].pack('Q<')
197
-
198
- body
199
- end
200
-
201
- def parse_pong
202
- constructor = @body[0, 4].unpack1('L<')
203
-
204
- if constructor == CONSTRUCTOR_BAD_MSG_NOTIFICATION
205
- bad_msg = TL::BadMsgNotification.deserialize(@body)
206
- raise "Bad message notification: #{bad_msg.error_message} (code: #{bad_msg.error_code}, msg_id: #{bad_msg.bad_msg_id}, seqno: #{bad_msg.bad_msg_seqno})"
207
- end
208
-
209
- if constructor == CONSTRUCTOR_MSG_CONTAINER
210
- container = TL::MsgContainer.deserialize(@body)
211
- pong_message = container.messages.find do |msg|
212
- msg[:body][0, 4].unpack1('L<') == CONSTRUCTOR_PONG
213
- end
214
-
215
- raise 'No pong message found in container' unless pong_message
216
-
217
- offset = 4
218
- msg_id = pong_message[:body][offset, 8].unpack1('Q<')
219
- offset += 8
220
- ping_id = pong_message[:body][offset, 8].unpack1('Q<')
221
-
222
- return { msg_id: msg_id, ping_id: ping_id }
223
- end
224
-
225
- raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR_PONG
226
-
227
- offset = 4
228
- msg_id = @body[offset, 8].unpack1('Q<')
229
- offset += 8
230
- ping_id = @body[offset, 8].unpack1('Q<')
231
-
232
- { msg_id: msg_id, ping_id: ping_id }
233
- end
234
-
235
- private
236
-
237
- def generate_msg_id
238
- time = Time.now.to_f
239
- msg_id = (time * (2**32)).to_i
240
- (msg_id / 4) * 4
241
- end
242
-
243
- def padding_length(length)
244
- (4 - (length % 4)) % 4
245
- end
246
-
247
- def self.padding_length(length)
248
- (4 - (length % 4)) % 4
249
- end
250
36
  end
251
37
  end
252
38
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'serializer'
4
+
5
+ module MTProto
6
+ module TL
7
+ module MethodBuilder
8
+ def invoke_with_layer(layer, query)
9
+ body = Serializer.serialize_int(0xda9b0d0d)
10
+ body += Serializer.serialize_int(layer)
11
+ body + query
12
+ end
13
+
14
+ def init_connection(api_id:, device_model:, system_version:, app_version:, system_lang_code:, lang_pack:, lang_code:, query:)
15
+ body = Serializer.serialize_int(0xc1cd5ea9)
16
+ flags = 0
17
+ body += Serializer.serialize_int(flags)
18
+ body += Serializer.serialize_int(api_id)
19
+ body += Serializer.serialize_string(device_model)
20
+ body += Serializer.serialize_string(system_version)
21
+ body += Serializer.serialize_string(app_version)
22
+ body += Serializer.serialize_string(system_lang_code)
23
+ body += Serializer.serialize_string(lang_pack)
24
+ body += Serializer.serialize_string(lang_code)
25
+ body + query
26
+ end
27
+ end
28
+ end
29
+ end