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
+138
-125
+138
-125
@@ -1,12 +1,17 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Binance Alpha Farm Agent
|
// @name Binance Alpha Farm Agent
|
||||||
// @namespace http://baf.thuanle.me
|
// @namespace http://baf.thuanle.me
|
||||||
// @version 2025.07.30
|
// @version 2025.07.31
|
||||||
// @author TL
|
// @author TL
|
||||||
// @match https://www.binance.com/en/alpha/bsc/*
|
// @match https://www.binance.com/en/alpha/bsc/*
|
||||||
// @run-at document-idle
|
// @run-at document-idle
|
||||||
// @grant GM_setValue
|
// @grant GM_setValue
|
||||||
// @grant GM_getValue
|
// @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
|
// @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
|
// @updateURL https://git.thuanle.me/public/binance-alpha-farm-agent/raw/branch/main/agent.user.js
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
@@ -14,140 +19,148 @@
|
|||||||
(async () => {
|
(async () => {
|
||||||
'use strict';
|
'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 = {
|
const TL = {
|
||||||
log: (msg, ...args) => console.log(`[TL] ${msg}`, ...args),
|
log: (msg, ...args) => console.log(`[TL] ${msg}`, ...args),
|
||||||
delay: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
error: (msg, ...args) => console.error(`[TL] ${msg}`, ...args),
|
||||||
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}`);
|
|
||||||
},
|
|
||||||
|
|
||||||
eventFillFloatInputById(id, value) {
|
// ====== Network helpers (GM_xmlhttpRequest) ======
|
||||||
const input = document.getElementById(id);
|
TL.net = {
|
||||||
if (!input) return;
|
gmRequest(url, init = {}) {
|
||||||
const current = parseFloat(input.value);
|
const headersToObject = (h) => {
|
||||||
const target = parseFloat(value);
|
const o = {};
|
||||||
if (Math.abs(current - target) < 1e-8) return;
|
(h || new Headers()).forEach((v, k) => { o[k] = v; });
|
||||||
|
return o;
|
||||||
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
};
|
||||||
setter.call(input, value);
|
return new Promise((resolve, reject) => {
|
||||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
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
|
// ====== BAF API ======
|
||||||
const PopupHelper = {
|
const BAF = {
|
||||||
defaultStyle: `
|
// Trả về object {label, url} theo mode hiện tại
|
||||||
position: fixed;
|
getServer: async () => {
|
||||||
bottom: 20px;
|
const mode = await STORAGE.getServerMode();
|
||||||
background: white;
|
return BAF_SERVERS[mode] || BAF_SERVERS.prod;
|
||||||
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;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
removePopup(id) {
|
// Trả về URL host hiện tại
|
||||||
const popup = document.getElementById(id);
|
getHost: async () => {
|
||||||
if (popup) {
|
const s = await BAF.getServer();
|
||||||
popup.remove();
|
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
|
// ====== Cấu hình menu ======
|
||||||
// show welcome popup
|
async function createGM_Menu() {
|
||||||
PopupHelper.createOrUpdatePopup('welcome-popup', {
|
const curSrv = await BAF.getServer();
|
||||||
html: `
|
GM_registerMenuCommand(`Server: ${curSrv.label} (${curSrv.url})`, async () => {
|
||||||
<div>
|
try {
|
||||||
<h2>Welcome to Binance Alpha Farm Agent</h2>
|
const cur = await STORAGE.getServerMode();
|
||||||
<p>This script will help you manage your Binance Alpha Farm tasks more efficiently.</p>
|
const next = (cur === 'local') ? 'prod' : 'local';
|
||||||
<p>Click <a href="https://baf.thuanle.me" target="_blank">here</a> to learn more.</p>
|
await STORAGE.setServerMode(next);
|
||||||
<button id="close-welcome-popup">Close</button>
|
const nsv = await BAF.getServer();
|
||||||
</div>
|
const msg = `Switched to ${nsv.label} (${nsv.url})`;
|
||||||
`, onClick: () => {
|
TL.log(msg);
|
||||||
PopupHelper.removePopup('welcome-popup');
|
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