random bun scripts that dont fit anywhere else

feat: add bluesky user verification and bundler

dunkirk.sh 04068637 71a01159

verified
+420
bluesky-community-verifications.js
··· 1 + (() => { 2 + // Define a storage key for trusted users 3 + const TRUSTED_USERS_STORAGE_KEY = "bsky_trusted_users"; 4 + 5 + // Function to get trusted users from local storage 6 + const getTrustedUsers = () => { 7 + const storedUsers = localStorage.getItem(TRUSTED_USERS_STORAGE_KEY); 8 + return storedUsers ? JSON.parse(storedUsers) : []; 9 + }; 10 + 11 + // Function to save trusted users to local storage 12 + const saveTrustedUsers = (users) => { 13 + localStorage.setItem(TRUSTED_USERS_STORAGE_KEY, JSON.stringify(users)); 14 + }; 15 + 16 + // Function to add a trusted user 17 + const addTrustedUser = (handle) => { 18 + const users = getTrustedUsers(); 19 + if (!users.includes(handle)) { 20 + users.push(handle); 21 + saveTrustedUsers(users); 22 + } 23 + }; 24 + 25 + // Function to remove a trusted user 26 + const removeTrustedUser = (handle) => { 27 + const users = getTrustedUsers(); 28 + const updatedUsers = users.filter((user) => user !== handle); 29 + saveTrustedUsers(updatedUsers); 30 + }; 31 + 32 + // Store all verifiers for a profile 33 + let profileVerifiers = []; 34 + 35 + // Function to check if a trusted user has verified the current profile 36 + const checkTrustedUserVerifications = async (currentProfileDid) => { 37 + const trustedUsers = getTrustedUsers(); 38 + profileVerifiers = []; // Reset the verifiers list 39 + 40 + if (trustedUsers.length === 0) { 41 + console.log("No trusted users to check for verifications"); 42 + return false; 43 + } 44 + 45 + console.log( 46 + `Checking if any trusted users have verified ${currentProfileDid}`, 47 + ); 48 + 49 + let isVerifiedByTrustedUser = false; 50 + 51 + for (const trustedUser of trustedUsers) { 52 + try { 53 + const response = await fetch( 54 + `https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=${trustedUser}&collection=app.bsky.graph.verification`, 55 + ); 56 + const data = await response.json(); 57 + 58 + if (data.records && data.records.length > 0) { 59 + for (const record of data.records) { 60 + if (record.value && record.value.subject === currentProfileDid) { 61 + console.log( 62 + `${currentProfileDid} is verified by trusted user ${trustedUser}`, 63 + ); 64 + 65 + // Add to verifiers list 66 + profileVerifiers.push(trustedUser); 67 + isVerifiedByTrustedUser = true; 68 + } 69 + } 70 + } 71 + } catch (error) { 72 + console.error( 73 + `Error checking verifications from ${trustedUser}:`, 74 + error, 75 + ); 76 + } 77 + } 78 + 79 + // If we have verifiers, display the badge 80 + if (profileVerifiers.length > 0) { 81 + displayVerificationBadge(profileVerifiers); 82 + return true; 83 + } 84 + 85 + console.log(`${currentProfileDid} is not verified by any trusted users`); 86 + return false; 87 + }; 88 + 89 + // Function to display verification badge on the profile 90 + const displayVerificationBadge = (verifierHandles) => { 91 + // Find the profile header or name element to add the badge to 92 + const nameElement = document.querySelector( 93 + '[data-testid="profileHeaderDisplayName"]', 94 + ); 95 + 96 + if (nameElement) { 97 + // Check if badge already exists 98 + if (!document.getElementById("user-trusted-verification-badge")) { 99 + const badge = document.createElement("span"); 100 + badge.id = "user-trusted-verification-badge"; 101 + badge.innerHTML = "✓"; 102 + 103 + // Create tooltip text with all verifiers 104 + const verifiersText = 105 + verifierHandles.length > 1 106 + ? `Verified by: ${verifierHandles.join(", ")}` 107 + : `Verified by ${verifierHandles[0]}`; 108 + 109 + badge.title = verifiersText; 110 + badge.style.cssText = ` 111 + background-color: #0070ff; 112 + color: white; 113 + border-radius: 50%; 114 + width: 18px; 115 + height: 18px; 116 + margin-left: 8px; 117 + font-size: 12px; 118 + font-weight: bold; 119 + cursor: help; 120 + display: inline-flex; 121 + align-items: center; 122 + justify-content: center; 123 + `; 124 + 125 + // Add a click event to show all verifiers 126 + badge.addEventListener("click", (e) => { 127 + e.stopPropagation(); 128 + showVerifiersPopup(verifierHandles); 129 + }); 130 + 131 + nameElement.appendChild(badge); 132 + } 133 + } 134 + }; 135 + 136 + // Function to show a popup with all verifiers 137 + const showVerifiersPopup = (verifierHandles) => { 138 + // Remove existing popup if any 139 + const existingPopup = document.getElementById("verifiers-popup"); 140 + if (existingPopup) { 141 + existingPopup.remove(); 142 + } 143 + 144 + // Create popup 145 + const popup = document.createElement("div"); 146 + popup.id = "verifiers-popup"; 147 + popup.style.cssText = ` 148 + position: fixed; 149 + top: 50%; 150 + left: 50%; 151 + transform: translate(-50%, -50%); 152 + background-color: #24273A; 153 + padding: 20px; 154 + border-radius: 10px; 155 + z-index: 10002; 156 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); 157 + max-width: 400px; 158 + width: 90%; 159 + `; 160 + 161 + // Create popup content 162 + popup.innerHTML = ` 163 + <h3 style="margin-top: 0; color: white;">Profile Verifiers</h3> 164 + <div style="max-height: 300px; overflow-y: auto;"> 165 + ${verifierHandles 166 + .map( 167 + (handle) => ` 168 + <div style="padding: 8px 0; border-bottom: 1px solid #444; color: white;"> 169 + ${handle} 170 + </div> 171 + `, 172 + ) 173 + .join("")} 174 + </div> 175 + <button id="close-verifiers-popup" style=" 176 + margin-top: 15px; 177 + padding: 8px 15px; 178 + background-color: #473A3A; 179 + color: white; 180 + border: none; 181 + border-radius: 4px; 182 + cursor: pointer; 183 + ">Close</button> 184 + `; 185 + 186 + // Add to body 187 + document.body.appendChild(popup); 188 + 189 + // Add close handler 190 + document 191 + .getElementById("close-verifiers-popup") 192 + .addEventListener("click", () => { 193 + popup.remove(); 194 + }); 195 + 196 + // Close when clicking outside 197 + document.addEventListener("click", function closePopup(e) { 198 + if (!popup.contains(e.target)) { 199 + popup.remove(); 200 + document.removeEventListener("click", closePopup); 201 + } 202 + }); 203 + }; 204 + 205 + // Create settings button and modal 206 + const createSettingsUI = () => { 207 + // Create settings button 208 + const settingsButton = document.createElement("button"); 209 + settingsButton.textContent = "Trusted Users Settings"; 210 + settingsButton.style.cssText = ` 211 + position: fixed; 212 + bottom: 20px; 213 + right: 20px; 214 + z-index: 10000; 215 + padding: 10px 15px; 216 + background-color: #2D578D; 217 + color: white; 218 + border: none; 219 + border-radius: 20px; 220 + cursor: pointer; 221 + font-weight: bold; 222 + `; 223 + 224 + // Create modal container 225 + const modal = document.createElement("div"); 226 + modal.style.cssText = ` 227 + display: none; 228 + position: fixed; 229 + top: 0; 230 + left: 0; 231 + width: 100%; 232 + height: 100%; 233 + background-color: rgba(0, 0, 0, 0.5); 234 + z-index: 10001; 235 + justify-content: center; 236 + align-items: center; 237 + `; 238 + 239 + // Create modal content 240 + const modalContent = document.createElement("div"); 241 + modalContent.style.cssText = ` 242 + background-color: #24273A; 243 + padding: 20px; 244 + border-radius: 10px; 245 + width: 400px; 246 + max-height: 80vh; 247 + overflow-y: auto; 248 + `; 249 + 250 + // Create modal header 251 + const modalHeader = document.createElement("div"); 252 + modalHeader.innerHTML = `<h2 style="margin-top: 0;">Trusted Bluesky Users</h2>`; 253 + 254 + // Create input form 255 + const form = document.createElement("div"); 256 + form.innerHTML = ` 257 + <p>Add Bluesky handles you trust:</p> 258 + <div style="display: flex; margin-bottom: 15px;"> 259 + <input id="trustedUserInput" type="text" placeholder="username.bsky.social" style="flex: 1; padding: 8px; margin-right: 10px; border: 1px solid #ccc; border-radius: 4px;"> 260 + <button id="addTrustedUserBtn" style="background-color: #2D578D; color: white; border: none; border-radius: 4px; padding: 8px 15px; cursor: pointer;">Add</button> 261 + </div> 262 + `; 263 + 264 + // Create trusted users list 265 + const trustedUsersList = document.createElement("div"); 266 + trustedUsersList.id = "trustedUsersList"; 267 + trustedUsersList.style.cssText = ` 268 + margin-top: 15px; 269 + border-top: 1px solid #eee; 270 + padding-top: 15px; 271 + `; 272 + 273 + // Create close button 274 + const closeButton = document.createElement("button"); 275 + closeButton.textContent = "Close"; 276 + closeButton.style.cssText = ` 277 + margin-top: 20px; 278 + padding: 8px 15px; 279 + background-color: #473A3A; 280 + border: none; 281 + border-radius: 4px; 282 + cursor: pointer; 283 + `; 284 + 285 + // Assemble modal 286 + modalContent.appendChild(modalHeader); 287 + modalContent.appendChild(form); 288 + modalContent.appendChild(trustedUsersList); 289 + modalContent.appendChild(closeButton); 290 + modal.appendChild(modalContent); 291 + 292 + // Add elements to the document 293 + document.body.appendChild(settingsButton); 294 + document.body.appendChild(modal); 295 + 296 + // Function to update the list of trusted users in the UI 297 + const updateTrustedUsersList = () => { 298 + const users = getTrustedUsers(); 299 + trustedUsersList.innerHTML = ""; 300 + 301 + if (users.length === 0) { 302 + trustedUsersList.innerHTML = "<p>No trusted users added yet.</p>"; 303 + return; 304 + } 305 + 306 + for (const user of users) { 307 + const userItem = document.createElement("div"); 308 + userItem.style.cssText = ` 309 + display: flex; 310 + justify-content: space-between; 311 + align-items: center; 312 + padding: 8px 0; 313 + border-bottom: 1px solid #eee; 314 + `; 315 + 316 + userItem.innerHTML = ` 317 + <span>${user}</span> 318 + <button class="remove-user" data-handle="${user}" style="background-color: #CE3838; color: white; border: none; border-radius: 4px; padding: 5px 10px; cursor: pointer;">Remove</button> 319 + `; 320 + 321 + trustedUsersList.appendChild(userItem); 322 + } 323 + 324 + // Add event listeners to remove buttons 325 + const removeButtons = document.querySelectorAll(".remove-user"); 326 + for (const btn of removeButtons) { 327 + btn.addEventListener("click", (e) => { 328 + const handle = e.target.getAttribute("data-handle"); 329 + removeTrustedUser(handle); 330 + updateTrustedUsersList(); 331 + }); 332 + } 333 + }; 334 + 335 + // Event listeners 336 + settingsButton.addEventListener("click", () => { 337 + modal.style.display = "flex"; 338 + updateTrustedUsersList(); 339 + }); 340 + 341 + closeButton.addEventListener("click", () => { 342 + modal.style.display = "none"; 343 + }); 344 + 345 + document 346 + .getElementById("addTrustedUserBtn") 347 + .addEventListener("click", () => { 348 + const input = document.getElementById("trustedUserInput"); 349 + const handle = input.value.trim(); 350 + if (handle) { 351 + addTrustedUser(handle); 352 + input.value = ""; 353 + updateTrustedUsersList(); 354 + } 355 + }); 356 + 357 + // Close modal when clicking outside 358 + modal.addEventListener("click", (e) => { 359 + if (e.target === modal) { 360 + modal.style.display = "none"; 361 + } 362 + }); 363 + }; 364 + 365 + const currentUrl = window.location.href; 366 + if (currentUrl.includes("bsky.app/profile/")) { 367 + const handle = currentUrl.split("/profile/")[1].split("/")[0]; 368 + console.log("Extracted handle:", handle); 369 + 370 + // Create and add the settings UI 371 + createSettingsUI(); 372 + 373 + // Fetch user profile data 374 + fetch( 375 + `https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=${handle}&collection=app.bsky.actor.profile&rkey=self`, 376 + ) 377 + .then((response) => response.json()) 378 + .then((data) => { 379 + console.log("User profile data:", data); 380 + 381 + // Extract the DID from the profile data 382 + const did = data.uri.split("/")[2]; 383 + console.log("User DID:", did); 384 + 385 + // Now fetch the app.bsky.graph.verification data specifically 386 + fetch( 387 + `https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=app.bsky.graph.verification`, 388 + ) 389 + .then((response) => response.json()) 390 + .then((verificationData) => { 391 + console.log("Verification data:", verificationData); 392 + if ( 393 + verificationData.records && 394 + verificationData.records.length > 0 395 + ) { 396 + console.log( 397 + "User has app.bsky.graph.verification:", 398 + verificationData.records, 399 + ); 400 + } else { 401 + console.log("User does not have app.bsky.graph.verification"); 402 + } 403 + 404 + // Check if any trusted users have verified this profile using the DID 405 + checkTrustedUserVerifications(did); 406 + }) 407 + .catch((verificationError) => { 408 + console.error( 409 + "Error fetching verification data:", 410 + verificationError, 411 + ); 412 + }); 413 + }) 414 + .catch((error) => { 415 + console.error("Error checking profile:", error); 416 + }); 417 + 418 + console.log("Example domain detected"); 419 + } 420 + })();
+27
bun.lock
··· 3 3 "workspaces": { 4 4 "": { 5 5 "name": "bunplayground", 6 + "dependencies": { 7 + "terser": "^5.39.0", 8 + }, 6 9 "devDependencies": { 7 10 "@types/bun": "latest", 8 11 }, ··· 12 15 }, 13 16 }, 14 17 "packages": { 18 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], 19 + 20 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 21 + 22 + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 23 + 24 + "@jridgewell/source-map": ["@jridgewell/source-map@0.3.6", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ=="], 25 + 26 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 27 + 28 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 29 + 15 30 "@types/bun": ["@types/bun@1.2.6", "", { "dependencies": { "bun-types": "1.2.6" } }, "sha512-fY9CAmTdJH1Llx7rugB0FpgWK2RKuHCs3g2cFDYXUutIy1QGiPQxKkGY8owhfZ4MXWNfxwIbQLChgH5gDsY7vw=="], 16 31 17 32 "@types/node": ["@types/node@22.13.13", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ=="], 18 33 19 34 "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 20 35 36 + "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], 37 + 38 + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], 39 + 21 40 "bun-types": ["bun-types@1.2.6", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-FbCKyr5KDiPULUzN/nm5oqQs9nXCHD8dVc64BArxJadCvbNzAI6lUWGh9fSJZWeDIRD38ikceBU8Kj/Uh+53oQ=="], 41 + 42 + "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], 43 + 44 + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 45 + 46 + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], 47 + 48 + "terser": ["terser@5.39.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw=="], 22 49 23 50 "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], 24 51
+43
package-bookmarklet.ts
··· 1 + #!/usr/bin/env bun 2 + 3 + import { readFileSync } from "node:fs"; 4 + import { resolve } from "node:path"; 5 + import { minify as terserMinify } from "terser"; 6 + 7 + const main = async () => { 8 + // Get the file path from command line arguments 9 + const filePath = process.argv[2]; 10 + 11 + if (!filePath) { 12 + console.error("Error: Please provide a file path as an argument"); 13 + process.exit(1); 14 + } 15 + 16 + try { 17 + // Read the JavaScript file 18 + const fullPath = resolve(process.cwd(), filePath); 19 + const fileContent = readFileSync(fullPath, "utf8"); 20 + 21 + // Minify the JavaScript using terser 22 + const result = await terserMinify(fileContent, { 23 + mangle: true, 24 + compress: true, 25 + }); 26 + 27 + if (!result.code) { 28 + throw new Error("Minification failed"); 29 + } 30 + 31 + const minified = result.code; 32 + 33 + // Create the bookmarklet by prefixing with "javascript:" 34 + const bookmarklet = `javascript:${encodeURIComponent(minified)}`; 35 + 36 + console.log(bookmarklet); 37 + } catch (error) { 38 + console.error(`Error: ${(error as Error).message}`); 39 + process.exit(1); 40 + } 41 + }; 42 + 43 + main();
+3
package.json
··· 7 7 }, 8 8 "peerDependencies": { 9 9 "typescript": "^5" 10 + }, 11 + "dependencies": { 12 + "terser": "^5.39.0" 10 13 } 11 14 }