From ac34f20563f0c4ccc6e065e6e942c4c88fcd750d Mon Sep 17 00:00:00 2001 From: Johan Flensmark Date: Wed, 19 Apr 2023 08:57:54 +0200 Subject: [PATCH 1/2] Implement instagram story behavior --- dist/behaviors.js | 2 +- src/site/instagram.ts | 452 ++++++++++++++++++++++++++---------------- 2 files changed, 278 insertions(+), 176 deletions(-) diff --git a/dist/behaviors.js b/dist/behaviors.js index 83862fd..f4058ad 100644 --- a/dist/behaviors.js +++ b/dist/behaviors.js @@ -1 +1 @@ -/*! behaviors.js is part of Webrecorder project. Copyright (C) 2021-2023, Webrecorder Software. Licensed under the Affero General Public License v3. */(()=>{var t={894:(t,e,i)=>{"use strict";i.r(e),i.d(e,{AutoFetcher:()=>l});var o=i(841),s=i(721);const n=/\s*(\S*\s+[\d.]+[wx]),|(?:\s*,(?:\s+|(?=https?:)))/,a=/(url\s*\(\s*[\\"']*)([^)'"]+)([\\"']*\s*\))/gi,r=/(@import\s*[\\"']*)([^)'";]+)([\\"']*\s*;?)/gi;class l extends o.BackgroundBehavior{constructor(t=!1){super(),this.urlSet=new Set,this.urlQueue=[],this.numPending=0,this.numDone=0,this._donePromise=new Promise((t=>this._markDone=t)),t&&this.start()}get numFetching(){return this.numDone+this.numPending+this.urlQueue.length}async start(){await(0,s.awaitLoad)(),this.run(),this.initObserver(),await(0,s.sleep)(500),this.urlQueue.length||this.numPending||this._markDone(null)}done(){return this._donePromise}async run(){this.extractSrcSrcSetAll(document),this.extractStyleSheets()}isValidUrl(t){return t&&(t.startsWith("http:")||t.startsWith("https:"))}queueUrl(t){try{t=new URL(t,document.baseURI).href}catch(t){return!1}return!!this.isValidUrl(t)&&(!this.urlSet.has(t)&&(this.urlSet.add(t),this.doFetch(t),!0))}async doFetchStream(t){try{const e=await fetch(t,{credentials:"include",referrerPolicy:"origin-when-cross-origin"});this.debug(`Autofetch: started ${t}`);const i=e.body.getReader();let o=null;for(;(o=await i.read())&&!o.done;);return this.debug(`Autofetch: finished ${t}`),!0}catch(t){return this.debug(t),!1}}async doFetchNonCors(t){try{const e=new AbortController;await fetch(t,{mode:"no-cors",credentials:"include",referrerPolicy:"origin-when-cross-origin",abort:e}),e.abort(),this.debug(`Autofetch: started non-cors stream for ${t}`)}catch(e){this.debug(`Autofetch: failed non-cors for ${t}`)}}async doFetch(t){if(this.urlQueue.push(t),this.numPending<=6){for(;this.urlQueue.length>0;){const t=this.urlQueue.shift();this.numPending++,!1||await this.doFetchNonCors(t),this.numPending--,this.numDone++}this.numPending||this._markDone(null)}}initObserver(){this.mutationObserver=new MutationObserver((t=>this.observeChange(t))),this.mutationObserver.observe(document.documentElement,{characterData:!1,characterDataOldValue:!1,attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0,attributeFilter:["srcset","loading"]})}processChangedNode(t){switch(t.nodeType){case Node.ATTRIBUTE_NODE:if("srcset"===t.nodeName&&this.extractSrcSetAttr(t.nodeValue),"loading"===t.nodeName&&"lazy"===t.nodeValue){const e=t.parentNode;"IMG"===e.tagName&&e.setAttribute("loading","eager")}break;case Node.TEXT_NODE:t.parentNode&&"STYLE"===t.parentNode.tagName&&this.extractStyleText(t.nodeValue);break;case Node.ELEMENT_NODE:t.sheet&&this.extractStyleSheet(t.sheet),this.extractSrcSrcSet(t),setTimeout((()=>this.extractSrcSrcSetAll(t)),1e3)}}observeChange(t){for(const e of t)if(this.processChangedNode(e.target),"childList"===e.type)for(const t of e.addedNodes)this.processChangedNode(t)}extractSrcSrcSetAll(t){const e=t.querySelectorAll("img[srcset], img[data-srcset], img[data-src], noscript > img[src], img[loading='lazy'], video[srcset], video[data-srcset], video[data-src], audio[srcset], audio[data-srcset], audio[data-src], picture > source[srcset], picture > source[data-srcset], picture > source[data-src], video > source[srcset], video > source[data-srcset], video > source[data-src], audio > source[srcset], audio > source[data-srcset], audio > source[data-src]");for(const t of e)this.extractSrcSrcSet(t)}extractSrcSrcSet(t){if(!t||t.nodeType!==Node.ELEMENT_NODE)return void console.warn("No elem to extract from");const e=t.getAttribute("data-src");e&&this.queueUrl(e),"lazy"===t.getAttribute("loading")&&t.setAttribute("loading","eager");const i=t.getAttribute("srcset");i&&this.extractSrcSetAttr(i);const o=t.getAttribute("data-srcset");o&&this.extractSrcSetAttr(o);const s=t.getAttribute("src");s&&"NOSCRIPT"===t.parentElement.tagName&&this.queueUrl(s)}extractSrcSetAttr(t){for(const e of t.split(n))if(e){const t=e.trim().split(" ");this.queueUrl(t[0])}}extractStyleSheets(t){t=t||document;for(const e of t.styleSheets)this.extractStyleSheet(e)}extractStyleSheet(t){let e;try{e=t.cssRules||t.rules}catch(t){return void this.debug("Can't access stylesheet")}for(const t of e)t.type===CSSRule.MEDIA_RULE&&this.extractStyleText(t.cssText)}extractStyleText(t){const e=(t,e,i,o)=>(this.queueUrl(i),e+i+o);t.replace(a,e).replace(r,e)}}},376:(t,e,i)=>{"use strict";i.r(e),i.d(e,{Autoplay:()=>n});var o=i(841),s=i(721);class n extends o.BackgroundBehavior{constructor(t){super(),this.mediaSet=new Set,this.autofetcher=t,this.numPlaying=0,this.promises=[],this._initDone=()=>null,this.promises.push(new Promise((t=>this._initDone=t))),this.start()}async start(){await(0,s.awaitLoad)(),this.pollAudioVideo(),this._initDone()}async pollAudioVideo(){for(;;){for(const[,t]of document.querySelectorAll("video, audio, picture").entries())t.__bx_autoplay_found||(await this.loadMedia(t),t.__bx_autoplay_found=!0);await(0,s.sleep)(500)}}fetchSrcUrl(t){const e=t.src||t.currentSrc;return!!e&&(!(!e.startsWith("http:")&&!e.startsWith("https:"))&&(this.mediaSet.has(e)||(this.debug("fetch media source URL: "+e),this.mediaSet.add(e),this.autofetcher.queueUrl(e)),!0))}processFetchableUrl(t){let e=this.fetchSrcUrl(t);const i=t.querySelectorAll("source");for(const t of i){const i=this.fetchSrcUrl(t);e=e||i}return e}async loadMedia(t){this.debug("processing media element: "+t.outerHTML);const e=this.processFetchableUrl(t);t.play?e?t.paused||(t.pause(),this.debug("media URL found, pausing playback")):t.paused?(this.debug("no src url found, attempting to click or play: "+t.outerHTML),this.attemptMediaPlay(t).then((async e=>{let i=!0;for(e&&e.then((()=>i=!1));i;)this.processFetchableUrl(t)&&(i=!1),this.debug("Waiting for fixed URL or media to finish: "+t.currentSrc),await(0,s.sleep)(1e3)}))):t.currentSrc&&this.debug("media playing from non-URL source: "+t.currentSrc):this.debug("media not playable, skipping")}async attemptMediaPlay(t){let e;const i=new Promise((t=>{e=t}));let o;const n=new Promise((t=>{o=t}));n.then((()=>this.promises.push(i))),t.addEventListener("loadstart",(()=>{this.debug("loadstart"),o(!0)})),t.addEventListener("playing",(()=>{this.debug("playing"),o(!0)})),t.addEventListener("loadeddata",(()=>this.debug("loadeddata"))),t.addEventListener("ended",(()=>{this.debug("ended"),e()})),t.addEventListener("pause",(()=>{this.debug("pause"),e()})),t.addEventListener("abort",(()=>{this.debug("abort"),e()})),t.addEventListener("error",(()=>{this.debug("error"),e()})),t.addEventListener("stalled",(()=>{this.debug("stalled"),e()})),t.addEventListener("suspend",(()=>{this.debug("suspend"),e()})),t.muted=!0;if(!t.closest("a")){t.click();if(await Promise.race([n,(0,s.sleep)(1e3)]))return i}return t.play(),i}done(){return Promise.allSettled(this.promises)}}},234:(t,e,i)=>{"use strict";i.r(e),i.d(e,{AutoScroll:()=>n});var o=i(841),s=i(721);class n extends o.Behavior{constructor(t){super(),this.autoFetcher=t,this.showMoreQuery="//*[contains(text(), 'show more') or contains(text(), 'Show more')]",this.state={segments:1},this.lastScrollPos=-1,this.samePosCount=0}currScrollPos(){return Math.round(self.scrollY+self.innerHeight)}canScrollMore(){const t=self.document.scrollingElement||self.document.body;return this.currScrollPos()n&&(this.state.segments++,n=a),e||i||(e=(0,s.xpathNode)(this.showMoreQuery)),e&&(0,s.isInViewport)(e)&&(yield this.getState("Clicking 'Show More', awaiting more content"),e.click(),await(0,s.sleep)(s.waitUnit),await Promise.race([(0,s.waitUntil)((()=>self.document.scrollingElement.scrollHeight>a),500),(0,s.sleep)(3e4)]),self.document.scrollingElement.scrollHeight===a&&(i=!0),e=null),self.scrollBy(o),await(0,s.sleep)(75),1===this.state.segments)yield this.getState(`Scrolling down by ${o.top} pixels every 0.075 seconds`),t=2;else{const e=t/(this.state.segments-1);this.debug(`Waiting up to ${e} seconds for more scroll segments`);const i=Date.now();await Promise.race([(0,s.waitUntil)((()=>this.canScrollMore()),75),(0,s.sleep)(e)]),t+=2*(Date.now()-i)}const r=this.currScrollPos();if(r===this.lastScrollPos){if(++this.samePosCount>=2)break}else this.samePosCount=0;this.lastScrollPos=r}}async*scrollUp(){const t={top:-Math.min(.1*self.document.scrollingElement.clientHeight,30),left:0,behavior:"auto"};let e=self.document.scrollingElement.scrollHeight;for(;self.scrollY>0;){const i=self.document.scrollingElement.scrollHeight;i>e&&(this.state.segments++,e=i),self.scrollBy(t),await(0,s.sleep)(75),1===this.state.segments?yield this.getState(`Scrolling up by ${t.top} pixels every 0.075 seconds`):await Promise.race([(0,s.waitUntil)((()=>self.scrollY>0),75),(0,s.sleep)(2e3*(this.state.segments-1))])}}}n.id="Autoscroll"},607:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BehaviorManager:()=>c});var o=i(894),s=i(376),n=i(234),a=i(721),r=i(841),l=i(954);class c{constructor(){this.behaviors=[],this.loadedBehaviors=l.default.reduce(((t,e)=>(t[e.id]=e,t)),{}),this.mainBehavior=null,this.inited=!1,this.started=!1,(0,a.behaviorLog)("Loaded behaviors for: "+self.location.href)}init(t){if(this.inited)return;if(this.inited=!0,!self.window)return;if(this.timeout=t.timeout,void 0!==t.log){let e=t.log;"string"==typeof e&&(e=self[e]),"function"==typeof e?(0,a._setLogFunc)(e):!1===e&&(0,a._setLogFunc)(null)}this.autofetch=new o.AutoFetcher(!!t.autofetch),t.autofetch&&((0,a.behaviorLog)("Enable AutoFetcher"),this.behaviors.push(this.autofetch)),t.autoplay&&((0,a.behaviorLog)("Enable Autoplay"),this.behaviors.push(new s.Autoplay(this.autofetch)));let e=!1;if(self.window.top===self.window){if(t.siteSpecific)for(const i in this.loadedBehaviors){const o=this.loadedBehaviors[i];if(o.isMatch()){(0,a.behaviorLog)("Starting Site-Specific Behavior: "+i),this.mainBehaviorClass=o;const s="object"==typeof t.siteSpecific&&t.siteSpecific[i]||{};this.mainBehavior=new r.BehaviorRunner(o,s),e=!0;break}}return!e&&t.autoscroll&&((0,a.behaviorLog)("Starting Autoscroll"),this.mainBehaviorClass=n.AutoScroll,this.mainBehavior=new n.AutoScroll(this.autofetch)),this.mainBehavior?(this.behaviors.push(this.mainBehavior),this.mainBehavior.name):""}}load(t){this.loadedBehaviors[t.name]=t}async resolve(t){const e=await i(75)(`${t}`);if(Array.isArray(e))for(const t of e)this.load(t);else this.load(e)}async run(t){if(console.log("DEBUG: Running Behaviors 2"),this.started)return void this.unpause();this.init(t),await(0,a.awaitLoad)(),this.mainBehavior&&this.mainBehavior.start(),this.started=!0;let e=Promise.allSettled(this.behaviors.map((t=>t.done())));this.timeout?((0,a.behaviorLog)(`Waiting for behaviors to finish or ${this.timeout}ms timeout`),await Promise.race([e,(0,a.sleep)(this.timeout)])):((0,a.behaviorLog)("Waiting for behaviors to finish"),await e),(0,a.behaviorLog)("All Behaviors Done for "+self.location.href),this.mainBehavior&&this.mainBehaviorClass.cleanup&&this.mainBehavior.cleanup()}async runOne(t,e={}){const i=l.default.find((e=>e.name===t));if(void 0===i)return void console.error(`No behavior of name ${t} found`);const o=new r.BehaviorRunner(i,e);o.start(),console.log(`Running behavior: ${t}`),await o.done(),console.log(`Behavior ${t} completed`)}pause(){(0,a.behaviorLog)("Pausing Main Behavior"+this.mainBehaviorClass.name),this.mainBehavior&&this.mainBehavior.pause()}unpause(){this.mainBehavior&&this.mainBehavior.unpause()}doAsyncFetch(t){return(0,a.behaviorLog)("Queueing Async Fetch Url: "+t),this.autofetch.queueUrl(t)}}(0,a._setBehaviorManager)(c),(0,a.installBehaviors)(self)},841:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BackgroundBehavior:()=>s,Behavior:()=>n,BehaviorRunner:()=>a});var o=i(721);class s{debug(t){(0,o.behaviorLog)(t,"debug")}log(t){(0,o.behaviorLog)(t,"info")}}class n extends s{constructor(){super(),this._running=null,this.paused=null,this._unpause=null,this.state={},this.scrollOpts={behavior:"smooth",block:"center",inline:"center"}}start(){this._running=this.run()}done(){return this._running?this._running:Promise.resolve()}async run(){try{for await(const t of this)this.log(t),this.paused&&await this.paused;this.log(this.getState("done!"))}catch(t){this.log(this.getState(t))}}pause(){this.paused||(this.paused=new Promise((t=>{this._unpause=t})))}unpause(){this._unpause&&(this._unpause(),this.paused=null,this._unpause=null)}getState(t,e){return e&&(void 0===this.state[e]?this.state[e]=1:this.state[e]++),{state:this.state,msg:t}}cleanup(){}static load(){self.__bx_behaviors?self.__bx_behaviors.load(this):console.warn(`Could not load ${this.name} behavior: window.__bx_behaviors is not initialized`)}async*[Symbol.asyncIterator](){yield}}class a extends s{constructor(t,e={}){if("function"!=typeof t.isMatch||"string"!=typeof t.name)throw Error("Invalid behavior found: missing `isMatch`, `init`, or `name` static methods");if(super(),this.inst=new t,"function"!=typeof this.inst.run||"AsyncGeneratorFunction"!==this.inst.run.constructor.name)throw Error("Invalid behavior found: missing `async run*` instance method");const i=t.init()||{};this.ctx={Lib:o,state:i.state||{},opts:{...i.opts||{},...e}},this._running=null,this.paused=null,this._unpause=null}start(){this._running=this.run()}done(){return this._running?this._running:Promise.resolve()}async run(){try{for await(const t of this.inst.run(this.ctx))this.log(t),this.paused&&await this.paused;this.log((0,o.getState)(this.ctx,"done!"))}catch(t){this.log((0,o.getState)(this.ctx,t))}}pause(){this.paused||(this.paused=new Promise((t=>{this._unpause=t})))}unpause(){this._unpause&&(this._unpause(),this.paused=null,this._unpause=null)}cleanup(){}static load(){self.__bx_behaviors?self.__bx_behaviors.load(this):console.warn(`Could not load ${this.name} behavior: window.__bx_behaviors is not initialized`)}}},721:(t,e,i)=>{"use strict";i.r(e),i.d(e,{HistoryState:()=>b,RestoreState:()=>v,_setBehaviorManager:()=>p,_setLogFunc:()=>f,awaitLoad:()=>h,behaviorLog:()=>w,getState:()=>T,installBehaviors:()=>g,isInViewport:()=>E,iterChildElem:()=>L,iterChildMatches:()=>P,openWindow:()=>m,scrollAndClick:()=>a,scrollIntoView:()=>_,scrollToOffset:()=>k,sleep:()=>l,waitUnit:()=>r,waitUntil:()=>c,waitUntilNode:()=>d,xpathNode:()=>y,xpathNodes:()=>S,xpathString:()=>x});let o=console.log,s=null;const n={behavior:"smooth",block:"center",inline:"center"};async function a(t,e=500,i=n){t.scrollIntoView(i),await l(e),t.click()}const r=200;function l(t){return new Promise((e=>setTimeout(e,t)))}async function c(t,e=r){for(;!t();)await l(e)}async function d(t,e=document,i=null,o=1e3,s=r){let n=null,a=!1;const l=c((()=>(n=y(t,e),a||n!==i&&null!==n)),s),d=new Promise((t=>setTimeout((()=>{a=!0,t("TIMEOUT")}),o)));return await Promise.race([l,d]),n}function h(){return new Promise((t=>{"complete"===document.readyState?t(null):window.addEventListener("load",t)}))}function u(t,e){try{t(e)}catch(i){t(JSON.stringify(e))}}function w(t,e="debug"){o&&u(o,{data:t,type:e})}async function m(t){if(self.__bx_open){const e=new Promise((t=>self.__bx_openResolve=t));u(self.__bx_open,{url:t});let i=null;try{if(i=await e,i)return i}catch(t){console.warn(t)}finally{delete self.__bx_openResolve}}return window.open(t)}function f(t){o=t}function p(t){s=t}function g(t){t.__bx_behaviors=new s}class v{constructor(t,e){this.matchValue=x(t,e)}async restore(t,e){let i=null;for(;i=y(t),!i;)await l(100);return y(e.replace("$1",this.matchValue),i)}}class b{constructor(t){this.loc=window.location.href,t()}get changed(){return window.location.href!==this.loc}goBack(t){if(!this.changed)return Promise.resolve(!0);const e=y(t);return new Promise((t=>{window.addEventListener("popstate",(()=>{t(null)}),{once:!0}),e?e.click():window.history.back()}))}}function y(t,e){return e=e||document,document.evaluate(t,e,null,XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue}function*S(t,e){e=e||document;let i=document.evaluate(t,e,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE),o=null;for(;null!==(o=i.iterateNext());)yield o}function x(t,e){return e=e||document,document.evaluate(t,e,null,XPathResult.STRING_TYPE).stringValue}async function*L(t,e,i){let o=t.firstElementChild;for(;o;)yield o,o.nextElementSibling||await Promise.race([c((()=>!!o.nextElementSibling),e),l(i)]),o=o.nextElementSibling}async function*P(t,e,i=r,o=5e3){let s=y(`.//${t}`,e);const n=e=>y(`./following-sibling::${t}`,e);for(;s;){yield s;let t=n(s);t?s=t:(await Promise.race([c((()=>(t=n(s),t)),i),l(o)]),s=t)}}function E(t){var e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)}function k(t,e=0){const i=t.getBoundingClientRect().top+window.pageYOffset-e;window.scrollTo({top:i,behavior:"smooth"})}function _(t,e={behavior:"smooth",block:"center",inline:"center"}){t.scrollIntoView(e)}function T(t,e,i){return void 0===typeof t.state&&(t.state={}),i&&(void 0===t.state[i]?t.state[i]=1:t.state[i]++),{state:t.state,msg:e}}},121:(t,e,i)=>{"use strict";i.r(e),i.d(e,{FacebookTimelineBehavior:()=>s});const o={feed:"//div[@role='feed']",article:".//div[@role='article']",pageletPostList:"//div[@data-pagelet='page']/div[@role='main']//div[@role='main']/div",pageletProfilePostList:"//div[@data-pagelet='page']//div[@data-pagelet='ProfileTimeline']",articleToPostList:"//div[@role='article']/../../../../div",photosOrVideos:`.//a[(contains(@href, '/photos/') or contains(@href, '/photo/?') or contains(@href, '/videos/')) and (starts-with(@href, '${window.location.origin}/') or starts-with(@href, '/'))]`,postQuery:".//a[contains(@href, '/posts/')]",extraLabel:"//*[starts-with(text(), '+')]",nextSlideQuery:"//div[@data-name='media-viewer-nav-container']/div[@data-visualcompletion][2]//div[@role='button']",nextSlide:"//div[@aria-hidden='false']//div[@role='button' and not(@aria-hidden) and @aria-label]",commentList:".//ul[(../h3) or (../h4)]",commentMoreReplies:"./div[2]/div[1]/div[2]/div[@role='button']",commentMoreComments:"./following-sibling::div/div/div[2][@role='button'][./span/span]",viewComments:".//h4/..//div[@role='button']",photoCommentList:"//ul[../h2]",firstPhotoThumbnail:"//div[@role='main']//div[3]//div[contains(@style, 'border-radius')]//div[contains(@style, 'max-width') and contains(@style, 'min-width')]//a[@role='link']",firstVideoThumbnail:"//div[@role='main']//div[contains(@style, 'z-index')]/following-sibling::div/div/div/div[last()]//a[contains(@href, '/videos/') and @aria-hidden!='true']",firstVideoSimple:"//div[@role='main']//a[contains(@href, '/videos/') and @aria-hidden!='true']",mainVideo:"//div[@data-pagelet='root']//div[@role='dialog']//div[@role='main']//video",nextVideo:"following::a[contains(@href, '/videos/') and @aria-hidden!='true']",isPhotoVideoPage:/^.*facebook\.com\/[^/]+\/(photos|videos)\/.+/,isPhotosPage:/^.*facebook\.com\/[^/]+\/photos\/?($|\?)/,isVideosPage:/^.*facebook\.com\/[^/]+\/videos\/?($|\?)/};class s{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?facebook\.com\//)}static init(){return{state:{}}}constructor(){this.extraWindow=null,this.allowNewWindow=!1}async*iterPostFeeds(t){const{iterChildElem:e,waitUnit:i,waitUntil:s,xpathNode:n,xpathNodes:a}=t.Lib,r=Array.from(a(o.feed));if(r&&r.length)for(const a of r)for await(const r of e(a,i,10*s))yield*this.viewPost(t,n(o.article,r));else{const a=n(o.pageletPostList)||n(o.pageletProfilePostList)||n(o.articleToPostList);if(!a)return;for await(const r of e(a,i,10*s))yield*this.viewPost(t,n(o.article,r))}this.extraWindow&&this.extraWindow.close()}async*viewPost(t,e,i=2){const{getState:s,scrollIntoView:n,sleep:a,waitUnit:r,xpathNode:l}=t.Lib;if(!e)return;const c=l(o.postQuery,e);let d=null;c&&(d=new URL(c.href,window.location.href),d.search=""),yield s(t,"Viewing post "+(d||""),"posts"),n(e),await a(2*r),l(".//video",e)&&(yield s(t,"Playing inline video","videos"),await a(2*r));let h=l(o.commentList,e);if(!h){const t=l(o.viewComments,e);t&&(t.click(),await a(2*r)),h=l(o.commentList,e)}yield*this.iterComments(t,h,i),await a(5*r)}async*viewPhotosOrVideos(t,e){const{getState:i,sleep:s,waitUnit:n,xpathNode:a,xpathNodes:r}=t.Lib,l=Array.from(r(o.photosOrVideos,e)),c=new Set;let d=0;for(const e of l){const r=new URL(e.href,window.location.href);if(-1===e.href.indexOf("?fbid")&&(r.search=""),c.has(r.href))continue;const h=e.href.indexOf("/video")>=0?"videos":"photos";++d,c.add(r.href),yield i(t,`Viewing ${h} ${r.href}`,h),e.scrollIntoView(),await s(5*n),e.click(),await s(10*n),this.allowNewWindow&&await this.openNewWindow(t,r.href),d===l.length&&(yield*this.viewExtraObjects(t,e,h,this.allowNewWindow));const u=a(o.nextSlide);u&&(u.click(),await s(2*n))}}async*viewExtraObjects(t,e,i,s){const{getState:n,sleep:a,waitUnit:r,waitUntil:l,xpathNode:c}=t.Lib,d=c(o.extraLabel,e);if(!d)return;const h=Number(d.innerText.slice(1));if(isNaN(h))return;let u;for(let e=0;ewindow.location.href!==u),2*r),yield n(t,`Viewing extra ${i} ${window.location.href}`),s&&await this.openNewWindow(t,window.location.href))}}async openNewWindow(t,e){this.extraWindow?this.extraWindow.location.href=e:this.extraWindow=await t.Lib.openWindow(e)}async*iterComments(t,e,i=2){const{getState:s,scrollIntoView:n,sleep:a,waitUnit:r,xpathNode:l}=t.Lib;if(!e)return void await a(5*r);let c=e.firstElementChild,d=null,h=0;for(;c&&hwindow.location.href!==c),2*n);let d=null;for(;(d=r(o.nextSlideQuery))&&(c=window.location.href,await s(n),d.click(),await s(5*n),await Promise.race([a((()=>window.location.href!==c),2*n),s(3e3)]),window.location.href!==c);){yield e(t,`Viewing photo ${window.location.href}`,"photos");const i=r(o.photoCommentList);yield*this.iterComments(t,i,2),await s(5*n)}}async*iterAllVideos(t){const{getState:e,scrollIntoView:i,sleep:s,waitUnit:n,waitUntil:a,xpathNode:r,xpathNodes:l}=t.Lib,c=r("//video");c&&(i(c),await s(5*n));let d=r(o.firstVideoThumbnail)||r(o.firstVideoSimple);if(d)for(;d;){i(d);let c=window.location.href;d.click(),await a((()=>window.location.href!==c),2*n),yield e(t,"Viewing video: "+window.location.href,"videos"),await s(10*n),await Promise.race([a((()=>{for(const t of l("//video"))if(t.readyState>=3)return!0;return!1}),2*n),s(2e4)]),await s(10*n);const h=r(o.nextSlide);if(!h)break;c=window.location.href,h.click(),await a((()=>window.location.href!==c),2*n),d=r(o.nextVideo,d)}}async*run(t){const{getState:e,sleep:i,xpathNode:s}=t.Lib;if(yield e(t,"Starting..."),await i(2e3),o.isPhotosPage.exec(window.location.href))return t.state={photos:0,comments:0},void(yield*this.iterPhotoSlideShow(t));if(o.isVideosPage.exec(window.location.href))return t.state={videos:0,comments:0},void(yield*this.iterAllVideos(t));if(o.isPhotoVideoPage.exec(window.location.href)){t.state={comments:0};const e=s(o.photoCommentList);yield*this.iterComments(t,e,1e3)}else t.state={posts:0,comments:0,videos:0},yield*this.iterPostFeeds(t)}}s.id="Facebook"},954:(t,e,i)=>{"use strict";i.r(e),i.d(e,{default:()=>l});var o=i(121),s=i(741),n=i(667),a=i(739),r=i(714);const l=[s.InstagramPostsBehavior,a.TwitterTimelineBehavior,o.FacebookTimelineBehavior,n.TelegramBehavior,r.TikTokVideoBehavior,r.TikTokProfileBehavior]},741:(t,e,i)=>{"use strict";i.r(e),i.d(e,{InstagramPostsBehavior:()=>n});const o="//article[@role='presentation']//div[@role='presentation']/following-sibling::button",s={rootPath:"//article/div/div",childMatchSelect:"string(.//a[starts-with(@href, '/')]/@href)",childMatch:"child::div[.//a[@href='$1']]",firstPostInRow:"div[1]/a",postCloseButton:"/html/body/div[last()]/div[1]/button[.//*[@aria-label]]",nextPost:"//button[.//*[local-name() = 'svg' and @aria-label='Next']]",postLoading:"//*[@aria-label='Loading...']",subpostNextOnlyChevron:o,subpostPrevNextChevron:o+"[2]",commentRoot:"//article[@role='presentation']/div[1]/div[2]//ul",viewReplies:"//li//button[span[not(count(*)) and text()!='$1']]",loadMore:"//button[span[@aria-label]]"};class n{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/)}static init(){return{state:{posts:0,slides:0,rows:0,comments:0}}}constructor(){this.maxCommentsTime=1e4,this.postOnlyWindow=null}cleanup(){this.postOnlyWindow&&(this.postOnlyWindow.close(),this.postOnlyWindow=null)}async waitForNext(t,e){return e?(await t.Lib.sleep(t.Lib.waitUnit),e.nextElementSibling?e.nextElementSibling:null):null}async*iterRow(t){const{RestoreState:e,sleep:i,waitUnit:o,xpathNode:n}=t.Lib;let a=n(s.rootPath);if(!a)return;let r=a.firstElementChild;if(r)for(;r;){await i(o);const n=new e(s.childMatchSelect,r);n.matchValue&&(yield r,r=await n.restore(s.rootPath,s.childMatch)),r=await this.waitForNext(t,r)}}async*viewStandalonePost(t,e){const{getState:i,sleep:o,waitUnit:n,waitUntil:a,xpathNode:r,xpathString:l}=t.Lib;let c=r(s.rootPath);if(!c||!c.firstElementChild)return;const d=l(s.childMatchSelect,c.firstElementChild);yield i(t,"Loading single post view for first post: "+d),window.history.replaceState({},"",d),window.dispatchEvent(new PopStateEvent("popstate",{state:{}}));let h=null,u=null;await o(5*n),await a((()=>(h=r(s.rootPath))!==c&&h),5*n),await o(5*n),window.history.replaceState({},"",e),window.dispatchEvent(new PopStateEvent("popstate",{state:{}})),await a((()=>(u=r(s.rootPath))!==h&&u),5*n)}async*iterSubposts(t){const{getState:e,sleep:i,waitUnit:o,xpathNode:n}=t.Lib;let a=n(s.subpostNextOnlyChevron),r=1;for(;a;)a.click(),await i(5*o),yield e(t,`Loading Slide ${++r} for ${window.location.href}`,"slides"),a=n(s.subpostPrevNextChevron);await i(5*o)}async iterComments(t){const{scrollIntoView:e,sleep:i,waitUnit:o,waitUntil:n,xpathNode:a}=t.Lib,r=a(s.commentRoot);if(!r)return;let l=r.firstElementChild,c=!1,d="";for(;l;){e(l),c=!0;let r=a(s.viewReplies.replace("$1",d),l);for(;r;){const e=r.textContent;r.click(),t.state.comments++,await i(2.5*o),await n((()=>e!==r.textContent),o),d=r.textContent,r=a(s.viewReplies.replace("$1",d),l)}if(l.nextElementSibling&&"LI"===l.nextElementSibling.tagName&&!l.nextElementSibling.nextElementSibling){let e=a(s.loadMore,l.nextElementSibling);e&&(e.click(),t.state.comments++,await i(5*o))}l=l.nextElementSibling,await i(2.5*o)}return c}async*iterPosts(t,e){const{getState:i,sleep:o,waitUnit:n,xpathNode:a}=t.Lib;let r=0;for(;e&&++r<=3;)for(e.click(),await o(10*n),yield i(t,"Loading Post: "+window.location.href,"posts"),await fetch(window.location.href),yield*this.iterSubposts(t),yield i(t,"Loaded Comments","comments"),await Promise.race([this.iterComments(t),o(this.maxCommentsTime)]),e=a(s.nextPost);!e&&a(s.postLoading);)await o(2.5*n);await o(5*n)}async*run(t){const{getState:e,scrollIntoView:i,sleep:o,waitUnit:n,xpathNode:a}=t.Lib,r=window.location.href;yield*this.viewStandalonePost(t,r);for await(const r of this.iterRow(t)){i(r),await o(2.5*n),yield e(t,"Loading Row","rows");const l=a(s.firstPostInRow,r);yield*this.iterPosts(t,l);const c=a(s.postCloseButton);c&&c.click(),await o(5*n)}}}n.id="Instagram"},667:(t,e,i)=>{"use strict";i.r(e),i.d(e,{TelegramBehavior:()=>a});const o="//main//section[@class='tgme_channel_history js-message_history']",s="string(./div[@data-post]/@data-post)",n="string(.//a[@class='tgme_widget_message_link_preview' and @href]/@href)";class a{static isMatch(){return window.location.href.match(/https:\/\/t.me\/s\/\w[\w]+/)}static init(){return{state:{messages:0}}}async waitForPrev(t,e){return e?(await t.Lib.sleep(5*t.Lib.waitUnit),e.previousElementSibling?e.previousElementSibling:null):null}async*run(t){const{getState:e,scrollIntoView:i,sleep:a,waitUnit:r,xpathNode:l,xpathString:c}=t.Lib;let d=l(o);if(!d)return;let h=d.lastElementChild;for(;h;){i(h);const o=c(s,h)||"unknown",l=c(n,h);if(l&&l.endsWith(".jpg")||l.endsWith(".png")){yield e(t,"Loading External Image: "+l);const i=new Image;i.src=l,document.body.appendChild(i),await a(2.5*r),document.body.removeChild(i)}yield e(t,"Loading Message: "+o,"messages"),h=await this.waitForPrev(t,h)}}}a.id="Telegram"},714:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BREADTH_ALL:()=>d,TikTokProfileBehavior:()=>u,TikTokVideoBehavior:()=>h});const o="//div[contains(@class, 'CommentListContainer')]",s="div[contains(@class, 'CommentItemContainer')]",n=".//p[contains(@class, 'ReplyActionText')]",a=".//p[starts-with(@data-e2e, 'view-more') and string-length(text()) > 0]",r="//div[starts-with(@data-e2e, 'user-post-item-list')]",l="div[contains(@class, 'DivItemContainerV2')]",c="button[contains(@class, 'StyledCloseIconContainer')]",d=Symbol("BREADTH_ALL");class h{static init(){return{state:{comments:0},opts:{breadth:d}}}static isMatch(){return window.location.href.match(/https:\/\/(www\.)?tiktok\.com\/@.+\/video\/\d+\/?.*/)}breadthComplete({opts:{breadth:t}},e){return t!==d&&t<=e}async*crawlThread(t,e,i=null,o=0){const{waitUntilNode:s,scrollAndClick:n,getState:r}=t.Lib,l=await s(a,e,i);l&&!this.breadthComplete(t,o)&&(await n(l,500),yield r(t,"View more replies","comments"),yield*this.crawlThread(t,e,l,o+1))}async*expandThread(t,e){const{xpathNode:i,scrollAndClick:o,getState:s}=t.Lib,a=i(n,e);a&&(await o(a,500),yield s(t,"View comment","comments"),yield*this.crawlThread(t,e,null,1))}async*run(t){const{xpathNode:e,iterChildMatches:i,scrollIntoView:n,getState:a}=t.Lib,r=e(o),l=i(s,r);for await(const e of l)n(e),yield a(t,"View comment","comments"),this.breadthComplete(t,0)||(yield*this.expandThread(t,e));yield a(t,"TikTok Video Behavior Complete")}}h.id="TikTokVideo";class u{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?tiktok\.com\/@[a-zA-Z0-9]+(\/?$|\/\?.*)/)}static init(){return{state:{videos:0,comments:0},opts:{breadth:d}}}async*openVideo(t,e){const{HistoryState:i,xpathNode:o,sleep:s}=t.Lib,n=o(".//a",e);if(!n)return;const a=new i((()=>n.click()));if(await s(500),a.changed){const e=new h;yield*e.run(t),await s(500),await a.goBack(c)}}async*run(t){const{xpathNode:e,iterChildMatches:i,scrollIntoView:o,getState:s,sleep:n}=t.Lib,a=e(r),c=i(l,a);for await(const e of c)o(e),yield s(t,"View video","videos"),yield*this.openVideo(t,e),await n(500);yield s(t,"TikTok Profile Behavior Complete")}}u.id="TikTokProfile"},739:(t,e,i)=>{"use strict";i.r(e),i.d(e,{TwitterTimelineBehavior:()=>g});const o="//h1[@role='heading' and @aria-level='1']/following-sibling::div[@aria-label]/*[1]",s=".//article",n="string(.//article//a[starts-with(@href, '/') and @aria-label]/@href)",a="child::div[.//a[@href='$1']]",r=".//div[@role='button' and not(@aria-haspopup) and not(@data-testid)]",l=".//div[@role='blockquote' and @aria-haspopup='false']",c=".//a[@role='link' and starts-with(@href, '/') and contains(@href, '/photo/')]",d="//div[@aria-roledescription='carousel']/div[2]/div[1]//div[@role='button']",h="//div[@aria-roledescription='carousel']/div[2]/div[2]//div[@role='button']",u="//div[@role='presentation']/div[@role='button' and @aria-label]",w="//div[@data-testid='titleContainer']//div[@role='button']",m=".//a[@href='/settings/content_you_see']/parent::div/parent::div/parent::div//div[@role='button']",f=".//*[@role='progressbar']",p=".//div[data-testid='placementTracking']";class g{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?twitter\.com\//)}static init(){return{state:{tweets:0,images:0,videos:0},opts:{maxDepth:0}}}constructor(){this.seenTweets=new Set,this.seenMediaTweets=new Set}showingProgressBar(t,e){const{xpathNode:i}=t.Lib,o=i(f,e);return!!o&&o.clientHeight>10}async waitForNext(t,e){const{sleep:i,waitUnit:o}=t.Lib;if(!e)return null;if(await i(2*o),!e.nextElementSibling)return null;for(;this.showingProgressBar(t,e.nextElementSibling);)await i(o);return e.nextElementSibling}async expandMore(t,e){const{sleep:i,waitUnit:o,xpathNode:s}=t.Lib,n=s(r,e);if(!n)return e;const a=e.previousElementSibling;for(n.click(),await i(o);this.showingProgressBar(t,a.nextElementSibling);)await i(o);return e=a.nextElementSibling}async*infScroll(t){const{scrollIntoView:e,RestoreState:i,sleep:l,waitUnit:c,xpathNode:d}=t.Lib;let h=d(o);if(!h)return;let u=h.firstElementChild;if(u)for(;u;){let h=d(s,u);if(!h&&r&&(u=await this.expandMore(t,u),h=d(s,u)),u&&u.innerText&&e(u),u&&h){await l(c);const t=new i(n,u);yield h,t.matchValue&&(u=await t.restore(o,a))}u=await this.waitForNext(t,u)}}async*mediaPlaying(t,e){const{getState:i,sleep:o,xpathNode:s,xpathString:a}=t.Lib,r=s("(.//video | .//audio)",e);if(!r||r.paused)return;let l,c=null;try{c=new URL(a(n,e.parentElement),window.location.origin).href}catch(t){console.warn(t)}if(r.src.startsWith("https://")&&r.src.indexOf(".mp4")>0)return void(yield i(t,`Loading video for ${c||"unknown"}`,"videos"));if(c){if(this.seenMediaTweets.has(c))return;l=`Waiting for media playback for ${c} to finish`,this.seenMediaTweets.add(c)}else l="Loading video";yield i(t,l,"videos");const d=new Promise((t=>{r.addEventListener("ended",(()=>t(null))),r.addEventListener("abort",(()=>t(null))),r.addEventListener("error",(()=>t(null))),r.addEventListener("pause",(()=>t(null)))}));await Promise.race([d,o(6e4)])}async*clickImages(t,e){const{getState:i,HistoryState:o,sleep:s,waitUnit:n,xpathNode:a}=t.Lib,r=a(c,e);if(r){const e=new o((()=>r.click()));yield i(t,"Loading Image: "+window.location.href,"images"),await s(5*n);let l=a(d),c=window.location.href;for(;l;){if(l.click(),await s(2*n),window.location.href===c){await s(5*n);break}c=window.location.href,yield i(t,"Loading Image: "+window.location.href,"images"),await s(5*n),l=a(h)}await e.goBack(u)}}async*clickTweet(t,e,i){const{getState:o,HistoryState:s,sleep:n,waitUnit:a}=t.Lib,r=new s((()=>e.click()));if(await n(a),r.changed){yield o(t,"Capturing Tweet: "+window.location.href,"tweets");i{var o={".":607,"./":607,"./autofetcher":894,"./autofetcher.ts":894,"./autoplay":376,"./autoplay.ts":376,"./autoscroll":234,"./autoscroll.ts":234,"./index":607,"./index.ts":607,"./lib/behavior":841,"./lib/behavior.ts":841,"./lib/utils":721,"./lib/utils.ts":721,"./site":954,"./site/":954,"./site/facebook":121,"./site/facebook.ts":121,"./site/index":954,"./site/index.ts":954,"./site/instagram":741,"./site/instagram.ts":741,"./site/telegram":667,"./site/telegram.ts":667,"./site/tiktok":714,"./site/tiktok.ts":714,"./site/twitter":739,"./site/twitter.ts":739};function s(t){return Promise.resolve().then((()=>{if(!i.o(o,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}var s=o[t];return i(s)}))}s.keys=()=>Object.keys(o),s.id=75,t.exports=s}},e={};function i(o){var s=e[o];if(void 0!==s)return s.exports;var n=e[o]={exports:{}};return t[o](n,n.exports,i),n.exports}i.d=(t,e)=>{for(var o in e)i.o(e,o)&&!i.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},i.e=()=>Promise.resolve(),i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),i.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},(()=>{"use strict";i(607)})()})(); \ No newline at end of file +/*! behaviors.js is part of Webrecorder project. Copyright (C) 2021-2023, Webrecorder Software. Licensed under the Affero General Public License v3. */(()=>{var t={894:(t,e,i)=>{"use strict";i.r(e),i.d(e,{AutoFetcher:()=>l});var o=i(841),s=i(721);const n=/\s*(\S*\s+[\d.]+[wx]),|(?:\s*,(?:\s+|(?=https?:)))/,a=/(url\s*\(\s*[\\"']*)([^)'"]+)([\\"']*\s*\))/gi,r=/(@import\s*[\\"']*)([^)'";]+)([\\"']*\s*;?)/gi;class l extends o.BackgroundBehavior{constructor(t=!1){super(),this.urlSet=new Set,this.urlQueue=[],this.numPending=0,this.numDone=0,this._donePromise=new Promise((t=>this._markDone=t)),t&&this.start()}get numFetching(){return this.numDone+this.numPending+this.urlQueue.length}async start(){await(0,s.awaitLoad)(),this.run(),this.initObserver(),await(0,s.sleep)(500),this.urlQueue.length||this.numPending||this._markDone(null)}done(){return this._donePromise}async run(){this.extractSrcSrcSetAll(document),this.extractStyleSheets()}isValidUrl(t){return t&&(t.startsWith("http:")||t.startsWith("https:"))}queueUrl(t){try{t=new URL(t,document.baseURI).href}catch(t){return!1}return!!this.isValidUrl(t)&&(!this.urlSet.has(t)&&(this.urlSet.add(t),this.doFetch(t),!0))}async doFetchStream(t){try{const e=await fetch(t,{credentials:"include",referrerPolicy:"origin-when-cross-origin"});this.debug(`Autofetch: started ${t}`);const i=e.body.getReader();let o=null;for(;(o=await i.read())&&!o.done;);return this.debug(`Autofetch: finished ${t}`),!0}catch(t){return this.debug(t),!1}}async doFetchNonCors(t){try{const e=new AbortController;await fetch(t,{mode:"no-cors",credentials:"include",referrerPolicy:"origin-when-cross-origin",abort:e}),e.abort(),this.debug(`Autofetch: started non-cors stream for ${t}`)}catch(e){this.debug(`Autofetch: failed non-cors for ${t}`)}}async doFetch(t){if(this.urlQueue.push(t),this.numPending<=6){for(;this.urlQueue.length>0;){const t=this.urlQueue.shift();this.numPending++,!1||await this.doFetchNonCors(t),this.numPending--,this.numDone++}this.numPending||this._markDone(null)}}initObserver(){this.mutationObserver=new MutationObserver((t=>this.observeChange(t))),this.mutationObserver.observe(document.documentElement,{characterData:!1,characterDataOldValue:!1,attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0,attributeFilter:["srcset","loading"]})}processChangedNode(t){switch(t.nodeType){case Node.ATTRIBUTE_NODE:if("srcset"===t.nodeName&&this.extractSrcSetAttr(t.nodeValue),"loading"===t.nodeName&&"lazy"===t.nodeValue){const e=t.parentNode;"IMG"===e.tagName&&e.setAttribute("loading","eager")}break;case Node.TEXT_NODE:t.parentNode&&"STYLE"===t.parentNode.tagName&&this.extractStyleText(t.nodeValue);break;case Node.ELEMENT_NODE:t.sheet&&this.extractStyleSheet(t.sheet),this.extractSrcSrcSet(t),setTimeout((()=>this.extractSrcSrcSetAll(t)),1e3)}}observeChange(t){for(const e of t)if(this.processChangedNode(e.target),"childList"===e.type)for(const t of e.addedNodes)this.processChangedNode(t)}extractSrcSrcSetAll(t){const e=t.querySelectorAll("img[srcset], img[data-srcset], img[data-src], noscript > img[src], img[loading='lazy'], video[srcset], video[data-srcset], video[data-src], audio[srcset], audio[data-srcset], audio[data-src], picture > source[srcset], picture > source[data-srcset], picture > source[data-src], video > source[srcset], video > source[data-srcset], video > source[data-src], audio > source[srcset], audio > source[data-srcset], audio > source[data-src]");for(const t of e)this.extractSrcSrcSet(t)}extractSrcSrcSet(t){if(!t||t.nodeType!==Node.ELEMENT_NODE)return void console.warn("No elem to extract from");const e=t.getAttribute("data-src");e&&this.queueUrl(e),"lazy"===t.getAttribute("loading")&&t.setAttribute("loading","eager");const i=t.getAttribute("srcset");i&&this.extractSrcSetAttr(i);const o=t.getAttribute("data-srcset");o&&this.extractSrcSetAttr(o);const s=t.getAttribute("src");s&&"NOSCRIPT"===t.parentElement.tagName&&this.queueUrl(s)}extractSrcSetAttr(t){for(const e of t.split(n))if(e){const t=e.trim().split(" ");this.queueUrl(t[0])}}extractStyleSheets(t){t=t||document;for(const e of t.styleSheets)this.extractStyleSheet(e)}extractStyleSheet(t){let e;try{e=t.cssRules||t.rules}catch(t){return void this.debug("Can't access stylesheet")}for(const t of e)t.type===CSSRule.MEDIA_RULE&&this.extractStyleText(t.cssText)}extractStyleText(t){const e=(t,e,i,o)=>(this.queueUrl(i),e+i+o);t.replace(a,e).replace(r,e)}}},376:(t,e,i)=>{"use strict";i.r(e),i.d(e,{Autoplay:()=>n});var o=i(841),s=i(721);class n extends o.BackgroundBehavior{constructor(t){super(),this.mediaSet=new Set,this.autofetcher=t,this.numPlaying=0,this.promises=[],this._initDone=()=>null,this.promises.push(new Promise((t=>this._initDone=t))),this.start()}async start(){await(0,s.awaitLoad)(),this.pollAudioVideo(),this._initDone()}async pollAudioVideo(){for(;;){for(const[,t]of document.querySelectorAll("video, audio, picture").entries())t.__bx_autoplay_found||(await this.loadMedia(t),t.__bx_autoplay_found=!0);await(0,s.sleep)(500)}}fetchSrcUrl(t){const e=t.src||t.currentSrc;return!!e&&(!(!e.startsWith("http:")&&!e.startsWith("https:"))&&(this.mediaSet.has(e)||(this.debug("fetch media source URL: "+e),this.mediaSet.add(e),this.autofetcher.queueUrl(e)),!0))}processFetchableUrl(t){let e=this.fetchSrcUrl(t);const i=t.querySelectorAll("source");for(const t of i){const i=this.fetchSrcUrl(t);e=e||i}return e}async loadMedia(t){this.debug("processing media element: "+t.outerHTML);const e=this.processFetchableUrl(t);t.play?e?t.paused||(t.pause(),this.debug("media URL found, pausing playback")):t.paused?(this.debug("no src url found, attempting to click or play: "+t.outerHTML),this.attemptMediaPlay(t).then((async e=>{let i=!0;for(e&&e.then((()=>i=!1));i;)this.processFetchableUrl(t)&&(i=!1),this.debug("Waiting for fixed URL or media to finish: "+t.currentSrc),await(0,s.sleep)(1e3)}))):t.currentSrc&&this.debug("media playing from non-URL source: "+t.currentSrc):this.debug("media not playable, skipping")}async attemptMediaPlay(t){let e;const i=new Promise((t=>{e=t}));let o;const n=new Promise((t=>{o=t}));n.then((()=>this.promises.push(i))),t.addEventListener("loadstart",(()=>{this.debug("loadstart"),o(!0)})),t.addEventListener("playing",(()=>{this.debug("playing"),o(!0)})),t.addEventListener("loadeddata",(()=>this.debug("loadeddata"))),t.addEventListener("ended",(()=>{this.debug("ended"),e()})),t.addEventListener("pause",(()=>{this.debug("pause"),e()})),t.addEventListener("abort",(()=>{this.debug("abort"),e()})),t.addEventListener("error",(()=>{this.debug("error"),e()})),t.addEventListener("stalled",(()=>{this.debug("stalled"),e()})),t.addEventListener("suspend",(()=>{this.debug("suspend"),e()})),t.muted=!0;if(!t.closest("a")){t.click();if(await Promise.race([n,(0,s.sleep)(1e3)]))return i}return t.play(),i}done(){return Promise.allSettled(this.promises)}}},234:(t,e,i)=>{"use strict";i.r(e),i.d(e,{AutoScroll:()=>n});var o=i(841),s=i(721);class n extends o.Behavior{constructor(t){super(),this.autoFetcher=t,this.showMoreQuery="//*[contains(text(), 'show more') or contains(text(), 'Show more')]",this.state={segments:1},this.lastScrollPos=-1,this.samePosCount=0}currScrollPos(){return Math.round(self.scrollY+self.innerHeight)}canScrollMore(){const t=self.document.scrollingElement||self.document.body;return this.currScrollPos()n&&(this.state.segments++,n=a),e||i||(e=(0,s.xpathNode)(this.showMoreQuery)),e&&(0,s.isInViewport)(e)&&(yield this.getState("Clicking 'Show More', awaiting more content"),e.click(),await(0,s.sleep)(s.waitUnit),await Promise.race([(0,s.waitUntil)((()=>self.document.scrollingElement.scrollHeight>a),500),(0,s.sleep)(3e4)]),self.document.scrollingElement.scrollHeight===a&&(i=!0),e=null),self.scrollBy(o),await(0,s.sleep)(75),1===this.state.segments)yield this.getState(`Scrolling down by ${o.top} pixels every 0.075 seconds`),t=2;else{const e=t/(this.state.segments-1);this.debug(`Waiting up to ${e} seconds for more scroll segments`);const i=Date.now();await Promise.race([(0,s.waitUntil)((()=>this.canScrollMore()),75),(0,s.sleep)(e)]),t+=2*(Date.now()-i)}const r=this.currScrollPos();if(r===this.lastScrollPos){if(++this.samePosCount>=2)break}else this.samePosCount=0;this.lastScrollPos=r}}async*scrollUp(){const t={top:-Math.min(.1*self.document.scrollingElement.clientHeight,30),left:0,behavior:"auto"};let e=self.document.scrollingElement.scrollHeight;for(;self.scrollY>0;){const i=self.document.scrollingElement.scrollHeight;i>e&&(this.state.segments++,e=i),self.scrollBy(t),await(0,s.sleep)(75),1===this.state.segments?yield this.getState(`Scrolling up by ${t.top} pixels every 0.075 seconds`):await Promise.race([(0,s.waitUntil)((()=>self.scrollY>0),75),(0,s.sleep)(2e3*(this.state.segments-1))])}}}n.id="Autoscroll"},607:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BehaviorManager:()=>c});var o=i(894),s=i(376),n=i(234),a=i(721),r=i(841),l=i(954);class c{constructor(){this.behaviors=[],this.loadedBehaviors=l.default.reduce(((t,e)=>(t[e.id]=e,t)),{}),this.mainBehavior=null,this.inited=!1,this.started=!1,(0,a.behaviorLog)("Loaded behaviors for: "+self.location.href)}init(t){if(this.inited)return;if(this.inited=!0,!self.window)return;if(this.timeout=t.timeout,void 0!==t.log){let e=t.log;"string"==typeof e&&(e=self[e]),"function"==typeof e?(0,a._setLogFunc)(e):!1===e&&(0,a._setLogFunc)(null)}this.autofetch=new o.AutoFetcher(!!t.autofetch),t.autofetch&&((0,a.behaviorLog)("Enable AutoFetcher"),this.behaviors.push(this.autofetch)),t.autoplay&&((0,a.behaviorLog)("Enable Autoplay"),this.behaviors.push(new s.Autoplay(this.autofetch)));let e=!1;if(self.window.top===self.window){if(t.siteSpecific)for(const i in this.loadedBehaviors){const o=this.loadedBehaviors[i];if(o.isMatch()){(0,a.behaviorLog)("Starting Site-Specific Behavior: "+i),this.mainBehaviorClass=o;const s="object"==typeof t.siteSpecific&&t.siteSpecific[i]||{};this.mainBehavior=new r.BehaviorRunner(o,s),e=!0;break}}return!e&&t.autoscroll&&((0,a.behaviorLog)("Starting Autoscroll"),this.mainBehaviorClass=n.AutoScroll,this.mainBehavior=new n.AutoScroll(this.autofetch)),this.mainBehavior?(this.behaviors.push(this.mainBehavior),this.mainBehavior.name):""}}load(t){this.loadedBehaviors[t.name]=t}async resolve(t){const e=await i(75)(`${t}`);if(Array.isArray(e))for(const t of e)this.load(t);else this.load(e)}async run(t){if(console.log("DEBUG: Running Behaviors 2"),this.started)return void this.unpause();this.init(t),await(0,a.awaitLoad)(),this.mainBehavior&&this.mainBehavior.start(),this.started=!0;let e=Promise.allSettled(this.behaviors.map((t=>t.done())));this.timeout?((0,a.behaviorLog)(`Waiting for behaviors to finish or ${this.timeout}ms timeout`),await Promise.race([e,(0,a.sleep)(this.timeout)])):((0,a.behaviorLog)("Waiting for behaviors to finish"),await e),(0,a.behaviorLog)("All Behaviors Done for "+self.location.href),this.mainBehavior&&this.mainBehaviorClass.cleanup&&this.mainBehavior.cleanup()}async runOne(t,e={}){const i=l.default.find((e=>e.name===t));if(void 0===i)return void console.error(`No behavior of name ${t} found`);const o=new r.BehaviorRunner(i,e);o.start(),console.log(`Running behavior: ${t}`),await o.done(),console.log(`Behavior ${t} completed`)}pause(){(0,a.behaviorLog)("Pausing Main Behavior"+this.mainBehaviorClass.name),this.mainBehavior&&this.mainBehavior.pause()}unpause(){this.mainBehavior&&this.mainBehavior.unpause()}doAsyncFetch(t){return(0,a.behaviorLog)("Queueing Async Fetch Url: "+t),this.autofetch.queueUrl(t)}}(0,a._setBehaviorManager)(c),(0,a.installBehaviors)(self)},841:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BackgroundBehavior:()=>s,Behavior:()=>n,BehaviorRunner:()=>a});var o=i(721);class s{debug(t){(0,o.behaviorLog)(t,"debug")}log(t){(0,o.behaviorLog)(t,"info")}}class n extends s{constructor(){super(),this._running=null,this.paused=null,this._unpause=null,this.state={},this.scrollOpts={behavior:"smooth",block:"center",inline:"center"}}start(){this._running=this.run()}done(){return this._running?this._running:Promise.resolve()}async run(){try{for await(const t of this)this.log(t),this.paused&&await this.paused;this.log(this.getState("done!"))}catch(t){this.log(this.getState(t))}}pause(){this.paused||(this.paused=new Promise((t=>{this._unpause=t})))}unpause(){this._unpause&&(this._unpause(),this.paused=null,this._unpause=null)}getState(t,e){return e&&(void 0===this.state[e]?this.state[e]=1:this.state[e]++),{state:this.state,msg:t}}cleanup(){}static load(){self.__bx_behaviors?self.__bx_behaviors.load(this):console.warn(`Could not load ${this.name} behavior: window.__bx_behaviors is not initialized`)}async*[Symbol.asyncIterator](){yield}}class a extends s{constructor(t,e={}){if("function"!=typeof t.isMatch||"string"!=typeof t.name)throw Error("Invalid behavior found: missing `isMatch`, `init`, or `name` static methods");if(super(),this.inst=new t,"function"!=typeof this.inst.run||"AsyncGeneratorFunction"!==this.inst.run.constructor.name)throw Error("Invalid behavior found: missing `async run*` instance method");const i=t.init()||{};this.ctx={Lib:o,state:i.state||{},opts:{...i.opts||{},...e}},this._running=null,this.paused=null,this._unpause=null}start(){this._running=this.run()}done(){return this._running?this._running:Promise.resolve()}async run(){try{for await(const t of this.inst.run(this.ctx))this.log(t),this.paused&&await this.paused;this.log((0,o.getState)(this.ctx,"done!"))}catch(t){this.log((0,o.getState)(this.ctx,t))}}pause(){this.paused||(this.paused=new Promise((t=>{this._unpause=t})))}unpause(){this._unpause&&(this._unpause(),this.paused=null,this._unpause=null)}cleanup(){}static load(){self.__bx_behaviors?self.__bx_behaviors.load(this):console.warn(`Could not load ${this.name} behavior: window.__bx_behaviors is not initialized`)}}},721:(t,e,i)=>{"use strict";i.r(e),i.d(e,{HistoryState:()=>b,RestoreState:()=>v,_setBehaviorManager:()=>p,_setLogFunc:()=>f,awaitLoad:()=>h,behaviorLog:()=>w,getState:()=>T,installBehaviors:()=>g,isInViewport:()=>E,iterChildElem:()=>L,iterChildMatches:()=>P,openWindow:()=>m,scrollAndClick:()=>a,scrollIntoView:()=>_,scrollToOffset:()=>k,sleep:()=>l,waitUnit:()=>r,waitUntil:()=>c,waitUntilNode:()=>d,xpathNode:()=>y,xpathNodes:()=>S,xpathString:()=>x});let o=console.log,s=null;const n={behavior:"smooth",block:"center",inline:"center"};async function a(t,e=500,i=n){t.scrollIntoView(i),await l(e),t.click()}const r=200;function l(t){return new Promise((e=>setTimeout(e,t)))}async function c(t,e=r){for(;!t();)await l(e)}async function d(t,e=document,i=null,o=1e3,s=r){let n=null,a=!1;const l=c((()=>(n=y(t,e),a||n!==i&&null!==n)),s),d=new Promise((t=>setTimeout((()=>{a=!0,t("TIMEOUT")}),o)));return await Promise.race([l,d]),n}function h(){return new Promise((t=>{"complete"===document.readyState?t(null):window.addEventListener("load",t)}))}function u(t,e){try{t(e)}catch(i){t(JSON.stringify(e))}}function w(t,e="debug"){o&&u(o,{data:t,type:e})}async function m(t){if(self.__bx_open){const e=new Promise((t=>self.__bx_openResolve=t));u(self.__bx_open,{url:t});let i=null;try{if(i=await e,i)return i}catch(t){console.warn(t)}finally{delete self.__bx_openResolve}}return window.open(t)}function f(t){o=t}function p(t){s=t}function g(t){t.__bx_behaviors=new s}class v{constructor(t,e){this.matchValue=x(t,e)}async restore(t,e){let i=null;for(;i=y(t),!i;)await l(100);return y(e.replace("$1",this.matchValue),i)}}class b{constructor(t){this.loc=window.location.href,t()}get changed(){return window.location.href!==this.loc}goBack(t){if(!this.changed)return Promise.resolve(!0);const e=y(t);return new Promise((t=>{window.addEventListener("popstate",(()=>{t(null)}),{once:!0}),e?e.click():window.history.back()}))}}function y(t,e){return e=e||document,document.evaluate(t,e,null,XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue}function*S(t,e){e=e||document;let i=document.evaluate(t,e,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE),o=null;for(;null!==(o=i.iterateNext());)yield o}function x(t,e){return e=e||document,document.evaluate(t,e,null,XPathResult.STRING_TYPE).stringValue}async function*L(t,e,i){let o=t.firstElementChild;for(;o;)yield o,o.nextElementSibling||await Promise.race([c((()=>!!o.nextElementSibling),e),l(i)]),o=o.nextElementSibling}async function*P(t,e,i=r,o=5e3){let s=y(`.//${t}`,e);const n=e=>y(`./following-sibling::${t}`,e);for(;s;){yield s;let t=n(s);t?s=t:(await Promise.race([c((()=>(t=n(s),t)),i),l(o)]),s=t)}}function E(t){var e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)}function k(t,e=0){const i=t.getBoundingClientRect().top+window.pageYOffset-e;window.scrollTo({top:i,behavior:"smooth"})}function _(t,e={behavior:"smooth",block:"center",inline:"center"}){t.scrollIntoView(e)}function T(t,e,i){return void 0===typeof t.state&&(t.state={}),i&&(void 0===t.state[i]?t.state[i]=1:t.state[i]++),{state:t.state,msg:e}}},121:(t,e,i)=>{"use strict";i.r(e),i.d(e,{FacebookTimelineBehavior:()=>s});const o={feed:"//div[@role='feed']",article:".//div[@role='article']",pageletPostList:"//div[@data-pagelet='page']/div[@role='main']//div[@role='main']/div",pageletProfilePostList:"//div[@data-pagelet='page']//div[@data-pagelet='ProfileTimeline']",articleToPostList:"//div[@role='article']/../../../../div",photosOrVideos:`.//a[(contains(@href, '/photos/') or contains(@href, '/photo/?') or contains(@href, '/videos/')) and (starts-with(@href, '${window.location.origin}/') or starts-with(@href, '/'))]`,postQuery:".//a[contains(@href, '/posts/')]",extraLabel:"//*[starts-with(text(), '+')]",nextSlideQuery:"//div[@data-name='media-viewer-nav-container']/div[@data-visualcompletion][2]//div[@role='button']",nextSlide:"//div[@aria-hidden='false']//div[@role='button' and not(@aria-hidden) and @aria-label]",commentList:".//ul[(../h3) or (../h4)]",commentMoreReplies:"./div[2]/div[1]/div[2]/div[@role='button']",commentMoreComments:"./following-sibling::div/div/div[2][@role='button'][./span/span]",viewComments:".//h4/..//div[@role='button']",photoCommentList:"//ul[../h2]",firstPhotoThumbnail:"//div[@role='main']//div[3]//div[contains(@style, 'border-radius')]//div[contains(@style, 'max-width') and contains(@style, 'min-width')]//a[@role='link']",firstVideoThumbnail:"//div[@role='main']//div[contains(@style, 'z-index')]/following-sibling::div/div/div/div[last()]//a[contains(@href, '/videos/') and @aria-hidden!='true']",firstVideoSimple:"//div[@role='main']//a[contains(@href, '/videos/') and @aria-hidden!='true']",mainVideo:"//div[@data-pagelet='root']//div[@role='dialog']//div[@role='main']//video",nextVideo:"following::a[contains(@href, '/videos/') and @aria-hidden!='true']",isPhotoVideoPage:/^.*facebook\.com\/[^/]+\/(photos|videos)\/.+/,isPhotosPage:/^.*facebook\.com\/[^/]+\/photos\/?($|\?)/,isVideosPage:/^.*facebook\.com\/[^/]+\/videos\/?($|\?)/};class s{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?facebook\.com\//)}static init(){return{state:{}}}constructor(){this.extraWindow=null,this.allowNewWindow=!1}async*iterPostFeeds(t){const{iterChildElem:e,waitUnit:i,waitUntil:s,xpathNode:n,xpathNodes:a}=t.Lib,r=Array.from(a(o.feed));if(r&&r.length)for(const a of r)for await(const r of e(a,i,10*s))yield*this.viewPost(t,n(o.article,r));else{const a=n(o.pageletPostList)||n(o.pageletProfilePostList)||n(o.articleToPostList);if(!a)return;for await(const r of e(a,i,10*s))yield*this.viewPost(t,n(o.article,r))}this.extraWindow&&this.extraWindow.close()}async*viewPost(t,e,i=2){const{getState:s,scrollIntoView:n,sleep:a,waitUnit:r,xpathNode:l}=t.Lib;if(!e)return;const c=l(o.postQuery,e);let d=null;c&&(d=new URL(c.href,window.location.href),d.search=""),yield s(t,"Viewing post "+(d||""),"posts"),n(e),await a(2*r),l(".//video",e)&&(yield s(t,"Playing inline video","videos"),await a(2*r));let h=l(o.commentList,e);if(!h){const t=l(o.viewComments,e);t&&(t.click(),await a(2*r)),h=l(o.commentList,e)}yield*this.iterComments(t,h,i),await a(5*r)}async*viewPhotosOrVideos(t,e){const{getState:i,sleep:s,waitUnit:n,xpathNode:a,xpathNodes:r}=t.Lib,l=Array.from(r(o.photosOrVideos,e)),c=new Set;let d=0;for(const e of l){const r=new URL(e.href,window.location.href);if(-1===e.href.indexOf("?fbid")&&(r.search=""),c.has(r.href))continue;const h=e.href.indexOf("/video")>=0?"videos":"photos";++d,c.add(r.href),yield i(t,`Viewing ${h} ${r.href}`,h),e.scrollIntoView(),await s(5*n),e.click(),await s(10*n),this.allowNewWindow&&await this.openNewWindow(t,r.href),d===l.length&&(yield*this.viewExtraObjects(t,e,h,this.allowNewWindow));const u=a(o.nextSlide);u&&(u.click(),await s(2*n))}}async*viewExtraObjects(t,e,i,s){const{getState:n,sleep:a,waitUnit:r,waitUntil:l,xpathNode:c}=t.Lib,d=c(o.extraLabel,e);if(!d)return;const h=Number(d.innerText.slice(1));if(isNaN(h))return;let u;for(let e=0;ewindow.location.href!==u),2*r),yield n(t,`Viewing extra ${i} ${window.location.href}`),s&&await this.openNewWindow(t,window.location.href))}}async openNewWindow(t,e){this.extraWindow?this.extraWindow.location.href=e:this.extraWindow=await t.Lib.openWindow(e)}async*iterComments(t,e,i=2){const{getState:s,scrollIntoView:n,sleep:a,waitUnit:r,xpathNode:l}=t.Lib;if(!e)return void await a(5*r);let c=e.firstElementChild,d=null,h=0;for(;c&&hwindow.location.href!==c),2*n);let d=null;for(;(d=r(o.nextSlideQuery))&&(c=window.location.href,await s(n),d.click(),await s(5*n),await Promise.race([a((()=>window.location.href!==c),2*n),s(3e3)]),window.location.href!==c);){yield e(t,`Viewing photo ${window.location.href}`,"photos");const i=r(o.photoCommentList);yield*this.iterComments(t,i,2),await s(5*n)}}async*iterAllVideos(t){const{getState:e,scrollIntoView:i,sleep:s,waitUnit:n,waitUntil:a,xpathNode:r,xpathNodes:l}=t.Lib,c=r("//video");c&&(i(c),await s(5*n));let d=r(o.firstVideoThumbnail)||r(o.firstVideoSimple);if(d)for(;d;){i(d);let c=window.location.href;d.click(),await a((()=>window.location.href!==c),2*n),yield e(t,"Viewing video: "+window.location.href,"videos"),await s(10*n),await Promise.race([a((()=>{for(const t of l("//video"))if(t.readyState>=3)return!0;return!1}),2*n),s(2e4)]),await s(10*n);const h=r(o.nextSlide);if(!h)break;c=window.location.href,h.click(),await a((()=>window.location.href!==c),2*n),d=r(o.nextVideo,d)}}async*run(t){const{getState:e,sleep:i,xpathNode:s}=t.Lib;if(yield e(t,"Starting..."),await i(2e3),o.isPhotosPage.exec(window.location.href))return t.state={photos:0,comments:0},void(yield*this.iterPhotoSlideShow(t));if(o.isVideosPage.exec(window.location.href))return t.state={videos:0,comments:0},void(yield*this.iterAllVideos(t));if(o.isPhotoVideoPage.exec(window.location.href)){t.state={comments:0};const e=s(o.photoCommentList);yield*this.iterComments(t,e,1e3)}else t.state={posts:0,comments:0,videos:0},yield*this.iterPostFeeds(t)}}s.id="Facebook"},954:(t,e,i)=>{"use strict";i.r(e),i.d(e,{default:()=>l});var o=i(121),s=i(741),n=i(667),a=i(739),r=i(714);const l=[s.InstagramPostsBehavior,a.TwitterTimelineBehavior,o.FacebookTimelineBehavior,n.TelegramBehavior,r.TikTokVideoBehavior,r.TikTokProfileBehavior]},741:(t,e,i)=>{"use strict";i.r(e),i.d(e,{InstagramPostsBehavior:()=>n});const o="//article[@role='presentation']//div[@role='presentation']/following-sibling::button",s={rootPath:"//article/div/div",childMatchSelect:"string(.//a[starts-with(@href, '/')]/@href)",childMatch:"child::div[.//a[@href='$1']]",firstPostInRow:"div[1]/a",postCloseButton:"/html/body/div[last()]/div[1]/button[.//*[@aria-label]]",nextPost:"//button[.//*[local-name() = 'svg' and @aria-label='Next']]",postLoading:"//*[@aria-label='Loading...']",subpostNextOnlyChevron:o,subpostPrevNextChevron:o+"[2]",commentRoot:"//article[@role='presentation']/div[1]/div[2]//ul",viewReplies:"//li//button[span[not(count(*)) and text()!='$1']]",loadMore:"//button[span[@aria-label]]",stories:"//div[@role='menu']//li[@tabindex='-1']",storyClickTarget:".//div[@role='menuitem']",storiesNext:"//section/div/button[2]",lastStoryProgress:"//div/section//header/div[1]/div[last()]/*[@style]",nextImageInStory:"//section/div/button[last()]",storyCloseButton:"//section/div/div[@tabindex]",nextStory:"//div[@role='menu']//button[last()]"};class n{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/)}static init(){return{state:{posts:0,slides:0,rows:0,comments:0,stories:0}}}constructor(){this.maxCommentsTime=1e4,this.postOnlyWindow=null}cleanup(){this.postOnlyWindow&&(this.postOnlyWindow.close(),this.postOnlyWindow=null)}async waitForNext(t,e){return e?(await t.Lib.sleep(t.Lib.waitUnit),e.nextElementSibling?e.nextElementSibling:null):null}async*iterRow(t){const{RestoreState:e,sleep:i,waitUnit:o,xpathNode:n}=t.Lib;let a=n(s.rootPath);if(!a)return;let r=a.firstElementChild;if(r)for(;r;){await i(o);const n=new e(s.childMatchSelect,r);n.matchValue&&(yield r,r=await n.restore(s.rootPath,s.childMatch)),r=await this.waitForNext(t,r)}}async*viewStandalonePost(t,e){const{getState:i,sleep:o,waitUnit:n,waitUntil:a,xpathNode:r,xpathString:l}=t.Lib;let c=r(s.rootPath);if(!c||!c.firstElementChild)return;const d=l(s.childMatchSelect,c.firstElementChild);yield i(t,"Loading single post view for first post: "+d),window.history.replaceState({},"",d),window.dispatchEvent(new PopStateEvent("popstate",{state:{}}));let h=null,u=null;await o(5*n),await a((()=>(h=r(s.rootPath))!==c&&h),5*n),await o(5*n),window.history.replaceState({},"",e),window.dispatchEvent(new PopStateEvent("popstate",{state:{}})),await a((()=>(u=r(s.rootPath))!==h&&u),5*n)}async*iterSubposts(t){const{getState:e,sleep:i,waitUnit:o,xpathNode:n}=t.Lib;let a=n(s.subpostNextOnlyChevron),r=1;for(;a;)a.click(),await i(5*o),yield e(t,`Loading Slide ${++r} for ${window.location.href}`,"slides"),a=n(s.subpostPrevNextChevron);await i(5*o)}async iterComments(t){const{scrollIntoView:e,sleep:i,waitUnit:o,waitUntil:n,xpathNode:a}=t.Lib,r=a(s.commentRoot);if(!r)return;let l=r.firstElementChild,c=!1,d="";for(;l;){e(l),c=!0;let r=a(s.viewReplies.replace("$1",d),l);for(;r;){const e=r.textContent;r.click(),t.state.comments++,await i(2.5*o),await n((()=>e!==r.textContent),o),d=r.textContent,r=a(s.viewReplies.replace("$1",d),l)}if(l.nextElementSibling&&"LI"===l.nextElementSibling.tagName&&!l.nextElementSibling.nextElementSibling){let e=a(s.loadMore,l.nextElementSibling);e&&(e.click(),t.state.comments++,await i(5*o))}l=l.nextElementSibling,await i(2.5*o)}return c}async*iterPosts(t,e){const{getState:i,sleep:o,waitUnit:n,xpathNode:a}=t.Lib;let r=0;for(;e&&++r<=3;)for(e.click(),await o(10*n),yield i(t,"Loading Post: "+window.location.href,"posts"),await fetch(window.location.href),yield*this.iterSubposts(t),yield i(t,"Loaded Comments","comments"),await Promise.race([this.iterComments(t),o(this.maxCommentsTime)]),e=a(s.nextPost);!e&&a(s.postLoading);)await o(2.5*n);await o(5*n)}async*iterStory(t,e){const{getState:i,sleep:o,waitUnit:n,xpathNode:a,waitUntil:r}=t.Lib;let l=a(`//li[contains(@style, '${e}')]`),c=a(s.nextStory);for(;null==l&&null!=c;)c.click(),l=a(`//li[contains(@style, '${e}')]`),c=a(s.nextStory),yield;const d=l.firstChild.firstChild;d.scrollIntoView(),await o(2*n),d.click(),await r((()=>null!=a(s.storiesNext)),2*n);let h=a(s.lastStoryProgress);const u=a(s.nextImageInStory);for(;null==h;)u.click(),await o(5*n),h=a(s.lastStoryProgress);a(s.storyCloseButton).click(),await o(10*n),yield i("Loaded story","stories")}async*iterStories(t){const{xpathNode:e}=t.Lib;let i=e(s.stories);if(i)for(;i;)yield i,i=i.nextElementSibling}async*run(t){const{getState:e,scrollIntoView:i,sleep:o,waitUnit:n,xpathNode:a}=t.Lib,r=window.location.href,l=[];for await(const i of this.iterStories(t))i.scrollIntoView({behavior:"smooth"}),yield e("Found story"),await o(5*n),l.push(i.getAttribute("style"));for(const e of l)yield*this.iterStory(t,e);yield*this.viewStandalonePost(t,r);for await(const r of this.iterRow(t)){i(r),await o(2.5*n),yield e(t,"Loading Row","rows");const l=a(s.firstPostInRow,r);yield*this.iterPosts(t,l);const c=a(s.postCloseButton);c&&c.click(),await o(5*n)}}}n.id="Instagram"},667:(t,e,i)=>{"use strict";i.r(e),i.d(e,{TelegramBehavior:()=>a});const o="//main//section[@class='tgme_channel_history js-message_history']",s="string(./div[@data-post]/@data-post)",n="string(.//a[@class='tgme_widget_message_link_preview' and @href]/@href)";class a{static isMatch(){return window.location.href.match(/https:\/\/t.me\/s\/\w[\w]+/)}static init(){return{state:{messages:0}}}async waitForPrev(t,e){return e?(await t.Lib.sleep(5*t.Lib.waitUnit),e.previousElementSibling?e.previousElementSibling:null):null}async*run(t){const{getState:e,scrollIntoView:i,sleep:a,waitUnit:r,xpathNode:l,xpathString:c}=t.Lib;let d=l(o);if(!d)return;let h=d.lastElementChild;for(;h;){i(h);const o=c(s,h)||"unknown",l=c(n,h);if(l&&l.endsWith(".jpg")||l.endsWith(".png")){yield e(t,"Loading External Image: "+l);const i=new Image;i.src=l,document.body.appendChild(i),await a(2.5*r),document.body.removeChild(i)}yield e(t,"Loading Message: "+o,"messages"),h=await this.waitForPrev(t,h)}}}a.id="Telegram"},714:(t,e,i)=>{"use strict";i.r(e),i.d(e,{BREADTH_ALL:()=>d,TikTokProfileBehavior:()=>u,TikTokVideoBehavior:()=>h});const o="//div[contains(@class, 'CommentListContainer')]",s="div[contains(@class, 'CommentItemContainer')]",n=".//p[contains(@class, 'ReplyActionText')]",a=".//p[starts-with(@data-e2e, 'view-more') and string-length(text()) > 0]",r="//div[starts-with(@data-e2e, 'user-post-item-list')]",l="div[contains(@class, 'DivItemContainerV2')]",c="button[contains(@class, 'StyledCloseIconContainer')]",d=Symbol("BREADTH_ALL");class h{static init(){return{state:{comments:0},opts:{breadth:d}}}static isMatch(){return window.location.href.match(/https:\/\/(www\.)?tiktok\.com\/@.+\/video\/\d+\/?.*/)}breadthComplete({opts:{breadth:t}},e){return t!==d&&t<=e}async*crawlThread(t,e,i=null,o=0){const{waitUntilNode:s,scrollAndClick:n,getState:r}=t.Lib,l=await s(a,e,i);l&&!this.breadthComplete(t,o)&&(await n(l,500),yield r(t,"View more replies","comments"),yield*this.crawlThread(t,e,l,o+1))}async*expandThread(t,e){const{xpathNode:i,scrollAndClick:o,getState:s}=t.Lib,a=i(n,e);a&&(await o(a,500),yield s(t,"View comment","comments"),yield*this.crawlThread(t,e,null,1))}async*run(t){const{xpathNode:e,iterChildMatches:i,scrollIntoView:n,getState:a}=t.Lib,r=e(o),l=i(s,r);for await(const e of l)n(e),yield a(t,"View comment","comments"),this.breadthComplete(t,0)||(yield*this.expandThread(t,e));yield a(t,"TikTok Video Behavior Complete")}}h.id="TikTokVideo";class u{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?tiktok\.com\/@[a-zA-Z0-9]+(\/?$|\/\?.*)/)}static init(){return{state:{videos:0,comments:0},opts:{breadth:d}}}async*openVideo(t,e){const{HistoryState:i,xpathNode:o,sleep:s}=t.Lib,n=o(".//a",e);if(!n)return;const a=new i((()=>n.click()));if(await s(500),a.changed){const e=new h;yield*e.run(t),await s(500),await a.goBack(c)}}async*run(t){const{xpathNode:e,iterChildMatches:i,scrollIntoView:o,getState:s,sleep:n}=t.Lib,a=e(r),c=i(l,a);for await(const e of c)o(e),yield s(t,"View video","videos"),yield*this.openVideo(t,e),await n(500);yield s(t,"TikTok Profile Behavior Complete")}}u.id="TikTokProfile"},739:(t,e,i)=>{"use strict";i.r(e),i.d(e,{TwitterTimelineBehavior:()=>g});const o="//h1[@role='heading' and @aria-level='1']/following-sibling::div[@aria-label]/*[1]",s=".//article",n="string(.//article//a[starts-with(@href, '/') and @aria-label]/@href)",a="child::div[.//a[@href='$1']]",r=".//div[@role='button' and not(@aria-haspopup) and not(@data-testid)]",l=".//div[@role='blockquote' and @aria-haspopup='false']",c=".//a[@role='link' and starts-with(@href, '/') and contains(@href, '/photo/')]",d="//div[@aria-roledescription='carousel']/div[2]/div[1]//div[@role='button']",h="//div[@aria-roledescription='carousel']/div[2]/div[2]//div[@role='button']",u="//div[@role='presentation']/div[@role='button' and @aria-label]",w="//div[@data-testid='titleContainer']//div[@role='button']",m=".//a[@href='/settings/content_you_see']/parent::div/parent::div/parent::div//div[@role='button']",f=".//*[@role='progressbar']",p=".//div[data-testid='placementTracking']";class g{static isMatch(){return window.location.href.match(/https:\/\/(www\.)?twitter\.com\//)}static init(){return{state:{tweets:0,images:0,videos:0},opts:{maxDepth:0}}}constructor(){this.seenTweets=new Set,this.seenMediaTweets=new Set}showingProgressBar(t,e){const{xpathNode:i}=t.Lib,o=i(f,e);return!!o&&o.clientHeight>10}async waitForNext(t,e){const{sleep:i,waitUnit:o}=t.Lib;if(!e)return null;if(await i(2*o),!e.nextElementSibling)return null;for(;this.showingProgressBar(t,e.nextElementSibling);)await i(o);return e.nextElementSibling}async expandMore(t,e){const{sleep:i,waitUnit:o,xpathNode:s}=t.Lib,n=s(r,e);if(!n)return e;const a=e.previousElementSibling;for(n.click(),await i(o);this.showingProgressBar(t,a.nextElementSibling);)await i(o);return e=a.nextElementSibling}async*infScroll(t){const{scrollIntoView:e,RestoreState:i,sleep:l,waitUnit:c,xpathNode:d}=t.Lib;let h=d(o);if(!h)return;let u=h.firstElementChild;if(u)for(;u;){let h=d(s,u);if(!h&&r&&(u=await this.expandMore(t,u),h=d(s,u)),u&&u.innerText&&e(u),u&&h){await l(c);const t=new i(n,u);yield h,t.matchValue&&(u=await t.restore(o,a))}u=await this.waitForNext(t,u)}}async*mediaPlaying(t,e){const{getState:i,sleep:o,xpathNode:s,xpathString:a}=t.Lib,r=s("(.//video | .//audio)",e);if(!r||r.paused)return;let l,c=null;try{c=new URL(a(n,e.parentElement),window.location.origin).href}catch(t){console.warn(t)}if(r.src.startsWith("https://")&&r.src.indexOf(".mp4")>0)return void(yield i(t,`Loading video for ${c||"unknown"}`,"videos"));if(c){if(this.seenMediaTweets.has(c))return;l=`Waiting for media playback for ${c} to finish`,this.seenMediaTweets.add(c)}else l="Loading video";yield i(t,l,"videos");const d=new Promise((t=>{r.addEventListener("ended",(()=>t(null))),r.addEventListener("abort",(()=>t(null))),r.addEventListener("error",(()=>t(null))),r.addEventListener("pause",(()=>t(null)))}));await Promise.race([d,o(6e4)])}async*clickImages(t,e){const{getState:i,HistoryState:o,sleep:s,waitUnit:n,xpathNode:a}=t.Lib,r=a(c,e);if(r){const e=new o((()=>r.click()));yield i(t,"Loading Image: "+window.location.href,"images"),await s(5*n);let l=a(d),c=window.location.href;for(;l;){if(l.click(),await s(2*n),window.location.href===c){await s(5*n);break}c=window.location.href,yield i(t,"Loading Image: "+window.location.href,"images"),await s(5*n),l=a(h)}await e.goBack(u)}}async*clickTweet(t,e,i){const{getState:o,HistoryState:s,sleep:n,waitUnit:a}=t.Lib,r=new s((()=>e.click()));if(await n(a),r.changed){yield o(t,"Capturing Tweet: "+window.location.href,"tweets");i{var o={".":607,"./":607,"./autofetcher":894,"./autofetcher.ts":894,"./autoplay":376,"./autoplay.ts":376,"./autoscroll":234,"./autoscroll.ts":234,"./index":607,"./index.ts":607,"./lib/behavior":841,"./lib/behavior.ts":841,"./lib/utils":721,"./lib/utils.ts":721,"./site":954,"./site/":954,"./site/facebook":121,"./site/facebook.ts":121,"./site/index":954,"./site/index.ts":954,"./site/instagram":741,"./site/instagram.ts":741,"./site/telegram":667,"./site/telegram.ts":667,"./site/tiktok":714,"./site/tiktok.ts":714,"./site/twitter":739,"./site/twitter.ts":739};function s(t){return Promise.resolve().then((()=>{if(!i.o(o,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}var s=o[t];return i(s)}))}s.keys=()=>Object.keys(o),s.id=75,t.exports=s}},e={};function i(o){var s=e[o];if(void 0!==s)return s.exports;var n=e[o]={exports:{}};return t[o](n,n.exports,i),n.exports}i.d=(t,e)=>{for(var o in e)i.o(e,o)&&!i.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},i.e=()=>Promise.resolve(),i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),i.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},(()=>{"use strict";i(607)})()})(); \ No newline at end of file diff --git a/src/site/instagram.ts b/src/site/instagram.ts index c422191..b794241 100644 --- a/src/site/instagram.ts +++ b/src/site/instagram.ts @@ -1,249 +1,351 @@ -const subpostNextOnlyChevron = "//article[@role='presentation']//div[@role='presentation']/following-sibling::button"; +const subpostNextOnlyChevron = + "//article[@role='presentation']//div[@role='presentation']/following-sibling::button"; const Q = { - rootPath: "//article/div/div", - childMatchSelect: "string(.//a[starts-with(@href, '/')]/@href)", - childMatch: "child::div[.//a[@href='$1']]", - firstPostInRow: "div[1]/a", - postCloseButton: "/html/body/div[last()]/div[1]/button[.//*[@aria-label]]", - nextPost: "//button[.//*[local-name() = 'svg' and @aria-label='Next']]", - postLoading: "//*[@aria-label='Loading...']", - subpostNextOnlyChevron, - subpostPrevNextChevron: subpostNextOnlyChevron + "[2]", - commentRoot: "//article[@role='presentation']/div[1]/div[2]//ul", - viewReplies: "//li//button[span[not(count(*)) and text()!='$1']]", - loadMore: "//button[span[@aria-label]]", + rootPath: "//article/div/div", + childMatchSelect: "string(.//a[starts-with(@href, '/')]/@href)", + childMatch: "child::div[.//a[@href='$1']]", + firstPostInRow: "div[1]/a", + postCloseButton: "/html/body/div[last()]/div[1]/button[.//*[@aria-label]]", + nextPost: "//button[.//*[local-name() = 'svg' and @aria-label='Next']]", + postLoading: "//*[@aria-label='Loading...']", + subpostNextOnlyChevron, + subpostPrevNextChevron: subpostNextOnlyChevron + "[2]", + commentRoot: "//article[@role='presentation']/div[1]/div[2]//ul", + viewReplies: "//li//button[span[not(count(*)) and text()!='$1']]", + loadMore: "//button[span[@aria-label]]", + stories: "//div[@role='menu']//li[@tabindex='-1']", + storyClickTarget: ".//div[@role='menuitem']", + storiesNext: "//section/div/button[2]", + lastStoryProgress: "//div/section//header/div[1]/div[last()]/*[@style]", + nextImageInStory: "//section/div/button[last()]", + storyCloseButton: "//section/div/div[@tabindex]", + nextStory: "//div[@role='menu']//button[last()]", }; export class InstagramPostsBehavior { - maxCommentsTime: number; - postOnlyWindow: any; + maxCommentsTime: number; + postOnlyWindow: any; + + static id = "Instagram"; + + static isMatch() { + return window.location.href.match( + /https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/ + ); + } - static id = "Instagram"; + static init() { + return { + state: { + posts: 0, + slides: 0, + rows: 0, + comments: 0, + stories: 0, + }, + }; + } - static isMatch() { - return window.location.href.match(/https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/); - } + constructor() { + this.maxCommentsTime = 10000; + // extra window for first post, if allowed + this.postOnlyWindow = null; + } - static init() { - return { - state: { - posts: 0, - slides: 0, - rows: 0, - comments: 0, - } - }; - } + cleanup() { + if (this.postOnlyWindow) { + this.postOnlyWindow.close(); + this.postOnlyWindow = null; + } + } + + async waitForNext(ctx, child) { + if (!child) { + return null; + } + + await ctx.Lib.sleep(ctx.Lib.waitUnit); - constructor() { - this.maxCommentsTime = 10000; - // extra window for first post, if allowed - this.postOnlyWindow = null; - } + if (!child.nextElementSibling) { + return null; + } - cleanup() { - if (this.postOnlyWindow) { - this.postOnlyWindow.close(); - this.postOnlyWindow = null; - } - } + return child.nextElementSibling; + } - async waitForNext(ctx, child) { - if (!child) { - return null; - } + async *iterRow(ctx) { + const { RestoreState, sleep, waitUnit, xpathNode } = ctx.Lib; + let root = xpathNode(Q.rootPath); - await ctx.Lib.sleep(ctx.Lib.waitUnit); + if (!root) { + return; + } - if (!child.nextElementSibling) { - return null; - } + let child = root.firstElementChild; - return child.nextElementSibling; - } + if (!child) { + return; + } - async* iterRow(ctx) { - const { RestoreState, sleep, waitUnit, xpathNode } = ctx.Lib; - let root = xpathNode(Q.rootPath); + while (child) { + await sleep(waitUnit); - if (!root) { - return; - } + const restorer = new RestoreState(Q.childMatchSelect, child); - let child = root.firstElementChild; + if (restorer.matchValue) { + yield child; - if (!child) { - return; - } + child = await restorer.restore(Q.rootPath, Q.childMatch); + } - while (child) { - await sleep(waitUnit); + child = await this.waitForNext(ctx, child); + } + } - const restorer = new RestoreState(Q.childMatchSelect, child); + async *viewStandalonePost(ctx, origLoc) { + const { getState, sleep, waitUnit, waitUntil, xpathNode, xpathString } = + ctx.Lib; + let root = xpathNode(Q.rootPath); - if (restorer.matchValue) { - yield child; + if (!root || !root.firstElementChild) { + return; + } - child = await restorer.restore(Q.rootPath, Q.childMatch); - } + const firstPostHref = xpathString( + Q.childMatchSelect, + root.firstElementChild + ); - child = await this.waitForNext(ctx, child); - } - } + yield getState( + ctx, + "Loading single post view for first post: " + firstPostHref + ); - async* viewStandalonePost(ctx, origLoc) { - const { getState, sleep, waitUnit, waitUntil, xpathNode, xpathString } = ctx.Lib; - let root = xpathNode(Q.rootPath); + window.history.replaceState({}, "", firstPostHref); + window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); - if (!root || !root.firstElementChild) { - return; - } + let root2 = null; + let root3 = null; - const firstPostHref = xpathString(Q.childMatchSelect, root.firstElementChild); + await sleep(waitUnit * 5); - yield getState(ctx, "Loading single post view for first post: " + firstPostHref); + await waitUntil( + () => (root2 = xpathNode(Q.rootPath)) !== root && root2, + waitUnit * 5 + ); - window.history.replaceState({}, "", firstPostHref); - window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); + await sleep(waitUnit * 5); - let root2 = null; - let root3 = null; + window.history.replaceState({}, "", origLoc); + window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); - await sleep(waitUnit * 5); + await waitUntil( + () => (root3 = xpathNode(Q.rootPath)) !== root2 && root3, + waitUnit * 5 + ); + //} + } - await waitUntil(() => (root2 = xpathNode(Q.rootPath)) !== root && root2, waitUnit * 5); + async *iterSubposts(ctx) { + const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; + let next = xpathNode(Q.subpostNextOnlyChevron); - await sleep(waitUnit * 5); + let count = 1; - window.history.replaceState({}, "", origLoc); - window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); + while (next) { + next.click(); + await sleep(waitUnit * 5); - await waitUntil(() => (root3 = xpathNode(Q.rootPath)) !== root2 && root3, waitUnit * 5); - //} - } + yield getState( + ctx, + `Loading Slide ${++count} for ${window.location.href}`, + "slides" + ); - async *iterSubposts(ctx) { - const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; - let next = xpathNode(Q.subpostNextOnlyChevron); + next = xpathNode(Q.subpostPrevNextChevron); + } - let count = 1; + await sleep(waitUnit * 5); + } - while (next) { - next.click(); - await sleep(waitUnit * 5); + async iterComments(ctx) { + const { scrollIntoView, sleep, waitUnit, waitUntil, xpathNode } = ctx.Lib; + const root = xpathNode(Q.commentRoot); - yield getState(ctx, `Loading Slide ${++count} for ${window.location.href}`, "slides"); + if (!root) { + return; + } - next = xpathNode(Q.subpostPrevNextChevron); - } + let child = root.firstElementChild; - await sleep(waitUnit * 5); - } + let commentsLoaded = false; - async iterComments(ctx) { - const { scrollIntoView, sleep, waitUnit, waitUntil, xpathNode } = ctx.Lib; - const root = xpathNode(Q.commentRoot); + let text = ""; - if (!root) { - return; - } + while (child) { + scrollIntoView(child); - let child = root.firstElementChild; + commentsLoaded = true; - let commentsLoaded = false; + let viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); - let text = ""; + while (viewReplies) { + const orig = viewReplies.textContent; + viewReplies.click(); + ctx.state.comments++; + await sleep(waitUnit * 2.5); - while (child) { - scrollIntoView(child); + await waitUntil(() => orig !== viewReplies.textContent, waitUnit); - commentsLoaded = true; + text = viewReplies.textContent; + viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); + } - let viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); + if ( + child.nextElementSibling && + child.nextElementSibling.tagName === "LI" && + !child.nextElementSibling.nextElementSibling + ) { + let loadMore = xpathNode(Q.loadMore, child.nextElementSibling); + if (loadMore) { + loadMore.click(); + ctx.state.comments++; + await sleep(waitUnit * 5); + } + } - while (viewReplies) { - const orig = viewReplies.textContent; - viewReplies.click(); - ctx.state.comments++; - await sleep(waitUnit * 2.5); + child = child.nextElementSibling; + await sleep(waitUnit * 2.5); + } - await waitUntil(() => orig !== viewReplies.textContent, waitUnit); + return commentsLoaded; + } - text = viewReplies.textContent; - viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); - } + async *iterPosts(ctx, next) { + const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; + let count = 0; - if (child.nextElementSibling && child.nextElementSibling.tagName === "LI" && !child.nextElementSibling.nextElementSibling) { - let loadMore = xpathNode(Q.loadMore, child.nextElementSibling); - if (loadMore) { - loadMore.click(); - ctx.state.comments++; - await sleep(waitUnit * 5); - } - } + while (next && ++count <= 3) { + next.click(); + await sleep(waitUnit * 10); - child = child.nextElementSibling; - await sleep(waitUnit * 2.5); - } + yield getState(ctx, "Loading Post: " + window.location.href, "posts"); - return commentsLoaded; - } + await fetch(window.location.href); - async* iterPosts(ctx, next) { - const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; - let count = 0; + yield* this.iterSubposts(ctx); - while (next && ++count <= 3) { - next.click(); - await sleep(waitUnit * 10); + yield getState(ctx, "Loaded Comments", "comments"); - yield getState(ctx, "Loading Post: " + window.location.href, "posts"); + await Promise.race([this.iterComments(ctx), sleep(this.maxCommentsTime)]); - await fetch(window.location.href); + next = xpathNode(Q.nextPost); - yield* this.iterSubposts(ctx); + while (!next && xpathNode(Q.postLoading)) { + await sleep(waitUnit * 2.5); + } + } - yield getState(ctx, "Loaded Comments", "comments"); + await sleep(waitUnit * 5); + } - await Promise.race([ - this.iterComments(ctx), - sleep(this.maxCommentsTime) - ]); + async *iterStory(ctx, story) { + const { getState, sleep, waitUnit, xpathNode, waitUntil } = ctx.Lib; + let node = xpathNode(`//li[contains(@style, '${story}')]`); - next = xpathNode(Q.nextPost); + let nextButton = xpathNode(Q.nextStory); + while (node == null && nextButton != null) { + nextButton.click(); - while (!next && xpathNode(Q.postLoading)) { - await sleep(waitUnit * 2.5); - } - } + node = xpathNode(`//li[contains(@style, '${story}')]`); - await sleep(waitUnit * 5); - } + nextButton = xpathNode(Q.nextStory); - async* run(ctx) { - const { getState, scrollIntoView, sleep, waitUnit, xpathNode } = ctx.Lib; - const origLoc = window.location.href; + yield; + } - yield* this.viewStandalonePost(ctx, origLoc); + const target = node.firstChild.firstChild; - for await (const row of this.iterRow(ctx)) { - scrollIntoView(row); + target.scrollIntoView(); - await sleep(waitUnit * 2.5); + await sleep(waitUnit * 2); - yield getState(ctx, "Loading Row", "rows"); + target.click(); - const first = xpathNode(Q.firstPostInRow, row); + await waitUntil(() => xpathNode(Q.storiesNext) != null, waitUnit * 2); - yield* this.iterPosts(ctx, first); + let lastStoryIndicator = xpathNode(Q.lastStoryProgress); - const close = xpathNode(Q.postCloseButton); - if (close) { - close.click(); - } + const nextStoryButton = xpathNode(Q.nextImageInStory); - await sleep(waitUnit * 5); - } - } + while (lastStoryIndicator == null) { + nextStoryButton.click(); + + await sleep(waitUnit * 5); + + lastStoryIndicator = xpathNode(Q.lastStoryProgress); + } + + const closeTarget = xpathNode(Q.storyCloseButton); + + closeTarget.click(); + + await sleep(waitUnit * 10); + + yield getState("Loaded story", "stories"); + } + + async *iterStories(ctx) { + const { xpathNode } = ctx.Lib; + let storyNode = xpathNode(Q.stories); + + if (!storyNode) return; + + while (storyNode) { + yield storyNode; + storyNode = storyNode.nextElementSibling; + } + } + + async *run(ctx) { + const { getState, scrollIntoView, sleep, waitUnit, xpathNode } = ctx.Lib; + const origLoc = window.location.href; + + const storySelector = []; + for await (const story of this.iterStories(ctx)) { + story.scrollIntoView({ behavior: "smooth" }); + + yield getState(`Found story`); + + await sleep(waitUnit * 5); + + storySelector.push(story.getAttribute("style")); + } + + for (const selector of storySelector) { + yield* this.iterStory(ctx, selector); + } + + yield* this.viewStandalonePost(ctx, origLoc); + + for await (const row of this.iterRow(ctx)) { + scrollIntoView(row); + + await sleep(waitUnit * 2.5); + + yield getState(ctx, "Loading Row", "rows"); + + const first = xpathNode(Q.firstPostInRow, row); + + yield* this.iterPosts(ctx, first); + + const close = xpathNode(Q.postCloseButton); + if (close) { + close.click(); + } + + await sleep(waitUnit * 5); + } + } } From 62639177575a0ca84ec4c30957bad06fa96a1ae7 Mon Sep 17 00:00:00 2001 From: Johan Flensmark Date: Wed, 19 Apr 2023 09:28:58 +0200 Subject: [PATCH 2/2] Fix lint --- src/site/instagram.ts | 506 +++++++++++++++++++++--------------------- 1 file changed, 253 insertions(+), 253 deletions(-) diff --git a/src/site/instagram.ts b/src/site/instagram.ts index b794241..ba9f458 100644 --- a/src/site/instagram.ts +++ b/src/site/instagram.ts @@ -1,351 +1,351 @@ const subpostNextOnlyChevron = - "//article[@role='presentation']//div[@role='presentation']/following-sibling::button"; + "//article[@role='presentation']//div[@role='presentation']/following-sibling::button"; const Q = { - rootPath: "//article/div/div", - childMatchSelect: "string(.//a[starts-with(@href, '/')]/@href)", - childMatch: "child::div[.//a[@href='$1']]", - firstPostInRow: "div[1]/a", - postCloseButton: "/html/body/div[last()]/div[1]/button[.//*[@aria-label]]", - nextPost: "//button[.//*[local-name() = 'svg' and @aria-label='Next']]", - postLoading: "//*[@aria-label='Loading...']", - subpostNextOnlyChevron, - subpostPrevNextChevron: subpostNextOnlyChevron + "[2]", - commentRoot: "//article[@role='presentation']/div[1]/div[2]//ul", - viewReplies: "//li//button[span[not(count(*)) and text()!='$1']]", - loadMore: "//button[span[@aria-label]]", - stories: "//div[@role='menu']//li[@tabindex='-1']", - storyClickTarget: ".//div[@role='menuitem']", - storiesNext: "//section/div/button[2]", - lastStoryProgress: "//div/section//header/div[1]/div[last()]/*[@style]", - nextImageInStory: "//section/div/button[last()]", - storyCloseButton: "//section/div/div[@tabindex]", - nextStory: "//div[@role='menu']//button[last()]", + rootPath: "//article/div/div", + childMatchSelect: "string(.//a[starts-with(@href, '/')]/@href)", + childMatch: "child::div[.//a[@href='$1']]", + firstPostInRow: "div[1]/a", + postCloseButton: "/html/body/div[last()]/div[1]/button[.//*[@aria-label]]", + nextPost: "//button[.//*[local-name() = 'svg' and @aria-label='Next']]", + postLoading: "//*[@aria-label='Loading...']", + subpostNextOnlyChevron, + subpostPrevNextChevron: subpostNextOnlyChevron + "[2]", + commentRoot: "//article[@role='presentation']/div[1]/div[2]//ul", + viewReplies: "//li//button[span[not(count(*)) and text()!='$1']]", + loadMore: "//button[span[@aria-label]]", + stories: "//div[@role='menu']//li[@tabindex='-1']", + storyClickTarget: ".//div[@role='menuitem']", + storiesNext: "//section/div/button[2]", + lastStoryProgress: "//div/section//header/div[1]/div[last()]/*[@style]", + nextImageInStory: "//section/div/button[last()]", + storyCloseButton: "//section/div/div[@tabindex]", + nextStory: "//div[@role='menu']//button[last()]", }; export class InstagramPostsBehavior { - maxCommentsTime: number; - postOnlyWindow: any; - - static id = "Instagram"; - - static isMatch() { - return window.location.href.match( - /https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/ - ); - } + maxCommentsTime: number; + postOnlyWindow: any; + + static id = "Instagram"; + + static isMatch() { + return window.location.href.match( + /https:\/\/(www\.)?instagram\.com\/\w[\w.-]+/ + ); + } - static init() { - return { - state: { - posts: 0, - slides: 0, - rows: 0, - comments: 0, - stories: 0, - }, - }; - } + static init() { + return { + state: { + posts: 0, + slides: 0, + rows: 0, + comments: 0, + stories: 0, + }, + }; + } - constructor() { - this.maxCommentsTime = 10000; - // extra window for first post, if allowed - this.postOnlyWindow = null; - } + constructor() { + this.maxCommentsTime = 10000; + // extra window for first post, if allowed + this.postOnlyWindow = null; + } - cleanup() { - if (this.postOnlyWindow) { - this.postOnlyWindow.close(); - this.postOnlyWindow = null; - } - } - - async waitForNext(ctx, child) { - if (!child) { - return null; - } - - await ctx.Lib.sleep(ctx.Lib.waitUnit); + cleanup() { + if (this.postOnlyWindow) { + this.postOnlyWindow.close(); + this.postOnlyWindow = null; + } + } + + async waitForNext(ctx, child) { + if (!child) { + return null; + } + + await ctx.Lib.sleep(ctx.Lib.waitUnit); - if (!child.nextElementSibling) { - return null; - } + if (!child.nextElementSibling) { + return null; + } - return child.nextElementSibling; - } + return child.nextElementSibling; + } - async *iterRow(ctx) { - const { RestoreState, sleep, waitUnit, xpathNode } = ctx.Lib; - let root = xpathNode(Q.rootPath); + async *iterRow(ctx) { + const { RestoreState, sleep, waitUnit, xpathNode } = ctx.Lib; + let root = xpathNode(Q.rootPath); - if (!root) { - return; - } + if (!root) { + return; + } - let child = root.firstElementChild; + let child = root.firstElementChild; - if (!child) { - return; - } + if (!child) { + return; + } - while (child) { - await sleep(waitUnit); + while (child) { + await sleep(waitUnit); - const restorer = new RestoreState(Q.childMatchSelect, child); + const restorer = new RestoreState(Q.childMatchSelect, child); - if (restorer.matchValue) { - yield child; + if (restorer.matchValue) { + yield child; - child = await restorer.restore(Q.rootPath, Q.childMatch); - } + child = await restorer.restore(Q.rootPath, Q.childMatch); + } - child = await this.waitForNext(ctx, child); - } - } + child = await this.waitForNext(ctx, child); + } + } - async *viewStandalonePost(ctx, origLoc) { - const { getState, sleep, waitUnit, waitUntil, xpathNode, xpathString } = - ctx.Lib; - let root = xpathNode(Q.rootPath); + async *viewStandalonePost(ctx, origLoc) { + const { getState, sleep, waitUnit, waitUntil, xpathNode, xpathString } = + ctx.Lib; + let root = xpathNode(Q.rootPath); - if (!root || !root.firstElementChild) { - return; - } + if (!root || !root.firstElementChild) { + return; + } - const firstPostHref = xpathString( - Q.childMatchSelect, - root.firstElementChild - ); + const firstPostHref = xpathString( + Q.childMatchSelect, + root.firstElementChild + ); - yield getState( - ctx, - "Loading single post view for first post: " + firstPostHref - ); + yield getState( + ctx, + "Loading single post view for first post: " + firstPostHref + ); - window.history.replaceState({}, "", firstPostHref); - window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); + window.history.replaceState({}, "", firstPostHref); + window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); - let root2 = null; - let root3 = null; + let root2 = null; + let root3 = null; - await sleep(waitUnit * 5); + await sleep(waitUnit * 5); - await waitUntil( - () => (root2 = xpathNode(Q.rootPath)) !== root && root2, - waitUnit * 5 - ); + await waitUntil( + () => (root2 = xpathNode(Q.rootPath)) !== root && root2, + waitUnit * 5 + ); - await sleep(waitUnit * 5); + await sleep(waitUnit * 5); - window.history.replaceState({}, "", origLoc); - window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); + window.history.replaceState({}, "", origLoc); + window.dispatchEvent(new PopStateEvent("popstate", { state: {} })); - await waitUntil( - () => (root3 = xpathNode(Q.rootPath)) !== root2 && root3, - waitUnit * 5 - ); - //} - } + await waitUntil( + () => (root3 = xpathNode(Q.rootPath)) !== root2 && root3, + waitUnit * 5 + ); + //} + } - async *iterSubposts(ctx) { - const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; - let next = xpathNode(Q.subpostNextOnlyChevron); + async *iterSubposts(ctx) { + const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; + let next = xpathNode(Q.subpostNextOnlyChevron); - let count = 1; + let count = 1; - while (next) { - next.click(); - await sleep(waitUnit * 5); + while (next) { + next.click(); + await sleep(waitUnit * 5); - yield getState( - ctx, - `Loading Slide ${++count} for ${window.location.href}`, - "slides" - ); + yield getState( + ctx, + `Loading Slide ${++count} for ${window.location.href}`, + "slides" + ); - next = xpathNode(Q.subpostPrevNextChevron); - } + next = xpathNode(Q.subpostPrevNextChevron); + } - await sleep(waitUnit * 5); - } + await sleep(waitUnit * 5); + } - async iterComments(ctx) { - const { scrollIntoView, sleep, waitUnit, waitUntil, xpathNode } = ctx.Lib; - const root = xpathNode(Q.commentRoot); + async iterComments(ctx) { + const { scrollIntoView, sleep, waitUnit, waitUntil, xpathNode } = ctx.Lib; + const root = xpathNode(Q.commentRoot); - if (!root) { - return; - } + if (!root) { + return; + } - let child = root.firstElementChild; + let child = root.firstElementChild; - let commentsLoaded = false; + let commentsLoaded = false; - let text = ""; + let text = ""; - while (child) { - scrollIntoView(child); + while (child) { + scrollIntoView(child); - commentsLoaded = true; + commentsLoaded = true; - let viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); + let viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); - while (viewReplies) { - const orig = viewReplies.textContent; - viewReplies.click(); - ctx.state.comments++; - await sleep(waitUnit * 2.5); + while (viewReplies) { + const orig = viewReplies.textContent; + viewReplies.click(); + ctx.state.comments++; + await sleep(waitUnit * 2.5); - await waitUntil(() => orig !== viewReplies.textContent, waitUnit); + await waitUntil(() => orig !== viewReplies.textContent, waitUnit); - text = viewReplies.textContent; - viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); - } + text = viewReplies.textContent; + viewReplies = xpathNode(Q.viewReplies.replace("$1", text), child); + } - if ( - child.nextElementSibling && - child.nextElementSibling.tagName === "LI" && - !child.nextElementSibling.nextElementSibling - ) { - let loadMore = xpathNode(Q.loadMore, child.nextElementSibling); - if (loadMore) { - loadMore.click(); - ctx.state.comments++; - await sleep(waitUnit * 5); - } - } + if ( + child.nextElementSibling && + child.nextElementSibling.tagName === "LI" && + !child.nextElementSibling.nextElementSibling + ) { + let loadMore = xpathNode(Q.loadMore, child.nextElementSibling); + if (loadMore) { + loadMore.click(); + ctx.state.comments++; + await sleep(waitUnit * 5); + } + } - child = child.nextElementSibling; - await sleep(waitUnit * 2.5); - } + child = child.nextElementSibling; + await sleep(waitUnit * 2.5); + } - return commentsLoaded; - } + return commentsLoaded; + } - async *iterPosts(ctx, next) { - const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; - let count = 0; + async *iterPosts(ctx, next) { + const { getState, sleep, waitUnit, xpathNode } = ctx.Lib; + let count = 0; - while (next && ++count <= 3) { - next.click(); - await sleep(waitUnit * 10); + while (next && ++count <= 3) { + next.click(); + await sleep(waitUnit * 10); - yield getState(ctx, "Loading Post: " + window.location.href, "posts"); + yield getState(ctx, "Loading Post: " + window.location.href, "posts"); - await fetch(window.location.href); + await fetch(window.location.href); - yield* this.iterSubposts(ctx); + yield* this.iterSubposts(ctx); - yield getState(ctx, "Loaded Comments", "comments"); + yield getState(ctx, "Loaded Comments", "comments"); - await Promise.race([this.iterComments(ctx), sleep(this.maxCommentsTime)]); + await Promise.race([this.iterComments(ctx), sleep(this.maxCommentsTime)]); - next = xpathNode(Q.nextPost); + next = xpathNode(Q.nextPost); - while (!next && xpathNode(Q.postLoading)) { - await sleep(waitUnit * 2.5); - } - } + while (!next && xpathNode(Q.postLoading)) { + await sleep(waitUnit * 2.5); + } + } - await sleep(waitUnit * 5); - } + await sleep(waitUnit * 5); + } - async *iterStory(ctx, story) { - const { getState, sleep, waitUnit, xpathNode, waitUntil } = ctx.Lib; - let node = xpathNode(`//li[contains(@style, '${story}')]`); + async *iterStory(ctx, story) { + const { getState, sleep, waitUnit, xpathNode, waitUntil } = ctx.Lib; + let node = xpathNode(`//li[contains(@style, '${story}')]`); - let nextButton = xpathNode(Q.nextStory); - while (node == null && nextButton != null) { - nextButton.click(); + let nextButton = xpathNode(Q.nextStory); + while (node == null && nextButton != null) { + nextButton.click(); - node = xpathNode(`//li[contains(@style, '${story}')]`); + node = xpathNode(`//li[contains(@style, '${story}')]`); - nextButton = xpathNode(Q.nextStory); + nextButton = xpathNode(Q.nextStory); - yield; - } + yield; + } - const target = node.firstChild.firstChild; + const target = node.firstChild.firstChild; - target.scrollIntoView(); + target.scrollIntoView(); - await sleep(waitUnit * 2); + await sleep(waitUnit * 2); - target.click(); + target.click(); - await waitUntil(() => xpathNode(Q.storiesNext) != null, waitUnit * 2); + await waitUntil(() => xpathNode(Q.storiesNext) != null, waitUnit * 2); - let lastStoryIndicator = xpathNode(Q.lastStoryProgress); + let lastStoryIndicator = xpathNode(Q.lastStoryProgress); - const nextStoryButton = xpathNode(Q.nextImageInStory); + const nextStoryButton = xpathNode(Q.nextImageInStory); - while (lastStoryIndicator == null) { - nextStoryButton.click(); + while (lastStoryIndicator == null) { + nextStoryButton.click(); - await sleep(waitUnit * 5); + await sleep(waitUnit * 5); - lastStoryIndicator = xpathNode(Q.lastStoryProgress); - } + lastStoryIndicator = xpathNode(Q.lastStoryProgress); + } - const closeTarget = xpathNode(Q.storyCloseButton); + const closeTarget = xpathNode(Q.storyCloseButton); - closeTarget.click(); + closeTarget.click(); - await sleep(waitUnit * 10); + await sleep(waitUnit * 10); - yield getState("Loaded story", "stories"); - } + yield getState("Loaded story", "stories"); + } - async *iterStories(ctx) { - const { xpathNode } = ctx.Lib; - let storyNode = xpathNode(Q.stories); + async *iterStories(ctx) { + const { xpathNode } = ctx.Lib; + let storyNode = xpathNode(Q.stories); - if (!storyNode) return; + if (!storyNode) return; - while (storyNode) { - yield storyNode; - storyNode = storyNode.nextElementSibling; - } - } + while (storyNode) { + yield storyNode; + storyNode = storyNode.nextElementSibling; + } + } - async *run(ctx) { - const { getState, scrollIntoView, sleep, waitUnit, xpathNode } = ctx.Lib; - const origLoc = window.location.href; + async *run(ctx) { + const { getState, scrollIntoView, sleep, waitUnit, xpathNode } = ctx.Lib; + const origLoc = window.location.href; - const storySelector = []; - for await (const story of this.iterStories(ctx)) { - story.scrollIntoView({ behavior: "smooth" }); + const storySelector = []; + for await (const story of this.iterStories(ctx)) { + story.scrollIntoView({ behavior: "smooth" }); - yield getState(`Found story`); + yield getState("Found story"); - await sleep(waitUnit * 5); + await sleep(waitUnit * 5); - storySelector.push(story.getAttribute("style")); - } + storySelector.push(story.getAttribute("style")); + } - for (const selector of storySelector) { - yield* this.iterStory(ctx, selector); - } + for (const selector of storySelector) { + yield* this.iterStory(ctx, selector); + } - yield* this.viewStandalonePost(ctx, origLoc); + yield* this.viewStandalonePost(ctx, origLoc); - for await (const row of this.iterRow(ctx)) { - scrollIntoView(row); + for await (const row of this.iterRow(ctx)) { + scrollIntoView(row); - await sleep(waitUnit * 2.5); + await sleep(waitUnit * 2.5); - yield getState(ctx, "Loading Row", "rows"); + yield getState(ctx, "Loading Row", "rows"); - const first = xpathNode(Q.firstPostInRow, row); + const first = xpathNode(Q.firstPostInRow, row); - yield* this.iterPosts(ctx, first); + yield* this.iterPosts(ctx, first); - const close = xpathNode(Q.postCloseButton); - if (close) { - close.click(); - } + const close = xpathNode(Q.postCloseButton); + if (close) { + close.click(); + } - await sleep(waitUnit * 5); - } - } + await sleep(waitUnit * 5); + } + } }