@re621/zestyapi
Version:
Comprehensive JS wrapper for e621.net API
209 lines (176 loc) • 8.03 kB
text/typescript
import Endpoint, { QueryParams } from "../components/Endpoint";
import { FormattedResponse, QueueResponse } from "../components/RequestQueue";
import Util from "../components/Util";
import { PrimitiveMap, SimpleMap } from "../components/UtilType";
import { MalformedRequestError } from "../error/RequestError";
import { ResponseCode, ResponseStatusMessage } from "../error/ResponseCode";
import APIPost from "../responses/APIPost";
export default class PostsEndpoint extends Endpoint<APIPost> {
/*
Endpoint Notes
- No search parameters, everything is in query parameters
- Output is wrapped in `{ posts: [] }` when searching for multiple posts (ex. `/posts.json`)
- Output is wrapped in `{ post: {} }` when searching for one post (ex. `/posts/12345.json`)
- Returns an empty object `{ posts: [] }` when no results are found in a search
*/
/**
* Search for posts with specified tags.
* Note that the hard limit for this request is 40 tags.
* Page number and post limit can be specified as parameters.
* @param {PostSearchParams} params Search parameters
* @returns {FormattedResponse<APIPost[]>} Post data
*/
public async find(query: PostQueryParams = {}): Promise<FormattedResponse<APIPost>> {
let lookup: PrimitiveMap;
try { lookup = this.validateParams({}, query); }
catch (e) { return Endpoint.makeMalformedRequestResponse(); }
return this.api.makeRequest("posts.json", { query: Endpoint.flattenParams(lookup, "+") })
.then(
(response: QueueResponse) => {
if (!response.data.posts || response.data.posts.length == 0) {
response.status.code = ResponseCode.NotFound;
response.status.message = ResponseStatusMessage.NotFound;
response.data = [];
} else response.data = response.data.posts;
return Endpoint.formatAPIResponse(response.status, response.data);
},
(error: QueueResponse) => {
return Endpoint.formatAPIResponse(error.status, []);
}
);
}
/**
* Fetch post data for a specific post
* @param {number} id ID of the post to return
* @returns {FormattedResponse<APIPost>} Post data
*/
public async get(id: number): Promise<FormattedResponse<APIPost>> {
if (typeof id !== "number")
return Endpoint.makeMalformedRequestResponse();
return this.api.makeRequest(`posts/${id}.json`)
.then(
(response: QueueResponse) => {
if (!response.data.post) {
response.status.code = ResponseCode.NotFound;
response.status.message = ResponseStatusMessage.NotFound;
response.data = [];
} else response.data = [response.data.post];
return Endpoint.formatAPIResponse(response.status, response.data);
},
(error: QueueResponse) => {
return Endpoint.formatAPIResponse(error.status, []);
}
);
}
/**
* Fetches data for multiple posts by their IDs.
* Note that up to 100 IDs are accepted at a time. Everything past that will be discarded.
* @param ids List of post IDs
* @returns {FormattedResponse<APIPost[]>} Post data
*/
public async getMany(ids: number[]): Promise<FormattedResponse<APIPost>> {
if (!Array.isArray(ids))
return Endpoint.makeMalformedRequestResponse();
return this.find({ tags: "id:" + ids.join(",") });
}
/**
* Fetches data for a random post
* @returns {FormattedResponse<APIPost>}Post data
*/
public async random(): Promise<FormattedResponse<APIPost>> {
return this.api.makeRequest(`posts/random.json`)
.then(
(response: QueueResponse) => {
if (!response.data.post) {
response.status.code = ResponseCode.NotFound;
response.status.message = ResponseStatusMessage.NotFound;
response.data = [];
} else response.data = [response.data.post];
return Endpoint.formatAPIResponse(response.status, response.data);
},
(error: QueueResponse) => Endpoint.formatAPIResponse(error.status, [])
)
}
/**
* Search for posts with specified tags.
* Note that the hard limit for this request is 39 tags.
* Page number and post limit can be specified as parameters.
* @param {PostQueryParams} query Search parameters
* @param {string} seed Random seed. Optional.
* @returns {FormattedResponse<APIPost[]>} Post data
*/
public async randomMany(query: PostQueryParams = {}, seed?: string): Promise<FormattedResponse<APIPost>> {
if (query.tags) {
if (!Array.isArray(query.tags)) query.tags = query.tags.trim().split(" ").filter(n => n);
} else query.tags = [];
query.tags.push("order:random");
if (seed) query.tags.push("randseed:" + seed);
return this.find(query);
}
public async update(id: number, data: PostUpdateParams): Promise<FormattedResponse<APIPost>> {
if (!this.api.isAuthSet) {
return Endpoint.formatAPIResponse(
{
code: ResponseCode.Unauthorized,
message: ResponseStatusMessage.Unauthorized,
url: null,
},
[]
)
}
return this.api.makeRequest(`posts/${id}.json`, {
method: "PATCH",
body: Util.Type.prefix("post", data),
}).then(
(response: QueueResponse) => {
if (!response.data.post) {
response.status.code = ResponseCode.NotFound;
response.status.message = ResponseStatusMessage.NotFound;
response.data = [];
} else response.data = [response.data.post];
return Endpoint.formatAPIResponse(response.status, response.data);
},
(error: QueueResponse) => {
return Endpoint.formatAPIResponse(error.status, []);
}
);
}
protected validateQueryParams(params: PostQueryParams = {}): PostQueryParams {
const result = super.validateQueryParams(params) as PostQueryParams;
if (!params.tags) result.tags = [];
else if (typeof params.tags !== "object") result.tags = (params.tags + "").trim().split(" ").filter(n => n);
else if (Array.isArray(params.tags)) result.tags = params.tags;
else throw MalformedRequestError.Params();
if (result.tags.length > 40) throw MalformedRequestError.TooMany("tags");
return result as PostQueryParams;
}
}
interface PostQueryParams extends QueryParams {
tags?: string | string[]
}
interface PostUpdateParams extends SimpleMap {
tag_string?: string,
old_tag_string?: string,
tag_string_diff?: string,
rating?: "e" | "q" | "s",
old_rating?: "e" | "q" | "s",
parent_id?: number,
old_parent_id?: number,
source?: string,
old_source?: string,
source_diff?: string,
description?: string,
old_description?: string,
edit_reason?: string,
// Privileged+
is_rating_locked?: boolean,
// Janitor+
is_note_locked?: boolean,
bg_color?: string,
// Admin
is_status_locked?: boolean,
is_comment_disabled?: boolean,
locked_tags?: string,
hide_from_anonymous?: boolean,
hide_from_search_engines?: boolean,
}