trev-reborn
Version:
Getting random text, nsfw images, hentai images and memes from reddit. Fast and tons of features supported.
290 lines (254 loc) • 8.15 kB
JavaScript
/* eslint-disable no-control-regex */
const fetch = require('node-fetch');
const subreddits = require('./subreddits.json');
const fs = require('fs');
const utf8 = require('utf8');
let paths;
let varstore;
let tableplace;
function isSafeCatName(str) {
for (const c of str) {
try {
c.match(/[A-Za-z0-9]/);
const thing = c.match(/[A-Za-z0-9]/);
if (!thing || thing === null) {
return false;
}
} catch {
return false;
}
}
return true;
}
function isSafePath(path) {
const p = path.split('.');
for (const pp of p) {
if (!isSafeCatName(pp)) return false;
}
return true;
}
function redo(str) {
return str.slice(1, str.length - 1);
}
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function choice(arr) {
return arr[randInt(0, arr.length - 1)];
}
function getValueByPath(obj, t) {
const pathSegments = t.split('.');
for (const path of pathSegments) {
obj = obj[path];
}
return obj;
}
function generatePathString(path) {
const pathSegments = path.split('.');
let result = '.';
for (const segment of pathSegments) {
result += segment + '.';
}
return result.slice(0, result.length - 1);
}
function generateObjectPaths(obj, path = '.') {
for (const key in obj) {
if (obj[key] && typeof obj[key] === 'object') {
if (path !== '' && !paths.includes(path)) {
if (path !== '.') {
paths.push(path);
}
}
generateObjectPaths(obj[key], path + key + '.');
}
}
}
class Trev {
constructor() {
this.verbose = false;
this.loadTrev();
}
loadTrev() {
paths = [];
varstore = [];
tableplace = 0;
this.subreddits = subreddits;
generateObjectPaths(this.subreddits);
for (let i = 0; i < paths.length; i++) {
paths[i] = redo(paths[i]);
}
if (this.verbose) console.log('[+] Loading/creating functions | trev');
for (const path of paths) {
const part = getValueByPath(this.subreddits, path);
for (const key of Object.keys(part)) {
const curp = path + '.' + key;
if (Array.isArray(getValueByPath(this.subreddits, curp))) {
if (!isSafePath(curp)) {
throw new Error(
'UnsafeCategoryName: One of the category names in the subreddits.json file was seen as unsafe.\nMake sure you use a trusted trevlist or the default one to avoid this error.\nRules for making a category name safe: only letters (caps or no caps) and numbers',
);
}
varstore[tableplace] = getValueByPath(this.subreddits, curp);
eval(`
this.subreddits${generatePathString(curp)} = async () => {
let subreddit = choice(varstore[${tableplace}]);
let result = await this.getCustomSubreddit(subreddit);
return result;
}
`);
tableplace++;
}
}
}
for (const key of Object.keys(this.subreddits)) {
this[key] = this.subreddits[key];
}
this.subreddits = subreddits;
if (this.verbose) console.log('[+] Fully loaded | trev');
}
changeTrevList(link) {
if (link === 'default')
link =
'https://gist.githubusercontent.com/TheMonDon/ff85efc32f06475d4483513f5ec7cfbc/raw/ec5bf4833f8115b1349cde2cea6304ef1c03240a/subreddits.json';
fetch(link)
.then((result) => result.json())
.then((trevList) => {
// save to file
// recall the constructor
// btw this is sync, not async
// the reason why its not async is that i want it to be blocking
fs.writeFileSync('subreddits.json', JSON.stringify(trevList, null, 2));
this.loadTrev();
});
}
async getSubreddit(subreddit) {
const link = `https://www.reddit.com/r/${subreddit}/new.json?limit=100`;
let result;
try {
result = await fetch(utf8.encode(link));
return result.json();
} catch (err) {
if (this.verbose) console.log('[-] Broken subreddit: ', err);
return undefined;
}
}
formatRedditRes(subredditData) {
if (subredditData === undefined) return undefined;
const data = subredditData[0].data.children[0].data;
if (data.url_overridden_by_dest) {
if (this.isImgurUpload(data.url_overridden_by_dest)) {
data.url_overridden_by_dest = this.getRawImgur(data.url_overridden_by_dest);
} else if (this.isRedGifsLink(data.url_overridden_by_dest)) {
data.url_overridden_by_dest = this.redGifsIframe(data.url_overridden_by_dest);
}
}
const media = this.getRawReddit(data);
const newData = {
title: data.title,
author: data.author,
subreddit: data.subreddit_name_prefixed,
permalink: 'https://www.reddit.com' + data.permalink,
media,
text: data.selftext.replace(/([^\x00-\x7F]|&#[0-9]+;)/g, ''),
over_18: data.over_18,
};
return newData;
}
isRedditGallery(url) {
const regex = /^https?:\/\/(?:www\.)?reddit\.com\/gallery\//;
return regex.test(url);
}
getRawReddit(data) {
let url = data.url_overridden_by_dest;
if (url) {
if (this.isRedditGallery(url)) {
url = Object.values(data.media_metadata)[0].s.u;
}
url = url.replace(/preview/, 'i').replace(/\?.+$/, '');
}
return url;
}
isImgurGallery(url) {
const regex = /^https?:\/\/(?:www\.)?imgur\.com\/gallery\//;
return regex.test(url);
}
isImgurMultiUpload(url) {
const regex = /^https?:\/\/(?:www\.)?imgur\.com\/a\//;
return regex.test(url);
}
isImgurUpload(url) {
return (
(url.startsWith('https://imgur.com/') || url.startsWith('https://www.imgur.com/')) &&
!this.isImgurGallery(url) &&
!this.isImgurMultiUpload(url)
);
}
getRawImgur(url) {
if (!this.isImgurUpload(url)) return undefined;
return 'https://i.' + url.slice('https://'.length) + '.jpeg';
}
async getCustomSubreddit(subreddit) {
subreddit = subreddit.replace(/^(\/)?(r\/)?/i, '');
let subredditData = await this.getSubreddit(subreddit);
let attempts = 0;
// Retry fetching if no valid data is returned
while (
(!subredditData || !subredditData.data || !subredditData.data.children.length) &&
attempts < 5
) {
subredditData = await this.getSubreddit(subreddit);
attempts++;
}
if (attempts >= 5) {
if (this.verbose) console.log('[-] Broken subreddit: ' + subreddit);
return undefined;
}
// Select a random post from the fetched posts
const posts = subredditData.data.children;
const randomIndex = Math.floor(Math.random() * posts.length);
const randomPost = posts[randomIndex]?.data;
return randomPost
? this.formatRedditRes([{ data: { children: [{ data: randomPost }] } }])
: undefined;
}
isRedGifsLink(url) {
if (!url) return false;
const urls = [
'https://www.redgifs.com',
'https://www.gfycat.com',
'https://redgifs.com',
'https://gfycat.com',
'http://v3.redgifs.com',
];
for (let i = 0; i < urls.length; i++) {
if (url.startsWith(urls[i])) return true;
}
return false;
}
redGifsIframe(url) {
const urls = [
'https://www.redgifs.com',
'https://www.gfycat.com',
'https://redgifs.com',
'https://gfycat.com',
'http://v3.redgifs.com',
];
let urlStart;
let name;
for (let i = 0; i < urls.length; i++) {
if (url.startsWith(urls[i])) {
urlStart = urls[i];
break;
}
}
if (urlStart.includes('redgifs')) {
// redgifs domain, +6 for /watch
name = url.slice(urlStart.length + 6);
} else {
// gfycat domain, leave normal
name = url.slice(urlStart.length);
}
return urlStart + '/ifr' + name;
}
}
module.exports = new Trev();