Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twitter Driver: Simple echo chatbot going into infinite loop #2

Open
dimplevador opened this issue Sep 13, 2018 · 10 comments
Open

Twitter Driver: Simple echo chatbot going into infinite loop #2

dimplevador opened this issue Sep 13, 2018 · 10 comments

Comments

@dimplevador
Copy link

  • BotMan Version: 2.0.
  • PHP Version: 7.0.3
  • Messaging Service(s): Twitter
  • Cache Driver: FilesystemCache

Description:

A simple Echo Chatbot for Twitter Direct Messages is going into infinite loop.

I have narrowed down the problem to this:
When user DM's, webhook receives event: "message_create".
When webhook posts reply, twitter sends delivery report (Response) with the following json, notice the event "message_create". The event name is same for receiving actual user message & also for bot message delivery report :

{
"event": {
  "type": "message_create",
  "message_create": {
    "target": {
      "recipient_id": "RECIPIENT_USER_ID"
    },
    "message_data": {
      "text": "Hello World!",
    }
  }
}
}

I have not been able to find difference between incoming message & delivery report json. Can someone please help me out with that?

The function that needs to be fixed in Twitter Driver is getMessages() on On line #68 - https://github.com/botman/driver-twitter/blob/master/src/TwitterDriver.php

Thank you very much in advance.

Steps To Reproduce:

Twitter webhook:

<?php
    require __DIR__ . '/vendor/autoload.php';

    use BotMan\BotMan\BotMan;
    use BotMan\BotMan\BotManFactory;
    use BotMan\BotMan\Drivers\DriverManager;

    use BotMan\BotMan\Cache\DoctrineCache;
    use Doctrine\Common\Cache\FilesystemCache;

    $config = [
        'twitter' => [
            'consumer_key' => '****',
            'consumer_secret' => '****',
            'token'=> '***',
            'token_secret'=> '*****',
        ]
    ];

    if(isset($_GET['crc_token'])) {
        $signature = hash_hmac('sha256', $_REQUEST['crc_token'], $config['twitter']['consumer_secret'], true);
        $response['response_token'] = 'sha256='.base64_encode($signature);
        print json_encode($response);
    }
    else{
        DriverManager::loadDriver(\BotMan\Drivers\Twitter\TwitterDriver::class);

        $doctrineCacheDriver = new FilesystemCache(__DIR__);
        $botman = BotManFactory::create($config, new DoctrineCache($doctrineCacheDriver));

        // Give the bot something to listen for.
        $botman->hears('(.*)', function (BotMan $bot, $text) {
            $bot->reply("Echo: ".$text);
        });

        // Start listening
        $botman->listen();
    }
?>
@christophrumpel christophrumpel transferred this issue from botman/botman Nov 14, 2018
@christophrumpel
Copy link

I haven't used the Twitter driver yet. Is still not working or did you found a solution?

@dimplevador
Copy link
Author

I haven't used the Twitter driver yet. Is still not working or did you found a solution?

Thank you for replying.

The original code is still not working. I found a solution that works for my situation. Taking the current TwitterDriver as base i wrote a custom driver - overriding the problematic functions that i needed. I was not able to resolve all the problems of the driver.

@sololance
Copy link
Contributor

Hi,

This can be fixed by checking user id of sender

<?php
$botman->hears('(.*)', function (BotMan $bot, $text) {
	
	// Get user
	$user = $bot->getUser();

	// Get user id
	$user_id = $user->getId();

	// Check if twitter user id is not bot id
	if($user_id != $twitter['user_id']){
		$bot->reply("Echo: ".$text);
	}
});

Maybe we can add new parameter to config "user_id", so when twitter send back message we can detect it from "recipient_id" and then pass message as false?

What do you think @mpociot ?

Thanks.

@dimplevador
Copy link
Author

I had to make changes to two functions in TwitterDriver.php for it to work. It works for my requirement, don't know whether it will work for all the requirements.

    /**
     * Determine if the request is for this driver.
     *
     * @return bool
     */
    public function matchesRequest()
    {
        if (isset($this->headers['x-twitter-webhooks-signature'])) {

            $signature = $this->headers['x-twitter-webhooks-signature'][0];
            $hash = hash_hmac('sha256', json_encode($this->payload->all()), $this->config->get('consumer_secret'), true);

            if( $signature === 'sha256='.base64_encode($hash) ){

                //Check whether the incoming message is just delivery report of the previously sent message.
                if( $this->payload->has('direct_message_events') ){
                    if( $this->payload->get('for_user_id') != $this->payload->get('direct_message_events')[0]['message_create']['sender_id'] ) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Retrieve User information.
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage
     * @return User
     */
    public function getUser(IncomingMessage $matchingMessage)
    {
        $sender_id = $matchingMessage->getSender();

        $user = Collection::make($this->payload->get('users'))->first(function ($user) use ($sender_id) {
            return $user['id'] === $sender_id;
        });

        return new User($user['id'], null, null, $user['name'], $user);
    }

@sololance
Copy link
Contributor

@dimplevador well, changes made by yourself in the source code isn't good choice. When library will be updated you'll need to follow up changes.

Does my example works as expected for you?

I still think that we don't have to make up any changes because it's really useful feature for some kind of logging (messages can be sent from any app integrated with twitter, so checking source app is also neat !)

@dimplevador
Copy link
Author

@dimplevador well, changes made by yourself in the source code isn't good choice. When library will be updated you'll need to follow up changes.

Does my example works as expected for you?

I still think that we don't have to make up any changes because it's really useful feature for some kind of logging (messages can be sent from any app integrated with twitter, so checking source app is also neat !)

@sololance :) Thank you for taking time to checkout the problem.
Your solution doesn't work though. The code now does not go into finite loop but it still makes two additional posts for each call - one to twitter and one to self.

I didn't make any change to the source code. I just wrote a custom driver by extending TwitterDriver and overrode the two functions - according to my requirements.

If i am using the code without any changes, it goes into infinite loop with just one simple echo call.

@salmanyaqoobin
Copy link

Can someone please help me, to send get started information for botman-twitter driver.

@dimplevador
Copy link
Author

Can someone please help me, to send get started information for botman-twitter driver.

@salmanyaqoobin The existing source code is faulty. You will have to create a custom driver by extending the existing TwitterDriver and override two methods: matchesRequest() and getUser(IncomingMessage $matchingMessage). You can find the new code for both these methods in the above discussion.

Documentation for extending / customizing driver https://botman.io/2.0/drivers

Please note that this modification works for my purpose. I am not sure whether it will work for you or not.

@salmanyaqoobin
Copy link

@dimplevador Thanks for the Quick Reply, your two functions even solve the loop messaging issue.
Rest of all the work to get ready the chatbot for twitter is giving me hard time, but eventually i got manage to figure out all the problem.
I created following new features:

  1. Welcome Message with image.(If someone can enhance it to command console)
  2. Add button Template Extension
  3. Add Button Element Extension

Welcome Message with image Code (browser base controller approach)
`public function upload(Request $request)
{
$welcome_message = $request->get('welcome_message');
$media_file = $request->file('media');

    $connection = new TwitterOAuth(
        config('botman.twitter.consumer_key'),
        config('botman.twitter.consumer_secret'),
        config('botman.twitter.token'),
        config('botman.twitter.token_secret')
    );

    $content = $connection->get("account/verify_credentials");

    if(!isset($content->errors)){
        $media = $connection->upload('media/upload', [
            //'media' => rawurldecode($url),
            'media' => $media_file,
            //'media_data'=>$imageBase64,
            'media_category'=>'dm_image',
            'media_type'=>'dm_image',
            'shared'=>true,
        ], true);

        if (isset($media->errors)) {
            $this->error('Unable to upload media file.');
            dump($media);
        } else {
            echo 'Your media has been uploaded to twitter';
            //dump($media);
            if(isset($media->media_id)){
                echo "media uploaded";
                $payload = [
                    'welcome_message' => [
                        'name' => 'in-welcome-message',
                        'message_data' => [
                            'text' => $welcome_message,
                            'attachment'=>[
                                "type"=> "media",
                                "media"=>[
                                    "id"=>$media->media_id
                                ]
                            ]
                        ],
                    ]
                ];
                $welcome_messages = $connection->post("direct_messages/welcome_messages/new", $payload, true);
                if(!isset($welcome_messages->errors) && isset($welcome_messages->welcome_message->id)){
                    echo "Welcome message has been created";
                    $payload = [
                        'welcome_message_rule' => [
                            'welcome_message_id' => $welcome_messages->welcome_message->id,
                        ]
                    ];
                    $welcome_messages_rule = $connection->post("direct_messages/welcome_messages/rules/new", $payload, true);
                    dump($welcome_messages_rule);
                }
                dump($welcome_messages);
            }
            exit;
        }
    }

}`

@dimplevador
Copy link
Author

@salmanyaqoobin I am glad it worked out for you :)

Thank you for sharing the welcome message with image extension function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants