2014-12-18 16:01:18 +09:00
|
|
|
var toMarkdown = Npm.require('to-markdown').toMarkdown;
|
2015-03-16 20:02:33 +08:00
|
|
|
var he = Npm.require('he');
|
|
|
|
var FeedParser = Npm.require('feedparser');
|
|
|
|
var Readable = Npm.require('stream').Readable;
|
2014-12-18 16:01:18 +09:00
|
|
|
|
|
|
|
var getFirstAdminUser = function() {
|
|
|
|
return Meteor.users.findOne({isAdmin: true}, {sort: {createdAt: 1}});
|
2015-03-16 20:02:33 +08:00
|
|
|
};
|
2014-12-18 16:01:18 +09:00
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
var feedHandler = {
|
|
|
|
getStream: function(content) {
|
|
|
|
var stream = new Readable();
|
|
|
|
stream.push(content);
|
|
|
|
stream.push(null);
|
2015-02-01 18:26:53 +09:00
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
return stream;
|
|
|
|
},
|
2015-02-01 18:26:53 +09:00
|
|
|
|
2015-03-18 09:45:44 +09:00
|
|
|
getItemCategories: function(item, feedCategories) {
|
|
|
|
|
|
|
|
var itemCategories = [];
|
|
|
|
|
|
|
|
// loop over RSS categories for the current item if it has any
|
2015-03-16 20:02:33 +08:00
|
|
|
if (item.categories && item.categories.length > 0) {
|
|
|
|
item.categories.forEach(function(name) {
|
2015-01-06 11:55:12 +09:00
|
|
|
|
2015-03-18 09:45:44 +09:00
|
|
|
// if the RSS category corresponds to a Telescope cateogry, add it
|
|
|
|
var category = Categories.findOne({name: name}, {fields: {_id: 1}});
|
2015-03-16 20:02:33 +08:00
|
|
|
if (category) {
|
|
|
|
itemCategories.push(category._id);
|
|
|
|
}
|
2015-03-18 09:45:44 +09:00
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
});
|
|
|
|
}
|
2015-01-14 12:31:09 +09:00
|
|
|
|
2015-03-18 09:45:44 +09:00
|
|
|
// add predefined feed categories if there are any and remove any duplicates
|
|
|
|
if (!!feedCategories) {
|
|
|
|
itemCategories = _.uniq(itemCategories.concat(feedCategories));
|
2015-03-16 20:02:33 +08:00
|
|
|
}
|
2015-01-05 09:56:02 +09:00
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
return itemCategories;
|
|
|
|
},
|
2015-01-04 12:51:30 +09:00
|
|
|
|
2015-03-18 09:45:44 +09:00
|
|
|
handle: function(content, userId, feedCategories, feedId) {
|
2015-03-16 20:02:33 +08:00
|
|
|
var stream = this.getStream(content),
|
|
|
|
feedParser = new FeedParser(),
|
|
|
|
newItemsCount = 0,
|
|
|
|
self = this;
|
|
|
|
|
|
|
|
stream.pipe(feedParser);
|
|
|
|
|
|
|
|
feedParser.on('meta', Meteor.bindEnvironment(function(meta) {
|
|
|
|
clog('// Parsing RSS feed: '+ meta.title);
|
|
|
|
}));
|
|
|
|
|
|
|
|
feedParser.on('error', Meteor.bindEnvironment(function(error) {
|
|
|
|
clog(error);
|
|
|
|
}));
|
|
|
|
|
|
|
|
feedParser.on('readable', Meteor.bindEnvironment(function() {
|
|
|
|
var s = this, item;
|
|
|
|
|
|
|
|
while (item = s.read()) {
|
|
|
|
// if item has no guid, use the URL to give it one
|
|
|
|
if (!item.guid) {
|
|
|
|
item.guid = item.link;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if post already exists
|
|
|
|
if (!!Posts.findOne({feedItemId: item.guid})) {
|
2015-03-18 09:45:44 +09:00
|
|
|
clog('// Feed item already imported');
|
2015-03-16 20:02:33 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
newItemsCount++;
|
|
|
|
|
|
|
|
var post = {
|
|
|
|
title: he.decode(item.title),
|
|
|
|
url: item.link,
|
|
|
|
feedId: feedId,
|
|
|
|
feedItemId: item.guid,
|
|
|
|
userId: userId,
|
2015-03-18 09:45:44 +09:00
|
|
|
categories: self.getItemCategories(item, feedCategories)
|
2015-03-16 20:02:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (item.description)
|
|
|
|
post.body = toMarkdown(he.decode(item.description));
|
|
|
|
|
|
|
|
// console.log(item)
|
|
|
|
|
|
|
|
// if RSS item link is a 301 or 302 redirect, follow the redirect
|
|
|
|
var get = HTTP.get(item.link, {followRedirects: false});
|
|
|
|
if (!!get.statusCode && (get.statusCode === 301 || get.statusCode === 302) &&
|
|
|
|
!!get.headers && !!get.headers.location) {
|
|
|
|
post.url = get.headers.location;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if RSS item has a date, use it
|
|
|
|
if (item.pubdate)
|
|
|
|
post.postedAt = moment(item.pubdate).toDate();
|
|
|
|
|
|
|
|
try {
|
|
|
|
submitPost(post);
|
|
|
|
} catch (error) {
|
|
|
|
// catch errors so they don't stop the loop
|
|
|
|
clog(error);
|
|
|
|
}
|
2014-12-18 16:01:18 +09:00
|
|
|
}
|
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
// clog('// Found ' + newItemsCount + ' new feed items');
|
|
|
|
}, function() {
|
|
|
|
clog('Failed to bind environment');
|
|
|
|
}, feedParser));
|
|
|
|
}
|
2014-12-18 16:01:18 +09:00
|
|
|
};
|
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
var fetchFeeds = function() {
|
2014-12-18 16:01:18 +09:00
|
|
|
var content;
|
|
|
|
|
|
|
|
Feeds.find().forEach(function(feed) {
|
2015-01-14 12:31:09 +09:00
|
|
|
|
|
|
|
// if feed doesn't specify a user, default to admin
|
|
|
|
var userId = !!feed.userId ? feed.userId : getFirstAdminUser()._id;
|
2015-03-18 09:45:44 +09:00
|
|
|
var feedCategories = feed.categories;
|
2015-02-09 18:39:50 +00:00
|
|
|
var feedId = feed._id;
|
2015-01-06 11:55:12 +09:00
|
|
|
|
2015-03-16 20:02:33 +08:00
|
|
|
try {
|
2014-12-18 16:01:18 +09:00
|
|
|
content = HTTP.get(feed.url).content;
|
2015-03-18 09:45:44 +09:00
|
|
|
feedHandler.handle(content, userId, feedCategories, feedId);
|
2015-01-06 11:55:12 +09:00
|
|
|
} catch (error) {
|
|
|
|
console.log(error);
|
2015-03-18 09:45:44 +09:00
|
|
|
return true; // just go to next feed URL
|
2015-01-06 11:55:12 +09:00
|
|
|
}
|
2014-12-18 16:01:18 +09:00
|
|
|
});
|
2015-03-16 20:02:33 +08:00
|
|
|
};
|
2014-12-18 16:01:18 +09:00
|
|
|
|
|
|
|
Meteor.methods({
|
|
|
|
fetchFeeds: function () {
|
|
|
|
fetchFeeds();
|
|
|
|
},
|
|
|
|
testEntities: function (text) {
|
|
|
|
console.log(he.decode(text));
|
|
|
|
},
|
|
|
|
testToMarkdown: function (text) {
|
|
|
|
console.log(toMarkdown(text));
|
2015-03-16 20:02:33 +08:00
|
|
|
}
|
2014-12-18 16:01:18 +09:00
|
|
|
})
|