mtproto 0.0.13 → 0.0.14
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 +2 -2
- data/lib/mtproto/client.rb +28 -18
- 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/delete_messages.rb +25 -0
- data/lib/mtproto/tl/objects/dh_gen_response.rb +1 -1
- data/lib/mtproto/tl/objects/dialogs.rb +8 -4
- data/lib/mtproto/tl/objects/edit_message.rb +58 -0
- data/lib/mtproto/tl/objects/export_login_token.rb +1 -1
- data/lib/mtproto/tl/objects/forward_messages.rb +49 -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/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/pq_inner_data.rb +1 -1
- data/lib/mtproto/tl/objects/raw_response.rb +29 -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/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_code.rb +1 -1
- data/lib/mtproto/tl/objects/send_media.rb +85 -0
- data/lib/mtproto/tl/objects/send_message.rb +70 -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_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 +2 -2
- data/lib/mtproto/tl/objects/updates_state.rb +1 -1
- data/lib/mtproto/tl/objects/users.rb +1 -1
- data/lib/mtproto/version.rb +1 -1
- metadata +36 -1
|
@@ -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,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)
|
|
@@ -97,13 +97,17 @@ module MTProto
|
|
|
97
97
|
|
|
98
98
|
offset = schema.skip(data, offset) # notify_settings (PeerNotifySettings)
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
pts = nil
|
|
101
|
+
if flags.anybits?(1 << 0)
|
|
102
|
+
pts = data[offset, 4].unpack1('L<')
|
|
103
|
+
offset += 4
|
|
104
|
+
end
|
|
101
105
|
offset = schema.skip(data, offset) if flags.anybits?(1 << 1) # draft (DraftMessage)
|
|
102
106
|
offset += 4 if flags.anybits?(1 << 4) # folder_id
|
|
103
107
|
offset += 4 if flags.anybits?(1 << 5) # ttl_period
|
|
104
108
|
|
|
105
109
|
[Dialog.new(peer_type: peer_type, peer_id: peer_id,
|
|
106
|
-
top_message: top_message, unread_count: unread_count), offset]
|
|
110
|
+
top_message: top_message, unread_count: unread_count, pts: pts), offset]
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
def parse_peer(data, offset)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class EditMessage
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def initialize(peer:, id:, message:)
|
|
9
|
+
@peer = peer
|
|
10
|
+
@id = id
|
|
11
|
+
@message = message
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def serialize
|
|
15
|
+
flags = 1 << 11 # message field present
|
|
16
|
+
result = u32_b(Constructors::MESSAGES_EDIT_MESSAGE)
|
|
17
|
+
result += u32_b(flags)
|
|
18
|
+
result += serialize_input_peer
|
|
19
|
+
result += u32_b(@id)
|
|
20
|
+
result += serialize_tl_string(@message)
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def serialize_input_peer
|
|
27
|
+
case @peer[:type]
|
|
28
|
+
when :self
|
|
29
|
+
u32_b(Constructors::INPUT_PEER_SELF)
|
|
30
|
+
when :user
|
|
31
|
+
u32_b(Constructors::INPUT_PEER_USER) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
|
|
32
|
+
when :chat
|
|
33
|
+
u32_b(Constructors::INPUT_PEER_CHAT) + u64_b(@peer[:id])
|
|
34
|
+
when :channel
|
|
35
|
+
u32_b(Constructors::INPUT_PEER_CHANNEL) + u64_b(@peer[:id]) + u64_b(@peer[:access_hash] || 0)
|
|
36
|
+
else
|
|
37
|
+
raise "Unknown peer type: #{@peer[:type]}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def serialize_tl_string(str)
|
|
42
|
+
bytes = str.encode('UTF-8').bytes
|
|
43
|
+
length = bytes.length
|
|
44
|
+
|
|
45
|
+
if length <= 253
|
|
46
|
+
[length] + bytes + padding(length + 1)
|
|
47
|
+
else
|
|
48
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def padding(current_length)
|
|
53
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
54
|
+
[0] * pad_length
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
class ForwardMessages
|
|
8
|
+
include Binary
|
|
9
|
+
|
|
10
|
+
CONSTRUCTOR = 0x978928ca
|
|
11
|
+
|
|
12
|
+
def initialize(from_peer:, to_peer:, ids:, random_ids: nil)
|
|
13
|
+
@from_peer = from_peer
|
|
14
|
+
@to_peer = to_peer
|
|
15
|
+
@ids = ids
|
|
16
|
+
@random_ids = random_ids || Array.new(ids.length) { SecureRandom.random_number(2**63) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def serialize
|
|
20
|
+
result = u32_b(CONSTRUCTOR)
|
|
21
|
+
result += u32_b(0) # flags
|
|
22
|
+
result += serialize_input_peer(@from_peer)
|
|
23
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
24
|
+
@ids.each { |id| result += u32_b(id) }
|
|
25
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@random_ids.length)
|
|
26
|
+
@random_ids.each { |rid| result += u64_b(rid) }
|
|
27
|
+
result += serialize_input_peer(@to_peer)
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def serialize_input_peer(peer)
|
|
34
|
+
case peer[:type]
|
|
35
|
+
when :self
|
|
36
|
+
u32_b(Constructors::INPUT_PEER_SELF)
|
|
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,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetChannelDifference
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def initialize(channel:, pts:, limit: 100, force: false)
|
|
9
|
+
@channel = channel
|
|
10
|
+
@pts = pts
|
|
11
|
+
@limit = limit
|
|
12
|
+
@force = force
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def serialize
|
|
16
|
+
flags = 0
|
|
17
|
+
flags |= (1 << 0) if @force
|
|
18
|
+
|
|
19
|
+
result = u32_b(Constructors::UPDATES_GET_CHANNEL_DIFFERENCE)
|
|
20
|
+
result += u32_b(flags)
|
|
21
|
+
result += serialize_input_channel
|
|
22
|
+
result += u32_b(Constructors::CHANNEL_MESSAGES_FILTER_EMPTY)
|
|
23
|
+
result += u32_b(@pts)
|
|
24
|
+
result += u32_b(@limit)
|
|
25
|
+
result
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def serialize_input_channel
|
|
31
|
+
if @channel.nil? || @channel[:type] == :empty
|
|
32
|
+
u32_b(Constructors::INPUT_CHANNEL_EMPTY)
|
|
33
|
+
else
|
|
34
|
+
u32_b(Constructors::INPUT_CHANNEL) +
|
|
35
|
+
u64_b(@channel[:id]) +
|
|
36
|
+
u64_b(@channel[:access_hash])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetContacts
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def initialize(hash: 0)
|
|
9
|
+
@hash = hash
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def serialize
|
|
13
|
+
u32_b(Constructors::CONTACTS_GET_CONTACTS) + u64_b(@hash)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class GetFile
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xbe5335be
|
|
9
|
+
INPUT_PHOTO_FILE_LOCATION = 0x40181ffe
|
|
10
|
+
|
|
11
|
+
# location: { id:, access_hash:, file_reference: (binary String), thumb_size: (String) }
|
|
12
|
+
def initialize(location:, offset: 0, limit: 1024 * 1024)
|
|
13
|
+
@location = location
|
|
14
|
+
@offset = offset
|
|
15
|
+
@limit = limit
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def serialize
|
|
19
|
+
result = u32_b(CONSTRUCTOR)
|
|
20
|
+
result += u32_b(0) # flags
|
|
21
|
+
result += serialize_location
|
|
22
|
+
result += u64_b(@offset)
|
|
23
|
+
result += u32_b(@limit)
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def serialize_location
|
|
30
|
+
u32_b(INPUT_PHOTO_FILE_LOCATION) +
|
|
31
|
+
u64_b(@location[:id]) +
|
|
32
|
+
u64_b(@location[:access_hash]) +
|
|
33
|
+
serialize_tl_bytes(@location[:file_reference]) +
|
|
34
|
+
serialize_tl_bytes(@location[:thumb_size])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def serialize_tl_bytes(value)
|
|
38
|
+
bytes = value.to_s.b.bytes
|
|
39
|
+
length = bytes.length
|
|
40
|
+
|
|
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
|