Module: MTProto::TL::MessageEntities

Extended by:
Binary
Defined in:
lib/mtproto/tl/message_entity.rb

Overview

Codec for Vector<MessageEntity> — the formatting-span list carried by messages.sendMessage/editMessage (entities, flags.3) and parsed back out of an inbound message. serialize builds the wire byte array from [MessageEntity]; parse reads it back. Layouts follow the layer-227 schema. The only input vs output split is the text-mention: sending uses inputMessageEntityMentionName (an InputUser), a received one is messageEntityMentionName (a bare user_id).

Constant Summary collapse

VECTOR =
0x1cb5c415
INPUT_USER =
0xf21158c6
INPUT_USER_SELF =
0xf7c1b13f
INPUT_MESSAGE_ENTITY_MENTION_NAME =
0x208e68c9
TYPE_TO_CTOR =

type -> constructor id for the entities that are identical on input/output.

{
  unknown: 0xbb92ba95, mention: 0xfa04579d, hashtag: 0x6f635b0d,
  bot_command: 0x6cef8ac7, url: 0x6ed02538, email: 0x64e475c2,
  bold: 0xbd610bc9, italic: 0x826f8b60, code: 0x28a20571, pre: 0x73924be0,
  text_url: 0x76a6d327, mention_name: 0xdc7b1140, phone: 0x9b69e34b,
  cashtag: 0x4c4e743f, underline: 0x9c4e7e8b, strike: 0xbf0693d4,
  bank_card: 0x761e6af4, spoiler: 0x32ca960f, custom_emoji: 0xc8cf05f8,
  blockquote: 0xf1ccaaac
}.freeze
CTOR_TO_TYPE =
TYPE_TO_CTOR.invert.freeze

Class Method Summary collapse

Methods included from Binary

b_u32, b_u64, u32_b, u64_b

Class Method Details

.parse(data, offset) ⇒ Object

Wire byte array + new offset for one Vector<MessageEntity> at offset; returns [[MessageEntity], new_offset]. An entity constructor we don't model is stepped over via the schema sizer so the walk stays aligned (and dropped).



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/mtproto/tl/message_entity.rb', line 84

def parse(data, offset)
  return [[], offset] unless data[offset, 4].unpack1('L<') == VECTOR

  io = offset + 4
  count = data[io, 4].unpack1('L<')
  io += 4
  entities = []
  count.times do
    entity, io = parse_entity(data, io)
    entities << entity if entity
  end
  [entities, io]
end

.parse_entity(data, offset) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/mtproto/tl/message_entity.rb', line 98

def parse_entity(data, offset)
  ctor = data[offset, 4].unpack1('L<')
  type = CTOR_TO_TYPE[ctor]
  return [nil, Reader.schema.skip(data, offset)] if type.nil?

  io = offset + 4
  collapsed = nil
  if type == :blockquote
    collapsed = data[io, 4].unpack1('L<').anybits?(1 << 0)
    io += 4
  end
  ent_offset = data[io, 4].unpack1('l<')
  ent_length = data[io + 4, 4].unpack1('l<')
  io += 8
  extra = {}
  case type
  when :text_url
    extra[:url], io = Reader.read_tl_string(data, io)
  when :pre
    extra[:language], io = Reader.read_tl_string(data, io)
  when :mention_name
    extra[:user_id] = data[io, 8].unpack1('q<')
    io += 8
  when :custom_emoji
    extra[:document_id] = data[io, 8].unpack1('q<')
    io += 8
  end
  [MessageEntity.new(type: type, offset: ent_offset, length: ent_length, collapsed: collapsed, **extra), io]
end

.serialize(entities) ⇒ Object

[MessageEntity] -> wire byte array for Vector<MessageEntity>.



55
56
57
58
59
# File 'lib/mtproto/tl/message_entity.rb', line 55

def serialize(entities)
  out = u32_b(VECTOR) + u32_b(entities.length)
  entities.each { |e| out += serialize_entity(e) }
  out
end

.serialize_entity(entity) ⇒ Object

Wire byte array for one entity, dispatched on its type. Sending a text-mention needs an InputUser: an explicit access_hash, or 0 for a user the DC already knows (e.g. a mutual contact); a wrong hash is rejected by the server.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/mtproto/tl/message_entity.rb', line 64

def serialize_entity(entity)
  return serialize_mention_name(entity) if entity.type == :mention_name

  ctor = TYPE_TO_CTOR.fetch(entity.type) do
    raise ArgumentError, "unknown entity type: #{entity.type.inspect}"
  end
  bytes = u32_b(ctor)
  bytes += u32_b(entity.collapsed ? 1 : 0) if entity.type == :blockquote # flags.0 collapsed
  bytes += u32_b(entity.offset) + u32_b(entity.length)
  case entity.type
  when :text_url then bytes += tl_string(entity.url.to_s)
  when :pre then bytes += tl_string(entity.language.to_s)
  when :custom_emoji then bytes += u64_b(entity.document_id)
  end
  bytes
end

.serialize_mention_name(entity) ⇒ Object

inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser.



129
130
131
132
133
134
135
136
137
# File 'lib/mtproto/tl/message_entity.rb', line 129

def serialize_mention_name(entity)
  input_user =
    if entity.user_id.nil?
      u32_b(INPUT_USER_SELF)
    else
      u32_b(INPUT_USER) + u64_b(entity.user_id) + u64_b(entity.access_hash || 0)
    end
  u32_b(INPUT_MESSAGE_ENTITY_MENTION_NAME) + u32_b(entity.offset) + u32_b(entity.length) + input_user
end

.tl_string(str) ⇒ Object

TL string serializer (byte array): a length-prefixed, 4-byte-padded blob. Mirrors SendMessage#serialize_tl_string; kept local so the codec stands alone.



141
142
143
144
145
146
147
148
149
150
151
# File 'lib/mtproto/tl/message_entity.rb', line 141

def tl_string(str)
  bytes = str.to_s.dup.force_encoding('BINARY').bytes
  length = bytes.length
  if length <= 253
    padding = (4 - ((length + 1) % 4)) % 4
    [length] + bytes + ([0] * padding)
  else
    padding = (4 - ((length + 4) % 4)) % 4
    [254] + u32_b(length)[0, 3] + bytes + ([0] * padding)
  end
end