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
632 stars 116 forks source link

API messages.sendMultiMedia Always Fails (400 MEDIA_INVALID) for valid media #148

Open mvdicarlo opened 3 years ago

mvdicarlo commented 3 years ago

I have been trying to get this particular api method to work (https://core.telegram.org/method/messages.sendMultiMedia).

I have provided sample code below that I am using to test this behavior. It shouldn't be an issue of the actual file being invalid as if you attempt to send the media using https://core.telegram.org/method/messages.sendMedia it works just fine.

It appears to be a bug, but I don't know enough about the protocol implementation to be able to fix it myself.

const { MTProto } = require("@mtproto/core");
const readline = require("readline");
const fs = require("fs");

const api_id = "<app id>";
const api_hash = "<app hash>";
const phone_number = "<phone>";
const file = fs.readFileSync("test.jpg"); // assumes under 512kb

// 1. Create an instance
const mtproto = new MTProto({
  api_id,
  api_hash,
});

async function auth() {
  const send = await mtproto.call("auth.sendCode", {
    phone_number,
    settings: {
      _: "codeSettings",
    },
  });

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  const code = await new Promise((resolve) => {
    rl.question("Code: ", (answer) => {
      rl.close();
      resolve(answer);
    });
  });

  const authed = await mtproto.call("auth.signIn", {
    phone_code: code,
    phone_number,
    phone_code_hash: send.phone_code_hash,
  });

  console.log(authed);
}

async function getChats() {
  return await mtproto
    .call("messages.getAllChats", {
      except_ids: [],
    })
    .then(({ chats }) => chats);
}

function wait() {
  return new Promise((resolve) => {
    setTimeout(resolve, 2000);
  });
}

async function upload(file) {
  const id = Date.now();
  await mtproto.call("upload.saveFilePart", {
    file_id: id,
    file_part: 0,
    bytes: file,
  });
  return id;
}

// This does all the stuff
async function run() {
  mtproto.setDefaultDc(
    await mtproto.call("help.getNearestDc").then((result) => {
      return result.nearest_dc;
    })
  );

  let chats;
  try {
    chats = await getChats();
  } catch {
    await auth();
    chats = await getChats();
  }

  const chat = chats[0];
  await wait();
  const upl = await upload(file);
  await wait();

// I AM BROKEN
  const res = await mtproto
    .call("messages.sendMultiMedia", {
      peer: {
        _: "inputPeerChannel",
        channel_id: chat.id,
        access_hash: chat.access_hash,
      },
      multi_media: [
        {
          _: "inputSingleMedia",
          random_id: Date.now(),
          //   message: "Hello, World!",
          media: {
            _: "inputMediaUploadedPhoto",
            file: {
              _: "inputFile",
              id: upl,
              name: "test.jpg",
              parts: 1,
            },
          },
        },
      ],
    })
    .catch((err) => err);
  // UNCOMMENT ME TO SEE THAT messages.sendMedia works correctly.
  //   const res = await mtproto
  //     .call("messages.sendMedia", {
  //       random_id: Date.now(),
  //       peer: {
  //         _: "inputPeerChannel",
  //         channel_id: chat.id,
  //         access_hash: chat.access_hash,
  //       },
  //       message: "Hello, World",
  //       media: {
  //         _: "inputMediaUploadedPhoto",
  //         file: {
  //           _: "inputFile",
  //           id: upl,
  //           name: "test.jpg",
  //           parts: 1,
  //         },
  //       },
  //     })
  //     .catch((err) => err);

  console.log(res);
}

run()
  .catch(console.error)
  .finally(() => process.exit(0));

process.on("unhandledRejection", console.error);
meylisatamuradov commented 3 years ago

https://stackoverflow.com/questions/65628877/in-nodejs-telegram-api-messages-sendmultimedia-method-is-not-working

bigbigsir commented 3 years ago

I have the same question. Have you solved your problem

bigbigsir commented 3 years ago

@alik0211

ember999 commented 3 years ago

@mvdicarlo Have you solved your problem? I need help

mvdicarlo commented 3 years ago

No. I just accepted the less preferable way of sending individually for now. Wasn't able to tell if this was a limitation of the library or some limitation of the Telegram API.

I don't know enough about the protocol to debug it myself

ember999 commented 3 years ago

I tried everything,This is very frustrating,The author doesn't have any explanation

2021年5月18日 下午8:34,Lemonynade @.***> 写道:

No. I just accepted the less preferable way of sending individually for now. Wasn't able to tell if this was a limitation of the library or some limitation of the Telegram API.

I don't know enough about the protocol to debug it myself

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/alik0211/mtproto-core/issues/148#issuecomment-843132377, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARDPXOTYCPI5TUMXMWXJMELTOJNDXANCNFSM4WPAAHKA.

milad7290 commented 2 years ago

Any update on this issue? I try all kinds of ways but still the response is "400 MEDIA_INVALID"

VityaSchel commented 2 years ago

This is just fuck this is so stupid, why python has everything while js developers has to deal with dead library with "call" method that pretty much don't do anything, what the actual fuck why just why

VityaSchel commented 2 years ago

I'm going to try to patch this error. It seems that problem is at src/tl/builder/index.js:7159

'messages.sendMultiMedia': function(params) {
    this.int32(-134016113);
    const flags = (this.has(params.silent) << 5) | (this.has(params.background) << 6) | (this.has(params.clear_draft) << 7) | (this.has(params.noforwards) << 14) | (this.has(params.reply_to_msg_id) << 0) | (this.has(params.schedule_date) << 10) | (this.has(params.send_as) << 13);
    this.int32(flags);
    this.predicate(params.peer);
    this.flag(this.int, params.reply_to_msg_id);
    this.vector(this.predicate, params.multi_media);
    this.flag(this.int, params.schedule_date);
    this.flag(this.predicate, params.send_as);
  },
ghost commented 2 years ago

you may use uploadMedia to upload each photo

...
...
// step 1 ,upload the file
const chat = chats[0];
await wait();
const upl_id = await upload(file);
await wait();
// step2, uploadMedia
const upl_media = await mtproto('messages.uploadMedia', {
    peer: target_peer,
    media: {
        "_": "inputMediaUploadedPhoto",
        "file": {
            "_": "inputFile",
            "id": upl_id,
            "parts": 1,
            "name": 'upload.png',
            "md5_checksum": ""
        },
        "mime_type": "image/png",
        "attributes": [{
            "_": "documentAttributeFilename",
            "file_name": 'upload.png'
        }]
    }
})
// step 3 send multi media
const res = await mtproto
    .call("messages.sendMultiMedia", {
        peer: {
            _: "inputPeerChannel",
            channel_id: chat.id,
            access_hash: chat.access_hash,
        },
        multi_media: [{
            "_": "inputSingleMedia",
            "random_id": this.createSendMessageId(),
            "message": text,
            "entities": entities,
            "media": {
                "_": "inputMediaPhoto",
                "id": {
                    "_": 'inputPhoto',
                    "id": upl_media.photo.id,
                    "access_hash": upl_media.photo.access_hash,
                    "file_reference": upl_media.photo.file_reference
                }
            }
        }, {
            "_": "inputSingleMedia",
            "random_id": this.createSendMessageId(),
            "media": {
                "_": "inputMediaPhoto",
                "id": {
                    "_": 'inputPhoto',
                    "id": upl_media.photo.id,
                    "access_hash": upl_media.photo.access_hash,
                    "file_reference": upl_media.photo.file_reference
                }
            }
        }],
    })
    .catch((err) => err);

=========================================================================================== the code is work for me.