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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf09099f0d758aef9c9b4f31c1aa0d4f35fa0bcd25f5fd405dcededb4e72bd03
4
- data.tar.gz: f79bed71d80f9856024a05c2f6abc244bf6dcc41456611e10410b7cffe315c76
3
+ metadata.gz: f989bd9b9704f0fbb46d0c538a2f0dfea8e1c9a387830e875ffecc44b0ad872a
4
+ data.tar.gz: acd4865006b5a95364fd5cfaf4db71366137571faeedd1623edf20bae0e3bf9e
5
5
  SHA512:
6
- metadata.gz: 9749b7de5172cad33106451be16e1c232fcbfaa24ecc97a5979d973ac9ba80617f7e4bee318a8a3f89fdf2bbf9f8473d821885640c46f86907a49ddb2d8ff724
7
- data.tar.gz: 6aa58a6d2d873becd74cd037f39b237f5bc092a9cf541453000a85f3ee44406c93356fcf3818256f5c2a4f33b9e58a6d239b366d847b59a053fe05ce3a38279f
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, :users, :pts, :state
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: [], users: [], pts: nil, state: nil)
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) # encrypted_messages
50
- offset = skip_vector(data, offset) # other_updates
51
- offset = skip_vector(data, offset) # chats
52
- users, = parse_users_vector(data, offset)
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
- users: users
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
- msg, offset = parse_message(data, offset)
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 parse_message(data, offset)
75
- msg_constructor = data[offset, 4].unpack1('L<')
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
- id = data[offset, 4].unpack1('L<')
95
+ flags2 = data[offset, 4].unpack1('L<')
83
96
  offset += 4
84
- offset += 12 if flags.anybits?(1 << 8) # from_id
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
- [{ id: id, date: date, message: message_text, flags: flags }, offset]
93
- rescue StandardError
94
- [nil, offset]
95
- end
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
- def parse_users_vector(data, offset)
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
- users = []
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MTProto
4
- VERSION = '0.0.11'
4
+ VERSION = '0.0.12'
5
5
  end
data/lib/mtproto.rb CHANGED
@@ -33,7 +33,6 @@ require_relative 'mtproto/auth_key_generator'
33
33
  require_relative 'mtproto/session'
34
34
  require_relative 'mtproto/encrypted_message'
35
35
  require_relative 'mtproto/client'
36
- require_relative 'mtproto/updates_poller'
37
36
 
38
37
  module MTProto
39
38
  end
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.11
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