Guest User

Untitled

a guest
Sep 9th, 2025
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. javascript:(function(){
  2.   const MAX_IMAGES_PER_POST = 10;
  3.  
  4.   function replaceDomain(e){ return e.replace(/^(https?:\/\/)([^\/]+)/,"$1bsky.app") }
  5.  
  6.   function cleanText(e){
  7.     return e ? e.replace(/<a href="([^"]+)"[^>]*>([^<]+)<\/a>/g,(match,url,text)=>(url.startsWith('/')?`[U][URL=https://bsky.app${url}]${text}[/URL][/U]`:`[U][URL=${url}]${text}[/URL][/U]`))
  8.                 .replace(/<br\s*\/?>/g,"\n")
  9.                 .replace(/<[^>]+>/g,"")
  10.                 .trim() : ""
  11.   }
  12.  
  13.   function extractMedia(e){
  14.     const images = Array.from(e.querySelectorAll('img[src*="cdn.bsky.app"]')).map(i=>i.src).filter(s=>!s.includes("/avatar_thumbnail/")),
  15.           videoSources = new Set([...Array.from(e.querySelectorAll("video source[src]")).map(v=>v.src), ...Array.from(e.querySelectorAll("video[src]")).map(v=>v.src)]);
  16.     return { images, videos: Array.from(videoSources) }
  17.   }
  18.  
  19.   function showToast(e){
  20.     const t=document.createElement("div");
  21.     t.style.cssText="position:fixed;bottom:20px;left:20px;padding:10px 20px;background:#333;color:#fff;border-radius:4px;z-index:10000;font-family:Arial,sans-serif;font-size:14px;opacity=1;transition:opacity 1s;box-shadow:0 2px 5px rgba(0,0,0,0.3)";
  22.     t.innerText=e; document.body.appendChild(t);
  23.     setTimeout(()=>{ t.style.opacity="0"; setTimeout(()=>document.body.removeChild(t),1000) },2000)
  24.   }
  25.  
  26.   function createPostCountPrompt(cb){
  27.     const t=document.createElement("div");
  28.     t.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:9999;display:flex;justify-content:center;align-items:center";
  29.     const r=document.createElement("div");
  30.     r.style.cssText="background:white;padding:20px;border-radius:8px;box-shadow:0 0 10px rgba(0,0,0,0.3);text-align:center";
  31.     const n=document.createElement("input"); n.type="text"; n.value="12"; n.style.cssText="font-size:30px;width:100px;text-align:center;margin-bottom:20px";
  32.     const o=document.createElement("div"); o.style.cssText="display:grid;grid-template-columns:repeat(3,1fr);gap:10px";
  33.     "1234567890".split("").forEach(d=>{ const b=document.createElement("button"); b.textContent=d; b.style.cssText="font-size:20px;padding:10px;cursor:pointer"; b.onclick=()=>n.value+=d; o.appendChild(b) });
  34.     const p=document.createElement("div"); p.style.cssText="display:flex;gap:10px;margin-bottom:20px";
  35.     [1,2,10,25,35,50].forEach(num=>{ const b=document.createElement("button"); b.textContent=num; b.style.cssText="font-size:16px;padding:8px;cursor:pointer"; b.onclick=()=>n.value=num; p.appendChild(b) });
  36.     const s=document.createElement("button"); s.textContent="Clear"; s.style.cssText="font-size:18px;margin-top:10px;padding:10px 20px;cursor:pointer"; s.onclick=()=>n.value="";
  37.     const a=document.createElement("button"); a.textContent="Set Number of Posts"; a.style.cssText="font-size:18px;margin-top:20px;padding:10px 20px;cursor:pointer";
  38.     a.onclick=()=>{ const num=parseInt(n.value,10); if(isNaN(num)||num<=0) showToast("Please enter a valid number."); else { document.body.removeChild(t); cb(num) } };
  39.     r.append(n,p,o,s,a); t.appendChild(r); document.body.appendChild(t)
  40.   }
  41.  
  42.   async function getEmbedUrl(threadData){
  43.     let url = null;
  44.     const post = threadData?.thread?.post;
  45.     if(post?.embed?.$type==="app.bsky.embed.video#view" && post.embed.playlist){
  46.       url = post.embed.playlist;
  47.       console.log("Found playlist (main):", url);
  48.     } else if(post?.record?.embed?.$type==="app.bsky.embed.video" && post.author?.did && post.record.embed.video?.ref?.$link){
  49.       url = constructVideoUrl(post.author.did, post.record.embed.video);
  50.       console.log("Constructed URL (main):", url);
  51.     }
  52.     return url;
  53.   }
  54.  
  55.   function constructVideoUrl(did, videoEmbed){
  56.     if(videoEmbed.mimeType === "video/webm") return `https://bsky.social/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(videoEmbed.ref.$link)}`;
  57.     return `https://video.bsky.app/watch/${encodeURIComponent(did)}/${encodeURIComponent(videoEmbed.ref.$link)}/playlist.m3u8`;
  58.   }
  59.  
  60.  
  61.   function isElementVisible(el){
  62.     if(!el || !(el instanceof Element)) return false;
  63.     const style = getComputedStyle(el);
  64.     if(style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity || "1") === 0) return false;
  65.     const rect = el.getBoundingClientRect();
  66.     if(rect.width === 0 || rect.height === 0) return false;
  67.     const vertOverlap = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
  68.     const horizOverlap = Math.min(rect.right, window.innerWidth) - Math.max(rect.left, 0);
  69.     return vertOverlap > 0 && horizOverlap > 0;
  70.   }
  71.  
  72.   function visibilityScore(el){
  73.     const rect = el.getBoundingClientRect();
  74.     const w = Math.max(0, Math.min(rect.right, window.innerWidth) - Math.max(rect.left, 0));
  75.     const h = Math.max(0, Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0));
  76.     return w * h;
  77.   }
  78.  
  79.   async function extractPost(el, isActivePost = false){
  80.     if(!(el instanceof Element)) return console.error("Invalid post element:", el), { text:"", author:"", url:"", threadData:null };
  81.  
  82.    
  83.     let mainHandle = el.getAttribute("data-testid")?.match(/postThreadItem-by-(.+)/)?.[1] || null;
  84.     if(!mainHandle){
  85.       const profileAnchor = el.querySelector('a[href^="/profile/"]');
  86.       if(profileAnchor){
  87.         try{
  88.           const segs = profileAnchor.getAttribute('href').split('/').filter(Boolean);
  89.           if(segs.length >= 2 && segs[0]==='profile') mainHandle = segs[1];
  90.         }catch(e){}
  91.       }
  92.     }
  93.  
  94.     const authorNode = el.querySelector(".css-146c3p1.r-dnmrzs.r-1udh08x.r-1udbk01.r-3s2u2q.r-1iln25a"),
  95.           authorText = authorNode?.textContent?.trim() || "Unknown",
  96.           textNode = el.querySelector('div[data-word-wrap="1"]') || el.querySelector('[data-testid="postText"]');
  97.  
  98.     let text = textNode ? cleanText(textNode.innerHTML || textNode.textContent) : "",
  99.         mainUrl = null;
  100.  
  101.    
  102.     let postId = null;
  103.     const postAnchors = Array.from(el.querySelectorAll('a[href*="/post/"]'));
  104.  
  105.     if (mainHandle && postAnchors.length) {
  106.      
  107.       const matchByHandle = postAnchors.find(a=>{
  108.         try{
  109.           const u = new URL(a.href, window.location.origin);
  110.           const segs = u.pathname.split('/').filter(Boolean);
  111.           const idx = segs.indexOf('post');
  112.           if(idx !== -1 && segs[idx-1] === mainHandle) return true;
  113.         }catch(e){}
  114.         return false;
  115.       });
  116.       if(matchByHandle){
  117.         try{
  118.           const u = new URL(matchByHandle.href, window.location.origin);
  119.           const segs = u.pathname.split('/').filter(Boolean);
  120.           postId = segs[segs.indexOf('post') + 1];
  121.         }catch(e){}
  122.       }
  123.     }
  124.  
  125.    
  126.     if(!postId && postAnchors.length){
  127.      
  128.       for(const a of postAnchors){
  129.         try{
  130.           const u = new URL(a.href, window.location.origin);
  131.           const segs = u.pathname.split('/').filter(Boolean);
  132.           const idx = segs.indexOf('post');
  133.           if(idx !== -1 && segs.length > idx+1){
  134.            
  135.             const quotedAncestor = a.closest('[role="link"][aria-label*="Post by"]');
  136.             if(quotedAncestor && el.contains(quotedAncestor) && quotedAncestor !== el){
  137.              
  138.               continue;
  139.             }
  140.             postId = segs[idx+1];
  141.             if(!mainHandle && segs[idx-1]) mainHandle = segs[idx-1];
  142.             break;
  143.           }
  144.         }catch(e){}
  145.       }
  146.     }
  147.  
  148.    
  149.     let threadData = null;
  150.     if (!postId && isActivePost && mainHandle) {
  151.       try {
  152.         const currentCandidate = window.location.href.split('/').pop().split('?')[0];
  153.         const apiUri = `at://${mainHandle}/app.bsky.feed.post/${currentCandidate}`;
  154.         const apiUrl = `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${encodeURIComponent(apiUri)}&depth=0`;
  155.         const resp = await fetch(apiUrl);
  156.         if (resp.ok) {
  157.           const json = await resp.json();
  158.           const uri = json?.thread?.post?.uri;
  159.           if (uri) {
  160.             postId = uri.split('/').pop();
  161.             threadData = json;
  162.             console.log("Fetched postId via API fallback:", postId);
  163.           }
  164.         }
  165.       } catch(err){
  166.         console.warn("API fetch for post ID failed:", err);
  167.       }
  168.     }
  169.  
  170.     mainUrl = postId && mainHandle ? `https://bsky.app/profile/${mainHandle}/post/${postId}` : "";
  171.  
  172.    
  173.     if (mainUrl && el.querySelector('video') && !threadData) {
  174.       try {
  175.         const apiUri = `at://${mainHandle}/app.bsky.feed.post/${postId || window.location.href.split('/').pop().split('?')[0]}`;
  176.         const tUrl = `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${encodeURIComponent(apiUri)}&depth=1`;
  177.         const res = await fetch(tUrl);
  178.         threadData = res.ok ? await res.json() : threadData;
  179.         if (threadData) console.log("API data for", mainUrl, ":", threadData);
  180.       } catch (err) {
  181.         console.warn("API fetch failed, skipping thread data:", err);
  182.       }
  183.     }
  184.  
  185.    
  186.     const media = extractMedia(el);
  187.     media.images.slice(0, MAX_IMAGES_PER_POST).forEach(i => text += `\n[img]${i}[/img]`);
  188.     if (threadData) {
  189.       const videoUrl = await getEmbedUrl(threadData);
  190.       if (videoUrl) text += `\n[U][URL]${videoUrl}[/URL][/U]`;
  191.     }
  192.  
  193.     const normalizeUrl = (url) => (url.startsWith('/') ? `https://bsky.app${url}` : url).replace(/\/+$/,"").toLowerCase();
  194.  
  195.     const externalLinks = new Set(Array.from(el.querySelectorAll("a[href]")).map(a=>normalizeUrl(a.href)).filter(u=>u.startsWith("http") && !u.includes("/post/") && !u.includes("/profile/") && !text.includes(u)));
  196.     externalLinks.forEach(u => text += `\n[U][URL]${u}[/URL][/U]`);
  197.  
  198.    
  199.     const quotedContainer = el.querySelector('[role="link"][aria-label*="Post by"]');
  200.     let quotedUrl = null;
  201.     if (quotedContainer && isActivePost) {
  202.       const quotedTextHtml = quotedContainer.querySelector('div[data-word-wrap="1"]')?.innerHTML || "";
  203.       if (quotedTextHtml) {
  204.         const quotedText = cleanText(quotedTextHtml);
  205.         const quotedHrefEl = quotedContainer.querySelector('a[href*="/post/"]');
  206.         let quotedHandleFromHref = null, quotedPostIdFromHref = null;
  207.         if (quotedHrefEl) {
  208.           try {
  209.             const u = new URL(quotedHrefEl.href, window.location.origin);
  210.             const segs = u.pathname.split('/').filter(Boolean);
  211.             const postIdx = segs.indexOf('post');
  212.             if (postIdx !== -1 && segs.length > postIdx + 1) {
  213.               quotedPostIdFromHref = segs[postIdx + 1];
  214.               if (postIdx - 1 >= 0) quotedHandleFromHref = segs[postIdx - 1];
  215.             }
  216.           } catch(e){ /* ignore */ }
  217.         }
  218.         const quotedVisibleHandle = quotedContainer.querySelector(".css-146c3p1.r-dnmrzs.r-1udh08x.r-1udbk01.r-3s2u2q.r-1iln25a")?.textContent?.trim() || "Unknown";
  219.         const finalQuotedHandle = quotedHandleFromHref || quotedVisibleHandle;
  220.  
  221.         text += `\n\n[QUOTED POST]\nšŸ‡ŗ ${finalQuotedHandle}\n${quotedText}`;
  222.  
  223.         let quotedVideoUrl = null;
  224.         if (quotedPostIdFromHref) {
  225.           quotedUrl = `https://bsky.app/profile/${finalQuotedHandle}/post/${quotedPostIdFromHref}`;
  226.         } else {
  227.           if (finalQuotedHandle && finalQuotedHandle !== "Unknown") {
  228.             try {
  229.               const qApi = `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${encodeURIComponent(`at://${finalQuotedHandle}/app.bsky.feed.post/${window.location.href.split('/').pop().split('?')[0]}`)}&depth=0`;
  230.               const r = await fetch(qApi);
  231.               const qJson = r.ok ? await r.json() : null;
  232.               if (qJson?.thread?.post?.uri) {
  233.                 const parts = qJson.thread.post.uri.split('/');
  234.                 const handleFromUri = parts[2];
  235.                 const idFromUri = parts.pop();
  236.                 quotedUrl = `https://bsky.app/profile/${handleFromUri}/post/${idFromUri}`;
  237.                 if (qJson.thread.post.record?.embed?.$type === "app.bsky.embed.video" && qJson.thread.post.author?.did && qJson.thread.post.record.embed.video?.ref?.$link) {
  238.                   quotedVideoUrl = constructVideoUrl(qJson.thread.post.author.did, qJson.thread.post.record.embed.video);
  239.                   console.log("Quoted video URL (API):", quotedVideoUrl);
  240.                 }
  241.               }
  242.             } catch(e){
  243.               console.warn("Quoted post API fetch failed:", e);
  244.             }
  245.           }
  246.         }
  247.  
  248.         if (!quotedVideoUrl && quotedContainer.querySelector('video[poster]')) {
  249.           try {
  250.             const poster = quotedContainer.querySelector('video[poster]').getAttribute('poster') || "";
  251.             if (poster.includes('video.bsky.app/watch/')) {
  252.               const parts = poster.split('/');
  253.               if (parts.length >= 6) {
  254.                 quotedVideoUrl = `https://video.bsky.app/watch/${parts[4]}/${parts[5]}/playlist.m3u8`;
  255.                 console.log("Quoted video URL (poster fallback):", quotedVideoUrl);
  256.               }
  257.             }
  258.           } catch(e){/* ignore */}
  259.         }
  260.  
  261.         if (!quotedVideoUrl && quotedContainer.querySelector('video[src^="blob:"]')) {
  262.           quotedVideoUrl = quotedContainer.querySelector('video[src^="blob:"]').src;
  263.           console.log("Quoted video URL (blob fallback):", quotedVideoUrl);
  264.         }
  265.  
  266.         if (quotedVideoUrl) text += `\n[U][URL]${quotedVideoUrl}[/URL][/U]`;
  267.         const quotedMedia = extractMedia(quotedContainer);
  268.         quotedMedia.images.slice(0, MAX_IMAGES_PER_POST).forEach(i => text += `\n[img]${i}[/img]`);
  269.       }
  270.     }
  271.  
  272.    
  273.     if (threadData?.thread?.post?.record?.embed?.record && isActivePost) {
  274.       try {
  275.         const qRecord = threadData.thread.post.record.embed.record;
  276.         const qText = cleanText(qRecord.value?.text || "");
  277.         if (qText) {
  278.           const qUriParts = qRecord.uri.split('/');
  279.           const qHandle = qUriParts[2];
  280.           const qId = qUriParts[4];
  281.           text += `\n\n[QUOTED POST]\nšŸ‡ŗ ${qHandle}\n${qText}`;
  282.           if (qRecord.value?.embed?.$type === "app.bsky.embed.video") {
  283.             const quotedVideoUrl = constructVideoUrl(qHandle, qRecord.value.embed.video);
  284.             text += `\n[U][URL]${quotedVideoUrl}[/URL][/U]`;
  285.             console.log("Quoted video URL from embed:", quotedVideoUrl);
  286.           }
  287.           quotedUrl = `https://bsky.app/profile/${qHandle}/post/${qId}`;
  288.         }
  289.       } catch(e){ console.warn("Error handling embedded quoted record:", e) }
  290.     }
  291.  
  292.     return { text, author: mainHandle ? `šŸ‡ŗ ${mainHandle}` : "", url: mainUrl, threadData, quotedUrl }
  293.   }
  294.  
  295.   async function extractPosts(count){
  296.     const allPosts = Array.from(document.querySelectorAll('[data-testid^="postThreadItem-by-"], [data-testid="postThreadItem"]'));
  297.     if (!allPosts.length) return void showToast("No posts found on this page.");
  298.  
  299.     let visiblePosts = allPosts.filter(isElementVisible);
  300.     if (!visiblePosts.length) visiblePosts = allPosts;
  301.  
  302.     const currentId = window.location.href.split('/').pop().split('?')[0];
  303.     let activePost = visiblePosts.find(p => p.querySelector('a[href*="/post/'+currentId+'"]'));
  304.     if (!activePost) {
  305.       let best = null, bestScore = -1;
  306.       for (const p of visiblePosts) {
  307.         const score = visibilityScore(p);
  308.         if (score > bestScore) { bestScore = score; best = p; }
  309.       }
  310.       activePost = best || visiblePosts[0] || allPosts[0];
  311.     }
  312.  
  313.     const visibleIndex = visiblePosts.indexOf(activePost);
  314.     const allIndex = allPosts.indexOf(activePost);
  315.  
  316.     const { text: activeText, author: activeAuthor, url: activeUrl, threadData: activeThreadData, quotedUrl: activeQuotedUrl } = await extractPost(activePost, true);
  317.  
  318.     const r = activePost.querySelector(".css-146c3p1.r-dnmrzs.r-1udh08x.r-1udbk01.r-3s2u2q.r-1iln25a"),
  319.           n = r?.textContent?.trim() || document.querySelector('[data-testid="profileHandle"]')?.textContent?.trim() || "";
  320.     if (!n) return void showToast("Could not determine main username.");
  321.  
  322.     let parentUrl = null, parentText = null, parentAuthor = null, quotedPostUrl = activeQuotedUrl;
  323.     let parentFromDomElement = null;
  324.  
  325.     if (visibleIndex > 0) parentFromDomElement = visiblePosts[visibleIndex - 1];
  326.     if (!parentFromDomElement && allIndex > 0) parentFromDomElement = allPosts[allIndex - 1];
  327.  
  328.     if (!activeThreadData?.thread?.post?.reply?.parent && parentFromDomElement) {
  329.       const parentExtract = await extractPost(parentFromDomElement, false);
  330.       if (parentExtract.author === activeAuthor || parentFromDomElement.querySelector(".css-146c3p1.r-dnmrzs.r-1udh08x.r-1udbk01.r-3s2u2q.r-1iln25a")?.textContent?.trim() === n) {
  331.         parentUrl = parentExtract.url; parentText = parentExtract.text; parentAuthor = parentExtract.author;
  332.       }
  333.     }
  334.  
  335.     if (activeThreadData?.thread?.post?.reply?.parent) {
  336.       const parentUri = activeThreadData.thread.post.reply.parent.uri;
  337.       const parentAuthorUri = parentUri.split('/')[2];
  338.       const parentId = parentUri.split('/')[4];
  339.       parentUrl = `https://bsky.app/profile/${parentAuthorUri}/post/${parentId}`;
  340.       parentText = cleanText(activeThreadData.thread.post.reply.parent.value?.text || "");
  341.       parentAuthor = `šŸ‡ŗ ${parentAuthorUri}`;
  342.       const parentQuotedPost = activeThreadData.thread.post.reply.parent.embed?.record;
  343.       if (parentQuotedPost) {
  344.         const qText = cleanText(parentQuotedPost.value?.text || "");
  345.         if (qText) parentText += `\n\n[QUOTED POST]\nšŸ‡ŗ ${parentQuotedPost.uri.split('/')[2]}\n${qText}`;
  346.         quotedPostUrl = `https://bsky.app/profile/${parentQuotedPost.uri.split('/')[2]}/post/${parentQuotedPost.uri.split('/')[4]}`;
  347.       }
  348.     }
  349.  
  350.    
  351.     const postsToProcessElements = [];
  352.     const addedEls = new Set();
  353.  
  354.     function pushEl(el){
  355.       if(!el || addedEls.has(el)) return false;
  356.       postsToProcessElements.push(el);
  357.       addedEls.add(el);
  358.       return true;
  359.     }
  360.  
  361.    
  362.     if (parentFromDomElement && parentText) pushEl(parentFromDomElement);
  363.     pushEl(activePost);
  364.  
  365.     let needed = Math.max(0, count - postsToProcessElements.length);
  366.  
  367.    
  368.     let src = visiblePosts;
  369.     let startIdx = visiblePosts.indexOf(activePost) >= 0 ? visiblePosts.indexOf(activePost) + 1 : -1;
  370.     if (startIdx >= 0) {
  371.       for (let i = startIdx; i < src.length && needed > 0; i++){
  372.         if(pushEl(src[i])) needed--;
  373.       }
  374.     }
  375.  
  376.    
  377.     if (needed > 0) {
  378.       const srcAll = allPosts;
  379.       let startAll = srcAll.indexOf(activePost) >= 0 ? srcAll.indexOf(activePost) + 1 : 0;
  380.       for (let i = startAll; i < srcAll.length && needed > 0; i++){
  381.         if(pushEl(srcAll[i])) needed--;
  382.       }
  383.     }
  384.  
  385.     if (needed > 0) {
  386.       for (const p of allPosts) {
  387.         if(needed <= 0) break;
  388.         if(pushEl(p)) needed--;
  389.       }
  390.     }
  391.  
  392.     const s = parentText ? [{ author: parentAuthor, text: parentText }, { author: activeAuthor, text: activeText }] : [{ author: activeAuthor, text: activeText }];
  393.     let aImgsCount = (parentText ? (parentText.match(/\[img\]/g)||[]).length : 0) + ((activeText.match(/\[img\]/g)||[]).length || 0);
  394.     let generatedCount = postsToProcessElements.length;
  395.     let mainPostUrls = new Set([activeUrl]);
  396.     if (parentAuthor === activeAuthor && parentUrl) mainPostUrls.add(parentUrl);
  397.     if (parentUrl && parentUrl !== activeUrl) mainPostUrls.add(parentUrl);
  398.  
  399.     const startProcessIndex = parentText ? 2 : 1;
  400.     const outputs = [];
  401.  
  402.     for (let idx = startProcessIndex; idx < postsToProcessElements.length; idx++){
  403.       const postEl = postsToProcessElements[idx];
  404.       const { text: pText, author: pAuthor, url: pUrl, threadData: pThread } = await extractPost(postEl, false);
  405.       if (!pText && !pUrl && !extractMedia(postEl).images.length) {
  406.         generatedCount--;
  407.         continue;
  408.       }
  409.  
  410.       if (pUrl && pAuthor === `šŸ‡ŗ ${n}` && pUrl !== activeUrl && pUrl !== parentUrl) mainPostUrls.add(pUrl);
  411.       s.push({ author: pAuthor, text: pText });
  412.       aImgsCount += (pText.match(/\[img\]/g) || []).length;
  413.  
  414.       if (aImgsCount >= 20 || idx === postsToProcessElements.length - 1 || s.length >= count) {
  415.         const hasThreads = (parentUrl && parentUrl !== activeUrl) || [...mainPostUrls].some(u=>u !== activeUrl) || (quotedPostUrl && quotedPostUrl !== activeUrl);
  416.         outputs.push(`${activeUrl || replaceDomain(window.location.href.split("?")[0])}${hasThreads ? `\n[SPOILER="Threads Continued"]${parentUrl && parentUrl !== activeUrl ? `\n${parentUrl}` : ""}${[...mainPostUrls].filter(u=>u!==activeUrl).length > 0 ? `\n${[...mainPostUrls].filter(u=>u!==activeUrl).join("\n")}` : ""}${quotedPostUrl && quotedPostUrl !== activeUrl ? `\n${quotedPostUrl}` : ""}\n[/SPOILER]` : ""}\n[SPOILER="full text & large images"]\n\n${s.map((e,t)=>`${t+1}/${s.length}\n${e.author}\n${e.text}`).join("\n\n")}\n\n[COLOR=rgb(184,49,47)][B][SIZE=5]To post tweets in this format, more info here: [URL]https://www.thecoli.com/threads/tips-and-tricks-for-posting-the-coli-megathread.984734/post-52211196[/URL][/SIZE][/B][/COLOR]\n[/SPOILER]`);
  417.         if (parentText) {
  418.           s.length = 0;
  419.           s.push({ author: parentAuthor, text: parentText }, { author: activeAuthor, text: activeText });
  420.         } else {
  421.           s.length = 0;
  422.           s.push({ author: activeAuthor, text: activeText });
  423.         }
  424.         aImgsCount = (parentText ? (parentText.match(/\[img\]/g)||[]).length : 0) + ((activeText.match(/\[img\]/g)||[]).length || 0);
  425.       }
  426.     }
  427.  
  428.     if (s.length && !outputs.length) {
  429.       const hasThreads = (parentUrl && parentUrl !== activeUrl) || [...mainPostUrls].some(u=>u !== activeUrl) || (quotedPostUrl && quotedPostUrl !== activeUrl);
  430.       outputs.push(`${activeUrl || replaceDomain(window.location.href.split("?")[0])}${hasThreads ? `\n[SPOILER="Threads Continued"]${parentUrl && parentUrl !== activeUrl ? `\n${parentUrl}` : ""}${[...mainPostUrls].filter(u=>u!==activeUrl).length > 0 ? `\n${[...mainPostUrls].filter(u=>u!==activeUrl).join("\n")}` : ""}${quotedPostUrl && quotedPostUrl !== activeUrl ? `\n${quotedPostUrl}` : ""}\n[/SPOILER]` : ""}\n[SPOILER="full text & large images"]\n\n${s.map((e,t)=>`${t+1}/${s.length}\n${e.author}\n${e.text}`).join("\n\n")}\n\n[COLOR=rgb(184,49,47)][B][SIZE=5]To post tweets in this format, more info here: [URL]https://www.thecoli.com/threads/tips-and-tricks-for-posting-the-coli-megathread.984734/post-52211196[/URL][/SIZE][/B][/COLOR]\n[/SPOILER]`);
  431.     }
  432.  
  433.     const finalOutput = outputs.join("\n\n[threads continued]\n\n");
  434.     const ta = document.createElement("textarea");
  435.     ta.value = finalOutput;
  436.     document.body.appendChild(ta);
  437.     ta.select();
  438.     document.execCommand("copy");
  439.     document.body.removeChild(ta);
  440.     showToast(`Copied: ${Math.max(1, postsToProcessElements.length)} posts`);
  441.   }
  442.  
  443.   createPostCountPrompt(extractPosts);
  444. })();
  445.  
Advertisement
Add Comment
Please, Sign In to add comment