VK Ads Fixes

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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)
})();