Add `User` class + other things I forgor
ci/woodpecker/push/woodpecker Pipeline was successful Details

pull/1/head
Drake 1 year ago
parent a104fb0775
commit 1210e75ca1

@ -1,5 +1,9 @@
{
"deno.enable": true,
"deno.unstable": true,
"editor.formatOnSave": true
"editor.formatOnSave": true,
"[typescript]": {
"editor.tabSize": 4,
"editor.defaultFormatter": "denoland.vscode-deno"
}
}

@ -1,10 +1,14 @@
# wattpad-deno
early library for accessing Wattpad using Deno
## Q&A
### Why?
Why not
### Docs?
The API *should* be pretty self-explanitory, if anything through deno.land's built in docs generator
The API _should_ be pretty self-explanitory, if anything through deno.land's
built in docs generator

@ -4,5 +4,6 @@ export default Wattpad;
import Chapter from "./src/classes/Chapter.ts";
import Search from "./src/classes/Search.ts";
import Story from "./src/classes/Story.ts";
import User from "./src/classes/User.ts";
const VERSION = "v0.1.1";
export { Chapter, Search, Story, VERSION, Wattpad };
export { Chapter, Search, Story, User, VERSION, Wattpad };

@ -1,4 +1,5 @@
import Chapter from "./Chapter.ts";
import User from "./User.ts";
import { Session, StoryJSON } from "../types.d.ts";
export default class Story {
@ -69,4 +70,8 @@ export default class Story {
new Chapter(this.storyJSON.id, part, this.#session)
);
}
getAuthor() {
return new User(this.#session, this.storyJSON.user.name);
}
}

@ -0,0 +1,61 @@
import Story from "./Story.ts";
import { SearchResults, Session, UserJSON } from "../types.d.ts";
export default class User {
#session: Session;
#searchLimit: number;
username: string;
displayName?: string;
description?: string;
createDate?: Date;
userJSON!: UserJSON;
stories: Story[] = [];
constructor(session: Session, username: string, limit: number = 20) {
this.#session = session;
this.username = username;
this.#searchLimit = limit;
}
async init() {
this.userJSON = await (await this.#session.get(
`/api/v3/users/${this.username}`,
false,
{
params: new URLSearchParams({
fields:
"username,description,avatar,name,email,genderCode,language,birthdate,verified,isPrivate,ambassador,is_staff,follower,following,backgroundUrl,votesReceived,numFollowing,numFollowers,createDate,followerRequest,website,facebook,twitter,followingRequest,numStoriesPublished,numLists,location,externalId,programs,showSocialNetwork,verified_email,has_accepted_latest_tos,email_reverification_status,highlight_colour,safety(isMuted,isBlocked),has_writer_subscription",
}),
},
))
.json();
this.displayName = this.userJSON.name;
this.description = this.userJSON.description;
this.createDate = new Date(this.userJSON.createDate);
}
async updateStories(pageNum: number) {
const res: SearchResults = await (await this.#session.get(
`/v4/users/${this.username}/stories/published`,
false,
{
params: new URLSearchParams({
fields: "stories(id)",
limit: this.#searchLimit.toString(),
offset: (pageNum * this.#searchLimit).toString(),
mature: "1",
}),
},
)).json();
for (let i = 0; i < res.stories.length; i++) {
const story = new Story(
res.stories[i].id,
this.#session,
);
this.stories.push(story);
}
}
}

@ -1,5 +1,6 @@
import Story from "./Story.ts";
import Search, { SearchParameters as QuerySearchParams } from "./Search.ts";
import User from "./User.ts";
import { newSession, Options } from "../utils/http.ts";
import { Session } from "../types.d.ts";
@ -45,6 +46,14 @@ export default class Wattpad {
return new Story(id, this.session);
}
/**
* gets a User from a username
* @returns {Promise<User>} a User class for the user
*/
getUser(username: string): User {
return new User(this.session, username);
}
search(opts: QuerySearchParams) {
return new Search(opts, this.session);
}

45
src/types.d.ts vendored

@ -28,7 +28,6 @@ export interface StoryJSON {
tagRankings: TagRanking[];
highlight_colour: string;
promoted: boolean;
sponsor: any[];
isAdExempt: boolean;
story_text_url: TextURL;
isPaywalled: boolean;
@ -110,3 +109,47 @@ export interface SearchTag {
id: string;
name: string;
}
export interface UserJSON {
username: string;
avatar: string;
isPrivate: boolean;
backgroundUrl: string;
follower: boolean;
following: boolean;
followerRequest: string;
followingRequest: string;
safety: Safety;
name: string;
description: string;
genderCode: string;
language: number;
createDate: string;
location: string;
verified: boolean;
ambassador: boolean;
facebook: string;
twitter: string;
website: string;
votesReceived: number;
numStoriesPublished: number;
numFollowing: number;
numFollowers: number;
numLists: number;
verified_email: boolean;
is_staff: boolean;
highlight_colour: string;
programs: Programs;
externalId: string;
showSocialNetwork: boolean;
}
export interface Programs {
wattpad_stars: boolean;
wattpad_circle: boolean;
}
export interface Safety {
isMuted: boolean;
isBlocked: boolean;
}

@ -1,10 +1,12 @@
import Wattpad from "../src/classes/Wattpad.ts";
import workTest from "./story.ts";
import storyTest from "./story.ts";
import chaptersTest from "./chapter.ts";
import searchTest from "./search.ts";
import userTest from "./user.ts";
const wattpad = new Wattpad();
workTest(wattpad);
storyTest(wattpad);
chaptersTest(wattpad);
searchTest(wattpad);
userTest(wattpad);

@ -26,11 +26,11 @@ export default function test(watt: Wattpad) {
await search.update(0);
assert(search.results.length == 30, "not enough search results");
assert(search.results.length === 30, "not enough search results");
await search.update(1);
assert(
search.results.length == 30,
search.results.length === 30,
"could not find second page of results",
);
});
@ -59,11 +59,11 @@ export default function test(watt: Wattpad) {
});
await search.update(0);
assert(search.results.length == 20, "not enough search results");
assert(search.results.length === 20, "not enough search results");
await search.update(1);
assert(
search.results.length == 20,
search.results.length === 20,
"could not find second page of results",
);
});

@ -4,24 +4,33 @@ import { assert } from "https://deno.land/std@0.167.0/testing/asserts.ts";
export default function test(watt: Wattpad) {
Deno.test("stories", async (test) => {
let work: Story;
let story: Story;
await test.step("initialization", async () => {
work = watt.getStory("327425279");
await work.init();
story = watt.getStory("327425279");
await story.init();
});
await test.step("tags", () => {
assert(work.tags.length > 0, "Failed to parse any tags");
assert(story.tags.length > 0, "Failed to parse any tags");
assert(
work.tags.includes("bendyxreader"),
story.tags.includes("bendyxreader"),
"Failed to get correct tags",
);
});
await test.step("other metadata", () => {
assert(
work.name === "Inky Desires [Bendy X Reader]",
"incorrect work name",
story.name === "Inky Desires [Bendy X Reader]",
"incorrect story name",
);
});
await test.step("getting author displayname", async () => {
const author = story.getAuthor();
await author.init();
assert(
author.displayName === "DustyAngel47",
"incorrect author displayname",
);
});
});

@ -0,0 +1,33 @@
import Wattpad from "../mod.ts";
import type { User } from "../mod.ts";
import { assert } from "https://deno.land/std@0.167.0/testing/asserts.ts";
export default function test(watt: Wattpad) {
Deno.test("users", async (test) => {
let user: User;
await test.step("initialization", async () => {
user = watt.getUser("DustyAngel47");
await user.init();
});
await test.step("metadata", () => {
assert(
user.username === "DustyAngel47",
"incorrect username (somehow)",
);
assert(
user.displayName === "DustyAngel47",
"incorrect display name",
);
});
await test.step("stories", async () => {
await user.updateStories(0);
// FIXME: should be resistant to me publishing more than one fic
assert(
user.stories.find((story) => story.id === "327425279"),
"incorrect stories found",
);
});
});
}
Loading…
Cancel
Save