import requestCreator from './libs/requestCreator'; import getTranslations from './i18n/extendedVillageProfile'; import { setPage, getPage } from './utils/pagination'; import getCurrentServer from './utils/getCurrentServer'; import getIDFromURL from './utils/getIDFromURL'; import buildUnitImgURL from './utils/buildUnitImgURL'; import { formatDate } from './utils/date'; import wait from './utils/wait'; import { setItem, getItem } from './utils/localStorage'; import calcLoyalty from './utils/calcLoyalty'; import getServerVersionCode from './utils/getServerVersionCode'; import * as twhelputils from './utils/twhelp'; import showEnnoblementsPopup from './common/showEnnoblementsPopup'; // ==UserScript== // @name Extended village profile // @namespace https://github.com/tribalwarshelp/scripts // @updateURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedVillageProfile.js // @downloadURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedVillageProfile.js // @version 0.7.6 // @description Extended village profile // @author Kichiyaki https://dwysokinski.me/ // @match *://*/game.php*screen=info_village* // @grant none // @run-at document-end // ==/UserScript== const SERVER = getCurrentServer(); const VILLAGE_ID = getIDFromURL(window.location.search); const LAST_CONQUER_QUERY = ` query ennoblements($server: String!, $limit: Int, $sort: [String!], $filter: EnnoblementFilter!) { ennoblements(server: $server, limit: $limit, sort: $sort, filter: $filter) { items { ennobledAt village { id } } } } `; const ENNOBLEMENTS_QUERY = ` query ennoblements($server: String!, $offset: Int, $limit: Int, $sort: [String!], $filter: EnnoblementFilter!) { ennoblements(server: $server, offset: $offset, limit: $limit, sort: $sort, filter: $filter) { total items { village { id name x y } oldOwner { id name } oldOwnerTribe { id tag } newOwner { id name } newOwnerTribe { id tag } ennobledAt } } } `; const ENNOBLEMENTS_PER_PAGE = 15; const CURR_SERVER_CONFIG = ` query server($key: String!) { server(key: $key) { config { speed } unitConfig { spear { pop } sword { pop } axe { pop } archer { pop } spy { pop } light { pop } marcher { pop } heavy { pop } ram { pop } catapult { pop } knight { pop } snob { pop } } } } `; const SERVER_CONFIG_LOCAL_STORAGE_KEY = 'kiszkowaty_extended_village_profile_server_cfg'; const actionContainer = document.querySelector( '#content_value > table > tbody > tr > td:nth-child(1) > table:nth-child(2) > tbody' ); const additionalInfoContainer = document.querySelector( '#content_value table.vis tbody' ); let serverConfig = {}; const translations = getTranslations(); const loadConfigFromLocalStorage = () => { return getItem(SERVER_CONFIG_LOCAL_STORAGE_KEY); }; const cacheServerConfig = (data = {}) => { setItem(SERVER_CONFIG_LOCAL_STORAGE_KEY, data); }; const isConfigExpired = date => { return Math.abs(date.getTime() - new Date().getTime()) > 1000 * 60 * 60 * 24; }; const loadConfig = async () => { let data = loadConfigFromLocalStorage(); if ( !data.server || isConfigExpired(new Date(data.loadedAt)) || !data.server.unitConfig || !data.server.config ) { data = await requestCreator({ query: CURR_SERVER_CONFIG, variables: { key: SERVER, }, }); data.loadedAt = new Date(); cacheServerConfig(data); } return data.server; }; const loadPageData = async () => { let data = await requestCreator({ query: LAST_CONQUER_QUERY, variables: { server: SERVER, filter: { villageID: [VILLAGE_ID], }, sort: ['ennobledAt DESC'], limit: 1, }, }); return data; }; const handleShowTribeEnnoblementsClick = async e => { e.preventDefault(); const page = getPage(e.target); if (!isNaN(page)) { const data = await requestCreator({ query: ENNOBLEMENTS_QUERY, variables: { filter: { villageID: [VILLAGE_ID], }, offset: ENNOBLEMENTS_PER_PAGE * (page - 1), limit: ENNOBLEMENTS_PER_PAGE, sort: ['ennobledAt DESC'], server: SERVER, }, }); showEnnoblementsPopup(e, data.ennoblements, { currentPage: page, limit: ENNOBLEMENTS_PER_PAGE, onPageChange: handleShowTribeEnnoblementsClick, }); } }; const buildCellsForIncSupport = units => { const cells = []; let pop = 0; for (let unit in units) { pop += units[unit] * serverConfig.unitConfig[unit].pop; cells.push(`${units[unit].toLocaleString()}`); } cells.push(`${pop.toLocaleString()}`); return cells; }; const handleCountIncomingSupportClick = async e => { e.preventDefault(); const ids = []; const allyCommand = {}; document .querySelectorAll('span.command_hover_details[data-command-type="support"]') .forEach(el => { const id = parseInt(el.getAttribute('data-command-id')); if (el.classList.contains('commandicon-ally')) { allyCommand[id] = true; } else { allyCommand[id] = false; } ids.push(id); }); const mySupport = { spear: 0, sword: 0, axe: 0, archer: 0, spy: 0, light: 0, marcher: 0, heavy: 0, ram: 0, catapult: 0, knight: 0, snob: 0, }; const allySupport = { ...mySupport, }; const total = { ...mySupport, }; for (let i = 0; i < ids.length; i++) { Dialog.show( 'incomingSupport', `${translations.loaded}: ${i} / ${ids.length}` ); const id = ids[i]; const url = TribalWars.buildURL('', { screen: 'info_command', ajax: 'details', id, }); try { const resp = await fetch(url); const { units } = await resp.json(); if (units) { for (let unit in mySupport) { const count = parseInt(units[unit].count); if (allyCommand[id]) { allySupport[unit] += count; } else { mySupport[unit] += count; } total[unit] += count; } } await wait(200); } catch (error) { console.log('count incoming support', error); } } const ths = ['']; for (let unit in mySupport) { ths.push(``); } ths.push(`${translations.pop}`); const mySupportCells = [ `${translations.mySupport}`, ...buildCellsForIncSupport(mySupport), ]; const allySupportCells = [ `${translations.allySupport}`, ...buildCellsForIncSupport(allySupport), ]; const totalCells = [ `${translations.total}`, ...buildCellsForIncSupport(total), ]; Dialog.show( 'incomingSupport', ` ${ths.join('')} ${mySupportCells.join('')} ${allySupportCells.join('')} ${totalCells.join('')}
` ); const popup = document.querySelector('.popup_box'); if (popup) { popup.style.width = 'auto'; popup.style.maxWidth = '900px'; } }; const wrapAction = action => { const actionWrapperTd = document.createElement('td'); actionWrapperTd.colSpan = '2'; actionWrapperTd.append(action); const actionWrapperTr = document.createElement('tr'); actionWrapperTr.appendChild(actionWrapperTd); return actionWrapperTr; }; const renderActions = () => { const linkToTWHelp = document.createElement('a'); linkToTWHelp.href = twhelputils.buildVillageURL( getServerVersionCode(SERVER), SERVER, VILLAGE_ID ); linkToTWHelp.innerHTML = translations.action.linkToTWHelp; actionContainer.appendChild(wrapAction(linkToTWHelp)); const showEnnoblementsPopup = document.createElement('a'); showEnnoblementsPopup.href = '#'; setPage(showEnnoblementsPopup, '1'); showEnnoblementsPopup.innerHTML = translations.action.showEnnoblements; showEnnoblementsPopup.addEventListener( 'click', handleShowTribeEnnoblementsClick ); actionContainer.appendChild(wrapAction(showEnnoblementsPopup)); const countIncomingSupport = document.createElement('a'); countIncomingSupport.href = '#'; countIncomingSupport.innerHTML = translations.action.countIncomingSupport; countIncomingSupport.addEventListener( 'click', handleCountIncomingSupportClick ); actionContainer.appendChild(wrapAction(countIncomingSupport)); }; 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')); additionalInfoContainer.append(tr); } tr.children[0].innerHTML = title; tr.children[1].innerHTML = data; }; const countUnitsInVillage = () => { const trs = document.querySelectorAll('#content_value > div tbody tr'); const units = []; if (trs.length === 0) throw new Error(); trs[0].querySelectorAll('.unit_link').forEach(() => { units.push(0); }); for (let i = 1; i < trs.length; i++) { const tr = trs[i]; tr.querySelectorAll('.unit-item').forEach((td, index) => { units[index] += parseInt(td.innerHTML); }); } return units; }; const renderAdditionalInfo = ({ config, ennoblements } = {}) => { const firstEnnoblement = ennoblements && Array.isArray(ennoblements.items) && ennoblements.items[0] ? ennoblements.items[0] : undefined; renderTr({ id: 'loyalty', title: `${translations.possibleLoyalty}:`, data: firstEnnoblement ? calcLoyalty(new Date(firstEnnoblement.ennobledAt), config.speed) : 100, }); renderTr({ id: 'ennobledAt', title: `${translations.ennobledAt}:`, data: firstEnnoblement ? formatDate(firstEnnoblement.ennobledAt) : translations.never, }); try { const units = countUnitsInVillage(); const tr = document.createElement('tr'); tr.style.textAlign = 'center'; tr.style.fontWeight = 'bold'; tr.appendChild(document.createElement('td')); units.forEach(count => { const td = document.createElement('td'); td.innerHTML = count; tr.appendChild(td); }); document.querySelector('#content_value > div tbody').appendChild(tr); } catch (error) {} }; (async function () { try { const pageData = await loadPageData(); serverConfig = await loadConfig(); renderAdditionalInfo({ config: serverConfig.config, ennoblements: pageData.ennoblements, }); renderActions(); } catch (error) { console.log('extended village profile', error); } })();