├── .gitignore ├── README.md ├── developers ├── api-response.md ├── archive │ ├── accurate-search-game.md │ ├── business-model.md │ ├── by-release-date.md │ ├── find-character.md │ ├── find-game.md │ ├── find-org-game.md │ ├── find-org.md │ ├── find-person.md │ ├── index.md │ ├── random-game.md │ └── search-game-list.md ├── change-list.md ├── oauth.md ├── overview.md └── request-setting.md └── htmlgen ├── LICENSE ├── build.gradle.kts ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src └── main ├── kotlin └── Main.kt └── resources └── template.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | *.iml 22 | *.ipr 23 | *.iws 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | #dirs 29 | .idea/ 30 | .mvn/ 31 | out/ 32 | build/ 33 | .gradle/ 34 | target/out/ 35 | target/ 36 | gradle/ 37 | **/src/main/resources/pro/ 38 | 39 | #files 40 | HELP.md 41 | mvnw 42 | mvnw.cmd 43 | htmlgen/src/main/resources/developer.html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | YMGalgame 开发者文档相关,如果你是在一无所知的情况下进入本仓库,那么你大概不会需要它 3 | 4 | 本仓库 issues **不作** 站务反馈用 5 | 6 | 本仓库 issues 仅用于受理月幕的开放 API 相关需求、文档修订反馈 7 | 8 | 9 | # License 10 | * 本仓库代码采用 [MIT License](htmlgen/LICENSE) 11 | * 本仓库的 Markdown 文档,及其内所陈述的 API 采用 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) & [开发者须知](developers/overview.md#开发者须知) -------------------------------------------------------------------------------- /developers/api-response.md: -------------------------------------------------------------------------------- 1 | # 全局返回对象 2 | 3 | 月幕的 API Response 有一个固定的格式,具体返回的数据会作为 `data` 字段嵌套在其中,这适用于没有显示声明特例的所有情况。 4 | 5 | 后续的接口文档中,所有阐述的返回值均遵循此规则,不会额外说明在外侧包装的全局返回对象。 6 | 7 | 8 | 9 |
10 | 11 | ## API Response 12 | 13 | 在接口调用出现异常、参数检查不通过等无法正确响应时,HTTP Status 也仍然会保持 200, 错误内容在 `code` `msg` 等字段中体现。 14 | 15 | - 例外情况:认证、授权失败时,对应的 HTTP Status 同步变更为 401、403。 16 | 17 | 18 | 19 | 如果接口调用失败,`success` 字段固定为 `false` 20 | 21 | 22 | 示例&说明 23 | ```json 24 | { 25 | "success": true, // 本次调用是否成功 26 | "code": 0, // 详情见 —— 返回码 27 | "msg": "", // 接口反馈的信息,可能会为 null 28 | "data": {}// 接口实际返回的数据,可能会为 null 29 | } 30 | ``` 31 | 32 | 33 | 一个仅代表调用成功的返回值可能是这样的,没有 data,也没有 msg 34 | 35 | ```json 36 | { 37 | "success": true, 38 | "code": 0 39 | } 40 | ``` 41 | Java 模型 42 | ```java 43 | @Data 44 | public class R { 45 | 46 | private boolean success = false; 47 | 48 | private int code = RCode.SUCCESS.getCode(); 49 | 50 | private String msg; 51 | 52 | private T data; 53 | 54 | public R(int code, String msg, T data) { 55 | this.code = code; 56 | this.msg = msg; 57 | this.data = data; 58 | 59 | this.success = RCode.SUCCESS.valueIs(code); 60 | } 61 | } 62 | ``` 63 | 64 | Kotlin 模型 65 | ```kotlin 66 | data class R( 67 | var code: Int = RCode.SUCCESS.code, 68 | var data: T? = null, 69 | var message: String? = null 70 | ) { 71 | 72 | var success: Boolean = true 73 | 74 | init { 75 | success = this.code == RCode.SUCCESS.code 76 | } 77 | } 78 | ``` 79 | TS 模型 80 | ```typescript 81 | declare interface R { 82 | success: boolean; 83 | code: RCode; 84 | msg?: string; 85 | data?: T; 86 | } 87 | ``` 88 | 89 |
90 | 91 | 92 | ## Page 93 | 94 | 如果您请求的是一个分页接口,那么默认返回值的 data 格式如下所示 95 | 96 | 请求的列表会作为数组保存在 result 字段中 97 | 98 | ```json 99 | { 100 | "result": [], // 响应的列表 101 | "total": 0, // 可供查询的总数 102 | "hasNext": false, // 是否有下一页 103 | "pageNum": 1, // 当前页 104 | "pageSize": 10 // 页大小 105 | } 106 | ``` 107 | 108 | 我们访问一个分页接口,得到的最终 HTTP Response Body 完整形态会长成这样 109 | ```json 110 | { 111 | "success": true, 112 | "code": 0, 113 | "data": { 114 | "result": [ 115 | { 116 | "name": "月に寄りそう乙女の作法", 117 | "chineseName": "近月少女的礼仪" 118 | }, 119 | { 120 | "name": "月に寄りそう乙女の作法2", 121 | "chineseName": "近月少女的礼仪2" 122 | } 123 | ], 124 | "total": 2, 125 | "hasNext": false, 126 | "pageNum": 1, 127 | "pageSize": 10 128 | } 129 | } 130 | ``` 131 | 132 |
133 | 134 | 135 | ## 全局返回码 136 | 返回码是全局返回对象中的 `code` 字段,标识了这个响应的状态 137 | 138 | 当 `code === 0` 时,返回对象中的 `success` 字段恒定为 `true` 139 | 140 | | code | name | description | 141 | |------|------------------|-----------------------------------| 142 | | 0 | SUCCESS | 调用成功,如果不是此响应码的话,数据肯定是拿不到的,需要自行检查 | 143 | | 7 | OTHER | 其他 | 144 | | 30 | TIME_OUT | 超时 | 145 | | 50 | SYSTEM_ERROR | 系统内部错误 | 146 | | 51 | ILLEGAL_VERSION | 非法版本 | 147 | | 401 | UNAUTHORIZED | 未经认证,这个code会改变 HTTP Status >> 401 | 148 | | 403 | FORBIDDEN | 未经授权,这个code会改变 HTTP Status >> 403 | 149 | | 404 | NOT_FOUNT | 找不到 | 150 | | 429 | TOO_MANY_REQUEST | 请求过多 | 151 | | 614 | ILLEGAL_PARAM | 非法参数,校验失败时会遇到 | 152 | 153 | Java 模型 154 | ```java 155 | public enum RCode { 156 | SUCCESS(0),//成功 157 | OTHER(7),//其他 158 | TIME_OUT(30),//超时 159 | SYSTEM_ERROR(50),//系统内部错误 160 | ILLEGAL_VERSION(51),//非法版本 161 | UNAUTHORIZED(401),//未经认证 162 | FORBIDDEN(403), // 未经授权 163 | NOT_FOUNT(404), // 找不到 164 | TOO_MANY_REQUEST(429), //请求过多 165 | ILLEGAL_PARAM(614);//非法参数 166 | 167 | private int code; 168 | 169 | RCode(int code) { 170 | this.code = code; 171 | } 172 | 173 | public int getCode() { 174 | return code; 175 | } 176 | } 177 | ``` 178 | 179 | Kotlin 模型 180 | ```kotlin 181 | enum class RCode(val code: Int, val defaultMessage: String) { 182 | SUCCESS(0, ""), 183 | OTHER(7, "未知错误"), 184 | TIME_OUT(30, "超时"), 185 | SYSTEM_ERROR(50, "系统内部错误"), 186 | ILLEGAL_VERSION(51, "非法版本"), 187 | INITIALIZATION_FAILED(52, "初始化失败"), 188 | UNAUTHORIZED(401, "未经认证"), 189 | FORBIDDEN(403, "未经授权"), 190 | NOT_FOUND(404, "找不到"), 191 | TOO_MANY_REQUEST(429, "请求过多"), 192 | ILLEGAL_PARAM(614, "非法参数"); 193 | } 194 | ``` 195 | 196 | TS模型 197 | ```typescript 198 | declare const enum RCode { 199 | SUCCESS = 0, // 成功 200 | OTHER = 7, // 其他 201 | TIME_OUT = 30, // 超时 202 | SYSTEM_ERROR = 50, // 系统内部错误 203 | ILLEGAL_VERSION = 51, // 非法版本 204 | UNAUTHORIZED = 401, // 未经认证 205 | FORBIDDEN = 403, // 未经授权 206 | NOT_FOUNT = 404, // 找不到 207 | TOO_MANY_REQUEST = 429, // 请求过多 208 | ILLEGAL_PARAM = 614// 非法参数 209 | } 210 | ``` 211 | 212 |
213 | 214 | ## 注意事项 215 | * 出于对某些开发语言、环境的礼貌,以及敬畏,月幕的所有接口中,若出现数字精度大于 2^53 的情况(比如有些模型是通过 SnowFlake 算法生成的 ID),接口中返回的所有 **数字长整型**(int64) 都会转变为 **字符串**, 这可能会使您感到疑惑,实际是正常的,一般情况下您不需要为此付出额外的心智负担, 各语言主流的 JSON 处理库不会因此出现异常。 216 | 217 |
-------------------------------------------------------------------------------- /developers/archive/accurate-search-game.md: -------------------------------------------------------------------------------- 1 | # 搜索游戏-精确 2 | 3 | 精确搜索游戏,直接返回对应目标 **详情** 4 | 5 | 具体的来说,此接口要求传入一个尽可能符合游戏名(或游戏中文名)的关键字以供查询 6 | 7 | 对于单个档案而言,将同时判定原名与中文名,取高值 8 | 9 | 本接口命中到复数档案时,返回名称相似度最高的数据 10 | 11 | > keyword 匹配根据 [Trigram](https://lhncbc.nlm.nih.gov/ii/tools/MTI/trigram.html) 算法实现 12 | 13 |
14 | 15 | 此接口并不能保证匹配精确性,只能说,它在满足以下条件时,是可靠的 16 | 17 | * 入参 keyword 是游戏的全名,如:月に寄りそう乙女の作法、近月少女的礼仪 18 | * 游戏的全名所对应的游戏档案只有 1 个 19 | 20 | 如果你的入参 keyword 是游戏别名、简称、外号等,大概率你是查不到的,除非它正好和某个游戏名的匹配度较高 21 | 22 | 23 | 24 |
25 | 26 | ## Resource Path 27 | 28 | `GET /open/archive/search-game` 29 | 30 | `version: 1` 31 | 32 |
33 | 34 | ## Request Parameter 35 | 36 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 37 | |-----|--------|-----|-----|-----|---------------------------| 38 | | mode | string | | ✅ | | 固定值: `accurate`, 请直接传入此参数 | 39 | | keyword | string | | ✅ | length < 96 | 查询关键字 | 40 | | similarity | int32 | | ✅ | 取值范围 50~99 | 百分比相似度,只会定位到相似度比此参数更高的数据 | 41 | 42 |
43 | 44 | ## Request Body 45 | 无 46 | 47 |
48 | 49 | ## Response Body 50 | ```json 51 | { 52 | "game": {}, 53 | "cidMapping": { 54 | "19366": { 55 | "cid": 19366, 56 | "name": "桜小路 ルナ", // 此档案名称,原名 57 | "mainImg": "", // 一个完整的图片路径 58 | "state": "PUBLIC_PUBLISHED", // 此档案状态 59 | "freeze": false // 是否属于一个被冻结的档案 60 | } 61 | }, 62 | "pidMapping": { 63 | "10018": { 64 | "pid": 10018, 65 | "name": "橋本 みゆき", 66 | "mainImg": "", 67 | "state": "PUBLIC_PUBLISHED", 68 | "freeze": false 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | **说明:** 75 | 76 | * game 字段数据和 [档案业务模型](business-model.md) 中定义的 Game 模型一致,内部嵌套了 character、staff 等关联关系 77 | * cidMapping 是一个映射表`(cid -> data)`,返回此游戏涉及到的所有 character 基础信息 78 | * pidMapping 是一个映射表`(pid -> data)`,返回此游戏涉及到的所有 person 基础信息 79 | 80 |
81 | 82 | **找不到的 Response body(完整)** 83 | ```json 84 | { 85 | "success": false, 86 | "code": 614, 87 | "msg": "根据关键字找不到对应档案" 88 | } 89 | ``` 90 | -------------------------------------------------------------------------------- /developers/archive/business-model.md: -------------------------------------------------------------------------------- 1 | # 档案业务模型 2 | 3 | 业务对象建模,在 Archive 模块中有 4 种平级的档案模型,他们互相之间通过某些形式约定了多对多的关系 4 | 5 | * Game,游戏 6 | * Organization,机构 7 | * Character,角色 8 | * Person,真实人物 9 | 10 |
11 | 12 | 其中, 游戏档案 (Game) **包含**以下实体关系, 一对多 13 | 14 | ``` 15 | characters - CharacterRelation[] 16 | staff - Staff[] 17 | releases - Release[] 18 | ``` 19 | 20 | 并且通过以下字段和其他的模型产生关联, 多对多 21 | 22 | ``` 23 | developerId - Organization 24 | staff - Staff#pid - Person 25 | characters - CharacterRelation#cid - Character 26 | characters - CharacterRelation#cvid - Person 27 | ``` 28 | 29 | 游戏之外的几种模型都比较简单,查看对应小节的字段解释与 JSON 样例即可 30 | 31 |
32 |
33 | 34 | ## Archive 35 | 首先请先看一下这个抽象的档案模型,本模型定义了月幕中档案的公用基本参数 36 | 37 | Game、Organization、Character、Person 均属于该模型的一种具体实现 38 | 39 | 虽然在后端的设计中它们之间并不是一个直接的继承或组合关系,但是在 API 对接时,你可以理解为上述四种档案模型均是继承自 Archive 的子类,这与请求、响应的现状是相符的 40 | 41 | 42 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 43 | |---------------|------------------|-------|--------|---------------------|------------------------------------------| 44 | | name | string | | 档案名 | 月に寄りそう乙女の作法 | 可能是英文、中文,反正原名是什么就是什么 | 45 | | chineseName | string | ✅ | 档案中文名 | 大藏游星 | 这个是汉化名。若本身原名即是中文,那么这里是可能没有的 | 46 | | extensionName | ExtensionName[] | | 拓展名 | [] | 一般来说是外号、别称、还有各个国家的不同翻译 | 47 | | introduction | string | | 简介、说明 | | | 48 | | state | ArchiveStateEnum | | 档案状态 | FREEZE | 标识状态的,可以等同于string | 49 | | weights | int32 | | 权重 | 0 | | 50 | | mainImg | string | ✅ | 主图 | | 在作为接口返回值时是不会为null的,检查到null时会自动设置为默认的图片地址 | 51 | | moreEntry | MoreEntry[] | | 额外的键值对 | [] | 一般用来放置额外信息 | 52 | | publishVersion | int32 | | 发布版本 | 0 | | 53 | | publishTime | datetime | ✅ | 发布时间 | 2023-08-07 00:01:50 | === string | 54 | | publisher | int64 | ✅ | 发布者 | | | 55 | 56 | ExtensionName、MoreEntry 均属于单纯为了承载信息的内容字段,并不与任何档案之间具备任何关联关系,在本章节最后面统一列出详细信息 57 | 58 |
59 | 60 | ### ArchiveStateEnum 61 | 62 | ArchiveStateEnum 是一个枚举,标识了这个档案处于什么状态,不同的状态可能需要不同的访问权限才可以查看。 63 | 64 | 共三种类型: 65 | * FREEZE 66 | * PROTECTED_PUBLISHED 67 | * PUBLIC_PUBLISHED 68 | 69 | 绝大部分接口中接入者都不需要关心这个东西 70 | 71 |
72 |
73 | 74 | 75 | 76 | ## Game 77 | 78 | 游戏档案,JSON示例请查看最后一节 79 | 80 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 81 | |-------------|---------------------|-----|-----------| ------------------------------ |------------------------------| 82 | | gid | int64 | | 游戏档案ID | 31147 | https://www.ymgal.games/ga31147 | 83 | | developerId | int64 | | 游戏品牌的机构ID | | 关联关系用, 等同于orgId | 84 | | haveChinese | boolean | | 有中文版 | true | | 85 | | typeDesc | string | | 游戏类型描述 | バトルADV | | 86 | | releaseDate | Date | ✅ | 发售日期 | 2020-01-01 | string:yyyy-MM-dd, 指的是正式发售日期 | 87 | | restricted | boolean | | 限制级 | false | 带有露点、暴力等内容为true | 88 | | country | string | ✅ | 国家 | | | 89 | | characters | CharacterRelation[] | | 出场人物 | | 主角配角们,关联的Charater | 90 | | releases | Release[] | | 发行版本 | | 该游戏发行过的版本 | 91 | | website | Website[] | | 相关网站 | | 官网、售卖网站等 | 92 | | staff | Staff[] | | 制作员工 | | 声优、画师、脚本等人力资源。 | 93 | 94 | Website 属于单纯为了承载信息的内容字段,在本章节最后面统一列出详细信息 95 | 96 |
97 | 98 | ### CharacterRelation 99 | 100 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 101 | | -------- |--------|-----|----------| ---- |------------------------| 102 | | cid | int64 | | 角色ID | | Character#cid | 103 | | cvId | int64 | ✅ | 配音员的人物ID | | Person#pid, 一个角色可以没有配音 | 104 | | characterPosition | number | | 角色定位 | 1 | 1=主角 2=配角 | 105 | 106 | 107 | 108 | ### Release 109 | 110 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 111 | |-------------------|--------|-----| ---------- | ------------------------------------------ |-----| 112 | | id | int64 | | 发布名称 | | 主键 | 113 | | release_name | string | | 发布名称 | ISLAND 体験版 | | 114 | | related_link | string | ✅ | 相关链接 | http://never-island.com/game/download.html | | 115 | | platform | string | | 发行平台 | PS4 、PC、 PS Vita、 Nintendo Switch | | 116 | | release_date | Date | ✅ | 发售日期 | 2020-01-01 | string:yyyy-MM-dd | 117 | | restriction_level | string | | 限制级等级 | All ages、 15+ | | 118 | | country | string | | 发售国家 | Chinese、English、Japanese | | 119 | 120 | 121 | 122 | ### Staff 123 | 124 | 游戏制作人员 125 | 126 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 127 | |----------| ------ |-----|-------------| -------------- |-----------------------------------| 128 | | sid | int64 | | | | 主键 | 129 | | pid | int64 | ✅ | 真实人物的pid | | 虽然有staff名字,但是不晓得是对应哪个人的话,这个字段就为空咯 | 130 | | job_name | string | | 职位名 | 原画设计、剧本 | 该staff的担当职责 | 131 | | desc | string | ✅ | 跟在员工名称后面的备注 | | | 132 | | emp_name | string | | 员工名称 | | **参与名**,不一定是真名 | 133 | 134 | 135 |
136 |
137 | 138 | ## Organization 139 | 140 | 组织机构,可能是发行商、开发商、工作室等等等等 141 | 142 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 143 | | -------------- |-----------|-----|------| ---------- |-----------------------------------------------------------| 144 | | orgId | int64 | | 主键 | 10041 | https://www.ymgal.games/oa10041 | 145 | | country | string | ✅ | 国家 | | | 146 | | website | Website[] | | 相关网站 | | 官方推特、官网等 | 147 | | birthday | Date | ✅ | 建立日期 | 2020-01-01 | string:yyyy-MM-dd,有时可能会出现 Y >= 3000的情况,这一般是因为不知道具体的年份才设置的 | 148 | 149 |
150 |
151 | 152 | 153 | ## Character 154 | 155 | 角色档案 156 | 157 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 158 | |----------------|------------|-----|-----------| ---------- |-------------------------| 159 | | cid | int64 | | 主键 | 19366 | https://www.ymgal.games/ca19366 | 160 | | gender | GenderEnum | | 性别 | 1 | number | 161 | | birthday | Date | ✅ | 生日 | 2201-01-01 | 同 Organization#birthday | 162 | 163 |
164 | 165 | ### GenderEnum 166 | 167 | 这个枚举也会在人那里用到,虽然真实人物没有扶她 = = 168 | 169 | Java 170 | ```java 171 | public enum GenderEnum { 172 | 173 | UNKNOWN(0, "未知"), 174 | MAN(1, "男"), 175 | WOMAN(2, "女"), 176 | // ふたなり 177 | FUTANARI(3, "扶她"); 178 | 179 | private int code; 180 | private String desc; 181 | 182 | GenderEnum(int code, String desc) { 183 | this.code = code; 184 | this.desc = desc; 185 | } 186 | } 187 | ``` 188 | 189 | TypeScript 190 | ```typescript 191 | export enum Gender { 192 | UNKNOWN = 0, //"未知" 193 | MAN = 1, //男 194 | WOMAN = 2,//女 195 | // ふたなり 196 | FUTANARI = 3,//扶她 197 | } 198 | ``` 199 | 200 |
201 |
202 | 203 | 204 | 205 | ## Person 206 | 207 | 现实人类个体 208 | 209 | 需要注意的是 Person 档案的 name 并不一定就是那个人的真名。 自称、约定速成的名字 都可能存在 210 | 211 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 212 | |----------|-----------|-----|------|-----------|----------------------| 213 | | pid | int64 | | 主键 | 10150 | https://www.ymgal.games/pa10150 | 214 | | country | string | ✅ | 国籍 | | | 215 | | birthday | Date | ✅ | 生日 | 2001-01-01 | 同 Character#birthday | 216 | | gender | GenderEnum | | 性别 | 1 | 同 Character#gender | 217 | | website | Website[] | | 相关网站 | [] | | 218 | 219 | 220 | 221 |
222 |
223 | 224 | ## 内容字段建模 225 | 226 | 227 | ### ExtensionName 228 | 229 | 拓展名称字段 230 | 231 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 232 | | ------ | ------ |----------| -------- | ------------------------------ | ------------------ | 233 | | name | string | | 名称 | アイランド、ISLAND | | 234 | | type | string | ✅ | 类型描述 | 中文译名、英文译名、别名、简称 | 描述就是中文描述 | 235 | | desc | string | ✅ | 备注 | | 跟在名称后面的备注 | 236 | 237 | 238 | ### MoreEntry 239 | 240 | 额外键值对, 补充说明用 241 | 242 | 有的编辑老哥会额外加些东西,比如: 243 | 244 | * 18X补丁发布时间:xxx 245 | * 特殊码:xxx 246 | * 发售价格:xxx 247 | * 等等…… 248 | 249 | | 字段名 | 类型 | nullable | 250 | |-------| ------ |----------| 251 | | key | string | | 252 | | value | string | | 253 | 254 | ### Website 255 | 256 | 和档案相关的网站 257 | 258 | | 字段名 | 类型 | nullable | 说明 | 例子 | 备注 | 259 | | ------ | ------ |----------| ------- | --------------------- | ---- | 260 | | title | string | | 标题 | wiki、官方网站 | | 261 | | link | string | | 网站URL | https://www.baidu.com | | 262 | 263 | 264 |
265 |
266 | 267 | --- 268 | 269 | ## JSON Example (Game) 270 | 271 | ```json 272 | { 273 | "publishVersion": 0, 274 | "publishTime": "2021-08-07 00:00:00", 275 | "name": "月に寄りそう乙女の作法", 276 | "chineseName": "近月少女的礼仪", 277 | "extensionName": [ 278 | { 279 | "name": "つり乙", 280 | "type": "", 281 | "desc": "" 282 | }, 283 | { 284 | "name": "Tsuriotsu", 285 | "type": "", 286 | "desc": "" 287 | } 288 | ], 289 | "introduction": "主人公大藏游星,作为代表了日本财界的“华丽一族”的大藏家的末端......", 290 | "state": "PUBLIC_PUBLISHED", 291 | "weights": 0, 292 | "mainImg": "http://store.dev-ymgal.com/archive/main/3d/3d99dc3f2888479f98eadb3501c50713.webp", 293 | "moreEntry": [], 294 | "gid": 31147, 295 | "developerId": 10041, 296 | "haveChinese": true, 297 | "typeDesc": "", 298 | "releaseDate": "2012-10-26", 299 | "restricted": true, 300 | "country": "", 301 | "website": [ 302 | { 303 | "title": "官中网站", 304 | "link": "https://hikarifield.co.jp/tsukiniyorisou/" 305 | } 306 | ], 307 | "characters": [ 308 | { 309 | "cid": 19365, 310 | "cvId": 11333, 311 | "characterPosition": 1 312 | }, 313 | { 314 | "cid": 19366, 315 | "cvId": 10954, 316 | "characterPosition": 2 317 | } 318 | ], 319 | "releases": [ 320 | { 321 | "id": 110064, 322 | "releaseName": "近月少女的礼仪", 323 | "relatedLink": "https://hikarifield.co.jp/tsukiniyorisou/", 324 | "platform": "Windows", 325 | "releaseLanguage": "Chinese", 326 | "restrictionLevel": "全年龄" 327 | }, 328 | { 329 | "id": 110065, 330 | "releaseName": "A moon a sun and three macaroons", 331 | "relatedLink": "https://drive.google.com/file/d/1vTyy7AY0MG6RWJmi-qLsgPERjmm44WiG/view?usp=sharing", 332 | "platform": "Windows", 333 | "releaseDate": "2021-07-05", 334 | "releaseLanguage": "English", 335 | "restrictionLevel": "未分级" 336 | } 337 | ], 338 | "staff": [ 339 | { 340 | "sid": 126376, 341 | "pid": 11083, 342 | "empName": "鈴平 ひろ", 343 | "empDesc": "", 344 | "jobName": "原画" 345 | }, 346 | { 347 | "sid": 126377, 348 | "pid": 11236, 349 | "empName": "西又 葵", 350 | "empDesc": "", 351 | "jobName": "原画" 352 | } 353 | ], 354 | "type": "GAME", 355 | "freeze": false 356 | } 357 | ``` 358 | -------------------------------------------------------------------------------- /developers/archive/by-release-date.md: -------------------------------------------------------------------------------- 1 | # 查询日期区间内发行的游戏 2 | 3 | 传入一个日期区间,查询出发行时间在该区间内的游戏 4 | 5 | 一般可用作查询 本月新作、上月新作 等 6 | 7 | 8 |
9 | 10 | ## Resource Path 11 | 12 | `GET /open/archive/game ` 13 | 14 | `version: 1` 15 | 16 |
17 | 18 | ## Request Parameter 19 | 20 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 21 | |-----|------|-----|-----|------------|---------------| 22 | | releaseStartDate | Date | | ✅ | yyyy-MM-dd | = string,开始时间 | 23 | | releaseEndDate | Date | | ✅ | yyyy-MM-dd | = string,结束时间 | 24 | 25 | 1. 开始时间不能在结束时间之后 26 | 2. 区间内的天数不能超过 50 27 | 3. 开始时间必须在 1980 年之后 28 | 4. 开始时间不能超过当前五年,假设今年是2020年,则 releaseStartDate 不能是 2026-01-01 29 | 30 |
31 | 32 | ## Request Body 33 | 34 | 无 35 | 36 |
37 | 38 | ## Response Body 39 | 40 | 正确返回的话内容不会为 null,如指定区间内不存在发行游戏则返回空数组 41 | 42 | ```json 43 | [ 44 | { 45 | "restricted": false, 46 | "orgName": "", 47 | "gid": 0, 48 | "developerId": 0, 49 | "name": "", 50 | "chineseName": "", 51 | "haveChinese": false, 52 | "mainImg": "", 53 | "releaseDate": "2024-05-13", 54 | "state": "FREEZE" 55 | } 56 | ] 57 | ``` 58 | -------------------------------------------------------------------------------- /developers/archive/find-character.md: -------------------------------------------------------------------------------- 1 | # 查询角色详情 2 | 3 | 通过 cid 查询单个游戏出场角色的详情 4 | 5 | 6 |
7 | 8 | ## Resource Path 9 | 10 | `GET /open/archive` 11 | 12 | `version: 1` 13 | 14 |
15 | 16 | ## Request Parameter 17 | 18 | 19 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 20 | |-----|-------|-----|-----|-----|------| 21 | | cid | int64 | | ✅ | | 角色ID | 22 | 23 |
24 | 25 | ## Request Body 26 | 无 27 | 28 |
29 | 30 | ## Response Body 31 | ```json 32 | { 33 | "character": { 34 | "publishVersion": 4, 35 | "publishTime": "2021-10-05 22:38:56", 36 | "publisher": 223, 37 | "name": "小倉 朝日", 38 | "chineseName": "小仓朝日", 39 | "extensionName": [ 40 | { 41 | "name": "Ookura Yuusei", 42 | "type": "", 43 | "desc": "" 44 | }, 45 | { 46 | "name": "大蔵 遊星", 47 | "type": "", 48 | "desc": "" 49 | }, 50 | { 51 | "name": "大藏游星", 52 | "type": "", 53 | "desc": "" 54 | } 55 | ], 56 | "introduction": "伪装起性别与身份,樱小路露娜的女仆……", 57 | "state": "PUBLIC_PUBLISHED", 58 | "weights": 0, 59 | "mainImg": "https://store.ymgal.games/archive/main/c9/c92c1ceafcc54ec9a182655faedae453.jpg", 60 | "moreEntry": [], 61 | "cid": 19365, 62 | "gender": 1, 63 | "birthday": "3000-01-06", 64 | "type": "CHARACTER", 65 | "freeze": false 66 | } 67 | } 68 | ``` 69 | 70 | **说明:** 71 | * character 字段数据和 [档案业务模型](business-model.md) 中定义的 Character 模型一致 72 | 73 |
74 | -------------------------------------------------------------------------------- /developers/archive/find-game.md: -------------------------------------------------------------------------------- 1 | # 查询游戏详情 2 | 3 | 通过 gid 查询单个游戏的详情 4 | 5 | 和**搜索游戏-精确**这个接口的返回值一模一样 6 | 7 | 除非你是瞎几把传的 gid, 否则不会出现 NOT FOUND 的错误 8 | 9 |
10 | 11 | 12 | [https://www.ymgal.games/GA31147](https://www.ymgal.games/GA31147) 13 | 14 | GA31147 === Game archive with id 31147 15 | 16 |
17 | 18 | ## Resource Path 19 | 20 | `GET /open/archive` 21 | 22 | `version: 1` 23 | 24 |
25 | 26 | ## Request Parameter 27 | 28 | 29 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 30 | |-----|-------|-----|-----|-----|------| 31 | | gid | int64 | | ✅ | | 游戏ID | 32 | 33 |
34 | 35 | ## Request Body 36 | 无 37 | 38 |
39 | 40 | ## Response Body 41 | ```json 42 | { 43 | "game": {}, 44 | "cidMapping": { 45 | "19366": { 46 | "cid": 19366, 47 | "name": "桜小路 ルナ", // 此档案名称,原名 48 | "mainImg": "", // 一个完整的图片路径 49 | "state": "PUBLIC_PUBLISHED", // 此档案状态 50 | "freeze": false // 是否属于一个被冻结的档案 51 | } 52 | }, 53 | "pidMapping": { 54 | "10018": { 55 | "pid": 10018, 56 | "name": "橋本 みゆき", 57 | "mainImg": "", 58 | "state": "PUBLIC_PUBLISHED", 59 | "freeze": false 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | **说明:** 66 | 67 | * game 字段数据和 [档案业务模型](business-model.md) 中定义的 Game 模型一致,内部嵌套了 character、staff 等关联关系 68 | * cidMapping 是一个映射表`(cid -> data)`,返回此游戏涉及到的所有 character 基础信息 69 | * pidMapping 是一个映射表`(pid -> data)`,返回此游戏涉及到的所有 person 基础信息 70 | 71 |
72 | -------------------------------------------------------------------------------- /developers/archive/find-org-game.md: -------------------------------------------------------------------------------- 1 | # 查询机构下的游戏 2 | 3 | 指定机构 4 | 5 | 返回所有该机构下的游戏,没有则为空数组 6 | 7 | 8 |
9 | 10 | ## Resource Path 11 | 12 | `GET /open/archive/game ` 13 | 14 | `version: 1` 15 | 16 |
17 | 18 | ## Request Parameter 19 | 20 | 21 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 22 | |-----|-------|-----|-----|-----|------| 23 | | orgId | int64 | | ✅ | | 机构ID | 24 | 25 |
26 | 27 | ## Request Body 28 | 无 29 | 30 |
31 | 32 | ## Response Body 33 | ```json 34 | [ 35 | { 36 | "gid": 0, 37 | "developerId": 0, 38 | "name": "", 39 | "chineseName": "", 40 | "haveChinese": false, 41 | "mainImg": "", 42 | "releaseDate": "2024-05-13", 43 | "state": "FREEZE" 44 | } 45 | ] 46 | ``` 47 | -------------------------------------------------------------------------------- /developers/archive/find-org.md: -------------------------------------------------------------------------------- 1 | # 查询机构详情 2 | 3 | 通过 orgId 查询单个机构的详情 4 | 5 | 6 |
7 | 8 | ## Resource Path 9 | 10 | `GET /open/archive` 11 | 12 | `version: 1` 13 | 14 |
15 | 16 | ## Request Parameter 17 | 18 | 19 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 20 | |-------|-------|-----|-----|-----|------| 21 | | orgId | int64 | | ✅ | | 机构ID | 22 | 23 |
24 | 25 | ## Request Body 26 | 无 27 | 28 |
29 | 30 | ## Response Body 31 | ```json 32 | { 33 | "org": { 34 | "publishVersion": 4, 35 | "publishTime": "2024-05-11 23:00:42", 36 | "publisher": 1, 37 | "name": "枕", 38 | "chineseName": "枕社", 39 | "extensionName": [ 40 | { 41 | "name": "まくら", 42 | "type": "", 43 | "desc": "" 44 | }, 45 | { 46 | "name": "Makura", 47 | "type": "", 48 | "desc": "" 49 | } 50 | ], 51 | "introduction": "枕,是一家日本视觉小说制作工作室……", 52 | "state": "PUBLIC_PUBLISHED", 53 | "weights": 0, 54 | "mainImg": "https://store.ymgal.games/archive/main/00/00946e721af7447fa8e5028f7950ca6b.webp", 55 | "moreEntry": [], 56 | "orgId": 10270, 57 | "country": "ja", 58 | "website": [ 59 | { 60 | "title": "wikidata", 61 | "link": "https://en.wikidata.org/wiki/Q6006086" 62 | }, 63 | { 64 | "title": "homepage", 65 | "link": "http://www.makura-soft.com/" 66 | } 67 | ], 68 | "type": "ORGANIZATION", 69 | "freeze": false 70 | } 71 | } 72 | ``` 73 | 74 | **说明:** 75 | * org 字段数据和 [档案业务模型](business-model.md) 中定义的 Organization 模型一致 76 | 77 |
78 | -------------------------------------------------------------------------------- /developers/archive/find-person.md: -------------------------------------------------------------------------------- 1 | # 查询人物详情 2 | 3 | 通过 pid 查询单个人物的详情 4 | 5 | 6 |
7 | 8 | ## Resource Path 9 | 10 | `GET /open/archive` 11 | 12 | `version: 1` 13 | 14 |
15 | 16 | ## Request Parameter 17 | 18 | 19 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 20 | |-----|-------|-----|-----|-----|------| 21 | | pid | int64 | | ✅ | | 人物ID | 22 | 23 |
24 | 25 | ## Request Body 26 | 无 27 | 28 |
29 | 30 | ## Response Body 31 | ```json 32 | { 33 | "person": { 34 | "publishVersion": 1, 35 | "publishTime": "2022-10-20 02:04:06", 36 | "publisher": 6228, 37 | "name": "高野直子", 38 | "chineseName": "高野直子", 39 | "extensionName": [ 40 | { 41 | "name": "川村みどり", 42 | "type": "马甲", 43 | "desc": "" 44 | }, 45 | { 46 | "name": "雅姫乃", 47 | "type": "马甲", 48 | "desc": "" 49 | } 50 | ], 51 | "introduction": "高野直子(1968年6月18日 - ),是日本的女性声优……", 52 | "state": "PUBLIC_PUBLISHED", 53 | "weights": 0, 54 | "mainImg": "https://store.ymgal.games/archive/main/d6/d6ba6218f7bd43a081978c6ff2c4ffc3.webp", 55 | "moreEntry": [], 56 | "pid": 10008, 57 | "country": "ja", 58 | "birthday": "1968-06-16", 59 | "gender": 2, 60 | "website": [ 61 | { 62 | "title": "事务所个人页", 63 | "link": "https://sigma7.co.jp/actors/takano_naoko" 64 | }, 65 | { 66 | "title": "个人博客", 67 | "link": "http://naokoblog.exblog.jp/" 68 | } 69 | ], 70 | "type": "PERSON", 71 | "freeze": false 72 | } 73 | } 74 | ``` 75 | 76 | **说明:** 77 | * person 字段数据和 [档案业务模型](business-model.md) 中定义的 Person 模型一致 78 | 79 |
80 | -------------------------------------------------------------------------------- /developers/archive/index.md: -------------------------------------------------------------------------------- 1 | # ⭐ 档案业务API -------------------------------------------------------------------------------- /developers/archive/random-game.md: -------------------------------------------------------------------------------- 1 | # 随机游戏 2 | 3 | 随机出一个游戏列表,可指定数量 4 | 5 | 经过默认的筛选,不会返回一些特别小众的、未经过修订的游戏 6 | 7 | 8 |
9 | 10 | ## Resource Path 11 | 12 | `GET /open/archive/random-game ` 13 | 14 | `version: 1` 15 | 16 |
17 | 18 | ## Request Parameter 19 | 20 | 21 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 22 | |-----|-------|-----|-----|----------|---------| 23 | | num | int32 | | ✅ | 取值范围1~10 | 要随机出的数量 | 24 | 25 |
26 | 27 | ## Request Body 28 | 无 29 | 30 |
31 | 32 | 33 | ## Response Body 34 | 35 | 本接口必定能返回符合期望的数据条数 36 | 37 | ```json 38 | [ 39 | { 40 | "gid": 0, 41 | "developerId": 0, 42 | "name": "", 43 | "chineseName": "", 44 | "haveChinese": false, 45 | "mainImg": "", 46 | "releaseDate": "2024-05-13", 47 | "state": "FREEZE" 48 | } 49 | ] 50 | ``` 51 | -------------------------------------------------------------------------------- /developers/archive/search-game-list.md: -------------------------------------------------------------------------------- 1 | # 搜索游戏列表 2 | 3 | 同月幕Galgame主站搜索功能。 4 | 5 | 传入查询 keyword 时,此接口会根据以下要素命中数据: 6 | 7 | * 中文名与原名的完全匹配 8 | * 与中文名或原名相似度 > 0.5 9 | * keyword 分词后,根据倒排索引命中的文本向量,按权重大小直出 10 | 11 | 以上优先度依次递减。 12 | 13 |
14 | 15 | 同时此接口支持别名、外号、圈内称呼等查询。 16 | 17 | 比如:罚抄 18 | 19 | 可以命中:Rewrite(改写) 20 | 21 | 当然,能查 != 查的对,查询不等式秒了。 22 | 23 | 24 |
25 | 26 | ## Resource Path 27 | 28 | `GET /open/archive/search-game` 29 | 30 | `version: 1` 31 | 32 |
33 | 34 | ## Request Parameter 35 | 36 | | 参数名 | 类型 | 默认值 | 必要 | 校验 | 说明 | 37 | |-----|--------|-----|-----|-------------|-----------------------| 38 | | mode | string | | ✅ | | 固定值: `list`, 请直接传入此参数 | 39 | | keyword | string | | ✅ | length < 96 | 查询关键字 | 40 | | pageNum | int32 | | ✅ | 大于 0 | 页号 | 41 | | pageSize | int32 | | ✅ | 取值范围 1~20 | 页大小 | 42 | 43 |
44 | 45 | ## Request Body 46 | 无 47 | 48 |
49 | 50 | ## Response Body 51 | 52 | 此接口返回一个 Page,下方示例仅为 Page 中列表的单条数据 53 | 54 | 字段说明请与档案API章节的档案模型对照,这里并没有需要额外注意的参数, 仅需注意返回值中每条数据的 id 等于 gid 即可。 55 | 56 | 57 | ```json 58 | { 59 | "orgId": 0, 60 | "orgName": "", 61 | "releaseDate": "2024-05-10", 62 | "haveChinese": false, 63 | "id": 0, // gid 64 | "name": "", 65 | "chineseName": "", 66 | "state": "FREEZE", 67 | "weights": 0, 68 | "mainImg": "", 69 | "publishVersion": 0, 70 | "publishTime": "2024-05-10 16:15:16", 71 | "publisher": 0, 72 | "score": "" 73 | } 74 | ``` -------------------------------------------------------------------------------- /developers/change-list.md: -------------------------------------------------------------------------------- 1 | # Change list 2 | 3 | ### 2025-04-12 4 | * 移动端适配 5 | 6 | ### 2024-06-23 7 | * API:查询机构详情 8 | * API:查询角色详情 9 | * API:查询人物详情 10 | 11 | ### 2024-05-15 12 | 13 | 经过观察,较稳定,正式发布 14 | 15 | * API:查询机构下的游戏 16 | * API:查询日期区间内发行的游戏 17 | * API:随机游戏 18 | 19 | ### 2024-05-11 20 | 21 | 预发布,稳定性及易用性待考察 22 | 23 | * OAuth2.0 认证接口已测试 24 | * API:搜索游戏-精确 25 | * API:搜索游戏列表 26 | * API:查询游戏详情 27 | * HTML 生成稳定 -------------------------------------------------------------------------------- /developers/oauth.md: -------------------------------------------------------------------------------- 1 | # 获取 Access Token 2 | 3 | access_token 是月幕开放API的全局唯一接口调用凭据,调用各接口时都需使用 access_token, 开发者需要进行妥善保存。 4 | 5 | access_token 的有效期为 1 个小时,通过相同的认证参数在有效期内重复获取时,将获得重复的 access_token。 access_token 有效期未受到完全的保障,所以请 **不要使用定时器** 实现更新逻辑,而是在 **拒绝策略** 中实现。 6 | 7 |
8 | 9 | ## 基本概念 10 | 11 | 月幕的开放API基于 OAuth2.0 认证协议,您或许会需要以下文档对此协议进行深入了解: 12 | 13 | * [https://www.oauth.com/](https://www.oauth.com/) 14 | 15 | * [https://datatracker.ietf.org/doc/html/rfc6749](https://datatracker.ietf.org/doc/html/rfc6749) 16 | 17 |
18 | 19 | 在这里需要指出:就算你完全没接触过 OAuth2.0,也并不影响你对接月幕的开放API,你只要把这个文档看完,照着做就行了。 20 | 21 | > 在月幕的认证、授权流程中,接口返回值不遵循[全局返回对象](api-response.md)的返回值实现 22 | > 23 | > 在月幕的认证、授权流程中,不需要遵循[HTTP 请求设置](request-setting.md)的规范 24 | 25 |
26 | 27 | ## 通过 Token 端点获取 Access Token 28 | 29 | 端点: `GET /oauth/token` 30 | 31 | 重复请求: 返回相同的 access_token 32 | 33 | 过期时间: 1h 34 | 35 |
36 | 37 | ### Client Credentials 38 | 39 | * 此节说明的示例基于 OAuth2.0 Client Credentials Grant,适用于所有公共接口 40 | 41 | 42 | * Client Credentials 模式不支持 Refresh Token 43 | 44 | | Request param | Type | Desc | Value | 45 | |---------------| ------ |---------|-----------------------------| 46 | | client_id | string | 标识一个客户端 | 你申请的 client_id,或公共的 client_id | 47 | | client_secret | string | 客户端的密钥 | client_id 所对应的密钥 | 48 | | grant_type | string | 授权类型 | client_credentials,固定值 | 49 | | scope | string | 可访问范围 | 您试图申请的可访问范围 | 50 | 51 |
52 | 53 | 54 | Example 55 | ```shell 56 | curl 'https://www.ymgal.games/oauth/token?grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=public' 57 | ``` 58 | 59 | Success 60 | 61 | ```json 62 | { 63 | "access_token": "2fdace3c-651f-4484-85ac-9a449da43f05", 64 | "token_type": "bearer", 65 | "expires_in": 3599, 66 | "scope": "public" 67 | } 68 | ``` 69 | Failed 70 | ```json 71 | { 72 | "error": "invalid_client", 73 | "error_description": "Bad client credentials" 74 | } 75 | ``` 76 |
77 | 78 | ### 其他 79 | 80 | 暂无需求,所以暂未提供。 81 | 82 | 如果后续提供了 Authorization Code Grant 的实现,也只会是在需要用户权限的接口被发布时。 83 | 84 | 不同认证模式获取的 access_token 在相同的 scope 之下访问 API 没有任何区别。 85 | 86 | 87 |
88 | 89 | ## Scopes 90 | 91 | | Scope | Desc | 92 | |--------|-----------------------------------------| 93 | | public | 公共接口,这里指的是不需要经过任何权限检查也能访问的接口,比如查询游戏详情。 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /developers/overview.md: -------------------------------------------------------------------------------- 1 | # 接入概述 2 | 3 | 本文档描述的是 月幕Galgame 站点(简称:月幕 或 ymgal)可供开发者接入的 HTTPS API。 4 | 5 | 部分接口可能需要访问权限,如用户登录、数据修改等,但大部分查询接口 —— 通常来说,这代表着游客在月幕可以阅览的所有数据,在我们的API中是没有权限判定的,您可以自由访问。 6 | 7 | 月幕的开放接口使用 OAuth2.0 协议认证,**包括公共接口**,这是为了保证后台设计的同一性而决定的。 调用时,您需要 `client_id` 与 `client_secret` 8 | 作为客户端凭证,通过允许的认证方式获取到一个 `access_token` 后,携带此token实行请求。 9 | 10 | 我们提供了公共的 `client_id` 与 `client_secret` 供您使用,它能够满足大部分人的学习、实验、整理、调研等需求。 11 | 12 | 月幕**不能**保证各API的可访问性,我们只能保证它在大部分地区,以及境外是可访问的。 13 | 14 | 月幕**不能**保证域名的变动可控,它可能会在某次DNS污染后被更换,发生这种情况时,我们会尽可能延续旧域名的可用性,这并不受到保障,不代表您可以直接使用旧域名、或拒绝更新。 15 | 16 | 文档中所陈列的接口均为稳定状态。 17 | 18 |
19 | 20 | ## 开发者须知 21 | (默认接入方已阅读) 22 | * 月幕的API服务仅适用于非商业用途,请不要将其用于商业项目。 23 | * API访问有速率限制以便控制服务器资源,请避免使用并发、循环等调用方式,对于静态资源需做好缓存。 24 | * 若您申请了专属的 `client_id` 与 `client_secret`,请不要将其发布到互联网中(如交流论坛、开源代码),当然,你发布了也行,只是在被坏东西滥用时,走的是您的流量池,这可能会导致您正常的业务受到影响。 25 | * 若您的项目是一个可在互联网访问、查看的项目,您需要在项目的说明区域(如README、关于、页脚等)提到月幕Galgame。 26 | * 在开发出现问题时,可以通过接口调用的返回码,来发现和解决问题,具体请查看[全局返回对象](api-response.md)。 27 | * 您的服务需要收集用户任何数据的,必须事先获得用户的明确同意,且仅应当收集为运营及功能实现目的而必要的用户数据, 同时应当告知用户相关数据收集的目的、范围及使用方式等,保障用户知情权。 28 | * 您收集用户的数据后,必须采取必要的保护措施,防止用户数据被盗、泄漏等。 29 | * 请勿公开表达或暗示您与月幕之间存在合作关系,或声称月幕对您的认可。 30 | 31 |
32 | 33 | ## Public client identifier 34 | 35 | * client_id: `ymgal` 36 | * client_secret: `luna0327` 37 | 38 | 暂未开启站内申请渠道(懒得写,有需要的话再看),若您想申请专属的 `client_id`,请通过提出 issue 的方式,言明您的用途与项目主页 39 | 40 | 当然,访问速率限制总归是有的,无非是多与少、共享与否的区别 41 | 42 | 若您开发的是服务型、分发型应用,为了避免影响业务,请自行实现一个妥善的请求拒绝策略,您可以在[全局返回对象](api-response.md)中查看相关错误码 43 | 44 |
45 | 46 | ## 关于 47 | 48 | 月幕的开放API并未提供所有服务功能。 49 | 50 | 一般情况下,日常更新迭代的API可能只是因为站长写代码写嗨了才得以实现,不一定会是按照业务线或模块顺序提供,同时,一不小心鸽它大半年也是很正常的一件事,不得不品鉴。 51 | 52 | 在 issues 内已同意实现的不会鸽,若有相关需求,请在 [issues](https://github.com/ymgal/docs/issues) 提出。 53 | 54 | 总之,别急。 55 | 56 | 另外,本文档是个单文件HTML,由程序自动构建,其内容由多个 Markdown 文件组成。 57 | 58 | 若你发现文档错误、显示异常、渲染失败等情况,欢迎前往此文档的 Github 仓库提交 PR 或 issue。 59 | 60 | 和 IE 相关的不受理,老哥换个浏览器吧。 61 | 62 | * [源文档](https://github.com/ymgal/docs/blob/main/developers/overview.md) 63 | * [页面构建程序](https://github.com/ymgal/docs/blob/main/htmlgen/src/main/kotlin/Main.kt) 64 | 65 |
-------------------------------------------------------------------------------- /developers/request-setting.md: -------------------------------------------------------------------------------- 1 | # HTTP 请求设置 2 | 3 | 域名:[https://www.ymgal.games](https://www.ymgal.games) 4 | 5 | API 端点均基于该域名实行请求。 6 | 7 | 在请求月幕的开放 API 时,需要遵守相关的规定才能成功访问。 8 | 9 | 简单的来说,你只需要在 Request Header 中设置几个字段即可。 10 | 11 |
12 | 13 | ## Accept 14 | 15 | 我们默认所有的接口均使用 JSON 格式返回。 16 | 17 | 请在你的请求头中设置: 18 | 19 | `Accept: application/json;charset=utf-8` 20 | 21 |
22 | 23 | ## Authorization 24 | 25 | 我们默认所有的接口调用均需要 access_token 26 | 27 | 请在你的请求头中设置: 28 | 29 | `Authorization: Bearer access_token` 30 | 31 | 如: 32 | 33 | `Authorization: Bearer 2fdace3c-651f-4484-85ac-9a449da43f05` 34 | 35 |
36 | 37 | ## version 38 | 我们默认所有的接口调用均需要指定 version ,接口文档中会声明接口版本 39 | 40 | 请在你的请求头中设置 (版本为1的情况): 41 | 42 | `version: 1` -------------------------------------------------------------------------------- /htmlgen/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 sorakylin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /htmlgen/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.6.10" 5 | application 6 | } 7 | 8 | java { 9 | sourceCompatibility = JavaVersion.VERSION_17 10 | targetCompatibility = JavaVersion.VERSION_17 11 | } 12 | 13 | group = "com.sorakylin" 14 | version = "1.0" 15 | 16 | repositories { 17 | maven { 18 | setUrl("https://maven.aliyun.com/repository/central") 19 | } 20 | mavenCentral() 21 | } 22 | 23 | dependencies { 24 | testImplementation(kotlin("test")) 25 | implementation("org.commonmark:commonmark:0.22.0") 26 | implementation("org.commonmark:commonmark-ext-gfm-tables:0.22.0") 27 | implementation("org.commonmark:commonmark-ext-heading-anchor:0.22.0") 28 | implementation("com.github.houbb:markdown-toc:1.2.0") 29 | } 30 | 31 | tasks.test { 32 | useJUnitPlatform() 33 | } 34 | 35 | tasks.withType { 36 | kotlinOptions.jvmTarget = "17" 37 | } 38 | 39 | application { 40 | mainClass.set("MainKt") 41 | } 42 | -------------------------------------------------------------------------------- /htmlgen/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /htmlgen/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /htmlgen/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /htmlgen/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "htmlgen" 2 | 3 | -------------------------------------------------------------------------------- /htmlgen/src/main/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import com.github.houbb.markdown.toc.core.impl.AtxMarkdownToc 2 | import org.commonmark.ext.gfm.tables.TablesExtension 3 | import org.commonmark.ext.heading.anchor.HeadingAnchorExtension 4 | import org.commonmark.node.Heading 5 | import org.commonmark.node.Link 6 | import org.commonmark.node.Node 7 | import org.commonmark.parser.Parser 8 | import org.commonmark.renderer.html.AttributeProvider 9 | import org.commonmark.renderer.html.HtmlRenderer 10 | import java.nio.file.Files 11 | import java.nio.file.Path 12 | import java.nio.file.Paths 13 | import kotlin.io.path.notExists 14 | import kotlin.io.path.readText 15 | 16 | val MENU = arrayListOf( 17 | markdownMenuNode("overview.md", false), 18 | markdownMenuNode("oauth.md", false), 19 | markdownMenuNode("api-response.md", false), 20 | markdownMenuNode("request-setting.md", false), 21 | MenuNode(""), 22 | markdownMenuNode("archive/index.md"), 23 | markdownMenuNode("archive/business-model.md", false), 24 | markdownMenuNode("archive/accurate-search-game.md"), 25 | markdownMenuNode("archive/search-game-list.md"), 26 | markdownMenuNode("archive/find-game.md"), 27 | markdownMenuNode("archive/find-org.md"), 28 | markdownMenuNode("archive/find-character.md"), 29 | markdownMenuNode("archive/find-person.md"), 30 | markdownMenuNode("archive/find-org-game.md"), 31 | markdownMenuNode("archive/by-release-date.md"), 32 | markdownMenuNode("archive/random-game.md"), 33 | MenuNode(""), 34 | markdownMenuNode("change-list.md"), 35 | ) 36 | 37 | val projectPath: Path = Paths.get("").toAbsolutePath() 38 | val developerPath: Path = projectPath.resolve("developers") 39 | val targetPath: Path = projectPath.resolve("htmlgen/src/main/resources") 40 | val resultHTMLFile: Path = targetPath.resolve("developer.html") 41 | val templateHTML = targetPath.resolve("template.html").readText() 42 | 43 | data class MenuNode( 44 | var file: String, 45 | var link: String? = null, 46 | val simpleHeadingAnchor: Boolean = true 47 | ) { 48 | val name: String 49 | 50 | init { 51 | val split = file.split("/") 52 | name = split[split.size - 1].replace(".md", "") 53 | } 54 | 55 | fun getPath(): Path = developerPath.resolve(file) 56 | } 57 | 58 | 59 | fun markdownMenuNode(md: String, simpleHeadingAnchor: Boolean = true): MenuNode { 60 | return MenuNode(file = md, simpleHeadingAnchor = simpleHeadingAnchor); 61 | } 62 | 63 | fun main(args: Array) { 64 | if (resultHTMLFile.notExists()) Files.createFile(resultHTMLFile) 65 | 66 | val nav: MutableList = arrayListOf() 67 | 68 | val mainHTML = MENU.map { menu -> 69 | 70 | //判定在菜单里插些额外数据 71 | if (menu.file.isBlank()) { 72 | nav.add("---") 73 | return@map "" 74 | } 75 | 76 | val path = menu.getPath() 77 | 78 | val tocList = AtxMarkdownToc.newInstance() 79 | .write(false) 80 | .genTocFile(path.toString()) 81 | .tocLines.filter { !it.startsWith("# Table of Contents") } 82 | 83 | nav.addAll( 84 | if (menu.simpleHeadingAnchor) listOf(tocList[0]) else tocList 85 | ) 86 | 87 | markdownToHtml(path.readText(), menu.simpleHeadingAnchor) 88 | }.filter { it.isNotBlank() }.joinToString("
") 89 | 90 | // 菜单一次性拼接,如有收缩的需要,可以改成分批拼 91 | val aside = nav.filter { it.isNotBlank() } 92 | .filter { !it.startsWith(" *") } 93 | .fold(StringBuilder()) { str, n -> str.appendLine(n) } 94 | println(aside) 95 | 96 | val asideHTML = markdownToHtml(aside.toString(), false) 97 | 98 | //token replace 99 | Files.writeString( 100 | resultHTMLFile, 101 | templateHTML.replaceFirst("{{aside}}", asideHTML).replaceFirst("{{main}}", mainHTML) 102 | ) 103 | 104 | } 105 | 106 | 107 | fun markdownToHtml(markdown: String, simpleHeadingAnchor: Boolean): String { 108 | //h标题生成id 109 | val headingAnchorExtensions = setOf(HeadingAnchorExtension.create()) 110 | //转换table的HTML 111 | val tableExtension = listOf(TablesExtension.create()) 112 | 113 | val parser = Parser.builder() 114 | .extensions(tableExtension) 115 | .build() 116 | 117 | val document = parser.parse(markdown) 118 | val renderer = HtmlRenderer.builder() 119 | .sanitizeUrls(false) 120 | // .escapeHtml(false) 121 | .extensions(headingAnchorExtensions) 122 | .extensions(tableExtension) 123 | .attributeProviderFactory { CustomAttributeProvider(simpleHeadingAnchor) } 124 | .build() 125 | 126 | return renderer.render(document) 127 | } 128 | 129 | class CustomAttributeProvider(private val simpleHeadingAnchor: Boolean) : AttributeProvider { 130 | 131 | override fun setAttributes(node: Node, tagName: String, attributes: MutableMap) { 132 | if (node is Link) { 133 | val destination = node.destination 134 | 135 | //改变a标签的target为新标签页打开 136 | if (destination.startsWith("https://") || destination.startsWith("http://")) { 137 | attributes["target"] = "_blank" 138 | return 139 | } 140 | 141 | //TODO 这块暂时先这样,懒得写了,有需要再说 142 | if (destination.endsWith(".md")) { 143 | node.destination = destination.replace(".md", "") 144 | attributes["href"] = node.destination 145 | attributes["href"] = "javascript:void(0)" 146 | } 147 | 148 | } 149 | 150 | //去除单个页面中除大标题以外的所有标题导航 151 | if (node is Heading && simpleHeadingAnchor) { 152 | if (node.level > 1) attributes["id"] = "" 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /htmlgen/src/main/resources/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 月幕Galgame - 开放API 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 398 | 399 | 400 | 401 |
402 | 405 |
406 |

YMGalgame API

407 |
{{main}}
408 |
409 |
410 | 411 | 460 | 461 | 482 | 483 | --------------------------------------------------------------------------------