🌐 Languages: 简体中文 · 繁體中文 · 日本語 · 한국어
📖 Documentation site: https://mouse233.github.io/MySekaiMapper/
A resource-gathering point map generator for the MySekai mode in Project Sekai (世界计划 多彩舞台).
Original intent: designed to work with MitM modules or Reqable's "Report Server" feature — the capture tool grabs MySekai data packets from the game and automatically uploads them to this service (single POST; chunked upload is also supported). The server decrypts the encrypted saves, extracts the resource drop coordinates of every station, draws gathering maps, and pushes the results (including a rare-resource summary) to the player's Telegram / Bark (iOS Day.app) — no manual intervention required.
Each task produces 4 maps: site_5.png (Grassland), site_6.png (Beach), site_7.png (Flower Garden), site_8.png (Memorial Place), plus a rare_resources.txt rare-resource summary.
This project has been tested and verified on the CN and TW servers operated by Nuverse (朝夕光年). Availability on other servers is unknown.
Game API response → MitM module / Reqable Report Server (captures mysekai data)
│ ① Auto upload (single POST; chunked supported) → server.py processes automatically
│ ② Or drop a .bin save manually → cli.py generate
▼
parser.py AES-128-CBC decrypt + msgpack parse + coordinate rotation
▼
render.py Draw site_5.png ~ site_8.png + rare_resources.txt → data/latest/
▼
notify.py Push:
├─ Telegram: images uploaded directly as multipart, no public URL needed ← default channel
└─ Bark: notified with image= URL links, requires a static file server
First finish the installation and basic .env configuration, then pick the path that matches your push setup:
- Path A (Telegram Bot only): fewest configs, recommended to get running first;
- Path B (enable Bark push): Path A plus Bark keys, player routing, and a static file server.
python -m venv venv
venv/bin/pip install -r requirements.txt
# Optional: install the mysekai command (equivalent to python cli.py ...)
venv/bin/pip install -e .cp .env.example .envAES_KEY / AES_IV are the AES-128-CBC decryption keys for MySekai saves (16 bytes each) — required on every path. The remaining variables depend on your chosen path:
| Variable | Required | Description |
|---|---|---|
AES_KEY / AES_IV |
✅ | AES-128-CBC keys for MySekai saves, 16 bytes each |
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID |
Optional* | Needed for Telegram push (default channel); from @BotFather |
BARK_ICON |
Optional | Icon URL for Bark notifications |
BARK_IMAGE_BASE |
Optional | Root URL of the static file server (for Bark image links; see below) |
FALLBACK_IMAGE_BASE |
Optional | Fallback base URL for image links when BARK_IMAGE_BASE is not set |
* If you only want Bark notifications: you may leave the Telegram config empty, but you must route the player to a Bark alias in
config/push_map.json, otherwise unconfigured players default to Telegram — and with Telegram unconfigured, only a warning is printed and nothing is pushed.
Use when: you just want maps and stats in Telegram without setting up anything else.
-
Fill in the Telegram config in
.env(from @BotFather):TELEGRAM_BOT_TOKEN=1234567890:AAAA-your-bot-token TELEGRAM_CHAT_ID=123456789 -
Run it once manually to verify:
python cli.py generate <mysekai.bin> python cli.py notify data/latest <task_id>
-
Daily use: start the upload service; saves are turned into maps and pushed automatically. Two capture clients are supported:
- MitM module: uploads the save per the Upload API
- Reqable Report Server: reports captured sessions to the built-in endpoint (see Reqable Report Server)
python cli.py server [--host 0.0.0.0] [--port 9478]
Path A does not need: config/push_map.json, config/bark_map.json, a static file server, or BARK_IMAGE_BASE. Unconfigured players are pushed to Telegram by default.
On top of Path A (the Telegram config may stay, or be left empty to push only to Bark), set up in order:
-
Configure Bark keys: give each alias a device key in
config/bark_map.json(template:bark_map.example.jsonin the same directory). -
Configure player routing: route player IDs to Bark aliases in
config/push_map.json, for example:{ "1234567890123456789": ["klee"], "1234567890123456790": ["telegram", "klee"] }⚠️ Required: unconfigured players default to Telegram; if Telegram is also unconfigured, only a warning is printed and nothing is pushed. -
Set up a static file server: expose the project's
data/directory as a publicly reachable HTTP(S) service and setBARK_IMAGE_BASE=https://<domain-or-ip:port>in.env. Otherwise Bark notifications carry no map images (see Static file server examples below). -
Verify and use daily the same as Path A (steps 2 and 3).
This endpoint receives the captured mysekai response body via POST /uploadMySekai (a single POST; chunked upload is kept for compatibility). The same protocol can be debugged manually with curl. Headers:
| Header | Description |
|---|---|
X-Upload-Id |
Upload task ID (alphanumeric plus - / _, length 1~64), required |
X-Chunk-Index |
Chunk index, starting at 0 (always 0 for a single POST), required |
X-Total-Chunks |
Total number of chunks (1~10; use 1 for a single POST), required |
X-Original-Url |
The client's original page URL, used to resolve the player ID (e.g. https://.../user/123456...); optional — if missing, the player ID is recorded as unknown |
X-Script-Version |
Client script version; ignored by the server, may be omitted |
The request body is the raw binary save data (no multipart needed).
Limits:
- Total file size ≤1MB (
MAX_TOTAL_SIZE) - Single chunk ≤1MB (
MAX_CHUNK_SIZE, returns 413 if exceeded) - Max 10 chunks (
MAX_CHUNKS)
Note: current saves are ~200KB, so a single POST is all you need. Chunked upload is kept for compatibility with older capture clients; if used, keep each chunk well below 1MB (e.g. 256KB) so 10 chunks fill the 1MB cap.
Responses:
| Status | Meaning |
|---|---|
200 |
Save received, returns OK; the server automatically: merges the save (if chunked) → generates maps → archives to data/archive/by-id/<user_id>/<timestamp>/ → pushes notifications. No manual intervention. |
400 |
Invalid parameters (bad upload id format, chunk index out of range, total chunks not in 1~10) |
413 |
Size limit exceeded (single chunk over 1MB, or cumulative total over 1MB) |
Single POST (all current saves fit in one request):
curl -X POST http://127.0.0.1:9478/uploadMySekai \
-H "X-Upload-Id: demo12345" \
-H "X-Chunk-Index: 0" \
-H "X-Total-Chunks: 1" \
-H "X-Original-Url: https://example.com/user/1234567890123456789" \
--data-binary @mysekai.binChunked upload (optional, for compatibility; 256KB per chunk fills the 1MB cap with 10 chunks):
file=mysekai.bin
id=$(openssl rand -hex 5)
total=$(( ($(wc -c < "$file") + 262143) / 262144 ))
split -b 262144 -a 2 -d "$file" /tmp/ms_chunk_
i=0
for c in /tmp/ms_chunk_*; do
curl -s -X POST http://127.0.0.1:9478/uploadMySekai \
-H "X-Upload-Id: $id" \
-H "X-Chunk-Index: $i" \
-H "X-Total-Chunks: $total" \
-H "X-Original-Url: https://example.com/user/1234567890123456789" \
--data-binary @"$c"
echo
i=$((i + 1))
done
rm -f /tmp/ms_chunk_*A 200 OK means the save was accepted; the pipeline (merge if chunked → generate → archive → notify) runs automatically. Replace 127.0.0.1:9478 with your actual service address; X-Upload-Id must match ^[a-zA-Z0-9_-]{1,64}$ (e.g. a random string from openssl rand -hex 5).
Instead of a custom capture client, you can use Reqable's built-in Report Server feature (Reqable v2.20.0+): it automatically POSTs each captured HTTP session to your server in the HAR JSON format, optionally compressed with gzip / brotli / zstd. The report endpoint is enabled by default and coexists with the chunked upload API — python cli.py server serves both. Set REPORT_ENABLED=0 to disable it:
python cli.py serverConfiguration (.env):
| Variable | Default | Description |
|---|---|---|
REPORT_ENABLED |
1 (on) |
Set to 0 / false to disable the report endpoint |
REPORT_PATH |
/reqable/report |
Endpoint path; fill this into the Reqable "Upload Path" field |
REPORT_MAX_SIZE |
1 |
Max HAR request body size in MB, same as the chunked upload limit |
REPORT_TOKEN |
(empty) | Optional shared token; when set, the endpoint requires the X-Report-Token header |
What the endpoint does with each report:
- Decompresses the body (
Content-Encoding: gzip/br/zstd) and parses the HAR. - Walks
log.entriesand takes the first session whose response body (fallback: request body) decrypts withAES_KEY/AES_IVand parses as a MySekai save — unrelated API traffic matching the rule is skipped. - Resolves the player ID from the session URL (
/user/<id>, same rule asX-Original-Url). - Saves the archive to
data/raw_mysekai/and launches the same generate → archive → notify pipeline as chunked uploads.
Notes:
- Reqable sends each session exactly once and never retries, so the endpoint answers
200as fast as possible; make sure your server is stable and watch the[REPORT]log lines. - Only one archive per report is processed (the first valid entry), so a rule matching many endpoints won't cause duplicate pushes.
- Security: the protocol has no built-in auth. Since Reqable cannot attach custom headers, prefer embedding a random secret in
REPORT_PATH(e.g./reqable/report/9f3a…) or restrict access with a reverse proxy / firewall instead of relying onREPORT_TOKEN.
Example Reqable configuration:
- URL matching rule:
https://<game-api-host>/api/user/*/mysekai* - Upload path:
http://<your-server>:9478/reqable/report - Compression: any of gzip / brotli / zstd (server supports all three)
Game API domains (one per region):
| Region | Game API domain |
|---|---|
| JP | https://production-game-api.sekai.colorfulpalette.org |
| EN | https://n-production-game-api.sekai-en.com |
| TW | https://mk-zian-obt-cdn.bytedgame.com |
| KR | https://mkkorea-obt-prod01-cdn.bytedgame.com |
| CN | https://mkcn-prod-public-60001-1.dailygn.com |
Recommended matching rule: https://<domain>/api/user/*/mysekai* (verified on CN). If your region's mysekai API path differs, adjust the rule accordingly.
Manual curl test (gzip-compressed HAR):
gzip -c report.har.json | curl -X POST http://127.0.0.1:9478/reqable/report \
-H "Content-Type: application/json" -H "Content-Encoding: gzip" \
--data-binary @-- Players not configured in
config/push_map.jsonalways default to Telegram; the same applies whenpush_map.jsonis missing. - Telegram uses the Bot API
sendMediaGroupto upload the 4 local PNGs directly as multipart — no public image URL and no static file server needed; ifTELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDare missing it just prints a warning and skips, without affecting the Bark channel.
Images in Bark (Day.app) notifications are URL links: notify.py encodes the image address into the image= parameter sent to api.day.app, and the Bark server fetches that image itself. The URL must therefore be publicly reachable (HTTPS recommended), otherwise Bark notifications have no images.
The 4 map links are composed by notify.py with this precedence:
base = image_base or BARK_IMAGE_BASE or FALLBACK_IMAGE_BASE
image_url = base.rstrip("/") + f"/site_{i}.png" # i = 5..8| Scenario | base value | Image link form |
|---|---|---|
| Server flow (recommended) | BARK_IMAGE_BASE + /archive/by-id/<user_id>/<timestamp> |
https://<BARK_IMAGE_BASE>/archive/by-id/<user_id>/<timestamp>/site_{5..8}.png |
| Manual CLI push | BARK_IMAGE_BASE or FALLBACK_IMAGE_BASE |
<base>/site_{5..8}.png (expose data/latest/ under <base>/) |
Note: the server flow only composes archive-path links when
BARK_IMAGE_BASEis configured; with onlyFALLBACK_IMAGE_BASEset, the server pushes<FALLBACK_IMAGE_BASE>/site_{5..8}.pnglinks too.
Purpose: expose the data/archive/ directory as a public URL so the Bark server can fetch the four maps.
Recommended setup: point the static server root at the project's data/, then set BARK_IMAGE_BASE=https://<your-domain-or-ip:port> for automatic mapping:
data/archive/by-id/<user_id>/<timestamp>/site_5.png
→ https://<BARK_IMAGE_BASE>/archive/by-id/<user_id>/<timestamp>/site_5.png
Common examples:
Python built-in (simplest; LAN/testing):
python -m http.server 8000 --directory data
# then set BARK_IMAGE_BASE=http://<server-ip>:8000nginx:
server {
listen 443 ssl;
server_name maps.example.com;
# ... ssl certificate config ...
root /path/to/MySekaiMapper/data;
}Caddy (automatic HTTPS):
caddy file-server --root /path/to/MySekaiMapper/data --listen :443Notes:
- Don't use
127.0.0.1/localhostas the link address; the Bark server must be able to reach it. In general, pick a publicly reachable address; LAN IPs only when connectivity is confirmed. - Telegram-only users need no static server at all — skip this section.
- Manual
cli.py notifylinks carry no archive path: exposedata/latest/underBARK_IMAGE_BASEseparately, or pointFALLBACK_IMAGE_BASEat the output directory (e.g.FALLBACK_IMAGE_BASE=http://<host>:5500/output→ that server mountsdata/latest/at/output).
Create local configs under config/ as needed (formats follow the *.example.json templates in the same directory; these files are .gitignored):
-
push_map.json— player ID → push method: the value can be"telegram", a Bark alias,"none"(no push), or a combination like["alias", "telegram"]/"alias+tg". Unconfigured players default totelegram.{ "1234567890123456789": ["telegram"], "1234567890123456790": ["telegram", "klee"] } -
bark_map.json— Bark alias → device key:{ "klee": "paste-your-bark-key-here" }
- Bark notifications have no images? Check whether the link is publicly reachable: open
https://<BARK_IMAGE_BASE>/archive/by-id/<user_id>/<timestamp>/site_5.pngin a browser or over cellular data — it should show the image. LAN addresses,127.0.0.1, or HTTPS with certificate problems all make the fetch fail. - Nothing was pushed? Check whether
push_map.jsonsets that player to"none"; whether Bark-only users forgot to assign a Bark alias to the player (unconfigured players default to Telegram); whether the Telegram channel has a token and chat id; whether the Bark channel lacks a key (look for[BARK] ... failedin the logs). - Don't want Bark, only Telegram? Nothing to do — unconfigured players already default to Telegram.
Everything can be driven through cli.py; after installing (pip install -e .), the equivalent mysekai command is also available. Commands exit with 0 on success and 1 on error (errors print to stderr).
python cli.py --help # subcommand overview
python cli.py <command> --help # show a subcommand's argumentspython cli.py generate <mysekai_bin><mysekai_bin>: path to the encrypted save (.bin), required- Flow: AES-128-CBC decrypt → msgpack parse → extract drop coordinates → draw 4 maps (
site_5.png~site_8.png) → writerare_resources.txt - Output goes to
data/latest/; the actual path is printed at the end - Requirements:
AES_KEY/AES_IVconfigured in.env; exits with an error if the save contains no drop points
python cli.py notify <output_dir> [task_id]<output_dir>: directory containingsite_*.pngandrare_resources.txt(usuallydata/latest/)[task_id]: optional upload task ID, defaults tounknown. Used to look up the player ID fromdata/raw_mysekai/: it first tries to matchmysekai_<playerID>_<task_id>.bin, otherwise falls back to the newest save in raw_mysekai- Telegram vs Bark is decided by the routing in
config/push_map.json(unconfigured players default to Telegram); see Player push routing
python cli.py server [--host 0.0.0.0] [--port 9478]- Starts the FastAPI service; clients upload encrypted saves to
POST /uploadMySekai(single POST or chunked; protocol details: Upload API), and Reqable can report HAR sessions to the built-in report endpoint (see Reqable Report Server) - When all chunks arrive, the server automatically: merges the save → generates maps → archives to
data/archive/by-id/<user_id>/<timestamp>/→ pushes notifications per player routing. No manual intervention. - Listens on
9478by default; for public deployment, expose it as HTTPS via a reverse proxy — the hardcoded upload URL (including the port) in your client script must match your actual deployment
python cli.py generate mysekai_xxx.bin # 1. generate maps to data/latest/
python cli.py notify data/latest <task_id> # 2. push (task_id = upload ID, e.g. chfto53c3)├── app/ # core package
│ ├── config.py # centralized paths / env vars / local config
│ ├── crypto.py # MySekai save AES-128-CBC decryption
│ ├── parser.py # msgpack parsing + station coordinate rotation (pure functions)
│ ├── har.py # HAR parsing + decompression for the Reqable report server (pure functions)
│ ├── render.py # extract drop points → matplotlib drawing + rare-resource stats
│ ├── notify.py # push: Telegram media groups / Bark, per-player routing
│ ├── server.py # FastAPI upload service (chunked upload + Reqable report server)
│ └── cli.py # CLI entry
├── assets/ # static assets (committed to the repo)
│ ├── resourceId.csv # item ID → name + icon (base64)
│ └── NotoSansSC-Regular.ttf # Chinese font (OFL license)
├── config/ # local configs (real files not committed; see *.example.json)
│ ├── bark_map.example.json # Bark alias → device key template
│ └── push_map.example.json # player ID → push method template
├── data/ # runtime data (whole directory gitignored)
│ ├── tmp/ # chunk upload staging, cleaned after merge
│ ├── raw_mysekai/ # merged original (encrypted) saves, kept permanently
│ ├── archive/ # historical output archive by-id/<user>/<timestamp>/ (Bark links point here)
│ └── latest/ # most recent output
├── cli.py # unified entry
├── tests/ # unit tests (pytest)
├── .env.example # env var template (copy to .env and fill in)
└── requirements.txt # runtime dependencies (pinned)
python -m pytestThis tool is for personal learning and entertainment only. Do not use it for any commercial purpose or in ways that violate the game's terms of service. Game data and art assets belong to their respective owners.
The project's code is licensed under the MIT License (Copyright © 2025 mouse233) — free to use, modify, and redistribute. See LICENSE for details.
⚠️ The license covers only the project's code: the game assets inassets/(e.g. the item icons insideresourceId.csv) and the game data belong to SEGA / Colorful Palette and other respective owners — not covered by the MIT license — please do not use them outside this tool.