Class: MTProto::Client::RPC

Inherits:
Object
  • Object
show all
Defined in:
lib/mtproto/client/rpc.rb,
lib/mtproto/client/rpc/response.rb

Defined Under Namespace

Classes: Response

Constant Summary collapse

MSGS_ACK =
0x62d6b459
VECTOR =
0x1cb5c415
PING_DELAY_DISCONNECT =
0xf3427b8c

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ RPC

Returns a new instance of RPC.



16
17
18
19
# File 'lib/mtproto/client/rpc.rb', line 16

def initialize(client)
  @client = client
  @pending_requests = {}
end

Instance Attribute Details

#pending_requestsObject (readonly)

Returns the value of attribute pending_requests.



14
15
16
# File 'lib/mtproto/client/rpc.rb', line 14

def pending_requests
  @pending_requests
end

Class Method Details

.ack_body(msg_ids) ⇒ Object

Serialized msgs_ack body (no envelope) — extracted for unit testing.



92
93
94
95
# File 'lib/mtproto/client/rpc.rb', line 92

def self.ack_body(msg_ids)
  [MSGS_ACK].pack('L<') + [VECTOR].pack('L<') + [msg_ids.length].pack('L<') +
    msg_ids.map { |id| [id].pack('Q<') }.join
end

.ping_body(ping_id, disconnect_delay) ⇒ Object

Serialized ping_delay_disconnect body (no envelope) — extracted for testing.



98
99
100
# File 'lib/mtproto/client/rpc.rb', line 98

def self.ping_body(ping_id, disconnect_delay)
  [PING_DELAY_DISCONNECT].pack('L<') + [ping_id].pack('Q<') + [disconnect_delay].pack('l<')
end

Instance Method Details

#call(request, response_class) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/mtproto/client/rpc.rb', line 21

def call(request, response_class)
  raise 'Mainloop not running' unless @client.mainloop_running?
  raise 'Auth key not generated' unless @client.auth_key?
  raise 'Session not initialized' unless @client.session

  body = serialize_request(request)
  msg_id = send_encrypted(body)
  response = Response.new(msg_id, response_class, body)
  @pending_requests[msg_id] = response

  response
end

#handle_bad_server_salt(response_body, client) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/mtproto/client/rpc.rb', line 34

def handle_bad_server_salt(response_body, client)
  bad_msg_id = response_body[4, 8].unpack1('Q<')
  new_server_salt = response_body[20, 8].unpack1('Q<')

  client.update_server_salt(new_server_salt)

  response = @pending_requests.delete(bad_msg_id)
  return unless response

  new_msg_id = send_encrypted(response.body_bytes)
  response.instance_variable_set(:@msg_id, new_msg_id)
  @pending_requests[new_msg_id] = response
end

#handle_rpc_result(response_body) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/mtproto/client/rpc.rb', line 48

def handle_rpc_result(response_body)
  req_msg_id = response_body[4, 8].unpack1('Q<')
  result = response_body[12..]

  result_constructor = result[0, 4].unpack1('L<')
  if result_constructor == TL::Constructors::GZIP_PACKED
    result = TL::GzipPacked.unpack(result)
    result_constructor = result[0, 4].unpack1('L<')
  end

  response = @pending_requests.delete(req_msg_id)
  return unless response

  if result_constructor == TL::Constructors::RPC_ERROR
    error = TL::RpcError.deserialize(result)
    response.signal_error(RpcError.new(error.error_code, error.error_message))
  else
    response.signal(result)
  end
end

#send_ack(msg_ids) ⇒ Object

Acknowledge received server messages (msgs_ack). Without this the server keeps the unacknowledged messages, resends them, and drops the connection after a fixed window. Fire-and-forget — no rpc_result is expected.



77
78
79
80
81
# File 'lib/mtproto/client/rpc.rb', line 77

def send_ack(msg_ids)
  return if msg_ids.nil? || msg_ids.empty?

  send_encrypted(self.class.ack_body(msg_ids), content_related: false)
end

#send_ping(disconnect_delay = 75) ⇒ Object

Keepalive: ping_delay_disconnect asks the server to disconnect us only if we stop pinging within disconnect_delay seconds. Fire-and-forget (the pong is ignored by process_message).



86
87
88
89
# File 'lib/mtproto/client/rpc.rb', line 86

def send_ping(disconnect_delay = 75)
  send_encrypted(self.class.ping_body(SecureRandom.random_number(2**63), disconnect_delay),
    content_related: false)
end

#signal_all_error(error) ⇒ Object



69
70
71
72
# File 'lib/mtproto/client/rpc.rb', line 69

def signal_all_error(error)
  @pending_requests.each_value { |response| response.signal_error(error) }
  @pending_requests.clear
end