def lineReceived(self, line): if not line: return if self.showirc: LOG.log(5, ">: %s" % line) if line[0] == ':': try: prefix, line = line[1:].split(' ', 1) except ValueError: return else: prefix = '' try: line, trailing = line.split(' :', 1) except ValueError: args = line.split() else: args = line.split() args.append(trailing) try: f = getattr(self, 'handleCmd_%s' % args[0].upper()) except AttributeError: pass else: f(prefix, args[1:])
def connectionLost(self, result): LOG.info("Lost IRC connection.") if self.ism.syncd: self.ism.removeMeFromMain() if self.shutdown_deferred: self.shutdown_deferred.callback("Bye!")
def handleCmd_PING(self, prefix, args): LOG.info("PING? PONG!") scfg = getServiceConfig() if len(args) == 1: self.sendLine("PONG %s :%s" % (scfg.my_host, args[0])) elif len(args) == 2: self.sendLine("PONG %s :%s" % (args[1], args[0]))
def checkIncomingNick(self, n): # Validate new nicks as they join. # If successful, return inick, else raise NickError. try: inick = dc_to_irc(n.nick) if inick.lower() == self.bot_user.inick.lower(): raise NickError("Nick '%s' conflicts with IRC bot." % inick) for q, reason in self.qlines.itervalues(): if q.match(inick): raise NickError("Nick '%s' is Q-lined: %s" % (inick, reason)) except NickError, e: LOG.debug("Bad nick: %s %s" % (n.nick, str(e))) # Bad nick. KICK! osm = self.main.osm chunks = [] osm.bsm.addKickChunk(chunks, n, cfg.irc_to_dc_bot, str(e), rejoin=False, silent=True) osm.bsm.sendBridgeChange(chunks) raise
def handleCmd_TKL(self, prefix, args): addrem = args[0] kind = args[1] if addrem == '+': on_off = True elif addrem == '-': on_off = False else: LOG.error("TKL: invalid modifier: '%s'" % addrem) return # :irc1.dhirc.com TKL + Z * 128.10.12.0/24 [email protected] 0 1171427130 :no reason if kind == 'Z' and args[2] == '*': cidr = args[3] self.ism.setNetworkBan(cidr, on_off) # :%s TKL + Q * %s* %s 0 %d :Reserved for Dtella elif kind == 'Q': nickmask = args[3] if on_off: reason = args[-1] self.ism.addQLine(nickmask, reason) else: self.ism.removeQLine(nickmask)
def handleCmd_SERVER(self, prefix, args): if prefix: # Not from our connected server return if self.server_name: # Could be a dupe? Ignore it. return # We got a reply from the our connected IRC server, so our password # was just accepted. Send the Dtella state information into IRC. # Save server name CHECK(args[0]) self.server_name = args[0] LOG.info("IRC Server Name: %s" % self.server_name) # Tell the ReconnectingClientFactory that we're cool self.factory.resetDelay() # This isn't very correct, because the Dtella nicks # haven't been sent yet, but it's the best we can practically do. scfg = getServiceConfig() cloak_checksum = scfg.hostmasker.getChecksum() self.sendLine("NETINFO 0 %d 0 %s 0 0 0 :%s" % (time.time(), cloak_checksum, scfg.network_name)) self.sendLine(":%s EOS" % scfg.my_host)
def handleCmd_EOS(self, prefix, args): if prefix != self.server_name: return if self.ism.syncd: return LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.sendLine( "TKL + Q * %s* %s 0 %d :Reserved for Dtella" % (cfg.dc_to_irc_prefix, scfg.my_host, time.time())) self.ism.killConflictingUsers() # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def handleCmd_KILL(self, prefix, args): l33t = prefix n00b = args[0] reason = irc_strip(args[1]) # In most cases, n00b is a UUID, but Anope seems to still use a nick. # Thus, we have to try both everywhere :-/ # Reconnect the bot if it's killed. if self.ism.isMyBot(n00b): if self.ism.syncd: self.pushBotJoin(do_nick=True) return l33t = self.ism.findUser(l33t).inick # If n00b is a Dtella node, kick 'em. n = self.ism.findDtellaNode(inick=n00b) if n: self.ism.kickDtellaNode(n, l33t, "KILL: " + reason, send_quit=False) return # If n00b is an IRC user, treat it like a QUIT. try: n00b_u = self.ism.findUser(n00b) except KeyError: LOG.warning("Tried to KILL unknown user: %s" % n00b) return message = ("%s has KILL'd %s: %s" % (irc_to_dc(l33t), irc_to_dc(n00b_u.inick), reason)) self.ism.removeUser(n00b_u, message)
def handleCmd_KILL(self, prefix, args): l33t = prefix n00b = args[0] reason = irc_strip(args[1]) # In most cases, n00b is a UUID, but Anope seems to still use a nick. # Thus, we have to try both everywhere :-/ # Reconnect the bot if it's killed. if self.ism.isMyBot(n00b): if self.ism.syncd: self.pushBotJoin(do_nick=True) return l33t = self.ism.findUser(l33t).inick # If n00b is a Dtella node, kick 'em. n = self.ism.findDtellaNode(inick=n00b) if n: self.ism.kickDtellaNode( n, l33t, "KILL: " + reason, send_quit=False) return # If n00b is an IRC user, treat it like a QUIT. try: n00b_u = self.ism.findUser(n00b) except KeyError: LOG.warning("Tried to KILL unknown user: %s" % n00b) return message = ( "%s has KILL'd %s: %s" % (irc_to_dc(l33t), irc_to_dc(n00b_u.inick), reason)) self.ism.removeUser(n00b_u, message)
def setNetworkBan(self, cidr, on_off): # See if this is a valid 1.2.3.4/5 CIDR string. try: ipmask = ipv4.CidrStringToIPMask(cidr) except ValueError, e: LOG.error("Bad CIDR string: %s") return
def handleCmd_EOS(self, prefix, args): if prefix != self.server_name: return if self.ism.syncd: return LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.sendLine("TKL + Q * %s* %s 0 %d :Reserved for Dtella" % (cfg.dc_to_irc_prefix, scfg.my_host, time.time())) self.ism.killConflictingUsers() # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def connectionMade(self): scfg = getServiceConfig() LOG.info("Connected to IRC server.") self.challenge = make_challenge() my_capabs = [ ("NICKMAX", 32), ("HALFOP", 1), ("CHANMAX", 65), ("MAXMODES", 20), ("IDENTMAX", 12), ("MAXQUIT", 255), ("MAXTOPIC", 307), ("MAXKICK", 255), ("MAXGECOS", 128), ("MAXAWAY", 200), ("IP6SUPPORT", 1), ("PROTOCOL", 1201), ("CHALLENGE", self.challenge), ] my_capabs_str = ' '.join("%s=%s" % x for x in my_capabs) self.sendLine("CAPAB START") self.sendLine("CAPAB CAPABILITIES :%s" % my_capabs_str) self.sendLine("CAPAB END")
def checkIncomingNick(self, n): # Validate new nicks as they join. # If successful, return inick, else raise NickError. try: inick = dc_to_irc(n.nick) if inick.lower() == self.bot_user.inick.lower(): raise NickError("Nick '%s' conflicts with IRC bot." % inick) for q, reason in self.qlines.itervalues(): if q.match(inick): raise NickError( "Nick '%s' is Q-lined: %s" % (inick, reason)) except NickError, e: LOG.debug("Bad nick: %s %s" % (n.nick, str(e))) # Bad nick. KICK! osm = self.main.osm chunks = [] osm.bsm.addKickChunk( chunks, n, cfg.irc_to_dc_bot, str(e), rejoin=False, silent=True) osm.bsm.sendBridgeChange(chunks) raise
def handleCmd_QUIT(self, prefix, args): uuid = prefix try: u = self.ism.findUser(uuid) except KeyError: LOG.warning("Can't quit user: %s" % uuid) else: self.ism.removeUser(u)
def cleanupOnExit(self): LOG.info("Reactor is shutting down. Doing cleanup.") self.shutdown(reconnect='no') self.state.saveState() # Cleanly close the IRC connection before terminating if self.ism: return self.ism.shutdown()
def killConflictingUsers(self): # Find any reserved nicks, and KILL them. CHECK(self.ircs and not self.syncd) bad_users = [u for u in set(self.users.itervalues()) if matches_dc_to_irc_prefix(u.inick)] LOG.info("Conflicting users: %r" % bad_users) for u in bad_users: self.ircs.event_KillUser(u) self.removeUser(u)
def removeQLine(self, nickmask): self.qlines.pop(nickmask, None) LOG.info("Removed Q-line: " + nickmask) # If some other bridge removes our reservation, abort. if self.syncd and (nickmask == cfg.dc_to_irc_prefix + "*"): LOG.error("My own Q-line was removed! Terminating.") if self.ircs: self.ircs.transport.loseConnection() reactor.stop()
def runDconfigPusher(): import dtella.bridge_config as cfg setLogFile(cfg.file_base + ".log", 4<<20, 4) LOG.debug("Dconfig Pusher Logging Manager Initialized") addTwistedErrorCatcher(LOG.critical) from dtella.bridge.push_dconfig_main import DtellaMain_DconfigPusher dtMain = DtellaMain_DconfigPusher() reactor.run()
def runDconfigPusher(): import dtella.bridge_config as cfg setLogFile(cfg.file_base + ".log", 4 << 20, 4) LOG.debug("Dconfig Pusher Logging Manager Initialized") addTwistedErrorCatcher(LOG.critical) from dtella.bridge.push_dconfig_main import DtellaMain_DconfigPusher dtMain = DtellaMain_DconfigPusher() reactor.run()
def killConflictingUsers(self): # Find any reserved nicks, and KILL them. CHECK(self.ircs and not self.syncd) bad_users = [ u for u in set(self.users.itervalues()) if matches_dc_to_irc_prefix(u.inick) ] LOG.info("Conflicting users: %r" % bad_users) for u in bad_users: self.ircs.event_KillUser(u) self.removeUser(u)
def handleCmd_NICK(self, prefix, args): # :268AAAAAF NICK Paul 1238303566 old_uuid = prefix new_nick = args[0] try: u = self.ism.findUser(old_uuid) except KeyError: # This might be an echo from our KICK. LOG.warning("NICK: can't find source: %s" % old_uuid) return self.ism.changeNick(u, new_nick)
def cb(): self.ping_dcall = None if self.ping_waiting: LOG.error("Ping timeout!") self.transport.loseConnection() else: scfg = getServiceConfig() self.sendLine("PING :%s" % scfg.my_host) self.ping_waiting = True self.ping_dcall = reactor.callLater(60.0, cb)
def startConnecting(self): udp_state = self.ph.getSocketState() if udp_state == 'dead': try: reactor.listenUDP(cfg.udp_port, self.ph) except twisted.internet.error.BindError: LOG.error("Failed to bind UDP port!") raise SystemExit elif udp_state == 'dying': return CHECK(self.ph.getSocketState() == 'alive') self.startInitialContact()
def addQLine(self, nickmask, reason): nick_re = wild_to_regex(nickmask) # After EOS, auto-remove any Q-lines which conflict with mine. # This may cause a conflicting bridge to abort. if self.syncd and nick_re.match(cfg.dc_to_irc_prefix): if self.ircs: self.ircs.pushRemoveQLine(nickmask) LOG.info("Conflicted Q-line: " + nickmask) return self.qlines[nickmask] = (nick_re, reason) LOG.info("Added Q-line: " + nickmask)
def runClient(dc_port): #Logging for Dtella Client setLogFile("dtella.log", 1<<20, 1) LOG.debug("Client Logging Manager Initialized") from dtella.client.main import DtellaMain_Client dtMain = DtellaMain_Client() import dtella.local_config as local from dtella.common.util import get_version_string def botErrorReporter(text): dch = dtMain.dch if dch: dch.bot.say( "Something bad happened. You might want to email this to " "[email protected] so we'll know about it:\n" "Version: %s %s\n%s" % (local.hub_name, get_version_string()[3:], text)) addTwistedErrorCatcher(botErrorReporter) addTwistedErrorCatcher(LOG.critical) from dtella.client.dc import DCFactory dfactory = DCFactory(dtMain, dc_port) LOG.info("%s %s" % (local.hub_name, local.version)) def cb(first): try: reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1') except twisted.internet.error.CannotListenError: if first: LOG.warning("TCP bind failed. Killing old process...") if terminate(dc_port): LOG.info("Ok. Sleeping...") reactor.callLater(2.0, cb, False) else: LOG.error("Kill failed. Giving up.") reactor.stop() else: LOG.error("Bind failed again. Giving up.") reactor.stop() else: LOG.info("Listening on 127.0.0.1:%d" % dc_port) dtMain.startConnecting() reactor.callWhenRunning(cb, True) reactor.run()
def runClient(dc_port): #Logging for Dtella Client setLogFile("dtella.log", 1 << 20, 1) LOG.debug("Client Logging Manager Initialized") from dtella.client.main import DtellaMain_Client dtMain = DtellaMain_Client() import dtella.local_config as local from dtella.common.util import get_version_string def botErrorReporter(text): dch = dtMain.dch if dch: dch.bot.say( "Something bad happened. You might want to email this to " "[email protected] so we'll know about it:\n" "Version: %s %s\n%s" % (local.hub_name, get_version_string()[3:], text)) addTwistedErrorCatcher(botErrorReporter) addTwistedErrorCatcher(LOG.critical) from dtella.client.dc import DCFactory dfactory = DCFactory(dtMain, dc_port) LOG.info("%s %s" % (local.hub_name, local.version)) def cb(first): try: reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1') except twisted.internet.error.CannotListenError: if first: LOG.warning("TCP bind failed. Killing old process...") if terminate(dc_port): LOG.info("Ok. Sleeping...") reactor.callLater(2.0, cb, False) else: LOG.error("Kill failed. Giving up.") reactor.stop() else: LOG.error("Bind failed again. Giving up.") reactor.stop() else: LOG.info("Listening on 127.0.0.1:%d" % dc_port) dtMain.startConnecting() reactor.callWhenRunning(cb, True) reactor.run()
def event_DtellaUp(self): osm = self.getOnlineStateManager() LOG.info("Sending Dtella state to IRC...") nicks = osm.nkm.nickmap.values() nicks.sort(key=lambda n: n.nick) for n in nicks: try: # This event does everything we need. self.event_AddNick(n) except NickError: osm.nkm.removeNode(n, "Bad Nick") n.setNoUser()
def runBridge(): import dtella.bridge_config as cfg setLogFile(cfg.file_base + ".log", 4<<20, 4) LOG.debug("Bridge Logging Manager Initialized") addTwistedErrorCatcher(LOG.critical) from dtella.bridge.main import DtellaMain_Bridge dtMain = DtellaMain_Bridge() from dtella.bridge.bridge_server import getServiceConfig scfg = getServiceConfig() scfg.startService(dtMain) reactor.run()
def signPacket(self, packet, broadcast): data = ''.join(packet) if broadcast: body = data[0:2] + data[10:] else: body = data data_hash = md5(body).digest() t = time.time() sig, = self.rsa_obj.sign(data_hash, None) LOG.debug("Sign Time = %f sec" % (time.time() - t)) packet.append(long_to_bytes(sig))
def runBridge(): import dtella.bridge_config as cfg setLogFile(cfg.file_base + ".log", 4 << 20, 4) LOG.debug("Bridge Logging Manager Initialized") addTwistedErrorCatcher(LOG.critical) from dtella.bridge.main import DtellaMain_Bridge dtMain = DtellaMain_Bridge() from dtella.bridge.bridge_server import getServiceConfig scfg = getServiceConfig() scfg.startService(dtMain) reactor.run()
def receivedBlockRequest(self, src_ipp, bhash): try: b = self.cached_blocks[bhash] except KeyError: LOG.warning("Requested block not found") return b.scheduleExpire(self.cached_blocks, bhash) packet = ['bB'] packet.append(self.main.osm.me.ipp) packet.append(struct.pack('!H', len(b.data))) packet.append(b.data) ad = Ad().setRawIPPort(src_ipp) self.main.ph.sendPacket(''.join(packet), ad.getAddrTuple())
def cb(first): try: reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1') except twisted.internet.error.CannotListenError: if first: LOG.warning("TCP bind failed. Killing old process...") if terminate(dc_port): LOG.info("Ok. Sleeping...") reactor.callLater(2.0, cb, False) else: LOG.error("Kill failed. Giving up.") reactor.stop() else: LOG.error("Bind failed again. Giving up.") reactor.stop() else: LOG.info("Listening on 127.0.0.1:%d" % dc_port) dtMain.startConnecting()
def joinChannel(self, u): if u in self.chanusers: LOG.error("joinChannel: %r already in channel." % u) return self.chanusers.add(u) u.chanmodes.clear() try: osm = self.getOnlineStateManager() except NotOnline: return scfg = getServiceConfig() infoindex = scfg.chan_umodes.getUserInfoIndex(u) chunks = [] osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), infoindex) osm.bsm.sendBridgeChange(chunks)
def joinChannel(self, u): if u in self.chanusers: LOG.error("joinChannel: %r already in channel." % u) return self.chanusers.add(u) u.chanmodes.clear() try: osm = self.getOnlineStateManager() except NotOnline: return scfg = getServiceConfig() infoindex = scfg.chan_umodes.getUserInfoIndex(u) chunks = [] osm.bsm.addNickChunk( chunks, irc_to_dc(u.inick), infoindex) osm.bsm.sendBridgeChange(chunks)
def setChannelBan(self, whoset, on_off, banmask): if on_off: self.chanbans[banmask] = wild_to_regex(banmask) action = "added" else: self.chanbans.pop(banmask, None) action = "removed" LOG.debug( "bans= %s" % self.chanbans.keys() ) try: osm = self.getOnlineStateManager() except NotOnline: return chunks = [] osm.bsm.addChatChunk( chunks, cfg.irc_to_dc_bot, "%s %s ban: %s" % (irc_to_dc(whoset), action, banmask)) osm.bsm.sendBridgeChange(chunks)
def setChannelBan(self, whoset, on_off, banmask): if on_off: self.chanbans[banmask] = wild_to_regex(banmask) action = "added" else: self.chanbans.pop(banmask, None) action = "removed" LOG.debug("bans= %s" % self.chanbans.keys()) try: osm = self.getOnlineStateManager() except NotOnline: return chunks = [] osm.bsm.addChatChunk( chunks, cfg.irc_to_dc_bot, "%s %s ban: %s" % (irc_to_dc(whoset), action, banmask)) osm.bsm.sendBridgeChange(chunks)
def setChannelUserModes(self, whoset, u, changes): # changes: dict of {mode -> on_off} if u not in self.chanusers: LOG.error("setChannelUserModes: %r not in channel." % u) return scfg = getServiceConfig() # Save old index, apply changes, and get new index. old_infoindex = scfg.chan_umodes.getUserInfoIndex(u) for mode, on_off in changes.iteritems(): if on_off: u.chanmodes.add(mode) else: u.chanmodes.discard(mode) new_infoindex = scfg.chan_umodes.getUserInfoIndex(u) try: osm = self.getOnlineStateManager() except NotOnline: return chunks = [] if new_infoindex == old_infoindex: friendly_change = "well that was pointless" else: friendly_change = "%s -> %s" % ( scfg.chan_umodes.friendly[old_infoindex], scfg.chan_umodes.friendly[new_infoindex]) osm.bsm.addNickChunk( chunks, irc_to_dc(u.inick), new_infoindex) osm.bsm.addChatChunk( chunks, cfg.irc_to_dc_bot, "%s set mode %s for %s: %s" % ( irc_to_dc(whoset), self.formatChannelUserModes(changes), irc_to_dc(u.inick), friendly_change)) osm.bsm.sendBridgeChange(chunks)
def handleCmd_SQUIT(self, prefix, args): # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed hostname = args[0] try: # All servers have been registered as users. sid = self.ism.findUser(hostname).uuid.lower() if len(sid) != 3: raise KeyError except KeyError: LOG.error("SQUIT: unknown server: %s" % hostname) return # Find all the users belonging to this server. This should # never match nicks, because only UUIDs start with a number. remove_uuids = [uuid for uuid in self.ism.users if uuid.startswith(sid)] # Drop them off the network. for uuid in remove_uuids: LOG.info("SQUIT: removing user: %s" % uuid) self.ism.removeUser(self.ism.findUser(uuid))
def handleCmd_ENDBURST(self, prefix, args): if self.ism.syncd: # FIXME LOG.warning("Ignoring ENDBURST") return CHECK(self.server_name) LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.ism.killConflictingUsers() self.sendLine(":%s ADDLINE Q %s* %s %d 0 :%s" % (self.sid, cfg.dc_to_irc_prefix, scfg.my_host, time.time(), self.qline_reason)) # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def handleCmd_ENDBURST(self, prefix, args): if self.ism.syncd: # FIXME LOG.warning("Ignoring ENDBURST") return CHECK(self.server_name) LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.ism.killConflictingUsers() self.sendLine( ":%s ADDLINE Q %s* %s %d 0 :%s" % (self.sid, cfg.dc_to_irc_prefix, scfg.my_host, time.time(), self.qline_reason)) # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def findConflictingBridge(self): # Determine if another bridge conflicts with me. # Return True if we need to abort. CHECK(self.ircs and not self.syncd) stale_qlines = [] for nickmask, (q, reason) in self.qlines.iteritems(): # Look for Q-lines which conflict with my prefix. if not q.match(cfg.dc_to_irc_prefix): continue LOG.info("Found a conflicting Q-line: %s" % nickmask) # If any nicks exist under that Q-line, we'll need to abort. for u in set(self.users.itervalues()): if q.match(u.inick): LOG.info("... and a nick to go with it: %s" % u.inick) return True stale_qlines.append(nickmask) # Remove all stale Q-lines from the network. LOG.info("Stale qlines: %r" % stale_qlines) for nickmask in stale_qlines: del self.qlines[nickmask] self.ircs.pushRemoveQLine(nickmask) # Conflict has been neutralized. return False
def handleCmd_SQUIT(self, prefix, args): # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed hostname = args[0] try: # All servers have been registered as users. sid = self.ism.findUser(hostname).uuid.lower() if len(sid) != 3: raise KeyError except KeyError: LOG.error("SQUIT: unknown server: %s" % hostname) return # Find all the users belonging to this server. This should # never match nicks, because only UUIDs start with a number. remove_uuids = [ uuid for uuid in self.ism.users if uuid.startswith(sid) ] # Drop them off the network. for uuid in remove_uuids: LOG.info("SQUIT: removing user: %s" % uuid) self.ism.removeUser(self.ism.findUser(uuid))