clawreum-sdk 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -36
- package/package.json +1 -1
- package/src/index.js +297 -35
package/README.md
CHANGED
|
@@ -15,24 +15,24 @@ const ClawreumMiner = require('clawreum-sdk');
|
|
|
15
15
|
|
|
16
16
|
const miner = new ClawreumMiner({
|
|
17
17
|
platform: 'telegram', // 'telegram' | 'discord' | 'whatsapp'
|
|
18
|
-
botId: '123456789', //
|
|
19
|
-
botName: 'MyMiningBot' //
|
|
18
|
+
botId: '123456789', // Platform bot ID
|
|
19
|
+
botName: 'MyMiningBot' // Bot display name
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// Event listeners
|
|
23
23
|
miner.on('reward', (data) => {
|
|
24
24
|
console.log(`+${data.reward.toFixed(4)} CLAWREUM`);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
miner.on('balance', (data) => {
|
|
28
|
-
console.log(
|
|
28
|
+
console.log(`Refining: ${data.refining}, Claimable: ${data.claimable}`);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
miner.on('error', (err) => {
|
|
32
|
-
console.error('
|
|
32
|
+
console.error('Error:', err.message);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Start mining
|
|
36
36
|
miner.start();
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -40,41 +40,41 @@ miner.start();
|
|
|
40
40
|
|
|
41
41
|
| Option | Type | Required | Default | Description |
|
|
42
42
|
|--------|------|----------|---------|-------------|
|
|
43
|
-
| `platform` | string |
|
|
44
|
-
| `botId` | string |
|
|
45
|
-
| `botName` | string |
|
|
46
|
-
| `server` | string | | `wss://api.clawreum.com` | WebSocket
|
|
47
|
-
| `ownerWallet` | string | | - |
|
|
48
|
-
| `autoReconnect` | boolean | | `true` |
|
|
49
|
-
| `miningInterval` | number | | `250` |
|
|
43
|
+
| `platform` | string | Yes | - | Bot platform (`telegram`, `discord`, `whatsapp`) |
|
|
44
|
+
| `botId` | string | Yes | - | Platform bot ID |
|
|
45
|
+
| `botName` | string | Yes | - | Bot display name |
|
|
46
|
+
| `server` | string | No | `wss://api.clawreum.com` | WebSocket server URL |
|
|
47
|
+
| `ownerWallet` | string | No | - | Wallet address for rewards |
|
|
48
|
+
| `autoReconnect` | boolean | No | `true` | Auto reconnect on disconnect |
|
|
49
|
+
| `miningInterval` | number | No | `250` | Mining action interval (ms) |
|
|
50
50
|
|
|
51
51
|
## Events
|
|
52
52
|
|
|
53
53
|
| Event | Data | Description |
|
|
54
54
|
|-------|------|-------------|
|
|
55
|
-
| `registered` | `{ botId, characterName }` |
|
|
56
|
-
| `authenticated` | `{ botName }` |
|
|
57
|
-
| `joined` | `{ room, botId }` |
|
|
58
|
-
| `mining` | `{ started: true }` |
|
|
59
|
-
| `progress` | `{ progress }` |
|
|
60
|
-
| `reward` | `{ reward, base, bonus, packId }` |
|
|
61
|
-
| `balance` | `{ refining, claimable, world, boost }` |
|
|
62
|
-
| `block` | `{ block, pack, difficulty }` |
|
|
63
|
-
| `disconnected` | `{ code, reason }` |
|
|
64
|
-
| `stopped` | - |
|
|
65
|
-
| `error` | `Error` |
|
|
66
|
-
| `log` | `string` |
|
|
55
|
+
| `registered` | `{ botId, characterName }` | Bot registration complete |
|
|
56
|
+
| `authenticated` | `{ botName }` | Authentication successful |
|
|
57
|
+
| `joined` | `{ room, botId }` | Joined mining room |
|
|
58
|
+
| `mining` | `{ started: true }` | Mining loop started |
|
|
59
|
+
| `progress` | `{ progress }` | Mining progress (0~1) |
|
|
60
|
+
| `reward` | `{ reward, base, bonus, packId }` | Mining reward received |
|
|
61
|
+
| `balance` | `{ refining, claimable, world, boost }` | Balance sync |
|
|
62
|
+
| `block` | `{ block, pack, difficulty }` | Block update |
|
|
63
|
+
| `disconnected` | `{ code, reason }` | Connection closed |
|
|
64
|
+
| `stopped` | - | Mining stopped |
|
|
65
|
+
| `error` | `Error` | Error occurred |
|
|
66
|
+
| `log` | `string` | Log message |
|
|
67
67
|
|
|
68
68
|
## Methods
|
|
69
69
|
|
|
70
70
|
### `start()`
|
|
71
|
-
|
|
71
|
+
Start mining.
|
|
72
72
|
|
|
73
73
|
### `stop()`
|
|
74
|
-
|
|
74
|
+
Stop mining and close connection.
|
|
75
75
|
|
|
76
76
|
### `getStatus()`
|
|
77
|
-
|
|
77
|
+
Get current status.
|
|
78
78
|
|
|
79
79
|
```javascript
|
|
80
80
|
const status = miner.getStatus();
|
|
@@ -104,27 +104,27 @@ miner.on('log', console.log);
|
|
|
104
104
|
miner.on('error', console.error);
|
|
105
105
|
|
|
106
106
|
miner.on('registered', ({ botId }) => {
|
|
107
|
-
console.log(
|
|
107
|
+
console.log(`Bot registered: ${botId}`);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
miner.on('reward', ({ reward, bonus }) => {
|
|
111
|
-
console.log(
|
|
111
|
+
console.log(`Mining success! +${reward.toFixed(4)} (bonus: ${bonus.toFixed(4)})`);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
miner.on('balance', ({ refining, claimable }) => {
|
|
115
|
-
console.log(
|
|
115
|
+
console.log(`Balance - Refining: ${refining.toFixed(2)}, Claimable: ${claimable.toFixed(2)}`);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
//
|
|
118
|
+
// Start
|
|
119
119
|
miner.start();
|
|
120
120
|
|
|
121
|
-
//
|
|
121
|
+
// Check status after 10 minutes
|
|
122
122
|
setTimeout(() => {
|
|
123
123
|
const status = miner.getStatus();
|
|
124
|
-
console.log(
|
|
124
|
+
console.log(`Total mined: ${status.stats.totalMined.toFixed(4)} CLAWREUM`);
|
|
125
125
|
}, 600000);
|
|
126
126
|
|
|
127
|
-
//
|
|
127
|
+
// Graceful shutdown
|
|
128
128
|
process.on('SIGINT', () => {
|
|
129
129
|
miner.stop();
|
|
130
130
|
process.exit();
|
|
@@ -134,7 +134,7 @@ process.on('SIGINT', () => {
|
|
|
134
134
|
## Requirements
|
|
135
135
|
|
|
136
136
|
- Node.js 18+
|
|
137
|
-
-
|
|
137
|
+
- Registered bot (Telegram/Discord/WhatsApp)
|
|
138
138
|
|
|
139
139
|
## License
|
|
140
140
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Clawreum Mining SDK
|
|
2
|
+
* Clawreum Mining SDK v2.0.0
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Rock-based mining system (compete for rocks)
|
|
6
|
+
* - AI chat integration (chatHandler callback)
|
|
7
|
+
* - Context system for AI decision making
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
5
10
|
* const ClawreumMiner = require('clawreum-sdk');
|
|
6
11
|
*
|
|
7
12
|
* const miner = new ClawreumMiner({
|
|
8
13
|
* platform: 'telegram',
|
|
9
14
|
* botId: '123456789',
|
|
10
|
-
* botName: 'MyMiningBot'
|
|
15
|
+
* botName: 'MyMiningBot',
|
|
16
|
+
* chatHandler: async (context) => {
|
|
17
|
+
* // Your AI logic here (OpenAI, Claude, Ollama, etc.)
|
|
18
|
+
* return "I'm mining!";
|
|
19
|
+
* }
|
|
11
20
|
* });
|
|
12
21
|
*
|
|
13
22
|
* miner.on('reward', (data) => console.log(`+${data.reward} CLAWREUM`));
|
|
@@ -25,24 +34,29 @@ class ClawreumMiner extends EventEmitter {
|
|
|
25
34
|
/**
|
|
26
35
|
* @param {Object} options
|
|
27
36
|
* @param {string} options.platform - 'telegram' | 'discord' | 'whatsapp'
|
|
28
|
-
* @param {string} options.botId -
|
|
29
|
-
* @param {string} options.botName -
|
|
30
|
-
* @param {string} [options.server] - WebSocket
|
|
31
|
-
* @param {string} [options.ownerWallet] -
|
|
32
|
-
* @param {boolean} [options.autoReconnect] -
|
|
33
|
-
* @param {number} [options.miningInterval] -
|
|
37
|
+
* @param {string} options.botId - Platform bot ID
|
|
38
|
+
* @param {string} options.botName - Bot display name
|
|
39
|
+
* @param {string} [options.server] - WebSocket server URL (default: wss://api.clawreum.com)
|
|
40
|
+
* @param {string} [options.ownerWallet] - Wallet address for rewards
|
|
41
|
+
* @param {boolean} [options.autoReconnect] - Auto reconnect (default: true)
|
|
42
|
+
* @param {number} [options.miningInterval] - Mining action interval in ms (default: 250)
|
|
43
|
+
* @param {number} [options.moveSpeed] - Movement speed (default: 5)
|
|
44
|
+
* @param {Function} [options.chatHandler] - Async function for AI chat (context) => string
|
|
45
|
+
* @param {number} [options.chatInterval] - Chat interval in ms when idle (default: 30000)
|
|
34
46
|
*/
|
|
35
47
|
constructor(options = {}) {
|
|
36
48
|
super();
|
|
37
49
|
|
|
38
50
|
if (!options.platform || !options.botId || !options.botName) {
|
|
39
|
-
throw new Error('
|
|
51
|
+
throw new Error('Required options: platform, botId, botName');
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
this.options = {
|
|
43
55
|
server: DEFAULT_SERVER,
|
|
44
56
|
autoReconnect: true,
|
|
45
57
|
miningInterval: 250,
|
|
58
|
+
moveSpeed: 5,
|
|
59
|
+
chatInterval: 30000,
|
|
46
60
|
...options
|
|
47
61
|
};
|
|
48
62
|
|
|
@@ -51,29 +65,46 @@ class ClawreumMiner extends EventEmitter {
|
|
|
51
65
|
this.sessionSecret = null;
|
|
52
66
|
this.currentRoom = null;
|
|
53
67
|
this.miningLoop = null;
|
|
68
|
+
this.chatLoop = null;
|
|
54
69
|
this.reconnectAttempts = 0;
|
|
55
70
|
this.maxReconnectAttempts = 10;
|
|
56
71
|
this.botId = null;
|
|
57
72
|
|
|
58
|
-
//
|
|
73
|
+
// Position and movement
|
|
74
|
+
this.position = { x: 0, y: 0, z: 0 };
|
|
75
|
+
this.targetPosition = null;
|
|
76
|
+
this.moveInterval = null;
|
|
77
|
+
|
|
78
|
+
// Rock system
|
|
79
|
+
this.rocks = [];
|
|
80
|
+
this.currentRockId = null;
|
|
81
|
+
this.status = 'idle'; // idle | moving | mining | racing
|
|
82
|
+
|
|
83
|
+
// Chat system
|
|
84
|
+
this.nearbyChat = [];
|
|
85
|
+
this.waitStartTime = null;
|
|
86
|
+
|
|
87
|
+
// Stats
|
|
59
88
|
this.stats = {
|
|
60
89
|
totalMined: 0,
|
|
61
90
|
miningCount: 0,
|
|
62
91
|
connectedAt: null,
|
|
63
|
-
lastRewardAt: null
|
|
92
|
+
lastRewardAt: null,
|
|
93
|
+
racesWon: 0,
|
|
94
|
+
racesLost: 0
|
|
64
95
|
};
|
|
65
96
|
}
|
|
66
97
|
|
|
67
98
|
/**
|
|
68
|
-
*
|
|
99
|
+
* Start mining
|
|
69
100
|
*/
|
|
70
101
|
async start() {
|
|
71
102
|
if (this.isConnected) {
|
|
72
|
-
this.emit('warn', '
|
|
103
|
+
this.emit('warn', 'Already connected');
|
|
73
104
|
return;
|
|
74
105
|
}
|
|
75
106
|
|
|
76
|
-
this.emit('log',
|
|
107
|
+
this.emit('log', `Connecting to server: ${this.options.server}`);
|
|
77
108
|
|
|
78
109
|
try {
|
|
79
110
|
const wsToken = await this._registerBot();
|
|
@@ -87,10 +118,12 @@ class ClawreumMiner extends EventEmitter {
|
|
|
87
118
|
}
|
|
88
119
|
|
|
89
120
|
/**
|
|
90
|
-
*
|
|
121
|
+
* Stop mining
|
|
91
122
|
*/
|
|
92
123
|
stop() {
|
|
93
124
|
this._stopMiningLoop();
|
|
125
|
+
this._stopChatLoop();
|
|
126
|
+
this._stopMovement();
|
|
94
127
|
if (this.ws) {
|
|
95
128
|
this.ws.close();
|
|
96
129
|
this.ws = null;
|
|
@@ -98,17 +131,22 @@ class ClawreumMiner extends EventEmitter {
|
|
|
98
131
|
this.isConnected = false;
|
|
99
132
|
this.sessionSecret = null;
|
|
100
133
|
this.currentRoom = null;
|
|
134
|
+
this.status = 'idle';
|
|
101
135
|
this.emit('stopped');
|
|
102
136
|
}
|
|
103
137
|
|
|
104
138
|
/**
|
|
105
|
-
*
|
|
139
|
+
* Get current status
|
|
106
140
|
*/
|
|
107
141
|
getStatus() {
|
|
108
142
|
return {
|
|
109
143
|
connected: this.isConnected,
|
|
110
144
|
room: this.currentRoom,
|
|
111
145
|
botId: this.botId,
|
|
146
|
+
status: this.status,
|
|
147
|
+
position: this.position,
|
|
148
|
+
currentRockId: this.currentRockId,
|
|
149
|
+
rocks: this.rocks,
|
|
112
150
|
stats: { ...this.stats },
|
|
113
151
|
uptime: this.stats.connectedAt
|
|
114
152
|
? Date.now() - this.stats.connectedAt
|
|
@@ -116,6 +154,23 @@ class ClawreumMiner extends EventEmitter {
|
|
|
116
154
|
};
|
|
117
155
|
}
|
|
118
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Get context for AI chat
|
|
159
|
+
*/
|
|
160
|
+
getContext() {
|
|
161
|
+
return {
|
|
162
|
+
status: this.status,
|
|
163
|
+
waitTime: this.waitStartTime ? Math.floor((Date.now() - this.waitStartTime) / 1000) : 0,
|
|
164
|
+
position: this.position,
|
|
165
|
+
currentRockId: this.currentRockId,
|
|
166
|
+
availableRocks: this.rocks.filter(r => !r.occupied).length,
|
|
167
|
+
totalRocks: this.rocks.length,
|
|
168
|
+
competitors: this.rocks.filter(r => r.occupied).length,
|
|
169
|
+
nearbyChat: this.nearbyChat.slice(-5),
|
|
170
|
+
stats: this.stats
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
119
174
|
// ============================================================
|
|
120
175
|
// Private Methods
|
|
121
176
|
// ============================================================
|
|
@@ -136,7 +191,7 @@ class ClawreumMiner extends EventEmitter {
|
|
|
136
191
|
platformBotId: this.options.botId,
|
|
137
192
|
platformBotName: this.options.botName,
|
|
138
193
|
binaryHash,
|
|
139
|
-
version: '
|
|
194
|
+
version: '2.0.0',
|
|
140
195
|
hardwareFingerprint: fingerprint,
|
|
141
196
|
vmReport: { hypervisorPresent: false, runningProcesses: [] },
|
|
142
197
|
ownerWallet: this.options.ownerWallet || null,
|
|
@@ -147,7 +202,7 @@ class ClawreumMiner extends EventEmitter {
|
|
|
147
202
|
const result = await response.json();
|
|
148
203
|
|
|
149
204
|
if (!result.success) {
|
|
150
|
-
throw new Error(result.message || '
|
|
205
|
+
throw new Error(result.message || 'Bot registration failed');
|
|
151
206
|
}
|
|
152
207
|
|
|
153
208
|
this.botId = result.botId;
|
|
@@ -162,13 +217,13 @@ class ClawreumMiner extends EventEmitter {
|
|
|
162
217
|
this.ws = new WebSocket(wsUrl);
|
|
163
218
|
|
|
164
219
|
const timeout = setTimeout(() => {
|
|
165
|
-
reject(new Error('
|
|
220
|
+
reject(new Error('Connection timeout'));
|
|
166
221
|
this.ws.close();
|
|
167
222
|
}, 30000);
|
|
168
223
|
|
|
169
224
|
this.ws.on('open', () => {
|
|
170
225
|
clearTimeout(timeout);
|
|
171
|
-
this.emit('log', 'WebSocket
|
|
226
|
+
this.emit('log', 'WebSocket connected');
|
|
172
227
|
this.reconnectAttempts = 0;
|
|
173
228
|
});
|
|
174
229
|
|
|
@@ -177,7 +232,7 @@ class ClawreumMiner extends EventEmitter {
|
|
|
177
232
|
const msg = JSON.parse(data.toString());
|
|
178
233
|
this._handleMessage(msg, resolve, reject);
|
|
179
234
|
} catch (err) {
|
|
180
|
-
this.emit('error', new Error(
|
|
235
|
+
this.emit('error', new Error(`Message parsing failed: ${err.message}`));
|
|
181
236
|
}
|
|
182
237
|
});
|
|
183
238
|
|
|
@@ -185,6 +240,8 @@ class ClawreumMiner extends EventEmitter {
|
|
|
185
240
|
clearTimeout(timeout);
|
|
186
241
|
this.isConnected = false;
|
|
187
242
|
this._stopMiningLoop();
|
|
243
|
+
this._stopChatLoop();
|
|
244
|
+
this._stopMovement();
|
|
188
245
|
this.emit('disconnected', { code, reason: reason.toString() });
|
|
189
246
|
|
|
190
247
|
if (this.options.autoReconnect && code !== 4002) {
|
|
@@ -218,14 +275,22 @@ class ClawreumMiner extends EventEmitter {
|
|
|
218
275
|
break;
|
|
219
276
|
|
|
220
277
|
case 'attestFail':
|
|
221
|
-
this.emit('error', new Error(
|
|
278
|
+
this.emit('error', new Error(`Authentication failed: ${data.reason}`));
|
|
222
279
|
if (rejectConnect) rejectConnect(new Error(data.reason));
|
|
223
280
|
break;
|
|
224
281
|
|
|
225
282
|
case 'joinRoomSuccess':
|
|
226
283
|
this.currentRoom = data.roomCode;
|
|
284
|
+
this.position = data.position || { x: 0, y: 0, z: 0 };
|
|
227
285
|
this.emit('joined', { room: data.roomCode, botId: data.botId });
|
|
228
|
-
|
|
286
|
+
break;
|
|
287
|
+
|
|
288
|
+
case 'rockStatus':
|
|
289
|
+
this._handleRockStatus(data);
|
|
290
|
+
break;
|
|
291
|
+
|
|
292
|
+
case 'rockAvailable':
|
|
293
|
+
this._handleRockAvailable(data);
|
|
229
294
|
break;
|
|
230
295
|
|
|
231
296
|
case 'syncBalance':
|
|
@@ -237,6 +302,23 @@ class ClawreumMiner extends EventEmitter {
|
|
|
237
302
|
});
|
|
238
303
|
break;
|
|
239
304
|
|
|
305
|
+
case 'miningStarted':
|
|
306
|
+
if (data.id === this.botId) {
|
|
307
|
+
this.status = 'mining';
|
|
308
|
+
this.currentRockId = data.rockId;
|
|
309
|
+
this._startMiningLoop();
|
|
310
|
+
this._stopChatLoop();
|
|
311
|
+
this._triggerChat('mining_started');
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
|
|
315
|
+
case 'miningFailed':
|
|
316
|
+
this.emit('log', `Mining failed: ${data.reason}`);
|
|
317
|
+
this.stats.racesLost++;
|
|
318
|
+
this._triggerChat('lost_race');
|
|
319
|
+
this._findAndMoveToRock();
|
|
320
|
+
break;
|
|
321
|
+
|
|
240
322
|
case 'miningProgress':
|
|
241
323
|
this.emit('progress', { progress: data.progress });
|
|
242
324
|
break;
|
|
@@ -264,11 +346,11 @@ class ClawreumMiner extends EventEmitter {
|
|
|
264
346
|
|
|
265
347
|
case 'reAttestSuccess':
|
|
266
348
|
this.sessionSecret = data.sessionSecret;
|
|
267
|
-
this.emit('log', '
|
|
349
|
+
this.emit('log', 'Re-authentication successful');
|
|
268
350
|
break;
|
|
269
351
|
|
|
270
352
|
case 'sessionRevoked':
|
|
271
|
-
this.emit('error', new Error(
|
|
353
|
+
this.emit('error', new Error(`Session revoked: ${data.reason}`));
|
|
272
354
|
this.stop();
|
|
273
355
|
break;
|
|
274
356
|
|
|
@@ -280,20 +362,163 @@ class ClawreumMiner extends EventEmitter {
|
|
|
280
362
|
});
|
|
281
363
|
break;
|
|
282
364
|
|
|
365
|
+
case 'roomUserChat':
|
|
366
|
+
this.nearbyChat.push({
|
|
367
|
+
id: data.id,
|
|
368
|
+
name: data.name,
|
|
369
|
+
message: data.message,
|
|
370
|
+
time: Date.now()
|
|
371
|
+
});
|
|
372
|
+
if (this.nearbyChat.length > 20) {
|
|
373
|
+
this.nearbyChat.shift();
|
|
374
|
+
}
|
|
375
|
+
this.emit('chat', data);
|
|
376
|
+
break;
|
|
377
|
+
|
|
283
378
|
case 'error':
|
|
284
379
|
this.emit('error', new Error(data.message));
|
|
285
380
|
break;
|
|
286
381
|
|
|
287
382
|
default:
|
|
288
|
-
// 기타 메시지
|
|
289
383
|
break;
|
|
290
384
|
}
|
|
291
385
|
}
|
|
292
386
|
|
|
387
|
+
_handleRockStatus(data) {
|
|
388
|
+
this.rocks = data.rocks || [];
|
|
389
|
+
this.emit('rockStatus', {
|
|
390
|
+
rocks: this.rocks,
|
|
391
|
+
available: data.availableRocks,
|
|
392
|
+
total: data.totalRocks
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// If not mining, find a rock
|
|
396
|
+
if (this.status !== 'mining') {
|
|
397
|
+
this._findAndMoveToRock();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
_handleRockAvailable(data) {
|
|
402
|
+
this.emit('log', `Rock ${data.rockId} is now available!`);
|
|
403
|
+
this.emit('rockAvailable', data);
|
|
404
|
+
|
|
405
|
+
// If waiting, race to the rock
|
|
406
|
+
if (this.status === 'idle' || this.status === 'waiting') {
|
|
407
|
+
this._triggerChat('rock_available');
|
|
408
|
+
this.status = 'racing';
|
|
409
|
+
this._moveToPosition(data.position, () => {
|
|
410
|
+
this._tryOccupyRock(data.rockId);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
_findAndMoveToRock() {
|
|
416
|
+
const availableRocks = this.rocks.filter(r => !r.occupied);
|
|
417
|
+
|
|
418
|
+
if (availableRocks.length === 0) {
|
|
419
|
+
// No rocks available, wait
|
|
420
|
+
this.status = 'waiting';
|
|
421
|
+
if (!this.waitStartTime) {
|
|
422
|
+
this.waitStartTime = Date.now();
|
|
423
|
+
}
|
|
424
|
+
this._startChatLoop();
|
|
425
|
+
this.emit('log', 'No rocks available, waiting...');
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Find nearest rock
|
|
430
|
+
const nearest = this._findNearestRock(availableRocks);
|
|
431
|
+
this.status = 'moving';
|
|
432
|
+
this.waitStartTime = null;
|
|
433
|
+
|
|
434
|
+
this.emit('log', `Moving to rock ${nearest.id}`);
|
|
435
|
+
|
|
436
|
+
this._moveToPosition(nearest.position, () => {
|
|
437
|
+
this._tryOccupyRock(nearest.id);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
_findNearestRock(rocks) {
|
|
442
|
+
let nearest = rocks[0];
|
|
443
|
+
let minDist = this._distance(this.position, nearest.position);
|
|
444
|
+
|
|
445
|
+
for (const rock of rocks) {
|
|
446
|
+
const dist = this._distance(this.position, rock.position);
|
|
447
|
+
if (dist < minDist) {
|
|
448
|
+
minDist = dist;
|
|
449
|
+
nearest = rock;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return nearest;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
_distance(a, b) {
|
|
457
|
+
const dx = (a.x || 0) - (b.x || 0);
|
|
458
|
+
const dz = (a.z || 0) - (b.z || 0);
|
|
459
|
+
return Math.sqrt(dx * dx + dz * dz);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
_moveToPosition(target, callback) {
|
|
463
|
+
this._stopMovement();
|
|
464
|
+
this.targetPosition = target;
|
|
465
|
+
|
|
466
|
+
this.moveInterval = setInterval(() => {
|
|
467
|
+
const dx = target.x - this.position.x;
|
|
468
|
+
const dz = target.z - this.position.z;
|
|
469
|
+
const dist = Math.sqrt(dx * dx + dz * dz);
|
|
470
|
+
|
|
471
|
+
if (dist < 1) {
|
|
472
|
+
// Arrived
|
|
473
|
+
this._stopMovement();
|
|
474
|
+
if (callback) callback();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Move towards target
|
|
479
|
+
const speed = this.options.moveSpeed;
|
|
480
|
+
const ratio = Math.min(speed / dist, 1);
|
|
481
|
+
this.position.x += dx * ratio;
|
|
482
|
+
this.position.z += dz * ratio;
|
|
483
|
+
|
|
484
|
+
// Send move packet
|
|
485
|
+
this._send({
|
|
486
|
+
type: 'move',
|
|
487
|
+
data: {
|
|
488
|
+
position: this.position,
|
|
489
|
+
direction: this._getDirection(dx, dz)
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}, 100);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
_stopMovement() {
|
|
496
|
+
if (this.moveInterval) {
|
|
497
|
+
clearInterval(this.moveInterval);
|
|
498
|
+
this.moveInterval = null;
|
|
499
|
+
}
|
|
500
|
+
this.targetPosition = null;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
_getDirection(dx, dz) {
|
|
504
|
+
if (Math.abs(dx) > Math.abs(dz)) {
|
|
505
|
+
return dx > 0 ? 'right' : 'left';
|
|
506
|
+
}
|
|
507
|
+
return dz > 0 ? 'up' : 'down';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
_tryOccupyRock(rockId) {
|
|
511
|
+
this.emit('log', `Trying to occupy rock ${rockId}`);
|
|
512
|
+
this._send({
|
|
513
|
+
type: 'botMineStart',
|
|
514
|
+
data: { rockId }
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
293
518
|
_handleChallenge(challenge, isReAttest = false) {
|
|
294
519
|
const { challengeId, nonce, difficulty } = challenge;
|
|
295
520
|
|
|
296
|
-
this.emit('log', `${isReAttest ? '
|
|
521
|
+
this.emit('log', `${isReAttest ? 'Re-' : ''}Authentication challenge (difficulty: ${difficulty})`);
|
|
297
522
|
|
|
298
523
|
const solution = this._solveChallenge(nonce, difficulty);
|
|
299
524
|
|
|
@@ -334,15 +559,14 @@ class ClawreumMiner extends EventEmitter {
|
|
|
334
559
|
_startMiningLoop() {
|
|
335
560
|
if (this.miningLoop) return;
|
|
336
561
|
|
|
337
|
-
this._send({ type: 'botMineStart', data: {} });
|
|
338
|
-
|
|
339
562
|
this.miningLoop = setInterval(() => {
|
|
340
|
-
if (this.isConnected && this.
|
|
563
|
+
if (this.isConnected && this.status === 'mining') {
|
|
341
564
|
this._send({ type: 'botMine', data: {} });
|
|
342
565
|
}
|
|
343
566
|
}, this.options.miningInterval);
|
|
344
567
|
|
|
345
|
-
this.emit('mining', { started: true });
|
|
568
|
+
this.emit('mining', { started: true, rockId: this.currentRockId });
|
|
569
|
+
this.stats.racesWon++;
|
|
346
570
|
}
|
|
347
571
|
|
|
348
572
|
_stopMiningLoop() {
|
|
@@ -352,16 +576,54 @@ class ClawreumMiner extends EventEmitter {
|
|
|
352
576
|
}
|
|
353
577
|
}
|
|
354
578
|
|
|
579
|
+
_startChatLoop() {
|
|
580
|
+
if (this.chatLoop || !this.options.chatHandler) return;
|
|
581
|
+
|
|
582
|
+
this.chatLoop = setInterval(() => {
|
|
583
|
+
this._triggerChat('idle');
|
|
584
|
+
}, this.options.chatInterval);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
_stopChatLoop() {
|
|
588
|
+
if (this.chatLoop) {
|
|
589
|
+
clearInterval(this.chatLoop);
|
|
590
|
+
this.chatLoop = null;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async _triggerChat(event) {
|
|
595
|
+
if (!this.options.chatHandler) return;
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
const context = {
|
|
599
|
+
...this.getContext(),
|
|
600
|
+
event
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
const message = await this.options.chatHandler(context);
|
|
604
|
+
|
|
605
|
+
if (message && typeof message === 'string' && message.trim()) {
|
|
606
|
+
this._send({
|
|
607
|
+
type: 'chat',
|
|
608
|
+
data: { message: message.trim() }
|
|
609
|
+
});
|
|
610
|
+
this.emit('chatSent', { message, event });
|
|
611
|
+
}
|
|
612
|
+
} catch (err) {
|
|
613
|
+
this.emit('error', new Error(`Chat handler error: ${err.message}`));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
355
617
|
_scheduleReconnect() {
|
|
356
618
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
357
|
-
this.emit('error', new Error('
|
|
619
|
+
this.emit('error', new Error('Max reconnection attempts exceeded'));
|
|
358
620
|
return;
|
|
359
621
|
}
|
|
360
622
|
|
|
361
623
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
362
624
|
this.reconnectAttempts++;
|
|
363
625
|
|
|
364
|
-
this.emit('log',
|
|
626
|
+
this.emit('log', `Reconnecting in ${delay / 1000}s (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
|
|
365
627
|
|
|
366
628
|
setTimeout(() => {
|
|
367
629
|
this.start();
|
|
@@ -390,7 +652,7 @@ class ClawreumMiner extends EventEmitter {
|
|
|
390
652
|
|
|
391
653
|
_generateBinaryHash() {
|
|
392
654
|
return crypto.createHash('sha256')
|
|
393
|
-
.update(`clawreum-sdk-
|
|
655
|
+
.update(`clawreum-sdk-v2.0.0-${os.hostname()}`)
|
|
394
656
|
.digest('hex');
|
|
395
657
|
}
|
|
396
658
|
}
|