grammyjs / grammY

The Telegram Bot Framework.
https://grammy.dev
MIT License
2.35k stars 115 forks source link

bug: `editMessageMedia` incorrect behaviour #317

Closed shevernitskiy closed 1 year ago

shevernitskiy commented 1 year ago

I had an issue during update message with animation.

I create message like this

const file = await Deno.open(LIVE_PREVIEW_FILE, { read: true })
const post = await this.bot.api.sendAnimation(this.channel, new InputFile(file.readable), params)

I use mkv file without sound - it work fine, message created with animated media preview.

image

Then in a couple minutes, i trying to update media like this.

const file = await Deno.open(LIVE_PREVIEW_FILE, { read: true })
await this.bot.api.editMessageMedia(
  this.announce.chat,
  this.announce.id,
  {
    type: 'animation',
    media: new InputFile(file.readable),
    caption: message,
    parse_mode: 'HTML',
  }
 )

And got a message without animation preview, but with attached file media.dat

image

I tried to use gif from new ULR(https://....)for update and it works. Is it my fault, telegram, or grammy?

KnorpelSenf commented 1 year ago

This is hard to say. Can you provide an example file that can be used to reproduce the issue?

Most likely, Telegram has some unexpected behaviour which can be mitigated in your code. Regarding API calls, grammY is a pretty simple wrapper, so it's unlikely that the root cause is in the library itself.

shevernitskiy commented 1 year ago

This is hard to say. Can you provide an example file that can be used to reproduce the issue?

Most likely, Telegram has some unexpected behaviour which can be mitigated in your code. Regarding API calls, grammY is a pretty simple wrapper, so it's unlikely that the root cause is in the library itself.

Here is the simple example

import { Bot, InputFile, InlineKeyboard } from 'https://deno.land/x/grammy@v1.10.1/mod.ts'

const STREAMER = 'blabla'

const bot = new Bot('TOKEN')

bot.on('message', async (ctx) => {
  if (ctx.message.text != 'animation') return

  const params = {
    reply_markup: new InlineKeyboard().url('СМОТРЕТЬ СТРИМ', `https://twitch.tv/${STREAMER}`),
    caption: '<b>Стрим началася!</b>',
    parse_mode: 'HTML',
  } as const

  const post = await ctx.replyWithAnimation(
    new InputFile(new URL('https://cdn.dribbble.com/users/402090/screenshots/3390594/twitch_shuffle.gif')),
    params,
  )

  setTimeout(async () => {
    const file = await Deno.open('./out.mp4', { read: true })
    await bot.api.editMessageMedia(
      ctx.chat.id,
      post.message_id,
      {
        type: 'animation',
        media: new InputFile(file.readable),
        caption: 'New caption',
        parse_mode: 'HTML',
      },
      {
        reply_markup: new InlineKeyboard().url('СМОТРЕТЬ СТРИМ', `https://twitch.tv/${STREAMER}`),
      },
    )
  }, 20 * 1000)
})

bot.start()

out.mp4 - new animation file

https://user-images.githubusercontent.com/28886342/203256952-2aeb2ba8-5ec8-4fdc-a779-764fc10b56ca.mp4

And yes, if I use the file by url it works fine. The problem occurs only when updating with a local file.

shevernitskiy commented 1 year ago

So, I asked my friend to check this method in python (aiogram) and it works well. It seems like issue is not on the api side.

import asyncio
from aiogram import Bot, Dispatcher, types
from tube_stream_loader import yt_stream
from advice import nice_advice
from aiogram.types import InputFile
from aiogram.types import input_media
from concurrent.futures import ThreadPoolExecutor

with open("token.txt", "r") as f:
    token = f.read()

bot = Bot(token=token)

dp = Dispatcher(bot)

@dp.message_handler(commands=["tubeload"])
async def youtube_loader(message: types.Message):
    url = message.get_full_command()[1].split(' ')

    stream = yt_stream(url)
    for title, buffer in stream:
        await message.answer_audio(buffer.getvalue(), title=title)

@dp.message_handler(commands=["advice"])
async def adv(message: types.Message):
    await message.answer(nice_advice())

@dp.message_handler(commands=["test"])
async def test_gif(message: types.Message):
    user_id = message.from_user.id
    input = InputFile("null.gif")
    msg = await bot.send_animation(user_id, animation=input)
    input = InputFile("null2.gif")
    media = input_media.InputMediaAnimation(media=input)
    asyncio.sleep(10)
    await bot.edit_message_media(media=media, message_id=msg["message_id"], chat_id=message.chat.id)

async def main():
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())
shevernitskiy commented 1 year ago

Hm... in sendAnimation we use InputFile - works fine In editMessageMedia we use InputMedia, then form array of input files and then form post multipart. Maybe something wrong with InputMedia -> InputFile procedure. And thats why we have dat extension (default in switch of function getExt())

shevernitskiy commented 1 year ago

So after much research, I found the answer. Because I pass in InputFile ReadableStream, grammy does not know what type of file is. Passing filename along with the stream solved the problem. It didn't seem obvious to me, because we pass the media type to the api.

I checked what if you pass Deno.FsFile to InputFile istead of stream, because you can get all the information you need from that object. But this way the file doesn't load to the end at all (it shows perpetual file loading).

Anyway my problem is solved, but maybe some tweaking can be done here to avoid similar for other users.

In any case thanks for the great framework!

KnorpelSenf commented 1 year ago

Sorry for the long delay. A fix is ready now. The above code works well, the edit operation works as expected. Thank you so much for tracking down the issue that accurately! Could you test out the changes at #388 and approve the pull request if you can confirm that it indeed fixes the issue?

KnorpelSenf commented 1 year ago

@all-contributors add @shevernitskiy for bug

allcontributors[bot] commented 1 year ago

@KnorpelSenf

I've put up a pull request to add @shevernitskiy! :tada:

shevernitskiy commented 1 year ago

Sorry for the long delay. A fix is ready now. The above code works well, the edit operation works as expected. Thank you so much for tracking down the issue that accurately! Could you test out the changes at #388 and approve the pull request if you can confirm that it indeed fixes the issue?

I'll try to test it in near future.

UPD: Unfortunately, example from above gives the same result - dat file on update instead of declared animation type (in case if we pass Deno.FsFile or ReadableStream)

KnorpelSenf commented 1 year ago

Did you use the code from the branch?

shevernitskiy commented 1 year ago

My bad, i used code from main. Branch code works prefect! Thanks!

KnorpelSenf commented 1 year ago

Awesome! Can you leave an approving review at https://github.com/grammyjs/grammY/pull/388/files? :)