import differenceInDays from 'date-fns/differenceInDays';
import getTranslations from './i18n/extendedTribeProfile';
import requestCreator from './libs/requestCreator';
import {
generatePaginationItems,
getContainerStyles,
getPage,
setPage,
} from './utils/pagination';
import renderTodaysStats from './common/renderTodaysStats';
import showEnnoblementsPopup from './common/showEnnoblementsPopup';
import showHistoryPopup from './common/showHistoryPopup';
import showPopup from './utils/showPopup';
import getIDFromURL from './utils/getIDFromURL';
import getCurrentServer from './utils/getCurrentServer';
import { getItem, setItem } from './utils/localStorage';
import { formatDate } from './utils/date';
import getServerVersionCode from './utils/getServerVersionCode';
import * as twstatsutils from './utils/twstats';
import * as twhelputils from './utils/twhelp';
import * as twutils from './utils/tribalwars';
// ==UserScript==
// @name Extended tribe profile
// @namespace https://github.com/tribalwarshelp/scripts
// @updateURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedTribeProfile.js
// @downloadURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedTribeProfile.js
// @version <%= version %>
// @description Extended tribe profile
// @author Kichiyaki https://dwysokinski.me/
// @match *://*/game.php*screen=info_ally*
// @grant none
// @run-at document-end
// ==/UserScript==
const SERVER = getCurrentServer();
const VERSION = getServerVersionCode(SERVER);
const TRIBE_ID = getIDFromURL(window.location.search);
const LOCAL_STORAGE_KEY = 'kichiyaki_extended_tribe_profile' + TRIBE_ID;
const TRIBE_QUERY = `
query tribe(
$server: String!
$id: Int!
$dailyTribeStatsSort: [String!]
$dailyTribeStatsLimit: Int
$playersLimit: Int
$playersSort: [String!]
$playerFilter: PlayerFilter!
$dailyTribeStatsFilter: DailyTribeStatsFilter!
) {
tribe(server: $server, id: $id) {
id
bestRank
bestRankAt
mostPoints
mostPointsAt
mostVillages
mostVillagesAt
createdAt
dominance
}
dailyTribeStats(
server: $server
limit: $dailyTribeStatsLimit
sort: $dailyTribeStatsSort
filter: $dailyTribeStatsFilter
) {
items {
rank
rankAtt
rankDef
rankTotal
points
scoreAtt
scoreAtt
scoreDef
scoreTotal
villages
members
}
}
players(server: $server, sort: $playersSort, filter: $playerFilter, limit: $playersLimit) {
items {
id
rankAtt
rankDef
rankSup
rankTotal
scoreAtt
scoreAtt
scoreDef
scoreSup
scoreTotal
dailyGrowth
}
}
}
`;
const ENNOBLEMENTS_QUERY = `
query ennoblements($server: String!, $limit: Int, $offset: Int, $sort: [String!], $filter: EnnoblementFilter!) {
ennoblements(server: $server, limit: $limit, offset: $offset, 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 TRIBE_HISTORY_AND_TRIBE_DAILY_STATS_QUERY = `
query tribeHistoryAndTribeDailyStats($server: String!,
$tribeHistoryFilter: TribeHistoryFilter!,
$dailyTribeStatsFilter: DailyTribeStatsFilter!,
$sort: [String!],
$offset: Int,
$limit: Int) {
tribeHistory(server: $server, sort: $sort, limit: $limit, offset: $offset, filter: $tribeHistoryFilter) {
total
items {
totalVillages
points
rank
scoreAtt
rankAtt
scoreDef
rankDef
scoreTotal
rankTotal
createDate
totalMembers
}
}
dailyTribeStats(server: $server, sort: $sort, limit: $limit, offset: $offset, filter: $dailyTribeStatsFilter) {
items {
points
scoreAtt
scoreDef
scoreTotal
villages
createDate
members
}
}
}
`;
const TRIBE_HISTORY_PER_PAGE = 15;
const TRIBE_MEMBERS_DAILY_STATS_QUERY = `
query tribeMembersDailyStats($server: String!,
$filter: DailyPlayerStatsFilter!,
$limit: Int,
$sort: [String!]) {
dailyPlayerStats(server: $server, limit: $limit, sort: $sort, filter: $filter) {
items {
player {
id
name
}
points
scoreAtt
scoreDef
scoreSup
scoreTotal
villages
createDate
}
}
}
`;
let MEMBERS_GROWTH_MODE = 'points';
const TRIBE_CHANGES_QUERY = `
query tribeChanges($server: String!, $limit: Int, $offset: Int, $sort: [String!], $filter: TribeChangeFilter!) {
tribeChanges(server: $server, offset: $offset, limit: $limit, sort: $sort, filter: $filter) {
total
items {
player {
id
name
}
newTribe {
id
tag
}
createdAt
}
}
}
`;
const TRIBE_CHANGES_PAGINATION_CONTAINER_ID = 'tribeChangesPagination';
const TRIBE_CHANGES_PER_PAGE = 15;
const contentValue = document.querySelector('#content_value');
const profileInfoTBody = document.querySelector(
'#content_value > table:nth-child(3) > tbody > tr > td:nth-child(1) > table > tbody'
);
const actionContainer = profileInfoTBody;
const otherElementsContainer = document.querySelector(
'#content_value > table:nth-child(3) > tbody > tr > td:nth-child(2)'
);
const membersContainer = contentValue
.querySelector('h3')
.nextElementSibling.querySelector('tbody');
const translations = getTranslations();
const loadDataFromCache = () => {
return getItem(LOCAL_STORAGE_KEY);
};
const cacheTribeData = (data = {}) => {
setItem(LOCAL_STORAGE_KEY, data);
};
const getMemberIDs = () => {
const ids = [];
membersContainer.querySelectorAll('a').forEach(a => {
const href = a.getAttribute('href');
if (href.includes('info_player')) {
ids.push(getIDFromURL(href));
}
});
return ids;
};
const getMemberNames = () => {
const ids = [];
membersContainer.querySelectorAll('a').forEach(a => {
if (a.getAttribute('href').includes('info_player')) {
ids.push(a.innerText.trim());
}
});
return ids;
};
const loadData = async () => {
const memberIDs = getMemberIDs();
const data = await requestCreator({
query: TRIBE_QUERY,
variables: {
server: SERVER,
id: TRIBE_ID,
dailyTribeStatsSort: ['createDate DESC'],
dailyTribeStatsLimit: 1,
dailyTribeStatsFilter: {
tribeID: [TRIBE_ID],
},
playersSort: ['rank ASC'],
playersLimit: memberIDs.length,
playerFilter: {
id: memberIDs,
},
},
});
cacheTribeData(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 extendMembersData = players => {
membersContainer.parentElement.style.width = '100%';
contentValue.append(membersContainer.parentElement);
const heading = membersContainer.querySelector('tr:first-child');
if (heading.children.length !== 11) {
[
translations.oda,
translations.odd,
translations.ods,
translations.od,
translations.dailyGrowth,
translations.playerLinks,
].forEach(v => {
const th = document.createElement('th');
th.innerHTML = v;
heading.appendChild(th);
});
}
membersContainer.querySelectorAll('tr').forEach(tr => {
const a = tr.querySelector('a');
if (!a) {
return;
}
const playerID = getIDFromURL(a.getAttribute('href'));
const player = players.items.find(p => p.id === playerID);
if (player) {
[
[player.scoreAtt, player.rankAtt],
[player.scoreDef, player.rankDef],
[player.scoreSup, player.rankSup],
[player.scoreTotal, player.rankTotal],
player.dailyGrowth,
[
{
link: twhelputils.buildPlayerURL(VERSION, SERVER, player.id),
label: 'TWHelp',
},
{
link: twstatsutils.buildPlayerURL(SERVER, player.id),
label: 'TWStats',
},
],
].forEach((data, index) => {
let td = tr.children[5 + index];
if (!td) {
td = document.createElement('td');
tr.appendChild(td);
}
if (Array.isArray(data)) {
if (typeof data[0] === 'number') {
td.innerHTML = `${data[0].toLocaleString()} (${
data[1]
})`;
} else if (data[0].link) {
td.innerHTML = data
.map(
({ link, label }) =>
`${label}`
)
.join('
');
}
} else if (typeof data === 'number') {
td.innerHTML = data.toLocaleString();
}
});
}
});
};
const render = ({ tribe, dailyTribeStats, players }) => {
[
{
title: translations.createdAt + ':',
data: formatDate(tribe.createdAt),
id: 'created_at',
},
{
title: translations.dominance + ':',
data: tribe.dominance.toFixed(2) + '%',
id: 'dominance',
},
{
title: translations.bestRank + ':',
data: tribe.bestRank + ' ' + `(${formatDate(tribe.bestRankAt)})`,
id: 'best_rank',
},
{
title: translations.mostPoints + ':',
data:
tribe.mostPoints.toLocaleString() +
' ' +
`(${formatDate(tribe.mostPointsAt)})`,
id: 'most_points',
},
{
title: translations.mostVillages + ':',
data: tribe.mostVillages + ' ' + `(${formatDate(tribe.mostVillagesAt)})`,
id: 'most_villages',
},
].forEach(data => {
renderTr(data);
});
if (dailyTribeStats && dailyTribeStats.items.length > 0) {
renderTodaysStats(otherElementsContainer, dailyTribeStats.items[0]);
}
if (players && players.items.length > 0) {
extendMembersData(players);
}
};
const handleShowTribeEnnoblementsClick = async e => {
e.preventDefault();
const page = getPage(e.target);
if (!isNaN(page)) {
const data = await requestCreator({
query: ENNOBLEMENTS_QUERY,
variables: {
filter: {
or: {
oldOwnerTribeID: [TRIBE_ID],
newOwnerTribeID: [TRIBE_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 handleShowTribeHistoryClick = async e => {
e.preventDefault();
const page = getPage(e.target);
if (!isNaN(page)) {
try {
const filter = {
tribeID: [TRIBE_ID],
};
const { tribeHistory, dailyTribeStats } = await requestCreator({
query: TRIBE_HISTORY_AND_TRIBE_DAILY_STATS_QUERY,
variables: {
server: SERVER,
offset: TRIBE_HISTORY_PER_PAGE * (page - 1),
limit: TRIBE_HISTORY_PER_PAGE,
sort: ['createDate DESC'],
tribeHistoryFilter: filter,
dailyTribeStatsFilter: filter,
},
});
showHistoryPopup(e, tribeHistory, dailyTribeStats, {
currentPage: page,
limit: TRIBE_HISTORY_PER_PAGE,
tribe: true,
onPageChange: handleShowTribeHistoryClick,
});
} catch (error) {
console.log('couldnt load tribe history', error);
}
}
};
const getMembersGrowthTdStyle = value => {
const statIncreaseStyle = 'color: #000; background-color: #0f0';
const statDecreaseStyle = 'color: #000; background-color: #f00';
const defaultStyle = 'color: #000; background-color: #808080';
return value > 0
? statIncreaseStyle
: value < 0
? statDecreaseStyle
: defaultStyle;
};
const mapMembersGrowthTdValue = i => {
switch (MEMBERS_GROWTH_MODE) {
case 'points':
return i.points;
case 'villages':
return i.villages;
case 'od':
return i.scoreTotal;
case 'oda':
return i.scoreAtt;
case 'odd':
return i.scoreDef;
case 'ods':
return i.scoreSup;
default:
return 0;
}
};
const buildMembersGrowthTBody = stats => {
const dates = [
...new Set(stats.items.map(item => item.createDate)),
].reverse();
return `
${translations.date} | ${translations.player} | ${translations.act} | |
---|---|---|---|
${formatDate(tribeChange.createdAt)} | `; if (tribeChange.player) { rowHTML += `${tribeChange.player.name} | `; } else { rowHTML += '- | '; } rowHTML += `${ tribeChange.newTribe && tribeChange.newTribe.id === TRIBE_ID ? translations.joined : translations.left } | `; return rowHTML + '