View difference between Paste ID: CtRm81Le and 4wBmRjdh
SHOW: | | - or go back to the newest paste.
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-
        mainHandle = el.getAttribute("data-testid")?.match(/postThreadItem-by-(.+)/)?.[1] || null,
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-
    const hrefEl = el.querySelector('a[href*="/post/"]');
92+
93-
    if (hrefEl) {
93+
94-
      const href = hrefEl.href;
94+
95
          authorText = authorNode?.textContent?.trim() || "Unknown",
96-
        const parts = new URL(href);
96+
97-
        const segs = parts.pathname.split('/').filter(Boolean);
97+
98-
        const postIndex = segs.indexOf('post');
98+
99-
        if (postIndex !== -1 && segs.length > postIndex + 1) {
99+
100-
          postId = segs[postIndex + 1];
100+
101-
          if(!mainHandle && segs[postIndex - 1]) mainHandle = segs[postIndex - 1];
101+
102
    let postId = null;
103-
      } catch(err){ /* ignore */ }
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-
            const u = new URL(quotedHrefEl.href);
165+
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-
    if (parentFromDomElement && parentText) pushEl(parentFromDomElement); 
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-
    let generatedCount = postsToProcessElements.length; 
356+
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-
        
372+
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-
        
385+
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