Archived
Update agent.user.js to version 2025.07.31, adding new storage and network helper functions, server management capabilities, and menu commands for easier token and server mode management.
This commit is contained in:
1 parent
164fd2a179
commit
ab0a553b96
1 file changed
+137
-124
+137
-124
@@ -1,12 +1,17 @@
|
||||
// ==UserScript==
|
||||
// @name Binance Alpha Farm Agent
|
||||
// @namespace http://baf.thuanle.me
|
||||
// @version 2025.07.30
|
||||
// @version 2025.07.31
|
||||
// @author TL
|
||||
// @match https://www.binance.com/en/alpha/bsc/*
|
||||
// @run-at document-idle
|
||||
// @grant GM_setValue
|
||||
// @grant GM_getValue
|
||||
// @grant GM_registerMenuCommand
|
||||
// @grant GM_notification
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @connect baf.thuanle.me
|
||||
// @connect localhost
|
||||
// @downloadURL https://git.thuanle.me/public/binance-alpha-farm-agent/raw/branch/main/agent.user.js
|
||||
// @updateURL https://git.thuanle.me/public/binance-alpha-farm-agent/raw/branch/main/agent.user.js
|
||||
// ==/UserScript==
|
||||
@@ -14,140 +19,148 @@
|
||||
(async () => {
|
||||
'use strict';
|
||||
|
||||
// Utility functions
|
||||
// ====== Storage ======
|
||||
const STORAGE = {
|
||||
key_token: 'baf-agent-token',
|
||||
getToken: () => GM_getValue(STORAGE.key_token, ''),
|
||||
setToken: token => GM_setValue(STORAGE.key_token, String(token || '').trim().replace(/^Bearer\s+/i, '')),
|
||||
|
||||
key_server_mode: 'baf-server-mode', // 'local' | 'prod'
|
||||
getServerMode: () => GM_getValue(STORAGE.key_server_mode, 'prod'),
|
||||
setServerMode: (mode) => GM_setValue(STORAGE.key_server_mode, mode),
|
||||
};
|
||||
|
||||
// ====== Servers (có icon) ======
|
||||
const BAF_SERVERS = {
|
||||
local: { label: '🏠 Local', url: 'http://localhost:3000' },
|
||||
prod: { label: '🌐 Prod', url: 'https://baf.thuanle.me' },
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ====== Utility ======
|
||||
const TL = {
|
||||
log: (msg, ...args) => console.log(`[TL] ${msg}`, ...args),
|
||||
delay: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
||||
getEl: sel => document.querySelector(sel),
|
||||
waitForEl: async (sel, timeout = 10000) => {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) return el;
|
||||
await TL.delay(100);
|
||||
}
|
||||
throw new Error(`waitForEl: Timeout waiting for ${sel}`);
|
||||
},
|
||||
error: (msg, ...args) => console.error(`[TL] ${msg}`, ...args),
|
||||
};
|
||||
|
||||
eventFillFloatInputById(id, value) {
|
||||
const input = document.getElementById(id);
|
||||
if (!input) return;
|
||||
const current = parseFloat(input.value);
|
||||
const target = parseFloat(value);
|
||||
if (Math.abs(current - target) < 1e-8) return;
|
||||
|
||||
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
||||
setter.call(input, value);
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
// ====== Network helpers (GM_xmlhttpRequest) ======
|
||||
TL.net = {
|
||||
gmRequest(url, init = {}) {
|
||||
const headersToObject = (h) => {
|
||||
const o = {};
|
||||
(h || new Headers()).forEach((v, k) => { o[k] = v; });
|
||||
return o;
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
GM_xmlhttpRequest({
|
||||
url,
|
||||
method: (init.method || 'GET').toUpperCase(),
|
||||
headers: headersToObject(init.headers),
|
||||
data: init.body,
|
||||
onload: (resp) => {
|
||||
const text = resp.responseText || '';
|
||||
let data = text;
|
||||
const isJSON = /content-type:\s*application\/json/i.test(resp.responseHeaders || '');
|
||||
if (isJSON) { try { data = JSON.parse(text); } catch { } }
|
||||
TL.log(`[GM] ${resp.status} ${url}`, data);
|
||||
resolve({
|
||||
status: resp.status,
|
||||
ok: resp.status >= 200 && resp.status < 300,
|
||||
data,
|
||||
rawText: text,
|
||||
headers: null
|
||||
});
|
||||
},
|
||||
onerror: reject,
|
||||
ontimeout: () => reject(new Error('GM_xmlhttpRequest timeout')),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// POP UP HELPER
|
||||
const PopupHelper = {
|
||||
defaultStyle: `
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
background: white;
|
||||
color: #000;
|
||||
border: 1px solid #ccc;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
z-index: 999999;
|
||||
font-family: sans-serif;
|
||||
font-size: 12px;
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
`,
|
||||
|
||||
createOrUpdatePopup(id, options = {}) {
|
||||
try {
|
||||
const {
|
||||
html = '',
|
||||
position = 'bottom-right',
|
||||
customStyle = '',
|
||||
onClick = null
|
||||
} = options;
|
||||
|
||||
TL.log(`🔍 Creating/updating popup with ID: ${id}`);
|
||||
|
||||
let popup = document.getElementById(id);
|
||||
if (!popup) {
|
||||
popup = document.createElement('div');
|
||||
popup.id = id;
|
||||
document.body.appendChild(popup);
|
||||
TL.log(`🔍 Created new popup element with ID: ${id}`);
|
||||
} else {
|
||||
TL.log(`🔍 Found existing popup with ID: ${id}`);
|
||||
}
|
||||
|
||||
// Set position based on position parameter
|
||||
let positionStyle = '';
|
||||
switch (position) {
|
||||
case 'bottom-right':
|
||||
positionStyle = 'right: 20px;';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
positionStyle = 'left: 20px;';
|
||||
break;
|
||||
case 'top-right':
|
||||
positionStyle = 'top: 20px; right: 20px;';
|
||||
break;
|
||||
case 'top-left':
|
||||
positionStyle = 'top: 20px; left: 20px;';
|
||||
break;
|
||||
case 'center':
|
||||
positionStyle = 'top: 50%; left: 50%; transform: translate(-50%, -50%);';
|
||||
break;
|
||||
default:
|
||||
positionStyle = 'right: 20px;';
|
||||
}
|
||||
|
||||
// Apply styles
|
||||
const finalStyle = this.defaultStyle + positionStyle + customStyle;
|
||||
popup.style.cssText = finalStyle;
|
||||
TL.log(`🔍 Applied styles to popup: ${finalStyle.substring(0, 100)}...`);
|
||||
|
||||
// Set content
|
||||
popup.innerHTML = html;
|
||||
TL.log(`🔍 Set HTML content to popup (length: ${html.length})`);
|
||||
|
||||
// Add click handler if provided
|
||||
if (onClick && typeof onClick === 'function') {
|
||||
popup.onclick = onClick;
|
||||
}
|
||||
|
||||
TL.log(`✅ Popup ${id} created/updated successfully`);
|
||||
return popup;
|
||||
} catch (error) {
|
||||
TL.log(`❌ Error in createOrUpdatePopup: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
// ====== BAF API ======
|
||||
const BAF = {
|
||||
// Trả về object {label, url} theo mode hiện tại
|
||||
getServer: async () => {
|
||||
const mode = await STORAGE.getServerMode();
|
||||
return BAF_SERVERS[mode] || BAF_SERVERS.prod;
|
||||
},
|
||||
|
||||
removePopup(id) {
|
||||
const popup = document.getElementById(id);
|
||||
if (popup) {
|
||||
popup.remove();
|
||||
}
|
||||
// Trả về URL host hiện tại
|
||||
getHost: async () => {
|
||||
const s = await BAF.getServer();
|
||||
return s.url;
|
||||
},
|
||||
|
||||
// Wrapper GMXHR
|
||||
request: async (method, path, { params, body, headers } = {}) => {
|
||||
const base = await BAF.getHost();
|
||||
const url = new URL(path, base);
|
||||
if (params) for (const [k, v] of Object.entries(params)) url.searchParams.append(k, v);
|
||||
|
||||
const h = new Headers(headers || {});
|
||||
const token = await STORAGE.getToken();
|
||||
if (token && !h.has('Authorization')) h.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
const init = { method, headers: h };
|
||||
if (body !== undefined && body !== null) {
|
||||
if (!(body instanceof FormData) && !h.has('Content-Type')) h.set('Content-Type', 'application/json');
|
||||
init.body = (h.get('Content-Type') || '').includes('application/json') && typeof body !== 'string'
|
||||
? JSON.stringify(body)
|
||||
: body;
|
||||
}
|
||||
return TL.net.gmRequest(url.toString(), init);
|
||||
},
|
||||
|
||||
get: (path, params, init = {}) => BAF.request('GET', path, { params, headers: init.headers }),
|
||||
post: (path, body, init = {}) => BAF.request('POST', path, { body, headers: init.headers }),
|
||||
|
||||
ping: () => BAF.post('/agent/ping'),
|
||||
};
|
||||
|
||||
// Main script logic
|
||||
// show welcome popup
|
||||
PopupHelper.createOrUpdatePopup('welcome-popup', {
|
||||
html: `
|
||||
<div>
|
||||
<h2>Welcome to Binance Alpha Farm Agent</h2>
|
||||
<p>This script will help you manage your Binance Alpha Farm tasks more efficiently.</p>
|
||||
<p>Click <a href="https://baf.thuanle.me" target="_blank">here</a> to learn more.</p>
|
||||
<button id="close-welcome-popup">Close</button>
|
||||
</div>
|
||||
`, onClick: () => {
|
||||
PopupHelper.removePopup('welcome-popup');
|
||||
}
|
||||
});
|
||||
// ====== Cấu hình menu ======
|
||||
async function createGM_Menu() {
|
||||
const curSrv = await BAF.getServer();
|
||||
GM_registerMenuCommand(`Server: ${curSrv.label} (${curSrv.url})`, async () => {
|
||||
try {
|
||||
const cur = await STORAGE.getServerMode();
|
||||
const next = (cur === 'local') ? 'prod' : 'local';
|
||||
await STORAGE.setServerMode(next);
|
||||
const nsv = await BAF.getServer();
|
||||
const msg = `Switched to ${nsv.label} (${nsv.url})`;
|
||||
TL.log(msg);
|
||||
GM_notification?.({ title: 'BAF Server Switched', text: msg, timeout: 2500 });
|
||||
alert(msg);
|
||||
} catch (e) {
|
||||
TL.error('switch server error', e);
|
||||
alert('Switch server error: ' + e.message);
|
||||
}
|
||||
});
|
||||
|
||||
GM_registerMenuCommand('Token', async () => {
|
||||
try {
|
||||
const curToken = await STORAGE.getToken();
|
||||
const input = prompt('Bearer token:', curToken || '');
|
||||
if (input !== null && input.trim() && input.trim() !== curToken) {
|
||||
await STORAGE.setToken(input);
|
||||
}
|
||||
const s = await BAF.getServer();
|
||||
const res = await BAF.ping();
|
||||
const resStr =
|
||||
`Server: ${s.label} (${s.url})\n` +
|
||||
`Status: ${res.ok ? 'Connected ✅' : 'Failed ❌'} (${res.status})`;
|
||||
TL.log(resStr);
|
||||
alert(resStr);
|
||||
} catch (e) {
|
||||
const resStr = `ping error: ${e.message}`;
|
||||
TL.error(resStr);
|
||||
alert(resStr);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// ====== Khởi tạo ======
|
||||
await createGM_Menu();
|
||||
})();
|
||||
Reference in new issue
Block a user