Skip to content

Commit

Permalink
feat: add a way to post a gallery
Browse files Browse the repository at this point in the history
  • Loading branch information
ekrzeptowski committed Mar 11, 2024
1 parent 0ff7bd1 commit 481b5b8
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/reddit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { Replyable } from "./replyable/object";
export type {
BanOptions,
Captcha,
GalleryPostOptions,
LinkPostOptions,
TextPostOptions,
} from "./subreddit/controls";
Expand Down
122 changes: 118 additions & 4 deletions src/reddit/subreddit/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ import { PostOrCommentListing } from "../post-or-comment/listing";
import { BannedUserListing } from "../user/moderator-actioned/banned";
import { ModeratorActionedUserListing } from "../user/moderator-actioned/base";
import { Moderator } from "../user/moderator-actioned/moderator";
import { assertKind, fromRedditData, isBrowser, webSocket } from "../util";
import {
assertKind,
formatId,
fromRedditData,
isBrowser,
webSocket,
} from "../util";
import { SubredditListing } from "./listing";
import { Subreddit } from "./object";

Expand Down Expand Up @@ -99,6 +105,27 @@ export interface LinkPostOptions extends TextPostOptions {
unique?: boolean;
}

/** Options for submitting a gallery post. */
export interface GalleryPostOptions {
/** An array of media files to upload. */
gallery: {
/** The media file to upload. */
file: Blob | File;
/** File name. */
fileName: string;
/** A caption for the embedded file to be used on gallery items. */
caption?: string;
/** An external URL to be used on gallery items. */
outboundUrl?: string;
}[];
/** Extra options for the gallery post. */
options: LinkPostOptions;
/** The title of the gallery post. */
subreddit: string;
/** The title of the gallery post. */
title: string;
}

/** Extra options for media uploads. */
export interface UploadMediaOptions {
/**
Expand Down Expand Up @@ -170,7 +197,7 @@ export interface UploadResponse {
};
}

type PostTypes = "self" | "link" | "crosspost" | "image";
type PostTypes = "self" | "link" | "crosspost" | "image" | "gallery";

interface PostOptions {
kind: PostTypes;
Expand All @@ -185,6 +212,11 @@ interface PostOptions {
// This only applies to link and cross posts
resubmit: boolean;
websocketUrl?: string;
items?: {
caption?: string;
mediaId: string;
outboundUrl?: string;
}[];
}

/**
Expand Down Expand Up @@ -1010,6 +1042,61 @@ export class SubredditControls extends BaseControls {
}
}

/**
* Submit a gallery post.
* @param params Options for submitting a gallery post.
* @returns A promise that resolves to the ID of the new post.
*/
async postGallery({
gallery,
title,
subreddit,
options,
}: GalleryPostOptions): Promise<string> {
const items = await Promise.all(
gallery.map(async item => {
let mediaId;
try {
const image =
item.file instanceof MediaImg
? item.file
: await this.uploadMedia({
file: item.file,
name: item.fileName,
type: "img",
caption: item.caption,
outboundUrl: item.outboundUrl,
});
debug("Image upload response %o", image);
mediaId = image?.mediaId;
} catch (error) {
debug("Failed to upload image: %o", error);
throw new Error(
`Failed to upload image: ${(error as Error).message}`
);
}
return {
caption: item.caption,
outboundUrl: item.outboundUrl,
mediaId: mediaId,
};
})
);
const filteredItems = items.filter(
item => item.mediaId
) as PostOptions["items"];
return this.post(subreddit, {
kind: "gallery",
items: filteredItems,
title: title,
sendReplies: options.sendReplies ?? false,
resubmit: !options.unique,
captcha: options.captcha,
nsfw: options.nsfw ?? false,
spoiler: options.spoiler ?? false,
});
}

/** @internal */
async uploadMedia({
file,
Expand Down Expand Up @@ -1212,6 +1299,17 @@ export class SubredditControls extends BaseControls {
request.captcha = options.captcha.response;
request.iden = options.captcha.iden;
}
if (options.kind === "gallery" && options.items) {
request.items = options.items.map(item => {
return {
// eslint-disable-next-line @typescript-eslint/naming-convention
media_id: item.mediaId,
caption: item.caption,
// eslint-disable-next-line @typescript-eslint/naming-convention
outbound_url: item.outboundUrl,
};
});
}

return request;
}
Expand Down Expand Up @@ -1276,9 +1374,25 @@ export class SubredditControls extends BaseControls {
}

private async handleRegularPost(request: Data): Promise<string> {
const submitResponse: Data = await this.gateway.post("api/submit", request);
let submitResponse: Data;
switch (request.kind) {
case "gallery":
submitResponse = await this.gateway.postJson(
"api/submit_gallery_post.json",
request
);
break;
case "poll":
submitResponse = await this.gateway.postJson(
"api/submit_poll_post",
request
);
break;
default:
submitResponse = await this.gateway.post("api/submit", request);
}
debugPost("Submit response %o", submitResponse);
return submitResponse.id as string;
return formatId(submitResponse.id as string);
}

/** @internal */
Expand Down
19 changes: 19 additions & 0 deletions src/reddit/subreddit/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { ModeratorActionedUser } from "../user/moderator-actioned/base";
import type { Moderator } from "../user/moderator-actioned/moderator";
import type {
BanOptions,
GalleryPostOptions,
LinkPostOptions,
SubredditControls,
TextPostOptions,
Expand Down Expand Up @@ -848,6 +849,24 @@ export class Subreddit extends Content implements SubredditData {
);
}

/**
* Submit a gallery post.
* @param params Options for submitting a gallery post.
* @returns A promise that resolves to the ID of the new post.
*/
async postGallery({
gallery,
title,
options,
}: Omit<GalleryPostOptions, "subreddit">): Promise<string> {
return this.controls.postGallery({
gallery,
title,
subreddit: this.displayName,
options,
});
}

/**
* Submit a link post to this subreddit.
*
Expand Down
7 changes: 7 additions & 0 deletions src/reddit/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,10 @@ export const isBrowser = typeof self === "object";
export const webSocket: typeof self.WebSocket = isBrowser
? self.WebSocket
: (ws as never);

/**
* Format a Reddit ID to remove the prefix.
* @param id
* @returns The formatted ID.
*/
export const formatId = (id: string) => id.replace(/^t\d_/, "");

0 comments on commit 481b5b8

Please sign in to comment.