feedsub 0.7.4 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/feedsub.d.ts +62 -0
- package/dist/feedsub.js +280 -0
- package/dist/feedsub.js.map +1 -0
- package/package.json +18 -9
- package/lib/feedsub.js +0 -302
package/README.md
CHANGED
|
@@ -73,8 +73,8 @@ Creates a new instance of FeedSub. `options` defaults to.
|
|
|
73
73
|
### FeedSub#read([callback(err, items)])
|
|
74
74
|
Reads the feed. Calls `callback` with possible error or new items discovered if provided. Causes `reader` to emit new item events.
|
|
75
75
|
|
|
76
|
-
### FeedSub#start()
|
|
77
|
-
Starts checking the feed for any new items.
|
|
76
|
+
### FeedSub#start(readOnStart)
|
|
77
|
+
Starts checking the feed for any new items every `options.interval` minutes. If `readOnStart` is true, it checks right away.
|
|
78
78
|
|
|
79
79
|
### FeedSub#options
|
|
80
80
|
Options that were passed to the constructor along with any defaults are kept here.
|
|
@@ -88,7 +88,7 @@ Stops the reader from automatically reading the feed.
|
|
|
88
88
|
Emitted whenever there is a new item.
|
|
89
89
|
|
|
90
90
|
### Event: items
|
|
91
|
-
* `Array
|
|
91
|
+
* `Array.<Object>` - List of items.
|
|
92
92
|
|
|
93
93
|
Emits all new items from one request in one array.
|
|
94
94
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { TypedEmitter } from 'tiny-typed-emitter';
|
|
2
|
+
import { FeedItem } from 'feedme';
|
|
3
|
+
import NewsEmitter from 'newsemitter';
|
|
4
|
+
import miniget from 'miniget';
|
|
5
|
+
export interface Options {
|
|
6
|
+
interval?: number;
|
|
7
|
+
forceInterval?: boolean;
|
|
8
|
+
autoStart?: boolean;
|
|
9
|
+
emitOnStart?: boolean;
|
|
10
|
+
lastDate?: null | string;
|
|
11
|
+
history?: string[];
|
|
12
|
+
maxHistory?: number;
|
|
13
|
+
skipHours?: boolean;
|
|
14
|
+
hoursToSkip?: number[];
|
|
15
|
+
skipDays?: boolean;
|
|
16
|
+
daysToSkip?: string[];
|
|
17
|
+
requestOpts?: miniget.Options;
|
|
18
|
+
}
|
|
19
|
+
export declare type DefaultOptions = Required<Options>;
|
|
20
|
+
interface FeedSubEvents {
|
|
21
|
+
'error': (err: Error) => void;
|
|
22
|
+
'item': (item: FeedItem) => void;
|
|
23
|
+
'items': (items: FeedItem[]) => void;
|
|
24
|
+
}
|
|
25
|
+
export { FeedItem } from 'feedme';
|
|
26
|
+
export default class FeedSub extends TypedEmitter<FeedSubEvents> {
|
|
27
|
+
feed: string;
|
|
28
|
+
options: DefaultOptions;
|
|
29
|
+
news: NewsEmitter;
|
|
30
|
+
getOpts: miniget.Options & {
|
|
31
|
+
headers: Record<string, string>;
|
|
32
|
+
};
|
|
33
|
+
private _first;
|
|
34
|
+
private _intervalid;
|
|
35
|
+
/**
|
|
36
|
+
* @constructor
|
|
37
|
+
* @param {string} feed
|
|
38
|
+
* @param {!Object} options
|
|
39
|
+
*/
|
|
40
|
+
constructor(feed: string, options?: Options);
|
|
41
|
+
/**
|
|
42
|
+
* Start calling the read function on interval.
|
|
43
|
+
*
|
|
44
|
+
* @param {boolean} readOnStart
|
|
45
|
+
*/
|
|
46
|
+
start(readOnStart?: boolean): void;
|
|
47
|
+
/**
|
|
48
|
+
* Stop interval if any
|
|
49
|
+
*/
|
|
50
|
+
stop(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Reads feed and determines if there are any new items
|
|
53
|
+
* emits new items through event `item`
|
|
54
|
+
* if there are new items, emits `items` event at end with all items
|
|
55
|
+
*
|
|
56
|
+
* if callback is given, calls callback with all new items
|
|
57
|
+
* even when there are 0.
|
|
58
|
+
*
|
|
59
|
+
* @param {!Function(!Error, Array.<Object>)} callback
|
|
60
|
+
*/
|
|
61
|
+
read(callback?: (err: null | Error, items?: FeedItem[]) => void): void;
|
|
62
|
+
}
|
package/dist/feedsub.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
7
|
+
const feedme_1 = __importDefault(require("feedme"));
|
|
8
|
+
const newsemitter_1 = __importDefault(require("newsemitter"));
|
|
9
|
+
const miniget_1 = __importDefault(require("miniget"));
|
|
10
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
11
|
+
// Used for the skipdays tag.
|
|
12
|
+
const DAYS = ['sunday', 'monday', 'tuesday', 'wednesday',
|
|
13
|
+
'thursday', 'friday', 'saturday'];
|
|
14
|
+
class FeedSub extends tiny_typed_emitter_1.TypedEmitter {
|
|
15
|
+
/**
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param {string} feed
|
|
18
|
+
* @param {!Object} options
|
|
19
|
+
*/
|
|
20
|
+
constructor(feed, options) {
|
|
21
|
+
super();
|
|
22
|
+
this.feed = feed;
|
|
23
|
+
this.options = Object.assign({
|
|
24
|
+
interval: 10,
|
|
25
|
+
forceInterval: false,
|
|
26
|
+
autoStart: false,
|
|
27
|
+
emitOnStart: false,
|
|
28
|
+
lastDate: null,
|
|
29
|
+
history: [],
|
|
30
|
+
maxHistory: 10,
|
|
31
|
+
skipHours: false,
|
|
32
|
+
hoursToSkip: null,
|
|
33
|
+
skipDays: false,
|
|
34
|
+
daysToSkip: null,
|
|
35
|
+
requestOpts: {},
|
|
36
|
+
}, options);
|
|
37
|
+
// Create news emitter.
|
|
38
|
+
this.news = new newsemitter_1.default({
|
|
39
|
+
maxHistory: this.options.maxHistory,
|
|
40
|
+
manageHistory: true,
|
|
41
|
+
identifier: (item) => {
|
|
42
|
+
let feedItem = item[0];
|
|
43
|
+
return [
|
|
44
|
+
feedItem.title,
|
|
45
|
+
feedItem.link,
|
|
46
|
+
feedItem.pubdate,
|
|
47
|
+
feedItem.published,
|
|
48
|
+
feedItem.updated
|
|
49
|
+
].join(',');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
this.news.addHistory('item', this.options.history);
|
|
53
|
+
this._first = this.news.history.get('item').size === 0;
|
|
54
|
+
this.getOpts = Object.assign({
|
|
55
|
+
headers: {},
|
|
56
|
+
acceptEncoding: {
|
|
57
|
+
gzip: () => zlib_1.default.createGunzip(),
|
|
58
|
+
deflate: () => zlib_1.default.createInflate(),
|
|
59
|
+
},
|
|
60
|
+
}, this.options.requestOpts);
|
|
61
|
+
if (this.options.autoStart) {
|
|
62
|
+
this.start(true);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Start calling the read function on interval.
|
|
67
|
+
*
|
|
68
|
+
* @param {boolean} readOnStart
|
|
69
|
+
*/
|
|
70
|
+
start(readOnStart) {
|
|
71
|
+
this.stop();
|
|
72
|
+
let ms = this.options.interval * 60000;
|
|
73
|
+
this._intervalid = setInterval(this.read.bind(this), ms);
|
|
74
|
+
if (readOnStart) {
|
|
75
|
+
this.read();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stop interval if any
|
|
80
|
+
*/
|
|
81
|
+
stop() {
|
|
82
|
+
clearInterval(this._intervalid);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reads feed and determines if there are any new items
|
|
86
|
+
* emits new items through event `item`
|
|
87
|
+
* if there are new items, emits `items` event at end with all items
|
|
88
|
+
*
|
|
89
|
+
* if callback is given, calls callback with all new items
|
|
90
|
+
* even when there are 0.
|
|
91
|
+
*
|
|
92
|
+
* @param {!Function(!Error, Array.<Object>)} callback
|
|
93
|
+
*/
|
|
94
|
+
read(callback) {
|
|
95
|
+
let ended = false;
|
|
96
|
+
let req;
|
|
97
|
+
let items = [];
|
|
98
|
+
let newItems = [];
|
|
99
|
+
let sortOrder = 0;
|
|
100
|
+
const error = (err) => {
|
|
101
|
+
ended = true;
|
|
102
|
+
this._first = false;
|
|
103
|
+
if (typeof callback === 'function') {
|
|
104
|
+
callback(err);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
this.emit('error', err);
|
|
108
|
+
}
|
|
109
|
+
req.destroy();
|
|
110
|
+
};
|
|
111
|
+
const success = () => {
|
|
112
|
+
if (ended) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
ended = true;
|
|
116
|
+
if (sortOrder <= 0) {
|
|
117
|
+
newItems.reverse();
|
|
118
|
+
}
|
|
119
|
+
this.news.addHistory('item', newItems.map((item) => [item]));
|
|
120
|
+
if (this._first && !this.options.emitOnStart) {
|
|
121
|
+
newItems = [];
|
|
122
|
+
}
|
|
123
|
+
newItems.forEach(item => this.emit('item', item));
|
|
124
|
+
this.emit('items', newItems);
|
|
125
|
+
if (req) {
|
|
126
|
+
req.destroy();
|
|
127
|
+
}
|
|
128
|
+
if (typeof callback === 'function') {
|
|
129
|
+
callback(null, newItems);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
// If skipHours or skipDays are enabled and feed provides hours/days
|
|
133
|
+
// to skip and it's currently one of those hours/days, abort.
|
|
134
|
+
const now = new Date();
|
|
135
|
+
const shouldSkip = () => {
|
|
136
|
+
return ((!this._first || !this.options.emitOnStart) &&
|
|
137
|
+
(this.options.hoursToSkip || this.options.daysToSkip)) && ((this.options.hoursToSkip &&
|
|
138
|
+
this.options.hoursToSkip.indexOf(now.getHours()) !== -1) ||
|
|
139
|
+
(this.options.daysToSkip &&
|
|
140
|
+
this.options.daysToSkip.some((day) => {
|
|
141
|
+
return day.toLowerCase() === DAYS[now.getDay()];
|
|
142
|
+
})));
|
|
143
|
+
};
|
|
144
|
+
if (shouldSkip()) {
|
|
145
|
+
return success();
|
|
146
|
+
}
|
|
147
|
+
req = miniget_1.default(this.feed, this.getOpts);
|
|
148
|
+
req.on('response', (res) => {
|
|
149
|
+
// Check if not modified code is sent back
|
|
150
|
+
// in this case, the body will be empty.
|
|
151
|
+
if (res.statusCode === 304) {
|
|
152
|
+
return success();
|
|
153
|
+
}
|
|
154
|
+
// Check headers for conditional get.
|
|
155
|
+
if (res.headers['last-modified']) {
|
|
156
|
+
this.getOpts.headers['If-Modified-Since'] = res.headers['last-modified'];
|
|
157
|
+
}
|
|
158
|
+
if (res.headers.etag) {
|
|
159
|
+
this.getOpts.headers['If-None-Match'] = res.headers.etag;
|
|
160
|
+
}
|
|
161
|
+
// Save date.
|
|
162
|
+
let date;
|
|
163
|
+
let getdate = (text) => date = text;
|
|
164
|
+
// Create feed parser.
|
|
165
|
+
const parser = new feedme_1.default();
|
|
166
|
+
parser.on('error', error);
|
|
167
|
+
// Try to get date from one of the fields.
|
|
168
|
+
parser.once('pubdate', getdate);
|
|
169
|
+
parser.once('lastbuilddate', getdate);
|
|
170
|
+
parser.once('updated', getdate);
|
|
171
|
+
// Change interval time if ttl available.
|
|
172
|
+
if (!this.options.forceInterval) {
|
|
173
|
+
parser.once('ttl', (minutes) => {
|
|
174
|
+
minutes = parseInt(minutes, 10);
|
|
175
|
+
// Only update if ttl is longer than requested interval.
|
|
176
|
+
if (minutes > this.options.interval) {
|
|
177
|
+
this.options.interval = minutes;
|
|
178
|
+
if (this.options.autoStart) {
|
|
179
|
+
this.start(false);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// Listen for skipHours if enabled.
|
|
185
|
+
if (this.options.skipHours && !this.options.hoursToSkip) {
|
|
186
|
+
parser.once('skiphours', (data) => {
|
|
187
|
+
this.options.hoursToSkip =
|
|
188
|
+
[].concat(data.hour).map(h => parseInt(h, 10));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// Listen for skipDays if enabled.
|
|
192
|
+
if (this.options.skipDays !== false && !this.options.daysToSkip) {
|
|
193
|
+
parser.once('skipdays', (data) => {
|
|
194
|
+
this.options.daysToSkip = [].concat(data.day);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// Compare date when first item is encountered.
|
|
198
|
+
const firstitem = (item) => {
|
|
199
|
+
// If date is the same as last, abort.
|
|
200
|
+
if (date && this.options.lastDate === date) {
|
|
201
|
+
return success();
|
|
202
|
+
}
|
|
203
|
+
if (shouldSkip()) {
|
|
204
|
+
return success();
|
|
205
|
+
}
|
|
206
|
+
// Continue if dates differ.
|
|
207
|
+
if (date) {
|
|
208
|
+
this.options.lastDate = date;
|
|
209
|
+
}
|
|
210
|
+
parser.on('item', getItem);
|
|
211
|
+
getItem(item);
|
|
212
|
+
};
|
|
213
|
+
parser.once('item', firstitem);
|
|
214
|
+
const getItemDate = (item) => +new Date(item.pubdate || item.published || 0);
|
|
215
|
+
const getItem = (item) => {
|
|
216
|
+
if (sortOrder === 0) {
|
|
217
|
+
items.push(item);
|
|
218
|
+
sortOrder = getItemDate(item) - getItemDate(items[0]);
|
|
219
|
+
if (sortOrder < 0) {
|
|
220
|
+
items.forEach(getOlderItem);
|
|
221
|
+
}
|
|
222
|
+
else if (sortOrder > 0) {
|
|
223
|
+
items.forEach(getNewerItem);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else if (sortOrder < 0) {
|
|
227
|
+
getOlderItem(item);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
getNewerItem(item);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const getOlderItem = (item) => {
|
|
234
|
+
if (this._first) {
|
|
235
|
+
newItems.push(item);
|
|
236
|
+
}
|
|
237
|
+
else if (!ended) {
|
|
238
|
+
let emitted = this.news.emit('item', item);
|
|
239
|
+
if (emitted) {
|
|
240
|
+
newItems.push(item);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// Check if this item has already been read in previous requests
|
|
244
|
+
// if it has, then stop parsing the rest of the document.
|
|
245
|
+
parser.removeListener('item', getItem);
|
|
246
|
+
success();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
let foundPrevItem = false;
|
|
251
|
+
const getNewerItem = (item) => {
|
|
252
|
+
if (this._first) {
|
|
253
|
+
newItems.push(item);
|
|
254
|
+
}
|
|
255
|
+
else if (!foundPrevItem && !this.news.emit('item', item)) {
|
|
256
|
+
foundPrevItem = true;
|
|
257
|
+
}
|
|
258
|
+
else if (foundPrevItem && this.news.emit('item', item)) {
|
|
259
|
+
newItems.push(item);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
req.pipe(parser);
|
|
263
|
+
parser.on('finish', () => {
|
|
264
|
+
if (ended) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// Process items in descending order if no order found at end.
|
|
268
|
+
if (sortOrder === 0 && newItems.length === 0) {
|
|
269
|
+
items.forEach(getOlderItem);
|
|
270
|
+
}
|
|
271
|
+
success();
|
|
272
|
+
this._first = false;
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
req.on('error', error);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
exports.default = FeedSub;
|
|
279
|
+
module.exports = FeedSub;
|
|
280
|
+
//# sourceMappingURL=feedsub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedsub.js","sourceRoot":"","sources":["../src/feedsub.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -8,30 +8,39 @@
|
|
|
8
8
|
"json",
|
|
9
9
|
"subscribe"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.7.
|
|
11
|
+
"version": "0.7.8",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "git://github.com/fent/node-feedsub.git"
|
|
15
15
|
},
|
|
16
16
|
"author": "fent <fentbox@gmail.com> (https://github.com/fent)",
|
|
17
|
-
"main": "./
|
|
17
|
+
"main": "./dist/feedsub.js",
|
|
18
18
|
"files": [
|
|
19
|
-
"
|
|
19
|
+
"dist"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"
|
|
22
|
+
"prepare": "tsc -p tsconfig.build.json",
|
|
23
|
+
"build": "tsc -p tsconfig.build.json",
|
|
24
|
+
"test": "nyc --extension .ts --reporter=lcov --reporter=text-summary npm run test:unit",
|
|
25
|
+
"test:unit": "mocha --require ts-node/register test/*-test.ts"
|
|
23
26
|
},
|
|
24
27
|
"dependencies": {
|
|
25
|
-
"feedme": "^
|
|
26
|
-
"miniget": "^
|
|
27
|
-
"newsemitter": "^1.0.
|
|
28
|
+
"feedme": "^2.0.2",
|
|
29
|
+
"miniget": "^4.2.2",
|
|
30
|
+
"newsemitter": "^1.0.2",
|
|
31
|
+
"tiny-typed-emitter": "^2.0.3"
|
|
28
32
|
},
|
|
29
33
|
"devDependencies": {
|
|
34
|
+
"@types/mocha": "^8.2.0",
|
|
35
|
+
"@types/nock": "^11.1.0",
|
|
36
|
+
"@types/node": "^14.14.11",
|
|
37
|
+
"@types/sinon": "^9.0.9",
|
|
30
38
|
"mocha": "^6.0.0",
|
|
31
|
-
"muk-prop": "^1.2.1",
|
|
32
39
|
"nock": "^11.1.0",
|
|
33
40
|
"nyc": "^15.1.0",
|
|
34
|
-
"sinon": "^8.0.0"
|
|
41
|
+
"sinon": "^8.0.0",
|
|
42
|
+
"ts-node": "^9.1.1",
|
|
43
|
+
"typescript": "^4.1.2"
|
|
35
44
|
},
|
|
36
45
|
"engines": {
|
|
37
46
|
"node": ">=10"
|
package/lib/feedsub.js
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events').EventEmitter;
|
|
2
|
-
const FeedMe = require('feedme');
|
|
3
|
-
const NewsEmitter = require('newsemitter');
|
|
4
|
-
const miniget = require('miniget');
|
|
5
|
-
const zlib = require('zlib');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// Used for the skipdays tag.
|
|
9
|
-
const DAYS = ['sunday', 'monday', 'tuesday', 'wednesday',
|
|
10
|
-
'thursday', 'friday', 'saturday'];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
module.exports = class FeedReader extends EventEmitter {
|
|
14
|
-
/**
|
|
15
|
-
* @constructor
|
|
16
|
-
* @param {string} feed
|
|
17
|
-
* @param {!Object} options
|
|
18
|
-
*/
|
|
19
|
-
constructor(feed, options) {
|
|
20
|
-
super();
|
|
21
|
-
this.feed = feed;
|
|
22
|
-
this.options = Object.assign({
|
|
23
|
-
interval : 10,
|
|
24
|
-
forceInterval : false,
|
|
25
|
-
autoStart : false,
|
|
26
|
-
emitOnStart : false,
|
|
27
|
-
lastDate : null,
|
|
28
|
-
history : [],
|
|
29
|
-
maxHistory : 10,
|
|
30
|
-
skipHours : false,
|
|
31
|
-
skipDays : false,
|
|
32
|
-
}, options);
|
|
33
|
-
|
|
34
|
-
// Create news emitter.
|
|
35
|
-
this.news = new NewsEmitter({
|
|
36
|
-
maxHistory: this.options.maxHistory,
|
|
37
|
-
manageHistory: true,
|
|
38
|
-
identifier: (item) => {
|
|
39
|
-
item = item[0];
|
|
40
|
-
return [
|
|
41
|
-
item.title,
|
|
42
|
-
item.link,
|
|
43
|
-
item.pubdate,
|
|
44
|
-
item.published,
|
|
45
|
-
item.updated
|
|
46
|
-
].join(',');
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
this.news.addHistory('item', this.options.history);
|
|
51
|
-
this.first = this.news.history.get('item').size === 0;
|
|
52
|
-
this.getOpts = Object.assign({
|
|
53
|
-
headers: {},
|
|
54
|
-
acceptEncoding: {
|
|
55
|
-
gzip: () => zlib.createGunzip(),
|
|
56
|
-
deflate: () => zlib.createInflate(),
|
|
57
|
-
},
|
|
58
|
-
}, this.options.requestOpts);
|
|
59
|
-
if (this.options.autoStart) {
|
|
60
|
-
this.start(true);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Start calling the read function on interval.
|
|
67
|
-
*
|
|
68
|
-
* @param {boolean} readOnStart
|
|
69
|
-
*/
|
|
70
|
-
start(readOnStart) {
|
|
71
|
-
this.stop();
|
|
72
|
-
let ms = this.options.interval * 60000;
|
|
73
|
-
this._intervalid = setInterval(this.read.bind(this), ms);
|
|
74
|
-
if (readOnStart) {
|
|
75
|
-
this.read();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Stop interval if any
|
|
82
|
-
*/
|
|
83
|
-
stop() {
|
|
84
|
-
clearInterval(this._intervalid);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Reads feed and determines if there are any new items
|
|
90
|
-
* emits new items through event `item`
|
|
91
|
-
* if there are new items, emits `items` event at end with all items
|
|
92
|
-
*
|
|
93
|
-
* if callback is given, calls callback with all new items
|
|
94
|
-
* even when there are 0.
|
|
95
|
-
*
|
|
96
|
-
* @param {!Function(!Error, Array.<Object>)} callback
|
|
97
|
-
*/
|
|
98
|
-
read(callback) {
|
|
99
|
-
let ended = false;
|
|
100
|
-
let req;
|
|
101
|
-
let items = [];
|
|
102
|
-
let newItems = [];
|
|
103
|
-
let sortOrder = 0;
|
|
104
|
-
|
|
105
|
-
const error = (err) => {
|
|
106
|
-
ended = true;
|
|
107
|
-
this.first = false;
|
|
108
|
-
if (typeof callback === 'function') {
|
|
109
|
-
callback(err);
|
|
110
|
-
} else {
|
|
111
|
-
this.emit('error', err);
|
|
112
|
-
}
|
|
113
|
-
req.destroy();
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const success = () => {
|
|
117
|
-
if (ended) { return; }
|
|
118
|
-
ended = true;
|
|
119
|
-
if (sortOrder <= 0) {
|
|
120
|
-
newItems.reverse();
|
|
121
|
-
}
|
|
122
|
-
this.news.addHistory('item', newItems.map((item) => [item]));
|
|
123
|
-
if (this.first && !this.options.emitOnStart) {
|
|
124
|
-
newItems = [];
|
|
125
|
-
}
|
|
126
|
-
newItems.forEach(this.emit.bind(this, 'item'));
|
|
127
|
-
this.emit('items', newItems);
|
|
128
|
-
if (req) {
|
|
129
|
-
req.destroy();
|
|
130
|
-
}
|
|
131
|
-
if (typeof callback === 'function') {
|
|
132
|
-
callback(null, newItems);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// If skipHours or skipDays are enabled and feed provides hours/days
|
|
137
|
-
// to skip and it's currently one of those hours/days, abort.
|
|
138
|
-
const now = new Date();
|
|
139
|
-
const shouldSkip = () => {
|
|
140
|
-
return (
|
|
141
|
-
(!this.first || !this.options.emitOnStart) &&
|
|
142
|
-
(this.options.hoursToSkip || this.options.daysToSkip)
|
|
143
|
-
) && (
|
|
144
|
-
(this.options.hoursToSkip &&
|
|
145
|
-
this.options.hoursToSkip.indexOf(now.getHours()) !== -1) ||
|
|
146
|
-
(this.options.daysToSkip &&
|
|
147
|
-
this.options.daysToSkip.some((day) => {
|
|
148
|
-
return day.toLowerCase() === DAYS[now.getDay()];
|
|
149
|
-
})
|
|
150
|
-
)
|
|
151
|
-
);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
if (shouldSkip()) {
|
|
155
|
-
return success();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
req = miniget(this.feed, this.getOpts);
|
|
159
|
-
req.on('response', (res) => {
|
|
160
|
-
// Check if not modified code is sent back
|
|
161
|
-
// in this case, the body will be empty.
|
|
162
|
-
if (res.statusCode === 304) {
|
|
163
|
-
return success();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Check headers for conditional get.
|
|
167
|
-
if (res.headers['last-modified']) {
|
|
168
|
-
this.getOpts.headers['If-Modified-Since'] = res.headers['last-modified'];
|
|
169
|
-
}
|
|
170
|
-
if (res.headers.etag) {
|
|
171
|
-
this.getOpts.headers['If-None-Match'] = res.headers.etag;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Save date.
|
|
175
|
-
let date;
|
|
176
|
-
let getdate = text => date = text;
|
|
177
|
-
|
|
178
|
-
// Create feed parser.
|
|
179
|
-
const parser = new FeedMe();
|
|
180
|
-
parser.on('error', error);
|
|
181
|
-
|
|
182
|
-
// Try to get date from one of the fields.
|
|
183
|
-
parser.once('pubdate', getdate);
|
|
184
|
-
parser.once('lastbuilddate', getdate);
|
|
185
|
-
parser.once('updated', getdate);
|
|
186
|
-
|
|
187
|
-
// Change interval time if ttl available.
|
|
188
|
-
if (!this.options.forceInterval) {
|
|
189
|
-
parser.once('ttl', (minutes) => {
|
|
190
|
-
minutes = parseInt(minutes, 10);
|
|
191
|
-
|
|
192
|
-
// Only update if ttl is longer than requested interval.
|
|
193
|
-
if (minutes > this.options.interval) {
|
|
194
|
-
this.options.interval = minutes;
|
|
195
|
-
if (this.options.autoStart) {
|
|
196
|
-
this.start(false);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Listen for skipHours if enabled.
|
|
203
|
-
if (this.options.skipHours && !this.options.hoursToSkip) {
|
|
204
|
-
parser.once('skiphours', (data) => {
|
|
205
|
-
this.options.hoursToSkip =
|
|
206
|
-
[].concat(data.hour).map(h => parseInt(h, 10));
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Listen for skipDays if enabled.
|
|
211
|
-
if (this.options.skipDays !== false && !this.options.daysToSkip) {
|
|
212
|
-
parser.once('skipdays', (data) => {
|
|
213
|
-
this.options.daysToSkip = [].concat(data.day);
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// Compare date when first item is encountered.
|
|
219
|
-
const firstitem = (item) => {
|
|
220
|
-
// If date is the same as last, abort.
|
|
221
|
-
if (date && this.options.lastDate === date) {
|
|
222
|
-
return success();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (shouldSkip()) {
|
|
226
|
-
return success();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Continue if dates differ.
|
|
230
|
-
if (date) {
|
|
231
|
-
this.options.lastDate = date;
|
|
232
|
-
}
|
|
233
|
-
parser.on('item', getItem);
|
|
234
|
-
getItem(item);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
parser.once('item', firstitem);
|
|
238
|
-
|
|
239
|
-
const getItemDate = (item) => new Date(item.pubdate || item.published || 0);
|
|
240
|
-
const getItem = (item) => {
|
|
241
|
-
if (sortOrder === 0) {
|
|
242
|
-
items.push(item);
|
|
243
|
-
sortOrder = getItemDate(item) - getItemDate(items[0]);
|
|
244
|
-
if (sortOrder < 0) {
|
|
245
|
-
items.forEach(getOlderItem);
|
|
246
|
-
} else if (sortOrder > 0) {
|
|
247
|
-
items.forEach(getNewerItem);
|
|
248
|
-
}
|
|
249
|
-
} else if (sortOrder < 0) {
|
|
250
|
-
getOlderItem(item);
|
|
251
|
-
|
|
252
|
-
} else {
|
|
253
|
-
getNewerItem(item);
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const getOlderItem = (item) => {
|
|
258
|
-
if (this.first) {
|
|
259
|
-
newItems.push(item);
|
|
260
|
-
} else if (!ended) {
|
|
261
|
-
let emitted = this.news.emit('item', item);
|
|
262
|
-
if (emitted) {
|
|
263
|
-
newItems.push(item);
|
|
264
|
-
} else {
|
|
265
|
-
// Check if this item has already been read in previous requests
|
|
266
|
-
// if it has, then stop parsing the rest of the document.
|
|
267
|
-
parser.removeListener('item', getItem);
|
|
268
|
-
success();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
let foundPrevItem = false;
|
|
274
|
-
const getNewerItem = (item) => {
|
|
275
|
-
if (this.first) {
|
|
276
|
-
newItems.push(item);
|
|
277
|
-
} else if (!foundPrevItem && !this.news.emit('item', item)) {
|
|
278
|
-
foundPrevItem = true;
|
|
279
|
-
} else if (foundPrevItem && this.news.emit('item', item)) {
|
|
280
|
-
newItems.push(item);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
req.pipe(parser);
|
|
285
|
-
|
|
286
|
-
const onEnd = () => {
|
|
287
|
-
if (ended) { return; }
|
|
288
|
-
// Process items in descending order if no order found at end.
|
|
289
|
-
if (sortOrder === 0 && newItems.length === 0) {
|
|
290
|
-
items.forEach(getOlderItem);
|
|
291
|
-
}
|
|
292
|
-
success();
|
|
293
|
-
this.first = false;
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
req.on('close', onEnd);
|
|
297
|
-
req.on('end', onEnd);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
req.on('error', error);
|
|
301
|
-
}
|
|
302
|
-
};
|