@pdc-test/chat-io 1.1.1 → 1.1.3
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/bin/cli.js +31 -3
- package/package.json +2 -2
- package/src/server/index.js +24 -10
package/bin/cli.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const WebSocket = require('ws');
|
|
3
3
|
const readline = require('readline');
|
|
4
|
+
const { exec } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const pkgVersion = require('../package.json').version;
|
|
7
|
+
|
|
8
|
+
let soundsEnabled = true;
|
|
9
|
+
|
|
10
|
+
function playSound(type) {
|
|
11
|
+
if (!soundsEnabled) return;
|
|
12
|
+
const opts = { windowsHide: true, stdio: 'ignore' };
|
|
13
|
+
if (type === 'ding') {
|
|
14
|
+
exec(`powershell -WindowStyle Hidden -c (New-Object Media.SoundPlayer 'C:\\Windows\\Media\\Windows Notify Email.wav').PlaySync();`, opts);
|
|
15
|
+
} else if (type === 'msg') {
|
|
16
|
+
exec(`powershell -WindowStyle Hidden -c (New-Object Media.SoundPlayer 'C:\\Windows\\Media\\Windows Proximity Notification.wav').PlaySync();`, opts);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
4
19
|
|
|
5
20
|
// ============================================
|
|
6
21
|
// CLIENTE MODULAR RÁPIDO (Carga NATIVA JSON)
|
|
@@ -33,7 +48,7 @@ function clearTopLine() {
|
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
// Interfaz del Teclado Local
|
|
36
|
-
const commands = ['/users', '/flip', '/rps', '/tiendita', '/help', '/w', '/all', '/blink', '/exit', '/clear'];
|
|
51
|
+
const commands = ['/users', '/flip', '/rps', '/tiendita', '/help', '/w', '/all', '/blink', '/exit', '/clear', '/sound', '/v', '/version'];
|
|
37
52
|
let knownUsersList = [];
|
|
38
53
|
|
|
39
54
|
function completer(line) {
|
|
@@ -148,7 +163,7 @@ ws.on('message', (data) => {
|
|
|
148
163
|
break;
|
|
149
164
|
|
|
150
165
|
case "ding":
|
|
151
|
-
|
|
166
|
+
playSound('ding');
|
|
152
167
|
break;
|
|
153
168
|
|
|
154
169
|
case "users_list":
|
|
@@ -161,8 +176,10 @@ ws.on('message', (data) => {
|
|
|
161
176
|
if (res.isWhisper) {
|
|
162
177
|
const dir = res.from === myName ? `Private → ${res.to}` : `${res.emoji} Secreto de ${res.from}`;
|
|
163
178
|
printMessage(`\n[${getTime()}] 🔒 [${dir}]: \x1b[35m${safeMsg}\x1b[0m`);
|
|
179
|
+
if (res.from !== myName) playSound('ding');
|
|
164
180
|
} else {
|
|
165
181
|
printMessage(`\n[${getTime()}] 🌍 [${res.emoji} ${res.from}]: ${safeMsg}`);
|
|
182
|
+
if (res.from !== myName) playSound('msg');
|
|
166
183
|
}
|
|
167
184
|
break;
|
|
168
185
|
|
|
@@ -237,6 +254,15 @@ rl.on('line', (input) => {
|
|
|
237
254
|
}
|
|
238
255
|
|
|
239
256
|
// Local Helper Menú
|
|
257
|
+
if (line.toLowerCase() === "/version" || line.toLowerCase() === "/v") {
|
|
258
|
+
printMessage(`\nℹ️ Versión actual de Chat-IO: \x1b[1m\x1b[33m${pkgVersion}\x1b[0m\n`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (line.toLowerCase().startsWith("/sound")) {
|
|
262
|
+
soundsEnabled = !soundsEnabled;
|
|
263
|
+
printMessage(`\n🔊 Sonidos ahora están: \x1b[1m\x1b[33m${soundsEnabled ? "ACTIVADOS" : "DESACTIVADOS"}\x1b[0m\n`);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
240
266
|
if (line.toLowerCase() === "/clear") { console.clear(); rl.setPrompt('> '); rl.prompt(true); return; }
|
|
241
267
|
if (line.toLowerCase() === "/exit") {
|
|
242
268
|
printMessage("\x1b[33mSaliendo...\x1b[0m");
|
|
@@ -253,6 +279,8 @@ rl.on('line', (input) => {
|
|
|
253
279
|
🌍 /all → Volver a la Sala Global Abierta
|
|
254
280
|
🏪 /tiendita → Letrero de Neón de colores acelerado
|
|
255
281
|
🧹 /clear → Limpia historial de consola
|
|
282
|
+
🔊 /sound → Activa y desactiva notificaciones y sonidos
|
|
283
|
+
🏷️ /version → Version del producto
|
|
256
284
|
❌ /exit → Salir del chat
|
|
257
285
|
ℹ️ /help → Te ayuda desde la memoria Caché
|
|
258
286
|
─────────────────────────────────────────────\n`);
|
|
@@ -291,7 +319,7 @@ rl.on('line', (input) => {
|
|
|
291
319
|
ws.send(JSON.stringify({ type: "chat", msg: line }));
|
|
292
320
|
}
|
|
293
321
|
|
|
294
|
-
|
|
322
|
+
currentIsTyping = false;
|
|
295
323
|
ws.send(JSON.stringify({ type: "typing_stop" }));
|
|
296
324
|
});
|
|
297
325
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pdc-test/chat-io",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"chat-io": "bin/cli.js"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"start": "node src/server/index.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@pdc-test/chat-io": "^1.1.
|
|
12
|
+
"@pdc-test/chat-io": "^1.1.3",
|
|
13
13
|
"crypto-js": "^4.2.0",
|
|
14
14
|
"ws": "^8.0.0"
|
|
15
15
|
}
|
package/src/server/index.js
CHANGED
|
@@ -41,6 +41,7 @@ function getExactName(name) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// State para Tiping
|
|
44
|
+
// typingMap trackea timeouts y a quién se le está escribiendo
|
|
44
45
|
let typingMap = new Map();
|
|
45
46
|
|
|
46
47
|
function broadcastUsers() {
|
|
@@ -56,8 +57,18 @@ function broadcastUsers() {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function broadcastTyping() {
|
|
59
|
-
const
|
|
60
|
-
|
|
60
|
+
for (const [s, data] of sessions.entries()) {
|
|
61
|
+
if (s.readyState === WebSocket.OPEN && data.name) {
|
|
62
|
+
const visibleTypers = [];
|
|
63
|
+
for (const [typerName, typeData] of typingMap.entries()) {
|
|
64
|
+
// Si está escribiendo en público (target == null) o le escribe directo a este usuario
|
|
65
|
+
if (!typeData.target || typeData.target.toLowerCase() === data.name.toLowerCase()) {
|
|
66
|
+
visibleTypers.push(typerName);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
try { s.send(JSON.stringify({ type: "typing_event", users: visibleTypers })); } catch(e) {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
wss.on("connection", (ws) => {
|
|
@@ -97,7 +108,7 @@ wss.on("connection", (ws) => {
|
|
|
97
108
|
// Limpiar typing automático si envía un mensaje real
|
|
98
109
|
if (req.type === "chat" || req.type === "command") {
|
|
99
110
|
if (typingMap.has(myName)) {
|
|
100
|
-
clearTimeout(typingMap.get(myName));
|
|
111
|
+
clearTimeout(typingMap.get(myName).timer);
|
|
101
112
|
typingMap.delete(myName);
|
|
102
113
|
broadcastTyping();
|
|
103
114
|
}
|
|
@@ -105,17 +116,20 @@ wss.on("connection", (ws) => {
|
|
|
105
116
|
|
|
106
117
|
switch (req.type) {
|
|
107
118
|
case "typing":
|
|
108
|
-
if (typingMap.has(myName)) clearTimeout(typingMap.get(myName));
|
|
109
|
-
typingMap.set(myName,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
if (typingMap.has(myName)) clearTimeout(typingMap.get(myName).timer);
|
|
120
|
+
typingMap.set(myName, {
|
|
121
|
+
target: user.privateTarget,
|
|
122
|
+
timer: setTimeout(() => {
|
|
123
|
+
typingMap.delete(myName);
|
|
124
|
+
broadcastTyping();
|
|
125
|
+
}, 5000)
|
|
126
|
+
});
|
|
113
127
|
broadcastTyping();
|
|
114
128
|
break;
|
|
115
129
|
|
|
116
130
|
case "typing_stop":
|
|
117
131
|
if (typingMap.has(myName)) {
|
|
118
|
-
clearTimeout(typingMap.get(myName));
|
|
132
|
+
clearTimeout(typingMap.get(myName).timer);
|
|
119
133
|
typingMap.delete(myName);
|
|
120
134
|
broadcastTyping();
|
|
121
135
|
}
|
|
@@ -218,7 +232,7 @@ wss.on("connection", (ws) => {
|
|
|
218
232
|
if (u && u.name) {
|
|
219
233
|
broadcast({ type: "system", msg: `🔴 [\x1b[31m${u.emoji} ${u.name}\x1b[0m] abandonó la sala.` }, ws);
|
|
220
234
|
if (typingMap.has(u.name)) {
|
|
221
|
-
clearTimeout(typingMap.get(u.name));
|
|
235
|
+
clearTimeout(typingMap.get(u.name).timer);
|
|
222
236
|
typingMap.delete(u.name);
|
|
223
237
|
broadcastTyping(); // Notificamos de inmediato al mundo para que eliminen su typing UI
|
|
224
238
|
}
|