kheina-com / Blue-Blocker

Blocks all Twitter Blue verified users on twitter.com
Mozilla Public License 2.0
337 stars 28 forks source link

'Skip legacy verified users' stops all user collection on v0.3.1 #156

Closed Anyasia closed 1 year ago

Anyasia commented 1 year ago

After I updated the extension to v0.3.1, I noticed it was no longer queuing any users to be blocked. The issue persisted after restarting Chrome, so I began playing with the options, and found that unchecking the 'skip legacy verified users' option resulted in the resumption of collection. Since many of the immediately-queued accounts were brand new, they certainly weren't legacy accounts.

For what it's worth, having noted the warning under the 'block users with promoted tweets' checkbox, I also tried disabling that while leaving 'skip legacy verified users' checked, to no avail. Only disabling 'skip legacy verified users' worked.

I'm using Google Chrome 109.0.5414.149.

Thanks for your time!

kheina commented 1 year ago

the latest extension versions are v0.3.0 and v0.3.1, so double check which extension version you have (it's in the context menu) and upgrade to v0.3.1 if you're not already on that version.

to get debugging information for the legacy users database please follow these steps, it'll help us debug the issue you're having:

  1. click the puzzle piece in the top right corner (next to chrome's omnibar) and click "Manage Extensions" at the bottom, this should take you to a page with all your extensions on it
  2. In the top right of the page, click "Developer mode" with a little toggle. this will show all the extension IDs as well as other things
  3. find Blue Blocker in this list and click the link next to "Inspect views" it should say "service worker (Inactive)" or just "service worker"

you should be able to right click anywhere on the console and download a log of all of the output. this is the service that downloads the legacy verified users database for that option.

if you could also go to your twitter page and look for any [Blue Blocker] labelled errors in the console there as well, it would be very helpful. (don't download the whole console here since it can contain sensitive information)

for the time being, however, you can set your follower threshold to something like 10k and that should also skip all legacy verifications as well

Anyasia commented 1 year ago

The version number was definitely a me problem, sorry; I've edited the issue accordingly.

There are no Blue Blocker logs whatsoever in the console on the user page while the 'skip verified' option is set and no accounts are queued.

More unfortunately, clicking the 'service worker (Inactive)' link doesn't seem to do… anything; no page is opened, nor is any error logged in the console. I tried it in a variety of extension usage states—with user collection active and paused, and with the 'skip verified' option both enabled and disabled—all to no avail. (Just for reference, I tried the same link on another extension, and it opened the console as expected.)

Installed the extension in Firefox (114.0.2 (64-bit)) and confirmed the same issue, and thankfully was able to access the debug log there:—

[Blue Blocker] soupcan response for @elonmusk: Error: Could not establish connection. Receiving end does not exist.
    <anonymous> moz-extension://5424c21d-aa2d-401a-9d35-a3b33c22c656/assets/chunk-feed8bd2.js:1
[Blue Blocker] set active tab: advanced chunk-feed8bd2.js:1:2569
[Blue Blocker] opening legacy verified user database: 
    IDBOpenDBRequest { onblocked: null, onupgradeneeded: null, source: null, transaction: null, readyState: "pending", onsuccess: null, onerror: null }
[Blue Blocker] failed to open legacy verified user database: 
    IDBOpenDBRequest { onblocked: onblocked(), onupgradeneeded: onupgradeneeded(), error: DOMException, source: null, transaction: null, readyState: "done", onsuccess: async onsuccess(), onerror: onblocked() }

The DOMException you can see it logging expands as follows:

error: DOMException: A mutation operation was attempted on a database that did not allow mutations.
code: 11
columnNumber: 0
data: null
filename: ""
lineNumber: 0
message: "A mutation operation was attempted on a database that did not allow mutations."
name: "InvalidStateError"
result: 2154168326
stack: ""
kheina commented 1 year ago

a mutation operation? that's weird. I may need to look more into the errors possible from indexed db, which is used to house the verified users db, but it never even got to the part where it tried to load all of the users, it looks like.

More unfortunately, clicking the 'service worker (Inactive)' link doesn't seem to do… anything; no page is opened, nor is any error logged in the console. I tried it in a variety of extension usage states—with user collection active and paused, and with the 'skip verified' option both enabled and disabled—all to no avail. (Just for reference, I tried the same link on another extension, and it opened the console as expected.)

I've never even heard of that happening before, it could mean that the console is frozen or something? I did notice that loading the database itself can cause it to hang but that shouldn't take more than a minute or two.

Installed the extension in Firefox (114.0.2 (64-bit)) and confirmed the same issue, and thankfully was able to access the debug log there:—

just to be clear, you loaded Blue Blocker on a browser that you don't frequently use and had never had the extension loaded onto it previously, and still got this error?

Anyasia commented 1 year ago

it could mean that the console is frozen or something?

That seems unlikely, given the reasons you posit, and given it's working for other extensions and the extension manager itself is otherwise responsive. It could very well be due to my running an outdated Chrome version (I'm still on Windows 8.1, which the latest versions of Chromium don't support), so I wouldn't necessarily concern yourself with that in particular, given the collection bug itself is also happening in Firefox.

just to be clear, you loaded Blue Blocker on a browser that you don't frequently use and had never had the extension loaded onto it previously, and still got this error?

A browser I specifically downloaded anew just to test, yes.

kheina commented 1 year ago

ok dumb suggestion but here we go. on chrome, try, specifically: 1. disabling the "skip legacy verified users" option 2. clicking the reload button on the extension (that little circular icon next to the on/off toggle) 3. try the inspect view now 4. if the inspect view works, with the inspector open and visible, enable the "skip legacy verified users"

looking at it, store-installed extensions don't even have the reload button

Anyasia commented 1 year ago

Oooh, I wondered if disabling and reënabling the extension would work in lieu of the reload button, and logged this error warning:—

Service worker registration failed. Status code: 15

{
   "action": {
      "default_icon": "icon/icon.png",
      "default_popup": "src/popup/index.html"
   },
   "background": {
      "service_worker": "service-worker-loader.js",
      "type": "module"
   },
   "content_scripts": [ {
      "js": [ "assets/content-script-loader.chunk-67987f8a.637ce6af.js" ],
      "matches": [ "*://*.twitter.com/*", "*://twitter.com/*" ]
   } ],
   "description": "Blocks all Twitter Blue verified users on twitter.com",
   "icons": {
      "128": "icon/icon-128.png"
   },
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8eGFY4bQ0K4F/v+qh1FcxvqlqoUxWWym5irISUeBqMBzWyg7WQbtT881vswjGg7xyVZdQh2/9KXHzEONjhuZ1ktJ0MSXzlTEsruHdCLzZYtbExgRYixSX+nGbbgEb+QF6EFIkJ8has6zBFP7M8IRA5497z7FBJpOyD4RNgwK2Wau/Gsh/JnRu4MOYrUWMA5MM7OMUNc/tPmDMzQnkukeyL4eXpvpz4FAKxn+gLLSsJ+j7gRh44SpsD/OoO+KzXUzILjl0RCbadW79ePX6eoGdYi/A9/YEIucjSkpsqQXNvjIv3j/bLaA6QWG8AjgIiQwaheTFH2f/6JmV0qYNFIu5QIDAQAB",
   "manifest_version": 3,
   "name": "Blue Blocker",
   "permissions": [ "storage", "unlimitedStorage" ],
   "update_url": "https://clients2.google.com/service/update2/crx",
   "version": "0.3.1",
   "web_accessible_resources": [ {
      "matches": [ "*://*.twitter.com/*", "*://twitter.com/*" ],
      "resources": [ "src/injected/*", "icon/*" ],
      "use_dynamic_url": false
   }, {
      "matches": [ "*://*.twitter.com/*", "*://twitter.com/*" ],
      "resources": [ "assets/chunk-ac68f6f0.js", "assets/chunk-22e056dd.js", "assets/chunk-a5b11d07.js", "assets/chunk-67987f8a.js" ],
      "use_dynamic_url": true
   } ]
}
kheina commented 1 year ago

there were no other errors or outputs or anything besides this? and was this with the "skip legacy verified users" option disabled?

Anyasia commented 1 year ago

It was with the option disabled, yes,

There was actually another error, sorry; I didn't notice it since it wasn't expanded. Not sure if it's relevant to the service worker, though.

Uncaught TypeError: s.action.setBadgeTextColor is not a function

import{a as s,j as h,D as k,P as p,l as f,k as w,C as y,m as g,n as B,o as u,S as C}from"./chunk-ac68f6f0.js";import{B as b}from"./chunk-22e056dd.js";s.action.setBadgeBackgroundColor({color:"#666"});s.action.setBadgeTextColor({color:"#fff"});s.storage.local.onChanged.addListener(e=>{e.hasOwnProperty("BlockCounter")&&s.action.setBadgeText({text:h(e.BlockCounter.newValue)})});s.storage.sync.get(k).then(async e=>{s.action.setIcon({path:e.suspendedBlockCollection?"/icon/icon-128-greyscale.png":"/icon/icon-128.png"}),e.skipVerified&&await p()});s.storage.sync.onChanged.addListener(async e=>{e.hasOwnProperty("skipVerified")&&e.skipVerified.newValue&&await p()});s.runtime.onMessage.addListener((e,l,d)=>((async(n,t,o)=>{switch(n==null?void 0:n.action){case w:const r=n;try{const c=await y(r.user_id,r.handle);o({status:g,result:c})}catch(c){o({status:u,message:"unknown error",error:c})}break;default:console.error(f,"got a message that couldn't be handled from sender:",t,n),o({status:u,message:"unknown action",error:null})}return!0})(e,l,d),!0));const V=new b(s.storage.local),[x]=["BLOCK"],m=new Set([C]);s.runtime.onMessageExternal.addListener((e,l,d)=>((async(n,t,o)=>{var r;if(!!m.has((r=t==null?void 0:t.id)!=null?r:""))switch(n==null?void 0:n.action){case x:const a=n;try{await V.push({user_id:a.user_id,user:{name:a.name,screen_name:a.screen_name},reason:B,external_reason:a.reason}),o({status:g,message:"user queued for blocking"})}catch(i){o({status:u,message:"unknown error",error:i})}return;default:console.error(f,"got a message that couldn't be handled from sender:",t,n),o({status:u,message:"unknown action",error:null})}})(e,l,d),!0));

(I've also updated the post above to clarify the one above was technically a warning rather than an error, as this one was.)

kheina commented 1 year ago

It was with the option disabled, yes,

There was actually another error, sorry; I didn't notice it since it wasn't expanded. Not sure if it's relevant to the service worker, though.

Uncaught TypeError: s.action.setBadgeTextColor is not a function

import{a as s,j as h,D as k,P as p,l as f,k as w,C as y,m as g,n as B,o as u,S as C}from"./chunk-ac68f6f0.js";import{B as b}from"./chunk-22e056dd.js";s.action.setBadgeBackgroundColor({color:"#666"});s.action.setBadgeTextColor({color:"#fff"});s.storage.local.onChanged.addListener(e=>{e.hasOwnProperty("BlockCounter")&&s.action.setBadgeText({text:h(e.BlockCounter.newValue)})});s.storage.sync.get(k).then(async e=>{s.action.setIcon({path:e.suspendedBlockCollection?"/icon/icon-128-greyscale.png":"/icon/icon-128.png"}),e.skipVerified&&await p()});s.storage.sync.onChanged.addListener(async e=>{e.hasOwnProperty("skipVerified")&&e.skipVerified.newValue&&await p()});s.runtime.onMessage.addListener((e,l,d)=>((async(n,t,o)=>{switch(n==null?void 0:n.action){case w:const r=n;try{const c=await y(r.user_id,r.handle);o({status:g,result:c})}catch(c){o({status:u,message:"unknown error",error:c})}break;default:console.error(f,"got a message that couldn't be handled from sender:",t,n),o({status:u,message:"unknown action",error:null})}return!0})(e,l,d),!0));const V=new b(s.storage.local),[x]=["BLOCK"],m=new Set([C]);s.runtime.onMessageExternal.addListener((e,l,d)=>((async(n,t,o)=>{var r;if(!!m.has((r=t==null?void 0:t.id)!=null?r:""))switch(n==null?void 0:n.action){case x:const a=n;try{await V.push({user_id:a.user_id,user:{name:a.name,screen_name:a.screen_name},reason:B,external_reason:a.reason}),o({status:g,message:"user queued for blocking"})}catch(i){o({status:u,message:"unknown error",error:i})}return;default:console.error(f,"got a message that couldn't be handled from sender:",t,n),o({status:u,message:"unknown action",error:null})}})(e,l,d),!0));

(I've also updated the post above to clarify the one above was technically a warning rather than an error, as this one was.)

looks like that function in particular was only added in chrome 1.10. so. if you're able to upgrade even one min version, it would fix this issue for you. if you can't, I'll have a fix out in v0.3.2 image

I can't believe how hard it was to nail down this issue. but that still leaves the firefox problem, which seems unrelated

Anyasia commented 1 year ago

Bizarre that the Firefox problem is unrelated when they end up affecting the same thing. (Mind you, I still don't know whether or not the same mutation error is occurring in the latest Chrome versions, either, though if you've not encountered it, hopefully that's a good sign.)

Whether the Chrome-specific issue is worth addressing at all is up to you; given it auto-updates and the overwhelming market share of Windows versions supporting 1.10, it's likely an exceptional edge case. Do let me know if there's anything else I can do to help you pin down the database issue, though, and thank you so much for your patience and diligence in investigating the issue.

kheina commented 1 year ago

luckily this should be a very simple fix, and I suspect more people are having this issue than we may think from the reviews on the store page saying 0.3.0 broke it for them. the fix should be a very simple check of whether or not the function exists

kheina commented 1 year ago

it looks like there's only a few places where that InvalidStateError can be thrown that are used in the codebase.

db.createObjectStore which seems exceedingly unlikely or impossible in version 114 DBOpenRequest.result when result isn't populated. however, result is only accessed in blueblocker onupgradeneeded and onsuccess so that doesn't make sense unless one of these is being called early transaction.commit() but this also seems unlikely without any of the "stored" logs before it store.add(item) again, unlikely without the checking logs. notably, the clear() method, which is also used, does not throw this error transaction.objectStore(dbStore) again, unlikely without the checking logs. store.count() again, unlikely without the checking logs.

unless I missed some in the mdn search results, that's all the places this error is thrown that's used in code

kheina commented 1 year ago

interestingly, mdn's docs don't use request.result, they use the event evt.currentTarget.result.createObjectStore

https://github.com/mdn/dom-examples/blob/9703a3fa6527c44137b91005dc87d03dd990ce3b/indexeddb-api/main.js#L37-L38

kheina commented 1 year ago

https://github.com/kheina-com/Blue-Blocker/pull/158#issuecomment-1612242218

builds: blue-blocker-chrome-0.3.1.zip blue-blocker-firefox-0.3.1.zip

contains a fix for the chrome prior to version 110 and also adds more logging to that idb.open request to potentially get more info regarding that issue, if you're willing to be a test subject again

kheina commented 1 year ago

while it likely won't fix it outright, v0.3.2 will have better logging and error handling such that it should prevent these kinds of deadlocks in the event that the legacy verified database is unable to start

Anyasia commented 1 year ago

#158 (comment)

builds: blue-blocker-chrome-0.3.1.zip blue-blocker-firefox-0.3.1.zip

contains a fix for the chrome prior to version 110 and also adds more logging to that idb.open request to potentially get more info regarding that issue, if you're willing to be a test subject again

Haven't had time to test Firefox yet, but results for Chrome look good! The service worker is initiated, the database loads properly, and users are queued and blocked:—

[Blue Blocker] opening legacy verified user database: IDBOpenDBRequest
[Blue Blocker] created database.
[Blue Blocker] checking verified user database.
[Blue Blocker] cleared existing db store.
[Blue Blocker] downloading legacy verified users database, this may take a few minutes.
[Blue Blocker] response csv good!
[Blue Blocker] committed 407520 users to legacy verified db: IDBTransaction {objectStoreNames: DOMStringList, mode: 'readwrite', durability: 'default', db: IDBDatabase, error: null, …}
[Blue Blocker] loaded 407,520 legacy verified users!
[Blue Blocker] did not block Twitter Blue verified user Mission: Impossible (@MissionFilm) because they are legacy verified.
[Blue Blocker] queued Mission: Impossible (@MissionFilm) for a block due to promoting tweets.
[Blue Blocker] did not block Twitter Blue verified user Paramount Canada (@ParamountCanada) because they are legacy verified.
[Blue Blocker] queued Paramount Canada (@ParamountCanada) for a block due to promoting tweets.
chunk-dd3a3527.js:1 [Blue Blocker] blocked Paramount Canada (@ParamountCanada) due to promoting tweets.
chunk-dd3a3527.js:1 [Blue Blocker] blocked Mission: Impossible (@MissionFilm) due to promoting tweets.

I'll reply again as soon as I've had time to test in Firefox.

Edit: Chrome is actually logging some errors in the Extension Manager, but they don't seem to be affecting its execution.

Multiple errors, all of the following form, but from different contexts:—

Uncaught Error: Extension context invalidated.

Context
https://twitter.com/googlecanada/status/1674465412023562257

Stack Trace
assets/chunk-dd3a3527.js:1 (anonymous function)

var K=Object.defineProperty;var G=(e,t,o)=>t in e?K(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o;var _=(e,t,o)=>(G(e,typeof t!="symbol"?t+"":t,o),o);import{R as L,l as c,a as m,E as v,D as x,b as C,M as J,H as q,F as b,c as D,I as Y,d as W,S as X,e as Z,f as ee,g as te,h as oe,i as ne}from"./chunk-499d2a27.js";import{B as se}from"./chunk-b0338604.js";const k="BlockCounterCriticalPoint",U=1e3;class ie{constructor(t){_(this,"storage");_(this,"value");_(this,"timeout");this.storage=t,this.value=0,this.timeout=null}async getCriticalPoint(t){var s;let o=null,n=50;do{const l=(await this.storage.get({[k]:null}))[k];!l||l.refId===t||l.time<=new Date().valueOf()?(await this.storage.set({[k]:{refId:t,time:new Date().valueOf()+U*1.5}}),await new Promise(i=>setTimeout(i,10)),o=(s=(await this.storage.get({[k]:null}))[k])==null?void 0:s.refId):(await new Promise(i=>setTimeout(i,n)),n=Math.min(n**2,U))}while(o!==t)}async releaseCriticalPoint(t){const o=(await this.storage.get({[k]:null}))[k];(o==null?void 0:o.refId)===t&&o.time>new Date().valueOf()&&await this.storage.set({[k]:null})}async sync(){const t=L();await this.getCriticalPoint(t);const o=await this.storage.get({BlockCounter:0});o.BlockCounter+=this.value,this.value=0,await this.storage.set(o),this.releaseCriticalPoint(t)}async increment(t=1){this.value+=t,this.timeout&&clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.sync(),100)}}const w="QueueConsumerCriticalPoint";class le{constructor(t,o,n){_(this,"storage");_(this,"func");_(this,"interval");_(this,"_timeout");_(this,"_interval");_(this,"_func_timeout");_(this,"_refId");this.storage=t,this.func=o,this.interval=n,this._timeout=null,this._interval=100,this._func_timeout=null,this._refId=L()}async getCriticalPoint(){let t=null;do{this._interval=await this.interval(this.storage);const o=(await this.storage.get({[w]:null}))[w];if(!o||o.refId===this._refId||o.time<=new Date().valueOf())await this.storage.set({[w]:{refId:this._refId,time:new Date().valueOf()+(this._interval||0)*1.5}}),await new Promise(n=>setTimeout(n,10)),t=(await this.storage.get({[w]:null}))[w].refId;else return!1}while(t!==this._refId);return!0}async releaseCriticalPoint(){const t=(await this.storage.get({[w]:null}))[w];(t==null?void 0:t.refId)===this._refId&&t.time>new Date().valueOf()&&await this.storage.set({[w]:null})}async sync(){await this.getCriticalPoint()?(this._func_timeout===null&&(this._func_timeout=setTimeout(()=>this.func().finally(()=>{this._func_timeout=null,this.sync()}),this._interval)),this._timeout=null):(this._func_timeout&&(clearTimeout(this._func_timeout),this._func_timeout=null),this._timeout=setTimeout(()=>this.sync(),this._interval))}start(){this._timeout||this._func_timeout||(console.debug(c,"queue consumer started"),this.sync())}stop(){this._timeout&&(clearTimeout(this._timeout),this._timeout=null),this._func_timeout&&(clearTimeout(this._func_timeout),this._func_timeout=null),this.releaseCriticalPoint(),console.debug(c,"queue consumer stopped")}}const T=new se(m.storage.local),ae=new ie(m.storage.local),j=new Set;function ce(e){m.storage.local.get({headers:{}}).then(t=>{for(const[o,n]of Object.entries(e))t.headers[o.toLowerCase()]=n;m.storage.local.set(t)})}setInterval(j.clear,10*6e4);function A(e,t,o,n=1){m.storage.sync.get({unblocked:{}}).then(h=>{h.unblocked[String(t)]=null,m.storage.sync.set(h)});const s=window.location.href.match(/^https?:\/\/(?:\w+\.)?twitter.com(?=$|\/)/);if(!s)throw new Error("unexpected or incorrectly formatted url");const l=s[0];let i="";l.includes("tweetdeck")?i="https://api.twitter.com/1.1/":i=`${l}/i/api/1.1/`,m.storage.sync.get(x).then(h=>{const u=h;u.mute?i+="mutes/users/destroy.json":i+="blocks/destroy.json",m.storage.local.get({headers:null}).then(d=>d.headers).then(d=>{const p=`user_id=${t}`,f={"content-length":p.length.toString(),"content-type":"application/x-www-form-urlencoded","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9",accept:"*/*"};for(const r of q)d[r]&&(f[r]=d[r]);const g=N.exec(document.cookie);g?f["x-csrf-token"]=g[1]:f["x-csrf-token"]=d["x-csrf-token"];const a={body:p,headers:f,method:"POST",credentials:"include"};console.debug(c,"unblock request:",{url:i,...a}),fetch(i,a).then(r=>{const y=document.createElement("div");y.className="toast",y.innerText=`unblocked @${e.screen_name}, they won't be blocked again.`;const P=document.getElementById("injected-blue-block-toasts");if(!P)throw new Error("blue blocker was unable to create or find toasts div.");r.status===403?(y.innerText=`could not unblock @${e.screen_name}, you may have been logged out.`,console.log(c,"user is logged out, failed to unblock user.")):r.status===404?(y.innerText=`could not unblock @${e.screen_name}, user has been suspended or no longer exists.`,console.log(c,`failed to unblock ${b(e)}, user no longer exists`)):r.status>=300?(y.innerText=`could not unblock @${e.screen_name}, twitter gave an unfamiliar response code.`,console.error(c,`failed to unblock ${b(e)}:`,e,r)):(y.innerText=`unblocked @${e.screen_name}, they won't be blocked again.`,console.log(c,`unblocked ${b(e)}`)),P.appendChild(y),setTimeout(()=>P.removeChild(y),u.popupTimer*1e3)}).catch(r=>{n<3?A(e,t,o,n+1):console.error(c,`failed to unblock ${b(e)}:`,e,r)})})})}const F="UserBlockedEvent";m.storage.local.onChanged.addListener(e=>{if(!e.hasOwnProperty(v))return;const t=e[v].newValue;m.storage.sync.get(x).then(o=>{switch(t.type){case J:if(o.showBlockPopups){const l=document.createElement("div");l.className="toast",l.innerText=t.message;const i=document.getElementById("injected-blue-block-toasts");i&&(i.appendChild(l),setTimeout(()=>i.removeChild(l),o.popupTimer*1e3))}break;case F:if(o.showBlockPopups){const l=t,{user:i,user_id:h,reason:u}=l,d=document.createElement("div");d.className="toast";const p=i.name.length>25?i.name.substring(0,23).trim()+"...":i.name;d.innerHTML=`blocked ${p} (<a href="/${i.screen_name}">@${i.screen_name}</a>)`;const f=document.createElement("button");f.onclick=()=>{A(i,h,u),d.removeChild(f)},f.innerText="undo",d.appendChild(f);const g=document.getElementById("injected-blue-block-toasts");g&&(g.appendChild(d),setTimeout(()=>g.removeChild(d),o.popupTimer*1e3))}break;case C:t.message&&console.error(c,t.message,t);const n=document.createElement("div");n.className="toast error",n.innerHTML='<p>an error occurred! check the console and create an issue on <a href="https://github.com/kheina-com/Blue-Blocker/issues" target="_blank">GitHub</a></p>';const s=document.getElementById("injected-blue-block-toasts");s&&(s.appendChild(n),setTimeout(()=>s.removeChild(n),6e4));break;default:console.error(c,"unknown multitab event occurred:",t)}})});function I(e,t,o){j.has(t)||(j.add(t),T.push({user_id:t,reason:o,user:{name:e.legacy.name,screen_name:e.legacy.screen_name}}),console.log(c,`queued ${b(e.legacy)} for a block due to ${D[o]}.`),$.start())}function re(){return new Promise(e=>{T.shift().then(t=>{const o=t;if(o===void 0){$.stop();return}const{user:n,user_id:s,reason:l}=o;if(n.hasOwnProperty("legacy"))for(const[i,h]of Object.entries(n.legacy))n[i]=h;V(n,s,l),e()}).catch(t=>{console.error(c,"unexpected error occurred while processing block queue",t),m.storage.local.set({[v]:{type:C,message:"unexpected error occurred while processing block queue",detail:{error:t,event:null}}}),e()})})}const $=new le(m.storage.local,re,async()=>(await m.storage.sync.get({blockInterval:x.blockInterval})).blockInterval*1e3);$.start();const N=/ct0=\s*(\w+);/;function V(e,t,o,n=1){const s=window.location.href.match(/^https?:\/\/(?:\w+\.)?twitter.com(?=$|\/)/);if(!s)throw new Error("unexpected or incorrectly formatted url");const l=s[0];let i="";l.includes("tweetdeck")?i="https://api.twitter.com/1.1/":i=`${l}/i/api/1.1/`,m.storage.sync.get(x).then(h=>{h.mute?i+="mutes/users/create.json":i+="blocks/create.json",m.storage.local.get({headers:null}).then(d=>d.headers).then(d=>{const p=`user_id=${t}`,f={"content-length":p.length.toString(),"content-type":"application/x-www-form-urlencoded","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9",accept:"*/*"};for(const r of q)d[r]&&(f[r]=d[r]);const g=N.exec(document.cookie);g?f["x-csrf-token"]=g[1]:f["x-csrf-token"]=d["x-csrf-token"];const a={body:p,headers:f,method:"POST",credentials:"include"};console.debug(c,"block request:",{url:i,...a}),fetch(i,a).then(r=>{console.debug(c,"block response:",r),r.status===403?($.stop(),T.push({user:e,user_id:t,reason:o}),console.log(c,"user is logged out, queue consumer has been halted.")):r.status===404?console.log(c,`did not block ${b(e)}, user no longer exists`):r.status>=300?(T.push({user:e,user_id:t,reason:o}),console.error(c,`failed to block ${b(e)}:`,e,r)):(ae.increment(),console.log(c,`blocked ${b(e)} due to ${D[o]}.`),m.storage.local.set({[v]:{type:F,user:e,user_id:t,reason:o}}))}).catch(r=>{n<3?V(e,t,o,n+1):(T.push({user:e,user_id:t,reason:o}),console.error(c,`failed to block ${b(e)}:`,e,r))})})})}const ue=new Set([]),M=new Set(["Business"]);async function S(e,t){var l,i,h,u,d,p,f,g;if(t.suspendedBlockCollection)return;if(e.rest_id===void 0||(e==null?void 0:e.legacy.name)===void 0||(e==null?void 0:e.legacy.screen_name)===void 0){console.error(c,"invalid user object passed to BlockBlueVerified");return}const o=b(e.legacy),n=M.has(((l=e.legacy)==null?void 0:l.verified_type)||""),s=ue.has(((h=(i=e.affiliates_highlighted_label)==null?void 0:i.label)==null?void 0:h.userLabelType)||"");if(!(((u=e.legacy)==null?void 0:u.verified_type)&&!M.has(e.legacy.verified_type))&&!((d=e.legacy)!=null&&d.blocking)){if(t.unblocked.hasOwnProperty(String(e.rest_id))){console.debug(c,`skipped user ${o} because you unblocked them previously.`);return}else if(!t.blockFollowing&&(((p=e.legacy)==null?void 0:p.following)||e.super_following)){console.debug(c,`skipped user ${o} because you follow them.`);return}else if(!t.blockFollowers&&((f=e.legacy)==null?void 0:f.followed_by)){console.debug(c,`skipped user ${o} because they follow you.`);return}if(e.is_blue_verified||n||s)if(t.skipVerified&&await Y(e.rest_id,e.legacy.screen_name))console.log(c,`did not block Twitter Blue verified user ${o} because they are legacy verified.`);else if(t.skipAffiliated&&(s||n))console.log(c,`did not block Twitter Blue verified user ${o} because they are verified through an affiliated organization.`);else if(t.skip1Mplus&&((g=e.legacy)==null?void 0:g.followers_count)>t.skipFollowerCount)console.log(c,`did not block Twitter Blue verified user ${o} because they have over ${W(t.skipFollowerCount)} followers and Elon is an idiot.`);else{let a=oe;n&&(a=ne),I(e,String(e.rest_id),a);return}if(t.blockNftAvatars&&(e.has_nft_avatar||e.profile_image_shape==="Hexagon")){I(e,String(e.rest_id),ee);return}if(t.blockPromoted&&e.promoted_tweet){I(e,String(e.rest_id),te);return}if(t.soupcanIntegration)try{const a=await chrome.runtime.sendMessage(X,{action:"check_twitter_user",screen_name:e.legacy.screen_name});if(console.debug(c,`soupcan response for @${e.legacy.screen_name}:`,a),(a==null?void 0:a.status)==="transphobic"){I(e,String(e.rest_id),Z);return}}catch(a){const r=a;console.debug(c,`soupcan error for @${e.legacy.screen_name}:`,r),r.message==="Could not establish connection. Receiving end does not exist."?(m.storage.sync.set({soupcanIntegration:!1}),console.log(c,"looks like soupcan was uninstalled, disabling integration.")):console.error(c,"an unknown error occurred while messaging soupcan:",r)}}}const de={HomeLatestTimeline:["data","home","home_timeline_urt","instructions"],HomeTimeline:["data","home","home_timeline_urt","instructions"],SearchTimeline:["data","search_by_raw_query","search_timeline","timeline","instructions"],UserTweets:["data","user","result","timeline_v2","timeline","instructions"],TweetDetail:["data","threaded_conversation_with_injections_v2","instructions"],"search/adaptive.json":["timeline","instructions"]},fe=["tweet_results","result","tweet","core","user_results","result"],Q=new Set(["TimelineTimelineCursor"]),R=new Set(["suggest_promoted","Promoted","promoted"]);function E(e,t,o){let n=e;for(const s of fe)n.hasOwnProperty(s)&&(n=n[s]);if(n.__typename!=="User"){console.error(c,"could not parse tweet",e);return}n.promoted_tweet=o,S(n,t)}function H(e,t){var n,s,l,i,h,u,d,p,f,g,a,r;if(Q.has(e.itemContent.itemType))return;let o=!1;(((n=e==null?void 0:e.itemContent)==null?void 0:n.promotedMetadata)!==void 0||R.has((s=e==null?void 0:e.clientEventInfo)==null?void 0:s.component)||R.has((h=(i=(l=e==null?void 0:e.clientEventInfo)==null?void 0:l.details)==null?void 0:i.timelinesDetails)==null?void 0:h.injectionType))&&(o=!0);try{(p=(d=(u=e==null?void 0:e.itemContent)==null?void 0:u.tweet_results)==null?void 0:d.result)!=null&&p.quoted_status_result?E(e.itemContent.tweet_results.result.quoted_status_result.result,t,o):(r=(a=(g=(f=e==null?void 0:e.itemContent)==null?void 0:f.tweet_results)==null?void 0:g.result)==null?void 0:a.legacy)!=null&&r.retweeted_status_result&&E(e.itemContent.tweet_results.result.legacy.retweeted_status_result.result,t,o),E(e.itemContent,t,o)}catch{console.error(c,"found unexpected tweet shape:",e),m.storage.local.set({[v]:{type:C}})}}function me(e,t,o){var h,u,d,p,f,g;let n=t;for(const a of de[e.detail.parsedUrl[1]])n=n[a];const s=n;console.debug(c,"parsed instructions path:",s);let l,i=!1;for(const a of s)if(a.type==="TimelineAddEntries"||a.type==="TimelineAddToModule"){l=a,i=a.type==="TimelineAddToModule";break}if(l===void 0){console.error(c,"response object does not contain an instruction to add entries",t);return}l.entries=l.entries||[],i&&(l.entries=[{content:{entryType:"TimelineTimelineModule",items:l.moduleItems}}]);for(const a of l.entries)switch((h=a==null?void 0:a.content)==null?void 0:h.entryType){case null:console.error(c,"tweet structure does not match expectation",a);break;case"TimelineTimelineItem":((u=a.content.itemContent)==null?void 0:u.itemType)=="TimelineTweet"&&H(a.content,o);break;case"TimelineTimelineModule":for(const r of a.content.items||[])H(r.item,o);break;default:if(!Q.has(a.content.entryType))throw{message:`unexpected tweet type found: ${(d=a==null?void 0:a.content)==null?void 0:d.entryType}`,name:"TweetType",tweet:a}}i&&(l.moduleItems=((g=(f=(p=l.entries)==null?void 0:p[0])==null?void 0:f.content)==null?void 0:g.items)||[],delete l.entries)}function he(e,t,o){var n,s,l,i;for(const[h,u]of Object.entries(t.globalObjects.users))S({is_blue_verified:u.ext_is_blue_verified,has_nft_avatar:u.ext_has_nft_avatar,legacy:{blocking:u.blocking,followed_by:u.followed_by,following:u.following,name:u.name,screen_name:u.screen_name,verified:u.verified,verified_type:(u==null?void 0:u.ext_verified_type)||"",followers_count:u.followers_count},super_following:(i=(l=(s=(n=u.ext)==null?void 0:n.superFollowMetadata)==null?void 0:s.r)==null?void 0:l.ok)==null?void 0:i.superFollowing,rest_id:h},o)}function ge(e,t,o){var n;if(!!((n=t==null?void 0:t.users)!=null&&n.length))for(const s of t.users)S({is_blue_verified:s.ext_is_blue_verified,has_nft_avatar:(s==null?void 0:s.ext_has_nft_avatar)||!1,legacy:{blocking:(s==null?void 0:s.is_blocked)||!1,followed_by:s.social_context.followed_by,following:s.social_context.following,name:s.name,screen_name:s.screen_name,verified:s.verified,verified_type:(s==null?void 0:s.ext_verified_type)||"",followers_count:1e10},super_following:!1,rest_id:s.id_str},o)}var pe="/assets/chunk-a5b11d07.js";const B=document.createElement("script");B.src=chrome.runtime.getURL(pe);B.id="injected-blue-block-xhr";B.type="text/javascript";document.head.prepend(B);let O=document.createElement("link");O.href=m.runtime.getURL("src/injected/style.css");O.rel="stylesheet";(document.head||document.documentElement).appendChild(O);let z=document.createElement("div");z.id="injected-blue-block-toasts";document.body.appendChild(z);document.addEventListener("blue-blocker-event",function(e){if(e.detail.status<300)ce(e.detail.request.headers);else return;m.storage.sync.get(x).then(t=>{const o=t,n=e.detail.body;try{const s=JSON.parse(n);switch(e.detail.parsedUrl[1]){case"HomeLatestTimeline":case"HomeTimeline":case"SearchTimeline":case"UserTweets":case"TweetDetail":return me(e,s,o);case"timeline/home.json":case"search/adaptive.json":return he(e,s,o);case"search/typeahead.json":return ge(e,s,o);default:console.error(c,"found an unexpected url that we don't know how to handle",e),m.storage.local.set({[v]:{type:C}})}}catch(s){console.error(c,"unexpected error occurred while parsing request body",{error:s,body_str:n,event:e}),m.storage.local.set({[v]:{type:C}})}})});
Anyasia commented 1 year ago

Firefox looks good, too, with a couple caveats:—

[Blue Blocker] opening legacy verified user database: 
    IDBOpenDBRequest { onblocked: onblocked(), onupgradeneeded: onupgradeneeded(), result: IDBDatabase, error: null, source: null, transaction: null, readyState: "done", onsuccess: async onsuccess(), onerror: onblocked() }
[Blue Blocker] DBOpenRequest.onupgradeneeded: 
    IDBOpenDBRequest { onblocked: onblocked(), onupgradeneeded: onupgradeneeded(), result: IDBDatabase, error: null, source: null, transaction: null, readyState: "done", onsuccess: async onsuccess(), onerror: onblocked() }
[Blue Blocker] created database.
[Blue Blocker] DBOpenRequest.onupgradeneeded: 
    IDBOpenDBRequest { onblocked: onblocked(), onupgradeneeded: onupgradeneeded(), result: IDBDatabase, error: null, source: null, transaction: null, readyState: "done", onsuccess: async onsuccess(), onerror: onblocked() }
[Blue Blocker] checking verified user database.
[Blue Blocker] cleared existing db store.
[Blue Blocker] downloading legacy verified users database, this may take a few minutes.
[Blue Blocker] response csv good!
[Blue Blocker] stored 1,000 legacy verified users
[Blue Blocker] committed 407520 users to legacy verified db: 
    IDBTransaction { mode: "readwrite", db: IDBDatabase, error: null, onabort: null, oncomplete: null, onerror: null, objectStoreNames: DOMStringList(1) }
[Blue Blocker] loaded 407,520 legacy verified users!
[Blue Blocker] soupcan response for @elonmusk: Error: Could not establish connection. Receiving end does not exist.
    <anonymous> moz-extension://3741ab25-5f5c-4e0d-85c9-afdc1bb19fcb/assets/chunk-7ea957eb.js:1
[Blue Blocker] set active tab: advanced

The noted caveats are:—

  1. I'm not sure why or whether it's an issue that it logged storing 1,000 legacy verified users before it seemed to load the full database, as this didn't happen in Chrome; and
  2. It's still logging the soupcan response for @elonmusk error when the extension popup is opened on Twitter; this likewise is not happening in Chrome.

Neither of those issues seems to be affecting execution, however.

Hopefully it shouldn't affect anything, but I figure it's worth noting that because Firefox' release version does not allow loading unsigned extensions, I had to test this instead in Firefox Developer Edition 115.0b9 (64-bit).

kheina commented 1 year ago

hi! thanks for checking again! v0.3.2 has actually been pushed to the firefox and chrome web stores already, firefox should be able to update right now and chrome should be available soon. (chrome is always really slow about accepting new versions)

but, I want to address everything you posted cause there's tons of info here and I don't want to leave you hanging

Multiple errors, all of the following form, but from different contexts:—

Uncaught Error: Extension context invalidated.

Context
https://twitter.com/googlecanada/status/1674465412023562257

Stack Trace
assets/chunk-dd3a3527.js:1 (anonymous function)

var K=Object.defineProperty;var G=(e,t,o)=>t in e?K(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o;var _=(e,t,o)=>(G(e,typeof t!="symbol"?t+"":t,o),o);import{R as L,l as c,a as m,E as v,D as x,b as C,M as J,H as q,F as b,c as D,I as Y,d as W,S as X,e as Z,f as ee,g as te,h as oe,i as ne}from"./chunk-499d2a27.js";import{B as se}from"./chunk-b0338604.js";const k="BlockCounterCriticalPoint",U=1e3;class ie{constructor(t){_(this,"storage");_(this,"value");_(this,"timeout");this.storage=t,this.value=0,this.timeout=null}async getCriticalPoint(t){var s;let o=null,n=50;do{const l=(await this.storage.get({[k]:null}))[k];!l||l.refId===t||l.time<=new Date().valueOf()?(await this.storage.set({[k]:{refId:t,time:new Date().valueOf()+U*1.5}}),await new Promise(i=>setTimeout(i,10)),o=(s=(await this.storage.get({[k]:null}))[k])==null?void 0:s.refId):(await new Promise(i=>setTimeout(i,n)),n=Math.min(n**2,U))}while(o!==t)}async releaseCriticalPoint(t){const o=(await this.storage.get({[k]:null}))[k];(o==null?void 0:o.refId)===t&&o.time>new Date().valueOf()&&await this.storage.set({[k]:null})}async sync(){const t=L();await this.getCriticalPoint(t);const o=await this.storage.get({BlockCounter:0});o.BlockCounter+=this.value,this.value=0,await this.storage.set(o),this.releaseCriticalPoint(t)}async increment(t=1){this.value+=t,this.timeout&&clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.sync(),100)}}const w="QueueConsumerCriticalPoint";class le{constructor(t,o,n){_(this,"storage");_(this,"func");_(this,"interval");_(this,"_timeout");_(this,"_interval");_(this,"_func_timeout");_(this,"_refId");this.storage=t,this.func=o,this.interval=n,this._timeout=null,this._interval=100,this._func_timeout=null,this._refId=L()}async getCriticalPoint(){let t=null;do{this._interval=await this.interval(this.storage);const o=(await this.storage.get({[w]:null}))[w];if(!o||o.refId===this._refId||o.time<=new Date().valueOf())await this.storage.set({[w]:{refId:this._refId,time:new Date().valueOf()+(this._interval||0)*1.5}}),await new Promise(n=>setTimeout(n,10)),t=(await this.storage.get({[w]:null}))[w].refId;else return!1}while(t!==this._refId);return!0}async releaseCriticalPoint(){const t=(await this.storage.get({[w]:null}))[w];(t==null?void 0:t.refId)===this._refId&&t.time>new Date().valueOf()&&await this.storage.set({[w]:null})}async sync(){await this.getCriticalPoint()?(this._func_timeout===null&&(this._func_timeout=setTimeout(()=>this.func().finally(()=>{this._func_timeout=null,this.sync()}),this._interval)),this._timeout=null):(this._func_timeout&&(clearTimeout(this._func_timeout),this._func_timeout=null),this._timeout=setTimeout(()=>this.sync(),this._interval))}start(){this._timeout||this._func_timeout||(console.debug(c,"queue consumer started"),this.sync())}stop(){this._timeout&&(clearTimeout(this._timeout),this._timeout=null),this._func_timeout&&(clearTimeout(this._func_timeout),this._func_timeout=null),this.releaseCriticalPoint(),console.debug(c,"queue consumer stopped")}}const T=new se(m.storage.local),ae=new ie(m.storage.local),j=new Set;function ce(e){m.storage.local.get({headers:{}}).then(t=>{for(const[o,n]of Object.entries(e))t.headers[o.toLowerCase()]=n;m.storage.local.set(t)})}setInterval(j.clear,10*6e4);function A(e,t,o,n=1){m.storage.sync.get({unblocked:{}}).then(h=>{h.unblocked[String(t)]=null,m.storage.sync.set(h)});const s=window.location.href.match(/^https?:\/\/(?:\w+\.)?twitter.com(?=$|\/)/);if(!s)throw new Error("unexpected or incorrectly formatted url");const l=s[0];let i="";l.includes("tweetdeck")?i="https://api.twitter.com/1.1/":i=`${l}/i/api/1.1/`,m.storage.sync.get(x).then(h=>{const u=h;u.mute?i+="mutes/users/destroy.json":i+="blocks/destroy.json",m.storage.local.get({headers:null}).then(d=>d.headers).then(d=>{const p=`user_id=${t}`,f={"content-length":p.length.toString(),"content-type":"application/x-www-form-urlencoded","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9",accept:"*/*"};for(const r of q)d[r]&&(f[r]=d[r]);const g=N.exec(document.cookie);g?f["x-csrf-token"]=g[1]:f["x-csrf-token"]=d["x-csrf-token"];const a={body:p,headers:f,method:"POST",credentials:"include"};console.debug(c,"unblock request:",{url:i,...a}),fetch(i,a).then(r=>{const y=document.createElement("div");y.className="toast",y.innerText=`unblocked @${e.screen_name}, they won't be blocked again.`;const P=document.getElementById("injected-blue-block-toasts");if(!P)throw new Error("blue blocker was unable to create or find toasts div.");r.status===403?(y.innerText=`could not unblock @${e.screen_name}, you may have been logged out.`,console.log(c,"user is logged out, failed to unblock user.")):r.status===404?(y.innerText=`could not unblock @${e.screen_name}, user has been suspended or no longer exists.`,console.log(c,`failed to unblock ${b(e)}, user no longer exists`)):r.status>=300?(y.innerText=`could not unblock @${e.screen_name}, twitter gave an unfamiliar response code.`,console.error(c,`failed to unblock ${b(e)}:`,e,r)):(y.innerText=`unblocked @${e.screen_name}, they won't be blocked again.`,console.log(c,`unblocked ${b(e)}`)),P.appendChild(y),setTimeout(()=>P.removeChild(y),u.popupTimer*1e3)}).catch(r=>{n<3?A(e,t,o,n+1):console.error(c,`failed to unblock ${b(e)}:`,e,r)})})})}const F="UserBlockedEvent";m.storage.local.onChanged.addListener(e=>{if(!e.hasOwnProperty(v))return;const t=e[v].newValue;m.storage.sync.get(x).then(o=>{switch(t.type){case J:if(o.showBlockPopups){const l=document.createElement("div");l.className="toast",l.innerText=t.message;const i=document.getElementById("injected-blue-block-toasts");i&&(i.appendChild(l),setTimeout(()=>i.removeChild(l),o.popupTimer*1e3))}break;case F:if(o.showBlockPopups){const l=t,{user:i,user_id:h,reason:u}=l,d=document.createElement("div");d.className="toast";const p=i.name.length>25?i.name.substring(0,23).trim()+"...":i.name;d.innerHTML=`blocked ${p} (<a href="/${i.screen_name}">@${i.screen_name}</a>)`;const f=document.createElement("button");f.onclick=()=>{A(i,h,u),d.removeChild(f)},f.innerText="undo",d.appendChild(f);const g=document.getElementById("injected-blue-block-toasts");g&&(g.appendChild(d),setTimeout(()=>g.removeChild(d),o.popupTimer*1e3))}break;case C:t.message&&console.error(c,t.message,t);const n=document.createElement("div");n.className="toast error",n.innerHTML='<p>an error occurred! check the console and create an issue on <a href="https://github.com/kheina-com/Blue-Blocker/issues" target="_blank">GitHub</a></p>';const s=document.getElementById("injected-blue-block-toasts");s&&(s.appendChild(n),setTimeout(()=>s.removeChild(n),6e4));break;default:console.error(c,"unknown multitab event occurred:",t)}})});function I(e,t,o){j.has(t)||(j.add(t),T.push({user_id:t,reason:o,user:{name:e.legacy.name,screen_name:e.legacy.screen_name}}),console.log(c,`queued ${b(e.legacy)} for a block due to ${D[o]}.`),$.start())}function re(){return new Promise(e=>{T.shift().then(t=>{const o=t;if(o===void 0){$.stop();return}const{user:n,user_id:s,reason:l}=o;if(n.hasOwnProperty("legacy"))for(const[i,h]of Object.entries(n.legacy))n[i]=h;V(n,s,l),e()}).catch(t=>{console.error(c,"unexpected error occurred while processing block queue",t),m.storage.local.set({[v]:{type:C,message:"unexpected error occurred while processing block queue",detail:{error:t,event:null}}}),e()})})}const $=new le(m.storage.local,re,async()=>(await m.storage.sync.get({blockInterval:x.blockInterval})).blockInterval*1e3);$.start();const N=/ct0=\s*(\w+);/;function V(e,t,o,n=1){const s=window.location.href.match(/^https?:\/\/(?:\w+\.)?twitter.com(?=$|\/)/);if(!s)throw new Error("unexpected or incorrectly formatted url");const l=s[0];let i="";l.includes("tweetdeck")?i="https://api.twitter.com/1.1/":i=`${l}/i/api/1.1/`,m.storage.sync.get(x).then(h=>{h.mute?i+="mutes/users/create.json":i+="blocks/create.json",m.storage.local.get({headers:null}).then(d=>d.headers).then(d=>{const p=`user_id=${t}`,f={"content-length":p.length.toString(),"content-type":"application/x-www-form-urlencoded","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9",accept:"*/*"};for(const r of q)d[r]&&(f[r]=d[r]);const g=N.exec(document.cookie);g?f["x-csrf-token"]=g[1]:f["x-csrf-token"]=d["x-csrf-token"];const a={body:p,headers:f,method:"POST",credentials:"include"};console.debug(c,"block request:",{url:i,...a}),fetch(i,a).then(r=>{console.debug(c,"block response:",r),r.status===403?($.stop(),T.push({user:e,user_id:t,reason:o}),console.log(c,"user is logged out, queue consumer has been halted.")):r.status===404?console.log(c,`did not block ${b(e)}, user no longer exists`):r.status>=300?(T.push({user:e,user_id:t,reason:o}),console.error(c,`failed to block ${b(e)}:`,e,r)):(ae.increment(),console.log(c,`blocked ${b(e)} due to ${D[o]}.`),m.storage.local.set({[v]:{type:F,user:e,user_id:t,reason:o}}))}).catch(r=>{n<3?V(e,t,o,n+1):(T.push({user:e,user_id:t,reason:o}),console.error(c,`failed to block ${b(e)}:`,e,r))})})})}const ue=new Set([]),M=new Set(["Business"]);async function S(e,t){var l,i,h,u,d,p,f,g;if(t.suspendedBlockCollection)return;if(e.rest_id===void 0||(e==null?void 0:e.legacy.name)===void 0||(e==null?void 0:e.legacy.screen_name)===void 0){console.error(c,"invalid user object passed to BlockBlueVerified");return}const o=b(e.legacy),n=M.has(((l=e.legacy)==null?void 0:l.verified_type)||""),s=ue.has(((h=(i=e.affiliates_highlighted_label)==null?void 0:i.label)==null?void 0:h.userLabelType)||"");if(!(((u=e.legacy)==null?void 0:u.verified_type)&&!M.has(e.legacy.verified_type))&&!((d=e.legacy)!=null&&d.blocking)){if(t.unblocked.hasOwnProperty(String(e.rest_id))){console.debug(c,`skipped user ${o} because you unblocked them previously.`);return}else if(!t.blockFollowing&&(((p=e.legacy)==null?void 0:p.following)||e.super_following)){console.debug(c,`skipped user ${o} because you follow them.`);return}else if(!t.blockFollowers&&((f=e.legacy)==null?void 0:f.followed_by)){console.debug(c,`skipped user ${o} because they follow you.`);return}if(e.is_blue_verified||n||s)if(t.skipVerified&&await Y(e.rest_id,e.legacy.screen_name))console.log(c,`did not block Twitter Blue verified user ${o} because they are legacy verified.`);else if(t.skipAffiliated&&(s||n))console.log(c,`did not block Twitter Blue verified user ${o} because they are verified through an affiliated organization.`);else if(t.skip1Mplus&&((g=e.legacy)==null?void 0:g.followers_count)>t.skipFollowerCount)console.log(c,`did not block Twitter Blue verified user ${o} because they have over ${W(t.skipFollowerCount)} followers and Elon is an idiot.`);else{let a=oe;n&&(a=ne),I(e,String(e.rest_id),a);return}if(t.blockNftAvatars&&(e.has_nft_avatar||e.profile_image_shape==="Hexagon")){I(e,String(e.rest_id),ee);return}if(t.blockPromoted&&e.promoted_tweet){I(e,String(e.rest_id),te);return}if(t.soupcanIntegration)try{const a=await chrome.runtime.sendMessage(X,{action:"check_twitter_user",screen_name:e.legacy.screen_name});if(console.debug(c,`soupcan response for @${e.legacy.screen_name}:`,a),(a==null?void 0:a.status)==="transphobic"){I(e,String(e.rest_id),Z);return}}catch(a){const r=a;console.debug(c,`soupcan error for @${e.legacy.screen_name}:`,r),r.message==="Could not establish connection. Receiving end does not exist."?(m.storage.sync.set({soupcanIntegration:!1}),console.log(c,"looks like soupcan was uninstalled, disabling integration.")):console.error(c,"an unknown error occurred while messaging soupcan:",r)}}}const de={HomeLatestTimeline:["data","home","home_timeline_urt","instructions"],HomeTimeline:["data","home","home_timeline_urt","instructions"],SearchTimeline:["data","search_by_raw_query","search_timeline","timeline","instructions"],UserTweets:["data","user","result","timeline_v2","timeline","instructions"],TweetDetail:["data","threaded_conversation_with_injections_v2","instructions"],"search/adaptive.json":["timeline","instructions"]},fe=["tweet_results","result","tweet","core","user_results","result"],Q=new Set(["TimelineTimelineCursor"]),R=new Set(["suggest_promoted","Promoted","promoted"]);function E(e,t,o){let n=e;for(const s of fe)n.hasOwnProperty(s)&&(n=n[s]);if(n.__typename!=="User"){console.error(c,"could not parse tweet",e);return}n.promoted_tweet=o,S(n,t)}function H(e,t){var n,s,l,i,h,u,d,p,f,g,a,r;if(Q.has(e.itemContent.itemType))return;let o=!1;(((n=e==null?void 0:e.itemContent)==null?void 0:n.promotedMetadata)!==void 0||R.has((s=e==null?void 0:e.clientEventInfo)==null?void 0:s.component)||R.has((h=(i=(l=e==null?void 0:e.clientEventInfo)==null?void 0:l.details)==null?void 0:i.timelinesDetails)==null?void 0:h.injectionType))&&(o=!0);try{(p=(d=(u=e==null?void 0:e.itemContent)==null?void 0:u.tweet_results)==null?void 0:d.result)!=null&&p.quoted_status_result?E(e.itemContent.tweet_results.result.quoted_status_result.result,t,o):(r=(a=(g=(f=e==null?void 0:e.itemContent)==null?void 0:f.tweet_results)==null?void 0:g.result)==null?void 0:a.legacy)!=null&&r.retweeted_status_result&&E(e.itemContent.tweet_results.result.legacy.retweeted_status_result.result,t,o),E(e.itemContent,t,o)}catch{console.error(c,"found unexpected tweet shape:",e),m.storage.local.set({[v]:{type:C}})}}function me(e,t,o){var h,u,d,p,f,g;let n=t;for(const a of de[e.detail.parsedUrl[1]])n=n[a];const s=n;console.debug(c,"parsed instructions path:",s);let l,i=!1;for(const a of s)if(a.type==="TimelineAddEntries"||a.type==="TimelineAddToModule"){l=a,i=a.type==="TimelineAddToModule";break}if(l===void 0){console.error(c,"response object does not contain an instruction to add entries",t);return}l.entries=l.entries||[],i&&(l.entries=[{content:{entryType:"TimelineTimelineModule",items:l.moduleItems}}]);for(const a of l.entries)switch((h=a==null?void 0:a.content)==null?void 0:h.entryType){case null:console.error(c,"tweet structure does not match expectation",a);break;case"TimelineTimelineItem":((u=a.content.itemContent)==null?void 0:u.itemType)=="TimelineTweet"&&H(a.content,o);break;case"TimelineTimelineModule":for(const r of a.content.items||[])H(r.item,o);break;default:if(!Q.has(a.content.entryType))throw{message:`unexpected tweet type found: ${(d=a==null?void 0:a.content)==null?void 0:d.entryType}`,name:"TweetType",tweet:a}}i&&(l.moduleItems=((g=(f=(p=l.entries)==null?void 0:p[0])==null?void 0:f.content)==null?void 0:g.items)||[],delete l.entries)}function he(e,t,o){var n,s,l,i;for(const[h,u]of Object.entries(t.globalObjects.users))S({is_blue_verified:u.ext_is_blue_verified,has_nft_avatar:u.ext_has_nft_avatar,legacy:{blocking:u.blocking,followed_by:u.followed_by,following:u.following,name:u.name,screen_name:u.screen_name,verified:u.verified,verified_type:(u==null?void 0:u.ext_verified_type)||"",followers_count:u.followers_count},super_following:(i=(l=(s=(n=u.ext)==null?void 0:n.superFollowMetadata)==null?void 0:s.r)==null?void 0:l.ok)==null?void 0:i.superFollowing,rest_id:h},o)}function ge(e,t,o){var n;if(!!((n=t==null?void 0:t.users)!=null&&n.length))for(const s of t.users)S({is_blue_verified:s.ext_is_blue_verified,has_nft_avatar:(s==null?void 0:s.ext_has_nft_avatar)||!1,legacy:{blocking:(s==null?void 0:s.is_blocked)||!1,followed_by:s.social_context.followed_by,following:s.social_context.following,name:s.name,screen_name:s.screen_name,verified:s.verified,verified_type:(s==null?void 0:s.ext_verified_type)||"",followers_count:1e10},super_following:!1,rest_id:s.id_str},o)}var pe="/assets/chunk-a5b11d07.js";const B=document.createElement("script");B.src=chrome.runtime.getURL(pe);B.id="injected-blue-block-xhr";B.type="text/javascript";document.head.prepend(B);let O=document.createElement("link");O.href=m.runtime.getURL("src/injected/style.css");O.rel="stylesheet";(document.head||document.documentElement).appendChild(O);let z=document.createElement("div");z.id="injected-blue-block-toasts";document.body.appendChild(z);document.addEventListener("blue-blocker-event",function(e){if(e.detail.status<300)ce(e.detail.request.headers);else return;m.storage.sync.get(x).then(t=>{const o=t,n=e.detail.body;try{const s=JSON.parse(n);switch(e.detail.parsedUrl[1]){case"HomeLatestTimeline":case"HomeTimeline":case"SearchTimeline":case"UserTweets":case"TweetDetail":return me(e,s,o);case"timeline/home.json":case"search/adaptive.json":return he(e,s,o);case"search/typeahead.json":return ge(e,s,o);default:console.error(c,"found an unexpected url that we don't know how to handle",e),m.storage.local.set({[v]:{type:C}})}}catch(s){console.error(c,"unexpected error occurred while parsing request body",{error:s,body_str:n,event:e}),m.storage.local.set({[v]:{type:C}})}})});

this is a chrome-specific error that is caused when you load a new extension version. essentially, it just means that you have active tabs open that are trying to run the old code (that no longer has permission to run). you can avoid these by closing all your twitter tabs and then reloading the extension instead of reload the extension and then refreshing still-open twitter tabs.

The noted caveats are:—

  1. I'm not sure why or whether it's an issue that it logged storing 1,000 legacy verified users before it seemed to load the full database, as this didn't happen in Chrome; and

  2. It's still logging the soupcan response for @elonmusk error when the extension popup is opened on Twitter; this likewise is not happening in Chrome.

Neither of those issues seems to be affecting execution, however.

Hopefully it shouldn't affect anything, but I figure it's worth noting that because Firefox' release version does not allow loading unsigned extensions, I had to test this instead in Firefox Developer Edition 115.0b9 (64-bit).

so for 1. this is a console.debug message just to show that usres are in fact being added to the database. it takes a little while and when I'm debugging things I want to know immediately if it's working or if it's frozen or otherwise failed. it's set for 1000 because, basically, any time you make output, even to the console, it takes some small amount of time. so outputting something for all 407,520 users actually slows the process quite a bit, but doing it once every 1,000 is negligible. these message are actually output on chrome, too. but chrome hides console.debug message by default, and only shows console.log and greater. if you'd like, you can view these debug messages on chrome also by going into the console and clicking "Verbose" from the "Custom levels" drop down next to the filter search bar at the top of the console.

for 2., this is the fix I added in v0.3.1 to avoid using the management api. basically, we're sending a message to soupcan that we know has a response for it rather than asking the browser directly if the extension is installed. this log message was also removed in the official v0.3.2 release because it's just not necessary.

thanks again so much for all the help, and sticking with me throughout this whole process. before I go, I wanted to double check that this is NOT the error you were experiencing. I've seen this around on twitter and haven't been able to replicate it image

Anyasia commented 1 year ago

before I go, I wanted to double check that this is NOT the error you were experiencing. I've seen this around on twitter and haven't been able to replicate it image

That's the error message I got when the new permissions were required as per Issue #155. As I noted in my comment in that issue, the error persisted even after the new version (that no longer needed the new permissions) went live, and clicking it still prompted me to allow the additional permissions. At that point I just removed and reïnstalled it from the Chrome store, so I can't say for sure what would have happened had I accepted them. I kind of assume it would just ditch the new permissions immediately, given the live version no longer requested them, but who really knows? It's not like I can explain it still giving the error at that point in the first place, after all.

I'll update the extension in Firefox' release version and run some tests and let you know if anything breaks. Thanks once again for all your help! ♥

Anyasia commented 1 year ago

Alright, so, I've worked out the cause of the Firefox error, and I must apologize profusely, because it turns out it's entirely my fault!

Before I tested the extension in Firefox yesterday, I tweaked a few of Firefox' user præferences to align as best I could with my default præferences in Chrome. One of the settings I selected and thought nothing of was to 'Never remember history.' It turns out this silently sets an about:config flag, browser.privatebrowsing.autostart, which disables all service workers.

I don't know why I was even able to open the debug console with the service workers disabled—Chrome didn't let me do that, clearly, and Firefox' own documentation seems to indicate it should behave likewise—but that seems to be the cause of the 'mutation operation' error we discussed above. The error persisted when I updated to 0.3.2, but changing the history setting to disable that flag resulted in the same normal operation I encountered in the Developer Edition—where, in my haste to test today, I had neglected to tweak the præferences likewise.

So yeah, that's, uh, my fault, and again, I sincerely apologize for the confusion it's caused. Turns out not being a complete idiot is helpful in not encountering bewildering bugs! ┬─┬ノ( º _ ºノ)

kheina commented 1 year ago

oh lmao well at least it's not an issue with blue blocker! I guess if people have that issue they've probably seen it before, or at least have other extensions that are also having issues

before I go, I wanted to double check that this is NOT the error you were experiencing. I've seen this around on twitter and haven't been able to replicate it image

That's the error message I got when the new permissions were required as per Issue #155. As I noted in my comment in that issue, the error persisted even after the new version (that no longer needed the new permissions) went live, and clicking it still prompted me to allow the additional permissions. At that point I just removed and reïnstalled it from the Chrome store, so I can't say for sure what would have happened had I accepted them. I kind of assume it would just ditch the new permissions immediately, given the live version no longer requested them, but who really knows? It's not like I can explain it still giving the error at that point in the first place, after all.

I'll update the extension in Firefox' release version and run some tests and let you know if anything breaks. Thanks once again for all your help! ♥

ohh okay! I'll let anyone I see with that issue that the fix is to reinstall then. this thread has been immensely helpful. I think this means that everything in this thread should be fixed in v0.3.2

thanks for all the debugging and testing ❤️