├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .commitlintrc.json ├── icons ├── TLD_icon_enabled-32.png ├── TLD_icon_enabled-48.png ├── TLD_icon_enabled-64.png ├── TLD_icon_enabled-96.png ├── TLD_icon_disabled-128.png ├── TLD_icon_disabled-32.png ├── TLD_icon_disabled-48.png ├── TLD_icon_disabled-64.png ├── TLD_icon_disabled-96.png └── TLD_icon_enabled-128.png ├── .gitignore ├── web-ext-config.js ├── _locales ├── zh │ └── messages.json ├── ro │ └── messages.json ├── en │ └── messages.json ├── de │ └── messages.json └── fr │ └── messages.json ├── scripts ├── .eslintrc.json ├── content_script.js └── background_script.js ├── .versionrc.json ├── LICENSE ├── package.json ├── manifest.json ├── README.md └── CHANGELOG.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@commitlint/config-conventional" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /icons/TLD_icon_enabled-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_enabled-32.png -------------------------------------------------------------------------------- /icons/TLD_icon_enabled-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_enabled-48.png -------------------------------------------------------------------------------- /icons/TLD_icon_enabled-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_enabled-64.png -------------------------------------------------------------------------------- /icons/TLD_icon_enabled-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_enabled-96.png -------------------------------------------------------------------------------- /icons/TLD_icon_disabled-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_disabled-128.png -------------------------------------------------------------------------------- /icons/TLD_icon_disabled-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_disabled-32.png -------------------------------------------------------------------------------- /icons/TLD_icon_disabled-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_disabled-48.png -------------------------------------------------------------------------------- /icons/TLD_icon_disabled-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_disabled-64.png -------------------------------------------------------------------------------- /icons/TLD_icon_disabled-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_disabled-96.png -------------------------------------------------------------------------------- /icons/TLD_icon_enabled-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theAlinP/twitter-link-deobfuscator/HEAD/icons/TLD_icon_enabled-128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the folder with packaged Firefox extensions 2 | web-ext-artifacts/ 3 | 4 | # Ignore the installed Node.js modules 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | # Validate the commit message using commitlint 5 | npx --no-install commitlint --edit $1 6 | -------------------------------------------------------------------------------- /web-ext-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignoreFiles: [ 3 | 'package.json', 4 | 'package-lock.json', 5 | 'web-ext-config.js' 6 | ], 7 | run: { 8 | browserConsole: true, 9 | firefox: "/opt/firefox-aurora/firefox", 10 | startUrl: [ 11 | "about:debugging#/runtime/this-firefox", 12 | "https://twitter.com/firefox" 13 | ] 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /_locales/zh/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Twitter 連結還原器", 4 | "description": "這個擴充功能的名稱。" 5 | }, 6 | "extensionDescription": { 7 | "message": "揭露被 Twitter 伺服器縮短的推文、回覆、直接訊息,以及\"網站\"連結等原本的連結目的地。此擴充功能只在瀏覽 twitter.com 網站時啟動。和 x.com", 8 | "description": "這個擴充功能的描述。" 9 | }, 10 | "enabledStateTitle": { 11 | "message": "Twitter 連結還原器 - 已啟用", 12 | "description": "當擴充功能啟用時,將滑鼠移至工具列上的擴充功能圖示時顯示的標題。" 13 | }, 14 | "disabledStateTitle": { 15 | "message": "Twitter 連結還原器 - 已停用", 16 | "description": "當擴充功能停用時,將滑鼠移至工具列上的擴充功能圖示時顯示的標題。" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint:recommended", 4 | "env": { 5 | "browser": true, 6 | "webextensions": true, 7 | "es2021": true 8 | }, 9 | "rules": { 10 | "indent": [ 11 | "warn", 12 | 2 13 | ], 14 | "linebreak-style": [ 15 | "error", 16 | "unix" 17 | ], 18 | "no-console": "off", 19 | "no-fallthrough": "off", 20 | "prefer-arrow-callback": 1, 21 | "quotes": [ 22 | "error", 23 | "double" 24 | ], 25 | "semi": [ 26 | "error", 27 | "always" 28 | ], 29 | "strict": [ 30 | "error", 31 | "global" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n", 3 | "types": [ 4 | {"type": "feat", "section": "Features"}, 5 | {"type": "fix", "section": "Bug Fixes"}, 6 | {"type": "chore", "section": "Other updates"}, 7 | {"type": "docs", "section": "Other updates"}, 8 | {"type": "style", "section": "Other updates"}, 9 | {"type": "refactor", "section": "Other updates"}, 10 | {"type": "perf", "section": "Other updates"}, 11 | {"type": "test", "section": "Other updates"}, 12 | {"type": "build", "section": "Other updates"}, 13 | {"type": "ci", "section": "Other updates"}, 14 | {"type": "revert", "section": "Other updates"} 15 | ], 16 | "tagPrefix": "", 17 | "skip": { 18 | "commit": true, 19 | "tag": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /_locales/ro/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Twitter Link Deobfuscator", 4 | "description": "Numele extensiei." 5 | }, 6 | "extensionDescription": { 7 | "message": "Dezvăluiește destinațiile originale ale link-urilor și ale Cardurilor Twitter din tweet-uri și răspunsuri cât și din Mesaje Directe, dar și link-ul „Site Web” și altele, care sunt de obicei ascunse folosind adrese URL scurtate. Extensia intră în acțiune doar când se navighează pe twitter.com și x.com.", 8 | "description": "Descrierea extensiei." 9 | }, 10 | "enabledStateTitle": { 11 | "message": "Twitter Link Deobfuscator - ACTIVAT", 12 | "description": "Titlul afișat la planarea cursorului peste pictograma extensiei din bara de unelte când este activată." 13 | }, 14 | "disabledStateTitle": { 15 | "message": "Twitter Link Deobfuscator - DEZACTIVAT", 16 | "description": "Titlul afișat la planarea cursorului peste pictograma extensiei din bara de unelte când este dezactivată." 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Twitter Link Deobfuscator", 4 | "description": "The name of the extension." 5 | }, 6 | "extensionDescription": { 7 | "message": "Reveals the original destinations of the links and of the Twitter Cards from tweets and replies as well as from Direct Messages, but also the \"Website\" link and others, which are usually concealed using shortened URLs. The extension goes into action only while browsing twitter.com and x.com.", 8 | "description": "The description of the extension." 9 | }, 10 | "enabledStateTitle": { 11 | "message": "Twitter Link Deobfuscator - ENABLED", 12 | "description": "The title shown while hovering the cursor over the extension's icon in the toolbar when is is enabled." 13 | }, 14 | "disabledStateTitle": { 15 | "message": "Twitter Link Deobfuscator - DISABLED", 16 | "description": "The title shown while hovering the cursor over the extension's icon in the toolbar when is is disabled." 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Twitter Link Deobfuscator", 4 | "description": "The name of the extension." 5 | }, 6 | "extensionDescription": { 7 | "message": "Offenbart die tatsächlichen Ziele der Links und Twitter Cards in Tweets, Antworten als auch in Direktnachrichten, aber auch den \"Website\"-Link and andere, die gewöhnlich durch verkürzte URLs verschleiert werden. Die Erweiterung tritt nur in Aktion, wenn twitter.com und x.com besucht werden.", 8 | "description": "The description of the extension." 9 | }, 10 | "enabledStateTitle": { 11 | "message": "Twitter Link Deobfuscator - AKTIVIERT", 12 | "description": "The title shown while hovering the cursor over the extension's icon in the toolbar when is is enabled." 13 | }, 14 | "disabledStateTitle": { 15 | "message": "Twitter Link Deobfuscator - DEAKTIVIERT", 16 | "description": "The title shown while hovering the cursor over the extension's icon in the toolbar when is is disabled." 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Twitter Link Deobfuscator", 4 | "description": "Le nom de l'extension." 5 | }, 6 | "extensionDescription": { 7 | "message": "Révèle les destinations originales des liens et des Cartes Twitter de tweets et de réponses ainsi que de Messages Directs, mais aussi le lien «Site Web» et d'autres, qui sont généralement cachés à l'aide des URL raccourcies. L'extension entre en action uniquement en naviguant sur twitter.com et x.com.", 8 | "description": "La description de l'extension." 9 | }, 10 | "enabledStateTitle": { 11 | "message": "Twitter Link Deobfuscator - ACTIVÉ", 12 | "description": "Le titre affiché en planer le curseur au dessous de l'icône de l'extension dans la barre d'utils quand elle est activé." 13 | }, 14 | "disabledStateTitle": { 15 | "message": "Twitter Link Deobfuscator - DÉSACTIVÉ", 16 | "description": "Le titre affiché en planer le curseur au dessous de l'icône de l'extension dans la barre d'utils quand elle est désactivé." 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alin Pasol 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitter-link-deobfuscator", 3 | "version": "1.8.0", 4 | "description": "Reveals the original destinations of the links and of the Twitter Cards from tweets and replies as well as from Direct Messages, but also the \"Website\" link and others, which are usually concealed using shortened URLs.", 5 | "scripts": { 6 | "lint": "npx web-ext lint", 7 | "load": "npx web-ext run", 8 | "build": "npx web-ext build", 9 | "cmt": "npx cz", 10 | "prepare": "npx husky install", 11 | "release": "npx standard-version" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/theAlinP/twitter-link-deobfuscator.git" 16 | }, 17 | "keywords": [ 18 | "WebExtension", 19 | "Twitter", 20 | "link", 21 | "cleaning" 22 | ], 23 | "author": "Alin Pasol", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/theAlinP/twitter-link-deobfuscator/issues" 27 | }, 28 | "homepage": "https://github.com/theAlinP/twitter-link-deobfuscator#readme", 29 | "devDependencies": { 30 | "@commitlint/cli": "^19.3.0", 31 | "@commitlint/config-conventional": "^19.2.2", 32 | "commitizen": "^4.3.0", 33 | "cz-conventional-changelog": "^3.3.0", 34 | "husky": "^9.0.11", 35 | "standard-version": "^9.5.0", 36 | "web-ext": "^8.2.0" 37 | }, 38 | "config": { 39 | "commitizen": { 40 | "path": "./node_modules/cz-conventional-changelog" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "__MSG_extensionName__", 4 | "version": "1.8.0", 5 | "author": "Alin Pasol", 6 | "homepage_url": "https://github.com/theAlinP/twitter-link-deobfuscator", 7 | "description": "__MSG_extensionDescription__", 8 | "default_locale": "en", 9 | "content_scripts": [ 10 | { 11 | "matches": [ 12 | "*://*.twitter.com/*", 13 | "*://*.x.com/*" 14 | ], 15 | "js": [ 16 | "scripts/content_script.js" 17 | ] 18 | } 19 | ], 20 | "browser_action": { 21 | "default_icon": { 22 | "32": "icons/TLD_icon_enabled-32.png", 23 | "48": "icons/TLD_icon_enabled-48.png", 24 | "64": "icons/TLD_icon_enabled-64.png", 25 | "96": "icons/TLD_icon_enabled-96.png", 26 | "128": "icons/TLD_icon_enabled-128.png" 27 | }, 28 | "default_title": "__MSG_enabledStateTitle__" 29 | }, 30 | "icons": { 31 | "32": "icons/TLD_icon_enabled-32.png", 32 | "48": "icons/TLD_icon_enabled-48.png", 33 | "64": "icons/TLD_icon_enabled-64.png", 34 | "96": "icons/TLD_icon_enabled-96.png", 35 | "128": "icons/TLD_icon_enabled-128.png" 36 | }, 37 | "browser_specific_settings": { 38 | "gecko": { 39 | "id": "{d820ea6b-de81-4b11-ad1f-832314b8a49f}" 40 | } 41 | }, 42 | "background": { 43 | "scripts": [ 44 | "scripts/background_script.js" 45 | ] 46 | }, 47 | "permissions": [ 48 | "*://*.twitter.com/*", 49 | "*://*.x.com/*", 50 | "storage", 51 | "tabs", 52 | "webRequest", 53 | "webRequestBlocking" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | 5 | #=============================================================================== 6 | # This script should be used as a Git hook 7 | # It can be copied to the .git/hooks directory or you can run the command 8 | # `git config core.hooksPath git_hooks` 9 | # to use the git_hooks directory as the path Git looks for hooks in. 10 | # Warning: Checking out older commits could change the version of this Git hook 11 | #=============================================================================== 12 | 13 | 14 | #------------------------------------------------------------------------------- 15 | # Validate the source code with web-ext 16 | # If there is a web-ext version installed locally, the script will exit with the 17 | # status code of the `web-ext lint` command. 18 | # If there is a global and a local version of web-ext, the local version will be 19 | # used. If there is only a global version installed, that one will be used. 20 | # If there is no web-ext module installed, neither globally nor locally, the 21 | # script exits with an exit status of 0 and allows the commit to continue, 22 | # meaning this hook can be enabled even if the dependencies were not installed 23 | #------------------------------------------------------------------------------- 24 | 25 | shopt -s expand_aliases 26 | alias webExt='npx --no-install web-ext' 27 | 28 | #npm list web-ext >& /dev/null # the proper way to check if web-ext is installed locally but it scans all the installed packages 29 | webExt --version &> /dev/null # a hack to check if web-ext is installed locally or globally 30 | webExt_exit_code=$? 31 | 32 | #[ "${webExt_exit_code}" -ne 0 ] && exit 0 33 | if [[ "${webExt_exit_code}" == 0 ]]; then 34 | echo "" 35 | echo "Please wait while the source code is validated using web-ext" 36 | echo "The commit will be aborted if any errors are found" 37 | webExt lint # lint the add-on 38 | webExtReturn=$? # store the exit code in a variable 39 | echo "" 40 | exit $webExtReturn # exit with the exit code of the linting command 41 | else 42 | echo "The web-ext package is not installed neither globally nor locally" 43 | echo "This script will exit and the commit will not be aborted" 44 | exit 0 45 | fi 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Twitter Link Deobfuscator 2 | 3 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 4 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) 5 | 6 | Reveals the original destinations of the links and of the Twitter Cards from tweets and replies as well as from Direct Messages, but also the "Website" link and others, which are usually concealed using shortened URLs. 7 | 8 | Okay, okay. I know that "deobfuscator" is not a real word, but it seems like the right word to describe what the add-on does. Plus, if "obfuscator" is a real one, then I think "deobfuscator" deserves some consideration, too. 9 | 10 | ## How it works 11 | The Twitter servers secretly conceal the hyperlinks' destinations with shortened [t.co](https://t.co "https://t.co") URLs. This add-on replaces the shortened URLs with the original ones then increases the counter with the number of cleaned links and Twitter Cards overlapping the TLD icon. 12 | 13 | Please note that TLD will expand URLs from t.co to other shortened URLs, like bit.ly or mzl.la, if the user originally posted those shortened URLs, but it will not further expand the original shortened URLs to their final destinations. 14 | 15 | NOTE: This extension goes into action only while browsing [twitter.com](https://twitter.com "https://twitter.com") or [x.com](https://x.com "https://x.com") and stays dormant when other websites are browsed. 16 | 17 | The add-on can be enabled and disabled by clicking its icon from the browser toolbar. Once the add-on is installed and while it is enabled, it will wait in the background for a page from [twitter.com](https://twitter.com "https://twitter.com") or [x.com](https://x.com "https://x.com") to be opened and only then it will go into action and scan the web page for any hyperlinks with shortened URLs and clean them. 18 | 19 | Twitter Link Deobfuscator only needs the minimum amount of permissions. It does not collect, use, store nor share user data. 20 | 21 | Right now, only three translations, into Romanian, French and German, are available. They will be used automatically if the browser is configured to use any of the respective languages for displaying menus, messages and notifications. Otherwise, it will default to English. 22 | 23 | ## Installation 24 | The easiest and safest way would be to go to AMO (addons.mozilla.org) and install the stable [Twitter Link Deobfuscator](https://addons.mozilla.org/en-US/firefox/addon/twitter-link-deobfuscator/ "Twitter Link Deobfuscator") from there. 25 | 26 | You can then (or before) clone/download this Git repository and temporarily install TLD from the latest commit. Please note that it might be buggy because the latest commits are not tested extensively. To install an add-on temporarily you need to enter "about:debugging" in the browser's address bar, click "Load Temporary Add-on", in the new window that appears select the "manifest.json" file from TLD's repository then click "Open". If TLD was not already installed, it will be installed temporarily until the browser is closed and if it was, it will be replaced temporarily by the updated add-on from the repository. 27 | 28 | There is another way to install add-ons temporarily, and that is by using the [web-ext](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Getting_started_with_web-ext "Getting started with web-ext") command line tool. Web-ext is a Node.js module that can do a lot more than just allow installing add-ons but I leave that for you to discover. 29 | 30 | ## Disclaimer 31 | TLD is guaranteed to work correctly on [twitter.com](https://twitter.com "https://twitter.com") or [x.com](https://x.com "https://x.com"), and the subdomain [mobile.twitter.com](https://mobile.twitter.com "https://mobile.twitter.com") or [mobile.x.com](https://mobile.x.com "https://mobile.x.com"), which is the version optimized for mobile devices. 32 | 33 | I cannot guarantee that it will work on any other subdomains (about.twitter.com or about.x.com, analytics/careers/data/developer/help/media/marketing.twitter.com or analytics/careers/data/developer/help/media/marketing.x.com etc.). Because not many people browse those subdomains and because there is not much user generated content there, in the sense of tweets, just some occasional quoted ones, I did not spend much time to make it work there. I intend to get to it at some point, but I don't know when. If someone says they need TLD to work on subdomains, I will make this a priority. 34 | 35 | ## For developers 36 | You can find detailed JSDoc comments in the source code that will help you understand how the add-on works. You can generate documentation from them using the [jsdoc](https://github.com/jsdoc/jsdoc/ "JSDoc") tool as described in the page linked earlier. 37 | 38 | ## Support 39 | In order to report bugs or ask for support please use Twitter Link Deobfuscator's [Issues](https://github.com/theAlinP/twitter-link-deobfuscator/issues "Issues") page instead. 40 | 41 | ## License 42 | This software is licensed under the MIT License (MIT Expat License). The full text can be found in the file LICENSE. 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | 6 | 7 | ## [1.8.0](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.7.2...1.8.0) (2024-07-14) 8 | 9 | 10 | ### Features 11 | 12 | * uncloak the Twitter Cards from the "Communities" page ([3fdaa00](https://github.com/theAlinP/twitter-link-deobfuscator/commit/3fdaa000d3387a67a8481eec7ea347c5b244b980)) 13 | * uncloak the text links from the "Communities" page ([ecf8e64](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ecf8e648b44865bc8595af1fc2d0989d0fa69094)) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * uncloak the Twitter Cards from the "Bookmarks" page ([de114ed](https://github.com/theAlinP/twitter-link-deobfuscator/commit/de114ed38ba7a4cacd15d81417c6a00e478042f2)) 19 | * uncloak the Twitter Cards from the "Explore" page ([e582a73](https://github.com/theAlinP/twitter-link-deobfuscator/commit/e582a7386c9f20462d002f4e6b2abf54dbf2a1c1)) 20 | * uncloak the text links from the "Explore" page ([61f3f32](https://github.com/theAlinP/twitter-link-deobfuscator/commit/61f3f32e02544941cce8c2a99ec696824d71e75b)) 21 | 22 | 23 | ### Other updates 24 | 25 | * update the dependencies ([8ca46d5](https://github.com/theAlinP/twitter-link-deobfuscator/commit/8ca46d58f91bc0899bb8630029059afdd8fb810d)) 26 | * remove unnecessary lines of code ([dd311b4](https://github.com/theAlinP/twitter-link-deobfuscator/commit/dd311b43938760f81b5251fd07ba352ec3a13070)) 27 | * **deps-dev:** bump braces from 3.0.2 to 3.0.3 ([6200e15](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6200e159c43b72cb8d46e9e1b1761d9dacb7751a)) 28 | 29 | 30 | 31 | ### [1.7.2](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.7.1...1.7.2) (2024-05-24) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * uncloak the Twitter Cards from pinned and regular tweets from profile pages ([89cd135](https://github.com/theAlinP/twitter-link-deobfuscator/commit/89cd135915657806fd7fc8160674751c6c0320ab)) 37 | * detect and uncloak the text links again ([699bcce](https://github.com/theAlinP/twitter-link-deobfuscator/commit/699bcce231b69e612ee2c51766996eb7870b1c75)) 38 | * run on the x.com domain, too ([ce3d06c](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ce3d06c459c05809331dbd4d8fc8b60a686cfd49)) 39 | 40 | 41 | ### Other updates 42 | 43 | * update the dependencies ([902f5dd](https://github.com/theAlinP/twitter-link-deobfuscator/commit/902f5ddeecd08e9f18d803b5e4446504d95acf5d)) 44 | 45 | 46 | ### [1.7.1](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.7.0...1.7.1) (2024-04-21) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * uncloak the text links again ([7659813](https://github.com/theAlinP/twitter-link-deobfuscator/commit/765981309356c81b2bdb9810664eb72d192a67c4)), closes [#35](https://github.com/theAlinP/twitter-link-deobfuscator/issues/35) 52 | 53 | 54 | ### Other updates 55 | 56 | * update the dependencies ([3a5ea76](https://github.com/theAlinP/twitter-link-deobfuscator/commit/3a5ea7610023ac6eca31c5298a479905a2900676)), closes [#34](https://github.com/theAlinP/twitter-link-deobfuscator/issues/34) 57 | * update the year from the LICENSE file ([6e8c315](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6e8c31579668394d8dbd6c0ceff9550175cfde5e)) 58 | 59 | 60 | ## [1.7.0](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.6.1...1.7.0) (2023-06-05) 61 | 62 | 63 | ### Features 64 | 65 | * add Chinese(zh) locale ([ac8ae12](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ac8ae1236102390993c69a87d58362815367396a)) 66 | 67 | 68 | ### Bug Fixes 69 | 70 | * uncloak the Twitter Cards from quoted tweets ([ff0b22a](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ff0b22ab31f1e92d2481ecd0fe0f0675d993afcd)) 71 | 72 | 73 | ### Other updates 74 | 75 | * **deps:** bump cacheable-request and got ([a18f169](https://github.com/theAlinP/twitter-link-deobfuscator/commit/a18f1691ca56c8fabd5e5ce574f744823a19fb7e)) 76 | * update the dependencies ([f0b7462](https://github.com/theAlinP/twitter-link-deobfuscator/commit/f0b7462b16a9f37fef733d87cfc55a559b106540)), closes [#28](https://github.com/theAlinP/twitter-link-deobfuscator/issues/28) 77 | * update the dependencies ([92b8280](https://github.com/theAlinP/twitter-link-deobfuscator/commit/92b8280390292347bece8070a323aa8ea057cfea)) 78 | 79 | 80 | ### [1.6.1](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.6.0...1.6.1) (2022-10-23) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * uncloak the text links from replies to tweets with photos ([de304ec](https://github.com/theAlinP/twitter-link-deobfuscator/commit/de304ec424a7cd4859f1d1c74da4dfa6fda991e6)) 86 | * uncloak the Twitter Cards from the top tweets from the "Home" page ([6cd15da](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6cd15daa8195308b465fb7cb3e43aba60c2530ad)) 87 | 88 | 89 | ### Other updates 90 | 91 | * update the dependencies ([0f738d5](https://github.com/theAlinP/twitter-link-deobfuscator/commit/0f738d5b0c363291c79a80e0a5ff0e87e99f0057)) 92 | * improve readability ([1115150](https://github.com/theAlinP/twitter-link-deobfuscator/commit/1115150ffbd7baab9d5c7f11653cfceb43f5b242)) 93 | 94 | ## [1.6.0](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.5.2...1.6.0) (2022-09-04) 95 | 96 | 97 | ### Features 98 | 99 | * support Twitter Tor service(.onion domain) ([d520aa7](https://github.com/theAlinP/twitter-link-deobfuscator/commit/d520aa715c5568653daad94b5a1674b3c588e159)) 100 | 101 | 102 | ### Other updates 103 | 104 | * update the dependencies ([742512d](https://github.com/theAlinP/twitter-link-deobfuscator/commit/742512d5aa200f441bdbf5c38ca4a70af2ca0d07)) 105 | * replace the "/" between the domains to not confuse them with an URL ([0b67acf](https://github.com/theAlinP/twitter-link-deobfuscator/commit/0b67acf7e489ad727778e9cd83db6e504a38a1e6)) 106 | 107 | ### [1.5.2](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.5.1...1.5.2) (2022-07-07) 108 | 109 | 110 | ### Bug Fixes 111 | 112 | * uncloak the Twitter Cards from additional replies to tweets ([9c92e32](https://github.com/theAlinP/twitter-link-deobfuscator/commit/9c92e321c3edd16ea719e967b683eada9ada6932)) 113 | * uncloak the Twitter Cards from the "Home" page - again ([9dcb74c](https://github.com/theAlinP/twitter-link-deobfuscator/commit/9dcb74c039876e811aead068a3e29197af7931b5)) 114 | * improve the detection of polls from Twitter Cards ([99d0acb](https://github.com/theAlinP/twitter-link-deobfuscator/commit/99d0acb5599aa9409701570c65a0ccd9e6e83da6)) 115 | 116 | 117 | ### Other updates 118 | 119 | * update the dependencies ([408aab9](https://github.com/theAlinP/twitter-link-deobfuscator/commit/408aab92ac49c9bddc74ee36cd2a0acfb8ed4eca)) 120 | 121 | ### [1.5.1](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.5.0...1.5.1) (2022-06-05) 122 | 123 | 124 | ### Bug Fixes 125 | 126 | * clean the Twitter Cards from individual tweets ([643cb3a](https://github.com/theAlinP/twitter-link-deobfuscator/commit/643cb3a163d005b62ea0d404a0cd47bf6f7acf2c)) 127 | 128 | 129 | ### Other updates 130 | 131 | * update the dependencies ([db71137](https://github.com/theAlinP/twitter-link-deobfuscator/commit/db71137b5b0ad0ee9d6e427843b908f5082d60e1)) 132 | 133 | ## [1.5.0](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.4.0...1.5.0) (2022-05-23) 134 | 135 | 136 | ### Features 137 | 138 | * uncloak the Twitter Cards from "Topic" pages ([b4932cb](https://github.com/theAlinP/twitter-link-deobfuscator/commit/b4932cb55e872086164fb0c13e3f536c8d013166)) 139 | * uncloak the text links from "Topic" pages ([9014428](https://github.com/theAlinP/twitter-link-deobfuscator/commit/9014428ea4d8db52d0bba2b7887b4ac365902a04)) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * uncloak the Twitter Cards from "List" pages ([d5990b3](https://github.com/theAlinP/twitter-link-deobfuscator/commit/d5990b39a8e38e80263cbd8a69212f0a862a66d6)) 145 | * uncloak the Twitter Cards from profile pages ([60b7e30](https://github.com/theAlinP/twitter-link-deobfuscator/commit/60b7e30a60cb93f1a6fa12b91036c00bc7b2efbf)) 146 | * fix the way to detect "event" or unknown pages ([a2ebf6d](https://github.com/theAlinP/twitter-link-deobfuscator/commit/a2ebf6dbf18ed7f64c83366c311337db4ceb89c8)) 147 | * uncloak the text links from the Direct Messages box/drawer ([95e6811](https://github.com/theAlinP/twitter-link-deobfuscator/commit/95e68114254d57cef061e6d29f88d17807da824d)) 148 | 149 | 150 | ### Other updates 151 | 152 | * remove unnecessary optional chaining ([fde4391](https://github.com/theAlinP/twitter-link-deobfuscator/commit/fde439117e4bcaf4f8ab62c76693dbb2c8c5441c)) 153 | * update the dependencies ([b4f3a78](https://github.com/theAlinP/twitter-link-deobfuscator/commit/b4f3a78aaa4c9a2ab8ce86f5a5ab305d470d7270)) 154 | 155 | ## [1.4.0](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.3.9...1.4.0) (2021-10-05) 156 | 157 | 158 | ### Features 159 | 160 | * uncloak the text links from "event" pages ([e1b24cd](https://github.com/theAlinP/twitter-link-deobfuscator/commit/e1b24cd99a854d292d4d7a33e6715a92648430e6)) 161 | * uncloak the Twitter Cards from "event" pages ([cc42ce7](https://github.com/theAlinP/twitter-link-deobfuscator/commit/cc42ce70724b3baf2b5df9c79716250498b5553d)) 162 | * uncloak the Twitter Cards from the "Explore" page ([206a018](https://github.com/theAlinP/twitter-link-deobfuscator/commit/206a01833cbed2e713685d2f3b0f986b25ab0bf5)) 163 | 164 | 165 | ### Other updates 166 | 167 | * update the dependencies ([7a9ef46](https://github.com/theAlinP/twitter-link-deobfuscator/commit/7a9ef46fae721896f6fff271dfd8262309cc130c)) 168 | 169 | ### [1.3.9](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.3.8...1.3.9) (2021-09-18) 170 | 171 | 172 | ### Bug Fixes 173 | 174 | * uncloak the Twitter Cards from the retweets on the "Home" page ([e155786](https://github.com/theAlinP/twitter-link-deobfuscator/commit/e155786c85b13ee97f6236735becf3599feec255)) 175 | * uncloak the Twitter Cards from the "Lists" page again ([db0e1d4](https://github.com/theAlinP/twitter-link-deobfuscator/commit/db0e1d48975b1ff02cceb899671d999255bebbb5)) 176 | * uncloak the Twitter Cards from the Bookmarks page again ([69dfc33](https://github.com/theAlinP/twitter-link-deobfuscator/commit/69dfc33482a0b75548245d8d81ec183f59d26770)) 177 | * uncloak the Twitter Cards from clicked-on tweets again ([0f47e9f](https://github.com/theAlinP/twitter-link-deobfuscator/commit/0f47e9f9ee9cf3506a6e3233a3b0e467fe1ffa64)) 178 | 179 | 180 | ### Other updates 181 | 182 | * update the dependencies ([447c92f](https://github.com/theAlinP/twitter-link-deobfuscator/commit/447c92f7a84a689ad8cab7399a3e681cadcc0077)) 183 | * remove references to object properties from outdated nestings ([0b9229c](https://github.com/theAlinP/twitter-link-deobfuscator/commit/0b9229ceac79798a22f05ddb0964c8fad8f06f24)) 184 | * merge 2 tweet cleaning functions into 1 ([29f3429](https://github.com/theAlinP/twitter-link-deobfuscator/commit/29f3429ec50b105195289706273ea9a1ca920baf)) 185 | * create a separate function to select and return the tweet entries ([a56497c](https://github.com/theAlinP/twitter-link-deobfuscator/commit/a56497c521ffa0405ef8f7ce2ecaadbbddcb4e03)) 186 | * use the strict equality operator where possible ([e0be872](https://github.com/theAlinP/twitter-link-deobfuscator/commit/e0be8721f32b1260792424930cb18ffe83b55859)) 187 | * use nesting instead of continue/return to control code execution ([b065410](https://github.com/theAlinP/twitter-link-deobfuscator/commit/b06541040dd429c753c9e55d31f5fa62614186bc)) 188 | * remove unnecessary return statement ([ce530e1](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ce530e1a834f5a9f6eb709c7cdd531b129b23272)) 189 | * reduce nesting ([50c7cff](https://github.com/theAlinP/twitter-link-deobfuscator/commit/50c7cffd57c14f9a5f457605340c45ea297afc34)) 190 | * remove duplications and similar code ([a79be0e](https://github.com/theAlinP/twitter-link-deobfuscator/commit/a79be0e0167164ff773402aa3e6f15265ac04b30)) 191 | * rephrase a JSDoc comment ([fe44d4e](https://github.com/theAlinP/twitter-link-deobfuscator/commit/fe44d4e9c41e89ed1ac407bb71787bc1c2234d17)) 192 | * add missing optional chaining ([3171101](https://github.com/theAlinP/twitter-link-deobfuscator/commit/317110163c21fcb11e25ce76488edadbbfbefe4b)) 193 | * remove unnecessary optional chaining ([7670aa4](https://github.com/theAlinP/twitter-link-deobfuscator/commit/7670aa4eefccef22c2f22b63e342df4df082abd2)) 194 | 195 | ### [1.3.8](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.3.7...1.3.8) (2021-06-22) 196 | 197 | 198 | ### Bug Fixes 199 | 200 | * uncloak the Twitter Cards from the profile pages again ([0dcceca](https://github.com/theAlinP/twitter-link-deobfuscator/commit/0dcceca167a054ad791047cc5ed8c1406be8f9e0)) 201 | 202 | 203 | ### Other updates 204 | 205 | * update the dependencies ([ba6b3fc](https://github.com/theAlinP/twitter-link-deobfuscator/commit/ba6b3fc264a39e22905e364eb7ed51be17572c78)) 206 | * remove unnecessary code ([a85587f](https://github.com/theAlinP/twitter-link-deobfuscator/commit/a85587f223646706fdcea6005d16b33cb24b09b5)) 207 | * update the ESLint configuration file ([6fb09e8](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6fb09e8ab59832dfde3f4d29690de55c059fc311)) 208 | 209 | ### [1.3.7](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.3.6...1.3.7) (2021-05-12) 210 | 211 | 212 | ### Bug Fixes 213 | 214 | * **background_script.js:** uncloak the Twitter Cards from the profile pages ([d75109b](https://github.com/theAlinP/twitter-link-deobfuscator/commit/d75109ba860594680206e898d3cdc2ff4eb7a9ee)) 215 | 216 | 217 | ### Other updates 218 | 219 | * update dependencies ([24a4dec](https://github.com/theAlinP/twitter-link-deobfuscator/commit/24a4dec7624eb9db0fd272c842be8993bcacc173)) 220 | 221 | ### [1.3.6](https://github.com/theAlinP/twitter-link-deobfuscator/compare/1.3.5...1.3.6) (2021-05-06) 222 | 223 | 224 | ### Bug Fixes 225 | 226 | * use optional chaining to prevent crashes in some browsers ([7ae7be0](https://github.com/theAlinP/twitter-link-deobfuscator/commit/7ae7be0daf8dda6872ff55bf5285c7cd811a69e7)), closes [#17](https://github.com/theAlinP/twitter-link-deobfuscator/issues/17) 227 | 228 | 229 | ### Other updates 230 | 231 | * configure standard-version ([dae38a8](https://github.com/theAlinP/twitter-link-deobfuscator/commit/dae38a89b6bf6172ed097450be4a8b78e3a12b29)) 232 | * add Standard Version as a development dependency ([6b6399e](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6b6399ed4bce981a2505a959314a0a142370c927)) 233 | * convert the Commitlint configuration to a JSON file ([67bfd87](https://github.com/theAlinP/twitter-link-deobfuscator/commit/67bfd87e3e7dd2aa24ebf2f49f6ec280487694c7)) 234 | * add Commitlint and Husky as development dependencies ([f2bc4a8](https://github.com/theAlinP/twitter-link-deobfuscator/commit/f2bc4a80dbcc2f564938125552dcfdafd04281e4)) 235 | * add Commitizen as a development dependency ([d5caad4](https://github.com/theAlinP/twitter-link-deobfuscator/commit/d5caad46e49427f450beae0705ecd9e856635941)) 236 | * add a pre-commit hook to lint the source files ([6af4e49](https://github.com/theAlinP/twitter-link-deobfuscator/commit/6af4e499ff4802f297aef36440722b73484dbf74)) 237 | * add Web-ext as a development dependency ([bad55b5](https://github.com/theAlinP/twitter-link-deobfuscator/commit/bad55b53313c5cd3d5544bce292ff6261f945f41)) 238 | * create a package.json file ([e583d39](https://github.com/theAlinP/twitter-link-deobfuscator/commit/e583d39731295530ae38fd8f86fed3b3b49cf9b7)) 239 | * add a .gitignore file to ignore the built extension packages ([01661b9](https://github.com/theAlinP/twitter-link-deobfuscator/commit/01661b9799ebe29335d50e950c93b19e0d38018b)) 240 | * add a Conventional Commits badge ([152a7ce](https://github.com/theAlinP/twitter-link-deobfuscator/commit/152a7ce684e6276e94fc4018ab07d7a4f769927c)) 241 | -------------------------------------------------------------------------------- /scripts/content_script.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | /** 5 | * The namespace that will contain all the methods and properties 6 | * @namespace TLD 7 | */ 8 | var TLD = TLD || {}; 9 | //console.log(TLD); // for debugging 10 | 11 | 12 | /** 13 | * A function that communicates with the background script {@link boolean} 14 | * @method notifyBackgroundScript 15 | * @memberof TLD 16 | * @param {object} message - The message to be sent to the background script 17 | */ 18 | TLD.notifyBackgroundScript = function(message) { 19 | let sending = browser.runtime.sendMessage(message); 20 | sending.then(TLD.handleResponse, TLD.handleError); // a response is received from the background script only if sendResponse is used 21 | }; 22 | 23 | 24 | /** 25 | * A function that handles the responses coming from the background script 26 | * @method handleResponse 27 | * @memberof TLD 28 | * @param {object} message - The response received from the background script 29 | * after sending it a message from TLD.notifyBackgroundScript() 30 | * @param {string} message.response - The contents of the response 31 | */ 32 | /*TLD.handleResponse = function(message) { 33 | console.log("Response from the background script:"); // for debugging 34 | console.log(message); // for debugging 35 | };*/ // for debugging 36 | TLD.handleResponse = function() {}; 37 | 38 | 39 | /** 40 | * A function that handles any messaging errors 41 | * @method handleError 42 | * @memberof TLD 43 | * @param {object} error - An object as defined by the browser 44 | */ 45 | TLD.handleError = function(error) { 46 | //console.error(error); // for debugging 47 | console.error(`Error: ${error.message}`); 48 | }; 49 | 50 | 51 | /** 52 | * A function that reveals the original values of the "href" attributes on pages built with React 53 | * @async 54 | * @method revealReactLinks 55 | * @memberof TLD 56 | * @param {HTMLDivElement} container - The element containing the tweets or 57 | * replies. It should be the type of element returned by getElementById() or 58 | * querySelector() or similar methods 59 | */ 60 | TLD.revealReactLinks = async function(container) { 61 | //console.log(container); // for debugging 62 | let storedSettings = await browser.storage.local.get(); // check if the add-on is enabled 63 | //console.log(`The add-on state is: ${storedSettings.enabled}`); // for debugging 64 | if (storedSettings.enabled === true) { // clean the links only if the add-on is enabled 65 | let links = TLD.selectLinks(container); 66 | //console.log(links); // for debugging 67 | TLD.uncloakLinks(links); 68 | } 69 | }; 70 | 71 | 72 | /** 73 | * A function that cleans the links from the user description and the 74 | * "Website" link, on pages built with React, if there are any 75 | * @async 76 | * @method cleanReactWebsiteLink 77 | * @memberof TLD 78 | */ 79 | TLD.cleanReactWebsiteLink = async function() { 80 | let storedSettings = await browser.storage.local.get(); // check if the add-on is enabled 81 | //console.log(`The add-on state is: ${storedSettings.enabled}`); // for debugging 82 | if (storedSettings.enabled !== true) { 83 | return; 84 | } // don't clean the links if the add-on is not enabled 85 | let userDescription = document.querySelector("div[data-testid=\"UserDescription\"]"); 86 | //console.log(userDescription); // for debugging 87 | let userDescriptionLinks = TLD.selectLinks(userDescription); 88 | //console.log(userDescriptionLinks); // for debugging 89 | TLD.uncloakLinks(userDescriptionLinks); 90 | let userProfileHeader = document.querySelector("div[data-testid=\"UserProfileHeader_Items\"]"); 91 | //console.log(userProfileHeader); // for debugging 92 | let userProfileHeaderLinks = TLD.selectLinks(userProfileHeader); 93 | //console.log(userProfileHeaderLinks); // for debugging 94 | TLD.uncloakLinks(userProfileHeaderLinks); 95 | }; 96 | 97 | 98 | /** 99 | * A function that listens for added tweets or replies on pages built with React 100 | * then cleans the links inside them 101 | * @method listenForReactTweetsAndReplies 102 | * @memberof TLD 103 | * @param {HTMLDivElement} container - The element containing the tweets or 104 | * replies. It should be the type of element returned by getElementById() or 105 | * querySelector() or similar methods 106 | */ 107 | TLD.listenForReactTweetsAndReplies = function(container) { 108 | //console.log(container); // for debugging 109 | 110 | TLD.revealReactLinks(container); 111 | 112 | /** 113 | * Call TLD.revealReactLinks() every time new tweets or replies are added 114 | */ 115 | TLD.tweetsAndRepliesContainerObserver = new MutationObserver(() => { 116 | //console.log("TLD.tweetsAndRepliesContainerObserver"); // for debugging 117 | TLD.revealReactLinks(container); 118 | }); 119 | const tweetsAndRepliesContainerObserverConfig = {childList: true, subtree: false}; 120 | TLD.tweetsAndRepliesContainerObserver.observe(container, tweetsAndRepliesContainerObserverConfig); 121 | TLD.tweetsAndRepliesContainerMOActive = true; 122 | //console.log(TLD.tweetsAndRepliesContainerObserver); 123 | }; 124 | 125 | 126 | /** 127 | * A function that listens for added messages on pages built with React then 128 | * cleans the links inside them 129 | * @method listenForReactMessages 130 | * @memberof TLD 131 | * @param {HTMLDivElement} container - The element containing the messages. It 132 | * should be the type of element returned by getElementById() or 133 | * querySelector() or similar methods 134 | */ 135 | TLD.listenForReactMessages = function(container) { 136 | //console.log(container); // for debugging 137 | 138 | TLD.revealReactLinks(container); 139 | 140 | /** 141 | * Call TLD.revealReactLinks() every time new messages are added 142 | */ 143 | TLD.messagesContainerObserver = new MutationObserver(() => { 144 | //console.log("TLD.messagesContainerObserver"); // for debugging 145 | TLD.revealReactLinks(container); 146 | }); 147 | const messagesContainerObserverConfig = {childList: true, subtree: true}; 148 | TLD.messagesContainerObserver.observe(container, messagesContainerObserverConfig); 149 | TLD.messagesContainerDMBoxMOActive = true; 150 | //console.log(TLD.messagesContainerObserver); 151 | }; 152 | 153 | 154 | /** 155 | * A function that detects what type of page was opened 156 | * @method detectPage 157 | * @memberof TLD 158 | * @returns {string} - Returns the type of page detected, or "unknown" 159 | * if TLD should not try to clean the page 160 | */ 161 | TLD.detectPage = function() { 162 | //console.log(window.location); // for debugging 163 | let locationPathname = window.location.pathname; 164 | //console.log(locationPathname); // for debugging 165 | 166 | if (/^\/[^/]+\/status\/.*/.test(locationPathname)) { 167 | if (/\/photo\/[0-9]+$/.test(locationPathname)) { 168 | //console.log("A photo was opened"); // for debugging 169 | return "photo"; 170 | } 171 | //console.log("A tweet page was opened."); // for debugging 172 | return "tweet"; 173 | } else if (/^\/home\/*$/.test(locationPathname)) { 174 | //console.log("The home page was opened."); // for debugging 175 | return "home"; 176 | } else if (/^\/explore(\/.*)?$/.test(locationPathname)) { 177 | //console.log("The \"Explore\" page was opened."); // for debugging 178 | return "explore"; 179 | } else if (/^\/messages\/*$/.test(locationPathname)) { 180 | //console.log("The \"Messages\" page was opened."); // for debugging 181 | return "messages"; 182 | } else if (/^\/messages\/.+$/.test(locationPathname)) { 183 | //console.log("A conversation from the \"Messages\" page was opened."); // for debugging 184 | return "conversation"; 185 | } else if (/^\/search\/*$/.test(locationPathname)) { 186 | //console.log("A \"Search\" page was opened."); // for debugging 187 | return "search"; 188 | } else if (/^\/notifications(\/mentions)?$/.test(locationPathname)) { 189 | //console.log("A \"Notifications\" page was opened."); // for debugging 190 | return "notifications"; 191 | } else if (/\/i\/timeline\/*$/.test(locationPathname)) { 192 | //console.log("A tweet from the \"Notifications\" page was opened."); // for debugging 193 | return "timeline"; 194 | } else if (/\/i\/lists\/[0-9]+\/*$/.test(locationPathname)) { 195 | //console.log("A list from the \"Lists\" page was opened."); // for debugging 196 | return "list"; 197 | } else if (/\/i\/bookmarks\/*$/.test(locationPathname)) { 198 | //console.log("The \"Bookmarks\" page was opened."); // for debugging 199 | return "bookmarks"; 200 | } else if (/\/i\/events\/[0-9]+\/*$/.test(locationPathname)) { 201 | //console.log("An \"event\" page was opened."); // for debugging 202 | return "event"; 203 | } else if (/\/i\/topics\/[0-9]+\/*$/.test(locationPathname)) { 204 | //console.log("A \"Topics\" page was opened."); // for debugging 205 | return "topics"; 206 | } else if (/^\/.*\/communities(\/explore)?$/.test(locationPathname)) { 207 | //console.log("A \"Communities\" page was opened."); // for debugging 208 | return "communities"; 209 | } else if (/\/[^/]+\/*$/.test(locationPathname)) { 210 | let mainElement = document.body.querySelector("#react-root main"); 211 | if (mainElement.querySelector("div[data-testid=\"UserDescription\"]") 212 | || mainElement.querySelector("div[data-testid=\"UserProfileHeader_Items\"]")) { 213 | //console.log("User description or profile header detected."); // for debugging 214 | //console.log("A profile page was opened."); // for debugging 215 | return "profile"; 216 | } 217 | } else { 218 | //console.log("A unknown page was opened."); // for debugging 219 | return "unknown"; 220 | } 221 | }; 222 | 223 | 224 | /** 225 | * A function that finds the Timeline on React pages 226 | * @method findReactTimeline 227 | * @memberof TLD 228 | * @returns {HTMLDivElement} - Returns the Timeline, or "null" if was not found 229 | */ 230 | TLD.findReactTimeline = function() { 231 | if (document.body.querySelector("#react-root main div[data-testid=\"primaryColumn\"] section > div[aria-label]")) { 232 | let timeline = document.body.querySelector("#react-root main div[data-testid=\"primaryColumn\"] section > div[aria-label]"); 233 | //console.log(timeline); // for debugging 234 | return timeline; 235 | } else { 236 | //console.log("The Timeline was not found"); // for debugging 237 | return null; 238 | } 239 | }; 240 | 241 | 242 | /** 243 | * A function that sends a message to the background script to increase the 244 | * badge number shown on top of the icon 245 | * @method increaseBadgeNumber 246 | * @memberof TLD 247 | */ 248 | TLD.increaseBadgeNumber = function() { 249 | TLD.cleanedLinks += 1; 250 | TLD.notifyBackgroundScript({setBadge: (TLD.cleanedLinks).toString()}); // send a message to the background script to update the badge number 251 | }; 252 | 253 | 254 | /** 255 | * A function that modifies the message box 256 | * @method modifyDMBox 257 | * @memberof TLD 258 | */ 259 | TLD.modifyDMBox = function() { 260 | let DMBox = document.querySelector("div[data-testid=\"DMDrawer\"]"); 261 | //console.log(DMBox); // for debugging 262 | let asideElement = DMBox.querySelector("aside"); 263 | if (asideElement === null) return; 264 | //console.log(asideElement); // for debugging 265 | let asideParent = asideElement.parentElement; 266 | //console.log(asideParent); // for debugging 267 | let messageContainerContainer = asideParent.firstElementChild; 268 | //console.log(messageContainerContainer); // for debugging 269 | let messageContainer = messageContainerContainer.querySelector( 270 | "div[style*='position'][style*='min-height']"); 271 | //console.log(messageContainer); // for debugging 272 | if (messageContainer !== null) TLD.revealReactLinks(messageContainer); 273 | }; 274 | 275 | 276 | /** 277 | * A function that modifies the React pages 278 | * @method modifyReactPages 279 | * @memberof TLD 280 | */ 281 | TLD.modifyReactPages = function() { 282 | let mainElement = document.body.querySelector("#react-root main"); 283 | //console.log(mainElement); // for debugging 284 | TLD.mainObserver = new MutationObserver(() => { 285 | //console.log("TLD.mainObserver"); // for debugging 286 | let tweetsContainer, tweetsAndRepliesContainers, DMBox; // declare some variables 287 | 288 | if (TLD.lastCleanedPage === window.location.href) { 289 | 290 | /** 291 | * On the "Communities" page, clicking on one of the topics loads more 292 | * tweets but the URL doesn't change so it's necessary to reset 293 | * TLD.lastCleanedPage for the new tweets to be cleaned 294 | */ 295 | if (TLD.detectPage() === "communities") { 296 | tweetsContainer = TLD.findReactTimeline()?.querySelector("div[style*='min-height']"); 297 | if (!tweetsContainer) { 298 | TLD.lastCleanedPage = null; // reset the property with the URL of the page which was last cleaned 299 | } 300 | } 301 | 302 | return; 303 | } // return if this page was already cleaned or a new topic from the "Communities" page was clicked 304 | 305 | /** 306 | * Clean the tweets or replies on the page which was opened initially 307 | */ 308 | switch (TLD.detectPage()) { // check what type of page was opened 309 | case "profile": // if a profile page was opened... 310 | TLD.cleanReactWebsiteLink(); 311 | // fall-through (no break statement) 312 | case "tweet": // if a page with a tweet was opened... 313 | case "home": // if the home page was opened... 314 | case "explore": // if the "Explore" page was opened... 315 | case "search": // if a "Search" page was opened... 316 | case "notifications": // if a "Notifications" page was opened... 317 | case "timeline": // if a tweet from the "Notifications" page was opened... 318 | case "list": // if a list from the "Lists" page was opened... 319 | case "bookmarks": // if the "Bookmarks" page was opened... 320 | case "event": // if an "event" page was opened... 321 | case "topics": // if a "Topics" page was opened... 322 | case "communities": // if a "Communities" page was opened... 323 | tweetsContainer = TLD.findReactTimeline()?.querySelector("div[style*='min-height']"); 324 | if (tweetsContainer?.childElementCount > 1) { 325 | //console.log("The container with tweets or replies was found."); // for debugging 326 | TLD.listenForReactTweetsAndReplies(tweetsContainer); // listen for added tweets or replies and clean them 327 | TLD.lastCleanedPage = window.location.href; // store the URL of this page which was just cleaned 328 | } else { // if the Timeline can't be found or was deleted... 329 | //console.log("The Timeline was not found."); // for debugging 330 | if (TLD.tweetsAndRepliesContainerMOActive === true) { 331 | TLD.tweetsAndRepliesContainerObserver.disconnect(); 332 | TLD.tweetsAndRepliesContainerMOActive = false; 333 | delete TLD.tweetsAndRepliesContainerObserver; 334 | } else if (TLD.messagesContainerMOActive === true) { 335 | TLD.messagesContainerObserver.disconnect(); 336 | TLD.messagesContainerMOActive = false; 337 | delete TLD.messagesContainerObserver; 338 | } 339 | TLD.lastCleanedPage = null; // reset the property with the URL of the page which was last cleaned 340 | } 341 | //console.log(`TLD.lastCleanedPage: ${TLD.lastCleanedPage}`); // for debugging 342 | break; 343 | case "messages": // if the "Messages" page was opened... 344 | TLD.lastCleanedPage = null; // reset the property with the URL of the page which was last cleaned 345 | //console.log(`TLD.lastCleanedPage: ${TLD.lastCleanedPage}`); // for debugging 346 | break; 347 | case "conversation": // if a message thread was opened... 348 | var sections = document.querySelectorAll("#react-root main section"); 349 | TLD.listenForReactMessages(sections[sections.length - 1]); // find the element with messages and clean them 350 | TLD.lastCleanedPage = window.location.href; // store the URL of this page which was just cleaned 351 | //console.log(`TLD.lastCleanedPage: ${TLD.lastCleanedPage}`); // for debugging 352 | break; 353 | case "photo": // if a photo was opened... 354 | tweetsAndRepliesContainers = document.querySelectorAll("div.css-1dbjc4n [aria-label=\"Timeline: Conversation\"] > div[style*='min-height']"); 355 | if (tweetsAndRepliesContainers.length > 0) { 356 | //console.log("The Timeline and/or the conversation container were found"); // for debugging 357 | tweetsAndRepliesContainers.forEach(container => { // clean both the containers 358 | TLD.listenForReactTweetsAndReplies(container); // listen for added tweets or replies and clean them 359 | TLD.lastCleanedPage = window.location.href; // store the URL of this page which was just cleaned 360 | }); 361 | } 362 | break; 363 | case "unknown": // if a unknown page was opened... 364 | TLD.lastCleanedPage = null; // reset the property with the URL of the page which was last cleaned 365 | //console.log(`TLD.lastCleanedPage: ${TLD.lastCleanedPage}`); // for debugging 366 | } 367 | 368 | /** 369 | * Monitor the DM box on all the pages 370 | */ 371 | //console.log(TLD.DMBoxMOActive); // for debugging 372 | DMBox = document.querySelector("div[data-testid=\"DMDrawer\"]"); 373 | if (DMBox !== null && TLD.DMBoxMOActive === false) { 374 | //console.log(DMBox); // for debugging 375 | TLD.modifyDMBox(); 376 | TLD.DMBoxObserver = new MutationObserver(TLD.modifyDMBox); 377 | const DMBoxObserverConfig = {childList: true, subtree: true}; 378 | TLD.DMBoxObserver.observe(DMBox, DMBoxObserverConfig); 379 | TLD.DMBoxMOActive = true; 380 | } else if (DMBox === null && TLD.DMBoxMOActive === true) { 381 | TLD.DMBoxObserver.disconnect(); 382 | TLD.DMBoxMOActive = false; 383 | delete TLD.DMBoxObserver; 384 | } 385 | }); 386 | const mainObserverConfig = {childList: true, subtree: true}; 387 | TLD.mainObserver.observe(mainElement, mainObserverConfig); 388 | 389 | /** 390 | * Monitor all the pages for opened photos 391 | */ 392 | let layersElement = document.querySelector("#react-root div#layers"); 393 | if (layersElement) { 394 | TLD.layersObserver = new MutationObserver(() => { 395 | if (TLD.lastCleanedPage === window.location.href) { 396 | return; 397 | } // return if this page was already cleaned 398 | 399 | let tweetsAndRepliesContainer = layersElement.querySelector("div.css-1dbjc4n [aria-label=\"Timeline: Conversation\"] > div[style*='min-height']"); 400 | if (!tweetsAndRepliesContainer) { 401 | return; 402 | } 403 | //console.log("The conversation container was found"); // for debugging 404 | TLD.listenForReactTweetsAndReplies(tweetsAndRepliesContainer); // listen for added tweets or replies and clean them 405 | TLD.lastCleanedPage = window.location.href; // store the URL of this page which was just cleaned 406 | }); 407 | } 408 | const layersObserverConfig = {childList: true, subtree: true}; 409 | TLD.layersObserver.observe(layersElement, layersObserverConfig); 410 | }; 411 | 412 | 413 | /** 414 | * A function that selects the text links 415 | * @method selectLinks 416 | * @memberof TLD 417 | * @param {HTMLDivElement} container - The element containing the text links. It 418 | * should be the type of element returned by getElementById() or 419 | * querySelector() or similar methods 420 | * @returns {NodeList} - Returns the list of text links found in the container 421 | */ 422 | TLD.selectLinks = function(container) { 423 | let links = container.querySelectorAll("a.css-1qaijid.r-bcqeeo.r-qvutc0.r-poiln3.r-1loqt21"); // the old way of detecting the text links 424 | if (links.length === 0) { 425 | links = container.querySelectorAll("a.css-1jxf684.r-bcqeeo.r-qvutc0.r-poiln3.r-1loqt21"); 426 | } // the new way of detecting the text links 427 | return links; 428 | }; 429 | 430 | 431 | /** 432 | * A function that uncloaks the text links 433 | * @method uncloakLinks 434 | * @memberof TLD 435 | * @param {NodeList} links - The list of text links. It should be a NodeList 436 | * as the one returned by TLD.selectLinks() 437 | */ 438 | TLD.uncloakLinks = function(links) { 439 | if (links.length === 0) { 440 | return; 441 | } 442 | for (let link of links) { 443 | //for (let [index, link] of links.entries()) { // for debugging 444 | //console.log(link); // for debugging 445 | if (link.hostname !== "t.co" || 446 | (link.hostname === "t.co" && link.pathname === "/")) { 447 | continue; 448 | } // if the link is not in the form "t.co/abc", skip it 449 | /*console.log(` 450 | ${index + 1}.href: ${link.href} 451 | ${index + 1}.title: ${link.title} 452 | ${index + 1}.innerText: ${link.innerText}`);*/ // for debugging 453 | link.setAttribute("data-shortened-url", link.href); 454 | if (link.hasAttribute("title")) { // use the "title" attribute if the link has one 455 | link.href = link.title; 456 | } else { 457 | let linkHref = link.innerText; // link.text and link.textContent works, too 458 | if (link.lastElementChild.innerText === "…") { 459 | //let linkHref = linkHref.substring(0, linkHref.length - 1); 460 | linkHref = linkHref.slice(0, -1); 461 | } // if there is a trailing ellipsis character, remove it 462 | if (!/^[a-zA-Z0-9.+-]+:\/\//.test(linkHref)) { 463 | linkHref = `http://${linkHref}`; 464 | } // add a protocol if there isn't one in the link text 465 | link.href = linkHref; 466 | } 467 | //console.log(link); // for debugging 468 | TLD.increaseBadgeNumber(); // increase the number shown on top of the icon 469 | } 470 | }; 471 | 472 | 473 | 474 | /** 475 | * Properties of the namespace TLD 476 | * @property {number} cleanedLinks - The number of links cleaned 477 | * @property {string} lastCleanedPage - The URL of the last cleaned page 478 | * @property {boolean} tweetsAndRepliesContainerMOActive - tweets_And_Replies_Container_MutationObserver_Active - shows 479 | * whether there is a MutationObserver attached to the tweets and replies container 480 | * @property {boolean} messagesContainerMOActive - messages_Container_MutationObserver_Active - shows 481 | * whether there is a MutationObserver attached to the message container 482 | * @property {boolean} DMBoxMOActive - DM_Box_MutationObserver_Active - shows 483 | * whether there is a MutationObserver attached to the message box 484 | * @memberof TLD 485 | */ 486 | TLD.cleanedLinks = 0; 487 | TLD.lastCleanedPage; 488 | TLD.tweetsAndRepliesContainerMOActive = false; 489 | TLD.messagesContainerMOActive = false; 490 | TLD.DMBoxMOActive = false; 491 | //console.log(TLD); // for debugging 492 | 493 | browser.runtime.onMessage.addListener(() => { 494 | //console.log("A message was received from the background script."); // for debugging 495 | TLD.increaseBadgeNumber(); // increase the number shown on top of the icon 496 | }); // listen for messages from the background script and increase the badge number 497 | 498 | if ( document.body.querySelector("#react-root div#layers") && 499 | document.body.querySelector("#react-root main")) { 500 | //console.log("The main element was found."); // for debugging 501 | TLD.modifyReactPages(); 502 | } else { 503 | TLD.bodyObserver = new MutationObserver(() => { 504 | //console.log("TLD.bodyObserver"); // for debugging 505 | if (!document.body.querySelector("#react-root div#layers") || 506 | !document.body.querySelector("#react-root main")) { 507 | return; 508 | } // return if the
element was not created yet 509 | //console.log("The main element was found."); // for debugging 510 | TLD.bodyObserver.disconnect(); 511 | delete TLD.bodyObserver; 512 | TLD.modifyReactPages(); 513 | }); 514 | const bodyObserverConfig = {childList: true, subtree: true}; 515 | TLD.bodyObserver.observe(document.body, bodyObserverConfig); 516 | } 517 | -------------------------------------------------------------------------------- /scripts/background_script.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | /** 5 | * The namespace that will contain all the methods and properties 6 | * @namespace TLD_background 7 | */ 8 | var TLD_background = TLD_background || {}; 9 | 10 | /** 11 | * Properties of the namespace TLD_background 12 | * @property {object} config - The add-on settings 13 | * @property {object} config.defaultAddonState - The default state of the add-on 14 | * @property {boolean} config.defaultAddonState.enabled - This property determines 15 | * whether the add-on will be enabled or not 16 | * @property {object} config.badgeBackgroundColor - This property determines the 17 | * background color of the badge text 18 | * @memberof TLD_background 19 | */ 20 | TLD_background.config = TLD_background.config || {}; 21 | TLD_background.config.defaultAddonState = TLD_background.config.defaultAddonState || {}; 22 | TLD_background.config.defaultAddonState.enabled = true; // default add-on state 23 | TLD_background.config.badgeBackgroundColor = TLD_background.config.badgeBackgroundColor || {color: "green"}; 24 | TLD_background.config.pathRegexPatterns = [ 25 | "/inbox_initial_state.json$", // if the JSON contains the initial batch of Direct Messages 26 | "/conversation/[0-9]+.json$", // if the JSON contains replies to tweets 27 | "/conversation/[0-9]+-[0-9]+.json$", // if the JSON contains additional Direct Messages 28 | "/user_updates.json$", // if the JSON contains additional Direct Messages 29 | "/profile/[0-9]+.json$", // if the JSON contains initial or additional tweets requested from a profile page 30 | "/graphql/.+[^/]/Conversation$", // if a GraphQL API call is made to request replies to tweets 31 | "/adaptive.json$", // if the JSON contains search results 32 | "/notifications/all.json$", // if an API call is made to request notifications for the "Notifications" page 33 | "/notifications/mentions.json$", // if an API call is made to request mentions for the "Notifications" page 34 | "/notifications/view/.+[^/].json$", // if a tweet from the "Notifications" page was opened 35 | "/timeline/list.json$", // if an API call is made to request tweets for the "Lists" page 36 | "/timeline/bookmark.json$", // if an API call is made to request tweets for the "Bookmarks" page 37 | "/graphql/[a-zA-Z0-9_.+-]+/UserTweets$", // if a GraphQL API call is made to request tweets from a profile page 38 | "/graphql/[a-zA-Z0-9_.+-]+/TweetDetail$", // if a GraphQL API call is made to request replies to tweets 39 | "/graphql/[a-zA-Z0-9_.+-]+/Bookmarks$", // if a GraphQL API call is made to request tweets for the "Bookmarks" page 40 | "/graphql/[a-zA-Z0-9-_]+/ListLatestTweetsTimeline$", // if a GraphQL API call is made to request tweets for the "Lists" page 41 | "/graphql/[a-zA-Z0-9-_]+/ExplorePage$", // if an API call is made to request tweets for the "Explore" page 42 | "/graphql/[a-zA-Z0-9]+/TopicLandingPage$", // if a GraphQL API call is made to request tweets for a "Topic" page 43 | "/graphql/[a-zA-Z0-9-_]+/HomeLatestTimeline$", // if a GraphQL API call is made to request the latest tweets for the "Home" page 44 | "/graphql/[a-zA-Z0-9-_]+/HomeTimeline$", // if a GraphQL API call is made to request the top tweets for the "Home" page 45 | "/graphql/[a-zA-Z0-9-_]+/CommunitiesExploreTimeline$" // if a GraphQL API call is made to request tweets for the "Communities" page 46 | ]; 47 | TLD_background.pathRegex = new RegExp(TLD_background.config.pathRegexPatterns.join("|"), "i"); 48 | //console.log(TLD_background); // for debugging 49 | 50 | 51 | /** 52 | * A function that changes the add-on's title 53 | * @method updateAddonTitle 54 | * @memberof TLD_background 55 | * @param {boolean} state - The title of the add-on icon will be changed based 56 | * on the value of this parameter 57 | */ 58 | TLD_background.updateAddonTitle = function(state) { 59 | //console.log(state); // for debugging 60 | state ? 61 | browser.browserAction.setTitle({ title: browser.i18n.getMessage("enabledStateTitle")}) 62 | : 63 | browser.browserAction.setTitle({ title: browser.i18n.getMessage("disabledStateTitle")}); 64 | }; 65 | 66 | 67 | /** 68 | * A function that changes the add-on's icon 69 | * @method updateAddonIcon 70 | * @memberof TLD_background 71 | * @param {boolean} state - The icon of the add-on will be changed based 72 | * on the value of this parameter 73 | */ 74 | TLD_background.updateAddonIcon = function(state) { 75 | //console.log(state); // for debugging 76 | state ? 77 | browser.browserAction.setIcon({path: { 78 | "32": "icons/TLD_icon_enabled-32.png", 79 | "48": "icons/TLD_icon_enabled-48.png", 80 | "64": "icons/TLD_icon_enabled-64.png", 81 | "96": "icons/TLD_icon_enabled-96.png", 82 | "128": "icons/TLD_icon_enabled-128.png" 83 | }}) 84 | : 85 | browser.browserAction.setIcon({path: { 86 | "32": "icons/TLD_icon_disabled-32.png", 87 | "48": "icons/TLD_icon_disabled-48.png", 88 | "64": "icons/TLD_icon_disabled-64.png", 89 | "96": "icons/TLD_icon_disabled-96.png", 90 | "128": "icons/TLD_icon_disabled-128.png" 91 | }}); 92 | }; 93 | 94 | 95 | /** 96 | * A function that enables and disables the add-on 97 | * @async 98 | * @method toggleStatus 99 | * @memberof TLD_background 100 | */ 101 | TLD_background.toggleStatus = async function() { 102 | let storedSettings = await browser.storage.local.get(); 103 | browser.storage.local.set({ enabled : !storedSettings.enabled }); 104 | 105 | TLD_background.updateAddonTitle(!storedSettings.enabled); 106 | TLD_background.updateAddonIcon(!storedSettings.enabled); 107 | }; 108 | 109 | 110 | /** 111 | * A function that communicates with the content script 112 | * @method handleMessage 113 | * @memberof TLD_background 114 | * @param {object} request - The message received from the content script 115 | * @param {object} sender - An object passed to the function by the onMessage 116 | * listener providing details about the sender of the message 117 | * @param {function} sendResponse - A function passed to the function by the 118 | * onMessage listener providing a way to send a response to the sender 119 | */ 120 | //TLD_background.handleMessage = function(request, sender, sendResponse) { // for debugging 121 | TLD_background.handleMessage = function(request, sender) { 122 | //console.log(request); // for debugging 123 | //console.log(sender); // for debugging 124 | //console.log(sendResponse); // for debugging 125 | //console.log(`Iframe location href: ${sender.url}`); // for debugging 126 | 127 | //console.log(sender.tab.id); // for debugging 128 | /*let gettingBadgeText = browser.browserAction.getBadgeText({tabId: sender.tab.id}); // get the badge text 129 | gettingBadgeText.then(badgeText => { console.log(`Old badge text: ${badgeText}`); }); // log the badge text*/ // for debugging 130 | browser.browserAction.setBadgeText({text: request.setBadge, tabId: sender.tab.id}); // update the badge text 131 | //console.log(`The badge text has been updated to ${request.setBadge}.`); // for debugging 132 | //sendResponse({response: `The badge text has been updated to ${request.setBadge}.`}); // only useful if handleResponse() is called from notifyBackgroundScript() 133 | }; 134 | 135 | 136 | /** 137 | * A function that handles any messaging errors 138 | * @method onMessageError 139 | * @memberof TLD_background 140 | * @param {object} error - An object as defined by the browser 141 | */ 142 | TLD_background.onMessageError = function(error) { 143 | //console.error(error); // for debugging 144 | console.error(`Error: ${error.message}`); 145 | }; 146 | 147 | 148 | /** 149 | * A function that intercepts the network requests 150 | * @async 151 | * @method interceptNetworkRequests 152 | * @memberof TLD_background 153 | * @param {object} requestDetails - An object passed over to the listener 154 | * function with detailed information about the request 155 | * @returns {object} - Returns a webRequest.StreamFilter object - or undefined 156 | */ 157 | TLD_background.interceptNetworkRequests = async function(requestDetails) { 158 | //console.log(`Loading: " ${requestDetails.url}`); // for debugging 159 | let storedSettings = await browser.storage.local.get(); 160 | var filter; 161 | if (storedSettings.enabled !== true) { 162 | return; 163 | } // don't clean the links if the add-on is not enabled 164 | let tabs = await browser.tabs.query({discarded: false, url: ["*://*.twitter.com/*", "*://*.x.com/*"]}); 165 | //console.log(tabs); // for debugging 166 | tabs.forEach(tab => { 167 | //console.log(tab); // for debugging 168 | if (tab.id !== requestDetails.tabId) { 169 | return; 170 | } 171 | //console.log(requestDetails.url); // for debugging 172 | let cleanUrl = requestDetails.url.replace(/\/?\?.*/, ""); // remove the last "/" and the query strings from the request URL 173 | //console.log(cleanUrl); // for debugging 174 | if (TLD_background.pathRegex.test(cleanUrl)) { 175 | //console.log(requestDetails); // for debugging 176 | filter = browser.webRequest.filterResponseData(requestDetails.requestId); 177 | } 178 | }); 179 | return filter; 180 | }; 181 | 182 | 183 | /** 184 | * A function that modifies the network requests 185 | * @async 186 | * @method modifyNetworkRequests 187 | * @memberof TLD_background 188 | * @param {object} requestDetails - An object passed over to the listener 189 | * function with detailed information about the request 190 | */ 191 | TLD_background.modifyNetworkRequests = async function(requestDetails) { 192 | let filter = await TLD_background.interceptNetworkRequests(requestDetails); 193 | 194 | if (filter === undefined) { 195 | return; 196 | } 197 | 198 | let decoder = new TextDecoder("utf-8"); 199 | let encoder = new TextEncoder(); 200 | let data = []; 201 | filter.ondata = event => { 202 | data.push(event.data); 203 | }; 204 | filter.onstop = () => { 205 | //console.log("The response will be modified"); // for debugging 206 | let stringResponse = ""; 207 | if (data.length === 1) { 208 | stringResponse = decoder.decode(data[0]); 209 | } else { 210 | for (let i = 0; i < data.length; i++){ 211 | let stream = (i === data.length - 1) ? false : true; 212 | stringResponse += decoder.decode(data[i], {stream}); 213 | } 214 | } 215 | //console.log(stringResponse); // for debugging 216 | 217 | if (!TLD_background.hasJsonStructure(stringResponse)) { 218 | return; 219 | } 220 | 221 | //console.log(stringResponse); // for debugging 222 | let jsonResponse = JSON.parse(stringResponse); 223 | //console.log(requestDetails.url); // for debugging 224 | //console.log(jsonResponse); // for debugging 225 | let msg_entries = jsonResponse?.inbox_initial_state?.entries || 226 | jsonResponse?.conversation_timeline?.entries || 227 | jsonResponse?.user_events?.entries; 228 | if (msg_entries) { // if the JSON contains messages... 229 | TLD_background.cleanDirectMessages(msg_entries, requestDetails); 230 | } else if (jsonResponse?.globalObjects?.tweets) { // if the JSON contains the top tweets for the "Home" page... 231 | TLD_background.cleanRegularTweets(jsonResponse, requestDetails); 232 | } else if (jsonResponse?.data?.threaded_conversation_with_injections_v2?.instructions[0]) { // if the JSON contains replies to tweets from a GraphQL API call... 233 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 234 | } else if (jsonResponse?.data?.user?.result?.timeline_v2?.timeline?.instructions) { // if the JSON contains tweets for a profile page 235 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 236 | } else if (jsonResponse?.data?.bookmark_timeline_v2?.timeline?.instructions[0]) { // if the JSON contains tweets for the "Bookmarks" page 237 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 238 | } else if (jsonResponse?.data?.list?.tweets_timeline?.timeline?.instructions[0]) { // if the JSON contains tweets for the "Lists" page 239 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 240 | } else if (jsonResponse?.data?.topic_by_rest_id?.topic_page?.body?.timeline) { // if the JSON contains tweets for a "Topic" page 241 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 242 | } else if (jsonResponse?.data?.home?.home_timeline_urt) { // if the JSON contains the latest tweets for the "Home" page... 243 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 244 | } else if (jsonResponse?.data?.viewer?.explore_communities_timeline?.timeline?.instructions) { // if the JSON contains tweets for the "Communities" page... 245 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 246 | } else if (jsonResponse?.data?.explore_page?.body?.initialTimeline?.timeline?.timeline?.instructions) { // if the JSON contains the latest tweets for the "Explore" page... 247 | TLD_background.cleanVariousTweets(jsonResponse, requestDetails); 248 | } 249 | //console.log(jsonResponse); // for debugging 250 | stringResponse = JSON.stringify(jsonResponse); // the slashes from URLs and the emojis are no longer \ and Unicode-escaped 251 | //console.log(stringResponse); // for debugging 252 | filter.write(encoder.encode(stringResponse)); 253 | filter.close(); 254 | //console.log("The response was modified successfully"); // for debugging 255 | }; 256 | }; 257 | 258 | 259 | /** 260 | * A function that checks if a string is a well-formed JSON structure 261 | * @method hasJsonStructure 262 | * @memberof TLD_background 263 | * @param {string} str - The string that should be checked if is valid JSON 264 | * @returns {boolean} - Returns true or false if the provided string 265 | * is valid JSON or not 266 | */ 267 | TLD_background.hasJsonStructure = function(str) { 268 | if (typeof str !== "string") return false; 269 | try { 270 | const result = JSON.parse(str); 271 | const type = Object.prototype.toString.call(result); 272 | return type === "[object Object]" 273 | || type === "[object Array]"; 274 | } catch (err) { 275 | return false; 276 | } 277 | }; 278 | 279 | 280 | /** 281 | * A function that messages the content script when a link is cleaned 282 | * @method messageContentScript 283 | * @memberof TLD_background 284 | * @param {number} tabID - The ID of the tab that should have its badge updated 285 | */ 286 | TLD_background.messageContentScript = function(tabID) { 287 | //console.log(tabID); // for debugging 288 | browser.tabs.sendMessage( 289 | tabID, 290 | {} 291 | ).catch(TLD_background.onMessageError); 292 | }; 293 | 294 | 295 | /** 296 | * A function that determines the Twitter Card's URL from the provided tweet 297 | * @method determineCardURL 298 | * @memberof TLD_background 299 | * @param {object} entry - An object containing a tweet 300 | * @param {object} [tweet_entries] - An optional object containing tweets 301 | * @returns {object} - Returns an object that is a property of the "entry" 302 | * argument which contains the original URL that should be used when 303 | * uncloaking the Twitter Card 304 | */ 305 | TLD_background.determineCardURL = function(entry, tweet_entries) { 306 | let urls; 307 | if (entry?.message?.message_data?.entities?.urls) { 308 | urls = entry.message.message_data.entities.urls; 309 | } else if (entry?.entities?.urls) { 310 | urls = entry.entities.urls; 311 | } else if (entry?.entities?.user_mentions) { 312 | /** 313 | * This code block is ran in rare cases of retweets on the "Home" page 314 | * or tweets that are actually embedded or promoted or contain a video 315 | */ 316 | if (entry?.retweeted_status_id_str) { // if the tweet is a retweet 317 | let retweetedTweetId = entry?.retweeted_status_id_str; 318 | urls = tweet_entries[retweetedTweetId]?.entities?.urls; 319 | if (!urls) { // if the retweeted tweet doesn't contain any URLs... 320 | return null; 321 | } 322 | } else { // if the tweet is embedded or promoted or contains a video card 323 | return null; 324 | } 325 | } else if (entry?.item?.itemContent?.tweet?.legacy?.entities?.urls) { 326 | urls = entry.item.itemContent.tweet.legacy.entities.urls; 327 | if (urls.length === 0 && 328 | entry.item.itemContent.tweet.legacy?.retweeted_status?.legacy?.entities?.urls) { 329 | urls = entry.item.itemContent.tweet.legacy.retweeted_status.legacy.entities.urls; 330 | } 331 | } else if (entry?.content?.itemContent?.tweet?.legacy?.entities?.urls) { 332 | urls = entry.content.itemContent.tweet.legacy.entities.urls; 333 | } else { // tweets, retweets, quoted tweets and threads from profile pages 334 | /** 335 | * Select the URLs from tweets from profile pages 336 | */ 337 | if (entry?.content?.itemContent?.tweet_results?.result?.legacy?.entities?.urls) { 338 | urls = entry.content.itemContent.tweet_results.result.legacy.entities.urls; 339 | } 340 | 341 | /** 342 | * Select the URLs from retweets from profile pages 343 | */ 344 | if ((urls === undefined || urls.length === 0) && 345 | entry?.content?.itemContent?.tweet_results?.result?.legacy?.retweeted_status_result?.result?.legacy?.entities?.urls) { 346 | urls = entry.content.itemContent.tweet_results.result.legacy.retweeted_status_result.result.legacy.entities.urls; 347 | } 348 | 349 | /** 350 | * Select the URLs from quoted tweets from profile pages 351 | */ 352 | if ((urls === undefined || urls.length === 0) && 353 | entry?.content?.itemContent?.tweet_results?.result?.quoted_status_result?.result?.legacy?.entities?.urls) { 354 | urls = entry.content.itemContent.tweet_results.result.quoted_status_result.result.legacy.entities.urls; 355 | } 356 | 357 | /** 358 | * Select the URLs from tweets inside threads from profile pages 359 | */ 360 | if (entry?.item?.itemContent?.tweet_results?.result?.legacy?.entities?.urls) { 361 | urls = entry.item.itemContent.tweet_results.result.legacy.entities.urls; 362 | } 363 | 364 | /** 365 | * Select the URLs from tweets from the "Communities" page 366 | */ 367 | if (entry?.content?.itemContent?.tweet_results?.result?.tweet?.legacy?.entities?.urls) { 368 | urls = entry.content.itemContent.tweet_results.result.tweet.legacy.entities.urls; 369 | } 370 | } 371 | 372 | //console.log(urls); // for debugging 373 | if (urls === undefined || urls.length === 0) { // if no URLs were found... 374 | //console.error("No URLs found"); // for debugging 375 | return null; 376 | } else { 377 | let lastURL = urls[urls.length - 1]; 378 | //console.log(lastURL); // for debugging 379 | return lastURL; 380 | } 381 | }; 382 | 383 | 384 | /** 385 | * A function that restores a Twitter Card's original URL 386 | * @method uncloakTwitterCard 387 | * @memberof TLD_background 388 | * @param {object} entry - An object containing a tweet 389 | * @param {object} card - An object containing a Twitter Card 390 | * @param {number} tabId - The ID of the tab whose badge text must be updated 391 | * @param {object} [tweet_entries] - An optional object containing tweets 392 | */ 393 | TLD_background.uncloakTwitterCard = function(entry, card, tabId, tweet_entries) { 394 | 395 | let binding_values = card.binding_values || card?.legacy?.binding_values; 396 | 397 | /** 398 | * Determine if the tweet contains a poll, and if it does, 399 | * don't uncloak the Card, wich is in fact the poll itself. 400 | */ 401 | if (Object.prototype.toString.call( 402 | binding_values) === "[object Array]") { 403 | for (let binding of binding_values) { 404 | if (binding.key === "choice1_count") { 405 | //console.log("This tweet contains a poll"); // for debugging 406 | return; 407 | } 408 | } 409 | } else if (Object.prototype.toString.call( 410 | binding_values) === "[object Object]") { 411 | if (binding_values?.choice1_count) { 412 | //console.log("This tweet contains a poll"); // for debugging 413 | return; 414 | } 415 | } 416 | 417 | /** 418 | * Determine the Twitter Card's original URL 419 | */ 420 | let lastURL = TLD_background.determineCardURL(entry, tweet_entries); 421 | if (!lastURL) { 422 | return; 423 | } 424 | 425 | /** 426 | * Restore the original URL of the Twitter Card 427 | */ 428 | if (Object.prototype.toString.call( 429 | binding_values) === "[object Array]") { 430 | for (let binding of binding_values) { 431 | if (binding.key === "card_url") { 432 | binding.value.string_value = lastURL.expanded_url; 433 | } 434 | } 435 | } else if (Object.prototype.toString.call( 436 | binding_values) === "[object Object]") { 437 | binding_values.card_url.string_value = lastURL.expanded_url; 438 | } 439 | 440 | /** 441 | * Restore the other original URL of the Twitter Card 442 | * (does not seem to be used/necessary any more) 443 | */ 444 | if (card.url) { 445 | card.url = lastURL.expanded_url; 446 | } else if (card?.legacy?.url) { 447 | card.legacy.url = lastURL.expanded_url; 448 | } 449 | 450 | /** 451 | * Update the badge text 452 | */ 453 | TLD_background.messageContentScript(tabId); 454 | }; 455 | 456 | 457 | /** 458 | * A function that uncloaks the Twitter Cards from Direct Messages 459 | * @method cleanDirectMessages 460 | * @memberof TLD_background 461 | * @param {array} msg_entries - An array containing Direct Messages 462 | * @param {object} requestDetails - An object passed over by the event listener 463 | */ 464 | TLD_background.cleanDirectMessages = function(msg_entries, requestDetails) { 465 | for (let entry of msg_entries) { 466 | //console.log(entry.message.message_data.text); // for debugging 467 | if (entry?.message?.message_data?.attachment?.card) { 468 | TLD_background.uncloakTwitterCard(entry, entry.message.message_data.attachment.card, requestDetails.tabId); 469 | } 470 | } 471 | }; 472 | 473 | 474 | /** 475 | * A function that uncloaks the Twitter Cards from tweets 476 | * @method cleanRegularTweets 477 | * @memberof TLD_background 478 | * @param {object} jsonResponse - A JSON containing tweets 479 | * @param {object} requestDetails - An object passed over by the event listener 480 | */ 481 | TLD_background.cleanRegularTweets = function(jsonResponse, requestDetails) { 482 | let tweet_entries = TLD_background.selectTweetEntries(jsonResponse); 483 | for (let entry of Object.keys(tweet_entries)) { 484 | //console.log(tweet_entries[entry].full_text); // for debugging 485 | 486 | if (tweet_entries[entry]?.card) { 487 | TLD_background.uncloakTwitterCard(tweet_entries[entry], tweet_entries[entry].card, requestDetails.tabId, tweet_entries); 488 | } 489 | } 490 | }; 491 | 492 | 493 | /** 494 | * A general function that uncloaks Twitter Cards from tweets from various pages 495 | * @method cleanVariousTweets 496 | * @memberof TLD_background 497 | * @param {object} jsonResponse - A parsed JSON containing tweets 498 | * @param {object} requestDetails - An object passed over by the event listener 499 | */ 500 | TLD_background.cleanVariousTweets = function(jsonResponse, requestDetails) { 501 | 502 | /** 503 | * Collect all the tweet entries into one array 504 | */ 505 | let tweet_entries = TLD_background.selectTweetEntries(jsonResponse); 506 | if (!tweet_entries) { 507 | return; 508 | } 509 | 510 | 511 | for (let entry of tweet_entries) { 512 | 513 | /** 514 | * Uncloak the Twitter Cards from regular tweets 515 | */ 516 | let tweetCard = entry?.content?.itemContent?.tweet_results?.result?.card || 517 | entry?.item?.itemContent?.tweet_results?.result?.card; // Cards from additional replies to tweets after clicking "Show replies" 518 | if (tweetCard) { 519 | TLD_background.uncloakTwitterCard(entry, tweetCard, requestDetails.tabId); 520 | } 521 | 522 | /** 523 | * Uncloak the Twitter Cards from retweets 524 | */ 525 | let retweetCard = entry?.content?.itemContent?.tweet_results?.result?.legacy?.retweeted_status_result?.result?.card; 526 | if (retweetCard) { 527 | TLD_background.uncloakTwitterCard(entry, retweetCard, requestDetails.tabId); 528 | } 529 | 530 | /** 531 | * Uncloak the Twitter Cards from quoted tweets 532 | */ 533 | let quotedCard = entry?.content?.itemContent?.tweet_results?.result?.quoted_status_result?.result?.card; 534 | if (quotedCard) { 535 | TLD_background.uncloakTwitterCard(entry, quotedCard, requestDetails.tabId); 536 | } 537 | 538 | /** 539 | * Uncloak the Twitter Cards from threads 540 | */ 541 | if (entry?.content?.items) { 542 | for (let threadEntry of entry.content.items) { 543 | /*if (threadEntry?.item?.itemContent?.tweet?.legacy?.full_text) { 544 | console.log(threadEntry.item.itemContent.tweet.legacy.full_text); // for debugging 545 | }*/ 546 | 547 | /** 548 | * Uncloak the Twitter Cards from regular tweets 549 | */ 550 | let threadCard = threadEntry?.item?.itemContent?.tweet_results?.result?.card; 551 | if (threadCard) { 552 | TLD_background.uncloakTwitterCard(threadEntry, threadCard, requestDetails.tabId); 553 | } 554 | } 555 | } 556 | 557 | /** 558 | * Uncloak the Twitter Cards from the "Communities" page 559 | */ 560 | let communitiesCard = entry?.content?.itemContent?.tweet_results?.result?.tweet?.card; 561 | if (communitiesCard) { 562 | TLD_background.uncloakTwitterCard(entry, communitiesCard, requestDetails.tabId); 563 | } 564 | } 565 | }; 566 | 567 | 568 | /** 569 | * A function that selects and returns an array or object containing tweets 570 | * @method selectTweetEntries 571 | * @memberof TLD_background 572 | * @param {object} jsonResponse - A parsed JSON containing tweets 573 | * @returns {(Array|Object)} - Returns an array or object with 574 | * the tweet entries as objects 575 | */ 576 | TLD_background.selectTweetEntries = function(jsonResponse) { 577 | 578 | let tweet_entries = []; 579 | 580 | /** 581 | * Add the tweets from various pages to the array with tweet entries 582 | */ 583 | tweet_entries = jsonResponse?.globalObjects?.tweets || // top tweets for the "Home" page 584 | jsonResponse?.data?.conversation_timeline?.instructions[0]?.moduleItems || // replies to tweets 585 | jsonResponse?.data?.threaded_conversation_with_injections_v2?.instructions[0]?.entries || // replies to tweets 586 | jsonResponse?.data?.bookmark_timeline_v2?.timeline?.instructions[0]?.entries || // tweets for the "Bookmarks" page 587 | jsonResponse?.data?.list?.tweets_timeline?.timeline?.instructions[0]?.entries || // tweets for the "Lists" page 588 | jsonResponse?.data?.home?.home_timeline_urt?.instructions[0]?.entries || // latest tweets for the "Home" page 589 | jsonResponse?.data?.threaded_conversation_with_injections_v2?.instructions[0]?.moduleItems; // additional replies to tweets after clicking "Show replies" 590 | 591 | /** 592 | * Add the tweets from profile pages to the array with tweet entries 593 | */ 594 | if (tweet_entries === undefined || tweet_entries.length === 0) { 595 | if (jsonResponse?.data?.user?.result?.timeline_v2?.timeline?.instructions) { 596 | let instructions = jsonResponse?.data?.user?.result?.timeline_v2?.timeline?.instructions; 597 | tweet_entries = TLD_background.parseTweetEntries(instructions); 598 | } 599 | } 600 | 601 | /** 602 | * For "Topic" pages, the "entries" property containing the array with tweets 603 | * can be found in the "instructions" array but at 3 different indexes 604 | * (instructions[0], [1] and [2]). It is cleaner to loop through that array 605 | * and search for that property 606 | */ 607 | if (jsonResponse?.data?.topic_by_rest_id?.topic_page?.body?.timeline?.instructions) { 608 | for (const value of jsonResponse.data.topic_by_rest_id.topic_page.body.timeline.instructions) { 609 | if (value.entries) { 610 | tweet_entries = value.entries; 611 | } 612 | } 613 | } 614 | 615 | /** 616 | * Add the tweets from the "Communities" page to the array with tweet entries 617 | */ 618 | if (tweet_entries === undefined || tweet_entries.length === 0) { 619 | if (jsonResponse?.data?.viewer?.explore_communities_timeline?.timeline?.instructions) { 620 | let instructions = jsonResponse?.data?.viewer?.explore_communities_timeline?.timeline?.instructions; 621 | tweet_entries = TLD_background.parseTweetEntries(instructions); 622 | } 623 | } 624 | 625 | /** 626 | * Add the tweets from the "Explore" page to the array with tweet entries 627 | */ 628 | if (tweet_entries === undefined || tweet_entries.length === 0) { 629 | if (jsonResponse?.data?.explore_page?.body?.initialTimeline?.timeline?.timeline?.instructions) { 630 | let instructions = jsonResponse?.data?.explore_page?.body?.initialTimeline?.timeline?.timeline?.instructions; 631 | tweet_entries = TLD_background.parseTweetEntries(instructions); 632 | } 633 | } 634 | 635 | return tweet_entries; 636 | }; 637 | 638 | 639 | /** 640 | * A function that selects and returns an array containing tweets 641 | * @method parseTweetEntries 642 | * @memberof TLD_background 643 | * @param {object} instructions - An array containing objects 644 | * @returns {(Array)} - Returns an array with the tweet entries as objects 645 | */ 646 | TLD_background.parseTweetEntries = function(instructions) { 647 | let tweet_entries = []; 648 | for (let instruction of instructions) { 649 | if (instruction.type === "TimelinePinEntry") { 650 | tweet_entries.push(instruction.entry); 651 | } // add the pinned tweet to the array 652 | if (instruction.type === "TimelineAddEntries") { 653 | for (let entry of instruction.entries) { 654 | tweet_entries.push(entry); 655 | } 656 | } // add the other tweets to the array 657 | } 658 | return tweet_entries; 659 | }; 660 | 661 | 662 | 663 | /** 664 | * Initialize the add-on 665 | */ 666 | // Set the background color of the badge text 667 | browser.browserAction.setBadgeBackgroundColor(TLD_background.config.badgeBackgroundColor); 668 | 669 | // Set the initial add-on state 670 | browser.storage.local.set(TLD_background.config.defaultAddonState) // initialize the storage with the default value 671 | /*.then(() => { // ...then log the stored value 672 | console.log("The default value was stored."); // for debugging 673 | browser.storage.local.get() 674 | .then((storedSettings) => { 675 | console.log(`The initial value is: ${storedSettings.enabled}`); // for debugging 676 | }) 677 | .catch(() => { 678 | console.error("Error retrieving stored settings"); 679 | }); 680 | }) // for debugging 681 | */.catch(() => { 682 | console.error("Error storing the default value."); 683 | }); 684 | 685 | 686 | /** 687 | * Add some event listeners 688 | */ 689 | /*browser.storage.onChanged.addListener((newSettings) => { // log the new value every time it changes 690 | browser.tabs.query({}).then(console.log(`The value was changed to ${newSettings.enabled.newValue}`)); 691 | });*/ // for debugging 692 | browser.browserAction.onClicked.addListener(TLD_background.toggleStatus); // toggle the add-on status when the icon is clicked 693 | 694 | browser.runtime.onMessage.addListener(TLD_background.handleMessage); // listen for messages from the background script 695 | 696 | browser.webRequest.onBeforeRequest.addListener( 697 | TLD_background.modifyNetworkRequests, 698 | {urls: ["*://*.twitter.com/*", "*://*.x.com/*"]}, 699 | ["blocking"] 700 | ); // intercept the network responses from twitter.com and x.com 701 | --------------------------------------------------------------------------------