Documentation Index Fetch the complete documentation index at: https://mintlify.com/botnadzor/extension/llms.txt
Use this file to discover all available pages before exploring further.
The insertion system is Botnadzor’s core mechanism for modifying the VK.com DOM to display bot detection UI elements. It uses a config-driven approach where insertion configurations are fetched from static lists and applied dynamically to matching elements.
Overview
The insertion system:
Monitors the DOM for elements matching insertion configs
Extracts data from markup (account names, avatars, identifiers)
Fetches service data (affiliations, registration dates)
Renders UI components (action bars, badges, highlights)
Manages lifecycle (mount, render, unmount)
Config-Driven Architecture
Insertions are defined as configuration objects stored in the insertions static list. Each config specifies:
Selector — CSS selector to find target elements
Variant — Type of insertion (account, comment, review, replyForm)
Markup Config — How to extract data and where to place UI
Applies To — Which VK variant (desktop/mobile)
Insertion Config Example
type InsertionConfig = {
id : string ;
disabled : boolean ;
variant : "account" | "comment" | "review" | "replyForm" ;
appliesTo : "desktopVkWebsite" | "mobileVkWebsite" | "desktopAndMobileVkWebsite" ;
selector : {
query : string ;
descendantMatcher ?: string ;
};
markup : {
data : { /* selectors for extracting data */ };
edits : { /* DOM modifications */ };
ui : { /* UI placement configs */ };
};
};
Insertion Variants
The system supports four insertion variants, each registered in src/entrypoints/content/insertion-variants.ts:
src/entrypoints/content/insertion-variants.ts
import type { InsertionVariant } from "@/shared/@model/insertion-configs" ;
import type { BaseInsertionVariantDefinition } from "./insertion-variant-typings" ;
import account from "./insertion-variants/=account" ;
import comment from "./insertion-variants/=comment" ;
import replyForm from "./insertion-variants/=reply-form" ;
import review from "./insertion-variants/=review" ;
export const insertionVariantLookup : Record <
InsertionVariant ,
BaseInsertionVariantDefinition
> = {
account ,
comment ,
replyForm ,
review ,
};
Account Variant
Used for user profile pages, account cards, and user lists.
Key Features:
Extracts account identifier, name, and avatar
Displays affiliation badges (bot/spam indicators)
Shows registration date
Provides action bar with inspector tools
src/entrypoints/content/insertion-variants/=account.ts
export default defineInsertionVariant <
AccountInsertionConfig ,
AccountInnerData ,
AccountMarkupData ,
AccountServiceData
> ({
defaultInnerData: {},
getMarkupData : async ({ config , instanceLogger , rootElement }) => {
const accountAvatarUrl = extractAccountAvatarUrlFromMarkup (
rootElement ,
config . markup . data . accountAvatar ,
);
const accountIdentifier = await extractAccountIdentifierFromMarkup (
rootElement ,
config . markup . data . accountIdentifier ,
);
const accountName = await extractAccountNameFromMarkup (
rootElement ,
config . markup . data . accountName ,
);
return {
accountAvatarUrl ,
accountIdentifier ,
accountName ,
};
},
getServiceData : async ({ markupData , serviceLookup }) => {
const [ accountAffiliation , frontendBaseUrl ] = await Promise . all ([
serviceLookup . affiliationService . checkAccount ( markupData . accountIdentifier ),
serviceLookup . frontendService . getBaseUrl (),
]);
return { accountAffiliation , frontendBaseUrl };
},
mount : ({ config , rootElement , serviceLookup , updateInnerData }) => {
const actionBarUi = mountUiWithActionBar ({
placement: config . markup . ui . actionBar ,
rootElement ,
serviceLookup ,
onRegDateInfoChange : ( regDateInfo ) => {
updateInnerData (( draft ) => {
if ( regDateInfo ) {
draft . regDateInfo = regDateInfo ;
} else {
delete draft . regDateInfo ;
}
});
},
});
return {
render : ({ innerData , markupData , serviceData }) => {
actionBarUi ?. render ({
accountAffiliation: serviceData . accountAffiliation ,
accountIdentifier: markupData . accountIdentifier ,
regDateInfo: innerData . regDateInfo ,
});
},
unmount : () => {
actionBarUi ?. unmount ();
},
};
},
});
Used for comments on posts, photos, videos, and wall posts.
Key Features:
Extracts comment identifier for tracking
Collects comment data for analysis
Displays affiliation indicators
Provides inspector trigger
src/entrypoints/content/insertion-variants/=comment.ts
render : ({ innerData , markupData , serviceData }) => {
// Collect comment for analysis
if ( markupData . commentIdentifier && ! derivedPageInfo . archivedSnapshot ) {
void serviceLookup . collectingService . collectCommentIfNeeded ({
wallVkId: markupData . commentIdentifier . wallVkId ,
postVkId: markupData . commentIdentifier . postVkId ,
commentVkId: markupData . commentIdentifier . commentVkId ,
commenterVkDomain: stringifyAccountIdentifier (
markupData . accountIdentifier ,
),
postCommentCount: undefined ,
});
}
actionBarUi ?. render ({
accountAffiliation: serviceData . accountAffiliation ,
inspectorTrigger: markupData . commentIdentifier
? { type: "comment" , ... markupData . commentIdentifier }
: undefined ,
regDateInfo: innerData . regDateInfo ,
});
};
Review Variant
Used for application and community reviews.
Used for comment reply forms.
Insertion Management
The startManagingInsertions function orchestrates the entire insertion lifecycle:
src/entrypoints/content/insertion-management.ts
export async function startManagingInsertions ({
archivedSnapshot ,
contentId ,
websiteVariant ,
} : DerivedPageInfo & { contentId : ContentId }) : Promise < void > {
// Inject insertion styles
const style = document . createElement ( "style" );
style . textContent = insertionStyling ;
document . head . append ( style );
const derivedPageInfo : DerivedPageInfo = { archivedSnapshot , websiteVariant };
const instanceMap : InsertionInstanceMap = new Map ();
let currentConfigs : InsertionConfig [] = [];
// Fetch configs from static lists
const initialItems = await staticListsService . getItems ( "insertions" );
const initialDxConfig = await dxConfigService . get ();
updateConfigs ( initialDxConfig . insertionsRemoved ? [] : initialItems );
// Mount initial insertions
mountNewInsertions ({
configs: currentConfigs ,
contentId ,
derivedPageInfo ,
instanceMap ,
});
// Observe DOM changes and mount new insertions
const mutationObserver = new MutationObserver (() => {
// Throttled to 100ms
mountNewInsertions ({
configs: currentConfigs ,
derivedPageInfo ,
instanceMap ,
contentId ,
});
});
mutationObserver . observe ( document . body , {
childList: true ,
subtree: true ,
});
// Poll for config changes
const stopPolling = startGlobalRerenderPolling ({
derivedPageInfo ,
instanceMap ,
contentId ,
getConfigs : () => currentConfigs ,
onConfigsChanged: updateConfigs ,
});
}
Tailwind bn: Prefix Convention
All Tailwind classes used in insertion UI components must use the bn: prefix.
This convention prevents style conflicts with VK.com’s existing styles:
// ✅ Correct: Use bn: prefix in insertions
const element = document . createElement ( "div" );
element . className = "bn:flex bn:items-center bn:gap-2 bn:px-3 bn:py-1.5" ;
// ❌ Wrong: Don't use unprefixed classes in insertions
element . className = "flex items-center gap-2 px-3 py-1.5" ;
Why the Prefix?
VK.com uses its own CSS framework with classes like flex, text-center, etc. Without prefixing, Botnadzor’s Tailwind classes would conflict with VK’s styles, causing layout issues.
Where to Use It
Insertion variants (src/entrypoints/content/insertion-variants/**) — Required
Popup UI (src/entrypoints/popup/**) — Not required (isolated context)
Inspector UI — Not required (shadow DOM isolation)
Configuration
The prefix is configured in tailwind.config.js:
module . exports = {
prefix: 'bn:' ,
// ... other config
};
UI Components
Insertion variants can mount several types of UI components:
Action Bar
Provides interactive buttons for:
Fetching registration dates
Triggering the inspector
Opening account profiles
Affiliation Badge
Displays a colored badge when an account is affiliated with bot/spam lists.
Affiliation Highlight
Highlights the entire element with a colored border based on affiliation.
Registration Date Display
Shows the VK registration date for an account.
Icons in Insertions
Import icons from lucide-static, not lucide-react.
Insertion UI is rendered using vanilla DOM manipulation, not React:
// ✅ Correct: Use lucide-static
import { Calendar } from "lucide-static" ;
const icon = document . createElement ( "div" );
icon . innerHTML = Calendar ;
// ❌ Wrong: Don't use lucide-react
import { Calendar } from "lucide-react" ;
return < Calendar />; // React components don't work in insertions
Class Utilities
Botnadzor provides two utility functions for managing classes:
cn() — Class Names
Combines class names with conditional logic:
import { cn } from "@/shared/tailwindcss-helpers" ;
const className = cn (
"bn:px-2 bn:py-1" ,
isActive && "bn:bg-blue-500" ,
"bn:rounded"
);
cnt() — Class Names with Tailwind prefix
Automatically adds the bn: prefix to unprefixed classes:
import { cnt } from "@/shared/tailwindcss-helpers" ;
// Automatically prefixes all classes
const className = cnt ( "flex items-center gap-2 px-3" );
// Result: "bn:flex bn:items-center bn:gap-2 bn:px-3"
Lifecycle Phases
1. Discovery
DOM observer detects elements matching insertion config selectors.
getMarkupData() extracts data from the DOM element using CSS selectors and React Fiber introspection.
3. Service Data Fetching
getServiceData() fetches data from background services (affiliation checks, base URLs).
4. Mounting
mount() creates UI components and attaches them to the DOM.
5. Rendering
render() updates UI components when data changes.
6. Unmounting
unmount() cleans up UI components when the target element is removed from the DOM.
Next Steps
Proxy Services Learn how insertions communicate with background services
Static Lists Understand how insertion configs are stored and fetched