Skip to content

Commit

Permalink
Ngve fix twitter (#265)
Browse files Browse the repository at this point in the history
* fix(eslint): tweak eslint config

* fix(twitter): change conditions to publish tweet
  • Loading branch information
leocabeza authored Jul 30, 2020
1 parent 07b2a47 commit 0f8f921
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 602 deletions.
9 changes: 7 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ module.exports = {
root: true,
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["./tsconfig.json"]
project: ["./tsconfig.json"],
sourceType: "module"
},
plugins: ["@typescript-eslint"],
extends: ["airbnb-typescript/base", "prettier/@typescript-eslint"],
extends: [
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
env: {
node: true
},
Expand Down
33 changes: 0 additions & 33 deletions .eslintrc.json

This file was deleted.

7 changes: 7 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 70,
"tabWidth": 2
};
65 changes: 34 additions & 31 deletions api/twitter/update.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import crypto from "crypto";
import { Buffer } from "buffer";
import * as Sentry from "@sentry/node";
import { NowRequest, NowResponse } from "@vercel/node";
import crypto from 'crypto';
import { Buffer } from 'buffer';
import * as Sentry from '@sentry/node';
import { NowRequest, NowResponse } from '@vercel/node';

import { sendMessage } from "../_utils/telegram/bot-methods";
import messages from "../_utils/messages";
import { sendMessage } from '../_utils/telegram/bot-methods';
import messages from '../_utils/messages';

const {
TWITTER_CONSUMER_SECRET,
MAIN_GROUP_ID,
NODE_ENV,
SENTRY_DSN
SENTRY_DSN,
} = process.env;

// taken from https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object
Expand All @@ -29,7 +29,7 @@ interface TweetInterface {
Sentry.init({ dsn: SENTRY_DSN });

const isReply = (tweet: TweetInterface): boolean =>
tweet.in_reply_to_status_id !== undefined;
tweet.in_reply_to_status_id !== null;

const isRt = (tweet: TweetInterface): boolean =>
tweet.retweeted_status !== undefined;
Expand All @@ -43,8 +43,8 @@ const handleTweets = async (tweets: TweetInterface[] = []) => {
const promise = sendMessage({
chatId: Number(MAIN_GROUP_ID),
text: messages.newTweet
.replace("#{tweetText}", tweet.text)
.replace("#{tweetUrl}", tweetUrl)
.replace('#{tweetText}', tweet.text)
.replace('#{tweetUrl}', tweetUrl),
});

promises.push(promise);
Expand All @@ -58,23 +58,26 @@ const handleTweets = async (tweets: TweetInterface[] = []) => {
* @description Used to verify that the request comes from Twitter
* @see https://github.com/twitterdev/autohook/blob/eac07b9c0bdb8fe3fad375ce5349b0c4c6d1e128/index.js#L78
*/
const validateSignature = (headers: { [index: string]: any }, body: Buffer) => {
const signatureHeaderName = "x-twitter-webhooks-signature";
const validateSignature = (
headers: { [index: string]: any },
body: Buffer,
) => {
const signatureHeaderName = 'x-twitter-webhooks-signature';

if (typeof headers[signatureHeaderName] === "undefined") {
if (typeof headers[signatureHeaderName] === 'undefined') {
throw new TypeError(
`validateSignature: header ${signatureHeaderName} not found`
`validateSignature: header ${signatureHeaderName} not found`,
);
}

const signature = `sha256=${crypto
.createHmac("sha256", TWITTER_CONSUMER_SECRET ?? "")
.createHmac('sha256', TWITTER_CONSUMER_SECRET ?? '')
.update(body)
.digest("base64")}`;
.digest('base64')}`;

return crypto.timingSafeEqual(
Buffer.from(headers[signatureHeaderName]),
Buffer.from(signature)
Buffer.from(signature),
);
};

Expand All @@ -84,23 +87,23 @@ const validateSignature = (headers: { [index: string]: any }, body: Buffer) => {
*/
const getChallengeResponse = (crcToken: string) =>
crypto
.createHmac("sha256", TWITTER_CONSUMER_SECRET ?? "")
.createHmac('sha256', TWITTER_CONSUMER_SECRET ?? '')
.update(crcToken)
.digest("base64");
.digest('base64');

const getRawBody = (readable: NowRequest): Promise<Buffer> => {
const chunks: any[] = [];
let bytes = 0;

return new Promise((resolve, reject) => {
readable.on("error", reject);
readable.on('error', reject);

readable.on("data", chunk => {
readable.on('data', chunk => {
chunks.push(chunk);
bytes += chunk.length;
});

readable.on("end", () => {
readable.on('end', () => {
resolve(Buffer.concat(chunks, bytes));
});
});
Expand All @@ -114,39 +117,39 @@ export default async (request: NowRequest, response: NowResponse) => {
order of the query arguments being reversed in vercel, but not with a local
environment (ngrok).
*/
if (request.method === "GET") {
if (request.method === 'GET') {
const crcToken = request.query.crc_token.toString();

if (crcToken) {
const hash = getChallengeResponse(crcToken);

response.status(200).json({
response_token: `sha256=${hash}`
response_token: `sha256=${hash}`,
});
} else {
response.status(400).send("crc_token missing from request");
response.status(400).send('crc_token missing from request');
}
} else if (request.method === "POST") {
} else if (request.method === 'POST') {
const rawBody = await getRawBody(request);

if (validateSignature(request.headers, rawBody)) {
const newTweets = request.body.tweet_create_events;
await handleTweets(newTweets);

response.status(200).send("ok");
response.status(200).send('ok');
} else {
response.status(400).send("signature is not valid");
response.status(400).send('signature is not valid');
}
} else {
response.status(401).send("Error: Unauthorized");
response.status(401).send('Error: Unauthorized');
}
} catch (error) {
if (NODE_ENV === "development") {
if (NODE_ENV === 'development') {
console.error(error);
} else {
Sentry.captureException(error);
}

response.status(400).send("not ok");
response.status(400).send('not ok');
}
};
Loading

0 comments on commit 0f8f921

Please sign in to comment.