Kenny Cason Kenny Cason - 3 months ago 10
Python Question

Handling "flags" types in telegram's TL schema language

I wrote a tl parser so can now use the latest layer (53). But I am unsure how to handle "flags" types. They are only mentioned in the tl docs but not defined (as far as I can tell) at the bottom of the page here: link.

For example, when a method returns a 'message' type it should look like this:

message#c09be45f flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;


If I understand correctly each flag is a bit set in some variable, right?

My parser breaks out the 'message' type like this:


id: -1063525281
params:
name: flags
type:
name: out
bit: 1
type: true

name: mentioned
bit: 4
type: true

name: media_unread
bit: 5
type: true

name: silent
bit: 13
type: true

name: post
bit: 14
type: true

name: from_id
bit: 8
type: int

name: fwd_from
bit: 2
type: MessageFwdHeader

name: via_bot_id
bit: 11
type: int

name: reply_to_msg_id
bit: 3
type: int

name: media
bit: 9
type: MessageMedia

name: reply_markup
bit: 6
type: ReplyMarkup

name: entities
bit: 7
type: Vector<MessageEntity>

name: views
bit: 10
type: int

name: edit_date
bit: 15
type: int

name: id
type: int

name: to_id
type: Peer

name: date
type: int

name: message
type: string

predicate: message
type: Message



But if the flags are bits in some variable, which variable?

Related: is tl based on a formal, standardized language spec or was it created specifically for telegram? I ask because if it is a subset of a formal language (like yaml) it might be better to use an already known parser for tl instead of reinventing the wheel.

Answer

But if the flags are bits in some variable, which variable?

Message#c09be45f flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;

Example: flags:# out:flags.1?true

Decoding Flags: BinaryAND(flags, 2^ix) --> this will help you determine if a field is included or not

flags = The value if the flags field, this is usually the first field for objects with flags

ix == flag index, this is a number that indicates flag position, e.g. out:flags.1?true here out is the field in flag position 1, and the type is true

For the example above out will have a value of true if BAND(flags,1^2) === true otherwise, out field is ignored

Code - Message Encoding (Elixir)

def encode(%Message{} = x), do: <<95, 228, 155, 192, encode(:Int, x.flags)::binary, enc_f(:True, x.out, x.flags, 2)::binary, enc_f(:True, x.mentioned, x.flags, 16)::binary, enc_f(:True, x.media_unread, x.flags, 32)::binary, enc_f(:True, x.silent, x.flags, 8192)::binary, enc_f(:True, x.post, x.flags, 16384)::binary, encode(:Int, x.id)::binary, enc_f(:Int, x.from_id, x.flags, 256)::binary, encode(x.to_id)::binary, enc_f(x.fwd_from, x.flags, 4)::binary, enc_f(:Int, x.via_bot_id, x.flags, 2048)::binary, enc_f(:Int, x.reply_to_msg_id, x.flags, 8)::binary, encode(:Int, x.date)::binary, encode(:String, x.message)::binary, enc_f(x.media, x.flags, 512)::binary, enc_f(x.reply_markup, x.flags, 64)::binary, enc_vf(x.entities, x.flags, 128)::binary, enc_f(:Int, x.views, x.flags, 1024)::binary, enc_f(:Int, x.edit_date, x.flags, 32768)::binary>>

Code - Message Decode (Elixir)

  def decode(<<95, 228, 155, 192, bin::binary>>) do
    {flags, bin} = decode(:Int, bin)
    {out, bin} = decode(:True, bin, flags, 2) # 1
    {mentioned, bin} = decode(:True, bin, flags, 16) # 4
    {media_unread, bin} = decode(:True, bin, flags, 32) # 5
    {silent, bin} = decode(:True, bin, flags, 8192) # 13
    {post, bin} = decode(:True, bin, flags, 16384) # 14
    {id, bin} = decode(:Int, bin)
    {from_id, bin} = decode(:Int, bin, flags, 256) # 8
    {to_id, bin} = decode(bin)
    {fwd_from, bin} = decode(bin, flags, 4) # 2
    {via_bot_id, bin} = decode(:Int, bin, flags, 2048) # 11
    {reply_to_msg_id, bin} = decode(:Int, bin, flags, 8) # 3
    {date, bin} = decode(:Int, bin)
    {message, bin} = decode(:String, bin)
    {media, bin} = decode(bin, flags, 512) # 9
    {reply_markup, bin} = decode(bin, flags, 64) # 6
    {entities, bin} = decode([:MessageEntity], bin, flags, 128) # 7
    {views, bin} = decode(:Int, bin, flags, 1024) # 10
    {edit_date, bin} = decode(:Int, bin, flags, 32768) # 15
    {%Message{flags: flags, out: out, mentioned: mentioned, media_unread: media_unread, silent: silent, post: post, id: id, from_id: from_id, to_id: to_id, fwd_from: fwd_from, via_bot_id: via_bot_id, reply_to_msg_id: reply_to_msg_id, date: date, message: message, media: media, reply_markup: reply_markup, entities: entities, views: views, edit_date: edit_date}, bin}
  end

#5 == 2^5 == 32
#4 == 2^4 == 16

so basically N == 2^N, where N == ix

Comments