// ==UserScript==
// @name LZT Contests Info
// @namespace LZTContestsInfo
// @version 1.4
// @description Информация о розыгрышах в профиле LZT с кэшем на 10 минут
// @author llimonix
// @match https://zelenka.guru/*
// @match https://lzt.market/*
// @match https://lolz.guru/*
// @match https://lolz.live/*
// @match https://zelenka.guru
// @match https://lzt.market
// @match https://lolz.guru
// @match https://lolz.live
// @icon https://cdn-icons-png.flaticon.com/512/5899/5899678.png
// @license MIT
// ==/UserScript==
(function() {
$('<style>').text(`
.contestsInfoContainer {
border-top: 1px solid #3D3D3D;
display: flex;
margin-bottom: 10px;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
display: -webkit-box;
display: -webkit-flex;
-webkit-align-items: flex-start;
-webkit-flex-wrap: wrap;
-webkit-justify-content: space-between;
}
.contestsInfo {
margin: 15px 0 0;
padding: 0 10px;
background: #2f2f2f;
border-radius: 8px;
height: 42px;
line-height: 40px;
box-sizing: border-box;
display: inline-block;
font-weight: 600;
width: 49%;
transition: 0.3s;
box-sizing: border-box;
}
.contestsInfo .data {
margin: 0 0 0 10px;
white-space: nowrap;
overflow: hidden;
max-width: 78%;
display: inline-block;
text-overflow: ellipsis;
user-select: all;
}
.contestsInfo .contactIcon {
font-size: 20px;
line-height: 42px;
float: left;
}
.constestsInfoDetails.button {
background-color: #1c1c1c;
}
.LZTContestsInfo {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.LZTContestsInfo .LZTContestsItem {
height: 63px;
background: #2D2D2D;
border-radius: 8px;
padding: 12px;
box-sizing: border-box;
display: flex;
align-items: center;
white-space: normal;
flex: 1 1 100%;
max-width: 100%;
font-weight: 700;
}
@media screen and (min-width: 700px) {
.LZTContestsInfo .LZTContestsItem {
flex: 1 1 calc((100% / 2) - 24px);
}
}
.LZTContestsIcon {
margin-right: 12px;
}
.LZTContestsInfo .LZTContestsItem i {
width: 24px;
height: 24px;
font-size: 24px;
}
.LZTContestsInfo--status {
display: flex;
color: #949494;
background: #303030;
padding: 15px 20px;
border-radius: 12px;
flex-direction: column;
}
.LZTContestsInfo--status .statusTitle {
font-size: 16px;
font-weight: 700;
margin: 0 0 4px;
color: #FF6A70;
}
.LZTContestsInfoWrapper {
display: flex;
flex-wrap: wrap;
}
.xenOverlay .userStatCounters.hasContacts,
.Responsive .xenOverlay .userStatCounters.hasContacts {
margin-bottom: 0px;
}
.xenOverlay .userStatCounters {
margin-bottom: 20px!important;
}
@media (max-width: 610px) {
.Responsive .xenOverlay.memberCard .bottom .contestsInfo {
margin: 10px 0 0;
width: 100%;
}
}
`).appendTo('head');
function getUserContestData(userId) {
return new Promise(function(resolve, reject) {
const cacheKey = 'LZTUserData_' + userId;
const cachedRaw = sessionStorage.getItem(cacheKey);
const TEN_MIN = 10 * 60 * 1000;
if (cachedRaw) {
try {
const cached = JSON.parse(cachedRaw);
if (Date.now() - cached.timestamp < TEN_MIN) {
console.log('[LZT Contests Info] Использую кэш для ' + userId);
return resolve(cached.data);
} else {
console.log('[LZT Contests Info] Кэш устарел, обновляю...');
}
} catch(e) {
console.warn('[LZT Contests Info] Ошибка чтения кэша:', e);
}
}
$.ajax({
url: 'https://lzt-winners-contests.vercel.app/user/' + userId + '?include_ranks=true',
dataType: 'json',
success: function(data) {
sessionStorage.setItem(cacheKey, JSON.stringify({
timestamp: Date.now(),
data: data
}));
resolve(data);
},
error: function(xhr, status, err) {
reject(err || status);
}
});
});
}
XenForo.register(".profilePage .insuranceDeposit", function() {
var $el = $(this);
var userLink = $('.userContentLinks a').first();
if (!userLink.length) return;
var userIdMatch = userLink.attr('href').match(/\/(\d+)\/?$/);
if (!userIdMatch) return;
var userId = userIdMatch[1];
var contestsBlock = $('.section.contestsInfoWin, .section.contestsInfoCreate').length > 0;
if (contestsBlock) return;
$el.after(`
<div class="section contestsInfoWin">
<div class="secondaryContent">
<h3 style="margin-bottom:6px;">Победы в розыгрышах</h3>
<p class="amount mainc" id="totalWinnings" style="font-size:18px;font-weight:600;">Загрузка...</p>
</div>
</div>
<div class="section contestsInfoCreate">
<div class="secondaryContent">
<h3 style="margin-bottom:6px;">Проведённые розыгрыши</h3>
<p class="amount redc" id="totalGiveaway" style="font-size:18px;font-weight:600;">Загрузка...</p>
</div>
</div>
<div class="section button block constestsInfoDetails">Подробнее</div>
`);
$('.constestsInfoDetails.button').on('click', () => renderContestsInfo(userId));
getUserContestData(userId).then(function(data){
$('#totalWinnings').text(
(data.total_winnings || 0).toLocaleString('ru-RU') + ' ₽'
);
$('#totalGiveaway').text(
(data.total_giveaway || 0).toLocaleString('ru-RU') + ' ₽'
);
}).catch(function(err){
console.error('[LZT Contests Info] Ошибка загрузки данных:', err);
$('#totalWinnings').text('Ошибка');
$('#totalGiveaway').text('Ошибка');
});
});
$(document).on('XFOverlay', function(e){
var $overlay = e.overlay.getOverlay();
if (!$overlay.is('.memberCard')) return;
var $stat = $overlay.find('.userStatCounters');
if (!$stat.length || $overlay.find('.contestsInfoContainer').length) return;
var userLink = $overlay.find('.userContentLinks a').first();
if (!userLink.length) return;
var userIdMatch = userLink.attr('href').match(/\/(\d+)\/?$/);
if (!userIdMatch) return;
var userId = userIdMatch[1];
var $block = $(`
<div class="contestsInfoContainer">
<div class="contestsInfo">
<span class="contactIcon fas fa-plus-circle mainc"></span>
<span class="data" id="totalWinnings">Выиграно: загрузка...</span>
</div>
<div class="contestsInfo">
<span class="contactIcon fas fa-minus-circle redc"></span>
<span class="data" id="totalGiveaway">Разыграно: загрузка...</span>
</div>
</div>
`);
$stat.after($block);
var $menuElement = $overlay.find('.memberCardInner a[rel="Menu"]');
if ($menuElement.length) {
var $buttonOverlay = $('.Menu .blockLinksList').last();
var $buttonInfo = $('<li><a id="constestsInfoDetails" class="OverlayTrigger">Статистика розыгрышей</a></li>');
$buttonInfo.on('click', () => renderContestsInfo(userId));
$buttonOverlay.append($buttonInfo);
}
getUserContestData(userId).then(function(data){
$block.find('#totalWinnings').text(
'Выиграно: ' + (data.total_winnings || 0).toLocaleString('ru-RU') + ' ₽'
);
$block.find('#totalGiveaway').text(
'Разыграно: ' + (data.total_giveaway || 0).toLocaleString('ru-RU') + ' ₽'
);
}).catch(function(err){
console.error('[LZT Contests Info] Ошибка загрузки данных:', err);
$block.find('#totalWinnings, #totalGiveaway').text('Ошибка');
});
});
async function renderContestsInfo(userId) {
const cacheKey = 'LZTUserData_' + userId;
const cachedRaw = sessionStorage.getItem(cacheKey);
let cached = null;
try {
cached = cachedRaw ? JSON.parse(cachedRaw) : null;
} catch (e) {
console.warn('Ошибка парсинга кэша:', e);
}
const TOTAL_WINNINGS = (cached?.data?.total_winnings || 0).toLocaleString('ru-RU');
const TOTAL_GIVEAWAY = (cached?.data?.total_giveaway || 0).toLocaleString('ru-RU');
const WINS_COUNT = (cached?.data?.wins_count || 0).toLocaleString('ru-RU');
const CONTESTS_CREATED = (cached?.data?.contests_created || 0).toLocaleString('ru-RU');
const MAX_SINGLE_WIN = (cached?.data?.max_single_win || 0).toLocaleString('ru-RU');
const MAX_SINGLE_GIVEAWAY = (cached?.data?.max_single_giveaway || 0).toLocaleString('ru-RU');
const RANK_BY_WINNINGS = (cached?.data?.rank_by_winnings || 0).toLocaleString('ru-RU');
const RANK_BY_WINS_COUNT = (cached?.data?.rank_by_wins_count || 0).toLocaleString('ru-RU');
const RANK_BY_GIVEAWAY = (cached?.data?.rank_by_giveaway || 0).toLocaleString('ru-RU');
const RANK_BY_CONTESTS_COUNT = (cached?.data?.rank_by_contests_count || 0).toLocaleString('ru-RU');
const statsData = [
{ label: 'Выиграно', icon: '', value: TOTAL_WINNINGS, symbol: '₽' },
{ label: 'Разыграно', icon: '', value: TOTAL_GIVEAWAY, symbol: '₽' },
{ label: 'Количество побед', icon: '', value: WINS_COUNT },
{ label: 'Количество розыгрышей', icon: '', value: CONTESTS_CREATED },
{ label: 'Максимальная сумма победы', icon: '', value: MAX_SINGLE_WIN, symbol: '₽' },
{ label: 'Максимальная сумма розыгрыша', icon: '', value: MAX_SINGLE_GIVEAWAY, symbol: '₽' },
{ label: 'Топ по сумме побед', icon: '', value: RANK_BY_WINNINGS, prefix: '#' },
{ label: 'Топ по сумме розыгрышей', icon: '', value: RANK_BY_GIVEAWAY, prefix: '#' },
{ label: 'Топ по количеству побед', icon: '', value: RANK_BY_WINS_COUNT, prefix: '#' },
{ label: 'Топ по количеству розыгрышей', icon: '', value: RANK_BY_CONTESTS_COUNT, prefix: '#' },
];
const $items = statsData.map(({ label, icon, value, prefix = '', symbol = '' }) => `
<div class="LZTContestsItem">
<div class="LZTContestsIcon">
<i class="${icon}"></i>
</div>
<div><p>${label}</p>${prefix}${value} ${symbol}</div>
</div>
`).join('');
const $warningContestsInfo = `
<div class="LZTContestsInfo--statusContainer">
<div class="LZTContestsInfo--status">
<div class="statusTitle">Важная информация</div>
<div>Топ и суммы могут отличаться от официального значения форума, так как средствами парсера не возможно получать удалённые темы, а форум может.</div>
<div>Данная статистика учитывает только денежные розыгрыши с неудалённых тем.</div>
</div>
</div>
`;
const $contestsInfo = $(`
<div class="LZTContestsInfoWrapper">
<div class="LZTContestsInfo">
${$items}
</div>
${$warningContestsInfo}
</div>
`);
XenForo.alert($contestsInfo.prop('outerHTML'), 'LZT Contests Info');
}
})();