bancho.js 0.11.5 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,7 +1,26 @@
1
1
  # Pre-release
2
2
 
3
+ ## Version 0.12
4
+
5
+ ### Version 0.12.0
6
+
7
+ - **Support creating lobbies with filtered names by queuing lobby creation**
8
+ Created lobbies are no longer checked against their names for the `createLobby` call to succeed. This helps in cases where the requested lobby name has filtered words.
9
+ As a consequence, lobby creation can no longer be done in parallel. They are now transparently queued. The promise returned by `createLobby` still resolves when the lobby is created, so this should be fully transparent to the user.
10
+ - **Fix infinite loop when message fails to send**
11
+ - BanchoChannel: Fix join and leave methods hanging forever if channel is already joined/left
12
+ - BanchoChannel: Fix JOIN and PART events being emitted before they're fully processed
13
+ - Fix slots types not being nullable
14
+
3
15
  ## Version 0.11
4
16
 
17
+ ### Version 0.11.6
18
+
19
+ - **BanchoLobby: Add timer events**
20
+ Added the following events for `!mp timer`: `timerTick`, `timerAborted`, `timerEnded`
21
+ Added the following events for `!mp start`: `startTimerStarted`, `startTimerTick`, `startTimerAborted`
22
+ They are emitted with a `seconds` parameter where applicable.
23
+
5
24
  ### Version 0.11.5
6
25
 
7
26
  - Apply new personal account rate limits
@@ -369,4 +388,4 @@
369
388
 
370
389
  ## Version 0.1
371
390
 
372
- - Initial single-file library with very basic IRC support
391
+ - Initial single-file library with very basic IRC support
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![NPM](https://nodei.co/npm/bancho.js.png?compact=true)](https://nodei.co/npm/bancho.js/)
4
4
  [![pipeline status](https://git.cartooncraft.fr/ThePooN/bancho.js/badges/master/pipeline.svg)](https://git.cartooncraft.fr/ThePooN/bancho.js/commits/master)
5
5
 
6
- ## Current release: 0.11.5
6
+ ## Current release: 0.12.0
7
7
 
8
8
  # Introduction
9
9
 
package/index.d.ts CHANGED
@@ -5,17 +5,17 @@ declare module "bancho.js" {
5
5
 
6
6
  export class BanchoClient extends EventEmitter {
7
7
  constructor(options: BanchoClientOptions)
8
-
8
+
9
9
  /**
10
10
  * Populated with a Nodesu client, if api key is passed to the constructor
11
11
  */
12
12
  osuApi: nodesu.Client
13
-
13
+
14
14
  /**
15
15
  * Get a BanchoUser instance for the specified user
16
16
  */
17
17
  getUser(username: string): BanchoUser
18
-
18
+
19
19
  /**
20
20
  * Get a BanchoUser representing ourself
21
21
  */
@@ -25,7 +25,7 @@ declare module "bancho.js" {
25
25
  * Get a BanchoUser instance for the specified user id
26
26
  */
27
27
  getUserById(userid: number): Promise<BanchoUser>
28
-
28
+
29
29
  /**
30
30
  * Get a BanchoChannel instance for the specified name
31
31
  */
@@ -37,99 +37,99 @@ declare module "bancho.js" {
37
37
  * @param privateLobby Mark as private
38
38
  */
39
39
  createLobby(name: string, private?: boolean): Promise<BanchoMultiplayerChannel>
40
-
40
+
41
41
  /**
42
42
  * Connects to Bancho, rejects an Error if connection fails
43
43
  */
44
44
  connect(): Promise<null>
45
-
45
+
46
46
  /**
47
47
  * Disconnects from Bancho
48
48
  */
49
49
  disconnect(): void
50
-
50
+
51
51
  /**
52
52
  * Returns the current connection state.
53
53
  */
54
54
  getConnectState(): Symbol
55
-
55
+
56
56
  /**
57
57
  * Returns true if the connectState is Connected, false otherwise.
58
58
  */
59
59
  isConnected(): boolean
60
-
60
+
61
61
  /**
62
62
  * Returns true if the connectState is Disconnected, otherwise false.
63
63
  */
64
64
  isDisconnected(): boolean
65
-
65
+
66
66
  /**
67
67
  * Registers a listener for channel messages.
68
68
  * @param listener the callback
69
69
  */
70
70
  on(event: "CM", listener: (message: ChannelMessage) => void): this
71
-
71
+
72
72
  /**
73
73
  * Registers a listener for the successful connection events.
74
74
  * @param listener the callback
75
75
  */
76
76
  on(event: "connected", listener: () => void): this
77
-
77
+
78
78
  /**
79
79
  * Registers a listener for the disconnection events.
80
80
  * @param listener the callback
81
81
  */
82
82
  on(event: "disconnected", listener: (error: Error) => void): this
83
-
83
+
84
84
  /**
85
85
  * Registers a listener for errors.
86
86
  * @param listener the callback
87
87
  */
88
88
  on(event: "error", listener: (error: Error) => void): this
89
-
89
+
90
90
  /**
91
91
  * Registers a listener for when a user joins a channel.
92
92
  * @param listener the callback
93
93
  */
94
94
  on(event: "JOIN", listener: (member: BanchoChannelMember) => void): this
95
-
95
+
96
96
  /**
97
97
  * Registers a listener for when a user leaves a channel.
98
98
  * @param listener the callback
99
99
  */
100
100
  on(event: "PART", listener: (member: BanchoChannelMember) => void): this
101
-
101
+
102
102
  /**
103
103
  * Registers a listener for when the client fails to enter a non-existant channel.
104
104
  * @param listener the callback with the concerned BanchoChannel
105
105
  */
106
106
  on(event: "nochannel", listener: (channel: BanchoChannel) => void): this
107
-
107
+
108
108
  /**
109
109
  * Registers a listener for when the client tries to execute something for an offline user.
110
110
  * @param listener the callback with the concerned BanchoUser
111
111
  */
112
112
  on(event: "nouser", listener: (user: BanchoUser) => void): this
113
-
113
+
114
114
  /**
115
115
  * Registers a listener for private messages.
116
116
  * @param listener the callback
117
117
  */
118
118
  on(event: "PM", listener: (message: PrivateMessage) => void): this
119
-
119
+
120
120
  /**
121
121
  * Registers a listener for when Bancho sends us back a PM that couldn't be sent.
122
122
  * As far as we know, only happens when a PM is rejected because the recipient blocks messages from non-friends.
123
123
  * @param listener the callback with the concerned PrivateMessage
124
124
  */
125
125
  on(event: "rejectedMessage", listener: (message: PrivateMessage) => void): this
126
-
126
+
127
127
  /**
128
128
  * Registers a listener for client state changes.
129
129
  * @param listener the callback with a Symbol from ConnectStates and a possible error.
130
130
  */
131
131
  on(event: "state", listener: (state: Symbol, error?: Error) => void): this
132
-
132
+
133
133
  }
134
134
 
135
135
  export class BanchoUser extends EventEmitter {
@@ -138,10 +138,10 @@ declare module "bancho.js" {
138
138
  * @param banchojs Bancho.js client this user was instancied by
139
139
  */
140
140
  constructor(banchojs: BanchoClient, ircUsername: string)
141
-
141
+
142
142
  banchojs: BanchoClient
143
143
  ircUsername: string
144
-
144
+
145
145
  id: number
146
146
  username: string
147
147
  joinDate: Date
@@ -162,19 +162,19 @@ declare module "bancho.js" {
162
162
  totalSecondsPlayed: number
163
163
  ppCountryRank: number
164
164
  events: Array<nodesu.UserEvent>
165
-
165
+
166
166
  /**
167
167
  * Fetch the user from the osu! API if possible. Populates all the "optional" properties of BanchoUser.
168
- *
168
+ *
169
169
  * @throws {Error} osu! API/no API key error
170
170
  */
171
171
  fetchFromAPI(): Promise<nodesu.User>
172
-
172
+
173
173
  /**
174
174
  * Returns true if the user is the client
175
175
  */
176
176
  isClient(): boolean
177
-
177
+
178
178
  /**
179
179
  * Sends a PM to this user.
180
180
  */
@@ -261,7 +261,7 @@ declare module "bancho.js" {
261
261
  constructor(banchojs: BanchoClient, recipient: BanchoUser|BanchoChannel, message: string)
262
262
  /**
263
263
  * Sends the prepared message to the recipient
264
- *
264
+ *
265
265
  * @throws {Error} If recipient isn't a valid type
266
266
  */
267
267
  send(): Promise<null>
@@ -288,7 +288,7 @@ declare module "bancho.js" {
288
288
  channelMembers: Map<string, BanchoChannelMember>
289
289
  /**
290
290
  * Sends a message to this channel
291
- *
291
+ *
292
292
  * Elevated Bancho users are advised to heavily sanitize their inputs.
293
293
  */
294
294
  sendMessage(message: string): Promise<null>
@@ -304,13 +304,13 @@ declare module "bancho.js" {
304
304
  * @param listener the callback with the message
305
305
  */
306
306
  on(event: "message", listener: (message: BanchoMessage) => void): this
307
-
307
+
308
308
  /**
309
309
  * Registers a listener for when a user joins this channel.
310
310
  * @param listener the callback
311
311
  */
312
312
  on(event: "JOIN", listener: (member: BanchoChannelMember) => void): this
313
-
313
+
314
314
  /**
315
315
  * Registers a listener for when a user leaves this channel.
316
316
  * @param listener the callback
@@ -328,7 +328,7 @@ declare module "bancho.js" {
328
328
 
329
329
  /**
330
330
  * Represents a Bancho multiplayer lobby.
331
- *
331
+ *
332
332
  * Highly recommended to await updateSettings before manipulating (else some properties will be null).
333
333
  */
334
334
  export class BanchoLobby extends EventEmitter {
@@ -348,7 +348,7 @@ declare module "bancho.js" {
348
348
  /**
349
349
  * Array of BanchoLobbyPlayer determining each users' slot, from 0 to 15
350
350
  */
351
- slots: Array<BanchoLobbyPlayer>
351
+ slots: Array<BanchoLobbyPlayer | null>
352
352
  /**
353
353
  * Current size of the lobby
354
354
  */
@@ -356,7 +356,7 @@ declare module "bancho.js" {
356
356
  gamemode: nodesu.ModeType
357
357
  /**
358
358
  * Beatmap fetched from the API (late/not as reliable, use beatmapId when possible)
359
- *
359
+ *
360
360
  */
361
361
  beatmap: nodesu.Beatmap
362
362
  beatmapId: number
@@ -436,7 +436,7 @@ declare module "bancho.js" {
436
436
  * @param winCondition See BanchoLobbyWinConditions
437
437
  */
438
438
  setSettings(teamMode?: number, winCondition?: number, size?: number): Promise<null>
439
-
439
+
440
440
  /**
441
441
  * Moves a player from one slot to another
442
442
  * @param slot starting from 0
@@ -599,6 +599,12 @@ declare module "bancho.js" {
599
599
  on(event: "winCondition", listener: (winCondition: number) => void): this
600
600
  on(event: "userNotFound", listener: () => void): this
601
601
  on(event: "userNotFoundUsername", listener: (username: string) => void): this
602
+ on(event: "timerEnded", listener: () => void): this
603
+ on(event: "timerAborted", listener: () => void): this
604
+ on(event: "timerTick", listener: (seconds: number) => void): this
605
+ on(event: "startTimerStarted", listener: (seconds: number) => void): this
606
+ on(event: "startTimerAborted", listener: () => void): this
607
+ on(event: "startTimerTick", listener: (seconds: number) => void): this
602
608
  }
603
609
 
604
610
  export class BanchoLobbyPlayer {
@@ -644,8 +650,8 @@ declare module "bancho.js" {
644
650
  /** A Bancho channel member */
645
651
  export class BanchoChannelMember {
646
652
  /**
647
- * @param client
648
- * @param channel
653
+ * @param client
654
+ * @param channel
649
655
  * @param userString Username with optional @ or + prefix (that will automatically determine the role)
650
656
  */
651
657
  constructor(client: BanchoClient, channel: BanchoChannel, userString: string)
@@ -747,7 +753,7 @@ declare module "bancho.js" {
747
753
  /**
748
754
  * Parse mods in their bit flags form and returns them in an array of BanchoMods.
749
755
  * @param bits Mods combination in bit flags form
750
- * @param returnNone Returns [BanchoMods.None] if bits is equal to 0
756
+ * @param returnNone Returns [BanchoMods.None] if bits is equal to 0
751
757
  */
752
758
  parseBitFlags(bits: number, returnNone?: boolean): BanchoMod[]
753
759
  /**
@@ -882,23 +888,23 @@ declare module "bancho.js" {
882
888
  * Contains the different connect states: Disconnected, Connecting, Reconnecting, Connected.
883
889
  */
884
890
  type ConnectStateTypes = {
885
- /**
886
- * When we're purposely disconnected from Bancho or after an auth fail
891
+ /**
892
+ * When we're purposely disconnected from Bancho or after an auth fail
887
893
  */
888
894
  Disconnected: Symbol,
889
-
890
- /**
891
- * When we've opened the socket before any reconnection attempts and waiting for the Welcome packet
895
+
896
+ /**
897
+ * When we've opened the socket before any reconnection attempts and waiting for the Welcome packet
892
898
  */
893
899
  Connecting: Symbol,
894
-
895
- /**
896
- * When we've gotten disconnected, and are currently waiting before trying to reconnect
900
+
901
+ /**
902
+ * When we've gotten disconnected, and are currently waiting before trying to reconnect
897
903
  */
898
904
  Reconnecting: Symbol,
899
-
900
- /**
901
- * When we're online! \o/
905
+
906
+ /**
907
+ * When we're online! \o/
902
908
  */
903
909
  Connected: Symbol
904
910
  }
@@ -97,6 +97,8 @@ class BanchoChannel extends EventEmitter {
97
97
  * @returns {Promise<null>}
98
98
  */
99
99
  join() {
100
+ if (this.joined)
101
+ return Promise.resolve();
100
102
  return this._joinOrPart("JOIN", "joinCallback", "joinPromise");
101
103
  }
102
104
 
@@ -107,6 +109,8 @@ class BanchoChannel extends EventEmitter {
107
109
  * @returns {Promise<null>}
108
110
  */
109
111
  leave() {
112
+ if (!this.joined)
113
+ return Promise.resolve();
110
114
  return this._joinOrPart("PART", "partCallback", "partPromise");
111
115
  }
112
116
 
@@ -137,4 +141,4 @@ class BanchoChannel extends EventEmitter {
137
141
  }
138
142
  }
139
143
 
140
- module.exports = BanchoChannel;
144
+ module.exports = BanchoChannel;
@@ -103,7 +103,7 @@ class BanchoClient extends EventEmitter {
103
103
  this.setMaxListeners(50);
104
104
  if(!this.username || !this.password)
105
105
  throw new Error("You gotta gimme an username and a password to connect to Bancho dumbass");
106
-
106
+
107
107
  this.client = null;
108
108
  this.connectState = ConnectStates.Disconnected;
109
109
  this.reconnect = true;
@@ -115,6 +115,7 @@ class BanchoClient extends EventEmitter {
115
115
  this.usersById = new CacheMap();
116
116
  this.channels = {};
117
117
  this.messagesQueue = [];
118
+ this.lobbyCreationQueue = [];
118
119
 
119
120
  // Register our own error listener so there are no uncaught exceptions
120
121
  // for exceptions WE throw for the end-user that don't need to be caught (such as timeouts)
@@ -151,10 +152,11 @@ class BanchoClient extends EventEmitter {
151
152
  else if(message.recipient instanceof BanchoChannel)
152
153
  this.emit("CM", new ChannelMessage(this.getSelf(), content, true, message.recipient));
153
154
 
154
- this.messagesQueue.shift();
155
155
  resolve();
156
156
  } catch(e) {
157
157
  reject(e);
158
+ } finally {
159
+ this.messagesQueue.shift();
158
160
  }
159
161
  }
160
162
  }
@@ -176,7 +178,7 @@ class BanchoClient extends EventEmitter {
176
178
  this.emit("error", err);
177
179
  this.onClose(err);
178
180
  });
179
-
181
+
180
182
  this.client.on("close", () => {
181
183
  if(!this.ignoreClose)
182
184
  this.onClose(new Error("Connection closed"));
@@ -205,7 +207,7 @@ class BanchoClient extends EventEmitter {
205
207
 
206
208
  /**
207
209
  * Send raw data over IRC.
208
- * @param {string} data
210
+ * @param {string} data
209
211
  * @param {boolean} throwIfDisconnected Throws an Error if we're disconnected
210
212
  * @private
211
213
  */
@@ -218,7 +220,7 @@ class BanchoClient extends EventEmitter {
218
220
 
219
221
  /**
220
222
  * Update connection state
221
- * @param {ConnectState} newConnectState
223
+ * @param {ConnectState} newConnectState
222
224
  * @param {Error} err Error to emit with the new state
223
225
  * @fires BanchoClient#connected
224
226
  * @fires BanchoClient#disconnected
@@ -257,7 +259,7 @@ class BanchoClient extends EventEmitter {
257
259
 
258
260
  /**
259
261
  * Executed when connection is expectedly or not closed
260
- * @param {Error} err
262
+ * @param {Error} err
261
263
  * @private
262
264
  */
263
265
  onClose(err) {
@@ -268,14 +270,14 @@ class BanchoClient extends EventEmitter {
268
270
 
269
271
  if(this.connectState == ConnectStates.Disconnected)
270
272
  return;
271
-
273
+
272
274
  if(!this.reconnect)
273
275
  return this.updateState(ConnectStates.Disconnected, err);
274
-
276
+
275
277
  this.updateState(ConnectStates.Reconnecting, err);
276
278
  if(this.reconnectTimeout)
277
279
  clearTimeout(this.reconnectTimeout);
278
-
280
+
279
281
  this.reconnectTimeout = setTimeout(() => {
280
282
  if(this.reconnect) {
281
283
  const oldCallback = this.connectCallback;
@@ -288,7 +290,7 @@ class BanchoClient extends EventEmitter {
288
290
 
289
291
  /**
290
292
  * Handle incoming IRC commands from Bancho
291
- * @param {string} command
293
+ * @param {string} command
292
294
  * @private
293
295
  */
294
296
  handleIrcCommand(command) {
@@ -309,8 +311,8 @@ class BanchoClient extends EventEmitter {
309
311
 
310
312
  /**
311
313
  * Get a BanchoUser instance for the specified username
312
- *
313
- * @param {string} username
314
+ *
315
+ * @param {string} username
314
316
  * @returns {BanchoUser}
315
317
  */
316
318
  getUser(username) {
@@ -332,7 +334,7 @@ class BanchoClient extends EventEmitter {
332
334
 
333
335
  /**
334
336
  * Get a BanchoUser instance for the specified user id
335
- *
337
+ *
336
338
  * @async
337
339
  * @param {number} userid
338
340
  * @returns {Promise<BanchoUser>}
@@ -355,8 +357,8 @@ class BanchoClient extends EventEmitter {
355
357
 
356
358
  /**
357
359
  * Get a BanchoChannel instance for the specified name
358
- *
359
- * @param {string} channelName
360
+ *
361
+ * @param {string} channelName
360
362
  * @returns {BanchoChannel|BanchoMultiplayerChannel}
361
363
  */
362
364
  getChannel(channelName) {
@@ -372,7 +374,7 @@ class BanchoClient extends EventEmitter {
372
374
 
373
375
  /**
374
376
  * Creates a multiplayer lobby and return its channel.
375
- *
377
+ *
376
378
  * @async
377
379
  * @param {string} name Lobby name
378
380
  * @param {boolean} [privateLobby] Mark as private
@@ -385,33 +387,58 @@ class BanchoClient extends EventEmitter {
385
387
  if(!name || !name.trim())
386
388
  throw new Error("Empty name!");
387
389
  name = name.trim();
388
-
389
- const BanchoBot = this.getUser("BanchoBot");
390
- const command = !privateLobby ? "make" : "makeprivate";
391
- BanchoBot.sendMessage("!mp "+command+" "+name).then(() => {
392
- setTimeout(() => {
393
- reject(new Error("Multiplayer lobby creation timeout has been reached!"));
394
- }, 10000);
395
- const matchCreatedRegex = /Created the tournament match https:\/\/osu\.ppy\.sh\/mp\/(\d+) (.+)/;
396
- const listener = (msg) => {
397
- const m = matchCreatedRegex.exec(msg.message);
398
- if(!m || m[2] != name)
399
- return;
400
-
401
- BanchoBot.removeListener("message", listener);
402
- const channel = this.getChannel("#mp_"+Number(m[1]));
403
- if(!channel.lobby)
404
- return reject(new Error("Not a multiplayer channel?! This shouldn't happen..."));
405
- resolve(channel);
406
- };
407
- BanchoBot.on("message", listener);
390
+ this.lobbyCreationQueue.push({
391
+ name,
392
+ privateLobby,
393
+ resolve,
394
+ reject,
408
395
  });
396
+ if(this.lobbyCreationQueue.length === 1)
397
+ this.processLobbyCreationQueue();
409
398
  });
410
399
  }
411
400
 
401
+ async processLobbyCreationQueue() {
402
+ while(this.lobbyCreationQueue[0]) {
403
+ const { name, privateLobby, resolve, reject } = this.lobbyCreationQueue[0];
404
+
405
+ try {
406
+ if(!this.isConnected())
407
+ throw new Error("Currently disconnected!");
408
+
409
+ // eslint-disable-next-line no-async-promise-executor
410
+ resolve(await new Promise(async (resolve, reject) => {
411
+ const BanchoBot = this.getUser("BanchoBot");
412
+ const command = !privateLobby ? "make" : "makeprivate";
413
+ await BanchoBot.sendMessage("!mp "+command+" "+name);
414
+ setTimeout(() => {
415
+ reject(new Error("Multiplayer lobby creation timeout has been reached!"));
416
+ }, 10000);
417
+ const matchCreatedRegex = /Created the tournament match https:\/\/osu\.ppy\.sh\/mp\/(\d+) (.+)/;
418
+ const listener = (msg) => {
419
+ const m = matchCreatedRegex.exec(msg.message);
420
+ if(!m)
421
+ return;
422
+
423
+ BanchoBot.removeListener("message", listener);
424
+ const channel = this.getChannel("#mp_"+Number(m[1]));
425
+ if(!channel.lobby)
426
+ return reject(new Error("Not a multiplayer channel?! This shouldn't happen..."));
427
+ resolve(channel);
428
+ };
429
+ BanchoBot.on("message", listener);
430
+ }));
431
+ } catch(e) {
432
+ reject(e);
433
+ } finally {
434
+ this.lobbyCreationQueue.shift();
435
+ }
436
+ }
437
+ }
438
+
412
439
  /**
413
440
  * Connects to Bancho, rejects an Error if connection fails
414
- *
441
+ *
415
442
  * @method
416
443
  * @async
417
444
  * @return {Promise<null, Error>}
@@ -421,7 +448,7 @@ class BanchoClient extends EventEmitter {
421
448
  if(this.connectState == ConnectStates.Connected ||
422
449
  this.connectState == ConnectStates.Connecting)
423
450
  return reject(new Error("Already connected/connecting"));
424
-
451
+
425
452
  this.connectCallback = (err) => {
426
453
  if(err)
427
454
  return reject(err);
@@ -441,18 +468,18 @@ class BanchoClient extends EventEmitter {
441
468
 
442
469
  /**
443
470
  * Disconnects from Bancho
444
- *
471
+ *
445
472
  * @method
446
473
  */
447
474
  disconnect() {
448
475
  if(this.connectState == ConnectStates.Disconnected)
449
476
  return;
450
-
477
+
451
478
  if(this.isConnected())
452
479
  this.send("QUIT");
453
480
  else if(this.connectState == ConnectStates.Connecting)
454
481
  this.client.destroy();
455
-
482
+
456
483
  if(this.reconnectTimeout) {
457
484
  clearTimeout(this.reconnectTimeout);
458
485
  this.reconnectTimeout = null;
@@ -463,7 +490,7 @@ class BanchoClient extends EventEmitter {
463
490
 
464
491
  /**
465
492
  * When we've just connected, execute the connect callback
466
- *
493
+ *
467
494
  * @param {any} arg Arg to pass to the callback
468
495
  * @param {boolean} [erase=true] Erase the existing callback
469
496
  * @param {boolean} [throwIfNonexistant=false] Throw an error if the callback doesn't exist
@@ -481,16 +508,16 @@ class BanchoClient extends EventEmitter {
481
508
 
482
509
  /**
483
510
  * Returns the current connection state.
484
- *
511
+ *
485
512
  * @returns {Symbol} Current connection state. See ConnectStates
486
513
  */
487
514
  getConnectState() {
488
515
  return this.connectState;
489
516
  }
490
-
517
+
491
518
  /**
492
519
  * Returns true if the connectState is Connected, otherwise false.
493
- *
520
+ *
494
521
  * @returns {boolean}
495
522
  */
496
523
  isConnected() {
@@ -499,7 +526,7 @@ class BanchoClient extends EventEmitter {
499
526
 
500
527
  /**
501
528
  * Returns true if the connectState is Disconnected, otherwise false.
502
- *
529
+ *
503
530
  * @returns {boolean}
504
531
  */
505
532
  isDisconnected() {
@@ -507,4 +534,4 @@ class BanchoClient extends EventEmitter {
507
534
  }
508
535
  }
509
536
 
510
- module.exports = BanchoClient;
537
+ module.exports = BanchoClient;
@@ -14,26 +14,27 @@ class JoinCommand extends IrcCommand {
14
14
 
15
15
  /**
16
16
  * Emits the JOIN event
17
- *
17
+ *
18
18
  * @fires BanchoClient#JOIN
19
- * @param {BanchoClient} client
19
+ * @param {BanchoClient} client
20
20
  * @param {BanchoUser} user
21
- * @param {BanchoChannel} channel
21
+ * @param {BanchoChannel} channel
22
22
  */
23
23
  emit(client, user, channel) {
24
- /**
25
- * Fired when a user has joined a channel
26
- * @event BanchoClient#JOIN
27
- * @type {BanchoChannelMember}
28
- */
29
24
  const member = new BanchoChannelMember(client, channel, user.ircUsername);
30
25
  channel.channelMembers.set(user.ircUsername, member);
31
- client.emit("JOIN", member);
32
26
  if(user.isClient()) {
33
27
  channel.joined = true;
34
28
  if(channel.joinCallback != null)
35
29
  channel.joinCallback();
36
30
  }
31
+
32
+ /**
33
+ * Fired when a user has joined a channel
34
+ * @event BanchoClient#JOIN
35
+ * @type {BanchoChannelMember}
36
+ */
37
+ client.emit("JOIN", member);
37
38
  }
38
39
 
39
40
  handleCommand(client, command, splits) {
@@ -43,4 +44,4 @@ class JoinCommand extends IrcCommand {
43
44
  }
44
45
  }
45
46
 
46
- module.exports = JoinCommand;
47
+ module.exports = JoinCommand;
@@ -13,27 +13,31 @@ class PartCommand extends JoinCommand {
13
13
 
14
14
  /**
15
15
  * Emits the PART event
16
- *
16
+ *
17
17
  * @fires BanchoClient#PART
18
- * @param {BanchoClient} client
19
- * @param {BanchoUser} user
20
- * @param {BanchoChannel} channel
18
+ * @param {BanchoClient} client
19
+ * @param {BanchoUser} user
20
+ * @param {BanchoChannel} channel
21
21
  */
22
22
  emit(client, user, channel) {
23
- /**
24
- * Fired when a user has left a channel
25
- * @event BanchoClient#PART
26
- * @type {BanchoChannelMember}
27
- */
28
- if(channel.channelMembers.has(user.ircUsername))
29
- client.emit("PART", channel.channelMembers.get(user.ircUsername));
23
+ const member = channel.channelMembers.get(user.ircUsername);
24
+
30
25
  if(user.isClient()) {
31
26
  channel.joined = false;
32
27
  channel.channelMembers.clear();
33
28
  if(channel.partCallback != null)
34
29
  channel.partCallback();
30
+ } else {
31
+ channel.channelMembers.delete(user.ircUsername);
35
32
  }
36
- channel.channelMembers.delete(user.ircUsername);
33
+
34
+ /**
35
+ * Fired when a user has left a channel
36
+ * @event BanchoClient#PART
37
+ * @type {BanchoChannelMember}
38
+ */
39
+ if(member)
40
+ client.emit("PART", member);
37
41
  }
38
42
  }
39
43
 
@@ -413,6 +413,50 @@ class BanchoLobby extends EventEmitter {
413
413
 
414
414
  this.emit("matchSettings", ret);
415
415
  break;
416
+ case "timerEnded":
417
+ case "timerAborted":
418
+ /**
419
+ * Emitted when a timer ends
420
+ * @event BanchoLobby#timerEnded
421
+ */
422
+ /**
423
+ * Emitted when a timer is aborted
424
+ * @event BanchoLobby#timerAborted
425
+ */
426
+ this.emit(regex.name);
427
+ break;
428
+ case "timerTick":
429
+ /**
430
+ * Emitted when a timer ticks
431
+ * @event BanchoLobby#timerTick
432
+ * @type {object}
433
+ * @prop {number} seconds
434
+ */
435
+ this.emit(regex.name, ret.seconds);
436
+ break;
437
+ case "startTimerStarted":
438
+ /**
439
+ * Emitted when a start timer is started
440
+ * @event BanchoLobby#startTimerStarted
441
+ */
442
+ this.emit(regex.name, ret.seconds);
443
+ break;
444
+ case "startTimerAborted":
445
+ /**
446
+ * Emitted when a start timer is aborted
447
+ * @event BanchoLobby#startTimerAborted
448
+ */
449
+ this.emit(regex.name);
450
+ break;
451
+ case "startTimerTick":
452
+ /**
453
+ * Emitted when a start timer ticks
454
+ * @event BanchoLobby#startTimerTick
455
+ * @type {object}
456
+ * @prop {number} seconds
457
+ */
458
+ this.emit(regex.name, ret.seconds);
459
+ break;
416
460
  }
417
461
  }
418
462
  }
@@ -291,6 +291,45 @@ module.exports = {
291
291
  },
292
292
  hostCleared: function(str) {
293
293
  return str == "Cleared match host";
294
+ },
295
+ timerEnded: function(str) {
296
+ return str == "Countdown finished";
297
+ },
298
+ timerAborted: function(str) {
299
+ return str == "Countdown aborted";
300
+ },
301
+ timerTick: function(str) {
302
+ const r = /^Countdown ends in (\d+) (minute|second)s?(?: and (\d+) seconds?)?$/;
303
+ if(r.test(str)) {
304
+ const m = r.exec(str);
305
+
306
+ return {
307
+ seconds: Number(m[1]) * (m[2] === "minute" ? 60 : 1) + (m[3] ? Number(m[3]) : 0)
308
+ };
309
+ }
310
+ },
311
+ startTimerStarted: function(str) {
312
+ const r = /^Queued the match to start in (\d+) (minute|second)s?(?: and (\d+) seconds?)?$/;
313
+ if(r.test(str)) {
314
+ const m = r.exec(str);
315
+
316
+ return {
317
+ seconds: Number(m[1]) * (m[2] === "minute" ? 60 : 1) + (m[3] ? Number(m[3]) : 0)
318
+ };
319
+ }
320
+ },
321
+ startTimerAborted: function(str) {
322
+ return str == "Aborted the match start timer";
323
+ },
324
+ startTimerTick: function(str) {
325
+ const r = /^Match starts in (\d+) (minute|second)s?(?: and (\d+) seconds?)?$/;
326
+ if(r.test(str)) {
327
+ const m = r.exec(str);
328
+
329
+ return {
330
+ seconds: Number(m[1]) * (m[2] === "minute" ? 60 : 1) + (m[3] ? Number(m[3]) : 0)
331
+ };
332
+ }
294
333
  }
295
334
  }
296
335
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bancho.js",
3
- "version": "0.11.5",
3
+ "version": "0.12.0",
4
4
  "description": "Interface with Bancho over IRC, made easy and reliable",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -19,6 +19,8 @@ const TestGoals = {
19
19
  MultiplayerScoresSorting: Symbol("MultiplayerScoresSorting"),
20
20
 
21
21
  MultiplayerLobbyExists: Symbol("MultiplayerLobbyExists"),
22
+ MultiplayerLobbyUpdateSettings: Symbol("MultiplayerLobbyUpdateSettings"),
23
+ MultiplayerLobbyNamed: Symbol("MultiplayerLobbyNamed"),
22
24
  MultiplayerLobbySetMap: Symbol("MultiplayerLobbySetMap"),
23
25
  MultiplayerLobbySetModsDTFree: Symbol("MultiplayerLobbySetModsDTFree"),
24
26
  MultiplayerLobbySetModsHDHR: Symbol("MultiplayerLobbySetModsHDHR"),
@@ -35,4 +37,4 @@ const TestGoals = {
35
37
  MultiplayerLobbyCloseLobby: Symbol("MultiplayerLobbyCloseLobby")
36
38
  };
37
39
 
38
- module.exports = Object.freeze(TestGoals);
40
+ module.exports = Object.freeze(TestGoals);
@@ -10,38 +10,34 @@ class JoinEventChannelUnit extends TestUnit {
10
10
  run() {
11
11
  return new Promise((resolve, reject) => {
12
12
  const channel = this.client.getChannel("#french");
13
- let returned = false;
14
13
  this.client.on("JOIN", (obj) => {
15
14
  if(obj.user.isClient() && obj.channel == channel) {
16
15
  this.fulFillGoal(TestGoals.JoinEvent);
17
- channel.leave(channel);
18
16
 
19
17
  this.client.on("PART", (obj) => {
20
18
  if(obj.user.isClient() && obj.channel == channel) {
21
19
  this.client.removeAllListeners("JOIN");
22
20
  this.client.removeAllListeners("nochannel");
21
+ this.client.removeAllListeners("PART");
23
22
  this.fulFillGoal(TestGoals.PartEvent);
24
- returned = true;
25
23
  resolve();
26
24
  }
27
25
  });
26
+ channel.leave(channel);
28
27
  }
29
28
  });
30
29
  this.client.on("nochannel", (errorChannel) => {
31
30
  if(errorChannel == channel) {
32
31
  this.client.removeAllListeners("JOIN");
33
32
  this.client.removeAllListeners("nochannel");
34
- returned = true;
33
+ this.client.removeAllListeners("PART");
35
34
  reject(new Error("No such channel: "+errorChannel.name));
36
35
  }
37
36
  });
38
37
  channel.join();
39
- setTimeout(() => {
40
- if(!returned)
41
- reject(new Error("Didn't join after timeout!"));
42
- }, 10000);
38
+ setTimeout(() => reject(new Error("Didn't join and leave after timeout!")), 10000);
43
39
  });
44
40
  }
45
41
  }
46
42
 
47
- module.exports = new JoinEventChannelUnit();
43
+ module.exports = new JoinEventChannelUnit();
@@ -20,6 +20,11 @@ class MultiplayerLobbiesUnit extends TestUnit {
20
20
  if(!channel.lobby)
21
21
  throw new Error("lobby doesn't exist! this is a regular channel, not a multiplayer one!");
22
22
  this.fulFillGoal(TestGoals.MultiplayerLobbyExists);
23
+ await channel.lobby.updateSettings();
24
+ this.fulFillGoal(TestGoals.MultiplayerLobbyUpdateSettings);
25
+ if(channel.lobby.name !== roomName)
26
+ throw new Error("lobby name doesn't match!");
27
+ this.fulFillGoal(TestGoals.MultiplayerLobbyNamed);
23
28
  await channel.lobby.setMap(75);
24
29
  this.fulFillGoal(TestGoals.MultiplayerLobbySetMap);
25
30
  await channel.lobby.setMods([BanchoMods.enum.DoubleTime], true);
@@ -54,4 +59,4 @@ class MultiplayerLobbiesUnit extends TestUnit {
54
59
  }
55
60
  }
56
61
 
57
- module.exports = new MultiplayerLobbiesUnit();
62
+ module.exports = new MultiplayerLobbiesUnit();
Binary file