@rooms-haxball/connector 0.1.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 +41 -0
- package/index.js +194 -0
- package/package.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @rooms-haxball/connector
|
|
2
|
+
|
|
3
|
+
Conecta una sala de HaxBall al dashboard multi-tenant de Rooms HaxBall sin tener que gestionar Socket.IO manualmente.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @rooms-haxball/connector socket.io-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`socket.io-client` se declara como **peer dependency** para evitar versiones duplicadas en tu bot.
|
|
12
|
+
|
|
13
|
+
## Environment Variables
|
|
14
|
+
|
|
15
|
+
- `DASHBOARD_HUB_URL`: URL base del dashboard (ej. `https://dashboard.example.com`).
|
|
16
|
+
- `DASHBOARD_API_KEY`: API key de tu sala (`rhb_...`) generada desde `/keys`.
|
|
17
|
+
|
|
18
|
+
## 5-line Usage Example
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
const { connectToDashboard } = require('@rooms-haxball/connector');
|
|
22
|
+
const room = createRoom();
|
|
23
|
+
process.env.DASHBOARD_HUB_URL = 'https://dashboard.example.com';
|
|
24
|
+
process.env.DASHBOARD_API_KEY = 'rhb_1234567890abcdef1234567890abcdef';
|
|
25
|
+
connectToDashboard(room, { roomId: 'my-room', roomName: 'My Room' });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration
|
|
29
|
+
|
|
30
|
+
`connectToDashboard(room, { roomId, roomName })`:
|
|
31
|
+
|
|
32
|
+
- `roomId`: identificador estable de la sala (ej. `rambo`, `rs-4v4`).
|
|
33
|
+
- `roomName`: nombre visible en el dashboard.
|
|
34
|
+
- El conector envía `connectorVersion` en el handshake para telemetría y compatibilidad operativa.
|
|
35
|
+
|
|
36
|
+
## Troubleshooting
|
|
37
|
+
|
|
38
|
+
- **No conecta al dashboard**: verifica `DASHBOARD_HUB_URL` y `DASHBOARD_API_KEY`.
|
|
39
|
+
- **Error 401/403**: la API key puede estar revocada o asociada a otra sala.
|
|
40
|
+
- **Dashboard reiniciado**: la reconexión es automática (`reconnection: true`).
|
|
41
|
+
- **Advertencia por `INTERNAL_API_KEY`**: migra a `DASHBOARD_API_KEY`; el fallback es temporal.
|
package/index.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const { io } = require('socket.io-client');
|
|
2
|
+
const { version: connectorVersion } = require('./package.json');
|
|
3
|
+
|
|
4
|
+
let dashboardSocket = null;
|
|
5
|
+
const playerIdentities = new Map();
|
|
6
|
+
|
|
7
|
+
function getHubUrl() {
|
|
8
|
+
const rawHubUrl = process.env.DASHBOARD_HUB_URL || 'http://localhost:4000';
|
|
9
|
+
return rawHubUrl.replace(/\/+$/, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getApiKey() {
|
|
13
|
+
const dashboardApiKey = process.env.DASHBOARD_API_KEY;
|
|
14
|
+
const apiKey = dashboardApiKey || process.env.INTERNAL_API_KEY;
|
|
15
|
+
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
console.log('[DASHBOARD] DASHBOARD_API_KEY / INTERNAL_API_KEY not set, skipping dashboard connection');
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!dashboardApiKey) {
|
|
22
|
+
console.log('[DASHBOARD] Deprecation warning: using INTERNAL_API_KEY fallback, configure DASHBOARD_API_KEY');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return apiKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function connectToDashboard(room, { roomId, roomName }) {
|
|
29
|
+
const apiKey = getApiKey();
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const socket = io(`${getHubUrl()}/bot`, {
|
|
35
|
+
auth: { apiKey, roomId, roomName, connectorVersion },
|
|
36
|
+
transports: ['websocket'],
|
|
37
|
+
reconnection: true,
|
|
38
|
+
reconnectionDelay: 3000,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
dashboardSocket = socket;
|
|
42
|
+
setupSocketHandlers(socket, room, roomName);
|
|
43
|
+
wrapRoomEventForwarders(socket, room);
|
|
44
|
+
|
|
45
|
+
return socket;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function setupSocketHandlers(socket, room, roomName) {
|
|
49
|
+
socket.on('connect', () => {
|
|
50
|
+
console.log(`[DASHBOARD] Conectado al Centro de Comando como "${roomName}"`);
|
|
51
|
+
const players = room.getPlayerList().filter((player) => player.id !== 0);
|
|
52
|
+
socket.emit('playerCount', players.length);
|
|
53
|
+
|
|
54
|
+
for (const player of players) {
|
|
55
|
+
playerIdentities.set(player.id, { auth: player.auth, conn: player.conn, name: player.name });
|
|
56
|
+
socket.emit('playerJoined', {
|
|
57
|
+
id: player.id,
|
|
58
|
+
name: player.name,
|
|
59
|
+
auth: player.auth,
|
|
60
|
+
conn: player.conn,
|
|
61
|
+
team: player.team,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
socket.on('disconnect', (reason) => {
|
|
67
|
+
console.log(`[DASHBOARD] Desconectado del Centro de Comando: ${reason}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
socket.on('connect_error', (err) => {
|
|
71
|
+
console.log(`[DASHBOARD] Error de conexion: ${err.message}`);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
socket.on('sendAnnouncement', ({ message }) => {
|
|
75
|
+
if (typeof message === 'string' && message) {
|
|
76
|
+
room.sendAnnouncement(`[ADMIN] ${message}`, null, 0xFF8C00, 'bold', 2);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
socket.on('executeCommand', ({ message }) => {
|
|
81
|
+
emitRoomControlEvent(room, 'onDashboardExecuteCommand', { message });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
socket.on('banPlayer', (payload, callback) => {
|
|
85
|
+
emitRoomControlEvent(room, 'onDashboardBanPlayer', payload, callback);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
socket.on('unbanPlayer', (payload, callback) => {
|
|
89
|
+
emitRoomControlEvent(room, 'onDashboardUnbanPlayer', payload, callback);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
socket.on('getBanList', (payload, callback) => {
|
|
93
|
+
emitRoomControlEvent(room, 'onDashboardGetBanList', payload, callback);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
socket.on('getPlayers', (payload, callback) => {
|
|
97
|
+
emitRoomControlEvent(room, 'onDashboardGetPlayers', payload, callback);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function emitRoomControlEvent(room, handlerName, payload, callback) {
|
|
102
|
+
const handler = room && room[handlerName];
|
|
103
|
+
if (typeof handler !== 'function') {
|
|
104
|
+
if (typeof callback === 'function') {
|
|
105
|
+
callback([]);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const result = handler(payload);
|
|
112
|
+
if (typeof callback === 'function') {
|
|
113
|
+
callback(result);
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error('[DASHBOARD] Error in control handler:', err.message);
|
|
117
|
+
if (typeof callback === 'function') {
|
|
118
|
+
callback([]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function wrapRoomEventForwarders(socket, room) {
|
|
124
|
+
const originalOnPlayerChat = room.onPlayerChat;
|
|
125
|
+
room.onPlayerChat = function onPlayerChat(player, message) {
|
|
126
|
+
socket.emit('chatMessage', { type: 'player', playerName: player.name, message });
|
|
127
|
+
if (originalOnPlayerChat) {
|
|
128
|
+
return originalOnPlayerChat.call(this, player, message);
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const originalOnPlayerJoin = room.onPlayerJoin;
|
|
134
|
+
room.onPlayerJoin = function onPlayerJoin(player) {
|
|
135
|
+
playerIdentities.set(player.id, { auth: player.auth, conn: player.conn, name: player.name });
|
|
136
|
+
socket.emit('chatMessage', {
|
|
137
|
+
type: 'system',
|
|
138
|
+
playerName: 'Sistema',
|
|
139
|
+
message: `${player.name} se unio a la sala`,
|
|
140
|
+
});
|
|
141
|
+
const count = room.getPlayerList().filter((p) => p.id !== 0).length;
|
|
142
|
+
socket.emit('playerCount', count);
|
|
143
|
+
socket.emit('playerJoined', {
|
|
144
|
+
id: player.id,
|
|
145
|
+
name: player.name,
|
|
146
|
+
auth: player.auth,
|
|
147
|
+
conn: player.conn,
|
|
148
|
+
team: player.team,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (originalOnPlayerJoin) {
|
|
152
|
+
return originalOnPlayerJoin.call(this, player);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return undefined;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const originalOnPlayerLeave = room.onPlayerLeave;
|
|
159
|
+
room.onPlayerLeave = function onPlayerLeave(player) {
|
|
160
|
+
const identity = playerIdentities.get(player.id) || {};
|
|
161
|
+
socket.emit('chatMessage', {
|
|
162
|
+
type: 'system',
|
|
163
|
+
playerName: 'Sistema',
|
|
164
|
+
message: `${player.name} salio de la sala`,
|
|
165
|
+
});
|
|
166
|
+
const count = room.getPlayerList().filter((p) => p.id !== 0 && p.id !== player.id).length;
|
|
167
|
+
socket.emit('playerCount', count);
|
|
168
|
+
socket.emit('playerLeft', {
|
|
169
|
+
id: player.id,
|
|
170
|
+
name: player.name,
|
|
171
|
+
auth: identity.auth || null,
|
|
172
|
+
conn: identity.conn || null,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
playerIdentities.delete(player.id);
|
|
176
|
+
|
|
177
|
+
if (originalOnPlayerLeave) {
|
|
178
|
+
return originalOnPlayerLeave.call(this, player);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return undefined;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function notifyAfkChange(playerId, playerName, isAfk) {
|
|
186
|
+
if (dashboardSocket && dashboardSocket.connected) {
|
|
187
|
+
dashboardSocket.emit('playerAfkChanged', { id: playerId, name: playerName, isAfk });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = {
|
|
192
|
+
connectToDashboard,
|
|
193
|
+
notifyAfkChange,
|
|
194
|
+
};
|