super-ienien / twitter-webhooks

Easy to use expressJs middleware for twitter webhooks api
MIT License
20 stars 3 forks source link

Error: Non-200 response code during CRC GET request (i.e. 404, 500, etc) #9

Closed moringaman closed 4 years ago

moringaman commented 5 years ago

I'm really sorry for raising this issue, it's probably something that I am doing wrong but I'm getting this error when I try to subscribe. My code is

/ server.js
// where your node app starts

// init project

const express = require ('express');
const bodyParser = require ('body-parser');
const twitterWebhooks = require('twitter-webhooks');
const https = require ('https');

const app = express();
app.use(bodyParser.json());

const userActivityWebhook = twitterWebhooks.userActivity({
    serverUrl: 'https://glitch.com/~twitter-webhooks',
    route: '/', //default : '/'
    consumerKey: process.env.CONSUMER_KEY,
    consumerSecret: process.env.CONSUMER_SECRET,
    accessToken: process.env.ACCESS_TOKEN,
    accessTokenSecret: process.env.ACCESS_TOKEN_SECRET,
    environment: 'dev', //default : 'env-beta'
    app
});

//Register your webhook url - just needed once per URL
userActivityWebhook.register()
.catch(err => {
  console.log(err)
});

//Subscribe for a particular user activity
userActivityWebhook.subscribe({
    userId: 'webnostix',
    accessToken:  process.env.ACCESS_TOKEN,
    accessTokenSecret:  process.env.ACCESS_TOKEN_SECRET
})
.then(function (userActivity) {
    userActivity
    .on('favorite', (data) => console.log (userActivity.id + ' - favorite'))
    .on ('tweet_create', (data) => console.log (userActivity.id + ' - tweet_create'))
    .on ('follow', (data) => console.log (userActivity.id + ' - follow'))
    .on ('mute', (data) => console.log (userActivity.id + ' - mute'))
    .on ('revoke', (data) => console.log (userActivity.id + ' - revoke'))
    .on ('direct_message', (data) => console.log (userActivity.id + ' - direct_message'))
    .on ('direct_message_indicate_typing', (data) => console.log (userActivity.id + ' - direct_message_indicate_typing'))
    .on ('direct_message_mark_read', (data) => console.log (userActivity.id + ' - direct_message_mark_read'))
    .on ('tweet_delete', (data) => console.log (userActivity.id + ' - tweet_delete'))
})
.catch(err => {
  console.log('error:', err)
});

//listen to any user activity
userActivityWebhook.on ('event', (event, userId, data) => console.log (userId + ' - favorite'));

//listen to unknown payload (in case of api new features)
userActivityWebhook.on ('unknown-event', (rawData) => console.log (rawData));

const server = https.createServer({
    app
});

 server.listen(process.env.PORT, () => {
   console.log('server listening..')
 });
super-ienien commented 5 years ago

Hi,

I think your serverUrl is wrong. It should be : https://twitter-webhooks.glitch.me

what URL do you use to test your CRC GET request ?

moringaman commented 5 years ago

Thanks for responding, The url: https://twitter-webhooks.glitch.me/?crc_token=12345 gives a non response, I have been using the following url, this is the one glitch creates automatically so that you can share your project

https://glitch.com/~twitter-webhooks/?crc_token=12345

Also my console is throwing:

error: { Error: Webhook URL must be defined to create subscriptions.
    at Request.request [as _callback] (/rbd/pnpm-volume/6f0047ef-6cff-4238-9278-e25d0e9eb882/node_modules/.registry.npmjs.org/twitter-webhooks/0.3.0/node_modules/twitter-webhooks/lib/helpers.js:27:33)
    at Request.self.callback (/rbd/pnpm-volume/6f0047ef-6cff-4238-9278-e25d0e9eb882/node_modules/.registry.npmjs.org/request/2.88.0/node_modules/request/request.js:185:22)
    at emitTwo (events.js:126:13)
    at Request.emit (events.js:214:7)
    at Request.<anonymous> (/rbd/pnpm-volume/6f0047ef-6cff-4238-9278-e25d0e9eb882/node_modules/.registry.npmjs.org/request/2.88.0/node_modules/request/request.js:1161:10)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at IncomingMessage.<anonymous> (/rbd/pnpm-volume/6f0047ef-6cff-4238-9278-e25d0e9eb882/node_modules/.registry.npmjs.org/request/2.88.0/node_modules/request/request.js:1083:12)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:111:20) code: 170, statusCode: 403, body: { errors: [ [Object] ] } }
super-ienien commented 5 years ago

I need to rewrite the "Usage" part of th doc because it's confusing. You cannot use the code in the usage section like that because it's not possible to create subscription if your webhook is not registered. You need to wait that the register() method's promise succeed before using the subscribe() method. That's why you receive this error in your log.

For the configuration you need to use https://twitter-webhooks.glitch.me and for your CRC test you need to use https://twitter-webhooks.glitch.me/?crc_token=12345

I never used this package on glitch. I don't if it works.

super-ienien commented 5 years ago

do you get another error in your logs ?

moringaman commented 5 years ago

Ok that makes sense, so I presume is returns a promise or do I need to create my own? I also get the following

Error: Webhook URL does not meet the requirements. Invalid CRC token or json response format.

FYI, I also tried the same on repl.it with the same results

super-ienien commented 5 years ago

Ok this error is sent by twitter when you try to register your webhook. It means that twitter is not able to make the CRC token test. It happens whent your server is not reachable. And that's the case here because when you try this : https://twitter-webhooks.glitch.me/?crc_token=12345 in your browser you get an error.

You need to understand why your server does'nt respond. I think your process. Check your logs. Do you have other errors ? Are you sure that your server is still running ?

I think this section of your code is wrong : const server = https.createServer({ app });

when you create an https server you need to provide an ssl certificate and the app option doesn't exists. You should try to create a simple http server, the https protocol should be handled by glitch not by you

moringaman commented 5 years ago

Hey there, I tried again but this time on my local pc using ngrok and my own ssl certificates but now I am getting; Error: High latency on CRC GET request. Your webhook should respond in less than 3 seconds. My Code is

const express = require ('express');
const bodyParser = require ('body-parser');
const twitterWebhooks = require('twitter-webhooks');
const https = require ('https');
const path = require("path")
const fs = require('fs')
require('dotenv').config()

const app = express();
app.use(bodyParser.json());

const userActivityWebhook = twitterWebhooks.userActivity({
    serverUrl: 'https://2a51ffd5.ngrok.io',
    route: '/', //default : '/'
    consumerKey: process.env.CONSUMER_KEY,
    consumerSecret: process.env.CONSUMER_SECRET,
    accessToken: process.env.ACCESS_TOKEN,
    accessTokenSecret: process.env.ACCESS_TOKEN_SECRET,
    environment: 'dev', //default : 'env-beta'
    app
});

//Register your webhook url - just needed once per URL
userActivityWebhook.register()
.then(() => {
  userActivityWebhook.subscribe({
    userId: 'webnostix',
    accessToken:  process.env.ACCESS_TOKEN,
    accessTokenSecret:  process.env.ACCESS_TOKEN_SECRET
})
.then(function (userActivity) {
    userActivity
    .on('favorite', (data) => console.log (userActivity.id + ' - favorite'))
    .on ('tweet_create', (data) => console.log (userActivity.id + ' - tweet_create'))
    .on ('follow', (data) => console.log (userActivity.id + ' - follow'))
    .on ('mute', (data) => console.log (userActivity.id + ' - mute'))
    .on ('revoke', (data) => console.log (userActivity.id + ' - revoke'))
    .on ('direct_message', (data) => console.log (userActivity.id + ' - direct_message'))
    .on ('direct_message_indicate_typing', (data) => console.log (userActivity.id + ' - direct_message_indicate_typing'))
    .on ('direct_message_mark_read', (data) => console.log (userActivity.id + ' - direct_message_mark_read'))
    .on ('tweet_delete', (data) => console.log (userActivity.id + ' - tweet_delete'))
})
})
.catch(err => {
  console.log(err)
});

//Subscribe for a particular user activity

//listen to any user activity
userActivityWebhook.on ('event', (event, userId, data) => console.log (userId + ' - favorite'));

//listen to unknown payload (in case of api new features)
userActivityWebhook.on ('unknown-event', (rawData) => console.log (rawData));

const options = {
  key: fs.readFileSync(path.resolve(__dirname, "./key.pem")),
  cert: fs.readFileSync(path.resolve(__dirname, "./cert.pem"))
};

const server = https.createServer(options, (req, res) => {
   console.log(req)
});

 server.listen(process.env.PORT, (req, res) => {
   console.log('server listening..')
 });
super-ienien commented 5 years ago

The error message you get is pretty clear. Your server is not fast enough to respond to Twitter CRC request in less than 3 sec. What timing do you get when you try it with your web browser ?

There is still another issue in your code. You should return the result of userActivityWebhook.subscribe() in the first .then handler

moringaman commented 5 years ago

Ok thanks that makes sense, I guess I try and deploy to the cloud, just thought that it would be quick and easy to test and then deploy once it was done, Don't you mean the result of userActivityWebhook.register()? as I thought userActivity was the result of userActivityWebhook.subscribe

super-ienien commented 5 years ago

Your indentation is wrong, so it got me confused.

You'd better write it like this :

userActivityWebhook.register()
.then(function () {
  return userActivityWebhook.subscribe({
    userId: 'webnostix',
    accessToken:  process.env.ACCESS_TOKEN,
    accessTokenSecret:  process.env.ACCESS_TOKEN_SECRET
  })
})
.then(function (userActivity) {
    userActivity
    .on('favorite', (data) => console.log (userActivity.id + ' - favorite'))
    .on ('tweet_create', (data) => console.log (userActivity.id + ' - tweet_create'))
    .on ('follow', (data) => console.log (userActivity.id + ' - follow'))
    .on ('mute', (data) => console.log (userActivity.id + ' - mute'))
    .on ('revoke', (data) => console.log (userActivity.id + ' - revoke'))
    .on ('direct_message', (data) => console.log (userActivity.id + ' - direct_message'))
    .on ('direct_message_indicate_typing', (data) => console.log (userActivity.id + ' - direct_message_indicate_typing'))
    .on ('direct_message_mark_read', (data) => console.log (userActivity.id + ' - direct_message_mark_read'))
    .on ('tweet_delete', (data) => console.log (userActivity.id + ' - tweet_delete'))
})
.catch(err => {
  console.log(err)
});
moringaman commented 5 years ago

I've done exactly as you suggested and deployed it to heroku and am now getting the original error again!

Error: Non-200 response code during CRC GET request (i.e. 404, 500, etc)

I'm about to give up on this, I cant even register the webhook

super-ienien commented 5 years ago

I’m sorry I cannot setup your environment for you, this is out of the scope of this library.. if you get an error when you hit the CRC endpoint with your web browser, you should try to look in your logs to see what error is happening. First of all, 404 and 500 does not mean the same thing. What code do you get exactly. What errors do you have in your server’s logs ? Are you making de CRC request to good url ? The CRC test is here to check that your server is alive. If twitter cannot reach the CRC endpoint of your server it will never accept your webhook registration. Twitter is checking your server’s CRC endpoint regulary. If you server fails to respond, Twitter will kill your registered webhook. Twitter doesn’t accept server’s that take more than 3 seconds to respond to the CRC check. So it should be available at all time and fast. I’m here to help you. But I cannot help whithout precise informations. Server logs, detailed response to the crc check when you hit it with your web browser

moringaman commented 5 years ago

Ok well thanks for your time and effort anyway, and your right you can't help me with specific implementations. I guess that I just don't understand what this library does as I was under the impression that it takes care of the webhook part based on the config you pass to it, not that you have to create the route yourself.

Maybe some more code examples in your docs will be of use to others going forward. I'm going to look a the twitter docks and create a webhook registration ack from scratch instead.

super-ienien commented 5 years ago

Yeah that lib take care of creating the routes needed by twitter. You do not need to implement it yourself. If you give me precise informations about your errors I could help you but if you prefer you can code the webhook from scratch. That’s up to you. Just tell me if I should close the issue

shreya-14-may commented 4 years ago

the first function(registerWebhook) is giving error:

{#1432 ▼ +"errors": array:1 [▼ 0 => {#1424 ▼ +"code": 214 +"message": "Non-200 response code during CRC GET request (i.e. 404, 500, etc)." } ] }

public function registerWebhook(){ $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret, $this->accessToken, $this->accessSecret); $webhook = $connection->post("account_activity/all/".$this->webhook_env. "/webhooks",["url" => $this->oauthcallback]); dd($webhook); } public function subscribeTwitterActivity(){ $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret, $this->accessToken, $this->accessSecret); $content = $connection->post("account_activity/all/".$this->webhook_env."/subscriptions"); if($content->errors[0]->message == "Subscription already exists."){

        $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret);
        $request_token = $connection->oauth2('oauth2/token', ['grant_type' => 'client_credentials']);
        $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret, $request_token->access_token);
        $subscribed = $connection->get("account_activity/all/".$this->webhook_env ."/subscriptions/list");
        print_r($subscribed);

        // $subscribed = $connection->get("account_activity/all/" . $this->webhook_env . "/subscriptions");
        // dd($subscribed);
    }
}
public function getTwitterBearerToken(){
    $client = new GuzzleClient();
        $response = $client->post('https://api.twitter.com/oauth2/token',
        [
            'auth' => [$this->consumerKey,$this->consumerSecret],
            'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',     // Correct!
            'form_params' => [
                'grant_type' => 'client_credentials'                   
            ]
        ]    
    );
    $BearerToken = json_decode($response->getBody()->getContents(),true);
    return $BearerToken['access_token'];
}

public function get_challenge_response($token) {
    $hash = hash_hmac('sha256', $token, $this->consumerSecret, true);
    $response = array(
      'response_token' => 'sha256=' . base64_encode($hash)
    );
    return json_encode($response);
}

public function TwitterWebhook(Request $request){
    $connection = new TwitterOAuth($this->consumerKey, $this->consumerSecret, $this->accessToken, $this->accessSecret);
    $webhook = $connection->post("account_activity/all/" . $this->webhook_env . "/webhooks", ["url" => $this->oauthcallback]);

    \File::put(public_path('payloadtweet.txt'), "came to post");
    if(isset($request['crc_token'])){
        \File::put(public_path('payloadtweet.txt'), $request['crc_token']);

        try{
            \Log::info("came to try crc");
            TwitterResponse::create(['reference_type' => 'Webhook Response', 'reference_id' => "200", 'response' => $this->get_challenge_response($request['crc_token']),'status' =>"success" ]);
            return $this->get_challenge_response($request['crc_token']);
        }
        catch(Exception $e){
            TwitterResponse::create(['reference_type' => 'Webhook Response', 'reference_id' => "400", 'response' => json_encode($e),'status' => "error" ]);
        }
    }
    else{

        \Log::info("postTwitterWebhook");  
        try {
            TwitterResponse::create(['reference_type' => 'Webhook Response', 'reference_id' => "200", 'response' => json_encode($request->all()), 'status' => "success"]);
            \File::put(public_path('payloadtweet.txt'),"came to post");
            \File::put(public_path('payloadtweet.txt'),json_encode($request->all()));
            return json_encode($request->all());
        } catch (Exception $e) {
            TwitterResponse::create(['reference_type' => 'Webhook Response', 'reference_id' => "400", 'response' => json_encode($e), 'status' => "error"]);
        }
    }
}