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.
115 lines
3.7 KiB
115 lines
3.7 KiB
import Work from "./Work.ts";
|
|
import { ID } from "../types.d.ts";
|
|
import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.36-alpha/deno-dom-wasm.ts";
|
|
import { HTMLDocument } from "../types.d.ts";
|
|
import Search, { SearchParameters } from "./Search.ts";
|
|
import {
|
|
CookieJar,
|
|
wrapFetch,
|
|
} from "https://deno.land/x/another_cookiejar@v5.0.1/mod.ts";
|
|
|
|
export default class AO3 {
|
|
session: {
|
|
get: (path: string) => Promise<Response>;
|
|
post: (
|
|
path: string,
|
|
payload: unknown,
|
|
) => Promise<Response>;
|
|
};
|
|
DOMParser = new DOMParser();
|
|
fetch: typeof fetch;
|
|
cookieJar: CookieJar;
|
|
#headers: Record<string, string>;
|
|
|
|
/**
|
|
* a representation of AO3 in class form
|
|
*/
|
|
constructor(opts?: {
|
|
url?: string;
|
|
}) {
|
|
this.cookieJar = new CookieJar();
|
|
this.fetch = wrapFetch({ cookieJar: this.cookieJar });
|
|
this.#headers = {
|
|
"User-Agent":
|
|
"Mozilla/5.0 (Windows NT 10.0; rv:108.0) Gecko/20100101 Firefox/108.0",
|
|
};
|
|
this.session = {
|
|
get: async (path: string) => {
|
|
const res = await this.fetch(
|
|
opts?.url ?? "https://archiveofourown.org/" + path,
|
|
{
|
|
headers: this.#headers,
|
|
},
|
|
);
|
|
if (res.status > 300) {
|
|
console.log(res);
|
|
throw new Error("Failed request, probably rate-limited");
|
|
}
|
|
return res;
|
|
},
|
|
|
|
post: async (
|
|
path: string,
|
|
// deno-lint-ignore no-explicit-any
|
|
payload: any,
|
|
headers?: string,
|
|
) => {
|
|
const res = await this.fetch(
|
|
opts?.url ?? "https://archiveofourown.org/" + path,
|
|
{
|
|
"credentials": "include",
|
|
headers: Object.assign(headers ?? {}, this.#headers),
|
|
method: "POST",
|
|
body: payload,
|
|
},
|
|
);
|
|
if (res.status > 300) {
|
|
console.log(res);
|
|
throw new Error("Failed request, probably rate-limited");
|
|
}
|
|
return res;
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* gets a Work from an ID
|
|
* @returns {Promise<Work>} a Work class for the work
|
|
*/
|
|
async getWork(id: ID): Promise<Work> {
|
|
const res = await this.session.get(
|
|
`/works/${id}?view_adult=true&view_full_work=true`,
|
|
);
|
|
return new Work(id, await res.text(), this.session, this.DOMParser);
|
|
}
|
|
|
|
async authenticate(username_or_email: string, password: string) {
|
|
const loginPage = await this.session.get("/users/login");
|
|
const document = this.DOMParser.parseFromString(
|
|
await loginPage.text(),
|
|
"text/html",
|
|
) as HTMLDocument;
|
|
const authenticity_token = document.querySelector(
|
|
"input[name='authenticity_token']",
|
|
)?.getAttribute("value");
|
|
if (authenticity_token) {
|
|
await this.session.post(
|
|
"/users/login",
|
|
new URLSearchParams({
|
|
"user[login]": username_or_email,
|
|
"user[password]": password,
|
|
authenticity_token,
|
|
utf8: "✓",
|
|
commit: "Log In",
|
|
}),
|
|
);
|
|
} else {
|
|
throw new Error("Failed to get authenticity token");
|
|
}
|
|
}
|
|
|
|
search(opts: SearchParameters) {
|
|
return new Search(opts, this.session, this.DOMParser);
|
|
}
|
|
}
|