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.
- checksums.yaml +4 -4
- data/data/tl-schema.json +42686 -0
- data/ext/aes_ige/extconf.rb +3 -9
- data/ext/factorization/extconf.rb +2 -0
- data/lib/mtproto/auth_key_generator.rb +68 -105
- data/lib/mtproto/binary.rb +21 -0
- data/lib/mtproto/client/api/check_password.rb +41 -0
- data/lib/mtproto/client/api/export_login_token.rb +27 -0
- data/lib/mtproto/client/api/get_dialogs.rb +21 -0
- data/lib/mtproto/client/api/get_history.rb +20 -0
- data/lib/mtproto/client/api/get_updates_difference.rb +21 -0
- data/lib/mtproto/client/api/get_updates_state.rb +14 -0
- data/lib/mtproto/client/api/get_users.rb +14 -0
- data/lib/mtproto/client/api/import_login_token.rb +23 -0
- data/lib/mtproto/client/api/send_code.rb +21 -0
- data/lib/mtproto/client/api/sign_in.rb +27 -0
- data/lib/mtproto/client/api.rb +36 -0
- data/lib/mtproto/client/rpc/response.rb +63 -0
- data/lib/mtproto/client/rpc.rb +60 -127
- data/lib/mtproto/client.rb +143 -32
- data/lib/mtproto/crypto/dh_key_exchange.rb +1 -2
- data/lib/mtproto/crypto/dh_validator.rb +17 -19
- data/lib/mtproto/crypto/factorization.rb +1 -1
- data/lib/mtproto/crypto/rsa_key.rb +2 -2
- data/lib/mtproto/crypto/srp.rb +117 -0
- data/lib/mtproto/delegate_methods.rb +11 -0
- data/lib/mtproto/errors.rb +8 -0
- data/lib/mtproto/message/message.rb +85 -0
- data/lib/mtproto/session.rb +1 -1
- data/lib/mtproto/tl/constructor_names.rb +2271 -0
- data/lib/mtproto/tl/constructors.rb +99 -0
- data/lib/mtproto/tl/object.rb +25 -0
- data/lib/mtproto/tl/objects/account_password.rb +69 -0
- data/lib/mtproto/tl/objects/authorization.rb +70 -0
- data/lib/mtproto/tl/objects/check_password.rb +43 -0
- data/lib/mtproto/tl/objects/client_dh_inner_data.rb +45 -0
- data/lib/mtproto/tl/objects/dh_gen_response.rb +46 -0
- data/lib/mtproto/tl/objects/dialogs.rb +453 -0
- data/lib/mtproto/tl/objects/export_login_token.rb +48 -0
- data/lib/mtproto/tl/objects/get_config.rb +13 -0
- data/lib/mtproto/tl/objects/get_dialogs.rb +51 -0
- data/lib/mtproto/tl/objects/get_difference.rb +34 -0
- data/lib/mtproto/tl/objects/get_history.rb +49 -0
- data/lib/mtproto/tl/objects/get_password.rb +13 -0
- data/lib/mtproto/tl/objects/get_state.rb +13 -0
- data/lib/mtproto/tl/objects/get_users.rb +16 -0
- data/lib/mtproto/{type → tl/objects}/gzip_packed.rb +6 -6
- data/lib/mtproto/tl/objects/help_config.rb +76 -0
- data/lib/mtproto/tl/objects/import_login_token.rb +37 -0
- data/lib/mtproto/tl/objects/init_connection.rb +57 -0
- data/lib/mtproto/tl/objects/invoke_with_layer.rb +20 -0
- data/lib/mtproto/tl/objects/login_token.rb +78 -0
- data/lib/mtproto/{type → tl/objects}/message.rb +3 -3
- data/lib/mtproto/tl/objects/messages.rb +162 -0
- data/lib/mtproto/{type → tl/objects}/msg_container.rb +1 -3
- data/lib/mtproto/{type → tl/objects}/new_session_created.rb +1 -3
- data/lib/mtproto/tl/objects/pq_inner_data.rb +66 -0
- data/lib/mtproto/tl/objects/req_dh_params.rb +63 -0
- data/lib/mtproto/tl/objects/req_pq_multi.rb +21 -0
- data/lib/mtproto/tl/objects/res_pq.rb +73 -0
- data/lib/mtproto/{type → tl/objects}/rpc_error.rb +1 -4
- data/lib/mtproto/tl/objects/send_code.rb +47 -0
- data/lib/mtproto/tl/objects/sent_code.rb +79 -0
- data/lib/mtproto/tl/objects/server_dh_inner_data.rb +74 -0
- data/lib/mtproto/tl/objects/server_dh_params.rb +53 -0
- data/lib/mtproto/tl/objects/set_client_dh_params.rb +46 -0
- data/lib/mtproto/tl/objects/sign_in.rb +45 -0
- data/lib/mtproto/tl/objects/update.rb +77 -0
- data/lib/mtproto/tl/objects/update_short.rb +20 -0
- data/lib/mtproto/tl/objects/update_short_message.rb +65 -0
- data/lib/mtproto/tl/objects/updates_difference.rb +152 -0
- data/lib/mtproto/tl/objects/updates_state.rb +35 -0
- data/lib/mtproto/tl/objects/users.rb +83 -0
- data/lib/mtproto/tl/schema.rb +102 -0
- data/lib/mtproto/transport/abridged_packet_codec.rb +35 -12
- data/lib/mtproto/transport/connection.rb +23 -0
- data/lib/mtproto/transport/errors.rb +11 -0
- data/lib/mtproto/transport/packet.rb +19 -0
- data/lib/mtproto/transport/tcp_connection.rb +57 -46
- data/lib/mtproto/updates_poller.rb +37 -33
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +17 -27
- data/scripts/generate_constructors.rb +65 -0
- metadata +76 -61
- data/lib/mtproto/async/middleware/base.rb +0 -17
- data/lib/mtproto/async/middleware/flood_wait.rb +0 -42
- data/lib/mtproto/async/request.rb +0 -18
- data/lib/mtproto/async/request_queue.rb +0 -63
- data/lib/mtproto/async_client.rb +0 -201
- data/lib/mtproto/rpc/get_config.rb +0 -34
- data/lib/mtproto/rpc/get_contacts.rb +0 -29
- data/lib/mtproto/rpc/get_updates_difference.rb +0 -51
- data/lib/mtproto/rpc/get_updates_state.rb +0 -29
- data/lib/mtproto/rpc/get_users.rb +0 -29
- data/lib/mtproto/rpc/ping.rb +0 -33
- data/lib/mtproto/rpc/send_code.rb +0 -41
- data/lib/mtproto/rpc/send_message.rb +0 -47
- data/lib/mtproto/rpc/sign_in.rb +0 -48
- data/lib/mtproto/type/auth_key/dh_gen_response.rb +0 -37
- data/lib/mtproto/type/auth_key/req_dh_params.rb +0 -31
- data/lib/mtproto/type/auth_key/req_pq_multi.rb +0 -18
- data/lib/mtproto/type/auth_key/res_pq.rb +0 -62
- data/lib/mtproto/type/auth_key/server_dh_params.rb +0 -43
- data/lib/mtproto/type/auth_key/set_client_dh_params.rb +0 -25
- data/lib/mtproto/type/bad_msg_notification.rb +0 -46
- data/lib/mtproto/type/client_dh_inner_data.rb +0 -29
- data/lib/mtproto/type/code_settings.rb +0 -25
- data/lib/mtproto/type/config.rb +0 -124
- data/lib/mtproto/type/pq_inner_data.rb +0 -41
- data/lib/mtproto/type/rpc/auth/authorization.rb +0 -107
- data/lib/mtproto/type/rpc/auth/send_code.rb +0 -28
- data/lib/mtproto/type/rpc/auth/sent_code.rb +0 -36
- data/lib/mtproto/type/rpc/auth/sign_in.rb +0 -32
- data/lib/mtproto/type/rpc/contacts/contacts.rb +0 -155
- data/lib/mtproto/type/rpc/contacts/get_contacts.rb +0 -18
- data/lib/mtproto/type/rpc/help/config.rb +0 -35
- data/lib/mtproto/type/rpc/help/get_config.rb +0 -17
- data/lib/mtproto/type/rpc/init_connection.rb +0 -28
- data/lib/mtproto/type/rpc/invoke_with_layer.rb +0 -19
- data/lib/mtproto/type/rpc/messages/send_message.rb +0 -43
- data/lib/mtproto/type/rpc/messages/updates.rb +0 -87
- data/lib/mtproto/type/rpc/ping.rb +0 -18
- data/lib/mtproto/type/rpc/pong.rb +0 -46
- data/lib/mtproto/type/rpc/updates/difference.rb +0 -332
- data/lib/mtproto/type/rpc/updates/get_difference.rb +0 -42
- data/lib/mtproto/type/rpc/updates/get_state.rb +0 -17
- data/lib/mtproto/type/rpc/updates/state.rb +0 -59
- data/lib/mtproto/type/rpc/users/get_users.rb +0 -25
- data/lib/mtproto/type/rpc/users/users.rb +0 -99
- data/lib/mtproto/type/sent_code.rb +0 -128
- data/lib/mtproto/type/serializer.rb +0 -55
- data/lib/mtproto/type/server_dh_inner_data.rb +0 -85
data/lib/mtproto/async_client.rb
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'concurrent'
|
|
4
|
-
require_relative 'async/request'
|
|
5
|
-
require_relative 'async/request_queue'
|
|
6
|
-
require_relative 'async/middleware/base'
|
|
7
|
-
require_relative 'async/middleware/flood_wait'
|
|
8
|
-
|
|
9
|
-
module MTProto
|
|
10
|
-
class AsyncClient
|
|
11
|
-
attr_reader :client
|
|
12
|
-
|
|
13
|
-
def initialize(client)
|
|
14
|
-
@client = client
|
|
15
|
-
@queue = Async::RequestQueue.new
|
|
16
|
-
@middleware_classes = []
|
|
17
|
-
@pending_requests = Concurrent::Hash.new
|
|
18
|
-
@timeout_threads = Concurrent::Hash.new
|
|
19
|
-
@receiver_thread = nil
|
|
20
|
-
@running = Concurrent::AtomicBoolean.new(false)
|
|
21
|
-
|
|
22
|
-
use(Async::Middleware::FloodWait)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def use(middleware_class, **options)
|
|
26
|
-
@middleware_classes << [middleware_class, options]
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def call(body, metadata: {})
|
|
31
|
-
ensure_receiver_running!
|
|
32
|
-
|
|
33
|
-
request = Async::Request.new(body: body, metadata: metadata)
|
|
34
|
-
|
|
35
|
-
@queue.call(key: request.key) do
|
|
36
|
-
send_and_wrap = lambda do |retries = 0|
|
|
37
|
-
base_future = send_request(request.body)
|
|
38
|
-
|
|
39
|
-
retry_fn = lambda do
|
|
40
|
-
send_and_wrap.call(retries + 1)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
build_middleware_stack.call(request, base_future, retry_fn, retries)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
send_and_wrap.call(0)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def shutdown
|
|
51
|
-
@running.make_false
|
|
52
|
-
|
|
53
|
-
@client.disconnect! if @client.connected?
|
|
54
|
-
|
|
55
|
-
if @receiver_thread
|
|
56
|
-
@receiver_thread.join(2)
|
|
57
|
-
@receiver_thread.kill if @receiver_thread.alive?
|
|
58
|
-
@receiver_thread = nil
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
@timeout_threads.each_value(&:kill)
|
|
62
|
-
@timeout_threads.clear
|
|
63
|
-
|
|
64
|
-
@pending_requests.each_value do |future|
|
|
65
|
-
future.reject(MTProto::Transport::ConnectionError.new('Client shutting down')) unless future.resolved?
|
|
66
|
-
end
|
|
67
|
-
@pending_requests.clear
|
|
68
|
-
|
|
69
|
-
@queue.shutdown
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
def send_request(body)
|
|
75
|
-
msg_id = @client.rpc.call(body)
|
|
76
|
-
future = Concurrent::Promises.resolvable_future
|
|
77
|
-
@pending_requests[msg_id] = future
|
|
78
|
-
|
|
79
|
-
timeout_thread = Thread.new do
|
|
80
|
-
sleep @client.timeout
|
|
81
|
-
if @pending_requests.delete(msg_id)
|
|
82
|
-
future.reject(MTProto::Transport::ConnectionError.new('RPC timeout'))
|
|
83
|
-
end
|
|
84
|
-
@timeout_threads.delete(msg_id)
|
|
85
|
-
end
|
|
86
|
-
@timeout_threads[msg_id] = timeout_thread
|
|
87
|
-
|
|
88
|
-
future.on_fulfillment! do
|
|
89
|
-
timeout_thread.kill
|
|
90
|
-
@timeout_threads.delete(msg_id)
|
|
91
|
-
end
|
|
92
|
-
future.on_rejection! do
|
|
93
|
-
timeout_thread.kill
|
|
94
|
-
@timeout_threads.delete(msg_id)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
future
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def ensure_receiver_running!
|
|
101
|
-
return if @running.true?
|
|
102
|
-
|
|
103
|
-
@running.make_true
|
|
104
|
-
@receiver_thread = Thread.new { receiver_loop }
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def receiver_loop
|
|
108
|
-
loop do
|
|
109
|
-
break unless @running.true?
|
|
110
|
-
|
|
111
|
-
begin
|
|
112
|
-
response_data = @client.connection.recv(timeout: 1)
|
|
113
|
-
|
|
114
|
-
decrypted = EncryptedMessage.decrypt(
|
|
115
|
-
auth_key: @client.auth_key,
|
|
116
|
-
encrypted_message_data: response_data,
|
|
117
|
-
sender: :server
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
process_message(decrypted[:body])
|
|
121
|
-
rescue MTProto::Transport::ConnectionError => e
|
|
122
|
-
next if e.message == 'Receive timeout' && @running.true?
|
|
123
|
-
break
|
|
124
|
-
rescue IOError => e
|
|
125
|
-
break
|
|
126
|
-
rescue => e
|
|
127
|
-
warn "AsyncClient receiver error: #{e.class}: #{e.message}"
|
|
128
|
-
warn e.backtrace.join("\n")
|
|
129
|
-
break
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def process_message(response_body)
|
|
135
|
-
constructor = response_body[0, 4].unpack1('L<')
|
|
136
|
-
|
|
137
|
-
case constructor
|
|
138
|
-
when Client::RPC::CONSTRUCTOR_BAD_SERVER_SALT
|
|
139
|
-
handle_bad_server_salt(response_body)
|
|
140
|
-
when Type::NewSessionCreated::CONSTRUCTOR
|
|
141
|
-
handle_new_session(response_body)
|
|
142
|
-
when Type::MsgContainer::CONSTRUCTOR
|
|
143
|
-
handle_container(response_body)
|
|
144
|
-
when Client::RPC::CONSTRUCTOR_RPC_RESULT
|
|
145
|
-
handle_rpc_result(response_body)
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def handle_bad_server_salt(response_body)
|
|
150
|
-
offset = 4
|
|
151
|
-
offset += 8
|
|
152
|
-
offset += 4
|
|
153
|
-
offset += 4
|
|
154
|
-
new_server_salt = response_body[offset, 8].unpack1('Q<')
|
|
155
|
-
|
|
156
|
-
@client.instance_variable_set(:@server_salt, new_server_salt)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def handle_new_session(response_body)
|
|
160
|
-
session_info = Type::NewSessionCreated.deserialize(response_body)
|
|
161
|
-
@client.instance_variable_set(:@server_salt, session_info.server_salt)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def handle_container(response_body)
|
|
165
|
-
container = Type::MsgContainer.deserialize(response_body)
|
|
166
|
-
|
|
167
|
-
container.messages.each do |msg|
|
|
168
|
-
process_message(msg[:body])
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def handle_rpc_result(response_body)
|
|
173
|
-
offset = 4
|
|
174
|
-
req_msg_id = response_body[offset, 8].unpack1('Q<')
|
|
175
|
-
offset += 8
|
|
176
|
-
result = response_body[offset..]
|
|
177
|
-
|
|
178
|
-
future = @pending_requests.delete(req_msg_id)
|
|
179
|
-
return unless future # No pending request for this msg_id
|
|
180
|
-
|
|
181
|
-
# Check if result is an RPC error
|
|
182
|
-
result_constructor = result[0, 4].unpack1('L<')
|
|
183
|
-
if result_constructor == Type::RpcError::CONSTRUCTOR
|
|
184
|
-
error = Type::RpcError.deserialize(result)
|
|
185
|
-
future.reject(MTProto::RpcError.new(error.error_code, error.error_message))
|
|
186
|
-
else
|
|
187
|
-
future.fulfill(result)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def build_middleware_stack
|
|
192
|
-
# Middleware now wraps Futures and can trigger retries
|
|
193
|
-
# The base "app" just returns the Future as-is
|
|
194
|
-
app = ->(request, future, retry_fn, retries) { future }
|
|
195
|
-
|
|
196
|
-
@middleware_classes.reverse.reduce(app) do |stack, (middleware_class, options)|
|
|
197
|
-
middleware_class.new(stack, **options)
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
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
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
|
@@ -1,51 +0,0 @@
|
|
|
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
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
data/lib/mtproto/rpc/ping.rb
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
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
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
|
@@ -1,47 +0,0 @@
|
|
|
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
|
data/lib/mtproto/rpc/sign_in.rb
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
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
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MTProto
|
|
4
|
-
module Type
|
|
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
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../serializer'
|
|
4
|
-
|
|
5
|
-
module MTProto
|
|
6
|
-
module Type
|
|
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
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MTProto
|
|
4
|
-
module Type
|
|
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
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MTProto
|
|
4
|
-
module Type
|
|
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
|