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
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../schema'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
class Dialogs
|
|
8
|
+
Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, keyword_init: true)
|
|
9
|
+
Chat = Struct.new(:id, :title, :type, :access_hash, keyword_init: true)
|
|
10
|
+
|
|
11
|
+
SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
|
|
12
|
+
|
|
13
|
+
attr_reader :dialogs, :chats, :users, :count, :message_dates
|
|
14
|
+
|
|
15
|
+
def initialize(dialogs:, chats:, users:, count: nil, message_dates: {})
|
|
16
|
+
@dialogs = dialogs
|
|
17
|
+
@chats = chats
|
|
18
|
+
@users = users
|
|
19
|
+
@count = count
|
|
20
|
+
@message_dates = message_dates
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def slice?
|
|
24
|
+
!@count.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.schema
|
|
28
|
+
@schema ||= Schema.new(SCHEMA_PATH)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.parse(data)
|
|
32
|
+
constructor = data[0, 4].unpack1('L<')
|
|
33
|
+
|
|
34
|
+
unless [Constructors::MESSAGES_DIALOGS, Constructors::MESSAGES_DIALOGS_SLICE].include?(constructor)
|
|
35
|
+
raise UnexpectedConstructorError, constructor
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
offset = 4
|
|
39
|
+
|
|
40
|
+
count = nil
|
|
41
|
+
if constructor == Constructors::MESSAGES_DIALOGS_SLICE
|
|
42
|
+
count = data[offset, 4].unpack1('L<')
|
|
43
|
+
offset += 4
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
dialogs, offset = parse_dialogs_vector(data, offset)
|
|
47
|
+
message_dates, offset = parse_messages_vector(data, offset)
|
|
48
|
+
chats, offset = parse_chats_vector(data, offset)
|
|
49
|
+
users, = parse_users_vector(data, offset)
|
|
50
|
+
|
|
51
|
+
new(dialogs: dialogs, chats: chats, users: users, count: count, message_dates: message_dates)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# --- Dialogs vector ---
|
|
58
|
+
|
|
59
|
+
def parse_dialogs_vector(data, offset)
|
|
60
|
+
offset += 4 # vector constructor
|
|
61
|
+
count = data[offset, 4].unpack1('L<')
|
|
62
|
+
offset += 4
|
|
63
|
+
|
|
64
|
+
dialogs = []
|
|
65
|
+
count.times do
|
|
66
|
+
dialog, offset = parse_dialog(data, offset)
|
|
67
|
+
dialogs << dialog if dialog
|
|
68
|
+
end
|
|
69
|
+
[dialogs, offset]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def parse_dialog(data, offset)
|
|
73
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
74
|
+
offset += 4
|
|
75
|
+
|
|
76
|
+
unless constructor == Constructors::DIALOG
|
|
77
|
+
offset = schema.skip_params(data, offset, constructor)
|
|
78
|
+
return [nil, offset]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
flags = data[offset, 4].unpack1('L<')
|
|
82
|
+
offset += 4
|
|
83
|
+
|
|
84
|
+
peer_type, peer_id, offset = parse_peer(data, offset)
|
|
85
|
+
|
|
86
|
+
top_message = data[offset, 4].unpack1('L<')
|
|
87
|
+
offset += 4
|
|
88
|
+
|
|
89
|
+
offset += 4 # read_inbox_max_id
|
|
90
|
+
offset += 4 # read_outbox_max_id
|
|
91
|
+
|
|
92
|
+
unread_count = data[offset, 4].unpack1('L<')
|
|
93
|
+
offset += 4
|
|
94
|
+
|
|
95
|
+
offset += 4 # unread_mentions_count
|
|
96
|
+
offset += 4 # unread_reactions_count
|
|
97
|
+
|
|
98
|
+
offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
|
|
99
|
+
|
|
100
|
+
offset += 4 if flags.anybits?(1 << 0) # pts
|
|
101
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
|
|
102
|
+
offset += 4 if flags.anybits?(1 << 4) # folder_id
|
|
103
|
+
offset += 4 if flags.anybits?(1 << 5) # ttl_period
|
|
104
|
+
|
|
105
|
+
[Dialog.new(peer_type: peer_type, peer_id: peer_id,
|
|
106
|
+
top_message: top_message, unread_count: unread_count), offset]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def parse_peer(data, offset)
|
|
110
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
111
|
+
offset += 4
|
|
112
|
+
|
|
113
|
+
case constructor
|
|
114
|
+
when Constructors::PEER_USER
|
|
115
|
+
[:user, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
116
|
+
when Constructors::PEER_CHAT
|
|
117
|
+
[:chat, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
118
|
+
when Constructors::PEER_CHANNEL
|
|
119
|
+
[:channel, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
120
|
+
else
|
|
121
|
+
raise "Unknown peer constructor: 0x#{constructor.to_s(16)}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# --- Messages vector ---
|
|
126
|
+
|
|
127
|
+
# message#9815cec8 flags:# flags2:#
|
|
128
|
+
# id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int
|
|
129
|
+
# peer_id:Peer saved_peer_id:flags.28?Peer
|
|
130
|
+
# fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long
|
|
131
|
+
# via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader
|
|
132
|
+
# date:int ...
|
|
133
|
+
#
|
|
134
|
+
# messageService#2b085862 flags:#
|
|
135
|
+
# id:int from_id:flags.8?Peer peer_id:Peer
|
|
136
|
+
# saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader
|
|
137
|
+
# date:int ...
|
|
138
|
+
MESSAGE_CONSTRUCTOR = Constructors::MESSAGE
|
|
139
|
+
MESSAGE_SERVICE = 0x7a800e0a
|
|
140
|
+
MESSAGE_EMPTY = 0x90a6ca84
|
|
141
|
+
|
|
142
|
+
def parse_messages_vector(data, offset)
|
|
143
|
+
offset += 4 # vector constructor
|
|
144
|
+
count = data[offset, 4].unpack1('L<')
|
|
145
|
+
offset += 4
|
|
146
|
+
|
|
147
|
+
dates = {}
|
|
148
|
+
count.times do
|
|
149
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
150
|
+
id_date = extract_message_id_date(data, offset, constructor)
|
|
151
|
+
dates[id_date[0]] = id_date[1] if id_date
|
|
152
|
+
|
|
153
|
+
offset = schema.skip(data, offset)
|
|
154
|
+
end
|
|
155
|
+
[dates, offset]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def extract_message_id_date(data, offset, constructor)
|
|
159
|
+
case constructor
|
|
160
|
+
when MESSAGE_CONSTRUCTOR
|
|
161
|
+
extract_message_date(data, offset)
|
|
162
|
+
when MESSAGE_SERVICE
|
|
163
|
+
extract_message_service_date(data, offset)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def extract_message_date(data, offset)
|
|
168
|
+
offset += 4 # constructor
|
|
169
|
+
flags = data[offset, 4].unpack1('L<')
|
|
170
|
+
offset += 4
|
|
171
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
172
|
+
offset += 4
|
|
173
|
+
id = data[offset, 4].unpack1('l<')
|
|
174
|
+
offset += 4
|
|
175
|
+
|
|
176
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
|
|
177
|
+
offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
|
|
178
|
+
offset = schema.skip(data, offset) # peer_id (Peer, always present)
|
|
179
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
|
|
180
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
|
|
181
|
+
offset += 8 if flags.anybits?(1 << 11) # via_bot_id
|
|
182
|
+
offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
|
|
183
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
|
|
184
|
+
|
|
185
|
+
date = data[offset, 4].unpack1('L<')
|
|
186
|
+
[id, date]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def extract_message_service_date(data, offset)
|
|
190
|
+
offset += 4 # constructor
|
|
191
|
+
flags = data[offset, 4].unpack1('L<')
|
|
192
|
+
offset += 4
|
|
193
|
+
id = data[offset, 4].unpack1('l<')
|
|
194
|
+
offset += 4
|
|
195
|
+
|
|
196
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
|
|
197
|
+
offset = schema.skip(data, offset) # peer_id (Peer, always present)
|
|
198
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
|
|
199
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
|
|
200
|
+
|
|
201
|
+
date = data[offset, 4].unpack1('L<')
|
|
202
|
+
[id, date]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# --- Chats vector ---
|
|
206
|
+
|
|
207
|
+
def parse_chats_vector(data, offset)
|
|
208
|
+
offset += 4 # vector constructor
|
|
209
|
+
count = data[offset, 4].unpack1('L<')
|
|
210
|
+
offset += 4
|
|
211
|
+
|
|
212
|
+
chats = []
|
|
213
|
+
count.times do
|
|
214
|
+
chat, offset = parse_chat(data, offset)
|
|
215
|
+
chats << chat if chat
|
|
216
|
+
end
|
|
217
|
+
[chats, offset]
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def parse_chat(data, offset)
|
|
221
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
222
|
+
offset += 4
|
|
223
|
+
|
|
224
|
+
case constructor
|
|
225
|
+
when Constructors::CHAT
|
|
226
|
+
parse_basic_chat(data, offset)
|
|
227
|
+
when Constructors::CHANNEL
|
|
228
|
+
parse_channel(data, offset)
|
|
229
|
+
when Constructors::CHAT_FORBIDDEN
|
|
230
|
+
parse_chat_forbidden(data, offset)
|
|
231
|
+
when Constructors::CHANNEL_FORBIDDEN
|
|
232
|
+
parse_channel_forbidden(data, offset)
|
|
233
|
+
when CHAT_EMPTY
|
|
234
|
+
id = data[offset, 8].unpack1('Q<')
|
|
235
|
+
offset += 8
|
|
236
|
+
[Chat.new(id: id, title: '(empty)', type: :chat_empty), offset]
|
|
237
|
+
else
|
|
238
|
+
raise "Unknown Chat constructor: 0x#{constructor.to_s(16).rjust(8, '0')}"
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
CHAT_EMPTY = 0x29562865
|
|
243
|
+
|
|
244
|
+
# chat#41cbf256 flags:#
|
|
245
|
+
# id:long title:string photo:ChatPhoto
|
|
246
|
+
# participants_count:int date:int version:int
|
|
247
|
+
# migrated_to:flags.6?InputChannel
|
|
248
|
+
# admin_rights:flags.14?ChatAdminRights
|
|
249
|
+
# default_banned_rights:flags.18?ChatBannedRights
|
|
250
|
+
def parse_basic_chat(data, offset)
|
|
251
|
+
flags = data[offset, 4].unpack1('L<')
|
|
252
|
+
offset += 4
|
|
253
|
+
id = data[offset, 8].unpack1('Q<')
|
|
254
|
+
offset += 8
|
|
255
|
+
title, offset = read_tl_string(data, offset)
|
|
256
|
+
offset = schema.skip(data, offset) # photo (ChatPhoto)
|
|
257
|
+
offset += 4 # participants_count
|
|
258
|
+
offset += 4 # date
|
|
259
|
+
offset += 4 # version
|
|
260
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # migrated_to (InputChannel)
|
|
261
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 14) # admin_rights
|
|
262
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 18) # default_banned_rights
|
|
263
|
+
|
|
264
|
+
[Chat.new(id: id, title: title, type: :chat), offset]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# channel#fe685355 flags:# flags2:#
|
|
268
|
+
# id:long access_hash:flags.13?long title:string
|
|
269
|
+
# username:flags.6?string photo:ChatPhoto date:int
|
|
270
|
+
# restriction_reason:flags.9?Vector<RestrictionReason>
|
|
271
|
+
# admin_rights:flags.14?ChatAdminRights
|
|
272
|
+
# banned_rights:flags.15?ChatBannedRights
|
|
273
|
+
# default_banned_rights:flags.18?ChatBannedRights
|
|
274
|
+
# participants_count:flags.17?int
|
|
275
|
+
# usernames:flags2.0?Vector<Username>
|
|
276
|
+
# stories_max_id:flags2.4?int
|
|
277
|
+
# color:flags2.7?PeerColor profile_color:flags2.8?PeerColor
|
|
278
|
+
# emoji_status:flags2.9?EmojiStatus
|
|
279
|
+
# level:flags2.10?int subscription_until_date:flags2.11?int
|
|
280
|
+
# bot_verification_icon:flags2.13?long
|
|
281
|
+
# send_paid_messages_stars:flags2.14?long
|
|
282
|
+
# linked_monoforum_id:flags2.18?long
|
|
283
|
+
def parse_channel(data, offset)
|
|
284
|
+
flags = data[offset, 4].unpack1('L<')
|
|
285
|
+
offset += 4
|
|
286
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
287
|
+
offset += 4
|
|
288
|
+
id = data[offset, 8].unpack1('Q<')
|
|
289
|
+
offset += 8
|
|
290
|
+
access_hash = nil
|
|
291
|
+
if flags.anybits?(1 << 13)
|
|
292
|
+
access_hash = data[offset, 8].unpack1('Q<')
|
|
293
|
+
offset += 8
|
|
294
|
+
end
|
|
295
|
+
title, offset = read_tl_string(data, offset)
|
|
296
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 6) # username
|
|
297
|
+
offset = schema.skip(data, offset) # photo (ChatPhoto)
|
|
298
|
+
offset += 4 # date
|
|
299
|
+
if flags.anybits?(1 << 9) # restriction_reason Vector<RestrictionReason>
|
|
300
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
|
|
301
|
+
end
|
|
302
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 14) # admin_rights
|
|
303
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 15) # banned_rights
|
|
304
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 18) # default_banned_rights
|
|
305
|
+
offset += 4 if flags.anybits?(1 << 17) # participants_count
|
|
306
|
+
if flags2.anybits?(1 << 0) # usernames Vector<Username>
|
|
307
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
|
|
308
|
+
end
|
|
309
|
+
offset += 4 if flags2.anybits?(1 << 4) # stories_max_id
|
|
310
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 7) # color (PeerColor)
|
|
311
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # profile_color (PeerColor)
|
|
312
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # emoji_status
|
|
313
|
+
offset += 4 if flags2.anybits?(1 << 10) # level
|
|
314
|
+
offset += 4 if flags2.anybits?(1 << 11) # subscription_until_date
|
|
315
|
+
offset += 8 if flags2.anybits?(1 << 13) # bot_verification_icon (long)
|
|
316
|
+
offset += 8 if flags2.anybits?(1 << 14) # send_paid_messages_stars (long)
|
|
317
|
+
offset += 8 if flags2.anybits?(1 << 18) # linked_monoforum_id (long)
|
|
318
|
+
|
|
319
|
+
type = flags.anybits?(1 << 8) ? :supergroup : :channel
|
|
320
|
+
[Chat.new(id: id, title: title, type: type, access_hash: access_hash), offset]
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# chatForbidden#6592a1a7 id:long title:string
|
|
324
|
+
def parse_chat_forbidden(data, offset)
|
|
325
|
+
id = data[offset, 8].unpack1('Q<')
|
|
326
|
+
offset += 8
|
|
327
|
+
title, offset = read_tl_string(data, offset)
|
|
328
|
+
[Chat.new(id: id, title: title, type: :chat_forbidden), offset]
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# channelForbidden#17d493d5 flags:#
|
|
332
|
+
# id:long access_hash:long title:string until_date:flags.16?int
|
|
333
|
+
def parse_channel_forbidden(data, offset)
|
|
334
|
+
flags = data[offset, 4].unpack1('L<')
|
|
335
|
+
offset += 4
|
|
336
|
+
id = data[offset, 8].unpack1('Q<')
|
|
337
|
+
offset += 8
|
|
338
|
+
access_hash = data[offset, 8].unpack1('Q<')
|
|
339
|
+
offset += 8
|
|
340
|
+
title, offset = read_tl_string(data, offset)
|
|
341
|
+
offset += 4 if flags.anybits?(1 << 16) # until_date
|
|
342
|
+
[Chat.new(id: id, title: title, type: :channel_forbidden, access_hash: access_hash), offset]
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# --- Users vector ---
|
|
346
|
+
|
|
347
|
+
def parse_users_vector(data, offset)
|
|
348
|
+
offset += 4 # vector constructor
|
|
349
|
+
count = data[offset, 4].unpack1('L<')
|
|
350
|
+
offset += 4
|
|
351
|
+
|
|
352
|
+
users = []
|
|
353
|
+
count.times do
|
|
354
|
+
user, offset = parse_user(data, offset)
|
|
355
|
+
users << user if user
|
|
356
|
+
end
|
|
357
|
+
[users, offset]
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
USER_EMPTY = 0xd3bc4b7a
|
|
361
|
+
|
|
362
|
+
# user#020b1422 flags:# flags2:#
|
|
363
|
+
# id:long access_hash:flags.0?long
|
|
364
|
+
# first_name:flags.1?string last_name:flags.2?string
|
|
365
|
+
# username:flags.3?string phone:flags.4?string
|
|
366
|
+
# photo:flags.5?UserProfilePhoto status:flags.6?UserStatus
|
|
367
|
+
# bot_info_version:flags.14?int
|
|
368
|
+
# restriction_reason:flags.18?Vector<RestrictionReason>
|
|
369
|
+
# bot_inline_placeholder:flags.19?string
|
|
370
|
+
# lang_code:flags.22?string
|
|
371
|
+
# emoji_status:flags.30?EmojiStatus
|
|
372
|
+
# usernames:flags2.0?Vector<Username>
|
|
373
|
+
# stories_max_id:flags2.5?int
|
|
374
|
+
# color:flags2.8?PeerColor profile_color:flags2.9?PeerColor
|
|
375
|
+
# bot_active_users:flags2.12?int
|
|
376
|
+
# bot_verification_icon:flags2.14?long
|
|
377
|
+
# send_paid_messages_stars:flags2.15?long
|
|
378
|
+
def parse_user(data, offset)
|
|
379
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
380
|
+
offset += 4
|
|
381
|
+
|
|
382
|
+
if constructor == USER_EMPTY
|
|
383
|
+
offset += 8 # id (long)
|
|
384
|
+
return [nil, offset]
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
raise "Unknown User constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::USER
|
|
388
|
+
|
|
389
|
+
flags = data[offset, 4].unpack1('L<')
|
|
390
|
+
offset += 4
|
|
391
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
392
|
+
offset += 4
|
|
393
|
+
id = data[offset, 8].unpack1('Q<')
|
|
394
|
+
offset += 8
|
|
395
|
+
access_hash = nil
|
|
396
|
+
if flags.anybits?(1 << 0)
|
|
397
|
+
access_hash = data[offset, 8].unpack1('Q<')
|
|
398
|
+
offset += 8
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
first_name = nil
|
|
402
|
+
first_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 1)
|
|
403
|
+
|
|
404
|
+
last_name = nil
|
|
405
|
+
last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
|
|
406
|
+
|
|
407
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 3) # username
|
|
408
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
|
|
409
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
|
|
410
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
|
|
411
|
+
offset += 4 if flags.anybits?(1 << 14) # bot_info_version
|
|
412
|
+
if flags.anybits?(1 << 18) # restriction_reason
|
|
413
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) }
|
|
414
|
+
end
|
|
415
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 19) # bot_inline_placeholder
|
|
416
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
|
|
417
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
|
|
418
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0) # usernames
|
|
419
|
+
offset += 4 if flags2.anybits?(1 << 5) # stories_max_id
|
|
420
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
|
|
421
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
|
|
422
|
+
offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
|
|
423
|
+
offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
|
|
424
|
+
offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
|
|
425
|
+
|
|
426
|
+
[{ id: id, first_name: first_name, last_name: last_name, access_hash: access_hash }, offset]
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# --- TL string ---
|
|
430
|
+
|
|
431
|
+
def read_tl_string(data, offset)
|
|
432
|
+
first_byte = data.getbyte(offset)
|
|
433
|
+
if first_byte == 254
|
|
434
|
+
len = data.getbyte(offset + 1) |
|
|
435
|
+
(data.getbyte(offset + 2) << 8) |
|
|
436
|
+
(data.getbyte(offset + 3) << 16)
|
|
437
|
+
total = 4 + len
|
|
438
|
+
else
|
|
439
|
+
len = first_byte
|
|
440
|
+
total = 1 + len
|
|
441
|
+
end
|
|
442
|
+
padding = (4 - (total % 4)) % 4
|
|
443
|
+
[data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def skip_tl_string(data, offset)
|
|
447
|
+
_, new_offset = read_tl_string(data, offset)
|
|
448
|
+
new_offset
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ExportLoginToken
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
attr_reader :api_id, :api_hash, :except_ids
|
|
9
|
+
|
|
10
|
+
def initialize(api_id:, api_hash:, except_ids: [])
|
|
11
|
+
@api_id = api_id
|
|
12
|
+
@api_hash = api_hash
|
|
13
|
+
@except_ids = except_ids
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def body
|
|
17
|
+
u32_b(Constructors::AUTH_EXPORT_LOGIN_TOKEN) +
|
|
18
|
+
u32_b(@api_id) +
|
|
19
|
+
serialize_tl_string(@api_hash) +
|
|
20
|
+
serialize_vector_long(@except_ids)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def serialize_vector_long(ids)
|
|
26
|
+
result = u32_b(Constructors::VECTOR) + u32_b(ids.length)
|
|
27
|
+
ids.each { |id| result += u64_b(id) }
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def serialize_tl_string(str)
|
|
32
|
+
bytes = str.encode('UTF-8').bytes
|
|
33
|
+
length = bytes.length
|
|
34
|
+
|
|
35
|
+
if length <= 253
|
|
36
|
+
[length] + bytes + padding(length + 1)
|
|
37
|
+
else
|
|
38
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def padding(current_length)
|
|
43
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
44
|
+
[0] * pad_length
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetDialogs
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
attr_reader :offset_date, :offset_id, :offset_peer, :limit, :exclude_pinned, :folder_id
|
|
9
|
+
|
|
10
|
+
def initialize(offset_date: 0, offset_id: 0, offset_peer: nil, limit: 100, exclude_pinned: false, folder_id: nil)
|
|
11
|
+
@offset_date = offset_date
|
|
12
|
+
@offset_id = offset_id
|
|
13
|
+
@offset_peer = offset_peer
|
|
14
|
+
@limit = limit
|
|
15
|
+
@exclude_pinned = exclude_pinned
|
|
16
|
+
@folder_id = folder_id
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def body
|
|
20
|
+
flags = 0
|
|
21
|
+
flags |= (1 << 0) if @exclude_pinned
|
|
22
|
+
flags |= (1 << 1) if @folder_id
|
|
23
|
+
|
|
24
|
+
result = u32_b(Constructors::MESSAGES_GET_DIALOGS) + u32_b(flags)
|
|
25
|
+
result += u32_b(@folder_id) if @folder_id
|
|
26
|
+
result += u32_b(@offset_date) + u32_b(@offset_id)
|
|
27
|
+
result += serialize_input_peer
|
|
28
|
+
result += u32_b(@limit)
|
|
29
|
+
result += u64_b(0) # hash
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def serialize_input_peer
|
|
36
|
+
return u32_b(Constructors::INPUT_PEER_EMPTY) unless @offset_peer
|
|
37
|
+
|
|
38
|
+
case @offset_peer[:type]
|
|
39
|
+
when :user
|
|
40
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(@offset_peer[:id]) + u64_b(@offset_peer[:access_hash] || 0)
|
|
41
|
+
when :chat
|
|
42
|
+
u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@offset_peer[:id])
|
|
43
|
+
when :channel
|
|
44
|
+
u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@offset_peer[:id]) + u64_b(@offset_peer[:access_hash] || 0)
|
|
45
|
+
else
|
|
46
|
+
u32_b(Constructors::INPUT_PEER_EMPTY)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetDifference
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
attr_reader :pts, :date, :qts, :pts_limit, :pts_total_limit, :qts_limit
|
|
9
|
+
|
|
10
|
+
def initialize(pts:, date:, qts:, pts_limit: nil, pts_total_limit: nil, qts_limit: nil)
|
|
11
|
+
@pts = pts
|
|
12
|
+
@date = date
|
|
13
|
+
@qts = qts
|
|
14
|
+
@pts_limit = pts_limit
|
|
15
|
+
@pts_total_limit = pts_total_limit
|
|
16
|
+
@qts_limit = qts_limit
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def body
|
|
20
|
+
flags = 0
|
|
21
|
+
flags |= (1 << 0) if @pts_limit
|
|
22
|
+
flags |= (1 << 1) if @pts_total_limit
|
|
23
|
+
flags |= (1 << 2) if @qts_limit
|
|
24
|
+
|
|
25
|
+
result = u32_b(Constructors::UPDATES_GET_DIFFERENCE) + u32_b(flags) + u32_b(@pts)
|
|
26
|
+
result += u32_b(@pts_limit) if @pts_limit
|
|
27
|
+
result += u32_b(@pts_total_limit) if @pts_total_limit
|
|
28
|
+
result += u32_b(@date) + u32_b(@qts)
|
|
29
|
+
result += u32_b(@qts_limit) if @qts_limit
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetHistory
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x4423e6c5
|
|
9
|
+
|
|
10
|
+
def initialize(peer:, offset_id: 0, offset_date: 0, add_offset: 0, limit: 100, max_id: 0, min_id: 0)
|
|
11
|
+
@peer = peer
|
|
12
|
+
@offset_id = offset_id
|
|
13
|
+
@offset_date = offset_date
|
|
14
|
+
@add_offset = add_offset
|
|
15
|
+
@limit = limit
|
|
16
|
+
@max_id = max_id
|
|
17
|
+
@min_id = min_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def body
|
|
21
|
+
result = u32_b(CONSTRUCTOR)
|
|
22
|
+
result += serialize_input_peer
|
|
23
|
+
result += u32_b(@offset_id)
|
|
24
|
+
result += u32_b(@offset_date)
|
|
25
|
+
result += u32_b(@add_offset)
|
|
26
|
+
result += u32_b(@limit)
|
|
27
|
+
result += u32_b(@max_id)
|
|
28
|
+
result += u32_b(@min_id)
|
|
29
|
+
result += u64_b(0) # hash
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def serialize_input_peer
|
|
36
|
+
case @peer[:type]
|
|
37
|
+
when :user
|
|
38
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
|
|
39
|
+
when :chat
|
|
40
|
+
u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
|
|
41
|
+
when :channel
|
|
42
|
+
u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
|
|
43
|
+
else
|
|
44
|
+
raise "Unknown peer type: #{@peer[:type]}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetUsers
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def body
|
|
9
|
+
u32_b(Constructors::USERS_GET_USERS) +
|
|
10
|
+
u32_b(Constructors::VECTOR) +
|
|
11
|
+
u32_b(1) +
|
|
12
|
+
u32_b(Constructors::INPUT_USER_SELF)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|