westacks / telebot

Easy way to create Telegram bots in PHP
https://westacks.github.io/telebot/
MIT License
282 stars 44 forks source link

php artisan telebot:webhook issue #4

Closed JTD420 closed 3 years ago

JTD420 commented 3 years ago

Hi,

I cant seem to get the webhook artisan command to work.. I can get the help command for it but it fails to execute no matter the options I try!

The error is more or less the same each time:

   WeStacks\TeleBot\Exception\TeleBotRequestException

  Not Found

  at vendor/westacks/telebot/src/Exception/TeleBotRequestException.php:17
     13▕             $parameters = ResponseParameters::create($result->parameters ?? null);
     14▕             $text .= '; Parameters: '.$parameters;
     15▕         }
     16▕
  ➜  17▕         return new TeleBotRequestException($text, $result->error_code);
     18▕     }
     19▕ }
     20▕

      +30 vendor frames
  31  artisan:37
      Illuminate\Foundation\Console\Kernel::handle()

Any help appreciated! :)

JTD420 commented 3 years ago

Am I supposed to add a route for the webhook to work?

I have the config and .env setup with my token and tried setting the webhook url to my domain/webhook but no luck there!

punyflash commented 3 years ago

Hello! This error tells that webhook url returns not found. To setup a webhook you need a route for that:

// WebhookController.php

...
public function webhook()
{
    TeleBot::bot('your_bot')->handleUpdate();
}
...
// routes/web.php

Route::post('/webhook', [WebhookController::class, 'webhook']);

Be sure you correctly set your bot token and webhook url in config or .env:

// config/telebot.php

...
'your_bot' => [
    'token' => '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
    'webhook' => [
        'url' => 'https://your-app.url/webhook'
    ]
]
...

Then when you run php artisan telebot:webhook -S your webhook should be set. Also, be sure that your webhook url is accessible for telegram servers. If you want to work with your bot localy, use php artisan telebot:polling -S instead.

punyflash commented 3 years ago

I think this guideline should be documented better or/and library should create routes by itself. Thanks for request!

punyflash commented 3 years ago

Oh, also as I understand you are not able to even get webhook info. In this case, I assume there is something wrong with your token.

JTD420 commented 3 years ago

Oh, also as I understand you are not able to even get webhook info. In this case, I assume there is something wrong with your token.

Thanks for your fast response and help! - I was able to get the artisan commands working by installing an earlier version of Laravel on a clean OS with properly configured permissions and the code you provided above! (Was running Laravel 8, now switched to 6.x and seems to be working far more stable.)

I think this guideline should be documented better or/and library should create routes by itself. Thanks for request!

Yes I agree! The documentation seems a little bit confusing and hard to work out the different steps to take while using Laravel vs the Standalone guides..

Once I'm able to verify the steps needed to get it working, I'd be more than happy to give updating the documentation a try. :)

I've been messing around trying to follow the documentation and see if I could figure out how to get it all working but I'm stumped and have a few questions if you don't mind!

How do I add commands to the bot? I get the following error after trying to add a command and register it in the handler section of my config file..

   WeStacks\TeleBot\Exception\TeleBotMehtodException  : Given update handler type "app\Telegram\Commands\StartCommand" is not valid update handler

  at /opt/bitnami/apache/htdocs/vendor/westacks/telebot/src/Exception/TeleBotMehtodException.php:14
    10|     }
    11|
    12|     public static function wrongHandlerType(string $type)
    13|     {
  > 14|         return new TeleBotMehtodException("Given update handler type \"{$type}\" is not valid update handler", 404);
    15|     }
    16| }
    17|

And lastly, how do I respond to "non-commands"?

Example: User initiates bot with a command /start

Bot runs the Start Command which has logic to respond to user with "Hello, what's your name?" User is now expected to respond e.g "Brad" Bot then responds "Hi, Brad! Nice to meet you."

In above scenario the user is not providing a "/" or command but rather just responding to an already executed command. How would I handle this?

Thanks again for your help! Great to see a well supported and documented php/laravel Telegram Bot Framework 💯

punyflash commented 3 years ago

For bot commands - check out #3. It seems like something is wrong with your namespace - app\Telegram\Commands\StartCommand. App should be capitalised in Laravel

Non commands working almost the same as commands - you should extend your handler from UpdateHandler class instead of CommandHandler and specify the trigger function as shown in docs. For you it should be something like this:

<?php

namespace Somewhere\InYour\App;

use WeStacks\TeleBot\Interfaces\UpdateHandler;

class NameMessageHandler extends UpdateHandler
{
    public static function trigger(Update $update)
    {
        if (!$update->is('message')) return false; // Check if update is a message

        $user = User::where('telegram_id', $update->from()->id)->first();
        return ($user && $user->input == 'name_message'); // Checking user input state, which you should set after executing the command as you described
    }

    public function handle()
    {
        $name = $this->update->message->text;
        $this->sendMessage([
            'text' => "Hello, {$name}! Nice to meet you."
        ]);
    }
}

The idea here is simple - you store what user should input in the database and then validate it inside the handler.

punyflash commented 3 years ago

Also I think my example kinda rough, User model probably should be used not only inside one handler, so in order not to provoke unnecessary calls to the database you should add a closure handler in first position of handlers array:

function ($update) {
    $user = User::where('telegram_id', $update->from()->id)->first();
    if (!$user) {
        // Create user if needed
    }

    ...

    Auth::login($user);
}

Then you will be able to access user anywhere using:

$user = Auth::user();

Also I'm not sure if laravel checks if there is already loged in user, so in long polling this may not work, need to test it out.

JTD420 commented 3 years ago

For bot commands - check out #3. It seems like something is wrong with your namespace - app\Telegram\Commands\StartCommand. App should be capitalised in Laravel

Non commands working almost the same as commands - you should extend your handler from UpdateHandler class instead of CommandHandler and specify the trigger function as shown in docs. For you it should be something like this:

<?php

namespace Somewhere\InYour\App;

use WeStacks\TeleBot\Interfaces\UpdateHandler;

class NameMessageHandler extends UpdateHandler
{
    public static function trigger(Update $update)
    {
        if (!$update->is('message')) return false; // Check if update is a message

        $user = User::where('telegram_id', $update->from()->id)->first();
        return ($user && $user->input == 'name_message'); // Checking user input state, which you should set after executing the command as you described
    }

    public function handle()
    {
        $name = $this->update->message->text;
        $this->sendMessage([
            'text' => "Hello, {$name}! Nice to meet you."
        ]);
    }
}

The idea here is simple - you store what user should input in the database and then validate it inside the handler.

Hi, Thanks again for your help. That's my bad, I thought the namespace needed to be the same case as the folder. Changing it to a capital "App" as you suggested fixed the problem of registering my commands! :)

I'm still a little bit confused though, if I'm being honest... To begin with, so now I have the "StartCommand" registered and showing up if I type "/" on telegram along with the command names and descriptions so it's clearly communicating with the server now which is great!

What I don't understand is why if I send "/start" or "/s" I don't get a reply from the bot saying "hello world" like it seems it would from the handle() function..?

Have I missed something there needed to get it to work?

Also I think my example kinda rough, User model probably should be used not only inside one handler, so in order not to provoke unnecessary calls to the database you should add a closure handler in first position of handlers array:

function ($update) {
    $user = User::where('telegram_id', $update->from()->id)->first();
    if (!$user) {
        // Create user if needed
    }

    ...

    Auth::login($user);
}

Then you will be able to access user anywhere using:

$user = Auth::user();

Also I'm not sure if laravel checks if there is already loged in user, so in long polling this may not work, need to test it out.

I'm also not entirely sure how this part works? Are we basically creating a user in the DB if their telegram_id doesn't exist in there already along with the messages the user sends to us and then querying against it to determine what was sent?

If that's how it works, then I think that's incredibly smart, and something that definitely needs to be documented better! I was not aware that was a feature of this project...

Can you explain the Auth::login($user); part? I know laravel comes with a user scaffolding etc but I didn't think it was being used here.

Also;

you should add a closure handler in first position of handlers array: What file and where do you mean when you refer to the handlers array? Not the one in Config/TeleBot.php?

Thanks again for your help! In the meanwhile, I'll get started now on updating the documentation, with what I know and have done so far! -Brad :)

JTD420 commented 3 years ago

I've submitted a pull-request with the changes I've made to the documentation so far.

I've still not figured out why the StartCommand isn't sending hello world back to the user but is registering the name and description on the telegram servers but once I figure out what I've missed, I'll add those steps to the documentation aswell so hopefully it's more clear how to get up and running! :D

-Brad

punyflash commented 3 years ago

I'm also not entirely sure how this part works? Are we basically creating a user in the DB if their telegram_id doesn't exist in there already along with the messages the user sends to us and then querying against it to determine what was sent?

If that's how it works, then I think that's incredibly smart, and something that definitely needs to be documented better! I was not aware that was a feature of this project...

This actualy not the part of the project, it's a feature of Laravel itself - I just showed an example how you may use it to solve your task - we just attach telegram_id to any Authenticatable model and then use it as authorised user for our handlers, so we don't execute database query for each Telegram handler trigger function. The way handlers work is a simple foreach loop, so this is my optimization tip - it will slow down an application if you run User::get() in each handler's trigger function, so its a good practice to avoid it.

What I don't understand is why if I send "/start" or "/s" I don't get a reply from the bot saying "hello world" like it seems it would from the handle() function..?

Have I missed something there needed to get it to work?

I think you forgot to setup a webhook or long polling. Notice that you can't run a webhook for local domains, so try out testing with polling if you run app localy.

What file and where do you mean when you refer to the handlers array? Not the one in config/telebot.php?

Yes, in config/telebot.php. You may import it from other file in someway to make config cleaner.

JTD420 commented 3 years ago

I'm also not entirely sure how this part works? Are we basically creating a user in the DB if their telegram_id doesn't exist in there already along with the messages the user sends to us and then querying against it to determine what was sent? If that's how it works, then I think that's incredibly smart, and something that definitely needs to be documented better! I was not aware that was a feature of this project...

This actualy not the part of the project, it's a feature of Laravel itself - I just showed an example how you may use it to solve your task - we just attach telegram_id to any Authenticatable model and then use it as authorised user for our handlers, so we don't execute database query for each Telegram handler trigger function. The way handlers work is a simple foreach loop, so this is my optimization tip - it will slow down an application if you run User::get() in each handler's trigger function, so its a good practice to avoid it.

What I don't understand is why if I send "/start" or "/s" I don't get a reply from the bot saying "hello world" like it seems it would from the handle() function..? Have I missed something there needed to get it to work?

I think you forgot to setup a webhook or long polling. Notice that you can't run a webhook for local domains, so try out testing with polling if you run app localy.

What file and where do you mean when you refer to the handlers array? Not the one in config/telebot.php?

Yes, in config/telebot.php. You may import it from other file in someway to make config cleaner.

I'm on my phone right now so sorry if reply is poorly formatted!

Thanks for clarifying, I will try and add your suggestions to my code and see if I can get the commands working!

I did actually setup a webhook address and included it in my routes... (Using a web-domain with https enabled and configured so DNS points to my web server...) So not sure why it's not working as one would expect...

Take a look at my pull request, specifically the guide.md file because I included my routes/web.php webhook code there aswell as the StartCommand.php code and how it's being called in the handlers array in TeleBot.php config file so maybe you'll spot where I messed up?

I think we should add your suggestion for using Laravels authenticateable model to store the telegram_id into the documentation aswell as I feel it would help a lot of users with many different uses!

Thanks again 💯🙌

punyflash commented 3 years ago

I think we should add your suggestion for using Laravels authenticateable model to store the telegram_id into the documentation aswell as I feel it would help a lot of users with many different uses!

This is totaly depends on how developer designing his app, database and bot, so I don't think that this needs to be in the docs. There are many ways to solve this task: cache user, store it in singleton, use the middleware for webhook request, etc; I just showed the first thing I thought about for task you described using Laravel's features.

punyflash commented 3 years ago

By the way this is common task for bot development, so I will try to think about more automated sollution for this

punyflash commented 3 years ago

Starting from version 1.5.0 route for webhook is being generated automatically.