134dd063c3
- Implemented Dongchedi adapter for user detection and publishing. - Implemented Jianshu adapter for user detection and publishing. - Implemented Juejin adapter for user detection and publishing. - Implemented Qiehao adapter for user detection and publishing. - Implemented Smzdm adapter for user detection and publishing. - Implemented Sohuhao adapter for user detection and publishing. - Implemented Toutiaohao adapter for user detection and publishing. - Implemented Wangyihao adapter for user detection and publishing. - Implemented Weixin Gzh adapter for user detection and publishing. - Implemented Zol adapter for user detection and publishing. - Added documentation for manual testing of media publishing.
189 lines
5.5 KiB
TypeScript
189 lines
5.5 KiB
TypeScript
import type {
|
|
PublisherBindRequest,
|
|
PublisherBindResponse,
|
|
PublisherLocalPlatformState,
|
|
PublisherPluginPingResponse,
|
|
PublisherRegisterInstallationPayload,
|
|
PublisherRegisterInstallationResult,
|
|
PublisherPublishArticleRequest,
|
|
PublisherPublishResponse,
|
|
} from "@geo/shared-types";
|
|
|
|
type PluginAction = "ping" | "detectPlatforms" | "registerInstallation" | "bindAccount" | "publishArticle";
|
|
|
|
type PluginSuccessPayload<T> = {
|
|
source: "geo-extension";
|
|
type: "geo.publisher.response";
|
|
requestId: string;
|
|
success: true;
|
|
data: T;
|
|
};
|
|
|
|
type PluginErrorPayload = {
|
|
source: "geo-extension";
|
|
type: "geo.publisher.response";
|
|
requestId: string;
|
|
success: false;
|
|
error: string;
|
|
};
|
|
|
|
type PluginPayloadMap = {
|
|
ping: PublisherPluginPingResponse;
|
|
detectPlatforms: PublisherLocalPlatformState[];
|
|
registerInstallation: PublisherRegisterInstallationResult;
|
|
bindAccount: PublisherBindResponse;
|
|
publishArticle: PublisherPublishResponse;
|
|
};
|
|
|
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === "object" && value !== null;
|
|
}
|
|
|
|
function isPingPayload(value: unknown): value is PublisherPluginPingResponse {
|
|
return isPlainObject(value) && typeof value.installed === "boolean";
|
|
}
|
|
|
|
function isDetectPlatformsPayload(value: unknown): value is PublisherLocalPlatformState[] {
|
|
return Array.isArray(value);
|
|
}
|
|
|
|
function isRegisterInstallationPayload(value: unknown): value is PublisherRegisterInstallationResult {
|
|
return isPlainObject(value) && typeof value.success === "boolean" && typeof value.plugin_installation_id === "number";
|
|
}
|
|
|
|
function isBindPayload(value: unknown): value is PublisherBindResponse {
|
|
return isPlainObject(value) && typeof value.success === "boolean" && typeof value.platform_id === "string";
|
|
}
|
|
|
|
function isPublishPayload(value: unknown): value is PublisherPublishResponse {
|
|
return isPlainObject(value) && Array.isArray(value.results);
|
|
}
|
|
|
|
function isPayloadValid<T extends PluginAction>(action: T, value: unknown): value is PluginPayloadMap[T] {
|
|
switch (action) {
|
|
case "ping":
|
|
return isPingPayload(value);
|
|
case "detectPlatforms":
|
|
return isDetectPlatformsPayload(value);
|
|
case "registerInstallation":
|
|
return isRegisterInstallationPayload(value);
|
|
case "bindAccount":
|
|
return isBindPayload(value);
|
|
case "publishArticle":
|
|
return isPublishPayload(value);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function nextRequestId(): string {
|
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
return crypto.randomUUID();
|
|
}
|
|
return `geo-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
}
|
|
|
|
function sleep(ms: number): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
window.setTimeout(resolve, ms);
|
|
});
|
|
}
|
|
|
|
async function requestPlugin<T extends PluginAction>(
|
|
action: T,
|
|
payload?: T extends "bindAccount"
|
|
? PublisherBindRequest
|
|
: T extends "registerInstallation"
|
|
? PublisherRegisterInstallationPayload
|
|
: T extends "publishArticle"
|
|
? PublisherPublishArticleRequest
|
|
: undefined,
|
|
timeoutMs = 4000,
|
|
): Promise<PluginPayloadMap[T]> {
|
|
if (typeof window === "undefined") {
|
|
throw new Error("plugin bridge is only available in browser contexts");
|
|
}
|
|
|
|
const requestId = nextRequestId();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const timer = window.setTimeout(() => {
|
|
cleanup();
|
|
reject(new Error("publisher_plugin_timeout"));
|
|
}, timeoutMs);
|
|
|
|
const handleMessage = (event: MessageEvent<PluginSuccessPayload<PluginPayloadMap[T]> | PluginErrorPayload>) => {
|
|
if (event.source !== window || !event.data || event.data.type !== "geo.publisher.response") {
|
|
return;
|
|
}
|
|
if (event.data.requestId !== requestId || event.data.source !== "geo-extension") {
|
|
return;
|
|
}
|
|
|
|
cleanup();
|
|
if (event.data.success) {
|
|
if (!isPayloadValid(action, event.data.data)) {
|
|
reject(new Error("publisher_plugin_invalid_response"));
|
|
return;
|
|
}
|
|
resolve(event.data.data);
|
|
return;
|
|
}
|
|
reject(new Error(event.data.error || "publisher_plugin_unknown_error"));
|
|
};
|
|
|
|
const cleanup = () => {
|
|
window.clearTimeout(timer);
|
|
window.removeEventListener("message", handleMessage);
|
|
};
|
|
|
|
window.addEventListener("message", handleMessage);
|
|
window.postMessage(
|
|
{
|
|
source: "geo-admin",
|
|
type: "geo.publisher.request",
|
|
requestId,
|
|
action,
|
|
payload,
|
|
},
|
|
window.location.origin,
|
|
);
|
|
});
|
|
}
|
|
|
|
export async function pingPublisherPlugin(): Promise<PublisherPluginPingResponse> {
|
|
try {
|
|
return await requestPlugin("ping", undefined, 1500);
|
|
} catch {
|
|
try {
|
|
await sleep(300);
|
|
return await requestPlugin("ping", undefined, 2000);
|
|
} catch {
|
|
return {
|
|
installed: false,
|
|
capabilities: [],
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function detectPublisherPlatforms(): Promise<PublisherLocalPlatformState[]> {
|
|
return requestPlugin("detectPlatforms", undefined, 2500);
|
|
}
|
|
|
|
export async function registerPublisherInstallation(
|
|
payload: PublisherRegisterInstallationPayload,
|
|
): Promise<PublisherRegisterInstallationResult> {
|
|
return requestPlugin("registerInstallation", payload, 5000);
|
|
}
|
|
|
|
export async function bindPublisherPlatform(payload: PublisherBindRequest): Promise<PublisherBindResponse> {
|
|
return requestPlugin("bindAccount", payload, 10000);
|
|
}
|
|
|
|
export async function publishWithPublisherPlugin(
|
|
payload: PublisherPublishArticleRequest,
|
|
): Promise<PublisherPublishResponse> {
|
|
return requestPlugin("publishArticle", payload, 25000);
|
|
}
|