@mulingai-npm/redis 1.3.0 → 1.3.2
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/dist/enums/redis-database.d.ts +2 -1
- package/dist/enums/redis-database.js +1 -0
- package/dist/managers/audio-chunk-manager.d.ts +24 -0
- package/dist/managers/audio-chunk-manager.js +143 -0
- package/dist/managers/audio-stt-manager.d.ts +28 -0
- package/dist/managers/audio-stt-manager.js +101 -0
- package/dist/managers/mulingstream-listener-manager copy.d.ts +24 -0
- package/dist/managers/mulingstream-listener-manager copy.js +143 -0
- package/dist/managers/stt-manager.d.ts +24 -0
- package/dist/managers/stt-manager.js +143 -0
- package/package.json +1 -1
|
@@ -4,4 +4,5 @@ exports.RedisDatabase = void 0;
|
|
|
4
4
|
var RedisDatabase;
|
|
5
5
|
(function (RedisDatabase) {
|
|
6
6
|
RedisDatabase[RedisDatabase["MULINGSTREAM_LISTENER"] = 0] = "MULINGSTREAM_LISTENER";
|
|
7
|
+
RedisDatabase[RedisDatabase["AUDIO_STT"] = 1] = "AUDIO_STT";
|
|
7
8
|
})(RedisDatabase = exports.RedisDatabase || (exports.RedisDatabase = {}));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type MulingstreamListenerData = {
|
|
3
|
+
listenerId: string;
|
|
4
|
+
roomId: string;
|
|
5
|
+
socketId?: string;
|
|
6
|
+
token: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
firstJoined: number;
|
|
9
|
+
language?: string;
|
|
10
|
+
isActive?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare class MulingstreamListenerManager {
|
|
13
|
+
private redisClient;
|
|
14
|
+
constructor(redisClient: RedisClient);
|
|
15
|
+
private parseHashData;
|
|
16
|
+
addListener(listenerData: Omit<MulingstreamListenerData, 'listenerId'>): Promise<string>;
|
|
17
|
+
getAllListeners(): Promise<MulingstreamListenerData[]>;
|
|
18
|
+
getListenersByRoom(roomId: string): Promise<MulingstreamListenerData[]>;
|
|
19
|
+
getListener(listenerIdOrToken: string): Promise<MulingstreamListenerData | null>;
|
|
20
|
+
removeListenerByToken(token: string): Promise<boolean>;
|
|
21
|
+
updateNameLanguage(listenerIdOrToken: string, name: string, language: string): Promise<boolean>;
|
|
22
|
+
updateSocketId(listenerIdOrToken: string, socketId: string): Promise<boolean>;
|
|
23
|
+
getTargetSocketIdsByRoomLanguage(roomId: string, language: string): Promise<string[]>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MulingstreamListenerManager = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const EXPIRATION = 24 * 60 * 60; // 24 hours in seconds
|
|
6
|
+
class MulingstreamListenerManager {
|
|
7
|
+
constructor(redisClient) {
|
|
8
|
+
this.redisClient = redisClient;
|
|
9
|
+
}
|
|
10
|
+
parseHashData(data) {
|
|
11
|
+
return {
|
|
12
|
+
listenerId: data.listenerId,
|
|
13
|
+
roomId: data.roomId,
|
|
14
|
+
socketId: data.socketId || '',
|
|
15
|
+
token: data.token,
|
|
16
|
+
name: data.name || '',
|
|
17
|
+
firstJoined: parseInt(data.firstJoined, 10),
|
|
18
|
+
language: data.language || '',
|
|
19
|
+
isActive: data.isActive === 'true'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Creates a new listener.
|
|
23
|
+
// -Generates a unique listenerId and adds it to the room's set.
|
|
24
|
+
// -Stores the listener data in a Redis hash under `listener:{listenerId}`.
|
|
25
|
+
async addListener(listenerData) {
|
|
26
|
+
var _a, _b, _c;
|
|
27
|
+
// You can combine roomId + uuid, or just a uuid, up to you:
|
|
28
|
+
const listenerId = `[${listenerData.roomId}]-[${(0, uuid_1.v4)()}]`;
|
|
29
|
+
// Add listenerId to the set for that room
|
|
30
|
+
await this.redisClient.sadd(`room:${listenerData.roomId}:listeners`, listenerId);
|
|
31
|
+
// Store listener data in a hash
|
|
32
|
+
await this.redisClient.hset(`listener:${listenerId}`, {
|
|
33
|
+
listenerId,
|
|
34
|
+
roomId: listenerData.roomId,
|
|
35
|
+
socketId: (_a = listenerData.socketId) !== null && _a !== void 0 ? _a : '',
|
|
36
|
+
token: listenerData.token,
|
|
37
|
+
name: (_b = listenerData.name) !== null && _b !== void 0 ? _b : '',
|
|
38
|
+
firstJoined: listenerData.firstJoined.toString(),
|
|
39
|
+
language: (_c = listenerData.language) !== null && _c !== void 0 ? _c : '',
|
|
40
|
+
isActive: 'true'
|
|
41
|
+
});
|
|
42
|
+
// expire listener
|
|
43
|
+
await this.redisClient.expire(`listener:${listenerId}`, EXPIRATION);
|
|
44
|
+
return listenerId;
|
|
45
|
+
}
|
|
46
|
+
async getAllListeners() {
|
|
47
|
+
// get all keys that match 'listener:*'
|
|
48
|
+
// TODO: if we have many keys, consider using SCAN instead of KEYS to avoid performance issues.
|
|
49
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
50
|
+
if (!keys || keys.length === 0) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
// fetch each hash with HGETALL
|
|
54
|
+
const listeners = [];
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const data = await this.redisClient.hgetall(key);
|
|
57
|
+
listeners.push(this.parseHashData(data));
|
|
58
|
+
}
|
|
59
|
+
return listeners;
|
|
60
|
+
}
|
|
61
|
+
async getListenersByRoom(roomId) {
|
|
62
|
+
// get the set of listener IDs
|
|
63
|
+
const listenerIds = await this.redisClient.smembers(`room:${roomId}:listeners`);
|
|
64
|
+
if (!listenerIds || listenerIds.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const listeners = [];
|
|
68
|
+
for (const id of listenerIds) {
|
|
69
|
+
// get hash data
|
|
70
|
+
const data = await this.redisClient.hgetall(`listener:${id}`);
|
|
71
|
+
// if the hash doesn't exist, it may have expired, so remove from the set
|
|
72
|
+
if (!data || Object.keys(data).length === 0) {
|
|
73
|
+
await this.redisClient.srem(`room:${roomId}:listeners`, id);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// otherwise, parse and keep it
|
|
77
|
+
listeners.push(this.parseHashData(data));
|
|
78
|
+
}
|
|
79
|
+
return listeners;
|
|
80
|
+
}
|
|
81
|
+
async getListener(listenerIdOrToken) {
|
|
82
|
+
// try direct lookup by listenerId:
|
|
83
|
+
const directData = await this.redisClient.hgetall(`listener:${listenerIdOrToken}`);
|
|
84
|
+
if (directData && directData.listenerId) {
|
|
85
|
+
return this.parseHashData(directData);
|
|
86
|
+
}
|
|
87
|
+
// if not found by listenerId, treat 'listenerIdOrToken' as a token and scan all listeners
|
|
88
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
89
|
+
if (!keys || keys.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
const data = await this.redisClient.hgetall(key);
|
|
94
|
+
if (data && data.token === listenerIdOrToken) {
|
|
95
|
+
return this.parseHashData(data);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
async removeListenerByToken(token) {
|
|
101
|
+
// find the listener by token
|
|
102
|
+
const listener = await this.getListener(token);
|
|
103
|
+
if (!listener) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// remove the listener ID from the room's set
|
|
107
|
+
await this.redisClient.srem(`room:${listener.roomId}:listeners`, listener.listenerId);
|
|
108
|
+
// remove the listener data (the hash)
|
|
109
|
+
await this.redisClient.del(`listener:${listener.listenerId}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
async updateNameLanguage(listenerIdOrToken, name, language) {
|
|
113
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
114
|
+
if (!listener) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// Update the name/language on the listener’s hash key
|
|
118
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
119
|
+
name,
|
|
120
|
+
language
|
|
121
|
+
});
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
async updateSocketId(listenerIdOrToken, socketId) {
|
|
125
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
126
|
+
if (!listener) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
// Update the name/language on the listener’s hash key
|
|
130
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
131
|
+
socketId
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async getTargetSocketIdsByRoomLanguage(roomId, language) {
|
|
136
|
+
const listeners = await this.getListenersByRoom(roomId);
|
|
137
|
+
const filteredListeners = listeners.filter((listener) => {
|
|
138
|
+
return listener.isActive === true && listener.language === language && listener.socketId && listener.socketId.trim() !== '';
|
|
139
|
+
});
|
|
140
|
+
return filteredListeners.map((listener) => listener.socketId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.MulingstreamListenerManager = MulingstreamListenerManager;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type AudioSttData = {
|
|
3
|
+
audioSttId: string;
|
|
4
|
+
roomId: string;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
duration: number;
|
|
8
|
+
chunkNumber: number;
|
|
9
|
+
isFirst: boolean;
|
|
10
|
+
isLast: boolean;
|
|
11
|
+
language: string;
|
|
12
|
+
theme: string;
|
|
13
|
+
azureTranscription?: string;
|
|
14
|
+
whisperTranscription?: string;
|
|
15
|
+
googleTranscription?: string;
|
|
16
|
+
processingStart: number;
|
|
17
|
+
totalStatusCheck: number;
|
|
18
|
+
status: 'RECEIVED' | 'DISCARDED' | 'READY' | 'USED';
|
|
19
|
+
};
|
|
20
|
+
export declare class AudioSttManager {
|
|
21
|
+
private redisClient;
|
|
22
|
+
constructor(redisClient: RedisClient);
|
|
23
|
+
private parseHashData;
|
|
24
|
+
addAudioStt(sttData: Omit<AudioSttData, 'audioSttId' | 'processingStart' | 'totalStatusCheck' | 'status'>): Promise<string>;
|
|
25
|
+
getAllAudioStt(): Promise<AudioSttData[]>;
|
|
26
|
+
getAllAudioSttByRoom(roomId: string): Promise<AudioSttData[]>;
|
|
27
|
+
getAudioStt(audioSttId: string): Promise<AudioSttData | null>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AudioSttManager = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const EXPIRATION = 4 * 60 * 60; // 4 hours in seconds
|
|
6
|
+
class AudioSttManager {
|
|
7
|
+
constructor(redisClient) {
|
|
8
|
+
this.redisClient = redisClient;
|
|
9
|
+
}
|
|
10
|
+
parseHashData(data) {
|
|
11
|
+
return {
|
|
12
|
+
audioSttId: data.audioSttId,
|
|
13
|
+
roomId: data.roomId,
|
|
14
|
+
start: parseInt(data.start, 10),
|
|
15
|
+
end: parseInt(data.end, 10),
|
|
16
|
+
duration: parseInt(data.duration, 10),
|
|
17
|
+
chunkNumber: parseInt(data.chunkNumber, 10),
|
|
18
|
+
isFirst: data.isFirst === 'true',
|
|
19
|
+
isLast: data.isLast === 'true',
|
|
20
|
+
language: data.language,
|
|
21
|
+
theme: data.theme,
|
|
22
|
+
azureTranscription: data.azureTranscription || undefined,
|
|
23
|
+
whisperTranscription: data.whisperTranscription || undefined,
|
|
24
|
+
googleTranscription: data.googleTranscription || undefined,
|
|
25
|
+
processingStart: parseInt(data.processingStart, 10),
|
|
26
|
+
totalStatusCheck: parseInt(data.totalStatusCheck, 10),
|
|
27
|
+
status: data.status
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// Creates a new audio-stt entry.
|
|
31
|
+
// - Generates a unique audioSttId and adds it to the room’s set.
|
|
32
|
+
// - Stores the data in a Redis hash under `audioStt:{audioSttId}`.
|
|
33
|
+
// - Optionally sets an expiration (4h).
|
|
34
|
+
async addAudioStt(sttData) {
|
|
35
|
+
const audioSttId = `[${sttData.roomId}]-[${(0, uuid_1.v4)()}]`;
|
|
36
|
+
// Add audioSttId to the set for that room
|
|
37
|
+
await this.redisClient.sadd(`room:${sttData.roomId}:audioStt`, audioSttId);
|
|
38
|
+
// Store audio-stt data in a hash
|
|
39
|
+
await this.redisClient.hset(`audioStt:${audioSttId}`, {
|
|
40
|
+
audioSttId,
|
|
41
|
+
roomId: sttData.roomId,
|
|
42
|
+
start: sttData.start.toString(),
|
|
43
|
+
end: sttData.end.toString(),
|
|
44
|
+
duration: sttData.duration.toString(),
|
|
45
|
+
chunkNumber: sttData.chunkNumber.toString(),
|
|
46
|
+
isFirst: sttData.isFirst.toString(),
|
|
47
|
+
isLast: sttData.isLast.toString(),
|
|
48
|
+
language: sttData.language,
|
|
49
|
+
theme: sttData.theme,
|
|
50
|
+
processingStart: Date.now().toString(),
|
|
51
|
+
totalStatusCheck: (0).toString(),
|
|
52
|
+
status: 'RECEIVED'
|
|
53
|
+
});
|
|
54
|
+
// Set expiration if desired
|
|
55
|
+
await this.redisClient.expire(`audioStt:${audioSttId}`, EXPIRATION);
|
|
56
|
+
return audioSttId;
|
|
57
|
+
}
|
|
58
|
+
// Retrieves *all* audio-stt entries by scanning the keys that match `audioStt:*`
|
|
59
|
+
async getAllAudioStt() {
|
|
60
|
+
const keys = await this.redisClient.keys('audioStt:*');
|
|
61
|
+
if (!keys || keys.length === 0) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const key of keys) {
|
|
66
|
+
const data = await this.redisClient.hgetall(key);
|
|
67
|
+
if (data && data.audioSttId) {
|
|
68
|
+
results.push(this.parseHashData(data));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
// Retrieves all audio-stt entries for a specific room by looking up
|
|
74
|
+
// the set `room:{roomId}:audioStt` and then fetching each hash.
|
|
75
|
+
async getAllAudioSttByRoom(roomId) {
|
|
76
|
+
const audioSttIds = await this.redisClient.smembers(`room:${roomId}:audioStt`);
|
|
77
|
+
if (!audioSttIds || audioSttIds.length === 0) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const id of audioSttIds) {
|
|
82
|
+
const data = await this.redisClient.hgetall(`audioStt:${id}`);
|
|
83
|
+
// if the hash doesn't exist, it may have expired, so remove from the set
|
|
84
|
+
if (!data || Object.keys(data).length === 0) {
|
|
85
|
+
await this.redisClient.srem(`room:${roomId}:audioStt`, id);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
results.push(this.parseHashData(data));
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
// Retrieves a single audio-stt entry by ID.
|
|
93
|
+
async getAudioStt(audioSttId) {
|
|
94
|
+
const data = await this.redisClient.hgetall(`audioStt:${audioSttId}`);
|
|
95
|
+
if (!data || !data.audioSttId) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return this.parseHashData(data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.AudioSttManager = AudioSttManager;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type MulingstreamListenerData = {
|
|
3
|
+
listenerId: string;
|
|
4
|
+
roomId: string;
|
|
5
|
+
socketId?: string;
|
|
6
|
+
token: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
firstJoined: number;
|
|
9
|
+
language?: string;
|
|
10
|
+
isActive?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare class MulingstreamListenerManager {
|
|
13
|
+
private redisClient;
|
|
14
|
+
constructor(redisClient: RedisClient);
|
|
15
|
+
private parseHashData;
|
|
16
|
+
addListener(listenerData: Omit<MulingstreamListenerData, 'listenerId'>): Promise<string>;
|
|
17
|
+
getAllListeners(): Promise<MulingstreamListenerData[]>;
|
|
18
|
+
getListenersByRoom(roomId: string): Promise<MulingstreamListenerData[]>;
|
|
19
|
+
getListener(listenerIdOrToken: string): Promise<MulingstreamListenerData | null>;
|
|
20
|
+
removeListenerByToken(token: string): Promise<boolean>;
|
|
21
|
+
updateNameLanguage(listenerIdOrToken: string, name: string, language: string): Promise<boolean>;
|
|
22
|
+
updateSocketId(listenerIdOrToken: string, socketId: string): Promise<boolean>;
|
|
23
|
+
getTargetSocketIdsByRoomLanguage(roomId: string, language: string): Promise<string[]>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MulingstreamListenerManager = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const EXPIRATION = 24 * 60 * 60; // 24 hours in seconds
|
|
6
|
+
class MulingstreamListenerManager {
|
|
7
|
+
constructor(redisClient) {
|
|
8
|
+
this.redisClient = redisClient;
|
|
9
|
+
}
|
|
10
|
+
parseHashData(data) {
|
|
11
|
+
return {
|
|
12
|
+
listenerId: data.listenerId,
|
|
13
|
+
roomId: data.roomId,
|
|
14
|
+
socketId: data.socketId || '',
|
|
15
|
+
token: data.token,
|
|
16
|
+
name: data.name || '',
|
|
17
|
+
firstJoined: parseInt(data.firstJoined, 10),
|
|
18
|
+
language: data.language || '',
|
|
19
|
+
isActive: data.isActive === 'true'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Creates a new listener.
|
|
23
|
+
// -Generates a unique listenerId and adds it to the room's set.
|
|
24
|
+
// -Stores the listener data in a Redis hash under `listener:{listenerId}`.
|
|
25
|
+
async addListener(listenerData) {
|
|
26
|
+
var _a, _b, _c;
|
|
27
|
+
// You can combine roomId + uuid, or just a uuid, up to you:
|
|
28
|
+
const listenerId = `[${listenerData.roomId}]-[${(0, uuid_1.v4)()}]`;
|
|
29
|
+
// Add listenerId to the set for that room
|
|
30
|
+
await this.redisClient.sadd(`room:${listenerData.roomId}:listeners`, listenerId);
|
|
31
|
+
// Store listener data in a hash
|
|
32
|
+
await this.redisClient.hset(`listener:${listenerId}`, {
|
|
33
|
+
listenerId,
|
|
34
|
+
roomId: listenerData.roomId,
|
|
35
|
+
socketId: (_a = listenerData.socketId) !== null && _a !== void 0 ? _a : '',
|
|
36
|
+
token: listenerData.token,
|
|
37
|
+
name: (_b = listenerData.name) !== null && _b !== void 0 ? _b : '',
|
|
38
|
+
firstJoined: listenerData.firstJoined.toString(),
|
|
39
|
+
language: (_c = listenerData.language) !== null && _c !== void 0 ? _c : '',
|
|
40
|
+
isActive: 'true'
|
|
41
|
+
});
|
|
42
|
+
// expire listener
|
|
43
|
+
await this.redisClient.expire(`listener:${listenerId}`, EXPIRATION);
|
|
44
|
+
return listenerId;
|
|
45
|
+
}
|
|
46
|
+
async getAllListeners() {
|
|
47
|
+
// get all keys that match 'listener:*'
|
|
48
|
+
// TODO: if we have many keys, consider using SCAN instead of KEYS to avoid performance issues.
|
|
49
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
50
|
+
if (!keys || keys.length === 0) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
// fetch each hash with HGETALL
|
|
54
|
+
const listeners = [];
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const data = await this.redisClient.hgetall(key);
|
|
57
|
+
listeners.push(this.parseHashData(data));
|
|
58
|
+
}
|
|
59
|
+
return listeners;
|
|
60
|
+
}
|
|
61
|
+
async getListenersByRoom(roomId) {
|
|
62
|
+
// get the set of listener IDs
|
|
63
|
+
const listenerIds = await this.redisClient.smembers(`room:${roomId}:listeners`);
|
|
64
|
+
if (!listenerIds || listenerIds.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const listeners = [];
|
|
68
|
+
for (const id of listenerIds) {
|
|
69
|
+
// get hash data
|
|
70
|
+
const data = await this.redisClient.hgetall(`listener:${id}`);
|
|
71
|
+
// if the hash doesn't exist, it may have expired, so remove from the set
|
|
72
|
+
if (!data || Object.keys(data).length === 0) {
|
|
73
|
+
await this.redisClient.srem(`room:${roomId}:listeners`, id);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// otherwise, parse and keep it
|
|
77
|
+
listeners.push(this.parseHashData(data));
|
|
78
|
+
}
|
|
79
|
+
return listeners;
|
|
80
|
+
}
|
|
81
|
+
async getListener(listenerIdOrToken) {
|
|
82
|
+
// try direct lookup by listenerId:
|
|
83
|
+
const directData = await this.redisClient.hgetall(`listener:${listenerIdOrToken}`);
|
|
84
|
+
if (directData && directData.listenerId) {
|
|
85
|
+
return this.parseHashData(directData);
|
|
86
|
+
}
|
|
87
|
+
// if not found by listenerId, treat 'listenerIdOrToken' as a token and scan all listeners
|
|
88
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
89
|
+
if (!keys || keys.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
const data = await this.redisClient.hgetall(key);
|
|
94
|
+
if (data && data.token === listenerIdOrToken) {
|
|
95
|
+
return this.parseHashData(data);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
async removeListenerByToken(token) {
|
|
101
|
+
// find the listener by token
|
|
102
|
+
const listener = await this.getListener(token);
|
|
103
|
+
if (!listener) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// remove the listener ID from the room's set
|
|
107
|
+
await this.redisClient.srem(`room:${listener.roomId}:listeners`, listener.listenerId);
|
|
108
|
+
// remove the listener data (the hash)
|
|
109
|
+
await this.redisClient.del(`listener:${listener.listenerId}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
async updateNameLanguage(listenerIdOrToken, name, language) {
|
|
113
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
114
|
+
if (!listener) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// Update the name/language on the listener’s hash key
|
|
118
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
119
|
+
name,
|
|
120
|
+
language
|
|
121
|
+
});
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
async updateSocketId(listenerIdOrToken, socketId) {
|
|
125
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
126
|
+
if (!listener) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
// Update the name/language on the listener’s hash key
|
|
130
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
131
|
+
socketId
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async getTargetSocketIdsByRoomLanguage(roomId, language) {
|
|
136
|
+
const listeners = await this.getListenersByRoom(roomId);
|
|
137
|
+
const filteredListeners = listeners.filter((listener) => {
|
|
138
|
+
return listener.isActive === true && listener.language === language && listener.socketId && listener.socketId.trim() !== '';
|
|
139
|
+
});
|
|
140
|
+
return filteredListeners.map((listener) => listener.socketId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.MulingstreamListenerManager = MulingstreamListenerManager;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type MulingstreamListenerData = {
|
|
3
|
+
listenerId: string;
|
|
4
|
+
roomId: string;
|
|
5
|
+
socketId?: string;
|
|
6
|
+
token: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
firstJoined: number;
|
|
9
|
+
language?: string;
|
|
10
|
+
isActive?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare class MulingstreamListenerManager {
|
|
13
|
+
private redisClient;
|
|
14
|
+
constructor(redisClient: RedisClient);
|
|
15
|
+
private parseHashData;
|
|
16
|
+
addListener(listenerData: Omit<MulingstreamListenerData, 'listenerId'>): Promise<string>;
|
|
17
|
+
getAllListeners(): Promise<MulingstreamListenerData[]>;
|
|
18
|
+
getListenersByRoom(roomId: string): Promise<MulingstreamListenerData[]>;
|
|
19
|
+
getListener(listenerIdOrToken: string): Promise<MulingstreamListenerData | null>;
|
|
20
|
+
removeListenerByToken(token: string): Promise<boolean>;
|
|
21
|
+
updateNameLanguage(listenerIdOrToken: string, name: string, language: string): Promise<boolean>;
|
|
22
|
+
updateSocketId(listenerIdOrToken: string, socketId: string): Promise<boolean>;
|
|
23
|
+
getTargetSocketIdsByRoomLanguage(roomId: string, language: string): Promise<string[]>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MulingstreamListenerManager = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const EXPIRATION = 24 * 60 * 60; // 24 hours in seconds
|
|
6
|
+
class MulingstreamListenerManager {
|
|
7
|
+
constructor(redisClient) {
|
|
8
|
+
this.redisClient = redisClient;
|
|
9
|
+
}
|
|
10
|
+
parseHashData(data) {
|
|
11
|
+
return {
|
|
12
|
+
listenerId: data.listenerId,
|
|
13
|
+
roomId: data.roomId,
|
|
14
|
+
socketId: data.socketId || '',
|
|
15
|
+
token: data.token,
|
|
16
|
+
name: data.name || '',
|
|
17
|
+
firstJoined: parseInt(data.firstJoined, 10),
|
|
18
|
+
language: data.language || '',
|
|
19
|
+
isActive: data.isActive === 'true'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Creates a new listener.
|
|
23
|
+
// -Generates a unique listenerId and adds it to the room's set.
|
|
24
|
+
// -Stores the listener data in a Redis hash under `listener:{listenerId}`.
|
|
25
|
+
async addListener(listenerData) {
|
|
26
|
+
var _a, _b, _c;
|
|
27
|
+
// You can combine roomId + uuid, or just a uuid, up to you:
|
|
28
|
+
const listenerId = `[${listenerData.roomId}]-[${(0, uuid_1.v4)()}]`;
|
|
29
|
+
// Add listenerId to the set for that room
|
|
30
|
+
await this.redisClient.sadd(`room:${listenerData.roomId}:listeners`, listenerId);
|
|
31
|
+
// Store listener data in a hash
|
|
32
|
+
await this.redisClient.hset(`listener:${listenerId}`, {
|
|
33
|
+
listenerId,
|
|
34
|
+
roomId: listenerData.roomId,
|
|
35
|
+
socketId: (_a = listenerData.socketId) !== null && _a !== void 0 ? _a : '',
|
|
36
|
+
token: listenerData.token,
|
|
37
|
+
name: (_b = listenerData.name) !== null && _b !== void 0 ? _b : '',
|
|
38
|
+
firstJoined: listenerData.firstJoined.toString(),
|
|
39
|
+
language: (_c = listenerData.language) !== null && _c !== void 0 ? _c : '',
|
|
40
|
+
isActive: 'true'
|
|
41
|
+
});
|
|
42
|
+
// expire listener
|
|
43
|
+
await this.redisClient.expire(`listener:${listenerId}`, EXPIRATION);
|
|
44
|
+
return listenerId;
|
|
45
|
+
}
|
|
46
|
+
async getAllListeners() {
|
|
47
|
+
// get all keys that match 'listener:*'
|
|
48
|
+
// TODO: if we have many keys, consider using SCAN instead of KEYS to avoid performance issues.
|
|
49
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
50
|
+
if (!keys || keys.length === 0) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
// fetch each hash with HGETALL
|
|
54
|
+
const listeners = [];
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const data = await this.redisClient.hgetall(key);
|
|
57
|
+
listeners.push(this.parseHashData(data));
|
|
58
|
+
}
|
|
59
|
+
return listeners;
|
|
60
|
+
}
|
|
61
|
+
async getListenersByRoom(roomId) {
|
|
62
|
+
// get the set of listener IDs
|
|
63
|
+
const listenerIds = await this.redisClient.smembers(`room:${roomId}:listeners`);
|
|
64
|
+
if (!listenerIds || listenerIds.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const listeners = [];
|
|
68
|
+
for (const id of listenerIds) {
|
|
69
|
+
// get hash data
|
|
70
|
+
const data = await this.redisClient.hgetall(`listener:${id}`);
|
|
71
|
+
// if the hash doesn't exist, it may have expired, so remove from the set
|
|
72
|
+
if (!data || Object.keys(data).length === 0) {
|
|
73
|
+
await this.redisClient.srem(`room:${roomId}:listeners`, id);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// otherwise, parse and keep it
|
|
77
|
+
listeners.push(this.parseHashData(data));
|
|
78
|
+
}
|
|
79
|
+
return listeners;
|
|
80
|
+
}
|
|
81
|
+
async getListener(listenerIdOrToken) {
|
|
82
|
+
// try direct lookup by listenerId:
|
|
83
|
+
const directData = await this.redisClient.hgetall(`listener:${listenerIdOrToken}`);
|
|
84
|
+
if (directData && directData.listenerId) {
|
|
85
|
+
return this.parseHashData(directData);
|
|
86
|
+
}
|
|
87
|
+
// if not found by listenerId, treat 'listenerIdOrToken' as a token and scan all listeners
|
|
88
|
+
const keys = await this.redisClient.keys('listener:*');
|
|
89
|
+
if (!keys || keys.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
const data = await this.redisClient.hgetall(key);
|
|
94
|
+
if (data && data.token === listenerIdOrToken) {
|
|
95
|
+
return this.parseHashData(data);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
async removeListenerByToken(token) {
|
|
101
|
+
// find the listener by token
|
|
102
|
+
const listener = await this.getListener(token);
|
|
103
|
+
if (!listener) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// remove the listener ID from the room's set
|
|
107
|
+
await this.redisClient.srem(`room:${listener.roomId}:listeners`, listener.listenerId);
|
|
108
|
+
// remove the listener data (the hash)
|
|
109
|
+
await this.redisClient.del(`listener:${listener.listenerId}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
async updateNameLanguage(listenerIdOrToken, name, language) {
|
|
113
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
114
|
+
if (!listener) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// Update the name/language on the listener’s hash key
|
|
118
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
119
|
+
name,
|
|
120
|
+
language
|
|
121
|
+
});
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
async updateSocketId(listenerIdOrToken, socketId) {
|
|
125
|
+
const listener = await this.getListener(listenerIdOrToken);
|
|
126
|
+
if (!listener) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
// Update the name/language on the listener’s hash key
|
|
130
|
+
await this.redisClient.hset(`listener:${listener.listenerId}`, {
|
|
131
|
+
socketId
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async getTargetSocketIdsByRoomLanguage(roomId, language) {
|
|
136
|
+
const listeners = await this.getListenersByRoom(roomId);
|
|
137
|
+
const filteredListeners = listeners.filter((listener) => {
|
|
138
|
+
return listener.isActive === true && listener.language === language && listener.socketId && listener.socketId.trim() !== '';
|
|
139
|
+
});
|
|
140
|
+
return filteredListeners.map((listener) => listener.socketId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.MulingstreamListenerManager = MulingstreamListenerManager;
|