alik0211 / mtproto-core

Telegram API JS (MTProto) client library for Node.js and browser
https://mtproto-core.js.org
GNU General Public License v3.0
625 stars 110 forks source link

Support for "flags2:#" for Layer 140-144 in parser/builder #267

Open aleksusklim opened 1 year ago

aleksusklim commented 1 year ago

I've tried to update Scheme for layer 144 using Telegram Desktop .tl source: https://github.com/telegramdesktop/tdesktop/blob/c793537d963efe96d0ae7ab30a8b6b262f039151/Telegram/Resources/tl/api.tl#L132 (I have my own script to convert TL to JSON that mtproto-core will eat properly. I'll add my script as attachment here).

However, new scheme now has flags2:# can_delete_channel:flags2.0?true which is spelled in JSON as {"name":"flags2","type":"#"},{"name":"can_delete_channel","type":"flags2.0?true"}, but Builder generates:

'channelFull': function(params) {
  this.int32(-362240487);
  const flags = (this.has(params.can_view_participants) << 3) | …
  this.int32(flags);
  const flags = (this.has(params.can_view_participants) << 3) | …
  this.int32(flags);
  this.long(params.id);

– which is incorrect and gives runtime error. The Parser is also produces incorrect code:

result.flags2 = this.int();
result.can_delete_channel = !!(result.flags & 1);

– reading from "flags" and not from "flags2".

Since this is a breaking change, I don't have enough knowledge in mtproto-core internals to fix that myself (I only know how to locally update scheme as I already did that once).

ATTACHMENT: tl2json_v1.htm (works locally in browser) requires vector#1cb5c415 {t:Type} # [ t ] = Vector t; to be replaced with vector#1cb5c415 = Vector t; in source TL before processing.

(Side question: how do I rewrite device_model, system_version, app_version, system_lang_code and lang_code with my own user-supplied values at runtime?)

aleksusklim commented 1 year ago

Anyway, I need this urgently, So I tried to fix it on my own. I won't open PR/fork/whatever. Here are my patched versions of generate-builder.js and generate-parser.js: generate-builder-parser.zip

Essential parts for builder:

const calcFlags = (params,name) => {
  const bitMap = [];

  params.forEach(param => {
    if (param.type.includes('?')) {
      const flags = param.type.split('?')[0].split('.');
      if (flags[0]===name) {
        const count = flags[1];
        bitMap.push(`(this.has(params.${param.name}) << ${count})`);
      }
    }
  });

  const flagsLine = `    const ${name} = ${bitMap.join(' | ')};`;

  return flagsLine;
};

…

    // Flags
    if (param.type === '#') {
      const flagsLine = calcFlags(params,param.name);

      paramsLines.push(flagsLine);

      fnName = 'int32';
      args = [param.name];
    } else if …

Essential parts for parser:

const calcFlag = (name, type) => {
  const [left, flagType] = type.split('?');
  const flags = left.split('.');
  const flagName = flags[0];
  const flagBit = +flags[1];

  const condition = `result.${flagName} & ${2 ** flagBit}`;
  …

I don't know is it really works properly; but generated code looks correct, it compiles and API invocation returns flags2 field:

  {
    _: 'messages.chatFull',
    full_chat: {
      _: 'channelFull',
      flags: 1,
      flags2: 0,
      id: '1006503122',
      …

P.S. For my tl2json to work correctly, you'll need to nuke all of {X:Type} too in api.tl (near to invokeAfterMsg and others).