import { c } from "#/di" import { WApi } from "#/lib/wynn/wapi" import { PG } from "#/services/pg" import { log } from "@temporalio/activity" import { type } from "arktype" import axios from "axios" const playerSchemaFail = type({ code: "string", message: "string", data: type({ player: { meta: { cached_at: "number" }, username: "string", id: "string", raw_id: "string", avatar: "string", skin_texture: "string", properties: [{ name: "string", value: "string", signature: "string" }], name_history: "unknown[]" } }), success: "false" }) const playerSchemaSuccess = type({ code: "string", message: "string", data: type({ player: { meta: { cached_at: "number" }, username: "string", id: "string", raw_id: "string", avatar: "string", skin_texture: "string", properties: [{ name: "string", value: "string", signature: "string" }], name_history: "unknown[]" } }), success: "true" }) const playerSchema = playerSchemaFail.or(playerSchemaSuccess) export const scrape_online_players = async()=>{ const api = await c.getAsync(WApi) const raw = await api.get('/v3/player') const onlineList = type({ total: "number", players: { "[string]": "string | null", } }).assert(raw.data) const { sql } = await c.getAsync(PG) for(const [playerName, server] of Object.entries(onlineList.players)){ // we do this optimistically without a tx, because temporal will probably handle // the race, and the worst case is we do extra requests. const ans = await sql`select uid from minecraft_user where name = ${playerName} limit 1` if(ans.length === 0){ // the user doesn't exist, so we need to grab their uuid try { const resp = await axios.get(`https://playerdb.co/api/player/minecraft/${playerName}`, { headers: { "User-Agent": "lil-robot-guy (a@tuxpa.in)", } }) const parsedPlayer = playerSchema.assert(resp.data) if(!parsedPlayer.success){ log.warn(`failed to get uuid for ${playerName}`, { "payload": parsedPlayer, }) continue } const uuid = parsedPlayer.data.player.id // insert the user. await sql`insert into minecraft_user (name, uid, server) values (${playerName}, ${uuid},${server}) on conflict (uid) do update set name = EXCLUDED.name, server = EXCLUDED.server ` log.info(`inserted ${playerName} with uuid ${uuid} on ${server}`) }catch(e) { log.warn(`failed to get uuid for ${playerName}`, { "err": e, }) continue } } } await sql.begin(async (sql)=>{ await sql`update minecraft_user set server = null` for(const [playerName, server] of Object.entries(onlineList.players)){ try { await sql`update minecraft_user set server = ${server} where name = ${playerName}` }catch(e) { log.warn(`failed to update server for ${playerName}`, { "err": e, }) continue } } }) }