From c48fdfd7ec1482b7edc8a4a50b34afd08c4d0e0f Mon Sep 17 00:00:00 2001 From: fenwick67 Date: Wed, 31 Jul 2019 12:13:56 -0400 Subject: [PATCH] a feed appears (v2) --- index.js | 63 +++++++++++++++++++++++ lib/convertv2.js | 123 ++++++++++++++++++++++++++++++++++++++++++++ npm-shrinkwrap.json | 26 +++++++++- package.json | 1 + 4 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 lib/convertv2.js diff --git a/index.js b/index.js index 38d45be..52ab2e8 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,8 @@ var Express = require('express'); +// v1 api var convert = require('./lib/convert'); +// v2 api +var convertv2 = require('./lib/convertv2'); var serveStatic = require('serve-static'); var request = require('request'); var cors = require('cors'); @@ -81,6 +84,66 @@ app.get('/api/feed',cors(),function(req,res){ }); +app.options('/apiv2/feed',cors()); +// http://localhost:8000/apiv2/feed?userurl=https%3A%2F%2Foctodon.social%2Fusers%2Ffenwick67 +app.get('/apiv2/feed',cors(),function(req,res){ + + // get feed url + var userUrl = req.query.userurl; + if (!userUrl){ + res.status(400); + res.send('You need to specify a user URL'); + } + + var feedUrl = req.query.feedUrl; + + var opts = {}; + if (req.query.size){ + opts.size = req.query.size; + } + if (req.query.theme){ + opts.theme = req.query.theme; + } + if (req.query.header){ + if (req.query.header.toLowerCase() == 'no' || req.query.header.toLowerCase() == 'false'){ + opts.header = false; + }else{ + opts.header = true; + } + } + + opts.boosts = true; + if (req.query.boosts){ + if (req.query.boosts.toLowerCase() == 'no' || req.query.boosts.toLowerCase() == 'false'){ + opts.boosts = false; + }else{ + opts.boosts = true; + } + } + + opts.replies = true; + if (req.query.replies){ + if (req.query.replies.toLowerCase() == 'no' || req.query.replies.toLowerCase() == 'false'){ + opts.replies = false; + }else{ + opts.replies = true; + } + } + opts.userUrl = userUrl; + opts.feedUrl = feedUrl; + opts.mastofeedUrl = req.url; + + convertv2(opts).then((data)=>{ + res.status(200); + res.send(data); + }).catch((er)=>{ + res.status(500); + res.send('Error fetching or parsing your feed.'); + // TODO log the error + console.error(er); + }) +}) + app.listen(process.env.PORT || 8000,function(){ log('listening on '+(process.env.PORT || 8000)); }); diff --git a/lib/convertv2.js b/lib/convertv2.js new file mode 100644 index 0000000..53bb9e6 --- /dev/null +++ b/lib/convertv2.js @@ -0,0 +1,123 @@ +var ejs = require('ejs'); +var fs = require('fs'); +var template = ejs.compile(fs.readFileSync('./lib/template.ejs', 'utf8')); +var timeAgo = require('timeago.js'); +var request = require('request-promise-native') + +// get JSON for an AP URL +async function apGet(url) { + return request.get( { + uri:url, + headers: { + "accept": "application/activity+json" + }, + transform: function (body) { + return JSON.parse(body); + } + }) +} + +// accumulate a stream of XML into a html file + +module.exports = async function (opts) { + var opts = opts; + + var feedUrl = opts.feedUrl; + var userUrl = opts.userUrl; + var isIndex = false; + + if (!userUrl) { + throw new Error('need user URL'); + } + + var user = await apGet(userUrl); + + if (userUrl && !feedUrl) { + isIndex = true; + var outbox = await apGet(user.outbox); + feedUrl = outbox.first; + + } + + var feed = await apGet(feedUrl); + + var items = itemsForFeed(feed); + + var templateData = { + opts: opts,// from the request + meta: metaForUser(user), + items: itemsForFeed(feed), + nextPageLink: getNextPage(user,feed), + isIndex: isIndex + }; + + return template(templateData); + +} + +function metaForUser(user) { + return { + avatar: user.icon && user.icon.url?user.icon.url:null, + headerImage:user.image && user.image.url?user.image.url:null, + title: user.preferredUsername||null, + description: user.summary||null + } +} + +// TODO make function +function itemsForFeed(feed) { + return feed.orderedItems.map((item)=>{ + return { + isBoost:false, + title:'', + isReply:!!(item.object && item.object.inReplyTo), + hasCw:false, + cw:'', + atomHref:item.published?item.published.replace(/\W+/g,''):Math.random().toString().replace('.',''), + enclosures:[],//type, url + stringDate:item.published?getTimeDisplay(Date.parse(item.published)):'', + author:{ + uri:'',// link to author page + avatar:'',// url of av + fullName:'',// display name + } + } + }) +} + +// TODO +function getNextPage(user,feed){ + return null; +} + + +// utilities below + +function getTimeDisplay(d) { + var d = d; + if (typeof d !== 'object') { + d = new Date(d); + } + // convert to number + dt = d.getTime(); + var now = Date.now(); + + var delta = now - dt; + + // over 6 days ago + if (delta > 1000 * 60 * 60 * 24 * 6) { + return isoDateToEnglish(d.toISOString()); + } else { + return timeAgo().format(dt); + } + +} + +function isoDateToEnglish(d) { + + var dt = d.split(/[t\-]/ig); + var months = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"]; + + return months[Number(dt[1]) - 1] + ' ' + dt[2] + ', ' + dt[0]; +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index edb1592..aeaef22 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -861,8 +861,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash.assign": { "version": "4.2.0", @@ -1403,6 +1402,24 @@ } } }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -1595,6 +1612,11 @@ "readable-stream": "^2.0.1" } }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", diff --git a/package.json b/package.json index 93e3ce7..7ac2ca9 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "express": "^4.16.4", "feedparser": "^2.2.9", "request": "^2.88.0", + "request-promise-native": "^1.0.7", "serve-static": "^1.13.2", "timeago.js": "^3.0.2" },