VK Ads Fixes

Script for blocking ads in VK (VKontakte), bypassing blocking detection, etc.

目前為 2024-11-23 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name      VK Ads Fixes
// @name:ru      Правки рекламы ВКонтакте
// @name:uk      Правки реклами ВКонтакте
// @namespace    https://vtosters.app/
// @version      2.2
// @description Script for blocking ads in VK (VKontakte), bypassing blocking detection, etc.
// @description:ru Скрипт для блокировки рекламы в VK (ВКонтакте), обхода обнаружения блокировки и т.д.
// @description:uk Скрипт для блокування реклами у VK (ВКонтакті), обходу виявлення блокування тощо.
// @author       gdlbo, Vologhat
// @match        https://vk.com/*
// @match        https://vk.ru/*
// @grant        none
// ==/UserScript==

(() => {
    "use strict"

    class VkPeProperties
    {
        static #KEYS=[
            "send_user_info_stats", "force_send_user_info", "send_user_info_on_localhost",
            "send_navigation_stats_in_spa", "log_send_user_info_errors", "web_mytracker_collect_post_stats",
            "web_stats_device_id", "web_stats_reduce_debounce", "web_stats_send_beacon",
            "web_stats_send_on_events_limit", "web_stats_transport_story_view", "sentry_js_web_request_timeouts_feature",
            "sentry_js_web_request_timeouts_forwarding", "sentry_js_web_timeouts_forwarding", "sentry_js_web_verbose",
            "sentry_log_network_errors", "ads_app_form_link_redirect", "ads_autopromotion_web_geo",
            "ads_easy_promote_goods_new_create_api", "ads_light_methods_protection", "ads_market_autopromotion_bookmarks_stats",
            "ads_use_vk_community_video_portrait_4_5", "clips_web_my_tracker", "feed_post_track_code_client_web",
            "games_send_track_visitor_activity", "js_errors_no_write_uncaught_errors", "tgb_adblock_protection",
            "post_adblock_protection_promo", "eager_error_monitoring", "mini_apps_performance_close_app_empty_event",
            "mini_apps_performance_iframe_errors", "mini_apps_performance_web", "mini_apps_send_my_tracker_activity",
            "post_click_analytics_int_ext_link_click_web", "posting_track_event_count", "unique_adblock_users",
            "audio_my_tracker_web", "mini_apps_send_stat_arguments_bridge_events_sdk", "ajax_request_parse_html_error",
            "js_errors_no_write_uncaught_errors", "tns_track_sections", "tns_track_hosts", "geminus_counter",
            "ads_pixels_track_new_events_web_mvk", "web_navigation_handlers", "measure_module_navigation_stats",
            "group_join_track_event_count", "feed_content_events_open_post_event_web", "feed_posts_duration_stats_fix",
            "collect_unsupported_user_info_stats", "log_fetch_requests", "log_fetch_requests_get",
            "post_adguard_protection_promo", "extended_ajax_logging", "messenger_mediascope_stats_collect",
            "audio_player_stats_web"
        ];

        static isValidProperty(key)
        { return key in this.#KEYS }
    }

    //hook vk
    let actualVk=window.vk
    const vkHooks=new Set();
    Object.defineProperty(window,"vk", {
        get:() => actualVk,
        set:(value) => {
            actualVk=value
            if(actualVk)vkHooks.forEach( hook => hook(actualVk))
            return true
        },
        configurable:true
    })

    //hook vk properties
    const createAndPerformVkPropHook=(key,hookfn) => {
        //hook when vk redefines
        vkHooks.add(vk => {
            if(vk&&vk[key])hookfn(vk[key])
            
            //hook when property redefines
            let actualProp=vk[key]
            Object.defineProperty(vk,key, {
                get:() => actualProp,
                set:(value) => {
                    actualProp=value
                    hookfn(actualProp)
                    return true
                },
                configurable:true,
                enumerable:true
            })
        })
    }

    //remove properties from vk.pe hook
    createAndPerformVkPropHook("pe",pe => {
        console.log("patch pe")
        
        Object.keys(pe)
            .filter(key => VkPeProperties.isValidProperty(key))
            .forEach(key => delete window.vk.pe[key])
    })

    //patch vk.AudioAdsConfig hook
    createAndPerformVkPropHook("audioAdsConfig",audioAdsConfig => {
        console.log("patch audioAdsConfig")
        
        Object.defineProperties(audioAdsConfig, {
            enabled: {
                value:false
            },
            day_limit_reached: {
                value:false
            },
            sections: {
                value:[]
            }
        })
    })

    //disable error monitor DSN hook
    createAndPerformVkPropHook("cfg",cfg => {
        console.log("disable error monitoring DSN")
        
        Object.defineProperty(cfg.error_monitoring_config,"dsn", {
            value:"http://127.0.0.1"
        })
    })

    //patch vk.adParams hook
    createAndPerformVkPropHook("adParams",adParams => {
        console.log("patch adParams")
  
        Object.defineProperties(adParams, {
            ads_can_show: {
                value:0
            },
            leftads: {
                value:""
            },
            wsTransport: {
                value:"http://127.0.0.1"
            },
            ads_rotate_interval: {
                vallue:Number.MAX_SAFE_INTEGER
            }
        })
    })

    //trigger vk and properties hooks
    window.vk=window.vk

    setInterval(() => {
        //replace "away.php" url to the redirect url
        document.querySelectorAll("a[href*='away.php']")
            .forEach(a => {
                const url=URL.parse(a.href)
                if(!url.pathname.endsWith("away.php"))return
                //find a search parameter with valid redirect url
                url.searchParams.forEach((value) => {
                    if(URL.canParse(value))
                    {
                        a.href=value
                        return
                    }
                })
            })

        //remove _ads_block_data_w blocks
        document.querySelectorAll("div._ads_block_data_w")
            .forEach(div => {
                console.log(`Remove block with id ${div.id}]`)
                div.remove()
            })

        //remove ads_left block
        const adsLeft=document.getElementById("ads_left")
        if(adsLeft)
        {
            console.log("Remove block with id: ads_left")
            adsLeft.remove()
        }

        //clear all AdsLight functions
        if(window.AdsLight)
            Object.keys(window.AdsLight)
                .filter(key => typeof window.AdsLight[key]==="function")
                .forEach(key => Object.defineProperty(window.AdsLight,key, {
                    value:() => {}
                }))

        //patch window ads properties
        Object.defineProperties(window, {
            noAds: {
                value:true,
                configurable:true
            },
            noAdsAtAll: {
                value:true,
                configurable:true
            },
            no_left_ads: {
                value:true,
                configurable:true
            },
            no_ads: {
                value:true,
                configurable:true
            },
            isNoAdsForce: {
                value:true,
                configurable:true
            },
            hide_ad: {
                value:true,
                configurable:true
            },
            ya_direct: {
                value:false,
                configurable:true
            },
            yaDIrectAdActive: {
                value:false,
                configurable:true
            },
            __adsSet: {
                value:() => {},
                configurable:true
            },
            __adsUpdate: {
                value:() => {},
                configurable:true
            },
            AdmanHTML: {
                value:false,
                configurable:true
            },
            audioAdsConfig: {
                value:false,
                configurable:true
            },
            __adsGetAjaxParams: {
                value:() => {},
                configurable:true
            },
            __adsLoader: {
                value:() => {},
                configurable:true
            },
        })
    },30000/navigator.hardwareConcurrency)
})();