random bun scripts that dont fit anywhere else

feat: add inline verification

dunkirk.sh fba39190 43dc5935

verified
+186
+186
bluesky-community-verifications.js
··· 825 825 // Initial check 826 826 checkCurrentProfile(); 827 827 828 + const checkUserLinksOnPage = async () => { 829 + // Look for profile links with handles 830 + // Find all profile links and filter to get only one link per parent 831 + const allProfileLinks = Array.from( 832 + document.querySelectorAll('a[href^="/profile/"]:not(:has(*))'), 833 + ); 834 + 835 + // Use a Map to keep track of parent elements and their first child link 836 + const parentMap = new Map(); 837 + 838 + // For each link, store only the first one found for each parent 839 + for (const link of allProfileLinks) { 840 + const parent = link.parentElement; 841 + if (parent && !parentMap.has(parent)) { 842 + parentMap.set(parent, link); 843 + } 844 + } 845 + 846 + // Get only the first link for each parent 847 + const profileLinks = Array.from(parentMap.values()); 848 + 849 + if (profileLinks.length === 0) return; 850 + 851 + console.log(`Found ${profileLinks.length} possible user links on page`); 852 + 853 + // Process profile links to identify user containers 854 + for (const link of profileLinks) { 855 + try { 856 + // Check if we already processed this link 857 + if (link.getAttribute("data-verification-checked") === "true") continue; 858 + 859 + // Mark as checked 860 + link.setAttribute("data-verification-checked", "true"); 861 + 862 + // Extract handle from href 863 + const handle = link.getAttribute("href").split("/profile/")[1]; 864 + if (!handle) continue; 865 + 866 + // check if there is anything after the handle 867 + const handleTrailing = handle.split("/").length > 1; 868 + if (handleTrailing) continue; 869 + 870 + // Find parent container that might contain the handle and verification icon 871 + // Look for containers where this link is followed by another link with the same handle 872 + const parent = link.parentElement; 873 + 874 + // If we found a container with the verification icon 875 + if (parent) { 876 + // Check if this user already has our verification badge 877 + if (parent.querySelector(".trusted-user-inline-badge")) continue; 878 + 879 + try { 880 + // Fetch user profile data to get DID 881 + const response = await fetch( 882 + `https://public.api.bsky.app/xrpc/com.atproto.repo.getRecord?repo=${handle}&collection=app.bsky.actor.profile&rkey=self`, 883 + ); 884 + const data = await response.json(); 885 + 886 + // Extract the DID from the profile data 887 + const did = data.uri.split("/")[2]; 888 + 889 + // Check if this user is verified by our trusted users 890 + const trustedUsers = getTrustedUsers(); 891 + let isVerified = false; 892 + const verifiers = []; 893 + 894 + // Check cache first for each trusted user 895 + for (const trustedUser of trustedUsers) { 896 + const cachedData = getCachedVerifications(trustedUser); 897 + 898 + if (cachedData && isCacheValid(cachedData)) { 899 + // Use cached verification data 900 + const records = cachedData.records; 901 + 902 + for (const record of records) { 903 + if (record.value && record.value.subject === did) { 904 + isVerified = true; 905 + verifiers.push(trustedUser); 906 + break; 907 + } 908 + } 909 + } 910 + } 911 + 912 + // If verified, add a small badge 913 + if (isVerified && verifiers.length > 0) { 914 + // Create a badge element 915 + const smallBadge = document.createElement("span"); 916 + smallBadge.className = "trusted-user-inline-badge"; 917 + smallBadge.innerHTML = "✓"; 918 + 919 + // Create tooltip text with all verifiers 920 + const verifiersText = 921 + verifiers.length > 1 922 + ? `Verified by: ${verifiers.join(", ")}` 923 + : `Verified by ${verifiers[0]}`; 924 + 925 + smallBadge.title = verifiersText; 926 + smallBadge.style.cssText = ` 927 + background-color: #0070ff; 928 + color: white; 929 + border-radius: 50%; 930 + width: 14px; 931 + height: 14px; 932 + font-size: 10px; 933 + font-weight: bold; 934 + cursor: help; 935 + display: inline-flex; 936 + align-items: center; 937 + justify-content: center; 938 + margin-left: 4px; 939 + `; 940 + 941 + // Add click event to show verifiers 942 + smallBadge.addEventListener("click", (e) => { 943 + e.stopPropagation(); 944 + showVerifiersPopup(verifiers); 945 + }); 946 + 947 + // Insert badge after the SVG element 948 + parent.firstChild.after(smallBadge); 949 + parent.style.flexDirection = "row"; 950 + parent.style.alignItems = "center"; 951 + } 952 + } catch (error) { 953 + console.error(`Error checking verification for ${handle}:`, error); 954 + } 955 + } 956 + } catch (error) { 957 + console.error("Error processing profile link:", error); 958 + } 959 + } 960 + }; 961 + 962 + const observeContentChanges = () => { 963 + // Use a debounced function to check for new user links 964 + const debouncedCheck = () => { 965 + clearTimeout(window.userLinksCheckTimeout); 966 + window.userLinksCheckTimeout = setTimeout(() => { 967 + checkUserLinksOnPage(); 968 + }, 300); 969 + }; 970 + 971 + // Create a mutation observer that watches for DOM changes 972 + const observer = new MutationObserver((mutations) => { 973 + let hasRelevantChanges = false; 974 + 975 + // Check if any mutations involve adding new nodes 976 + for (const mutation of mutations) { 977 + if (mutation.addedNodes.length > 0) { 978 + for (const node of mutation.addedNodes) { 979 + if (node.nodeType === Node.ELEMENT_NODE) { 980 + // Check if this element or its children might contain profile links 981 + if ( 982 + node.querySelector('a[href^="/profile/"]') || 983 + (node.tagName === "A" && 984 + node.getAttribute("href")?.startsWith("/profile/")) 985 + ) { 986 + hasRelevantChanges = true; 987 + break; 988 + } 989 + } 990 + } 991 + } 992 + if (hasRelevantChanges) break; 993 + } 994 + 995 + if (hasRelevantChanges) { 996 + debouncedCheck(); 997 + } 998 + }); 999 + 1000 + // Observe the entire document for content changes that might include profile links 1001 + observer.observe(document.body, { childList: true, subtree: true }); 1002 + 1003 + // Also check periodically for posts that might have been loaded but not caught by the observer 1004 + setInterval(debouncedCheck, 5000); 1005 + }; 1006 + 1007 + // Add these calls to the initialization section 1008 + // Initial check for user links 1009 + setTimeout(checkUserLinksOnPage, 1000); // Slight delay to ensure page has loaded 1010 + 1011 + // Start observing for content changes to detect newly loaded posts 1012 + observeContentChanges(); 1013 + 828 1014 // Set up a MutationObserver to watch for URL changes 829 1015 const observeUrlChanges = () => { 830 1016 let lastUrl = location.href;