const sinceInput = document.querySelector('#since') as HTMLInputElement; const newbies = document.querySelector('textarea#newbies') as HTMLTextAreaElement; { const twoDaysAgo = new Date(); twoDaysAgo.setUTCDate(twoDaysAgo.getUTCDate() - 2); twoDaysAgo.setUTCHours(0, 0, 0, 0); sinceInput.value = twoDaysAgo.toISOString().slice(0, 10); } sinceInput.addEventListener('change', () => void render()); render(); async function render(): Promise { const loader = document.querySelector('#loader') as HTMLElement; loader.style.display = 'block'; try { await _render(); } catch (e) { console.error(e); } loader.style.display = 'none'; } async function _render(): Promise { if (sinceInput.valueAsDate === null) { console.warn(sinceInput.value, 'is not a valid date'); newbies.value = ''; return; } newbies.style.display = 'none'; const planets = ['Katoa']; const joined: Map = new Map(); for (const planet of planets) { const messages: Message[] = await fetchJSON('https://api.fnar.net/chat/messages?' + new URLSearchParams({ 'channel_names': planet + ' Global Site Owners', 'updated_since': sinceInput.valueAsDate?.toISOString(), }).toString()); for (const message of messages) if (message.Type == 'JOINED') { const old_join = joined.get(message.SenderUserName) if (old_join === undefined || old_join.timestamp > message.MessageTimestamp) joined.set(message.SenderUserName, {timestamp: message.MessageTimestamp, planet}); } } const sortedJoined = Array.from(joined.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp); const lines: string[] = []; for (const [username, join] of sortedJoined) { const dtStr = new Date(join.timestamp).toISOString().replace('T', ' ').replace('Z', ''); lines.push(`${username}\t${dtStr}\t${join.planet}`); } newbies.value = lines.join('\n'); newbies.style.display = 'block'; } document.querySelector('#copy')!.addEventListener('click', () => { navigator.clipboard.writeText(newbies.value); }); async function fetchJSON(url: string, options: RequestInit = {}): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const doc = await fetch(url, {...options, signal: controller.signal}).then((r) => r.json()); clearTimeout(timeoutId); return doc; } interface Message { Type: string; MessageTimestamp: number; SenderUserName: string; } interface Join { timestamp: number; planet: string; }