feat: Add platform adapters for various content publishing platforms
- 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.
This commit is contained in:
@@ -28,6 +28,10 @@ const errorMessageMap: Record<string, string> = {
|
||||
article_title_required: "请输入文章标题",
|
||||
invalid_payload: "请求参数不合法",
|
||||
update_failed: "保存文章失败",
|
||||
publisher_plugin_timeout: "浏览器插件响应超时,请刷新当前页面后重试",
|
||||
publisher_plugin_empty_response: "浏览器插件未返回数据,请刷新当前页面后重试",
|
||||
publisher_plugin_invalid_response: "浏览器插件返回了无效数据,请刷新当前页面后重试",
|
||||
publisher_plugin_unknown_error: "浏览器插件调用失败,请稍后重试",
|
||||
network_error: "网络连接失败,请稍后重试",
|
||||
unknown_error: "发生未知错误",
|
||||
};
|
||||
@@ -39,7 +43,7 @@ export function formatError(error: unknown): string {
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
return errorMessageMap[error.message] ?? error.message;
|
||||
}
|
||||
|
||||
return "发生未知错误";
|
||||
|
||||
@@ -35,6 +35,47 @@ type PluginPayloadMap = {
|
||||
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();
|
||||
@@ -42,6 +83,12 @@ function nextRequestId(): string {
|
||||
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"
|
||||
@@ -75,6 +122,10 @@ async function requestPlugin<T extends PluginAction>(
|
||||
|
||||
cleanup();
|
||||
if (event.data.success) {
|
||||
if (!isPayloadValid(action, event.data.data)) {
|
||||
reject(new Error("publisher_plugin_invalid_response"));
|
||||
return;
|
||||
}
|
||||
resolve(event.data.data);
|
||||
return;
|
||||
}
|
||||
@@ -104,10 +155,15 @@ export async function pingPublisherPlugin(): Promise<PublisherPluginPingResponse
|
||||
try {
|
||||
return await requestPlugin("ping", undefined, 1500);
|
||||
} catch {
|
||||
return {
|
||||
installed: false,
|
||||
capabilities: [],
|
||||
};
|
||||
try {
|
||||
await sleep(300);
|
||||
return await requestPlugin("ping", undefined, 2000);
|
||||
} catch {
|
||||
return {
|
||||
installed: false,
|
||||
capabilities: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,23 @@ export interface PublisherRuntimeState {
|
||||
pluginInstallationId: number | null;
|
||||
}
|
||||
|
||||
function normalizePingResponse(ping: PublisherPluginPingResponse | null | undefined): PublisherPluginPingResponse {
|
||||
if (!ping || typeof ping.installed !== "boolean") {
|
||||
return {
|
||||
installed: false,
|
||||
capabilities: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
installed: ping.installed,
|
||||
version: ping.version,
|
||||
capabilities: Array.isArray(ping.capabilities) ? ping.capabilities : [],
|
||||
installation_key: ping.installation_key,
|
||||
plugin_installation_id: ping.plugin_installation_id ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function detectBrowserName(): string {
|
||||
const ua = navigator.userAgent;
|
||||
if (/Edg\//.test(ua)) {
|
||||
@@ -31,7 +48,7 @@ function detectBrowserName(): string {
|
||||
}
|
||||
|
||||
export async function loadPublisherRuntimeState(): Promise<PublisherRuntimeState> {
|
||||
const ping = await pingPublisherPlugin();
|
||||
const ping = normalizePingResponse(await pingPublisherPlugin());
|
||||
if (!ping.installed || !ping.installation_key) {
|
||||
return {
|
||||
ping,
|
||||
|
||||
Reference in New Issue
Block a user