2016-12-03 03:04:38 +02:00
//external library imports
2016-11-01 01:28:17 +02:00
var Dns = require ( "dns" ) ; //for connectivity checking
var Url = require ( "url" ) ; //for url parsing
var Uri = require ( "urijs" ) ; //for finding urls within message strings
var Discord = require ( "discord.io" ) ; //for obvious reasons
var FeedRead = require ( "feed-read" ) ; //for rss feed reading
2016-12-03 03:04:38 +02:00
//my imports
var Log = require ( "./log.js" ) ; //some very simple logging functions I made
2016-12-02 02:57:37 +02:00
var BotConfig = require ( "./bot-config.json" ) ; //bot config file containing bot token
2016-11-01 01:28:17 +02:00
var Config = require ( "./config.json" ) ; //config file containing other settings
2016-11-01 19:23:39 +02:00
2016-12-30 16:47:56 +02:00
var DiscordClient = {
2016-12-03 02:56:31 +02:00
bot : null ,
startup : function ( ) {
//check if we can connect to discordapp.com to authenticate the bot
Dns . resolve ( "discordapp.com" , function ( err ) {
2017-01-04 21:53:47 +02:00
if ( err )
Log . error ( "CONNECTION ERROR: Unable to locate discordapp.com to authenticate the bot" , err ) ;
2016-12-03 02:56:31 +02:00
else {
//if there was no error, go ahead and create and authenticate the bot
2016-12-30 16:47:56 +02:00
DiscordClient . bot = new Discord . Client ( {
2016-12-03 02:56:31 +02:00
token : BotConfig . token ,
autorun : true
} ) ;
2016-10-29 20:31:16 +03:00
2016-12-03 02:56:31 +02:00
//set up the bot's event handlers
2016-12-30 16:47:56 +02:00
DiscordClient . bot . on ( "ready" , DiscordClient . onReady ) ;
DiscordClient . bot . on ( "disconnect" , DiscordClient . onDisconnect ) ;
DiscordClient . bot . on ( "message" , DiscordClient . onMessage ) ;
2016-12-03 02:56:31 +02:00
}
} ) ;
} ,
onReady : function ( ) {
2016-12-30 16:51:25 +02:00
Log . info ( "Registered/connected bot " + DiscordClient . bot . username + " - (" + DiscordClient . bot . id + ")" ) ;
2016-12-03 02:56:31 +02:00
2017-01-04 21:53:47 +02:00
DiscordClient . checkPastMessagesForLinks ( ) ; //we need to check past messages for links on startup, but also on reconnect because we don't know what has happened during the downtime
2017-01-08 07:00:07 +02:00
2017-01-08 07:24:48 +02:00
intervalFunc = ( ) => {
2017-01-08 07:41:10 +02:00
Feed . check ( ( err , articles ) => {
2017-01-08 07:24:48 +02:00
Links . validate ( err , articles , DiscordClient . post ) ;
2017-01-08 07:41:10 +02:00
} ) ;
2017-01-08 07:24:48 +02:00
} ;
2016-12-03 02:56:31 +02:00
} ,
onDisconnect : function ( err , code ) {
2017-01-04 21:53:47 +02:00
Log . event ( "Bot was disconnected! " + ( err ? err : "" ) + ( code ? code : "No disconnect code provided." ) + "\nClearing the feed timer and starting reconnect timer" , "Discord.io" ) ;
2016-12-03 02:56:31 +02:00
2017-01-08 07:00:07 +02:00
intervalFunc = DiscordClient . startup ; //reassign the interval function to try restart the bot every 5 sec
2016-12-03 02:56:31 +02:00
} ,
onMessage : function ( user , userID , channelID , message ) {
2016-12-30 16:54:10 +02:00
//check if the message is in the right channel, contains a link, and is not the latest link from the rss feed
2017-01-08 07:27:49 +02:00
if ( channelID === Config . channelID && Links . messageContainsLink ( message ) && ( message !== Links . latestFromFeedlatestFeedLink ) )
2016-12-03 02:56:31 +02:00
Log . event ( "Detected posted link in this message: " + message , "Discord.io" ) ;
2016-12-30 16:54:10 +02:00
2017-01-08 07:24:48 +02:00
//extract the url from the string, and cache it
Uri . withinString ( message , function ( url ) {
Links . cache ( Links . standardise ( url ) ) ;
return url ;
} ) ;
2017-01-08 07:27:49 +02:00
} ,
2016-12-03 02:56:31 +02:00
checkPastMessagesForLinks : function ( ) {
var limit = 100 ;
Log . info ( "Attempting to check past " + limit + " messages for links" ) ;
//get the last however many messsages from our discord channel
2016-12-30 16:47:56 +02:00
DiscordClient . bot . getMessages ( {
2016-12-03 02:56:31 +02:00
channelID : Config . channelID ,
limit : limit
} , function ( err , messages ) {
if ( err ) Log . error ( "Error fetching discord messages." , err ) ;
else {
Log . info ( "Pulled last " + messages . length + " messages, scanning for links" ) ;
2016-12-30 17:37:13 +02:00
var messageContents = messages . map ( ( x ) => { return x . content ; } ) . reverse ( ) ; //extract an array of strings from the array of message objects
2016-12-03 02:56:31 +02:00
for ( var messageIdx in messageContents ) {
var message = messageContents [ messageIdx ] ;
2016-12-30 17:37:13 +02:00
if ( Links . messageContainsLink ( message ) ) //test if the message contains a url
2016-12-03 02:56:31 +02:00
//detect the url inside the string, and cache it
Uri . withinString ( message , function ( url ) {
Links . cache ( url ) ;
return url ;
} ) ;
}
}
} ) ;
2017-01-08 07:24:48 +02:00
} ,
post : function ( link ) {
//send a messsage containing the new feed link to our discord channel
DiscordClient . bot . sendMessage ( {
to : Config . channelID ,
message : link
} , function ( err , message ) {
if ( err ) {
Log . error ( "ERROR: Failed to send message: " + message . substring ( 0 , 15 ) + "..." , err ) ;
//if there is an error posting the message, check if it is because the bot isn't connected
2017-01-08 08:18:05 +02:00
if ( ! DiscordClient . bot . connected ) DiscordClient . onDisconnect ( ) ;
2017-01-08 07:24:48 +02:00
}
} ) ;
2016-12-03 02:56:31 +02:00
}
} ;
2016-11-01 01:28:17 +02:00
2016-12-03 02:23:15 +02:00
var YouTube = {
url : {
share : "http://youtu.be/" ,
full : "http://www.youtube.com/watch?v=" ,
2017-01-08 07:24:48 +02:00
createFullUrl : function ( shareUrl ) {
2016-12-03 03:52:16 +02:00
return shareUrl . replace ( YouTube . url . share , YouTube . url . full ) ;
2016-12-03 02:23:15 +02:00
} ,
2017-01-08 07:24:48 +02:00
createShareUrl : function ( fullUrl ) {
2016-12-03 03:52:16 +02:00
var shareUrl = fullUrl . replace ( YouTube . url . full , YouTube . url . share ) ;
2016-12-02 23:53:13 +02:00
2017-01-08 07:24:48 +02:00
if ( shareUrl . includes ( "&" ) ) shareUrl = shareUrl . slice ( 0 , fullUrl . indexOf ( "&" ) ) ;
2016-12-03 02:23:15 +02:00
return shareUrl ;
}
} ,
} ;
var Links = {
2016-12-30 16:54:10 +02:00
standardise : function ( link ) {
2016-12-30 17:40:03 +02:00
link = link . replace ( "https://" , "http://" ) ; //cheaty way to get around http and https not matching
2017-01-04 21:06:29 +02:00
if ( Config . youtubeMode ) link = link . split ( "&" ) [ 0 ] ; //quick way to chop off stuff like &feature=youtube etc
2016-12-30 17:40:03 +02:00
return link ;
2016-12-30 16:54:10 +02:00
} ,
messageContainsLink : function ( message ) {
var messageLower = message . toLowerCase ( ) ;
2016-12-30 17:24:12 +02:00
return messageLower . includes ( "http://" ) || messageLower . includes ( "https://" ) || messageLower . includes ( "www." ) ;
2016-12-30 16:54:10 +02:00
} ,
2016-12-03 02:23:15 +02:00
cached : [ ] ,
2017-01-08 07:24:48 +02:00
latestFeedLink : "" ,
2016-12-03 02:23:15 +02:00
cache : function ( link ) {
2016-12-30 16:54:10 +02:00
link = Links . standardise ( link ) ;
2016-12-03 03:52:16 +02:00
2017-01-08 07:24:48 +02:00
if ( Config . youtubeMode ) link = YouTube . url . createShareUrl ( link ) ;
2016-12-03 03:52:16 +02:00
2016-12-03 02:23:15 +02:00
//store the new link if not stored already
2017-01-08 07:24:48 +02:00
if ( ! Links . isCached ( link ) ) {
2016-12-03 03:37:02 +02:00
Links . cached . push ( link ) ;
2016-12-03 02:23:15 +02:00
Log . info ( "Cached URL: " + link ) ;
}
2017-01-08 07:24:48 +02:00
2017-01-08 07:27:49 +02:00
if ( Links . cached . length > Config . numLinksToCache ) Links . cached . shift ( ) ; //get rid of the first array element if we have reached our cache limit
2016-12-03 02:23:15 +02:00
} ,
2017-01-08 07:24:48 +02:00
isCached : function ( link ) {
2016-12-30 16:54:10 +02:00
link = Links . standardise ( link ) ;
2017-01-08 07:24:48 +02:00
if ( Config . youtubeMode )
return Links . cached . includes ( YouTube . url . createShareUrl ( link ) ) ;
2016-12-03 03:37:02 +02:00
return Links . cached . includes ( link ) ;
2016-12-03 02:56:31 +02:00
} ,
2017-01-08 07:24:48 +02:00
validate : function ( err , articles , callback ) {
2016-12-03 02:56:31 +02:00
if ( err ) Log . error ( "FEED ERROR: Error reading RSS feed." , err ) ;
else {
2017-01-08 08:18:05 +02:00
var latestLink = Links . standardise ( articles [ 0 ] . link ) ;
if ( Config . youtubeMode ) latestLink = YouTube . createShareUrl ( latestLink ) ;
2016-12-03 02:56:31 +02:00
2017-01-08 07:24:48 +02:00
//make sure we don't spam the latest link
if ( latestLink == Links . latestFeedLink )
return ;
2016-12-02 02:55:39 +02:00
2017-01-08 07:24:48 +02:00
//make sure the latest link hasn't been posted already
if ( Links . isCached ( latestLink ) ) {
Log . info ( "Didn't post new feed link because already detected as posted " + latestLink ) ;
return ;
2016-12-02 02:40:33 +02:00
}
2016-12-02 02:55:39 +02:00
2017-01-08 07:24:48 +02:00
callback ( latestLink ) ;
Links . cache ( latestLink ) ; //make sure the link is cached, so it doesn't get posted again
Links . latestFeedLink = latestLink ; //ensure our latest feed link variable is up to date, so we can track when the feed updates
2016-11-01 01:28:17 +02:00
}
}
2016-12-03 02:56:31 +02:00
} ;
2016-11-05 03:08:17 +02:00
2016-12-03 02:56:31 +02:00
var Feed = {
urlObj : Url . parse ( Config . feedUrl ) ,
2017-01-08 07:24:48 +02:00
check : function ( callback ) {
2016-12-30 17:37:13 +02:00
Dns . resolve ( Feed . urlObj . host , function ( err ) { //check that we have an internet connection (well not exactly - check that we have a connection to the host of the feedUrl)
2016-12-30 16:54:10 +02:00
if ( err ) Log . error ( "CONNECTION ERROR: Cannot resolve host." , err ) ;
2017-01-08 07:41:10 +02:00
else FeedRead ( Config . feedUrl , callback ) ;
2016-12-03 02:56:31 +02:00
} ) ;
}
2016-12-03 03:37:02 +02:00
} ;
2017-01-08 07:24:48 +02:00
var intervalFunc = ( ) => { } ; //do nothing by default
2017-01-08 07:00:07 +02:00
2016-12-03 03:37:02 +02:00
//IIFE to kickstart the bot when the app loads
2016-12-30 16:54:10 +02:00
( function ( ) {
2016-12-30 16:47:56 +02:00
DiscordClient . startup ( ) ;
2017-01-08 07:41:10 +02:00
setInterval ( ( ) => { intervalFunc ( ) ; } , Config . pollingInterval ) ;
2016-12-03 03:37:02 +02:00
} ) ( ) ;