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 111 forks source link

How to download photo from message.media #273

Open dieptang opened 1 year ago

dieptang commented 1 year ago

I want to download also the photo from getHistory. But seem upload.getFile did not help.

let history = await telegram('messages.getHistory', { peer: { _: 'inputPeerChannel', channel_id: chat.id, access_hash: chat.access_hash },, limit: 10 })

And I want to get the photo from media object. Something like this

if (messages[0].media && messages[0].media.caption) {
    if (messages[0].media.photo) {
      const file = await telegram('upload.getFile', {
        location: {
          _: 'inputPhotoFileLocation',
          id: messages[0].media.photo.id,
          dc_id: 5,
          access_hash: messages[0].media.photo.access_hash,
          file_reference: Buffer.from("some_binary_code_here", "hex"),
          //file_reference: buf, //tried
          //Buffer.from(message.media.photo.file_reference.toString('hex'), 'hex'),
          //[...message.media.photo.file_reference] and others
          thumb_size: 'y'
        },
        offset: 0,
        limit: 1024 * 1024
      });
      console.log('file', file);
    }

The error has appeared, due to missing file_reference. Any ideas?

FaiezWaseem commented 1 year ago

here is a function i found in some project to download file

/**
 * 
 * @param {'photo' | 'document'} type 
 * @param {string} id 
 * @param {string} accessHash 
 * @param {Array<Number>} fileReference 
 * @param {string?} photoSizeID 
 * @returns {string}
 */
async function downloadFile(type, id, accessHash, fileReference, photoSizeID) {

    const partSize = 524288 * 2
    /**
     * @type {Number|undefined}
     */
    let dcId = 4
    /**
     * 
     * @param {Number} offset 
     * @returns 
     */
    const downloadPart = async (offset) => {
        const body = {
            location: type === 'photo'
                ? { _: 'inputPhotoFileLocation', id, access_hash: accessHash, file_reference: fileReference, thumb_size: photoSizeID }
                : { _: 'inputDocumentFileLocation', id, access_hash: accessHash, file_reference: fileReference },
            offset: offset,
            limit: partSize
        }
        console.log(body, dcId)
        const file = await mtproto.call('upload.getFile', body, dcId && { dcId })
        return file
    }

    let partBytesLength
    let iter = 0
    const fileChunks = []
    while (partBytesLength === undefined || partBytesLength === partSize) {
        console.log('Downloading part of file', iter, iter * partSize)
        let file
        try {
            file = await downloadPart(iter * partSize)
        } catch (e) {
            if (e._ === 'mt_rpc_error' && e.error_message.startsWith('FILE_MIGRATE_')) {
                const _dcId = Number(e.error_message.substring('FILE_MIGRATE_'.length))
                dcId = _dcId
                continue
            } else {
                throw e
            }
        }
        partBytesLength = file.bytes.length
        console.log('Downloaded part of file', iter, iter * partSize, partBytesLength)
        fileChunks.push(file.bytes)
        iter++
    }
    const fileChunksBuffers = fileChunks.map(chunk => Buffer.from(chunk))
    const fileBuffer = Buffer.concat(fileChunksBuffers)
    return fileBuffer
}

I am using it like this

// for image
const type  = 'photo'
   downloadFile(
                type,
                chatFile.media.photo.id,
                chatFile.media.photo.access_hash,
                chatFile.media.photo.file_reference,
                'm'
            ).then(file => {
                const base64File = `data:image/png;base64,` + file.toString('base64')
                console.log(base64File)
            }).catch(err => {
                    console.log(err)
                })