Procházet zdrojové kódy

optimize viewing of long threads

Mike L před 3 roky
rodič
revize
de6873baae
2 změnil soubory, kde provedl 44 přidání a 7 odebrání
  1. 21 3
      dist/webshot.js
  2. 23 4
      src/webshot.ts

+ 21 - 3
dist/webshot.js

@@ -52,6 +52,9 @@ class Webshot extends CallableInstance {
         this.extendEntity = (media) => {
             logger.info('not working on a tweet');
         };
+        this.truncateLongThread = (atId) => {
+            logger.info('not working on a tweet');
+        };
         this.renderWebshot = (url, height, webshotDelay, ...morePostProcessings) => {
             temp.track();
             const jpeg = (data) => data.pipe(sharp()).jpeg({ quality: 90, trellisQuantisation: true });
@@ -125,15 +128,18 @@ class Webshot extends CallableInstance {
                                 const selector = '[data-testid="tweet"] :nth-child(2)>:first-child a';
                                 const getProfileUrl = () => (div.querySelector(selector) || { href: '' }).href;
                                 const ownerProfileUrl = getProfileUrl();
+                                const bottom = div;
                                 while (div = div.previousElementSibling) {
-                                    if (getProfileUrl() !== ownerProfileUrl)
+                                    if (getProfileUrl() !== ownerProfileUrl || div === bottom.previousElementSibling)
                                         continue;
-                                    return document.documentElement.scrollTop = window.scrollY + div.getBoundingClientRect().top;
+                                    const top = document.documentElement.scrollTop = window.scrollY + div.getBoundingClientRect().top;
+                                    if (top > 10)
+                                        return div.querySelector('article a[aria-label]').href.replace(/.*\/status\//, '');
                                 }
                             }
                             catch (_a) { }
                             document.documentElement.scrollTop = 0;
-                        }).then(() => handle);
+                        }).then(this.truncateLongThread).then(() => handle);
                     })
                         .then(handle => handle.evaluate(div => {
                         const cardImg = div.querySelector('div[data-testid^="card.layout"][data-testid$=".media"] img');
@@ -273,6 +279,7 @@ class Webshot extends CallableInstance {
             });
             const originTwi = twi.retweeted_status || twi;
             let messageChain = '';
+            let truncatedAt;
             let author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
             if (twi.retweeted_status)
                 author += `RT @${twi.retweeted_status.user.screen_name}: `;
@@ -300,6 +307,12 @@ class Webshot extends CallableInstance {
                             cardImg,
                         ] });
                 };
+                this.truncateLongThread = (atId) => {
+                    if (!atId)
+                        return;
+                    logger.info(`thread too long, truncating at tweet ${atId}...`);
+                    truncatedAt = atId;
+                };
                 promise = promise.then(() => this.renderWebshot(url, 1920, webshotDelay))
                     .then(fileurl => {
                     if (fileurl)
@@ -347,6 +360,11 @@ class Webshot extends CallableInstance {
                     });
                 }
             }
+            promise = promise.then(() => {
+                if (truncatedAt) {
+                    messageChain += `\n回复此命令查看对话串中更早的推文:\n/twitter_view ${truncatedAt}`;
+                }
+            });
             if (originTwi.is_quote_status) {
                 promise = promise.then(() => {
                     var _a, _b;

+ 23 - 4
src/webshot.ts

@@ -74,6 +74,10 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
     logger.info('not working on a tweet');
   };
 
+  private truncateLongThread = (atId: string) => {
+    logger.info('not working on a tweet');
+  };
+
   private renderWebshot = (
     url: string, height: number, webshotDelay: number,
     ...morePostProcessings: ((page: puppeteer.Page) => Promise<any>)[]
@@ -149,7 +153,7 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
               logger.warn(`navigation timed out at ${getTimerTime()} seconds`);
               return null;
             })
-            // scroll to 2nd last tweet by owner in thread, if any, or top of thread
+            // scroll back at least 2 tweets revealing 2nd last tweet by owner in thread, or top of thread, if any
             .then((handle: puppeteer.ElementHandle<HTMLDivElement>) => {
               if (handle === null) throw new puppeteer.errors.TimeoutError();
               return handle.evaluate(div => {
@@ -157,14 +161,17 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
                   const selector = '[data-testid="tweet"] :nth-child(2)>:first-child a';
                   const getProfileUrl = () => (div.querySelector<HTMLAnchorElement>(selector) || {href: ''}).href;
                   const ownerProfileUrl = getProfileUrl();
+                  const bottom = div;
                   // eslint-disable-next-line no-cond-assign
                   while (div = div.previousElementSibling as HTMLDivElement) {
-                    if (getProfileUrl() !== ownerProfileUrl) continue;
-                    return document.documentElement.scrollTop = window.scrollY + div.getBoundingClientRect().top;
+                    if (getProfileUrl() !== ownerProfileUrl || div === bottom.previousElementSibling) continue;
+                    const top = document.documentElement.scrollTop = window.scrollY + div.getBoundingClientRect().top;
+                    if (top > 10)
+                      return div.querySelector<HTMLAnchorElement>('article a[aria-label]').href.replace(/.*\/status\//, '');
                   }
                 } catch {/* handle errors like none-found cases */}
                 document.documentElement.scrollTop = 0;
-              }).then(() => handle);
+              }).then(this.truncateLongThread).then(() => handle);
             })
             // scrape card image from main tweet
             .then(handle => handle.evaluate(div => {
@@ -304,6 +311,7 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
       });
       const originTwi = twi.retweeted_status || twi;
       let messageChain = '';
+      let truncatedAt: string;
 
       // text processing
       let author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
@@ -337,6 +345,11 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
             ],
           };
         };
+        this.truncateLongThread = (atId: string) => {
+          if (!atId) return;
+          logger.info(`thread too long, truncating at tweet ${atId}...`);
+          truncatedAt = atId;
+        };
         promise = promise.then(() => this.renderWebshot(url, 1920, webshotDelay))
           .then(fileurl => {
             if (fileurl) return Message.Image(fileurl);
@@ -384,6 +397,12 @@ class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Prom
           });
         }
       }
+      // refer to earlier tweets if thread is truncated
+      promise = promise.then(() => {
+        if (truncatedAt) {
+          messageChain += `\n回复此命令查看对话串中更早的推文:\n/twitter_view ${truncatedAt}`;
+        }
+      });
       // refer to quoted tweet, if any
       if (originTwi.is_quote_status) {
         promise = promise.then(() => {