mtproto 0.0.14 → 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/rpc.rb +33 -0
- data/lib/mtproto/client.rb +54 -1
- data/lib/mtproto/file_downloader.rb +122 -0
- data/lib/mtproto/tl/constructor_names.rb +324 -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/create_bot.rb +54 -0
- data/lib/mtproto/tl/objects/dialogs.rb +25 -10
- data/lib/mtproto/tl/objects/edit_access_settings.rb +46 -0
- data/lib/mtproto/tl/objects/export_authorization.rb +17 -0
- data/lib/mtproto/tl/objects/export_bot_token.rb +32 -0
- data/lib/mtproto/tl/objects/exported_authorization.rb +32 -0
- data/lib/mtproto/tl/objects/exported_bot_token.rb +27 -0
- data/lib/mtproto/tl/objects/forward_messages.rb +12 -3
- data/lib/mtproto/tl/objects/get_access_settings.rb +28 -0
- 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/input_keyboard_button_request_peer.rb +54 -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/messages_get_messages.rb +26 -0
- data/lib/mtproto/tl/objects/reply_inline_markup.rb +30 -0
- data/lib/mtproto/tl/objects/reply_keyboard_markup.rb +35 -0
- data/lib/mtproto/tl/objects/request_peer_type_create_bot.rb +47 -0
- data/lib/mtproto/tl/objects/reset_bot_commands.rb +45 -0
- data/lib/mtproto/tl/objects/send_bot_requested_peer.rb +51 -0
- data/lib/mtproto/tl/objects/send_media.rb +87 -4
- data/lib/mtproto/tl/objects/send_message.rb +7 -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 +53 -0
- data/lib/mtproto/tl/objects/set_bot_guest_chat_result.rb +84 -0
- data/lib/mtproto/tl/objects/set_bot_info.rb +64 -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 +45 -69
- data/lib/mtproto/tl/objects/upload_profile_photo.rb +65 -0
- 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 +38 -2
- data/lib/mtproto/message/message.rb +0 -85
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'bot_command_scope'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
# bots.getBotCommands — fetch the bot's commands for a given scope/language.
|
|
8
|
+
# Returns Vector<BotCommand>. Result parsing belongs to a later layer; this
|
|
9
|
+
# object only builds the request.
|
|
10
|
+
class GetBotCommands
|
|
11
|
+
include Binary
|
|
12
|
+
|
|
13
|
+
CONSTRUCTOR = 0xe34c0dd6
|
|
14
|
+
|
|
15
|
+
# scope: BotCommandScope hash (see BotCommandScope); defaults to default scope.
|
|
16
|
+
def initialize(scope: { type: :default }, lang_code: '')
|
|
17
|
+
@scope = scope
|
|
18
|
+
@lang_code = lang_code
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def serialize
|
|
22
|
+
result = u32_b(CONSTRUCTOR)
|
|
23
|
+
result += BotCommandScope.serialize(@scope)
|
|
24
|
+
result += serialize_tl_string(@lang_code)
|
|
25
|
+
result
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def serialize_tl_string(str)
|
|
31
|
+
bytes = str.to_s.encode('UTF-8').bytes
|
|
32
|
+
length = bytes.length
|
|
33
|
+
if length <= 253
|
|
34
|
+
[length] + bytes + padding(length + 1)
|
|
35
|
+
else
|
|
36
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def padding(current_length)
|
|
41
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
42
|
+
[0] * pad_length
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -7,10 +7,18 @@ module MTProto
|
|
|
7
7
|
|
|
8
8
|
CONSTRUCTOR = 0xbe5335be
|
|
9
9
|
INPUT_PHOTO_FILE_LOCATION = 0x40181ffe
|
|
10
|
+
INPUT_DOCUMENT_FILE_LOCATION = 0xbad07584
|
|
11
|
+
|
|
12
|
+
LOCATION_CONSTRUCTORS = {
|
|
13
|
+
photo: INPUT_PHOTO_FILE_LOCATION,
|
|
14
|
+
document: INPUT_DOCUMENT_FILE_LOCATION
|
|
15
|
+
}.freeze
|
|
10
16
|
|
|
11
17
|
# location: { id:, access_hash:, file_reference: (binary String), thumb_size: (String) }
|
|
12
|
-
|
|
18
|
+
# type: :photo for photos, :document for documents/voice/video/audio.
|
|
19
|
+
def initialize(location:, type: :photo, offset: 0, limit: 1024 * 1024)
|
|
13
20
|
@location = location
|
|
21
|
+
@type = type
|
|
14
22
|
@offset = offset
|
|
15
23
|
@limit = limit
|
|
16
24
|
end
|
|
@@ -27,7 +35,7 @@ module MTProto
|
|
|
27
35
|
private
|
|
28
36
|
|
|
29
37
|
def serialize_location
|
|
30
|
-
u32_b(
|
|
38
|
+
u32_b(LOCATION_CONSTRUCTORS.fetch(@type)) +
|
|
31
39
|
u64_b(@location[:id]) +
|
|
32
40
|
u64_b(@location[:access_hash]) +
|
|
33
41
|
serialize_tl_bytes(@location[:file_reference]) +
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ImportAuthorization
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
def initialize(id:, bytes:)
|
|
9
|
+
@id = id
|
|
10
|
+
@bytes = bytes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def serialize
|
|
14
|
+
u32_b(Constructors::AUTH_IMPORT_AUTHORIZATION) +
|
|
15
|
+
u64_b(@id) +
|
|
16
|
+
serialize_tl_bytes(@bytes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def serialize_tl_bytes(value)
|
|
22
|
+
bytes = value.to_s.b.bytes
|
|
23
|
+
length = bytes.length
|
|
24
|
+
|
|
25
|
+
if length <= 253
|
|
26
|
+
[length] + bytes + padding(length + 1)
|
|
27
|
+
else
|
|
28
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def padding(current_length)
|
|
33
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
34
|
+
[0] * pad_length
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class InputKeyboardButtonRequestPeer
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x02b78156
|
|
9
|
+
|
|
10
|
+
def initialize(text:, button_id:, peer_type:, max_quantity: 1,
|
|
11
|
+
name_requested: false, username_requested: false, photo_requested: false)
|
|
12
|
+
@text = text
|
|
13
|
+
@button_id = button_id
|
|
14
|
+
@peer_type = peer_type
|
|
15
|
+
@max_quantity = max_quantity
|
|
16
|
+
@name_requested = name_requested
|
|
17
|
+
@username_requested = username_requested
|
|
18
|
+
@photo_requested = photo_requested
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def serialize
|
|
22
|
+
flags = 0
|
|
23
|
+
flags |= (1 << 0) if @name_requested
|
|
24
|
+
flags |= (1 << 1) if @username_requested
|
|
25
|
+
flags |= (1 << 2) if @photo_requested
|
|
26
|
+
|
|
27
|
+
result = u32_b(CONSTRUCTOR)
|
|
28
|
+
result += u32_b(flags)
|
|
29
|
+
result += serialize_tl_string(@text)
|
|
30
|
+
result += u32_b(@button_id)
|
|
31
|
+
result += @peer_type.serialize
|
|
32
|
+
result += u32_b(@max_quantity)
|
|
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,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class KeyboardButtonCallback
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xe62bc960
|
|
9
|
+
|
|
10
|
+
# data: raw callback payload bytes echoed back to the bot on press.
|
|
11
|
+
def initialize(text:, data:, requires_password: false)
|
|
12
|
+
@text = text
|
|
13
|
+
@data = data
|
|
14
|
+
@requires_password = requires_password
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def serialize
|
|
18
|
+
flags = 0
|
|
19
|
+
flags |= (1 << 0) if @requires_password
|
|
20
|
+
|
|
21
|
+
result = u32_b(CONSTRUCTOR)
|
|
22
|
+
result += u32_b(flags)
|
|
23
|
+
result += serialize_tl_string(@text)
|
|
24
|
+
result += serialize_tl_bytes(@data)
|
|
25
|
+
result
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def serialize_tl_string(str)
|
|
31
|
+
serialize_tl_bytes(str.to_s.encode('UTF-8').b)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def serialize_tl_bytes(bytes_str)
|
|
35
|
+
bytes = bytes_str.to_s.b.bytes
|
|
36
|
+
length = bytes.length
|
|
37
|
+
if length <= 253
|
|
38
|
+
[length] + bytes + padding(length + 1)
|
|
39
|
+
else
|
|
40
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def padding(current_length)
|
|
45
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
46
|
+
[0] * pad_length
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -1,37 +1,113 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../reader'
|
|
4
|
+
|
|
3
5
|
module MTProto
|
|
4
6
|
module TL
|
|
7
|
+
# The high-level chat-message TL structure `message#7600b9d3` (text/media/
|
|
8
|
+
# fwd_from/reply/sender/...). Owns the field walk for that one constructor; the
|
|
9
|
+
# low-level TL reads it needs (peer/string/bytes/media/document, plus the
|
|
10
|
+
# shared schema) live in Reader and are reused here.
|
|
11
|
+
#
|
|
12
|
+
# Reading is decoupled from alignment: `.deserialize` only READS the fields we
|
|
13
|
+
# surface and returns a Message (or nil for a non-`message` constructor — e.g.
|
|
14
|
+
# messageService/messageEmpty). Advancing past the whole object is the caller's
|
|
15
|
+
# job via `Schema#skip`, so any unmodelled tail never drops the message. This is
|
|
16
|
+
# the single parser for all three inbound paths (live `Updates` push,
|
|
17
|
+
# `updates.getDifference`, and `messages.getHistory`).
|
|
5
18
|
class Message
|
|
6
|
-
|
|
19
|
+
MESSAGE = Constructors::MESSAGE
|
|
20
|
+
MESSAGE_FWD_HEADER = 0x4e4df4bb
|
|
7
21
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
FIELDS = %i[id date message peer_type peer_id from_type from_id out media
|
|
23
|
+
document photo is_reply reply_to_msg_id fwd_from_type fwd_from_id].freeze
|
|
24
|
+
|
|
25
|
+
attr_reader(*FIELDS)
|
|
26
|
+
|
|
27
|
+
# `text` is the message body; a get_history-friendly alias for `message`.
|
|
28
|
+
alias text message
|
|
13
29
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
body_length = [@body.bytesize].pack('L<')
|
|
30
|
+
def initialize(attrs)
|
|
31
|
+
FIELDS.each { |f| instance_variable_set("@#{f}", attrs[f]) }
|
|
32
|
+
end
|
|
18
33
|
|
|
19
|
-
|
|
34
|
+
# The legacy hash shape the Updates / UpdatesDifference consumers expect.
|
|
35
|
+
def to_h
|
|
36
|
+
FIELDS.each_with_object({}) { |f, h| h[f] = public_send(f) }
|
|
20
37
|
end
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
class << self
|
|
40
|
+
def deserialize(data, offset, constructor)
|
|
41
|
+
return unless constructor == MESSAGE
|
|
42
|
+
|
|
43
|
+
offset += 4 # constructor
|
|
44
|
+
flags = data[offset, 4].unpack1('L<')
|
|
45
|
+
offset += 4
|
|
46
|
+
flags2 = data[offset, 4].unpack1('L<')
|
|
47
|
+
offset += 4
|
|
48
|
+
id = data[offset, 4].unpack1('l<')
|
|
49
|
+
offset += 4
|
|
50
|
+
|
|
51
|
+
from_type = from_id = nil
|
|
52
|
+
from_type, from_id, offset = x.parse_peer(data, offset) if flags.anybits?(1 << 8) # from_id (Peer)
|
|
53
|
+
offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
|
|
54
|
+
_, offset = x.read_tl_string(data, offset) if flags2.anybits?(1 << 12) # from_rank (layer 227)
|
|
55
|
+
peer_type, peer_id, offset = x.parse_peer(data, offset)
|
|
56
|
+
offset = x.schema.skip(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
|
|
57
|
+
fwd_from_type = fwd_from_id = nil
|
|
58
|
+
if flags.anybits?(1 << 2) # fwd_from
|
|
59
|
+
fwd_from_type, fwd_from_id = parse_fwd_origin(data, offset)
|
|
60
|
+
offset = x.schema.skip(data, offset)
|
|
61
|
+
end
|
|
62
|
+
offset += 8 if flags.anybits?(1 << 11) # via_bot_id
|
|
63
|
+
offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
|
|
64
|
+
offset = x.schema.skip(data, offset) if flags2.anybits?(1 << 19) # guestchat_via_from (layer 227, Peer)
|
|
65
|
+
|
|
66
|
+
is_reply = false
|
|
67
|
+
reply_to_msg_id = nil
|
|
68
|
+
if flags.anybits?(1 << 3) # reply_to
|
|
69
|
+
reply_to_msg_id, is_reply = x.parse_reply_to(data, offset)
|
|
70
|
+
offset = x.schema.skip(data, offset)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
date = data[offset, 4].unpack1('L<')
|
|
74
|
+
offset += 4
|
|
75
|
+
message_text, offset = x.read_tl_string(data, offset)
|
|
76
|
+
has_media = flags.anybits?(1 << 9)
|
|
77
|
+
media = has_media ? x.parse_media(data, offset) : nil
|
|
78
|
+
document = has_media ? x.parse_document(data, offset) : nil
|
|
79
|
+
photo = has_media ? x.parse_photo(data, offset) : nil
|
|
80
|
+
|
|
81
|
+
new(id: id, date: date, message: message_text, peer_type: peer_type, peer_id: peer_id,
|
|
82
|
+
from_type: from_type, from_id: from_id, out: flags.anybits?(1 << 1),
|
|
83
|
+
media: media, document: document, photo: photo, is_reply: is_reply,
|
|
84
|
+
reply_to_msg_id: reply_to_msg_id, fwd_from_type: fwd_from_type, fwd_from_id: fwd_from_id)
|
|
27
85
|
end
|
|
28
86
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# The low-level TL primitives, shared from Reader.
|
|
90
|
+
def x
|
|
91
|
+
Reader
|
|
92
|
+
end
|
|
33
93
|
|
|
34
|
-
|
|
94
|
+
# messageFwdHeader#4e4df4bb flags:# imported:flags.7?true from_id:flags.0?Peer
|
|
95
|
+
# ... — from_id is the forward origin (a peerChannel for a forwarded channel
|
|
96
|
+
# post). imported (flags.7) is a flag-only bool, so from_id sits right after
|
|
97
|
+
# the flags. => [type, id] or [nil, nil].
|
|
98
|
+
def parse_fwd_origin(data, offset)
|
|
99
|
+
return [nil, nil] unless data[offset, 4].unpack1('L<') == MESSAGE_FWD_HEADER
|
|
100
|
+
|
|
101
|
+
io = offset + 4
|
|
102
|
+
flags = data[io, 4].unpack1('L<')
|
|
103
|
+
io += 4
|
|
104
|
+
return [nil, nil] unless flags.anybits?(1 << 0) # from_id present
|
|
105
|
+
|
|
106
|
+
type, id, = x.parse_peer(data, io)
|
|
107
|
+
[type, id]
|
|
108
|
+
rescue StandardError
|
|
109
|
+
[nil, nil]
|
|
110
|
+
end
|
|
35
111
|
end
|
|
36
112
|
end
|
|
37
113
|
end
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../schema'
|
|
4
|
+
require_relative 'message'
|
|
4
5
|
|
|
5
6
|
module MTProto
|
|
6
7
|
module TL
|
|
8
|
+
# A messages.messages / messagesSlice / channelMessages container (e.g. the
|
|
9
|
+
# messages.getHistory reply). Its messages are TL::Message instances.
|
|
7
10
|
class Messages
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
MESSAGES_MESSAGES = 0x8c718e87
|
|
11
|
-
MESSAGES_SLICE = 0x762b263d
|
|
11
|
+
MESSAGES_MESSAGES = 0x1d73e7ea
|
|
12
|
+
MESSAGES_SLICE = 0x5f206716
|
|
12
13
|
MESSAGES_CHANNEL = 0xc776ba4e
|
|
13
14
|
|
|
14
|
-
MESSAGE = Constructors::MESSAGE
|
|
15
|
-
MESSAGE_SERVICE = 0x7a800e0a
|
|
16
|
-
MESSAGE_EMPTY = 0x90a6ca84
|
|
17
|
-
|
|
18
15
|
VALID_CONSTRUCTORS = [MESSAGES_MESSAGES, MESSAGES_SLICE, MESSAGES_CHANNEL].freeze
|
|
19
16
|
|
|
20
17
|
attr_reader :messages, :count
|
|
@@ -86,76 +83,12 @@ module MTProto
|
|
|
86
83
|
messages = []
|
|
87
84
|
count.times do
|
|
88
85
|
constructor = data[offset, 4].unpack1('L<')
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
msg, = parse_message(data, offset)
|
|
92
|
-
messages << msg if msg
|
|
93
|
-
end
|
|
94
|
-
|
|
86
|
+
msg = Message.deserialize(data, offset, constructor)
|
|
87
|
+
messages << msg if msg
|
|
95
88
|
offset = schema.skip(data, offset)
|
|
96
89
|
end
|
|
97
90
|
[messages, offset]
|
|
98
91
|
end
|
|
99
|
-
|
|
100
|
-
# message#9815cec8 flags:# flags2:# id:int
|
|
101
|
-
# from_id:flags.8?Peer ... date:int message:string ...
|
|
102
|
-
def parse_message(data, offset)
|
|
103
|
-
offset += 4 # constructor
|
|
104
|
-
flags = data[offset, 4].unpack1('L<')
|
|
105
|
-
offset += 4
|
|
106
|
-
flags2 = data[offset, 4].unpack1('L<')
|
|
107
|
-
offset += 4
|
|
108
|
-
id = data[offset, 4].unpack1('l<')
|
|
109
|
-
offset += 4
|
|
110
|
-
|
|
111
|
-
from_id = nil
|
|
112
|
-
_, from_id, offset = parse_peer(data, offset) if flags.anybits?(1 << 8)
|
|
113
|
-
offset += 4 if flags.anybits?(1 << 29) # from_boosts_applied
|
|
114
|
-
_, _, offset = parse_peer(data, offset) # peer_id (skip)
|
|
115
|
-
_, _, offset = parse_peer(data, offset) if flags.anybits?(1 << 28) # saved_peer_id
|
|
116
|
-
offset = schema.skip(data, offset) if flags.anybits?(1 << 2) # fwd_from
|
|
117
|
-
offset += 8 if flags.anybits?(1 << 11) # via_bot_id
|
|
118
|
-
offset += 8 if flags2.anybits?(1 << 0) # via_business_bot_id
|
|
119
|
-
offset = schema.skip(data, offset) if flags.anybits?(1 << 3) # reply_to
|
|
120
|
-
|
|
121
|
-
date = data[offset, 4].unpack1('L<')
|
|
122
|
-
offset += 4
|
|
123
|
-
|
|
124
|
-
text, offset = read_tl_string(data, offset)
|
|
125
|
-
|
|
126
|
-
msg = Message.new(id: id, from_id: from_id, date: date, text: text)
|
|
127
|
-
[msg, offset]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def parse_peer(data, offset)
|
|
131
|
-
constructor = data[offset, 4].unpack1('L<')
|
|
132
|
-
offset += 4
|
|
133
|
-
case constructor
|
|
134
|
-
when Constructors::PEER_USER
|
|
135
|
-
[:user, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
136
|
-
when Constructors::PEER_CHAT
|
|
137
|
-
[:chat, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
138
|
-
when Constructors::PEER_CHANNEL
|
|
139
|
-
[:channel, data[offset, 8].unpack1('Q<'), offset + 8]
|
|
140
|
-
else
|
|
141
|
-
raise "Unknown peer constructor: 0x#{constructor.to_s(16)}"
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def read_tl_string(data, offset)
|
|
146
|
-
first_byte = data.getbyte(offset)
|
|
147
|
-
if first_byte == 254
|
|
148
|
-
len = data.getbyte(offset + 1) |
|
|
149
|
-
(data.getbyte(offset + 2) << 8) |
|
|
150
|
-
(data.getbyte(offset + 3) << 16)
|
|
151
|
-
total = 4 + len
|
|
152
|
-
else
|
|
153
|
-
len = first_byte
|
|
154
|
-
total = 1 + len
|
|
155
|
-
end
|
|
156
|
-
padding = (4 - (total % 4)) % 4
|
|
157
|
-
[data[offset + (total - len), len].force_encoding('UTF-8'), offset + total + padding]
|
|
158
|
-
end
|
|
159
92
|
end
|
|
160
93
|
end
|
|
161
94
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
# messages.getMessages#63c66506 id:Vector<InputMessage> — fetch messages by id
|
|
6
|
+
# from a basic group or private chat (the non-channel counterpart of
|
|
7
|
+
# channels.getMessages). Returns messages.messages / messagesSlice.
|
|
8
|
+
class MessagesGetMessages
|
|
9
|
+
include Binary
|
|
10
|
+
|
|
11
|
+
CONSTRUCTOR = 0x63c66506
|
|
12
|
+
INPUT_MESSAGE_ID = 0xa676a322
|
|
13
|
+
|
|
14
|
+
def initialize(ids:)
|
|
15
|
+
@ids = ids
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def serialize
|
|
19
|
+
result = u32_b(CONSTRUCTOR)
|
|
20
|
+
result += u32_b(Constructors::VECTOR) + u32_b(@ids.length)
|
|
21
|
+
@ids.each { |id| result += u32_b(INPUT_MESSAGE_ID) + u32_b(id) }
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ReplyInlineMarkup
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x48a30254
|
|
9
|
+
KEYBOARD_BUTTON_ROW = 0x77608b83
|
|
10
|
+
|
|
11
|
+
# rows: array of rows; each row is an array of button objects (each responds to #serialize).
|
|
12
|
+
def initialize(rows:)
|
|
13
|
+
@rows = rows
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serialize
|
|
17
|
+
result = u32_b(CONSTRUCTOR)
|
|
18
|
+
result += u32_b(Constructors::VECTOR)
|
|
19
|
+
result += u32_b(@rows.size)
|
|
20
|
+
@rows.each do |row|
|
|
21
|
+
result += u32_b(KEYBOARD_BUTTON_ROW)
|
|
22
|
+
result += u32_b(Constructors::VECTOR)
|
|
23
|
+
result += u32_b(row.size)
|
|
24
|
+
row.each { |button| result += button.serialize }
|
|
25
|
+
end
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ReplyKeyboardMarkup
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x85dd99d1
|
|
9
|
+
KEYBOARD_BUTTON_ROW = 0x77608b83
|
|
10
|
+
|
|
11
|
+
# buttons: a single row of button objects (each responds to #serialize)
|
|
12
|
+
def initialize(buttons:, resize: true, single_use: false)
|
|
13
|
+
@buttons = buttons
|
|
14
|
+
@resize = resize
|
|
15
|
+
@single_use = single_use
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def serialize
|
|
19
|
+
flags = 0
|
|
20
|
+
flags |= (1 << 0) if @resize
|
|
21
|
+
flags |= (1 << 1) if @single_use
|
|
22
|
+
|
|
23
|
+
result = u32_b(CONSTRUCTOR)
|
|
24
|
+
result += u32_b(flags)
|
|
25
|
+
result += u32_b(Constructors::VECTOR)
|
|
26
|
+
result += u32_b(1)
|
|
27
|
+
result += u32_b(KEYBOARD_BUTTON_ROW)
|
|
28
|
+
result += u32_b(Constructors::VECTOR)
|
|
29
|
+
result += u32_b(@buttons.size)
|
|
30
|
+
@buttons.each { |button| result += button.serialize }
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class RequestPeerTypeCreateBot
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x3e81e078
|
|
9
|
+
|
|
10
|
+
def initialize(bot_managed: true, suggested_name: nil, suggested_username: nil)
|
|
11
|
+
@bot_managed = bot_managed
|
|
12
|
+
@suggested_name = suggested_name
|
|
13
|
+
@suggested_username = suggested_username
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serialize
|
|
17
|
+
flags = 0
|
|
18
|
+
flags |= (1 << 0) if @bot_managed
|
|
19
|
+
flags |= (1 << 1) if @suggested_name
|
|
20
|
+
flags |= (1 << 2) if @suggested_username
|
|
21
|
+
|
|
22
|
+
result = u32_b(CONSTRUCTOR)
|
|
23
|
+
result += u32_b(flags)
|
|
24
|
+
result += serialize_tl_string(@suggested_name) if @suggested_name
|
|
25
|
+
result += serialize_tl_string(@suggested_username) if @suggested_username
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def serialize_tl_string(str)
|
|
32
|
+
bytes = str.encode('UTF-8').bytes
|
|
33
|
+
length = bytes.length
|
|
34
|
+
if length <= 253
|
|
35
|
+
[length] + bytes + padding(length + 1)
|
|
36
|
+
else
|
|
37
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def padding(current_length)
|
|
42
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
43
|
+
[0] * pad_length
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'bot_command_scope'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
# bots.resetBotCommands — clear the bot's commands for a given scope/language.
|
|
8
|
+
# Returns Bool.
|
|
9
|
+
class ResetBotCommands
|
|
10
|
+
include Binary
|
|
11
|
+
|
|
12
|
+
CONSTRUCTOR = 0x3d8de0f9
|
|
13
|
+
|
|
14
|
+
# scope: BotCommandScope hash (see BotCommandScope); defaults to default scope.
|
|
15
|
+
def initialize(scope: { type: :default }, lang_code: '')
|
|
16
|
+
@scope = scope
|
|
17
|
+
@lang_code = lang_code
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def serialize
|
|
21
|
+
result = u32_b(CONSTRUCTOR)
|
|
22
|
+
result += BotCommandScope.serialize(@scope)
|
|
23
|
+
result += serialize_tl_string(@lang_code)
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def serialize_tl_string(str)
|
|
30
|
+
bytes = str.to_s.encode('UTF-8').bytes
|
|
31
|
+
length = bytes.length
|
|
32
|
+
if length <= 253
|
|
33
|
+
[length] + bytes + padding(length + 1)
|
|
34
|
+
else
|
|
35
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def padding(current_length)
|
|
40
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
41
|
+
[0] * pad_length
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|