Skip to content

Commit

Permalink
Exporting to GitHub. Early work in progress, bot can generate text po…
Browse files Browse the repository at this point in the history
…sts using Tracery and keeps track of its followers.
  • Loading branch information
Glitch (glitch-fediverse-bot) committed Sep 29, 2018
1 parent 24d24fa commit 3e35b08
Show file tree
Hide file tree
Showing 42 changed files with 6,114 additions and 2 deletions.
14 changes: 14 additions & 0 deletions .glitch-assets
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{"name":"drag-in-files.svg","date":"2016-10-22T16:17:49.954Z","url":"https://cdn.hyperdev.com/drag-in-files.svg","type":"image/svg","size":7646,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/drag-in-files.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(102, 153, 205)","uuid":"adSBq97hhhpFNUna"}
{"name":"click-me.svg","date":"2016-10-23T16:17:49.954Z","url":"https://cdn.hyperdev.com/click-me.svg","type":"image/svg","size":7116,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/click-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(243, 185, 186)","uuid":"adSBq97hhhpFNUnb"}
{"name":"paste-me.svg","date":"2016-10-24T16:17:49.954Z","url":"https://cdn.hyperdev.com/paste-me.svg","type":"image/svg","size":7242,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/paste-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(42, 179, 185)","uuid":"adSBq97hhhpFNUnc"}
{"uuid":"adSBq97hhhpFNUna","deleted":true}
{"uuid":"adSBq97hhhpFNUnb","deleted":true}
{"uuid":"adSBq97hhhpFNUnc","deleted":true}
{"name":"null.png","date":"2018-09-13T21:12:59.652Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fnull.png","type":"image/png","size":5255,"imageWidth":128,"imageHeight":128,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fnull.png","thumbnailWidth":128,"thumbnailHeight":128,"dominantColor":"rgb(76,172,220)","uuid":"y4bX37K73cQTbTjE"}
{"uuid":"y4bX37K73cQTbTjE","deleted":true}
{"name":"smiling-face-with-smiling-eyes.png","date":"2018-09-27T21:38:47.331Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fsmiling-face-with-smiling-eyes.png","type":"image/png","size":31773,"imageWidth":400,"imageHeight":400,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fthumbnails%2Fsmiling-face-with-smiling-eyes.png","thumbnailWidth":330,"thumbnailHeight":330,"dominantColor":null,"uuid":"lmZRNK6DdCO5aJr7"}
{"name":"glitch-fediverse-bot.png","date":"2018-09-29T12:44:17.606Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fglitch-fediverse-bot.png","type":"image/png","size":55034,"imageWidth":1920,"imageHeight":1079,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fthumbnails%2Fglitch-fediverse-bot.png","thumbnailWidth":330,"thumbnailHeight":186,"dominantColor":"rgb(252,252,252)","uuid":"4Jq94M3MUTFcsK6b"}
{"uuid":"4Jq94M3MUTFcsK6b","deleted":true}
{"name":"glitch-fediverse-bot-small-1024px.png","date":"2018-09-29T12:49:07.895Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fglitch-fediverse-bot-small-1024px.png","type":"image/png","size":60397,"imageWidth":1024,"imageHeight":509,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fthumbnails%2Fglitch-fediverse-bot-small-1024px.png","thumbnailWidth":330,"thumbnailHeight":165,"dominantColor":"rgb(252,252,252)","uuid":"iLCHg6pDTsAX7jkg"}
{"name":"glitch-fediverse-bot.png","date":"2018-09-29T12:49:07.963Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fglitch-fediverse-bot.png","type":"image/png","size":58653,"imageWidth":1920,"imageHeight":1079,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fthumbnails%2Fglitch-fediverse-bot.png","thumbnailWidth":330,"thumbnailHeight":186,"dominantColor":"rgb(252,252,252)","uuid":"0KNy0WXAgI1pZ0fd"}
{"name":"glitch-fediverse-bot-960px.png","date":"2018-09-29T12:49:08.030Z","url":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fglitch-fediverse-bot-960px.png","type":"image/png","size":50867,"imageWidth":960,"imageHeight":540,"thumbnail":"https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fthumbnails%2Fglitch-fediverse-bot-960px.png","thumbnailWidth":330,"thumbnailHeight":186,"dominantColor":"rgb(252,252,252)","uuid":"8tHyMsnvvVKkjFXF"}
Binary file added .rnd
Binary file not shown.
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,49 @@
# glitch-fediverse-bot
A fediverse bot hosted on Glitch.
![Glitch Fediverse bot](https://cdn.glitch.com/a4825d5c-d1d6-4780-8464-8636780177ef%2Fglitch-fediverse-bot-small-1024px.png?1538225347895)

# Glitch Fediverse bot

*Work in progress.*

## Bot logic (the back end)

- `TBD`

## The look of your bot's page (the front end)

- You can update the style files inside `src/styles`. You can use [sass](https://sass-lang.com/guide), it will be compiled using [node-sass-middleware](https://github.com/sass/node-sass-middleware).
- Update the scripts inside `src/scripts`. You can use [ES6](http://es6-features.org/#Constants), you script files will be compiled using [express-babelify-middleware](https://github.com/luisfarzati/express-babelify-middleware).
- All templates are inside the `views` folder and use [handlebars.js](http://handlebarsjs.com/).

## TO-DO:

- ~~profile customization via .env~~
- ~~account discovery via webfinger~~
- ~~follow the bot~~
- ~~unfollow the bot~~
- ~~notify followers about new posts~~
- ~~add post pagination~~
- a way to delete posts (password-protected, with the password stored in `.env`)
- upload images:
- https://neocities.org/api, https://neocities.org/supporter, https://github.com/neocities/neocities-node
- https://www.digitalocean.com/products/spaces/, https://glitch.com/~digitalocean-spaces-example
- Flickr API
- reply to messages
- verify the payload

## Resources:

- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
- https://github.com/tootsuite/mastodon/issues/1441
- https://github.com/dariusk/express-activitypub

## Notes

- https://glitch-fediverse-bot.glitch.me/.well-known/webfinger?resource=acct:[email protected]
- https://glitch-fediverse-bot.glitch.me/bot?debug=true



Powered by [Glitch](https://glitch.com/)
-------------------

\ ゜o゜)ノ
64 changes: 64 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
var path = require('path'),
express = require('express'),
exphbs = require('express-handlebars'),
bodyParser = require('body-parser'),
pubSubHubbub = require('pubsubhubbub'),
sassMiddleware = require('node-sass-middleware'),
babelify = require('express-babelify-middleware'),
helpers = require(__dirname + '/helpers/general.js'),
db = require(__dirname + '/helpers/db.js'),
app = express();

app.use(express.static('public'));

app.use(bodyParser.json({
type: 'application/activity+json'
}));

app.use(bodyParser.urlencoded({
extended: true
}));

app.use(sassMiddleware({
// src: __dirname,
src: __dirname + '/src/styles',
dest: path.join(__dirname, 'public'),
force: true,
// debug: true,
outputStyle: 'compressed',
response: true
}));

app.use('/js/scripts.js', babelify('src/scripts/scripts.js', {
minify: true
}));

app.engine('handlebars', exphbs({
defaultLayout: 'main',
helpers: {
for: require('./handlebar-helpers/for'),
equals: require('./handlebar-helpers/equals')
}
}));

app.set('views', __dirname + '/views');
app.set('view engine', 'handlebars');

app.use('/', require('./routes/index.js'))
app.use('/bot', require('./routes/bot.js'));
app.use('/feed', require('./routes/feed.js'));
app.use('/inbox', require('./routes/inbox.js'));
app.use('/outbox', require('./routes/outbox.js'));
app.use('/post', require('./routes/post.js'));
app.use('/pubsub', require('./routes/pubsub.js'));
app.use('/salmon', require('./routes/salmon.js'));
app.use('/webhook', require('./routes/webhook.js'));
app.use('/.well-known', require('./routes/well-known.js'));

app.use(`/${process.env.BOT_ENDPOINT}`, require('./routes/bot-endpoint.js'));

app.get('/js/helpers.js', function (req, res) {
res.sendFile(path.join(__dirname + '/helpers/general.js'));
});

module.exports = app;
191 changes: 191 additions & 0 deletions bot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
var fs = require('fs'),
crypto = require('crypto'),
url = require('url'),
util = require('util'),
moment = require('moment'),
db = require(__dirname + '/helpers/db.js'),
keys = require(__dirname + '/helpers/keys.js'),
request = require('request'),
crypto = require('crypto'),
public_key_path = '.data/rsa/pubKey',
private_key_path = '.data/rsa/privKey',
bot_url = `https://${process.env.PROJECT_DOMAIN}.glitch.me`;

if (!fs.existsSync(public_key_path) || !fs.existsSync(private_key_path)) {
keys.generate_keys(function(){
process.kill(process.pid);
});
}
else{
var public_key = fs.readFileSync(public_key_path, 'utf8'),
private_key = fs.readFileSync(private_key_path, 'utf8');

module.exports = {
bot_url: bot_url,
links: [
// {
// rel: 'http://webfinger.net/rel/profile-page',
// type: 'text/html',
// href: `${bot_url}`
// },
// {
// rel: 'http://schemas.google.com/g/2010#updates-from',
// type: 'application/atom+xml',
// href: `${bot_url}/feed`
// },
{
rel: 'self',
type: 'application/activity+json',
href: `${bot_url}/bot`
},
// {
// rel: 'hub',
// href: `${bot_url}/pubsub`
// },
// {
// rel: 'salmon',
// href: `${bot_url}/salmon`
// },
// {
// rel: 'magic-public-key',
// href: `data:application/magic-public-key,RSA.${public_key.replace('-----BEGIN PUBLIC KEY-----\n', '').replace('\n-----END PUBLIC KEY-----', '').replace('\\n', '')}`
// }
],
info: {
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id': `${bot_url}/bot`,
'icon': [{
'url': process.env.BOT_AVATAR_URL,
'type': 'Image'
}],
'image': [{
'url': process.env.BOT_AVATAR_URL,
'type': 'Image'
}],
'type': 'Person',
'name': process.env.BOT_USERNAME,
'preferredUsername': process.env.BOT_USERNAME,
'inbox': `${bot_url}/inbox`,
'publicKey': {
'id': `${bot_url}/bot#main-key`,
'owner': `${bot_url}/bot`,
'publicKeyPem': public_key
}
},
create_post: function(options, cb){
var bot = this;

if (!options.content || options.content.trim().length === 0 ){
console.log('error: missing post content')
return false;
}

var type = options.type || 'Note',
post_date = moment().format(),
post_in_reply_to = options.in_reply_to || null,
post_content = options.content || '';

db.save_post({
type: type,
content: post_content
}, function(err, data){
var post_id = data.lastID;
var obj = {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `${bot_url}/post/${post_id}`,
'type': 'Create',
'actor': `${bot_url}/bot`,

'object': {
'id': `${bot_url}/post/${post_id}`,
'type': type,
'published': post_date,
'attributedTo': `${bot_url}/bot`,
// 'inReplyTo': post_in_reply_to,
'content': post_content,
'to': 'https://www.w3.org/ns/activitystreams#Public'
}
}
if (cb){
cb(null, obj);
}
});
},
accept: function(payload, cb){
var bot = this,
guid = crypto.randomBytes(16).toString('hex');

bot.sign_and_send({
follower: {
url: payload.actor
},
message: {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `${bot.bot_url}/${guid}`,
'type': 'Accept',
'actor': `${bot.bot_url}/bot`,
'object': payload,
}
}, function(err, data){
if (cb){
cb(err, payload, data);
}
});
},
sign_and_send: function(options, cb){
var bot = this;
// console.log('message to sign:');
// console.log(util.inspect(options.message, false, null, true));

options.follower.url = options.follower.url.replace('http://localhost:3000', 'https://befc66af.ngrok.io');

if (options.follower.url && options.follower.url !== 'undefined'){
options.follower.domain = url.parse(options.follower.url).hostname;

var signer = crypto.createSign('sha256'),
d = new Date(),
string_to_sign = `(request-target): post /inbox\nhost: ${options.follower.domain}\ndate: ${d.toUTCString()}`;

signer.update(string_to_sign);
signer.end();

var signature = signer.sign(private_key);
var signature_b64 = signature.toString('base64');
var header = `keyId="${bot_url}/bot",headers="(request-target) host date",signature="${signature_b64}"`;

var req_object = {
url: `https://${options.follower.domain}/inbox`,
headers: {
'Host': options.follower.domain,
'Date': d.toUTCString(),
'Signature': header
},
method: 'POST',
json: true,
body: options.message
};

// console.log('request object:');
// console.log(util.inspect(req_object, false, null, true));

request(req_object, function (error, response){
console.log(`sent message to ${options.follower.url}...`);
if (error) {
console.log('error:', error, response);
}
else {
console.log('response:', response.statusCode, response.statusMessage);
// console.log(response);
}

if (cb){
cb(error, response);
}
});
}
}
};
}
3 changes: 3 additions & 0 deletions handlebar-helpers/equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function(arg1, arg2, options){
return (arg1 == arg2) ? options.fn(this) : options.inverse(this);
}
6 changes: 6 additions & 0 deletions handlebar-helpers/for.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function(from, to, incr, block){
var accum = '';
for(var i = from; i <= to; i += incr)
accum += block.fn(i);
return accum;
}
4 changes: 4 additions & 0 deletions handlebar-helpers/not_equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = function(arg1, arg2, options){
console.log(arg1, arg2);
return (arg1 != arg2) ? options.fn(this) : options.inverse(this);
}
Loading

0 comments on commit 3e35b08

Please sign in to comment.