media-grab
Version:
Grab random media from the internet
134 lines (133 loc) • 5.22 kB
JavaScript
// Imports
import MagicStrings from './magicStrings.js';
import * as helpers from './helpers.js';
import Reddit from './reddit/index.js';
/**
* The possible states of the tool.
* @private
*/
// Export a class called RedditGrabber
/**
* The super tool class. Holds a state and implements the execute command.
*/
export class RedditGrabber {
/**
* The possible states of the tool.
* @return {{great: string, cool: string, awesome: string, swag: string}}
*/
// The constructor should set the username, password, appId, appSecret, and userAgent
constructor(options) {
// Ensure that options is an object with properties username, password, appId, appSecret else throw an error
if (typeof options === 'object' &&
options?.username &&
options?.password &&
options?.appId &&
options?.appSecret) {
this.username = options.username;
this.password = options.password;
this.appId = options.appId;
this.appSecret = options.appSecret;
this.userAgent = options.userAgent;
}
else {
throw new Error('Invalid options');
}
// Optional
this.userAgent = options?.userAgent;
this.subreddits = options?.subreddits || ['funny'];
this.alwaysTypes = ['hot'];
this.randomTypes = ['top'];
this.times = ['t=year', 't=month'];
this.mediaSources = ['redgifs', 'imgur', 'gfycat', 'v.redd.it', 'i.redd.it'];
this.reddit = new Reddit({
username: this.username,
password: this.password,
appId: this.appId,
appSecret: this.appSecret,
userAgent: this.userAgent || 'Bot',
});
}
// Function that gets username
/**
* Validates a state. To be valid, the value needs to be part of the {tool.states}.
* Throws an Error if invalid. Returns void / undefined if passed.
* @param value The state candidate to be validated.
* @throws if state is not a valid state
*/
getSubreddits() {
return this.subreddits;
}
// Function that sets username
/**
* Validates and sets a new state value if given and returns the updated value. If no defined value is given it just returns the
* current state value.
* @param value {String|undefined} optional state to be set.
* @return {String} the current state value
*/
setSubreddits(subreddits) {
// Make sure subreddits is an array of strings
if (subreddits.every((e) => typeof e === 'string')) {
this.subreddits = subreddits;
}
else {
throw new Error('Subreddits must be an array of strings');
}
}
// Main grabbit function
async grabbit() {
let redditSlugs = [];
for (const subreddit of this.subreddits) {
redditSlugs = redditSlugs.concat(helpers.getRedditSlug(subreddit, this.alwaysTypes[0], this.times[0]) // TODO make types/times configurable in the future
);
}
// Throw an error if there are no reddit slugs
if (redditSlugs.length === 0) {
throw new Error('No reddit slugs due to lack of subreddits');
}
const allDemUrls = await Promise.all(redditSlugs.map(async (url) => {
const res = await this.reddit.get(url);
const after = res.data.after;
const data = res?.data?.children
.map((e) => {
if (e?.data?.media?.reddit_video?.fallback_url) {
return e?.data?.media?.reddit_video?.fallback_url;
}
else {
return e?.data?.url_overridden_by_dest;
}
})
.filter((e) => typeof e === 'string')
.filter((e) => {
return this.mediaSources.some((source) => e.includes(source));
});
return { data, after };
}));
// Filter out any string that doesnt contain any value form an array of strings
const flattened = allDemUrls.map((e) => e.data).flat();
// Get a random one
let theChosenOne = helpers.getRandomIndex(flattened);
// Check if the chosen one is a valid url else get another one (5 tries)
let start = 0;
while (!helpers.isValidURL(theChosenOne) && start < 5) {
theChosenOne = helpers.getRandomIndex(flattened);
start++;
}
// Final check and package response data
const baseRespData = {
url: theChosenOne,
subreddits: this.subreddits,
};
if (helpers.isValidURL(theChosenOne)) {
return helpers.createResponse(true, {
...baseRespData,
message: MagicStrings.SUCCESS_MESSAGE,
});
}
else {
return helpers.createResponse(false, {
...baseRespData,
message: MagicStrings.ERROR_MESSAGE,
});
}
}
}