// ==UserScript==
// @name MZ - GB Message Sender
// @namespace douglaskampl
// @version 1.2
// @description Sends a guestbook message to multiple users
// @author Douglas
// @match https://www.managerzone.com/?p=guestbook
// @icon https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant GM_addStyle
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(`
.custom-button {
margin-left: 10px;
background-color: #74ACDF;
color: white;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.custom-button:hover {
background-color: #6B9FD0;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.custom-button:after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: 0.5s;
}
.custom-button:hover:after {
left: 100%;
}
.modal {
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.7);
opacity: 0;
transition: opacity 0.3s ease;
}
.modal.show {
opacity: 1;
}
.modal-content {
background-color: #2E2E2E;
color: #F5F5F5;
margin: 15% auto;
padding: 25px;
border: 2px solid #F0F0F0;
width: 80%;
max-width: 600px;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transform: translateY(-50px);
opacity: 0;
transition: all 0.4s ease;
}
.modal.show .modal-content {
transform: translateY(0);
opacity: 1;
}
.modal-header {
border-bottom: 3px solid #74ACDF;
padding-bottom: 15px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-title {
color: #F5F5F5;
margin: 0;
font-size: 22px;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
}
.close {
color: #F0F0F0;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s ease;
}
.close:hover,
.close:focus {
color: #74ACDF;
text-decoration: none;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #74ACDF;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
background-color: #3D3D3D;
color: #F5F5F5;
border: 1px solid #555;
border-radius: 6px;
box-sizing: border-box;
transition: all 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #74ACDF;
box-shadow: 0 0 8px rgba(116, 172, 223, 0.5);
}
.form-group textarea {
height: 120px;
resize: vertical;
}
.send-button {
background: linear-gradient(to right, #74ACDF, #85C2F5);
color: white;
padding: 12px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
letter-spacing: 0.5px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.send-button:hover {
background: linear-gradient(to right, #85C2F5, #74ACDF);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.send-button:after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.send-button:hover:after {
left: 100%;
}
.send-button:disabled {
background: #555;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.status-area {
margin-top: 20px;
padding: 15px;
border: 1px solid #444;
border-radius: 6px;
max-height: 180px;
overflow-y: auto;
display: none;
background-color: #222;
transition: all 0.3s ease;
}
.status-area.show {
display: block;
animation: fadeIn 0.5s ease forwards;
}
.status-message {
margin: 8px 0;
padding: 5px 10px;
border-radius: 4px;
transition: opacity 0.3s ease;
animation: slideIn 0.3s ease forwards;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.status-success {
color: #7CFC00;
background-color: rgba(124, 252, 0, 0.1);
border-left: 3px solid #7CFC00;
}
.status-error {
color: #FF6B6B;
background-color: rgba(255, 107, 107, 0.1);
border-left: 3px solid #FF6B6B;
}
.status-info {
color: #74ACDF;
background-color: rgba(116, 172, 223, 0.1);
border-left: 3px solid #74ACDF;
}
.footer {
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #444;
text-align: center;
color: #AAA;
font-size: 12px;
}
.footer img {
height: 20px;
vertical-align: middle;
margin-left: 5px;
}
.progress-bar {
height: 4px;
width: 0%;
background: linear-gradient(to right, #74ACDF, #FCBF49);
position: absolute;
top: 0;
left: 0;
transition: width 0.5s ease;
}
.toggle-container {
display: flex;
justify-content: center;
margin-bottom: 20px;
background-color: #222;
border-radius: 6px;
padding: 4px;
width: 100%;
}
.toggle-option {
flex: 1;
padding: 10px;
text-align: center;
cursor: pointer;
border-radius: 4px;
transition: all 0.3s ease;
color: #AAA;
}
.toggle-option.active {
background-color: #74ACDF;
color: white;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.toggle-option:not(.active):hover {
background-color: #333;
color: #DDD;
}
.input-container {
display: none;
}
.input-container.active {
display: block;
animation: fadeIn 0.3s ease;
}
`);
window.addEventListener('load', function() {
const postButton = document.getElementById('formpostbutton');
if (!postButton) return;
const sendMessagesButton = document.createElement('div');
sendMessagesButton.style.display = 'inline-block';
sendMessagesButton.innerHTML = `
<a href="#" class="mzbtn button_account buttondiv custom-button" id="sendMessagesBtn">
<span class="buttonClassMiddle"><span style="white-space: nowrap">Send Messages</span></span><span class="buttonClassRight"> </span>
</a>
`;
postButton.parentNode.insertBefore(sendMessagesButton, postButton.nextSibling);
const modal = document.createElement('div');
modal.className = 'modal';
modal.id = 'messageModal';
modal.innerHTML = `
<div class="modal-content">
<div class="progress-bar" id="progressBar"></div>
<div class="modal-header">
<h2 class="modal-title">メッセーsender</h2>
<span class="close">×</span>
</div>
<div class="form-group">
<label for="messageText">Message:</label>
<textarea id="messageText" placeholder="Enter your message here"></textarea>
</div>
<div class="toggle-container">
<div class="toggle-option active" data-target="users-input">Users</div>
<div class="toggle-option" data-target="federation-input">Federation</div>
</div>
<div class="input-container active" id="users-input">
<div class="form-group">
<label for="usersInput">Usernames (comma separated):</label>
<input type="text" id="usersInput" placeholder="user1, user2, user3">
</div>
</div>
<div class="input-container" id="federation-input">
<div class="form-group">
<label for="federationId">Federation ID:</label>
<input type="text" id="federationId" placeholder="Enter federation ID (e.g. 63)">
</div>
</div>
<button id="sendButton" class="send-button">Send Messages</button>
<div id="statusArea" class="status-area">
<div id="statusMessages"></div>
</div>
<div class="footer">
requested by gordola
<svg width="18" height="12" viewBox="0 0 18 12" style="vertical-align: middle; margin-left: 5px;">
<rect width="18" height="4" fill="#74ACDF" />
<rect y="4" width="18" height="4" fill="#FFFFFF" />
<rect y="8" width="18" height="4" fill="#74ACDF" />
<circle cx="9" cy="6" r="1.5" fill="#FCBF49" />
</svg>
</div>
</div>
`;
document.body.appendChild(modal);
const toggleOptions = document.querySelectorAll('.toggle-option');
toggleOptions.forEach(option => {
option.addEventListener('click', function() {
toggleOptions.forEach(opt => opt.classList.remove('active'));
this.classList.add('active');
const targetId = this.getAttribute('data-target');
document.querySelectorAll('.input-container').forEach(container => {
container.classList.remove('active');
});
document.getElementById(targetId).classList.add('active');
});
});
document.getElementById('sendMessagesBtn').addEventListener('click', function(e) {
e.preventDefault();
const modalEl = document.getElementById('messageModal');
modalEl.style.display = 'block';
setTimeout(() => {
modalEl.classList.add('show');
}, 10);
});
document.querySelector('.close').addEventListener('click', function() {
const modalEl = document.getElementById('messageModal');
modalEl.classList.remove('show');
setTimeout(() => {
modalEl.style.display = 'none';
}, 300);
});
window.addEventListener('click', function(event) {
const modalEl = document.getElementById('messageModal');
if (event.target === modalEl) {
modalEl.classList.remove('show');
setTimeout(() => {
modalEl.style.display = 'none';
}, 300);
}
});
document.getElementById('sendButton').addEventListener('click', async function() {
const message = document.getElementById('messageText').value.trim();
if (!message) {
alert('Please enter a message');
return;
}
const statusArea = document.getElementById('statusArea');
const statusMessages = document.getElementById('statusMessages');
const progressBar = document.getElementById('progressBar');
statusArea.classList.add('show');
statusMessages.innerHTML = '';
updateProgressBar(progressBar, 0);
const activeMode = document.querySelector('.toggle-option.active').getAttribute('data-target');
if (activeMode === 'users-input') {
const usersText = document.getElementById('usersInput').value.trim();
if (!usersText) {
alert('Please enter at least one username');
return;
}
const users = usersText.split(',').map(u => u.trim()).filter(u => u);
if (users.length === 0) {
alert('Please enter valid usernames');
return;
}
const totalUsers = users.length;
let processedUsers = 0;
addStatus('Starting to send messages...', 'status-info');
for (const username of users) {
try {
const userId = await getUserId(username);
if (!userId) {
addStatus(`Could not find user ID for ${username}`, 'status-error');
continue;
}
addStatus(`Found user ID: ${userId}`, 'status-info');
await sendMessage(userId, message);
addStatus(`Message sent to ${username}`, 'status-success');
processedUsers++;
updateProgressBar(progressBar, (processedUsers / totalUsers) * 100);
if (processedUsers < totalUsers) {
addStatus(`Waiting 5 seconds before sending the next message...`, 'status-info');
await new Promise(resolve => setTimeout(resolve, 5000));
}
} catch (error) {
addStatus(`Error: ${error.message}`, 'status-error');
}
}
updateProgressBar(progressBar, 100);
addStatus('All messages have been sent!', 'status-success');
} else {
const federationId = document.getElementById('federationId').value.trim();
if (!federationId) {
alert('Please enter a federation ID');
return;
}
if (!/^\d+$/.test(federationId)) {
alert('Federation ID must be a number');
return;
}
addStatus(`Starting to fetch federation members for federation ID: ${federationId}...`, 'status-info');
try {
const users = await getFederationMembers(federationId);
if (users.length === 0) {
addStatus('No users found in federation', 'status-error');
return;
}
addStatus(`Found ${users.length} federation members`, 'status-info');
const totalUsers = users.length;
let processedUsers = 0;
addStatus('Starting to send messages...', 'status-info');
for (const username of users) {
try {
const userId = await getUserId(username);
if (!userId) {
addStatus(`Could not find user ID for ${username}`, 'status-error');
continue;
}
addStatus(`Found user ID: ${userId}`, 'status-info');
await sendMessage(userId, message);
addStatus(`Message sent to ${username}`, 'status-success');
processedUsers++;
updateProgressBar(progressBar, (processedUsers / totalUsers) * 100);
if (processedUsers < totalUsers) {
addStatus(`Waiting 5 seconds before sending the next message...`, 'status-info');
await new Promise(resolve => setTimeout(resolve, 5000));
}
} catch (error) {
addStatus(`Error: ${error.message}`, 'status-error');
}
}
updateProgressBar(progressBar, 100);
addStatus('All messages have been sent!', 'status-success');
} catch (error) {
addStatus(`Error fetching federation members: ${error.message}`, 'status-error');
}
}
});
async function getFederationMembers(federationId) {
const users = [];
let offset = 0;
let hasMoreMembers = true;
while (hasMoreMembers) {
addStatus(`Fetching federation members (offset: ${offset})...`, 'status-info');
try {
const url = `https://www.managerzone.com/ajax.php?p=federations&sub=federation_members&fid=${federationId}&offset=${offset}&sport=soccer`;
const response = await fetch(url);
const data = await response.json();
if (!data || !data[0] || !data[0].length) {
hasMoreMembers = false;
break;
}
const newUsers = extractUsernames(data[0]);
if (newUsers.length === 0) {
hasMoreMembers = false;
break;
}
users.push(...newUsers);
addStatus(`Found ${newUsers.length} members at offset ${offset}`, 'status-info');
hasMoreMembers = data[1] && data[1].includes(`offset=${offset + 10}`);
offset += 10;
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
addStatus(`Error fetching members at offset ${offset}: ${error.message}`, 'status-error');
hasMoreMembers = false;
}
}
return [...new Set(users)];
}
function extractUsernames(html) {
const regex = /\"(?:Epic|Legendary|Elite|Gold|Senior|\"|\s)*\s*([a-zA-Z0-9_-]+)<\/a>/g;
const matches = [];
let match;
while ((match = regex.exec(html)) !== null) {
if (match[1] && match[1].trim()) {
matches.push(match[1].trim());
}
}
const simpleRegex = /([a-zA-Z0-9_-]+)<\/a><\/span><\/td>/g;
while ((match = simpleRegex.exec(html)) !== null) {
if (match[1] && match[1].trim() && !matches.includes(match[1].trim())) {
matches.push(match[1].trim());
}
}
return matches;
}
function addStatus(message, className) {
const statusMessages = document.getElementById('statusMessages');
const messageElement = document.createElement('div');
messageElement.className = `status-message ${className}`;
messageElement.textContent = message;
statusMessages.appendChild(messageElement);
statusMessages.scrollTop = statusMessages.scrollHeight;
}
function updateProgressBar(progressBar, percentage) {
progressBar.style.width = `${percentage}%`;
}
async function getUserId(username) {
const response = await fetch(`https://www.managerzone.com/xml/manager_data.php?sport_id=1&username=${encodeURIComponent(username)}`);
const text = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
const userDataElement = xmlDoc.querySelector('UserData');
if (userDataElement && userDataElement.hasAttribute('userId')) {
return userDataElement.getAttribute('userId');
}
return null;
}
async function sendMessage(userId, message) {
await fetch(`https://www.managerzone.com/ajax.php?p=messageBoard&sub=write&template=1&ident_id=${userId}&sport=soccer`, {
method: 'POST',
body: new URLSearchParams({
'msg': message
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest'
}
});
return true;
}
});
})();