Files
scripts/cams.js
2025-12-10 14:32:49 -05:00

828 lines
31 KiB
JavaScript

// ==UserScript==
// @name cams
// @namespace http://tampermonkey.net/
// @version 1.2.1
// @description Set maxDockedCamsForUser, keep-alive, and multi-poke
// @author You
// @match https://chat.fabswingers.com/*
// @updateURL https://git.upto.im/geekery/scripts/raw/branch/main/meta.js
// @downloadURL https://git.upto.im/geekery/scripts/raw/branch/main/cams.js
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const BUTTON_ID = 'maxDockedCamsButton_SINGLETON';
const KEEPALIVE_BUTTON_ID = 'keepAliveButton_SINGLETON';
const MULTIPOKE_BUTTON_ID = 'multiPokeButton_SINGLETON';
const LOCK_KEY = 'maxDockedCams_scriptLock';
const KEEPALIVE_SETTINGS_KEY = 'keepAlive_settings';
const MULTIPOKE_SETTINGS_KEY = 'multiPoke_settings';
const BUTTON_POSITIONS_KEY = 'buttonPositions';
// Load saved button positions
function loadButtonPositions() {
try {
const saved = localStorage.getItem(BUTTON_POSITIONS_KEY);
if (saved) {
return JSON.parse(saved);
}
} catch (e) {
console.log('Error loading button positions', e);
}
return {};
}
function saveButtonPosition(buttonId, x, y) {
const positions = loadButtonPositions();
positions[buttonId] = { x, y };
localStorage.setItem(BUTTON_POSITIONS_KEY, JSON.stringify(positions));
}
function makeDraggable(button) {
let isDragging = false;
let wasDragged = false;
let startX, startY, initialX, initialY;
button.style.cursor = 'move';
button.addEventListener('mousedown', function(e) {
if (e.button !== 0) return; // Only left click
isDragging = true;
wasDragged = false;
startX = e.clientX;
startY = e.clientY;
initialX = button.offsetLeft;
initialY = button.offsetTop;
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
// Only count as drag if moved more than 5px
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
wasDragged = true;
}
let newX = initialX + deltaX;
let newY = initialY + deltaY;
// Keep within viewport
newX = Math.max(0, Math.min(newX, window.innerWidth - button.offsetWidth));
newY = Math.max(0, Math.min(newY, window.innerHeight - button.offsetHeight));
button.style.left = newX + 'px';
button.style.top = newY + 'px';
button.style.right = 'auto';
});
document.addEventListener('mouseup', function(e) {
if (isDragging) {
isDragging = false;
if (wasDragged) {
saveButtonPosition(button.id, button.offsetLeft, button.offsetTop);
}
}
});
// Prevent click event if we were dragging
button.addEventListener('click', function(e) {
if (wasDragged) {
e.preventDefault();
e.stopPropagation();
wasDragged = false;
}
}, true);
// Apply saved position if exists
const positions = loadButtonPositions();
if (positions[button.id]) {
button.style.left = positions[button.id].x + 'px';
button.style.top = positions[button.id].y + 'px';
button.style.right = 'auto';
}
}
// Public room names to match against
const PUBLIC_ROOMS = [
'General Chat',
'Directing Room',
'Directing Room #2',
'Directing Room #3',
'Directing Room #4',
'Directing Room #5',
'Directing Room #6',
'Site Supporters',
'Verified Users',
'Scotland Swingers',
'Northern Swingers',
'South East Swing',
'South West Swing',
'Midlands Swing',
'East Anglia',
'Ireland Swingers',
'London Swingers',
'Wales Swingers',
'USA Swingers',
'Canadian Swingers',
'Bisexual Chat',
'Bi Directing Room'
];
const DEFAULT_INTERVAL = 10; // minutes
const DEFAULT_POKE_COUNT = 10;
const POKE_DELAY = 150; // ms between pokes
let keepAliveInterval = null;
let isKeepAliveRunning = false;
let keepAliveSettings = loadKeepAliveSettings();
let multiPokeEnabled = loadMultiPokeSettings();
function loadMultiPokeSettings() {
return localStorage.getItem(MULTIPOKE_SETTINGS_KEY) !== 'false';
}
function saveMultiPokeSettings() {
localStorage.setItem(MULTIPOKE_SETTINGS_KEY, multiPokeEnabled.toString());
}
function loadKeepAliveSettings() {
try {
const saved = localStorage.getItem(KEEPALIVE_SETTINGS_KEY);
if (saved) {
return JSON.parse(saved);
}
} catch (e) {
console.log('KeepAlive: Error loading settings', e);
}
return { intervalMinutes: DEFAULT_INTERVAL };
}
function saveKeepAliveSettings() {
localStorage.setItem(KEEPALIVE_SETTINGS_KEY, JSON.stringify(keepAliveSettings));
}
// Try to acquire lock
const lockValue = Date.now().toString();
if (localStorage.getItem(LOCK_KEY)) {
console.log('MaxDockedCams: Another instance already running, exiting');
return;
}
localStorage.setItem(LOCK_KEY, lockValue);
window.addEventListener('beforeunload', function() {
if (localStorage.getItem(LOCK_KEY) === lockValue) {
localStorage.removeItem(LOCK_KEY);
}
});
// Find a public room that the user has joined
function findPublicRoom() {
// Try multiple selectors for chat windows
const chatWindows = document.querySelectorAll('[id^="ChatWindow_"], [id^="chatwindow_"], .x-window');
for (const win of chatWindows) {
// Try multiple selectors for the title
const titleEl = win.querySelector('.x-window-header-text') ||
win.querySelector('.x-window-header span') ||
win.querySelector('[class*="header"] span');
if (titleEl) {
const title = titleEl.textContent.trim();
for (const publicRoom of PUBLIC_ROOMS) {
// Use startsWith to match "General Chat" in "General Chat (45)"
if (title.startsWith(publicRoom)) {
// Extract room ID from window ID
const idMatch = win.id.match(/(\d+)/);
const roomId = idMatch ? idMatch[1] : win.id.replace(/[^\d]/g, '');
if (roomId) {
console.log(`KeepAlive: Found public room "${title}" with ID ${roomId}`);
return { roomId, title };
}
}
}
}
}
// Fallback: check RoomListStore
if (typeof RoomListStore !== 'undefined' && RoomListStore.data) {
const rooms = RoomListStore.data.items || [];
for (const room of rooms) {
const roomData = room.data || room;
const name = roomData.Name || '';
for (const publicRoom of PUBLIC_ROOMS) {
if (name.startsWith(publicRoom)) {
console.log(`KeepAlive: Found public room "${name}" with ID ${roomData.ID}`);
return { roomId: roomData.ID, title: name };
}
}
}
}
// Debug: log what windows we can find
console.log('KeepAlive: Debug - searching for windows...');
document.querySelectorAll('.x-window').forEach(w => {
console.log('KeepAlive: Found window:', w.id, w.querySelector('.x-window-header-text')?.textContent);
});
return null;
}
function getUserId() {
if (typeof userID !== 'undefined') return userID;
if (typeof window.userID !== 'undefined') return window.userID;
if (typeof _currentUserID !== 'undefined') return _currentUserID;
if (typeof currentUserID !== 'undefined') return currentUserID;
return null;
}
function getAuthCode() {
if (typeof auth_code !== 'undefined') return auth_code;
if (typeof window.auth_code !== 'undefined') return window.auth_code;
if (typeof _authCode !== 'undefined') return _authCode;
if (typeof authCode !== 'undefined') return authCode;
return null;
}
function sendKeepAlive() {
const room = findPublicRoom();
if (!room) {
console.log('KeepAlive: No public room found');
return false;
}
// Find the chat input field for this room
// Look for input field within the chat window
const chatWindow = document.getElementById('ChatWindow_' + room.roomId) ||
document.querySelector(`[id*="${room.roomId}"].x-window`);
if (!chatWindow) {
console.log('KeepAlive: Could not find chat window element');
return sendKeepAliveViaAPI(room);
}
// Find the text input field - usually a textarea or input within the window
const inputField = chatWindow.querySelector('textarea') ||
chatWindow.querySelector('input[type="text"]') ||
chatWindow.querySelector('.x-form-text');
if (!inputField) {
console.log('KeepAlive: Could not find input field, trying API method');
return sendKeepAliveViaAPI(room);
}
// Store current value, set our message, trigger submit, restore
const originalValue = inputField.value;
inputField.value = ' ';
inputField.focus();
// Trigger input event
inputField.dispatchEvent(new Event('input', { bubbles: true }));
inputField.dispatchEvent(new Event('change', { bubbles: true }));
// Simulate Enter key press to submit
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
keyCode: 13,
which: 13,
bubbles: true
});
inputField.dispatchEvent(enterEvent);
const enterPress = new KeyboardEvent('keypress', {
key: 'Enter',
code: 'Enter',
keyCode: 13,
which: 13,
bubbles: true
});
inputField.dispatchEvent(enterPress);
const enterUp = new KeyboardEvent('keyup', {
key: 'Enter',
code: 'Enter',
keyCode: 13,
which: 13,
bubbles: true
});
inputField.dispatchEvent(enterUp);
console.log(`KeepAlive: Simulated input to "${room.title}" at ${new Date().toLocaleTimeString()}`);
return true;
}
function sendKeepAliveViaAPI(room) {
const userId = getUserId();
if (!userId) {
console.log('KeepAlive: User ID not found');
return false;
}
const authCodeVal = getAuthCode();
if (typeof Coolite !== 'undefined' && Coolite.AjaxMethods && Coolite.AjaxMethods.AddChatLine) {
Coolite.AjaxMethods.AddChatLine(
room.roomId,
' ',
userId,
window._ipaddress,
window._urlwebchatid,
window._urlwebchatkey,
false,
false,
false,
'',
authCodeVal,
{
success: function() {
console.log(`KeepAlive: Sent via API to "${room.title}" at ${new Date().toLocaleTimeString()}`);
},
failure: function(error) {
console.log('KeepAlive: Failed to send message', error);
}
}
);
return true;
} else {
console.log('KeepAlive: Coolite.AjaxMethods.AddChatLine not available');
return false;
}
}
function startKeepAlive() {
if (isKeepAliveRunning) return;
const intervalMs = keepAliveSettings.intervalMinutes * 60 * 1000;
keepAliveInterval = setInterval(sendKeepAlive, intervalMs);
isKeepAliveRunning = true;
updateKeepAliveButtonState();
console.log(`KeepAlive: Started with ${keepAliveSettings.intervalMinutes} minute interval`);
sendKeepAlive();
}
function stopKeepAlive() {
if (keepAliveInterval) {
clearInterval(keepAliveInterval);
keepAliveInterval = null;
}
isKeepAliveRunning = false;
updateKeepAliveButtonState();
console.log('KeepAlive: Stopped');
}
function updateKeepAliveButtonState() {
const button = document.getElementById(KEEPALIVE_BUTTON_ID);
if (button) {
if (isKeepAliveRunning) {
button.style.backgroundColor = '#f44336';
button.textContent = `Keep Alive ON (${keepAliveSettings.intervalMinutes}m)`;
} else {
button.style.backgroundColor = '#2196F3';
button.textContent = 'Keep Alive OFF';
}
}
}
function showKeepAliveDialog() {
const overlay = document.createElement('div');
overlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9999999; display: flex; align-items: center; justify-content: center;';
const dialog = document.createElement('div');
dialog.style.cssText = 'background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); min-width: 300px;';
dialog.innerHTML = `
<div style="font-family: Arial, sans-serif;">
<h3 style="margin: 0 0 15px 0; color: #333;">Keep Alive Settings</h3>
<p style="margin: 0 0 10px 0; color: #666;">Interval (minutes):</p>
<input type="number" id="keepAliveIntervalInput" value="${keepAliveSettings.intervalMinutes}" min="1" max="30" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; box-sizing: border-box;" />
<p style="margin: 10px 0; color: #888; font-size: 12px;">Status: ${isKeepAliveRunning ? 'Running' : 'Stopped'}</p>
<div style="margin-top: 15px; display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap;">
<button id="keepAliveCancelBtn" style="padding: 8px 15px; background: #ccc; color: #333; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">Cancel</button>
<button id="keepAliveToggleBtn" style="padding: 8px 15px; background: ${isKeepAliveRunning ? '#f44336' : '#4CAF50'}; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">${isKeepAliveRunning ? 'Stop' : 'Start'}</button>
<button id="keepAliveSaveBtn" style="padding: 8px 15px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">Save</button>
</div>
</div>
`;
overlay.appendChild(dialog);
document.body.appendChild(overlay);
const input = document.getElementById('keepAliveIntervalInput');
const saveBtn = document.getElementById('keepAliveSaveBtn');
const cancelBtn = document.getElementById('keepAliveCancelBtn');
const toggleBtn = document.getElementById('keepAliveToggleBtn');
setTimeout(() => input.focus(), 100);
saveBtn.onclick = function() {
const value = parseInt(input.value, 10);
if (value >= 1 && value <= 30) {
keepAliveSettings.intervalMinutes = value;
saveKeepAliveSettings();
if (isKeepAliveRunning) {
stopKeepAlive();
startKeepAlive();
}
updateKeepAliveButtonState();
overlay.remove();
} else {
alert('Please enter a number between 1 and 30');
}
};
cancelBtn.onclick = function() {
overlay.remove();
};
toggleBtn.onclick = function() {
if (isKeepAliveRunning) {
stopKeepAlive();
} else {
const value = parseInt(input.value, 10);
if (value >= 1 && value <= 30) {
keepAliveSettings.intervalMinutes = value;
saveKeepAliveSettings();
}
startKeepAlive();
}
overlay.remove();
};
input.onkeypress = function(e) {
if (e.key === 'Enter') {
saveBtn.click();
}
};
overlay.onkeydown = function(e) {
if (e.key === 'Escape') {
cancelBtn.click();
}
};
}
// ==================== MULTI-POKE FUNCTIONALITY ====================
function performMultiPoke(roomId, userId, buttonElement) {
const originalText = buttonElement.textContent;
let pokeCount = 0;
const totalPokes = DEFAULT_POKE_COUNT;
// Disable the button during multi-poke
buttonElement.style.opacity = '0.5';
buttonElement.style.pointerEvents = 'none';
function sendPoke() {
if (pokeCount < totalPokes) {
try {
if (typeof window.PK === 'function') {
window.PK(roomId, userId);
} else if (typeof Coolite !== 'undefined' && Coolite.AjaxMethods && Coolite.AjaxMethods.Poke) {
Coolite.AjaxMethods.Poke(roomId, '', userId, '', '');
} else {
const tempPokeLink = document.createElement('a');
tempPokeLink.href = `javascript:PK('${roomId}','${userId}')`;
tempPokeLink.style.display = 'none';
document.body.appendChild(tempPokeLink);
tempPokeLink.click();
document.body.removeChild(tempPokeLink);
}
} catch (error) {
console.log('Poke error:', error);
}
pokeCount++;
buttonElement.textContent = `${totalPokes - pokeCount}x`;
if (pokeCount < totalPokes) {
setTimeout(sendPoke, POKE_DELAY);
} else {
setTimeout(() => {
buttonElement.textContent = originalText;
buttonElement.style.opacity = '';
buttonElement.style.pointerEvents = '';
}, 500);
}
}
}
sendPoke();
}
function addMultiPokeButtons() {
if (!multiPokeEnabled) return;
const userRows = document.querySelectorAll('.ur');
userRows.forEach(userRow => {
const allDiv = userRow.querySelector('.all');
if (!allDiv) return;
if (allDiv.innerHTML.trim() === '') return;
if (allDiv.querySelector('.multi-poke-btn')) return;
const pokeButton = allDiv.querySelector('a[href*="PK("]');
if (!pokeButton) return;
const pokeHref = pokeButton.getAttribute('href');
const match = pokeHref.match(/PK\('([^']+)','([^']+)'\)/);
if (!match) return;
const roomId = match[1];
const odlUserId = match[2];
const multiPokeBtn = document.createElement('a');
multiPokeBtn.className = 'multi-poke-btn';
multiPokeBtn.href = 'javascript:void(0)';
multiPokeBtn.textContent = '10x';
multiPokeBtn.title = 'Send 10 pokes';
multiPokeBtn.setAttribute('data-multi-poke', 'true');
multiPokeBtn.setAttribute('data-user-id', odlUserId);
multiPokeBtn.addEventListener('click', function(e) {
e.preventDefault();
performMultiPoke(roomId, odlUserId, this);
});
const space = document.createTextNode('\u00A0');
pokeButton.parentNode.insertBefore(space, pokeButton.nextSibling);
pokeButton.parentNode.insertBefore(multiPokeBtn, space.nextSibling);
});
}
function removeMultiPokeButtons() {
const multiPokeElements = document.querySelectorAll('.multi-poke-btn, [data-multi-poke="true"]');
multiPokeElements.forEach(element => {
if (element.previousSibling && element.previousSibling.nodeType === Node.TEXT_NODE) {
element.previousSibling.remove();
}
element.remove();
});
}
function toggleMultiPoke() {
multiPokeEnabled = !multiPokeEnabled;
saveMultiPokeSettings();
updateMultiPokeButtonState();
if (multiPokeEnabled) {
addMultiPokeButtons();
} else {
removeMultiPokeButtons();
}
console.log('Multi-poke', multiPokeEnabled ? 'enabled' : 'disabled');
}
function updateMultiPokeButtonState() {
const button = document.getElementById(MULTIPOKE_BUTTON_ID);
if (button) {
if (multiPokeEnabled) {
button.style.backgroundColor = '#ff6b35';
button.textContent = '10x ON';
} else {
button.style.backgroundColor = '#6c757d';
button.textContent = '10x OFF';
}
}
}
function observeUserList() {
const targetNode = document.querySelector('div[id*="CHATUSERLABEL"]') ||
document.querySelector('.x-panel-body') ||
document.body;
const observer = new MutationObserver(function(mutations) {
let shouldUpdate = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE &&
(node.classList && node.classList.contains('ur') || node.querySelector && node.querySelector('.ur'))) {
shouldUpdate = true;
}
});
}
if (mutation.type === 'childList' && mutation.target.classList && mutation.target.classList.contains('ur')) {
shouldUpdate = true;
}
});
if (shouldUpdate && multiPokeEnabled) {
clearTimeout(window.multiPokeUpdateDebounce);
window.multiPokeUpdateDebounce = setTimeout(() => {
addMultiPokeButtons();
}, 200);
}
});
observer.observe(targetNode, {
childList: true,
subtree: true
});
}
// ==================== END MULTI-POKE ====================
// Create custom dialog for MaxDockedCams
function showCustomPrompt() {
const overlay = document.createElement('div');
overlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9999999; display: flex; align-items: center; justify-content: center;';
const dialog = document.createElement('div');
dialog.style.cssText = 'background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); min-width: 300px;';
dialog.innerHTML = `
<div style="font-family: Arial, sans-serif;">
<h3 style="margin: 0 0 15px 0; color: #333;">Set MaxDockedCams</h3>
<p style="margin: 0 0 10px 0; color: #666;">Enter a number:</p>
<input type="number" id="maxDockedInput" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; box-sizing: border-box;" placeholder="Enter number" />
<div style="margin-top: 15px; text-align: right;">
<button id="cancelBtn" style="padding: 8px 15px; margin-right: 10px; background: #ccc; color: #333; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">Cancel</button>
<button id="okBtn" style="padding: 8px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">OK</button>
</div>
</div>
`;
overlay.appendChild(dialog);
document.body.appendChild(overlay);
const input = document.getElementById('maxDockedInput');
const okBtn = document.getElementById('okBtn');
const cancelBtn = document.getElementById('cancelBtn');
setTimeout(() => input.focus(), 100);
okBtn.onclick = function() {
const value = input.value;
if (value === '') {
alert('Please enter a number');
return;
}
const number = parseInt(value, 10);
if (!isNaN(number)) {
window.maxDockedCamsForUser = number;
console.log(`maxDockedCamsForUser set to: ${number}`);
overlay.remove();
alert(`maxDockedCamsForUser successfully set to ${number}`);
} else {
alert('Please enter a valid number');
}
};
cancelBtn.onclick = function() {
console.log('User cancelled');
overlay.remove();
};
input.onkeypress = function(e) {
if (e.key === 'Enter') {
okBtn.click();
}
};
overlay.onkeydown = function(e) {
if (e.key === 'Escape') {
cancelBtn.click();
}
};
}
// Wait and create buttons
setTimeout(function() {
// Cleanup existing buttons
document.querySelectorAll(`[id^="maxDockedCamsButton"]`).forEach(btn => btn.remove());
document.querySelectorAll(`[id^="keepAliveButton"]`).forEach(btn => btn.remove());
// MaxDockedCams button
const maxDockedButton = document.createElement('button');
maxDockedButton.id = BUTTON_ID;
maxDockedButton.textContent = 'Set MaxDockedCams';
maxDockedButton.type = 'button';
maxDockedButton.style.cssText = 'position: fixed; top: 10px; right: 10px; z-index: 999999; padding: 8px 12px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.3);';
maxDockedButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showCustomPrompt();
});
document.body.appendChild(maxDockedButton);
makeDraggable(maxDockedButton);
console.log('MaxDockedCams button created');
// Keep Alive button
const keepAliveButton = document.createElement('button');
keepAliveButton.id = KEEPALIVE_BUTTON_ID;
keepAliveButton.type = 'button';
keepAliveButton.style.cssText = 'position: fixed; top: 10px; right: 170px; z-index: 999999; padding: 8px 12px; background-color: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.3);';
keepAliveButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showKeepAliveDialog();
});
document.body.appendChild(keepAliveButton);
makeDraggable(keepAliveButton);
updateKeepAliveButtonState();
console.log('KeepAlive button created');
// Multi-Poke toggle button
document.querySelectorAll(`[id^="multiPokeButton"]`).forEach(btn => btn.remove());
const multiPokeButton = document.createElement('button');
multiPokeButton.id = MULTIPOKE_BUTTON_ID;
multiPokeButton.type = 'button';
multiPokeButton.style.cssText = 'position: fixed; top: 10px; right: 340px; z-index: 999999; padding: 8px 12px; background-color: #ff6b35; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.3);';
multiPokeButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggleMultiPoke();
});
document.body.appendChild(multiPokeButton);
makeDraggable(multiPokeButton);
updateMultiPokeButtonState();
console.log('MultiPoke button created');
// Auto-start keep-alive
setTimeout(function() {
startKeepAlive();
}, 2000);
// Initialize multi-poke if enabled
setTimeout(function() {
if (multiPokeEnabled) {
addMultiPokeButtons();
}
observeUserList();
// Periodic check for new users
setInterval(() => {
if (multiPokeEnabled) {
addMultiPokeButtons();
}
}, 2000);
}, 1000);
// Cleanup duplicate buttons periodically
setInterval(function() {
const maxDockedButtons = document.querySelectorAll(`#${BUTTON_ID}`);
if (maxDockedButtons.length > 1) {
for (let i = 1; i < maxDockedButtons.length; i++) {
maxDockedButtons[i].remove();
}
}
const keepAliveButtons = document.querySelectorAll(`#${KEEPALIVE_BUTTON_ID}`);
if (keepAliveButtons.length > 1) {
for (let i = 1; i < keepAliveButtons.length; i++) {
keepAliveButtons[i].remove();
}
}
const multiPokeButtons = document.querySelectorAll(`#${MULTIPOKE_BUTTON_ID}`);
if (multiPokeButtons.length > 1) {
for (let i = 1; i < multiPokeButtons.length; i++) {
multiPokeButtons[i].remove();
}
}
}, 500);
}, 100);
// Add CSS for multi-poke buttons
const style = document.createElement('style');
style.textContent = `
.multi-poke-btn {
background-color: #ff6b35 !important;
color: white !important;
padding: 1px 4px !important;
border-radius: 3px !important;
text-decoration: none !important;
font-weight: bold !important;
font-size: 10px !important;
margin-left: 2px !important;
}
.multi-poke-btn:hover {
background-color: #ff8c42 !important;
color: white !important;
}
`;
document.head.appendChild(style);
})();