2017-12-11 03:05:18 +02:00
// @ts-ignore
2017-11-27 01:37:54 +02:00
const Config = require ( "../config.json" ) ;
2017-12-11 03:05:18 +02:00
2017-12-08 02:29:16 +02:00
const { promisify } = require ( "util" ) ;
2017-12-11 03:05:18 +02:00
const Camo = require ( "camo" ) ;
const Core = require ( "../../discord-bot-core" ) ;
const DiscordUtil = require ( "../../discord-bot-core" ) . util ;
const GetUrls = require ( "get-urls" ) ;
const Url = require ( "url" ) ;
2017-09-09 22:26:04 +03:00
2017-12-11 03:05:18 +02:00
// @ts-ignore
const readFeed = url => promisify ( require ( "feed-read" ) ) ( url ) ;
const resolveDns = promisify ( require ( "dns" ) . resolve ) ;
module . exports = class FeedData extends Core . BaseEmbeddedData {
2017-12-07 03:35:00 +02:00
constructor ( ) {
super ( ) ;
2017-09-09 22:26:04 +03:00
2017-12-11 03:05:18 +02:00
this . feedID = "" ;
this . url = "" ;
this . channelID = "" ;
this . roleID = "" ;
this . cachedLinks = [ ] ;
this . maxCacheSize = 100 ;
// @ts-ignore
this . schema ( {
feedID : String ,
url : String ,
channelID : String ,
roleID : String ,
cachedLinks : [ String ] ,
maxCacheSize : Number
} ) ;
2017-12-07 03:35:00 +02:00
}
2017-10-02 02:41:55 +03:00
2017-12-07 03:35:00 +02:00
cache ( ... elements ) {
2017-12-11 03:05:18 +02:00
const newArticles = elements
. map ( el => normaliseUrl ( el ) )
. filter ( el => this . cachedLinks . indexOf ( el ) === - 1 ) ;
Array . prototype . push . apply ( this . cachedLinks , newArticles ) ;
this . cachedLinks . splice ( 0 , this . cachedLinks . length - this . maxCacheSize ) ; //seeing as new links come in at the end of the array, we need to remove the old links from the beginning
return elements . length > 0 ;
2017-09-09 22:26:04 +03:00
}
updatePastPostedLinks ( guild ) {
2017-10-02 01:19:10 +03:00
const channel = guild . channels . get ( this . channelID ) ;
if ( ! channel )
return Promise . reject ( "Channel not found!" ) ;
2017-09-09 22:26:04 +03:00
return new Promise ( ( resolve , reject ) => {
channel . fetchMessages ( { limit : 100 } )
. then ( messages => {
2017-10-02 01:19:10 +03:00
/ * w e w a n t t o p u s h t h e l i n k s i n o l d e s t f i r s t , b u t d i s c o r d . j s r e t u r n s m e s s a g e s n e w e s t f i r s t , s o w e n e e d t o r e v e r s e t h e m
* discord . js returns a map , and maps don ' t have . reverse methods , hence needing to spread the elements into an array first * /
2017-12-08 00:48:49 +02:00
[ ... messages . values ( ) ] . reverse ( ) . forEach ( m => this . cache ( ... GetUrls ( m . content ) ) ) ;
2017-10-02 01:19:10 +03:00
resolve ( ) ;
2017-09-09 22:26:04 +03:00
} )
. catch ( reject ) ;
} ) ;
}
2017-10-02 01:19:10 +03:00
fetchLatest ( guild ) {
2017-12-11 03:05:18 +02:00
const dnsPromise = resolveDns ( Url . parse ( this . url ) . host ) . then ( ( ) => this . _doFetchRSS ( guild ) ) ;
2017-12-09 03:38:28 +02:00
2017-12-11 03:05:18 +02:00
dnsPromise . catch ( err => DiscordUtil . dateDebugError ( "Connection error: Can't resolve host" , err . message || err ) ) ;
2017-12-09 03:38:28 +02:00
return dnsPromise ;
2017-09-09 22:26:04 +03:00
}
toString ( ) {
const blacklist = [ "cachedLinks" , "maxCacheSize" ] ;
2017-10-02 01:19:10 +03:00
return ` \` \` \` JavaScript \n ${ JSON . stringify ( this , ( k , v ) => ! blacklist . find ( x => x === k ) ? v : undefined , "\t" ) } \` \` \` ` ;
}
_doFetchRSS ( guild ) {
2017-12-11 03:05:18 +02:00
const feedPromise = readFeed ( this . url ) . then ( articles => this . _processLatestArticle ( guild , articles ) ) ;
2017-12-09 03:38:28 +02:00
feedPromise . catch ( err => DiscordUtil . dateDebugError ( [ ` Error reading feed ${ this . url } ` , err ] ) ) ;
return feedPromise ;
}
_processLatestArticle ( guild , articles ) {
if ( articles . length === 0 || ! articles [ 0 ] . link )
2017-12-11 03:05:18 +02:00
return false ;
2017-12-09 03:38:28 +02:00
const latest = normaliseUrl ( articles [ 0 ] . link ) ;
2017-12-11 03:05:18 +02:00
if ( this . cachedLinks . indexOf ( latest ) > - 1 )
return false ;
2017-12-09 03:38:28 +02:00
this . cache ( latest ) ;
2017-12-11 03:05:18 +02:00
2017-12-09 03:38:28 +02:00
const channel = guild . channels . get ( this . channelID ) ,
role = guild . roles . get ( this . roleID ) ;
2017-12-11 03:05:18 +02:00
2017-12-09 03:38:28 +02:00
channel . send ( ( role || "" ) + formatPost ( articles [ 0 ] ) )
. catch ( err => DiscordUtil . dateDebugError ( ` Error posting in ${ channel . id } : ${ err . message || err } ` ) ) ;
2017-12-11 03:05:18 +02:00
return true ;
2017-09-09 22:26:04 +03:00
}
} ;
2017-11-11 05:21:05 +02:00
function formatPost ( article ) {
let message = "" ;
2017-12-11 03:05:18 +02:00
if ( article . title ) message += ` \n ** ${ article . title } ** ` ;
if ( article . content ) message += article . content . length > Config . charLimit ? "\nArticle content too long for a single Discord message!" : ` \n ${ article . content } ` ;
if ( article . link ) message += ` \n \n ${ normaliseUrl ( article . link ) } ` ;
2017-11-11 05:21:05 +02:00
return message ;
}
2017-09-09 22:26:04 +03:00
function normaliseUrl ( url ) {
2017-09-13 01:50:03 +03:00
url = url . replace ( "https://" , "http://" ) ; //hacky way to treat http and https the same
2017-09-09 22:26:04 +03:00
2017-09-20 02:43:59 +03:00
const parsedUrl = Url . parse ( url ) ;
2017-12-03 17:53:12 +02:00
if ( parsedUrl . host && parsedUrl . host . includes ( "youtube.com" ) ) {
2017-09-20 02:55:23 +03:00
const videoIDParam = ( parsedUrl . query || "" ) . split ( "&" ) . find ( x => x . startsWith ( "v=" ) ) ;
2017-09-20 02:43:59 +03:00
if ( videoIDParam ) {
const videoID = videoIDParam . substring ( videoIDParam . indexOf ( "=" ) + 1 , videoIDParam . length ) ;
url = "http://youtu.be/" + videoID ;
}
}
2017-09-09 22:26:04 +03:00
return url ;
}