mtproto 0.0.15 → 0.0.17
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 +47158 -42684
- data/lib/mtproto/auth_key_generator.rb +2 -2
- data/lib/mtproto/client/api/export_authorization.rb +17 -0
- data/lib/mtproto/client/api/import_authorization.rb +23 -0
- data/lib/mtproto/client/api.rb +2 -0
- data/lib/mtproto/client.rb +1 -1
- data/lib/mtproto/file_downloader.rb +122 -0
- data/lib/mtproto/tl/constructor_names.rb +314 -109
- data/lib/mtproto/tl/constructors.rb +16 -8
- data/lib/mtproto/tl/objects/bot_command_scope.rb +81 -0
- data/lib/mtproto/tl/objects/channels_join_channel.rb +1 -1
- data/lib/mtproto/tl/objects/channels_update_username.rb +42 -0
- data/lib/mtproto/tl/objects/contacts.rb +1 -1
- data/lib/mtproto/tl/objects/dialogs.rb +15 -10
- data/lib/mtproto/tl/objects/export_authorization.rb +17 -0
- data/lib/mtproto/tl/objects/exported_authorization.rb +32 -0
- data/lib/mtproto/tl/objects/forward_messages.rb +5 -2
- data/lib/mtproto/tl/objects/get_bot_callback_answer.rb +67 -0
- data/lib/mtproto/tl/objects/get_bot_commands.rb +46 -0
- data/lib/mtproto/tl/objects/get_file.rb +10 -2
- data/lib/mtproto/tl/objects/import_authorization.rb +38 -0
- data/lib/mtproto/tl/objects/keyboard_button_callback.rb +50 -0
- data/lib/mtproto/tl/objects/message.rb +97 -21
- data/lib/mtproto/tl/objects/messages.rb +7 -74
- data/lib/mtproto/tl/objects/reply_inline_markup.rb +30 -0
- data/lib/mtproto/tl/objects/reset_bot_commands.rb +45 -0
- data/lib/mtproto/tl/objects/send_media.rb +76 -4
- data/lib/mtproto/tl/objects/send_message.rb +4 -2
- data/lib/mtproto/tl/objects/send_message_action.rb +48 -0
- data/lib/mtproto/tl/objects/set_bot_callback_answer.rb +54 -0
- data/lib/mtproto/tl/objects/set_bot_commands.rb +6 -3
- data/lib/mtproto/tl/objects/set_bot_guest_chat_result.rb +84 -0
- data/lib/mtproto/tl/objects/set_typing.rb +49 -0
- data/lib/mtproto/tl/objects/update_status.rb +24 -0
- data/lib/mtproto/tl/objects/updates.rb +117 -0
- data/lib/mtproto/tl/objects/updates_difference.rb +16 -119
- data/lib/mtproto/tl/reader.rb +188 -0
- data/lib/mtproto/transport/abridged_packet_codec.rb +5 -1
- data/lib/mtproto/unencrypted_message.rb +39 -0
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +4 -1
- data/scripts/gen_constructor_names.rb +72 -0
- data/scripts/tl_to_json.rb +72 -0
- data/scripts/verify_ids.rb +33 -0
- metadata +25 -2
- data/lib/mtproto/message/message.rb +0 -85
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Regenerate lib/mtproto/tl/constructor_names.rb (the id -> name map used only for
|
|
5
|
+
# logging and error text) from the LOCAL data/tl-schema.json (current layer),
|
|
6
|
+
# preserving the stable MTProto transport-layer names the API schema lacks.
|
|
7
|
+
#
|
|
8
|
+
# Why not scripts/generate_constructors.rb: that one fetches
|
|
9
|
+
# core.telegram.org/schema/json, which is STALE (~layer 214, no guest mode), and
|
|
10
|
+
# overwrites constructors.rb. This one is offline and uses the migrated schema.
|
|
11
|
+
#
|
|
12
|
+
# Run: ruby scripts/gen_constructor_names.rb
|
|
13
|
+
|
|
14
|
+
require 'json'
|
|
15
|
+
|
|
16
|
+
root = File.expand_path('..', __dir__)
|
|
17
|
+
schema = JSON.parse(File.read(File.join(root, 'data/tl-schema.json')))
|
|
18
|
+
names_file = File.join(root, 'lib/mtproto/tl/constructor_names.rb')
|
|
19
|
+
|
|
20
|
+
api = {} # unsigned id => predicate/method name
|
|
21
|
+
api_names = {} # name => true
|
|
22
|
+
schema.fetch('constructors').each do |c|
|
|
23
|
+
api[c['id'].to_i & 0xffffffff] = c['predicate']
|
|
24
|
+
api_names[c['predicate']] = true
|
|
25
|
+
end
|
|
26
|
+
schema.fetch('methods').each do |m|
|
|
27
|
+
api[m['id'].to_i & 0xffffffff] = m['method']
|
|
28
|
+
api_names[m['method']] = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Preserve MTProto transport-layer names (rpc_result, msgs_ack, dh_gen_*, ...) that
|
|
32
|
+
# the API schema does not carry. An existing id absent from the API schema is
|
|
33
|
+
# transport-core UNLESS its name is an API predicate/method — that marks a pre-bump
|
|
34
|
+
# constructor whose id changed, so drop it (the current id is already in `api`).
|
|
35
|
+
core = {}
|
|
36
|
+
File.foreach(names_file) do |line|
|
|
37
|
+
next unless line =~ /0x([0-9a-fA-F]{8})\s*=>\s*'([^']+)'/
|
|
38
|
+
|
|
39
|
+
id = Regexp.last_match(1).to_i(16)
|
|
40
|
+
name = Regexp.last_match(2)
|
|
41
|
+
next if api.key?(id) || api_names.key?(name)
|
|
42
|
+
|
|
43
|
+
core[id] = name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
entries = api.merge(core).sort.map { |id, name| format(" 0x%08x => '%s'", id, name) }
|
|
47
|
+
|
|
48
|
+
out = []
|
|
49
|
+
out << '# frozen_string_literal: true'
|
|
50
|
+
out << ''
|
|
51
|
+
out << '# rubocop:disable Metrics/ModuleLength'
|
|
52
|
+
out << ''
|
|
53
|
+
out << '# Auto-generated id -> constructor/method name map (logging + error messages).'
|
|
54
|
+
out << '# API names from data/tl-schema.json (current layer); MTProto transport-layer'
|
|
55
|
+
out << '# names preserved from the prior map.'
|
|
56
|
+
out << '# Run: ruby scripts/gen_constructor_names.rb'
|
|
57
|
+
out << ''
|
|
58
|
+
out << "require_relative 'constructors'"
|
|
59
|
+
out << ''
|
|
60
|
+
out << 'module MTProto'
|
|
61
|
+
out << ' module TL'
|
|
62
|
+
out << ' module ConstructorNames'
|
|
63
|
+
out << ' NAMES = {'
|
|
64
|
+
entries.each_with_index { |e, i| out << (i == entries.size - 1 ? e : "#{e},") }
|
|
65
|
+
out << ' }.freeze'
|
|
66
|
+
out << ' end'
|
|
67
|
+
out << ' end'
|
|
68
|
+
out << 'end'
|
|
69
|
+
out << '# rubocop:enable Metrics/ModuleLength'
|
|
70
|
+
|
|
71
|
+
File.write(names_file, "#{out.join("\n")}\n")
|
|
72
|
+
puts "wrote #{api.size + core.size} entries (#{api.size} api + #{core.size} transport-core)"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# Convert a tdesktop-style TL schema (.tl) into mtproto-ruby's tl-schema.json
|
|
5
|
+
# format ({ "constructors": [...], "methods": [...] }, each entry
|
|
6
|
+
# { id (signed int32 string), predicate|method, params:[{name,type}], type }).
|
|
7
|
+
#
|
|
8
|
+
# Telegram's published core.telegram.org/schema JSON lags the live layer (it is
|
|
9
|
+
# still ~214 with no guest constructors), so the authoritative current source is
|
|
10
|
+
# tdesktop's Telegram/SourceFiles/mtproto/scheme/api.tl (layer 225).
|
|
11
|
+
#
|
|
12
|
+
# Usage: ruby tl_to_json.rb api.tl [mtproto.tl ...] > tl-schema.json
|
|
13
|
+
|
|
14
|
+
require 'json'
|
|
15
|
+
|
|
16
|
+
def signed32(hexid)
|
|
17
|
+
u = hexid.to_i(16)
|
|
18
|
+
u >= (1 << 31) ? u - (1 << 32) : u
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Params come straight from the .tl tokens: "name:type" where type is already in
|
|
22
|
+
# the gem's native form ("#", "flags.8?Peer", "Vector<Message>", "long", "!X").
|
|
23
|
+
# Skip generic declarations ("{X:Type}") and bare core noise ("#", "[", "t", "]").
|
|
24
|
+
def parse_params(tokens)
|
|
25
|
+
tokens.each_with_object([]) do |tok, acc|
|
|
26
|
+
next if tok.start_with?('{')
|
|
27
|
+
next unless tok.include?(':')
|
|
28
|
+
|
|
29
|
+
name, type = tok.split(':', 2)
|
|
30
|
+
acc << { 'name' => name, 'type' => type }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
constructors = []
|
|
35
|
+
methods = []
|
|
36
|
+
section = :constructors
|
|
37
|
+
|
|
38
|
+
ARGV.each do |path|
|
|
39
|
+
File.foreach(path) do |raw|
|
|
40
|
+
line = raw.strip
|
|
41
|
+
next if line.empty? || line.start_with?('//')
|
|
42
|
+
|
|
43
|
+
if line == '---functions---'
|
|
44
|
+
section = :methods
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
if line == '---types---'
|
|
48
|
+
section = :constructors
|
|
49
|
+
next
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
next unless line.include?('=') && line.end_with?(';')
|
|
53
|
+
|
|
54
|
+
body = line[0..-2] # drop trailing ';'
|
|
55
|
+
lhs, rhs = body.split('=', 2)
|
|
56
|
+
lhs_tokens = lhs.split(/\s+/)
|
|
57
|
+
head = lhs_tokens.shift
|
|
58
|
+
next unless head&.include?('#') # skip id-less core types ("int ? = Int;")
|
|
59
|
+
|
|
60
|
+
name, hexid = head.split('#', 2)
|
|
61
|
+
entry = {
|
|
62
|
+
'id' => signed32(hexid).to_s,
|
|
63
|
+
(section == :constructors ? 'predicate' : 'method') => name,
|
|
64
|
+
'params' => parse_params(lhs_tokens),
|
|
65
|
+
'type' => rhs.strip
|
|
66
|
+
}
|
|
67
|
+
(section == :constructors ? constructors : methods) << entry
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
warn "constructors=#{constructors.size} methods=#{methods.size}"
|
|
72
|
+
puts JSON.pretty_generate('constructors' => constructors, 'methods' => methods)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Flag every hardcoded 0x id in the gem's lib that is NOT a valid layer-227
|
|
3
|
+
# constructor/method id (api.tl). Survivors are either mtproto-layer ids (fine,
|
|
4
|
+
# they live in mtproto.tl) or genuinely-changed api ids (must be updated).
|
|
5
|
+
require 'set'
|
|
6
|
+
|
|
7
|
+
tl = ARGV[0]
|
|
8
|
+
libdir = ARGV[1]
|
|
9
|
+
|
|
10
|
+
ids = Set.new
|
|
11
|
+
names = {}
|
|
12
|
+
File.foreach(tl) do |l|
|
|
13
|
+
next unless l =~ /\A([\w.]+)#([0-9a-f]+)/
|
|
14
|
+
ids << Regexp.last_match(2).to_i(16)
|
|
15
|
+
names[Regexp.last_match(2).to_i(16)] = Regexp.last_match(1)
|
|
16
|
+
end
|
|
17
|
+
warn "api227 ids: #{ids.size}"
|
|
18
|
+
|
|
19
|
+
IGNORE = %w[0xffffffff 0x7fffffff 0x80000000 0x00000000 0xfffffffe].freeze
|
|
20
|
+
seen = {}
|
|
21
|
+
Dir.glob("#{libdir}/**/*.rb").each do |f|
|
|
22
|
+
File.foreach(f).with_index do |line, i|
|
|
23
|
+
line.scan(/0x[0-9a-fA-F]{6,8}/).each do |tok|
|
|
24
|
+
t = tok.downcase
|
|
25
|
+
next if IGNORE.include?(t)
|
|
26
|
+
next if ids.include?(t.to_i(16))
|
|
27
|
+
(seen[t] ||= []) << "#{File.basename(f)}:#{i + 1}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
puts "=== gem 0x ids NOT present as a layer-227 api constructor/method ==="
|
|
33
|
+
seen.sort.each { |tok, locs| puts format('%-12s %s', tok, locs.uniq.join(', ')) }
|
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.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Artem Levenkov
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- lib/mtproto/client.rb
|
|
64
64
|
- lib/mtproto/client/api.rb
|
|
65
65
|
- lib/mtproto/client/api/check_password.rb
|
|
66
|
+
- lib/mtproto/client/api/export_authorization.rb
|
|
66
67
|
- lib/mtproto/client/api/export_login_token.rb
|
|
67
68
|
- lib/mtproto/client/api/get_contacts.rb
|
|
68
69
|
- lib/mtproto/client/api/get_dialogs.rb
|
|
@@ -70,6 +71,7 @@ files:
|
|
|
70
71
|
- lib/mtproto/client/api/get_updates_difference.rb
|
|
71
72
|
- lib/mtproto/client/api/get_updates_state.rb
|
|
72
73
|
- lib/mtproto/client/api/get_users.rb
|
|
74
|
+
- lib/mtproto/client/api/import_authorization.rb
|
|
73
75
|
- lib/mtproto/client/api/import_login_token.rb
|
|
74
76
|
- lib/mtproto/client/api/send_code.rb
|
|
75
77
|
- lib/mtproto/client/api/send_message.rb
|
|
@@ -88,7 +90,7 @@ files:
|
|
|
88
90
|
- lib/mtproto/delegate_methods.rb
|
|
89
91
|
- lib/mtproto/encrypted_message.rb
|
|
90
92
|
- lib/mtproto/errors.rb
|
|
91
|
-
- lib/mtproto/
|
|
93
|
+
- lib/mtproto/file_downloader.rb
|
|
92
94
|
- lib/mtproto/message_id.rb
|
|
93
95
|
- lib/mtproto/session.rb
|
|
94
96
|
- lib/mtproto/tl/constructor_names.rb
|
|
@@ -96,6 +98,7 @@ files:
|
|
|
96
98
|
- lib/mtproto/tl/object.rb
|
|
97
99
|
- lib/mtproto/tl/objects/account_password.rb
|
|
98
100
|
- lib/mtproto/tl/objects/authorization.rb
|
|
101
|
+
- lib/mtproto/tl/objects/bot_command_scope.rb
|
|
99
102
|
- lib/mtproto/tl/objects/channels_create_channel.rb
|
|
100
103
|
- lib/mtproto/tl/objects/channels_delete_messages.rb
|
|
101
104
|
- lib/mtproto/tl/objects/channels_delete_participant_history.rb
|
|
@@ -105,6 +108,7 @@ files:
|
|
|
105
108
|
- lib/mtproto/tl/objects/channels_join_channel.rb
|
|
106
109
|
- lib/mtproto/tl/objects/channels_leave_channel.rb
|
|
107
110
|
- lib/mtproto/tl/objects/channels_report_spam.rb
|
|
111
|
+
- lib/mtproto/tl/objects/channels_update_username.rb
|
|
108
112
|
- lib/mtproto/tl/objects/check_password.rb
|
|
109
113
|
- lib/mtproto/tl/objects/client_dh_inner_data.rb
|
|
110
114
|
- lib/mtproto/tl/objects/contacts.rb
|
|
@@ -114,11 +118,15 @@ files:
|
|
|
114
118
|
- lib/mtproto/tl/objects/dialogs.rb
|
|
115
119
|
- lib/mtproto/tl/objects/edit_access_settings.rb
|
|
116
120
|
- lib/mtproto/tl/objects/edit_message.rb
|
|
121
|
+
- lib/mtproto/tl/objects/export_authorization.rb
|
|
117
122
|
- lib/mtproto/tl/objects/export_bot_token.rb
|
|
118
123
|
- lib/mtproto/tl/objects/export_login_token.rb
|
|
124
|
+
- lib/mtproto/tl/objects/exported_authorization.rb
|
|
119
125
|
- lib/mtproto/tl/objects/exported_bot_token.rb
|
|
120
126
|
- lib/mtproto/tl/objects/forward_messages.rb
|
|
121
127
|
- lib/mtproto/tl/objects/get_access_settings.rb
|
|
128
|
+
- lib/mtproto/tl/objects/get_bot_callback_answer.rb
|
|
129
|
+
- lib/mtproto/tl/objects/get_bot_commands.rb
|
|
122
130
|
- lib/mtproto/tl/objects/get_channel_difference.rb
|
|
123
131
|
- lib/mtproto/tl/objects/get_config.rb
|
|
124
132
|
- lib/mtproto/tl/objects/get_contacts.rb
|
|
@@ -134,12 +142,14 @@ files:
|
|
|
134
142
|
- lib/mtproto/tl/objects/get_users.rb
|
|
135
143
|
- lib/mtproto/tl/objects/gzip_packed.rb
|
|
136
144
|
- lib/mtproto/tl/objects/help_config.rb
|
|
145
|
+
- lib/mtproto/tl/objects/import_authorization.rb
|
|
137
146
|
- lib/mtproto/tl/objects/import_bot_authorization.rb
|
|
138
147
|
- lib/mtproto/tl/objects/import_login_token.rb
|
|
139
148
|
- lib/mtproto/tl/objects/init_connection.rb
|
|
140
149
|
- lib/mtproto/tl/objects/input_keyboard_button_request_peer.rb
|
|
141
150
|
- lib/mtproto/tl/objects/invite_to_channel.rb
|
|
142
151
|
- lib/mtproto/tl/objects/invoke_with_layer.rb
|
|
152
|
+
- lib/mtproto/tl/objects/keyboard_button_callback.rb
|
|
143
153
|
- lib/mtproto/tl/objects/login_token.rb
|
|
144
154
|
- lib/mtproto/tl/objects/message.rb
|
|
145
155
|
- lib/mtproto/tl/objects/messages.rb
|
|
@@ -148,11 +158,13 @@ files:
|
|
|
148
158
|
- lib/mtproto/tl/objects/new_session_created.rb
|
|
149
159
|
- lib/mtproto/tl/objects/pq_inner_data.rb
|
|
150
160
|
- lib/mtproto/tl/objects/raw_response.rb
|
|
161
|
+
- lib/mtproto/tl/objects/reply_inline_markup.rb
|
|
151
162
|
- lib/mtproto/tl/objects/reply_keyboard_markup.rb
|
|
152
163
|
- lib/mtproto/tl/objects/req_dh_params.rb
|
|
153
164
|
- lib/mtproto/tl/objects/req_pq_multi.rb
|
|
154
165
|
- lib/mtproto/tl/objects/request_peer_type_create_bot.rb
|
|
155
166
|
- lib/mtproto/tl/objects/res_pq.rb
|
|
167
|
+
- lib/mtproto/tl/objects/reset_bot_commands.rb
|
|
156
168
|
- lib/mtproto/tl/objects/resolve_username.rb
|
|
157
169
|
- lib/mtproto/tl/objects/rpc_error.rb
|
|
158
170
|
- lib/mtproto/tl/objects/save_file_part.rb
|
|
@@ -160,31 +172,42 @@ files:
|
|
|
160
172
|
- lib/mtproto/tl/objects/send_code.rb
|
|
161
173
|
- lib/mtproto/tl/objects/send_media.rb
|
|
162
174
|
- lib/mtproto/tl/objects/send_message.rb
|
|
175
|
+
- lib/mtproto/tl/objects/send_message_action.rb
|
|
163
176
|
- lib/mtproto/tl/objects/send_reaction.rb
|
|
164
177
|
- lib/mtproto/tl/objects/sent_code.rb
|
|
165
178
|
- lib/mtproto/tl/objects/server_dh_inner_data.rb
|
|
166
179
|
- lib/mtproto/tl/objects/server_dh_params.rb
|
|
180
|
+
- lib/mtproto/tl/objects/set_bot_callback_answer.rb
|
|
167
181
|
- lib/mtproto/tl/objects/set_bot_commands.rb
|
|
182
|
+
- lib/mtproto/tl/objects/set_bot_guest_chat_result.rb
|
|
168
183
|
- lib/mtproto/tl/objects/set_bot_info.rb
|
|
169
184
|
- lib/mtproto/tl/objects/set_client_dh_params.rb
|
|
185
|
+
- lib/mtproto/tl/objects/set_typing.rb
|
|
170
186
|
- lib/mtproto/tl/objects/sign_in.rb
|
|
171
187
|
- lib/mtproto/tl/objects/update.rb
|
|
172
188
|
- lib/mtproto/tl/objects/update_short.rb
|
|
173
189
|
- lib/mtproto/tl/objects/update_short_message.rb
|
|
174
190
|
- lib/mtproto/tl/objects/update_short_sent_message.rb
|
|
191
|
+
- lib/mtproto/tl/objects/update_status.rb
|
|
175
192
|
- lib/mtproto/tl/objects/update_username.rb
|
|
193
|
+
- lib/mtproto/tl/objects/updates.rb
|
|
176
194
|
- lib/mtproto/tl/objects/updates_difference.rb
|
|
177
195
|
- lib/mtproto/tl/objects/updates_state.rb
|
|
178
196
|
- lib/mtproto/tl/objects/upload_profile_photo.rb
|
|
179
197
|
- lib/mtproto/tl/objects/users.rb
|
|
198
|
+
- lib/mtproto/tl/reader.rb
|
|
180
199
|
- lib/mtproto/tl/schema.rb
|
|
181
200
|
- lib/mtproto/transport/abridged_packet_codec.rb
|
|
182
201
|
- lib/mtproto/transport/connection.rb
|
|
183
202
|
- lib/mtproto/transport/errors.rb
|
|
184
203
|
- lib/mtproto/transport/packet.rb
|
|
185
204
|
- lib/mtproto/transport/tcp_connection.rb
|
|
205
|
+
- lib/mtproto/unencrypted_message.rb
|
|
186
206
|
- lib/mtproto/version.rb
|
|
207
|
+
- scripts/gen_constructor_names.rb
|
|
187
208
|
- scripts/generate_constructors.rb
|
|
209
|
+
- scripts/tl_to_json.rb
|
|
210
|
+
- scripts/verify_ids.rb
|
|
188
211
|
- tmp/.keep
|
|
189
212
|
homepage: https://github.com/alev-pro/mtproto-ruby
|
|
190
213
|
licenses:
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MTProto
|
|
4
|
-
class Message
|
|
5
|
-
include Binary
|
|
6
|
-
extend Binary
|
|
7
|
-
|
|
8
|
-
attr_reader :auth_key_id, :msg_id, :body,
|
|
9
|
-
:msg_key, :encrypted_data,
|
|
10
|
-
:server_salt, :session_id, :seq_no
|
|
11
|
-
|
|
12
|
-
def initialize(body, msg_id: self.class.generate_msg_id, auth_key_id: 0)
|
|
13
|
-
@auth_key_id = auth_key_id
|
|
14
|
-
@msg_id = msg_id
|
|
15
|
-
@body = body
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def self.parse(packet)
|
|
19
|
-
data = packet.data
|
|
20
|
-
auth_key_id = b_u64(data[0, 8])
|
|
21
|
-
|
|
22
|
-
if auth_key_id.zero?
|
|
23
|
-
parse_unencrypted(data)
|
|
24
|
-
else
|
|
25
|
-
parse_encrypted(data, auth_key_id)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def encrypted?
|
|
30
|
-
!@auth_key_id.zero?
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def decrypt(auth_key:, sender: :server)
|
|
34
|
-
raise 'Message is not encrypted' unless encrypted?
|
|
35
|
-
|
|
36
|
-
keys = Crypto::MessageKey.derive_aes_key_iv(auth_key, @msg_key, sender: sender)
|
|
37
|
-
plaintext = Crypto::AES_IGE.decrypt_ige(@encrypted_data, keys[:aes_key], keys[:aes_iv])
|
|
38
|
-
|
|
39
|
-
expected_msg_key = Crypto::MessageKey.generate_msg_key(auth_key, plaintext, sender: sender)
|
|
40
|
-
raise 'msg_key mismatch!' unless @msg_key == expected_msg_key
|
|
41
|
-
|
|
42
|
-
offset = 0
|
|
43
|
-
@server_salt = plaintext[offset, 8].unpack1('Q<')
|
|
44
|
-
offset += 8
|
|
45
|
-
@session_id = plaintext[offset, 8].unpack1('Q<')
|
|
46
|
-
offset += 8
|
|
47
|
-
@msg_id = plaintext[offset, 8].unpack1('Q<')
|
|
48
|
-
offset += 8
|
|
49
|
-
@seq_no = plaintext[offset, 4].unpack1('L<')
|
|
50
|
-
offset += 4
|
|
51
|
-
body_length = plaintext[offset, 4].unpack1('L<')
|
|
52
|
-
offset += 4
|
|
53
|
-
@body = plaintext[offset, body_length].bytes
|
|
54
|
-
|
|
55
|
-
self
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def bytes
|
|
59
|
-
u64_b(@auth_key_id) +
|
|
60
|
-
u64_b(@msg_id) +
|
|
61
|
-
u32_b(@body.length) +
|
|
62
|
-
@body
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def self.generate_msg_id
|
|
66
|
-
(Time.now.to_f * (2**32)).to_i & ~3
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
private_class_method def self.parse_unencrypted(data)
|
|
70
|
-
msg_id = b_u64(data[8, 8])
|
|
71
|
-
body_length = b_u32(data[16, 4])
|
|
72
|
-
body = data[20, body_length]
|
|
73
|
-
|
|
74
|
-
new(body, msg_id: msg_id, auth_key_id: 0)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private_class_method def self.parse_encrypted(data, auth_key_id)
|
|
78
|
-
msg = allocate
|
|
79
|
-
msg.instance_variable_set(:@auth_key_id, auth_key_id)
|
|
80
|
-
msg.instance_variable_set(:@msg_key, data[8, 16].pack('C*'))
|
|
81
|
-
msg.instance_variable_set(:@encrypted_data, data[24..].pack('C*'))
|
|
82
|
-
msg
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|