def linecount(self, message: core.BotMessage) -> None: """Gets a user's linecount Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ if len(message.arguments) < 3: return message.respond( f"Usage: ``{config.commandCharacter}linecount <user>, <room>, [optional number of days]``." ) userID = psclient.toID(message.arguments[1]) roomID = psclient.toID(message.arguments[2]) try: days = int(message.arguments[3]) except (IndexError, ValueError): days = 30 room = message.connection.getRoom(roomID) if not message.connection.rustChatlogger: return message.respond("There is currently no chatlogger loaded.") if not room: return message.respond(f"Invalid room: {roomID}") if not message.sender.can("searchlog", room): return message.respond("Permission denied.") message.respondHTMLPatched( message.connection.rustChatlogger.linecount_html( roomID, userID, days))
def addJP(self, message: core.BotMessage) -> None: """Adds a joinphrase for a user Arguments: message {Message} -- the Message object that invoked the command """ phrase = "" userid = "" if message.room: room = message.room if len(message.arguments) > 2: userid = psclient.toID(message.arguments[1]) phrase = ",".join(message.arguments[2:]).strip() elif len(message.arguments) > 3: room = message.connection.getRoom(message.arguments[1]) userid = psclient.toID(message.arguments[2]) phrase = ",".join(message.arguments[3:]) else: return message.respond("You must specify a room.") if not phrase or not userid: return message.respond( f"Usage: ``{config.commandCharacter}addjoinphrase {'[room], ' if not message.room else ''}[user], [phrase]``. " ) if not message.sender.can("manage", room): return message.respond("Permission denied.") room.addJoinphrase(phrase, userid) return message.respond("Joinphrase successfully added!")
def showSnippet(self, message: core.BotMessage) -> None: """Shows a fact, quote, or topic in chat Arguments: message {Message} -- the Message object that invoked the command """ kind = 'facts' snippetList = self.factList if "topic" in message.arguments[0]: kind = 'topics' snippetList = self.topicList elif "quote" in message.arguments[0]: kind = 'quotes' snippetList = self.quoteList if message.room: roomid = message.room.id if roomid == 'trivia' and kind == 'quotes': return message.respond( 'This command is disabled in the Trivia room.') elif len(message.arguments) > 1: roomid = psclient.toID(message.arguments[1]) else: return message.respond("You must specify a room.") if not snippetList or roomid not in snippetList.keys(): return message.respond(f"There are no {kind} for this room.") return message.respond(random.choice(snippetList[roomid]))
def topusers(self, message: core.BotMessage) -> None: """Gets the top users of a room Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}topusers <room>, [optional number of days]``." ) roomID = psclient.toID(message.arguments[1]) try: days = int(message.arguments[2]) except (IndexError, ValueError): days = 30 room = message.connection.getRoom(roomID) if not message.connection.rustChatlogger: return message.respond("There is currently no chatlogger loaded.") if not room: return message.respond(f"Invalid room: {roomID}") if not message.sender.can("searchlog", room): return message.respond("Permission denied.") message.respond("Please wait; fetching userstats...") return message.respondHTMLPatched( message.connection.rustChatlogger.topusers_html(roomID, days, 30))
def addPoints(self, message: core.BotMessage) -> None: """Adds points to the minigame leaderboard Arguments: message {Message} -- the Message object that invoked the command """ if not message.room: return message.respond("You can only add points in a room.") if not message.sender.can("hostgame", message.room): return message.respond("Permission denied.") if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}addpoints [comma-separated list of users], [optional number of points]``." ) usernames = message.arguments[1:] points = 1 if len(usernames) > 1 and isInt(usernames[len(usernames) - 1].strip()): points = int(usernames.pop()) if message.room.id not in self.minigamePoints.keys(): self.minigamePoints[message.room.id] = {} for name in usernames: userid = psclient.toID((name)) if userid not in self.minigamePoints[message.room.id].keys(): self.minigamePoints[message.room.id][userid] = points else: self.minigamePoints[message.room.id][userid] += points return message.respond("Points added!")
def handleModule(self, message: core.BotMessage) -> None: """Handles loading, reloading, and hotpatching modules Args: message (message: core.BotMessage) -> None: the message that triggered the command """ if not message.sender.id in config.sysops: return message.respond("Permission denied.") if not message.arguments or len(message.arguments) < 2: return message.respond( f"Usage: ``{message.arguments[0]} <module>``.") module = psclient.toID(message.arguments[1]) action = '' if 'load' in message.arguments[0]: action = 'load' if 'unload' in message.arguments[0]: action = 'unload' if 'hotpatch' in message.arguments[0]: action = 'hotpatch' if module == __name__ and action == 'unload': return message.respond( f"Don't unload the module that provides ``{message.arguments[0]}``." ) if action == 'unload': return message.respond(self.unload(message.connection, module)) if action == 'load': return message.respond(self.load(message.connection, module)) if action == 'hotpatch': mod = importlib.import_module(module) importlib.reload(mod) message.connection.commands.update( mod.Module().commands) # type: ignore return message.respond( f"Successfully hotpatched the {module} module.") return message.respond("Something went wrong -- no action detected!")
def checkHouse(self, message: core.BotMessage) -> None: """Checks what house a user is in Arguments: message (core.Botmessage: core.BotMessage) -> None: the Message object that invoked the command """ user: str = ','.join(message.arguments[1:]) if len(message.arguments) > 1 else message.senderName houses: List[str] = getUserHouses(psclient.toID(user)) if houses: return message.respond(f"{user} is in {houses[0].title()} house!") return message.respond(f"{user} is not in any house.")
def switchRoomContext(self, room: str) -> None: """Changes the room context Args: room (str): the name/ID of the room to change the context to """ if not self.connection.getRoom(room): self.connection.roomList.add(psclient.Room(room, self.connection)) self.roomContext = psclient.toID(room) self.prompt.message = self.getPrompt()
def showSampleTeams(self, message: core.BotMessage) -> None: """Displays sample teams Arguments: message {Message} -- the Message object that invoked the command """ formatid = psclient.toID(','.join(message.arguments[1:]) if len(message.arguments) > 1 else '') if not formatid or formatid not in htmlboxes: return message.respond(f"You must specify a format that I have sample teams for: {', '.join(list(htmlboxes.keys()))}") return message.respondHTML(generateHTML(htmlboxes[formatid]))
def removePlayers(self, message: core.BotMessage) -> None: """Remove users from the minigame leaderboard Arguments: message {Message} -- the Message object that invoked the command """ PM = False room = message.room if not room: if len(message.arguments) < 3: return message.respond( "You must specify a room and at least one user.") room = message.connection.getRoom( psclient.toID(message.arguments[1])) PM = True if not room: return message.respond("You must specify a room.") if not message.sender.can("hostgame", room): return message.respond("Permission denied.") if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}removeplayers [comma-separated list of users]``." ) usernames = message.arguments[1:] usersOnLB = [] if PM: usernames = message.arguments[2:] if room.id not in self.minigamePoints.keys(): return message.respond("There are no scores.") for name in usernames: userid = psclient.toID(name) if userid in self.minigamePoints[room.id].keys(): usersOnLB.append(userid) else: return message.respond(f"{name} isn't on the leaderboard.") for user in usersOnLB: del self.minigamePoints[room.id][user] return message.respond("Users removed!")
def translate(room: Union[str, psclient.Room], text: str) -> str: """Translates a message Args: room (Union[str, psclient.Room]): the room the text is being translated for text (str): the text to translate Returns: str: the translated text """ roomid = psclient.toID(room) if isinstance(room, str) else room.id translations = STRINGS.get(getLanguageID(roomid)) if not translations: return text return translations.get(text) or text
def deleteJP(self, message: core.BotMessage) -> None: """Removes a joinphrase for a user Arguments: message {Message} -- the Message object that invoked the command """ userid = "" if message.room: room = message.room if len(message.arguments) > 1: userid = psclient.toID(message.arguments[1]) elif len(message.arguments) > 2: room = message.connection.getRoom(message.arguments[1]) userid = psclient.toID(message.arguments[2]) else: return message.respond("You must specify a room.") if not userid: return message.respond( f"Usage: ``{config.commandCharacter}removejoinphrase {'[room], ' if not message.room else ''}[user]``. " ) if not message.sender.can("manage", room): return message.respond("Permission denied.") room.removeJoinphrase(userid) return message.respond("Joinphrase successfully removed!")
def logsearch(self, message: core.BotMessage) -> None: """Searches logs Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}logsearch <room>, [optional user], [optional keyword]``." ) if not message.connection.rustChatlogger: return message.respond("There is currently no chatlogger loaded.") roomID = psclient.toID(message.arguments[1]).lower() userID = psclient.toID(message.arguments[2]).lower() if len( message.arguments) > 2 else None keywords = message.arguments[3:] if len( message.arguments) > 3 else None room = message.connection.getRoom(roomID) if not room: return message.respond(f"Invalid room: {roomID}") if not message.sender.can("searchlog", room): return message.respond("Permission denied.") message.respond( f"Fetching the {MAX_MESSAGES} most recent messages in the room {roomID}" + (f" sent by the user '{userID}'" if userID else "") + (f" containing all of the following keywords: {', '.join(keywords)}" if keywords else "") + ".") return message.respondHTMLPatched( message.connection.rustChatlogger.html_search( roomID, userID or None, None, # `oldest` param in Rust keywords or None, MAX_MESSAGES))
def createHouse(self, message: core.BotMessage) -> None: """Creates a new house Args: message (core.Botmessage: core.BotMessage) -> None: the message that invoked the command """ if message.sender.id not in config.sysops: return message.respond( f"Only bot operators (sysops) can create houses. The bot operators are: {', '.join(config.sysops)}" ) if len(message.arguments) < 2: return message.respond(f"Usage: ``{config.commandCharacter}createhouse <house>``") house: str = psclient.toID(','.join(message.arguments[1:])) houseData: Dict[str, list] = data.get("houses") or {} if house in houseData: return message.respond(f"The house {house} already exists. Houses can only be deleted manually.") houseData[house] = [] data.store("houses", houseData) return message.respond(f"Successfully created the house {house.title()}!")
def showLB(self, message: core.BotMessage) -> None: """Displays the minigame leaderboard Arguments: message {Message} -- the Message object that invoked the command """ roomid = message.room.id if message.room else None if not roomid: if len(message.arguments) < 2: return message.respond("You must specify a room.") roomid = psclient.toID(message.arguments[1]) if not roomid: return message.respond("You must specify a room.") if roomid not in self.minigamePoints.keys(): return message.respond("There are no scores.") points = self.minigamePoints[roomid] # TODO: investigate mypy errors sortedUsers = sorted(points, key=points.get, reverse=True) # type: ignore formattedPoints = ", ".join([f"{key} (**{points[key]}**)" for key in sortedUsers]) return message.respond(f"**Scores**: {formattedPoints}" if formattedPoints else "There are no scores.")
def reverse(self, message: core.BotMessage) -> None: """Sends a reversed phrase for the Reversio game. Arguments: message {Message} -- the Message object that invoked the command """ if message.room: roomID = message.room.id elif message.arguments and len(message.arguments) > 1: roomID = psclient.toID(message.arguments[1]) else: return message.respond("You must specify a room.") if roomID not in self.reversioWords.keys() or len(self.reversioWords[roomID]) < 1: return message.respond(f"There are no reversio words for the room {roomID}.") response = "/wall " if (not message.room) or message.sender.can("wall", message.room) else "" response += random.choice(self.reversioWords[roomID]).lower()[::-1].strip() return message.respond(response)
def joinHouse(self, message: core.BotMessage) -> None: """Joins a house Args: message (core.Botmessage: core.BotMessage) -> None: the message that invoked the command """ if not message.sender.id: return message.respond("Only users can join houses.") # Shouldn't happen if len(message.arguments) < 2: return message.respond(f"Usage: ``{config.commandCharacter}joinhouse <house>``") house: str = psclient.toID(','.join(message.arguments[1:])) houseData: Dict[str, list] = data.get("houses") or {} if house not in houseData: return message.respond(f"{house.title()} is not a known house. Try one of: {', '.join(list(houseData.keys()))}") currentHouses: List[str] = getUserHouses(message.sender.id) for oldHouse in currentHouses: houseData[oldHouse].remove(message.sender.id) houseData[house].append(message.sender.id) data.store("houses", houseData) leaveMessage = f" left {', '.join([house.title() for house in currentHouses])} and" if currentHouses else "" return message.respond( f"Successfully{leaveMessage} joined {house.title()}!" )
def configure(self, isAdvanced: str) -> None: """Configures preferences Args: isAdvanced (str): 'advanced' means we show advanced settings """ # pylint: disable=unused-argument preferences: dict = { "username": "******", "password": "******", "autojoins": "The rooms you want to automatically join upon logging in" } advancedPreferences: dict = { "commandchar": "The character to use before commands ('%' is recommended)", "prompt": "The prompt to use. If you don't know what you're doing, it's best to set this to '{room}> '" } loopDict = preferences if isAdvanced != 'advanced' else dict( preferences, **advancedPreferences) for pref in loopDict: isPassword: bool = pref == "password" currentValue: str = "******" if prefs.getPref( pref) and isPassword else str(prefs.getPref(pref)) value: Any = inputDialog( title=f"Configure {pref}", text=f"{loopDict[pref]} (currently: {currentValue})", password=isPassword).run() if not value: continue if pref == "autojoins": value = [psclient.toID(room) for room in value.split(',')] prefs.setPref(pref, value) prefs.setPref( "showjoins", yesNoDialog(title="Configure showjoins", text="Display join/leave messages?").run())
def changeModeration(self, message: core.BotMessage) -> None: """Changes moderation settings Arguments: message {Message} -- the Message object that invoked the command """ def usage() -> None: return message.respond(f"Usage: ``{message.arguments[0]} [optional room], <moderation type>``.") if len(message.arguments) < 2: return usage() isEnabling = 'enable' in message.arguments[0] room = message.connection.getRoom(message.arguments[1]) index = 2 if not room: if not message.room: return message.respond("You must specify a room in PMs.") room = message.room index = 1 if len(message.arguments) < index + 1: return usage() if not message.sender.can("manage", room): return message.respond("Permission denied.") moderationType = psclient.toID(message.arguments[index]) if moderationType not in core.VALID_AUTOMOD_TYPES: return message.respond( f"'{moderationType}' is not a valid moderation type. Use one of: {', '.join(core.VALID_AUTOMOD_TYPES)}" ) room.setModerationType(moderationType, isEnabling) return message.respond( f"Successfully {'enabled' if isEnabling else 'disabled'} moderation for {moderationType} in {room.id}!" )
def superhero(self, message: core.BotMessage) -> None: """Gets information on a superhero from the Superhero API Arguments: message {Message} -- the Message object that invoked the command """ superheroIDDictionary = data.get( "superheroIDDictionary") or _initializeData() superhero = psclient.toID(config.separator.join(message.arguments[1:])) if superhero not in superheroIDDictionary: return message.respond( f"{superhero} isn't a superhero that can be looked up with the API." ) superheroID = superheroIDDictionary[superhero] APIResponse = requests.get( f"https://superheroapi.com/api/{config.superheroAPIKey}/{superheroID}" ).json() if APIResponse['response'] != 'success': return message.respond( f"The API request for {superhero} (ID: {superheroID}) failed with response {APIResponse['response']}." ) # Aliases and relatives can be lists aliases = APIResponse['biography']['aliases'] relatives = APIResponse['connections']['relatives'] aliases = aliases if isinstance(aliases, str) else ", ".join(aliases) relatives = relatives if isinstance(relatives, str) else ", ".join(relatives) message.respond(f"!show {APIResponse['image']['url']}") html = f""" <details><summary>{APIResponse['name']}</summary><details><summary>Stats</summary> <b>Intelligence:</b> {APIResponse['powerstats']['intelligence']}<br> <b>Strength:</b> {APIResponse['powerstats']['strength']}<br> <b>Speed:</b> {APIResponse['powerstats']['speed']}<br> <b>Durability:</b> {APIResponse['powerstats']['durability']}<br> <b>Power:</b> {APIResponse['powerstats']['power']}<br> <b>Combat:</b> {APIResponse['powerstats']['combat']}<br> </details><details><summary>Biography</summary> <b>Full Name:</b> {APIResponse['biography']['full-name']}<br> <b>Alter Egos:</b> {APIResponse['biography']['alter-egos']}<br> <b>Aliases:</b> {aliases}<br> <b>Birthplace:</b> {APIResponse['biography']['place-of-birth']}<br> <b>Debut:</b> {APIResponse['biography']['first-appearance']}<br> <b>Publisher:</b> {APIResponse['biography']['publisher']}<br> <b>Alignment:</b> {APIResponse['biography']['alignment']}<br> </details><details><summary>Appearance</summary> <b>Gender:</b> {APIResponse['appearance']['gender']}<br> <b>Race:</b> {APIResponse['appearance']['race']}<br> <b>Height:</b> {APIResponse['appearance']['height'][1]}<br> <b>Weight:</b> {APIResponse['appearance']['weight'][1]}<br> <b>Eye Color:</b> {APIResponse['appearance']['eye-color']}<br> <b>Hair Color:</b> {APIResponse['appearance']['hair-color']}<br> </details><details><summary>Work</summary> <b>Occupation:</b> {APIResponse['work']['occupation']}<br> <b>Base:</b> {APIResponse['work']['base']}<br> </details><details><summary>Connections</summary> <b>Group Affiliation:</b> {APIResponse['connections']['group-affiliation']}<br> <b>Relatives:</b> {relatives}<br> </details></details> """ return message.respondHTML(html)
def testToID(): """Tests the toID() function """ assert psclient.toID("hi") == "hi" assert psclient.toID("HI") == "hi" assert psclient.toID("$&@*%$HI ^4åå") == "hi4"