def connect(self, host, port=15000): self.sock.connect((host, port)) # and ensure correct port self._handshake() if "[version]" not in self.receive_string(): raise WesException("Server did not ask for version").quit() self.send_wml_string('[version]\nversion="' + self.wesnothVersion + '"\n[/version]\n') response = self.receive_string() parser = wmlparser.Parser(None) wml = parser.parse_text(response) result = {} if type(wml) is wmlparser.RootNode: wml = wml.get_all() for i in wml: for j in i.get_all(): result[j.get_name()] = j.get_text() if "port" in result: self.main.log.debug("need to use different port %s", result) self.shutdown() self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(WesSock.socketTimeout) self.connect(result["host"], int(result["port"]))
def receive(self): try: response: str = self.sock.recv(4096).decode("utf8") response = self.responseBuffer + response # if len(response) == 0: # raise WesException("IRC recv returned len 0").addAction(WesException.QUIT_IRC) self.responseBuffer = "" splitlines = response.splitlines(True) unhandledResponse = "" for i in range(len(splitlines)): line = splitlines[i] if len(line) == len(line.rstrip()): if i == len(splitlines) - 1: # Save it to buffer self.responseBuffer = line break else: # Line does not end with newline symbol, but there is another line somehow, should never happen raise WesException( "line without newline but is not last").addAction( WesException.QUIT_IRC) if not self._actOnLine(line): unhandledResponse += line return unhandledResponse except socket.timeout: self.ensure_connected() return ""
def onServerMessage(self, message: str, private): self.log.info("Got server message: " + message) if "The server has been restarted" in message and not private: raise WesException().reconnectWes() if "Can't find '" in message: name = message.split("'")[2] self.log.info("User " + name + "is offline") del self.pingUsers[name]
def _send_bytes(self, msg: bytes): totalsent = 0 MSGLEN = len(msg) while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise WesException("socket connection broken").addAction( WesException.QUIT_WES) totalsent = totalsent + sent
def _receive_byte_string(self) -> bytes: try: chunks = [] bytes_recd = 0 chunk = self.sock.recv(4) if len(chunk) == 0: raise WesException("wes recv len 0").addAction( WesException.QUIT_WES) bytes_wanted = int.from_bytes(chunk, byteorder="big") while bytes_recd != bytes_wanted: chunk = self.sock.recv(min(4096, bytes_wanted - bytes_recd)) if not chunk: self.main.log.warn("chunk not") break if chunk == b'': raise WesException("socket connection broken").addAction( WesException.QUIT_WES) chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) try: result: bytes = gzip.decompress(b''.join(chunks)) except OSError: self.main.log.error( "Problem when decompressing received chunks") self.main.log.error("Bytes wanted {}".format(bytes_wanted)) self.main.log.error("Bytes read {}".format(bytes_recd)) self.main.log.error(b''.join(chunks)) raise if len(result) == 0: raise WesException("Received empty, so quitting").addAction( WesException.QUIT_WES) if self.LOG_RECEIVED: self.log_rec.debug(result) return result except socket.timeout: return b''
def actOnGamelistDiff(self, node: wmlparser.TagNode): self.main.log.log(2, "in actOnGamelistDiff with %s", node.get_name()) usersToRemove = set() usersToAdd = set() for child in node.get_all(tag="delete_child"): index = int(child.get_text_val("index")) if len(child.get_all(tag="user")) == 1: self.main.log.log(5, "user %s should be removed" % index) usersToRemove.add(self.main.lobby.users.getI(index).name) for child in node.get_all(tag="insert_child"): index = int(child.get_text_val("index")) self.main.log.log(3, "%s %s", child.get_name(), index) for child in child.get_all(tag="user"): u = User(child) self.main.log.log(5, "user %s should be inserted to %s", u.name, index) usersToAdd.add(u.name) if u.name in usersToRemove: self.main.lobby.users.insertUser(u, index, None) else: self.main.lobby.users.insertUser(u, index) for child in child.get_all(tag="game"): g = Game(child) self.main.log.log(5, "game %s(%s) should be inserted to %s", g.name, g.id, index) self.main.lobby.games.insertGame(g, index) for child in node.get_all(tag="delete_child"): index = int(child.get_text_val("index")) self.main.log.log(3, "%s %s", child.get_name(), index) if len(child.get_all(tag="user")) == 1: self.main.log.log(5, "user %s should be removed" % index) if self.main.lobby.users.getI(index).name in usersToAdd: self.main.lobby.users.deleteI(index, None) else: self.main.lobby.users.deleteI(index) elif len(child.get_all(tag="game")) == 1: self.main.log.log(5, "game %s should be removed" % index) self.main.lobby.games.removeGame(index) else: self.main.log.error("actOnGamelistDiff with %s users and %s games", len(child.get_all(tag="user")), len(child.get_all(tag="game"))) for child in node.get_all(tag="change_child"): if int(child.get_text_val("index")) != 0: raise WesException("[gamelist_diff][change_child]index={}, 0 expected" .format(node.get_text_val("index"))) # This has subtags like delete_child, instead of initial uncondition addition gamelists = child.get_all(tag="gamelist") assert len(gamelists) == 1 self.actOnGamelistDiff(gamelists[0])
def actOnWML(self, wml, path=None): if path is None: path = [] if (type(wml) is wmlparser.TagNode) or (type(wml) is wmlparser.RootNode): attr: List[wmlparser.AttributeNode] = [] attrPath = path[:] for node in wml.get_all(): if type(node) is wmlparser.AttributeNode: attr.append(node) else: self.main.log.log(2, "got node %s", node.get_name()) if node.get_name() == "gamelist_diff": self.actOnGamelistDiff(node) elif node.get_name() == "user": self.actOnUser(node) elif node.get_name() == "gamelist": self.actOnGamelist(node) elif node.get_name() == "whisper": self.actOnWhisper(node) elif node.get_name() == "message": # TODO only use relevant messages, not those in scenario code self.actOnMessage(node) elif node.get_name() == "speak": self.actOnSpeak(node) elif node.get_name() == "error": self.actOnError(node) elif node.get_name() == "observer": self.actOnObserver(node) elif node.get_name() == "observer_quit": self.actOnObserverQuit(node) else: for sub in node.get_all(): self.actOnWML(sub, path + [node.get_name()]) self.parseAttr(attr, attrPath) elif type(wml) is list: raise WesException("actOnWML got wml as list") # attr = [] # attrPath = path[:] # tags = [] # for node in wml: # if type(node) is wmlparser.AttributeNode: # attr.append(node) # else: # tags.append(node) # self.parseAttr(attr, attrPath) # for node in tags: # self.actOnWML(node.get_all(), path + [node.get_name()]) elif type(wml) is wmlparser.AttributeNode: self.main.log.log(2, "AttributeNode @%s %s %s", path, wml.get_name(), wml.get_text())
def onIrcMessage(self, data): self.log.debug("with unparsed irc message in commandhandler %s", data) # message = data.split('#',1)[1].split(":",1)[1].strip("\r\n") message = data.split(":", 2)[2] sender = data[1:].split("!", 1)[0] if sender == "IRC": if "Too many connections from your IP" in message: net_ = self.main.cfg.ircNetAlt if self.main.cfg.ircNet in net_: net_.remove(self.main.cfg.ircNet) self.main.cfg.ircNet = random.choice(list(net_)) raise WesException().reconnectIrc() return # Message from IRC network, not something that should be treated as user message target = data.split(" ", 3)[2] self.log.debug("target %s", target) whisper = "#" not in target self.log.debug("with irc message in commandhandler %s %s %s", sender, ">", message) permission = 0 if sender in self.ircMasters: permission = PERMISSION_ADMIN + 5 self.onMessage(message, sender, permission, "irc", whisper)
def restart(**kwargs): """Restarts bot""" kwargs["reply"]("restarting") raise WesException("restart command used").restart()
def q(**kwargs): """Quits bot""" kwargs["reply"]("quitting") raise WesException("quit command used").quit()
def onCommand(self, message, sender, permission, origin): def reply(message): self.sendPrivately(sender, origin, str(message)) if " " in message: message = message.split(" ", 1) command = message[0] args = message[1] else: command = message args = "" self.log.debug("got command %s %s %s", command, "with args", args) if command in self.commands and permission > self.commands[ command].permission: self.commands[command].command(reply=reply, args=args, permission=permission, sender=sender) if command == "join" and permission > PERMISSION_ADMIN: self.irc.join(args) elif command == "part" and permission > PERMISSION_ADMIN: self.irc.part(args) elif command == "quitwes" and permission > PERMISSION_ADMIN: raise WesException("quitwes command used").addAction( WesException.QUIT_WES) elif command == "quitirc" and permission > PERMISSION_ADMIN: raise WesException("quitirc command used").addAction( WesException.QUIT_IRC) elif command == "ircreconnect" and permission > PERMISSION_ADMIN: raise WesException("ircreconnect command used").reconnectIrc() elif command == "wesreconnect" and permission > PERMISSION_ADMIN: raise WesException("wesreconnect command used").reconnectWes() elif command == "follow" and permission > PERMISSION_ADMIN: if not args or args == "": args = sender try: user = self.main.lobby.users.get(args) self.wes.send_wml_string( "[join]\nid={}\nobserve=yes\n[/join]".format(user.game_id)) except WesException as e: if e.action != [WesException.ASSERT]: raise e reply("User {} not found".format(args)) elif command == "control" and permission > PERMISSION_ADMIN: parts = args.split(" ", 1) if len(parts) == 2: side, target = parts[0], parts[1] self.wes.send_wml_string("""[change_controller] controller="human" player="%s" side="%s" [/change_controller]""" % (target, side)) else: reply("control needs to have two arguments") elif command == "leave" and permission > PERMISSION_ADMIN: self.wes.send_wml_string("[leave_game]\n[/leave_game]") elif command == "trust" and permission > PERMISSION_ADMIN: self.main.cfg.botTrustedNames.append(args.strip()) reply("Added '{}' to trusted names. Current list: {}".format( args.strip(), self.main.cfg.botTrustedNames)) else: if command not in self.commands: reply("Command {} not recognized".format(command))