@cogitator-ai/channels 0.9.2 → 0.10.0

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.
Files changed (102) hide show
  1. package/README.md +101 -11
  2. package/dist/assistant-config.d.ts +16 -0
  3. package/dist/assistant-config.d.ts.map +1 -1
  4. package/dist/assistant-config.js +22 -0
  5. package/dist/assistant-config.js.map +1 -1
  6. package/dist/channels/bluesky/account.d.ts +18 -0
  7. package/dist/channels/bluesky/account.d.ts.map +1 -0
  8. package/dist/channels/bluesky/account.js +25 -0
  9. package/dist/channels/bluesky/account.js.map +1 -0
  10. package/dist/channels/bluesky/channel.d.ts +89 -0
  11. package/dist/channels/bluesky/channel.d.ts.map +1 -0
  12. package/dist/channels/bluesky/channel.js +427 -0
  13. package/dist/channels/bluesky/channel.js.map +1 -0
  14. package/dist/channels/bluesky/config.d.ts +16 -0
  15. package/dist/channels/bluesky/config.d.ts.map +1 -0
  16. package/dist/channels/bluesky/config.js +2 -0
  17. package/dist/channels/bluesky/config.js.map +1 -0
  18. package/dist/channels/bluesky/connection.d.ts +40 -0
  19. package/dist/channels/bluesky/connection.d.ts.map +1 -0
  20. package/dist/channels/bluesky/connection.js +159 -0
  21. package/dist/channels/bluesky/connection.js.map +1 -0
  22. package/dist/channels/bluesky/feed.d.ts +59 -0
  23. package/dist/channels/bluesky/feed.d.ts.map +1 -0
  24. package/dist/channels/bluesky/feed.js +324 -0
  25. package/dist/channels/bluesky/feed.js.map +1 -0
  26. package/dist/channels/bluesky/index.d.ts +5 -0
  27. package/dist/channels/bluesky/index.d.ts.map +1 -0
  28. package/dist/channels/bluesky/index.js +4 -0
  29. package/dist/channels/bluesky/index.js.map +1 -0
  30. package/dist/channels/threads/account.d.ts +42 -0
  31. package/dist/channels/threads/account.d.ts.map +1 -0
  32. package/dist/channels/threads/account.js +64 -0
  33. package/dist/channels/threads/account.js.map +1 -0
  34. package/dist/channels/threads/channel.d.ts +119 -0
  35. package/dist/channels/threads/channel.d.ts.map +1 -0
  36. package/dist/channels/threads/channel.js +405 -0
  37. package/dist/channels/threads/channel.js.map +1 -0
  38. package/dist/channels/threads/client.d.ts +42 -0
  39. package/dist/channels/threads/client.d.ts.map +1 -0
  40. package/dist/channels/threads/client.js +97 -0
  41. package/dist/channels/threads/client.js.map +1 -0
  42. package/dist/channels/threads/feed.d.ts +74 -0
  43. package/dist/channels/threads/feed.d.ts.map +1 -0
  44. package/dist/channels/threads/feed.js +372 -0
  45. package/dist/channels/threads/feed.js.map +1 -0
  46. package/dist/channels/threads/index.d.ts +6 -0
  47. package/dist/channels/threads/index.d.ts.map +1 -0
  48. package/dist/channels/threads/index.js +6 -0
  49. package/dist/channels/threads/index.js.map +1 -0
  50. package/dist/channels/threads/token.d.ts +51 -0
  51. package/dist/channels/threads/token.d.ts.map +1 -0
  52. package/dist/channels/threads/token.js +128 -0
  53. package/dist/channels/threads/token.js.map +1 -0
  54. package/dist/feeds/attachments.d.ts +15 -0
  55. package/dist/feeds/attachments.d.ts.map +1 -0
  56. package/dist/feeds/attachments.js +85 -0
  57. package/dist/feeds/attachments.js.map +1 -0
  58. package/dist/feeds/errors.d.ts +39 -0
  59. package/dist/feeds/errors.d.ts.map +1 -0
  60. package/dist/feeds/errors.js +62 -0
  61. package/dist/feeds/errors.js.map +1 -0
  62. package/dist/feeds/index.d.ts +8 -0
  63. package/dist/feeds/index.d.ts.map +1 -0
  64. package/dist/feeds/index.js +6 -0
  65. package/dist/feeds/index.js.map +1 -0
  66. package/dist/feeds/publish-store.d.ts +148 -0
  67. package/dist/feeds/publish-store.d.ts.map +1 -0
  68. package/dist/feeds/publish-store.js +345 -0
  69. package/dist/feeds/publish-store.js.map +1 -0
  70. package/dist/feeds/publisher.d.ts +118 -0
  71. package/dist/feeds/publisher.d.ts.map +1 -0
  72. package/dist/feeds/publisher.js +308 -0
  73. package/dist/feeds/publisher.js.map +1 -0
  74. package/dist/feeds/storage.d.ts +33 -0
  75. package/dist/feeds/storage.d.ts.map +1 -0
  76. package/dist/feeds/storage.js +61 -0
  77. package/dist/feeds/storage.js.map +1 -0
  78. package/dist/feeds/text.d.ts +26 -0
  79. package/dist/feeds/text.d.ts.map +1 -0
  80. package/dist/feeds/text.js +126 -0
  81. package/dist/feeds/text.js.map +1 -0
  82. package/dist/feeds/token-store.d.ts +45 -0
  83. package/dist/feeds/token-store.d.ts.map +1 -0
  84. package/dist/feeds/token-store.js +128 -0
  85. package/dist/feeds/token-store.js.map +1 -0
  86. package/dist/formatters/markdown.d.ts.map +1 -1
  87. package/dist/formatters/markdown.js +6 -0
  88. package/dist/formatters/markdown.js.map +1 -1
  89. package/dist/formatters/plain-text.d.ts +7 -0
  90. package/dist/formatters/plain-text.d.ts.map +1 -0
  91. package/dist/formatters/plain-text.js +38 -0
  92. package/dist/formatters/plain-text.js.map +1 -0
  93. package/dist/gateway.js +1 -1
  94. package/dist/gateway.js.map +1 -1
  95. package/dist/index.d.ts +5 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +4 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/runtime-builder.d.ts.map +1 -1
  100. package/dist/runtime-builder.js +54 -0
  101. package/dist/runtime-builder.js.map +1 -1
  102. package/package.json +23 -15
@@ -0,0 +1,42 @@
1
+ import { type FeedErrorCode } from '../../feeds/errors.js';
2
+ export declare const THREADS_API = "https://graph.threads.net/v1.0";
3
+ /** The error object of a Graph API response. */
4
+ interface GraphError {
5
+ message?: string;
6
+ type?: string;
7
+ code?: number;
8
+ error_subcode?: number;
9
+ error_user_msg?: string;
10
+ fbtrace_id?: string;
11
+ }
12
+ /** The feed error code a Graph API error means. */
13
+ export declare function threadsErrorCode(status: number, error: GraphError): FeedErrorCode;
14
+ export type ThreadsParams = Record<string, string | number | boolean | undefined>;
15
+ /** One page of a Graph API listing. */
16
+ export interface ThreadsPage<T> {
17
+ data?: T[];
18
+ paging?: {
19
+ cursors?: {
20
+ before?: string;
21
+ after?: string;
22
+ };
23
+ next?: string;
24
+ previous?: string;
25
+ };
26
+ }
27
+ /** The query of the page after `page`, or none when it was the last. */
28
+ export declare function nextPageQuery<T>(page: ThreadsPage<T>, query: ThreadsParams): ThreadsParams | undefined;
29
+ /** Calls the Threads Graph API with a token, turning its errors into `FeedError`s. */
30
+ export declare class ThreadsClient {
31
+ private readonly options;
32
+ constructor(options?: {
33
+ base?: string;
34
+ fetch?: typeof fetch;
35
+ });
36
+ get<T>(path: string, token: string, params?: ThreadsParams, signal?: AbortSignal): Promise<T>;
37
+ post<T>(path: string, token: string, params?: ThreadsParams, signal?: AbortSignal): Promise<T>;
38
+ delete<T>(path: string, token: string, signal?: AbortSignal): Promise<T>;
39
+ private send;
40
+ }
41
+ export {};
42
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/channels/threads/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,aAAa,EAA+B,MAAM,oBAAoB,CAAC;AAEhG,eAAO,MAAM,WAAW,mCAAmC,CAAC;AAE5D,gDAAgD;AAChD,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CASjF;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAElF,uCAAuC;AACvC,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9F;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,CAAC,EAC7B,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,aAAa,GACnB,aAAa,GAAG,SAAS,CAa3B;AAED,sFAAsF;AACtF,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;KAAO;IAE5E,GAAG,CAAC,CAAC,EACT,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,aAAkB,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC;IAQP,IAAI,CAAC,CAAC,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,aAAkB,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC;IASP,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;YAMhE,IAAI;CAsCnB"}
@@ -0,0 +1,97 @@
1
+ import { FeedError, feedErrorCode, retryAfterOf } from '../../feeds/errors.js';
2
+ export const THREADS_API = 'https://graph.threads.net/v1.0';
3
+ const RATE_LIMIT_CODES = new Set([4, 17, 32, 613]);
4
+ /** The feed error code a Graph API error means. */
5
+ export function threadsErrorCode(status, error) {
6
+ const text = `${error.message ?? ''} ${error.error_user_msg ?? ''}`;
7
+ if (error.code === 190)
8
+ return error.error_subcode === 463 ? 'token_expired' : 'auth';
9
+ if (error.code !== undefined && RATE_LIMIT_CODES.has(error.code))
10
+ return 'rate_limited';
11
+ if (/LINK_LIMIT_EXCEEDED|limit how often|publishing limit/i.test(text)) {
12
+ return text.includes('LINK_LIMIT_EXCEEDED') ? 'invalid_post' : 'quota_exceeded';
13
+ }
14
+ if (error.code === 10 || error.code === 200)
15
+ return 'auth';
16
+ return feedErrorCode(status);
17
+ }
18
+ /** The query of the page after `page`, or none when it was the last. */
19
+ export function nextPageQuery(page, query) {
20
+ const count = page.data?.length ?? 0;
21
+ if (count === 0)
22
+ return undefined;
23
+ const { cursors, next } = page.paging ?? {};
24
+ if (!next && typeof query.limit === 'number' && count < query.limit)
25
+ return undefined;
26
+ if (cursors?.after && cursors.after !== query.after)
27
+ return { ...query, after: cursors.after };
28
+ if (!next)
29
+ return undefined;
30
+ try {
31
+ const params = [...new URL(next).searchParams].filter(([name]) => name !== 'access_token');
32
+ return params.length > 0 ? { ...query, ...Object.fromEntries(params) } : undefined;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ /** Calls the Threads Graph API with a token, turning its errors into `FeedError`s. */
39
+ export class ThreadsClient {
40
+ options;
41
+ constructor(options = {}) {
42
+ this.options = options;
43
+ }
44
+ async get(path, token, params = {}, signal) {
45
+ const url = new URL(`${this.options.base ?? THREADS_API}/${path.replace(/^\//, '')}`);
46
+ for (const [key, value] of Object.entries({ ...params, access_token: token })) {
47
+ if (value !== undefined)
48
+ url.searchParams.set(key, String(value));
49
+ }
50
+ return this.send(url, { method: 'GET', ...(signal && { signal }) }, path);
51
+ }
52
+ async post(path, token, params = {}, signal) {
53
+ const url = new URL(`${this.options.base ?? THREADS_API}/${path.replace(/^\//, '')}`);
54
+ const body = new URLSearchParams();
55
+ for (const [key, value] of Object.entries({ ...params, access_token: token })) {
56
+ if (value !== undefined)
57
+ body.set(key, String(value));
58
+ }
59
+ return this.send(url, { method: 'POST', body, ...(signal && { signal }) }, path);
60
+ }
61
+ async delete(path, token, signal) {
62
+ const url = new URL(`${this.options.base ?? THREADS_API}/${path.replace(/^\//, '')}`);
63
+ url.searchParams.set('access_token', token);
64
+ return this.send(url, { method: 'DELETE', ...(signal && { signal }) }, path);
65
+ }
66
+ async send(url, init, path) {
67
+ let response;
68
+ try {
69
+ response = await (this.options.fetch ?? fetch)(url, init);
70
+ }
71
+ catch (error) {
72
+ if (init.signal?.aborted)
73
+ throw error;
74
+ throw new FeedError('threads', 'unavailable', `${init.method} ${path} failed: ${String(error)}`, {
75
+ cause: error,
76
+ });
77
+ }
78
+ const text = await response.text();
79
+ let body;
80
+ try {
81
+ body = text ? JSON.parse(text) : {};
82
+ }
83
+ catch {
84
+ body = { error: { message: text.slice(0, 300) } };
85
+ }
86
+ const error = typeof body === 'object' && body !== null && 'error' in body && typeof body.error === 'object'
87
+ ? body.error
88
+ : undefined;
89
+ if (!response.ok || error) {
90
+ const graph = error ?? { message: `HTTP ${response.status}` };
91
+ const retryAfter = retryAfterOf(response.headers);
92
+ throw new FeedError('threads', threadsErrorCode(response.status, graph), `${init.method} ${path} failed: ${graph.error_user_msg ?? graph.message ?? `HTTP ${response.status}`}`, { status: response.status, ...(retryAfter !== undefined && { retryAfter }), cause: body });
93
+ }
94
+ return body;
95
+ }
96
+ }
97
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/channels/threads/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import type { FeedChannel, FeedLimits, FeedPost, FeedPublishOptions, PublishedPost } from '@cogitator-ai/types';
2
+ import { type ThreadsAccount, type ThreadsAccountConfig } from './account.js';
3
+ export type ThreadsFeedConfig = ({
4
+ account: ThreadsAccount;
5
+ } | ThreadsAccountConfig) & {
6
+ /** How long to wait for Threads to get a post ready, in ms (default five minutes). */
7
+ containerTimeout?: number;
8
+ /** The waits between readiness checks, in ms. The last one repeats. */
9
+ pollDelays?: readonly number[];
10
+ /** Checks the 24-hour publishing quota before each post (default true). */
11
+ checkQuota?: boolean;
12
+ };
13
+ /** What a Threads post accepts. */
14
+ export declare const THREADS_LIMITS: FeedLimits & {
15
+ maxLinks: number;
16
+ maxTopicTagLength: number;
17
+ };
18
+ /**
19
+ * Publishes posts to a Threads account in the two steps Threads asks for: a
20
+ * media container, then its publication once Threads says it is ready.
21
+ * Text posts carry a link card and a topic tag. One image or a carousel of
22
+ * up to twenty is published from public URLs, since Threads fetches images
23
+ * itself. Replies go under the post they answer. The account's token is
24
+ * renewed before it expires.
25
+ *
26
+ * Threads takes no client-chosen ids, so a publication with an idempotency
27
+ * key first looks for a post an earlier attempt made: one of the account's
28
+ * posts (or replies) since the key was made, with the same text (and the same
29
+ * parent for a reply). A full quota fails with `quota_exceeded` and a
30
+ * `retryAfter` of when its oldest post leaves the window.
31
+ */
32
+ export declare class ThreadsFeed implements FeedChannel {
33
+ readonly type = "threads";
34
+ readonly limits: FeedLimits;
35
+ readonly account: ThreadsAccount;
36
+ private readonly timeout;
37
+ private readonly delays;
38
+ private readonly checkQuota;
39
+ /** The posts this feed published, by id, with the key each was published under. */
40
+ private readonly published;
41
+ /** The post each key was published as, so a retry in this process needs no lookup. */
42
+ private readonly byKey;
43
+ constructor(config: ThreadsFeedConfig);
44
+ measure(text: string): number;
45
+ connect(): Promise<void>;
46
+ /** A key of the time it is made and a random part, see `FeedPublishOptions`. */
47
+ idempotencyKey(): string;
48
+ publish(post: FeedPost, options?: FeedPublishOptions): Promise<PublishedPost>;
49
+ delete(id: string): Promise<void>;
50
+ close(): Promise<void>;
51
+ /** Checks the post against Threads' rules and returns its topic tag. */
52
+ private validate;
53
+ private assertQuota;
54
+ /**
55
+ * How long until the oldest post (or reply) inside the quota window of
56
+ * `window` seconds leaves it, plus a margin. An hour when that cannot be
57
+ * found out.
58
+ */
59
+ private quotaFreesIn;
60
+ /**
61
+ * The post an earlier attempt with the same key made: one of the account's
62
+ * posts (or replies to `post.replyTo`) since `since`, less the clock skew,
63
+ * that is the same post and that this feed did not publish under another
64
+ * key. Two posts the same in all Threads lists, published by other
65
+ * processes within that minute, cannot be told apart.
66
+ */
67
+ private findEarlier;
68
+ /** Keeps a post this feed published, under the key it was published with. */
69
+ private remember;
70
+ private ready;
71
+ }
72
+ /** A Threads feed. */
73
+ export declare function threadsFeed(config: ThreadsFeedConfig): ThreadsFeed;
74
+ //# sourceMappingURL=feed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../../../src/channels/threads/feed.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAoB,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAG7F,MAAM,MAAM,iBAAiB,GAAG,CAAC;IAAE,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG,oBAAoB,CAAC,GAAG;IACrF,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,mCAAmC;AACnC,eAAO,MAAM,cAAc,EAAE,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAOtF,CAAC;AA+IF;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAY,YAAW,WAAW;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAkB;IAC7C,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyC;IACnE,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;gBAE9C,MAAM,EAAE,iBAAiB;IASrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,gFAAgF;IAChF,cAAc,IAAI,MAAM;IAIlB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,aAAa,CAAC;IA+FjF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,wEAAwE;IACxE,OAAO,CAAC,QAAQ;YA2CF,WAAW;IAqBzB;;;;OAIG;YACW,YAAY;IA+B1B;;;;;;OAMG;YACW,WAAW;IAoCzB,6EAA6E;IAC7E,OAAO,CAAC,QAAQ;YAUF,KAAK;CA2BpB;AAED,sBAAsB;AACtB,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAElE"}
@@ -0,0 +1,372 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { assertImage } from '../../feeds/attachments.js';
3
+ import { FeedError } from '../../feeds/errors.js';
4
+ import { threadsLength } from '../../feeds/text.js';
5
+ import { threadsAccountOf } from './account.js';
6
+ /** What a Threads post accepts. */
7
+ export const THREADS_LIMITS = {
8
+ maxLength: 500,
9
+ maxImages: 20,
10
+ maxImageBytes: 8_000_000,
11
+ maxLinks: 5,
12
+ maxTags: 1,
13
+ maxTopicTagLength: 50,
14
+ };
15
+ const URL_PATTERN = /https?:\/\/[^\s<>"']+/giu;
16
+ const KEY_PATTERN = /^(\d{1,15})\.([\w-]{8,64})$/u;
17
+ const HOUR = 60 * 60_000;
18
+ /** How much earlier than its key a lookup starts, for clocks that disagree. */
19
+ const CLOCK_SKEW = 60_000;
20
+ /** How long after the oldest post leaves the quota window a retry waits. */
21
+ const QUOTA_MARGIN = 60_000;
22
+ const QUOTA_WINDOW = 86_400;
23
+ const LOOKUP_PAGES = 4;
24
+ const MAX_PUBLISHED = 1000;
25
+ const QUOTA_PAGES = 12;
26
+ const LOOKUP_FIELDS = 'id,text,timestamp,permalink,media_type,link_attachment_url,topic_tag,alt_text';
27
+ /** A URL as compared with the one Threads lists: no trailing slashes. */
28
+ function comparableUrl(url) {
29
+ let end = url.length;
30
+ while (end > 0 && url.charCodeAt(end - 1) === 47)
31
+ end--;
32
+ return url.slice(0, end);
33
+ }
34
+ /** The media type Threads lists for `post`. */
35
+ function mediaTypeOf(post) {
36
+ const images = post.images?.length ?? 0;
37
+ return images === 0 ? 'TEXT_POST' : images === 1 ? 'IMAGE' : 'CAROUSEL_ALBUM';
38
+ }
39
+ /**
40
+ * Whether a listed post is `post` as published: the same kind, text, link
41
+ * card, topic tag, alt text of a single image and the post it answers.
42
+ * Anything missing or different makes it another post, since posting twice
43
+ * shows, while a post taken for another one is lost without a trace.
44
+ */
45
+ function samePost(listed, post, topicTag) {
46
+ const images = post.images ?? [];
47
+ const alt = images.length === 1 ? (images[0]?.alt ?? '') : '';
48
+ const link = post.link ? comparableUrl(post.link.url) : '';
49
+ return (listed.media_type === mediaTypeOf(post) &&
50
+ normalized(listed.text) === normalized(post.text) &&
51
+ listed.replied_to?.id === post.replyTo &&
52
+ (listed.link_attachment_url ? comparableUrl(listed.link_attachment_url) : '') === link &&
53
+ (listed.topic_tag ?? '').toLowerCase() === (topicTag ?? '').toLowerCase() &&
54
+ (images.length === 1 ? (listed.alt_text ?? '') === alt : true));
55
+ }
56
+ /**
57
+ * Whether `post` can be told apart from other posts by what Threads lists:
58
+ * its text, or the alt text of its single image. Images themselves are not
59
+ * listed in a comparable form, so posts without either are never looked up.
60
+ */
61
+ function identifiable(post) {
62
+ const images = post.images ?? [];
63
+ return normalized(post.text) !== '' || (images.length === 1 && Boolean(images[0]?.alt?.trim()));
64
+ }
65
+ const TRAILING_PUNCTUATION = new Set(['.', ',', ';', ':', '!', '?', ')']);
66
+ /** `text` without the run of `characters` it ends with, in linear time. */
67
+ function withoutTrailing(text, characters) {
68
+ let end = text.length;
69
+ while (end > 0 && characters.has(text.charAt(end - 1)))
70
+ end--;
71
+ return text.slice(0, end);
72
+ }
73
+ /** Text as compared between a post and the one Threads lists: trimmed, whitespace runs as one space. */
74
+ function normalized(text) {
75
+ return (text ?? '').trim().replace(/\s+/gu, ' ');
76
+ }
77
+ function dateOf(timestamp) {
78
+ const time = timestamp === undefined ? Number.NaN : Date.parse(timestamp);
79
+ return Number.isFinite(time) ? new Date(time) : undefined;
80
+ }
81
+ /** When a key from `ThreadsFeed.idempotencyKey()` was made, in ms since the epoch. */
82
+ function keyTime(key) {
83
+ const match = KEY_PATTERN.exec(key);
84
+ const time = match ? Number(match[1]) : Number.NaN;
85
+ if (!Number.isSafeInteger(time)) {
86
+ throw new FeedError('threads', 'invalid_post', `"${key}" is not an idempotency key from ThreadsFeed.idempotencyKey()`);
87
+ }
88
+ return time;
89
+ }
90
+ function abortError(signal) {
91
+ return signal.reason instanceof Error ? signal.reason : new Error('The publication was aborted');
92
+ }
93
+ function sleep(ms, signal) {
94
+ return new Promise((resolve, reject) => {
95
+ if (signal?.aborted)
96
+ return reject(abortError(signal));
97
+ const timer = setTimeout(resolve, ms);
98
+ signal?.addEventListener('abort', () => {
99
+ clearTimeout(timer);
100
+ reject(abortError(signal));
101
+ }, { once: true });
102
+ });
103
+ }
104
+ /**
105
+ * Publishes posts to a Threads account in the two steps Threads asks for: a
106
+ * media container, then its publication once Threads says it is ready.
107
+ * Text posts carry a link card and a topic tag. One image or a carousel of
108
+ * up to twenty is published from public URLs, since Threads fetches images
109
+ * itself. Replies go under the post they answer. The account's token is
110
+ * renewed before it expires.
111
+ *
112
+ * Threads takes no client-chosen ids, so a publication with an idempotency
113
+ * key first looks for a post an earlier attempt made: one of the account's
114
+ * posts (or replies) since the key was made, with the same text (and the same
115
+ * parent for a reply). A full quota fails with `quota_exceeded` and a
116
+ * `retryAfter` of when its oldest post leaves the window.
117
+ */
118
+ export class ThreadsFeed {
119
+ type = 'threads';
120
+ limits = THREADS_LIMITS;
121
+ account;
122
+ timeout;
123
+ delays;
124
+ checkQuota;
125
+ /** The posts this feed published, by id, with the key each was published under. */
126
+ published = new Map();
127
+ /** The post each key was published as, so a retry in this process needs no lookup. */
128
+ byKey = new Map();
129
+ constructor(config) {
130
+ this.account = threadsAccountOf(config);
131
+ this.timeout = config.containerTimeout ?? 5 * 60_000;
132
+ this.delays = config.pollDelays?.length
133
+ ? config.pollDelays
134
+ : [1000, 2000, 4000, 8000, 15000, 30000];
135
+ this.checkQuota = config.checkQuota ?? true;
136
+ }
137
+ measure(text) {
138
+ return threadsLength(text);
139
+ }
140
+ async connect() {
141
+ await this.account.connect(this);
142
+ }
143
+ /** A key of the time it is made and a random part, see `FeedPublishOptions`. */
144
+ idempotencyKey() {
145
+ return `${Date.now()}.${nanoid()}`;
146
+ }
147
+ async publish(post, options = {}) {
148
+ const topicTag = this.validate(post);
149
+ const { signal, idempotencyKey, retry = true } = options;
150
+ if (idempotencyKey !== undefined) {
151
+ const since = keyTime(idempotencyKey);
152
+ const known = this.byKey.get(idempotencyKey);
153
+ if (known)
154
+ return known;
155
+ if (retry && identifiable(post)) {
156
+ const earlier = await this.findEarlier(post, topicTag, idempotencyKey, since, signal);
157
+ if (earlier)
158
+ return earlier;
159
+ }
160
+ }
161
+ if (this.checkQuota)
162
+ await this.assertQuota(Boolean(post.replyTo), signal);
163
+ const user = this.account.userId;
164
+ const shared = {
165
+ ...(topicTag && { topic_tag: topicTag }),
166
+ ...(post.replyTo && { reply_to_id: post.replyTo }),
167
+ };
168
+ const images = post.images ?? [];
169
+ let container;
170
+ if (images.length === 0) {
171
+ container = await this.account.post(`${user}/threads`, {
172
+ media_type: 'TEXT',
173
+ text: post.text,
174
+ ...(post.link && { link_attachment: post.link.url }),
175
+ ...shared,
176
+ }, signal);
177
+ }
178
+ else if (images.length === 1) {
179
+ container = await this.account.post(`${user}/threads`, {
180
+ media_type: 'IMAGE',
181
+ image_url: images[0].image.url,
182
+ ...(post.text && { text: post.text }),
183
+ ...(images[0].alt && { alt_text: images[0].alt }),
184
+ ...shared,
185
+ }, signal);
186
+ }
187
+ else {
188
+ const children = [];
189
+ for (const image of images) {
190
+ const item = await this.account.post(`${user}/threads`, {
191
+ media_type: 'IMAGE',
192
+ image_url: image.image.url,
193
+ is_carousel_item: true,
194
+ ...(image.alt && { alt_text: image.alt }),
195
+ }, signal);
196
+ await this.ready(item.id, signal);
197
+ children.push(item.id);
198
+ }
199
+ container = await this.account.post(`${user}/threads`, {
200
+ media_type: 'CAROUSEL',
201
+ children: children.join(','),
202
+ ...(post.text && { text: post.text }),
203
+ ...shared,
204
+ }, signal);
205
+ }
206
+ await this.ready(container.id, signal);
207
+ const published = await this.account.post(`${user}/threads_publish`, { creation_id: container.id }, signal);
208
+ let url = '';
209
+ try {
210
+ const details = await this.account.get(published.id, {
211
+ fields: 'permalink',
212
+ });
213
+ url = details.permalink ?? '';
214
+ }
215
+ catch (error) {
216
+ console.warn(`[threads] Published ${published.id} but could not read its permalink:`, error);
217
+ }
218
+ const result = {
219
+ feed: this.type,
220
+ id: published.id,
221
+ url,
222
+ publishedAt: new Date(),
223
+ };
224
+ this.remember(result, idempotencyKey);
225
+ return result;
226
+ }
227
+ async delete(id) {
228
+ await this.account.delete(id);
229
+ }
230
+ async close() {
231
+ this.account.close(this);
232
+ }
233
+ /** Checks the post against Threads' rules and returns its topic tag. */
234
+ validate(post) {
235
+ const invalid = (message) => new FeedError('threads', 'invalid_post', message);
236
+ const length = threadsLength(post.text);
237
+ if (length > THREADS_LIMITS.maxLength) {
238
+ throw invalid(`The text is ${length} long, Threads takes ${THREADS_LIMITS.maxLength}`);
239
+ }
240
+ const images = post.images ?? [];
241
+ if (images.length > 0 && post.link) {
242
+ throw invalid('Threads shows either images or a link card: put the link in the text');
243
+ }
244
+ if (images.length > THREADS_LIMITS.maxImages) {
245
+ throw invalid(`The post has ${images.length} images, Threads takes ${THREADS_LIMITS.maxImages}`);
246
+ }
247
+ if (images.length === 0 && !post.text.trim())
248
+ throw invalid('The post is empty');
249
+ for (const [index, image] of images.entries()) {
250
+ assertImage('threads', image.image, `Image ${index + 1}`);
251
+ if (!image.image.url) {
252
+ throw invalid(`Image ${index + 1} has no url: Threads fetches images itself, so each needs a public URL`);
253
+ }
254
+ }
255
+ const links = new Set([...post.text.matchAll(URL_PATTERN), ...(post.link ? [[post.link.url]] : [])].map((match) => withoutTrailing(match[0], TRAILING_PUNCTUATION)));
256
+ if (links.size > THREADS_LIMITS.maxLinks) {
257
+ throw invalid(`The post has ${links.size} links, Threads takes ${THREADS_LIMITS.maxLinks}`);
258
+ }
259
+ const tag = post.tags?.[0]?.replace(/^#/, '');
260
+ if (tag === undefined)
261
+ return undefined;
262
+ if (tag.length < 1 || threadsLength(tag) > THREADS_LIMITS.maxTopicTagLength) {
263
+ throw invalid(`The topic tag "${tag}" must be 1 to ${THREADS_LIMITS.maxTopicTagLength} characters`);
264
+ }
265
+ if (/[.&]/u.test(tag))
266
+ throw invalid(`The topic tag "${tag}" cannot hold "." or "&"`);
267
+ return tag;
268
+ }
269
+ async assertQuota(reply, signal) {
270
+ const limit = await this.account.get(`${this.account.userId}/threads_publishing_limit`, { fields: 'quota_usage,config,reply_quota_usage,reply_config' }, signal);
271
+ const entry = limit.data?.[0];
272
+ const usage = reply ? entry?.reply_quota_usage : entry?.quota_usage;
273
+ const config = reply ? entry?.reply_config : entry?.config;
274
+ const total = config?.quota_total;
275
+ if (usage === undefined || total === undefined || usage < total)
276
+ return;
277
+ const window = config?.quota_duration ?? QUOTA_WINDOW;
278
+ const retryAfter = await this.quotaFreesIn(reply, window, signal);
279
+ throw new FeedError('threads', 'quota_exceeded', `The account used its ${total} ${reply ? 'replies' : 'posts'} for the last ${Math.round(window / 3600)} hours, one frees up in about ${Math.ceil(retryAfter / 60_000)} min`, { retryAfter });
280
+ }
281
+ /**
282
+ * How long until the oldest post (or reply) inside the quota window of
283
+ * `window` seconds leaves it, plus a margin. An hour when that cannot be
284
+ * found out.
285
+ */
286
+ async quotaFreesIn(reply, window, signal) {
287
+ const now = Date.now();
288
+ const start = now - window * 1000;
289
+ let oldest;
290
+ try {
291
+ const pages = this.account.pages(`${this.account.userId}/${reply ? 'replies' : 'threads'}`, { since: Math.floor(start / 1000), fields: 'timestamp', limit: 100 }, QUOTA_PAGES, signal);
292
+ for await (const posts of pages) {
293
+ for (const listed of posts) {
294
+ const time = dateOf(listed.timestamp)?.getTime();
295
+ if (time !== undefined && time >= start && (oldest === undefined || time < oldest)) {
296
+ oldest = time;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ catch (error) {
302
+ if (signal?.aborted)
303
+ throw error;
304
+ return HOUR;
305
+ }
306
+ if (oldest === undefined)
307
+ return HOUR;
308
+ return Math.max(0, oldest + window * 1000 - now) + QUOTA_MARGIN;
309
+ }
310
+ /**
311
+ * The post an earlier attempt with the same key made: one of the account's
312
+ * posts (or replies to `post.replyTo`) since `since`, less the clock skew,
313
+ * that is the same post and that this feed did not publish under another
314
+ * key. Two posts the same in all Threads lists, published by other
315
+ * processes within that minute, cannot be told apart.
316
+ */
317
+ async findEarlier(post, topicTag, key, since, signal) {
318
+ const parent = post.replyTo;
319
+ const pages = this.account.pages(`${this.account.userId}/${parent ? 'replies' : 'threads'}`, {
320
+ since: Math.floor((since - CLOCK_SKEW) / 1000),
321
+ fields: `${LOOKUP_FIELDS}${parent ? ',replied_to' : ''}`,
322
+ limit: 50,
323
+ }, LOOKUP_PAGES, signal);
324
+ for await (const posts of pages) {
325
+ const match = posts.find((listed) => (!this.published.has(listed.id) || this.published.get(listed.id) === key) &&
326
+ samePost(listed, post, topicTag));
327
+ if (match) {
328
+ return {
329
+ feed: this.type,
330
+ id: match.id,
331
+ url: match.permalink ?? '',
332
+ publishedAt: dateOf(match.timestamp) ?? new Date(),
333
+ };
334
+ }
335
+ }
336
+ return undefined;
337
+ }
338
+ /** Keeps a post this feed published, under the key it was published with. */
339
+ remember(post, key) {
340
+ this.published.set(post.id, key);
341
+ if (key !== undefined)
342
+ this.byKey.set(key, post);
343
+ if (this.published.size > MAX_PUBLISHED) {
344
+ const [oldest, oldestKey] = this.published.entries().next().value ?? [];
345
+ if (oldest !== undefined)
346
+ this.published.delete(oldest);
347
+ if (oldestKey !== undefined)
348
+ this.byKey.delete(oldestKey);
349
+ }
350
+ }
351
+ async ready(id, signal) {
352
+ const deadline = Date.now() + this.timeout;
353
+ for (let attempt = 0;; attempt++) {
354
+ const { status, error_message } = await this.account.get(id, { fields: 'status,error_message' }, signal);
355
+ if (status === 'FINISHED' || status === 'PUBLISHED')
356
+ return;
357
+ if (status === 'ERROR' || status === 'EXPIRED') {
358
+ throw new FeedError('threads', 'invalid_post', `Threads could not prepare the post (${status}${error_message ? `: ${error_message}` : ''})`);
359
+ }
360
+ const wait = this.delays[Math.min(attempt, this.delays.length - 1)];
361
+ if (Date.now() + wait > deadline) {
362
+ throw new FeedError('threads', 'unavailable', `Threads did not get the post ready within ${Math.round(this.timeout / 1000)} s`);
363
+ }
364
+ await sleep(wait, signal);
365
+ }
366
+ }
367
+ }
368
+ /** A Threads feed. */
369
+ export function threadsFeed(config) {
370
+ return new ThreadsFeed(config);
371
+ }
372
+ //# sourceMappingURL=feed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../../../src/channels/threads/feed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { ThreadsAccount, type ThreadsAccountConfig } from './account.js';
2
+ export { THREADS_API, ThreadsClient, threadsErrorCode, type ThreadsParams } from './client.js';
3
+ export { THREADS_LIMITS, ThreadsFeed, threadsFeed, type ThreadsFeedConfig } from './feed.js';
4
+ export { ThreadsTokenManager, type ThreadsTokenOptions } from './token.js';
5
+ export { ThreadsChannel, threadsChannel, verifyThreadsSignature, type ThreadsChannelConfig, type ThreadsWebhookConfig, type ThreadsWebhookRequest, type ThreadsWebhookResponse, } from './channel.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/threads/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC1F,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EACL,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC"}