mtproto 0.0.11 → 0.0.12
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/lib/mtproto/tl/objects/updates_difference.rb +41 -60
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +0 -1
- metadata +1 -2
- data/lib/mtproto/updates_poller.rb +0 -115
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f989bd9b9704f0fbb46d0c538a2f0dfea8e1c9a387830e875ffecc44b0ad872a
|
|
4
|
+
data.tar.gz: acd4865006b5a95364fd5cfaf4db71366137571faeedd1623edf20bae0e3bf9e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20d07a1e57c051d9ca1a3617191e7222b7bd2a6674546473c00130a5cec94ef6dae02c0ce2f0c1cc2df5fd924aadeea3d0405a39f931ec345ba87575bb0183ad
|
|
7
|
+
data.tar.gz: 91f8f836e61180a4fb41b1ca9a56807449e809acf794b7b01fbf7c3bd34ae7ebb4d18c704fc1c53045777e49bc73fb0f604fc2b20c59f54c41afe5a12ff689ff
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../schema'
|
|
4
|
+
|
|
3
5
|
module MTProto
|
|
4
6
|
module TL
|
|
5
7
|
class UpdatesDifference
|
|
6
|
-
attr_reader :type, :date, :seq, :new_messages, :
|
|
8
|
+
attr_reader :type, :date, :seq, :new_messages, :pts, :state
|
|
9
|
+
|
|
10
|
+
SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
|
|
7
11
|
|
|
8
|
-
def initialize(type:, date: nil, seq: nil, new_messages: [],
|
|
12
|
+
def initialize(type:, date: nil, seq: nil, new_messages: [], pts: nil, state: nil)
|
|
9
13
|
@type = type
|
|
10
14
|
@date = date
|
|
11
15
|
@seq = seq
|
|
12
16
|
@new_messages = new_messages
|
|
13
|
-
@users = users
|
|
14
17
|
@pts = pts
|
|
15
18
|
@state = state
|
|
16
19
|
end
|
|
17
20
|
|
|
21
|
+
def self.schema
|
|
22
|
+
@schema ||= Schema.new(SCHEMA_PATH)
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
def self.parse(data)
|
|
19
26
|
constructor = data[0, 4].unpack1('L<')
|
|
20
27
|
|
|
@@ -31,6 +38,10 @@ module MTProto
|
|
|
31
38
|
end
|
|
32
39
|
end
|
|
33
40
|
|
|
41
|
+
MESSAGE_CONSTRUCTOR = Constructors::MESSAGE
|
|
42
|
+
MESSAGE_SERVICE = 0x7a800e0a
|
|
43
|
+
MESSAGE_EMPTY = 0x90a6ca84
|
|
44
|
+
|
|
34
45
|
class << self
|
|
35
46
|
private
|
|
36
47
|
|
|
@@ -42,19 +53,21 @@ module MTProto
|
|
|
42
53
|
)
|
|
43
54
|
end
|
|
44
55
|
|
|
56
|
+
# updates.difference: new_messages new_encrypted_messages other_updates chats users state
|
|
45
57
|
def parse_difference(data, constructor)
|
|
46
58
|
offset = 4
|
|
47
59
|
|
|
48
60
|
messages, offset = parse_messages_vector(data, offset)
|
|
49
|
-
offset = skip_vector(data, offset) #
|
|
50
|
-
offset = skip_vector(data, offset) # other_updates
|
|
51
|
-
offset = skip_vector(data, offset) # chats
|
|
52
|
-
|
|
61
|
+
offset = schema.skip_vector(data, offset) # new_encrypted_messages
|
|
62
|
+
offset = schema.skip_vector(data, offset) # other_updates
|
|
63
|
+
offset = schema.skip_vector(data, offset) # chats
|
|
64
|
+
offset = schema.skip_vector(data, offset) # users
|
|
65
|
+
state = UpdatesState.parse(data[offset..])
|
|
53
66
|
|
|
54
67
|
new(
|
|
55
68
|
type: constructor == Constructors::UPDATES_DIFFERENCE ? :difference : :slice,
|
|
56
69
|
new_messages: messages,
|
|
57
|
-
|
|
70
|
+
state: state
|
|
58
71
|
)
|
|
59
72
|
end
|
|
60
73
|
|
|
@@ -65,71 +78,39 @@ module MTProto
|
|
|
65
78
|
|
|
66
79
|
messages = []
|
|
67
80
|
count.times do
|
|
68
|
-
|
|
81
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
82
|
+
msg = extract_message(data, offset, constructor)
|
|
69
83
|
messages << msg if msg
|
|
84
|
+
offset = schema.skip(data, offset)
|
|
70
85
|
end
|
|
71
86
|
[messages, offset]
|
|
72
87
|
end
|
|
73
88
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
offset += 4
|
|
77
|
-
|
|
78
|
-
return [nil, offset] unless msg_constructor == Constructors::MESSAGE
|
|
89
|
+
def extract_message(data, offset, constructor)
|
|
90
|
+
return unless constructor == MESSAGE_CONSTRUCTOR
|
|
79
91
|
|
|
92
|
+
offset += 4 # constructor
|
|
80
93
|
flags = data[offset, 4].unpack1('L<')
|
|
81
94
|
offset += 4
|
|
82
|
-
|
|
95
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
83
96
|
offset += 4
|
|
84
|
-
|
|
85
|
-
offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
|
|
86
|
-
offset += 12 # peer_id
|
|
87
|
-
offset += 8 if flags.anybits?(1 << 11) # via_bot_id
|
|
88
|
-
date = data[offset, 4].unpack1('L<')
|
|
97
|
+
id = data[offset, 4].unpack1('l<')
|
|
89
98
|
offset += 4
|
|
90
|
-
message_text, offset = read_tl_string(data, offset)
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
|
|
101
|
+
offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
|
|
102
|
+
offset = schema.skip(data, offset) # peer_id (Peer, always present)
|
|
103
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
|
|
104
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
|
|
105
|
+
offset += 8 if flags.anybits?(1 << 11) # via_bot_id
|
|
106
|
+
offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
|
|
107
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
offset += 4 # vector constructor
|
|
99
|
-
count = data[offset, 4].unpack1('L<')
|
|
109
|
+
date = data[offset, 4].unpack1('L<')
|
|
100
110
|
offset += 4
|
|
111
|
+
message_text, = read_tl_string(data, offset)
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
count.times do
|
|
104
|
-
user_constructor = data[offset, 4].unpack1('L<')
|
|
105
|
-
offset += 4
|
|
106
|
-
next unless user_constructor == Constructors::USER
|
|
107
|
-
|
|
108
|
-
flags = data[offset, 4].unpack1('L<')
|
|
109
|
-
offset += 4
|
|
110
|
-
offset += 4 # flags2
|
|
111
|
-
user_id = data[offset, 8].unpack1('Q<')
|
|
112
|
-
offset += 8
|
|
113
|
-
|
|
114
|
-
access_hash = nil
|
|
115
|
-
if flags.anybits?(1 << 0)
|
|
116
|
-
access_hash = data[offset, 8].unpack1('Q<')
|
|
117
|
-
offset += 8
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
users << { id: user_id, access_hash: access_hash }
|
|
121
|
-
rescue StandardError
|
|
122
|
-
break
|
|
123
|
-
end
|
|
124
|
-
[users, offset]
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def skip_vector(data, offset)
|
|
128
|
-
offset += 4 # vector constructor
|
|
129
|
-
count = data[offset, 4].unpack1('L<')
|
|
130
|
-
offset += 4
|
|
131
|
-
count.times { offset += 4 }
|
|
132
|
-
offset
|
|
113
|
+
{ id: id, date: date, message: message_text }
|
|
133
114
|
end
|
|
134
115
|
|
|
135
116
|
def read_tl_string(data, offset)
|
|
@@ -144,7 +125,7 @@ module MTProto
|
|
|
144
125
|
total = 1 + len
|
|
145
126
|
end
|
|
146
127
|
padding = (4 - (total % 4)) % 4
|
|
147
|
-
[data[offset + (total - len), len], offset + total + padding]
|
|
128
|
+
[data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
|
|
148
129
|
end
|
|
149
130
|
end
|
|
150
131
|
end
|
data/lib/mtproto/version.rb
CHANGED
data/lib/mtproto.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mtproto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Artem Levenkov
|
|
@@ -137,7 +137,6 @@ files:
|
|
|
137
137
|
- lib/mtproto/transport/errors.rb
|
|
138
138
|
- lib/mtproto/transport/packet.rb
|
|
139
139
|
- lib/mtproto/transport/tcp_connection.rb
|
|
140
|
-
- lib/mtproto/updates_poller.rb
|
|
141
140
|
- lib/mtproto/version.rb
|
|
142
141
|
- scripts/generate_constructors.rb
|
|
143
142
|
- tmp/.keep
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MTProto
|
|
4
|
-
class UpdatesPoller
|
|
5
|
-
attr_reader :state, :running
|
|
6
|
-
|
|
7
|
-
def initialize(client, poll_interval: 1.0)
|
|
8
|
-
@client = client
|
|
9
|
-
@poll_interval = poll_interval
|
|
10
|
-
@state = nil
|
|
11
|
-
@running = false
|
|
12
|
-
@on_message_callbacks = []
|
|
13
|
-
@on_update_callbacks = []
|
|
14
|
-
@users_cache = {}
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def on_message(&block)
|
|
18
|
-
@on_message_callbacks << block
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def on_update(&block)
|
|
22
|
-
@on_update_callbacks << block
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def start
|
|
26
|
-
raise 'Already running' if @running
|
|
27
|
-
raise 'Auth key not set' unless @client.auth_key?
|
|
28
|
-
|
|
29
|
-
@running = true
|
|
30
|
-
|
|
31
|
-
@state = call_with_retry { @client.api.get_updates_state }
|
|
32
|
-
|
|
33
|
-
poll_loop
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def stop
|
|
37
|
-
@running = false
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def poll_loop
|
|
43
|
-
while @running
|
|
44
|
-
begin
|
|
45
|
-
difference = call_with_retry do
|
|
46
|
-
@client.api.get_updates_difference(
|
|
47
|
-
pts: @state.pts,
|
|
48
|
-
date: @state.date,
|
|
49
|
-
qts: @state.qts
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
process_difference(difference)
|
|
54
|
-
|
|
55
|
-
if difference.state
|
|
56
|
-
@state = difference.state
|
|
57
|
-
elsif difference.type == :too_long
|
|
58
|
-
@state = call_with_retry { @client.api.get_updates_state }
|
|
59
|
-
elsif difference.date
|
|
60
|
-
@state.date = difference.date
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
sleep @poll_interval
|
|
64
|
-
rescue MTProto::RpcError => e
|
|
65
|
-
warn "RPC Error during polling: #{e.message}"
|
|
66
|
-
sleep @poll_interval
|
|
67
|
-
rescue StandardError => e
|
|
68
|
-
warn "Error during polling: #{e.class} - #{e.message}"
|
|
69
|
-
warn e.backtrace.first(5).join("\n")
|
|
70
|
-
sleep @poll_interval
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def call_with_retry(max_retries: 5)
|
|
76
|
-
retries = 0
|
|
77
|
-
begin
|
|
78
|
-
yield
|
|
79
|
-
rescue MTProto::UnexpectedConstructorError, RuntimeError
|
|
80
|
-
retries += 1
|
|
81
|
-
retry if retries < max_retries
|
|
82
|
-
raise
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def process_difference(difference)
|
|
87
|
-
case difference.type
|
|
88
|
-
when :empty
|
|
89
|
-
nil
|
|
90
|
-
when :difference, :slice, :short_message
|
|
91
|
-
difference.users&.each do |user|
|
|
92
|
-
@users_cache[user[:id]] = user[:access_hash] if user[:access_hash]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
if difference.new_messages && !difference.new_messages.empty?
|
|
96
|
-
difference.new_messages.each do |message|
|
|
97
|
-
process_message(message)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
@on_update_callbacks.each { |callback| callback.call(difference) }
|
|
102
|
-
when :too_long
|
|
103
|
-
puts "Update gap too long (pts=#{difference.pts}), refreshing state..."
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def process_message(message)
|
|
108
|
-
return unless message
|
|
109
|
-
|
|
110
|
-
message[:access_hash] = @users_cache[message[:user_id]] if message[:user_id] && @users_cache[message[:user_id]]
|
|
111
|
-
|
|
112
|
-
@on_message_callbacks.each { |callback| callback.call(message) }
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|