Commit 6d5b0e91 authored by Mathis Dedial's avatar Mathis Dedial
Browse files

Implement Twitter backend

parent 44fc6a31
import m from 'mithril';
import Raven from 'raven-js';
const TWITTER_CONSUMER_KEY = 'Ur9mJuJ3c074G71UD2Nk9yefZ';
const TWITTER_CONSUMER_SECRET = 'BtaPdb67kctOaenIpwzAVptBHSwlpUG8GPidl0vdO3qoLGPho6';
const TWITTER_AUTH_ENDPOINT = 'https://api.twitter.com/oauth2/token';
const TWITTER_TIMELINE_ENDPOINT = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
const TWITTER_SCREEN_NAME = 'AMIV_ETHZ';
export default class Twitter {
constructor() {
this.bearerToken = undefined;
this.tweets = [];
}
/**
* Perform Application-only authentication.
* Returns a bearer token which can be used for further interaction with the Twitter API.
* https://developer.twitter.com/en/docs/basics/authentication/overview/application-only
*/
static async authenticate() {
// Only authenticate once
if (this.bearerToken !== undefined) {
return this.bearerToken;
}
// Encode credentials string
const credentials = btoa(
`${encodeURI(TWITTER_CONSUMER_KEY)}:${encodeURI(TWITTER_CONSUMER_SECRET)}`
);
// Await the bearer token
await m
.request({
method: 'POST',
url: TWITTER_AUTH_ENDPOINT,
data: {
grant_type: 'client_credentials',
},
serialize: m.buildQueryString,
headers: {
Authorization: `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then(result => {
// Ensure the API returns the expected token type
if (result.token_type !== 'bearer') {
Raven.captureException(
Error(`Twitter API returned unexpected token type: ${result.token_type}`)
);
return;
}
this.bearerToken = result.access_token;
})
.catch(error => {
Raven.captureException(error);
});
return this.bearerToken;
}
/**
* Load AMIV_ETHZ timeline.
* https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-user_timeline.html
*/
static async loadTimeline() {
// Wait for authentication to succeed
const token = await this.authenticate();
m
.request({
method: 'GET',
url: TWITTER_TIMELINE_ENDPOINT,
data: {
screen_name: TWITTER_SCREEN_NAME,
count: 20,
},
headers: {
Authorization: `Bearer ${token}`,
},
})
.then(tweets => {
this.tweets = tweets;
})
.catch(error => {
Raven.captureException(error);
});
}
}
const config = {
context: `${__dirname}/src`, // `__dirname` is root of project and `src` is source
entry: './index.js',
entry: ['babel-polyfill', './index.js'],
output: {
path: `${__dirname}/dist`, // `dist` is the destination
......@@ -26,7 +26,7 @@ const config = {
use: [
{
loader: 'babel-loader',
options: { presets: [['env', { targets: "last 2 years"}]] },
options: { presets: [['env', { targets: 'last 2 years' }]] },
},
],
},
......@@ -47,7 +47,7 @@ const config = {
{
loader: 'string-replace-loader', // Adds /#! prefix to local urls
options: {
search: '\[(.*)\]\((\/(?!dist).*)\)',
search: '[(.*)]((/(?!dist).*))',
replace: '(/#!$1',
flags: 'g',
},
......@@ -71,7 +71,7 @@ const config = {
{
loader: 'string-replace-loader', // Adds /#! prefix to local urls
options: {
search: '\[(.*)\]\((\/(?!dist).*)\)',
search: '[(.*)]((/(?!dist).*))',
replace: '(/#!$1',
flags: 'g',
},
......@@ -129,7 +129,6 @@ const config = {
},
},
devtool: 'eval-source-map', // Default development sourcemap
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment