import requestCreator from './libs/requestCreator'; import InADayParser from './libs/InADayParser'; import getIDFromURL from './utils/getIDFromURL'; import getCurrentServer from './utils/getCurrentServer'; import formatDate from './utils/formatDate'; import renderPopup from './utils/renderPopup'; import { formatPlayerURL } from './utils/twstats'; import { formatTribeURL } from './utils/tribalwars'; import { setItem, getItem } from './utils/localStorage'; // ==UserScript== // @name Extended Player Profile // @namespace https://gist.github.com/Kichiyaki/3c273582cc6856512e22c86c375f795a // @version 0.1 // @description Extended Player Profile // @author Kichiyaki http://dawid-wysokinski.pl/ // @match *://*.plemiona.pl/game.php*&screen=info_player* // @match *://*.tribalwars.net/game.php*&screen=info_player* // @grant none // ==/UserScript== const SERVER = getCurrentServer(); let PLAYER_ID = getIDFromURL(window.location.search); const CURRENT_PLAYER_ID = parseInt(game_data.player.id); if (isNaN(PLAYER_ID) || !PLAYER_ID) { PLAYER_ID = CURRENT_PLAYER_ID; } const LOCAL_STORAGE_KEY = 'kichiyaki_extended_player_profile' + PLAYER_ID; const PLAYER_QUERY = ` query pageData($server: String!, $id: Int!, $filter: DailyPlayerStatsFilter) { player(server: $server, id: $id) { id name servers joinedAt nameChanges { oldName newName changeDate } dailyGrowth } dailyPlayerStats(server: $server, filter: $filter) { items { rank rankAtt rankDef rankSup rankTotal points scoreAtt scoreAtt scoreDef scoreSup scoreTotal villages } } } `; const TRIBE_CHANGES_QUERY = ` query tribeChanges($server: String!, $filter: TribeChangeFilter!) { tribeChanges(server: $server, filter: $filter) { total items { oldTribe { id tag } newTribe { id tag } createdAt } } } `; const TRIBE_CHANGES_PAGINATION_CONTAINER_ID = 'tribeChangesPagination'; const TRIBE_CHANGES_PER_PAGE = 15; const profileInfoTBody = document.querySelector('#player_info > tbody'); const actionsContainer = PLAYER_ID === CURRENT_PLAYER_ID ? profileInfoTBody : document.querySelector( '#content_value > table > tbody > tr > td:nth-child(1) > table:nth-child(2) > tbody' ); const otherElementsContainer = document.querySelector( PLAYER_ID === CURRENT_PLAYER_ID ? '#content_value > table:nth-child(7) > tbody > tr > td:nth-child(2)' : '#content_value > table > tbody > tr > td:nth-child(2)' ); const loadDataFromCache = () => { return getItem(LOCAL_STORAGE_KEY); }; const cachePlayerData = (data = {}) => { setItem(LOCAL_STORAGE_KEY, data); }; const loadInADayRankAndScore = async (name, playerID, type) => { try { const response = await fetch( TribalWars.buildURL('', { screen: 'ranking', mode: 'in_a_day', type, name, }) ); const html = await response.text(); const res = new InADayParser(html, { playerID }).parse(); if (res.length === 0) { throw new Error(); } return res[0]; } catch (error) { return { rank: 0, playerID: 0, score: 0, tribeID: 0, date: new Date(), }; } }; const loadData = async () => { const data = await requestCreator({ query: PLAYER_QUERY, variables: { server: SERVER, id: PLAYER_ID, filter: { sort: 'createDate DESC', limit: 1, playerID: [PLAYER_ID], }, }, }); if (data.player) { const inADay = {}; inADay.att = await loadInADayRankAndScore( data.player.name, data.player.id, 'kill_att' ); inADay.def = await loadInADayRankAndScore( data.player.name, data.player.id, 'kill_def' ); inADay.sup = await loadInADayRankAndScore( data.player.name, data.player.id, 'kill_sup' ); inADay.lootRes = await loadInADayRankAndScore( data.player.name, data.player.id, 'loot_res' ); inADay.lootVil = await loadInADayRankAndScore( data.player.name, data.player.id, 'loot_vil' ); inADay.scavenge = await loadInADayRankAndScore( data.player.name, data.player.id, 'scavenge' ); inADay.conquer = await loadInADayRankAndScore( data.player.name, data.player.id, 'conquer' ); data.player.inADay = inADay; } cachePlayerData(data); return data; }; const renderTr = ({ title, data, id }) => { let tr = document.querySelector('#' + id); if (!tr) { tr = document.createElement('tr'); tr.id = id; tr.appendChild(document.createElement('td')); tr.appendChild(document.createElement('td')); profileInfoTBody.append(tr); } tr.children[0].innerHTML = title; tr.children[1].innerHTML = data; }; const renderPlayerServers = (player) => { let playerServers = document.querySelector('#playerServers'); if (!playerServers) { playerServers = document.createElement('table'); playerServers.id = 'playerServers'; playerServers.classList.add('vis'); playerServers.width = '100%'; playerServers.innerHTML = ` Player's Servers `; otherElementsContainer.prepend(playerServers); } playerServers.querySelector('td').innerHTML = player.servers .sort() .map( (server) => `${server}` ) .join(''); }; const renderPlayerOtherNames = (player) => { let playerOtherNames = document.querySelector('#playerOtherNames'); if (!playerOtherNames) { playerOtherNames = document.createElement('div'); playerOtherNames.id = 'playerOtherNames'; playerOtherNames.width = '100%'; otherElementsContainer.prepend(playerOtherNames); } playerOtherNames.innerHTML = ` ${player.nameChanges .map((nameChange) => { return ` `; }) .join('')}
Old name New name Date
${nameChange.oldName} ${nameChange.newName} ${formatDate(nameChange.changeDate, { year: 'numeric', month: 'numeric', day: 'numeric', })}
`; }; const renderTodaysStats = (stats) => { let todaysStats = document.querySelector('#todaysStats'); if (!todaysStats) { todaysStats = document.createElement('div'); todaysStats.id = 'todaysStats'; todaysStats.width = '100%'; otherElementsContainer.prepend(todaysStats); } const statIncreaseStyle = 'color: #000; background-color: #0f0'; const statDecreaseStyle = 'color: #000; background-color: #f00'; todaysStats.innerHTML = `
Today's stats
Points ${Math.abs(stats.points).toLocaleString()}
Rank ${Math.abs(stats.rank)}
Villages ${Math.abs(stats.villages).toLocaleString()}
ODA ${Math.abs(stats.scoreAtt).toLocaleString()}
Rank (ODA) ${Math.abs(stats.rankAtt)}
ODD ${Math.abs(stats.scoreDef).toLocaleString()}
Rank (ODD) ${Math.abs(stats.rankDef)}
ODS ${Math.abs(stats.scoreSup).toLocaleString()}
Rank (ODS) ${Math.abs(stats.rankSup)}
OD ${Math.abs(stats.scoreTotal).toLocaleString()}
Rank (OD) ${Math.abs(stats.rankTotal)}
`; }; const renderInADayRanks = (player) => { let inADayRanks = document.querySelector('#inADayRanks'); if (!inADayRanks) { inADayRanks = document.createElement('div'); inADayRanks.id = 'inADayRanks'; inADayRanks.width = '100%'; otherElementsContainer.prepend(inADayRanks); } inADayRanks.innerHTML = `
'In a day' best scores
Units defeated while attacking: ${player.inADay.att.score.toLocaleString()} (${ player.inADay.att.rank }.)
Units defeated while defending: ${player.inADay.def.score.toLocaleString()} (${ player.inADay.def.rank }.)
Units defeated while supporting: ${player.inADay.sup.score.toLocaleString()} (${ player.inADay.sup.rank }.)
Resources plundered: ${player.inADay.lootRes.score.toLocaleString()} (${ player.inADay.lootRes.rank }.)
Villages plundered: ${player.inADay.lootVil.score.toLocaleString()} (${ player.inADay.lootVil.rank }.)
Resources gathered: ${player.inADay.scavenge.score.toLocaleString()} (${ player.inADay.scavenge.rank }.)
Villages conquered: ${player.inADay.conquer.score.toLocaleString()} (${ player.inADay.conquer.rank }.)
`; }; const render = ({ player, dailyPlayerStats }) => { [ { title: 'Joined at:', data: formatDate(player.joinedAt), id: 'joined_at', }, { title: 'Daily growth:', data: player.dailyGrowth.toLocaleString(), id: 'dg', }, ].forEach((data) => { renderTr(data); }); renderInADayRanks(player); if (dailyPlayerStats && dailyPlayerStats.items.length > 0) { renderTodaysStats(dailyPlayerStats.items[0]); } if (player.nameChanges.length > 0) { renderPlayerOtherNames(player); } if (player.servers.length > 0) { renderPlayerServers(player); } }; const addTribeChangesListeners = () => { document .querySelectorAll('#' + TRIBE_CHANGES_PAGINATION_CONTAINER_ID + ' a') .forEach((el) => { el.addEventListener('click', handleShowTribeChangesClick); }); }; const renderTribeChanges = (e, currentPage, tribeChanges) => { const numberOfPages = tribeChanges.total > 0 ? Math.ceil(tribeChanges.total / TRIBE_CHANGES_PER_PAGE) : 1; const paginationItems = []; for (let i = 1; i <= numberOfPages; i++) { if (i === currentPage) { paginationItems.push(`>${i}<`); } else { paginationItems.push( `${i}` ); } } const html = `
${paginationItems.join('')}
${tribeChanges.items .map((tribeChange) => { let rowHTML = '' + ``; if (tribeChange.newTribe) { rowHTML += ``; } else { rowHTML += ''; } if (tribeChange.oldTribe) { rowHTML += ``; } else { rowHTML += ''; } return rowHTML; }) .join('')}
Date New tribe Old tribe
${formatDate(tribeChange.createdAt)}${tribeChange.newTribe.tag}-${tribeChange.oldTribe.tag}-
`; renderPopup({ e, title: `Tribe changes`, id: 'tribeChanges', html, }); addTribeChangesListeners(); }; const handleShowTribeChangesClick = async (e) => { e.preventDefault(); const page = parseInt(e.target.getAttribute('data-page')); if (!isNaN(page)) { const data = await requestCreator({ query: TRIBE_CHANGES_QUERY, variables: { filter: { playerID: [PLAYER_ID], offset: TRIBE_CHANGES_PER_PAGE * (page - 1), limit: TRIBE_CHANGES_PER_PAGE, sort: 'createdAt DESC', }, server: SERVER, }, }); renderTribeChanges(e, page, data.tribeChanges); } }; const renderActions = () => { const showTribeChanges = document.createElement('a'); showTribeChanges.href = '#'; showTribeChanges.setAttribute('data-page', '1'); showTribeChanges.innerHTML = 'Show tribe changes'; showTribeChanges.addEventListener('click', handleShowTribeChangesClick); const showTribeChangesTd = document.createElement('td'); showTribeChangesTd.colSpan = '2'; showTribeChangesTd.append(showTribeChanges); actionsContainer.appendChild( document.createElement('tr').appendChild(showTribeChangesTd) ); }; (async function () { try { renderActions(); const dataFromCache = loadDataFromCache(); if (dataFromCache && dataFromCache.player) { render(dataFromCache); } const dataFromAPI = await loadData(); if (dataFromAPI) { render(dataFromAPI); } console.log(dataFromAPI); } catch (error) { console.log('extended player profile', error); } })();