// ==UserScript==
// @name Friends List & AutoDarts Enhancements
// @namespace Owl
// @version 3.9
// @description Manage friends list with drag, resize, and a direct game link
// @match https://play.autodarts.io/*
// @run-at document-idle
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log("[Friends List Script] Starting...");
let friendsList = JSON.parse(localStorage.getItem('friendsList')) || [];
function saveFriendsList() {
localStorage.setItem('friendsList', JSON.stringify(friendsList));
}
// Popup variables
let popupVisible = false;
let popupContainer = null;
// Menu item ID
const MENU_ITEM_ID = 'autodarts-friendslist-menu-item';
// --------------------------------------------------
// A) DRAG LOGIC
// --------------------------------------------------
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
let hasConvertedCenter = false;
function onDragMouseDown(e) {
e.preventDefault();
if (!hasConvertedCenter) {
convertCenterToAbsolutePosition(popupContainer);
hasConvertedCenter = true;
}
isDragging = true;
offsetX = popupContainer.offsetLeft - e.clientX;
offsetY = popupContainer.offsetTop - e.clientY;
}
function onDragMouseMove(e) {
if (!isDragging) return;
e.preventDefault();
const newLeft = e.clientX + offsetX;
const newTop = e.clientY + offsetY;
popupContainer.style.left = newLeft + 'px';
popupContainer.style.top = newTop + 'px';
}
function onDragMouseUp() {
isDragging = false;
}
function convertCenterToAbsolutePosition(elem) {
const rect = elem.getBoundingClientRect();
elem.style.left = rect.left + 'px';
elem.style.top = rect.top + 'px';
elem.style.transform = 'none';
}
// --------------------------------------------------
// B) CREATE POPUP
// --------------------------------------------------
function createPopup() {
if (popupContainer) return;
popupContainer = document.createElement('div');
popupContainer.id = 'autodarts-friendslist-popup';
Object.assign(popupContainer.style, {
position: 'fixed',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
padding: '20px',
backgroundColor: '#FFEB3B', // Yellow color for Friends list
color: '#1A202C',
border: '1px solid #FF9800',
borderRadius: '8px',
boxShadow: '0 0 10px rgba(0,0,0,0.5)',
zIndex: '99999',
fontFamily: 'sans-serif',
width: 'auto',
minWidth: '300px',
maxWidth: '80vw',
maxHeight: '80vh',
overflowY: 'auto',
display: 'none',
resize: 'both',
overflow: 'auto'
});
// Drag bar
const dragBar = document.createElement('div');
dragBar.style.height = '20px';
dragBar.style.cursor = 'move';
dragBar.style.marginBottom = '10px';
popupContainer.appendChild(dragBar);
// Close "X" button
const closeXButton = document.createElement('button');
closeXButton.textContent = '×';
Object.assign(closeXButton.style, {
position: 'absolute',
top: '4px',
right: '8px',
background: 'transparent',
border: 'none',
color: '#1A202C',
fontSize: '20px',
lineHeight: '20px',
cursor: 'pointer'
});
closeXButton.addEventListener('click', () => {
togglePopup(false);
});
popupContainer.appendChild(closeXButton);
// Title
const title = document.createElement('h2');
title.textContent = 'Freundesliste';
title.style.marginTop = '0';
title.style.fontSize = '1.4rem';
title.style.fontWeight = 'bold';
popupContainer.appendChild(title);
// Input area for name and board ID
const inputWrapper = document.createElement('div');
inputWrapper.style.display = 'flex';
inputWrapper.style.marginBottom = '10px';
popupContainer.appendChild(inputWrapper);
const nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.placeholder = 'Name des Freundes';
Object.assign(nameInput.style, {
flex: '1',
marginRight: '5px',
padding: '4px 8px'
});
inputWrapper.appendChild(nameInput);
const boardIdInput = document.createElement('input');
boardIdInput.type = 'text';
boardIdInput.placeholder = 'Board ID des Freundes';
Object.assign(boardIdInput.style, {
flex: '1',
marginRight: '5px',
padding: '4px 8px'
});
inputWrapper.appendChild(boardIdInput);
const addButton = document.createElement('button');
addButton.textContent = 'Freund Hinzufügen';
Object.assign(addButton.style, {
padding: '4px 8px',
cursor: 'pointer',
backgroundColor: '#FF9800',
color: '#fff',
border: 'none',
borderRadius: '4px'
});
inputWrapper.appendChild(addButton);
// UL list
const listElement = document.createElement('ul');
listElement.style.listStyle = 'none';
listElement.style.paddingLeft = '0';
popupContainer.appendChild(listElement);
// Add function for name and board ID
function addFriend() {
const name = nameInput.value.trim();
const boardId = boardIdInput.value.trim();
if (name && boardId) {
const friend = { name, boardId };
if (!friendsList.some(f => f.name === name && f.boardId === boardId)) {
friendsList.push(friend);
saveFriendsList();
updateList(listElement);
}
nameInput.value = '';
boardIdInput.value = '';
}
}
addButton.addEventListener('click', addFriend);
nameInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
addFriend();
}
});
// Show the list initially
updateList(listElement);
// Add a button to access the game
const gameLinkButton = document.createElement('button');
gameLinkButton.textContent = 'Zum Spiel';
Object.assign(gameLinkButton.style, {
padding: '4px 8px',
cursor: 'pointer',
backgroundColor: '#2D3748',
color: '#fff',
border: 'none',
borderRadius: '4px',
marginTop: '10px'
});
gameLinkButton.addEventListener('click', () => {
window.location.href = 'https://play.autodarts.io/boards/bc4e64af-b60b-42b2-8420-1982eb2c3ac6/follow';
});
popupContainer.appendChild(gameLinkButton);
document.body.appendChild(popupContainer);
// Register drag events
dragBar.addEventListener('mousedown', onDragMouseDown);
document.addEventListener('mousemove', onDragMouseMove);
document.addEventListener('mouseup', onDragMouseUp);
}
// ---- REMOVE FRIEND (with confirmation) ----
function removeFriend(name, boardId, listElement) {
const confirmed = confirm(`Bist du sicher, dass du ${name} mit Board ID ${boardId} entfernen möchtest?`);
if (confirmed) {
friendsList = friendsList.filter(friend => friend.name !== name || friend.boardId !== boardId);
saveFriendsList();
updateList(listElement);
}
}
function updateList(listElement) {
listElement.innerHTML = '';
friendsList.forEach(friend => {
const li = document.createElement('li');
li.textContent = `${friend.name} (Board ID: ${friend.boardId})`;
Object.assign(li.style, {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '4px 0'
});
const removeBtn = document.createElement('button');
removeBtn.textContent = 'X';
Object.assign(removeBtn.style, {
marginLeft: '10px',
backgroundColor: '#C53030',
color: '#fff',
border: 'none',
borderRadius: '4px',
padding: '2px 8px',
cursor: 'pointer'
});
removeBtn.addEventListener('click', () => {
removeFriend(friend.name, friend.boardId, listElement);
});
li.appendChild(removeBtn);
listElement.appendChild(li);
});
}
function togglePopup(forceOpen) {
if (!popupContainer) {
createPopup();
}
popupVisible = (typeof forceOpen === 'boolean') ? forceOpen : !popupVisible;
popupContainer.style.display = popupVisible ? 'block' : 'none';
}
// --------------------------------------------------
// MENU ITEM
// --------------------------------------------------
function addFriendsListMenuItem(menuContainer) {
let friendsListLink = document.getElementById(MENU_ITEM_ID);
if (!friendsListLink) {
friendsListLink = document.createElement('a');
friendsListLink.id = MENU_ITEM_ID;
friendsListLink.textContent = 'Freundesliste';
friendsListLink.className = 'chakra-button css-1nal3hj';
friendsListLink.style.cursor = 'pointer';
menuContainer.appendChild(friendsListLink);
friendsListLink.addEventListener('click', () => {
togglePopup();
});
}
}
// --------------------------------------------------
// MUTATIONOBSERVER MIT DEBOUNCE
// --------------------------------------------------
let checkTimeout = null;
function triggerCheckWithDebounce() {
if (checkTimeout) {
clearTimeout(checkTimeout);
}
checkTimeout = setTimeout(() => {
checkTimeout = null;
}, 300);
}
const observer = new MutationObserver(() => {
triggerCheckWithDebounce();
});
observer.observe(document.body, { childList: true, subtree: true });
// --------------------------------------------------
// MENU FIND + ADD
// --------------------------------------------------
const intervalId = setInterval(() => {
const menuContainer = [...document.querySelectorAll('div.chakra-stack')]
.find(div => div.querySelector('a[href="/"]') && div.querySelector('a[href="/lobbies"]'));
if (menuContainer) {
addFriendsListMenuItem(menuContainer);
clearInterval(intervalId);
}
}, 1000);
// Start
updateList();
})();