UNPKG

@re621/zestyapi

Version:

Comprehensive JS wrapper for e621.net API

220 lines (185 loc) 8.31 kB
import Logger from "./components/Logger"; import RequestQueue from "./components/RequestQueue"; import Util from "./components/Util"; import { PrimitiveType } from "./components/UtilType"; import BlipsEndpoint from "./endpoints/Blips"; import CommentsEndpoint from "./endpoints/Comments"; import Favorites from "./endpoints/Favorites"; import ForumPostsEndpoint from "./endpoints/ForumPosts"; import ForumTopicsEndpoint from "./endpoints/ForumTopics"; import IQDBQueriesEndpoint from "./endpoints/IQDBQueries"; import NotesEndpoint from "./endpoints/Notes"; import PoolsEndpoint from "./endpoints/Pools"; import PostEventsEndpoint from "./endpoints/PostEvents"; import PostsEndpoint from "./endpoints/Posts"; import PostSets from "./endpoints/PostSets"; import PostVotes from "./endpoints/PostVotes"; import TagAliasesEndpoint from "./endpoints/TagAliases"; import TagImplicationsEndpoint from "./endpoints/TagImplications"; import TagsEndpoint from "./endpoints/Tags"; import UserFeedbacksEndpoint from "./endpoints/UserFeedbacks"; import UsersEndpoint from "./endpoints/Users"; import UtilityEndpoint from "./endpoints/Utility"; import WikiPagesEndpoint from "./endpoints/WikiPages"; import InitializationError from "./error/InitializationError"; export default class ZestyAPI { private static instance: ZestyAPI; private userAgent: string; private rateLimit: number; private domain: string; private authToken: AuthToken; private authLogin: AuthLogin; // Endpoint declarations public Blips = new BlipsEndpoint(this); public Comments = new CommentsEndpoint(this); public Favorites = new Favorites(this); public ForumPosts = new ForumPostsEndpoint(this); public ForumTopics = new ForumTopicsEndpoint(this); public IQDBQueries = new IQDBQueriesEndpoint(this); public Notes = new NotesEndpoint(this); public Pools = new PoolsEndpoint(this); public Posts = new PostsEndpoint(this); public PostEvents = new PostEventsEndpoint(this); public PostSets = new PostSets(this); public PostVotes = new PostVotes(this); public Tags = new TagsEndpoint(this); public TagAliases = new TagAliasesEndpoint(this); public TagImplications = new TagImplicationsEndpoint(this); public Users = new UsersEndpoint(this); public UserFeedbacks = new UserFeedbacksEndpoint(this); public Utility = new UtilityEndpoint(this) public WikiPages = new WikiPagesEndpoint(this); private constructor(config: APIConfig = {}) { // User Agent if (!config.userAgent || typeof config.userAgent !== "string" || config.userAgent.length > 250) throw InitializationError.UserAgent(); else this.userAgent = config.userAgent; // Rate Limit if (!config.rateLimit || typeof config.rateLimit !== "number" || config.rateLimit < 500) this.rateLimit = 500; else this.rateLimit = config.rateLimit; // Domain if (!config.domain) config.domain = "https://e621.net"; else if (typeof config.domain !== "string") throw InitializationError.Domain(); try { this.domain = new URL(config.domain).href; } catch { throw InitializationError.Domain(); } // Authentication if (config.authToken) this.login(config.authToken); else if (config.authLogin) this.login(config.authLogin); // Debug if (config.debug) Logger.debug = true; } /** * Get an instance of a E621 object, with access to various endpoints * @param {APIConfig} config Configuration object. Not necessary if `connect()` was called before. * @returns {ZestyAPI} E621 object */ public static connect(config?: APIConfig): ZestyAPI { if (!this.instance) this.instance = new ZestyAPI(config); return this.instance; } public login(auth: AuthToken | AuthLogin): void { this.logout(); if (typeof auth == "string") { if (auth.length > 250) throw InitializationError.Auth(); else this.authToken = auth; } else { if (!auth.username || typeof auth.username !== "string" || auth.username.length > 250 || !auth.apiKey || typeof auth.apiKey !== "string" || auth.apiKey.length > 250) throw InitializationError.Auth(); else this.authLogin = auth; } } public logout(): void { this.authToken = undefined; this.authLogin = undefined; } public getAuthToken(): AuthToken { return this.authToken; } public getAuthLogin(): AuthLogin { return this.authLogin; } public get isAuthSet(): boolean { return typeof this.authToken !== "undefined" || typeof this.authLogin !== "undefined"; } /** * Method used to make requests to E621's API. * It is strongly recommended not to use it directly, and to instead rely on endpoint methods. * @param {string} endpoint Endpoint address * @param {RequestConfig} config Request parameters * @returns {Promise<any>} Promise that is fulfilled when the request receives a response */ public makeRequest(endpoint: string, config?: RequestConfig): Promise<any> { const requestInfo = {}; requestInfo["headers"] = {}; /* Validating the request config */ if (!config) config = {}; // Request method if (!config.method) config.method = "GET"; requestInfo["method"] = config.method; // Query parameters and headers if (!config.query) config.query = {}; if (Util.isBrowser) config.query["_client"] = encodeURIComponent(this.userAgent); else { requestInfo["headers"]["User-Agent"] = this.userAgent; requestInfo["headers"]["X-User-Agent"] = this.userAgent; } // Request body if (!config.body) config.body = {}; if (config.method !== "GET") { if (this.authToken) config.body["authenticity_token"] = encodeURIComponent(this.authToken); // const bodyParams = APIQuery.flatten(config.body); if (Object.keys(config.body).length > 0) { const data = new FormData(); for (const [key, value] of Object.entries(config.body)) data.append(key, value + ""); requestInfo["body"] = data; } } // Timeout if (!config.rateLimit) config.rateLimit = this.rateLimit; else if (config.rateLimit < 500) config.rateLimit = 500; // Authentication if (this.authLogin) { // TODO Check if there is a difference in auth between browser and node requestInfo["headers"]["Authorization"] = `Basic ${Util.btoa(this.authLogin.username + ":" + this.authLogin.apiKey)}`; } /* Compiling the data and adding it to the queue */ let url = this.domain + endpoint; const queryParams = APIQuery.flatten(config.query); if (queryParams.length > 0) url += "?" + queryParams.join("&"); return RequestQueue.add(url, requestInfo, config.rateLimit); } } if (typeof process === "undefined") (window as any).ZestyAPI = ZestyAPI; interface APIConfig { userAgent?: string, rateLimit?: 500 | number, domain?: "https://e621.net" | "https://e926.net" | string, authToken?: AuthToken; authLogin?: AuthLogin debug?: boolean; } type AuthToken = string; interface AuthLogin { username: string; apiKey: string; } interface RequestConfig { method?: "GET" | "POST" | "HEAD" | "PATCH" | "DELETE", query?: APIQuery, body?: APIQuery, rateLimit?: number, } export interface APIQuery { [prop: string]: PrimitiveType; } namespace APIQuery { export function flatten(input: APIQuery): string[] { const result = []; for (const [key, value] of Object.entries(input)) { if (value === null || typeof value == "undefined") continue; result.push(key + "=" + value); // TODO URLEncode??? } return result; } }