mtproto 0.0.13 → 0.0.15
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/FUTURE.md +9 -0
- data/docs/test_architecture_level1.md +237 -0
- data/lib/mtproto/auth_key_generator.rb +5 -5
- data/lib/mtproto/client/api/get_contacts.rb +14 -0
- data/lib/mtproto/client/api/send_message.rb +15 -0
- data/lib/mtproto/client/api.rb +2 -0
- data/lib/mtproto/client/rpc/response.rb +1 -1
- data/lib/mtproto/client/rpc.rb +35 -2
- data/lib/mtproto/client.rb +81 -18
- data/lib/mtproto/tl/constructor_names.rb +10 -0
- data/lib/mtproto/tl/constructors.rb +26 -0
- data/lib/mtproto/tl/object.rb +1 -1
- data/lib/mtproto/tl/objects/account_password.rb +1 -1
- data/lib/mtproto/tl/objects/authorization.rb +1 -1
- data/lib/mtproto/tl/objects/channels_create_channel.rb +48 -0
- data/lib/mtproto/tl/objects/channels_delete_messages.rb +32 -0
- data/lib/mtproto/tl/objects/channels_delete_participant_history.rb +34 -0
- data/lib/mtproto/tl/objects/channels_edit_banned.rb +54 -0
- data/lib/mtproto/tl/objects/channels_get_messages.rb +33 -0
- data/lib/mtproto/tl/objects/channels_get_participant.rb +36 -0
- data/lib/mtproto/tl/objects/channels_join_channel.rb +22 -0
- data/lib/mtproto/tl/objects/channels_leave_channel.rb +22 -0
- data/lib/mtproto/tl/objects/channels_report_spam.rb +37 -0
- data/lib/mtproto/tl/objects/check_password.rb +1 -1
- data/lib/mtproto/tl/objects/client_dh_inner_data.rb +1 -1
- data/lib/mtproto/tl/objects/contacts.rb +156 -0
- data/lib/mtproto/tl/objects/create_bot.rb +54 -0
- data/lib/mtproto/tl/objects/delete_messages.rb +25 -0
- data/lib/mtproto/tl/objects/dh_gen_response.rb +1 -1
- data/lib/mtproto/tl/objects/dialogs.rb +18 -4
- data/lib/mtproto/tl/objects/edit_access_settings.rb +46 -0
- data/lib/mtproto/tl/objects/edit_message.rb +58 -0
- data/lib/mtproto/tl/objects/export_bot_token.rb +32 -0
- data/lib/mtproto/tl/objects/export_login_token.rb +1 -1
- data/lib/mtproto/tl/objects/exported_bot_token.rb +27 -0
- data/lib/mtproto/tl/objects/forward_messages.rb +55 -0
- data/lib/mtproto/tl/objects/get_access_settings.rb +28 -0
- data/lib/mtproto/tl/objects/get_channel_difference.rb +41 -0
- data/lib/mtproto/tl/objects/get_config.rb +1 -1
- data/lib/mtproto/tl/objects/get_contacts.rb +17 -0
- data/lib/mtproto/tl/objects/get_dialogs.rb +1 -1
- data/lib/mtproto/tl/objects/get_difference.rb +1 -1
- data/lib/mtproto/tl/objects/get_file.rb +54 -0
- data/lib/mtproto/tl/objects/get_full_channel.rb +29 -0
- data/lib/mtproto/tl/objects/get_full_user.rb +28 -0
- data/lib/mtproto/tl/objects/get_history.rb +1 -1
- data/lib/mtproto/tl/objects/get_messages_reactions.rb +39 -0
- data/lib/mtproto/tl/objects/get_password.rb +1 -1
- data/lib/mtproto/tl/objects/get_state.rb +1 -1
- data/lib/mtproto/tl/objects/get_users.rb +1 -1
- data/lib/mtproto/tl/objects/help_config.rb +1 -1
- data/lib/mtproto/tl/objects/import_bot_authorization.rb +44 -0
- data/lib/mtproto/tl/objects/import_login_token.rb +1 -1
- data/lib/mtproto/tl/objects/init_connection.rb +2 -2
- data/lib/mtproto/tl/objects/input_keyboard_button_request_peer.rb +54 -0
- data/lib/mtproto/tl/objects/invite_to_channel.rb +35 -0
- data/lib/mtproto/tl/objects/invoke_with_layer.rb +2 -2
- data/lib/mtproto/tl/objects/login_token.rb +2 -2
- data/lib/mtproto/tl/objects/messages.rb +1 -1
- data/lib/mtproto/tl/objects/messages_get_messages.rb +26 -0
- data/lib/mtproto/tl/objects/pq_inner_data.rb +1 -1
- data/lib/mtproto/tl/objects/raw_response.rb +29 -0
- data/lib/mtproto/tl/objects/reply_keyboard_markup.rb +35 -0
- data/lib/mtproto/tl/objects/req_dh_params.rb +1 -1
- data/lib/mtproto/tl/objects/req_pq_multi.rb +1 -1
- data/lib/mtproto/tl/objects/request_peer_type_create_bot.rb +47 -0
- data/lib/mtproto/tl/objects/res_pq.rb +1 -1
- data/lib/mtproto/tl/objects/resolve_username.rb +40 -0
- data/lib/mtproto/tl/objects/save_file_part.rb +40 -0
- data/lib/mtproto/tl/objects/send_bot_requested_peer.rb +51 -0
- data/lib/mtproto/tl/objects/send_code.rb +1 -1
- data/lib/mtproto/tl/objects/send_media.rb +96 -0
- data/lib/mtproto/tl/objects/send_message.rb +73 -0
- data/lib/mtproto/tl/objects/send_reaction.rb +68 -0
- data/lib/mtproto/tl/objects/sent_code.rb +1 -1
- data/lib/mtproto/tl/objects/server_dh_inner_data.rb +1 -1
- data/lib/mtproto/tl/objects/server_dh_params.rb +1 -1
- data/lib/mtproto/tl/objects/set_bot_commands.rb +50 -0
- data/lib/mtproto/tl/objects/set_bot_info.rb +64 -0
- data/lib/mtproto/tl/objects/set_client_dh_params.rb +1 -1
- data/lib/mtproto/tl/objects/sign_in.rb +1 -1
- data/lib/mtproto/tl/objects/update.rb +1 -1
- data/lib/mtproto/tl/objects/update_short.rb +2 -2
- data/lib/mtproto/tl/objects/update_short_message.rb +1 -1
- data/lib/mtproto/tl/objects/update_short_sent_message.rb +36 -0
- data/lib/mtproto/tl/objects/update_username.rb +39 -0
- data/lib/mtproto/tl/objects/updates_difference.rb +93 -14
- data/lib/mtproto/tl/objects/updates_state.rb +1 -1
- data/lib/mtproto/tl/objects/upload_profile_photo.rb +65 -0
- data/lib/mtproto/tl/objects/users.rb +1 -1
- data/lib/mtproto/version.rb +1 -1
- metadata +49 -1
|
@@ -16,7 +16,7 @@ module MTProto
|
|
|
16
16
|
!@sign_up_required && @user_id
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def self.
|
|
19
|
+
def self.deserialize(data)
|
|
20
20
|
constructor = data[0, 4].unpack1('L<')
|
|
21
21
|
|
|
22
22
|
return new(sign_up_required: true) if constructor == Constructors::AUTH_AUTHORIZATION_SIGN_UP_REQUIRED
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
# channels.createChannel — create a broadcast channel or, with the megagroup
|
|
6
|
+
# flag, a supergroup. Returns Updates carrying the new channel.
|
|
7
|
+
class ChannelsCreateChannel
|
|
8
|
+
include Binary
|
|
9
|
+
|
|
10
|
+
CONSTRUCTOR = 0x91006707
|
|
11
|
+
|
|
12
|
+
def initialize(title:, about: '', megagroup: true, broadcast: false)
|
|
13
|
+
@title = title
|
|
14
|
+
@about = about
|
|
15
|
+
@megagroup = megagroup
|
|
16
|
+
@broadcast = broadcast
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def serialize
|
|
20
|
+
flags = 0
|
|
21
|
+
flags |= (1 << 0) if @broadcast
|
|
22
|
+
flags |= (1 << 1) if @megagroup
|
|
23
|
+
|
|
24
|
+
result = u32_b(CONSTRUCTOR)
|
|
25
|
+
result += u32_b(flags)
|
|
26
|
+
result += serialize_tl_string(@title)
|
|
27
|
+
result += serialize_tl_string(@about)
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def serialize_tl_string(str)
|
|
34
|
+
bytes = str.to_s.b.bytes
|
|
35
|
+
length = bytes.length
|
|
36
|
+
if length <= 253
|
|
37
|
+
[length] + bytes + padding(length + 1)
|
|
38
|
+
else
|
|
39
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def padding(current_length)
|
|
44
|
+
[0] * ((4 - (current_length % 4)) % 4)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsDeleteMessages
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x84c1fd4e
|
|
9
|
+
|
|
10
|
+
def initialize(channel:, ids:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
@ids = ids
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def serialize
|
|
16
|
+
result = u32_b(CONSTRUCTOR)
|
|
17
|
+
result += serialize_input_channel
|
|
18
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
19
|
+
@ids.each { |id| result += u32_b(id) }
|
|
20
|
+
result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def serialize_input_channel
|
|
26
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
27
|
+
u64_b(@channel[:id]) +
|
|
28
|
+
u64_b(@channel[:access_hash])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsDeleteParticipantHistory
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x367544db
|
|
9
|
+
|
|
10
|
+
def initialize(channel:, participant:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
@participant = participant
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def serialize
|
|
16
|
+
result = u32_b(CONSTRUCTOR)
|
|
17
|
+
result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
|
|
18
|
+
result += serialize_input_peer(@participant)
|
|
19
|
+
result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def serialize_input_peer(peer)
|
|
25
|
+
case peer[:type]
|
|
26
|
+
when :user
|
|
27
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
|
|
28
|
+
else
|
|
29
|
+
raise "Unsupported participant peer type: #{peer[:type]}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsEditBanned
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x96e6cd81
|
|
9
|
+
CHAT_BANNED_RIGHTS = 0x9f120418
|
|
10
|
+
|
|
11
|
+
def initialize(channel:, participant:, view_messages: false, send_messages: false, until_date: 0)
|
|
12
|
+
@channel = channel
|
|
13
|
+
@participant = participant
|
|
14
|
+
@view_messages = view_messages
|
|
15
|
+
@send_messages = send_messages
|
|
16
|
+
@until_date = until_date
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def serialize
|
|
20
|
+
result = u32_b(CONSTRUCTOR)
|
|
21
|
+
result += serialize_input_channel
|
|
22
|
+
result += serialize_input_peer(@participant)
|
|
23
|
+
result += serialize_banned_rights
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def serialize_banned_rights
|
|
30
|
+
flags = 0
|
|
31
|
+
flags |= (1 << 0) if @view_messages
|
|
32
|
+
flags |= (1 << 1) if @send_messages
|
|
33
|
+
u32_b(CHAT_BANNED_RIGHTS) + u32_b(flags) + u32_b(@until_date)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def serialize_input_channel
|
|
37
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
38
|
+
u64_b(@channel[:id]) +
|
|
39
|
+
u64_b(@channel[:access_hash])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def serialize_input_peer(peer)
|
|
43
|
+
case peer[:type]
|
|
44
|
+
when :user
|
|
45
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
|
|
46
|
+
when :channel
|
|
47
|
+
u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
|
|
48
|
+
else
|
|
49
|
+
raise "Unsupported participant peer type: #{peer[:type]}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsGetMessages
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xad8c9a23
|
|
9
|
+
INPUT_MESSAGE_ID = 0xa676a322
|
|
10
|
+
|
|
11
|
+
def initialize(channel:, ids:)
|
|
12
|
+
@channel = channel
|
|
13
|
+
@ids = ids
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serialize
|
|
17
|
+
result = u32_b(CONSTRUCTOR)
|
|
18
|
+
result += serialize_input_channel
|
|
19
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
20
|
+
@ids.each { |id| result += u32_b(INPUT_MESSAGE_ID) + u32_b(id) }
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def serialize_input_channel
|
|
27
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
28
|
+
u64_b(@channel[:id]) +
|
|
29
|
+
u64_b(@channel[:access_hash])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsGetParticipant
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xa0ab6cc6
|
|
9
|
+
|
|
10
|
+
def initialize(channel:, participant:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
@participant = participant
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def serialize
|
|
16
|
+
result = u32_b(CONSTRUCTOR)
|
|
17
|
+
result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
|
|
18
|
+
result += serialize_input_peer(@participant)
|
|
19
|
+
result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def serialize_input_peer(peer)
|
|
25
|
+
case peer[:type]
|
|
26
|
+
when :user
|
|
27
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
|
|
28
|
+
when :self
|
|
29
|
+
u32_b(Constructors::INPUT_PEER_SELF)
|
|
30
|
+
else
|
|
31
|
+
raise "Unsupported participant peer type: #{peer[:type]}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsJoinChannel
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x24b524c5
|
|
9
|
+
|
|
10
|
+
def initialize(channel:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def serialize
|
|
15
|
+
u32_b(CONSTRUCTOR) +
|
|
16
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
17
|
+
u64_b(@channel[:id]) +
|
|
18
|
+
u64_b(@channel[:access_hash])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsLeaveChannel
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xf836aa95
|
|
9
|
+
|
|
10
|
+
def initialize(channel:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def serialize
|
|
15
|
+
u32_b(CONSTRUCTOR) +
|
|
16
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
17
|
+
u64_b(@channel[:id]) +
|
|
18
|
+
u64_b(@channel[:access_hash])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ChannelsReportSpam
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xf44a8315
|
|
9
|
+
|
|
10
|
+
def initialize(channel:, participant:, ids:)
|
|
11
|
+
@channel = channel
|
|
12
|
+
@participant = participant
|
|
13
|
+
@ids = ids
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serialize
|
|
17
|
+
result = u32_b(CONSTRUCTOR)
|
|
18
|
+
result += u32_b(Constructors::INPUT_CHANNEL) + u64_b(@channel[:id]) + u64_b(@channel[:access_hash])
|
|
19
|
+
result += serialize_input_peer(@participant)
|
|
20
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
21
|
+
@ids.each { |id| result += u32_b(id) }
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def serialize_input_peer(peer)
|
|
28
|
+
case peer[:type]
|
|
29
|
+
when :user
|
|
30
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(peer[:id]) + u64_b(peer[:access_hash] || 0)
|
|
31
|
+
else
|
|
32
|
+
raise "Unsupported participant peer type: #{peer[:type]}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../schema'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
class Contacts
|
|
8
|
+
Contact = Struct.new(:user_id, :mutual, keyword_init: true)
|
|
9
|
+
User = Struct.new(:id, :first_name, :last_name, :access_hash, keyword_init: true)
|
|
10
|
+
|
|
11
|
+
SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
|
|
12
|
+
|
|
13
|
+
attr_reader :contacts, :saved_count, :users, :not_modified
|
|
14
|
+
|
|
15
|
+
def initialize(contacts: [], saved_count: 0, users: [], not_modified: false)
|
|
16
|
+
@contacts = contacts
|
|
17
|
+
@saved_count = saved_count
|
|
18
|
+
@users = users
|
|
19
|
+
@not_modified = not_modified
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def not_modified?
|
|
23
|
+
@not_modified
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.schema
|
|
27
|
+
@schema ||= Schema.new(SCHEMA_PATH)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.deserialize(data)
|
|
31
|
+
constructor = data[0, 4].unpack1('L<')
|
|
32
|
+
return new(not_modified: true) if constructor == Constructors::CONTACTS_CONTACTS_NOT_MODIFIED
|
|
33
|
+
raise UnexpectedConstructorError, constructor unless constructor == Constructors::CONTACTS_CONTACTS
|
|
34
|
+
|
|
35
|
+
offset = 4
|
|
36
|
+
contacts, offset = parse_contacts_vector(data, offset)
|
|
37
|
+
saved_count = data[offset, 4].unpack1('l<')
|
|
38
|
+
offset += 4
|
|
39
|
+
users, = parse_users_vector(data, offset)
|
|
40
|
+
|
|
41
|
+
new(contacts: contacts, saved_count: saved_count, users: users)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
USER_EMPTY = 0xd3bc4b7a
|
|
45
|
+
private_constant :USER_EMPTY
|
|
46
|
+
|
|
47
|
+
class << self
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def parse_contacts_vector(data, offset)
|
|
51
|
+
offset += 4 # vector tag
|
|
52
|
+
count = data[offset, 4].unpack1('L<')
|
|
53
|
+
offset += 4
|
|
54
|
+
|
|
55
|
+
contacts = []
|
|
56
|
+
count.times do
|
|
57
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
58
|
+
offset += 4
|
|
59
|
+
raise "Unknown Contact constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::CONTACT
|
|
60
|
+
|
|
61
|
+
user_id = data[offset, 8].unpack1('Q<')
|
|
62
|
+
offset += 8
|
|
63
|
+
mutual_tag = data[offset, 4].unpack1('L<')
|
|
64
|
+
offset += 4
|
|
65
|
+
mutual = mutual_tag == Constructors::BOOL_TRUE
|
|
66
|
+
contacts << Contact.new(user_id: user_id, mutual: mutual)
|
|
67
|
+
end
|
|
68
|
+
[contacts, offset]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# User parsing mirrors TL::Dialogs.parse_user (user#020b1422). Kept private here
|
|
72
|
+
# so this class can be used without instantiating Dialogs.
|
|
73
|
+
def parse_users_vector(data, offset)
|
|
74
|
+
offset += 4 # vector tag
|
|
75
|
+
count = data[offset, 4].unpack1('L<')
|
|
76
|
+
offset += 4
|
|
77
|
+
|
|
78
|
+
users = []
|
|
79
|
+
count.times do
|
|
80
|
+
user, offset = parse_user(data, offset)
|
|
81
|
+
users << user if user
|
|
82
|
+
end
|
|
83
|
+
[users, offset]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def parse_user(data, offset)
|
|
87
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
88
|
+
offset += 4
|
|
89
|
+
|
|
90
|
+
if constructor == USER_EMPTY
|
|
91
|
+
offset += 8
|
|
92
|
+
return [nil, offset]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
raise "Unknown User constructor: 0x#{constructor.to_s(16)}" unless constructor == Constructors::USER
|
|
96
|
+
|
|
97
|
+
flags = data[offset, 4].unpack1('L<')
|
|
98
|
+
offset += 4
|
|
99
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
100
|
+
offset += 4
|
|
101
|
+
id = data[offset, 8].unpack1('Q<')
|
|
102
|
+
offset += 8
|
|
103
|
+
access_hash = nil
|
|
104
|
+
if flags.anybits?(1 << 0)
|
|
105
|
+
access_hash = data[offset, 8].unpack1('Q<')
|
|
106
|
+
offset += 8
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
first_name = nil
|
|
110
|
+
first_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 1)
|
|
111
|
+
last_name = nil
|
|
112
|
+
last_name, offset = read_tl_string(data, offset) if flags.anybits?(1 << 2)
|
|
113
|
+
|
|
114
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 3) # username
|
|
115
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 4) # phone
|
|
116
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 5) # photo
|
|
117
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 6) # status
|
|
118
|
+
offset += 4 if flags.anybits?(1 << 14) # bot_info_version
|
|
119
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags.anybits?(1 << 18)
|
|
120
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 19) # bot_inline_placeholder
|
|
121
|
+
offset = skip_tl_string(data, offset) if flags.anybits?(1 << 22) # lang_code
|
|
122
|
+
offset = schema.skip(data, offset) if flags.anybits?(1 << 30) # emoji_status
|
|
123
|
+
offset = schema.skip_vector(data, offset) { |d, o| schema.skip(d, o) } if flags2.anybits?(1 << 0)
|
|
124
|
+
offset += 4 if flags2.anybits?(1 << 5) # stories_max_id
|
|
125
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 8) # color
|
|
126
|
+
offset = schema.skip(data, offset) if flags2.anybits?(1 << 9) # profile_color
|
|
127
|
+
offset += 4 if flags2.anybits?(1 << 12) # bot_active_users
|
|
128
|
+
offset += 8 if flags2.anybits?(1 << 14) # bot_verification_icon
|
|
129
|
+
offset += 8 if flags2.anybits?(1 << 15) # send_paid_messages_stars
|
|
130
|
+
|
|
131
|
+
[User.new(id: id, first_name: first_name, last_name: last_name, access_hash: access_hash), offset]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def read_tl_string(data, offset)
|
|
135
|
+
first_byte = data.getbyte(offset)
|
|
136
|
+
if first_byte == 254
|
|
137
|
+
len = data.getbyte(offset + 1) |
|
|
138
|
+
(data.getbyte(offset + 2) << 8) |
|
|
139
|
+
(data.getbyte(offset + 3) << 16)
|
|
140
|
+
total = 4 + len
|
|
141
|
+
else
|
|
142
|
+
len = first_byte
|
|
143
|
+
total = 1 + len
|
|
144
|
+
end
|
|
145
|
+
padding = (4 - (total % 4)) % 4
|
|
146
|
+
[data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def skip_tl_string(data, offset)
|
|
150
|
+
_, new_offset = read_tl_string(data, offset)
|
|
151
|
+
new_offset
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class CreateBot
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xe5b17f2b
|
|
9
|
+
INPUT_USER = 0xf21158c6
|
|
10
|
+
INPUT_USER_SELF = 0xf7c1b13f
|
|
11
|
+
|
|
12
|
+
# manager: { id:, access_hash: } or { type: :self }
|
|
13
|
+
def initialize(name:, username:, manager:, via_deeplink: false)
|
|
14
|
+
@name = name
|
|
15
|
+
@username = username
|
|
16
|
+
@manager = manager
|
|
17
|
+
@via_deeplink = via_deeplink
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def serialize
|
|
21
|
+
flags = 0
|
|
22
|
+
flags |= (1 << 0) if @via_deeplink
|
|
23
|
+
|
|
24
|
+
result = u32_b(CONSTRUCTOR)
|
|
25
|
+
result += u32_b(flags)
|
|
26
|
+
result += serialize_tl_string(@name)
|
|
27
|
+
result += serialize_tl_string(@username)
|
|
28
|
+
result += if @manager[:type] == :self
|
|
29
|
+
u32_b(INPUT_USER_SELF)
|
|
30
|
+
else
|
|
31
|
+
u32_b(INPUT_USER) + u64_b(@manager[:id]) + u64_b(@manager[:access_hash] || 0)
|
|
32
|
+
end
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def serialize_tl_string(str)
|
|
39
|
+
bytes = str.encode('UTF-8').bytes
|
|
40
|
+
length = bytes.length
|
|
41
|
+
if length <= 253
|
|
42
|
+
[length] + bytes + padding(length + 1)
|
|
43
|
+
else
|
|
44
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def padding(current_length)
|
|
49
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
50
|
+
[0] * pad_length
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class DeleteMessages
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def initialize(ids:, revoke: false)
|
|
9
|
+
@ids = ids
|
|
10
|
+
@revoke = revoke
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def serialize
|
|
14
|
+
flags = 0
|
|
15
|
+
flags |= (1 << 0) if @revoke
|
|
16
|
+
|
|
17
|
+
result = u32_b(Constructors::MESSAGES_DELETE_MESSAGES)
|
|
18
|
+
result += u32_b(flags)
|
|
19
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
20
|
+
@ids.each { |id| result += u32_b(id) }
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -5,7 +5,7 @@ require_relative '../schema'
|
|
|
5
5
|
module MTProto
|
|
6
6
|
module TL
|
|
7
7
|
class Dialogs
|
|
8
|
-
Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, keyword_init: true)
|
|
8
|
+
Dialog = Struct.new(:peer_type, :peer_id, :top_message, :unread_count, :pts, keyword_init: true)
|
|
9
9
|
Chat = Struct.new(:id, :title, :type, :access_hash, keyword_init: true)
|
|
10
10
|
|
|
11
11
|
SCHEMA_PATH = File.expand_path('../../../../data/tl-schema.json', __dir__)
|
|
@@ -28,7 +28,7 @@ module MTProto
|
|
|
28
28
|
@schema ||= Schema.new(SCHEMA_PATH)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def self.
|
|
31
|
+
def self.deserialize(data)
|
|
32
32
|
constructor = data[0, 4].unpack1('L<')
|
|
33
33
|
|
|
34
34
|
unless [Constructors::MESSAGES_DIALOGS, Constructors::MESSAGES_DIALOGS_SLICE].include?(constructor)
|
|
@@ -51,6 +51,16 @@ module MTProto
|
|
|
51
51
|
new(dialogs: dialogs, chats: chats, users: users, count: count, message_dates: message_dates)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
# Parse a chats:Vector<Chat> / users:Vector<User> at an arbitrary offset.
|
|
55
|
+
# Reused by UpdatesDifference — the same vectors appear in updates.difference.
|
|
56
|
+
def self.chats_at(data, offset)
|
|
57
|
+
parse_chats_vector(data, offset)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.users_at(data, offset)
|
|
61
|
+
parse_users_vector(data, offset)
|
|
62
|
+
end
|
|
63
|
+
|
|
54
64
|
class << self
|
|
55
65
|
private
|
|
56
66
|
|
|
@@ -97,13 +107,17 @@ module MTProto
|
|
|
97
107
|
|
|
98
108
|
offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
pts = nil
|
|
111
|
+
if flags.anybits?(1 << 0)
|
|
112
|
+
pts = data[offset, 4].unpack1('L<')
|
|
113
|
+
offset += 4
|
|
114
|
+
end
|
|
101
115
|
offset = schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
|
|
102
116
|
offset += 4 if flags.anybits?(1 << 4) # folder_id
|
|
103
117
|
offset += 4 if flags.anybits?(1 << 5) # ttl_period
|
|
104
118
|
|
|
105
119
|
[Dialog.new(peer_type: peer_type, peer_id: peer_id,
|
|
106
|
-
top_message: top_message, unread_count: unread_count), offset]
|
|
120
|
+
top_message: top_message, unread_count: unread_count, pts: pts), offset]
|
|
107
121
|
end
|
|
108
122
|
|
|
109
123
|
def parse_peer(data, offset)
|