mtproto 0.0.6 → 0.0.8

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +4 -0
  3. data/lib/mtproto/async/middleware/base.rb +17 -0
  4. data/lib/mtproto/async/middleware/flood_wait.rb +42 -0
  5. data/lib/mtproto/async/request.rb +18 -0
  6. data/lib/mtproto/async/request_queue.rb +63 -0
  7. data/lib/mtproto/async_client.rb +201 -0
  8. data/lib/mtproto/auth_key_generator.rb +22 -12
  9. data/lib/mtproto/client/rpc.rb +165 -0
  10. data/lib/mtproto/client.rb +65 -176
  11. data/lib/mtproto/crypto/aes_ige.rb +1 -1
  12. data/lib/mtproto/crypto/factorization.rb +1 -1
  13. data/lib/mtproto/message_id.rb +13 -0
  14. data/lib/mtproto/rpc/get_config.rb +34 -0
  15. data/lib/mtproto/rpc/get_contacts.rb +29 -0
  16. data/lib/mtproto/rpc/get_updates_difference.rb +51 -0
  17. data/lib/mtproto/rpc/get_updates_state.rb +29 -0
  18. data/lib/mtproto/rpc/get_users.rb +29 -0
  19. data/lib/mtproto/rpc/ping.rb +33 -0
  20. data/lib/mtproto/rpc/send_code.rb +41 -0
  21. data/lib/mtproto/rpc/send_message.rb +47 -0
  22. data/lib/mtproto/rpc/sign_in.rb +48 -0
  23. data/lib/mtproto/type/auth_key/dh_gen_response.rb +37 -0
  24. data/lib/mtproto/type/auth_key/req_dh_params.rb +31 -0
  25. data/lib/mtproto/type/auth_key/req_pq_multi.rb +18 -0
  26. data/lib/mtproto/type/auth_key/res_pq.rb +62 -0
  27. data/lib/mtproto/type/auth_key/server_dh_params.rb +43 -0
  28. data/lib/mtproto/type/auth_key/set_client_dh_params.rb +25 -0
  29. data/lib/mtproto/{tl → type}/bad_msg_notification.rb +1 -1
  30. data/lib/mtproto/{tl → type}/client_dh_inner_data.rb +1 -1
  31. data/lib/mtproto/{tl → type}/code_settings.rb +1 -1
  32. data/lib/mtproto/{tl → type}/config.rb +1 -1
  33. data/lib/mtproto/{tl → type}/gzip_packed.rb +1 -1
  34. data/lib/mtproto/type/message.rb +38 -0
  35. data/lib/mtproto/{tl → type}/msg_container.rb +1 -1
  36. data/lib/mtproto/{tl → type}/new_session_created.rb +1 -1
  37. data/lib/mtproto/{tl/p_q_inner_data.rb → type/pq_inner_data.rb} +1 -1
  38. data/lib/mtproto/type/rpc/auth/authorization.rb +107 -0
  39. data/lib/mtproto/type/rpc/auth/send_code.rb +28 -0
  40. data/lib/mtproto/type/rpc/auth/sent_code.rb +36 -0
  41. data/lib/mtproto/type/rpc/auth/sign_in.rb +32 -0
  42. data/lib/mtproto/type/rpc/contacts/contacts.rb +155 -0
  43. data/lib/mtproto/type/rpc/contacts/get_contacts.rb +18 -0
  44. data/lib/mtproto/type/rpc/help/config.rb +35 -0
  45. data/lib/mtproto/type/rpc/help/get_config.rb +17 -0
  46. data/lib/mtproto/type/rpc/init_connection.rb +28 -0
  47. data/lib/mtproto/type/rpc/invoke_with_layer.rb +19 -0
  48. data/lib/mtproto/type/rpc/messages/send_message.rb +43 -0
  49. data/lib/mtproto/type/rpc/messages/updates.rb +87 -0
  50. data/lib/mtproto/type/rpc/ping.rb +18 -0
  51. data/lib/mtproto/type/rpc/pong.rb +46 -0
  52. data/lib/mtproto/type/rpc/updates/difference.rb +332 -0
  53. data/lib/mtproto/type/rpc/updates/get_difference.rb +42 -0
  54. data/lib/mtproto/type/rpc/updates/get_state.rb +17 -0
  55. data/lib/mtproto/type/rpc/updates/state.rb +59 -0
  56. data/lib/mtproto/type/rpc/users/get_users.rb +25 -0
  57. data/lib/mtproto/type/rpc/users/users.rb +99 -0
  58. data/lib/mtproto/{tl → type}/rpc_error.rb +1 -1
  59. data/lib/mtproto/{tl → type}/sent_code.rb +1 -1
  60. data/lib/mtproto/{tl → type}/serializer.rb +1 -1
  61. data/lib/mtproto/{tl → type}/server_dh_inner_data.rb +1 -1
  62. data/lib/mtproto/updates_poller.rb +111 -0
  63. data/lib/mtproto/version.rb +1 -1
  64. data/lib/mtproto.rb +28 -14
  65. metadata +86 -18
  66. data/ext/aes_ige/Makefile +0 -273
  67. data/ext/factorization/Makefile +0 -273
  68. data/lib/mtproto/connection.rb +0 -103
  69. data/lib/mtproto/tl/message.rb +0 -252
@@ -2,16 +2,21 @@
2
2
 
3
3
  require 'securerandom'
4
4
  require 'digest'
5
+ require 'base64'
5
6
  require_relative 'transport/tcp_connection'
6
7
  require_relative 'transport/abridged_packet_codec'
7
- require_relative 'tl/message'
8
+ require_relative 'type/message'
9
+ require_relative 'client/rpc'
10
+ require_relative 'type/rpc/help/get_config'
11
+ require_relative 'type/rpc/help/config'
12
+ require_relative 'type/rpc/invoke_with_layer'
13
+ require_relative 'type/rpc/init_connection'
8
14
 
9
15
  module MTProto
10
16
  class Client
11
- attr_reader :connection, :server_key, :auth_key, :server_salt, :time_offset, :session, :timeout
12
- attr_accessor :api_id, :api_hash, :device_model, :system_version, :app_version, :system_lang_code, :lang_pack, :lang_code
13
17
 
14
- CONSTRUCTOR_MSGS_ACK = 0x62d6b459
18
+ attr_reader :connection, :server_key, :auth_key, :server_salt, :time_offset, :session, :timeout, :user_id, :access_hash, :dc_number
19
+ attr_accessor :api_id, :api_hash, :device_model, :system_version, :app_version, :system_lang_code, :lang_pack, :lang_code
15
20
 
16
21
  def initialize(api_id: nil, api_hash: nil, host:, port: 443, public_key: nil, dc_number: nil, test_mode: false, timeout: 10)
17
22
  raise ArgumentError, "host is required" if host.nil? || host.empty?
@@ -29,6 +34,8 @@ module MTProto
29
34
  @time_offset = 0
30
35
  @session = nil
31
36
  @connection_initialized = false
37
+ @user_id = nil
38
+ @access_hash = nil
32
39
 
33
40
  # Client configuration defaults
34
41
  @api_id = api_id || 0
@@ -45,29 +52,15 @@ module MTProto
45
52
  @connection.connect!
46
53
  end
47
54
 
48
- def disconnect!
49
- @connection.close if @connection
55
+ def connected?
56
+ @connection&.connected?
50
57
  end
51
58
 
52
- def req_pq_multi
53
- nonce = SecureRandom.random_bytes(16)
54
-
55
- message = TL::Message.req_pq_multi(nonce)
56
- payload = message.serialize
57
-
58
- @connection.send(payload)
59
-
60
- response_data = @connection.recv(timeout: @timeout)
61
- response_message = TL::Message.deserialize(response_data)
62
-
63
- res_pq = response_message.parse_res_pq
64
-
65
- raise 'Nonce mismatch!' unless res_pq[:nonce] == nonce
66
-
67
- res_pq
59
+ def disconnect!
60
+ @connection.close if @connection
68
61
  end
69
62
 
70
- def make_auth_key
63
+ def exchange_keys!
71
64
  raise ArgumentError, "public_key is required for auth key generation" if @public_key.nil? || @public_key.empty?
72
65
 
73
66
  generator = AuthKeyGenerator.new(@connection, @public_key, @dc_number, test_mode: @test_mode, timeout: @timeout)
@@ -81,175 +74,71 @@ module MTProto
81
74
  result
82
75
  end
83
76
 
84
- def generate_msg_id
85
- time = Time.now.to_f + @time_offset
86
- msg_id = (time * (2**32)).to_i
87
- (msg_id / 4) * 4
77
+ def rpc
78
+ @rpc ||= RPC.new(self)
88
79
  end
89
80
 
90
- def rpc_call(body, content_related: true)
91
- raise 'Auth key not generated' unless @auth_key
92
- raise 'Session not initialized' unless @session
93
-
94
- msg_id = generate_msg_id
95
- seq_no = @session.next_seq_no(content_related: content_related)
96
-
97
- encrypted_msg = EncryptedMessage.encrypt(
98
- auth_key: @auth_key,
99
- server_salt: @server_salt,
100
- session_id: @session.session_id,
101
- msg_id: msg_id,
102
- seq_no: seq_no,
103
- body: body
104
- )
105
-
106
- @connection.send(encrypted_msg.serialize)
107
-
108
- response_data = @connection.recv(timeout: @timeout)
109
-
110
- decrypted = EncryptedMessage.decrypt(
111
- auth_key: @auth_key,
112
- encrypted_message_data: response_data,
113
- sender: :server
114
- )
115
-
116
- response_body = decrypted[:body]
117
-
118
- constructor = response_body[0, 4].unpack1('L<')
119
-
120
- if constructor == TL::NewSessionCreated::CONSTRUCTOR
121
- session_info = TL::NewSessionCreated.deserialize(response_body)
122
- @server_salt = session_info.server_salt
81
+ def call(body, content_related: true)
82
+ rpc.call(body, content_related: content_related)
83
+ end
123
84
 
124
- response_data = @connection.recv(timeout: @timeout)
125
- decrypted = EncryptedMessage.decrypt(
126
- auth_key: @auth_key,
127
- encrypted_message_data: response_data,
128
- sender: :server
85
+ def init_connection!
86
+ return if @connection_initialized
87
+
88
+ query = Type::RPC::InvokeWithLayer.build(
89
+ layer: 214,
90
+ query: Type::RPC::InitConnection.build(
91
+ api_id: api_id,
92
+ device_model: device_model,
93
+ system_version: system_version,
94
+ app_version: app_version,
95
+ system_lang_code: system_lang_code,
96
+ lang_pack: lang_pack,
97
+ lang_code: lang_code,
98
+ query: Type::RPC::Help::GetConfig.build
129
99
  )
130
- response_body = decrypted[:body]
131
- constructor = response_body[0, 4].unpack1('L<')
132
- end
133
-
134
- if constructor == TL::Message::CONSTRUCTOR_MSG_CONTAINER
135
- container = TL::MsgContainer.deserialize(response_body)
136
-
137
- rpc_result = container.messages.find do |msg|
138
- msg[:body][0, 4].unpack1('L<') == 0xf35c6d01
139
- end
140
-
141
- return rpc_result[:body][12..] if rpc_result
142
-
143
- new_session = container.messages.find { |msg| msg[:body][0, 4].unpack1('L<') == TL::NewSessionCreated::CONSTRUCTOR }
144
- if new_session
145
- session_info = TL::NewSessionCreated.deserialize(new_session[:body])
146
- @server_salt = session_info.server_salt
147
-
148
- other_messages = container.messages.reject do |msg|
149
- constructor = msg[:body][0, 4].unpack1('L<')
150
- constructor == TL::NewSessionCreated::CONSTRUCTOR || constructor == CONSTRUCTOR_MSGS_ACK
151
- end
152
-
153
- if !other_messages.empty?
154
- return other_messages.first[:body]
155
- end
156
-
157
- response_data = @connection.recv(timeout: @timeout)
158
- decrypted = EncryptedMessage.decrypt(
159
- auth_key: @auth_key,
160
- encrypted_message_data: response_data,
161
- sender: :server
162
- )
163
- response_body = decrypted[:body]
164
- constructor = response_body[0, 4].unpack1('L<')
165
-
166
- return response_body[12..] if constructor == 0xf35c6d01
100
+ )
167
101
 
168
- if constructor == TL::Message::CONSTRUCTOR_MSG_CONTAINER
169
- container = TL::MsgContainer.deserialize(response_body)
170
- rpc_result = container.messages.find { |msg| msg[:body][0, 4].unpack1('L<') == 0xf35c6d01 }
171
- return rpc_result[:body][12..] if rpc_result
172
- end
102
+ response = rpc.call_sync(query)
103
+ config = Type::RPC::Help::Config.parse(response)
173
104
 
174
- return response_body
175
- end
105
+ @connection_initialized = true
176
106
 
177
- return container.messages.first[:body]
178
- end
107
+ config
108
+ end
179
109
 
180
- return response_body[12..] if constructor == 0xf35c6d01
110
+ def save_auth_data
111
+ raise 'Cannot save auth_data: auth_key not set' unless @auth_key
181
112
 
182
- response_body
113
+ {
114
+ auth_key: Base64.strict_encode64(@auth_key),
115
+ server_salt: @server_salt,
116
+ user_id: @user_id,
117
+ access_hash: @access_hash,
118
+ dc_number: @dc_number,
119
+ time_offset: @time_offset
120
+ }
183
121
  end
184
122
 
185
- def invoke_with_layer(layer, query)
186
- body = TL::Serializer.serialize_int(0xda9b0d0d)
187
- body += TL::Serializer.serialize_int(layer)
188
- body += query
189
- body
190
- end
123
+ def load_auth_data(auth_data)
124
+ raise ArgumentError, 'auth_data must be a Hash' unless auth_data.is_a?(Hash)
125
+ raise ArgumentError, 'auth_key is required' unless auth_data[:auth_key]
126
+ raise ArgumentError, 'server_salt is required' unless auth_data[:server_salt]
191
127
 
192
- def init_connection(api_id:, device_model:, system_version:, app_version:, system_lang_code:, lang_pack:, lang_code:, query:)
193
- body = TL::Serializer.serialize_int(0xc1cd5ea9)
194
- flags = 0
195
- body += TL::Serializer.serialize_int(flags)
196
- body += TL::Serializer.serialize_int(api_id)
197
- body += TL::Serializer.serialize_string(device_model)
198
- body += TL::Serializer.serialize_string(system_version)
199
- body += TL::Serializer.serialize_string(app_version)
200
- body += TL::Serializer.serialize_string(system_lang_code)
201
- body += TL::Serializer.serialize_string(lang_pack)
202
- body += TL::Serializer.serialize_string(lang_code)
203
- body += query
204
- body
205
- end
128
+ @auth_key = Base64.strict_decode64(auth_data[:auth_key])
129
+ @server_salt = auth_data[:server_salt]
130
+ @time_offset = auth_data[:time_offset] || 0
131
+ @user_id = auth_data[:user_id]
132
+ @access_hash = auth_data[:access_hash]
206
133
 
207
- def help_get_config
208
- query = [0xc4f9186b].pack('L<')
209
-
210
- unless @connection_initialized
211
- query = init_connection(
212
- api_id: @api_id,
213
- device_model: @device_model,
214
- system_version: @system_version,
215
- app_version: @app_version,
216
- system_lang_code: @system_lang_code,
217
- lang_pack: @lang_pack,
218
- lang_code: @lang_code,
219
- query: query
220
- )
221
- query = invoke_with_layer(214, query)
222
- @connection_initialized = true
223
- end
134
+ @session = Session.new
224
135
 
225
- rpc_call(query)
136
+ true
226
137
  end
227
138
 
228
- def auth_send_code(phone_number, code_settings: {})
229
- raise ArgumentError, 'phone_number is required' if phone_number.nil? || phone_number.empty?
230
-
231
- query = [0xa677244f].pack('L<')
232
- query += TL::Serializer.serialize_string(phone_number)
233
- query += TL::Serializer.serialize_int(@api_id)
234
- query += TL::Serializer.serialize_string(@api_hash)
235
- query += TL::CodeSettings.serialize(code_settings)
236
-
237
- unless @connection_initialized
238
- query = init_connection(
239
- api_id: @api_id,
240
- device_model: @device_model,
241
- system_version: @system_version,
242
- app_version: @app_version,
243
- system_lang_code: @system_lang_code,
244
- lang_pack: @lang_pack,
245
- lang_code: @lang_code,
246
- query: query
247
- )
248
- query = invoke_with_layer(214, query)
249
- @connection_initialized = true
250
- end
251
-
252
- rpc_call(query)
139
+ def update_user(user_id:, access_hash: nil)
140
+ @user_id = user_id
141
+ @access_hash = access_hash
253
142
  end
254
143
  end
255
144
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'aes_ige/aes_ige'
4
+ require 'aes_ige'
5
5
  rescue LoadError => e
6
6
  warn "Failed to load AES-IGE C extension: #{e.message}"
7
7
  warn 'Run: cd ext/aes_ige && ruby extconf.rb && make'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'factorization/factorization'
4
+ require 'factorization'
5
5
  rescue LoadError => e
6
6
  warn "Failed to load Factorization C extension: #{e.message}"
7
7
  warn 'Run: cd ext/factorization && ruby extconf.rb && make'
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module MessageId
5
+ module_function
6
+
7
+ def generate(time_offset: 0)
8
+ time = Time.now.to_f + time_offset
9
+ msg_id = (time * (2**32)).to_i
10
+ (msg_id / 4) * 4
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/help/get_config'
4
+ require_relative '../type/rpc/help/config'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class GetConfig
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call
14
+ query = build_query
15
+
16
+ @client.rpc.call(query)
17
+ end
18
+
19
+ def call_sync
20
+ query = build_query
21
+
22
+ response = @client.rpc.call_sync(query)
23
+
24
+ Type::RPC::Help::Config.parse(response)
25
+ end
26
+
27
+ private
28
+
29
+ def build_query
30
+ Type::RPC::Help::GetConfig.build
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/contacts/get_contacts'
4
+ require_relative '../type/rpc/contacts/contacts'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class GetContacts
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(hash: 0)
14
+ raise 'Auth key not generated' unless @client.auth_key
15
+
16
+ query = Type::RPC::Contacts::GetContacts.build(hash: hash)
17
+ @client.rpc.call(query)
18
+ end
19
+
20
+ def call_sync(hash: 0)
21
+ raise 'Auth key not generated' unless @client.auth_key
22
+
23
+ query = Type::RPC::Contacts::GetContacts.build(hash: hash)
24
+ response = @client.rpc.call_sync(query)
25
+ Type::RPC::Contacts::Contacts.parse(response)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/updates/get_difference'
4
+ require_relative '../type/rpc/updates/difference'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class GetUpdatesDifference
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call(pts:, date:, qts:, pts_limit: 100, qts_limit: 100, pts_total_limit: nil)
14
+ raise 'Auth key not generated' unless @client.auth_key
15
+ raise ArgumentError, 'pts is required' if pts.nil?
16
+ raise ArgumentError, 'date is required' if date.nil?
17
+ raise ArgumentError, 'qts is required' if qts.nil?
18
+
19
+ query = Type::RPC::Updates::GetDifference.build(
20
+ pts: pts,
21
+ pts_limit: pts_limit,
22
+ pts_total_limit: pts_total_limit,
23
+ date: date,
24
+ qts: qts,
25
+ qts_limit: qts_limit
26
+ )
27
+
28
+ @client.rpc.call(query)
29
+ end
30
+
31
+ def call_sync(pts:, date:, qts:, pts_limit: 100, qts_limit: 100, pts_total_limit: nil)
32
+ raise 'Auth key not generated' unless @client.auth_key
33
+ raise ArgumentError, 'pts is required' if pts.nil?
34
+ raise ArgumentError, 'date is required' if date.nil?
35
+ raise ArgumentError, 'qts is required' if qts.nil?
36
+
37
+ query = Type::RPC::Updates::GetDifference.build(
38
+ pts: pts,
39
+ pts_limit: pts_limit,
40
+ pts_total_limit: pts_total_limit,
41
+ date: date,
42
+ qts: qts,
43
+ qts_limit: qts_limit
44
+ )
45
+
46
+ response = @client.rpc.call_sync(query)
47
+ Type::RPC::Updates::Difference.parse(response)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/updates/get_state'
4
+ require_relative '../type/rpc/updates/state'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class GetUpdatesState
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call
14
+ raise 'Auth key not generated' unless @client.auth_key
15
+
16
+ query = Type::RPC::Updates::GetState.build
17
+ @client.rpc.call(query)
18
+ end
19
+
20
+ def call_sync
21
+ raise 'Auth key not generated' unless @client.auth_key
22
+
23
+ query = Type::RPC::Updates::GetState.build
24
+ response = @client.rpc.call_sync(query)
25
+ Type::RPC::Updates::State.parse(response)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/users/get_users'
4
+ require_relative '../type/rpc/users/users'
5
+
6
+ module MTProto
7
+ module RPC
8
+ class GetUsers
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def call
14
+ raise 'Auth key not generated' unless @client.auth_key
15
+
16
+ query = Type::RPC::Users::GetUsers.build
17
+ @client.rpc.call(query)
18
+ end
19
+
20
+ def call_sync
21
+ raise 'Auth key not generated' unless @client.auth_key
22
+
23
+ query = Type::RPC::Users::GetUsers.build
24
+ response = @client.rpc.call_sync(query)
25
+ Type::RPC::Users::Users.parse(response)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/ping'
4
+ require_relative '../type/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 = Type::RPC::Ping.build(ping_id)
16
+
17
+ @client.rpc.call(body)
18
+ end
19
+
20
+ def call_sync(ping_id = nil)
21
+ ping_id ||= rand(2**63)
22
+ body = Type::RPC::Ping.build(ping_id)
23
+
24
+ response_body = @client.rpc.call_sync(body)
25
+ pong = Type::RPC::Pong.parse(response_body)
26
+
27
+ raise PingMismatchError unless pong[:ping_id] == ping_id
28
+
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/auth/send_code'
4
+ require_relative '../type/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
+ query = build_query(phone_number, code_settings)
15
+
16
+ @client.rpc.call(query)
17
+ end
18
+
19
+ def call_sync(phone_number, code_settings: {})
20
+ query = build_query(phone_number, code_settings)
21
+
22
+ response = @client.rpc.call_sync(query)
23
+
24
+ Type::RPC::Auth::SentCode.parse(response)
25
+ end
26
+
27
+ private
28
+
29
+ def build_query(phone_number, code_settings)
30
+ raise ArgumentError, 'phone_number is required' if phone_number.nil? || phone_number.empty?
31
+
32
+ Type::RPC::Auth::SendCode.build(
33
+ phone_number: phone_number,
34
+ api_id: @client.api_id,
35
+ api_hash: @client.api_hash,
36
+ code_settings: code_settings
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/messages/send_message'
4
+ require_relative '../type/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 = Type::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
+ @client.rpc.call(query)
27
+ end
28
+
29
+ def call_sync(user_id:, access_hash:, message:, random_id: nil)
30
+ raise 'Auth key not generated' unless @client.auth_key
31
+ raise ArgumentError, 'user_id is required' if user_id.nil?
32
+ raise ArgumentError, 'access_hash is required' if access_hash.nil?
33
+ raise ArgumentError, 'message is required and cannot be empty' if message.nil? || message.empty?
34
+
35
+ query = Type::RPC::Messages::SendMessage.build(
36
+ user_id: user_id,
37
+ access_hash: access_hash,
38
+ message: message,
39
+ random_id: random_id
40
+ )
41
+
42
+ response = @client.rpc.call_sync(query)
43
+ Type::RPC::Messages::Updates.parse(response)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../type/rpc/auth/sign_in'
4
+ require_relative '../type/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
+ query = build_query(phone_number, phone_code_hash, phone_code)
15
+
16
+ @client.rpc.call(query)
17
+ end
18
+
19
+ def call_sync(phone_number:, phone_code_hash:, phone_code:)
20
+ query = build_query(phone_number, phone_code_hash, phone_code)
21
+
22
+ response = @client.rpc.call_sync(query)
23
+
24
+ result = Type::RPC::Auth::Authorization.parse(response)
25
+
26
+ if result[:authorization] && result[:user_id]
27
+ @client.update_user(user_id: result[:user_id], access_hash: result[:access_hash])
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ private
34
+
35
+ def build_query(phone_number, phone_code_hash, phone_code)
36
+ raise ArgumentError, 'phone_number is required' if phone_number.nil? || phone_number.empty?
37
+ raise ArgumentError, 'phone_code_hash is required' if phone_code_hash.nil? || phone_code_hash.empty?
38
+ raise ArgumentError, 'phone_code is required' if phone_code.nil? || phone_code.empty?
39
+
40
+ Type::RPC::Auth::SignIn.build(
41
+ phone_number: phone_number,
42
+ phone_code_hash: phone_code_hash,
43
+ phone_code: phone_code
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end