Last active
August 9, 2025 01:53
-
-
Save kenzouno1/5660ecbca5f65b08e0c03b0d254bb9c0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const script = document.createElement("script"); | |
script.textContent = ` | |
// CENTRALIZED SETTINGS MANAGER | |
window.syncViaSettingsManager = { | |
_settings: null, | |
_listeners: [], | |
// Default settings | |
defaults: { | |
mainSync: true, | |
autoResolve: true, | |
}, | |
// Get settings (cached) | |
get() { | |
if (this._settings === null) { | |
this.load(); | |
} | |
return { ...this._settings }; // Return copy to prevent mutation | |
}, | |
// Load from localStorage | |
load() { | |
try { | |
const saved = localStorage.getItem('sync-via-settings'); | |
if (saved) { | |
const parsed = JSON.parse(saved); | |
// Validate settings structure | |
this._settings = { ...this.defaults, ...parsed }; | |
console.log('Settings loaded:', this._settings); | |
} else { | |
this._settings = { ...this.defaults }; | |
console.log('Using default settings:', this._settings); | |
} | |
} catch (error) { | |
console.error('Error loading settings:', error); | |
this._settings = { ...this.defaults }; | |
} | |
return this._settings; | |
}, | |
// Save to localStorage | |
save(newSettings) { | |
try { | |
// Merge with existing settings | |
this._settings = { ...this._settings, ...newSettings }; | |
localStorage.setItem('sync-via-settings', JSON.stringify(this._settings)); | |
console.log('Settings saved:', this._settings); | |
// Notify listeners | |
this._notifyListeners(this._settings); | |
return true; | |
} catch (error) { | |
console.error('Error saving settings:', error); | |
return false; | |
} | |
}, | |
// Update specific setting | |
set(key, value) { | |
const currentSettings = this.get(); | |
currentSettings[key] = value; | |
return this.save(currentSettings); | |
}, | |
// Add listener for settings changes | |
addListener(callback) { | |
this._listeners.push(callback); | |
}, | |
// Remove listener | |
removeListener(callback) { | |
const index = this._listeners.indexOf(callback); | |
if (index > -1) { | |
this._listeners.splice(index, 1); | |
} | |
}, | |
// Notify all listeners | |
_notifyListeners(settings) { | |
this._listeners.forEach(callback => { | |
try { | |
callback(settings); | |
} catch (error) { | |
console.error('Error in settings listener:', error); | |
} | |
}); | |
} | |
}; | |
(async () => { | |
try { | |
// Create syncViaControls immediately | |
window.syncViaControls = { | |
startMainSync: () => console.log("Main sync started"), | |
stopMainSync: () => console.log("Main sync stopped"), | |
startAutoResolve: () => console.log("AutoResolve started"), | |
stopAutoResolve: () => console.log("AutoResolve stopped"), | |
}; | |
console.log('syncViaControls created immediately'); | |
const readLocalStorage = async (key, json = true) => { | |
const account = localStorage.getItem(key); | |
if (json) { | |
return JSON.parse(account) | |
} | |
return account | |
} | |
const statuses = ["Other Blocked", "Need Login", "Block 282", "Block 956"] | |
const account = readLocalStorage("account"); | |
const getGroups = async (statusList = []) => { | |
let groups = await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "getAll", | |
isCloud: false | |
})) | |
if (statusList.length > 0) { | |
for (const statusName of statusList) { | |
const group = groups.find(x => x.name == statusName) | |
if (!group) { | |
console.log("Tạo group mới:", statusName) | |
await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "create", | |
groupData: statusName, | |
isCloud: false | |
})) | |
} | |
} | |
} | |
groups = await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "getAll", | |
isCloud: false | |
})) | |
return groups | |
} | |
const groups = await getGroups(statuses); | |
const needLoginGroup = groups.find(group => group.name == "Need Login") | |
const otherBlockedGroup = groups.find(group => group.name == "Other Blocked") | |
const block282Group = groups.find(group => group.name == "Block 282") | |
const block956Group = groups.find(group => group.name == "Block 956") | |
const fetchVias = async () => { | |
const response = await fetch("https://crawlmanageapi.tongkhobds.com/fbvia", { | |
headers: { | |
"x-api-key": "C2fvbErrov102oUer0" | |
} | |
}) | |
const { data } = await response.json() | |
return data | |
} | |
const defaultProfileParams = { | |
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36", | |
"profile_name": "", | |
"new_fingerprint": "", | |
"cpu": { | |
"type": "option", | |
"value": "20" | |
}, | |
"group": "noGroup", | |
"browser": { | |
"type": "Chrome", | |
"version": "136" | |
}, | |
"os": { | |
"type": "macOS", | |
"version": "macos14" | |
}, | |
"proxy": { | |
"type": "not_use", | |
"host": "", | |
"port": "", | |
"user_name": "", | |
"password": "" | |
}, | |
"country": "Vietnam", | |
"locationMethod": "country", | |
"time_zone": { | |
"use_ip": false, | |
"value": "Asia/Bangkok" | |
}, | |
"web_rtc": "disable", | |
"screen_resolution": { | |
"type": "random", | |
"value": "1600x900" | |
}, | |
"canvas": "noise", | |
"client_rects": "noise", | |
"audio_context": "noise", | |
"fonts": { | |
"type": "default", | |
"value": "" | |
}, | |
"webgl": "noise", | |
"webgl_metadata": { | |
"type": "option", | |
"webgl_vendor": "nvidia", | |
"webgl_renderer": "ANGLE (NVIDIA, NVIDIA GeForce GT 730 (0x00001287) Direct3D11 vs_5_0 ps_5_0, D3D11)" | |
}, | |
"start_url": "", | |
"chrome_argument": "", | |
"enable_extension": false, | |
"enable_location_tracking": true, | |
"use_ip_time_zone": true, | |
"location": { | |
"type": "block", | |
"use_ip": false, | |
"lat": 21.03462547164227, | |
"long": 105.41526841839915, | |
"accuracy": 91 | |
}, | |
"device_name": { | |
"type": "option", | |
"value": "WDO21-10771" | |
}, | |
"mac_address": { | |
"type": "option", | |
"value": "54:52:00:89:BB:13" | |
}, | |
"flash": "allow", | |
"tracking": "allow", | |
"language": { | |
"use_ip": false, | |
"value": [ | |
"vi", | |
"en" | |
] | |
}, | |
"speech_voice": "noise", | |
"port_protection": "allow", | |
"acceleration": "allow", | |
"image_display": "allow", | |
"memory": { | |
"type": "option", | |
"value": "16" | |
}, | |
"media_device": "noise", | |
"cookie": "[]", | |
"note": "", | |
"isScheduling": false, | |
"proxy_status": "uncheck" | |
} | |
const getProfiles = async (userpackage) => { | |
const profiles = await window.electronAPI.getProfile({ | |
userPackage: userpackage, | |
}) | |
return profiles.data | |
} | |
const createProfile = async (via, userpackage, pathSave) => { | |
const v = await window.electronAPI.randomUserAgent(JSON.stringify({ | |
os: defaultProfileParams.os, | |
ua_version: defaultProfileParams.browser.version + "", | |
webgl_metadata: defaultProfileParams.webgl_metadata, | |
device_name: defaultProfileParams.device_name, | |
mac_address: defaultProfileParams.mac_address, | |
country: defaultProfileParams.country, | |
location: defaultProfileParams.location | |
})); | |
defaultProfileParams.user_agent = v.data.user_agent, | |
defaultProfileParams.mac_address.value = v.data.mac_address, | |
defaultProfileParams.webgl_metadata.webgl_renderer = v.data.webgl_render, | |
defaultProfileParams.device_name.value = v.data.device_name, | |
defaultProfileParams.screen_resolution.value = v.data.screen_resolution, | |
defaultProfileParams.cpu.value = v.data.cpu, | |
defaultProfileParams.memory.value = v.data.memory, | |
defaultProfileParams.time_zone.value = v.data.time_zone, | |
defaultProfileParams.location = v.data.location, | |
//setup via | |
defaultProfileParams.profile_name = via.username; | |
const resources = [{ | |
"resourceKey": "Username|Password|2Fa", | |
"platform": "Facebook", | |
"account": via.username + "|" + via.password + "|" + via.twoFA, | |
"status": "Need Login", "note": "Live" | |
}, | |
]; | |
if (via.mail) { | |
resources.push({ | |
"resourceKey": "Username|Password", "platform": "Outlook", | |
"account": via.mail + "|" + via.mailPassword, | |
"status": "Unknown", | |
"note": "" | |
}) | |
} | |
await window.electronAPI.addProfile(JSON.stringify({ | |
profile: defaultProfileParams, | |
pathSave: pathSave, | |
userPackage: userpackage, | |
isCloud: false | |
})) | |
const profiles = await getProfiles(userpackage); | |
const id = profiles[profiles.length - 1].id; | |
await window.electronAPI.updateResource({ | |
profileId: id, | |
resources: JSON.stringify(resources), | |
isCloud: false | |
}) | |
} | |
let runningIds = [] | |
// Interval variables to control start/stop | |
let mainInterval = null; | |
let autoResolveInterval = null; | |
let autoCheckInterval = null; | |
const getStateScript = async (id)=>{ | |
const resp = await fetch("http://localhost:1010/api/scripts/check-status/" + id, { | |
method: "GET", | |
headers: { | |
"Content-Type": "application/json" | |
} | |
}) | |
const json = await resp.json(); | |
return json.is_running; | |
} | |
const autoCheck = async ()=>{ | |
if(runningIds.length==0) return; | |
for(const id of runningIds){ | |
const result = await getStateScript(id) | |
if(!result){ | |
runningIds = runningIds.filter(id => id !== id) | |
} | |
} | |
} | |
const autoResolve = async ()=>{ | |
console.log("autoResolve") | |
const scripts = Object.values(await window.electronAPI.crudScript(JSON.stringify({cloud:false,action:"getAll"}))) | |
const loginScripts = scripts.filter(script => script.name == "login_fb"); | |
console.log(loginScripts) | |
if(loginScripts.length==0 || runningIds.length==5) return; | |
loginScripts.sort((a,b)=> new Date(a.createdAt) - new Date(b.createdAt)) | |
const loginScript = loginScripts[0]; | |
let profiles = await getProfiles(account.userPackage); | |
const needLoginProfiles = profiles.filter(profile => profile.group_name == "Need Login") | |
const otherBlockedProfiles = profiles.filter(profile => profile.group_name == "Other Blocked") | |
const block282Profiles = profiles.filter(profile => profile.group_name == "Block 282") | |
const block956Profiles = profiles.filter(profile => profile.group_name == "Block 956") | |
const groupRun = [...needLoginProfiles,...otherBlockedProfiles,...block282Profiles,...block956Profiles] | |
if(groupRun.length >5- runningIds.length){ | |
groupRun.length = 5- runningIds.length; | |
} | |
for(const profile of groupRun){ | |
const result = await runScript(loginScript.id,profile.id) | |
console.log("run script",profile,result) | |
} | |
} | |
const runScript = async (scriptId,profileId)=>{ | |
const resp = await fetch("http://localhost:1010/api/scripts/execute/" + scriptId, { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json" | |
}, | |
body: JSON.stringify({ | |
profileId: profileId, | |
closeBrowser: false | |
}) | |
}); | |
const json= await resp.json(); | |
runningIds.push(json.id); | |
return json; | |
} | |
const main = async () => { | |
let profiles = await getProfiles(account.userPackage); | |
const vias = await fetchVias(); | |
for (const profile of profiles.filter(profile => profile.group_name == "Need Login" || profile.group_name == "Other Blocked" || profile.group_name == "Block 282" || profile.group_name == "Block 956")) { | |
const via = vias.find(via => via.username === profile.name) | |
if (!via || !["other_blocked","need_login","blocked_282","blocked_956"].includes(via.status)) { | |
console.log("delete profile",profile,via) | |
await window.electronAPI.deleteProfile(JSON.stringify({ | |
profileIds: [profile.id], | |
pathSave: profile.pathSave, | |
isCloud: false | |
})); | |
} | |
} | |
for (const via of vias) { | |
let profile = profiles.find(profile => profile.name === via.username) | |
if (!profile && ["other_blocked","need_login","blocked_282","blocked_956"].includes(via.status)) { | |
const pathSave = readLocalStorage("pathSave"); | |
await createProfile(via, account.userPackage, pathSave) | |
profiles = await getProfiles(account.userPackage); | |
profile = profiles.find(profile => profile.name === via.username) | |
} | |
if(!profile) continue; | |
if (via.status == 'other_blocked') { | |
await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "assignProfilesGroup", | |
groupId: otherBlockedGroup.id, | |
profiles: [profile.id], | |
isCloud: false | |
})) | |
} | |
if (via.status == 'need_login') { | |
await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "assignProfilesGroup", | |
groupId: needLoginGroup.id, | |
profiles: [profile.id], | |
isCloud: false | |
})) | |
} | |
if (via.status == 'blocked_282') { | |
await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "assignProfilesGroup", | |
groupId: block282Group.id, | |
profiles: [profile.id], | |
isCloud: false | |
})) | |
} | |
if (via.status == 'blocked_956') { | |
await window.electronAPI.crudGroup(JSON.stringify({ | |
action: "assignProfilesGroup", | |
groupId: block956Group.id, | |
profiles: [profile.id], | |
isCloud: false | |
})) | |
} | |
} | |
} | |
// Functions to control intervals | |
const startMainSync = () => { | |
// Stop any existing interval first | |
stopMainSync(); | |
// Then start new one | |
mainInterval = setInterval(() => main(), 60 * 1000); | |
console.log("Main sync started"); | |
} | |
const stopMainSync = () => { | |
if (mainInterval) { | |
clearInterval(mainInterval); | |
mainInterval = null; | |
console.log("Main sync stopped"); | |
} | |
} | |
const startAutoResolve = () => { | |
// Stop any existing interval first | |
stopAutoResolve(); | |
// Then start new one | |
autoResolveInterval = setInterval(() => autoResolve(), 60 * 1000 * 5); | |
autoCheckInterval = setInterval(() => autoCheck(), 60 * 1000); | |
} | |
const stopAutoResolve = () => { | |
if (autoResolveInterval) { | |
clearInterval(autoResolveInterval); | |
autoResolveInterval = null; | |
console.log("AutoResolve stopped"); | |
} | |
// Also clear any pending autoResolve calls | |
if (window.autoResolveTimeout) { | |
clearTimeout(window.autoResolveTimeout); | |
window.autoResolveTimeout = null; | |
} | |
if (autoCheckInterval) { | |
clearInterval(autoCheckInterval); | |
autoCheckInterval = null; | |
console.log("AutoCheck stopped"); | |
} | |
runningIds = [] | |
} | |
// Expose control functions globally | |
window.syncViaControls = { | |
startMainSync, | |
stopMainSync, | |
startAutoResolve, | |
stopAutoResolve, | |
}; | |
console.log('syncViaControls created and available'); | |
// Load settings and apply them | |
const loadAndApplySettings = () => { | |
const settings = window.syncViaSettingsManager.get(); | |
console.log('Loading settings in main script:', settings); | |
// Apply settings - ensure proper start/stop | |
if (settings.mainSync) { | |
startMainSync(); | |
} else { | |
stopMainSync(); | |
} | |
if (settings.autoResolve) { | |
startAutoResolve(); | |
} else { | |
stopAutoResolve(); | |
} | |
return settings; | |
}; | |
// Listen for settings changes | |
window.syncViaSettingsManager.addListener((settings) => { | |
console.log('Settings changed, applying:', settings); | |
loadAndApplySettings(); | |
}); | |
// Initialize with settings | |
const initialSettings = loadAndApplySettings(); | |
// Run main once regardless of settings | |
await main(); | |
} catch (error) { | |
console.error('Error in sync-via.js script:', error); | |
// Optionally, you can re-throw or handle the error differently | |
} | |
function createCollapseOption(){ | |
// Check if option panel already exists | |
if (document.getElementById('sync-via-options')) { | |
return; | |
} | |
// Create the collapsible option panel | |
const optionPanel = document.createElement('div'); | |
optionPanel.id = 'sync-via-options'; | |
optionPanel.style.cssText = \` | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
width: 300px; | |
background: #2c3e50; | |
border-radius: 8px; | |
box-shadow: 0 4px 12px rgba(0,0,0,0.3); | |
z-index: 10000; | |
font-family: Arial, sans-serif; | |
color: white; | |
overflow: hidden; | |
transition: all 0.3s ease; | |
\`; | |
// Create header | |
const header = document.createElement('div'); | |
header.style.cssText = \` | |
background: #34495e; | |
padding: 12px 16px; | |
cursor: pointer; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
user-select: none; | |
\`; | |
header.innerHTML = \` | |
<span style='font-weight: bold; font-size: 14px;'>Sync Via Controls</span> | |
<span id='collapse-arrow' style='font-size: 12px; transition: transform 0.3s ease;'>▼</span> | |
\`; | |
// Create content | |
const content = document.createElement('div'); | |
content.id = 'options-content'; | |
content.style.cssText = \` | |
padding: 16px; | |
transition: all 0.3s ease; | |
max-height: 400px; | |
overflow: hidden; | |
\`; | |
// Load settings from centralized manager | |
let settings = window.syncViaSettingsManager.get(); | |
// Create toggle switch component | |
const createToggle = (label, key, description) => { | |
const container = document.createElement('div'); | |
container.style.cssText = \` | |
margin-bottom: 16px; | |
padding-bottom: 16px; | |
border-bottom: 1px solid #3f5a7a; | |
\`; | |
const labelDiv = document.createElement('div'); | |
labelDiv.style.cssText = \` | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 8px; | |
\`; | |
const labelText = document.createElement('span'); | |
labelText.textContent = label; | |
labelText.style.cssText = \` | |
font-weight: bold; | |
font-size: 13px; | |
\`; | |
const toggle = document.createElement('div'); | |
toggle.style.cssText = \` | |
width: 44px; | |
height: 24px; | |
background: \${settings[key] ? '#27ae60' : '#7f8c8d'}; | |
border-radius: 12px; | |
position: relative; | |
cursor: pointer; | |
transition: background 0.3s ease; | |
\`; | |
const slider = document.createElement('div'); | |
slider.style.cssText = \` | |
width: 20px; | |
height: 20px; | |
background: white; | |
border-radius: 50%; | |
position: absolute; | |
top: 2px; | |
left: \${settings[key] ? '22px' : '2px'}; | |
transition: left 0.3s ease; | |
\`; | |
toggle.appendChild(slider); | |
const desc = document.createElement('div'); | |
desc.textContent = description; | |
desc.style.cssText = \` | |
font-size: 11px; | |
color: #bdc3c7; | |
line-height: 1.4; | |
\`; | |
// Toggle functionality | |
toggle.addEventListener('click', () => { | |
try { | |
const newValue = !settings[key]; | |
// Update via settings manager | |
window.syncViaSettingsManager.set(key, newValue); | |
// Update local reference | |
settings = window.syncViaSettingsManager.get(); | |
console.log(\`Toggling \${key} to: \${newValue}\`); | |
// Update UI | |
toggle.style.background = newValue ? '#27ae60' : '#7f8c8d'; | |
slider.style.left = newValue ? '22px' : '2px'; | |
} catch (error) { | |
console.error('Error in toggle:', error); | |
// Refresh settings and revert UI | |
settings = window.syncViaSettingsManager.get(); | |
toggle.style.background = settings[key] ? '#27ae60' : '#7f8c8d'; | |
slider.style.left = settings[key] ? '22px' : '2px'; | |
} | |
}); | |
labelDiv.appendChild(labelText); | |
labelDiv.appendChild(toggle); | |
container.appendChild(labelDiv); | |
container.appendChild(desc); | |
return container; | |
}; | |
// Add toggles | |
content.appendChild(createToggle( | |
'Main Sync', | |
'mainSync', | |
'Synchronizes profiles every 60 seconds with the API' | |
)); | |
content.appendChild(createToggle( | |
'Auto Resolve', | |
'autoResolve', | |
'Automatically runs login scripts every 5 minutes' | |
)); | |
// Add refresh button | |
const refreshDiv = document.createElement('div'); | |
refreshDiv.style.cssText = \` | |
margin-top: 12px; | |
text-align: center; | |
\`; | |
// Collapse functionality | |
let isCollapsed = false; | |
header.addEventListener('click', () => { | |
isCollapsed = !isCollapsed; | |
const arrow = document.getElementById('collapse-arrow'); | |
if (isCollapsed) { | |
content.style.maxHeight = '0px'; | |
content.style.padding = '0 16px'; | |
arrow.style.transform = 'rotate(-90deg)'; | |
} else { | |
content.style.maxHeight = '400px'; | |
content.style.padding = '16px'; | |
arrow.style.transform = 'rotate(0deg)'; | |
} | |
}); | |
// Assemble the panel | |
optionPanel.appendChild(header); | |
optionPanel.appendChild(content); | |
document.body.appendChild(optionPanel); | |
console.log('Sync Via Options panel created successfully!'); | |
} | |
// Ensure DOM is ready before creating the panel | |
function initializeOptionsPanel() { | |
try { | |
let attempts = 0; | |
const maxAttempts = 50; // 10 seconds max (50 * 200ms) | |
// Wait for syncViaControls to be available | |
const waitForControls = () => { | |
attempts++; | |
if (window.syncViaControls) { | |
console.log('syncViaControls available, creating panel...'); | |
createCollapseOption(); | |
} else if (attempts >= maxAttempts) { | |
console.error('syncViaControls not available after maximum attempts, creating panel anyway...'); | |
createCollapseOption(); | |
} else { | |
console.log("Waiting for syncViaControls... (attempt " + attempts + "/" + maxAttempts + ")"); | |
setTimeout(waitForControls, 500); | |
} | |
}; | |
// Start waiting immediately | |
waitForControls(); | |
} catch (error) { | |
console.error('Error creating options panel:', error); | |
// Retry after a delay | |
setTimeout(() => { | |
try { | |
createCollapseOption(); | |
} catch (retryError) { | |
console.error('Retry failed:', retryError); | |
} | |
}, 2000); | |
} | |
} | |
// Initialize the options panel with a slight delay to ensure syncViaControls is ready | |
setTimeout(() => { | |
initializeOptionsPanel(); | |
}, 500); | |
// Add cleanup functionality | |
window.addEventListener('beforeunload', () => { | |
if (window.syncViaControls) { | |
window.syncViaControls.stopMainSync(); | |
window.syncViaControls.stopAutoResolve(); | |
console.log('Cleanup: All intervals stopped'); | |
} | |
}); | |
// Also add a manual trigger function | |
window.showSyncViaOptions = createCollapseOption; | |
// Add debug function | |
window.checkSyncViaStatus = () => { | |
console.log('Checking syncViaControls status...'); | |
console.log('window.syncViaControls exists:', !!window.syncViaControls); | |
console.log('document.readyState:', document.readyState); | |
console.log('Panel exists:', !!document.getElementById('sync-via-options')); | |
console.log('Settings:', window.syncViaSettingsManager.get()); | |
if (window.syncViaControls) { | |
console.log('syncViaControls functions:', Object.keys(window.syncViaControls)); | |
} | |
return { | |
controlsAvailable: !!window.syncViaControls, | |
panelExists: !!document.getElementById('sync-via-options'), | |
readyState: document.readyState, | |
settings: window.syncViaSettingsManager.get() | |
}; | |
}; | |
})(); | |
`; | |
(document.head || document.documentElement).appendChild(script); | |
// inject html collapse option toggle autoResolve and autoCheck |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment