import { GalleryMediaStatus as GalleryMediaStatusProto } from '@devvit/protos/json/devvit/plugin/redditapi/common/common_msg.js'; import { type CustomPostStylesInput } from '@devvit/protos/json/devvit/plugin/redditapi/linksandcomments/linksandcomments_msg.js'; import type { PostData } from '@devvit/shared-types/PostData.js'; import { RichTextBuilder } from '@devvit/shared-types/richtext/RichTextBuilder.js'; import { T2, T3, T5 } from '@devvit/shared-types/tid.js'; import { type UserGeneratedContent } from '../common.js'; import { type FilterOptions } from '../helpers/filterThing.js'; import type { CustomPostStyles } from '../RedditClient.js'; import type { CommentSubmissionOptions } from './Comment.js'; import { Comment } from './Comment.js'; import type { CommonFlair } from './Flair.js'; import type { ListingFetchOptions } from './Listing.js'; import { Listing } from './Listing.js'; import { type AddRemovalNoteOptions } from './ModNote.js'; import { User } from './User.js'; /** A moderator report attached to a post or comment. */ export type ModeratorReport = { reason: string; /** Username of the author without the u/ prefix, e.g. 'spez' */ author: string; }; /** * Crowd Control threshold for comments on a post. Determines which comments * should be collapsed by default. * * OFF: Do not collapse or filter comments through Crowd Control. * LENIENT: Collapse or filter comments from accounts with negative community * karma. * MEDIUM: LENIENT but also applies to new accounts. * STRICT: MEDIUM but applies to accounts that have not joined the community. */ export type CrowdControlLevel = 'OFF' | 'LENIENT' | 'MEDIUM' | 'STRICT'; export type GetPostsOptions = ListingFetchOptions & { subredditName?: string; }; export type GetBestPostsOptions = ListingFetchOptions; export type SearchPostsOptions = ListingFetchOptions & { /** Search query. */ query: string; /** * The subreddit to search without the `r/` prefix. If specified, restricts * the search to posts in this subreddit. */ subredditName?: string; /** How to sort the search results. Defaults to `relevance`. */ sort?: 'relevance' | 'hot' | 'top' | 'new' | 'comments'; /** Limit search results to a timeframe. Defaults to `all`. */ timeframe?: 'hour' | 'day' | 'week' | 'month' | 'year' | 'all'; }; export type GetDuplicatesOptions = ListingFetchOptions & { /** The post identifier. */ postId: T3; /** One of: "num_comments", "new" */ sort?: 'num_comments' | 'new'; /** Limit search to the given subreddit name. The r/ prefix is optional. */ subredditName?: string; /** Only return duplicates that are crossposting this post. */ crosspostsOnly?: boolean; /** Use `"all"` to include results hidden by the account's preferences. */ show?: string; }; export type GetPostsOptionsWithTimeframe = GetPostsOptions & { timeframe?: 'hour' | 'day' | 'week' | 'month' | 'year' | 'all'; }; export type GetSortedPostsOptions = GetPostsOptionsWithTimeframe & { sort: 'top' | 'controversial'; }; export type GetHotPostsOptions = GetPostsOptions & { location?: 'GLOBAL' | 'US' | 'AR' | 'AU' | 'BG' | 'CA' | 'CL' | 'CO' | 'HR' | 'CZ' | 'FI' | 'FR' | 'DE' | 'GR' | 'HU' | 'IS' | 'IN' | 'IE' | 'IT' | 'JP' | 'MY' | 'MX' | 'NZ' | 'PH' | 'PL' | 'PT' | 'PR' | 'RO' | 'RS' | 'SG' | 'ES' | 'SE' | 'TW' | 'TH' | 'TR' | 'GB' | 'US_WA' | 'US_DE' | 'US_DC' | 'US_WI' | 'US_WV' | 'US_HI' | 'US_FL' | 'US_WY' | 'US_NH' | 'US_NJ' | 'US_NM' | 'US_TX' | 'US_LA' | 'US_NC' | 'US_ND' | 'US_NE' | 'US_TN' | 'US_NY' | 'US_PA' | 'US_CA' | 'US_NV' | 'US_VA' | 'US_CO' | 'US_AK' | 'US_AL' | 'US_AR' | 'US_VT' | 'US_IL' | 'US_GA' | 'US_IN' | 'US_IA' | 'US_OK' | 'US_AZ' | 'US_ID' | 'US_CT' | 'US_ME' | 'US_MD' | 'US_MA' | 'US_OH' | 'US_UT' | 'US_MO' | 'US_MN' | 'US_MI' | 'US_RI' | 'US_KS' | 'US_MT' | 'US_MS' | 'US_SC' | 'US_KY' | 'US_OR' | 'US_SD'; }; export type GetPostsByUserOptions = { username: string; sort?: 'hot' | 'new' | 'top' | 'controversial'; timeframe?: 'hour' | 'day' | 'week' | 'month' | 'year' | 'all'; pageSize?: number; limit?: number; after?: string; before?: string; }; export type PostSuggestedCommentSort = 'BLANK' /** "Best" sort. */ | 'CONFIDENCE' | 'CONTROVERSIAL' | 'LIVE' /** Sort comments by creation time. */ | 'NEW' | 'OLD' /** Similar to the "best" (confidence) sort, but specially designed for Q&A-type threads to highlight good question/answer pairs. */ | 'QA' | 'RANDOM' /** Sort by top upvoted comments. */ | 'TOP'; /** Options for replacing a post body. */ export type PostTextOptions = { /** The post body in Markdown. */ text: string; } | { /** The post body as rich text. */ richtext: object | RichTextBuilder; }; export type CustomPostRichTextFallback = RichTextBuilder | string; export type CustomPostTextFallbackOptions = { /** * The fallback content as plaintext or Markdown. See * https://www.reddit.com/r/reddit.com/wiki/markdown/. */ text: string; } | { /** The fallback content as richtext. */ richtext: CustomPostRichTextFallback; }; export type SetCustomPostTextFallbackOptions = CustomPostTextFallbackOptions & { /** The identity used to update the custom Post. */ runAs?: 'USER' | 'APP'; }; export type SubmitLinkOptions = CommonSubmitPostOptions & { url: string; /** * @deprecated Unsupported. This property is for backwards compatibility and * has no effect. It will removed in a future version. New code should not * use it. */ resubmit?: boolean; }; export type SubmitMediaOptions = CommonSubmitPostOptions & { kind: 'image' | 'video' | 'videogif'; videoPosterUrl?: string; imageUrls?: [string]; }; export type SubmitSelfPostOptions = CommonSubmitPostOptions & PostTextOptions; export type SubmitCustomPostOptions = CommonSubmitPostOptions & { /** * The entrypoint name. Must correspond to a `post.entrypoints` key in the * app's `devvit.json`. * * @default The default `devvit.json` entrypoint (`'default'`). * * @example Only `'default'` and `'game'` are valid entries given the * following `devvit.json` configuration: * ```json * { * "$schema": "https://developers.reddit.com/schema/config-file.v1.json", * "name": "example", * "post": { * "entrypoints": { * "default": {"entry": "splash.html"}, * "game": {"entry": "game.html"} * } * } * } * ``` */ entry?: string; /** * Arbitrary data to associate to the post. Limited to two kilobytes. * * @see {@link PostData}. */ postData?: PostData; /** Content to show when rendered on `https://old.reddit.com`. */ textFallback?: CustomPostTextFallbackOptions; userGeneratedContent?: UserGeneratedContent; /** * Styles associated with the custom post, such as height or background color. */ styles?: CustomPostStylesInput; }; export type CommonSubmitPostOptions = { /** The title of the new post. */ title: string; /** Whether the author receives notifications for new comments. */ sendreplies?: boolean; /** Whether to mark the new post as NSFW. */ nsfw?: boolean; /** Whether to mark the new post as a spoiler. */ spoiler?: boolean; /** The flair template to apply to the new post. */ flairId?: string; /** The flair text to apply to the new post. */ flairText?: string; /** The account that creates the post. Defaults to the app account. */ runAs?: 'USER' | 'APP'; }; export type SubredditOptions = { /** Defaults to the current subreddit name. */ subredditName?: string; }; /** Link, self, or media post options exclusively. */ export type SubmitPostOptions = (SubmitLinkOptions & { richtext?: never; text?: never; kind?: never; }) | (SubmitSelfPostOptions & { kind?: never; url?: never; }) | (SubmitMediaOptions & { richtext?: never; text?: never; url?: never; }); /** Options for creating a crosspost. */ export type CrosspostOptions = CommonSubmitPostOptions & Required & { postId: T3; }; /** @deprecated Use {@link CommonFlair}. */ export type LinkFlair = CommonFlair; /** * oEmbed is a format for allowing an embedded representation of a URL on * third-party sites. The API lets a website display embedded content, such as * photos or videos, without parsing the resource directly. * See: https://oembed.com/ */ export type Oembed = { /** The resource type and its type-specific parameters, such as `"video"`. */ type: string; /** A text title, describing the resource. */ title?: string | undefined; /** A URL for the author/owner of the resource. E.g. "YouTube" */ providerName?: string | undefined; /** The name of the resource provider. E.g "https://www.youtube.com/" */ providerUrl?: string | undefined; /** The oEmbed version number. This must be 1.0. */ version: string; /** The width of the optional thumbnail in pixels */ thumbnailWidth?: number | undefined; /** The height of the optional thumbnail in pixels */ thumbnailHeight?: number | undefined; /** A URL to a thumbnail image representing the resource. */ thumbnailUrl?: string | undefined; /** * The HTML required to embed a video player. It should have no padding or * margins. Consider loading it in a separate-origin iframe to avoid XSS * vulnerabilities. */ html: string; /** The height in pixels required to display the HTML. */ height?: number | undefined; /** The width in pixels required to display the HTML. */ width?: number | undefined; /** * A URL for the resource's author or owner. * * @example "https://www.youtube.com/@Reddit" */ authorUrl?: string | undefined; /** The name of the author/owner of the resource. E.g. "Reddit" */ authorName?: string | undefined; }; /** * Contains the data for a video hosted on Reddit that is in a post */ export type RedditVideo = { /** The bitrate of the video in kilobits per second. E.g. 450 */ bitrateKbps?: number | undefined; /** * The URL to the DASH playlist file. * * @example "https://v.redd.it/abc123/DASHPlaylist.mpd" */ dashUrl?: string | undefined; /** The duration of the video in seconds. E.g. 30 */ duration?: number | undefined; /** * The direct URL to the video. * * @example "https://v.redd.it/abc123/DASH_1080.mp4?source=fallback" */ fallbackUrl?: string | undefined; /** The height of the video in pixels. E.g. 1080 */ height?: number | undefined; /** * The URL to the HLS playlist file. * * @example "https://v.redd.it/abc123/HLSPlaylist.m3u8" */ hlsUrl?: string | undefined; /** If `true`, the video is a GIF */ isGif?: boolean | undefined; /** * The URL to the scrubber media file. * * @example "https://v.redd.it/abc123/DASH_96.mp4" */ scrubberMediaUrl?: string | undefined; /** The status of the transcoding process. E.g. "completed" */ transcodingStatus?: string | undefined; /** The width of the video in pixels. E.g. 1920 */ width?: number | undefined; }; export type SecureMedia = { /** The type of the oEmbed media, if present (e.g. "youtube.com") */ type?: string | undefined; oembed?: Oembed | undefined; redditVideo?: RedditVideo | undefined; }; /** * Contains a post's thumbnail and, for NSFW content, its blurred version. */ export type EnrichedThumbnail = { /** Attribution text for the thumbnail */ attribution?: string; /** * The thumbnail image. Its resolution can differ from {@link Post.thumbnail}. */ image: { url: string; height: number; width: number; }; /** Whether this thumbnail appears blurred by default */ isObfuscatedDefault: boolean; /** The blurred image for NSFW thumbnails */ obfuscatedImage?: { url: string; height: number; width: number; }; }; export declare const GalleryMediaStatus: { readonly UNKNOWN: 0; readonly VALID: 1; readonly FAILED: 2; }; export type GalleryMediaStatus = (typeof GalleryMediaStatusProto)[keyof typeof GalleryMediaStatusProto]; /** * Represents media that the post may contain. */ export type GalleryMedia = { /** The media processing status. Successful uploads have `VALID` status. */ status: GalleryMediaStatus; url: string; height: number; width: number; }; /** Poll option on a poll post. */ export type PollOption = { /** ID of the poll option. */ id: string; /** The text of the poll option. */ text: string; /** The number of votes this poll option has received. */ voteCount: number; }; /** Aggregated poll data for a poll post. */ export type PollData = { /** Options in the poll. */ options: PollOption[]; /** Total votes cast across all of the poll's options. */ totalVoteCount: number; /** Time the poll voting closes, in Unix milliseconds. */ votingEndTimestamp: number; }; export declare class Post { #private; get id(): T3; /** The creator's account identifier, or `undefined` when unavailable. */ get authorId(): T2 | undefined; /** The creator's username without the leading `u/`. */ get authorName(): string; /** The subreddit identifier where the post was created. */ get subredditId(): T5; /** The owning subreddit's name without the leading `r/`. */ get subredditName(): string; /** * The post's path relative to `https://www.reddit.com`. * * @example "/r/wallstreetbets/comments/abc123/post/" */ get permalink(): string; /** The title displayed for the post. */ get title(): string; /** The post body in Markdown. `undefined` if absent. */ get body(): string | undefined; /** The post body rendered as HTML, or `undefined` when unavailable. */ get bodyHtml(): string | undefined; /** * The post URL. * * This is the submitted URL for a link post or the full-size media URL for an * image or video post. Use {@link permalink} for the relative path. * * @example "https://www.reddit.com/r/wallstreetbets/comments/abc123/post/" */ get url(): string; /** * The post's preview thumbnail URL and dimensions in pixels. * * `undefined` means no thumbnail is available or the source field contains a * placeholder such as `"self"` or `"nsfw"`. */ get thumbnail(): { url: string; height: number; width: number; } | undefined; /** The date when the post was created. */ get createdAt(): Date; /** The post's upvotes minus downvotes, or `0` when unavailable. */ get score(): number; /** The number of comments, or `0` when none are available. */ get numberOfComments(): number; /** The number of reports, or `0` when none are available. */ get numberOfReports(): number; /** Whether the post has been approved by a moderator. */ get approved(): boolean; /** * The moderation approval time as Unix seconds, or `0` when unavailable. * * Use {@link approved} to check the current approval state. Convert a nonzero * value to a `Date` with `new Date(post.approvedAtUtc * 1000)`. */ get approvedAtUtc(): number; /** * The ban time as Unix seconds, or `0` when unavailable. * * Convert a nonzero value to a `Date` with * `new Date(post.bannedAtUtc * 1000)`. */ get bannedAtUtc(): number; /** Whether the post has been marked as spam by a moderator. */ get spam(): boolean; /** Whether the post is presented before other posts in its subreddit. */ get stickied(): boolean; /** Whether the post has been removed by a moderator. */ get removed(): boolean; /** * The username of the account that removed the post, without the leading * `u/`, or `undefined` when unavailable. */ get removedBy(): string | undefined; /** * Identifies who or what removed the post: * * - `"anti_evil_ops"`: Reddit Anti-Evil Operations. * - `"author"`: The post's author. * - `"automod_filtered"`: AutoModerator filtering. * - `"community_ops"`: Reddit Community Operations. * - `"content_takedown"`: A content-policy takedown. * - `"copyright_takedown"`: A copyright takedown. * - `"deleted"`: The post was deleted. * - `"moderator"`: A subreddit moderator. * - `"reddit"`: Any other remover. * - `undefined`: No removal category is available. */ get removedByCategory(): string | undefined; /** Whether the post is archived. */ get archived(): boolean; /** Whether the post body has been edited since it was created. */ get edited(): boolean; /** Whether the post is locked and new comments are disabled. */ get locked(): boolean; /** Whether the post is marked not safe for work (NSFW). */ get nsfw(): boolean; /** Whether the post is quarantined. */ get quarantined(): boolean; /** * Whether the post's content is hidden until the user explicitly opens it. */ get spoiler(): boolean; /** Whether the post is hidden from listings. */ get hidden(): boolean; /** Whether reports on the post are being ignored. */ get ignoringReports(): boolean; /** * The post's distinction category. * * For example, a post distinguished by a moderator or employee returns * `"moderator"` or `"admin"`. `undefined` means no distinction is available. */ get distinguishedBy(): string | undefined; /** * A listing of the post's top-level comments. Each comment exposes its * replies separately. * * @example * ```ts * const comments = await post.comments.get(25); * ``` */ get comments(): Listing; /** * A listing of other posts that reference the same URL. * * @example * ```ts * const duplicates = await post.getDuplicates().get(25); * ``` */ getDuplicates(opts?: Omit): Listing; /** The post flair, or `undefined` when unavailable. */ get flair(): CommonFlair | undefined; /** The author's subreddit flair, or `undefined` when unavailable. */ get authorFlair(): CommonFlair | undefined; /** * Metadata for embedded or Reddit-hosted media, including oEmbed or Reddit * video data. * * Returns `undefined` when the post has no secure media metadata. */ get secureMedia(): SecureMedia | undefined; /** User report reasons, or an empty array when none are available. */ get userReportReasons(): string[]; /** Moderator reports and authors, or an empty array when unavailable. */ get modReports(): ModeratorReport[]; /** @deprecated Use {@link modReports} to retain each report's author. */ get modReportReasons(): string[]; /** * Get the image or GIF metadata in the post. Empty if the post doesn't have * any media. * * Gallery posts can contain multiple entries. For other posts, one entry from * the first preview image or GIF variant. */ get gallery(): GalleryMedia[]; /** * The post's poll options, vote totals, and voting end time. `undefined` if * the post is not a poll. */ get pollData(): PollData | undefined; /** * The original post's identifier when this post is a crosspost. `undefined` * if not a crosspost or parent is unavailable. */ get crosspostParentId(): T3 | undefined; /** JSON representation of public fields. */ toJSON(): Pick; /** The post's approval state. */ isApproved(): boolean; /** The post's spam state. */ isSpam(): boolean; /** The post's stickied state. */ isStickied(): boolean; /** The post's removal state. */ isRemoved(): boolean; /** The post's archived state. */ isArchived(): boolean; /** The post's edited state. */ isEdited(): boolean; /** The post's locked state. */ isLocked(): boolean; /** The post's NSFW state. */ isNsfw(): boolean; /** The post's quarantine state. */ isQuarantined(): boolean; /** The post's spoiler state. */ isSpoiler(): boolean; /** The post's hidden state. */ isHidden(): boolean; /** The post's report-ignore state. */ isIgnoringReports(): boolean; /** The post's distinction category. */ isDistinguishedBy(): string | undefined; /** * Replaces the post body as the app account, then updates the cached body and * edited state from the response. */ edit(opts: Readonly): Promise; /** * Sets the suggested default sort for the post's comments. * * @throws {Error} If the suggested sort is rejected. * * @example * ```ts * const post = await reddit.getPostById(context.postId); * await post.setSuggestedCommentSort('NEW'); * ``` */ setSuggestedCommentSort(suggestedSort: PostSuggestedCommentSort): Promise; /** * Get the post data for the custom post. * * @example * ```ts * const post = await reddit.getPostById(context.postId); * const postData = await post.getPostData(); * ``` */ getPostData(): Promise; /** * Replace the post data stored on a custom post. * * @param postData - Represents the postData to be set, eg: { currentScore: 55, secretWord: 'barbeque' } * @throws {Error} Throws an error if the postData could not be set. * @example * ```ts * const post = await reddit.getPostById(context.postId); * * // Existing postData: { settings: { theme: 'dark', fontSize: 12 } } * * await post.setPostData({ * currentScore: 55, * secretWord: 'barbeque', * }); * // Result: { currentScore: 55, secretWord: 'barbeque' } * ``` */ setPostData(postData: PostData): Promise; /** * Shallow-merge `postData` with any existing post data. * * Existing top-level properties are preserved unless the input replaces * them. Nested objects are replaced rather than deeply merged. * * @throws {Error} If the post data could not be updated. * * @example * ```ts * const post = await reddit.getPostById(context.postId); * * // Existing data: * // { currentScore: 55, settings: { theme: 'dark', fontSize: 12 } } * * await post.mergePostData({ settings: { fontSize: 14 } }); * // Result: { currentScore: 55, settings: { fontSize: 14 } } * ``` */ mergePostData(postData: PostData): Promise; /** * Replaces the content shown when a custom post cannot be rendered. Eg, on * `old.reddit.com`. * * The fallback may be plain text, Markdown, or rich text. This instance's * body and edited state are updated from the response. * * @throws {Error} If the fallback could not be updated. * * @example * ```ts * const newTextFallback = { text: 'This is an updated text fallback' }; * const post = await context.reddit.getPostById(context.postId); * await post.setTextFallback(newTextFallback); * ``` */ setTextFallback(opts: Readonly): Promise; /** Creates a top-level comment on the post. */ addComment(opts: Readonly): Promise; /** Deletes the post as the app account. */ delete(): Promise; /** Approves the post and updates this instance's moderation state. */ approve(): Promise; /** * Filters a post. When a post is filtered, it is added to the ModQueue for review, and in addition: * - if @param options.keep is `false`, the post stops being in displayed the subreddit * - if @param options.keep is `true`, the post is still displayed in the subreddit * * @experimental */ filter(options?: FilterOptions): Promise; /** * Removes the post and updates this instance's moderation state. * * @param isSpam - Whether to classify the removed post as spam. */ remove(isSpam?: boolean): Promise; /** Disables new comments and updates this instance's locked state. */ lock(): Promise; /** Enables new comments and updates this instance's locked state. */ unlock(): Promise; /** Hides the post from the app account and updates this instance. */ hide(): Promise; /** Unhides the post for the app account and updates this instance. */ unhide(): Promise; /** Marks the post as NSFW and updates this instance. */ markAsNsfw(): Promise; /** Removes the NSFW designation and updates this instance. */ unmarkAsNsfw(): Promise; /** Marks the post as a spoiler and updates this instance. */ markAsSpoiler(): Promise; /** Removes the spoiler designation and updates this instance. */ unmarkAsSpoiler(): Promise; /** * Pins the post in a sticky slot. * * @param position - The sticky slot. If omitted, the bottom-most available * slot is used. Use 1 or 2 for subreddit posts. 3 and 4 are reserved for * profile pins. */ sticky(position?: 1 | 2 | 3 | 4): Promise; /** * Unpins the post without updating this instance's cached {@link stickied} * value. */ unsticky(): Promise; /** Distinguishes the post as a moderator and updates this instance. */ distinguish(): Promise; /** Distinguishes the post as an administrator and updates this instance. */ distinguishAsAdmin(): Promise; /** Removes the post's distinction and updates this instance. */ undistinguish(): Promise; /** Ignores reports and updates this instance's report-ignore state. */ ignoreReports(): Promise; /** Stops ignoring reports and updates this instance's cached state. */ unignoreReports(): Promise; /** * Snoozes subsequent reports with the same reason from the same users for * seven days. This only works for free-form reports. * * @param reason - The report reason to snooze. */ snoozeReports(reason: string): Promise; /** * Unsnoozes reports with the given reason. This only works for free-form * reports. * * @param reason - The report reason to unsnooze. */ unsnoozeReports(reason: string): Promise; /** * Sets which comments Crowd Control collapses on this post. * * @param level - See {@link CrowdControlLevel} for the available levels. */ updateCrowdControlLevel(level: CrowdControlLevel): Promise; /** * Fetches the creator's account, or `undefined` if it is unavailable. */ getAuthor(): Promise; /** Creates a crosspost of this post in another subreddit. */ crosspost(opts: Readonly>): Promise; /** * Adds a moderator note explaining why the post was removed. */ addRemovalNote(opts: Readonly>): Promise; /** * Get a thumbnail that contains a preview image and also contains a blurred preview for * NSFW images. The thumbnail returned has higher resolution than Post.thumbnail. * Returns undefined if the post doesn't have a thumbnail * * @returns {EnrichedThumbnail | undefined} * @throws {Error} Throws an error if the thumbnail could not be fetched * @example * ```ts * // from a menu action, form, scheduler, trigger, custom post click event, etc * const post = await context.reddit.getPostById(context.postId); * const enrichedThumbnail = await post.getEnrichedThumbnail(); * ``` */ getEnrichedThumbnail(): Promise; /** * Updates a custom post's styles. * * Unspecified properties retain their existing values. Passing `undefined` * removes all custom styles. * * @experimental */ setCustomPostStyles(styles: CustomPostStylesInput | undefined): Promise; /** * Get the custom styles for a custom post. * @experimental */ getCustomPostStyles(): Promise; /** * Get the poll option the authenticated user selected for this post. * Returns undefined if the post is not a poll or the user has not voted. * * This method will get the poll option for the app account by default. * To get the poll option for a user, please contact Reddit. */ getCurrentUserPollOption(): Promise; } //# sourceMappingURL=Post.d.ts.map