badfarm / zanzara

Asynchronous PHP Telegram Bot Framework built on top of ReactPHP
MIT License
202 stars 29 forks source link

Error: Call to a member function getId() on null when editing a message with inline_message_id #5

Closed sergix44 closed 4 years ago

sergix44 commented 4 years ago

A similar issue to the #4 is given when trying to edit the message produced by an inline request, for example:

$bot->onInlineQuery(function (Context $ctx) {
    $ctx->answerInlineQuery([
        [
            'type' => 'article',
            'id' => sha1(time()),
            'title' => 'the title',
            'input_message_content' => ['message_text' =>'content'],
            'reply_markup' => [
                'inline_keyboard' => [[['text' => 'Stop', 'callback_data' => 'stop']]]
            ]
        ]
    ], [
        'cache_time' => 0
    ]);
});

$bot->onCbQueryData(['stop'], function (Context $ctx) {
    $ctx->editMessageText('Canceled');
});

Produces:

Failed to process Telegram update , reason: Error: Call to a member function getId() on null in vendor\badfarm\zanzara\src\Zanzara\Telegram\TelegramTrait.php:1015
Stack trace:
#0 main.php(56): Zanzara\Context->editMessageText('Canceled')
#1 vendor\badfarm\zanzara\src\Zanzara\Listener\Listener.php(47): {closure}(Object(Zanzara\Context))
#2 vendor\badfarm\zanzara\src\Zanzara\Middleware\MiddlewareNode.php(46): Zanzara\Listener\Listener->handle(Object(Zanzara\Context), NULL)
#3 vendor\badfarm\zanzara\src\Zanzara\Zanzara.php(272): Zanzara\Middleware\MiddlewareNode->__invoke(Object(Zanzara\Context))
#4 vendor\badfarm\zanzara\src\Zanzara\Zanzara.php(245): Zanzara\Zanzara->processUpdate(Object(Zanzara\Telegram\Type\Update))
#5 vendor\react\promise\src\FulfilledPromise.php(28): Zanzara\Zanzara->Zanzara\{closure}(Array)
#6 vendor\react\promise\src\Promise.php(134): React\Promise\FulfilledPromise->then(Object(Closure), Object(Closure))
#7 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#8 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#9 vendor\react\promise\src\FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}(Array)
#10 vendor\react\promise\src\Promise.php(135): React\Promise\FulfilledPromise->done(Object(Closure), Object(Closure), Object(Closure))
#11 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#12 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#13 vendor\react\promise\src\Deferred.php(36): React\Promise\Promise::React\Promise\{closure}(Object(RingCentral\Psr7\Response))
#14 vendor\clue\buzz-react\src\Io\Transaction.php(91): React\Promise\Deferred->resolve(Object(RingCentral\Psr7\Response))
#15 vendor\react\promise\src\FulfilledPromise.php(28): Clue\React\Buzz\Io\Transaction->Clue\React\Buzz\Io\{closure}(Object(RingCentral\Psr7\Response))
#16 vendor\react\promise\src\Promise.php(134): React\Promise\FulfilledPromise->then(Object(Closure), Object(Closure))
#17 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#18 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#19 vendor\react\promise\src\FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}(Object(RingCentral\Psr7\Response))
#20 vendor\react\promise\src\Promise.php(135): React\Promise\FulfilledPromise->done(Object(Closure), Object(Closure), Object(Closure))
#21 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#22 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#23 vendor\react\promise\src\FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}(Object(RingCentral\Psr7\Response))
#24 vendor\react\promise\src\Promise.php(66): React\Promise\FulfilledPromise->done(Object(Closure), Object(Closure))
#25 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#26 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#27 vendor\react\promise\src\FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}(Object(RingCentral\Psr7\Response))
#28 vendor\react\promise\src\Promise.php(135): React\Promise\FulfilledPromise->done(Object(Closure), Object(Closure), Object(Closure))
#29 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#30 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#31 vendor\react\promise\src\FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}('{"ok":true,"res...')
#32 vendor\react\promise\src\Promise.php(135): React\Promise\FulfilledPromise->done(Object(Closure), Object(Closure), Object(Closure))
#33 vendor\react\promise\src\Promise.php(168): React\Promise\Promise::React\Promise\{closure}(Object(React\Promise\FulfilledPromise))
#34 vendor\react\promise\src\Promise.php(231): React\Promise\Promise->settle(Object(React\Promise\FulfilledPromise))
#35 vendor\react\promise-stream\src\functions.php(75): React\Promise\Promise::React\Promise\{closure}('{"ok":true,"res...')
#36 vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): React\Promise\Stream\{closure}()
#37 vendor\clue\buzz-react\src\Message\ReadableBodyStream.php(50): Evenement\EventEmitter->emit('close')
#38 vendor\clue\buzz-react\src\Message\ReadableBodyStream.php(151): Clue\React\Buzz\Message\ReadableBodyStream->close()
#39 vendor\clue\buzz-react\src\Message\ReadableBodyStream.php(33): Clue\React\Buzz\Message\ReadableBodyStream->handleEnd()
#40 vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): Clue\React\Buzz\Message\ReadableBodyStream->Clue\React\Buzz\Message\{closure}('{"ok":true,"res...')
#41 vendor\react\http-client\src\Response.php(97): Evenement\EventEmitter->emit('data', Array)
#42 vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): React\HttpClient\Response->handleData('{"ok":true,"res...')
#43 vendor\react\http-client\src\Request.php(169): Evenement\EventEmitter->emit('data', Array)
#44 vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): React\HttpClient\Request->handleData('HTTP/1.1 200 OK...')
#45 vendor\react\stream\src\Util.php(71): Evenement\EventEmitter->emit('data', Array)
#46 vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): React\Stream\Util::React\Stream\{closure}('HTTP/1.1 200 OK...')
#47 vendor\react\stream\src\DuplexResourceStream.php(193): Evenement\EventEmitter->emit('data', Array)
#48 vendor\react\event-loop\src\StreamSelectLoop.php(245): React\Stream\DuplexResourceStream->handleData(Resource id #187)
#49 vendor\react\event-loop\src\StreamSelectLoop.php(212): React\EventLoop\StreamSelectLoop->waitForStreamActivity(1976907)
#50 vendor\badfarm\zanzara\src\Zanzara\Zanzara.php(160): React\EventLoop\StreamSelectLoop->run()
#51 main.php(63): Zanzara\Zanzara->run()
#52 {main}

(Sorry for the iterative report, but without the previous fix i couldn't find this problem)

cheeghi commented 4 years ago

Solved. We had seen the comment on the previous issue and we were already working on it, anyway, you did well creating a new issue. The fix was published within the 0.4.2 release.

Note: your call will fail anyway because editMessageText requires a chat_id and a message_id to be passed.

sergix44 commented 4 years ago

Actually should works, if is inline_message_id is specified those two are optional https://core.telegram.org/bots/api#editmessagetext

cheeghi commented 4 years ago

Ah you're right, maybe the framework could set the inline_message_id by itself (retrieving it from $update->getCallbackQuery()->getInlineMessageId()) if the user doesn't provide one. So, the user instead of doing:

$bot->onCbQueryData(['stop'], function (Context $ctx) {
    $ctx->editMessageText('Canceled', ['inline_message_id' => $ctx->getCallbackQuery()->getInlineMessageId()]);
});

can just do:

$bot->onCbQueryData(['stop'], function (Context $ctx) {
    $ctx->editMessageText('Canceled');
});

What do you think?

sergix44 commented 4 years ago

Ye sure, will reduce some boilerplate

sergix44 commented 4 years ago

There is also another issue i've found out, related to the update method, it seems is not possible to update a message outside the callback context with the latest update:

            $bot->getTelegram()->editMessageText('another message', [
                'inline_message_id' => $inlineMessageId
            ]);

It give and error that the update is null (obviously). I think that the framework should not try to set the vars outside the update context, and delegate to the dev instead (or check if the updated is set or not).

``` Fatal error: Uncaught Error: Call to a member function getEffectiveChat() on null in vendor\badfarm\zanzara\src\Zanzara\Telegram\TelegramTrait.php:1016 Stack trace: #0 main.php(71): Zanzara\Telegram\Telegram->editMessageText('00:04:56', Array) #1 vendor\react\event-loop\src\Timer\Timers.php(96): {closure}(Object(React\EventLoop\Timer\Timer)) #2 vendor\react\event-loop\src\StreamSelectLoop.php(184): React\EventLoop\Timer\Timers->tick() #3 vendor\badfarm\zanzara\src\Zanzara\Zanzara.php(160): React\EventLoop\StreamSelectLoop->run() #4 main.php(77): Zanzara\Zanzara->run() #5 {main} thrown in vendor\badfarm\zanzara\src\Zanzara\Telegram\TelegramTrait.php on line 1016 ```
cheeghi commented 4 years ago

Yes, I agree. We've released a 0.4.3 with this check. Then we've implemented what we said here, extending the behaviour also for editMessageLiveLocation, stopMessageLiveLocation, editMessageCaption, editMessageMedia, editMessageReplyMarkup, setGameScore, getGameHighScores methods.

sergix44 commented 4 years ago

Yes, I agree. We've released a 0.4.3 with this check. Then we've implemented what we said here, extending the behaviour also for editMessageLiveLocation, stopMessageLiveLocation, editMessageCaption, editMessageMedia, editMessageReplyMarkup, setGameScore, getGameHighScores methods.

I've tested, the fix works for outside context, but with the inline_message_id is not working, I still need to specified it.

cheeghi commented 4 years ago

I accidentally removed an import, fixed it (059b468). Released 0.4.4 version.