@@ -16,9 +16,29 @@ import FishPi from "fishpi";
1616
1717const router = Router ( ) ;
1818
19- router . get ( '/:username/:game' , async ( req : Request , res : Response ) => {
19+ function valueToColor ( value : string , defaultValue : string ) : string {
20+ if ( ! value ) return defaultValue ;
21+ if ( value . match ( / ^ ( [ 0 - 9 a - f A - F ] { 6 } | [ 0 - 9 a - f A - F ] { 3 } ) $ / ) ) {
22+ return '#' + value ;
23+ } else {
24+ return value ;
25+ }
26+ }
27+
28+ router . get ( '/state/:username/:game.svg' , async ( req : Request , res : Response ) => {
2029 const { username, game } = req . params ;
2130
31+ // Extract color variables from query params
32+ const contentColor = valueToColor ( req . query [ 'content' ] as string , '#000' ) ;
33+ const bgColor = valueToColor ( req . query [ 'bg' ] as string , '#f3f4f6' ) ;
34+ const winColor = valueToColor ( req . query [ 'win' ] as string , '#16a34a' ) ;
35+ const drawColor = valueToColor ( req . query [ 'draw' ] as string , '#ca8a04' ) ;
36+ const lossColor = valueToColor ( req . query [ 'loss' ] as string , '#dc2626' ) ;
37+ const scoreColor = valueToColor ( req . query [ 'score' ] as string , '#06b6d4' ) ;
38+ const progressBg = valueToColor ( req . query [ 'progress-bg' ] as string , '#e5e7eb' ) ;
39+ const textGray = valueToColor ( req . query [ 'text-gray' ] as string , '#6b7280' ) ;
40+ const errorColor = valueToColor ( req . query [ 'error' ] as string , 'red' ) ;
41+
2242 try {
2343 const stats = await getPlayerStat ( username , game ) ;
2444 const gameName = Games [ game ] ?. name || game ;
@@ -28,84 +48,92 @@ router.get('/:username/:game', async (req: Request, res: Response) => {
2848 const drawPercent = stats . total > 0 ? ( stats . draws / stats . total * 100 ) : 0 ;
2949 const lossPercent = stats . total > 0 ? ( stats . losses / stats . total * 100 ) : 0 ;
3050 htmlContent = `
31- <style>
32- .card { background-color: #f3f4f6; border-radius: 8px; padding: 16px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; }
33- .header { display: flex; justify-content: space-between; align-items: baseline; }
34- .game-name { font-size: 14px; font-weight: bold; margin-bottom: 4px; }
35- .score-label { font-size: 12px; opacity: 0.6; }
36- .stats { display: flex; justify-content: space-between; font-size: 12px; }
37- .win { color: #16a34a; }
38- .draw { color: #ca8a04; }
39- .loss { color: #dc2626; }
40- .progress-bar { width: 100%; background-color: #e5e7eb; border-radius: 9999px; height: 8px; margin-top: 8px; overflow: hidden; display: flex; }
41- .progress-win { background-color: #16a34a; height: 100%; }
42- .progress-draw { background-color: #ca8a04; height: 100%; }
43- .progress-loss { background-color: #dc2626; height: 100%; }
44- .score-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 0; }
45- .score { font-size: 30px; font-weight: 900; color: #06b6d4; }
46- .bottom { text-align: right; font-size: 12px; margin-top: 4px; opacity: 0.6; padding-right: 4px; }
47- </style>
48- <div class="card">
49- <header class="header">
50- <div class="game-name">${ gameName } </div>
51- ${ stats . score ? '<div class="score-label">最高得分</div>' : '' }
52- </header>
53- ${ ! stats . score ? `
54- <div class="stats">
55- <span class="win">胜: ${ stats . wins } </span>
56- <span class="draw">平: ${ stats . draws } </span>
57- <span class="loss">负: ${ stats . losses } </span>
58- </div>
59- <div class="progress-bar">
60- <div class="progress-win" style="width: ${ winPercent } %;"></div>
61- <div class="progress-draw" style="width: ${ drawPercent } %;"></div>
62- <div class="progress-loss" style="width: ${ lossPercent } %;"></div>
63- </div>
64- ` : `
65- <div class="score-container">
66- <div class="score">${ stats . score } </div>
67- </div>
68- ` }
69- <div class="bottom">游玩次数:${ stats . total } </div>
51+ <style>
52+ :root {
53+ --bg-color: ${ bgColor } ;
54+ --win-color: ${ winColor } ;
55+ --draw-color: ${ drawColor } ;
56+ --loss-color: ${ lossColor } ;
57+ --score-color: ${ scoreColor } ;
58+ --progress-bg: ${ progressBg } ;
59+ --content-color: ${ contentColor } ;
60+ --text-gray: ${ textGray } ;
61+ }
62+ .card { color: var(--content-color); background-color: var(--bg-color); border-radius: 8px; padding: 16px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; }
63+ .header { display: flex; justify-content: space-between; align-items: baseline; }
64+ .game-name { font-size: 14px; font-weight: bold; margin-bottom: 4px; opacity: 0.6; }
65+ .score-label { font-size: 12px; opacity: 0.6; }
66+ .stats { display: flex; justify-content: space-between; font-size: 12px; }
67+ .win { color: var(--win-color); }
68+ .draw { color: var(--draw-color); }
69+ .loss { color: var(--loss-color); }
70+ .progress-bar { width: 100%; background-color: var(--progress-bg); border-radius: 9999px; height: 8px; margin-top: 8px; overflow: hidden; display: flex; }
71+ .progress-win { background-color: var(--win-color); height: 100%; }
72+ .progress-draw { background-color: var(--draw-color); height: 100%; }
73+ .progress-loss { background-color: var(--loss-color); height: 100%; }
74+ .score-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 0; }
75+ .score { font-size: 30px; font-weight: 900; color: var(--score-color); }
76+ .bottom { text-align: right; font-size: 12px; margin-top: 4px; opacity: 0.8; padding-right: 4px; color: var(--text-gray); }
77+ </style>
78+ <div class="card">
79+ <header class="header">
80+ <div class="game-name">${ gameName } </div>${ stats . score ? `
81+ <div class="score-label">最高得分</div>` : '' }
82+ </header>${ ! stats . score ? `
83+ <div class="stats">
84+ <span class="win">胜: ${ stats . wins } </span>
85+ <span class="draw">平: ${ stats . draws } </span>
86+ <span class="loss">负: ${ stats . losses } </span>
7087 </div>
71- <div class="bottom">@${ username } </div>
72- ` ;
88+ <div class="progress-bar">
89+ <div class="progress-win" style="width: ${ winPercent } %;"></div>
90+ <div class="progress-draw" style="width: ${ drawPercent } %;"></div>
91+ <div class="progress-loss" style="width: ${ lossPercent } %;"></div>
92+ </div>` : `
93+ <div class="score-container">
94+ <div class="score">${ stats . score } </div>
95+ </div>` }
96+ <div class="bottom">游玩次数:${ stats . total } </div>
97+ </div>
98+ <div class="bottom">@${ username } </div>
99+ ` ;
73100 } else {
74101 htmlContent = `
75- <style>
76- .no-data { background-color: #f3f4f6; border-radius: 8px; padding: 16px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
77- .no-data-text { text-align: center; color: #6b7280; }
78- </style>
79- <div class="no-data">
80- <div class="no-data-text">未找到 ${ username } 在 ${ gameName } 中的战绩</div>
81- </div>
82- ` ;
83- }
84- const svg = `
85- <svg width="400" height="168" xmlns="http://www.w3.org/2000/svg">
86- <foreignObject x="0" y="0" width="400" height="168">
87- <html xmlns="http://www.w3.org/1999/xhtml">
88- ${ htmlContent }
89- </html>
90- </foreignObject>
91- </svg>
102+ <style>
103+ :root {
104+ --bg-color: ${ bgColor } ;
105+ --text-gray: ${ textGray } ;
106+ }
107+ .no-data { background-color: var(--bg-color); border-radius: 8px; padding: 16px; width: 368px; height: 168px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
108+ .no-data-text { text-align: center; color: var(--text-gray); }
109+ </style>
110+ <div class="no-data">
111+ <div class="no-data-text">未找到 ${ username } 在 ${ gameName } 中的战绩</div>
112+ </div>
92113 ` ;
114+ }
115+ const svg = `<svg width="360" height="168" xmlns="http://www.w3.org/2000/svg">
116+ <foreignObject x="0" y="0" width="360" height="168">
117+ <html xmlns="http://www.w3.org/1999/xhtml">${ htmlContent } </html>
118+ </foreignObject>
119+ </svg>` ;
93120 res . setHeader ( 'Content-Type' , 'image/svg+xml; charset=utf-8' ) ;
94121 res . send ( svg ) ;
95122 } catch ( error ) {
96123 console . error ( error ) ;
97- const errorSvg = `
98- <svg width="400" height="168" xmlns="http://www.w3.org/2000/svg">
99- <foreignObject x="0" y="0" width="400" height="168">
100- <html xmlns="http://www.w3.org/1999/xhtml">
101- <style>
102- .error { color: red; text-align: center; padding: 20px; width: 400px; height: 168px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
103- </style>
104- <div class="error">服务器错误</div>
105- </html>
106- </foreignObject>
107- </svg>
108- ` ;
124+ const errorSvg = `<svg width="360" height="168" xmlns="http://www.w3.org/2000/svg">
125+ <foreignObject x="0" y="0" width="360" height="168">
126+ <html xmlns="http://www.w3.org/1999/xhtml">
127+ <style>
128+ :root {
129+ --error-color: ${ errorColor } ;
130+ }
131+ .error { color: var(--error-color); text-align: center; padding: 20px; width: 360px; height: 168px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
132+ </style>
133+ <div class="error">服务器错误</div>
134+ </html>
135+ </foreignObject>
136+ </svg>` ;
109137 res . status ( 500 ) . send ( errorSvg ) ;
110138 }
111139} ) ;
0 commit comments