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
+140
-127
+140
-127
@@ -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