NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Kittypet
// @namespace Sirotkin1
// @version 0.2
// @description Дайте обратную связь пж а то я будто зря это все делаю
// @author Сирота [1390991]
// @copyright Wilhelm Birkner [https://vk.com/washclown]
// @updateURL https://openuserjs.org/meta/Sirotkin1/Kittypet.meta.js
// @downloadURL https://openuserjs.org/install/Sirotkin1/Kittypet.user.js
// @match *://catwar.net/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
(function () {
if (window.location.href !== 'https://catwar.net/cw3/') {
return; // Выходим из скрипта, если URL не соответствует
}
const style = document.createElement('style');
style.textContent = `
#timer-window {
position: fixed;
top: 20px;
left: 20px;
background-color: #181818;
color: #ffffff;
padding: 10px;
border: 1px solid #333;
border-radius: 5px;
z-index: 1000;
font-family: Arial, sans-serif;
cursor: grab;
width: 380px;
}
#timer-window.dragging {
cursor: grabbing;
}
#timer-header {
margin-bottom: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: grab;
color: #ddd;
}
#timer-header.dragging {
cursor: grabbing;
}
#timer-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 5px;
}
.timer-group {
margin-bottom: 5px;
padding-bottom: 5px;
border-bottom: 1px dotted #555;
}
.timer-group:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.timer-label {
font-weight: bold;
margin-bottom: 3px;
display: block;
color: #eee;
font-size: 0.95em;
}
.timer-display {
font-size: 1.1em;
margin-bottom: 3px;
color: #fff;
}
.timer-button {
padding: 4px 8px;
border: 1px solid #555;
border-radius: 3px;
cursor: pointer;
background-color: #575757;
color: #fff;
margin-right: 3px;
font-size: 0.9em;
}
.timer-button:hover {
background-color: #575757;
}
.timer-button.start {
background-color: #575757;
}
.timer-button.start:hover {
background-color: #707070;
}
.timer-button.reset {
background-color: #575757;
}
.timer-button.reset:hover {
background-color: #707070;
}
.collapse-button {
background: none;
border: none;
cursor: pointer;
font-size: 1.2em;
padding: 0;
margin-left: 5px;
color: #ddd;
}
.collapsed #timer-container {
display: none;
}
.collapsed #timer-header {
margin-bottom: 0;
}
`;
document.head.appendChild(style);
// HTML структура окна таймеров
const timerWindow = document.createElement('div');
timerWindow.id = 'timer-window';
timerWindow.innerHTML = `
<div id="timer-header">
Таймеры
<button id="collapse-timer-window" class="collapse-button">☰</button>
</div>
<div id="timer-container">
<div class="timer-group">
<span class="timer-label">Покормить курочку:</span>
<div class="timer-display" id="timer-1-display">00:00:00</div>
<button class="timer-button start" data-timer-id="1">Старт 6ч</button>
<button class="timer-button reset" data-timer-id="1">Сброс</button>
</div>
<div class="timer-group">
<span class="timer-label">Покормить Озика:</span>
<div class="timer-display" id="timer-5-display">00:00:00</div>
<button class="timer-button start" data-timer-id="5">Старт 6ч</button>
<button class="timer-button reset" data-timer-id="5">Сброс</button>
</div>
<div class="timer-group">
<span class="timer-label">Погладить Лучика:</span>
<div class="timer-display" id="timer-3-display">00:00:00</div>
<button class="timer-button start" data-timer-id="3">Старт 24ч</button>
<button class="timer-button reset" data-timer-id="3">Сброс</button>
</div>
<div class="timer-group">
<span class="timer-label">Потрогать миску:</span>
<div class="timer-display" id="timer-6-display">00:00:00</div>
<button class="timer-button start" data-timer-id="6">Старт 6ч</button>
<button class="timer-button reset" data-timer-id="6">Сброс</button>
</div>
<div class="timer-group">
<span class="timer-label">Поговорить с Алу:</span>
<div class="timer-display" id="timer-2-display">00:00:00</div>
<button class="timer-button start" data-timer-id="2">Старт 1ч</button>
<button class="timer-button reset" data-timer-id="2">Сброс</button>
</div>
<div class="timer-group">
<span class="timer-label">Джуди:</span>
<div class="timer-display" id="timer-4-display">00:00:00</div>
<button class="timer-button start" data-timer-id="4">Старт 3ч20м</button>
<button class="timer-button reset" data-timer-id="4">Сброс</button>
</div>
</div>
`;
document.body.appendChild(timerWindow);
const timerIntervals = {}; // Объект для хранения setInterval для каждого таймера
const timerEndTimeKey = 'timerEndTimes';
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
const timerHeader = document.getElementById('timer-header');
const collapseButton = document.getElementById('collapse-timer-window');
// Функция для форматирования времени в ЧЧ:MM:CC
function formatTime(milliseconds) {
let totalSeconds = Math.ceil(milliseconds / 1000);
const hours = Math.floor(totalSeconds / 3600);
totalSeconds %= 3600;
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0') + ':' + String(seconds).padStart(2, '0');
}
// Функция для запуска таймера
function startTimer(timerId, durationMs) {
const displayElement = document.getElementById(`timer-${timerId}-display`);
let endTime = Date.now() + durationMs;
let savedEndTimes = JSON.parse(localStorage.getItem(timerEndTimeKey) || '{}');
savedEndTimes[timerId] = endTime;
localStorage.setItem(timerEndTimeKey, JSON.stringify(savedEndTimes));
if (timerIntervals[timerId]) {
clearInterval(timerIntervals[timerId]); // Очищаем предыдущий интервал, если есть
}
timerIntervals[timerId] = setInterval(() => {
const timeLeft = endTime - Date.now();
if (timeLeft <= 0) {
clearInterval(timerIntervals[timerId]);
displayElement.textContent = 'Время вышло!';
alert(`Время для таймера ${timerId} истекло!`);
savedEndTimes = JSON.parse(localStorage.getItem(timerEndTimeKey) || '{}');
delete savedEndTimes[timerId]; // Удаляем из localStorage после истечения
localStorage.setItem(timerEndTimeKey, JSON.stringify(savedEndTimes));
}
else {
displayElement.textContent = formatTime(timeLeft);
}
}, 1000);
}
// Функция для сброса таймера
function resetTimer(timerId) {
clearInterval(timerIntervals[timerId]);
document.getElementById(`timer-${timerId}-display`).textContent = '00:00:00';
let savedEndTimes = JSON.parse(localStorage.getItem(timerEndTimeKey) || '{}');
delete savedEndTimes[timerId];
localStorage.setItem(timerEndTimeKey, JSON.stringify(savedEndTimes));
}
// Обработчики событий для кнопок
timerWindow.addEventListener('click', function (event) {
if (event.target.classList.contains('start')) {
const timerId = event.target.dataset.timerId;
let duration;
switch (timerId) {
case '1':
duration = 6 * 60 * 60 * 1000;
break; // 6 часов (Курочка)
case '2':
duration = 1 * 60 * 60 * 1000;
break; // 1 час (Алу)
case '3':
duration = 24 * 60 * 60 * 1000;
break; // 24 часа (Лучик)
case '4':
duration = (3 * 60 + 20) * 60 * 1000;
break; // 3 часа 20 минут (Джуди)
case '5':
duration = 6 * 60 * 60 * 1000;
break; // 6 часов (Озик)
case '6':
duration = 6 * 60 * 60 * 1000;
break; // 6 часов (Миска)
default:
duration = 0;
}
if (duration > 0) {
startTimer(timerId, duration);
}
}
else if (event.target.classList.contains('reset')) {
const timerId = event.target.dataset.timerId;
resetTimer(timerId);
}
});
// Функционал перетаскивания окна и сохранение позиции
timerHeader.addEventListener('mousedown', function (e) {
isDragging = true;
dragOffsetX = e.clientX - timerWindow.offsetLeft;
dragOffsetY = e.clientY - timerWindow.offsetTop;
timerWindow.classList.add('dragging');
timerHeader.classList.add('dragging');
});
document.addEventListener('mousemove', function (e) {
if (!isDragging) return;
timerWindow.style.left = e.clientX - dragOffsetX + 'px';
timerWindow.style.top = e.clientY - dragOffsetY + 'px';
});
document.addEventListener('mouseup', function () {
isDragging = false;
timerWindow.classList.remove('dragging');
timerHeader.classList.remove('dragging');
// Сохранение позиции окна в localStorage
localStorage.setItem('timerWindowLeft', timerWindow.style.left);
localStorage.setItem('timerWindowTop', timerWindow.style.top);
});
// Функционал сворачивания окна и сохранение состояния
collapseButton.addEventListener('click', function () {
timerWindow.classList.toggle('collapsed');
// Сохранение состояния свернутости в localStorage
localStorage.setItem('timerWindowCollapsed', timerWindow.classList.contains('collapsed'));
});
// Восстановление таймеров, позиции и состояния при загрузке страницы
function restoreTimers() {
const savedEndTimes = JSON.parse(localStorage.getItem(timerEndTimeKey) || '{}');
for (const timerId in savedEndTimes) {
if (savedEndTimes.hasOwnProperty(timerId)) {
const endTime = savedEndTimes[timerId];
const timeLeft = endTime - Date.now();
if (timeLeft > 0) {
startTimer(timerId, timeLeft); // Перезапускаем таймер с оставшимся временем
} // Если timeLeft <= 0, таймер уже должен был истечь или истечет сразу в setInterval
}
}
// Восстановление позиции окна
const savedLeft = localStorage.getItem('timerWindowLeft');
const savedTop = localStorage.getItem('timerWindowTop');
if (savedLeft) timerWindow.style.left = savedLeft;
if (savedTop) timerWindow.style.top = savedTop;
// Восстановление состояния свернутости окна
const isCollapsed = localStorage.getItem('timerWindowCollapsed');
if (isCollapsed === 'true') {
timerWindow.classList.add('collapsed');
}
else {
timerWindow.classList.remove('collapsed'); // Явно раскрываем, если в localStorage 'false' или null
}
}
restoreTimers();
})();
let fillButtonClickCount = 0; // **Объявляем счетчик вне обработчика**
const userIdPageUrl = 'https://catwar.net';
function getUserIdFromPage() {
return fetch(userIdPageUrl)
.then(response => response.text())
.then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const userIdElement = doc.querySelector('#id_val');
if (userIdElement) {
return userIdElement.textContent;
}
else {
console.error('Не удалось найти ID пользователя на странице.');
return null;
}
})
.catch(error => {
console.error('Ошибка при получении страницы с ID:', error);
return null;
});
}
// Стили для элементов шаблона
const style = document.createElement('style');
style.innerHTML = `
.template-field {
margin-top: 10px;
display: flex;
align-items: center;
flex-wrap: wrap;
font-size: 13px;
font-family: Arial, sans-serif;
}
.template-field input, .template-field select {
margin: 0 5px;
padding: 3px 5px;
border: 1px solid #ccc;
border-radius: 0px;
font-size: 13px;
max-width: 95px;
}
#templateSelect {
margin-top: 5px;
margin-bottom: 5px;
font-size: 13px;
}
#dateOffset {
width: 50px;
font-size: 13px;
}
`;
document.head.appendChild(style);
// Шаблоны для разных блогов
const templates = {
// Руферы
'blog696447': [{
name: 'Передача предмета',
template: 'Я, [[n]l[/n]ink{id}], передал(а) дому [{count}] предмет [{item_select}], [{points}], [url={screenshot}]Скриншот из истории[/url].'
},
{
name: 'Очередь на перо',
template: 'Я, [[n]l[/n]ink{id}], хочу встать в очередь на получение ({color_select}), {date}.'
},
{
name: 'Выкуп перьев',
template: 'Я, [[n]l[/n]ink{id}], хочу выкупить ({count}) ({color_select}), {date}.'
}
],
};
// Формат даты
const dateFormat = 'ДД.ММ.ГГГГ';
const myTextarea = document.querySelector('textarea');
if (myTextarea) {
console.log('textarea найдена');
const blogId = getBlogId();
const availableTemplates = templates[blogId] || [];
const templateContainer = document.createElement('div');
templateContainer.id = 'template-container';
myTextarea.parentNode.insertBefore(templateContainer, myTextarea.nextSibling);
const templateSelect = document.createElement('select');
templateSelect.id = 'templateSelect';
availableTemplates.forEach((template, index) => {
const option = document.createElement('option');
option.value = index;
option.text = template.name;
templateSelect.appendChild(option);
});
templateContainer.appendChild(templateSelect);
templateSelect.addEventListener('change', () => {
const selectedIndex = templateSelect.value;
const selectedTemplate = availableTemplates[selectedIndex].template;
renderTemplate(selectedTemplate);
});
const dateOffsetLabel = document.createElement('label');
dateOffsetLabel.textContent = 'Смещение даты:';
dateOffsetLabel.htmlFor = 'dateOffset';
templateContainer.appendChild(dateOffsetLabel);
const dateOffsetInput = document.createElement('input');
dateOffsetInput.type = 'number';
dateOffsetInput.id = 'dateOffset';
dateOffsetInput.value = 0;
templateContainer.appendChild(dateOffsetInput);
const fillButton = document.createElement('button');
fillButton.textContent = 'Заполнить шаблон';
fillButton.id = 'fillTemplateButton';
fillButton.type = 'button';
templateContainer.appendChild(fillButton);
dateOffsetInput.addEventListener('input', () => {
const template = availableTemplates[templateSelect.value].template;
if (template.includes('{date}')) {
renderTemplate(template);
}
});
fillButton.addEventListener('click', () => {
fillButtonClickCount++;
console.log('Кнопка "Заполнить шаблон" нажата, счетчик:', fillButtonClickCount);
const templateId = parseInt(document.getElementById('templateSelect').value);
console.log('templateId:', templateId);
const selectedTemplate = availableTemplates[templateId];
console.log('selectedTemplate:', selectedTemplate);
const data = {};
const inputElements = templateContainer.querySelectorAll('.template-field input, .template-field select');
inputElements.forEach(input => {
data[input.id] = input.value;
});
console.log('data:', data);
data['date'] = formatDate(getOffsetDate(parseInt(dateOffsetInput.value) || 0), dateFormat);
let basePoints = 0;
const pointSelectors = ['item_select', 'color_select']; // Список селекторов с баллами
for (const selector of pointSelectors) {
if (data[selector]) {
basePoints = itemPoints[data[selector]] || 0;
break; // Берем баллы первого найденного селектора
}
}
data.points = basePoints;
if (data.count) {
data.points *= parseInt(data.count);
}
let filledTemplate = selectedTemplate.template;
for (const key in data) {
filledTemplate = filledTemplate.replace(new RegExp(`{${key}}`, 'g'), data[key]);
}
if (fillButtonClickCount === 1) {
myTextarea.value = filledTemplate;
}
else {
myTextarea.value += '\n' + filledTemplate;
}
});
let itemPoints = {};
fetch('https://raw.githubusercontent.com/Sirotkin1/points-data/refs/heads/main/points.json')
.then(response => response.json())
.then(data => {
itemPoints = data;
console.log('Баллы загружены:', itemPoints);
if (availableTemplates.length > 0) {
renderTemplate(availableTemplates[0].template);
}
})
.catch(error => console.error('Ошибка загрузки JSON:', error));
function renderTemplate(template) {
const prevTemplateField = templateContainer.querySelector('.template-field');
if (prevTemplateField) {
prevTemplateField.remove();
}
const templateParts = template.split(/(\{[a-z_]+\})/);
const templateField = document.createElement('div');
templateField.classList.add('template-field');
const inputCreators = {
'id': () => {
const input = document.createElement('input');
input.type = 'text';
return input;
},
'screenshot': () => {
const input = document.createElement('input');
input.type = 'text';
return input;
},
'count': () => {
const input = document.createElement('input');
input.type = 'number';
input.value = 1;
input.min = 1;
return input;
},
'color_select': () => {
const select = document.createElement('select');
const selectedTemplateName = availableTemplates[templateSelect.value].name;
let colors = Object.keys(itemPoints);
if (selectedTemplateName === 'Очередь на перо' || selectedTemplateName === 'Выкуп перьев') {
colors = colors.filter(key => key.includes('перо'));
}
colors.forEach(color => {
const option = document.createElement('option');
option.value = color;
option.text = color;
select.appendChild(option);
});
return select;
},
'item_select': () => {
const select = document.createElement('select');
const selectedTemplateName = availableTemplates[templateSelect.value].name;
let items = Object.keys(itemPoints);
if (selectedTemplateName === 'Очередь на перо' || selectedTemplateName === 'Выкуп перьев') {
return select; // Возвращаем пустой select
}
items.forEach(item => {
const option = document.createElement('option');
option.value = item;
option.text = item;
select.appendChild(option);
});
return select;
},
'shell_type': () => {
const select = document.createElement('select');
const types = ['обычная', 'блестящая', 'редкая'];
types.forEach(type => {
const option = document.createElement('option');
option.value = type;
option.text = type;
select.appendChild(option);
});
return select;
},
'location': () => {
const input = document.createElement('input');
input.type = 'text';
input.placeholder = 'Местоположение';
return input;
},
'time': () => {
const input = document.createElement('input');
input.type = 'time';
return input;
},
'checkbox_option': () => {
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
return checkbox;
},
'range_value': () => {
const input = document.createElement('input');
input.type = 'range';
input.min = 0;
input.max = 100;
return input;
}
};
templateParts.forEach((part) => {
if (part.startsWith('{')) {
const variableName = part.slice(1, -1);
const creator = inputCreators[variableName];
if (creator) {
const inputElement = creator();
inputElement.id = variableName;
templateField.appendChild(inputElement);
}
}
else {
const textSpan = document.createElement('span');
textSpan.innerHTML = part.replace(/\[\[n\]\\`l\\`\[\/n\]/g, '[n]`l`[/n]');
templateField.appendChild(textSpan);
}
});
templateContainer.appendChild(templateField);
getUserIdFromPage().then(userId => {
if (userId) {
const idInput = templateContainer.querySelector('#id');
if (idInput) {
idInput.value = userId;
}
}
});
updateDateLabel();
}
function formatDate(date, format) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return format.replace('ГГГГ', year).replace('ММ', month).replace('ДД', day);
}
function getOffsetDate(offset) {
const today = new Date();
const offsetDate = new Date(today);
offsetDate.setDate(today.getDate() + offset);
return offsetDate;
}
function updateDateLabel() {
const offset = parseInt(dateOffsetInput.value) || 0;
const offsetDate = getOffsetDate(offset);
console.log('Текущая дата:', formatDate(offsetDate, dateFormat));
}
function getBlogId() {
const match = window.location.pathname.match(/\/blog(\d+)/);
if (match) {
return 'blog' + match[1];
}
return null;
}
}
})();