@codybrom/denim 1.3.3 → 1.3.4
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/deno.json +1 -1
- package/mod.ts +86 -83
- package/package.json +1 -1
package/deno.json
CHANGED
package/mod.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface ThreadsPostRequest {
|
|
|
40
40
|
* Creates a Threads media container.
|
|
41
41
|
*
|
|
42
42
|
* @param request - The ThreadsPostRequest object containing post details
|
|
43
|
-
* @returns A Promise that resolves to
|
|
43
|
+
* @returns A Promise that resolves to the container ID
|
|
44
44
|
* @throws Will throw an error if the API request fails
|
|
45
45
|
*
|
|
46
46
|
* @example
|
|
@@ -53,12 +53,12 @@ export interface ThreadsPostRequest {
|
|
|
53
53
|
* videoUrl: "https://example.com/video.mp4",
|
|
54
54
|
* altText: "A cool video"
|
|
55
55
|
* };
|
|
56
|
-
* const
|
|
56
|
+
* const containerId = await createThreadsContainer(request);
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
59
|
export async function createThreadsContainer(
|
|
60
60
|
request: ThreadsPostRequest
|
|
61
|
-
): Promise<
|
|
61
|
+
): Promise<string> {
|
|
62
62
|
// Input validation
|
|
63
63
|
validateRequest(request);
|
|
64
64
|
|
|
@@ -80,10 +80,12 @@ export async function createThreadsContainer(
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Handle media type specific parameters
|
|
83
|
-
if (request.mediaType === "
|
|
83
|
+
if (request.mediaType === "VIDEO") {
|
|
84
|
+
const videoItemId = await createVideoItemContainer(request);
|
|
85
|
+
body.set("media_type", "CAROUSEL");
|
|
86
|
+
body.append("children", videoItemId);
|
|
87
|
+
} else if (request.mediaType === "IMAGE" && request.imageUrl) {
|
|
84
88
|
body.append("image_url", request.imageUrl);
|
|
85
|
-
} else if (request.mediaType === "VIDEO" && request.videoUrl) {
|
|
86
|
-
body.append("video_url", request.videoUrl);
|
|
87
89
|
} else if (request.mediaType === "TEXT" && request.linkAttachment) {
|
|
88
90
|
body.append("link_attachment", request.linkAttachment);
|
|
89
91
|
} else if (request.mediaType === "CAROUSEL" && request.children) {
|
|
@@ -111,7 +113,7 @@ export async function createThreadsContainer(
|
|
|
111
113
|
|
|
112
114
|
try {
|
|
113
115
|
const data = JSON.parse(responseText);
|
|
114
|
-
return
|
|
116
|
+
return data.id;
|
|
115
117
|
} catch (error) {
|
|
116
118
|
console.error(`Failed to parse response JSON: ${error}`);
|
|
117
119
|
throw new Error(`Invalid response from Threads API: ${responseText}`);
|
|
@@ -164,6 +166,49 @@ function validateRequest(request: ThreadsPostRequest): void {
|
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Creates a video item container for Threads.
|
|
171
|
+
* @param request - The ThreadsPostRequest object containing video post details
|
|
172
|
+
* @returns A Promise that resolves to the video item container ID
|
|
173
|
+
* @throws Will throw an error if the API request fails
|
|
174
|
+
*/
|
|
175
|
+
async function createVideoItemContainer(
|
|
176
|
+
request: ThreadsPostRequest
|
|
177
|
+
): Promise<string> {
|
|
178
|
+
const url = `${THREADS_API_BASE_URL}/${request.userId}/threads`;
|
|
179
|
+
const body = new URLSearchParams({
|
|
180
|
+
access_token: request.accessToken,
|
|
181
|
+
is_carousel_item: "true",
|
|
182
|
+
media_type: "VIDEO",
|
|
183
|
+
video_url: request.videoUrl!,
|
|
184
|
+
...(request.altText && { alt_text: request.altText }),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const response = await fetch(url, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
body: body,
|
|
190
|
+
headers: {
|
|
191
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const responseText = await response.text();
|
|
196
|
+
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Failed to create video item container: ${response.statusText}. Details: ${responseText}`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const data = JSON.parse(responseText);
|
|
205
|
+
return data.id;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(`Failed to parse response JSON: ${error}`);
|
|
208
|
+
throw new Error(`Invalid response from Threads API: ${responseText}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
167
212
|
/**
|
|
168
213
|
* Creates a carousel item for a Threads carousel post.
|
|
169
214
|
*
|
|
@@ -272,52 +317,24 @@ async function checkContainerStatus(
|
|
|
272
317
|
*
|
|
273
318
|
* @param userId - The user ID of the Threads account
|
|
274
319
|
* @param accessToken - The access token for authentication
|
|
275
|
-
* @param containerId - The ID of the container to publish
|
|
276
|
-
* @param mediaType - The type of media being published
|
|
320
|
+
* @param containerId - The ID of the container to publish
|
|
277
321
|
* @returns A Promise that resolves to the published container ID
|
|
278
|
-
* @throws Will throw an error if the API request fails
|
|
322
|
+
* @throws Will throw an error if the API request fails or if publishing times out
|
|
279
323
|
*
|
|
280
324
|
* @example
|
|
281
325
|
* ```typescript
|
|
282
|
-
* const
|
|
283
|
-
* const publishedId = await publishThreadsContainer("123456", "your_access_token", parseInt(containerId, 10), mediaType);
|
|
326
|
+
* const publishedId = await publishThreadsContainer("123456", "your_access_token", "container_id");
|
|
284
327
|
* ```
|
|
285
328
|
*/
|
|
286
329
|
export async function publishThreadsContainer(
|
|
287
330
|
userId: string,
|
|
288
331
|
accessToken: string,
|
|
289
|
-
containerId:
|
|
290
|
-
|
|
291
|
-
): Promise<number> {
|
|
292
|
-
if (mediaType === "VIDEO") {
|
|
293
|
-
// Check container status before publishing for videos
|
|
294
|
-
let status = await checkContainerStatus(
|
|
295
|
-
containerId.toString(),
|
|
296
|
-
accessToken
|
|
297
|
-
);
|
|
298
|
-
let attempts = 0;
|
|
299
|
-
const maxAttempts = 10; // Increased from 5 to 10 for videos
|
|
300
|
-
|
|
301
|
-
while (status !== "FINISHED" && attempts < maxAttempts) {
|
|
302
|
-
await new Promise((resolve) => setTimeout(resolve, 30000)); // Wait for 30 seconds
|
|
303
|
-
status = await checkContainerStatus(containerId.toString(), accessToken);
|
|
304
|
-
attempts++;
|
|
305
|
-
console.log(
|
|
306
|
-
`Video container status check attempt ${attempts}: ${status}`
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (status !== "FINISHED") {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`Video container not ready after ${maxAttempts} attempts. Current status: ${status}`
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
332
|
+
containerId: string
|
|
333
|
+
): Promise<string> {
|
|
317
334
|
const publishUrl = `${THREADS_API_BASE_URL}/${userId}/threads_publish`;
|
|
318
335
|
const publishBody = new URLSearchParams({
|
|
319
336
|
access_token: accessToken,
|
|
320
|
-
creation_id: containerId
|
|
337
|
+
creation_id: containerId,
|
|
321
338
|
});
|
|
322
339
|
|
|
323
340
|
const publishResponse = await fetch(publishUrl, {
|
|
@@ -328,43 +345,35 @@ export async function publishThreadsContainer(
|
|
|
328
345
|
},
|
|
329
346
|
});
|
|
330
347
|
|
|
331
|
-
const responseText = await publishResponse.text();
|
|
332
|
-
|
|
333
348
|
if (!publishResponse.ok) {
|
|
334
|
-
console.error(`Publish response body: ${responseText}`);
|
|
335
349
|
throw new Error(
|
|
336
|
-
`Failed to publish Threads container: ${publishResponse.statusText}
|
|
350
|
+
`Failed to publish Threads container: ${publishResponse.statusText}`
|
|
337
351
|
);
|
|
338
352
|
}
|
|
339
353
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
accessToken
|
|
345
|
-
);
|
|
346
|
-
let attempts = 0;
|
|
347
|
-
const maxAttempts = 5;
|
|
348
|
-
|
|
349
|
-
while (
|
|
350
|
-
status !== "PUBLISHED" &&
|
|
351
|
-
status !== "FINISHED" &&
|
|
352
|
-
attempts < maxAttempts
|
|
353
|
-
) {
|
|
354
|
-
await new Promise((resolve) => setTimeout(resolve, 60000)); // Wait for 1 minute
|
|
355
|
-
status = await checkContainerStatus(containerId.toString(), accessToken);
|
|
356
|
-
attempts++;
|
|
357
|
-
}
|
|
354
|
+
// Check container status
|
|
355
|
+
let status = await checkContainerStatus(containerId, accessToken);
|
|
356
|
+
let attempts = 0;
|
|
357
|
+
const maxAttempts = 5;
|
|
358
358
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
while (
|
|
360
|
+
status !== "PUBLISHED" &&
|
|
361
|
+
status !== "FINISHED" &&
|
|
362
|
+
attempts < maxAttempts
|
|
363
|
+
) {
|
|
364
|
+
await new Promise((resolve) => setTimeout(resolve, 60000)); // Wait for 1 minute
|
|
365
|
+
status = await checkContainerStatus(containerId, accessToken);
|
|
366
|
+
attempts++;
|
|
367
|
+
}
|
|
362
368
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
369
|
+
if (status === "ERROR") {
|
|
370
|
+
throw new Error(`Failed to publish container. Error: ${status}`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (status !== "PUBLISHED" && status !== "FINISHED") {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Container not published after ${maxAttempts} attempts. Current status: ${status}`
|
|
376
|
+
);
|
|
368
377
|
}
|
|
369
378
|
|
|
370
379
|
return containerId; // Return the container ID as the published ID
|
|
@@ -408,24 +417,18 @@ export function serveRequests() {
|
|
|
408
417
|
}
|
|
409
418
|
|
|
410
419
|
// Create the Threads container
|
|
411
|
-
const
|
|
412
|
-
requestData
|
|
413
|
-
);
|
|
420
|
+
const containerId = await createThreadsContainer(requestData);
|
|
414
421
|
|
|
415
|
-
//
|
|
422
|
+
// Immediately attempt to publish the Threads container
|
|
416
423
|
const publishedId = await publishThreadsContainer(
|
|
417
424
|
requestData.userId,
|
|
418
425
|
requestData.accessToken,
|
|
419
|
-
containerId
|
|
420
|
-
mediaType
|
|
426
|
+
containerId
|
|
421
427
|
);
|
|
422
428
|
|
|
423
|
-
return new Response(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
headers: { "Content-Type": "application/json" },
|
|
427
|
-
}
|
|
428
|
-
);
|
|
429
|
+
return new Response(JSON.stringify({ success: true, publishedId }), {
|
|
430
|
+
headers: { "Content-Type": "application/json" },
|
|
431
|
+
});
|
|
429
432
|
} catch (error) {
|
|
430
433
|
console.error("Error posting to Threads:", error);
|
|
431
434
|
return new Response(
|