2020-12-21 12:27:31 +01:00
|
|
|
|
module.exports = function(request, fs) {
|
2020-12-13 16:29:27 +01:00
|
|
|
|
const config = require('../config')
|
2020-11-17 21:44:32 +01:00
|
|
|
|
this.downloadFile = (url) => {
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
request(url, { encoding: 'binary' }, (error, response, body) => {
|
|
|
|
|
if(!error && response.statusCode === 200) {
|
|
|
|
|
resolve({ success: true, data: body })
|
|
|
|
|
} else {
|
|
|
|
|
resolve({ success: false, data: null })
|
|
|
|
|
}
|
|
|
|
|
})
|
2020-12-05 21:34:08 +01:00
|
|
|
|
}).catch((err) => {
|
|
|
|
|
console.log(`Downloading media file failed for unknown reason. Details:`, err)
|
2020-11-17 21:44:32 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.writeToDisk = (data, filename) => {
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
fs.writeFile(filename, data, 'binary', (error, result) => {
|
|
|
|
|
if(!error) {
|
|
|
|
|
resolve({ success: true })
|
|
|
|
|
} else {
|
|
|
|
|
resolve({ success: false })
|
|
|
|
|
}
|
|
|
|
|
})
|
2020-12-05 21:34:08 +01:00
|
|
|
|
}).catch((err) => {
|
|
|
|
|
console.log(`Writing media file to disk failed for unknown reason. Details:`, err)
|
2020-11-17 21:44:32 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.logTimestamp = (date) => {
|
|
|
|
|
return date.toGMTString()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.cleanUrl = (url) => {
|
|
|
|
|
return url.replace(/&/g, '&')
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-21 21:22:14 +01:00
|
|
|
|
this.teddifyUrl = (url, user_preferences) => {
|
2020-12-10 19:16:44 +01:00
|
|
|
|
try {
|
|
|
|
|
let u = new URL(url)
|
|
|
|
|
if(u.host === 'www.reddit.com' || u.host === 'reddit.com') {
|
2020-12-10 19:21:07 +01:00
|
|
|
|
url = url.replace(u.host, config.domain)
|
2021-01-04 17:42:22 +01:00
|
|
|
|
if(u.pathname.startsWith('/gallery/'))
|
|
|
|
|
url = url.replace('/gallery/', '/comments/')
|
2020-12-10 19:16:44 +01:00
|
|
|
|
}
|
|
|
|
|
if(u.host === 'i.redd.it' || u.host === 'v.redd.it') {
|
|
|
|
|
let image_exts = ['png', 'jpg', 'jpeg']
|
|
|
|
|
let video_exts = ['mp4', 'gif', 'gifv']
|
|
|
|
|
let file_ext = getFileExtension(url)
|
|
|
|
|
if(image_exts.includes(file_ext))
|
2020-12-10 19:21:07 +01:00
|
|
|
|
url = url.replace(`${u.host}/`, `${config.domain}/pics/w:null_`)
|
2020-12-10 19:16:44 +01:00
|
|
|
|
if(video_exts.includes(file_ext) || !image_exts.includes(file_ext))
|
2020-12-10 19:21:07 +01:00
|
|
|
|
url = url.replace(u.host, `${config.domain}/vids`) + '.mp4'
|
2020-12-10 19:16:44 +01:00
|
|
|
|
}
|
2020-12-21 12:27:31 +01:00
|
|
|
|
|
2020-12-10 19:16:44 +01:00
|
|
|
|
} catch(e) { }
|
2021-03-21 21:20:45 +01:00
|
|
|
|
url = replacePrivacyDomains(url, user_preferences)
|
2020-12-10 19:16:44 +01:00
|
|
|
|
return url
|
2020-11-17 21:44:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.kFormatter = (num) => {
|
|
|
|
|
return Math.abs(num) > 999 ? Math.sign(num)*((Math.abs(num)/1000).toFixed(1)) + 'k' : Math.sign(num)*Math.abs(num)
|
|
|
|
|
}
|
2020-12-21 12:27:31 +01:00
|
|
|
|
|
2021-03-21 22:59:10 +01:00
|
|
|
|
this.timeDifference = (time, hide_suffix) => {
|
2020-11-17 21:44:32 +01:00
|
|
|
|
time = parseInt(time) * 1000
|
|
|
|
|
let ms_per_minute = 60 * 1000
|
|
|
|
|
let ms_per_hour = ms_per_minute * 60
|
|
|
|
|
let ms_per_day = ms_per_hour * 24
|
|
|
|
|
let ms_per_month = ms_per_day * 30
|
|
|
|
|
let ms_per_year = ms_per_day * 365
|
|
|
|
|
let current = + new Date()
|
2021-03-21 22:59:10 +01:00
|
|
|
|
let suffix = 'ago'
|
|
|
|
|
|
|
|
|
|
if(hide_suffix)
|
|
|
|
|
suffix = ''
|
2020-11-17 21:44:32 +01:00
|
|
|
|
|
|
|
|
|
let elapsed = Math.abs(time - current)
|
|
|
|
|
let r = ''
|
|
|
|
|
let e
|
|
|
|
|
|
|
|
|
|
if(elapsed < ms_per_minute) {
|
|
|
|
|
e = Math.round(elapsed/1000)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} seconds ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(e === 1)
|
|
|
|
|
r = 'just now'
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(elapsed < ms_per_hour) {
|
|
|
|
|
e = Math.round(elapsed/ms_per_minute)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} minutes ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(r === 1)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `a minute ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(elapsed < ms_per_day ) {
|
|
|
|
|
e = Math.round(elapsed/ms_per_hour)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} hours ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(e === 1)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `an hour ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(elapsed < ms_per_month) {
|
|
|
|
|
e = Math.round(elapsed/ms_per_day)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} days ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(e === 1)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `1 day ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(elapsed < ms_per_year) {
|
|
|
|
|
e = Math.round(elapsed/ms_per_month)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} months ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(e === 1)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `1 month ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
e = Math.round(elapsed/ms_per_year)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `${e} years ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(e === 1)
|
2021-03-21 22:59:10 +01:00
|
|
|
|
r = `1 year ${suffix}`
|
2020-11-17 21:44:32 +01:00
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-21 12:27:31 +01:00
|
|
|
|
|
2020-11-17 21:44:32 +01:00
|
|
|
|
this.toUTCString = (time) => {
|
2021-01-16 21:14:12 +01:00
|
|
|
|
let d = new Date()
|
|
|
|
|
d.setTime(time*1000)
|
|
|
|
|
return d.toUTCString()
|
2020-11-17 21:44:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-21 21:14:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.unescape = (s, user_preferences) => {
|
2021-01-16 21:14:12 +01:00
|
|
|
|
/* It would make much more sense to rename this function to something
|
|
|
|
|
* like "formatter".
|
|
|
|
|
*/
|
2020-11-17 21:44:32 +01:00
|
|
|
|
if(s) {
|
|
|
|
|
var re = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
|
|
|
|
|
var unescaped = {
|
|
|
|
|
'&': '&',
|
|
|
|
|
'&': '&',
|
|
|
|
|
'<': '<',
|
|
|
|
|
'<': '<',
|
|
|
|
|
'>': '>',
|
|
|
|
|
'>': '>',
|
|
|
|
|
''': "'",
|
|
|
|
|
''': "'",
|
|
|
|
|
'"': '"',
|
|
|
|
|
'"': '"'
|
|
|
|
|
}
|
2021-03-21 21:14:11 +01:00
|
|
|
|
let result = s.replace(re, (m) => {
|
|
|
|
|
return unescaped[m]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
result = replacePrivacyDomains(result, user_preferences)
|
|
|
|
|
|
|
|
|
|
return result
|
2020-11-17 21:44:32 +01:00
|
|
|
|
} else {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-21 21:14:11 +01:00
|
|
|
|
this.replacePrivacyDomains = (str, user_preferences) => {
|
2021-03-31 20:48:59 +02:00
|
|
|
|
if(typeof(str) == 'undefined' || !str)
|
|
|
|
|
return
|
|
|
|
|
|
2021-03-21 21:14:11 +01:00
|
|
|
|
let redditRegex = /([A-z.]+\.)?(reddit(\.com)|redd(\.it))/gm;
|
|
|
|
|
let youtubeRegex = /([A-z.]+\.)?youtu(be\.com|\.be)/gm;
|
|
|
|
|
let twitterRegex = /([A-z.]+\.)?twitter\.com/gm;
|
|
|
|
|
let instagramRegex = /([A-z.]+\.)?instagram.com/gm;
|
|
|
|
|
|
|
|
|
|
str = str.replace(redditRegex, config.domain)
|
|
|
|
|
|
2021-03-21 21:48:28 +01:00
|
|
|
|
if(typeof(user_preferences) == 'undefined')
|
|
|
|
|
return str
|
|
|
|
|
|
2021-03-21 21:14:11 +01:00
|
|
|
|
if(typeof(user_preferences.domain_youtube) != 'undefined')
|
|
|
|
|
if(user_preferences.domain_youtube)
|
|
|
|
|
str = str.replace(youtubeRegex, user_preferences.domain_youtube)
|
|
|
|
|
|
|
|
|
|
if(typeof(user_preferences.domain_twitter) != 'undefined')
|
|
|
|
|
if(user_preferences.domain_twitter)
|
|
|
|
|
str = str.replace(twitterRegex, user_preferences.domain_twitter)
|
|
|
|
|
|
|
|
|
|
if(typeof(user_preferences.domain_instagram) != 'undefined')
|
|
|
|
|
if(user_preferences.domain_instagram)
|
|
|
|
|
str = str.replace(instagramRegex, user_preferences.domain_instagram)
|
|
|
|
|
|
|
|
|
|
return str
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 21:44:32 +01:00
|
|
|
|
this.deleteFiles = (files, callback) => {
|
|
|
|
|
var i = files.length
|
|
|
|
|
files.forEach((filepath) => {
|
|
|
|
|
fs.unlink(filepath, (err) => {
|
|
|
|
|
i--
|
|
|
|
|
if(err) {
|
|
|
|
|
callback(err)
|
|
|
|
|
return
|
|
|
|
|
} else if(i <= 0) {
|
|
|
|
|
callback(null)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
2020-12-21 12:27:31 +01:00
|
|
|
|
|
2020-11-17 21:44:32 +01:00
|
|
|
|
this.isGif = (url) => {
|
2021-03-21 18:32:13 +01:00
|
|
|
|
if(url.startsWith('/r/'))
|
|
|
|
|
return false
|
|
|
|
|
|
2020-11-17 21:44:32 +01:00
|
|
|
|
try {
|
|
|
|
|
url = new URL(url)
|
|
|
|
|
let pathname = url.pathname
|
|
|
|
|
let file_ext = pathname.substring(pathname.lastIndexOf('.') + 1)
|
|
|
|
|
if(file_ext === 'gif' || file_ext === 'gifv') {
|
|
|
|
|
return true
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Invalid url supplied to isGif(). URL: ${url}`, error)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.getFileExtension = (url) => {
|
|
|
|
|
try {
|
|
|
|
|
url = new URL(url)
|
|
|
|
|
let pathname = url.pathname
|
|
|
|
|
let file_ext = pathname.substring(pathname.lastIndexOf('.') + 1)
|
|
|
|
|
if(file_ext) {
|
|
|
|
|
return file_ext
|
|
|
|
|
} else {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Invalid url supplied to getFileExtension(). URL: ${url}`, error)
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-21 12:27:31 +01:00
|
|
|
|
|
2020-12-21 12:52:48 +01:00
|
|
|
|
this.formatLinkFlair = async (post) => {
|
2020-12-21 13:00:31 +01:00
|
|
|
|
if (!config.flairs_enabled) {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 12:33:43 +01:00
|
|
|
|
const wrap = (inner) => `<span class="flair">${inner}</span>`
|
|
|
|
|
|
|
|
|
|
if (post.link_flair_text === null)
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
if (post.link_flair_type === 'text')
|
|
|
|
|
return wrap(post.link_flair_text)
|
|
|
|
|
|
|
|
|
|
if (post.link_flair_type === 'richtext') {
|
|
|
|
|
let flair = ''
|
|
|
|
|
for (let fragment of post.link_flair_richtext) {
|
|
|
|
|
if (fragment.e === 'text')
|
|
|
|
|
flair += fragment.t
|
|
|
|
|
else if (fragment.e === 'emoji')
|
2020-12-21 12:52:48 +01:00
|
|
|
|
flair += `<span class="emoji" style="background-image: url(${await downloadAndSave(fragment.u, 'flair_')})"></span>`
|
2020-12-21 12:33:43 +01:00
|
|
|
|
}
|
|
|
|
|
return wrap(flair)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 12:52:48 +01:00
|
|
|
|
this.formatUserFlair = async (post) => {
|
2020-12-21 13:00:31 +01:00
|
|
|
|
if (!config.flairs_enabled) {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 12:27:31 +01:00
|
|
|
|
// Generate the entire HTML here for consistency in both pug and HTML
|
|
|
|
|
const wrap = (inner) => `<span class="flair">${inner}</span>`
|
|
|
|
|
|
|
|
|
|
if (post.author_flair_text === null)
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
if (post.author_flair_type === 'text')
|
|
|
|
|
return wrap(post.author_flair_text)
|
|
|
|
|
|
|
|
|
|
if (post.author_flair_type === 'richtext') {
|
|
|
|
|
let flair = ''
|
|
|
|
|
for (let fragment of post.author_flair_richtext) {
|
|
|
|
|
// `e` seems to mean `type`
|
|
|
|
|
if (fragment.e === 'text')
|
|
|
|
|
flair += fragment.t // `t` is the text
|
|
|
|
|
else if (fragment.e === 'emoji')
|
2020-12-21 12:52:48 +01:00
|
|
|
|
flair += `<span class="emoji" style="background-image: url(${await downloadAndSave(fragment.u, 'flair_')})"></span>` // `u` is the emoji URL
|
2020-12-21 12:27:31 +01:00
|
|
|
|
}
|
|
|
|
|
return wrap(flair)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 21:44:32 +01:00
|
|
|
|
}
|