Skip to content
This repository
Browse code

Merge pull request #30 from callumacrae/issue/27

[issues/27] Added comments
  • Loading branch information...
commit 2ea6bd4b4e8b004501d807afe37ed5ddb8418f3e 2 parents 3ae545c + 5b149fe
Callum Macrae authored February 18, 2012
100  Hex/handler.js
@@ -4,11 +4,13 @@ handler = function (info, admin, noreply) {
4 4
 	nick = info[1];
5 5
 	chan = info[3];
6 6
 
  7
+	// If received as a PM, replies to the PM.
7 8
 	pm = chan.search(/^[^#]/) !== -1;
8 9
 	if (pm) {
9 10
 		chan = nick;
10 11
 	}
11 12
 
  13
+	// If it is @ someone, sets the nick to be them (see commands.md)
12 14
 	reply = /^(.+) @ ?(.+)/.exec(info[4]);
13 15
 	if (reply) {
14 16
 		info[4] = reply[1];
@@ -16,10 +18,12 @@ handler = function (info, admin, noreply) {
16 18
 	}
17 19
 	reply = null;
18 20
 
  21
+	// sets cmd and cmd_end
19 22
 	index = info[4].indexOf(' ');
20 23
 	cmd = (index === -1) ? info[4] : info[4].slice(0, index);
21 24
 	cmd_end = (index === -1) ? null : info[4].slice(index + 1);
22 25
 
  26
+	// prevents h4x!
23 27
 	if (cmd.search(/(\.|\/)/) !== -1) {
24 28
 		return false;
25 29
 	}
@@ -31,11 +35,17 @@ handler = function (info, admin, noreply) {
31 35
 				console.log(nick + ' tried to access admin without correct permissions.')
32 36
 				return false;
33 37
 			}
  38
+
  39
+			// splits cmd_end; find the subcommand.
34 40
 			index = cmd_end.indexOf(' ');
35 41
 			cmd = (index === -1) ? cmd_end : cmd_end.slice(0, index);
36 42
 			cmd_end = (index === -1) ? null : cmd_end.slice(index + 1);
37 43
 
38 44
 			switch (cmd.toLowerCase()) {
  45
+
  46
+				/**
  47
+				 * Sends a list of admin commands to the user via PM.
  48
+				 */
39 49
 				case 'help':
40 50
 				case 'h':
41 51
 					chan = nick;
@@ -65,6 +75,9 @@ handler = function (info, admin, noreply) {
65 75
 					];
66 76
 					break;
67 77
 
  78
+				/**
  79
+				 * Bans user from current or specified channel.
  80
+				 */
68 81
 				case 'ban':
69 82
 					if (admin < 4) {
70 83
 						reply = 'Admin level 4 required for this operation.';
@@ -78,6 +91,9 @@ handler = function (info, admin, noreply) {
78 91
 					log = 'ban ' + cmd_end + ' (from ' + chan + ')';
79 92
 					break;
80 93
 
  94
+				/**
  95
+				 * Devoices user in current or specified channel.
  96
+				 */
81 97
 				case 'devoice':
82 98
 					if (admin < 2) {
83 99
 						reply = 'Admin level 2 required for this operation.';
@@ -91,11 +107,17 @@ handler = function (info, admin, noreply) {
91 107
 					log = 'devoice ' + cmd_end + ' (from ' + chan + ')';
92 108
 					break;
93 109
 
  110
+				/**
  111
+				 * Reloads handler.js
  112
+				 */
94 113
 				case 'flush':
95 114
 					flush = true;
96 115
 					reply = 'Flushing...';
97 116
 					break;
98 117
 
  118
+				/**
  119
+				 * Makes the bot join specified channel.
  120
+				 */
99 121
 				case 'join':
100 122
 					if (admin < 7) {
101 123
 						reply = 'Admin level 7 required for this operation.';
@@ -107,6 +129,9 @@ handler = function (info, admin, noreply) {
107 129
 					flush = true;
108 130
 					break;
109 131
 
  132
+				/**
  133
+				 * Kicks user from current or specified channel.
  134
+				 */
110 135
 				case 'kick':
111 136
 					if (admin < 3) {
112 137
 						reply = 'Admin level 3 required for this operation.';
@@ -120,6 +145,9 @@ handler = function (info, admin, noreply) {
120 145
 					log = 'kick ' + cmd_end + ' (from ' + chan + ')';
121 146
 					break;
122 147
 
  148
+				/**
  149
+				 * Mutes the bot in the current channel.
  150
+				 */
123 151
 				case 'mute':
124 152
 					if (admin < 6) {
125 153
 						reply = 'Admin level 6 required for this operation.';
@@ -131,6 +159,9 @@ handler = function (info, admin, noreply) {
131 159
 					reply = 'Muted.';
132 160
 					break;
133 161
 
  162
+				/**
  163
+				 * Parts the current or specified channel.
  164
+				 */
134 165
 				case 'part':
135 166
 					if (admin < 7) {
136 167
 						reply = 'Admin level 7 required for this operation.';
@@ -144,6 +175,9 @@ handler = function (info, admin, noreply) {
144 175
 					flush = true;
145 176
 					break;
146 177
 
  178
+				/**
  179
+				 * Restarts the bot.
  180
+				 */
147 181
 				case 'quit':
148 182
 				case 'q':
149 183
 				case 'restart':
@@ -156,6 +190,9 @@ handler = function (info, admin, noreply) {
156 190
 					process.exit();
157 191
 					break;
158 192
 
  193
+				/**
  194
+				 * Sends raw data to the IRC server.
  195
+				 */
159 196
 				case 'raw':
160 197
 					if (admin < 10) {
161 198
 						reply = 'Admin level 10 required for this operation.';
@@ -165,6 +202,9 @@ handler = function (info, admin, noreply) {
165 202
 					console.log(nick + ' sent RAW: ' + cmd_end);
166 203
 					break;
167 204
 
  205
+				/**
  206
+				 * Removes the specified command.
  207
+				 */
168 208
 				case 'remove':
169 209
 				case 'rm':
170 210
 					if (admin < 6) {
@@ -177,6 +217,9 @@ handler = function (info, admin, noreply) {
177 217
 					log = cmd + ' ' + cmd_end;
178 218
 					break;
179 219
 
  220
+				/**
  221
+				 * Adds or sets the specified command.
  222
+				 */
180 223
 				case 'set':
181 224
 					if (admin < 6) {
182 225
 						reply = 'Admin level 6 required for this operation.';
@@ -193,8 +236,11 @@ handler = function (info, admin, noreply) {
193 236
 					reply = 'Successfully set ' + cmd;
194 237
 					break;
195 238
 
  239
+				/**
  240
+				 * Adds, modifies, removes or lists super users.
  241
+				 */
196 242
 				case 'su':
197  
-					//dont check whether admin is level 10 yet - level 3s can list admins
  243
+					// Don't check whether admin is level 10 yet - level 3s can list admins
198 244
 					cmd = cmd_end.split(' ', 3);
199 245
 					switch (cmd[0].toLowerCase()) {
200 246
 						case 'add':
@@ -255,6 +301,9 @@ handler = function (info, admin, noreply) {
255 301
 					log = 'su ' + cmd;
256 302
 					break;
257 303
 
  304
+				/**
  305
+				 * Mutes bot for half an hour.
  306
+				 */
258 307
 				case 'tmpmute':
259 308
 					if (admin < 4) {
260 309
 						reply = 'Admin level 4 required for this operation.';
@@ -274,6 +323,11 @@ handler = function (info, admin, noreply) {
274 323
 					reply = 'Muted for half an hour.';
275 324
 					break;
276 325
 
  326
+				/**
  327
+				 * Unmutes the bot.
  328
+				 *
  329
+				 * Can unmute mutes set using both mute and tmpmute.
  330
+				 */
277 331
 				case 'unmute':
278 332
 					if (admin < 4) {
279 333
 						reply = 'Admin level 4 required for this operation.';
@@ -287,6 +341,9 @@ handler = function (info, admin, noreply) {
287 341
 					}
288 342
 					break;
289 343
 
  344
+				/**
  345
+				 * Voices specified user in current or specified channel.
  346
+				 */
290 347
 				case 'voice':
291 348
 					if (admin < 2) {
292 349
 						reply = 'Admin level 2 required for this operation.';
@@ -306,6 +363,9 @@ handler = function (info, admin, noreply) {
306 363
 			console.log('"admin ' + ((log === undefined) ? cmd : log) + '" called by ' + nick);
307 364
 			break;
308 365
 
  366
+		/**
  367
+		 * Returns a link to a specified google search.
  368
+		 */
309 369
 		case 'g':
310 370
 		case 'google':
311 371
 			reply = 'http://google.com/';
@@ -314,6 +374,9 @@ handler = function (info, admin, noreply) {
314 374
 			}
315 375
 			break;
316 376
 
  377
+		/**
  378
+		 * Executes given JavaScript and returns value.
  379
+		 */
317 380
 		case 'js':
318 381
 		case 'javascript':
319 382
 			var exec = require('child_process').exec;
@@ -341,6 +404,9 @@ handler = function (info, admin, noreply) {
341 404
 			});
342 405
 			break;
343 406
 
  407
+		/**
  408
+		 * Returns a link to a specified lmgtfy search.
  409
+		 */
344 410
 		case 'lmgtfy':
345 411
 			reply = 'http://lmgtfy.com/';
346 412
 			if (cmd_end) {
@@ -348,6 +414,9 @@ handler = function (info, admin, noreply) {
348 414
 			}
349 415
 			break;
350 416
 
  417
+		/**
  418
+		 * Shows the bot uptime.
  419
+		 */
351 420
 		case 'uptime':
352 421
 			var num, uptime = new Date().getTime() - start.getTime();
353 422
 			reply = 'Uptime: ';
@@ -395,7 +464,9 @@ handler = function (info, admin, noreply) {
395 464
 			}
396 465
 			break;
397 466
 
398  
-
  467
+		/**
  468
+		 * Displays a wiki link
  469
+		 */
399 470
 		case 'w':
400 471
 		case 'wiki':
401 472
 			if (!cmd_end) {
@@ -427,6 +498,9 @@ handler = function (info, admin, noreply) {
427 498
 			req.end();
428 499
 			break;
429 500
 
  501
+		/**
  502
+		 * Displays a wolframalpha link.
  503
+		 */
430 504
 		case 'wa':
431 505
 		case 'wolfram':
432 506
 		case 'wolframalpha':
@@ -436,12 +510,18 @@ handler = function (info, admin, noreply) {
436 510
 			}
437 511
 			break;
438 512
 
  513
+		/**
  514
+		 * Displays info the bot has on the user.
  515
+		 */
439 516
 		case 'whoami':
440 517
 			nick = info[1];
441 518
 			reply = 'Your nick is "' + info[1] + '". Your hostmask is "' + info[2] + '". ';
442 519
 			reply += (admin) ? 'You are admin level ' + admin + '.' : 'You are not an admin.';
443 520
 			break;
444 521
 
  522
+		/**
  523
+		 * Searches the msgs directory for other messages set using the set command.
  524
+		 */
445 525
 		default:
446 526
 			var file, fs = require('fs');
447 527
 			try {
@@ -456,6 +536,7 @@ handler = function (info, admin, noreply) {
456 536
 			break;
457 537
 	}
458 538
 
  539
+	// Splits the message and sends them one by one.
459 540
 	if (reply && !noreply) {
460 541
 		if (typeof reply === 'string') {
461 542
 			reply = [reply];
@@ -472,6 +553,11 @@ handler = function (info, admin, noreply) {
472 553
 	return flush;
473 554
 };
474 555
 
  556
+/**
  557
+ * Turns HTML into a string.
  558
+ *
  559
+ * @param s The HTML string.
  560
+ */
475 561
 html_decode = function (s) {
476 562
 	var c, m, d = s;
477 563
 
@@ -492,6 +578,13 @@ html_decode = function (s) {
492 578
 	return d;
493 579
 };
494 580
 
  581
+/**
  582
+ * Function called by Hex on every message; simply monitors how many messages
  583
+ * users are sending to a channel. Takes action if they're flooding.
  584
+ *
  585
+ * @param nick The nick of the user.
  586
+ * @param chan The channel the message was sent to.
  587
+ */
495 588
 antiflood = function (nick, chan) {
496 589
 	if (hex.info.names[chan][nick] === undefined) {
497 590
 		return;
@@ -562,6 +655,9 @@ antiflood = function (nick, chan) {
562 655
 	}
563 656
 };
564 657
 
  658
+/**
  659
+ * Simple server to handle HTTP requests to the logs.
  660
+ */
565 661
 server = function (req, res) {
566 662
 	var date, file, output;
567 663
 
28  Hex/hex.js
@@ -25,6 +25,10 @@ hex = new IRC(config, function (msg) {
25 25
 
26 26
 global.mute = [];
27 27
 
  28
+/**
  29
+ * Handles all private messages. Handles messages sent to the bot, but also
  30
+ * handles stuff like flooding.
  31
+ */
28 32
 hex.on(/^:([^!]+)![^@]+@([^ ]+) PRIVMSG ([^ ]+) :(.+)$/i, function (info) {
29 33
 	var admin, ad_info, flush;
30 34
 	admin = (admins[info[1]] === undefined) ? 0 : admins[info[1]].level;
@@ -64,6 +68,11 @@ hex.on(/^:([^!]+)![^@]+@([^ ]+) PRIVMSG ([^ ]+) :(.+)$/i, function (info) {
64 68
 	}
65 69
 });
66 70
 
  71
+/**
  72
+ * Listens for kicks, and if it is the bot which is kicked then it rejoin and
  73
+ * sends an angry message to whoever kicked it. If it cannot rejoin, it waits
  74
+ * until it can rejoin before sending said angry message.
  75
+ */
67 76
 hex.on(/^:([^!]+)![^@]+@[^ ]+ KICK (#[^ ]+) ([^ ]+) :/, function(info) {
68 77
 	if (info[3] === hex.info.nick) {
69 78
 		console.log('Kicked from ' + info[2] + ' by ' + info[1] + '. Rejoining.');
@@ -74,6 +83,11 @@ hex.on(/^:([^!]+)![^@]+@[^ ]+ KICK (#[^ ]+) ([^ ]+) :/, function(info) {
74 83
 	}
75 84
 });
76 85
 
  86
+/**
  87
+ * Handles bot admin stuff - when someone joins, it checks whether they
  88
+ * are a super user or not and if they are it validates it. If they quit, it clears
  89
+ * up the admin array.
  90
+ */
77 91
 hex.on(/^:([^!]+)![^@]+@([^ ]+) (JOIN|QUIT)/, function (info) {
78 92
 	if (config.su[info[1]] !== undefined) {
79 93
 		if (info[3] === 'JOIN') {
@@ -94,6 +108,9 @@ hex.on(/^:([^!]+)![^@]+@([^ ]+) (JOIN|QUIT)/, function (info) {
94 108
 	}
95 109
 });
96 110
 
  111
+/**
  112
+ * Handles admins who change nick.
  113
+ */
97 114
 hex.on(/^:([^!]+)![^@]+@[^ ]+ NICK :(.+)$/, function (info) {
98 115
 	if (admins[info[1]] !== undefined) {
99 116
 		admins[info[2]] = admins[info[1]];
@@ -101,11 +118,18 @@ hex.on(/^:([^!]+)![^@]+@[^ ]+ NICK :(.+)$/, function (info) {
101 118
 	}
102 119
 });
103 120
 
  121
+/**
  122
+ * Creates the server for displaying logs. May also handle more, in the
  123
+ * distant and faraway future…
  124
+ */
104 125
 http.createServer(function (req, res) {
105 126
 	server(req, res); //wrapper here so that we can edit server()
106 127
 }).listen(config.log.web.port, config.log.web.addr);
107 128
 console.log('Server now listening.');
108 129
 
  130
+/**
  131
+ * Listens to the x10hosting twitter, and forwards any tweets into #x10hosting
  132
+ */
109 133
 var Twitter = require('twitter');
110 134
 var twit = new Twitter({
111 135
 	user: config.twitter.user,
@@ -121,6 +145,10 @@ twit.on('tweet', function (tweet) {
121 145
 	console.log('Tweet streamer error: ' + err);
122 146
 });
123 147
 
  148
+/**
  149
+ * Logs any uncaught exceptions, so that the bot doesn't shut down whenever
  150
+ * anyone finds a bug.
  151
+ */
124 152
 process.on('uncaughtException', function (error) {
125 153
 	console.log('Uncaught Exception: ' + error);
126 154
 });
281  docs/commands.md
Source Rendered
@@ -5,10 +5,40 @@ completely up to date, just throw something at one of us on irc or file an
5 5
 issue if you find any out of date content.
6 6
 
7 7
 This is in addition to `Hex: help` and `Hex: a help`, as it is more
8  
-comprehensive and detailed (and easier to read). It is divided into two
  8
+comprehensive and detailed (and easier to read). It is divided into two main
9 9
 sections; user commands and admin commands.
10 10
 
11 11
 
  12
+## Calling the bot ##
  13
+
  14
+There are a couple ways to call the bot. You can either send it a private
  15
+message, or you can send it in the channel by prefixing "Hex" to it:
  16
+
  17
+```irc
  18
+<callumacrae> Hex: help
  19
+<@Hex> callumacrae: Currently available commands:
  20
+<@Hex> callumacrae: about, admin (a), buydomain, cpanel (cp), dns, domainadd,
  21
+            flushdns, google (d), help, lmgtfy, mysql, paid, tickets, tos, upgrade,
  22
+            uptime, wiki (w), wolframalpha (wa) and whoami.
  23
+```
  24
+
  25
+The colon is entirely optional, and both the bot name and commands are
  26
+not case sensitive.
  27
+
  28
+
  29
+### Directing a command ###
  30
+
  31
+You can direct a command at someone using the `@` operator:
  32
+
  33
+```irc
  34
+<callumacrae> Hex: help @ GtoXic
  35
+<@Hex> GtoXic: Currently available commands:
  36
+<@Hex> GtoXic: about, admin (a), buydomain, cpanel (cp), dns, domainadd, flushdns,
  37
+            google (d), help, lmgtfy, mysql, paid, tickets, tos, upgrade, uptime, wiki
  38
+            (w), wolframalpha (wa) and whoami.
  39
+```
  40
+
  41
+
12 42
 ## User Commands ##
13 43
 
14 44
 ### google ###
@@ -61,6 +91,21 @@ That For You) instead.
61 91
 ```
62 92
 
63 93
 
  94
+### login ###
  95
+**Aliases:** None
  96
+
  97
+```
  98
+Hex: login
  99
+```
  100
+
  101
+Logs the user in as an admin, instead of them having to cycle.
  102
+
  103
+```irc
  104
+<callumacrae> Hex: login
  105
+<@Hex> callumacrae: You have been identified as an admin.
  106
+```
  107
+
  108
+
64 109
 ### uptime ###
65 110
 **Aliases:** None
66 111
 
@@ -165,3 +210,237 @@ change at any time. The below list contains most of them, as of 16/	02/12.
165 210
 * **ping:** Shouts PONG at the user.
166 211
 * **tickets:** Tells the user how to create a ticket.
167 212
 * **tos:** Links the user to the ToS.
  213
+
  214
+
  215
+
  216
+## Admin Commands ##
  217
+
  218
+Admin commands can be accessed by appending the command with "admin "
  219
+or "a ". For example:
  220
+
  221
+```irc
  222
+<callumacrae> Hex: a ban Dead-i
  223
+```
  224
+
  225
+They require the user calling them to be of a certain admin level (specified
  226
+below the commands below by "Level"). Failing to be of that level will result
  227
+in an error (or if they're not an admin at all, it will fail silently).
  228
+
  229
+
  230
+### help ###
  231
+**Aliases:** h
  232
+**Level:** 0
  233
+
  234
+```
  235
+Hex: a help [ all ]
  236
+```
  237
+
  238
+Sends the user a list of admin commands that they can use, or if they
  239
+specify "all", it gives them more detail about each command (but it doesn't
  240
+contain quite as much detail as this document).
  241
+
  242
+It is sent in multiple private messages, not into the channel you requested
  243
+it in (assuming you requested it in a channel).
  244
+
  245
+
  246
+### ban ###
  247
+**Aliases:** None
  248
+**Level:** 4
  249
+
  250
+```
  251
+Hex: a ban <user> [ <channel >]
  252
+```
  253
+
  254
+Bans the specified user from either the current channel or the specified
  255
+channel; if a channel is not specified, it will ban the user from the
  256
+current channel.
  257
+
  258
+
  259
+### devoice ###
  260
+**Aliases:** None
  261
+**Level:** 2
  262
+
  263
+```
  264
+Hex: a devoice <user> [ <channel> ]
  265
+```
  266
+
  267
+Devoices the specified user in either the current channel or the specified
  268
+channel; if the channel is not specified, it will devoice the user in the
  269
+current channel.
  270
+
  271
+
  272
+### flush ###
  273
+**Aliases:** None
  274
+**Level:** 0 (not harmful)
  275
+
  276
+```
  277
+Hex: a flush
  278
+```
  279
+
  280
+Reloads handler.js, causing any updates in that file to be applied.
  281
+
  282
+
  283
+### join ###
  284
+**Aliases:** None
  285
+**Level:** 7
  286
+
  287
+```
  288
+Hex: a join <channel>
  289
+```
  290
+
  291
+Commands the bot to join the specified channel. If it is banned from that
  292
+channel, it will *not* try to join the channel again or try to unban itself, it
  293
+just won't join.
  294
+
  295
+
  296
+### kick ###
  297
+**Aliases:** None
  298
+**Level:** 3
  299
+
  300
+```
  301
+Hex: a kick <user> [ <channel> ]
  302
+```
  303
+
  304
+Kicks the specified user from either the current channel or the specified
  305
+channel; if the channel is not specified, it will kick the user from the
  306
+current channel. This command does not ban the user, and they will be able
  307
+to rejoin immediately.
  308
+
  309
+
  310
+### mute ###
  311
+**Aliases:** None
  312
+**Level:** 6
  313
+
  314
+```
  315
+Hex: a mute
  316
+```
  317
+
  318
+Mutes the bot in the current channel. Use the `unmute` command to unmute
  319
+the bot, or `tmpmute` if you only want to mute the bot for a period of time.
  320
+
  321
+
  322
+### part ###
  323
+**Aliases:** None
  324
+**Level:** 7
  325
+
  326
+```
  327
+Hex: a part [ <channel> ]
  328
+```
  329
+
  330
+Commands the bot to part either the current channel or the specified
  331
+channel; if the channel is not specified, it will part the current channel.
  332
+
  333
+
  334
+### restart ###
  335
+**Aliases:** quit, q
  336
+**Level:** 10
  337
+
  338
+```
  339
+Hex: a restart
  340
+```
  341
+
  342
+Restarts the bot. It is not possible to quit the bot without access to the
  343
+server.
  344
+
  345
+
  346
+### raw ###
  347
+**Aliases:** None
  348
+**Level:** 10
  349
+
  350
+```
  351
+Hex: a raw <text>
  352
+```
  353
+
  354
+Sends the specified text as raw IRC (equivilant to client /quote).
  355
+
  356
+
  357
+### remove ###
  358
+**Aliases:** rm
  359
+**Level:** 6
  360
+
  361
+```
  362
+Hex: a rm <command>
  363
+```
  364
+
  365
+Removes the specified command. Use `set` to modify or add a command.
  366
+
  367
+
  368
+### set ###
  369
+**Aliases**: None
  370
+**Level:** 6
  371
+
  372
+```
  373
+Hex: a set <command> <text>
  374
+```
  375
+
  376
+Sets the specified command to the specified text.
  377
+
  378
+
  379
+### su add ###
  380
+**Aliases:** su add
  381
+**Level:** 10
  382
+
  383
+```
  384
+Hex: a su add <user> <level>
  385
+```
  386
+
  387
+Adds the specified user as a super user of the specified level.
  388
+
  389
+
  390
+### su remove ###
  391
+**Aliases:** su rm
  392
+**Level:** 10
  393
+
  394
+```
  395
+Hex: a su remove <user>
  396
+```
  397
+
  398
+Removes the super user permissions of the specified user.
  399
+
  400
+
  401
+### su list ###
  402
+**Aliases:** None
  403
+**Level:** 3
  404
+
  405
+```
  406
+Hex: a su list
  407
+```
  408
+
  409
+Sends a list of super users, their levels, and whether they're logged into the
  410
+bot or not in a private message to the user who requested it.
  411
+
  412
+
  413
+### tmpmute ###
  414
+**Aliases:** None
  415
+**Level:** 4
  416
+
  417
+```
  418
+Hex: a tmpmute
  419
+```
  420
+
  421
+Mutes the bot in the current channel for half an hour.
  422
+
  423
+
  424
+### unmute ###
  425
+**Aliases:** None
  426
+**Level:** 4
  427
+
  428
+```
  429
+Hex: a unmute
  430
+```
  431
+
  432
+Unmutes the bot in the current channel. It can unmute mutes set with both
  433
+`mute` and `tmpmute`.
  434
+
  435
+
  436
+### voice ###
  437
+**Aliases:** None
  438
+**Level:** 2
  439
+
  440
+```
  441
+Hex: a voice <user> [ <channel> ]
  442
+```
  443
+
  444
+Voices the specified user in either the current channel or the specified
  445
+channel; if the channel is not specified, it will voice the user in the
  446
+current channel.

0 notes on commit 2ea6bd4

Please sign in to comment.
Something went wrong with that request. Please try again.