-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmute.js
366 lines (338 loc) · 9.34 KB
/
mute.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/**
* This is a Haxball Headless Manager plugin that enables authenticated
* admins to mute players.
*
* The mute is IP based and will persist over room restarts. Use !clearmutes
* to remove all mutes.
*
* This plugin provides following commands:
*
* - !mute #PLAYER_ID
* - Mutes player with given id.
* - !unmute MUTE_NUMBER or #PLAYER_ID
* - !mutelist
* - Use this to see muted players and get the MUTE_NUMBER for unmute command.
* - !clearmutes
*
* And exports following functions:
*
* - room.getPlugin('hr/mute').isMuted(playerObject);
* - playerObject is the HaxBall PlayerObject.
* - Returns true if the player is muted.
*
*/
let room = HBInit();
room.pluginSpec = {
name: "hr/mute",
author: "salamini",
version: "1.0.1",
config: {
// This message will be sent to muted players when they try to write
// something.
muteMessage: "You cannot send messages because you are muted.",
// These roles cannot be muted.
protectedRoles: ["host", "admin"],
// Only these roles can use the commands.
allowedRoles: ["admin", "admin"],
// Allow talking when team captain. If this is set to true, the player is
// allowed to talk when (s)he is the first player in a team.
allowTalkingWhenCaptain: false,
},
dependencies: ["sav/players", "sav/roles", "sav/commands", "hr/utils"],
order: {},
incompatible_with: [],
};
// Holds the muted players.
let muteMap = new Map();
/**
* Parses the id into number. The id can be prefixed with #.
*
* @param {string|number} id
* @returns {number|null} - Id or null if the given id cant be parsed.
*/
function parseId(id) {
let pId = id;
if (isNaN(+pId)) {
if (typeof pId === "string" && pId.startsWith("#")) {
pId = +pId.slice(1);
return isNaN(pId) ? null : pId;
} else {
return null;
}
} else {
return +pId;
}
}
/**
* Mutes the player.
*
* @param {PlayerObject} player - HaxBall PlayerObject.
* @returns {void}
*/
function mutePlayer(player) {
muteMap.set(player.conn, player);
room.getPlugin("hhm/persistence").persistPluginData(room);
}
/**
* Unmutes the player.
*
* The argument can be either index of muteMap or a player id prefixed with
* #, but if player id is used the player needs to be in the room.
*
* Returns the removed player if the index or id was valid and player was
* unmuted.
*
* @param {number|string} indexOrPlayerId - Index of the rule to remove.
* @returns {PlayerObject} - Unmuted player or null if no player was unmuted.
*/
function removeMute(indexOrPlayerId) {
if (typeof indexOrPlayerId === "string" && indexOrPlayerId.startsWith("#")) {
const removedPlayer = room.getPlayer(parseId(indexOrPlayerId));
if (!removedPlayer || !muteMap.has(removedPlayer.conn)) {
return null;
}
muteMap.delete(removedPlayer.conn);
room.getPlugin("hhm/persistence").persistPluginData(room);
return removedPlayer;
}
const index = +indexOrPlayerId;
if (isNaN(index)) return null;
let i = 0;
for (let key of muteMap.keys()) {
if (i === indexOrPlayerId) {
const removedPlayer = muteMap.get(key);
muteMap.delete(key);
room.getPlugin("hhm/persistence").persistPluginData(room);
return removedPlayer;
}
i++;
}
return null;
}
/**
* Checks if the player is team captain.
*
* Determines the captain by checking if the player is on top of either
* teams list.
*
* @param {PlayerObject} player - HaxBall PlayerObject.
* @returns {boolean} - Is the player team captain.
*/
function isPlayerTeamCaptain(player) {
const playerList = room.getPlayerList();
const redTeamCap = playerList.find((p) => p.team === 1);
const blueTeamCap = playerList.find((p) => p.team === 2);
if (
(redTeamCap && redTeamCap.id === player.id) ||
(blueTeamCap && blueTeamCap.id === player.id)
) {
return true;
}
return false;
}
/**
* Gets the player with given conn string in the room. If there is no player
* with given conn string, then returns undefined.
*
* @param {string} conn - Player conn string.
* @returns {PlayerObject} - Player in the room whos conn string
* matches the given one.
*/
function findPlayerInRoomWithConn(conn) {
return room.getPlayerList().find((p) => p.conn === conn);
}
/**
* Checks if the player with given id is protected against mute based on the
* `config.protectedRoles` property.
*
* @param {number} playerId - The players id.
* @returns {boolean} - Is the player protected.
*/
function isPlayerProtected(playerId) {
const protectedRoles = room.getConfig("protectedRoles");
if (!Array.isArray(protectedRoles)) {
return false;
}
const roles = room.getPlugin("sav/roles");
for (let role of protectedRoles) {
if (roles.hasPlayerRole(playerId, role)) {
return true;
}
}
return false;
}
/**
* Checks if the player is allowed to run a command based on the
* config.allowedRoles property.
*
* @param {PlayerObject} player - HaxBall PlayerObject.
* @param {boolean} - Is player allowed to run command.
*/
function isPlayerAllowedToRunCommand(player) {
const roles = room.getPlugin(`sav/roles`);
if (!roles) return false;
return roles.ensurePlayerRoles(
player.id,
room.getConfig("allowedRoles"),
room
);
}
/***************
* PERSIST DATA
***************/
function serializeMuteMap(muteMap) {
let serializedMuteMap = {};
for (const [key, value] of muteMap) {
serializedMuteMap[key] = value;
}
return JSON.stringify(serializedMuteMap);
}
function deserializeMuteMap(data) {
let obj = JSON.parse(data);
let deserializedMuteMap = new Map();
for (const key of Object.getOwnPropertyNames(obj)) {
deserializedMuteMap.set(key, obj[key]);
}
return deserializedMuteMap;
}
function handlePersistData() {
return serializeMuteMap(muteMap);
}
function handleRestoreData(data) {
if (data === undefined) return;
muteMap = deserializeMuteMap(data);
}
/***********
* COMMANDS
***********/
room.onCommand1_mute = {
function: (byPlayer, [id]) => {
if (!isPlayerAllowedToRunCommand(byPlayer)) return;
const player = room.getPlayer(parseId(id));
if (!player) {
room.sendAnnouncement(`No player with id ${id}.`, byPlayer.id, 0xff0000);
return;
}
if (isPlayerProtected(player.id)) {
room.sendAnnouncement(
`This player has immunity for mutes.`,
byPlayer.id,
0xff0000
);
return;
}
mutePlayer(player);
room.sendAnnouncement(
`Player ${player.name} muted.`,
byPlayer.id,
0x00ff00
);
room.sendAnnouncement(`You have been muted.`, player.id, 0xff0000);
},
data: {
"sav/help": {
text: " #PLAYER_ID (mutes player with the given id)",
roles: room.getConfig("allowedRoles"),
},
},
};
room.onCommand1_unmute = {
function: (byPlayer, [muteNumber]) => {
if (!isPlayerAllowedToRunCommand(byPlayer)) return;
const unmutedPlayer = removeMute(muteNumber);
if (!unmutedPlayer) {
room.sendAnnouncement(
`Could not unmute ${muteNumber}! ` +
`Make sure the argument is either a number listed ` +
`in !mutelist or player ID prefixed with #.`,
byPlayer.id,
0xff0000
);
return;
}
room.sendAnnouncement(
`Player ${unmutedPlayer.name} unmuted.`,
byPlayer.id,
0x00ff00
);
const playerInRoom = findPlayerInRoomWithConn(unmutedPlayer.conn);
if (playerInRoom) {
room.sendAnnouncement(
`You have been unmuted.`,
playerInRoom.id,
0x00ff00
);
}
},
data: {
"sav/help": {
text:
" MUTE_NUMBER or #PLAYER_ID (Unmutes player. " +
"See !mutelist for the numbers or use #PLAYER_ID)",
roles: room.getConfig("allowedRoles"),
},
},
};
room.onCommand0_clearmutes = {
function: (byPlayer) => {
if (!isPlayerAllowedToRunCommand(byPlayer)) return;
muteMap.clear();
room.sendAnnouncement(`All mutes cleared!`, null, 0x00ff00);
},
data: {
"sav/help": {
text: " (removes all mutes)",
roles: room.getConfig("allowedRoles"),
},
},
};
room.onCommand0_mutelist = {
function: (byPlayer) => {
if (!isPlayerAllowedToRunCommand(byPlayer)) return;
if (muteMap.size === 0) {
room.sendAnnouncement("No muted players.", byPlayer.id, 0xff0000);
return;
}
room.sendAnnouncement("MUTE_NUMBER - PLAYER", byPlayer.id, 0x00ff00);
const mutelist = [...muteMap.values()].map((p, i) => `${i} - ${p.name}`);
const utils = room.getPlugin('hr/utils');
utils.sendLongAnnouncement(mutelist.join('\n'), byPlayer.id, 0x00FF00);
},
data: {
"sav/help": {
text: " (lists players that have been muted)",
roles: room.getConfig("allowedRoles"),
},
},
};
/**********
* EXPORTS
**********/
/**
* Checks if the player is muted.
*
* @param {PlayerObject} player - HaxBall PlayerObject.
* @returns {boolean} - True if player is muted, false if not.
*/
function isMuted(player) {
return muteMap.has(player.conn);
}
/*****************
* EVENT HANDLERS
*****************/
function handlePlayerChat(player) {
if (isMuted(player)) {
if (
room.getConfig("allowTalkingWhenCaptain") &&
isPlayerTeamCaptain(player)
) {
return;
}
room.sendAnnouncement(room.getConfig("muteMessage"), player.id, 0xff0000);
return false;
}
}
room.isMuted = isMuted;
room.onPlayerChat = handlePlayerChat;
room.onPersist = handlePersistData;
room.onRestore = handleRestoreData;