@opencoredev/social-sdk 0.3.0 → 0.4.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.
- package/dist/cli-request.d.ts +15 -0
- package/dist/cli-request.js +193 -0
- package/dist/cli.d.ts +4 -3
- package/dist/cli.js +19 -21
- package/dist/cloud/common.d.ts +7 -6
- package/dist/cloud/common.js +35 -54
- package/dist/cloud/lifecycle.js +31 -35
- package/dist/cloud/media.d.ts +2 -2
- package/dist/cloud/media.js +13 -3
- package/dist/cloud/outcomes.d.ts +4 -3
- package/dist/cloud/outcomes.js +8 -15
- package/dist/cloud/post-for-me.js +41 -49
- package/dist/cloud/zernio.js +58 -98
- package/dist/core/client.js +79 -99
- package/dist/core/fields.d.ts +14 -0
- package/dist/core/fields.js +14 -0
- package/dist/core/idempotency.d.ts +7 -2
- package/dist/core/idempotency.js +37 -20
- package/dist/core/pagination.js +8 -7
- package/dist/core/types.d.ts +3 -2
- package/dist/platforms/bluesky.d.ts +65 -1
- package/dist/platforms/bluesky.js +675 -276
- package/dist/platforms/instagram.d.ts +2 -0
- package/dist/platforms/instagram.js +130 -105
- package/dist/platforms/linkedin.d.ts +58 -1
- package/dist/platforms/linkedin.js +877 -107
- package/dist/platforms/threads.d.ts +13 -1
- package/dist/platforms/threads.js +204 -302
- package/dist/platforms/tiktok.d.ts +4 -0
- package/dist/platforms/tiktok.js +140 -124
- package/dist/platforms/webhook-adapter.d.ts +9 -0
- package/dist/platforms/webhook-adapter.js +24 -0
- package/dist/platforms/x-engagement.js +7 -12
- package/dist/platforms/x-stream.d.ts +83 -0
- package/dist/platforms/x-stream.js +350 -0
- package/dist/platforms/x.d.ts +72 -0
- package/dist/platforms/x.js +328 -119
- package/dist/platforms/youtube-upload.d.ts +1 -1
- package/dist/platforms/youtube-upload.js +6 -2
- package/dist/platforms/youtube.d.ts +28 -4
- package/dist/platforms/youtube.js +291 -133
- package/dist/server/bluesky-oauth.d.ts +177 -0
- package/dist/server/bluesky-oauth.js +1229 -0
- package/dist/server/connections.d.ts +14 -0
- package/dist/server/connections.js +10 -2
- package/dist/server/egress.d.ts +14 -0
- package/dist/server/egress.js +115 -0
- package/dist/server/oauth-internal.d.ts +6 -0
- package/dist/server/oauth-internal.js +66 -0
- package/dist/server/oauth.d.ts +1 -1
- package/dist/server/oauth.js +46 -99
- package/dist/server/webhooks.d.ts +136 -3
- package/dist/server/webhooks.js +639 -25
- package/dist/testing/index.js +14 -28
- package/dist/transport/http.d.ts +1 -1
- package/dist/transport/http.js +0 -1
- package/dist/transport/json.d.ts +7 -0
- package/dist/transport/json.js +32 -4
- package/dist/transport/upload.d.ts +1 -1
- package/dist/transport/upload.js +46 -38
- package/dist/transport/validation.d.ts +16 -5
- package/dist/transport/validation.js +29 -7
- package/package.json +2 -2
package/dist/cloud/lifecycle.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SocialError } from "../core/errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { definedFields } from "../core/fields.js";
|
|
3
|
+
import { array, isString, object, string } from "../transport/validation.js";
|
|
3
4
|
import { accountMatches } from "./common.js";
|
|
4
5
|
const reject = (message) => {
|
|
5
6
|
throw new SocialError({ code: "invalid_input", operation: "posts.lifecycle", message });
|
|
@@ -28,8 +29,7 @@ export function managedLifecycle(provider, request, now) {
|
|
|
28
29
|
reject("This operation requires a backend record with exactly one destination.");
|
|
29
30
|
const entry = entries[0];
|
|
30
31
|
const rawId = provider === "zernio" ? entry["accountId"] : entry["id"];
|
|
31
|
-
|
|
32
|
-
const accountId = typeof rawId === "string" ? rawId : object(rawId)["_id"];
|
|
32
|
+
const accountId = isString(rawId) ? rawId : object(rawId)["_id"];
|
|
33
33
|
const platform = entry["platform"] === "twitter" ? "x" : entry["platform"];
|
|
34
34
|
if (accountId !== ref.accountId || platform !== ref.platform)
|
|
35
35
|
throw new SocialError({
|
|
@@ -46,13 +46,26 @@ export function managedLifecycle(provider, request, now) {
|
|
|
46
46
|
: response["message"] !== "Post deleted successfully")
|
|
47
47
|
unconfirmed();
|
|
48
48
|
}
|
|
49
|
+
async function removeFromPlatform(ref, context) {
|
|
50
|
+
if (!["x", "threads", "bluesky", "youtube", "linkedin", "facebook"].includes(ref.platform))
|
|
51
|
+
reject("Zernio does not support native removal for this platform.");
|
|
52
|
+
const id = string(ref.native?.["backendRecordId"]);
|
|
53
|
+
const record = await owned(ref, id, context);
|
|
54
|
+
const entry = object(array(record["platforms"])[0]);
|
|
55
|
+
if (entry["status"] !== "published" || entry["platformPostId"] !== ref.postId)
|
|
56
|
+
reject("The backend record does not identify this published native post.");
|
|
57
|
+
const result = object(await request(`${path(id)}/unpublish`, context, {
|
|
58
|
+
platform: ref.platform === "x" ? "twitter" : ref.platform,
|
|
59
|
+
}));
|
|
60
|
+
if (result["success"] !== true)
|
|
61
|
+
unconfirmed();
|
|
62
|
+
}
|
|
49
63
|
return {
|
|
50
64
|
async cancelScheduled(ref, context) {
|
|
51
65
|
const record = await owned(ref, ref.jobId, context);
|
|
52
66
|
const scheduledAt = record[provider === "zernio" ? "scheduledFor" : "scheduled_at"];
|
|
53
67
|
if (record["status"] !== "scheduled" ||
|
|
54
|
-
|
|
55
|
-
typeof scheduledAt !== "string" ||
|
|
68
|
+
!isString(scheduledAt) ||
|
|
56
69
|
!Number.isFinite(Date.parse(scheduledAt)) ||
|
|
57
70
|
Date.parse(scheduledAt) <= Date.parse(now()))
|
|
58
71
|
reject("Only a future scheduled record can be cancelled. Reconcile a due or dispatched post.");
|
|
@@ -61,23 +74,22 @@ export function managedLifecycle(provider, request, now) {
|
|
|
61
74
|
return { state: "cancelled", backendRecord: "deleted" };
|
|
62
75
|
}
|
|
63
76
|
// A missing/null scheduled_at means publish immediately. Keep the timestamp.
|
|
64
|
-
|
|
77
|
+
const copied = [
|
|
78
|
+
"media",
|
|
79
|
+
"platform_configurations",
|
|
80
|
+
"account_configurations",
|
|
81
|
+
"external_id",
|
|
82
|
+
].flatMap((key) => {
|
|
83
|
+
const value = record[key];
|
|
84
|
+
return value === undefined || value === null ? [] : [[key, value]];
|
|
85
|
+
});
|
|
65
86
|
const body = {
|
|
66
87
|
caption: string(record["caption"]),
|
|
67
88
|
social_accounts: [ref.accountId],
|
|
68
89
|
scheduled_at: string(scheduledAt),
|
|
69
90
|
isDraft: true,
|
|
91
|
+
...Object.fromEntries(copied),
|
|
70
92
|
};
|
|
71
|
-
for (const key of [
|
|
72
|
-
"media",
|
|
73
|
-
"platform_configurations",
|
|
74
|
-
"account_configurations",
|
|
75
|
-
"external_id",
|
|
76
|
-
]) {
|
|
77
|
-
if (record[key] !== undefined && record[key] !== null)
|
|
78
|
-
// oxlint-disable-next-line anti-slop/require-safety-comment-for-type-assertion -- provider payload is validated at this adapter boundary.
|
|
79
|
-
body[key] = record[key];
|
|
80
|
-
}
|
|
81
93
|
const result = object(await request(path(ref.jobId), context, body, {}, "PUT"));
|
|
82
94
|
if (result["id"] !== ref.jobId || result["status"] !== "draft")
|
|
83
95
|
unconfirmed();
|
|
@@ -89,24 +101,8 @@ export function managedLifecycle(provider, request, now) {
|
|
|
89
101
|
reject("Delete only a draft backend record. Cancel a future schedule explicitly; never infer native deletion.");
|
|
90
102
|
await deleteRecord(ref.recordId, context);
|
|
91
103
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
async removeFromPlatform(ref, context) {
|
|
96
|
-
if (!["x", "threads", "bluesky", "youtube", "linkedin", "facebook"].includes(ref.platform))
|
|
97
|
-
reject("Zernio does not support native removal for this platform.");
|
|
98
|
-
const id = string(ref.native?.["backendRecordId"]);
|
|
99
|
-
const record = await owned(ref, id, context);
|
|
100
|
-
const entry = object(array(record["platforms"])[0]);
|
|
101
|
-
if (entry["status"] !== "published" || entry["platformPostId"] !== ref.postId)
|
|
102
|
-
reject("The backend record does not identify this published native post.");
|
|
103
|
-
const result = object(await request(`${path(id)}/unpublish`, context, {
|
|
104
|
-
platform: ref.platform === "x" ? "twitter" : ref.platform,
|
|
105
|
-
}));
|
|
106
|
-
if (result["success"] !== true)
|
|
107
|
-
unconfirmed();
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
: {}),
|
|
104
|
+
...definedFields({
|
|
105
|
+
removeFromPlatform: provider === "zernio" ? removeFromPlatform : undefined,
|
|
106
|
+
}),
|
|
111
107
|
};
|
|
112
108
|
}
|
package/dist/cloud/media.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdapterOperationContext, ConnectedAccountRef, JsonObject, MediaAttachment, MediaRef } from "../core/types.js";
|
|
1
|
+
import type { AdapterOperationContext, ConnectedAccountRef, JsonObject, JsonValue, MediaAttachment, MediaRef } from "../core/types.js";
|
|
2
2
|
import { type ManagedOptions } from "./common.js";
|
|
3
3
|
/** Keep this record server-side. A provider storage URL may grant access to the asset. */
|
|
4
4
|
export interface ManagedMediaRecord {
|
|
@@ -17,7 +17,7 @@ export declare class MemoryManagedMediaStore implements ManagedMediaStore {
|
|
|
17
17
|
put(record: ManagedMediaRecord): Promise<void>;
|
|
18
18
|
get(mediaId: string): Promise<ManagedMediaRecord | undefined>;
|
|
19
19
|
}
|
|
20
|
-
export declare function managedMedia(provider: "zernio" | "post-for-me", options: ManagedOptions, presign: (body: JsonObject, context: AdapterOperationContext) => Promise<
|
|
20
|
+
export declare function managedMedia(provider: "zernio" | "post-for-me", options: ManagedOptions, presign: (body: JsonObject, context: AdapterOperationContext) => Promise<JsonValue>): {
|
|
21
21
|
resolve: (item: MediaAttachment, account: ConnectedAccountRef, context: AdapterOperationContext) => Promise<string>;
|
|
22
22
|
upload(item: MediaAttachment, account: ConnectedAccountRef, context: AdapterOperationContext): Promise<MediaRef>;
|
|
23
23
|
};
|
package/dist/cloud/media.js
CHANGED
|
@@ -10,12 +10,16 @@ export class MemoryManagedMediaStore {
|
|
|
10
10
|
return record ? structuredClone(record) : undefined;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
export function managedMedia(provider, options,
|
|
14
|
-
// oxlint-disable-next-line anti-slop/no-unknown-returns -- provider payload is validated at this adapter boundary.
|
|
15
|
-
presign) {
|
|
13
|
+
export function managedMedia(provider, options, presign) {
|
|
16
14
|
const store = options.mediaStore ?? new MemoryManagedMediaStore();
|
|
17
15
|
async function resolve(item, account, context) {
|
|
18
16
|
accountMatches(account, context);
|
|
17
|
+
if (item.kind === "document")
|
|
18
|
+
throw new SocialError({
|
|
19
|
+
code: "unsupported_capability",
|
|
20
|
+
operation: "media.resolve",
|
|
21
|
+
message: "Managed backends accept image and video media only through this adapter.",
|
|
22
|
+
});
|
|
19
23
|
if (item.source.kind !== "media-ref")
|
|
20
24
|
return uploadManagedMedia(item, (body) => presign(body, context), {
|
|
21
25
|
options,
|
|
@@ -61,6 +65,12 @@ presign) {
|
|
|
61
65
|
resolve,
|
|
62
66
|
async upload(item, account, context) {
|
|
63
67
|
accountMatches(account, context);
|
|
68
|
+
if (item.kind === "document")
|
|
69
|
+
throw new SocialError({
|
|
70
|
+
code: "unsupported_capability",
|
|
71
|
+
operation: "media.upload",
|
|
72
|
+
message: "Managed backends accept image and video media only through this adapter.",
|
|
73
|
+
});
|
|
64
74
|
if (item.source.kind === "media-ref") {
|
|
65
75
|
await resolve(item, account, context);
|
|
66
76
|
return item.source.ref;
|
package/dist/cloud/outcomes.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ConnectedAccountRef, DeliveryOutcome } from "../core/types.js";
|
|
1
|
+
import type { ConnectedAccountRef, DeliveryOutcome, JsonValue } from "../core/types.js";
|
|
2
|
+
import { type JsonField } from "../transport/validation.js";
|
|
2
3
|
export interface OutcomeContext {
|
|
3
4
|
account: ConnectedAccountRef;
|
|
4
5
|
targetIndex: number;
|
|
5
6
|
observedAt: string;
|
|
6
7
|
}
|
|
7
8
|
/** Never infer destination success from the aggregate HTTP/parent status. */
|
|
8
|
-
export declare function zernioOutcome(value:
|
|
9
|
-
export declare function postForMeOutcome(parentValue:
|
|
9
|
+
export declare function zernioOutcome(value: JsonValue, context: OutcomeContext): DeliveryOutcome;
|
|
10
|
+
export declare function postForMeOutcome(parentValue: JsonValue, resultsValue: JsonField, context: OutcomeContext): DeliveryOutcome;
|
package/dist/cloud/outcomes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { array, object, optionalString, string } from "../transport/validation.js";
|
|
1
|
+
import { array, isJsonArray, isJsonObject, isString, object, optionalString, string, } from "../transport/validation.js";
|
|
2
2
|
function delivery(context, deliveryId) {
|
|
3
3
|
return {
|
|
4
4
|
kind: "delivery",
|
|
@@ -10,7 +10,6 @@ function delivery(context, deliveryId) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
/** Never infer destination success from the aggregate HTTP/parent status. */
|
|
13
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- provider payload is validated at this adapter boundary.
|
|
14
13
|
export function zernioOutcome(value, context) {
|
|
15
14
|
const response = object(value);
|
|
16
15
|
const post = object(response["post"] ?? response["existingPost"] ?? response);
|
|
@@ -18,14 +17,12 @@ export function zernioOutcome(value, context) {
|
|
|
18
17
|
const platform = context.account.platform === "x" ? "twitter" : context.account.platform;
|
|
19
18
|
const entries = array(post["platforms"]).map(object);
|
|
20
19
|
const matches = entries.filter((entry) => {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
? optionalString(object(entry["accountId"])["_id"])
|
|
28
|
-
: undefined;
|
|
20
|
+
const rawAccountId = entry["accountId"];
|
|
21
|
+
const accountId = isString(rawAccountId)
|
|
22
|
+
? rawAccountId
|
|
23
|
+
: isJsonObject(rawAccountId) || isJsonArray(rawAccountId)
|
|
24
|
+
? optionalString(object(rawAccountId)["_id"])
|
|
25
|
+
: undefined;
|
|
29
26
|
return entry["platform"] === platform && accountId === context.account.accountId;
|
|
30
27
|
});
|
|
31
28
|
const base = { ...context, delivery: delivery(context, postId) };
|
|
@@ -104,11 +101,7 @@ export function zernioOutcome(value, context) {
|
|
|
104
101
|
};
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
|
-
export function postForMeOutcome(
|
|
108
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- provider payload is validated at this adapter boundary.
|
|
109
|
-
parentValue,
|
|
110
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- provider payload is validated at this adapter boundary.
|
|
111
|
-
resultsValue, context) {
|
|
104
|
+
export function postForMeOutcome(parentValue, resultsValue, context) {
|
|
112
105
|
const parent = object(parentValue);
|
|
113
106
|
const postId = string(parent["id"]);
|
|
114
107
|
const backendState = optionalString(parent["status"]) ?? "unknown";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* oxlint-disable anti-slop/require-safety-comment-for-type-assertion -- validated external boundary or fixture contract. */
|
|
2
1
|
import { managedLifecycle } from "./lifecycle.js";
|
|
3
2
|
import { managedMedia } from "./media.js";
|
|
4
3
|
export { MemoryManagedMediaStore, } from "./media.js";
|
|
5
4
|
import { defineAdapter } from "../core/adapter.js";
|
|
6
5
|
import { SocialError } from "../core/errors.js";
|
|
7
|
-
import {
|
|
6
|
+
import { definedFields } from "../core/fields.js";
|
|
7
|
+
import { array, isBoolean, object, optionalNumber, optionalString, string, } from "../transport/validation.js";
|
|
8
8
|
import { verifyPostForMeWebhook, decodeWebhook } from "../server/webhooks.js";
|
|
9
9
|
import { accountMatches, capabilityManifest, managedHttp, managedPreparation, managedOptionIssues, optionsObject, platform, publicFields, } from "./common.js";
|
|
10
10
|
import { postForMeOutcome } from "./outcomes.js";
|
|
@@ -12,7 +12,6 @@ export function postForMe(options) {
|
|
|
12
12
|
const request = managedHttp("https://api.postforme.dev", options);
|
|
13
13
|
const mediaPipeline = managedMedia("post-for-me", options, (body, context) => request("/v1/media/create-upload-url", context, body));
|
|
14
14
|
const now = () => (options.clock?.() ?? new Date()).toISOString();
|
|
15
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- provider payload is validated at this adapter boundary.
|
|
16
15
|
const account = (value, backend) => {
|
|
17
16
|
const row = object(value);
|
|
18
17
|
const handle = optionalString(row["username"]);
|
|
@@ -25,8 +24,7 @@ export function postForMe(options) {
|
|
|
25
24
|
accountId: string(row["id"]),
|
|
26
25
|
},
|
|
27
26
|
displayName: handle ?? string(row["user_id"]),
|
|
28
|
-
|
|
29
|
-
...(handle ? { handle } : {}),
|
|
27
|
+
...definedFields({ handle: handle || undefined }),
|
|
30
28
|
status: row["status"] === "connected"
|
|
31
29
|
? "connected"
|
|
32
30
|
: row["status"] === "disconnected"
|
|
@@ -34,7 +32,6 @@ export function postForMe(options) {
|
|
|
34
32
|
: "unknown",
|
|
35
33
|
};
|
|
36
34
|
};
|
|
37
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- validated by account at this boundary.
|
|
38
35
|
const supportedAccount = (value, backend) => {
|
|
39
36
|
try {
|
|
40
37
|
return account(value, backend);
|
|
@@ -47,9 +44,11 @@ export function postForMe(options) {
|
|
|
47
44
|
};
|
|
48
45
|
const feed = async (ref, context, metrics) => {
|
|
49
46
|
accountMatches(ref, context);
|
|
50
|
-
const result = object(await request(`/v1/social-account-feeds/${encodeURIComponent(ref.accountId)}`, context, undefined,
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
const result = object(await request(`/v1/social-account-feeds/${encodeURIComponent(ref.accountId)}`, context, undefined, {
|
|
48
|
+
platform_post_id: ref.postId,
|
|
49
|
+
limit: "1",
|
|
50
|
+
...definedFields({ expand: metrics ? "metrics" : undefined }),
|
|
51
|
+
}));
|
|
53
52
|
const row = array(result["data"])
|
|
54
53
|
.map(object)
|
|
55
54
|
.find((item) => item["platform_post_id"] === ref.postId && item["social_account_id"] === ref.accountId);
|
|
@@ -102,8 +101,7 @@ export function postForMe(options) {
|
|
|
102
101
|
const parsed = supportedAccount(value, context.backendInstance);
|
|
103
102
|
return parsed ? [parsed] : [];
|
|
104
103
|
}),
|
|
105
|
-
|
|
106
|
-
...(next ? { nextCursor: next } : {}),
|
|
104
|
+
...definedFields({ nextCursor: next }),
|
|
107
105
|
};
|
|
108
106
|
},
|
|
109
107
|
async get(ref, context) {
|
|
@@ -131,8 +129,7 @@ export function postForMe(options) {
|
|
|
131
129
|
});
|
|
132
130
|
const result = object(await request(`/v1/social-account-feeds/${encodeURIComponent(account.accountId)}`, context, undefined, {
|
|
133
131
|
limit: String(limit),
|
|
134
|
-
|
|
135
|
-
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
132
|
+
...definedFields({ cursor: input.cursor }),
|
|
136
133
|
}));
|
|
137
134
|
const rows = array(result["data"])
|
|
138
135
|
.map(object)
|
|
@@ -159,14 +156,7 @@ export function postForMe(options) {
|
|
|
159
156
|
: optionalString(meta["next"]) && cursor
|
|
160
157
|
? cursor
|
|
161
158
|
: undefined;
|
|
162
|
-
|
|
163
|
-
// oxlint-disable-next-line anti-slop/require-safety-comment-for-type-assertion -- provider payload is validated at this adapter boundary.
|
|
164
|
-
// oxlint-disable-next-line anti-slop/no-conditional-empty-object-spread -- validated external boundary or fixture contract.
|
|
165
|
-
// oxlint-disable-next-line anti-slop/require-safety-comment-for-type-assertion -- validated external boundary or fixture contract.
|
|
166
|
-
// oxlint-disable-next-line anti-slop/no-conditional-empty-object-spread -- validated external boundary or fixture contract.
|
|
167
|
-
// oxlint-disable-next-line anti-slop/require-safety-comment-for-type-assertion -- validated external boundary or fixture contract.
|
|
168
|
-
// oxlint-disable-next-line anti-slop/no-conditional-empty-object-spread -- validated external boundary or fixture contract.
|
|
169
|
-
return { items: rows, ...(next === undefined ? {} : { nextCursor: next }) };
|
|
159
|
+
return { items: rows, ...definedFields({ nextCursor: next }) };
|
|
170
160
|
},
|
|
171
161
|
prepareTarget(target) {
|
|
172
162
|
const issues = [
|
|
@@ -200,9 +190,9 @@ export function postForMe(options) {
|
|
|
200
190
|
media.push({ url: await mediaPipeline.resolve(item, target.account, context) });
|
|
201
191
|
const config = optionsObject(target);
|
|
202
192
|
const platformConfig = {};
|
|
203
|
-
|
|
204
|
-
if (target.account.platform === "instagram" &&
|
|
205
|
-
platformConfig["share_to_feed"] =
|
|
193
|
+
const shareToFeed = config["shareToFeed"];
|
|
194
|
+
if (target.account.platform === "instagram" && isBoolean(shareToFeed))
|
|
195
|
+
platformConfig["share_to_feed"] = shareToFeed;
|
|
206
196
|
if (target.account.platform === "x" &&
|
|
207
197
|
config["replySettings"] !== undefined &&
|
|
208
198
|
config["replySettings"] !== "everyone")
|
|
@@ -210,9 +200,9 @@ export function postForMe(options) {
|
|
|
210
200
|
if (target.account.platform === "youtube") {
|
|
211
201
|
platformConfig["title"] = string(config["title"]);
|
|
212
202
|
platformConfig["privacy_status"] = string(config["visibility"]);
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
platformConfig["made_for_kids"] =
|
|
203
|
+
const madeForKids = config["madeForKids"];
|
|
204
|
+
if (isBoolean(madeForKids))
|
|
205
|
+
platformConfig["made_for_kids"] = madeForKids;
|
|
216
206
|
}
|
|
217
207
|
if (target.account.platform === "tiktok") {
|
|
218
208
|
platformConfig["privacy_status"] =
|
|
@@ -220,31 +210,34 @@ export function postForMe(options) {
|
|
|
220
210
|
platformConfig["auto_add_music"] = false;
|
|
221
211
|
platformConfig["allow_duet"] = !config["disableDuet"];
|
|
222
212
|
platformConfig["allow_stitch"] = !config["disableStitch"];
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
213
|
+
// prepareTarget requires these TikTok choices to be booleans. Skipping an
|
|
214
|
+
// absent value sends the same JSON body, since serialization drops undefined.
|
|
215
|
+
for (const [nativeKey, optionKey] of [
|
|
216
|
+
["disclose_your_brand", "ownBrand"],
|
|
217
|
+
["is_ai_generated", "aiGenerated"],
|
|
218
|
+
["is_draft", "draft"],
|
|
219
|
+
]) {
|
|
220
|
+
const value = config[optionKey];
|
|
221
|
+
if (value !== undefined)
|
|
222
|
+
platformConfig[nativeKey] = value;
|
|
223
|
+
}
|
|
224
|
+
const { disableComments, brandedContent } = config;
|
|
225
|
+
if (isBoolean(disableComments))
|
|
226
|
+
platformConfig["allow_comment"] = !disableComments;
|
|
227
|
+
if (isBoolean(brandedContent))
|
|
228
|
+
platformConfig["disclose_branded_content"] = brandedContent;
|
|
235
229
|
}
|
|
236
230
|
const response = await request("/v1/social-posts", context, {
|
|
237
231
|
caption: target.content.text ?? "",
|
|
238
232
|
social_accounts: [target.account.accountId],
|
|
239
233
|
media,
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
: {}),
|
|
234
|
+
...definedFields({
|
|
235
|
+
scheduled_at: target.schedule?.at,
|
|
236
|
+
external_id: context.targetIdempotencyKey || undefined,
|
|
237
|
+
platform_configurations: Object.keys(platformConfig).length
|
|
238
|
+
? { [target.account.platform]: platformConfig }
|
|
239
|
+
: undefined,
|
|
240
|
+
}),
|
|
248
241
|
});
|
|
249
242
|
const parent = object(response);
|
|
250
243
|
let results;
|
|
@@ -385,8 +378,7 @@ export function postForMe(options) {
|
|
|
385
378
|
platform: input.platform,
|
|
386
379
|
external_id: input.externalId,
|
|
387
380
|
permissions: input.permissions,
|
|
388
|
-
|
|
389
|
-
...(input.redirectUrl ? { redirect_url_override: input.redirectUrl } : {}),
|
|
381
|
+
...definedFields({ redirect_url_override: input.redirectUrl || undefined }),
|
|
390
382
|
}));
|
|
391
383
|
return { url: string(result["url"]), platform: string(result["platform"]) };
|
|
392
384
|
},
|