After quitting Genshin Impact, Honkai: Star Rail, and Zenless Zone Zero, I've recently become hooked on Wuthering Waves (that's right, the game where 'xxx just needs to xxx, while xxx has a lot more to consider'). When it comes to such anime-style games, the dreadful gacha is a necessary part; with gacha comes the chance of winning or losing the character or weapon, which has spawned a bunch of third-party mini-programs like "xxx Assistant", "xxx Workshop", etc. One of their key features is gacha analysis.
The catalyst was that a friend wanted to see my Honkai: Star Rail gacha records, but when they opened the previously used mini-program, they found that all the saved gacha records had disappeared. They tried downloading the Honkai Impact 3rd Cloud Gaming to re-import the gacha link and restore data, but after a lot of fiddling, the mini-program kept saying the gacha address was wrong, and they couldn't recover the history. I've been brooding over this ever since, so I decided to build my own gacha record analysis tool to keep the data in my own hands, hence I wrote this Gacha Analysis Tool.

Astrionyx
Astrionyx is a web app based on Next.js, Astrionyx is currently a front-end Next back-end Go project, supporting analysis of different types of banner records. You can manually import or update data, or import data via API. Besides Wuthering Waves, it now also supports Genshin Impact, Honkai: Star Rail, Zenless Zone Zero, and Arknights: Endfield. The front-end can be directly deployed on Vercel / EdgeOne pages, while the back-end is deployed via Docker on my own server – very, very convenient.
During the development of Astrionyx, I encountered many interesting problems. If you also want to build such an app, I hope this helps.
Data Import and Update
Data Import
Like most games, Wuthering Waves does not provide an official API for exporting gacha data. The gacha history is displayed as: when the user clicks the "Gacha History" button, the game generates a temporary link and opens it through the in-game browser to show the gacha history content. We can capture this link starting with https://aki-gm-resources.aki-game.com/aki/gacha/index.html by packet capture or from log files.
The page uses POST to query the API https://gmserver-api.aki-game2.com/gacha/record/query to fetch the summon history for each banner. This request contains key information such as player ID (playerId), server ID (serverId), banner ID (gachaId), etc., which can be extracted from URL parameters:
Where the banner type parameter cardPoolType takes values [1, 7], specifically mapped as follows:
Create a back-end API, forward to the official API to fetch the gacha history for each banner.
Data Update
In Wuthering Waves and other games, there is a "10-pull" situation.

十连抽
As shown, a "10-pull" can result in obtaining two identical items, meaning two records with exactly the same attributes including summon time. The result from JSON looks like this:
This causes us to be unable to determine which records have already been imported when updating later, affecting the accuracy of statistics. So we need to construct a unique identifier that can distinguish two identical items even at the same time.
We can use banner type ID + timestamp + draw number to determine this unique ID. The draw number represents: starting from 1, the nth draw at the same timestamp. Thus, for the two identical weapons above, we can set uniqueIds as 01174845445601 and 01174845445605 respectively.
This way, even if the imported data overlaps with existing data, we can still identify new gacha records and update the database correctly.
Probability Data Calculation
In the statistical overview page, we use the ECharts library to implement a gacha probability line chart as a component background to display the probability distribution of the gacha system. The line chart data is calculated using two main functions:
Theoretical Probability Calculation
According to the Bilibili UP主 "A Balanced Tree"’s Wuthering Waves Gacha System Brief Analysis, the gacha probability model for Wuthering Waves is as follows, where is the number of pulls:
Using this, we build a theoretical probability function calculateTheoreticalProbability:
Actual Probability Calculation
Since the tool is mainly for personal use, the gacha data sample size is small, and some pull positions might have no data. If we directly use frequency estimation, it will cause the probability curve to fluctuate wildly, appearing with extreme values like 0% or 100%.

Frequency estimation
To avoid this, we can introduce Bayesian smoothing for better results.
Where:
: number of 5-star observed at pull position , : total pulls at pull position , : theoretical probability, : smoothing factor
Its behavior is as follows:
If is small (i.e., data is limited), leans more toward the theoretical probability .
If is large (i.e., data is abundant), leans more toward the actual frequency .
The optimized result looks like this:

Bayesian smoothing
Implementation code:
Automatic Deployment [Deprecated, kept for record]
As mentioned earlier, Astrionyx is deployed both on Vercel and on my own server, so every time I push changes to the repository, I need to pull and build on the server, which is very troublesome. The theme's author had written a workflow that automatically builds and deploys the theme to a remote server; after slight modifications, I used it for Astrionyx.
However, during use, I found that the network connection between GitHub and the server was very poor, causing a lot of time to transfer the built package to the server (average 20+ minutes). Coincidentally, Cloudflare's R2 object storage has 10 GB of free storage per month and free download fees, and the download quality within the country is quite good, so it can serve as a "transfer station" to solve this problem.
The main workflow integrated with object storage is as follows, with 5 retries added for both upload and download (a single download may still fail due to network fluctuations, and 5 retries have proven to solve most issues).
With this workflow, every time I push to the main branch, Astrionyx automatically builds and deploys to the server. Using R2 object storage as a transfer station greatly reduces deployment time (from 20 min to under 4 min), saving life.

Optimized with R2 object storage
That's all🎉.