From 6cf0214a407492706b00ae6eb002199833d818d4 Mon Sep 17 00:00:00 2001 From: Taevas <67872932+TTTaevas@users.noreply.github.com> Date: Tue, 26 Mar 2024 01:48:17 +0100 Subject: [PATCH] Update beatmapset discussion functions, plus some documentation --- lib/beatmapset.ts | 56 ++++++++++++++++++++++++++++++++--------------- lib/changelog.ts | 6 ++--- lib/chat.ts | 10 ++++----- lib/forum.ts | 16 +++++++------- lib/index.ts | 2 +- lib/news.ts | 2 +- 6 files changed, 56 insertions(+), 36 deletions(-) diff --git a/lib/beatmapset.ts b/lib/beatmapset.ts index bf8709b..35c0864 100644 --- a/lib/beatmapset.ts +++ b/lib/beatmapset.ts @@ -232,6 +232,21 @@ export namespace Beatmapset { } export namespace Discussion { + /** + * An interface to tell the API how the returned Array should be like + * @group Parameter Object Interfaces + */ + export interface Config { + /** The maximum amount of elements to get */ + limit?: number + /** "id_asc" to have the oldest element first, "id_desc" to have the newest instead */ + sort?: "id_desc" | "id_asc" + /** Which page of the results to get */ + page?: number + /** A cursor_string provided by a previous request */ + cursor_string?: string + } + export interface WithStartingpost extends Discussion { starting_post: Post } @@ -254,18 +269,17 @@ export namespace Beatmapset { * Get complex data about the posts of a beatmapset's discussion or of a user! * @param from From where/who are the posts coming from? A specific discussion, a specific user? * @param types What kind of posts? - * @param cursor_stuff How many results maximum to get, which page of those results, a cursor_string if you have that... - * @param sort (defaults to "id_desc") "id_asc" to have the oldest recent post first, "id_desc" to have the newest instead + * @param config How many results maximum, how to sort them, which page of those, maybe a cursor_string... * @returns Relevant posts and info about them * @remarks (2024-03-11) For months now, the API's documentation says the response is likely to change, so beware */ export async function getMultiple(this: API, from?: {discussion?: Discussion["id"] | Discussion, user?: User["id"] | User}, - types?: ("first" | "reply" | "system")[], cursor_stuff?: {page?: number, limit?: number, cursor_string?: string}, sort: "id_desc" | "id_asc" = "id_desc"): + types?: ("first" | "reply" | "system")[], config?: Config): Promise<{beatmapsets: Beatmapset.WithHype[], posts: Post[], users: User[], cursor_string: string | null}> { const discussion = from?.discussion ? getId(from.discussion) : undefined const user = from?.user ? getId(from.user) : undefined - return await this.request("get", "beatmapsets/discussions/posts", {beatmapset_discussion_id: discussion, limit: cursor_stuff?.limit, - page: cursor_stuff?.page, sort, types, user, cursor_string: cursor_stuff?.cursor_string}) + return await this.request("get", "beatmapsets/discussions/posts", {beatmapset_discussion_id: discussion, limit: config?.limit, + page: config?.page, sort: config?.sort, types, user, cursor_string: config?.cursor_string}) } } @@ -283,20 +297,19 @@ export namespace Beatmapset { * Get complex data about the votes of a beatmapset's discussions or/and received/given by a specific user! * @param from The discussion with the votes, the user who voted, the user who's gotten the votes... * @param score An upvote (1) or a downvote (-1) - * @param cursor_stuff How many results maximum to get, which page of those results, a cursor_string if you have that... - * @param sort "id_asc" to have the oldest recent vote first, "id_desc" to have the newest instead (defaults to **id_desc**) + * @param config How many results maximum, how to sort them, which page of those, maybe a cursor_string... * @returns Relevant votes and info about them * @remarks (2024-03-11) For months now, the API's documentation says the response is likely to change, so beware */ export async function getMultiple(this: API, from?: {discussion?: Discussion["id"] | Discussion, vote_giver?: User["id"] | User, - vote_receiver?: User["id"] | User}, score?: 1 | -1, cursor_stuff?: {page?: number, limit?: number, cursor_string?: string}, - sort: "id_desc" | "id_asc" = "id_desc"): Promise<{votes: Vote[], discussions: Discussion[], users: User.WithGroups[], cursor_string: string | null}> { + vote_receiver?: User["id"] | User}, score?: 1 | -1, config?: Config): + Promise<{votes: Vote[], discussions: Discussion[], users: User.WithGroups[], cursor_string: string | null}> { const discussion = from?.discussion ? getId(from.discussion) : undefined const user = from?.vote_giver ? getId(from.vote_giver) : undefined const receiver = from?.vote_receiver ? getId(from.vote_receiver) : undefined - return await this.request("get", "beatmapsets/discussions/votes", {beatmapset_discussion_id: discussion, limit: cursor_stuff?.limit, - page: cursor_stuff?.page, receiver, score, sort, user, cursor_string: cursor_stuff?.cursor_string}) + return await this.request("get", "beatmapsets/discussions/votes", {beatmapset_discussion_id: discussion, limit: config?.limit, + page: config?.page, receiver, score, sort: config?.sort, user, cursor_string: config?.cursor_string}) } } @@ -304,15 +317,14 @@ export namespace Beatmapset { * Get complex data about the discussion page of any beatmapet that you want! * @param from From where/who are the discussions coming from? Maybe only qualified sets? * @param filter Should those discussions only be unresolved problems, for example? - * @param cursor_stuff How many results maximum to get, which page of those results, a cursor_string if you have that... - * @param sort "id_asc" to have the oldest recent discussion first, "id_desc" to have the newest instead (defaults to **id_desc**) + * @param config How many results maximum, how to sort them, which page of those, maybe a cursor_string... * @returns Relevant discussions and info about them * @remarks (2024-03-11) For months now, the API's documentation says the response is likely to change, so beware * @privateRemarks I don't allow setting `beatmap_id` because my testing has led me to believe it does nothing (and is therefore misleading) */ export async function getMultiple(this: API, from?: {beatmapset?: Beatmapset["id"] | Beatmapset, user?: User["id"] | User, status?: "all" | "ranked" | "qualified" | "disqualified" | "never_qualified"}, filter?: {types?: Beatmapset.Discussion["message_type"][], - only_unresolved?: boolean}, cursor_stuff?: {page?: number, limit?: number, cursor_string?: string}, sort: "id_desc" | "id_asc" = "id_desc"): + only_unresolved?: boolean}, config?: Config): Promise<{beatmaps: Beatmap.Extended[], beatmapsets: Beatmapset.Extended[], discussions: Beatmapset.Discussion.WithStartingpost[] included_discussions: Beatmapset.Discussion.WithStartingpost[], reviews_config: {max_blocks: number}, users: User.WithGroups[], cursor_string: string | null}> { @@ -320,8 +332,8 @@ export namespace Beatmapset { const user = from?.user ? getId(from.user) : undefined return await this.request("get", "beatmapsets/discussions", {beatmapset_id: beatmapset, beatmapset_status: from?.status, - limit: cursor_stuff?.limit, message_types: filter?.types, only_unresolved: filter?.only_unresolved, page: cursor_stuff?.page, sort, - user, cursor_string: cursor_stuff?.cursor_string}) + limit: config?.limit, message_types: filter?.types, only_unresolved: filter?.only_unresolved, page: config?.page, sort: config?.sort, + user, cursor_string: config?.cursor_string}) } } @@ -344,9 +356,17 @@ export namespace Beatmapset { categories?: "Any" | "Ranked" | "Qualified" | "Loved" | "Favourites" | "Pending" | "WIP" | "Graveyard" | "My Maps", /** Use this to hide all sets that are marked as explicit */ hide_explicit_content?: true, - /** Specify the musical genre of the music of the beatmapsets you're searching for */ + /** + * Specify the musical genre of the music of the beatmapsets you're searching for (don't specify to get any genre) + * @remarks "Any"/0 actually looks up sets that specifically have the Genre "Any" such as `5947`, it's excluded because it's counter-intuitive + * and near useless (but you can do something like `1-1` if you actually want that!) + */ genre?: Exclude, - /** Specify the spoken language of the music of the beatmapsets you're searching for */ + /** + * Specify the spoken language of the music of the beatmapsets you're searching for (don't specify to get any language) + * @remarks "Any"/0 actually looks up sets that specifically have the Language "Any" (and no set has that), it's excluded because it's counter-intuitive + * and near useless (but you can do something like `1-1` if you actually want that!) + */ language?: Exclude, /** Should all sets have a video, a storyboard, maybe both at once? */ extra?: ("must_have_video" | "must_have_storyboard")[], diff --git a/lib/changelog.ts b/lib/changelog.ts index a668237..d34450b 100644 --- a/lib/changelog.ts +++ b/lib/changelog.ts @@ -107,7 +107,7 @@ export namespace Changelog { const max_id = typeof range?.to === "number" ? range.to : undefined const response = await this.request("get", "changelog", {from, to, max_id, stream, message_formats}) - return response.builds + return response.builds // NOT the only property; `streams` is irrelevant while `search` is useless } } @@ -140,8 +140,8 @@ export namespace Changelog { * ``` */ export async function getAll(this: API): Promise { - const response = await this.request("get", "changelog", {max_id: 0}) - return response.streams + const response = await this.request("get", "changelog", {max_id: 0}) // Limit how many `builds` we receive, for the sake of speed + return response.streams // NOT the only property; both `builds` and `search` are irrelevant } } } diff --git a/lib/chat.ts b/lib/chat.ts index 21ecac4..ead4944 100644 --- a/lib/chat.ts +++ b/lib/chat.ts @@ -51,12 +51,12 @@ export namespace Chat { /** * Get a ChatChannel, and the users in it if it is a private channel! * @scope {@link Scope"chat.read"} - * @remarks Will 404 if the user has not joined the channel (use `joinChatChannel` for that) * @param channel The channel in question + * @remarks Will 404 if the user has not joined the channel (use `joinChatChannel` for that) */ export async function getOne(this: API, channel: Channel["channel_id"] | Channel): Promise { const response = await this.request("get", `chat/channels/${getId(channel, "channel_id")}`) - return response.channel + return response.channel // NOT the only property; `users` is already provided within `channel` so it is useless } /** @@ -92,11 +92,11 @@ export namespace Chat { /** * Create a new announcement! * @scope {@link Scope"chat.write_manage"} - * @remarks From my understanding, this WILL 403 unless the user is kinda special * @param channel Details of the channel you're creating * @param user_targets The people that will receive your message * @param message The message to send with the announcement * @returns The newly created channel! + * @remarks From my understanding, this WILL 403 unless the user is kinda special */ export async function createAnnouncement(this: API, channel: {name: string, description: string}, user_targets: Array, message: string): Promise { @@ -177,12 +177,12 @@ export namespace Chat { /** * Send a private message to someone! * @scope {@link Scope"chat.write"} - * @remarks You don't need to use `createChatPrivateChannel` before sending a message * @param user_target The User you wanna send your message to! * @param message The message you wanna send * @param is_action Is it a command? Like `/me dances` (defaults to **false**) * @param uuid A client-side message identifier * @returns The message you sent + * @remarks You don't need to use `createChatPrivateChannel` before sending a message */ export async function sendPrivate(this: API, user_target: User["id"] | User, message: string, is_action: boolean = false, uuid?: string): Promise<{channel: Channel, message: Message}> { @@ -197,7 +197,7 @@ export namespace Chat { * @returns A list of recent silences * @remarks Every 30 seconds is a good idea */ - export async function keepAlive(this: API, since?: {user_silence?: UserSilence["id"]| UserSilence, message?: Message["message_id"] | Message}): + export async function keepAlive(this: API, since?: {user_silence?: UserSilence["id"] | UserSilence, message?: Message["message_id"] | Message}): Promise { const history_since = since?.user_silence ? getId(since.user_silence) : undefined const message_since = since?.message ? getId(since.message, "message_id") : undefined diff --git a/lib/forum.ts b/lib/forum.ts index 8876a94..1459c5a 100644 --- a/lib/forum.ts +++ b/lib/forum.ts @@ -1,4 +1,4 @@ -import { API } from "./index.js" +import { API, User } from "./index.js" import { getId } from "./misc.js" export namespace Forum { @@ -16,8 +16,8 @@ export namespace Forum { edited_by_id: number | null forum_id: number id: number - topic_id: number - user_id: number + topic_id: Topic["id"] + user_id: User["id"] body: { /** Post content in HTML format */ html: string @@ -48,7 +48,7 @@ export namespace Forum { export interface Topic { created_at: Date deleted_at: Date | null - first_post_id: number + first_post_id: Post["id"] forum_id: number id: number is_locked: boolean @@ -57,7 +57,7 @@ export namespace Forum { title: string type: "normal" | "sticky" | "announcement" updated_at: Date - user_id: number + user_id: User["id"] poll: { allow_vote_change: boolean /** @remarks Can be in the future */ @@ -87,12 +87,12 @@ export namespace Forum { /** * Create a new ForumTopic in the forum of your choice! * @scope {@link Scope"forum.write"} - * @remarks Some users may not be allowed to do that, such as newly registered users, so this can 403 even with the right scopes * @param forum_id The id of the forum you're creating your topic in * @param title The topic's title * @param text The first post's content/message * @param poll If you want to make a poll, specify the parameters of that poll! * @returns An object with the topic you've made, and its first initial post (which uses your `text`) + * @remarks Some users may not be allowed to do that, such as newly registered users, so this can 403 even with the right scopes */ export async function create(this: API, forum_id: number, title: string, text: string, poll?: { title: string @@ -134,10 +134,10 @@ export namespace Forum { /** * Edit the title of a Forum.Topic! * @scope {@link Scope"forum.write"} - * @remarks Use `editForumPost` if you wanna edit the post at the top of the topic * @param topic The topic or the id of the topic in question * @param new_title The new title of the topic * @returns The edited ForumTopic + * @remarks Use `editForumPost` if you wanna edit the post at the top of the topic */ export async function editTitle(this: API, topic: Topic["id"] | Topic, new_title: string): Promise { return await this.request("put", `forums/topics/${getId(topic)}`, {forum_topic: {topic_title: new_title}}) @@ -146,9 +146,9 @@ export namespace Forum { /** * Get a forum topic, as well as its main post (content) and the posts that were sent in it! - * @remarks The oldest post of a topic is the text of a topic * @param topic An object with the id of the topic in question * @param config How many results maximum, how to sort them, etc... + * @remarks The oldest post of a topic is the text of a topic */ export async function getTopicAndPosts(this: API, topic: Topic["id"] | Topic, config?: { /** The id (or the post itself) of the first post to be returned in `posts` (irrelevant if using a `cursor_string`) */ diff --git a/lib/index.ts b/lib/index.ts index 68c62fa..72a0956 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -47,7 +47,7 @@ export { Comment } from "./comment.js" function correctType(x: any): any { const bannedProperties = [ "name", "artist", "title", "location", "interests", "occupation", "twitter", - "discord", "version", "author", "raw", "bbcode", "title", "message" + "discord", "version", "author", "raw", "bbcode", "title", "message", "creator" ] if (typeof x === "boolean") { diff --git a/lib/news.ts b/lib/news.ts index 1a5b2f9..66165c3 100644 --- a/lib/news.ts +++ b/lib/news.ts @@ -38,10 +38,10 @@ export namespace NewsPost { /** * Get all the NewsPosts of a specific year! - * @remarks If the specified year is invalid/has no news, it fallbacks to the default year * @param year The year the posts were made (defaults to **current year**) * @privateRemarks Because the only filter is the year, everything but `news_sidebar.news_posts` is actually completely useless! * You could maybe make a case for `years` being useful, but I don't believe it's useful enough to sacrifice the simplicity + * @remarks If the specified year is invalid/has no news, it fallbacks to the default year */ export async function getMultiple(this: API, year?: number): Promise { const response = await this.request("get", "news", {year, limit: 0}) // Put the limit at minimum because it's about stuff we're filtering out anyway