- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 520
- Reaction score
- 7
Found this consolidated Python build floating around the underground. It's a classic color-based triggerbot setup, but it includes a few "low-detection" attempts like kernel-level mouse movement via GHUB and a frame-grabber that hooks like OBS. The author claims it's "vibecoded," which is forum-speak for "I pasted this together and haven't tested it on a live Vanguard session yet."
Technical Architecture
This isn't your standard pyautogui trash. It attempts to bypass common capture and input detections using secondary DLLs. Here is the breakdown of the stack:
The Source Code
Safety & Deployment Notes
Let’s be real—running raw Python on Valorant is a one-way ticket to a HWID ban if you aren't careful. Use this as a base for learning how color bots handle frame slicing and kernel mouse offsets. If you want to actually use this live, you'll need to:
— Pack your scripts to avoid simple process name detection.
— Ensure your ghub_device.dll is clean and not a blacklisted public version.
— Consider running the capture on a second machine via DMA or a capture card if you want to stay UD.
Anyone actually tested these specific OBS hooks on the latest Vanguard build?
Technical Architecture
This isn't your standard pyautogui trash. It attempts to bypass common capture and input detections using secondary DLLs. Here is the breakdown of the stack:
- Screen Capture: Supports mss for borderless, but includes an OBSCapture class designed to use a GameCapture.dll to hook the game process directly—useful for exclusive fullscreen bypasses.
- Input Method: Utilizes ghub_device.dll to communicate with the Logitech G-Hub kernel driver. This is significantly safer than using mouse_event or SendInput, though Vanguard still flags certain GHUB communication patterns.
- Logic: Standard HSV (Hue, Saturation, Value) filtering combined with a circular FOV mask. It uses numpy for faster matrix operations and cv2 (OpenCV) for vectorized color thresholding.
- UI: Built on DearPyGui for a lightweight, hardware-accelerated menu.
If you're going to run this, you'll need the following overhead installed:
You also need the ghub_device.dll and GameCapture.dll in the root directory for the "cleaner" methods to function.
Code:
pip install numpy dearpygui opencv-python mss keyboard pyautogui pywin32
The Source Code
Code:
import os, sys, time, threading, colorsys, ctypes
from ctypes import *
import numpy as np
import dearpygui.dearpygui as dpg
# Optional imports with availability flags
try:
import cv2; HAS_CV2 = True
except ImportError: HAS_CV2 = False
try:
import win32api; HAS_WIN32 = True
except ImportError: HAS_WIN32 = False
try:
import win32gui; HAS_WIN32GUI = True
except ImportError: HAS_WIN32GUI = False
try:
import mss as mss_lib; HAS_MSS = True
except ImportError: HAS_MSS = False
try:
import keyboard as kb; HAS_KEYBOARD = True
except ImportError: HAS_KEYBOARD = False
try:
import pyautogui
pyautogui.PAUSE = 0
pyautogui.FAILSAFE = False
HAS_PYAUTOGUI = True
except ImportError: HAS_PYAUTOGUI = False
# Windows high-precision timer
try: ctypes.windll.winmm.timeBeginPeriod(1)
except: pass
# Screen center cached at startup
_user32 = ctypes.windll.user32
CENTER_X = _user32.GetSystemMetrics(0) // 2
CENTER_Y = _user32.GetSystemMetrics(1) // 2
SCREEN_W = _user32.GetSystemMetrics(0)
SCREEN_H = _user32.GetSystemMetrics(1)
# ─────────────────────────────────────────────
# GHUB KERNEL MOUSE DRIVER
# Drop ghub_device.dll next to this script.
# Much harder to detect than SendInput.
# ─────────────────────────────────────────────
class GHUBMouse:
def __init__(self):
self.found = False
dll_path = os.path.abspath("ghub_device.dll")
if not os.path.exists(dll_path):
print("[GHUB] ghub_device.dll not found — skipping.")
return
try:
self.lib = ctypes.CDLL(dll_path)
self.lib.device_open.restype = ctypes.c_int
self.lib.moveR.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_bool]
self.lib.moveR.restype = ctypes.c_int
if self.lib.device_open() == 0:
print("[GHUB] device_open() failed — is GHUB running?")
return
# Some builds expose mouse_up, some don't
self.has_mouse_up = False
try: _ = self.lib.mouse_up; self.has_mouse_up = True
except: pass
self.found = True
print("[GHUB] Kernel driver loaded.")
except Exception as e:
print(f"[GHUB] Load failed: {e}")
def click(self):
if not self.found: return False
try:
if self.has_mouse_up:
self.lib.mouse_down(1); time.sleep(0.01); self.lib.mouse_up(1)
else:
self.lib.mouse_down(1); time.sleep(0.01); self.lib.mouse_down(0)
return True
except Exception as e:
print(f"[GHUB] Click error: {e}"); return False
# ─────────────────────────────────────────────
# OBS GAME CAPTURE
# Drop GameCapture.dll next to this script.
# Works in exclusive fullscreen — hooks the
# game process directly like OBS does.
# Requires cv2.
# ─────────────────────────────────────────────
class GCImage(Structure):
_fields_ = [("width", c_int32),
("height", c_int32),
("pitch", c_int32),
("data", POINTER(c_uint8))]
class OBSCapture:
def __init__(self, window_name):
self.found = False
self.frame = None
dll_path = os.path.abspath("GameCapture.dll")
if not os.path.exists(dll_path):
print("[OBS] GameCapture.dll not found — falling back to mss.")
return
if not HAS_CV2:
print("[OBS] cv2 required for OBS capture — falling back to mss.")
return
if not HAS_WIN32GUI:
print("[OBS] pywin32 required for OBS capture.")
return
try:
self.lib = ctypes.WinDLL(dll_path)
self.lib.gc_create.argtypes = [c_int32, c_int32, c_int32, c_int32, c_char_p]
self.lib.gc_create.restype = c_void_p
self.lib.gc_get_frame.argtypes = [c_void_p, POINTER(GCImage)]
self.lib.gc_get_frame.restype = c_int32
self._img = GCImage()
hwnd = win32gui.FindWindow(None, window_name)
if hwnd == 0:
print(f"[OBS] Window '{window_name}' not found.")
return
self.handle = self.lib.gc_create(
SCREEN_W, SCREEN_H, SCREEN_W, SCREEN_H,
window_name.encode("utf-8")
)
if not self.handle:
print("[OBS] gc_create() failed.")
return
self.found = True
print("[OBS] Game capture online.")
except Exception as e:
print(f"[OBS] Init failed: {e}")
def get_frame(self):
"""Returns full BGRA numpy frame or None."""
if not self.found: return None
ok = self.lib.gc_get_frame(self.handle, byref(self._img))
if not ok or not self._img.data: return None
size = self._img.height * self._img.pitch
raw = string_at(self._img.data, size)
buf = np.frombuffer(raw, dtype=np.uint8).reshape((self._img.height, self._img.pitch))
return buf[:, :(self._img.width * 4)].reshape((self._img.height, self._img.width, 4))
# ─────────────────────────────────────────────
# SHARED STATE
# All config the GUI writes to, bot reads from.
# ─────────────────────────────────────────────
state = {
"running": False,
"fov_radius": 150, # pixels from center to edge of scan circle
"reaction_ms": 0, # artificial delay before firing (ms)
"trigger_key": "space", # keyboard key OR "MOUSE1"
"cooldown": 0.05, # min seconds between triggers
"hue_low": 277.0, # degrees 0-360
"hue_high": 320.0,
"sat_low": 0.320, # 0.0-1.0
"sat_high": 0.700,
"val_low": 106.0, # 0-255
"val_high": 255.0,
"use_ghub": False, # set True automatically if DLL found
"use_obs": False,
"obs_window": "",
"log": "Ready.",
}
state_lock = threading.Lock()
stop_event = threading.Event()
# Hardware singletons
ghub = GHUBMouse()
obs_capture: OBSCapture = None # created when obs_window is set
if ghub.found:
with state_lock:
state["use_ghub"] = True
# ─────────────────────────────────────────────
# INPUT FIRE
# Handles MOUSE1 vs keyboard keys,
# GHUB vs fallback backends.
# ─────────────────────────────────────────────
def fire_trigger():
with state_lock:
key = state["trigger_key"]
use_ghub = state["use_ghub"]
if key.upper() == "MOUSE1":
if use_ghub and ghub.found:
ghub.click()
elif HAS_WIN32:
# win32api fallback
win32api.mouse_event(0x0002, 0, 0) # MOUSEEVENTF_LEFTDOWN
time.sleep(0.01)
win32api.mouse_event(0x0004, 0, 0) # MOUSEEVENTF_LEFTUP
elif HAS_PYAUTOGUI:
pyautogui.click()
else:
if HAS_KEYBOARD:
kb.send(key)
elif HAS_PYAUTOGUI:
pyautogui.press(key)
# ─────────────────────────────────────────────
# BOT LOOP
# Runs on background thread.
# Captures FOV region, checks HSV circle mask,
# applies reaction delay, fires.
# ─────────────────────────────────────────────
def bot_loop():
last_trigger = 0.0
cached_fov = -1
circle_mask = None
with mss_lib.mss() as sct:
while not stop_event.is_set():
# Read state once per frame
with state_lock:
running = state["running"]
cooldown = state["cooldown"]
reaction_ms = state["reaction_ms"]
fov_r = state["fov_radius"]
use_obs = state["use_obs"]
hl = state["hue_low"] / 360.0
hh = state["hue_high"] / 360.0
sl = state["sat_low"]
sh = state["sat_high"]
vl = state["val_low"] / 255.0
vh = state["val_high"] / 255.0
if not running:
time.sleep(0.01)
continue
# Recompute circular mask only when FOV radius changes
if fov_r != cached_fov:
size = fov_r * 2
Y, X = np.ogrid[:size, :size]
circle_mask = (np.sqrt((X - fov_r)**2 + (Y - fov_r)**2) <= fov_r)
cached_fov = fov_r
size = fov_r * 2
# ── Capture ──────────────────────────────────────
img = None
if use_obs and obs_capture and obs_capture.found:
# OBS path: full frame, slice FOV region from center
full = obs_capture.get_frame()
if full is not None:
y1 = max(0, CENTER_Y - fov_r)
x1 = max(0, CENTER_X - fov_r)
img = full[y1:y1+size, x1:x1+size]
if img is None:
# mss fallback (borderless/windowed fullscreen)
region = {
"top": CENTER_Y - fov_r,
"left": CENTER_X - fov_r,
"width": size,
"height": size,
}
try:
shot = sct.grab(region)
img = np.frombuffer(shot.bgra, dtype=np.uint8).reshape((size, size, 4))
except Exception as e:
set_log(f"Capture error: {e}")
time.sleep(0.1)
continue
# ── Color detection ───────────────────────────────
# img is always BGRA shaped (H, W, 4)
detected = False
if HAS_CV2:
# Fast path: vectorised HSV conversion via cv2
# cv2 HSV: H→0-180, S→0-255, V→0-255
bgr = img[:, :, :3]
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
lo = np.array([hl * 180, sl * 255, vl * 255], dtype=np.uint8)
hi = np.array([hh * 180, sh * 255, vh * 255], dtype=np.uint8)
cmask = cv2.inRange(hsv, lo, hi)
detected = bool(np.any(circle_mask & (cmask > 0)))
else:
# Slow path: per-pixel colorsys (no cv2 installed)
rgb = img[:, :, [2, 1, 0]] # BGRA → RGB
pixels = np.argwhere(circle_mask)
for (py, px) in pixels:
r, g, b = rgb[py, px] / 255.0
h, s, v = colorsys.rgb_to_hsv(r, g, b)
if hl <= h <= hh and sl <= s <= sh and vl <= v <= vh:
detected = True
break
# ── Fire ─────────────────────────────────────────
now = time.perf_counter()
if detected and (now - last_trigger >= cooldown):
if reaction_ms > 0:
time.sleep(reaction_ms / 1000.0)
fire_trigger()
last_trigger = time.perf_counter()
set_log(f"Triggered at {time.strftime('%H:%M:%S')}")
def set_log(msg):
with state_lock:
state["log"] = msg
try:
dpg.set_value("log_text", msg)
except: pass
# ─────────────────────────────────────────────
# GUI CALLBACKS
# ─────────────────────────────────────────────
def toggle_bot():
with state_lock:
state["running"] = not state["running"]
is_on = state["running"]
dpg.set_value("status_text", "● ACTIVE" if is_on else "● INACTIVE")
dpg.configure_item("status_text", color=[0, 220, 80] if is_on else [220, 60, 60])
dpg.set_item_label("toggle_btn", "Stop Bot" if is_on else "Start Bot")
def update_state(sender, app_data, user_data):
with state_lock:
state[user_data] = app_data
def on_hsv_change(sender, app_data, user_data):
update_state(sender, app_data, user_data)
update_preview()
def update_preview():
with state_lock:
h = ((state["hue_low"] + state["hue_high"]) / 2.0) / 360.0
s = (state["sat_low"] + state["sat_high"]) / 2.0
v = (state["val_low"] + state["val_high"]) / 2.0 / 255.0
r, g, b = colorsys.hsv_to_rgb(h, s, v)
dpg.set_value("color_preview", [int(r*255), int(g*255), int(b*255), 255])
def apply_obs_window(sender, app_data, user_data):
global obs_capture
window_name = app_data.strip()
with state_lock:
state["obs_window"] = window_name
if window_name:
obs_capture = OBSCapture(window_name)
with state_lock:
state["use_obs"] = obs_capture.found
dpg.set_value("obs_status",
"[✓] OBS capture active" if obs_capture.found
else "[✗] Window not found — using mss")
dpg.configure_item("obs_status",
color=[0,200,80] if obs_capture.found else [200,100,60])
# ─────────────────────────────────────────────
# GUI BUILD
# ─────────────────────────────────────────────
def build_gui():
dpg.create_context()
dpg.create_viewport(title="Triggerbot", width=450, height=680, resizable=False)
dpg.setup_dearpygui()
with dpg.theme() as global_theme:
with dpg.theme_component(dpg.mvAll):
dpg.add_theme_color(dpg.mvThemeCol_WindowBg, [18, 20, 28])
dpg.add_theme_color(dpg.mvThemeCol_FrameBg, [30, 33, 45])
dpg.add_theme_color(dpg.mvThemeCol_FrameBgHovered, [40, 44, 60])
dpg.add_theme_color(dpg.mvThemeCol_SliderGrab, [90, 150, 255])
dpg.add_theme_color(dpg.mvThemeCol_Button, [50, 80, 160])
dpg.add_theme_color(dpg.mvThemeCol_ButtonHovered, [70, 110, 210])
dpg.add_theme_color(dpg.mvThemeCol_Header, [40, 60, 120])
dpg.add_theme_color(dpg.mvThemeCol_HeaderHovered, [55, 80, 150])
dpg.add_theme_color(dpg.mvThemeCol_Text, [210, 215, 230])
dpg.add_theme_color(dpg.mvThemeCol_CheckMark, [90, 150, 255])
dpg.add_theme_style(dpg.mvStyleVar_FrameRounding, 6)
dpg.add_theme_style(dpg.mvStyleVar_GrabRounding, 4)
dpg.add_theme_style(dpg.mvStyleVar_WindowPadding, 12, 12)
dpg.add_theme_style(dpg.mvStyleVar_ItemSpacing, 8, 6)
dpg.bind_theme(global_theme)
with dpg.window(label="Triggerbot", tag="main_window",
no_close=True, no_move=True, no_resize=True,
width=450, height=680, pos=[0, 0]):
# ── Status ───────────────────────────────────────────
with dpg.group(horizontal=True):
dpg.add_text("● INACTIVE", tag="status_text", color=[220, 60, 60])
dpg.add_spacer(width=10)
dpg.add_text("Ready.", tag="log_text", color=[120, 125, 140])
dpg.add_separator()
dpg.add_spacer(height=4)
dpg.add_button(label="Start Bot", tag="toggle_btn",
callback=toggle_bot, width=-1, height=36)
dpg.add_spacer(height=6)
# ── Detection ────────────────────────────────────────
with dpg.collapsing_header(label="Detection", default_open=True):
dpg.add_spacer(height=2)
dpg.add_text(f"Screen center: ({CENTER_X}, {CENTER_Y})",
color=[120, 125, 140])
dpg.add_slider_int(
label="FOV Radius (px)",
default_value=state["fov_radius"],
min_value=5, max_value=600, width=220,
callback=update_state, user_data="fov_radius")
dpg.add_slider_int(
label="Reaction Delay (ms)",
default_value=state["reaction_ms"],
min_value=0, max_value=500, width=220,
callback=update_state, user_data="reaction_ms",
format="%d ms")
dpg.add_spacer(height=4)
# ── Trigger ──────────────────────────────────────────
with dpg.collapsing_header(label="Trigger", default_open=True):
dpg.add_spacer(height=2)
dpg.add_text("Key options: 'space', 'f', 'MOUSE1', 'ctrl', etc.",
color=[120, 125, 140])
dpg.add_input_text(
label="Trigger Key",
default_value=state["trigger_key"], width=130,
callback=update_state, user_data="trigger_key")
dpg.add_slider_float(
label="Cooldown (s)",
default_value=state["cooldown"],
min_value=0.01, max_value=2.0, width=220,
callback=update_state, user_data="cooldown",
format="%.3f s")
dpg.add_spacer(height=4)
# ── Hardware ─────────────────────────────────────────
with dpg.collapsing_header(label="Hardware", default_open=True):
dpg.add_spacer(height=2)
# GHUB
ghub_ok = ghub.found
ghub_label = "[✓] ghub_device.dll loaded" if ghub_ok else "[✗] ghub_device.dll not found"
dpg.add_text(ghub_label, color=[0,200,80] if ghub_ok else [180,60,60])
dpg.add_checkbox(
label="Use GHUB driver for MOUSE1",
default_value=ghub_ok,
callback=update_state, user_data="use_ghub")
dpg.add_spacer(height=6)
# OBS
obs_dll_ok = os.path.exists("GameCapture.dll")
obs_label = "[✓] GameCapture.dll found" if obs_dll_ok else "[✗] GameCapture.dll not found"
dpg.add_text(obs_label, color=[0,200,80] if obs_dll_ok else [180,60,60])
dpg.add_text("Enter game window title to enable OBS capture:",
color=[150,155,170])
dpg.add_input_text(
label="Window Name", hint="e.g. Counter-Strike 2",
default_value=state["obs_window"], width=240,
callback=apply_obs_window, user_data="obs_window",
on_enter=True)
dpg.add_text("[mss fallback active]", tag="obs_status",
color=[120,125,140])
dpg.add_spacer(height=4)
# ── HSV Color Range ──────────────────────────────────
with dpg.collapsing_header(label="HSV Color Range", default_open=True):
dpg.add_spacer(height=2)
dpg.add_text("Hue (0 – 360°)")
with dpg.group(horizontal=True):
dpg.add_slider_float(label="Low ##h",
default_value=state["hue_low"],
min_value=0, max_value=360, width=155,
callback=on_hsv_change, user_data="hue_low", format="%.1f°")
dpg.add_slider_float(label="High##h",
default_value=state["hue_high"],
min_value=0, max_value=360, width=155,
callback=on_hsv_change, user_data="hue_high", format="%.1f°")
dpg.add_text("Sat (0.0 – 1.0)")
with dpg.group(horizontal=True):
dpg.add_slider_float(label="Low ##s",
default_value=state["sat_low"],
min_value=0.0, max_value=1.0, width=155,
callback=on_hsv_change, user_data="sat_low", format="%.3f")
dpg.add_slider_float(label="High##s",
default_value=state["sat_high"],
min_value=0.0, max_value=1.0, width=155,
callback=on_hsv_change, user_data="sat_high", format="%.3f")
dpg.add_text("Val (0 – 255)")
with dpg.group(horizontal=True):
dpg.add_slider_float(label="Low ##v",
default_value=state["val_low"],
min_value=0, max_value=255, width=155,
callback=on_hsv_change, user_data="val_low", format="%.0f")
dpg.add_slider_float(label="High##v",
default_value=state["val_high"],
min_value=0, max_value=255, width=155,
callback=on_hsv_change, user_data="val_high", format="%.0f")
dpg.add_spacer(height=6)
with dpg.group(horizontal=True):
dpg.add_text("Preview (HSV midpoint):")
dpg.add_color_button(tag="color_preview",
default_value=[128, 0, 200, 255],
width=24, height=24)
update_preview()
dpg.add_spacer(height=4)
dpg.set_primary_window("main_window", True)
# ─────────────────────────────────────────────
# ENTRY
# ─────────────────────────────────────────────
if __name__ == "__main__":
missing = []
if not HAS_MSS: missing.append("mss")
if not HAS_KEYBOARD: missing.append("keyboard")
if missing:
print(f"[ERROR] Missing packages: {', '.join(missing)}")
print(f"Run: pip install {' '.join(missing)}")
sys.exit(1)
if not HAS_CV2:
print("[WARN] cv2 not installed — color detection will be slower.")
print(" Install with: pip install opencv-python")
threading.Thread(target=bot_loop, daemon=True).start()
build_gui()
dpg.show_viewport()
dpg.start_dearpygui()
stop_event.set()
dpg.destroy_context()
Safety & Deployment Notes
Let’s be real—running raw Python on Valorant is a one-way ticket to a HWID ban if you aren't careful. Use this as a base for learning how color bots handle frame slicing and kernel mouse offsets. If you want to actually use this live, you'll need to:
— Pack your scripts to avoid simple process name detection.
— Ensure your ghub_device.dll is clean and not a blacklisted public version.
— Consider running the capture on a second machine via DMA or a capture card if you want to stay UD.
Anyone actually tested these specific OBS hooks on the latest Vanguard build?