|
|
@@ -1,9 +1,13 @@
|
|
|
from __future__ import annotations
|
|
|
|
|
|
+import collections
|
|
|
import datetime
|
|
|
+import pathlib
|
|
|
+import traceback
|
|
|
import typing
|
|
|
import urllib.parse
|
|
|
|
|
|
+from js import console # pyright: ignore[reportMissingImports]
|
|
|
from workers import Response, WorkerEntrypoint, fetch
|
|
|
|
|
|
if typing.TYPE_CHECKING:
|
|
|
@@ -14,28 +18,108 @@ class Default(WorkerEntrypoint):
|
|
|
url = urllib.parse.urlparse(request.url)
|
|
|
match url.path:
|
|
|
case '/gy-694c':
|
|
|
- return await gy_694c(self.env.punoted_api_key)
|
|
|
+ return await self.gy_694c()
|
|
|
+ case '/kit':
|
|
|
+ return await self.kit()
|
|
|
case _:
|
|
|
return Response('404', status=404)
|
|
|
|
|
|
-async def gy_694c(punoted_api_key: str) -> Response:
|
|
|
- headers = {'X-Data-Token': punoted_api_key}
|
|
|
- response = await fetch('https://api.punoted.net/v1/storages/user', headers=headers)
|
|
|
- storages = await response.json() # pyright: ignore[reportGeneralTypeIssues]
|
|
|
- (storage,) = (s for s in storages if s['Location'] == 'GY-694c')
|
|
|
- tio = 0
|
|
|
- for item in storage['StorageItems']:
|
|
|
- if item['MaterialTicker'] == 'TIO':
|
|
|
- tio = item['MaterialAmount']
|
|
|
- last_update = datetime.datetime.fromtimestamp(storage['LastUpdatedEpochMs'] / 1000)
|
|
|
- delta = datetime.datetime.now() - last_update
|
|
|
-
|
|
|
- response = await fetch('https://api.punoted.net/v1/production/user/burn?location=gy-694c', headers=headers)
|
|
|
- production = await response.json() # pyright: ignore[reportGeneralTypeIssues]
|
|
|
- (tio_production,) = (p for p in production['GY-694c'] if p['MaterialTicker'] == 'TIO')
|
|
|
- daily_production = tio_production['Production']
|
|
|
-
|
|
|
- estimated = int(tio + daily_production * (delta.total_seconds() / 60 / 60 / 24))
|
|
|
- return Response(f'raylu had {tio} TIO in storage as of {last_update} UTC ({delta.total_seconds() / 60 / 60:.1f} hours ago)\n'
|
|
|
- f'raylu is producing {daily_production}/day\n'
|
|
|
- f'estimated TIO in inventory: {estimated}\n')
|
|
|
+ async def gy_694c(self) -> Response:
|
|
|
+ headers = {'X-Data-Token': self.env.punoted_api_key}
|
|
|
+ response = await fetch('https://api.punoted.net/v1/storages/user', headers=headers)
|
|
|
+ storages = await response.json() # pyright: ignore[reportGeneralTypeIssues]
|
|
|
+ (storage,) = (s for s in storages if s['Location'] == 'GY-694c')
|
|
|
+ tio = 0
|
|
|
+ for item in storage['StorageItems']:
|
|
|
+ if item['MaterialTicker'] == 'TIO':
|
|
|
+ tio = item['MaterialAmount']
|
|
|
+ last_update = datetime.datetime.fromtimestamp(storage['LastUpdatedEpochMs'] / 1000)
|
|
|
+ delta = datetime.datetime.now() - last_update
|
|
|
+
|
|
|
+ response = await fetch('https://api.punoted.net/v1/production/user/burn?location=gy-694c', headers=headers)
|
|
|
+ production = await response.json() # pyright: ignore[reportGeneralTypeIssues]
|
|
|
+ (tio_production,) = (p for p in production['GY-694c'] if p['MaterialTicker'] == 'TIO')
|
|
|
+ daily_production = tio_production['Production']
|
|
|
+
|
|
|
+ estimated = int(tio + daily_production * (delta.total_seconds() / 60 / 60 / 24))
|
|
|
+ return Response(f'raylu had {tio} TIO in storage as of {last_update} UTC ({delta.total_seconds() / 60 / 60:.1f} hours ago)\n'
|
|
|
+ f'raylu is producing {daily_production}/day\n'
|
|
|
+ f'estimated TIO in inventory: {estimated}\n')
|
|
|
+
|
|
|
+ async def kit(self) -> Response:
|
|
|
+ html = (pathlib.Path(__file__).parent / 'kit.html').read_text()
|
|
|
+ try:
|
|
|
+ headers = {'X-Data-Token': self.env.punoted_api_key}
|
|
|
+ response = await fetch('https://api.punoted.net/v1/storages/user', headers=headers)
|
|
|
+ storages = await response.json() # pyright: ignore[reportGeneralTypeIssues]
|
|
|
+
|
|
|
+ lcb = {
|
|
|
+ 'FFC': 1,
|
|
|
+ 'RCT': 1,
|
|
|
+ 'FSE': 1,
|
|
|
+ 'LFE': 2,
|
|
|
+ 'MFE': 2,
|
|
|
+ 'SFE': 1,
|
|
|
+ 'LCB': 1,
|
|
|
+ 'MFL': 1,
|
|
|
+ 'MSL': 1,
|
|
|
+ 'LHP': 94,
|
|
|
+ 'SSC': 128,
|
|
|
+ 'BR1': 1,
|
|
|
+ 'CQM': 1,
|
|
|
+ }
|
|
|
+ wcb_upgrade = {
|
|
|
+ 'LFE': 1,
|
|
|
+ 'WCB': 1,
|
|
|
+ 'MFL': 1,
|
|
|
+ 'LHP': 64,
|
|
|
+ 'SSC': 26,
|
|
|
+ }
|
|
|
+ ship_parts = list(lcb.keys())
|
|
|
+ ship_parts.extend(mat for mat in wcb_upgrade.keys() if mat not in ship_parts)
|
|
|
+ items = collections.defaultdict(int)
|
|
|
+ items_beanironia = collections.defaultdict(int)
|
|
|
+ last_update = None
|
|
|
+ for storage in storages:
|
|
|
+ for item in storage['StorageItems']:
|
|
|
+ if item['MaterialTicker'] in ship_parts:
|
|
|
+ items[item['MaterialTicker']] += item['MaterialAmount']
|
|
|
+ if storage['Location'] == 'Beanironia':
|
|
|
+ items_beanironia[item['MaterialTicker']] += item['MaterialAmount']
|
|
|
+ if storage['Location'] == 'Beanironia':
|
|
|
+ last_update = datetime.datetime.fromtimestamp(storage['LastUpdatedEpochMs'] / 1000)
|
|
|
+ assert last_update is not None
|
|
|
+ delta = datetime.datetime.now() - last_update
|
|
|
+
|
|
|
+ availability = '''
|
|
|
+ <table>
|
|
|
+ <tr>
|
|
|
+ <th></th>
|
|
|
+ <th>LCB</th>
|
|
|
+ <th>LCB<br>(at beanironia)</th>
|
|
|
+ <th>WCB upgrade</th>
|
|
|
+ <th>WCB upgrade<br>(at beanironia)</th>
|
|
|
+ </tr>
|
|
|
+ '''
|
|
|
+ for part in ship_parts:
|
|
|
+ availability += f'<tr><td>{part}</td>'
|
|
|
+ if part in lcb:
|
|
|
+ percent = min(items[part] / lcb[part], 1) * 100
|
|
|
+ availability += f'<td><span style="color: color-mix(in xyz, #0aa {percent}%, #f80)">{items[part]}</span>/{lcb[part]}</td>'
|
|
|
+ percent = min(items_beanironia[part] / lcb[part], 1) * 100
|
|
|
+ availability += f'<td><span style="color: color-mix(in xyz, #0aa {percent}%, #f80)">{items_beanironia[part]}</span>/{lcb[part]}</td>'
|
|
|
+ else:
|
|
|
+ availability += '<td></td><td></td>'
|
|
|
+ if part in wcb_upgrade:
|
|
|
+ percent = min(items[part] / wcb_upgrade[part], 1) * 100
|
|
|
+ availability += f'<td><span style="color: color-mix(in xyz, #0aa {percent}%, #f80)">{items[part]}</span>/{wcb_upgrade[part]}</td>'
|
|
|
+ percent = min(items_beanironia[part] / wcb_upgrade[part], 1) * 100
|
|
|
+ availability += f'<td><span style="color: color-mix(in xyz, #0aa {percent}%, #f80)">{items_beanironia[part]}</span>/{wcb_upgrade[part]}</td>'
|
|
|
+ else:
|
|
|
+ availability += '<td></td><td></td>'
|
|
|
+ availability += '</tr>'
|
|
|
+ availability += f'</table>as of {last_update.strftime("%Y-%m-%d %H:%M:%S")} UTC ({delta.total_seconds() / 60 / 60:.1f} hours ago)'
|
|
|
+ html = html.replace('{{availability}}', availability, 1)
|
|
|
+ except Exception:
|
|
|
+ console.error('Error fetching kit data:', traceback.format_exc())
|
|
|
+ return Response(html, headers={'Content-Type': 'text/html'})
|