Skip to content

Commit 84a267a

Browse files
committed
fix(platforms): restore reddit embeds
1 parent 7d1246b commit 84a267a

4 files changed

Lines changed: 77 additions & 21 deletions

File tree

apps/api/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ const app = new Hono<{ Bindings: CloudflareBindings }>()
9999

100100
let raw: unknown;
101101
try {
102-
raw = await p.fetch(id, { EMBED_USER_AGENT: c.env.EMBED_USER_AGENT });
102+
raw = await p.fetch(id, {
103+
EMBED_USER_AGENT: c.env.EMBED_USER_AGENT,
104+
REDDIT_CLIENT_ID: c.env.REDDIT_CLIENT_ID,
105+
REDDIT_CLIENT_SECRET: c.env.REDDIT_CLIENT_SECRET,
106+
});
103107
} catch (cause) {
104108
const problem = createProblem(EmbedlyErrors.PlatformFetchFailed, {
105109
request_id: requestId,

apps/api/worker-configuration.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ declare namespace Cloudflare {
99
CACHE: KVNamespace;
1010
AUTH_SECRET: string;
1111
EMBED_USER_AGENT: string;
12+
REDDIT_CLIENT_ID: string;
13+
REDDIT_CLIENT_SECRET: string;
1214
}
1315
}
1416
interface CloudflareBindings extends Cloudflare.Env {}
@@ -17,7 +19,10 @@ type StringifyValues<EnvType extends Record<string, unknown>> = {
1719
};
1820
declare namespace NodeJS {
1921
interface ProcessEnv extends StringifyValues<
20-
Pick<Cloudflare.Env, "AUTH_SECRET" | "EMBED_USER_AGENT">
22+
Pick<
23+
Cloudflare.Env,
24+
"AUTH_SECRET" | "EMBED_USER_AGENT" | "REDDIT_CLIENT_ID" | "REDDIT_CLIENT_SECRET"
25+
>
2126
> {}
2227
}
2328

packages/platforms/src/platforms/reddit.ts

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,50 @@ const MATCH_RE =
55
const FOLLOWUP_RE =
66
/^(?:https?:\/\/)?(?:www\.|old\.|m\.)?reddit\.com\/r\/(?<subreddit>\w+)\/comments\/(?<post_id>[a-z0-9]+)/;
77

8+
async function fetchAccessToken(env: {
9+
EMBED_USER_AGENT: string;
10+
REDDIT_CLIENT_ID?: string;
11+
REDDIT_CLIENT_SECRET?: string;
12+
}) {
13+
if (!env.REDDIT_CLIENT_ID || !env.REDDIT_CLIENT_SECRET) {
14+
throw {
15+
code: 500,
16+
message: "Reddit credentials are not configured",
17+
};
18+
}
19+
20+
const resp = await fetch("https://www.reddit.com/api/v1/access_token", {
21+
method: "POST",
22+
headers: {
23+
Authorization: `Basic ${btoa(`${env.REDDIT_CLIENT_ID}:${env.REDDIT_CLIENT_SECRET}`)}`,
24+
"Content-Type": "application/x-www-form-urlencoded",
25+
"User-Agent": env.EMBED_USER_AGENT,
26+
},
27+
body: new URLSearchParams({ grant_type: "client_credentials" }),
28+
});
29+
30+
if (!resp.ok) {
31+
throw { code: resp.status, message: resp.statusText };
32+
}
33+
34+
const data = (await resp.json()) as Record<string, any>;
35+
return data.access_token as string;
36+
}
37+
38+
async function fetchReddit(
39+
path: string,
40+
env: { EMBED_USER_AGENT: string; REDDIT_CLIENT_ID?: string; REDDIT_CLIENT_SECRET?: string },
41+
) {
42+
const token = await fetchAccessToken(env);
43+
return fetch(`https://oauth.reddit.com${path}`, {
44+
method: "GET",
45+
headers: {
46+
Authorization: `Bearer ${token}`,
47+
"User-Agent": env.EMBED_USER_AGENT,
48+
},
49+
});
50+
}
51+
852
function parseMedia(raw: Record<string, any>): NormalizedPost["media"] {
953
if (raw.domain === "i.redd.it") {
1054
return [
@@ -46,12 +90,17 @@ export const Reddit: Platform<"Reddit", Record<string, any>, {}> = {
4690
async match(url, env) {
4791
const match = url.match(MATCH_RE);
4892
if (!match) return null;
93+
94+
const directGroups = url.match(FOLLOWUP_RE)?.groups;
95+
if (directGroups) {
96+
const { subreddit, post_id } = directGroups;
97+
return `${subreddit}/${post_id}`;
98+
}
99+
49100
const req = await fetch(url, {
50101
method: "GET",
51102
redirect: "follow",
52-
headers: {
53-
"User-Agent": env?.EMBED_USER_AGENT ?? "curl/8.7.1",
54-
},
103+
headers: env ? { "User-Agent": env.EMBED_USER_AGENT } : undefined,
55104
});
56105

57106
const groups = req.url.match(FOLLOWUP_RE)?.groups;
@@ -60,14 +109,18 @@ export const Reddit: Platform<"Reddit", Record<string, any>, {}> = {
60109
return `${subreddit}/${post_id}`;
61110
},
62111
async fetch(id, env) {
112+
if (!env) {
113+
throw {
114+
code: 500,
115+
message: "Reddit environment is not configured",
116+
};
117+
}
118+
63119
const [subreddit, reddit_id] = id.split("/");
64-
const url = `https://www.reddit.com/r/${subreddit}/comments/${reddit_id}.json?raw_json=1`;
65-
const postResp = await fetch(url, {
66-
method: "GET",
67-
headers: {
68-
"User-Agent": env?.EMBED_USER_AGENT ?? "curl/8.7.1",
69-
},
70-
});
120+
const postResp = await fetchReddit(
121+
`/r/${subreddit}/comments/${reddit_id}.json?raw_json=1`,
122+
env,
123+
);
71124

72125
if (!postResp.ok) {
73126
throw { code: postResp.status, message: postResp.statusText };
@@ -88,15 +141,7 @@ export const Reddit: Platform<"Reddit", Record<string, any>, {}> = {
88141
message: "Reddit post missing author information",
89142
};
90143
}
91-
const profileResp = await fetch(
92-
`https://www.reddit.com/user/${authorName}/about.json?raw_json=1`,
93-
{
94-
method: "GET",
95-
headers: {
96-
"User-Agent": env?.EMBED_USER_AGENT ?? "",
97-
},
98-
},
99-
);
144+
const profileResp = await fetchReddit(`/user/${authorName}/about.json?raw_json=1`, env);
100145
if (!profileResp.ok) {
101146
throw {
102147
code: profileResp.status,

packages/platforms/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ interface TransformOptions {
2929

3030
interface FetchEnv {
3131
EMBED_USER_AGENT: string;
32+
REDDIT_CLIENT_ID?: string;
33+
REDDIT_CLIENT_SECRET?: string;
3234
}
3335

3436
export interface Platform<PlatformName extends string, PlatformData, PlatformMeta> {

0 commit comments

Comments
 (0)