ADD ACCOUNT LOGIN!!!!!!!!!
ci/woodpecker/push/woodpecker Pipeline failed Details

note: literally no error handling, slightly jank, BUT IT WORKS!!!

right now this mostly just allows us to access restricted works, but in the future this lets us do WAY more things that normally require an account (reading/modifying bookmarks, maybe even posting works?)
master
Drake 1 year ago
parent c8e8bb19af
commit 37f137881d

@ -0,0 +1,19 @@
import AO3 from "./src/classes/AO3.ts";
const ao3 = new AO3();
await ao3.authenticate(
Deno.env.get("AO3USERNAME") as string,
Deno.env.get("AO3PASSWORD") as string,
);
const res = await ao3.search({
fandoms: ["Murder Drones (Web Series)"],
limit: 10,
});
await res.update(1);
res.results.forEach(
(v) => console.log(v.name),
);

@ -41,6 +41,10 @@
"https://deno.land/std@0.97.0/path/posix.ts": "f56c3c99feb47f30a40ce9d252ef6f00296fa7c0fcb6dd81211bdb3b8b99ca3b",
"https://deno.land/std@0.97.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.97.0/path/win32.ts": "77f7b3604e0de40f3a7c698e8a79e7f601dc187035a1c21cb1e596666ce112f8",
"https://deno.land/x/another_cookiejar@v5.0.1/cookie.ts": "2be7548d01a3a9df97deb187761a843a77fd824057478919abf1e1e89ae1eb2e",
"https://deno.land/x/another_cookiejar@v5.0.1/cookie_jar.ts": "e47d7b2c608bcd9600fd26825b600946f16ae167216cea71935049188d2fc6d1",
"https://deno.land/x/another_cookiejar@v5.0.1/fetch_wrapper.ts": "4fbca1e77383cf7da4703798e06a1129b21120f4d01c3a4c0801674dd7b6d53b",
"https://deno.land/x/another_cookiejar@v5.0.1/mod.ts": "eff949014965771f2cd447fe78625a1ad28b59333afa40640f02c0922534d89a",
"https://deno.land/x/axiod@0.26.2/helpers.ts": "467f2ca75608f368c8092e800eab0d6d79b3fa42346372be62a5b93acf50fe7e",
"https://deno.land/x/axiod@0.26.2/interfaces.ts": "1b5a7b70b1e7faecd2f251ad080bd87188fba585e2de667af472c4d72abf636e",
"https://deno.land/x/axiod@0.26.2/mod.ts": "1175ec90a040d764b9940753f8d8e3f37a2328a0536eed48e411a8f1a3e9e5cb",
@ -79,5 +83,57 @@
"https://denopkg.dev/gh/Ruthenic/deno-dom@master/src/dom/utils-types.ts": "96db30e3e4a75b194201bb9fa30988215da7f91b380fca6a5143e51ece2a8436",
"https://denopkg.dev/gh/Ruthenic/deno-dom@master/src/dom/utils.ts": "ecd889ba74f3ce282620d8ca1d4d5e0365e6cc86101d2352f3bbf936ae496e2c",
"https://denopkg.dev/gh/Ruthenic/deno-dom@master/src/parser.ts": "b65eb7e673fa7ca611de871de109655f0aa9fa35ddc1de73df1a5fc2baafc332"
},
"npm": {
"specifiers": { "fetch-cookie": "fetch-cookie@2.1.0" },
"packages": {
"fetch-cookie@2.1.0": {
"integrity": "sha512-39+cZRbWfbibmj22R2Jy6dmTbAWC+oqun1f1FzQaNurkPDUP4C38jpeZbiXCR88RKRVDp8UcDrbFXkNhN+NjYg==",
"dependencies": {
"set-cookie-parser": "set-cookie-parser@2.5.1",
"tough-cookie": "tough-cookie@4.1.2"
}
},
"psl@1.9.0": {
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dependencies": {}
},
"punycode@2.1.1": {
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dependencies": {}
},
"querystringify@2.2.0": {
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"dependencies": {}
},
"requires-port@1.0.0": {
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dependencies": {}
},
"set-cookie-parser@2.5.1": {
"integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==",
"dependencies": {}
},
"tough-cookie@4.1.2": {
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
"dependencies": {
"psl": "psl@1.9.0",
"punycode": "punycode@2.1.1",
"universalify": "universalify@0.2.0",
"url-parse": "url-parse@1.5.10"
}
},
"universalify@0.2.0": {
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dependencies": {}
},
"url-parse@1.5.10": {
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "querystringify@2.2.0",
"requires-port": "requires-port@1.0.0"
}
}
}
}
}

@ -3,13 +3,25 @@ import { ID } from "../types.d.ts";
import {
DOMParser,
} from "https://denopkg.dev/gh/Ruthenic/deno-dom@master/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: Record<string, any>,
) => Promise<Response>;
};
DOMParser = new DOMParser();
fetch: typeof fetch;
cookieJar: CookieJar;
#headers: Record<string, any>;
/**
* a representation of AO3 in class form
@ -17,15 +29,40 @@ export default class AO3 {
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 fetch(
const res = await this.fetch(
opts?.url ?? "https://archiveofourown.org/" + path,
{
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; rv:106.0) Gecko/20100101 Firefox/106.0",
},
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) {
@ -48,6 +85,31 @@ export default class AO3 {
return new Work(id, await res.text(), this.session, new 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, new DOMParser());
}

Loading…
Cancel
Save