121 lines
3.2 KiB
TypeScript
121 lines
3.2 KiB
TypeScript
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
|
|
}
|
|
}
|
|
})
|
|
}
|