Skip to content

Commit 83fd28d

Browse files
committed
Initial commit
0 parents  commit 83fd28d

8 files changed

+199
-0
lines changed

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
src/secret.js
3+
lambda-config.js
4+
dist/
5+
dist.zip

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# @randomcoasters
2+
3+
A bot that tweets a random coaster from [RCDB](https://rcdb.com/) on the regular. Based on [Erin McKean's template](https://github.com/emckean/blank-lambda-bot). Her guide [here](https://twitter.com/emckean?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor) can be used to figure out how to configure AWS to run it.

Diff for: gulpfile.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
var gulp = require('gulp');
2+
var zip = require('gulp-zip');
3+
var del = require('del');
4+
var install = require('gulp-install');
5+
var runSequence = require('run-sequence');
6+
var awsLambda = require("node-aws-lambda");
7+
8+
gulp.task('clean', function() {
9+
return del(['./dist', './dist.zip']);
10+
});
11+
12+
gulp.task('js', function() {
13+
//this is the name of your main function file
14+
return gulp.src('src/*.js')
15+
.pipe(gulp.dest('dist/'));
16+
});
17+
18+
gulp.task('node-mods', function() {
19+
return gulp.src('./package.json')
20+
.pipe(gulp.dest('dist/'))
21+
.pipe(install({production: true}));
22+
});
23+
24+
gulp.task('zip', function() {
25+
return gulp.src(['dist/**/*', '!dist/package.json'])
26+
.pipe(zip('dist.zip'))
27+
.pipe(gulp.dest('./'));
28+
});
29+
30+
gulp.task('upload', function(callback) {
31+
awsLambda.deploy('./dist.zip', require("./lambda-config.js"), callback);
32+
});
33+
34+
gulp.task('deploy', function(callback) {
35+
return runSequence(
36+
['clean'],
37+
['js', 'node-mods'],
38+
['zip'],
39+
['upload'],
40+
callback
41+
);
42+
});

Diff for: jsconfig.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=759670
3+
// for the documentation about the jsconfig.json format
4+
"compilerOptions": {
5+
"target": "es6",
6+
"module": "commonjs",
7+
"allowSyntheticDefaultImports": true
8+
},
9+
"exclude": [
10+
"node_modules",
11+
"bower_components",
12+
"jspm_packages",
13+
"tmp",
14+
"temp"
15+
]
16+
}

Diff for: lambda-config.template.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
profile: 'your-profile-name', // load your AWS credentials from a custom profile
3+
region: 'us-east-1', //the region of your Lambda function
4+
handler: 'index.handler', //the name of the handler function: index because the main file is index.js
5+
role: 'arn:aws:iam::youraccountid:role/lambda_basic_execution',
6+
functionName: 'randomcoasters', //name
7+
timeout: 10, // how many seconds your function should run before it times out
8+
memorySize: 128, // how much memory your function needs (shouldn't need more than this)
9+
publish: true, // this creates a new version of your Lambda function every time you update it
10+
runtime: 'nodejs4.3',
11+
}

Diff for: package.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "coasterbot",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "Thomas Boyt <[email protected]>",
10+
"license": "MIT",
11+
"dependencies": {
12+
"axios": "^0.14.0",
13+
"cheerio": "^0.22.0",
14+
"lodash.samplesize": "^4.2.0",
15+
"twit": "^2.2.4"
16+
},
17+
"devDependencies": {
18+
"del": "^2.2.2",
19+
"gulp": "^3.9.1",
20+
"gulp-install": "^0.6.0",
21+
"gulp-zip": "^3.2.0",
22+
"node-aws-lambda": "^0.1.8",
23+
"run-sequence": "^1.2.2"
24+
}
25+
}

Diff for: src/index.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict';
2+
3+
process.on('unhandledRejection', (err) => {
4+
if (err.stack) {
5+
console.error(err.stack);
6+
} else {
7+
console.error(err);
8+
}
9+
process.exit(1);
10+
});
11+
12+
const sampleSize = require('lodash.samplesize');
13+
const Twit = require('twit');
14+
const axios = require('axios');
15+
const cheerio = require('cheerio');
16+
const secret = require('./secret');
17+
18+
const T = new Twit({
19+
consumer_key: secret.consumer_key,
20+
consumer_secret: secret.consumer_secret,
21+
access_token: secret.access_token,
22+
access_token_secret: secret.access_token_secret,
23+
});
24+
25+
function getCoaster() {
26+
return axios.get('https://rcdb.com/')
27+
.then((resp) => {
28+
const $ = cheerio.load(resp.data);
29+
30+
const link = $('#rrc #rrc_text p a').first().attr('href');
31+
32+
return axios.get(`https://rcdb.com${link}`);
33+
34+
}).then((resp) => {
35+
const $ = cheerio.load(resp.data);
36+
const name = $('#feature h1').text();
37+
const park = $('#feature a').first().text();
38+
const url = resp.config.url;
39+
40+
const pictureUrls = $('#pic_data div').map((i, el) => $(el).data('url')).get();
41+
42+
return {name, park, url, pictureUrls};
43+
});
44+
}
45+
46+
function getImage(pictureUrl) {
47+
return axios.get(`https://rcdb.com${pictureUrl}`, {
48+
responseType: 'arraybuffer',
49+
}).then((resp) => {
50+
const image = resp.data.toString('base64');
51+
return image;
52+
});
53+
}
54+
55+
function uploadImage(pictureUrl) {
56+
return getImage(pictureUrl).then((b64Image) => {
57+
return T.post('media/upload', {media_data: b64Image});
58+
}).then((result) => {
59+
return result.data.media_id_string;
60+
});
61+
}
62+
63+
function tweetCoaster(params) {
64+
const urls = sampleSize(params.pictureUrls, 3)
65+
66+
return Promise.all(urls.map(uploadImage)).then((imageIds) => {
67+
// TODO: Ensure there is always room for url, status is not truncated
68+
return T.post('/statuses/update', {
69+
status: `${params.name} in ${params.park} - ${params.url}`,
70+
media_ids: imageIds
71+
});
72+
});
73+
}
74+
75+
exports.handler = function(event, context) {
76+
getCoaster().then(tweetCoaster).then((result) => {
77+
console.log(result.data);
78+
context.succeed();
79+
80+
}).catch((err) => {
81+
console.log('errored:');
82+
83+
if (err.stack) {
84+
console.log(err.stack);
85+
} else {
86+
console.log(err);
87+
}
88+
89+
context.fail();
90+
})
91+
}

Diff for: src/secret.template.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
"consumer_key": "",
3+
"consumer_secret": "",
4+
"access_token": "",
5+
"access_token_secret": ""
6+
};

0 commit comments

Comments
 (0)