You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
13 KiB

const find = require('findit');
const path = require('path');
const mongoose = require('mongoose');
const crypto = require('crypto');
module.exports.Modules = class Modules
{
/**
*
* @param {String} name Scanlator Name - Same as in the Module.
*/
constructor (name=null)
{
this.name = name;
this.Modules;
}
/**
* @returns {Array<Modules>}
* @typedef {Class} Modules
*/
getAll()
{
let finder = find(path.resolve('Modules'));
finder.on('file', file =>
{
let Module = require(file);
if (Module.constructor)
{
this.Modules.push(Module);
}
});
finder.on('end', () =>
{
return {Modules:this.Modules}
});
}
/**
*
* @returns {Boolean}
*/
async exists()
{
if (!this.name) return false;
return new Promise((resolve, reject) =>
{
const finder = find(path.resolve('Modules'));
finder.on('file', async file =>
{
let Module = require(file);
if (Module.constructor)
{
const test = new Module();
if (this.name == test.scanlator)
{
finder.removeAllListeners();
resolve(true);
}
}
});
finder.on('error', err =>
{
reject(false);
});
finder.on('end', () =>
{
resolve(false);
});
});
}
/**
*
* @param {String} title
* @returns {Boolean}
*/
async titleExists(title)
{
if(!this.name) return false;
const module = require(`./Modules/${this.name.split('-')[0]+'Module'}`);
return await new module().hasTitle(title);
}
}
module.exports.Search = class Search
{
/**
*
* @param {String} searchString - String to search by.
* @param {Scanlator} scanlator - Scanlator Name
* @typedef {String} Scanlator
* @pattern /^[\w-]+-scans$/
*/
constructor(searchString, scanlator=null)
{
this.ss = searchString;
this.scanlator = scanlator;
this.Modules = [];
this.results = [];
this.initialized = new Promise((resolve) =>
{
let finder = find(path.resolve('Modules'));
finder.on('file', file =>
{
let Module = require(file);
if (Module.constructor)
{
this.Modules.push(Module);
}
});
finder.on('end', () =>
{
resolve();
});
})
}
/**
*
* @returns {Array<Results>}
* @typedef {Object} Results
* @property {Array<Manga>} Results
* @property {Scanlator} scanlator
* @typedef {Object} Manga
* @property {String} link - Manga Link
* @property {String} title - Manga Title
* @property {String} img - Image Link
* @typedef {String} Scanlator
* @pattern /^[\w-]+-scans$/
*/
async search()
{
await this.initialized;
if(this.Modules.length==0) return
for(const module of this.Modules)
{
const auxModule = new module()
if(!this.scanlator || this.scanlator == auxModule.scanlator )
try
{
var results = await auxModule.Search(this.ss);
//TODO: Defaults for missing info from the modules;
// console.log('Lib: Search: search method: result:', result);
results.sort((a, b) => {
if (a.title.toUpperCase() < b.title.toUpperCase()) return -1;
else return 1;
});
for(var i = 0; i< results.length; i++)
{
results[i]['Status'] = results[i].status;
}
this.results.push({
Results:results,
scanlator:auxModule.scanlator
});
}
catch (error)
{
console.error('Lib: Module Errored:', auxModule.scanlator, 'Error:', error)
}
}
return this.results
}
}
module.exports.SearchByTag = class SearchByTag
{
/**
*
* @param {String} tag
*/
constructor(tag)
{
this.ss = tag.toLowerCase();
this.Modules = [];
this.results = [];
this.initialized = new Promise((resolve) =>
{
let finder = find(path.resolve('Modules'));
finder.on('file', file =>
{
let Module = require(file);
if (Module.constructor)
{
this.Modules.push(Module);
}
});
finder.on('end', () =>
{
resolve();
});
})
}
/**
* @param {Number} ammount - 5 by default
* @returns {Array<Results>}
* @typedef {Object} Results
* @property {Array<Manga>} Results
* @property {Scanlator} scanlator
* @typedef {Object} Manga
* @property {String} link - Manga Link
* @property {String} title - Manga Title
* @property {String} img - Image Link
* @typedef {String} Scanlator
* @pattern /^[\w-]+-scans$/
*/
async search(ammount=5)
{
await this.initialized;
if(this.Modules.length==0) return
for(const module of this.Modules)
{
const auxModule = new module()
try
{
let results = await auxModule.SearchByTag(this.ss, ammount);
//TODO: Defaults for missing info from the modules;
if(results)
{
results.sort((a, b) =>
{
if (a.title.toUpperCase() < b.title.toUpperCase()) return -1;
else return 1;
});
for(let i = 0; i< results.length; i++)
{
let manga = await auxModule.GetManga(results[i].link, results[i].title);
results[i] = {...manga};
}
this.results.push({
Results:results,
scanlator:auxModule.scanlator
});
}
}
catch (error)
{
console.error('Lib: Module Errored:', auxModule.scanlator, 'Error:', error)
}
}
return this.results
}
}
module.exports.Manga = class Manga
{
/**
*
* @param {Scanlator} scanlator
* @param {Link} link Manga Link
* @param {String} title
* @typedef {String} Link
* @pattern ^(https?:\/\/)?([\w\d.-]+)\.([a-z]{2,})(:[0-9]+)?(\/\S*)?$
* @typedef {String} Scanlator
* @pattern /^[\w-]+-scans$/
*/
constructor(scanlator, link, title)
{
this.scanlator = scanlator;
this.link = link;
this.title = title;
this.Engine;
this.initialized = new Promise((resolve) =>
{
let finder = find(path.resolve('Modules'));
finder.on('file', file =>
{
let Module = require(file);
if (Module.constructor)
{
if(this.scanlator == new Module().scanlator)
{
this.Engine = Module;
resolve();
}
}
});
})
}
/**
*
* @returns {Manga}
* @typedef {Object} Manga
* @property {String} link - Manga Link
* @property {String} title - Manga Title
* @property {String} img - Image Link
* @property {Array<String>} tags - Manga Tags
* @property {Array<Chapter>} List - Chapter List
* @typedef {Object} Chapter
* @property {Number} num - Chapter Number
* @property {Link} link - Chapter Link
* @property {Date_} date - Chapter uploaded date
* @typedef {String} Link
* @typedef {String} Date_
*/
async get()
{
await this.initialized;
try {
const Scanlator = new this.Engine();
const Manga = await Scanlator.GetManga(this.link, this.title);
const List = await Scanlator.ChapterList(this.link, this.title);
const Status = Manga.Status?Manga.Status:Manga.status;
//TODO: Defaults for missing info from the modules;
return {...Manga, Status, link:this.link, scanlator:this.scanlator, List, title:this.title}
} catch (error) {
console.error('Lib: Manga: Error:' ,error)
}
}
}
module.exports.Chapter = class Chapter
{
/**
*
* @param {Scanlator} scanlator - Scanlator Name
* @param {Link} Link - Chapter Link
* @param {String} title - Manga Title
* @param {Number} chNum - Chapter Number
* @typedef {String} Link
* @pattern ^(https?:\/\/)?([\w\d.-]+)\.([a-z]{2,})(:[0-9]+)?(\/\S*)?$
* @typedef {String} Scanlator
* @pattern /^[\w-]+-scans$/
*/
constructor(scanlator, Link,title, chNum)
{
this.scanlator = scanlator;
this.title = title;
this.Link = Link;
this.chNum = chNum;
this.Engine;
this.initialized = new Promise((resolve) =>
{
let finder = find(path.resolve('Modules'));
finder.on('file', file =>
{
let Module = require(file);
if (Module.constructor)
{
if(this.scanlator == new Module().scanlator)
{
this.Engine = Module;
resolve();
}
}
});
})
}
/**
*
* @returns {Chapter}
* @typedef {Object} Chapter
* @property {Manga} Manga - Manga Link
* @property {Array<Img>} List - Chapter List of images
* @property {Number} chNum - Chapter number
* @typedef {Object} Manga
* @property {String} link - Manga Link
* @property {String} title - Manga Title
* @property {String} img - Image Link
* @typedef {String} Link
* @typedef {Link} Img
*/
async get()
{
await this.initialized;
try
{
const Scanlator = new this.Engine();
const {List, mangaLink} = await Scanlator.Chapter(this.Link);
const {latestChap } = await Scanlator.GetManga(mangaLink);
//TODO: Defaults for missing info from the modules;
return {List, latestChap,mangaLink, chNum:parseInt(this.chNum), link:this.Link,title:this.title, scanlator:this.scanlator}
}
catch (error)
{
console.error('Lib: Chapter: Error:', error)
}
}
async getMangaLink()
{
await this.initialized;
try
{
const Scanlator = new this.Engine();
const {mangaLink} = await Scanlator.Chapter(this.Link);
return mangaLink;
}
catch (error)
{
console.error('Lib: Chapter: Error:', error);
}
}
};
exports.decodeHTML = (encodedString)=>
{
return encodedString.replace(/&nbsp;|&#(\d+);|\n/g, function(match, dec)
{
if (dec)
{
return String.fromCharCode(dec); // Replace HTML character codes
}
else
{
return ''; // Remove newline characters
}
});
}
exports.isValidID = (id) =>
{
return mongoose.Types.ObjectId.isValid(id);
}
/**
*
* @param {String} cookie
* @param {String} name
* @returns
*/
exports.cookieParser = (cookie, name)=>
{
if(!cookie.includes(name)) return null
return cookie.split(name+'=')[1].split(';')[0];
}
/**
* @argument {Response} res - Response: express response object.
* @argument {String} name - Name of the cookie.
* @argument {*} value - Value of the cookie.
*/
exports.Baker = async (res, name, value, maxAge =2) =>
{
await res.cookie(name, '',{maxAge:0});
await res.cookie(name, value, {maxAge:hours(2),path:'/',secure:false,httpOnly:false});
return;
}
/**
* Use it to transform an hour value into miliseconds.
* @param {Number} Hours - Hours to transform
*/
function hours(Hours)
{
return Hours*60*60*1000;
}
exports.Crypto = class Crypto
{
/**
*
* @param {String} username
* @param {String} password
*/
constructor(username, password)
{
this.salt = '4$3H6Q9B8d4VXcE3Rft4';
this.secret = '7xJYrX@KF7oSNr9Zm4UH';
this.pepper = String.fromCharCode(this.getRandomInt(65, 90));
this.password = password;
this.user = username;
this.algorithm = 'aes-192-cbc';
}
get Hash() // to use on register
{
return this.hash()
}
hash(p)
{
if(!p)
{
return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+this.pepper).digest('hex');
}
else
{
return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+p).digest('hex');
}
}
getRandomInt(min, max)
{
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
}