You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			163 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
| /* eslint-disable no-await-in-loop */
 | |
| /* eslint-disable no-loop-func */
 | |
| /* global log, window, textsecure */
 | |
| 
 | |
| const EventEmitter = require('events');
 | |
| 
 | |
| const PER_MIN = 60 * 1000;
 | |
| const PER_HR = 60 * PER_MIN;
 | |
| const RSS_POLL_EVERY = 1 * PER_HR; // once an hour
 | |
| 
 | |
| function xml2json(xml) {
 | |
|   try {
 | |
|     let obj = {};
 | |
|     if (xml.children.length > 0) {
 | |
|       for (let i = 0; i < xml.children.length; i += 1) {
 | |
|         const item = xml.children.item(i);
 | |
|         const { nodeName } = item;
 | |
| 
 | |
|         if (typeof obj[nodeName] === 'undefined') {
 | |
|           obj[nodeName] = xml2json(item);
 | |
|         } else {
 | |
|           if (typeof obj[nodeName].push === 'undefined') {
 | |
|             const old = obj[nodeName];
 | |
| 
 | |
|             obj[nodeName] = [];
 | |
|             obj[nodeName].push(old);
 | |
|           }
 | |
|           obj[nodeName].push(xml2json(item));
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       obj = xml.textContent;
 | |
|     }
 | |
|     return obj;
 | |
|   } catch (e) {
 | |
|     log.error(e.message);
 | |
|   }
 | |
|   return {};
 | |
| }
 | |
| 
 | |
| class LokiRssAPI extends EventEmitter {
 | |
|   constructor(settings) {
 | |
|     super();
 | |
|     // properties
 | |
|     this.feedUrl = settings.RSS_FEED;
 | |
|     this.groupId = settings.CONVO_ID;
 | |
|     this.feedTitle = settings.title;
 | |
|     this.closeable = settings.closeable;
 | |
|     // non configureable options
 | |
|     this.feedTimer = null;
 | |
|     // initial set up
 | |
|     this.getFeed();
 | |
|   }
 | |
| 
 | |
|   async getFeed() {
 | |
|     // deal with file server proxy hardcoding
 | |
|     const map = {
 | |
|       'https://loki.network/category/messenger-updates/feed/':
 | |
|         'loki/v1/rss/messenger',
 | |
|       'https://loki.network/feed/': 'loki/v1/rss/loki',
 | |
|     };
 | |
|     if (map[this.feedUrl] === undefined) {
 | |
|       log.warn('LokiRssAPI unsupported rss feed', this.feedUrl);
 | |
|       return;
 | |
|     }
 | |
|     const result = await window.tokenlessFileServerAdnAPI.serverRequest(
 | |
|       map[this.feedUrl]
 | |
|     );
 | |
|     if (!result) {
 | |
|       log.error('LokiRssAPI empty rss proxy response');
 | |
|       return;
 | |
|     }
 | |
|     if (!result.response) {
 | |
|       log.error('LokiRssAPI rss proxy error, no response', result);
 | |
|       return;
 | |
|     }
 | |
|     if (!result.response.data) {
 | |
|       log.error(
 | |
|         'LokiRssAPI rss proxy error, no data, response',
 | |
|         typeof result.response,
 | |
|         result.response
 | |
|       );
 | |
|       return;
 | |
|     }
 | |
|     const responseXML = result.response.data;
 | |
|     let feedDOM = {};
 | |
|     try {
 | |
|       feedDOM = await new window.DOMParser().parseFromString(
 | |
|         responseXML,
 | |
|         'text/xml'
 | |
|       );
 | |
|     } catch (e) {
 | |
|       log.error('LokiRssAPI xml parsing error', e, responseXML);
 | |
|       return;
 | |
|     }
 | |
|     const feedObj = xml2json(feedDOM);
 | |
|     let receivedAt = new Date().getTime();
 | |
| 
 | |
|     if (!feedObj || !feedObj.rss || !feedObj.rss.channel) {
 | |
|       log.error(
 | |
|         'LokiRssAPI rss structure error',
 | |
|         feedObj,
 | |
|         feedDOM,
 | |
|         responseXML
 | |
|       );
 | |
|       return;
 | |
|     }
 | |
|     if (!feedObj.rss.channel.item) {
 | |
|       // no records, not an error
 | |
|       return;
 | |
|     }
 | |
|     if (feedObj.rss.channel.item.constructor !== Array) {
 | |
|       // Treat single record as array for consistency
 | |
|       feedObj.rss.channel.item = [feedObj.rss.channel.item];
 | |
|     }
 | |
|     feedObj.rss.channel.item.reverse().forEach(item => {
 | |
|       // log.debug('item', item)
 | |
| 
 | |
|       const pubDate = new Date(item.pubDate);
 | |
| 
 | |
|       // if we use group style, we can put the title in the source
 | |
|       const messageData = {
 | |
|         isSessionRequest: false,
 | |
|         source: this.groupId,
 | |
|         sourceDevice: 1,
 | |
|         timestamp: pubDate.getTime(),
 | |
|         serverTimestamp: pubDate.getTime(),
 | |
|         receivedAt,
 | |
|         isRss: true,
 | |
|         message: {
 | |
|           body: `<h2>${item.title} </h2>${item.description}`,
 | |
|           attachments: [],
 | |
|           group: {
 | |
|             id: this.groupId,
 | |
|             type: textsecure.protobuf.GroupContext.Type.DELIVER,
 | |
|           },
 | |
|           flags: 0,
 | |
|           expireTimer: 0,
 | |
|           profileKey: null,
 | |
|           timestamp: pubDate.getTime(),
 | |
|           received_at: receivedAt,
 | |
|           sent_at: pubDate.getTime(),
 | |
|           quote: null,
 | |
|           contact: [],
 | |
|           preview: [],
 | |
|           profile: null,
 | |
|         },
 | |
|       };
 | |
|       receivedAt += 1; // Ensure different arrival times
 | |
|       this.emit('rssMessage', {
 | |
|         message: messageData,
 | |
|       });
 | |
|     });
 | |
|     const ref = this;
 | |
|     function callTimer() {
 | |
|       ref.getFeed();
 | |
|     }
 | |
|     this.feedTimer = setTimeout(callTimer, RSS_POLL_EVERY);
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = LokiRssAPI;
 |