@opentabs-dev/opentabs-plugin-reddit 0.0.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.iife.js +15177 -0
- package/dist/adapter.iife.js.map +7 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/reddit-api.d.ts +59 -0
- package/dist/reddit-api.d.ts.map +1 -0
- package/dist/reddit-api.js +254 -0
- package/dist/reddit-api.js.map +1 -0
- package/dist/tools/get-me.d.ts +13 -0
- package/dist/tools/get-me.d.ts.map +1 -0
- package/dist/tools/get-me.js +39 -0
- package/dist/tools/get-me.js.map +1 -0
- package/dist/tools/get-post.d.ts +43 -0
- package/dist/tools/get-post.d.ts.map +1 -0
- package/dist/tools/get-post.js +115 -0
- package/dist/tools/get-post.js.map +1 -0
- package/dist/tools/get-subreddit.d.ts +16 -0
- package/dist/tools/get-subreddit.d.ts.map +1 -0
- package/dist/tools/get-subreddit.js +43 -0
- package/dist/tools/get-subreddit.js.map +1 -0
- package/dist/tools/get-user.d.ts +15 -0
- package/dist/tools/get-user.d.ts.map +1 -0
- package/dist/tools/get-user.js +41 -0
- package/dist/tools/get-user.js.map +1 -0
- package/dist/tools/list-posts.d.ts +40 -0
- package/dist/tools/list-posts.d.ts.map +1 -0
- package/dist/tools/list-posts.js +79 -0
- package/dist/tools/list-posts.js.map +1 -0
- package/dist/tools/list-subscriptions.d.ts +15 -0
- package/dist/tools/list-subscriptions.d.ts.map +1 -0
- package/dist/tools/list-subscriptions.js +46 -0
- package/dist/tools/list-subscriptions.js.map +1 -0
- package/dist/tools/read-inbox.d.ts +19 -0
- package/dist/tools/read-inbox.d.ts.map +1 -0
- package/dist/tools/read-inbox.js +54 -0
- package/dist/tools/read-inbox.js.map +1 -0
- package/dist/tools/save.d.ts +8 -0
- package/dist/tools/save.d.ts.map +1 -0
- package/dist/tools/save.js +24 -0
- package/dist/tools/save.js.map +1 -0
- package/dist/tools/search-posts.d.ts +39 -0
- package/dist/tools/search-posts.d.ts.map +1 -0
- package/dist/tools/search-posts.js +77 -0
- package/dist/tools/search-posts.js.map +1 -0
- package/dist/tools/search-subreddits.d.ts +18 -0
- package/dist/tools/search-subreddits.d.ts.map +1 -0
- package/dist/tools/search-subreddits.js +52 -0
- package/dist/tools/search-subreddits.js.map +1 -0
- package/dist/tools/send-message.d.ts +9 -0
- package/dist/tools/send-message.d.ts.map +1 -0
- package/dist/tools/send-message.js +32 -0
- package/dist/tools/send-message.js.map +1 -0
- package/dist/tools/submit-comment.d.ts +10 -0
- package/dist/tools/submit-comment.d.ts.map +1 -0
- package/dist/tools/submit-comment.js +43 -0
- package/dist/tools/submit-comment.js.map +1 -0
- package/dist/tools/submit-post.d.ts +20 -0
- package/dist/tools/submit-post.d.ts.map +1 -0
- package/dist/tools/submit-post.js +61 -0
- package/dist/tools/submit-post.js.map +1 -0
- package/dist/tools/subscribe.d.ts +11 -0
- package/dist/tools/subscribe.d.ts.map +1 -0
- package/dist/tools/subscribe.js +26 -0
- package/dist/tools/subscribe.js.map +1 -0
- package/dist/tools/vote.d.ts +8 -0
- package/dist/tools/vote.d.ts.map +1 -0
- package/dist/tools/vote.js +29 -0
- package/dist/tools/vote.js.map +1 -0
- package/dist/tools.json +1326 -0
- package/package.json +57 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk';
|
|
2
|
+
import type { ToolDefinition } from '@opentabs-dev/plugin-sdk';
|
|
3
|
+
declare class RedditPlugin extends OpenTabsPlugin {
|
|
4
|
+
readonly name = "reddit";
|
|
5
|
+
readonly description = "OpenTabs plugin for Reddit";
|
|
6
|
+
readonly displayName = "Reddit";
|
|
7
|
+
readonly urlPatterns: string[];
|
|
8
|
+
readonly homepage = "https://www.reddit.com";
|
|
9
|
+
readonly tools: ToolDefinition[];
|
|
10
|
+
/**
|
|
11
|
+
* Check if the Reddit session is authenticated. The new Reddit SPA
|
|
12
|
+
* hydrates asynchronously, so the `user-logged-in` attribute on
|
|
13
|
+
* `<shreddit-app>` may not be set on first check. Retries briefly
|
|
14
|
+
* at 500ms intervals for up to 3 seconds.
|
|
15
|
+
*/
|
|
16
|
+
isReady(): Promise<boolean>;
|
|
17
|
+
}
|
|
18
|
+
declare const _default: RedditPlugin;
|
|
19
|
+
export default _default;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,cAAM,YAAa,SAAQ,cAAc;IACvC,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,WAAW,gCAAgC;IACpD,SAAkB,WAAW,YAAY;IACzC,QAAQ,CAAC,WAAW,WAA4E;IAChG,SAAkB,QAAQ,4BAA4B;IACtD,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,CAgB9B;IAEF;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;CAIlC;;AAED,wBAAkC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isAuthenticated, waitForAuth } from './reddit-api.js';
|
|
2
|
+
import { getMe } from './tools/get-me.js';
|
|
3
|
+
import { getPost } from './tools/get-post.js';
|
|
4
|
+
import { getSubreddit } from './tools/get-subreddit.js';
|
|
5
|
+
import { getUser } from './tools/get-user.js';
|
|
6
|
+
import { listPosts } from './tools/list-posts.js';
|
|
7
|
+
import { listSubscriptions } from './tools/list-subscriptions.js';
|
|
8
|
+
import { readInbox } from './tools/read-inbox.js';
|
|
9
|
+
import { save } from './tools/save.js';
|
|
10
|
+
import { searchPosts } from './tools/search-posts.js';
|
|
11
|
+
import { searchSubreddits } from './tools/search-subreddits.js';
|
|
12
|
+
import { sendMessage } from './tools/send-message.js';
|
|
13
|
+
import { submitComment } from './tools/submit-comment.js';
|
|
14
|
+
import { submitPost } from './tools/submit-post.js';
|
|
15
|
+
import { subscribe } from './tools/subscribe.js';
|
|
16
|
+
import { vote } from './tools/vote.js';
|
|
17
|
+
import { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk';
|
|
18
|
+
class RedditPlugin extends OpenTabsPlugin {
|
|
19
|
+
name = 'reddit';
|
|
20
|
+
description = 'OpenTabs plugin for Reddit';
|
|
21
|
+
displayName = 'Reddit';
|
|
22
|
+
urlPatterns = ['*://www.reddit.com/*', '*://old.reddit.com/*', '*://new.reddit.com/*'];
|
|
23
|
+
homepage = 'https://www.reddit.com';
|
|
24
|
+
tools = [
|
|
25
|
+
getMe,
|
|
26
|
+
listPosts,
|
|
27
|
+
getPost,
|
|
28
|
+
searchPosts,
|
|
29
|
+
submitPost,
|
|
30
|
+
submitComment,
|
|
31
|
+
vote,
|
|
32
|
+
save,
|
|
33
|
+
getSubreddit,
|
|
34
|
+
searchSubreddits,
|
|
35
|
+
listSubscriptions,
|
|
36
|
+
subscribe,
|
|
37
|
+
getUser,
|
|
38
|
+
sendMessage,
|
|
39
|
+
readInbox,
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Check if the Reddit session is authenticated. The new Reddit SPA
|
|
43
|
+
* hydrates asynchronously, so the `user-logged-in` attribute on
|
|
44
|
+
* `<shreddit-app>` may not be set on first check. Retries briefly
|
|
45
|
+
* at 500ms intervals for up to 3 seconds.
|
|
46
|
+
*/
|
|
47
|
+
async isReady() {
|
|
48
|
+
if (isAuthenticated())
|
|
49
|
+
return true;
|
|
50
|
+
return waitForAuth();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export default new RedditPlugin();
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,MAAM,YAAa,SAAQ,cAAc;IAC9B,IAAI,GAAG,QAAQ,CAAC;IAChB,WAAW,GAAG,4BAA4B,CAAC;IAClC,WAAW,GAAG,QAAQ,CAAC;IAChC,WAAW,GAAG,CAAC,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,CAAC,CAAC;IAC9E,QAAQ,GAAG,wBAAwB,CAAC;IAC7C,KAAK,GAAqB;QACjC,KAAK;QACL,SAAS;QACT,OAAO;QACP,WAAW;QACX,UAAU;QACV,aAAa;QACb,IAAI;QACJ,IAAI;QACJ,YAAY;QACZ,gBAAgB;QAChB,iBAAiB;QACjB,SAAS;QACT,OAAO;QACP,WAAW;QACX,SAAS;KACV,CAAC;IAEF;;;;;OAKG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,eAAe,EAAE;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;CACF;AAED,eAAe,IAAI,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if the user is logged in by looking for the `user-logged-in` attribute
|
|
3
|
+
* on the `<shreddit-app>` element (new Reddit UI).
|
|
4
|
+
*/
|
|
5
|
+
declare const isAuthenticated: () => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Wait for Reddit auth to become available, retrying at short intervals.
|
|
8
|
+
* The SPA hydrates asynchronously, so the auth attribute may not be set
|
|
9
|
+
* on the first check. Polls at 500ms intervals for up to 3 seconds.
|
|
10
|
+
*/
|
|
11
|
+
declare const waitForAuth: () => Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Reddit API response wrapper. All .json endpoints return data wrapped in
|
|
14
|
+
* a Listing structure with `kind` and `data` fields.
|
|
15
|
+
*/
|
|
16
|
+
interface RedditListing<T> {
|
|
17
|
+
kind: string;
|
|
18
|
+
data: {
|
|
19
|
+
after: string | null;
|
|
20
|
+
before: string | null;
|
|
21
|
+
children: Array<{
|
|
22
|
+
kind: string;
|
|
23
|
+
data: T;
|
|
24
|
+
}>;
|
|
25
|
+
dist: number;
|
|
26
|
+
modhash?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Make an authenticated GET request to a Reddit .json endpoint.
|
|
31
|
+
* Cookies handle authentication automatically via credentials: 'include'.
|
|
32
|
+
*
|
|
33
|
+
* @param path - URL path relative to reddit.com (e.g., '/r/programming/hot.json')
|
|
34
|
+
* @param params - Query parameters
|
|
35
|
+
* @returns Parsed JSON response
|
|
36
|
+
*/
|
|
37
|
+
declare const redditGet: <T>(path: string, params?: Record<string, string>) => Promise<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Make an authenticated POST request to a Reddit API endpoint.
|
|
40
|
+
* Includes the modhash as X-Modhash header for CSRF protection.
|
|
41
|
+
*
|
|
42
|
+
* @param path - API path (e.g., '/api/comment')
|
|
43
|
+
* @param body - Form body parameters
|
|
44
|
+
* @returns Parsed JSON response
|
|
45
|
+
*/
|
|
46
|
+
declare const redditPost: <T>(path: string, body: Record<string, string>) => Promise<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Make an authenticated POST request to oauth.reddit.com using a bearer token.
|
|
49
|
+
* Some endpoints (e.g., /api/compose for private messages) reject cookie-based
|
|
50
|
+
* auth on www.reddit.com and require bearer auth on the OAuth domain instead.
|
|
51
|
+
*
|
|
52
|
+
* @param path - API path (e.g., '/api/compose')
|
|
53
|
+
* @param body - Form body parameters
|
|
54
|
+
* @returns Parsed JSON response
|
|
55
|
+
*/
|
|
56
|
+
declare const redditOAuthPost: <T>(path: string, body: Record<string, string>) => Promise<T>;
|
|
57
|
+
export { isAuthenticated, waitForAuth, redditGet, redditPost, redditOAuthPost };
|
|
58
|
+
export type { RedditListing };
|
|
59
|
+
//# sourceMappingURL=reddit-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reddit-api.d.ts","sourceRoot":"","sources":["../src/reddit-api.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,QAAA,MAAM,eAAe,QAAO,OAS3B,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,WAAW,QAAO,OAAO,CAAC,OAAO,CAIpC,CAAC;AA2DJ;;;GAGG;AACH,UAAU,aAAa,CAAC,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;;;;;GAOG;AACH,QAAA,MAAM,SAAS,GAAU,CAAC,EAAE,MAAM,MAAM,EAAE,SAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAAG,OAAO,CAAC,CAAC,CA2CxF,CAAC;AAEF;;;;;;;GAOG;AACH,QAAA,MAAM,UAAU,GAAU,CAAC,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,OAAO,CAAC,CAAC,CAsDlF,CAAC;AAEF;;;;;;;;GAQG;AACH,QAAA,MAAM,eAAe,GAAU,CAAC,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,OAAO,CAAC,CAAC,CAsDvF,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;AAChF,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { ToolError, getCookie, waitUntil } from '@opentabs-dev/plugin-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Modhash token required for write operations (vote, comment, submit, etc.).
|
|
4
|
+
* Fetched once from /api/me.json and cached for the session lifetime.
|
|
5
|
+
*/
|
|
6
|
+
let cachedModhash = null;
|
|
7
|
+
/**
|
|
8
|
+
* OAuth bearer token for endpoints that require oauth.reddit.com.
|
|
9
|
+
* Some endpoints (e.g., /api/compose) reject cookie-based auth on
|
|
10
|
+
* www.reddit.com and require a bearer token on oauth.reddit.com instead.
|
|
11
|
+
* Fetched from the shreddit token endpoint and cached until expiry.
|
|
12
|
+
*/
|
|
13
|
+
let cachedBearerToken = null;
|
|
14
|
+
let bearerTokenExpiry = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Check if the user is logged in by looking for the `user-logged-in` attribute
|
|
17
|
+
* on the `<shreddit-app>` element (new Reddit UI).
|
|
18
|
+
*/
|
|
19
|
+
const isAuthenticated = () => {
|
|
20
|
+
const app = document.querySelector('shreddit-app');
|
|
21
|
+
if (app?.getAttribute('user-logged-in') === 'true')
|
|
22
|
+
return true;
|
|
23
|
+
// Old Reddit fallback: check for the logged-in user link
|
|
24
|
+
const userSpan = document.querySelector('.user a');
|
|
25
|
+
if (userSpan && !userSpan.textContent?.includes('login'))
|
|
26
|
+
return true;
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Wait for Reddit auth to become available, retrying at short intervals.
|
|
31
|
+
* The SPA hydrates asynchronously, so the auth attribute may not be set
|
|
32
|
+
* on the first check. Polls at 500ms intervals for up to 3 seconds.
|
|
33
|
+
*/
|
|
34
|
+
const waitForAuth = () => waitUntil(() => isAuthenticated(), { interval: 500, timeout: 3000 }).then(() => true, () => false);
|
|
35
|
+
/**
|
|
36
|
+
* Fetch and cache the modhash token from /api/me.json.
|
|
37
|
+
* The modhash is a CSRF token required for all write operations.
|
|
38
|
+
*/
|
|
39
|
+
const getModhash = async () => {
|
|
40
|
+
if (cachedModhash)
|
|
41
|
+
return cachedModhash;
|
|
42
|
+
const response = await fetch('https://www.reddit.com/api/me.json', {
|
|
43
|
+
credentials: 'include',
|
|
44
|
+
signal: AbortSignal.timeout(10_000),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw ToolError.auth(`Failed to fetch modhash: HTTP ${response.status}`);
|
|
48
|
+
}
|
|
49
|
+
const data = (await response.json());
|
|
50
|
+
const modhash = data.data?.modhash;
|
|
51
|
+
if (!modhash) {
|
|
52
|
+
throw ToolError.auth('No modhash found — user may not be logged in');
|
|
53
|
+
}
|
|
54
|
+
cachedModhash = modhash;
|
|
55
|
+
return modhash;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Fetch and cache an OAuth bearer token from the shreddit token endpoint.
|
|
59
|
+
* The new Reddit UI (shreddit) exposes a token endpoint that exchanges
|
|
60
|
+
* the CSRF cookie for a short-lived bearer token usable on oauth.reddit.com.
|
|
61
|
+
*/
|
|
62
|
+
const getBearerToken = async () => {
|
|
63
|
+
if (cachedBearerToken && Date.now() < bearerTokenExpiry)
|
|
64
|
+
return cachedBearerToken;
|
|
65
|
+
const csrfToken = getCookie('csrf_token') ?? '';
|
|
66
|
+
const response = await fetch('https://www.reddit.com/svc/shreddit/token', {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: { 'Content-Type': 'application/json' },
|
|
69
|
+
body: JSON.stringify({ csrf_token: csrfToken }),
|
|
70
|
+
credentials: 'include',
|
|
71
|
+
signal: AbortSignal.timeout(10_000),
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw ToolError.auth(`Failed to fetch bearer token: HTTP ${response.status}`);
|
|
75
|
+
}
|
|
76
|
+
const data = (await response.json());
|
|
77
|
+
if (!data.token) {
|
|
78
|
+
throw ToolError.auth('No bearer token returned — user may not be logged in');
|
|
79
|
+
}
|
|
80
|
+
cachedBearerToken = data.token;
|
|
81
|
+
bearerTokenExpiry = data.expires ? new Date(data.expires).getTime() - 30_000 : Date.now() + 600_000;
|
|
82
|
+
return cachedBearerToken;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Make an authenticated GET request to a Reddit .json endpoint.
|
|
86
|
+
* Cookies handle authentication automatically via credentials: 'include'.
|
|
87
|
+
*
|
|
88
|
+
* @param path - URL path relative to reddit.com (e.g., '/r/programming/hot.json')
|
|
89
|
+
* @param params - Query parameters
|
|
90
|
+
* @returns Parsed JSON response
|
|
91
|
+
*/
|
|
92
|
+
const redditGet = async (path, params = {}) => {
|
|
93
|
+
const url = new URL(path, 'https://www.reddit.com');
|
|
94
|
+
for (const [key, value] of Object.entries(params)) {
|
|
95
|
+
if (value !== undefined && value !== '') {
|
|
96
|
+
url.searchParams.set(key, value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let response;
|
|
100
|
+
try {
|
|
101
|
+
response = await fetch(url.toString(), {
|
|
102
|
+
credentials: 'include',
|
|
103
|
+
signal: AbortSignal.timeout(30_000),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
108
|
+
throw ToolError.timeout('Reddit API request timed out after 30000ms');
|
|
109
|
+
}
|
|
110
|
+
throw ToolError.internal(`Reddit API network error: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
if (response.status === 429) {
|
|
113
|
+
const reset = response.headers.get('x-ratelimit-reset');
|
|
114
|
+
const retryMs = reset ? Number.parseInt(reset, 10) * 1000 : undefined;
|
|
115
|
+
throw ToolError.rateLimited('Reddit API rate limited (429)', retryMs);
|
|
116
|
+
}
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
119
|
+
if (response.status === 401 || response.status === 403) {
|
|
120
|
+
throw ToolError.auth(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
121
|
+
}
|
|
122
|
+
if (response.status === 404) {
|
|
123
|
+
throw ToolError.notFound(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
124
|
+
}
|
|
125
|
+
throw ToolError.internal(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return (await response.json());
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
throw ToolError.internal('Failed to parse Reddit API response');
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Make an authenticated POST request to a Reddit API endpoint.
|
|
136
|
+
* Includes the modhash as X-Modhash header for CSRF protection.
|
|
137
|
+
*
|
|
138
|
+
* @param path - API path (e.g., '/api/comment')
|
|
139
|
+
* @param body - Form body parameters
|
|
140
|
+
* @returns Parsed JSON response
|
|
141
|
+
*/
|
|
142
|
+
const redditPost = async (path, body) => {
|
|
143
|
+
const modhash = await getModhash();
|
|
144
|
+
const form = new URLSearchParams();
|
|
145
|
+
for (const [key, value] of Object.entries(body)) {
|
|
146
|
+
if (value !== undefined && value !== '') {
|
|
147
|
+
form.append(key, value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
form.append('uh', modhash);
|
|
151
|
+
form.append('api_type', 'json');
|
|
152
|
+
let response;
|
|
153
|
+
try {
|
|
154
|
+
response = await fetch(`https://www.reddit.com${path}`, {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
headers: {
|
|
157
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
158
|
+
'X-Modhash': modhash,
|
|
159
|
+
},
|
|
160
|
+
body: form.toString(),
|
|
161
|
+
credentials: 'include',
|
|
162
|
+
signal: AbortSignal.timeout(30_000),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
167
|
+
throw ToolError.timeout('Reddit API request timed out after 30000ms');
|
|
168
|
+
}
|
|
169
|
+
throw ToolError.internal(`Reddit API network error: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
|
+
}
|
|
171
|
+
if (response.status === 429) {
|
|
172
|
+
const reset = response.headers.get('x-ratelimit-reset');
|
|
173
|
+
const retryMs = reset ? Number.parseInt(reset, 10) * 1000 : undefined;
|
|
174
|
+
throw ToolError.rateLimited('Reddit API rate limited (429)', retryMs);
|
|
175
|
+
}
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
178
|
+
if (response.status === 401 || response.status === 403) {
|
|
179
|
+
cachedModhash = null;
|
|
180
|
+
throw ToolError.auth(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
181
|
+
}
|
|
182
|
+
if (response.status === 404) {
|
|
183
|
+
throw ToolError.notFound(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
184
|
+
}
|
|
185
|
+
throw ToolError.internal(`Reddit API HTTP ${response.status}: ${errorText}`);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
return (await response.json());
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
throw ToolError.internal('Failed to parse Reddit API response');
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Make an authenticated POST request to oauth.reddit.com using a bearer token.
|
|
196
|
+
* Some endpoints (e.g., /api/compose for private messages) reject cookie-based
|
|
197
|
+
* auth on www.reddit.com and require bearer auth on the OAuth domain instead.
|
|
198
|
+
*
|
|
199
|
+
* @param path - API path (e.g., '/api/compose')
|
|
200
|
+
* @param body - Form body parameters
|
|
201
|
+
* @returns Parsed JSON response
|
|
202
|
+
*/
|
|
203
|
+
const redditOAuthPost = async (path, body) => {
|
|
204
|
+
const token = await getBearerToken();
|
|
205
|
+
const form = new URLSearchParams();
|
|
206
|
+
for (const [key, value] of Object.entries(body)) {
|
|
207
|
+
if (value !== undefined && value !== '') {
|
|
208
|
+
form.append(key, value);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
form.append('api_type', 'json');
|
|
212
|
+
let response;
|
|
213
|
+
try {
|
|
214
|
+
response = await fetch(`https://oauth.reddit.com${path}`, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: {
|
|
217
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
218
|
+
Authorization: `Bearer ${token}`,
|
|
219
|
+
},
|
|
220
|
+
body: form.toString(),
|
|
221
|
+
signal: AbortSignal.timeout(30_000),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
226
|
+
throw ToolError.timeout('Reddit OAuth API request timed out after 30000ms');
|
|
227
|
+
}
|
|
228
|
+
throw ToolError.internal(`Reddit OAuth API network error: ${error instanceof Error ? error.message : String(error)}`);
|
|
229
|
+
}
|
|
230
|
+
if (response.status === 429) {
|
|
231
|
+
const reset = response.headers.get('x-ratelimit-reset');
|
|
232
|
+
const retryMs = reset ? Number.parseInt(reset, 10) * 1000 : undefined;
|
|
233
|
+
throw ToolError.rateLimited('Reddit API rate limited (429)', retryMs);
|
|
234
|
+
}
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
237
|
+
if (response.status === 401 || response.status === 403) {
|
|
238
|
+
cachedBearerToken = null;
|
|
239
|
+
throw ToolError.auth(`Reddit OAuth API HTTP ${response.status}: ${errorText}`);
|
|
240
|
+
}
|
|
241
|
+
if (response.status === 404) {
|
|
242
|
+
throw ToolError.notFound(`Reddit OAuth API HTTP ${response.status}: ${errorText}`);
|
|
243
|
+
}
|
|
244
|
+
throw ToolError.internal(`Reddit OAuth API HTTP ${response.status}: ${errorText}`);
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
return (await response.json());
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
throw ToolError.internal('Failed to parse Reddit OAuth API response');
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
export { isAuthenticated, waitForAuth, redditGet, redditPost, redditOAuthPost };
|
|
254
|
+
//# sourceMappingURL=reddit-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reddit-api.js","sourceRoot":"","sources":["../src/reddit-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const getMe: import("@opentabs-dev/plugin-sdk").ToolDefinition<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
3
|
+
name: z.ZodString;
|
|
4
|
+
id: z.ZodString;
|
|
5
|
+
total_karma: z.ZodNumber;
|
|
6
|
+
link_karma: z.ZodNumber;
|
|
7
|
+
comment_karma: z.ZodNumber;
|
|
8
|
+
has_verified_email: z.ZodBoolean;
|
|
9
|
+
is_gold: z.ZodBoolean;
|
|
10
|
+
is_mod: z.ZodBoolean;
|
|
11
|
+
created_utc: z.ZodNumber;
|
|
12
|
+
}, z.core.$strip>>;
|
|
13
|
+
//# sourceMappingURL=get-me.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-me.d.ts","sourceRoot":"","sources":["../../src/tools/get-me.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsBxB,eAAO,MAAM,KAAK;;;;;;;;;;kBAkChB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { redditGet } from '../reddit-api.js';
|
|
2
|
+
import { defineTool } from '@opentabs-dev/plugin-sdk';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export const getMe = defineTool({
|
|
5
|
+
name: 'get_me',
|
|
6
|
+
displayName: 'Get My Profile',
|
|
7
|
+
description: "Get the authenticated user's Reddit profile including username, karma, and account details",
|
|
8
|
+
summary: 'Get the current user profile',
|
|
9
|
+
icon: 'user',
|
|
10
|
+
group: 'User',
|
|
11
|
+
input: z.object({}),
|
|
12
|
+
output: z.object({
|
|
13
|
+
name: z.string().describe('Reddit username'),
|
|
14
|
+
id: z.string().describe('User ID'),
|
|
15
|
+
total_karma: z.number().describe('Total karma (link + comment)'),
|
|
16
|
+
link_karma: z.number().describe('Post/link karma'),
|
|
17
|
+
comment_karma: z.number().describe('Comment karma'),
|
|
18
|
+
has_verified_email: z.boolean().describe('Whether the user has verified their email'),
|
|
19
|
+
is_gold: z.boolean().describe('Whether the user has Reddit Premium'),
|
|
20
|
+
is_mod: z.boolean().describe('Whether the user is a moderator of any subreddit'),
|
|
21
|
+
created_utc: z.number().describe('Account creation time as Unix timestamp'),
|
|
22
|
+
}),
|
|
23
|
+
handle: async () => {
|
|
24
|
+
const data = await redditGet('/user/me/about.json');
|
|
25
|
+
const u = data.data;
|
|
26
|
+
return {
|
|
27
|
+
name: u.name,
|
|
28
|
+
id: u.id,
|
|
29
|
+
total_karma: u.total_karma,
|
|
30
|
+
link_karma: u.link_karma,
|
|
31
|
+
comment_karma: u.comment_karma,
|
|
32
|
+
has_verified_email: u.has_verified_email,
|
|
33
|
+
is_gold: u.is_gold,
|
|
34
|
+
is_mod: u.is_mod,
|
|
35
|
+
created_utc: u.created_utc,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=get-me.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-me.js","sourceRoot":"","sources":["../../src/tools/get-me.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsBxB,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAAC;IAC9B,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,gBAAgB;IAC7B,WAAW,EAAE,4FAA4F;IACzG,OAAO,EAAE,8BAA8B;IACvC,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC5C,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;QAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QAChE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAClD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;QACnD,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QACrF,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QACpE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;QAChF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;KAC5E,CAAC;IACF,MAAM,EAAE,KAAK,IAAI,EAAE;QACjB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAa,qBAAqB,CAAC,CAAC;QAChE,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;QACpB,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,aAAa,EAAE,CAAC,CAAC,aAAa;YAC9B,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;YACxC,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const getPost: import("@opentabs-dev/plugin-sdk").ToolDefinition<z.ZodObject<{
|
|
3
|
+
subreddit: z.ZodString;
|
|
4
|
+
post_id: z.ZodString;
|
|
5
|
+
comment_limit: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
comment_depth: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
sort: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
confidence: "confidence";
|
|
9
|
+
top: "top";
|
|
10
|
+
new: "new";
|
|
11
|
+
controversial: "controversial";
|
|
12
|
+
old: "old";
|
|
13
|
+
qa: "qa";
|
|
14
|
+
}>>;
|
|
15
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
16
|
+
post: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
title: z.ZodString;
|
|
20
|
+
author: z.ZodString;
|
|
21
|
+
subreddit: z.ZodString;
|
|
22
|
+
score: z.ZodNumber;
|
|
23
|
+
upvote_ratio: z.ZodNumber;
|
|
24
|
+
num_comments: z.ZodNumber;
|
|
25
|
+
url: z.ZodString;
|
|
26
|
+
permalink: z.ZodString;
|
|
27
|
+
selftext: z.ZodString;
|
|
28
|
+
is_self: z.ZodBoolean;
|
|
29
|
+
created_utc: z.ZodNumber;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
comments: z.ZodArray<z.ZodObject<{
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
author: z.ZodString;
|
|
35
|
+
body: z.ZodString;
|
|
36
|
+
score: z.ZodNumber;
|
|
37
|
+
created_utc: z.ZodNumber;
|
|
38
|
+
parent_id: z.ZodString;
|
|
39
|
+
depth: z.ZodNumber;
|
|
40
|
+
is_submitter: z.ZodBoolean;
|
|
41
|
+
}, z.core.$strip>>;
|
|
42
|
+
}, z.core.$strip>>;
|
|
43
|
+
//# sourceMappingURL=get-post.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-post.d.ts","sourceRoot":"","sources":["../../src/tools/get-post.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2ExB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA4FlB,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { redditGet } from '../reddit-api.js';
|
|
2
|
+
import { ToolError, defineTool } from '@opentabs-dev/plugin-sdk';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const commentSchema = z.object({
|
|
5
|
+
id: z.string().describe('Comment ID'),
|
|
6
|
+
name: z.string().describe('Comment fullname (e.g., "t1_abc123")'),
|
|
7
|
+
author: z.string().describe('Comment author username'),
|
|
8
|
+
body: z.string().describe('Comment body text (markdown)'),
|
|
9
|
+
score: z.number().describe('Comment score'),
|
|
10
|
+
created_utc: z.number().describe('Comment creation time as Unix timestamp'),
|
|
11
|
+
parent_id: z.string().describe('Parent thing fullname (t3_ for post, t1_ for parent comment)'),
|
|
12
|
+
depth: z.number().describe('Nesting depth (0 = top-level reply)'),
|
|
13
|
+
is_submitter: z.boolean().describe('Whether the commenter is the post author (OP)'),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Flatten a nested comment tree into a flat array, preserving depth information.
|
|
17
|
+
*/
|
|
18
|
+
const flattenComments = (children, maxDepth) => {
|
|
19
|
+
const result = [];
|
|
20
|
+
for (const child of children) {
|
|
21
|
+
if (child.kind !== 't1')
|
|
22
|
+
continue;
|
|
23
|
+
result.push(child.data);
|
|
24
|
+
if (child.data.replies && typeof child.data.replies === 'object' && child.data.depth < maxDepth) {
|
|
25
|
+
result.push(...flattenComments(child.data.replies.data.children, maxDepth));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
export const getPost = defineTool({
|
|
31
|
+
name: 'get_post',
|
|
32
|
+
displayName: 'Get Post',
|
|
33
|
+
description: 'Get a Reddit post and its comments by subreddit and post ID. Returns the post details and a flattened comment tree.',
|
|
34
|
+
summary: 'Get a post and its comments',
|
|
35
|
+
icon: 'file-text',
|
|
36
|
+
group: 'Posts',
|
|
37
|
+
input: z.object({
|
|
38
|
+
subreddit: z.string().min(1).describe('Subreddit name without r/ prefix'),
|
|
39
|
+
post_id: z.string().min(1).describe('Post ID without t3_ prefix (e.g., "1ki00n1")'),
|
|
40
|
+
comment_limit: z
|
|
41
|
+
.number()
|
|
42
|
+
.int()
|
|
43
|
+
.min(0)
|
|
44
|
+
.max(500)
|
|
45
|
+
.optional()
|
|
46
|
+
.describe('Max number of top-level comments (default 50)'),
|
|
47
|
+
comment_depth: z.number().int().min(0).max(10).optional().describe('Max comment nesting depth (default 3)'),
|
|
48
|
+
sort: z
|
|
49
|
+
.enum(['confidence', 'top', 'new', 'controversial', 'old', 'qa'])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Comment sort order (default "confidence")'),
|
|
52
|
+
}),
|
|
53
|
+
output: z.object({
|
|
54
|
+
post: z.object({
|
|
55
|
+
id: z.string().describe('Post ID'),
|
|
56
|
+
name: z.string().describe('Post fullname (e.g., "t3_abc123")'),
|
|
57
|
+
title: z.string().describe('Post title'),
|
|
58
|
+
author: z.string().describe('Author username'),
|
|
59
|
+
subreddit: z.string().describe('Subreddit name'),
|
|
60
|
+
score: z.number().describe('Post score'),
|
|
61
|
+
upvote_ratio: z.number().describe('Upvote ratio'),
|
|
62
|
+
num_comments: z.number().describe('Total number of comments'),
|
|
63
|
+
url: z.string().describe('Post URL'),
|
|
64
|
+
permalink: z.string().describe('Reddit permalink'),
|
|
65
|
+
selftext: z.string().describe('Self post body'),
|
|
66
|
+
is_self: z.boolean().describe('Whether this is a text post'),
|
|
67
|
+
created_utc: z.number().describe('Creation timestamp'),
|
|
68
|
+
}),
|
|
69
|
+
comments: z.array(commentSchema).describe('Flattened array of comments with depth info'),
|
|
70
|
+
}),
|
|
71
|
+
handle: async (params) => {
|
|
72
|
+
const queryParams = {
|
|
73
|
+
limit: String(params.comment_limit ?? 50),
|
|
74
|
+
depth: String(params.comment_depth ?? 3),
|
|
75
|
+
};
|
|
76
|
+
if (params.sort) {
|
|
77
|
+
queryParams.sort = params.sort;
|
|
78
|
+
}
|
|
79
|
+
const data = await redditGet(`/r/${params.subreddit}/comments/${params.post_id}.json`, queryParams);
|
|
80
|
+
const postData = data[0]?.data.children[0]?.data;
|
|
81
|
+
if (!postData) {
|
|
82
|
+
throw ToolError.notFound('Post not found');
|
|
83
|
+
}
|
|
84
|
+
const comments = flattenComments(data[1]?.data.children ?? [], params.comment_depth ?? 3);
|
|
85
|
+
return {
|
|
86
|
+
post: {
|
|
87
|
+
id: postData.id,
|
|
88
|
+
name: postData.name,
|
|
89
|
+
title: postData.title,
|
|
90
|
+
author: postData.author,
|
|
91
|
+
subreddit: postData.subreddit,
|
|
92
|
+
score: postData.score,
|
|
93
|
+
upvote_ratio: postData.upvote_ratio,
|
|
94
|
+
num_comments: postData.num_comments,
|
|
95
|
+
url: postData.url,
|
|
96
|
+
permalink: postData.permalink,
|
|
97
|
+
selftext: postData.selftext ?? '',
|
|
98
|
+
is_self: postData.is_self,
|
|
99
|
+
created_utc: postData.created_utc,
|
|
100
|
+
},
|
|
101
|
+
comments: comments.map(c => ({
|
|
102
|
+
id: c.id,
|
|
103
|
+
name: c.name,
|
|
104
|
+
author: c.author,
|
|
105
|
+
body: c.body,
|
|
106
|
+
score: c.score,
|
|
107
|
+
created_utc: c.created_utc,
|
|
108
|
+
parent_id: c.parent_id,
|
|
109
|
+
depth: c.depth,
|
|
110
|
+
is_submitter: c.is_submitter,
|
|
111
|
+
})),
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=get-post.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-post.js","sourceRoot":"","sources":["../../src/tools/get-post.ts"],"names":[],"mappings":"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"}
|