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:
267
agent.user.js
267
agent.user.js
@@ -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();
|
// ====== Network helpers (GM_xmlhttpRequest) ======
|
||||||
while (Date.now() - start < timeout) {
|
TL.net = {
|
||||||
const el = document.querySelector(sel);
|
gmRequest(url, init = {}) {
|
||||||
if (el) return el;
|
const headersToObject = (h) => {
|
||||||
await TL.delay(100);
|
const o = {};
|
||||||
}
|
(h || new Headers()).forEach((v, k) => { o[k] = v; });
|
||||||
throw new Error(`waitForEl: Timeout waiting for ${sel}`);
|
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,
|
||||||
eventFillFloatInputById(id, value) {
|
ontimeout: () => reject(new Error('GM_xmlhttpRequest timeout')),
|
||||||
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 }));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 = {}) {
|
// 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'),
|
||||||
|
};
|
||||||
|
|
||||||
|
// ====== Cấu hình menu ======
|
||||||
|
async function createGM_Menu() {
|
||||||
|
const curSrv = await BAF.getServer();
|
||||||
|
GM_registerMenuCommand(`Server: ${curSrv.label} (${curSrv.url})`, async () => {
|
||||||
try {
|
try {
|
||||||
const {
|
const cur = await STORAGE.getServerMode();
|
||||||
html = '',
|
const next = (cur === 'local') ? 'prod' : 'local';
|
||||||
position = 'bottom-right',
|
await STORAGE.setServerMode(next);
|
||||||
customStyle = '',
|
const nsv = await BAF.getServer();
|
||||||
onClick = null
|
const msg = `Switched to ${nsv.label} (${nsv.url})`;
|
||||||
} = options;
|
TL.log(msg);
|
||||||
|
GM_notification?.({ title: 'BAF Server Switched', text: msg, timeout: 2500 });
|
||||||
TL.log(`🔍 Creating/updating popup with ID: ${id}`);
|
alert(msg);
|
||||||
|
} catch (e) {
|
||||||
let popup = document.getElementById(id);
|
TL.error('switch server error', e);
|
||||||
if (!popup) {
|
alert('Switch server error: ' + e.message);
|
||||||
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) {
|
|
||||||
const popup = document.getElementById(id);
|
|
||||||
if (popup) {
|
|
||||||
popup.remove();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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