diff --git a/src/adaptors/web/juejin/juejinConfig.ts b/src/adaptors/web/juejin/juejinConfig.ts index e595f433..127067bd 100644 --- a/src/adaptors/web/juejin/juejinConfig.ts +++ b/src/adaptors/web/juejin/juejinConfig.ts @@ -38,10 +38,10 @@ export class JuejinConfig extends CommonWebConfig { this.passwordType = PasswordType.PasswordType_Cookie this.usernameEnabled = false this.tagEnabled = true - this.cateEnabled = true + this.cateEnabled = false this.categoryType = CategoryTypeEnum.CategoryType_Single this.knowledgeSpaceEnabled = true - this.knowledgeSpaceTitle = "专栏" + this.knowledgeSpaceTitle = "分类" this.knowledgeSpaceType = CategoryTypeEnum.CategoryType_Single this.allowKnowledgeSpaceChange = false this.placeholder.knowledgeSpaceReadonlyModeTip = diff --git a/src/adaptors/web/juejin/juejinWebAdaptor.ts b/src/adaptors/web/juejin/juejinWebAdaptor.ts index b5bc3517..b006bb19 100644 --- a/src/adaptors/web/juejin/juejinWebAdaptor.ts +++ b/src/adaptors/web/juejin/juejinWebAdaptor.ts @@ -25,6 +25,7 @@ import { BaseWebApi } from "~/src/adaptors/web/base/baseWebApi.ts" import { CategoryInfo, Post, UserBlog } from "zhi-blog-api" +import { StrUtil } from "zhi-common" /** * 掘金网页授权适配器 @@ -56,23 +57,20 @@ class JuejinWebAdaptor extends BaseWebApi { public async getUsersBlogs(): Promise> { let result: UserBlog[] = [] - const url = "https://api.juejin.cn/content_api/v1/column/author_center_list" - const params = { - audit_status: null, - page_no: 1, - page_size: 10, + const header = { + accept: "application/json", } - const res = await this.webProxyFetch(url, [], params, "POST") - this.logger.debug("get juejin columns =>", { res }) + const res = await this.webProxyFetch("https://api.juejin.cn/tag_api/v1/query_category_list", [header], {}, "POST") + this.logger.info(`get juejin categories`, res.data) - if (res.err_no === 0) { - const columns = res.data - columns.forEach((item: any) => { + if (res.data && res.data.length > 0) { + const cates = res.data + cates.forEach((item: any) => { const useBlog = new UserBlog() - useBlog.blogid = item.column_id - useBlog.blogName = item.column_version.title - useBlog.url = item.column_version.cover + useBlog.blogid = item.category_id + useBlog.blogName = item.category.category_name + useBlog.url = item.category.icon result.push(useBlog) }) } @@ -81,35 +79,163 @@ class JuejinWebAdaptor extends BaseWebApi { return result } - public async getCategories(): Promise { - const cats = [] as CategoryInfo[] + public async addPost(post: Post) { + const cate_slug = post.cate_slugs?.[0] ?? this.cfg.blogid + if (StrUtil.isEmptyString(cate_slug)) { + throw new Error("掘金平台必须选择一个分类") + } + + // 保存草稿 + const draftUrl = "https://api.juejin.cn/content_api/v1/article_draft/create" + const draftParams = { + category_id: cate_slug, + tag_ids: [], + link_url: "", + cover_image: "", + title: post.title, + brief_content: post.shortDesc, + edit_type: 10, + html_content: "deprecated", + mark_content: post.description, + theme_ids: [], + } + const draftRes = await this.webProxyFetch(draftUrl, [], draftParams, "POST") + this.logger.debug("juejin add post =>", draftRes) + if (draftRes.err_no !== 0) { + throw new Error("掘金文章草稿保存错误 =>" + draftRes.err_msg) + } + const draftId = draftRes.data.id.toString() + + // 发布文章 + const pageId = await this.publishPost(draftId) + const postid = [pageId, draftId].join("_") + return { + status: "success", + post_id: postid, + } + } + + public async editPost(postid: string, post: Post, publish?: boolean): Promise { + const cate_slug = post.cate_slugs?.[0] ?? this.cfg.blogid + if (StrUtil.isEmptyString(cate_slug)) { + throw new Error("掘金平台必须选择一个分类") + } + + const juejinPostKey = this.getJuejinPostidKey(postid) + // const pageId = juejinPostKey.pageId + const draftId = juejinPostKey.draftId + + // 更新文章 + const draftUpdateUrl = "https://api.juejin.cn/content_api/v1/article_draft/update" + const draftParams = { + id: draftId, + category_id: cate_slug, + tag_ids: [], + link_url: "", + cover_image: "", + title: post.title, + brief_content: post.shortDesc, + edit_type: 10, + html_content: "deprecated", + mark_content: post.markdown, + theme_ids: [], + } + // 更新草稿 + const draftRes = await this.webProxyFetch(draftUpdateUrl, [], draftParams, "POST") + this.logger.debug("juejin update post =>", draftRes) + if (draftRes.err_no !== 0) { + throw new Error("掘金文章更新错误 =>" + draftRes.err_msg) + } + + // 发布文章 + await this.publishPost(draftId) + + return true + } + + public async getPreviewUrl(postid: string): Promise { + const juejinPostKey = this.getJuejinPostidKey(postid) + const pageId = juejinPostKey.pageId + const postUrl = this.cfg.previewUrl.replace("[postid]", pageId) + return StrUtil.pathJoin(this.cfg.home ?? "", postUrl) + } - const url = "https://api.juejin.cn/content_api/v1/column/author_center_list" + public async deletePost(postid: string): Promise { + const url = "https://api.juejin.cn/content_api/v1/article/delete" const params = { - audit_status: null, - page_no: 1, - page_size: 10, + article_id: postid, } const res = await this.webProxyFetch(url, [], params, "POST") - this.logger.debug("get juejin columns =>", { res }) + this.logger.debug("juejin delete post res =>", res) + if (res.err_no !== 0) { + throw new Error("掘金文章删除失败 =>" + res.err_msg) + } + + return true + } + + public async getCategories(): Promise { + const cats = [] as CategoryInfo[] + + const header = { + accept: "application/json", + } + const res = await this.webProxyFetch("https://api.juejin.cn/tag_api/v1/query_category_list", [header], {}, "POST") + this.logger.info(`get juejin categories`, res.data) - if (res.err_no === 0) { - const columns = res.data - columns.forEach((item: any) => { + if (res.data && res.data.length > 0) { + res.data.forEach((item: any) => { const cat = new CategoryInfo() - cat.categoryId = item.column_id - cat.categoryName = item.column_version.title + cat.categoryId = item.category_id + cat.categoryName = item.category.category_name cats.push(cat) }) } return cats } + // ================ + // private methods + // ================ + /** + * 获取封装的postid + * + * @param postid + * @private postid + */ + private getJuejinPostidKey(postid: string): any { + let pageId: string + let draftId: string + if (postid.indexOf("_") > 0) { + const idArr = postid.split("_") + pageId = idArr[0] + draftId = idArr[1] + } else { + pageId = postid + } - public async addPost(post: Post) {} + return { + pageId: pageId, + draftId: draftId, + } + } - public async editPost(postid: string, post: Post, publish?: boolean): Promise { - return false + private async publishPost(draftId: string) { + // 发布文章 + const url = "https://api.juejin.cn/content_api/v1/article/publish" + const params = { + draft_id: draftId, + sync_to_org: false, + column_ids: [], + theme_ids: [], + } + const res = await this.webProxyFetch(url, [], params, "POST") + this.logger.debug("juejin publish post res =>", res) + if (res.err_no !== 0) { + throw new Error("掘金文章发布失败 =>" + res?.err_msg ?? res) + } + + return res.data.article_id.toString() } } diff --git a/src/adaptors/web/juejin/useJuejinWeb.ts b/src/adaptors/web/juejin/useJuejinWeb.ts index 34fc71cf..aa96b5e6 100644 --- a/src/adaptors/web/juejin/useJuejinWeb.ts +++ b/src/adaptors/web/juejin/useJuejinWeb.ts @@ -83,14 +83,11 @@ const useJuejinWeb = async (key?: string, newCfg?: JuejinConfig) => { cfg.tagEnabled = true // 掘金使用单选分类 - cfg.cateEnabled = true - cfg.categoryType = CategoryTypeEnum.CategoryType_Single + cfg.cateEnabled = false cfg.knowledgeSpaceEnabled = true - cfg.knowledgeSpaceTitle = "专栏" + cfg.knowledgeSpaceTitle = "分类" cfg.knowledgeSpaceType = CategoryTypeEnum.CategoryType_Single - cfg.allowKnowledgeSpaceChange = false - cfg.placeholder.knowledgeSpaceReadonlyModeTip = - "由于掘金平台的限制,暂时不支持编辑所属专栏。如果您想移动文档,请先点击取消删除该文档,然后重新选择新的专栏发布" + cfg.allowKnowledgeSpaceChange = true const webApi = new JuejinWebAdaptor(appInstance, cfg) return { diff --git a/testdata/juejin/juejin.http b/testdata/juejin/juejin.http index 57e119b6..722dd415 100644 --- a/testdata/juejin/juejin.http +++ b/testdata/juejin/juejin.http @@ -9,3 +9,80 @@ Content-Type: application/json "page_size": 10 } +### 获取文章详情 +POST https://api.juejin.cn/content_api/v1/article/detail?aid=2608 +Cookie: {{cookie}} +Content-Type: application/json + +{ + "article_id": "7275943600780935209" +} + +### 保存文章草稿 +POST https://api.juejin.cn/content_api/v1/article_draft/create +Cookie: {{cookie}} +Content-Type: application/json + +{ + "category_id": "6809637769959178254", + "tag_ids": [ + "6809640407484334093" + ], + "link_url": "", + "cover_image": "", + "title": "cesssss3", + "brief_content": "dfgdfgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg", + "edit_type": 10, + "html_content": "deprecated", + "mark_content": "", + "theme_ids": [] +} + +### 更新草稿 +POST https://api.juejin.cn/content_api/v1/article_draft/update?aid=2608&uuid=7267424099298854436 +Cookie: {{cookie}} +Content-Type: application/json + +{ + "id": "7275934458133545001", + "category_id": "6809637769959178254", + "tag_ids": [ + "6809640407484334093" + ], + "link_url": "", + "cover_image": "", + "title": "cesssss3", + "brief_content": "dfgdfgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg", + "edit_type": 10, + "html_content": "deprecated", + "mark_content": "dfgfdgdfgdfgdfgdfgfddfgdf7f", + "theme_ids": [] +} + +### 发布草稿 +POST https://api.juejin.cn/content_api/v1/article/publish +Cookie: {{cookie}} +Content-Type: application/json + +{ + "draft_id": "7275945995623792679", + "sync_to_org": false, + "column_ids": [], + "theme_ids": [] +} + +### 删除文章 +POST https://api.juejin.cn/content_api/v1/article/delete +cookie: {{cookie}} +Content-Type: application/json + +{ + "article_id": "7275943600781164585" +} + +### 获取分类 +POST https://api.juejin.cn/tag_api/v1/query_category_list +cookie: {{cookie}} +Content-Type: application/json + +{} \ No newline at end of file