{"id":19514090,"url":"https://github.com/hydro-dev/xcpc-tools","last_synced_at":"2026-04-04T00:08:29.868Z","repository":{"id":166674762,"uuid":"641444399","full_name":"hydro-dev/xcpc-tools","owner":"hydro-dev","description":"A tool for CN XCPC contests","archived":false,"fork":false,"pushed_at":"2025-04-25T09:25:19.000Z","size":3408,"stargazers_count":23,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-25T10:19:28.636Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hydro-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-05-16T13:34:25.000Z","updated_at":"2025-03-16T05:22:08.000Z","dependencies_parsed_at":"2024-10-28T12:16:51.976Z","dependency_job_id":"db08d3e4-fa77-4921-8ad5-2cd4286adb85","html_url":"https://github.com/hydro-dev/xcpc-tools","commit_stats":null,"previous_names":["hydro-dev/xcpc-tools"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hydro-dev%2Fxcpc-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hydro-dev%2Fxcpc-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hydro-dev%2Fxcpc-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hydro-dev%2Fxcpc-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hydro-dev","download_url":"https://codeload.github.com/hydro-dev/xcpc-tools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250943859,"owners_count":21511638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T23:34:44.690Z","updated_at":"2026-04-04T00:08:29.852Z","avatar_url":"https://github.com/hydro-dev.png","language":"TypeScript","readme":"# Hydro/XCPC-TOOLS\n\nA tool for CN XCPC contests\n\n- 代码打印和小票机打印（全平台支持）\n- 支持封榜期间发放鼓励气球\n- 支持连接 DOMjudge 与 Hydro 系统，同时亦可独立运行\n- 选手机赛时数据监控与屏幕监控\n\nTODO Features：\n\n- [ ] 更好的选手机座位绑定\n- [ ] 优化 UI 顺畅度\n- [ ] 使用 WebSocket 返回指令执行情况\n- [ ] 支持全考场监视\n- [ ] 企业微信/钉钉/TG Webhook 气球机\n- [ ] 滚榜\n\n当前最新版本可直接在 [Releases](https://github.com/hydro-dev/xcpc-tools/releases/) 下载使用\n\n### Server\n\nServer 端分为 `Server Mode` 和 `Fetch Mode` ，在 `Fetch Mode` 下支持获取用户气球与连接小票机打印。各功能配置方法如下：\n\n#### 安装\n\n在 [Releases](https://github.com/hydro-dev/xcpc-tools/releases/) 下载已经封装好的 Windows, Linux, macOS 二进制使用，如有未封装好的架构但 Node.js 支持的系统或系统内已有 Node.js 亦可下载 `xcpc-tools-bundle.js`使用。\n\n下载后首次运行可见填写配置文件字样，打开 `config.yaml` ，如使用 `Fetch Mode` 请填写相关赛事系统配置，如使用 `Server Mode` 则无须填写配置可直接启动。\n\n系统配置介绍如下：\n\n```ts\nconst serverSchema = Schema.intersect([\n    Schema.object({\n        type: Schema.union([\n            Schema.const('server'),\n            Schema.const('domjudge'),\n            Schema.const('hydro'),\n        ] as const).description('server type').required(), // 服务类型\n        port: Schema.number().default(5283), // 服务端口\n        viewPass: Schema.string().default(String.random(8)), // UI登录密码，可通过 admin / {viewPass} 登录\n        secretRoute: Schema.string().default(String.random(12)), // 打印路径，用于远程调用\n    }).description('Basic Config'),\n    Schema.union([\n        Schema.object({\n            type: Schema.union([\n                Schema.const('domjudge'),\n                Schema.const('hydro'),\n            ] as const).required(), // 赛事系统类型\n            server: Schema.string().role('url').required(), // 赛事系统地址\n            contestId: Schema.string(), // 赛事ID，如无则自动获取（DOMjudge），hydro 请使用 domainId/contestId 作为ID\n            token: Schema.string(), // 赛事系统 Token 如无可使用用户名密码登录\n            username: Schema.string(), // 赛事系统用户名\n            password: Schema.string(), // 赛事系统密码\n            freezeEncourage: Schema.number().default(0), // 封榜后鼓励气球数（0 为不发放），需赛事系统配置封榜后仍生成气球\n        }).description('Fetcher Config'), // token 与 username/password 二选一\n        Schema.object({\n            type: Schema.const('server').required(),\n        }).description('Server Mode Config'),\n    ]),\n]);\n```\n\n填写好配置重启后即可使用，程序默认监听 0.0.0.0 ，可通过 `http://服务IP:5283` 访问 UI 界面。服务用户名为 `admin` ，密码为填写的 `viewPass` 。\n\n#### Print\n\n支持各类比赛系统推送打印信息，系统将自动调用 `typst` 为选手代码进行高亮并转换为 PDF 文件，由打印/气球客户端进行打印。 建议使用命令进行打印，避免服务交互数据泄漏，如需使用请从`https://github.com/hydro-dev/xcpc-tools/blob/main/scripts/print`下载脚本并提前将脚本放置在 `PATH` 中。\n\n`print [file] [original] [language] [username] [teamname] [teamid] [location]` 为打印命令，其中 `file` 为代码文件路径，`original` 为原文件名，`language` 为语言，`username` 为用户名，`teamname` 为队伍名，`teamid` 为队伍ID，`location` 为选手位置。\n\n#### Balloon\n服务支持 `Fetch Mode` 下的气球推送，支持 `DOMjudge` 与 `Hydro` 系统，支持 `DOMjudge` 与 `Hydro` 系统的 `Balloon` 推送，同时若赛事在封榜后仍然推送气球，则支持自定义鼓励气球数，高于设定值则不推送，为所有队伍打造优质赛场体验。\n\n#### Monitor\n服务支持监控选手机情况和监控服务器桌面，如您需要选手机监控，可通过设置 Systemd 定时执行任务等多种方式定时执行 `monitor` 命令，如需监控服务器桌面，请在选手机上提前运行 `vlc-camera` 和 `vlc-desktop` 服务， CAICPC 镜像已经内置了这三两个服务，您只需在选手机上运行即可，如您为自己的镜像，可从 `https://github.com/hydro-dev/xcpc-tools/blob/main/scripts/monitor` 下载 `monitor` 服务。\n\n当您的选手机启动了 `monitor` 服务后，服务会定时向服务器发送选手机状态，您可以在 `http://服务IP:5283/monitor` 查看选手机状态，如选手机掉线/未启动，系统会有明显提示协助找到对应机型。\n\n由于 VLC 自带的服务不支持 CORS ， 因此产品内置了一个代理服务，代理服务会将请求转发到选手机上，您可以通过代理服务访问选手机上的 VLC 服务以实现监控。\n\n请注意，默认上报的选手机是不支持查看屏幕的，需要在 UI 上配置选手机信息。点击选手机列表中的选手机的详情按钮，然后在弹出的对话框中即可修改选手机信息。字段含义如下：\n\n- `Client Name` 选手机名称\n- `Client group` 选手机组别\n- `Camera Stream` 选手机摄像头地址（暂只支持 TS 流地址）\n- `Desktop Stream` 选手机桌面地址（暂只支持 TS 流地址）\n\n流地址可使用 `proxy://xxxx` 代理服务，`proxy://` 取代的是 `http://{ip}`， 如 `proxy://:9090/`, 此时代理服务会将请求转发到选手机 `http://{ip}:9090/` 上。\n\n如您有可直接访问的 TS 流地址，可直接填写，您可通过 CDS 等服务获得此类流地址，注意流地址需要支持跨域访问，否则无法在 UI 上正常显示，如您的流地址不支持跨域访问，您可以使用代理服务进行转发，同时 CDS 服务提供的流在封榜后将无法观看，请自行取舍。如修改成功， Info 选项卡后便会多出桌面和摄像头的预览标签页，同时在选手机列表中也会支持直接查看选手机的摄像头和桌面。\n\n#### Batch / Quick Operation\n为了方便修改选手机信息，服务支持批量操作和根据选手机字段快速操作，如您需要批量修改选手机信息，可通过 `Batch Operation` 选项卡进行批量操作。\n\n快速操作即你可以在对话框中填写 `[]` 指代已有的字段，如 `[hostname]`, `[ip]`, `[mac]` 等，系统会自动将对应字段填充到选手机信息中。\n\n同时，组别名支持只取名字前缀，如 `[hostname:3]` 会取选手机名称的前三位，如您需要使用 hostname 为 AXX 的选手机 hostname 中的第一位作为组别名，您可以在快速操作中填写 `[hostname:1]`，系统会自动填充对应的选手机信息。\n\n在字段中输入 `del` 可以删除对应字段的信息。\n\n#### Commands\n服务支持通过 `ssh` 执行命令，如您需要执行命令，内置的命令分别为 重启、根据 `config.seatFile` 选手座位绑定文件更新选手机机器名称、显示选手机座位信息。如您需要执行其他命令，请直接在 UI 界面中输入指令，系统会自动向所有选手机发送指令，并返回结果。\n\n#### Arena Layouts\n监视大屏的座位图布局可在 Arena View 中通过 **Import JSON** 导入，数据会保存在浏览器 `localStorage` 中，无需重启或重新构建即可生效。\n- 顶层字段：`id`（唯一标识，缺省使用文件名）、`name`、`description`(可选)、`seatKey`(默认匹配 `hostname`)、`normalize`(`none`/`upper`/`lower`/`trim`/`trim-upper`/`trim-lower`)、`default`(可选，用于默认选中)、`sections`。\n- `sections` 数组中的每个对象需包含 `grid` 二维数组（元素只能是座位号字符串或 `null` 表示空位），可选字段包括 `title`、`rowLabels`、`seatSize`、`gapSize`、`meta` 等。\n- 同一个 JSON 可携带一个布局对象或布局数组，`default: true` 的布局会在导入后默认选中，可随时使用 **Clear Layouts** 清除本地缓存。\n\n```json\n{\n  \"id\": \"sample-layout\",\n  \"name\": \"Sample Venue\",\n  \"description\": \"Short note shown in the selector\",\n  \"seatKey\": \"hostname\",\n  \"normalize\": \"trim-upper\",\n  \"default\": true,\n  \"sections\": [\n    {\n      \"id\": \"main-hall\",\n      \"title\": \"Main Hall\",\n      \"rowLabels\": [\"3\", \"2\", \"1\"],\n      \"seatSize\": 40,\n      \"gapSize\": 10,\n      \"grid\": [\n        [\"A0301\", \"A0302\", null],\n        [\"A0201\", null, \"A0203\"],\n        [\"A0101\", \"A0102\", \"A0103\"]\n      ]\n    }\n  ]\n}\n```\n\n### Client\n\nClient 端分为打印代码和打印小票两个功能，支持 Windows, Linux, macOS 三大平台，支持打印机自动检测，支持自动分散打印机任务，为了方便使用， Server 与 Client 一同打包为单文件，启动时仅需添加 `--client` 参数即可启动 Client 。\n\n由于 Windows 限制，在 Windows 下打印代码需要安装 `SumatraPDF` 用于打印 PDF 文件，如您的系统没有安装 `SumatraPDF` ，请根据提示下载便携版并放置于同一目录中；打印气球需将气球打印机设置为共享打印机，后续会自动检测。\n\nClient 端的配置文件为 `config.yaml` ，配置文件介绍如下：\n\n```ts\nconst clientSchema = Schema.object({\n    server: Schema.string().role('url').required(), // XCPC-TOOLS 服务地址\n    balloon: Schema.string(), // 气球小票机路径或名称，请自行根据启动后的提示填写\n    balloonLang: Schema.union(['zh', 'en']).default('zh').required(), // 气球小票语言\n    balloonType: Schema.union([58, 80]).default(80), // 气球小票机纸张宽度\n    printColor: Schema.boolean().default(false), // 是否打印彩色代码\n    printers: Schema.array(Schema.string()).default([]).description('printer id list, will disable printing if unset'), // 打印机列表，如果为空则不启用打印功能\n    token: Schema.string().required().description('Token generated on server'), // 服务端 Token\n    fonts: Schema.array(Schema.string()).default([]), // 额外字体路径\n});\n```\n\n在启动 Client 前，请前往服务端新建 Client ，获取到 Client 所需的 token ， token 为 Client 与 Server 通信的密钥，如您的 token 泄漏，请及时删除 Client 并重新生成，以保证系统安全，一个 token 只能对应一个 Client ，但同时支持两个功能，无需重复生成。\n\n首次启动时，系统会检测打印机并提示您填写配置文件，填写好配置文件后即可启动客户端，客户端会自动连接服务端并获取打印信息。","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhydro-dev%2Fxcpc-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhydro-dev%2Fxcpc-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhydro-dev%2Fxcpc-tools/lists"}