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.
243 lines
6.5 KiB
TypeScript
243 lines
6.5 KiB
TypeScript
import type { StoredPlatformState } from "../platforms";
|
|
|
|
import {
|
|
connectedPlatform,
|
|
disconnectedPlatform,
|
|
fetchImageBlob,
|
|
fetchJson,
|
|
getCookieString,
|
|
manageUrlResult,
|
|
normalizeArticleHtml,
|
|
sleep,
|
|
uploadHtmlImages,
|
|
} from "./common";
|
|
import type { AdapterContext, PlatformAdapter, PlatformPublishResult } from "./types";
|
|
|
|
const QIEHAO_APP_ID = "LA6zXi1lWzAioIzdiAD6iM10aHarlHF6";
|
|
|
|
type QiehaoBasicInfoResponse = {
|
|
data?: {
|
|
cpInfo?: {
|
|
mediaId?: string | number;
|
|
mediaName?: string;
|
|
header?: string;
|
|
};
|
|
};
|
|
};
|
|
|
|
type QiehaoPublishResponse = {
|
|
response?: {
|
|
code?: number;
|
|
msg?: string;
|
|
};
|
|
data?: {
|
|
articleId?: string | number;
|
|
};
|
|
msg?: string;
|
|
};
|
|
|
|
type QiehaoArticleListResponse = {
|
|
data?: {
|
|
articles?: Array<{
|
|
article_id?: string | number;
|
|
url?: string;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
type QiehaoUploadStepOneResponse = {
|
|
data?: {
|
|
url?: {
|
|
size?: Record<string, { imageUrl?: string }>;
|
|
};
|
|
};
|
|
};
|
|
|
|
type QiehaoUploadStepTwoResponse = {
|
|
data?: {
|
|
url?: string;
|
|
};
|
|
};
|
|
|
|
async function fetchBasicInfo() {
|
|
const cookie = await getCookieString("om.qq.com");
|
|
return fetchJson<QiehaoBasicInfoResponse>("https://om.qq.com/maccountsetting/basicinfo?relogin=1", {
|
|
headers: {
|
|
accept: "application/json, text/plain, */*",
|
|
...(cookie ? { cookie } : {}),
|
|
},
|
|
}).catch(() => null);
|
|
}
|
|
|
|
async function detectQiehao(platform: StoredPlatformState): Promise<StoredPlatformState> {
|
|
try {
|
|
const cpInfo = (await fetchBasicInfo())?.data?.cpInfo;
|
|
const uid = cpInfo?.mediaId != null ? String(cpInfo.mediaId) : "";
|
|
if (!uid || !cpInfo?.mediaName) {
|
|
return disconnectedPlatform(platform, "未检测到企鹅号登录态");
|
|
}
|
|
return connectedPlatform(platform, uid, cpInfo.mediaName, cpInfo.header ?? null);
|
|
} catch (error) {
|
|
return disconnectedPlatform(platform, error instanceof Error ? error.message : "企鹅号登录检测失败");
|
|
}
|
|
}
|
|
|
|
async function uploadImage(sourceUrl: string, includeCoverMeta: boolean): Promise<string | null> {
|
|
const blob = await fetchImageBlob(sourceUrl);
|
|
if (!blob) {
|
|
return null;
|
|
}
|
|
|
|
const form = new FormData();
|
|
form.append("file", blob, "image.png");
|
|
form.append("appid", QIEHAO_APP_ID);
|
|
form.append("isUpOrg", "1");
|
|
form.append("endpoint", "1");
|
|
form.append("isRetImgAttr", "1");
|
|
form.append("opCode", "151");
|
|
|
|
const first = await fetchJson<QiehaoUploadStepOneResponse>("https://image.om.qq.com/cpom_pimage/ArchacaleUploadViaFile", {
|
|
method: "POST",
|
|
body: form,
|
|
}).catch(() => null);
|
|
|
|
const imageUrl = first?.data?.url?.size?.["641"]?.imageUrl ?? null;
|
|
if (!imageUrl) {
|
|
return null;
|
|
}
|
|
|
|
if (!includeCoverMeta) {
|
|
return imageUrl;
|
|
}
|
|
|
|
const second = await fetchJson<QiehaoUploadStepTwoResponse>("https://image.om.qq.com/cpom_pimage/ListImageUploadViaUrl", {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
auth: {
|
|
appid: QIEHAO_APP_ID,
|
|
endpoint: 1,
|
|
},
|
|
reqData: {
|
|
appid: QIEHAO_APP_ID,
|
|
isUpOrg: 1,
|
|
endpoint: 1,
|
|
isRetImgAttr: 1,
|
|
opCode: 151,
|
|
imageUrl,
|
|
},
|
|
relogin: 1,
|
|
}),
|
|
}).catch(() => null);
|
|
|
|
return second?.data?.url ?? imageUrl;
|
|
}
|
|
|
|
async function publishQiehao(context: AdapterContext): Promise<PlatformPublishResult> {
|
|
const cpInfo = (await fetchBasicInfo())?.data?.cpInfo;
|
|
const mediaId = cpInfo?.mediaId != null ? String(cpInfo.mediaId) : "";
|
|
if (!mediaId) {
|
|
throw new Error("qiehao_not_logged_in");
|
|
}
|
|
|
|
const cookie = await getCookieString("om.qq.com");
|
|
if (!cookie) {
|
|
throw new Error("qiehao_cookie_missing");
|
|
}
|
|
|
|
const content = normalizeArticleHtml(context.article);
|
|
const processed = await uploadHtmlImages(content, async (sourceUrl) => uploadImage(sourceUrl, false));
|
|
const coverImages = context.article.cover_asset_url?.trim()
|
|
? [await uploadImage(context.article.cover_asset_url.trim(), true)].filter(Boolean)
|
|
: [];
|
|
|
|
const uploadedImageList = Array.from(processed.uploaded.values());
|
|
const resourceMarkInfo = uploadedImageList.reduce<Record<string, Record<string, unknown>>>((result, imageUrl) => {
|
|
result[imageUrl] = {
|
|
image_url: imageUrl,
|
|
id: imageUrl,
|
|
resource_type: "image",
|
|
can_modify: 1,
|
|
aigc_user_statement: 2,
|
|
};
|
|
return result;
|
|
}, {});
|
|
|
|
const endpoint =
|
|
context.article.publish_type === "publish"
|
|
? "https://om.qq.com/marticlepublish/omPublish"
|
|
: "https://om.qq.com/marticlepublish/omSave";
|
|
|
|
const response = await fetchJson<QiehaoPublishResponse>(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
cookie,
|
|
},
|
|
body: JSON.stringify({
|
|
title: context.article.title,
|
|
content: processed.html,
|
|
imgurl_ext: JSON.stringify(coverImages),
|
|
cover_type: "1",
|
|
imgurlsrc: "custom",
|
|
orignal: 0,
|
|
user_original: 0,
|
|
mediaId,
|
|
needpub: 1,
|
|
type: 0,
|
|
relogin: 1,
|
|
self_declare: '{"id":7,"desc":"作者声明:该文章由AI辅助创作"}',
|
|
resource_aigc_mark_info: JSON.stringify(resourceMarkInfo),
|
|
}),
|
|
});
|
|
|
|
const articleId = response.data?.articleId != null ? String(response.data.articleId) : "";
|
|
if (response.response?.code !== 0 || !articleId) {
|
|
throw new Error(response.msg || response.response?.msg || "qiehao_publish_failed");
|
|
}
|
|
|
|
await sleep(1500);
|
|
|
|
const list = await fetchJson<QiehaoArticleListResponse>(
|
|
"https://om.qq.com/marticle/article/list?category=&search=&source=&startDate=&endDate=&num=20&ftype=&readChannel=qb&dstChannel=&isPartDst=0&refreshField=&relogin=1",
|
|
{
|
|
headers: {
|
|
accept: "application/json, text/plain, */*",
|
|
cookie,
|
|
},
|
|
},
|
|
).catch(() => null);
|
|
|
|
const article = list?.data?.articles?.find((item) => String(item.article_id) === articleId);
|
|
const articleUrl = article?.url?.trim() || null;
|
|
const externalArticleId = articleUrl ? articleUrl.split("/").filter(Boolean).pop() || articleId : articleId;
|
|
|
|
if (context.article.publish_type === "draft") {
|
|
return manageUrlResult(
|
|
true,
|
|
"pending_review",
|
|
externalArticleId,
|
|
"https://om.qq.com/article/manage",
|
|
"企鹅号草稿保存成功。",
|
|
articleUrl,
|
|
);
|
|
}
|
|
|
|
return manageUrlResult(
|
|
true,
|
|
"success",
|
|
externalArticleId,
|
|
"https://om.qq.com/article/manage",
|
|
"企鹅号发布成功。",
|
|
articleUrl,
|
|
);
|
|
}
|
|
|
|
export const qiehaoAdapter: PlatformAdapter = {
|
|
platformId: "qiehao",
|
|
detect: detectQiehao,
|
|
publish: publishQiehao,
|
|
};
|