clean up version
This commit is contained in:
575
README.md
575
README.md
@@ -1,575 +0,0 @@
|
||||
# Binance Alpha Farm Agent
|
||||
|
||||
Automated trading agent for Binance Alpha Farm with robust state management and task-step architecture.
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
- [System Architecture](#system-architecture)
|
||||
- [Code Structure & Module Order](#code-structure--module-order)
|
||||
- [Task-Step System](#task-step-system)
|
||||
- [Bot Status Flow](#bot-status-flow)
|
||||
- [State Management](#state-management)
|
||||
- [Navigation State Management](#navigation-state-management)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [Configuration](#configuration)
|
||||
- [API Endpoints](#api-endpoints)
|
||||
- [Development Notes](#development-notes)
|
||||
|
||||
## 🏗️ System Architecture
|
||||
|
||||
The agent follows a **hybrid architecture** combining centralized task definitions with dedicated step execution:
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ AppState │ │ TaskRunner │ │ StepRunner │
|
||||
│ (State Mgmt) │◄──►│ (Task Mgmt) │◄──►│ (Step Exec) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ StateWatchers │ │ TASK_DEFINITIONS│ │ Step Functions │
|
||||
│ (Observers) │ │ (Task Config) │ │ (Step Logic) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Key Components:
|
||||
- **AppState**: Centralized state management with observer pattern
|
||||
- **TaskRunner**: Handles task polling and coordination
|
||||
- **StepRunner**: Executes individual steps with navigation handling
|
||||
- **StateWatchers**: React to state changes (page, login, bot status)
|
||||
- **TASK_DEFINITIONS**: Centralized task configurations
|
||||
- **Step Functions**: Modular step logic (LoginSteps, OrderHistorySteps, BalanceSteps)
|
||||
|
||||
## 📁 Code Structure & Module Order
|
||||
|
||||
The code follows this specific order for maintainability:
|
||||
|
||||
### 1. **Configuration & Constants**
|
||||
```javascript
|
||||
// ====== CONFIGURATION ======
|
||||
const CONFIG = { ... }
|
||||
|
||||
// ====== ENUMS & CONSTANTS ======
|
||||
const BOT_STATUS = { ... }
|
||||
const TASK_TYPES = { ... }
|
||||
const STEP_TYPES = { ... }
|
||||
const LOGIN_METHOD = { ... }
|
||||
const ORDER_TYPE = { ... }
|
||||
const ORDER_STATUS = { ... }
|
||||
const BALANCE_FORMAT = { ... }
|
||||
const GAS_FEE_TYPE = { ... }
|
||||
```
|
||||
|
||||
### 2. **State Management**
|
||||
```javascript
|
||||
// ====== STATE MANAGEMENT ======
|
||||
class AppState { ... }
|
||||
|
||||
// ====== STORAGE MODULE ======
|
||||
const STORAGE = { ... }
|
||||
```
|
||||
|
||||
### 3. **Utility Modules**
|
||||
```javascript
|
||||
// ====== TL UTILITY MODULE ======
|
||||
const TL = { ... }
|
||||
|
||||
// ====== BAF API MODULE ======
|
||||
const BAF = { ... }
|
||||
|
||||
// ====== BINANCE PAGE MODULE ======
|
||||
const BINANCE = { ... }
|
||||
```
|
||||
|
||||
### 4. **Step Functions (Modular Logic)**
|
||||
```javascript
|
||||
// ====== STEP FUNCTIONS ======
|
||||
const LoginSteps = { ... }
|
||||
const OrderHistorySteps = { ... }
|
||||
const BalanceSteps = { ... }
|
||||
```
|
||||
|
||||
### 5. **Task Definitions**
|
||||
```javascript
|
||||
// ====== TASK DEFINITIONS ======
|
||||
const TASK_DEFINITIONS = { ... }
|
||||
```
|
||||
|
||||
### 6. **Core Execution Engine**
|
||||
```javascript
|
||||
// ====== STEP RUNNER ======
|
||||
class StepRunner { ... }
|
||||
|
||||
// ====== TASK RUNNER ======
|
||||
const TaskRunner = { ... }
|
||||
|
||||
// ====== STATE WATCHERS ======
|
||||
const StateWatchers = { ... }
|
||||
```
|
||||
|
||||
### 7. **UI & Initialization**
|
||||
```javascript
|
||||
// ====== UI & MENU ======
|
||||
async function createGM_Menu() { ... }
|
||||
|
||||
// ====== MONITORING ======
|
||||
async function heartbeat_report() { ... }
|
||||
async function monitorPageChanges() { ... }
|
||||
|
||||
// ====== INITIALIZATION ======
|
||||
async function initialize() { ... }
|
||||
```
|
||||
|
||||
## 🔄 Task-Step System
|
||||
|
||||
### Task Types
|
||||
```javascript
|
||||
const TASK_TYPES = {
|
||||
LOGIN: 'login',
|
||||
GET_ORDER_HISTORY: 'get_order_history',
|
||||
GET_BALANCE: 'get_balance',
|
||||
SWAP: 'swap',
|
||||
NO_TASK: 'no_task'
|
||||
};
|
||||
```
|
||||
|
||||
### Step Types by Task
|
||||
|
||||
#### **Login Task**
|
||||
```javascript
|
||||
const STEP_TYPES = {
|
||||
LOGIN: {
|
||||
NAVIGATE_TO_LOGIN: 'NavigateToLogin',
|
||||
SELECT_QR_CODE: 'SelectQRCode',
|
||||
WAIT_FOR_LOGIN: 'WaitForLogin'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### **Order History Task**
|
||||
```javascript
|
||||
const STEP_TYPES = {
|
||||
GET_ORDER_HISTORY: {
|
||||
NAVIGATE_TO_ORDER_HISTORY: 'NavigateToOrderHistory',
|
||||
EXTRACT_ORDER_DATA: 'ExtractOrderData',
|
||||
SEND_ORDER_DATA: 'SendOrderData'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### **Balance Task**
|
||||
```javascript
|
||||
const STEP_TYPES = {
|
||||
GET_BALANCE: {
|
||||
NAVIGATE_TO_BALANCE: 'NavigateToBalance',
|
||||
EXTRACT_BALANCE_DATA: 'ExtractBalanceData',
|
||||
SEND_BALANCE_DATA: 'SendBalanceData'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Task Data Examples
|
||||
|
||||
#### **Login Task**
|
||||
```javascript
|
||||
{
|
||||
type: TASK_TYPES.LOGIN,
|
||||
id: "login_123",
|
||||
data: {
|
||||
method: LOGIN_METHOD.QR_CODE,
|
||||
timeout: 300000, // 5 minutes
|
||||
retry_count: 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Order History Task**
|
||||
```javascript
|
||||
{
|
||||
type: TASK_TYPES.GET_ORDER_HISTORY,
|
||||
id: "order_history_456",
|
||||
data: {
|
||||
limit: 50,
|
||||
date_from: "2024-01-01",
|
||||
date_to: "2024-12-31",
|
||||
status: ORDER_STATUS.COMPLETED
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Balance Task**
|
||||
```javascript
|
||||
{
|
||||
type: TASK_TYPES.GET_BALANCE,
|
||||
id: "balance_789",
|
||||
data: {
|
||||
currencies: ["USDT", "BTC", "ETH"],
|
||||
format: BALANCE_FORMAT.DECIMAL
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🤖 Bot Status Flow
|
||||
|
||||
### Status Transitions
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ ready-for-new- │────►│ performing- │────►│ ready-for-new- │
|
||||
│ tasks │ │ tasks │ │ tasks │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ ▲
|
||||
▼ ▼ │
|
||||
┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ pause- │◄────┤ Task Complete │─────────────┘
|
||||
│ automation │ │ or Error │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Flow Description:
|
||||
1. **ready-for-new-tasks**: Bot polls server for new tasks
|
||||
2. **performing-tasks**: Bot executes task steps
|
||||
3. **pause-automation**: Bot stops (manual or error)
|
||||
4. **Back to ready-for-new-tasks**: After task completion or resume
|
||||
|
||||
### Task Polling Logic:
|
||||
```javascript
|
||||
// Poll only when ready-for-new-tasks
|
||||
if (bot_status === BOT_STATUS.READY_FOR_NEW_TASKS) {
|
||||
const task = await BAF.getTask();
|
||||
if (task.type === TASK_TYPES.NO_TASK) {
|
||||
// Sleep for interval before next poll
|
||||
await delay(CONFIG.task_poll_interval);
|
||||
} else {
|
||||
// Start task execution
|
||||
bot_status = BOT_STATUS.PERFORMING_TASKS;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 State Management
|
||||
|
||||
### AppState Structure
|
||||
```javascript
|
||||
class AppState {
|
||||
data = {
|
||||
// Server configuration
|
||||
server_type: 'prod',
|
||||
server_url: null,
|
||||
|
||||
// Page state
|
||||
current_page: null,
|
||||
is_logged_in: false,
|
||||
|
||||
// Bot status
|
||||
bot_status: BOT_STATUS.READY_FOR_NEW_TASKS,
|
||||
|
||||
// Task state
|
||||
current_task: null,
|
||||
task_data: null,
|
||||
current_step: null,
|
||||
step_data: null,
|
||||
|
||||
// UI state
|
||||
is_loading: false,
|
||||
error_message: null
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Observer Pattern
|
||||
```javascript
|
||||
// Subscribe to state changes
|
||||
APP_STATE.subscribe('bot_status', StateWatchers.onBotStatusChange);
|
||||
APP_STATE.subscribe('current_page', StateWatchers.onCurrentPageChange);
|
||||
APP_STATE.subscribe('is_logged_in', StateWatchers.onLoginStateChange);
|
||||
|
||||
// Notify observers on state change
|
||||
await APP_STATE.update({ bot_status: BOT_STATUS.PERFORMING_TASKS });
|
||||
```
|
||||
|
||||
## 🧭 Navigation State Management
|
||||
|
||||
### Navigation State Structure
|
||||
```javascript
|
||||
{
|
||||
navigating: true,
|
||||
navigation_start: 1703123456789,
|
||||
navigation_target: "https://accounts.binance.com/login",
|
||||
task_id: "task_123",
|
||||
step_index: 0
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation Flow
|
||||
```
|
||||
1. Step executes → ctx.goto('/login')
|
||||
↓
|
||||
2. Mark navigating = true
|
||||
↓
|
||||
3. Save navigation state to storage
|
||||
↓
|
||||
4. window.location.href = '/login'
|
||||
↓
|
||||
5. Page reloads, userscript restarts
|
||||
↓
|
||||
6. resumeTask() → detect navigation state
|
||||
↓
|
||||
7. handleNavigationResume() → check target page
|
||||
↓
|
||||
8. Skip navigation step, continue next step
|
||||
```
|
||||
|
||||
### guardDoubleRun Helper
|
||||
```javascript
|
||||
// Prevents double execution during navigation
|
||||
await TL.guardDoubleRun(ctx, async () => {
|
||||
// Step logic here
|
||||
if (!BINANCE.isOnLoginPage()) {
|
||||
BINANCE.navigateToLogin();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 🛠️ Helper Functions
|
||||
|
||||
### TL Utility Module
|
||||
```javascript
|
||||
const TL = {
|
||||
// Logging
|
||||
log: (level, message, data) => { ... },
|
||||
debug: (level, message, data) => { ... },
|
||||
error: (level, message, error) => { ... },
|
||||
|
||||
// Utilities
|
||||
delay: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
|
||||
notification: (title, text, timeout) => { ... },
|
||||
|
||||
// Navigation guard
|
||||
guardDoubleRun: async (ctx, stepLogic) => { ... },
|
||||
|
||||
// DOM helpers
|
||||
dom: {
|
||||
isVisible: (el) => { ... },
|
||||
isDisabled: (el) => { ... },
|
||||
click: (el) => { ... },
|
||||
scrollToView: (el, behavior) => { ... }
|
||||
},
|
||||
|
||||
// Network helpers
|
||||
net: {
|
||||
gmRequest: (url, init) => { ... }
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### TaskRunner Helpers
|
||||
```javascript
|
||||
const TaskRunner = {
|
||||
// Task validation
|
||||
validateEnum: (value, enumObj, defaultValue) => { ... },
|
||||
validateTaskData: (taskType, taskData) => { ... },
|
||||
|
||||
// Task state
|
||||
getCurrentTaskData: () => { ... },
|
||||
updateTaskData: (newData) => { ... },
|
||||
isCurrentTaskType: (taskType) => { ... },
|
||||
|
||||
// Task execution
|
||||
checkForNewTasks: async () => { ... },
|
||||
continueCurrentStep: async () => { ... }
|
||||
};
|
||||
```
|
||||
|
||||
### StepRunner Context
|
||||
```javascript
|
||||
// Context passed to step functions
|
||||
{
|
||||
task_id: "task_123",
|
||||
task_type: TASK_TYPES.LOGIN,
|
||||
task_data: { method: LOGIN_METHOD.QR_CODE },
|
||||
step_data: {},
|
||||
is_logged_in: false,
|
||||
current_page: BINANCE_PAGES.LOGIN,
|
||||
current_step_name: "NavigateToLogin",
|
||||
|
||||
// Helper methods
|
||||
done: (result) => this.completeTask(result),
|
||||
goto: (url) => this.navigateTo(url),
|
||||
wait: (ms) => this.wait(ms),
|
||||
retry: (fn, maxAttempts) => this.retry(fn, maxAttempts)
|
||||
}
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Server Configuration
|
||||
```javascript
|
||||
const CONFIG = {
|
||||
heartbeat_interval: 10000, // 10 seconds
|
||||
task_poll_interval: 10000, // 10 seconds
|
||||
is_debug: true,
|
||||
servers: {
|
||||
local: {
|
||||
label: '🏠 Local',
|
||||
url: 'http://localhost:3000'
|
||||
},
|
||||
prod: {
|
||||
label: '🌐 Prod',
|
||||
url: 'https://baf.thuanle.me'
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Storage Keys
|
||||
```javascript
|
||||
const STORAGE = {
|
||||
key_token: 'baf-agent-token',
|
||||
key_server_type: 'baf-server-type',
|
||||
key_bot_status: 'baf-bot-status',
|
||||
key_navigation_state: 'baf-navigation-state'
|
||||
};
|
||||
```
|
||||
|
||||
## 🌐 API Endpoints
|
||||
|
||||
### BAF API Methods
|
||||
```javascript
|
||||
const BAF = {
|
||||
// Server management
|
||||
getServer: async () => { ... },
|
||||
getHost: async () => { ... },
|
||||
|
||||
// API requests
|
||||
request: async (method, path, options) => { ... },
|
||||
|
||||
// Task management
|
||||
getTask: async () => { ... },
|
||||
submitTaskResult: async (taskId, result) => { ... },
|
||||
|
||||
// Heartbeat
|
||||
heartbeat: async (data) => { ... }
|
||||
};
|
||||
```
|
||||
|
||||
### Page Detection
|
||||
```javascript
|
||||
const BINANCE = {
|
||||
// Page detection
|
||||
detectPage: () => { ... },
|
||||
isOnLoginPage: () => { ... },
|
||||
isOnAlphaSwapPage: () => { ... },
|
||||
isOnAlphaOrderHistoryPage: () => { ... },
|
||||
isLoggedIn: async () => { ... },
|
||||
|
||||
// Navigation
|
||||
navigateToLogin: () => { ... },
|
||||
navigateToAlphaSwap: () => { ... },
|
||||
navigateToAlphaOrderHistory: () => { ... }
|
||||
};
|
||||
```
|
||||
|
||||
## 🚀 Development Notes
|
||||
|
||||
### Adding New Tasks
|
||||
|
||||
1. **Define Task Type**:
|
||||
```javascript
|
||||
const TASK_TYPES = {
|
||||
// ... existing types
|
||||
NEW_TASK: 'new-task'
|
||||
};
|
||||
```
|
||||
|
||||
2. **Create Step Functions**:
|
||||
```javascript
|
||||
const NewTaskSteps = {
|
||||
matchStep1: (url, ctx) => { /* match logic */ },
|
||||
runStep1: async (ctx) => {
|
||||
await TL.guardDoubleRun(ctx, async () => {
|
||||
// Step logic
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **Add Task Definition**:
|
||||
```javascript
|
||||
const TASK_DEFINITIONS = {
|
||||
[TASK_TYPES.NEW_TASK]: {
|
||||
id: "new-task",
|
||||
steps: [
|
||||
{
|
||||
name: "Step1",
|
||||
match: NewTaskSteps.matchStep1,
|
||||
run: NewTaskSteps.runStep1
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Adding New Steps
|
||||
|
||||
1. **Add Step Type**:
|
||||
```javascript
|
||||
const STEP_TYPES = {
|
||||
NEW_TASK: {
|
||||
// ... existing steps
|
||||
NEW_STEP: 'NewStep'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
2. **Implement Step Functions**:
|
||||
```javascript
|
||||
const NewTaskSteps = {
|
||||
// ... existing steps
|
||||
matchNewStep: (url, ctx) => {
|
||||
return url.includes("/target-page") && ctx?.step_data?.previous_step_completed;
|
||||
},
|
||||
|
||||
runNewStep: async (ctx) => {
|
||||
await TL.guardDoubleRun(ctx, async () => {
|
||||
// Step logic here
|
||||
ctx.step_data.new_step_completed = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **Update Task Definition**:
|
||||
```javascript
|
||||
[TASK_TYPES.NEW_TASK]: {
|
||||
id: "new-task",
|
||||
steps: [
|
||||
// ... existing steps
|
||||
{
|
||||
name: "NewStep",
|
||||
match: NewTaskSteps.matchNewStep,
|
||||
run: NewTaskSteps.runNewStep
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Always use `guardDoubleRun`** for step functions to prevent double execution
|
||||
2. **Use enums** for type safety (TASK_TYPES, LOGIN_METHOD, etc.)
|
||||
3. **Validate task data** using `TaskRunner.validateTaskData`
|
||||
4. **Handle navigation** using `ctx.goto()` instead of direct `window.location`
|
||||
5. **Use context helpers** (`ctx.wait()`, `ctx.retry()`) for robust execution
|
||||
6. **Log appropriately** using `TL.log()`, `TL.debug()`, `TL.error()`
|
||||
7. **Update step_data** to track progress and avoid duplicate work
|
||||
8. **Handle errors gracefully** with try-catch and proper error reporting
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
1. **Enable debug mode**: Set `CONFIG.is_debug = true`
|
||||
2. **Check navigation state**: Use `STORAGE.getNavigationState()`
|
||||
3. **Monitor state changes**: Watch `APP_STATE.getData()` values
|
||||
4. **Check step execution**: Look for `[STEP]` logs in console
|
||||
5. **Verify page detection**: Use `BINANCE.detectPage()` to check current page
|
||||
6. **Test step matching**: Use step `match` functions directly
|
||||
7. **Check task data**: Use `TaskRunner.getCurrentTaskData()`
|
||||
1939
agent.user.js
1939
agent.user.js
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user