From 91954f28d903fb4cbfbd24e6e9a8ccdb767e847b Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Tue, 28 Nov 2023 19:16:13 +0200 Subject: [PATCH 1/8] TW-1180 Remake placing ads and sending 'External links activity' event --- src/contentScript.ts | 29 -- src/lib/constants.ts | 13 + src/lib/slise/get-ads-containers.ts | 74 ++++- src/lib/slise/get-slot-id.ts | 6 +- src/lib/temple/back/main.ts | 17 +- src/lib/utils/url-track/constants.ts | 344 --------------------- src/lib/utils/url-track/get-tracked-url.ts | 65 ---- src/replaceAds.tsx | 29 +- webpack/manifest.ts | 2 +- 9 files changed, 110 insertions(+), 469 deletions(-) delete mode 100644 src/lib/utils/url-track/constants.ts delete mode 100644 src/lib/utils/url-track/get-tracked-url.ts diff --git a/src/contentScript.ts b/src/contentScript.ts index fdc5fe588..989c1e3eb 100644 --- a/src/contentScript.ts +++ b/src/contentScript.ts @@ -1,13 +1,10 @@ import { TemplePageMessage, TemplePageMessageType } from '@temple-wallet/dapp/dist/types'; import browser from 'webextension-polyfill'; -import { ContentScriptType, WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; import { IntercomClient } from 'lib/intercom/client'; import { serealizeError } from 'lib/intercom/helpers'; import { TempleMessageType, TempleResponse } from 'lib/temple/types'; -const TRACK_URL_CHANGE_INTERVAL = 5000; - enum BeaconMessageTarget { Page = 'toPage', Extension = 'toExtension' @@ -36,32 +33,6 @@ type BeaconMessage = }; type BeaconPageMessage = BeaconMessage | { message: BeaconMessage; sender: { id: string } }; -// Prevents the script from running in an Iframe -if (window.frameElement === null) { - browser.storage.local.get(WEBSITES_ANALYTICS_ENABLED).then(storage => { - if (storage[WEBSITES_ANALYTICS_ENABLED]) { - let oldHref = ''; - - const trackUrlChange = () => { - const newHref = window.parent.location.href; - if (oldHref !== newHref) { - oldHref = newHref; - - browser.runtime.sendMessage({ - type: ContentScriptType.ExternalLinksActivity, - url: newHref - }); - } - }; - - trackUrlChange(); - - // Track url changes without page reload - setInterval(trackUrlChange, TRACK_URL_CHANGE_INTERVAL); - } - }); -} - const SENDER = { id: browser.runtime.id, name: 'Temple - Tezos Wallet', diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 805443675..e2e0b1550 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -2,6 +2,19 @@ export enum ContentScriptType { ExternalLinksActivity = 'ExternalLinksActivity' } +export enum AdType { + EtherscanBuiltin = 'etherscan-builtin', + Bitmedia = 'bitmedia', + Coinzilla = 'coinzilla', + Cointraffic = 'cointraffic' +} + export const WEBSITES_ANALYTICS_ENABLED = 'WEBSITES_ANALYTICS_ENABLED'; export const ACCOUNT_PKH_STORAGE_KEY = 'account_publickeyhash'; + +export const ETHERSCAN_BUILTIN_ADS_WEBSITES = [ + 'https://etherscan.io', + 'https://bscscan.com', + 'https://polygonscan.com' +]; diff --git a/src/lib/slise/get-ads-containers.ts b/src/lib/slise/get-ads-containers.ts index 3f9b0aa1a..c3859fbcc 100644 --- a/src/lib/slise/get-ads-containers.ts +++ b/src/lib/slise/get-ads-containers.ts @@ -1,41 +1,85 @@ +import { AdType, ETHERSCAN_BUILTIN_ADS_WEBSITES } from 'lib/constants'; + interface AdContainerProps { element: HTMLElement; width: number; + height: number; + type: AdType; } -const getFinalWidth = (element: Element) => { +const getFinalSize = (element: Element) => { const elementStyle = getComputedStyle(element); - const rawWidthFromStyle = elementStyle.width; - const rawWidthFromAttribute = element.getAttribute('width'); + const size = { width: 0, height: 0 }; + const dimensions = ['width', 'height'] as const; + + for (const dimension of dimensions) { + const rawDimensionFromStyle = elementStyle[dimension]; + const rawDimensionFromAttribute = element.getAttribute(dimension); + const rawDimension = rawDimensionFromAttribute || rawDimensionFromStyle; + + if (/\d+px/.test(rawDimension)) { + size[dimension] = Number(rawDimension.replace('px', '')); + } else { + size[dimension] = dimension === 'width' ? element.clientWidth : element.clientHeight; + } + } - return Number((rawWidthFromAttribute || rawWidthFromStyle).replace('px', '') || element.clientWidth); + return size; }; +const mapBannersWithType = (banners: NodeListOf, type: AdType) => + [...banners].map(banner => ({ banner, type })); + export const getAdsContainers = () => { - const builtInAdsImages = [...document.querySelectorAll('span + img')].filter(element => { - const { width, height } = element.getBoundingClientRect(); - const label = element.previousElementSibling?.innerHTML ?? ''; + const locationUrl = window.parent.location.href; + const builtInAdsImages = ETHERSCAN_BUILTIN_ADS_WEBSITES.some(urlPrefix => locationUrl.startsWith(urlPrefix)) + ? [...document.querySelectorAll('span + img')].filter(element => { + const { width, height } = element.getBoundingClientRect(); + const label = element.previousElementSibling?.innerHTML ?? ''; - return (width > 0 || height > 0) && ['Featured', 'Ad'].includes(label); - }); - const coinzillaBanners = [...document.querySelectorAll('.coinzilla')]; - const bitmediaBanners = [...document.querySelectorAll('iframe[src*="media.bmcdn"], iframe[src*="cdn.bmcdn"]')]; + return (width > 0 || height > 0) && ['Featured', 'Ad'].includes(label); + }) + : []; + const coinzillaBanners = mapBannersWithType( + document.querySelectorAll('iframe[src*="coinzilla.io"], iframe[src*="czilladx.com"]'), + AdType.Coinzilla + ); + const bitmediaBanners = mapBannersWithType( + document.querySelectorAll('iframe[src*="media.bmcdn"], iframe[src*="cdn.bmcdn"]'), + AdType.Bitmedia + ); + const cointrafficBanners = mapBannersWithType( + document.querySelectorAll('iframe[src*="ctengine.io"]'), + AdType.Cointraffic + ); return builtInAdsImages .map((image): AdContainerProps | null => { const element = image.closest('div'); - return element && { element, width: getFinalWidth(image) }; + return ( + element && { + ...getFinalSize(image), + element, + type: AdType.EtherscanBuiltin + } + ); }) .concat( - [...bitmediaBanners, ...coinzillaBanners].map(banner => { + [...bitmediaBanners, ...coinzillaBanners, ...cointrafficBanners].map(({ banner, type }) => { const parentElement = banner.parentElement; const closestDiv = parentElement?.closest('div') ?? null; - const element = bitmediaBanners.includes(banner) ? closestDiv : parentElement; + const element = banner.tagName === 'div' ? parentElement : closestDiv; const widthDefinedElement = element?.parentElement ?? parentElement; const bannerFrame = banner.tagName === 'iframe' ? banner : banner.querySelector('iframe'); - return element && { element, width: getFinalWidth(bannerFrame || widthDefinedElement!) }; + return ( + element && { + ...getFinalSize(bannerFrame || widthDefinedElement!), + element, + type + } + ); }) ) .filter((element): element is AdContainerProps => Boolean(element)); diff --git a/src/lib/slise/get-slot-id.ts b/src/lib/slise/get-slot-id.ts index 6f0e67880..83d0cc005 100644 --- a/src/lib/slise/get-slot-id.ts +++ b/src/lib/slise/get-slot-id.ts @@ -1,7 +1,9 @@ export const getSlotId = () => { - const hostnameParts = window.location.hostname.split('.').filter(part => part !== 'www'); + const hostnameParts = window.parent.location.hostname.split('.').filter(part => part !== 'www'); const serviceId = hostnameParts[0]; - const pathnameParts = window.location.pathname.split('/').filter(part => part !== '' && !/0x[0-9a-f]+/i.test(part)); + const pathnameParts = window.parent.location.pathname + .split('/') + .filter(part => part !== '' && !/(0x)?[0-9a-f]+/i.test(part) && !/[0-9a-z]{30,}/i.test(part)); return [serviceId, ...pathnameParts].join('-'); }; diff --git a/src/lib/temple/back/main.ts b/src/lib/temple/back/main.ts index 34e52aab1..955c033e9 100644 --- a/src/lib/temple/back/main.ts +++ b/src/lib/temple/back/main.ts @@ -5,7 +5,6 @@ import { BACKGROUND_IS_WORKER } from 'lib/env'; import { encodeMessage, encryptMessage, getSenderId, MessageType, Response } from 'lib/temple/beacon'; import { clearAsyncStorages } from 'lib/temple/reset'; import { TempleMessageType, TempleRequest, TempleResponse } from 'lib/temple/types'; -import { getTrackedUrl } from 'lib/utils/url-track/get-tracked-url'; import { ACCOUNT_PKH_STORAGE_KEY, ContentScriptType } from '../../constants'; import * as Actions from './actions'; @@ -246,16 +245,12 @@ const processRequest = async (req: TempleRequest, port: Runtime.Port): Promise { if (msg?.type === ContentScriptType.ExternalLinksActivity) { - const url = getTrackedUrl(msg.url); - - if (url) { - browser.storage.local - .get(ACCOUNT_PKH_STORAGE_KEY) - .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => - Analytics.client.track('External links activity', { url, accountPkh }) - ) - .catch(console.error); - } + browser.storage.local + .get(ACCOUNT_PKH_STORAGE_KEY) + .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => + Analytics.client.track('External links activity', { url: msg.url, accountPkh }) + ) + .catch(console.error); } if (msg?.type === E2eMessageType.ResetRequest) { diff --git a/src/lib/utils/url-track/constants.ts b/src/lib/utils/url-track/constants.ts deleted file mode 100644 index 43c3a1b3c..000000000 --- a/src/lib/utils/url-track/constants.ts +++ /dev/null @@ -1,344 +0,0 @@ -interface RegExpsWithUrls { - regExp: RegExp; - url: string; -} - -export const REG_EXPS_WITH_URLS: RegExpsWithUrls[] = [ - { - // https://www.coindesk.com/price/bitcoin/ - regExp: /^https:\/\/www\.coindesk\.com\/price\/[a-z-]+\/$/, - url: 'https://www.coindesk.com/price/tokens' - }, - { - // https://de.fi/account/0xd874387ebb001a6b0bea98072f8de05f8965e51e/dashboard/portfolio-overview - regExp: /^https:\/\/de\.fi\/account\/[a-zA-Z0-9]+\/dashboard\/portfolio-overview$/, - url: 'https://de.fi/account/' - }, - { - // https://decrypt.co/197988/how-to-get-a-refund-for-fortnite-from-epic-games - regExp: /^https:\/\/decrypt\.co\/[0-9]+\/[a-z0-9-]+$/, - url: 'https://decrypt.co/articles' - }, - { - // https://cryptopotato.com/is-ripple-xrp-about-to-outperform-ethereum-eth-by-500/ - regExp: /^https:\/\/cryptopotato\.com\/[a-z0-9-]+\/$/, - url: 'https://cryptopotato.com/articles' - }, - { - // https://www.newsbtc.com/news/polygon-price-speculation-can-matic-defend-the-0-5-threshold/ - regExp: /^https:\/\/www\.newsbtc\.com\/news\/[a-z0-9-]+\/$/, - url: 'https://www.newsbtc.com/news/articles' - }, - { - // https://news.bitcoin.com/secs-crypto-chief-signals-ramp-up-in-enforcement-were-going-to-continue-to-bring-those-charges/ - regExp: /^https:\/\/news\.bitcoin\.com\/[a-z0-9-]+\/$/, - url: 'https://news.bitcoin.com/articles' - }, - { - // https://cryptonews.com/news/next-pepe-coin-make-crypto-millionaires-wall-street-memes-presale-ends-4-days.htm - regExp: /^https:\/\/cryptonews\.com\/news\/[a-z0-9.-]+$/, - url: 'https://cryptonews.com/news/articles' - }, - { - // https://ambcrypto.com/will-polkadots-usdc-integration-boost-the-network/ - regExp: /^https:\/\/ambcrypto\.com\/[a-z0-9-]+\/$/, - url: 'https://ambcrypto.com/articles' - }, - { - // https://www.investopedia.com/crypto-adoption-flatlined-as-fomo-turned-into-buyer-s-remorse-in-2022-7971317 - regExp: /^https:\/\/www\.investopedia\.com\/[a-z0-9-]+$/, - url: 'https://investopedia.com/articles' - }, - { - // https://thecryptobasic.com/2023/09/20/bybit-lists-paypal-usd-pyusd-stablecoin-for-spot-trading/ - regExp: /^https:\/\/thecryptobasic\.com\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9-]+\/$/, - url: 'https://thecryptobasic.com/articles' - }, - { - // https://beincrypto.com/ltc-price-breaks-out-double-bottom-pattern/ - regExp: /^https:\/\/beincrypto\.com\/[a-z0-9-]+\/$/, - url: 'https://beincrypto.com/articles' - }, - { - // https://beincrypto.com/price/bitcoin/ - regExp: /^https:\/\/beincrypto\.com\/price\/[a-z-]+\/$/, - url: 'https://beincrypto.com/price/tokens' - }, - { - // https://blockworks.co/news/hal-finney-bitcoin-satoshi-nakamoto-zk-proofs - regExp: /^https:\/\/blockworks\.co\/news\/[a-z0-9-]+$/, - url: 'https://blockworks.co/news/articles' - }, - { - // https://www.tradingview.com/symbols/BTCUSD/?exchange=CRYPTO - regExp: /^https:\/\/www\.tradingview\.com\/symbols\/[A-Z]+\/\?exchange=CRYPTO$/, - url: 'https://www.tradingview.com/symbols/tokens' - }, - { - // https://www.tradingview.com/chart/ARBUSDT/DCKdJWUH-Long-trade-12-for-Arbitrum-ARB-price/ - regExp: /^https:\/\/www\.tradingview\.com\/chart\/[A-Z]+\/[A-Za-z0-9-]+\/$/, - url: 'https://www.tradingview.com/chart/articles' - }, - { - // https://cryptoslate.com/coins/ - regExp: /^https:\/\/cryptoslate\.com\/coins\/$/, - url: 'https://cryptoslate.com/coins' - }, - { - // https://cryptoslate.com/celsius-to-purchase-45m-core-scientific-mining-facility-as-part-of-bankruptcy-settlement/ - regExp: /^https:\/\/cryptoslate\.com\/[a-z0-9-]+\/$/, - url: 'https://cryptoslate.com/articles' - }, - { - // https://cryptoslate.com/coins/bitcoin/ - regExp: /^https:\/\/cryptoslate\.com\/coins\/[a-z0-9-]+\/$/, - url: 'https://cryptoslate.com/coins/tokens' - }, - { - // https://xtz.news/defi/plenty-set-to-launch-advanced-v3-decentralized-exchange-on-tezos-today/ - regExp: /^https:\/\/xtz\.news\/[a-z-]+\/[a-z0-9-]+\/$/, - url: 'https://xtz.news/articles' - }, - { - // https://www.investing.com/news/economy/futures-dither-after-wall-st-rout-fed-rate-verdict-in-focus-3176453 - regExp: /^https:\/\/www\.investing\.com\/news\/[a-z-]+\/[a-z0-9-]+$/, - url: 'https://www.investing.com/news/articles' - }, - { - // https://www.investing.com/crypto/bitcoin - regExp: /^https:\/\/www\.investing\.com\/crypto\/[a-z-]+$/, - url: 'https://www.investing.com/crypto/tokens' - }, - { - // https://cointelegraph.com/ethereum-price - regExp: /^https:\/\/cointelegraph\.com\/[a-z-]+(-price)$/, - url: 'https://cointelegraph.com/tokens' - }, - { - // https://airdrops.io/celestia/ - regExp: /^https:\/\/airdrops\.io\/[a-z0-9-]+\/$/, - url: 'https://airdrops.io/projects' - }, - { - // https://airdropalert.com/posttech-airdrop - regExp: /^https:\/\/airdropalert\.com\/[a-z-]+(-airdrop)$/, - url: 'https://airdropalert.com/airdrop' - }, - { - // https://www.dextools.io/app/en/ether/pairs - regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pairs$/, - url: 'https://www.dextools.io/app/chains' - }, - { - // https://www.dextools.io/app/en/ether/pool-explorer - regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pool-explorer$/, - url: 'https://www.dextools.io/app/newpools' - }, - { - // https://www.dextools.io/app/en/ether/pair-explorer/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pair-explorer\/[a-z0-9]+$/, - url: 'https://www.dextools.io/app/pairs' - }, - { - // https://blockexplorer.one/ethereum/mainnet - regExp: /^https:\/\/blockexplorer\.one\/[a-z]+\/mainnet$/, - url: 'https://blockexplorer.one/tokens' - }, - { - // https://blockexplorer.one/bitcoin/mainnet/blockHash/00000000000000000001bb52fa052634627b8b0f67503035e5b42186ccb2014a - regExp: /^https:\/\/blockexplorer\.one\/bitcoin\/mainnet\/blockHash\/[A-Za-z0-9]+$/, - url: 'https://blockexplorer.one/bitcoin/mainnet/txs' - }, - { - // https://blockexplorer.one/bitcoin/mainnet/address/38XnPvu9PmonFU9WouPXUjYbW91wa5MerL - regExp: /^https:\/\/blockexplorer\.one\/bitcoin\/mainnet\/address\/[A-Za-z0-9]+$/, - url: 'https://blockexplorer.one/bitcoin/mainnet/addresses' - }, - { - // https://basescan.org/address/0xaabb3340c2071f009bd78b762a2d89031ef32ad5 - regExp: /^https:\/\/basescan\.org\/address\/[A-Za-z0-9]+$/, - url: 'https://basescan.org/addresses' - }, - { - // https://basescan.org/token/0xa9a683b599c148ffc0a8c62369260695c3149967 - regExp: /^https:\/\/basescan\.org\/token\/[A-Za-z0-9]+$/, - url: 'https://basescan.org/tokens' - } -]; - -// https://tzkt.io/KT1WT8hZsixALTmxcM3SDzCyh4UF8hYXVaUb/operations -export const tzktTokenOrAccountRegExp = /^https:\/\/tzkt\.io\/[a-zA-Z0-9]+\/operations$/; - -// https://tzkt.io/ooSNf4vavf8Eo6UmiBKnSPfLjESqHwbtumh4kzYAFtiAo9XVEY9/69189236 -export const tzktOpHashRegExp = /https:\/\/tzkt\.io\/([^/]+)/; - -// https://www.coindesk.com/policy/2023/08/11/sam-bankman-fried-jailed-ahead-of-trial/ -export const coindeskArticlesRegExp = - /^https:\/\/www\.coindesk\.com\/(policy|tech|business|markets)\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9-]+\/$/; - -export const bitcoinMagazineArticlesRegExp = /^https:\/\/bitcoinmagazine\.com\/[a-z]+\/[a-z0-9-]+$/; - -export const EXACT_MATCH_URLS = [ - 'https://tzkt.io', - - 'https://etherscan.io/', - - 'https://bscscan.com/', - - 'https://polygonscan.com/', - - 'https://tronscan.org/#/', - - 'https://arbiscan.io/', - - 'https://www.livecoinwatch.com/', - - 'https://www.coindesk.com/', - 'https://www.coindesk.com/policy/', - 'https://www.coindesk.com/tech/', - 'https://www.coindesk.com/business/', - 'https://www.coindesk.com/markets/', - 'https://www.coindesk.com/data/', - - 'https://decrypt.co/', - 'https://decrypt.co/news', - - 'https://dappradar.com/rankings/explorer/', - - 'https://u.today/latest-cryptocurrency-news', - 'https://u.today/bitcoin-news', - 'https://u.today/ripple-news', - 'https://u.today/meme-cryptocurrencies', - - 'https://cryptopotato.com/', - 'https://cryptopotato.com/crypto-news/', - 'https://cryptopotato.com/bitcoin-price-analysis/', - 'https://cryptopotato.com/ethereum-price-analysis/', - 'https://cryptopotato.com/ripple-price-analysis/', - - 'https://www.newsbtc.com/', - 'https://www.newsbtc.com/news/', - - 'https://cryptonews.com/', - 'https://cryptonews.com/news/', - - 'https://coincodex.com/', - 'https://coincodex.com/news/', - - 'https://news.bitcoin.com/', - - 'https://bitcoinmagazine.com/', - 'https://bitcoinmagazine.com/articles', - - 'https://www.investopedia.com/', - 'https://www.investopedia.com/cryptocurrency-news-5114163', - - 'https://www.cnbc.com/cryptocurrency/', - - 'https://ambcrypto.com/', - 'https://ambcrypto.com/category/new-news/', - - 'https://thecryptobasic.com/', - 'https://thecryptobasic.com/category/latest-crypto-news/', - - 'https://beincrypto.com/', - 'https://beincrypto.com/news/', - 'https://beincrypto.com/bitcoin-news/', - 'https://beincrypto.com/altcoin-news/', - 'https://beincrypto.com/price/', - - 'https://blockworks.co/news', - - 'https://www.tradingview.com/markets/cryptocurrencies/', - 'https://www.tradingview.com/markets/cryptocurrencies/ideas/', - - 'https://cryptoslate.com/', - 'https://cryptoslate.com/top-news/', - 'https://cryptoslate.com/coins', - - 'https://xtz.news/', - 'https://xtz.news/category/latest-news/', - - 'https://www.investing.com/', - - 'https://dappradar.com/rankings/explorer', - 'https://dappradar.com/rankings', - 'https://dappradar.com/rankings/tokens', - - 'https://cointelegraph.com/', - - 'https://coinmarketcap.com/coins/', - - 'https://magic.store/', - 'https://magic.store/apps', - 'https://magic.store/upcoming', - 'https://magic.store/games', - - 'https://airdrops.io/', - 'https://airdrops.io/latest/', - 'https://airdrops.io/hot/', - 'https://airdrops.io/speculative/', - - 'https://airdropalert.com/', - 'https://airdropalert.com/new-airdrops', - 'https://airdropalert.com/upcoming-airdrops', - 'https://airdropalert.com/featured-airdrops', - - 'https://www.dextools.io/app/en/pairs', - - 'https://www.blockchain.com/explorer/', - - 'https://www.coingecko.com/', - - 'https://basescan.org/', - - 'https://optimistic.etherscan.io/' -]; - -export const STARTS_WITH_URLS = [ - 'https://etherscan.io/tx', - 'https://etherscan.io/address', - 'https://etherscan.io/token', - - 'https://bscscan.com/tx', - 'https://bscscan.com/address', - 'https://bscscan.com/token', - - 'https://polygonscan.com/tx', - 'https://polygonscan.com/address', - 'https://polygonscan.com/token', - - 'https://tronscan.org/#/transaction', - 'https://tronscan.org/#/address', - 'https://tronscan.org/#/block', - 'https://tronscan.org/#/token', - - 'https://cointelegraph.com/news', - - 'https://www.coingecko.com/en/exchanges', - 'https://www.coingecko.com/en/coins/', - - 'https://www.cnbc.com/quotes', - - 'https://blockworks.co/category', - - 'https://coincodex.com/article', - 'https://coincodex.com/crypto', - - 'https://www.investing.com/indices', - - 'https://www.dappt.io/app', - - 'https://coinmarketcap.com/currencies', - - 'https://www.reddit.com/r/Bitcoin/comments', - 'https://www.reddit.com/r/Bitcoin/', - - 'https://airdropalert.com/earn', - - 'https://basescan.org/tx', - - 'https://optimistic.etherscan.io/tx', - 'https://optimistic.etherscan.io/token/', - 'https://optimistic.etherscan.io/address/' -]; diff --git a/src/lib/utils/url-track/get-tracked-url.ts b/src/lib/utils/url-track/get-tracked-url.ts deleted file mode 100644 index 0dbaf0fbc..000000000 --- a/src/lib/utils/url-track/get-tracked-url.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - bitcoinMagazineArticlesRegExp, - coindeskArticlesRegExp, - EXACT_MATCH_URLS, - REG_EXPS_WITH_URLS, - STARTS_WITH_URLS, - tzktOpHashRegExp, - tzktTokenOrAccountRegExp -} from './constants'; - -export const getTrackedUrl = (actualUrl: string) => { - if (EXACT_MATCH_URLS.includes(actualUrl)) { - return actualUrl; - } - - if (tzktTokenOrAccountRegExp.test(actualUrl) || tzktOpHashRegExp.test(actualUrl)) { - return transformTzktUrl(actualUrl); - } - - if (coindeskArticlesRegExp.test(actualUrl)) { - const articleCategory = actualUrl.split('/')[3]; - - return `https://www.coindesk.com/${articleCategory}/articles`; - } - - if (bitcoinMagazineArticlesRegExp.test(actualUrl)) { - const articleCategory = actualUrl.split('/')[3]; - - return `https://bitcoinmagazine.com/${articleCategory}/article`; - } - - for (let i = 0; i < REG_EXPS_WITH_URLS.length; i++) { - const currentItem = REG_EXPS_WITH_URLS[i]; - - if (currentItem.regExp.test(actualUrl)) { - return currentItem.url; - } - } - - for (let i = 0; i < STARTS_WITH_URLS.length; i++) { - const currentLink = STARTS_WITH_URLS[i]; - - if (actualUrl.startsWith(currentLink)) { - return currentLink; - } - } - - return null; -}; - -const transformTzktUrl = (url: string) => { - const splittedUrl = url.split('/'); - const indexOfParamToCheck = 3; - const checkedParam = splittedUrl[indexOfParamToCheck]; - - if (checkedParam.startsWith('tz')) { - return `https://tzkt.io/address/`; - } else if (checkedParam.startsWith('KT')) { - return `https://tzkt.io/token/`; - } else if (checkedParam.startsWith('o')) { - return `https://tzkt.io/transaction/`; - } - - return 'https://tzkt.io/'; -}; diff --git a/src/replaceAds.tsx b/src/replaceAds.tsx index eb5a89a4e..05f525b81 100644 --- a/src/replaceAds.tsx +++ b/src/replaceAds.tsx @@ -4,7 +4,7 @@ import debounce from 'debounce'; import { createRoot } from 'react-dom/client'; import browser from 'webextension-polyfill'; -import { WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; +import { AdType, ContentScriptType, ETHERSCAN_BUILTIN_ADS_WEBSITES, WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; import { getAdsContainers } from 'lib/slise/get-ads-containers'; import { getSlotId } from 'lib/slise/get-slot-id'; import { SliseAd } from 'lib/slise/slise-ad'; @@ -14,12 +14,30 @@ const availableAdsResolutions = [ { width: 728, height: 90 } ]; +let oldHref = ''; + const replaceAds = debounce( () => { try { const adsContainers = getAdsContainers(); + const adsContainersToReplace = adsContainers.filter( + ({ width, height }) => + ((width >= 600 && width <= 900) || (width >= 180 && width <= 430)) && height >= 60 && height <= 120 + ); + + const newHref = window.parent.location.href; + if (oldHref !== newHref) { + oldHref = newHref; + + if (adsContainersToReplace.length > 0) { + browser.runtime.sendMessage({ + type: ContentScriptType.ExternalLinksActivity, + url: window.parent.location.origin + }); + } + } - adsContainers.forEach(({ element: adContainer, width: containerWidth }) => { + adsContainersToReplace.forEach(({ element: adContainer, width: containerWidth, type }) => { let adsResolution = availableAdsResolutions[0]; for (let i = 1; i < availableAdsResolutions.length; i++) { const candidate = availableAdsResolutions[i]; @@ -28,6 +46,13 @@ const replaceAds = debounce( } } + if ( + ETHERSCAN_BUILTIN_ADS_WEBSITES.some(urlPrefix => newHref.startsWith(urlPrefix)) && + type === AdType.Coinzilla + ) { + adContainer.style.textAlign = 'left'; + } + const adRoot = createRoot(adContainer); adRoot.render( diff --git a/webpack/manifest.ts b/webpack/manifest.ts index fa985076d..7ad1c0f64 100644 --- a/webpack/manifest.ts +++ b/webpack/manifest.ts @@ -154,7 +154,7 @@ const buildManifestCommons = (vendor: string): Omit Date: Tue, 28 Nov 2023 19:30:24 +0200 Subject: [PATCH 2/8] TW-1180 Fix sending analytics --- src/replaceAds.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/replaceAds.tsx b/src/replaceAds.tsx index 05f525b81..49d8e80b7 100644 --- a/src/replaceAds.tsx +++ b/src/replaceAds.tsx @@ -26,15 +26,13 @@ const replaceAds = debounce( ); const newHref = window.parent.location.href; - if (oldHref !== newHref) { + if (oldHref !== newHref && adsContainersToReplace.length > 0) { oldHref = newHref; - if (adsContainersToReplace.length > 0) { - browser.runtime.sendMessage({ - type: ContentScriptType.ExternalLinksActivity, - url: window.parent.location.origin - }); - } + browser.runtime.sendMessage({ + type: ContentScriptType.ExternalLinksActivity, + url: window.parent.location.origin + }); } adsContainersToReplace.forEach(({ element: adContainer, width: containerWidth, type }) => { From 588f7c0c439e8a5a43eb4aa7ced223c9450b7ba7 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Wed, 29 Nov 2023 14:36:47 +0200 Subject: [PATCH 3/8] TW-1180 Change new events name and revert removal of old events --- src/contentScript.ts | 29 ++ src/lib/constants.ts | 3 +- src/lib/temple/back/main.ts | 39 ++- src/lib/utils/url-track/constants.ts | 344 +++++++++++++++++++++ src/lib/utils/url-track/get-tracked-url.ts | 65 ++++ 5 files changed, 465 insertions(+), 15 deletions(-) create mode 100644 src/lib/utils/url-track/constants.ts create mode 100644 src/lib/utils/url-track/get-tracked-url.ts diff --git a/src/contentScript.ts b/src/contentScript.ts index 989c1e3eb..fdc5fe588 100644 --- a/src/contentScript.ts +++ b/src/contentScript.ts @@ -1,10 +1,13 @@ import { TemplePageMessage, TemplePageMessageType } from '@temple-wallet/dapp/dist/types'; import browser from 'webextension-polyfill'; +import { ContentScriptType, WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; import { IntercomClient } from 'lib/intercom/client'; import { serealizeError } from 'lib/intercom/helpers'; import { TempleMessageType, TempleResponse } from 'lib/temple/types'; +const TRACK_URL_CHANGE_INTERVAL = 5000; + enum BeaconMessageTarget { Page = 'toPage', Extension = 'toExtension' @@ -33,6 +36,32 @@ type BeaconMessage = }; type BeaconPageMessage = BeaconMessage | { message: BeaconMessage; sender: { id: string } }; +// Prevents the script from running in an Iframe +if (window.frameElement === null) { + browser.storage.local.get(WEBSITES_ANALYTICS_ENABLED).then(storage => { + if (storage[WEBSITES_ANALYTICS_ENABLED]) { + let oldHref = ''; + + const trackUrlChange = () => { + const newHref = window.parent.location.href; + if (oldHref !== newHref) { + oldHref = newHref; + + browser.runtime.sendMessage({ + type: ContentScriptType.ExternalLinksActivity, + url: newHref + }); + } + }; + + trackUrlChange(); + + // Track url changes without page reload + setInterval(trackUrlChange, TRACK_URL_CHANGE_INTERVAL); + } + }); +} + const SENDER = { id: browser.runtime.id, name: 'Temple - Tezos Wallet', diff --git a/src/lib/constants.ts b/src/lib/constants.ts index e2e0b1550..66e9c8bba 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,5 +1,6 @@ export enum ContentScriptType { - ExternalLinksActivity = 'ExternalLinksActivity' + ExternalLinksActivity = 'ExternalLinksActivity', + ExternalAdsActivity = 'ExternalAdsActivity' } export enum AdType { diff --git a/src/lib/temple/back/main.ts b/src/lib/temple/back/main.ts index 955c033e9..e2aae7ac1 100644 --- a/src/lib/temple/back/main.ts +++ b/src/lib/temple/back/main.ts @@ -1,12 +1,13 @@ import browser, { Runtime } from 'webextension-polyfill'; +import { ACCOUNT_PKH_STORAGE_KEY, ContentScriptType } from 'lib/constants'; import { E2eMessageType } from 'lib/e2e/types'; import { BACKGROUND_IS_WORKER } from 'lib/env'; import { encodeMessage, encryptMessage, getSenderId, MessageType, Response } from 'lib/temple/beacon'; import { clearAsyncStorages } from 'lib/temple/reset'; import { TempleMessageType, TempleRequest, TempleResponse } from 'lib/temple/types'; +import { getTrackedUrl } from 'lib/utils/url-track/get-tracked-url'; -import { ACCOUNT_PKH_STORAGE_KEY, ContentScriptType } from '../../constants'; import * as Actions from './actions'; import * as Analytics from './analytics'; import { intercom } from './defaults'; @@ -244,20 +245,30 @@ const processRequest = async (req: TempleRequest, port: Runtime.Port): Promise { - if (msg?.type === ContentScriptType.ExternalLinksActivity) { - browser.storage.local - .get(ACCOUNT_PKH_STORAGE_KEY) - .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => - Analytics.client.track('External links activity', { url: msg.url, accountPkh }) - ) - .catch(console.error); - } + switch (msg?.type) { + case ContentScriptType.ExternalLinksActivity: + const url = getTrackedUrl(msg.url); + + if (url) { + browser.storage.local + .get(ACCOUNT_PKH_STORAGE_KEY) + .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => + Analytics.client.track('External links activity', { url, accountPkh }) + ) + .catch(console.error); + } - if (msg?.type === E2eMessageType.ResetRequest) { - return new Promise(async resolve => { - await clearAsyncStorages(); - resolve({ type: E2eMessageType.ResetResponse }); - }); + break; + case ContentScriptType.ExternalAdsActivity: + browser.storage.local + .get(ACCOUNT_PKH_STORAGE_KEY) + .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => + Analytics.client.track('External Ads Activity', { url: msg.url, accountPkh }) + ) + .catch(console.error); + break; + case E2eMessageType.ResetRequest: + return clearAsyncStorages().then(() => ({ type: E2eMessageType.ResetResponse })); } return; diff --git a/src/lib/utils/url-track/constants.ts b/src/lib/utils/url-track/constants.ts new file mode 100644 index 000000000..43c3a1b3c --- /dev/null +++ b/src/lib/utils/url-track/constants.ts @@ -0,0 +1,344 @@ +interface RegExpsWithUrls { + regExp: RegExp; + url: string; +} + +export const REG_EXPS_WITH_URLS: RegExpsWithUrls[] = [ + { + // https://www.coindesk.com/price/bitcoin/ + regExp: /^https:\/\/www\.coindesk\.com\/price\/[a-z-]+\/$/, + url: 'https://www.coindesk.com/price/tokens' + }, + { + // https://de.fi/account/0xd874387ebb001a6b0bea98072f8de05f8965e51e/dashboard/portfolio-overview + regExp: /^https:\/\/de\.fi\/account\/[a-zA-Z0-9]+\/dashboard\/portfolio-overview$/, + url: 'https://de.fi/account/' + }, + { + // https://decrypt.co/197988/how-to-get-a-refund-for-fortnite-from-epic-games + regExp: /^https:\/\/decrypt\.co\/[0-9]+\/[a-z0-9-]+$/, + url: 'https://decrypt.co/articles' + }, + { + // https://cryptopotato.com/is-ripple-xrp-about-to-outperform-ethereum-eth-by-500/ + regExp: /^https:\/\/cryptopotato\.com\/[a-z0-9-]+\/$/, + url: 'https://cryptopotato.com/articles' + }, + { + // https://www.newsbtc.com/news/polygon-price-speculation-can-matic-defend-the-0-5-threshold/ + regExp: /^https:\/\/www\.newsbtc\.com\/news\/[a-z0-9-]+\/$/, + url: 'https://www.newsbtc.com/news/articles' + }, + { + // https://news.bitcoin.com/secs-crypto-chief-signals-ramp-up-in-enforcement-were-going-to-continue-to-bring-those-charges/ + regExp: /^https:\/\/news\.bitcoin\.com\/[a-z0-9-]+\/$/, + url: 'https://news.bitcoin.com/articles' + }, + { + // https://cryptonews.com/news/next-pepe-coin-make-crypto-millionaires-wall-street-memes-presale-ends-4-days.htm + regExp: /^https:\/\/cryptonews\.com\/news\/[a-z0-9.-]+$/, + url: 'https://cryptonews.com/news/articles' + }, + { + // https://ambcrypto.com/will-polkadots-usdc-integration-boost-the-network/ + regExp: /^https:\/\/ambcrypto\.com\/[a-z0-9-]+\/$/, + url: 'https://ambcrypto.com/articles' + }, + { + // https://www.investopedia.com/crypto-adoption-flatlined-as-fomo-turned-into-buyer-s-remorse-in-2022-7971317 + regExp: /^https:\/\/www\.investopedia\.com\/[a-z0-9-]+$/, + url: 'https://investopedia.com/articles' + }, + { + // https://thecryptobasic.com/2023/09/20/bybit-lists-paypal-usd-pyusd-stablecoin-for-spot-trading/ + regExp: /^https:\/\/thecryptobasic\.com\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9-]+\/$/, + url: 'https://thecryptobasic.com/articles' + }, + { + // https://beincrypto.com/ltc-price-breaks-out-double-bottom-pattern/ + regExp: /^https:\/\/beincrypto\.com\/[a-z0-9-]+\/$/, + url: 'https://beincrypto.com/articles' + }, + { + // https://beincrypto.com/price/bitcoin/ + regExp: /^https:\/\/beincrypto\.com\/price\/[a-z-]+\/$/, + url: 'https://beincrypto.com/price/tokens' + }, + { + // https://blockworks.co/news/hal-finney-bitcoin-satoshi-nakamoto-zk-proofs + regExp: /^https:\/\/blockworks\.co\/news\/[a-z0-9-]+$/, + url: 'https://blockworks.co/news/articles' + }, + { + // https://www.tradingview.com/symbols/BTCUSD/?exchange=CRYPTO + regExp: /^https:\/\/www\.tradingview\.com\/symbols\/[A-Z]+\/\?exchange=CRYPTO$/, + url: 'https://www.tradingview.com/symbols/tokens' + }, + { + // https://www.tradingview.com/chart/ARBUSDT/DCKdJWUH-Long-trade-12-for-Arbitrum-ARB-price/ + regExp: /^https:\/\/www\.tradingview\.com\/chart\/[A-Z]+\/[A-Za-z0-9-]+\/$/, + url: 'https://www.tradingview.com/chart/articles' + }, + { + // https://cryptoslate.com/coins/ + regExp: /^https:\/\/cryptoslate\.com\/coins\/$/, + url: 'https://cryptoslate.com/coins' + }, + { + // https://cryptoslate.com/celsius-to-purchase-45m-core-scientific-mining-facility-as-part-of-bankruptcy-settlement/ + regExp: /^https:\/\/cryptoslate\.com\/[a-z0-9-]+\/$/, + url: 'https://cryptoslate.com/articles' + }, + { + // https://cryptoslate.com/coins/bitcoin/ + regExp: /^https:\/\/cryptoslate\.com\/coins\/[a-z0-9-]+\/$/, + url: 'https://cryptoslate.com/coins/tokens' + }, + { + // https://xtz.news/defi/plenty-set-to-launch-advanced-v3-decentralized-exchange-on-tezos-today/ + regExp: /^https:\/\/xtz\.news\/[a-z-]+\/[a-z0-9-]+\/$/, + url: 'https://xtz.news/articles' + }, + { + // https://www.investing.com/news/economy/futures-dither-after-wall-st-rout-fed-rate-verdict-in-focus-3176453 + regExp: /^https:\/\/www\.investing\.com\/news\/[a-z-]+\/[a-z0-9-]+$/, + url: 'https://www.investing.com/news/articles' + }, + { + // https://www.investing.com/crypto/bitcoin + regExp: /^https:\/\/www\.investing\.com\/crypto\/[a-z-]+$/, + url: 'https://www.investing.com/crypto/tokens' + }, + { + // https://cointelegraph.com/ethereum-price + regExp: /^https:\/\/cointelegraph\.com\/[a-z-]+(-price)$/, + url: 'https://cointelegraph.com/tokens' + }, + { + // https://airdrops.io/celestia/ + regExp: /^https:\/\/airdrops\.io\/[a-z0-9-]+\/$/, + url: 'https://airdrops.io/projects' + }, + { + // https://airdropalert.com/posttech-airdrop + regExp: /^https:\/\/airdropalert\.com\/[a-z-]+(-airdrop)$/, + url: 'https://airdropalert.com/airdrop' + }, + { + // https://www.dextools.io/app/en/ether/pairs + regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pairs$/, + url: 'https://www.dextools.io/app/chains' + }, + { + // https://www.dextools.io/app/en/ether/pool-explorer + regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pool-explorer$/, + url: 'https://www.dextools.io/app/newpools' + }, + { + // https://www.dextools.io/app/en/ether/pair-explorer/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d + regExp: /^https:\/\/www\.dextools\.io\/app\/[a-z]+\/[a-z]+\/pair-explorer\/[a-z0-9]+$/, + url: 'https://www.dextools.io/app/pairs' + }, + { + // https://blockexplorer.one/ethereum/mainnet + regExp: /^https:\/\/blockexplorer\.one\/[a-z]+\/mainnet$/, + url: 'https://blockexplorer.one/tokens' + }, + { + // https://blockexplorer.one/bitcoin/mainnet/blockHash/00000000000000000001bb52fa052634627b8b0f67503035e5b42186ccb2014a + regExp: /^https:\/\/blockexplorer\.one\/bitcoin\/mainnet\/blockHash\/[A-Za-z0-9]+$/, + url: 'https://blockexplorer.one/bitcoin/mainnet/txs' + }, + { + // https://blockexplorer.one/bitcoin/mainnet/address/38XnPvu9PmonFU9WouPXUjYbW91wa5MerL + regExp: /^https:\/\/blockexplorer\.one\/bitcoin\/mainnet\/address\/[A-Za-z0-9]+$/, + url: 'https://blockexplorer.one/bitcoin/mainnet/addresses' + }, + { + // https://basescan.org/address/0xaabb3340c2071f009bd78b762a2d89031ef32ad5 + regExp: /^https:\/\/basescan\.org\/address\/[A-Za-z0-9]+$/, + url: 'https://basescan.org/addresses' + }, + { + // https://basescan.org/token/0xa9a683b599c148ffc0a8c62369260695c3149967 + regExp: /^https:\/\/basescan\.org\/token\/[A-Za-z0-9]+$/, + url: 'https://basescan.org/tokens' + } +]; + +// https://tzkt.io/KT1WT8hZsixALTmxcM3SDzCyh4UF8hYXVaUb/operations +export const tzktTokenOrAccountRegExp = /^https:\/\/tzkt\.io\/[a-zA-Z0-9]+\/operations$/; + +// https://tzkt.io/ooSNf4vavf8Eo6UmiBKnSPfLjESqHwbtumh4kzYAFtiAo9XVEY9/69189236 +export const tzktOpHashRegExp = /https:\/\/tzkt\.io\/([^/]+)/; + +// https://www.coindesk.com/policy/2023/08/11/sam-bankman-fried-jailed-ahead-of-trial/ +export const coindeskArticlesRegExp = + /^https:\/\/www\.coindesk\.com\/(policy|tech|business|markets)\/\d{4}\/\d{2}\/\d{2}\/[a-z0-9-]+\/$/; + +export const bitcoinMagazineArticlesRegExp = /^https:\/\/bitcoinmagazine\.com\/[a-z]+\/[a-z0-9-]+$/; + +export const EXACT_MATCH_URLS = [ + 'https://tzkt.io', + + 'https://etherscan.io/', + + 'https://bscscan.com/', + + 'https://polygonscan.com/', + + 'https://tronscan.org/#/', + + 'https://arbiscan.io/', + + 'https://www.livecoinwatch.com/', + + 'https://www.coindesk.com/', + 'https://www.coindesk.com/policy/', + 'https://www.coindesk.com/tech/', + 'https://www.coindesk.com/business/', + 'https://www.coindesk.com/markets/', + 'https://www.coindesk.com/data/', + + 'https://decrypt.co/', + 'https://decrypt.co/news', + + 'https://dappradar.com/rankings/explorer/', + + 'https://u.today/latest-cryptocurrency-news', + 'https://u.today/bitcoin-news', + 'https://u.today/ripple-news', + 'https://u.today/meme-cryptocurrencies', + + 'https://cryptopotato.com/', + 'https://cryptopotato.com/crypto-news/', + 'https://cryptopotato.com/bitcoin-price-analysis/', + 'https://cryptopotato.com/ethereum-price-analysis/', + 'https://cryptopotato.com/ripple-price-analysis/', + + 'https://www.newsbtc.com/', + 'https://www.newsbtc.com/news/', + + 'https://cryptonews.com/', + 'https://cryptonews.com/news/', + + 'https://coincodex.com/', + 'https://coincodex.com/news/', + + 'https://news.bitcoin.com/', + + 'https://bitcoinmagazine.com/', + 'https://bitcoinmagazine.com/articles', + + 'https://www.investopedia.com/', + 'https://www.investopedia.com/cryptocurrency-news-5114163', + + 'https://www.cnbc.com/cryptocurrency/', + + 'https://ambcrypto.com/', + 'https://ambcrypto.com/category/new-news/', + + 'https://thecryptobasic.com/', + 'https://thecryptobasic.com/category/latest-crypto-news/', + + 'https://beincrypto.com/', + 'https://beincrypto.com/news/', + 'https://beincrypto.com/bitcoin-news/', + 'https://beincrypto.com/altcoin-news/', + 'https://beincrypto.com/price/', + + 'https://blockworks.co/news', + + 'https://www.tradingview.com/markets/cryptocurrencies/', + 'https://www.tradingview.com/markets/cryptocurrencies/ideas/', + + 'https://cryptoslate.com/', + 'https://cryptoslate.com/top-news/', + 'https://cryptoslate.com/coins', + + 'https://xtz.news/', + 'https://xtz.news/category/latest-news/', + + 'https://www.investing.com/', + + 'https://dappradar.com/rankings/explorer', + 'https://dappradar.com/rankings', + 'https://dappradar.com/rankings/tokens', + + 'https://cointelegraph.com/', + + 'https://coinmarketcap.com/coins/', + + 'https://magic.store/', + 'https://magic.store/apps', + 'https://magic.store/upcoming', + 'https://magic.store/games', + + 'https://airdrops.io/', + 'https://airdrops.io/latest/', + 'https://airdrops.io/hot/', + 'https://airdrops.io/speculative/', + + 'https://airdropalert.com/', + 'https://airdropalert.com/new-airdrops', + 'https://airdropalert.com/upcoming-airdrops', + 'https://airdropalert.com/featured-airdrops', + + 'https://www.dextools.io/app/en/pairs', + + 'https://www.blockchain.com/explorer/', + + 'https://www.coingecko.com/', + + 'https://basescan.org/', + + 'https://optimistic.etherscan.io/' +]; + +export const STARTS_WITH_URLS = [ + 'https://etherscan.io/tx', + 'https://etherscan.io/address', + 'https://etherscan.io/token', + + 'https://bscscan.com/tx', + 'https://bscscan.com/address', + 'https://bscscan.com/token', + + 'https://polygonscan.com/tx', + 'https://polygonscan.com/address', + 'https://polygonscan.com/token', + + 'https://tronscan.org/#/transaction', + 'https://tronscan.org/#/address', + 'https://tronscan.org/#/block', + 'https://tronscan.org/#/token', + + 'https://cointelegraph.com/news', + + 'https://www.coingecko.com/en/exchanges', + 'https://www.coingecko.com/en/coins/', + + 'https://www.cnbc.com/quotes', + + 'https://blockworks.co/category', + + 'https://coincodex.com/article', + 'https://coincodex.com/crypto', + + 'https://www.investing.com/indices', + + 'https://www.dappt.io/app', + + 'https://coinmarketcap.com/currencies', + + 'https://www.reddit.com/r/Bitcoin/comments', + 'https://www.reddit.com/r/Bitcoin/', + + 'https://airdropalert.com/earn', + + 'https://basescan.org/tx', + + 'https://optimistic.etherscan.io/tx', + 'https://optimistic.etherscan.io/token/', + 'https://optimistic.etherscan.io/address/' +]; diff --git a/src/lib/utils/url-track/get-tracked-url.ts b/src/lib/utils/url-track/get-tracked-url.ts new file mode 100644 index 000000000..0dbaf0fbc --- /dev/null +++ b/src/lib/utils/url-track/get-tracked-url.ts @@ -0,0 +1,65 @@ +import { + bitcoinMagazineArticlesRegExp, + coindeskArticlesRegExp, + EXACT_MATCH_URLS, + REG_EXPS_WITH_URLS, + STARTS_WITH_URLS, + tzktOpHashRegExp, + tzktTokenOrAccountRegExp +} from './constants'; + +export const getTrackedUrl = (actualUrl: string) => { + if (EXACT_MATCH_URLS.includes(actualUrl)) { + return actualUrl; + } + + if (tzktTokenOrAccountRegExp.test(actualUrl) || tzktOpHashRegExp.test(actualUrl)) { + return transformTzktUrl(actualUrl); + } + + if (coindeskArticlesRegExp.test(actualUrl)) { + const articleCategory = actualUrl.split('/')[3]; + + return `https://www.coindesk.com/${articleCategory}/articles`; + } + + if (bitcoinMagazineArticlesRegExp.test(actualUrl)) { + const articleCategory = actualUrl.split('/')[3]; + + return `https://bitcoinmagazine.com/${articleCategory}/article`; + } + + for (let i = 0; i < REG_EXPS_WITH_URLS.length; i++) { + const currentItem = REG_EXPS_WITH_URLS[i]; + + if (currentItem.regExp.test(actualUrl)) { + return currentItem.url; + } + } + + for (let i = 0; i < STARTS_WITH_URLS.length; i++) { + const currentLink = STARTS_WITH_URLS[i]; + + if (actualUrl.startsWith(currentLink)) { + return currentLink; + } + } + + return null; +}; + +const transformTzktUrl = (url: string) => { + const splittedUrl = url.split('/'); + const indexOfParamToCheck = 3; + const checkedParam = splittedUrl[indexOfParamToCheck]; + + if (checkedParam.startsWith('tz')) { + return `https://tzkt.io/address/`; + } else if (checkedParam.startsWith('KT')) { + return `https://tzkt.io/token/`; + } else if (checkedParam.startsWith('o')) { + return `https://tzkt.io/transaction/`; + } + + return 'https://tzkt.io/'; +}; From b80b34130749343bab2c6416937cc074a7e67630 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Wed, 29 Nov 2023 14:39:05 +0200 Subject: [PATCH 4/8] TW-1180 Remove a typo --- src/replaceAds.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/replaceAds.tsx b/src/replaceAds.tsx index 49d8e80b7..deaaa1eaa 100644 --- a/src/replaceAds.tsx +++ b/src/replaceAds.tsx @@ -30,7 +30,7 @@ const replaceAds = debounce( oldHref = newHref; browser.runtime.sendMessage({ - type: ContentScriptType.ExternalLinksActivity, + type: ContentScriptType.ExternalAdsActivity, url: window.parent.location.origin }); } From e50689e2e762fc2172a4c6d583e362915972d480 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 30 Nov 2023 16:03:49 +0200 Subject: [PATCH 5/8] TW-1180: Replace existing Ads to SliseXYZ. + import() --- .browserslistrc | 2 +- package.json | 1 - src/lib/slise/slise-ad.tsx | 8 ++++++- src/{replaceAds.tsx => replaceAds.ts} | 22 +++++++++--------- webpack.config.ts | 20 +++++------------ webpack/manifest.ts | 11 +++++++++ yarn.lock | 32 ++------------------------- 7 files changed, 37 insertions(+), 59 deletions(-) rename src/{replaceAds.tsx => replaceAds.ts} (82%) diff --git a/.browserslistrc b/.browserslistrc index ca521ad75..c517611c3 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,5 +1,5 @@ [production] -chrome >= 49 +chrome >= 103 firefox >= 52 opera >= 36 safari >= 14 diff --git a/package.json b/package.json index 546a87bdf..093722ba3 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,6 @@ "webextension-polyfill": "^0.10.0", "webpack": "^5.74.0", "webpack-cli": "^5", - "webpack-ext-reloader": "^1.1.9", "webpack-ext-reloader-mv3": "^2.1.1", "webpack-target-webextension": "^1.0.4", "webpackbar": "^5", diff --git a/src/lib/slise/slise-ad.tsx b/src/lib/slise/slise-ad.tsx index f7ea675d4..8efe6c26d 100644 --- a/src/lib/slise/slise-ad.tsx +++ b/src/lib/slise/slise-ad.tsx @@ -4,12 +4,18 @@ import { SliseAd as OriginalSliseAd, SliseAdProps } from '@slise/embed-react'; import { useDidMount } from 'lib/ui/hooks/useDidMount'; +import { getSlotId } from './get-slot-id'; + interface CunningSliseAdProps extends Omit { width: number; height: number; } -export const SliseAd = memo(({ width, height, ...restProps }: CunningSliseAdProps) => { +export const buildSliceAdReactNode = (width: number, height: number) => ( + +); + +const SliseAd = memo(({ width, height, ...restProps }: CunningSliseAdProps) => { useDidMount(() => require('./slise-ad.embed')); return ; diff --git a/src/replaceAds.tsx b/src/replaceAds.ts similarity index 82% rename from src/replaceAds.tsx rename to src/replaceAds.ts index deaaa1eaa..6d752ec62 100644 --- a/src/replaceAds.tsx +++ b/src/replaceAds.ts @@ -1,13 +1,8 @@ -import React from 'react'; - import debounce from 'debounce'; -import { createRoot } from 'react-dom/client'; import browser from 'webextension-polyfill'; import { AdType, ContentScriptType, ETHERSCAN_BUILTIN_ADS_WEBSITES, WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; import { getAdsContainers } from 'lib/slise/get-ads-containers'; -import { getSlotId } from 'lib/slise/get-slot-id'; -import { SliseAd } from 'lib/slise/slise-ad'; const availableAdsResolutions = [ { width: 270, height: 90 }, @@ -17,7 +12,7 @@ const availableAdsResolutions = [ let oldHref = ''; const replaceAds = debounce( - () => { + async () => { try { const adsContainers = getAdsContainers(); const adsContainersToReplace = adsContainers.filter( @@ -35,6 +30,11 @@ const replaceAds = debounce( }); } + if (!adsContainersToReplace.length) return; + + const ReactDomModule = await import('react-dom/client'); + const SliceAdModule = await import('lib/slise/slise-ad'); + adsContainersToReplace.forEach(({ element: adContainer, width: containerWidth, type }) => { let adsResolution = availableAdsResolutions[0]; for (let i = 1; i < availableAdsResolutions.length; i++) { @@ -51,12 +51,12 @@ const replaceAds = debounce( adContainer.style.textAlign = 'left'; } - const adRoot = createRoot(adContainer); - adRoot.render( - - ); + const adRoot = ReactDomModule.createRoot(adContainer); + adRoot.render(SliceAdModule.buildSliceAdReactNode(adsResolution.width, adsResolution.height)); }); - } catch {} + } catch (error) { + console.error('Replacing Ads error:', error); + } }, 100, true diff --git a/webpack.config.ts b/webpack.config.ts index e71aa8003..fe44ff136 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -10,7 +10,6 @@ import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import * as Path from 'path'; -import ExtensionReloaderBadlyTyped, { ExtensionReloader as ExtensionReloaderType } from 'webpack-ext-reloader'; import ExtensionReloaderMV3BadlyTyped, { ExtensionReloader as ExtensionReloaderMV3Type } from 'webpack-ext-reloader-mv3'; @@ -32,7 +31,6 @@ import { buildManifest } from './webpack/manifest'; import { PATHS } from './webpack/paths'; import { isTruthy } from './webpack/utils'; -const ExtensionReloader = ExtensionReloaderBadlyTyped as ExtensionReloaderType; const ExtensionReloaderMV3 = ExtensionReloaderMV3BadlyTyped as ExtensionReloaderMV3Type; const PAGES_NAMES = ['popup', 'fullpage', 'confirm', 'options']; @@ -158,9 +156,12 @@ const mainConfig = (() => { const scriptsConfig = (() => { const config = buildBaseConfig(); + // Required for dynamic imports `import()` + config.output!.chunkFormat = 'module'; + config.entry = { contentScript: Path.join(PATHS.SOURCE, 'contentScript.ts'), - replaceAds: Path.join(PATHS.SOURCE, 'replaceAds.tsx') + replaceAds: Path.join(PATHS.SOURCE, 'replaceAds.ts') }; if (BACKGROUND_IS_WORKER) @@ -183,18 +184,7 @@ const scriptsConfig = (() => { cleanOnceBeforeBuildPatterns: ['scripts/**'], cleanStaleWebpackAssets: false, verbose: false - }), - - /* Page reloading in development mode */ - DEVELOPMENT_ENV && - new ExtensionReloader({ - port: RELOADER_PORTS.SCRIPTS, - reloadPage: true, - entries: { - background: '', - contentScript: CONTENT_SCRIPTS - } - }) + }) ].filter(isTruthy) ); diff --git a/webpack/manifest.ts b/webpack/manifest.ts index 7ad1c0f64..a7470932c 100644 --- a/webpack/manifest.ts +++ b/webpack/manifest.ts @@ -43,6 +43,14 @@ const buildManifestV3 = (vendor: string): Manifest.WebExtensionManifest => { ...commons, + web_accessible_resources: [ + { + matches: ['https://*/*'], + // Required for dynamic imports `import()` + resources: ['scripts/*.chunk.js'] + } + ], + permissions: PERMISSIONS, host_permissions: HOST_PERMISSIONS, @@ -72,6 +80,9 @@ const buildManifestV2 = (vendor: string): Manifest.WebExtensionManifest => { content_security_policy: "script-src 'self' 'unsafe-eval' blob:; object-src 'self'", + // Required for dynamic imports `import()` + web_accessible_resources: ['scripts/*.chunk.js'], + browser_action: buildBrowserAction(vendor), options_ui: { diff --git a/yarn.lock b/yarn.lock index f00b23cb8..177238581 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3458,7 +3458,7 @@ resolved "https://registry.yarnpkg.com/@types/w3c-web-hid/-/w3c-web-hid-1.0.3.tgz#e08587a7d737f8654ea6bc0a88689ce5d3ce2d19" integrity sha512-eTQRkPd2JukZfS9+kRtrBAaTCCb6waGh5X8BJHmH1MiVQPLMYwm4+EvhwFfOo9SDna15o9dFAwmWwN6r/YM53A== -"@types/webextension-polyfill@^0.8.2", "@types/webextension-polyfill@^0.8.3": +"@types/webextension-polyfill@^0.8.3": version "0.8.3" resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.8.3.tgz#eb601b3fcf524f0ecf7d7579ccbd237928d0dda7" integrity sha512-GN+Hjzy9mXjWoXKmaicTegv3FJ0WFZ3aYz77Wk8TMp1IY3vEzvzj1vnsa0ggV7vMI1i+PUxe4qqnIJKCzf9aTg== @@ -9003,11 +9003,6 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -minimist@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -12455,11 +12450,6 @@ webextension-polyfill@^0.10.0: resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8" integrity sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g== -webextension-polyfill@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.8.0.tgz#f80e9f4b7f81820c420abd6ffbebfa838c60e041" - integrity sha512-a19+DzlT6Kp9/UI+mF9XQopeZ+n2ussjhxHJ4/pmIGge9ijCDz7Gn93mNnjpZAk95T4Tae8iHZ6sSf869txqiQ== - webextension-polyfill@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.9.0.tgz#de6c1941d0ef1b0858b20e9c7b46bbc042c5a960" @@ -12517,24 +12507,6 @@ webpack-ext-reloader-mv3@^2.1.1: webpack-sources "^3.2.3" ws "^8.6.0" -webpack-ext-reloader@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/webpack-ext-reloader/-/webpack-ext-reloader-1.1.9.tgz#7d4145c459ba3ec6e377b0e87065f7ea68686a73" - integrity sha512-6AVXGrjcVHKtIQn4yGGghJpiIV2h9F7hNKLsh1oP8m+d6H3QLF3jTNu3vNdKu/8Lab3J/gwb7Bm7tjZLa+DS6g== - dependencies: - "@types/webextension-polyfill" "^0.8.2" - "@types/webpack" "^5.28.0" - "@types/webpack-sources" "^3.2.0" - clean-webpack-plugin "^4.0.0" - colors "^1.4.0" - cross-env "^7.0.3" - lodash "^4.17.21" - minimist "^1.2.5" - useragent "^2.3.0" - webextension-polyfill "^0.8.0" - webpack-sources "^3.2.3" - ws "^8.4.2" - webpack-merge@^5.7.3: version "5.8.0" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" @@ -12720,7 +12692,7 @@ write@^1.0.3: dependencies: mkdirp "^0.5.1" -ws@7.4.6, ws@^7.4.6, ws@^7.5.1, ws@^7.5.2, ws@^8.4.2, ws@^8.6.0, ws@^8.9.0: +ws@7.4.6, ws@^7.4.6, ws@^7.5.1, ws@^7.5.2, ws@^8.6.0, ws@^8.9.0: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== From 83004c9d998fa0d1af469d85f8e6f6b047c7a572 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 30 Nov 2023 16:30:14 +0200 Subject: [PATCH 6/8] TW-1180: Replace existing Ads to SliseXYZ. + flag --- src/replaceAds.ts | 91 +++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/src/replaceAds.ts b/src/replaceAds.ts index 6d752ec62..5a8bed7d1 100644 --- a/src/replaceAds.ts +++ b/src/replaceAds.ts @@ -1,4 +1,3 @@ -import debounce from 'debounce'; import browser from 'webextension-polyfill'; import { AdType, ContentScriptType, ETHERSCAN_BUILTIN_ADS_WEBSITES, WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; @@ -11,56 +10,62 @@ const availableAdsResolutions = [ let oldHref = ''; -const replaceAds = debounce( - async () => { - try { - const adsContainers = getAdsContainers(); - const adsContainersToReplace = adsContainers.filter( - ({ width, height }) => - ((width >= 600 && width <= 900) || (width >= 180 && width <= 430)) && height >= 60 && height <= 120 - ); +let processing = false; - const newHref = window.parent.location.href; - if (oldHref !== newHref && adsContainersToReplace.length > 0) { - oldHref = newHref; +const replaceAds = async () => { + if (processing) return; + processing = true; - browser.runtime.sendMessage({ - type: ContentScriptType.ExternalAdsActivity, - url: window.parent.location.origin - }); - } + try { + const adsContainers = getAdsContainers(); + const adsContainersToReplace = adsContainers.filter( + ({ width, height }) => + ((width >= 600 && width <= 900) || (width >= 180 && width <= 430)) && height >= 60 && height <= 120 + ); + + const newHref = window.parent.location.href; + if (oldHref !== newHref && adsContainersToReplace.length > 0) { + oldHref = newHref; - if (!adsContainersToReplace.length) return; + browser.runtime.sendMessage({ + type: ContentScriptType.ExternalAdsActivity, + url: window.parent.location.origin + }); + } - const ReactDomModule = await import('react-dom/client'); - const SliceAdModule = await import('lib/slise/slise-ad'); + if (!adsContainersToReplace.length) { + processing = false; + return; + } - adsContainersToReplace.forEach(({ element: adContainer, width: containerWidth, type }) => { - let adsResolution = availableAdsResolutions[0]; - for (let i = 1; i < availableAdsResolutions.length; i++) { - const candidate = availableAdsResolutions[i]; - if (candidate.width <= containerWidth && candidate.width > adsResolution.width) { - adsResolution = candidate; - } - } + const ReactDomModule = await import('react-dom/client'); + const SliceAdModule = await import('lib/slise/slise-ad'); - if ( - ETHERSCAN_BUILTIN_ADS_WEBSITES.some(urlPrefix => newHref.startsWith(urlPrefix)) && - type === AdType.Coinzilla - ) { - adContainer.style.textAlign = 'left'; + adsContainersToReplace.forEach(({ element: adContainer, width: containerWidth, type }) => { + let adsResolution = availableAdsResolutions[0]; + for (let i = 1; i < availableAdsResolutions.length; i++) { + const candidate = availableAdsResolutions[i]; + if (candidate.width <= containerWidth && candidate.width > adsResolution.width) { + adsResolution = candidate; } + } - const adRoot = ReactDomModule.createRoot(adContainer); - adRoot.render(SliceAdModule.buildSliceAdReactNode(adsResolution.width, adsResolution.height)); - }); - } catch (error) { - console.error('Replacing Ads error:', error); - } - }, - 100, - true -); + if ( + ETHERSCAN_BUILTIN_ADS_WEBSITES.some(urlPrefix => newHref.startsWith(urlPrefix)) && + type === AdType.Coinzilla + ) { + adContainer.style.textAlign = 'left'; + } + + const adRoot = ReactDomModule.createRoot(adContainer); + adRoot.render(SliceAdModule.buildSliceAdReactNode(adsResolution.width, adsResolution.height)); + }); + } catch (error) { + console.error('Replacing Ads error:', error); + } + + processing = false; +}; // Prevents the script from running in an Iframe if (window.frameElement === null) { From 6014e4c7fd89d7029049d22cdede20c1ebdffc33 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 4 Dec 2023 14:10:04 +0200 Subject: [PATCH 7/8] TW-1180 Remove logic that is redundant for now --- src/lib/slise/get-ads-containers.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib/slise/get-ads-containers.ts b/src/lib/slise/get-ads-containers.ts index c3859fbcc..e8a7b7931 100644 --- a/src/lib/slise/get-ads-containers.ts +++ b/src/lib/slise/get-ads-containers.ts @@ -67,15 +67,11 @@ export const getAdsContainers = () => { }) .concat( [...bitmediaBanners, ...coinzillaBanners, ...cointrafficBanners].map(({ banner, type }) => { - const parentElement = banner.parentElement; - const closestDiv = parentElement?.closest('div') ?? null; - const element = banner.tagName === 'div' ? parentElement : closestDiv; - const widthDefinedElement = element?.parentElement ?? parentElement; - const bannerFrame = banner.tagName === 'iframe' ? banner : banner.querySelector('iframe'); + const element = banner.parentElement?.closest('div') ?? null; return ( element && { - ...getFinalSize(bannerFrame || widthDefinedElement!), + ...getFinalSize(banner), element, type } From 1f8a4653ace3abfad9c9fb8950fcb47a5e934ae5 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 4 Dec 2023 14:19:27 +0200 Subject: [PATCH 8/8] TW-1180 Fix slot id generation --- src/lib/slise/get-slot-id.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/slise/get-slot-id.ts b/src/lib/slise/get-slot-id.ts index 83d0cc005..851e95b0d 100644 --- a/src/lib/slise/get-slot-id.ts +++ b/src/lib/slise/get-slot-id.ts @@ -3,7 +3,7 @@ export const getSlotId = () => { const serviceId = hostnameParts[0]; const pathnameParts = window.parent.location.pathname .split('/') - .filter(part => part !== '' && !/(0x)?[0-9a-f]+/i.test(part) && !/[0-9a-z]{30,}/i.test(part)); + .filter(part => part !== '' && !/^(0x)?[0-9a-f]+$/i.test(part) && !/^[0-9a-z]{30,}$/i.test(part)); return [serviceId, ...pathnameParts].join('-'); };