message: Also handle the case where a MatrixUser may part via other means
diff: @@ -723,14 +723,14 @@ IrcHandler.prototype.onKick = Promise.coroutine(function*(req, server,
  if (!bridgedIrcClient || bridgedIrcClient.isBot) {
  return; // unexpected given isVirtual == true, but meh, bail.
  }
  - let promises = matrixRooms.map((room) => {
  - req.log.info("Kicking %s from room %s", bridgedIrcClient.userId, room.getId());
  - return this.ircBridge.getAppServiceBridge().getIntent().kick(
  - room.getId(), bridgedIrcClient.userId,
  - `${kicker.nick} has kicked ${bridgedIrcClient.userId} from ${chan} (${reason})`
  - );
  + yield this._leaveQueue.enqueue(chan + bridgedIrcClient.userId, {
  + rooms: matrixRooms,
  + userId: bridgedIrcClient.userId,
  + shouldKick: true,
  + kickReason: `${kicker.nick} has kicked this user from ${chan} (${reason})`,
  + retry: true, // We must retry a kick to avoid leaking history
  + req,
  });
  - yield Promise.all(promises);
  }
  else {
  // the kickee is just some random IRC user, but we still need to bridge this as IRC
  @@ -744,13 +744,13 @@ IrcHandler.prototype.onKick = Promise.coroutine(function*(req, server,
  req.log.info("No mapped matrix rooms for IRC channel %s", chan);
  return;
  }
  - let promises = matrixRooms.map((room) => {
  - req.log.info("Leaving (due to kick) room %s", room.getId());
  - return this.ircBridge.getAppServiceBridge().getIntent(
  - matrixUser.getId()
  - ).leave(room.getId());
  + yield this._leaveQueue.enqueue(chan + bridgedIrcClient.userId, {
  + rooms: matrixRooms,
  + userId: matrixUser.getId(),
  + shouldKick: false,
  + retry: false, // We can forgo this leave if we must, this is just for membership.
  + req,
  });
  - yield Promise.all(promises);
  }
  });
  @@ -791,6 +791,10 @@ IrcHandler.prototype.onPart = Promise.coroutine(function*(req, server, leavingUs
  const bridgedClient = this.ircBridge.getClientPool().getBridgedClientByNick(
  server, nick
  );
  + if (!bridgedClient.inChannel(chan)) {
  + // We don't need to send a leave to a channel we were never in.
  + return BridgeRequest.ERR_DROPPED;
  + }
  matrixUser = bridgedClient.userId;
  }
  else {
  @@ -820,7 +824,8 @@ IrcHandler.prototype.onPart = Promise.coroutine(function*(req, server, leavingUs
  userId: typeof(matrixUser) === "string" ? matrixUser : matrixUser.getId(),
  shouldKick: leavingUser.isVirtual, // kick if they are not ours
  req,
  - kickReason: "Client PARTed from channel" // this will only be used if shouldKick is true
  + kickReason: "Client PARTed from channel", // this will only be used if shouldKick is true
  + retry: true, // We must retry these so that membership isn't leaked.
  });
  stats.membership(true, "part");
  yield promise;
  @@ -1137,9 +1142,9 @@ IrcHandler.prototype._handleLeaveQueue = async function(item) {
  );
  try {
  if (item.shouldKick) {
  - await bridge.getIntent(item.userId).kick(
  + await bridge.getIntent().kick(
  roomId,
  - "@foobar:localhost",
  + item.userId,
  item.kickReason,
  );
  }
  