Uhmegle bypasses, uhmegle no camera, uhmegle black screen bypass, uhmegle hack
// ==UserScript==
// @name foenemegle - uhmegle bypasses & patches
// @namespace http://tampermonkey.net/
// @version 5.7
// @description
// @author
// @match https://uhmegle.com/video*
// @license MIT
// @grant none
// @run-at document-start
// @description Uhmegle bypasses, uhmegle no camera, uhmegle black screen bypass, uhmegle hack
// ==/UserScript==
(() => {
'use strict';
const CONFIG = {
imageURL: 'https://i.imgur.com/ghjBrek.png',
fallbackImageURL: 'https://i.imgur.com/ghjBrek.png',
canvasSize: {
width: 640,
height: 480
},
spoofedDevices: {
video: {
deviceId: "uf5_vid_" + Math.random().toString(36).substr(2, 9),
kind: "videoinput",
label: "HD Pro Webcam C920",
groupId: "uf5_group_vid"
},
audioIn: {
deviceId: "uf5_aud_in_" + Math.random().toString(36).substr(2, 9),
kind: "audioinput",
label: "Default - Microphone (Realtek Audio)",
groupId: "uf5_group_aud"
},
},
scriptVersion: "V5.7",
debugMode: false,
clippingEnabled: true,
defaultClipDuration: 30,
clipLayout: 'side-by-side',
clipMimeType: 'video/webm; codecs=vp9,opus',
maxHistory: 20,
};
let _audioconfig = {
rawMode: true
};
let scriptState = {
logs: [],
imstore: {},
fakeFrameCanvas: null,
perturbedCanvas: null,
realDevices: [],
managementUI: null,
originalMethods: {},
bypassStatus: {
deviceSpoof: false,
cameraBypass: false,
afkBypass: false,
fingerprintBypass: false,
websocketIntercept: false,
broadcastChannel: false,
faceDetection: false,
varianceBypass: false
},
isInitialized: false,
deviceSpoofEnabled: false,
forceRelay: true,
removeWatermark: true,
clippingManager: null,
savedConnections: [],
currentPartnerInfo: {
ip: null,
type: null
},
csesh: null,
resetPartnerInfo: () => {
log('Resetting current partner info.', 'debug');
scriptState.currentPartnerInfo = {
ip: null,
type: null
};
}
};
const log = (message, type = "info", context = null) => {
if (!CONFIG.debugMode && type === "debug") return;
const timestamp = new Date().toLocaleTimeString();
const logEntry = {
timestamp,
message: String(message),
type,
context
};
scriptState.logs.push(logEntry);
if (scriptState.logs.length > 200) scriptState.logs.shift();
const prefix = `[U-Fix ${CONFIG.scriptVersion}]`;
const style = {
error: 'color: #F44336; font-weight: bold;',
warn: 'color: #FF9800;',
success: 'color: #4CAF50; font-weight: bold;',
info: 'color: #2196F3;',
debug: 'color: #9E9E9E; font-style: italic;'
}[type] || '';
console.log(`%c${prefix} ${message}`, style, context || '');
if (scriptState.managementUI?.isVisible) scriptState.managementUI.updateLogs();
};
const createNativeWrapper = (originalFunc, newFunc) => {
const wrapper = function (...args) {
return newFunc.call(this, originalFunc, ...args);
};
Object.defineProperty(wrapper, 'toString', {
value: () => Function.prototype.toString.call(originalFunc),
enumerable: false,
configurable: true
});
return wrapper;
};
const ICONS = {
close: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
status: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path><path d="m9 12 2 2 4-4"></path></svg>`,
devices: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="15" rx="2" ry="2"></rect><polyline points="17 2 12 7 7 2"></polyline></svg>`,
history: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>`,
power: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m13 2-3 14h3l-3 8 11-14h-5l3-8z"></path></svg>`,
settings: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 0 2l-.15.08a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.38a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1 0-2l.15-.08a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path><circle cx="12" cy="12" r="3"></circle></svg>`,
logs: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>`,
info: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>`,
flipCam: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 2.7A2 2 0 0 1 3.5 1h17a2 2 0 0 1 2 2v18a2 2 0 0 1-2 2H3.5a2 2 0 0 1-2-1.3"></path><path d="M15 6.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"></path><path d="m13.3 16.5-.4-2.5h-2l-.4 2.5"></path><path d="M12 14v-2"></path><path d="M8.5 2v20"></path><path d="m16 9 3-3-3-3"></path></svg>`,
clapper: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.2 6 3.8 6.8A2 2 0 0 0 2 8.75v10.5A2 2 0 0 0 3.8 21.1l16.4-.8a2 2 0 0 0 1.8-1.95V8a2 2 0 0 0-1.8-2Z"/><path d="m.9 5.2 3.2-1.6 16.5.9-3.3 1.5Z"/><path d="m8 13 4-2.5"/><path d="m12.5 16 -5-2.5"/><path d="m8 18 4-2.5"/></svg>`,
download: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`,
trash: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>`,
globe: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>`,
zap: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22"/></svg>`,
};
class PeerConnectionInterceptor {
static #POLL_INTERVAL_MS = 100;
static #POLL_TIMEOUT_MS = 5000;
static #extractPartnerInfoFromStats(stats) {
for (const stat of stats.values()) {
if (stat.type === 'candidate-pair' && stat.state === 'succeeded' && stat.remoteCandidateId) {
const remoteCandidate = stats.get(stat.remoteCandidateId);
if (remoteCandidate && remoteCandidate.ip) {
return {
ip: remoteCandidate.ip,
type: remoteCandidate.candidateType
};
}
}
}
return null;
}
static #waitForSessionAndAssignIP(partnerInfo) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const intervalId = setInterval(() => {
if (Date.now() - startTime > this.#POLL_TIMEOUT_MS) {
clearInterval(intervalId);
log(`Timed out after ${this.#POLL_TIMEOUT_MS / 1000}s waiting for session to assign IP.`, 'warn');
reject(new Error('Session assignment timed out.'));
return;
}
const currentSession = window.savedConnections?.find(s => s.connid === window.connectionID);
if (currentSession) {
clearInterval(intervalId);
if (!currentSession.ip) {
currentSession.ip = {
...partnerInfo
};
}
log(`Assigned IP info to session: ${currentSession.connid}`, 'debug');
if (scriptState.managementUI?.isVisible) {
scriptState.managementUI.updateConnectionHistory();
}
resolve();
}
}, this.#POLL_INTERVAL_MS);
});
}
static initialize() {
if (window.RTCPeerConnection.isPatchedByUFix) return;
const OriginalRTCPeerConnection = window.RTCPeerConnection;
window.RTCPeerConnection = function (...args) {
const pc = new OriginalRTCPeerConnection(...args);
pc.addEventListener('connectionstatechange', async () => {
if (pc.connectionState === 'connected') {
try {
const stats = await pc.getStats();
const partnerInfo = PeerConnectionInterceptor.#extractPartnerInfoFromStats(stats);
if (partnerInfo) {
scriptState.currentPartnerInfo = partnerInfo;
log(`Partner connected: IP=${partnerInfo.ip}, Type=${partnerInfo.type}`, 'success');
await PeerConnectionInterceptor.#waitForSessionAndAssignIP(partnerInfo);
}
} catch (e) {
log('Error processing partner connection info.', 'warn', e);
}
}
});
pc.addEventListener('track', (event) => {
if (event.track.kind === 'video') {
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo.srcObject !== event.streams[0]) {
remoteVideo.srcObject = event.streams[0];
log('Remote video stream attached.', 'info');
}
remoteVideo.addEventListener('playing', () => {
}, {
once: true
});
}
});
return pc;
};
window.RTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype;
window.RTCPeerConnection.isPatchedByUFix = true;
scriptState.bypassStatus.peerConnectionIntercept = true;
log('PeerConnection Interceptor Initialized.', 'success');
}
}
class ClippingManager {
constructor() {
this.activeRecorder = null;
this.activeAnimationId = null;
this.activeAudioContext = null;
}
stopCurrentRecording() {
if (this.activeRecorder?.state === 'recording') {
log('Stopping active recording session.', 'debug');
this.activeRecorder.stop();
}
if (this.activeAnimationId) cancelAnimationFrame(this.activeAnimationId);
if (this.activeAudioContext?.state !== 'closed') this.activeAudioContext.close();
this.activeRecorder = this.activeAnimationId = this.activeAudioContext = null;
}
startNewRecording(sessionId) {
if (!CONFIG.clippingEnabled) return;
if (this.activeRecorder) {
this.stopCurrentRecording();
}
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
if (!localVideo?.srcObject || !remoteVideo?.srcObject) {
log('Cannot start recording, streams not ready.', 'warn');
return;
}
try {
const canvas = document.createElement('canvas');
canvas.width = CONFIG.clipLayout === 'remote-only' ? 640 : 1280;
canvas.height = 480;
const ctx = canvas.getContext('2d');
this.activeAudioContext = new AudioContext();
const audioDestination = this.activeAudioContext.createMediaStreamDestination();
[localVideo, remoteVideo].forEach(v => {
v.srcObject.getAudioTracks().forEach(track => {
const source = this.activeAudioContext.createMediaStreamSource(new MediaStream([track.clone()]));
source.connect(audioDestination);
});
});
const canvasStream = canvas.captureStream(30);
audioDestination.stream.getAudioTracks().forEach(track => canvasStream.addTrack(track));
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(canvasStream, {
mimeType: CONFIG.clipMimeType
});
this.activeRecorder = mediaRecorder;
const updateCanvasFrame = () => {
if (mediaRecorder.state !== 'recording') return;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (CONFIG.clipLayout === 'side-by-side') {
if (localVideo.videoWidth > 0) ctx.drawImage(localVideo, 0, 0, canvas.width / 2, canvas.height);
if (remoteVideo.videoWidth > 0) ctx.drawImage(remoteVideo, canvas.width / 2, 0, canvas.width / 2, canvas.height);
} else {
if (remoteVideo.videoWidth > 0) ctx.drawImage(remoteVideo, 0, 0, canvas.width, canvas.height);
}
this.activeAnimationId = requestAnimationFrame(updateCanvasFrame);
};
mediaRecorder.ondataavailable = e => {
if (e.data.size > 0) {
recordedChunks.push(e.data);
const maxChunks = CONFIG.defaultClipDuration + 5;
if (recordedChunks.length > maxChunks) recordedChunks.splice(0, recordedChunks.length - maxChunks);
}
};
mediaRecorder.onstop = () => {
const session = window.savedConnections.find(s => s.connid === sessionId);
if (!session) return;
if (recordedChunks.length > 0) {
const finalBlob = new Blob(recordedChunks, {
type: mediaRecorder.mimeType
});
if (session.clip.url) URL.revokeObjectURL(session.clip.url);
session.clip = {
blob: finalBlob,
url: URL.createObjectURL(finalBlob),
status: 'available',
timestamp: new Date()
};
log(`Clip saved for session ${session.connid}. Size: ${(finalBlob.size / 1024 / 1024).toFixed(2)} MB`, 'success');
} else {
session.clip.status = 'none';
log(`No data recorded for session ${sessionId}, clip discarded.`, 'warn');
}
canvasStream.getTracks().forEach(t => t.stop());
if (scriptState.managementUI) scriptState.managementUI.updateConnectionHistory();
};
mediaRecorder.start(1000);
this.activeAnimationId = requestAnimationFrame(updateCanvasFrame);
log(`Rolling clip buffer started for session ${sessionId}.`, 'success');
} catch (e) {
log('Failed to start new recording session.', 'error', e);
this.stopCurrentRecording();
}
}
}
class SessionDataCollector {
static initialize() {
log('Session Data Collector starting...', 'info');
const observer = new MutationObserver((mutations, obs) => {
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo && !remoteVideo.isHookedByUFix) {
this.augmentMetadataHook(remoteVideo);
remoteVideo.isHookedByUFix = true;
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
static augmentMetadataHook(remoteVideo) {
log('Remote video found. Augmenting onloadedmetadata.', 'debug');
const originalOnLoadedMetadata = remoteVideo.onloadedmetadata;
remoteVideo.onloadedmetadata = (...args) => {
if (typeof originalOnLoadedMetadata === 'function') originalOnLoadedMetadata.apply(remoteVideo, args);
setTimeout(() => {
const currentSession = window.savedConnections?.find(s => s.connid === window.connectionID);
if (currentSession && !currentSession.clip) {
log(`Augmenting new session: ${currentSession.connid}`, 'debug');
currentSession.timestamp = new Date();
currentSession.ip = {
...scriptState.currentPartnerInfo
};
currentSession.clip = {
blob: null,
url: null,
status: 'recording'
};
scriptState.csesh = currentSession.connid
scriptState.clippingManager.startNewRecording(currentSession.connid);
if (scriptState.managementUI?.isVisible) scriptState.managementUI.updateConnectionHistory();
}
}, 150);
};
}
}
class ManagementUI {
constructor() {
this.isVisible = false;
this.container = null;
this.currentTab = 'status';
this.createUI();
}
createUI() {
this.container = document.createElement('div');
this.container.id = 'uhmegle-management-ui';
this.createStyles();
this.createHTML();
document.body.appendChild(this.container);
this.renderTabs();
this.setupEventListeners();
this.makeDraggable();
this.switchTab(this.currentTab);
}
createStyles() {
document.head.insertAdjacentHTML('beforeend', `<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
:root {
--glass-bg: rgba(15, 17, 23, 0.85);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-highlight: rgba(255, 255, 255, 0.04);
--primary: #00d4ff;
--primary-hover: #00b8e6;
--primary-glow: rgba(0, 212, 255, 0.15);
--accent: #6c5ce7;
--accent-hover: #5a4fcf;
--success: #00c851;
--success-hover: #00a043;
--warning: #ffbb33;
--danger: #ff4757;
--danger-hover: #ff3742;
--text-primary: #f8fafc;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--surface: rgba(30, 35, 45, 0.7);
--surface-hover: rgba(40, 45, 55, 0.8);
--surface-active: rgba(50, 55, 65, 0.9);
--shadow-soft: 0 4px 24px rgba(0, 0, 0, 0.12);
--shadow-medium: 0 8px 32px rgba(0, 0, 0, 0.18);
--shadow-strong: 0 16px 48px rgba(0, 0, 0, 0.25);
}
#uhmegle-management-ui {
position: fixed;
top: 20px;
left: 20px;
width: 680px;
max-height: 88vh;
background: var(--glass-bg);
backdrop-filter: blur(24px) saturate(200%);
-webkit-backdrop-filter: blur(24px) saturate(200%);
border: 1px solid var(--glass-border);
border-radius: 20px;
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 14px;
font-weight: 400;
z-index: 999999;
box-shadow: var(--shadow-strong);
display: none;
flex-direction: column;
overflow: hidden;
user-select: none;
opacity: 0;
transform: scale(0.92) translateY(-10px);
transition: opacity .3s cubic-bezier(.4, 0, .2, 1), transform .3s cubic-bezier(.4, 0, .2, 1);
}
#uhmegle-management-ui.visible {
display: flex;
opacity: 1;
transform: scale(1) translateY(0);
}
.ui-header {
background: linear-gradient(135deg, var(--surface) 0%, rgba(20, 25, 35, 0.8) 100%);
padding: 16px 24px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
border-bottom: 1px solid var(--glass-border);
position: relative;
flex-shrink: 0;
}
.ui-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--primary), transparent);
opacity: .3;
}
.ui-header h3 {
display: flex;
align-items: center;
gap: 12px;
margin: 0;
font-size: 17px;
font-weight: 600;
letter-spacing: -0.01em;
}
.ui-header h3 .version-tag {
font-size: 10px;
font-weight: 700;
padding: 3px 8px;
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
color: #fff;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(108, 92, 231, 0.2);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.ui-header .close-btn {
cursor: pointer;
color: var(--text-muted);
transition: all .3s ease;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
.ui-header .close-btn:hover {
color: var(--text-primary);
background: var(--glass-highlight);
transform: rotate(90deg) scale(1.1);
}
.ui-tabs {
background: transparent;
display: flex;
padding: 12px 16px 0;
gap: 4px;
flex-shrink: 0;
}
.tab-btn {
flex: 1;
padding: 12px 16px;
background: transparent;
color: var(--text-muted);
border: none;
cursor: pointer;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
border-radius: 12px;
transition: all .3s cubic-bezier(.4, 0, .2, 1);
position: relative;
overflow: hidden;
}
.tab-btn::before {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%) scaleX(0);
width: 70%;
height: 2px;
background: linear-gradient(90deg, var(--primary), var(--accent));
border-radius: 1px;
transition: transform .3s ease;
}
.tab-btn.active {
color: var(--text-primary);
background: var(--glass-highlight);
box-shadow: inset 0 1px 0 var(--glass-border);
}
.tab-btn.active::before {
transform: translateX(-50%) scaleX(1);
}
.tab-btn:hover:not(.active) {
color: var(--text-secondary);
background: rgba(255, 255, 255, 0.02);
}
.tab-btn svg {
width: 16px;
height: 16px;
}
.ui-content {
padding: 24px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--glass-border) transparent;
}
.ui-content::-webkit-scrollbar {
width: 6px;
}
.ui-content::-webkit-scrollbar-track {
background: transparent;
}
.ui-content::-webkit-scrollbar-thumb {
background: var(--glass-border);
border-radius: 3px;
}
.control-group {
background: var(--surface);
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-soft);
transition: all .3s ease;
position: relative;
overflow: hidden;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-group h4 {
margin: 0 0 16px;
color: var(--primary);
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 8px;
}
.control-group h4::before {
content: '';
width: 3px;
height: 12px;
background: linear-gradient(135deg, var(--primary), var(--accent));
border-radius: 2px;
}
.control-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
font-size: 14px;
padding: 4px 0;
}
.control-item:last-child {
margin-bottom: 0;
}
.control-item label {
display: flex;
align-items: center;
gap: 10px;
color: var(--text-secondary);
}
.tooltip {
position: relative;
display: inline-flex;
cursor: pointer;
color: var(--text-muted);
transition: color .2s ease;
}
.tooltip:hover {
color: var(--text-secondary);
}
.tooltip .tooltiptext {
visibility: hidden;
width: 240px;
background: rgba(10, 12, 18, 0.95);
backdrop-filter: blur(16px);
color: var(--text-primary);
text-align: left;
border-radius: 12px;
padding: 12px 16px;
position: absolute;
z-index: 1;
bottom: 140%;
left: 50%;
margin-left: -120px;
opacity: 0;
transition: all .3s cubic-bezier(.4, 0, .2, 1);
font-size: 12px;
line-height: 1.4;
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-medium);
transform: translateY(4px);
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
transform: translateY(0);
}
.status-indicator span:last-child {
font-weight: 600;
padding: 3px 8px;
border-radius: 6px;
font-size: 12px;
}
.status-indicator .active {
color: #fff;
background-color: var(--success);
box-shadow: 0 2px 8px rgba(0, 200, 81, 0.3);
}
.status-indicator .inactive {
color: #fff;
background-color: var(--danger);
box-shadow: 0 2px 8px rgba(255, 71, 87, 0.3);
}
.device-list .device-item {
padding: 16px;
margin: 8px 0;
background: var(--glass-highlight);
border-radius: 12px;
cursor: pointer;
border-left: 3px solid var(--glass-border);
transition: all .3s cubic-bezier(.4, 0, .2, 1);
font-weight: 500;
color: var(--text-secondary);
}
.device-list .device-item:hover {
background: var(--surface-hover);
border-left-color: var(--text-muted);
transform: translateX(2px);
}
.device-list .device-item.selected {
border-left-color: var(--primary);
background: var(--primary-glow);
color: var(--text-primary);
}
input[type=text],
select {
width: 100%;
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--glass-border);
border-radius: 10px;
padding: 12px 16px;
color: var(--text-primary);
margin-top: 6px;
box-sizing: border-box;
font-family: inherit;
font-size: 14px;
transition: all .3s ease;
backdrop-filter: blur(8px);
}
input[type=text]:focus,
select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-glow);
}
select {
appearance: none;
-webkit-appearance: none;
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%2364748b%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat;
background-position: right 16px top 50%;
background-size: 12px auto;
padding-right: 44px;
}
.btn,
a.btn {
width: 100%;
padding: 12px 20px;
font-size: 14px;
font-weight: 600;
border: none;
border-radius: 12px;
cursor: pointer;
text-decoration: none;
transition: all .3s cubic-bezier(.4, 0, .2, 1);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: inherit;
position: relative;
overflow: hidden;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left .5s ease;
}
.btn:hover::before {
left: 100%;
}
.btn:disabled {
background: var(--surface) !important;
color: var(--text-muted) !important;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), var(--primary-hover));
color: #fff;
box-shadow: 0 4px 16px var(--primary-glow);
}
.btn-primary:not(:disabled):hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px var(--primary-glow);
}
.btn-secondary {
background: var(--surface);
color: var(--text-secondary);
border: 1px solid var(--glass-border);
}
.btn-secondary:not(:disabled):hover {
background: var(--surface-hover);
color: var(--text-primary);
border-color: var(--glass-highlight);
transform: translateY(-1px);
box-shadow: var(--shadow-soft);
}
.btn-danger {
background: linear-gradient(135deg, var(--danger), var(--danger-hover));
color: #fff;
box-shadow: 0 4px 16px rgba(255, 71, 87, 0.2);
}
.btn-danger:not(:disabled):hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(255, 71, 87, 0.3);
}
.btn-review {
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
color: #fff;
box-shadow: 0 4px 16px rgba(108, 92, 231, 0.2);
}
.btn-review:not(:disabled):hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(108, 92, 231, 0.3);
}
.log-container {
background: rgba(0, 0, 0, 0.6);
padding: 16px;
border-radius: 12px;
font-family: 'SF Mono', 'JetBrains Mono', 'Courier New', monospace;
font-size: 11px;
max-height: 320px;
overflow-y: auto;
border: 1px solid var(--glass-border);
line-height: 1.6;
color: var(--text-secondary);
backdrop-filter: blur(8px);
}
#connection-history {
display: flex;
flex-direction: column;
gap: 20px;
}
.session-history-item {
display: flex;
flex-direction: column;
gap: 16px;
background: var(--surface);
padding: 20px;
border-radius: 16px;
border: 1px solid var(--glass-border);
transition: all .3s ease;
}
.session-main-content {
display: flex;
gap: 16px;
}
.session-history-item img {
width: 100px;
height: 75px;
border-radius: 12px;
object-fit: cover;
border: 1px solid var(--glass-border);
flex-shrink: 0;
background: #000;
}
.session-history-details {
flex: 1;
min-width: 0;
}
.session-history-details .country {
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
}
.session-history-details .timestamp {
font-size: 12px;
color: var(--text-muted);
margin-top: 6px;
}
.session-ip-info {
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
padding: 8px 14px;
margin-top: 12px;
font-size: 11px;
font-family: 'SF Mono', 'JetBrains Mono', 'Courier New', monospace;
display: flex;
align-items: center;
gap: 16px;
border: 1px solid var(--glass-border);
}
.session-ip-info .conn-type {
font-weight: 600;
}
.session-ip-info .conn-p2p {
color: var(--danger);
}
.session-ip-info .conn-relay {
color: var(--success);
}
.session-clip-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
}
.session-clip-controls .btn,
.session-clip-controls a.btn {
padding: 8px 12px;
font-size: 13px;
font-weight: 500;
}
.clip-player-container {
margin-top: 16px;
display: none;
}
.clip-player-container video {
width: 100%;
border-radius: 12px;
background: #000;
border: 1px solid var(--glass-border);
}
.no-history-msg {
color: var(--text-muted);
text-align: center;
padding: 40px 20px;
font-style: italic;
font-size: 15px;
}
@media (max-width:768px) {
#uhmegle-management-ui {
width: calc(100vw - 24px);
left: 12px;
top: 12px;
max-height: calc(100vh - 24px);
}
.session-clip-controls {
grid-template-columns: 1fr;
}
}
</style>`);
}
createHTML() {
this.container.innerHTML = `
<div class="ui-header" id="ui-header">
<h3>${ICONS.globe} foenemegle <span class="version-tag">${CONFIG.scriptVersion}</span></h3>
<span class="close-btn" id="ui-close">${ICONS.close}</span>
</div>
<div class="ui-tabs">
${this.createTabButton("status", "Status", ICONS.status, true)}
${this.createTabButton("history", "History", ICONS.history)}
${this.createTabButton("devices", "Devices", ICONS.devices)}
${this.createTabButton("power", "Power", ICONS.power)}
${this.createTabButton("settings", "Settings", ICONS.settings)}
${this.createTabButton("logs", "Logs", ICONS.logs)}
</div>
<div class="ui-content">
<div id="tab-status" class="tab-content"></div>
<div id="tab-history" class="tab-content" style="display:none"></div>
<div id="tab-devices" class="tab-content" style="display:none"></div>
<div id="tab-power" class="tab-content" style="display:none"></div>
<div id="tab-settings" class="tab-content" style="display:none"></div>
<div id="tab-logs" class="tab-content" style="display:none"></div>
</div>
`;
}
createTabButton(id, text, icon, active = false) {
return `<button class="tab-btn ${active ? "active" : ""}" data-tab="${id}">${icon}<span>${text}</span></button>`;
}
renderTabs() {
document.getElementById("tab-status").innerHTML = `
<div class="control-group">
<h4>Bypass Status</h4>
<div id="status-indicators"></div>
</div>
<div class="control-group">
<h4>Session Tools</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<button id="flip-camera-btn" class="btn btn-secondary">
${ICONS.flipCam} Flip Camera
</button>
<button id="clip-now-btn" class="btn btn-primary">
${ICONS.clapper} Clip Now
</button>
</div>
</div>
<div class="control-group">
<label for="interests-input">Common Interests (comma-separated):</label>
<input type="text" id="interests-input" placeholder="e.g. gaming, music, art">
</div>
`;
document.getElementById("tab-history").innerHTML = `
<div class="control-group">
<h4>Connection History (Last ${CONFIG.maxHistory})</h4>
<div id="connection-history"></div>
</div>
`;
document.getElementById("tab-devices").innerHTML = `
<div class="control-group">
<h4>Video Devices</h4>
<div id="video-devices-list" class="device-list">Loading...</div>
</div>
<div class="control-group">
<h4>Audio Input Devices</h4>
<div id="audio-input-devices-list" class="device-list">Loading...</div>
</div>
`;
document.getElementById("tab-power").innerHTML = `
<div class="control-group">
<h4>Power Tools</h4>
<div style="display:grid;grid-template-columns:1fr;gap:12px">
<button id="force-skip-btn" class="btn btn-primary">Force New Connection</button>
<button id="force-reconnect-btn" class="btn btn-secondary">Force Server Reconnect</button>
<button id="safe-report-btn" class="btn btn-danger">Safely Report Last User</button>
</div>
</div>
`;
document.getElementById("tab-logs").innerHTML = `
<div class="control-group">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<h4 style="margin:0">System Logs</h4>
<button id="clear-logs-btn" class="btn btn-secondary" style="width:auto;padding:4px 12px;font-size:12px">Clear</button>
</div>
<div id="log-container" class="log-container"></div>
</div>
`;
document.getElementById("tab-settings").innerHTML = `
<div class="control-group">
<h4>General Settings</h4>
<div class="control-item">
<label>
Device Spoofing
<span class="tooltip">
${ICONS.info}
<span class="tooltiptext">Hides your real camera/mic names from the site. Recommended on.</span>
</span>
</label>
<input type="checkbox" id="device-spoof-toggle">
</div>
<div class="control-item">
<label>
Force Relay (IP Protection)
<span class="tooltip">
${ICONS.info}
<span class="tooltiptext">Forces connection through Uhmegle's servers, hiding your IP from partners. May increase lag.</span>
</span>
</label>
<input type="checkbox" id="force-relay-toggle">
</div>
<div class="control-item">
<label>
Remove Watermark
<span class="tooltip">
${ICONS.info}
<span class="tooltiptext">Hides the "uhmegle.com" watermark from the remote video feed.</span>
</span>
</label>
<input type="checkbox" id="watermark-remove-toggle">
</div>
<div class="control-item">
<label>
Debug Logging
<span class="tooltip">
${ICONS.info}
<span class="tooltiptext">Show detailed script logs in the browser console (F12).</span>
</span>
</label>
<input type="checkbox" id="debug-mode-toggle">
</div>
</div>
<div class="control-group">
<h4>Clipping Settings</h4>
<div class="control-item">
<label>
Enable Clipping
<span class="tooltip">
${ICONS.info}
<span class="tooltiptext">Enables the rolling buffer to record clips of your sessions.</span>
</span>
</label>
<input type="checkbox" id="clipping-enabled-toggle">
</div>
<div class="control-item">
<label for="clip-duration-select">Clip Duration</label>
<select id="clip-duration-select">
<option value="15">15s</option>
<option value="30">30s</option>
<option value="45">45s</option>
<option value="60">60s</option>
</select>
</div>
<div class="control-item">
<label for="clip-layout-select">Clip Layout</label>
<select id="clip-layout-select">
<option value="side-by-side">Side-by-Side</option>
<option value="remote-only">Stranger Only</option>
</select>
</div>
</div>
<div class="control-group">
<h4>Audio Settings</h4>
<div class="control-item">
<label>Raw Audio Mode <span class="tooltip">Disables noise suppression</span></label>
<input type="checkbox" id="raw-audio-toggle">
</div>
</div>
`;
}
setupEventListeners() {
document.getElementById("ui-close").addEventListener("click", () => this.hide()), document.querySelectorAll(".tab-btn").forEach(e => e.addEventListener("click", t => this.switchTab(t.currentTarget.dataset.tab))), document.getElementById("device-spoof-toggle").addEventListener("change", e => {
scriptState.deviceSpoofEnabled = e.target.checked, log(`Device Spoofing ${e.target.checked ? "Enabled" : "Disabled"}.`, "info")
}), document.getElementById("force-relay-toggle").addEventListener("change", e => {
scriptState.forceRelay = e.target.checked, window.relay = e.target.checked, log(`Force Relay ${e.target.checked ? "Enabled" : "Disabled"}.`, "success")
}), document.getElementById("watermark-remove-toggle").addEventListener("change", e => {
scriptState.removeWatermark = e.target.checked;
const t = document.querySelector(".videoContainer.remote .wmark");
t && (t.style.opacity = scriptState.removeWatermark ? "0" : "1"), log(`Watermark removal ${scriptState.removeWatermark ? "Enabled" : "Disabled"}.`, "info")
}), document.getElementById("debug-mode-toggle").addEventListener("change", e => {
CONFIG.debugMode = e.target.checked, log(`Debug Mode ${e.target.checked ? "Enabled" : "Disabled"}.`, "info")
}), document.getElementById("flip-camera-btn").addEventListener("click", () => {
"function" == typeof window.unflipFunc && (window.unflipFunc(), log("Flipped camera.", "success"))
}), document.getElementById("force-skip-btn").addEventListener("click", () => {
"function" == typeof window.pressSkip && (window.pressSkip(!0), log("Forced a new connection.", "success"))
}), document.getElementById("force-reconnect-btn").addEventListener("click", () => {
window.socket && window.socket.close(), log("Forced server reconnection.", "info")
}), document.getElementById("safe-report-btn").addEventListener("click", () => {
"function" == typeof window.report && (window.action = "report", window.popupOpen(), log("Opened safe report dialog.", "info"))
}), document.getElementById("clear-logs-btn").addEventListener("click", () => {
scriptState.logs = [], this.updateLogs()
});
document.getElementById('raw-audio-toggle').addEventListener('change', (e) => {
_audioconfig.rawMode = e.target.checked;
log(`Raw Audio Mode: ${_audioconfig.rawMode ? 'Enabled' : 'Disabled'}. Reconnect for changes to take effect.`, 'info');
})
const e = document.getElementById("interests-input");
e.value = (localStorage.getItem("interests") || "[]").replace(/[\[\]"]/g, ""), e.addEventListener("change", e => {
const t = e.target.value.split(",").map(e => e.trim()).filter(Boolean);
localStorage.setItem("interests", JSON.stringify(t)), log("Interests updated.", "info", t)
}), document.getElementById("clipping-enabled-toggle").addEventListener("change", e => {
CONFIG.clippingEnabled = e.target.checked, log(`Clipping ${CONFIG.clippingEnabled ? "Enabled" : "Disabled"}.`, "info")
}), document.getElementById("clip-duration-select").addEventListener("change", e => {
CONFIG.defaultClipDuration = parseInt(e.target.value, 10), log(`Clip duration set to ${CONFIG.defaultClipDuration}s.`, "info")
}), document.getElementById("clip-layout-select").addEventListener("change", e => {
CONFIG.clipLayout = e.target.value, log(`Clip layout set to ${CONFIG.clipLayout}.`, "info")
}), document.getElementById("clip-now-btn").addEventListener("click", () => {
scriptState.clippingManager && (log('"Clip Now" button pressed, finalizing current clip.', "info"), scriptState.clippingManager.stopCurrentRecording(), this.show(), this.switchTab("history"), scriptState.clippingManager.startNewRecording(scriptState.csesh))
}), document.getElementById("device-spoof-toggle").checked = scriptState.deviceSpoofEnabled, document.getElementById("force-relay-toggle").checked = scriptState.forceRelay, document.getElementById("watermark-remove-toggle").checked = scriptState.removeWatermark, document.getElementById("debug-mode-toggle").checked = CONFIG.debugMode, document.getElementById("clipping-enabled-toggle").checked = CONFIG.clippingEnabled, document.getElementById("clip-duration-select").value = CONFIG.defaultClipDuration, document.getElementById("clip-layout-select").value = CONFIG.clipLayout, document.getElementById("connection-history").addEventListener("click", e => {
const t = e.target.closest("button, a.btn");
if (!t || !t.dataset.connid) return;
e.preventDefault();
const o = window.savedConnections.find(e => e.connid === t.dataset.connid);
o && (t.matches(".btn-review") ? this.toggleClipReview(o) : t.matches(".btn-discard") ? this.discardClip(o) : t.matches("a.btn-primary") && window.open(t.href))
});
}
toggleClipReview(session) {
const playerContainer = document.getElementById(`player-for-${session.connid}`);
if (!playerContainer) return;
if (playerContainer.style.display === 'block') {
playerContainer.style.display = 'none';
playerContainer.innerHTML = '';
} else if (session.clip?.url) {
playerContainer.style.display = 'block';
playerContainer.innerHTML = `<video src="${session.clip.url}" controls autoplay loop muted></video>`;
}
}
discardClip(session) {
if (session.clip?.url) URL.revokeObjectURL(session.clip.url);
session.clip = {
blob: null,
url: null,
status: 'none'
};
log(`Clip for session ${session.connid} discarded.`, 'info');
this.updateConnectionHistory();
}
updateConnectionHistory() {
const container = document.getElementById('connection-history');
if (!container) return;
let saved = (scriptState.savedConnections || [])
if (saved.length === 0) {
container.innerHTML = '<p class="no-history-msg">No recent connections to display.</p>';
return;
}
if (window.savedConnections[0] !== saved[0]) {
}
container.innerHTML = saved.slice().reverse().map(conn => {
if (!conn.image) {
if (scriptState.imstore[conn.connid]) {
conn.image = scriptState.imstore[conn.connid]
} else {
conn.image = captureRemoteVideoFrame()
scriptState.imstore[conn.connid] = conn.image
}
}
const hasIpInfo = conn.ip && conn.ip.ip;
const ipInfoHtml = hasIpInfo ? `<div class="session-ip-info"><div>${ICONS.globe} <span>${conn.ip.ip}</span></div><div>${ICONS.zap} <span class="conn-type ${conn.ip.type === 'relay' ? 'conn-relay' : 'conn-p2p'}">${conn.ip.type === 'relay' ? 'Relay (Protected)' : 'P2P (Direct)'}</span></div></div>` : '';
let clipControls = '';
if (conn.clip?.status === 'available') {
const size = (conn.clip.blob.size / 1024 / 1024).toFixed(2);
const clipDate = new Date(conn.clip.timestamp).getTime();
clipControls = `<div class="session-clip-controls">
<button class="btn btn-review" data-connid="${conn.connid}">Review (${size}MB)</button>
<a class="btn btn-primary" data-connid="${conn.connid}" href="${conn.clip.url}" download="foenemegle-clip-${clipDate}.webm">${ICONS.download} Save</a>
<button class="btn btn-danger btn-discard" id="btn-discard" data-connid="${conn.connid}">${ICONS.trash} Discard</button>
</div>`;
} else if (conn.clip?.status === 'recording') {
clipControls = `<div style="font-size:12px; color:var(--text-muted); text-align:center; padding: 16px 0;">Currently recording...</div>`;
}
return `<div class="session-history-item" data-connid="${conn.connid}">
<div class="session-main-content">
<img src="${conn.image || CONFIG.fallbackImageURL}" alt="Partner thumbnail">
<div class="session-history-details">
<div class="country">${conn.countryname || 'Unknown Location'} <span class="${conn.flag}"></span></div>
<div class="timestamp">${conn.timestamp?.toLocaleString() || "Not available"}</div>
${ipInfoHtml}
</div>
</div>
${clipControls}
<div class="clip-player-container" id="player-for-${conn.connid}"></div>
</div>`;
}).join('');
}
switchTab(tabName) {
this.currentTab = tabName, document.querySelectorAll(".tab-btn").forEach(e => e.classList.remove("active")), document.querySelector(`[data-tab="${tabName}"]`)?.classList.add("active"), document.querySelectorAll(".tab-content").forEach(e => e.style.display = "none");
const e = document.getElementById(`tab-${tabName}`);
e && (e.style.display = "block"), "status" === tabName ? this.updateStatusIndicators() : "history" === tabName ? this.updateConnectionHistory() : "devices" === tabName ? this.updateDeviceInfo() : "logs" === tabName && this.updateLogs();
}
updateStatusIndicators() {
const e = document.getElementById("status-indicators");
e && (e.innerHTML = Object.entries(scriptState.bypassStatus).map(([e, t]) => {
const o = e.replace(/([A-Z])/g, " $1").replace(/^./, e => e.toUpperCase());
return `<div class="control-item status-indicator"><span>${o}</span><span class="${t ? "active" : "inactive"}">${t ? "Active" : "Inactive"}</span></div>`
}).join(""));
}
async updateDeviceInfo() {
try {
const e = scriptState.deviceSpoofEnabled;
scriptState.deviceSpoofEnabled = !1, scriptState.realDevices = await scriptState.originalMethods.enumerateDevices(), scriptState.deviceSpoofEnabled = e;
const t = (e, t) => {
if (0 === e.length) return '<p style="color:var(--text-muted); text-align: center; padding: 10px;">No devices found.</p>';
const o = localStorage.getItem("video" === t ? "webcamDeviceId" : "micDeviceId");
return e.map(e => `<div class="device-item ${o === e.deviceId ? "selected" : ""}" data-device-id="${e.deviceId}" data-type="${t}">${e.label || `Unknown (${e.deviceId.slice(0, 10)}...)`}</div>`).join("")
};
document.getElementById("video-devices-list").innerHTML = t(scriptState.realDevices.filter(e => "videoinput" === e.kind), "video"), document.getElementById("audio-input-devices-list").innerHTML = t(scriptState.realDevices.filter(e => "audioinput" === e.kind), "audio"), document.querySelectorAll(".device-item").forEach(e => {
e.addEventListener("click", () => {
const {
deviceId: t,
type: o
} = e.dataset, i = "video" === o ? window.changeWebcam : window.changeMicrophone;
"function" == typeof i && (i(t), log(`Changed ${o} device to: ${t.substring(0, 15)}...`, "success"), this.updateDeviceInfo())
})
})
} catch (e) {
log("Error updating device info", "error", e), document.getElementById("video-devices-list").innerHTML = '<p style="color:var(--danger);">Error loading devices.</p>', document.getElementById("audio-input-devices-list").innerHTML = '<p style="color:var(--danger);">Error loading devices.</p>';
}
}
updateLogs() {
const e = document.getElementById("log-container");
if (!e || !this.isVisible) return;
e.innerHTML = scriptState.logs.slice(-50).map(e => {
const t = {
error: "var(--danger)",
warn: "var(--warning)",
success: "var(--success)",
info: "var(--primary)",
debug: "var(--text-muted)"
}[e.type] || "var(--text-primary)";
return `<div style=color:${t}><span style="color:var(--text-muted);opacity:0.6">[${e.timestamp}]</span> ${e.message}</div>`
}).join(""), e.scrollTop = e.scrollHeight;
}
makeDraggable() {
const e = document.getElementById("ui-header");
let t = {
x: 0,
y: 0,
cx: 0,
cy: 0
};
const o = o => {
if (o.target.closest(".close-btn")) return;
o.preventDefault(), t.cx = o.clientX, t.cy = o.clientY, document.onmouseup = i, document.onmousemove = s
},
s = o => {
o.preventDefault(), t.x = t.cx - o.clientX, t.y = t.cy - o.clientY, t.cx = o.clientX, t.cy = o.clientY, this.container.style.top = `${this.container.offsetTop - t.y}px`, this.container.style.left = `${this.container.offsetLeft - t.x}px`
},
i = () => {
document.onmouseup = null, document.onmousemove = null
};
e.onmousedown = o;
}
show() {
this.container.classList.add('visible');
this.isVisible = true;
this.switchTab(this.currentTab);
}
hide() {
this.container.classList.remove('visible');
this.isVisible = false;
}
toggle() {
this.isVisible ? this.hide() : this.show();
}
}
class VideoFrameManager {
static async initialize() {
try {
scriptState.fakeFrameCanvas = await this.loadImageToCanvas(CONFIG.imageURL);
} catch (e) {
log(`Primary image failed: ${e.message}. Using fallback.`, 'warn');
scriptState.fakeFrameCanvas = await this.loadImageToCanvas(CONFIG.fallbackImageURL);
}
scriptState.perturbedCanvas = document.createElement('canvas');
scriptState.perturbedCanvas.width = CONFIG.canvasSize.width;
scriptState.perturbedCanvas.height = CONFIG.canvasSize.height;
}
static loadImageToCanvas(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = CONFIG.canvasSize.width;
canvas.height = CONFIG.canvasSize.height;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
resolve(canvas);
};
img.onerror = () => reject(new Error(`Image load failed: ${url}`));
img.src = url;
});
}
static getDynamicFrame() {
if (!scriptState.fakeFrameCanvas) return null;
const pCtx = scriptState.perturbedCanvas.getContext('2d');
pCtx.drawImage(scriptState.fakeFrameCanvas, 0, 0);
const r = Math.random();
if (r < 0.3) {
pCtx.globalAlpha = 0.98 + r * 0.04;
pCtx.drawImage(scriptState.fakeFrameCanvas, 0, 0);
pCtx.globalAlpha = 1.0;
} else if (r < 0.6) {
pCtx.drawImage(scriptState.fakeFrameCanvas, (r - 0.5), (r - 0.5));
}
return scriptState.perturbedCanvas;
}
static getDynamicFrameData() {
return this.getDynamicFrame()?.toDataURL('image/jpeg', 0.85).split(';base64,')[1] || null;
}
}
class UserPersona {
constructor() {
this.platform = this.getRandomElement(["Win32", "MacIntel", "Linux x86_64"]);
this.hardwareConcurrency = this.getRandomElement([4, 8, 12, 16]);
this.deviceMemory = this.getRandomElement([8, 16, 32]);
this.userAgent = this.generateUserAgent();
this.scrollLength = this.randomInRange(15000, 40000);
this.gifTestTime = Math.random() * (150 - 50) + 50;
}
getRandomElement(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
randomInRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
generateUserAgent() {
const version = this.randomInRange(105, 115);
if (this.platform === 'Win32') {
return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version}.0.0.0 Safari/537.36`;
} else if (this.platform === 'MacIntel') {
return `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version}.0.0.0 Safari/537.36`;
} else {
return `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version}.0.0.0 Safari/537.36`;
}
}
}
const userPersona = new UserPersona();
class CoreBypasses {
static initialize() {
this.initializeSessionHooks();
this.initializePreConnectionBypasses();
this.initializeCameraAndFaceBypasses();
this.initializeVarianceBypass();
this.initializePenaltyBypasses();
this.initializeFingerprintBypasses();
this.initializeBroadcastChannelBypass();
this.initializeAFKBypass();
}
static initializeSessionHooks() {
const wrap = (original, name) => function (...args) {
log(`Session end via: ${name}. Cleaning up.`, 'info');
if (scriptState.currentPartnerInfo.ip !== null) {
scriptState.resetPartnerInfo();
scriptState.clippingManager.stopCurrentRecording();
}
return typeof original === 'function' ? original.apply(this, args) : undefined;
};
window.onEnd = wrap(window.onEnd, 'onEnd (Disconnect)');
window.pressSkip = wrap(window.pressSkip, 'pressSkip (Skip Button)');
log('Hooked session end functions for robust state/clip management.', 'success');
}
static initializePreConnectionBypasses() {
const secureDefine = (prop, value) => Object.defineProperty(window, prop, {
value,
writable: false,
configurable: true
});
secureDefine('calculateScrollLength', () => userPersona.scrollLength);
secureDefine('setGIFSelector', async () => crypto.randomUUID().replace(/-/g, ''));
secureDefine('testGIF', async () => userPersona.gifTestTime);
secureDefine('bypass', true);
log('Pre-connection fingerprints hardened.', 'success');
}
static initializeFingerprintBypasses() {
try {
const spoofedProperties = {
webdriver: {
value: false
},
hardwareConcurrency: {
value: userPersona.hardwareConcurrency
},
deviceMemory: {
value: userPersona.deviceMemory
},
platform: {
value: userPersona.platform
},
userAgent: {
value: userPersona.userAgent
},
plugins: {
get: new Proxy(function () {
return []
}, {
apply: (target, thisArg, args) => Reflect.apply(target, thisArg, args),
get: (target, prop) => prop === 'length' ? 0 : target[prop],
toString: () => 'function plugins() { [native code] }',
})
}
};
const define = (obj, prop, descriptor) => {
Object.defineProperty(obj, prop, {
...descriptor,
configurable: false,
enumerable: true,
});
};
for (const prop in spoofedProperties) {
define(navigator, prop, spoofedProperties[prop]);
}
scriptState.bypassStatus.fingerprintBypass = true;
log('Advanced fingerprint bypasses initialized.', 'success');
} catch (e) {
log('Fingerprint bypass init failed.', 'error', e);
}
}
static initializeBroadcastChannelBypass() {
if (!window.BroadcastChannel) return;
try {
const originalBC = window.BroadcastChannel;
window.BroadcastChannel = new Proxy(originalBC, {
construct(target, [name]) {
if (/^(tab|session|multitab-check)/i.test(name)) {
log(`Blocked multi-tab check on channel: ${name}.`, 'success');
return {
postMessage: () => { },
close: () => { },
onmessage: null,
onerror: null
};
}
return new target(name);
}
});
scriptState.bypassStatus.broadcastChannel = true;
} catch (e) {
log('BroadcastChannel bypass failed.', 'error', e);
}
}
static initializeAFKBypass() {
window.setAfk = () => {
log('AFK timer check blocked.', 'debug');
clearTimeout(window.afkTimer);
};
setInterval(() => {
if (window.lastUserActivity) window.lastUserActivity = Date.now();
}, 60000);
scriptState.bypassStatus.afkBypass = true;
log('AFK bypass initialized.', 'success');
}
static initializeCameraAndFaceBypasses() {
window.captureFrameToBase64 = () => VideoFrameManager.getDynamicFrameData();
window.makeLocalCanvas = () => VideoFrameManager.getDynamicFrame();
scriptState.bypassStatus.cameraBypass = true;
scriptState.bypassStatus.faceDetection = true;
log('Camera & Face Detection bypasses initialized.', 'success');
}
static initializeVarianceBypass() {
let noiseSeed = Math.random();
const getNoise = () => {
let x = Math.sin(noiseSeed) * 10000;
noiseSeed += 0.1;
return x - Math.floor(x);
};
window.calculateVariance = () => 1000 + (getNoise() * 500);
scriptState.bypassStatus.varianceBypass = true;
log('Variance bypass initialized.', 'success');
}
static initializePenaltyBypasses() {
const penaltySetterProxy = {
set: function (target, prop, value) {
if (value === true && prop.toLowerCase().includes('block')) {
log(`Potential face/skip penalty blocked (property: ${prop}).`, 'warn');
return true;
}
return Reflect.set(...arguments);
}
};
window.Proxy = window.Proxy || function () { };
const originalEval = window.eval;
window.eval = (script) => {
if (typeof script === 'string' && script.includes('FaceOverlay')) {
log('Face overlay popup execution prevented via eval interception.', 'debug');
return;
}
return originalEval(script);
};
log('Hooked penalty system.', 'success');
}
}
function captureRemoteVideoFrame() {
const remoteVideo = document.getElementById('remoteVideo');
if (!remoteVideo || remoteVideo.videoWidth === 0) {
log('Remote video not available for capture.', 'warn');
return null;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 640;
canvas.height = 480;
if (!window.remoteUnflip) {
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
}
let sourceX, sourceY, sourceWidth, sourceHeight;
const videoAspectRatio = remoteVideo.videoWidth / remoteVideo.videoHeight;
const targetAspectRatio = 4 / 3;
if (videoAspectRatio > targetAspectRatio) {
sourceHeight = remoteVideo.videoHeight;
sourceWidth = sourceHeight * targetAspectRatio;
sourceX = (remoteVideo.videoWidth - sourceWidth) / 2;
sourceY = 0;
} else {
sourceWidth = remoteVideo.videoWidth;
sourceHeight = sourceWidth / targetAspectRatio;
sourceX = 0;
sourceY = (remoteVideo.videoHeight - sourceHeight) / 2;
}
ctx.drawImage(
remoteVideo,
sourceX, sourceY, sourceWidth, sourceHeight,
0, 0, canvas.width, canvas.height
);
const dataUrl = canvas.toDataURL('image/jpeg');
return dataUrl
}
class ConnectionHandler {
static #extractSrflxIP(candidates) {
if (!candidates || candidates.length === 0) {
return null;
}
const srflxCandidate = candidates.find(c => c?.candidate?.includes(' typ srflx'));
if (srflxCandidate) {
try {
const parts = srflxCandidate.candidate.split(' ');
if (parts.length > 4) {
const ip = parts[4];
if (ip.includes('.') || ip.includes(':')) {
log(`Extracted potential partner IP from srflx candidate: ${ip}`, 'debug');
return ip;
}
}
} catch (e) {
log('Failed to parse srflx candidate string.', 'warn', e);
return null;
}
}
return null;
}
static handleNewConnection(eventData) {
if (eventData?.event !== 'conn' || !eventData.connectionID) {
log('Received a non-connection or malformed event, ignoring.', 'debug');
return;
}
const potentialIp = this.#extractSrflxIP(eventData.candidates);
let initialIpInfo = null;
if (potentialIp) {
initialIpInfo = {
ip: potentialIp,
type: 'p2p (pending)'
};
}
const newSession = {
connid: eventData.connectionID,
countryname: eventData.cName || eventData.country || 'Unknown Location',
flag: `flag-${eventData.country?.toLowerCase()}`,
interests: eventData.interests || [],
timestamp: new Date(),
ip: initialIpInfo,
image: null,
clip: null
};
log(`New connection initiated: ${newSession.connid}`, 'info');
if (!scriptState.savedConnections) {
scriptState.savedConnections = [];
}
if (scriptState.savedConnections.length >= CONFIG.maxHistory) {
scriptState.savedConnections.shift();
}
scriptState.savedConnections.push(newSession);
window.savedConnections = scriptState.savedConnections;
if (scriptState.managementUI?.isVisible) {
scriptState.managementUI.updateConnectionHistory();
}
}
}
class ImageCaptureHandler {
static captureThumbnail(remoteVideoElement) {
const currentConnId = window.connectionID;
if (!currentConnId) {
log('Cannot capture thumbnail, no active connectionID.', 'warn');
return;
}
const session = scriptState.savedConnections.find(s => s.connid === currentConnId);
if (!session) {
log(`Cannot capture thumbnail, session not found for connid: ${currentConnId}`, 'warn');
return;
}
try {
const canvas = document.createElement('canvas');
canvas.width = remoteVideoElement.videoWidth;
canvas.height = remoteVideoElement.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(remoteVideoElement, 0, 0, canvas.width, canvas.height);
const imageURL = canvas.toDataURL('image/jpeg', 0.8);
session.image = imageURL;
log(`Thumbnail captured for session: ${session.connid}`, 'debug');
if (scriptState.managementUI?.isVisible) {
scriptState.managementUI.updateConnectionHistory();
}
} catch (e) {
log('Error capturing video thumbnail.', 'error', e);
}
}
}
class WebSocketInterceptor {
static initialize() {
if (window.WebSocket) {
const OrigWS = window.WebSocket;
const Osend = OrigWS.prototype.send;
window.WebSocket = function (...args) {
const sock = new OrigWS(...args);
sock.addEventListener('message', (e) => {
try {
if (typeof e.data !== 'string' || !e.data.startsWith('{')) return;
console.log(e.data)
const msg = JSON.parse(e.data);
if (msg.event === 'ban' || msg.event === 'banned' || msg.event === 'injection') {
log(`Server ${msg.event} event BLOCKED.`, 'error', msg);
e.stopImmediatePropagation();
if (msg.event !== 'injection' && typeof window.pressSkip === 'function') {
setTimeout(() => window.pressSkip(true), 1500);
}
}
if (msg.event === 'conn') {
console.log(msg)
ConnectionHandler.handleNewConnection(msg);
}
if (msg.event === 'rimage') {
alert("You just got reported. Foenemegle saved you. Sorry king, gonna have to be redirected to prevent it.");
window.location = "https://uhmegle.com/video?" + Date.now()
}
} catch (err) { }
}, true);
sock.send = function (data) {
try {
if (typeof data === 'string' && data.includes('"event":"image"')) {
const payload = JSON.parse(data);
if (payload.event === 'image' && payload.image) {
log('Blocking image event.', 'success');
return;
}
}
} catch (e) {
}
return Osend.apply(this, arguments);
};
return sock;
};
window.WebSocket.prototype = OrigWS.prototype;
scriptState.bypassStatus.websocketIntercept = true;
}
}
}
async function main() {
if (scriptState.isInitialized) return;
scriptState.isInitialized = true;
log('DOM ready. Initializing script...');
if (typeof window.savedConnections === 'undefined') window.savedConnections = [];
await VideoFrameManager.initialize();
PeerConnectionInterceptor.initialize();
scriptState.clippingManager = new ClippingManager();
SessionDataCollector.initialize();
CoreBypasses.initialize();
scriptState.managementUI = new ManagementUI();
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.altKey && e.key.toLowerCase() === 'u') {
e.preventDefault();
scriptState.managementUI.toggle();
}
if (e.ctrlKey && e.altKey && e.key.toLowerCase() === 'c') {
e.preventDefault();
log('Clip hotkey pressed.', 'info');
if (scriptState.clippingManager) {
scriptState.clippingManager.stopCurrentRecording();
scriptState.managementUI.show();
scriptState.managementUI.switchTab('history');
}
}
});
log('foenemegle Fixes is fully operational.', 'success');
log('Pro Tip: Press Ctrl+Alt+U for menu, Ctrl+Alt+C to save a clip.', 'info');
scriptState.managementUI.updateStatusIndicators();
}
log('Script injected. Applying pre-emptive patches.');
if (navigator.mediaDevices) {
scriptState.originalMethods.enumerateDevices = navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
scriptState.originalMethods.getUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.enumerateDevices = createNativeWrapper(scriptState.originalMethods.enumerateDevices, (original) =>
scriptState.deviceSpoofEnabled ? Promise.resolve([CONFIG.spoofedDevices.video, CONFIG.spoofedDevices.audioIn]) : original()
);
navigator.mediaDevices.getUserMedia = createNativeWrapper(scriptState.originalMethods.getUserMedia, async (original, constraints) => {
if (constraints?.audio && _audioconfig.rawMode) {
log('Raw Audio Mode: Forcing audio processing off.', 'debug');
if (typeof constraints.audio !== 'object') {
constraints.audio = {};
}
constraints.audio.noiseSuppression = false;
constraints.audio.echoCancellation = false;
constraints.audio.autoGainControl = false;
}
if (scriptState.deviceSpoofEnabled && constraints?.video) {
log('getUserMedia intercepted: Providing dynamic fake video stream.', 'debug');
const canvas = VideoFrameManager.getDynamicFrame();
const fakeStream = canvas.captureStream(30);
try {
const audioStream = await original({
audio: constraints.audio || true,
video: false
});
audioStream.getAudioTracks().forEach(track => fakeStream.addTrack(track));
} catch (e) {
log('Could not get real audio for passthrough.', 'warn');
}
return fakeStream;
}
return original(constraints);
});
scriptState.bypassStatus.deviceSpoof = true;
}
WebSocketInterceptor.initialize();
if (document.readyState === 'loading') window.addEventListener('DOMContentLoaded', main);
else main();
})();