├── .gitignore ├── README.md ├── app.js ├── libs └── http.js ├── nodemon.json ├── package.json └── routers └── hbooker ├── apis ├── add_tsukkomi.js ├── book_chapters.js ├── book_info.js ├── bookshelves.js ├── chapter_buy.js ├── chapter_content.js ├── chapter_ifm.js ├── give_recommend.js ├── like_tsukkomi.js ├── login.js ├── my_info.js ├── set_last_read.js ├── shelf_books.js ├── sign_recommend.js ├── sign_record.js ├── tsukkomi_list.js ├── tsukkomi_num.js └── unlike_tsukkomi.js ├── index.js └── mixin ├── decrypt.js ├── fix_param.js ├── http.js └── parse_res.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | static 4 | app 5 | dist/ 6 | 7 | .token 8 | demos 9 | 10 | yarn.lock 11 | package-lock.json 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | *.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cirno-Server 2 | 3 | --- 4 | 5 | This is the server side of cirno. 6 | 7 | Fews apis from ciweimao app are wrappered. 8 | 9 | After login it will generate a file named `.token`, don't delete or modify it. 10 | 11 | --- 12 | 13 | ## Usage 14 | 15 | ```shell 16 | mkdir static 17 | copy dist files to static 18 | yarn 19 | node app.js // run the server 20 | yarn pkg //pack it to an executable file 21 | ``` 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const Koa = require("koa"); 2 | const cors = require("koa2-cors"); 3 | const app = new Koa(); 4 | const Router = require("koa-router"); 5 | const bodyParser = require("koa-bodyparser"); 6 | const path = require("path"); 7 | const static = require("koa-static"); 8 | const fs = require("fs"); 9 | const open = require("open"); 10 | 11 | //允许跨域 12 | app.use(cors()); 13 | app.use(bodyParser()); 14 | 15 | const staticPath = "/static"; 16 | app.use(static(path.join(__dirname, staticPath))); 17 | 18 | //读取 tokens 19 | allTokens = {}; 20 | let tokenExist = fs.existsSync(path.join(process.cwd(), "./.token")); 21 | if (tokenExist) { 22 | let token = fs.readFileSync(path.join(process.cwd(), "./.token"), "utf8"); 23 | allTokens = JSON.parse(token); 24 | } else { 25 | allTokens = {}; 26 | } 27 | 28 | //刺猬猫路由 29 | const hbooker = require("./routers/hbooker"); 30 | 31 | //装载子路由 32 | let router = new Router(); 33 | router.use("/hbooker", hbooker.routes(), hbooker.allowedMethods()); 34 | 35 | //加载路由中间件 36 | app.use(router.routes()).use(router.allowedMethods()); 37 | 38 | //开始监听 39 | app.listen(9096, () => { 40 | console.log("程序启动成功,请在浏览器中访问 http://localhost:9096"); 41 | }); 42 | 43 | (async () => { 44 | await open("http://localhost:9096"); 45 | })(); 46 | 47 | module.exports = app; 48 | -------------------------------------------------------------------------------- /libs/http.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | const mixin = { 4 | standardFlag: true, 5 | timeout: 15000, 6 | }; 7 | 8 | axios.interceptors.response.use( 9 | (response) => { 10 | return response; 11 | }, 12 | (error) => { 13 | if (error && error.response) { 14 | switch (error.response.status) { 15 | case 400: 16 | error.message = "请求错误"; 17 | break; 18 | case 401: 19 | error.message = "未授权,请登录"; 20 | break; 21 | case 403: 22 | error.message = "拒绝访问"; 23 | break; 24 | case 404: 25 | error.message = "请求地址出错"; 26 | break; 27 | case 408: 28 | error.message = "请求超时"; 29 | break; 30 | case 500: 31 | error.message = "服务器内部错误"; 32 | break; 33 | case 501: 34 | error.message = "服务未实现"; 35 | break; 36 | case 502: 37 | error.message = "网关错误"; 38 | break; 39 | case 503: 40 | error.message = "服务不可用"; 41 | break; 42 | case 504: 43 | error.message = "网关超时"; 44 | break; 45 | case 505: 46 | error.message = "HTTP 版本不受支持"; 47 | break; 48 | default: 49 | } 50 | } 51 | return Promise.reject(error); 52 | } 53 | ); 54 | 55 | function get(options) { 56 | let params = options.para; 57 | return new Promise((resolve, reject) => { 58 | axios 59 | .get(options.url, { 60 | params: params, 61 | }) 62 | .then((response) => { 63 | let data = response.data; 64 | resolve(data); 65 | }) 66 | .catch((err) => { 67 | reject(err); 68 | }); 69 | }); 70 | } 71 | 72 | function post(obj) { 73 | options = Object.assign({}, mixin, obj); 74 | return new Promise((resolve, reject) => { 75 | axios 76 | .post(options.url, options.body, { 77 | headers: options.headers, 78 | params: options.para, 79 | timeout: options.timeout, 80 | withCredentials: options.withCredentials, 81 | }) 82 | .then( 83 | (response) => { 84 | let data = response.data; 85 | resolve(data); 86 | }, 87 | (err) => { 88 | reject(err); 89 | } 90 | ); 91 | }); 92 | } 93 | 94 | const http = { 95 | get, 96 | post, 97 | }; 98 | 99 | module.exports = http; 100 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "restartable": "rs", 3 | "ignore": [".git", ".svn", "node_modules/**/node_modules"], 4 | "verbose": true, 5 | "execMap": { 6 | "js": "node --harmony" 7 | }, 8 | "watch": [], 9 | "env": { 10 | "NODE_ENV": "development" 11 | }, 12 | "ext": "js json" 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cirno", 3 | "version": "1.0.0", 4 | "description": "a wrapper for hbooker app interface ", 5 | "main": "app.js", 6 | "scripts": { 7 | "pkg": "pkg . --out-path=dist/" 8 | }, 9 | "keywords": [ 10 | "hbooker" 11 | ], 12 | "author": "zsakvo", 13 | "license": "ISC", 14 | "dependencies": { 15 | "axios": "^0.19.2", 16 | "http-server": "^0.12.3", 17 | "koa": "^2.13.0", 18 | "koa-bodyparser": "^4.3.0", 19 | "koa-router": "^9.1.0", 20 | "koa-static": "^5.0.0", 21 | "koa2-cors": "^2.0.6", 22 | "open": "^7.1.0", 23 | "qs": "^6.9.4" 24 | }, 25 | "bin": "./app.js", 26 | "pkg": { 27 | "scripts": [ 28 | "./static/js/*.js" 29 | ], 30 | "assets": [ 31 | "./static/index.html", 32 | "./static/favicon.ico", 33 | "./static/robots.txt", 34 | "./static/css/*", 35 | "./static/fonts/*", 36 | "./static/img/*" 37 | ], 38 | "targets": [ 39 | "node8-macos-x64", 40 | "node8-win-x64", 41 | "node8-linux-x64" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /routers/hbooker/apis/add_tsukkomi.js: -------------------------------------------------------------------------------- 1 | const post = require("../mixin/http").postHbooker; 2 | 3 | let add_tsukkomi = async function ( 4 | book_id, 5 | chapter_id, 6 | paragraph_index, 7 | tsukkomi_content 8 | ) { 9 | let res = await post({ 10 | url: "/chapter/add_tsukkomi", 11 | para: { 12 | book_id: book_id, 13 | chapter_id: chapter_id, 14 | paragraph_index: paragraph_index, 15 | tsukkomi_content: tsukkomi_content, 16 | }, 17 | }); 18 | return res; 19 | }; 20 | 21 | module.exports = add_tsukkomi; 22 | -------------------------------------------------------------------------------- /routers/hbooker/apis/book_chapters.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | const post = require("../mixin/http").postHbooker; 3 | 4 | let book_division = async function (book_id) { 5 | let res = await get({ 6 | url: "/book/get_division_list", 7 | para: { 8 | book_id: book_id, 9 | }, 10 | }); 11 | return res; 12 | }; 13 | 14 | let division_chapters = async function (division_id) { 15 | let res = await post({ 16 | url: "/chapter/get_updated_chapter_by_division_id", 17 | body: { 18 | division_id: division_id, 19 | last_update_time: 0, 20 | }, 21 | }); 22 | return res; 23 | }; 24 | 25 | let book_chapters = async function (book_id) { 26 | let divisions_res = await book_division(book_id); 27 | let result = {}; 28 | let chapters = []; 29 | if (divisions_res.success) { 30 | let division_list = divisions_res.data.division_list; 31 | for (let division of division_list) { 32 | let division_id = division["division_id"]; 33 | let chapters_res = await division_chapters(division_id); 34 | if (chapters_res.success) { 35 | chapters.push(...chapters_res.data.chapter_list); 36 | } else { 37 | result.success = false; 38 | result.tip = `获取分卷 --${division.division_name}-- 失败`; 39 | break; 40 | } 41 | } 42 | result.success = true; 43 | result.data = chapters; 44 | } else { 45 | result.success = false; 46 | result.tip = divisions_res.tip; 47 | } 48 | return result; 49 | }; 50 | 51 | module.exports = book_chapters; 52 | -------------------------------------------------------------------------------- /routers/hbooker/apis/book_info.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let book_info = async function (book_id) { 4 | let res = await get({ 5 | url: "/book/get_info_by_id", 6 | para: { 7 | book_id: book_id, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = book_info; 14 | -------------------------------------------------------------------------------- /routers/hbooker/apis/bookshelves.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let bookshelves = async function () { 4 | let res = await get({ 5 | url: "/bookshelf/get_shelf_list", 6 | para: {}, 7 | }); 8 | return res; 9 | }; 10 | 11 | module.exports = bookshelves; 12 | -------------------------------------------------------------------------------- /routers/hbooker/apis/chapter_buy.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let chapter_buy = async function (chapter_id) { 4 | let res = await get({ 5 | url: "/chapter/buy", 6 | para: { 7 | chapter_id: chapter_id, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = chapter_buy; 14 | -------------------------------------------------------------------------------- /routers/hbooker/apis/chapter_content.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | const decrypt = require("../mixin/decrypt"); 3 | 4 | let chapter_key = async function (chapter_id) { 5 | let res = await get({ 6 | url: "/chapter/get_chapter_cmd", 7 | para: { 8 | chapter_id: chapter_id, 9 | }, 10 | }); 11 | return res; 12 | }; 13 | 14 | let content_clear = async function (chapter_id, chapter_command) { 15 | let res = await get({ 16 | url: "/chapter/get_cpt_ifm", 17 | para: { 18 | chapter_id: chapter_id, 19 | chapter_command: chapter_command, 20 | }, 21 | }); 22 | return res; 23 | }; 24 | 25 | let chapter_content = async function (chapter_id) { 26 | let key_res = await chapter_key(chapter_id); 27 | let result = {}; 28 | if (key_res.success) { 29 | let key = key_res.data.command; 30 | let content_res = await content_clear(chapter_id, key); 31 | if (content_res.success) { 32 | result.success = true; 33 | result.data = content_res.data; 34 | result["data"]["chapter_info"]["txt_content"] = decrypt( 35 | content_res["data"]["chapter_info"]["txt_content"], 36 | key 37 | ); 38 | } 39 | } else { 40 | result.success = false; 41 | result.tip = key_res.tip; 42 | } 43 | return result; 44 | }; 45 | 46 | module.exports = chapter_content; 47 | -------------------------------------------------------------------------------- /routers/hbooker/apis/chapter_ifm.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | const post = require("../mixin/http").postHbooker; 3 | const decrypt = require("../mixin/decrypt"); 4 | 5 | let chapter_key = async function (chapter_id) { 6 | let res = await get({ 7 | url: "/chapter/get_chapter_cmd", 8 | para: { 9 | chapter_id: chapter_id, 10 | }, 11 | }); 12 | return res; 13 | }; 14 | 15 | let content_clear = async function (chapter_id, chapter_command) { 16 | let res = await post({ 17 | url: "/chapter/get_cpt_ifm", 18 | body: { 19 | chapter_id: chapter_id, 20 | chapter_command: chapter_command, 21 | }, 22 | }); 23 | return res; 24 | }; 25 | 26 | let chapter_content = async function (chapter_id) { 27 | let key_res = await chapter_key(chapter_id); 28 | let result = {}; 29 | if (key_res.success) { 30 | let key = key_res.data.command; 31 | let content_res = await content_clear(chapter_id, key); 32 | if (content_res.success) { 33 | result.success = true; 34 | result.data = content_res.data; 35 | result["data"]["chapter_info"]["txt_content"] = decrypt( 36 | content_res["data"]["chapter_info"]["txt_content"], 37 | key 38 | ); 39 | } 40 | } else { 41 | result.success = false; 42 | result.tip = key_res.tip; 43 | } 44 | return result; 45 | }; 46 | 47 | module.exports = chapter_content; 48 | -------------------------------------------------------------------------------- /routers/hbooker/apis/give_recommend.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let give_recommend = async function (book_id, count) { 4 | let res = await get({ 5 | url: "/book/give_recommend", 6 | para: { 7 | book_id: book_id, 8 | count: count, 9 | }, 10 | }); 11 | return res; 12 | }; 13 | 14 | module.exports = give_recommend; 15 | -------------------------------------------------------------------------------- /routers/hbooker/apis/like_tsukkomi.js: -------------------------------------------------------------------------------- 1 | const post = require("../mixin/http").postHbooker; 2 | 3 | let like_tsukkomi = async function (tsukkomi_id) { 4 | let res = await post({ 5 | url: "/chapter/like_tsukkomi", 6 | para: { 7 | tsukkomi_id: tsukkomi_id, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = like_tsukkomi; 14 | -------------------------------------------------------------------------------- /routers/hbooker/apis/login.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | 5 | let login = async function (name, passwd) { 6 | let res = await get({ 7 | url: "/signup/login", 8 | para: { 9 | login_name: name, 10 | passwd: passwd, 11 | }, 12 | }); 13 | // delete res.success; 14 | if (res.success) { 15 | delete res.success; 16 | fs.writeFileSync( 17 | path.join(process.cwd(), ".token"), 18 | JSON.stringify({ hbooker: res }), 19 | "utf8" 20 | ); 21 | allTokens.hbooker = res; 22 | res.success = true; 23 | } else { 24 | fs.writeFileSync( 25 | path.join(process.cwd(), ".token"), 26 | JSON.stringify({ hbooker: {} }), 27 | "utf8" 28 | ); 29 | allTokens.hbooker = {}; 30 | } 31 | return res; 32 | }; 33 | 34 | module.exports = login; 35 | -------------------------------------------------------------------------------- /routers/hbooker/apis/my_info.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let my_info = async function () { 4 | if(!allTokens.hbooker){ 5 | return { 6 | success:false, 7 | code:200100, 8 | tip:'尚未登录,正在跳转登录页面……' 9 | } 10 | }else{ 11 | let res = await get({ 12 | url: "/reader/get_my_info", 13 | para: { 14 | reader_id: allTokens.hbooker.reader_id , 15 | }, 16 | }); 17 | return res; 18 | } 19 | }; 20 | 21 | module.exports = my_info; 22 | -------------------------------------------------------------------------------- /routers/hbooker/apis/set_last_read.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let set_last_read = async function (book_id, last_read_chapter_id) { 4 | let res = await get({ 5 | url: "/bookshelf/set_last_read_chapter", 6 | para: { 7 | book_id: book_id, 8 | last_read_chapter_id: last_read_chapter_id, 9 | }, 10 | }); 11 | return res; 12 | }; 13 | 14 | module.exports = set_last_read; 15 | -------------------------------------------------------------------------------- /routers/hbooker/apis/shelf_books.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let shelfBooks = async function (shelf_id) { 4 | let res = await get({ 5 | url: "/bookshelf/get_shelf_book_list_new", 6 | para: { 7 | count: 9999, 8 | shelf_id: shelf_id, 9 | page: 0, 10 | order: "last_read_time", 11 | }, 12 | }); 13 | return res; 14 | }; 15 | 16 | module.exports = shelfBooks; 17 | -------------------------------------------------------------------------------- /routers/hbooker/apis/sign_recommend.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let sign_recommend = async function () { 4 | let res = await get({ 5 | url: "/reader/get_task_bonus_with_sign_recommend", 6 | para: { 7 | task_type: 1, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = sign_recommend; 14 | -------------------------------------------------------------------------------- /routers/hbooker/apis/sign_record.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let sign_record = async function () { 4 | let res = await get({ 5 | url: "/task/get_sign_record", 6 | para: {}, 7 | }); 8 | return res; 9 | }; 10 | 11 | module.exports = sign_record; 12 | -------------------------------------------------------------------------------- /routers/hbooker/apis/tsukkomi_list.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let tsukkomi_list = async function (chapter_id, paragraph_index, page) { 4 | let res = await get({ 5 | url: "/chapter/get_paragraph_tsukkomi_list_new", 6 | para: { 7 | chapter_id: chapter_id, 8 | paragraph_index: paragraph_index, 9 | count: 20, 10 | page: page, 11 | }, 12 | }); 13 | return res; 14 | }; 15 | 16 | module.exports = tsukkomi_list; 17 | -------------------------------------------------------------------------------- /routers/hbooker/apis/tsukkomi_num.js: -------------------------------------------------------------------------------- 1 | const get = require("../mixin/http").getHbooker; 2 | 3 | let tsukkomi_num = async function (chapter_id) { 4 | let res = await get({ 5 | url: "/chapter/get_tsukkomi_num", 6 | para: { 7 | chapter_id: chapter_id, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = tsukkomi_num; 14 | -------------------------------------------------------------------------------- /routers/hbooker/apis/unlike_tsukkomi.js: -------------------------------------------------------------------------------- 1 | const post = require("../mixin/http").postHbooker; 2 | 3 | let unlike_tsukkomi = async function (tsukkomi_id) { 4 | let res = await post({ 5 | url: "/chapter/unlike_tsukkomi", 6 | para: { 7 | tsukkomi_id: tsukkomi_id, 8 | }, 9 | }); 10 | return res; 11 | }; 12 | 13 | module.exports = unlike_tsukkomi; 14 | -------------------------------------------------------------------------------- /routers/hbooker/index.js: -------------------------------------------------------------------------------- 1 | const Router = require("koa-router"); 2 | let hbooker = new Router(); 3 | 4 | const login = require("./apis/login"); 5 | const bookshelves = require("./apis/bookshelves"); 6 | const shelf_books = require("./apis/shelf_books"); 7 | const book_info = require("./apis/book_info"); 8 | const book_chapters = require("./apis/book_chapters"); 9 | const chapter_content = require("./apis/chapter_content"); 10 | const chapter_buy = require("./apis/chapter_buy"); 11 | const tsukkomi_num = require("./apis/tsukkomi_num"); 12 | const tsukkomi_list = require("./apis/tsukkomi_list"); 13 | const chapter_ifm = require("./apis/chapter_ifm"); 14 | const like_tsukkomi = require("./apis/like_tsukkomi"); 15 | const unlike_tsukkomi = require("./apis/unlike_tsukkomi"); 16 | const add_tsukkomi = require("./apis/add_tsukkomi"); 17 | const my_info = require("./apis/my_info"); 18 | const give_recommend = require("./apis/give_recommend"); 19 | const set_last_read = require("./apis/set_last_read"); 20 | const sign_record = require("./apis/sign_record"); 21 | const sign_recommend = require("./apis/sign_recommend"); 22 | 23 | hbooker 24 | .post("/login", async (ctx) => { 25 | let postData = ctx.request.body; 26 | let name = postData.name; 27 | let passwd = postData.passwd; 28 | let res = await login(name, passwd); 29 | ctx.body = res; 30 | }) 31 | .get("/bookshelves", async (ctx) => { 32 | let res = await bookshelves(); 33 | ctx.body = res; 34 | }) 35 | .get("/shelf_books", async (ctx) => { 36 | let ctx_query = ctx.query; 37 | let res = await shelf_books(ctx_query["shelf_id"]); 38 | ctx.body = res; 39 | }) 40 | .get("/book_info", async (ctx) => { 41 | let ctx_query = ctx.query; 42 | let res = await book_info(ctx_query["book_id"]); 43 | ctx.body = res; 44 | }) 45 | .get("/book_chapters", async (ctx) => { 46 | let ctx_query = ctx.query; 47 | let res = await book_chapters(ctx_query["book_id"]); 48 | ctx.body = res; 49 | }) 50 | .get("/chapter_content", async (ctx) => { 51 | let ctx_query = ctx.query; 52 | let res = await chapter_content(ctx_query["chapter_id"]); 53 | ctx.body = res; 54 | }) 55 | .get("/chapter_buy", async (ctx) => { 56 | let ctx_query = ctx.query; 57 | let res = await chapter_buy(ctx_query["chapter_id"]); 58 | ctx.body = res; 59 | }) 60 | .get("/tsukkomi_num", async (ctx) => { 61 | let ctx_query = ctx.query; 62 | let res = await tsukkomi_num(ctx_query["chapter_id"]); 63 | ctx.body = res; 64 | }) 65 | .get("/tsukkomi_list", async (ctx) => { 66 | let ctx_query = ctx.query; 67 | let res = await tsukkomi_list( 68 | ctx_query["chapter_id"], 69 | ctx_query["paragraph_index"], 70 | ctx_query["page"] 71 | ); 72 | ctx.body = res; 73 | }) 74 | .get("/chapter_ifm", async (ctx) => { 75 | let ctx_query = ctx.query; 76 | let res = await chapter_ifm(ctx_query["chapter_id"]); 77 | ctx.body = res; 78 | }) 79 | .get("/like_tsukkomi", async (ctx) => { 80 | let ctx_query = ctx.query; 81 | let res = await like_tsukkomi(ctx_query["tsukkomi_id"]); 82 | ctx.body = res; 83 | }) 84 | .get("/unlike_tsukkomi", async (ctx) => { 85 | let ctx_query = ctx.query; 86 | let res = await unlike_tsukkomi(ctx_query["tsukkomi_id"]); 87 | ctx.body = res; 88 | }) 89 | .post("/add_tsukkomi", async (ctx) => { 90 | let postData = ctx.request.body; 91 | let book_id = postData.book_id; 92 | let chapter_id = postData.chapter_id; 93 | let paragraph_index = postData.paragraph_index; 94 | let tsukkomi_content = postData.tsukkomi_content; 95 | let res = await add_tsukkomi( 96 | book_id, 97 | chapter_id, 98 | paragraph_index, 99 | tsukkomi_content 100 | ); 101 | ctx.body = res; 102 | }) 103 | .get("/my_info", async (ctx) => { 104 | let res = await my_info(); 105 | ctx.body = res; 106 | }) 107 | .get("/give_recommend", async (ctx) => { 108 | let ctx_query = ctx.query; 109 | let res = await give_recommend(ctx_query["book_id"], ctx_query["count"]); 110 | ctx.body = res; 111 | }) 112 | .get("/set_last_read", async (ctx) => { 113 | let ctx_query = ctx.query; 114 | let res = await set_last_read( 115 | ctx_query["book_id"], 116 | ctx_query["chapter_id"] 117 | ); 118 | ctx.body = res; 119 | }) 120 | .get("/sign_record", async (ctx) => { 121 | let res = await sign_record(); 122 | ctx.body = res; 123 | }) 124 | .get("/sign_recommend", async (ctx) => { 125 | let res = await sign_recommend(); 126 | ctx.body = res; 127 | }); 128 | 129 | module.exports = hbooker; 130 | -------------------------------------------------------------------------------- /routers/hbooker/mixin/decrypt.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | 3 | var decrypt = function decrypt(data, key) { 4 | if (key == null) { 5 | key = crypto 6 | .createHash("sha256") 7 | .update("zG2nSeEfSHfvTCHy5LCcqtBbQehKNLXn") 8 | .digest(); 9 | } else { 10 | key = crypto.createHash("sha256").update(key).digest(); 11 | } 12 | let decipher = crypto.createDecipheriv( 13 | "aes-256-cbc", 14 | key, 15 | new Uint8Array(16) 16 | ); 17 | decipher.setAutoPadding(false); 18 | let decrypted = decipher.update(data, "base64", "utf8"); 19 | return decrypted; 20 | }; 21 | 22 | module.exports = decrypt; 23 | -------------------------------------------------------------------------------- /routers/hbooker/mixin/fix_param.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | url: "https://app.hbooker.com", 3 | params: { 4 | app_version: "2.5.016", 5 | device_token: "ciweimao_koa_server", 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /routers/hbooker/mixin/http.js: -------------------------------------------------------------------------------- 1 | const parse = require("./parse_res"); 2 | const qs = require("qs"); 3 | const get = require("../../../libs/http").get; 4 | const post = require("../../../libs/http").post; 5 | const fix = require("./fix_param"); 6 | let getHbooker = function (options) { 7 | options.url = fix.url + options.url; 8 | options.para = Object.assign({}, options.para, fix.params, allTokens.hbooker); 9 | return get(options).then( 10 | (res) => { 11 | let clearRes = parse(res); 12 | let result = {}; 13 | if (clearRes.code == 100000) { 14 | result.success = true; 15 | if (options.url === fix.url + "/signup/login") { 16 | result.login_token = clearRes.data.login_token; 17 | result.account = clearRes.data.reader_info.account; 18 | result.reader_id = clearRes.data.reader_info.reader_id; 19 | } else { 20 | result.data = clearRes.data; 21 | } 22 | return result; 23 | } else { 24 | result.success = false; 25 | result.tip = clearRes.tip; 26 | result.code = clearRes.code; 27 | return result; 28 | } 29 | }, 30 | (err) => { 31 | let result = {}; 32 | result.success = false; 33 | result.tip = err.message; 34 | result.code = 0; 35 | return result; 36 | } 37 | ); 38 | }; 39 | 40 | let postHbooker = function (options) { 41 | options.url = fix.url + options.url; 42 | let json = Object.assign({}, options.body, fix.params, allTokens.hbooker); 43 | let formData = qs.stringify(json); 44 | options.body = formData; 45 | options.headers = { 46 | "User-Agent": "Android com.kuangxiangciweimao.novel fake_server_by_koa", 47 | }; 48 | return post(options).then( 49 | (res) => { 50 | let clearRes = parse(res); 51 | let result = {}; 52 | if (clearRes.code == 100000) { 53 | result.success = true; 54 | result.data = clearRes.data; 55 | return result; 56 | } else { 57 | result.success = false; 58 | result.tip = clearRes.tip; 59 | result.code = clearRes.code; 60 | return result; 61 | } 62 | }, 63 | (err) => { 64 | let result = {}; 65 | result.success = false; 66 | result.tip = err.message; 67 | result.code = 0; 68 | return result; 69 | } 70 | ); 71 | }; 72 | 73 | let http = { 74 | getHbooker, 75 | postHbooker, 76 | }; 77 | 78 | module.exports = http; 79 | -------------------------------------------------------------------------------- /routers/hbooker/mixin/parse_res.js: -------------------------------------------------------------------------------- 1 | const decrypt = require("./decrypt"); 2 | 3 | let parse = function (res) { 4 | let clearRes = decrypt(res.trim()); 5 | clearRes = clearRes.substr(0, clearRes.lastIndexOf("}") + 1); 6 | clearRes = JSON.parse(clearRes); 7 | return clearRes; 8 | }; 9 | 10 | module.exports = parse; 11 | --------------------------------------------------------------------------------