(() => { // Script has already been initialized check if (window.bskyTrustedUsersInitialized) { console.log("Trusted Users script already initialized"); return; } // Mark script as initialized window.bskyTrustedUsersInitialized = true; // Define storage keys const TRUSTED_USERS_STORAGE_KEY = "bsky_trusted_users"; const VERIFICATION_CACHE_STORAGE_KEY = "bsky_verification_cache"; const CACHE_EXPIRY_TIME = 24 * 60 * 60 * 1000; // 24 hours // Function to get trusted users from local storage const getTrustedUsers = () => { const storedUsers = localStorage.getItem(TRUSTED_USERS_STORAGE_KEY); return storedUsers ? JSON.parse(storedUsers) : []; }; // Function to save trusted users to local storage const saveTrustedUsers = (users) => { localStorage.setItem(TRUSTED_USERS_STORAGE_KEY, JSON.stringify(users)); }; // Function to add a trusted user const addTrustedUser = (handle) => { const users = getTrustedUsers(); if (!users.includes(handle)) { users.push(handle); saveTrustedUsers(users); } }; // Function to remove a trusted user const removeTrustedUser = (handle) => { const users = getTrustedUsers(); const updatedUsers = users.filter((user) => user !== handle); saveTrustedUsers(updatedUsers); }; // Cache functions const getVerificationCache = () => { const cache = localStorage.getItem(VERIFICATION_CACHE_STORAGE_KEY); return cache ? JSON.parse(cache) : {}; }; const saveVerificationCache = (cache) => { localStorage.setItem(VERIFICATION_CACHE_STORAGE_KEY, JSON.stringify(cache)); }; const getCachedVerifications = (user) => { const cache = getVerificationCache(); return cache[user] || null; }; const cacheVerifications = (user, records) => { const cache = getVerificationCache(); cache[user] = { records, timestamp: Date.now(), }; saveVerificationCache(cache); }; const isCacheValid = (cacheEntry) => { return cacheEntry && Date.now() - cacheEntry.timestamp < CACHE_EXPIRY_TIME; }; const clearCache = () => { localStorage.removeItem(VERIFICATION_CACHE_STORAGE_KEY); console.log("Verification cache cleared"); }; // Store all verifiers for a profile let profileVerifiers = []; // Store current profile DID let currentProfileDid = null; // Function to check if a trusted user has verified the current profile const checkTrustedUserVerifications = async (profileDid) => { currentProfileDid = profileDid; // Store for recheck functionality const trustedUsers = getTrustedUsers(); profileVerifiers = []; // Reset the verifiers list if (trustedUsers.length === 0) { console.log("No trusted users to check for verifications"); return false; } console.log(`Checking if any trusted users have verified ${profileDid}`); // Use Promise.all to fetch all verification data in parallel const verificationPromises = trustedUsers.map(async (trustedUser) => { try { // Helper function to fetch all verification records with pagination const fetchAllVerifications = async (user) => { // Check cache first const cachedData = getCachedVerifications(user); if (cachedData && isCacheValid(cachedData)) { console.log(`Using cached verification data for ${user}`); return cachedData.records; } console.log(`Fetching fresh verification data for ${user}`); let allRecords = []; let cursor = null; let hasMore = true; while (hasMore) { const url = cursor ? `https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=${user}&collection=app.bsky.graph.verification&cursor=${cursor}` : `https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=${user}&collection=app.bsky.graph.verification`; const response = await fetch(url); const data = await response.json(); if (data.records && data.records.length > 0) { allRecords = [...allRecords, ...data.records]; } if (data.cursor) { cursor = data.cursor; } else { hasMore = false; } } // Save to cache cacheVerifications(user, allRecords); return allRecords; }; // Fetch all verification records for this trusted user const records = await fetchAllVerifications(trustedUser); console.log(`Received verification data from ${trustedUser}`, { records, }); // Check if this trusted user has verified the current profile if (records.length > 0) { for (const record of records) { if (record.value && record.value.subject === profileDid) { console.log( `${profileDid} is verified by trusted user ${trustedUser}`, ); // Add to verifiers list profileVerifiers.push(trustedUser); break; // Once we find a verification, we can stop checking } } } return { trustedUser, success: true }; } catch (error) { console.error( `Error checking verifications from ${trustedUser}:`, error, ); return { trustedUser, success: false, error }; } }); // Wait for all verification checks to complete const results = await Promise.all(verificationPromises); // Log summary of API calls console.log(`API calls completed: ${results.length}`); console.log(`Successful calls: ${results.filter((r) => r.success).length}`); console.log(`Failed calls: ${results.filter((r) => !r.success).length}`); // If we have verifiers, display the badge if (profileVerifiers.length > 0) { await displayVerificationBadge(profileVerifiers); return true; } console.log(`${profileDid} is not verified by any trusted users`); // Add recheck button even when no verifications are found createPillButtons(); return false; }; // Function to create a pill with recheck and settings buttons const createPillButtons = () => { // Remove existing buttons if any const existingPill = document.getElementById( "trusted-users-pill-container", ); if (existingPill) { existingPill.remove(); } // Create pill container const pillContainer = document.createElement("div"); pillContainer.id = "trusted-users-pill-container"; pillContainer.style.cssText = ` position: fixed; bottom: 20px; right: 20px; z-index: 10000; display: flex; border-radius: 20px; overflow: hidden; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); `; // Create recheck button (left half of pill) const recheckButton = document.createElement("button"); recheckButton.id = "trusted-users-recheck-button"; recheckButton.innerHTML = "↻ Recheck"; recheckButton.style.cssText = ` padding: 10px 15px; background-color: #2D578D; color: white; border: none; cursor: pointer; font-weight: bold; border-top-left-radius: 20px; border-bottom-left-radius: 20px; `; // Add click event to recheck recheckButton.addEventListener("click", async () => { if (currentProfileDid) { // Remove any existing badges when rechecking const existingBadge = document.getElementById( "user-trusted-verification-badge", ); if (existingBadge) { existingBadge.remove(); } // Show loading state recheckButton.innerHTML = "⟳ Checking..."; recheckButton.disabled = true; // Recheck verifications await checkTrustedUserVerifications(currentProfileDid); // Reset button recheckButton.innerHTML = "↻ Recheck"; recheckButton.disabled = false; } }); // Create vertical divider const divider = document.createElement("div"); divider.style.cssText = ` width: 1px; background-color: rgba(255, 255, 255, 0.3); `; // Create settings button (right half of pill) const settingsButton = document.createElement("button"); settingsButton.id = "bsky-trusted-settings-button"; settingsButton.textContent = "Settings"; settingsButton.style.cssText = ` padding: 10px 15px; background-color: #2D578D; color: white; border: none; cursor: pointer; font-weight: bold; border-top-right-radius: 20px; border-bottom-right-radius: 20px; `; // Add elements to pill pillContainer.appendChild(recheckButton); pillContainer.appendChild(divider); pillContainer.appendChild(settingsButton); // Add pill to page document.body.appendChild(pillContainer); // Add event listener to settings button settingsButton.addEventListener("click", () => { if (settingsModal) { settingsModal.style.display = "flex"; updateTrustedUsersList(); } else { createSettingsModal(); } }); }; const findProfileHeaderWithRetry = (retryCount = 0, maxRetries = 10) => { const nameElements = document.querySelectorAll( '[data-testid="profileHeaderDisplayName"]', ); const nameElement = nameElements[nameElements.length - 1]; if (nameElement) { console.log("Profile header found"); return nameElement; } if (retryCount < maxRetries) { // Retry with exponential backoff const delay = Math.min(100 * 1.5 ** retryCount, 2000); console.log( `Profile header not found, retrying in ${delay}ms (attempt ${retryCount + 1}/${maxRetries})`, ); return new Promise((resolve) => { setTimeout(() => { resolve(findProfileHeaderWithRetry(retryCount + 1, maxRetries)); }, delay); }); } console.log("Failed to find profile header after maximum retries"); return null; }; // Function to display verification badge on the profile const displayVerificationBadge = async (verifierHandles) => { // Find the profile header or name element to add the badge to const nameElement = await findProfileHeaderWithRetry(); console.log(nameElement); if (nameElement) { // Remove existing badge if present const existingBadge = document.getElementById( "user-trusted-verification-badge", ); if (existingBadge) { existingBadge.remove(); } const badge = document.createElement("span"); badge.id = "user-trusted-verification-badge"; badge.innerHTML = "✓"; // Create tooltip text with all verifiers const verifiersText = verifierHandles.length > 1 ? `Verified by: ${verifierHandles.join(", ")}` : `Verified by ${verifierHandles[0]}`; badge.title = verifiersText; badge.style.cssText = ` background-color: #0070ff; color: white; border-radius: 50%; width: 18px; height: 18px; margin-left: 8px; font-size: 12px; font-weight: bold; cursor: help; display: inline-flex; align-items: center; justify-content: center; `; // Add a click event to show all verifiers badge.addEventListener("click", (e) => { e.stopPropagation(); showVerifiersPopup(verifierHandles); }); nameElement.appendChild(badge); } // Also add pill buttons when verification is found createPillButtons(); }; // Function to show a popup with all verifiers const showVerifiersPopup = (verifierHandles) => { // Remove existing popup if any const existingPopup = document.getElementById("verifiers-popup"); if (existingPopup) { existingPopup.remove(); } // Create popup const popup = document.createElement("div"); popup.id = "verifiers-popup"; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #24273A; padding: 20px; border-radius: 10px; z-index: 10002; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); max-width: 400px; width: 90%; `; // Create popup content popup.innerHTML = `

Profile Verifiers

${verifierHandles .map( (handle) => `
${handle}
`, ) .join("")}
`; // Add to body document.body.appendChild(popup); // Add close handler document .getElementById("close-verifiers-popup") .addEventListener("click", () => { popup.remove(); }); // Close when clicking outside document.addEventListener("click", function closePopup(e) { if (!popup.contains(e.target)) { popup.remove(); document.removeEventListener("click", closePopup); } }); }; // Create settings modal let settingsModal = null; // Function to update the list of trusted users in the UI const updateTrustedUsersList = () => { const trustedUsersList = document.getElementById("trustedUsersList"); if (!trustedUsersList) return; const users = getTrustedUsers(); trustedUsersList.innerHTML = ""; if (users.length === 0) { trustedUsersList.innerHTML = "

No trusted users added yet.

"; return; } for (const user of users) { const userItem = document.createElement("div"); userItem.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #eee; `; userItem.innerHTML = ` ${user} `; trustedUsersList.appendChild(userItem); } // Add event listeners to remove buttons const removeButtons = document.querySelectorAll(".remove-user"); for (const btn of removeButtons) { btn.addEventListener("click", (e) => { const handle = e.target.getAttribute("data-handle"); removeTrustedUser(handle); updateTrustedUsersList(); }); } }; // Function to create the settings modal const createSettingsModal = () => { // Create modal container settingsModal = document.createElement("div"); settingsModal.id = "bsky-trusted-settings-modal"; settingsModal.style.cssText = ` display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 10001; justify-content: center; align-items: center; `; // Create modal content const modalContent = document.createElement("div"); modalContent.style.cssText = ` background-color: #24273A; padding: 20px; border-radius: 10px; width: 400px; max-height: 80vh; overflow-y: auto; `; // Create modal header const modalHeader = document.createElement("div"); modalHeader.innerHTML = `

Trusted Bluesky Users

`; // Create input form const form = document.createElement("div"); form.innerHTML = `

Add Bluesky handles you trust:

`; // Create trusted users list const trustedUsersList = document.createElement("div"); trustedUsersList.id = "trustedUsersList"; trustedUsersList.style.cssText = ` margin-top: 15px; border-top: 1px solid #eee; padding-top: 15px; `; // Create cache control buttons const cacheControls = document.createElement("div"); cacheControls.style.cssText = ` margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee; `; const clearCacheButton = document.createElement("button"); clearCacheButton.textContent = "Clear Verification Cache"; clearCacheButton.style.cssText = ` padding: 8px 15px; background-color: #735A5A; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; `; clearCacheButton.addEventListener("click", () => { clearCache(); alert( "Verification cache cleared. Fresh data will be fetched on next check.", ); }); cacheControls.appendChild(clearCacheButton); // Create close button const closeButton = document.createElement("button"); closeButton.textContent = "Close"; closeButton.style.cssText = ` margin-top: 20px; padding: 8px 15px; background-color: #473A3A; border: none; border-radius: 4px; cursor: pointer; `; // Assemble modal modalContent.appendChild(modalHeader); modalContent.appendChild(form); modalContent.appendChild(trustedUsersList); modalContent.appendChild(cacheControls); modalContent.appendChild(closeButton); settingsModal.appendChild(modalContent); // Add to document document.body.appendChild(settingsModal); // Event listeners closeButton.addEventListener("click", () => { settingsModal.style.display = "none"; }); // Add trusted user button event document .getElementById("addTrustedUserBtn") .addEventListener("click", () => { const input = document.getElementById("trustedUserInput"); const handle = input.value.trim(); if (handle) { addTrustedUser(handle); input.value = ""; updateTrustedUsersList(); } }); // Close modal when clicking outside settingsModal.addEventListener("click", (e) => { if (e.target === settingsModal) { settingsModal.style.display = "none"; } }); // Initialize the list updateTrustedUsersList(); }; // Function to create the settings UI if it doesn't exist yet const createSettingsUI = () => { // Create pill with buttons createPillButtons(); // Create the settings modal if it doesn't exist yet if (!settingsModal) { createSettingsModal(); } }; // Function to check the current profile const checkCurrentProfile = () => { const currentUrl = window.location.href; // Only trigger on profile pages if ( currentUrl.match(/bsky\.app\/profile\/[^\/]+$/) || currentUrl.match(/bsky\.app\/profile\/[^\/]+\/follows/) || currentUrl.match(/bsky\.app\/profile\/[^\/]+\/followers/) ) { const handle = currentUrl.split("/profile/")[1].split("/")[0]; console.log("Detected profile page for:", handle); // Create and add the settings UI (only once) createSettingsUI(); // Fetch user profile data fetch( `https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=${handle}&collection=app.bsky.actor.profile&rkey=self`, ) .then((response) => response.json()) .then((data) => { console.log("User profile data:", data); // Extract the DID from the profile data const did = data.uri.split("/")[2]; console.log("User DID:", did); // Check if any trusted users have verified this profile using the DID checkTrustedUserVerifications(did); }) .catch((error) => { console.error("Error checking profile:", error); }); console.log("Bluesky profile detected"); } else { // Not on a profile page, reset state currentProfileDid = null; profileVerifiers = []; // Remove UI elements if present const existingBadge = document.getElementById( "user-trusted-verification-badge", ); if (existingBadge) { existingBadge.remove(); } const existingPill = document.getElementById( "trusted-users-pill-container", ); if (existingPill) { existingPill.remove(); } } }; // Initial check checkCurrentProfile(); // Set up a MutationObserver to watch for URL changes const observeUrlChanges = () => { let lastUrl = location.href; const observer = new MutationObserver(() => { if (location.href !== lastUrl) { const oldUrl = lastUrl; lastUrl = location.href; console.log("URL changed from:", oldUrl, "to:", location.href); // Reset current profile DID currentProfileDid = null; profileVerifiers = []; // Clean up UI elements const existingBadge = document.getElementById( "user-trusted-verification-badge", ); if (existingBadge) { existingBadge.remove(); } const existingPill = document.getElementById( "trusted-users-pill-container", ); if (existingPill) { existingPill.remove(); } // Check if we're on a profile page now setTimeout(checkCurrentProfile, 500); // Small delay to ensure DOM has updated } }); observer.observe(document, { subtree: true, childList: true }); }; // Start observing for URL changes observeUrlChanges(); })();