@zhin.js/plugin-lottery 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/README.md +114 -0
  2. package/agent/agent.ts +6 -0
  3. package/agent/instructions.md +2 -0
  4. package/agent/schedules/daily_pipeline.ts +34 -0
  5. package/agent/skills/lottery.md +36 -0
  6. package/agent/tools/compute_recommend.ts +11 -0
  7. package/agent/tools/get_model_state.ts +11 -0
  8. package/agent/tools/history.ts +22 -0
  9. package/agent/tools/list_pending.ts +11 -0
  10. package/agent/tools/save_prediction.ts +14 -0
  11. package/agent/tools/stats_snapshot.ts +11 -0
  12. package/agent/tools/sync.ts +18 -0
  13. package/evals/pipeline_smoke.eval.ts +8 -0
  14. package/lib/agent/agent.d.ts +3 -0
  15. package/lib/agent/agent.d.ts.map +1 -0
  16. package/lib/agent/agent.js +6 -0
  17. package/lib/agent/agent.js.map +1 -0
  18. package/lib/agent/prompts.d.ts +11 -0
  19. package/lib/agent/prompts.d.ts.map +1 -0
  20. package/lib/agent/prompts.js +11 -0
  21. package/lib/agent/prompts.js.map +1 -0
  22. package/lib/agent/read-skills.d.ts +3 -0
  23. package/lib/agent/read-skills.d.ts.map +1 -0
  24. package/lib/agent/read-skills.js +25 -0
  25. package/lib/agent/read-skills.js.map +1 -0
  26. package/lib/agent/run-turn.d.ts +14 -0
  27. package/lib/agent/run-turn.d.ts.map +1 -0
  28. package/lib/agent/run-turn.js +47 -0
  29. package/lib/agent/run-turn.js.map +1 -0
  30. package/lib/agent/schedules/daily_pipeline.d.ts +3 -0
  31. package/lib/agent/schedules/daily_pipeline.d.ts.map +1 -0
  32. package/lib/agent/schedules/daily_pipeline.js +36 -0
  33. package/lib/agent/schedules/daily_pipeline.js.map +1 -0
  34. package/lib/agent/tools/compute_recommend.d.ts +5 -0
  35. package/lib/agent/tools/compute_recommend.d.ts.map +1 -0
  36. package/lib/agent/tools/compute_recommend.js +11 -0
  37. package/lib/agent/tools/compute_recommend.js.map +1 -0
  38. package/lib/agent/tools/get_model_state.d.ts +5 -0
  39. package/lib/agent/tools/get_model_state.d.ts.map +1 -0
  40. package/lib/agent/tools/get_model_state.js +11 -0
  41. package/lib/agent/tools/get_model_state.js.map +1 -0
  42. package/lib/agent/tools/history.d.ts +6 -0
  43. package/lib/agent/tools/history.d.ts.map +1 -0
  44. package/lib/agent/tools/history.js +24 -0
  45. package/lib/agent/tools/history.js.map +1 -0
  46. package/lib/agent/tools/list_pending.d.ts +5 -0
  47. package/lib/agent/tools/list_pending.d.ts.map +1 -0
  48. package/lib/agent/tools/list_pending.js +11 -0
  49. package/lib/agent/tools/list_pending.js.map +1 -0
  50. package/lib/agent/tools/save_prediction.d.ts +6 -0
  51. package/lib/agent/tools/save_prediction.d.ts.map +1 -0
  52. package/lib/agent/tools/save_prediction.js +14 -0
  53. package/lib/agent/tools/save_prediction.js.map +1 -0
  54. package/lib/agent/tools/stats_snapshot.d.ts +5 -0
  55. package/lib/agent/tools/stats_snapshot.d.ts.map +1 -0
  56. package/lib/agent/tools/stats_snapshot.js +11 -0
  57. package/lib/agent/tools/stats_snapshot.js.map +1 -0
  58. package/lib/agent/tools/sync.d.ts +5 -0
  59. package/lib/agent/tools/sync.d.ts.map +1 -0
  60. package/lib/agent/tools/sync.js +19 -0
  61. package/lib/agent/tools/sync.js.map +1 -0
  62. package/lib/db.d.ts +33 -0
  63. package/lib/db.d.ts.map +1 -0
  64. package/lib/db.js +108 -0
  65. package/lib/db.js.map +1 -0
  66. package/lib/evals/pipeline_smoke.eval.d.ts +3 -0
  67. package/lib/evals/pipeline_smoke.eval.d.ts.map +1 -0
  68. package/lib/evals/pipeline_smoke.eval.js +8 -0
  69. package/lib/evals/pipeline_smoke.eval.js.map +1 -0
  70. package/lib/evaluate/adaptive-sim.d.ts +31 -0
  71. package/lib/evaluate/adaptive-sim.d.ts.map +1 -0
  72. package/lib/evaluate/adaptive-sim.js +155 -0
  73. package/lib/evaluate/adaptive-sim.js.map +1 -0
  74. package/lib/evaluate/backtest.d.ts +29 -0
  75. package/lib/evaluate/backtest.d.ts.map +1 -0
  76. package/lib/evaluate/backtest.js +87 -0
  77. package/lib/evaluate/backtest.js.map +1 -0
  78. package/lib/evaluate/hit-rate.d.ts +8 -0
  79. package/lib/evaluate/hit-rate.d.ts.map +1 -0
  80. package/lib/evaluate/hit-rate.js +120 -0
  81. package/lib/evaluate/hit-rate.js.map +1 -0
  82. package/lib/evaluate/review-pending.d.ts +13 -0
  83. package/lib/evaluate/review-pending.d.ts.map +1 -0
  84. package/lib/evaluate/review-pending.js +89 -0
  85. package/lib/evaluate/review-pending.js.map +1 -0
  86. package/lib/evaluate/tracker.d.ts +28 -0
  87. package/lib/evaluate/tracker.d.ts.map +1 -0
  88. package/lib/evaluate/tracker.js +265 -0
  89. package/lib/evaluate/tracker.js.map +1 -0
  90. package/lib/evaluate/train-weights.d.ts +28 -0
  91. package/lib/evaluate/train-weights.d.ts.map +1 -0
  92. package/lib/evaluate/train-weights.js +101 -0
  93. package/lib/evaluate/train-weights.js.map +1 -0
  94. package/lib/evaluate/weight-guard.d.ts +15 -0
  95. package/lib/evaluate/weight-guard.d.ts.map +1 -0
  96. package/lib/evaluate/weight-guard.js +46 -0
  97. package/lib/evaluate/weight-guard.js.map +1 -0
  98. package/lib/games/kl8-groups.d.ts +31 -0
  99. package/lib/games/kl8-groups.d.ts.map +1 -0
  100. package/lib/games/kl8-groups.js +62 -0
  101. package/lib/games/kl8-groups.js.map +1 -0
  102. package/lib/games/registry.d.ts +13 -0
  103. package/lib/games/registry.d.ts.map +1 -0
  104. package/lib/games/registry.js +45 -0
  105. package/lib/games/registry.js.map +1 -0
  106. package/lib/index.d.ts +3 -0
  107. package/lib/index.d.ts.map +1 -0
  108. package/lib/index.js +215 -0
  109. package/lib/index.js.map +1 -0
  110. package/lib/pipeline.d.ts +36 -0
  111. package/lib/pipeline.d.ts.map +1 -0
  112. package/lib/pipeline.js +130 -0
  113. package/lib/pipeline.js.map +1 -0
  114. package/lib/push.d.ts +3 -0
  115. package/lib/push.d.ts.map +1 -0
  116. package/lib/push.js +31 -0
  117. package/lib/push.js.map +1 -0
  118. package/lib/recommend/game-pick.d.ts +14 -0
  119. package/lib/recommend/game-pick.d.ts.map +1 -0
  120. package/lib/recommend/game-pick.js +169 -0
  121. package/lib/recommend/game-pick.js.map +1 -0
  122. package/lib/recommend/report.d.ts +22 -0
  123. package/lib/recommend/report.d.ts.map +1 -0
  124. package/lib/recommend/report.js +173 -0
  125. package/lib/recommend/report.js.map +1 -0
  126. package/lib/src/agent/prompts.d.ts +11 -0
  127. package/lib/src/agent/prompts.d.ts.map +1 -0
  128. package/lib/src/agent/prompts.js +11 -0
  129. package/lib/src/agent/prompts.js.map +1 -0
  130. package/lib/src/agent/read-skills.d.ts +3 -0
  131. package/lib/src/agent/read-skills.d.ts.map +1 -0
  132. package/lib/src/agent/read-skills.js +25 -0
  133. package/lib/src/agent/read-skills.js.map +1 -0
  134. package/lib/src/agent/run-turn.d.ts +14 -0
  135. package/lib/src/agent/run-turn.d.ts.map +1 -0
  136. package/lib/src/agent/run-turn.js +47 -0
  137. package/lib/src/agent/run-turn.js.map +1 -0
  138. package/lib/src/db.d.ts +33 -0
  139. package/lib/src/db.d.ts.map +1 -0
  140. package/lib/src/db.js +108 -0
  141. package/lib/src/db.js.map +1 -0
  142. package/lib/src/evaluate/adaptive-sim.d.ts +31 -0
  143. package/lib/src/evaluate/adaptive-sim.d.ts.map +1 -0
  144. package/lib/src/evaluate/adaptive-sim.js +159 -0
  145. package/lib/src/evaluate/adaptive-sim.js.map +1 -0
  146. package/lib/src/evaluate/backtest.d.ts +29 -0
  147. package/lib/src/evaluate/backtest.d.ts.map +1 -0
  148. package/lib/src/evaluate/backtest.js +87 -0
  149. package/lib/src/evaluate/backtest.js.map +1 -0
  150. package/lib/src/evaluate/hit-rate.d.ts +8 -0
  151. package/lib/src/evaluate/hit-rate.d.ts.map +1 -0
  152. package/lib/src/evaluate/hit-rate.js +118 -0
  153. package/lib/src/evaluate/hit-rate.js.map +1 -0
  154. package/lib/src/evaluate/review-pending.d.ts +13 -0
  155. package/lib/src/evaluate/review-pending.d.ts.map +1 -0
  156. package/lib/src/evaluate/review-pending.js +89 -0
  157. package/lib/src/evaluate/review-pending.js.map +1 -0
  158. package/lib/src/evaluate/tracker.d.ts +28 -0
  159. package/lib/src/evaluate/tracker.d.ts.map +1 -0
  160. package/lib/src/evaluate/tracker.js +269 -0
  161. package/lib/src/evaluate/tracker.js.map +1 -0
  162. package/lib/src/evaluate/train-weights.d.ts +28 -0
  163. package/lib/src/evaluate/train-weights.d.ts.map +1 -0
  164. package/lib/src/evaluate/train-weights.js +101 -0
  165. package/lib/src/evaluate/train-weights.js.map +1 -0
  166. package/lib/src/evaluate/weight-guard.d.ts +15 -0
  167. package/lib/src/evaluate/weight-guard.d.ts.map +1 -0
  168. package/lib/src/evaluate/weight-guard.js +46 -0
  169. package/lib/src/evaluate/weight-guard.js.map +1 -0
  170. package/lib/src/games/kl8-groups.d.ts +31 -0
  171. package/lib/src/games/kl8-groups.d.ts.map +1 -0
  172. package/lib/src/games/kl8-groups.js +62 -0
  173. package/lib/src/games/kl8-groups.js.map +1 -0
  174. package/lib/src/games/registry.d.ts +13 -0
  175. package/lib/src/games/registry.d.ts.map +1 -0
  176. package/lib/src/games/registry.js +45 -0
  177. package/lib/src/games/registry.js.map +1 -0
  178. package/lib/src/index.d.ts +3 -0
  179. package/lib/src/index.d.ts.map +1 -0
  180. package/lib/src/index.js +183 -0
  181. package/lib/src/index.js.map +1 -0
  182. package/lib/src/lottery-agent-deps.d.ts +24 -0
  183. package/lib/src/lottery-agent-deps.d.ts.map +1 -0
  184. package/lib/src/lottery-agent-deps.js +10 -0
  185. package/lib/src/lottery-agent-deps.js.map +1 -0
  186. package/lib/src/lottery-tool-handlers.d.ts +17 -0
  187. package/lib/src/lottery-tool-handlers.d.ts.map +1 -0
  188. package/lib/src/lottery-tool-handlers.js +155 -0
  189. package/lib/src/lottery-tool-handlers.js.map +1 -0
  190. package/lib/src/pipeline.d.ts +36 -0
  191. package/lib/src/pipeline.d.ts.map +1 -0
  192. package/lib/src/pipeline.js +130 -0
  193. package/lib/src/pipeline.js.map +1 -0
  194. package/lib/src/push.d.ts +3 -0
  195. package/lib/src/push.d.ts.map +1 -0
  196. package/lib/src/push.js +31 -0
  197. package/lib/src/push.js.map +1 -0
  198. package/lib/src/recommend/game-pick.d.ts +14 -0
  199. package/lib/src/recommend/game-pick.d.ts.map +1 -0
  200. package/lib/src/recommend/game-pick.js +169 -0
  201. package/lib/src/recommend/game-pick.js.map +1 -0
  202. package/lib/src/recommend/report.d.ts +22 -0
  203. package/lib/src/recommend/report.d.ts.map +1 -0
  204. package/lib/src/recommend/report.js +176 -0
  205. package/lib/src/recommend/report.js.map +1 -0
  206. package/lib/src/stats/engine.d.ts +18 -0
  207. package/lib/src/stats/engine.d.ts.map +1 -0
  208. package/lib/src/stats/engine.js +122 -0
  209. package/lib/src/stats/engine.js.map +1 -0
  210. package/lib/src/stats/pro-analysis.d.ts +7 -0
  211. package/lib/src/stats/pro-analysis.d.ts.map +1 -0
  212. package/lib/src/stats/pro-analysis.js +121 -0
  213. package/lib/src/stats/pro-analysis.js.map +1 -0
  214. package/lib/src/stats/weights.d.ts +8 -0
  215. package/lib/src/stats/weights.d.ts.map +1 -0
  216. package/lib/src/stats/weights.js +25 -0
  217. package/lib/src/stats/weights.js.map +1 -0
  218. package/lib/src/sync/fetch-official.d.ts +4 -0
  219. package/lib/src/sync/fetch-official.d.ts.map +1 -0
  220. package/lib/src/sync/fetch-official.js +160 -0
  221. package/lib/src/sync/fetch-official.js.map +1 -0
  222. package/lib/src/sync/run-sync.d.ts +8 -0
  223. package/lib/src/sync/run-sync.d.ts.map +1 -0
  224. package/lib/src/sync/run-sync.js +29 -0
  225. package/lib/src/sync/run-sync.js.map +1 -0
  226. package/lib/src/tools/agent-tools.d.ts +15 -0
  227. package/lib/src/tools/agent-tools.d.ts.map +1 -0
  228. package/lib/src/tools/agent-tools.js +211 -0
  229. package/lib/src/tools/agent-tools.js.map +1 -0
  230. package/lib/src/types.d.ts +84 -0
  231. package/lib/src/types.d.ts.map +1 -0
  232. package/lib/src/types.js +3 -0
  233. package/lib/src/types.js.map +1 -0
  234. package/lib/stats/engine.d.ts +18 -0
  235. package/lib/stats/engine.d.ts.map +1 -0
  236. package/lib/stats/engine.js +122 -0
  237. package/lib/stats/engine.js.map +1 -0
  238. package/lib/stats/pro-analysis.d.ts +7 -0
  239. package/lib/stats/pro-analysis.d.ts.map +1 -0
  240. package/lib/stats/pro-analysis.js +121 -0
  241. package/lib/stats/pro-analysis.js.map +1 -0
  242. package/lib/stats/weights.d.ts +8 -0
  243. package/lib/stats/weights.d.ts.map +1 -0
  244. package/lib/stats/weights.js +25 -0
  245. package/lib/stats/weights.js.map +1 -0
  246. package/lib/sync/fetch-official.d.ts +4 -0
  247. package/lib/sync/fetch-official.d.ts.map +1 -0
  248. package/lib/sync/fetch-official.js +160 -0
  249. package/lib/sync/fetch-official.js.map +1 -0
  250. package/lib/sync/run-sync.d.ts +8 -0
  251. package/lib/sync/run-sync.d.ts.map +1 -0
  252. package/lib/sync/run-sync.js +29 -0
  253. package/lib/sync/run-sync.js.map +1 -0
  254. package/lib/tools/agent-tools.d.ts +15 -0
  255. package/lib/tools/agent-tools.d.ts.map +1 -0
  256. package/lib/tools/agent-tools.js +211 -0
  257. package/lib/tools/agent-tools.js.map +1 -0
  258. package/lib/tsconfig.tsbuildinfo +1 -0
  259. package/lib/types.d.ts +84 -0
  260. package/lib/types.d.ts.map +1 -0
  261. package/lib/types.js +3 -0
  262. package/lib/types.js.map +1 -0
  263. package/package.json +73 -0
  264. package/src/agent/prompts.ts +21 -0
  265. package/src/agent/read-skills.ts +27 -0
  266. package/src/agent/run-turn.ts +74 -0
  267. package/src/db.ts +132 -0
  268. package/src/evaluate/adaptive-sim.ts +210 -0
  269. package/src/evaluate/backtest.ts +128 -0
  270. package/src/evaluate/hit-rate.ts +132 -0
  271. package/src/evaluate/review-pending.ts +120 -0
  272. package/src/evaluate/tracker.ts +311 -0
  273. package/src/evaluate/train-weights.ts +134 -0
  274. package/src/evaluate/weight-guard.ts +68 -0
  275. package/src/games/kl8-groups.ts +98 -0
  276. package/src/games/registry.ts +55 -0
  277. package/src/index.ts +216 -0
  278. package/src/lottery-agent-deps.ts +29 -0
  279. package/src/lottery-tool-handlers.ts +166 -0
  280. package/src/pipeline.ts +206 -0
  281. package/src/push.ts +34 -0
  282. package/src/recommend/game-pick.ts +248 -0
  283. package/src/recommend/report.ts +211 -0
  284. package/src/stats/engine.ts +143 -0
  285. package/src/stats/pro-analysis.ts +136 -0
  286. package/src/stats/weights.ts +29 -0
  287. package/src/sync/fetch-official.ts +185 -0
  288. package/src/sync/run-sync.ts +33 -0
  289. package/src/types.ts +98 -0
@@ -0,0 +1,136 @@
1
+ import { DEFAULT_WEIGHTS, type DrawNumbers, type GameId, type NormalizedDraw, type ScoreWeights } from '../types.js';
2
+
3
+ import { formatWeights, normalizeWeights } from './weights.js';
4
+ import { stableHash } from './engine.js';
5
+
6
+ const RECENT_WINDOW = 30;
7
+
8
+ export function affinityOfNumbers(
9
+ nums: number[],
10
+ freqMap: Map<number, number>,
11
+ omitMap: Map<number, number>,
12
+ trendMap: Map<number, number>,
13
+ ): ScoreWeights {
14
+ if (!nums.length) return { ...DEFAULT_WEIGHTS };
15
+ let f = 0;
16
+ let o = 0;
17
+ let t = 0;
18
+ for (const n of nums) {
19
+ f += freqMap.get(n) ?? 0;
20
+ o += omitMap.get(n) ?? 0;
21
+ t += trendMap.get(n) ?? 0;
22
+ }
23
+ const div = nums.length;
24
+ return normalizeWeights({ freq: f / div, omit: o / div, trend: t / div });
25
+ }
26
+
27
+ export function buildOmitMap(draws: number[][], poolMin: number, poolMax: number): Map<number, number> {
28
+ const lastSeen = new Map<number, number>();
29
+ for (let i = poolMin; i <= poolMax; i++) lastSeen.set(i, draws.length);
30
+ draws.forEach((nums, idx) => {
31
+ for (const n of nums) {
32
+ if (n >= poolMin && n <= poolMax) lastSeen.set(n, idx);
33
+ }
34
+ });
35
+ const maxOmit = Math.max(1, ...lastSeen.values());
36
+ const out = new Map<number, number>();
37
+ for (let i = poolMin; i <= poolMax; i++) out.set(i, (lastSeen.get(i) ?? 0) / maxOmit);
38
+ return out;
39
+ }
40
+
41
+ export function buildFreqMap(draws: number[][], poolMin: number, poolMax: number): Map<number, number> {
42
+ const freq = new Map<number, number>();
43
+ for (let i = poolMin; i <= poolMax; i++) freq.set(i, 0);
44
+ for (const row of draws) {
45
+ for (const n of row) {
46
+ if (n >= poolMin && n <= poolMax) freq.set(n, (freq.get(n) ?? 0) + 1);
47
+ }
48
+ }
49
+ const max = Math.max(1, ...freq.values());
50
+ for (const [k, v] of freq) freq.set(k, v / max);
51
+ return freq;
52
+ }
53
+
54
+ export function computeProMetrics(
55
+ gameId: GameId,
56
+ pick: number[],
57
+ draws: NormalizedDraw[],
58
+ weights: ScoreWeights,
59
+ poolMin?: number,
60
+ poolMax?: number,
61
+ mid?: number,
62
+ ): import('../types.js').ProMetrics {
63
+ const odd = pick.filter((n) => n % 2 === 1).length;
64
+ const even = pick.length - odd;
65
+ const m = mid ?? (poolMax && poolMin ? Math.floor((poolMin + poolMax) / 2) : 5);
66
+ const big = pick.filter((n) => n > m).length;
67
+ const small = pick.length - big;
68
+ const sum = pick.reduce((a, b) => a + b, 0);
69
+ const span = pick.length ? Math.max(...pick) - Math.min(...pick) : 0;
70
+
71
+ let zones: string | undefined;
72
+ if (poolMin != null && poolMax != null && gameId !== 'fc3d' && gameId !== 'pl3' && gameId !== 'pl5') {
73
+ const size = Math.ceil((poolMax - poolMin + 1) / 3);
74
+ const z = [0, 0, 0];
75
+ for (const n of pick) {
76
+ const idx = Math.min(2, Math.floor((n - poolMin) / size));
77
+ z[idx] = (z[idx] ?? 0) + 1;
78
+ }
79
+ zones = `三区 ${z.join(':')}`;
80
+ }
81
+
82
+ const rows = draws.map((d) => extractFlat(d.numbers, gameId)).filter((a) => a.length);
83
+ const recent = rows.slice(0, 10).flat();
84
+ const freq = new Map<number, number>();
85
+ for (const n of recent) freq.set(n, (freq.get(n) ?? 0) + 1);
86
+ const recentHot = [...freq.entries()]
87
+ .sort((a, b) => b[1] - a[1] || stableHash(`hot:${a[0]}`) - stableHash(`hot:${b[0]}`))
88
+ .slice(0, 5)
89
+ .map(([n]) => String(n))
90
+ .join(',');
91
+
92
+ return {
93
+ oddEven: `奇偶 ${odd}:${even}`,
94
+ bigSmall: `大小 ${big}:${small}`,
95
+ sum,
96
+ span,
97
+ zones,
98
+ recentHot: recentHot ? `近10期热号 ${recentHot}` : '',
99
+ modelWeights: `权重 ${formatWeights(weights)}`,
100
+ };
101
+ }
102
+
103
+ export function computeDigitProMetrics(
104
+ digits: number[],
105
+ draws: NormalizedDraw[],
106
+ weights: ScoreWeights,
107
+ ): import('../types.js').ProMetrics {
108
+ const sum = digits.reduce((a, b) => a + b, 0);
109
+ const span = digits.length ? Math.max(...digits) - Math.min(...digits) : 0;
110
+ const posHints: string[] = [];
111
+ const rows = draws.map((d) => d.numbers.digits ?? []).filter((a) => a.length);
112
+ for (let pos = 0; pos < digits.length; pos++) {
113
+ const col = rows.map((r) => r[pos]).filter((n) => n !== undefined);
114
+ const freq = new Map<number, number>();
115
+ for (let d = 0; d <= 9; d++) freq.set(d, 0);
116
+ col.slice(0, RECENT_WINDOW).forEach((n) => freq.set(n, (freq.get(n) ?? 0) + 1));
117
+ const top = [...freq.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([d]) => d);
118
+ posHints.push(`第${pos + 1}位[${top.join(',')}]`);
119
+ }
120
+ return {
121
+ oddEven: '',
122
+ bigSmall: '',
123
+ sum,
124
+ span,
125
+ digitPos: posHints.join(' '),
126
+ recentHot: '',
127
+ modelWeights: `权重 ${formatWeights(weights)}`,
128
+ };
129
+ }
130
+
131
+ function extractFlat(n: DrawNumbers, gameId: GameId): number[] {
132
+ if (gameId === 'kl8') return n.main ?? [];
133
+ if (gameId === 'ssq') return n.red ?? [];
134
+ if (gameId === 'dlt') return n.front ?? [];
135
+ return n.digits ?? [];
136
+ }
@@ -0,0 +1,29 @@
1
+ import { DEFAULT_WEIGHTS, type ScoreWeights } from '../types.js';
2
+
3
+ export function normalizeWeights(w: ScoreWeights | undefined): ScoreWeights {
4
+ const src = w ?? DEFAULT_WEIGHTS;
5
+ const freq = Math.max(0.1, src.freq);
6
+ const omit = Math.max(0.1, src.omit);
7
+ const trend = Math.max(0.1, src.trend);
8
+ const sum = freq + omit + trend;
9
+ return { freq: freq / sum, omit: omit / sum, trend: trend / sum };
10
+ }
11
+
12
+ export function formatWeights(w: ScoreWeights | undefined): string {
13
+ const n = normalizeWeights(w);
14
+ return `F${Math.round(n.freq * 100)} O${Math.round(n.omit * 100)} T${Math.round(n.trend * 100)}`;
15
+ }
16
+
17
+ export function clampWeight(v: number, min = 0.1, max = 0.6): number {
18
+ return Math.min(max, Math.max(min, v));
19
+ }
20
+
21
+ export function mergeWeights(current: ScoreWeights, target: ScoreWeights, lr = 0.05): ScoreWeights {
22
+ return normalizeWeights({
23
+ freq: clampWeight(current.freq + lr * (target.freq - current.freq)),
24
+ omit: clampWeight(current.omit + lr * (target.omit - current.omit)),
25
+ trend: clampWeight(current.trend + lr * (target.trend - current.trend)),
26
+ });
27
+ }
28
+
29
+ export { DEFAULT_WEIGHTS };
@@ -0,0 +1,185 @@
1
+ import type { DrawNumbers, GameId, NormalizedDraw } from '../types.js';
2
+ import { parseNumberList } from '../stats/engine.js';
3
+
4
+ const FUCAI_URL = 'https://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice';
5
+ const TICAI_URL = 'https://webapi.sporttery.cn/gateway/lottery/getHistoryPageListV1.qry';
6
+ const PAGE_SIZE = 30;
7
+
8
+ const BROWSER_UA =
9
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
10
+
11
+ const FUCAI_REFERER: Record<string, string> = {
12
+ kl8: 'https://www.cwl.gov.cn/ygkj/wqkjgg/kl8/',
13
+ ssq: 'https://www.cwl.gov.cn/ygkj/wqkjgg/ssq/',
14
+ '3d': 'https://www.cwl.gov.cn/ygkj/wqkjgg/3d/',
15
+ };
16
+
17
+ function fucaiHeaders(fucaiName: string): Record<string, string> {
18
+ return {
19
+ 'User-Agent': BROWSER_UA,
20
+ Accept: 'application/json, text/plain, */*',
21
+ 'Accept-Language': 'zh-CN,zh;q=0.9',
22
+ Referer: FUCAI_REFERER[fucaiName] ?? 'https://www.cwl.gov.cn/',
23
+ };
24
+ }
25
+
26
+ function ticaiHeaders(): Record<string, string> {
27
+ return {
28
+ 'User-Agent': BROWSER_UA,
29
+ Accept: 'application/json, text/plain, */*',
30
+ 'Accept-Language': 'zh-CN,zh;q=0.9',
31
+ Referer: 'https://www.lottery.gov.cn/',
32
+ Origin: 'https://www.lottery.gov.cn',
33
+ };
34
+ }
35
+
36
+ async function fetchJson<T>(
37
+ url: string,
38
+ headers: Record<string, string>,
39
+ fetchImpl: typeof fetch,
40
+ ): Promise<T> {
41
+ const res = await fetchImpl(url, { headers });
42
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
43
+ return (await res.json()) as T;
44
+ }
45
+
46
+ export async function fetchFucaiDraws(
47
+ fucaiName: string,
48
+ count: number,
49
+ fetchImpl: typeof fetch = fetch,
50
+ ): Promise<NormalizedDraw[]> {
51
+ const headers = fucaiHeaders(fucaiName);
52
+ const draws: NormalizedDraw[] = [];
53
+ let pageNo = 1;
54
+ while (draws.length < count) {
55
+ const pageSize = Math.min(PAGE_SIZE, count - draws.length);
56
+ const params = new URLSearchParams({
57
+ name: fucaiName,
58
+ pageNo: String(pageNo),
59
+ pageSize: String(pageSize),
60
+ systemType: 'PC',
61
+ issueCount: '',
62
+ issueStart: '',
63
+ issueEnd: '',
64
+ dayStart: '',
65
+ dayEnd: '',
66
+ });
67
+ const json = await fetchJson<{ state?: number; message?: string; result?: unknown[] }>(
68
+ `${FUCAI_URL}?${params}`,
69
+ headers,
70
+ fetchImpl,
71
+ );
72
+ if (json.state !== 0) {
73
+ throw new Error(json.message ?? `福彩接口 state=${String(json.state)}`);
74
+ }
75
+ const batch = (json.result ?? []).map((row) => parseFucaiRow(fucaiName, row));
76
+ if (!batch.length) break;
77
+ draws.push(...batch);
78
+ if (batch.length < pageSize) break;
79
+ pageNo++;
80
+ }
81
+ return draws.slice(0, count);
82
+ }
83
+
84
+ export async function fetchTicaiDraws(
85
+ gameNo: string,
86
+ count: number,
87
+ fetchImpl: typeof fetch = fetch,
88
+ ): Promise<NormalizedDraw[]> {
89
+ const headers = ticaiHeaders();
90
+ const draws: NormalizedDraw[] = [];
91
+ let pageNo = 1;
92
+ while (draws.length < count) {
93
+ const pageSize = Math.min(PAGE_SIZE, count - draws.length);
94
+ const params = new URLSearchParams({
95
+ gameNo,
96
+ provinceId: '0',
97
+ pageSize: String(pageSize),
98
+ pageNo: String(pageNo),
99
+ isVerify: '1',
100
+ });
101
+ const json = await fetchJson<{
102
+ success?: boolean;
103
+ errorMessage?: string;
104
+ value?: { list?: unknown[] };
105
+ }>(`${TICAI_URL}?${params}`, headers, fetchImpl);
106
+ if (json.success === false) {
107
+ throw new Error(json.errorMessage ?? '体彩接口返回失败');
108
+ }
109
+ const batch = (json.value?.list ?? []).map((row) => parseTicaiRow(gameNo, row));
110
+ if (!batch.length) break;
111
+ draws.push(...batch);
112
+ if (batch.length < pageSize) break;
113
+ pageNo++;
114
+ }
115
+ return draws.slice(0, count);
116
+ }
117
+
118
+ function parseFucaiRow(fucaiName: string, row: unknown): NormalizedDraw {
119
+ const r = row as Record<string, string>;
120
+ const issue = String(r.code ?? r.issue ?? '');
121
+ const drawTime = String(r.date ?? r.opentime ?? '').replace(/\([^)]*\)$/, '');
122
+ const gameId = fucaiNameToGameId(fucaiName);
123
+ const numbers = parseFucaiNumbers(gameId, r);
124
+ return { gameId, issue, drawTime, numbers, source: 'fucai' };
125
+ }
126
+
127
+ function fucaiNameToGameId(name: string): GameId {
128
+ if (name === 'kl8') return 'kl8';
129
+ if (name === 'ssq') return 'ssq';
130
+ return 'fc3d';
131
+ }
132
+
133
+ function parseFucaiNumbers(gameId: GameId, r: Record<string, string>): DrawNumbers {
134
+ if (gameId === 'kl8') {
135
+ const raw = r.red ?? r.kjhyjsx ?? r.kjSpare ?? r.number ?? '';
136
+ return { main: parseNumberList(raw) };
137
+ }
138
+ if (gameId === 'ssq') {
139
+ return {
140
+ red: parseNumberList(r.red),
141
+ blue: parseNumberList(r.blue),
142
+ };
143
+ }
144
+ const digits = parseNumberList(r.red ?? r.code ?? '');
145
+ if (digits.length >= 3) return { digits: digits.slice(0, 3) };
146
+ const s = String(r.red ?? r.code ?? '').replace(/\D/g, '');
147
+ return { digits: [...s].slice(0, 3).map((c) => Number.parseInt(c, 10)) };
148
+ }
149
+
150
+ function parseTicaiRow(gameNo: string, row: unknown): NormalizedDraw {
151
+ const r = row as Record<string, string>;
152
+ const issue = String(r.lotteryDrawNum ?? r.issue ?? '');
153
+ const drawTime = String(r.lotteryDrawTime ?? r.drawTime ?? '').split(' ')[0] ?? '';
154
+ const gameId = ticaiGameNoToGameId(gameNo);
155
+ const numbers = parseTicaiNumbers(gameId, r);
156
+ return { gameId, issue, drawTime, numbers, source: 'ticai' };
157
+ }
158
+
159
+ function ticaiGameNoToGameId(gameNo: string): GameId {
160
+ if (gameNo === '85') return 'dlt';
161
+ if (gameNo === '35') return 'pl3';
162
+ return 'pl5';
163
+ }
164
+
165
+ function parseTicaiNumbers(gameId: GameId, r: Record<string, string>): DrawNumbers {
166
+ const result = String(r.lotteryDrawResult ?? r.number ?? '');
167
+ if (gameId === 'dlt') {
168
+ if (result.includes('+')) {
169
+ const [frontRaw, backRaw] = result.split('+').map((s) => s.trim());
170
+ return {
171
+ front: parseNumberList(frontRaw),
172
+ back: parseNumberList(backRaw),
173
+ };
174
+ }
175
+ const nums = parseNumberList(result);
176
+ return { front: nums.slice(0, 5), back: nums.slice(5, 7) };
177
+ }
178
+ const digits = result
179
+ .replace(/\D/g, '')
180
+ .split('')
181
+ .map((c) => Number.parseInt(c, 10))
182
+ .filter((n) => Number.isFinite(n));
183
+ const len = gameId === 'pl5' ? 5 : 3;
184
+ return { digits: digits.slice(0, len) };
185
+ }
@@ -0,0 +1,33 @@
1
+ import type { GameId, NormalizedDraw } from '../types.js';
2
+ import { syncGame, type LotteryDb } from '../db.js';
3
+
4
+ export async function runDataSync(
5
+ getDb: () => LotteryDb | null,
6
+ enabledGames: () => GameId[],
7
+ historyLimit: number,
8
+ gameId?: GameId,
9
+ ): Promise<{ text: string; newSummary: string; inserted: NormalizedDraw[] }> {
10
+ const db = getDb();
11
+ if (!db) return { text: 'db not ready', newSummary: '', inserted: [] };
12
+ const ids = gameId ? [gameId] : enabledGames();
13
+ const parts: string[] = [];
14
+ const newParts: string[] = [];
15
+ const inserted: NormalizedDraw[] = [];
16
+ for (const id of ids) {
17
+ try {
18
+ const { count, inserted: ins } = await syncGame(db, id, historyLimit);
19
+ parts.push(`${id}:+${count}`);
20
+ inserted.push(...ins);
21
+ if (ins.length) {
22
+ newParts.push(`${id}:${ins.map((d) => d.issue).join(',')}`);
23
+ }
24
+ } catch {
25
+ parts.push(`${id}:err`);
26
+ }
27
+ }
28
+ return {
29
+ text: `sync ${parts.join(', ')}`,
30
+ newSummary: newParts.join('; '),
31
+ inserted,
32
+ };
33
+ }
package/src/types.ts ADDED
@@ -0,0 +1,98 @@
1
+ export type GameId = 'kl8' | 'ssq' | 'dlt' | 'fc3d' | 'pl3' | 'pl5';
2
+
3
+ export type LotterySource = 'fucai' | 'ticai';
4
+
5
+ export interface DrawNumbers {
6
+ main?: number[];
7
+ red?: number[];
8
+ blue?: number[];
9
+ front?: number[];
10
+ back?: number[];
11
+ digits?: number[];
12
+ }
13
+
14
+ export interface NormalizedDraw {
15
+ gameId: GameId;
16
+ issue: string;
17
+ drawTime: string;
18
+ numbers: DrawNumbers;
19
+ source: LotterySource;
20
+ }
21
+
22
+ /** 打分权重:频率 / 遗漏 / 近期趋势 */
23
+ export interface ScoreWeights {
24
+ freq: number;
25
+ omit: number;
26
+ trend: number;
27
+ }
28
+
29
+ export interface ProMetrics {
30
+ oddEven: string;
31
+ bigSmall: string;
32
+ sum: number;
33
+ span: number;
34
+ zones?: string;
35
+ digitPos?: string;
36
+ recentHot: string;
37
+ modelWeights: string;
38
+ }
39
+
40
+ export interface AccuracySnapshot {
41
+ evalCount: number;
42
+ avgHitRate: number;
43
+ recent5: string;
44
+ }
45
+
46
+ export interface StatsSnapshot {
47
+ gameId: GameId;
48
+ sampleSize: number;
49
+ hot: string[];
50
+ cold: string[];
51
+ detail: string;
52
+ pro?: ProMetrics;
53
+ accuracy?: AccuracySnapshot;
54
+ }
55
+
56
+ export interface GamePick {
57
+ gameId: GameId;
58
+ label: string;
59
+ numbers: DrawNumbers;
60
+ stats: StatsSnapshot;
61
+ weights: ScoreWeights;
62
+ /** KL8 multi-strategy rows (primary numbers still in numbers.main) */
63
+ kl8Groups?: Kl8PickGroup[];
64
+ }
65
+
66
+ export type Kl8GroupStrategy = 'balanced' | 'hot' | 'cold' | 'trend';
67
+
68
+ export interface Kl8PickGroup {
69
+ index: number;
70
+ strategy: Kl8GroupStrategy;
71
+ label: string;
72
+ numbers: number[];
73
+ weights: ScoreWeights;
74
+ }
75
+
76
+ export interface HitSummary {
77
+ total: number;
78
+ hits: number;
79
+ rate: number;
80
+ detail: string;
81
+ }
82
+
83
+ export interface DailyReport {
84
+ date: string;
85
+ picks: GamePick[];
86
+ disclaimer: string;
87
+ accuracyOverview?: string;
88
+ backtestOverview?: string;
89
+ /** games that used DEFAULT due to holdout guard */
90
+ weightFallbackGames?: GameId[];
91
+ /** Agent 生成的完整正文(优先展示) */
92
+ body?: string;
93
+ }
94
+
95
+ export const DISCLAIMER =
96
+ '免责声明:本推荐由历史数据统计生成,仅供参考,不构成投注建议,不保证中奖。请理性购彩。';
97
+
98
+ export const DEFAULT_WEIGHTS: ScoreWeights = { freq: 0.4, omit: 0.35, trend: 0.25 };