def ping(self, message: core.BotMessage) -> None: """Ping: replies "Pong!" Arguments: message {Message} -- the Message object that invoked the command """ message.respond("Pong!")
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 removeRepeat(self, message: core.BotMessage) -> None: """removerepeat: removes a repeat Arguments: message {Message} -- the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}removerepeat <message>``.") if not message.sender.can('searchlog', message.room): return message.respond("Permission denied.") msg = config.separator.join(message.arguments[1:]) repeats = data.get("repeats") if not repeats or message.room.id not in repeats: return message.respond( f"There are no repeats for the room '{message.room.id}'.") repeatFound = False newRepeats = [] for repeat in repeats[message.room.id]: if msg in repeat: repeatFound = True continue newRepeats.append(repeat) if not repeatFound: return message.respond("No repeat found.") repeats[message.room.id] = newRepeats data.store("repeats", repeats) return message.respond("Repeat removed!")
def startGame(self, message: core.BotMessage) -> None: """Starts a tournament or game of UNO Arguments: message {Message} -- the Message object that invoked the command """ if not message.room: return message.respond("You cannot start a game in PMs.") if not message.sender.can("hostgame", message.room): return message.respond("Permission denied.") isTournament = message.arguments[0].strip( config.commandCharacter) in ['tour', 'tournament'] if isTournament and len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}tournament [format], [comma-separated custom rules]``." ) if isTournament: commands = TOUR_SETUP_COMMANDS tourFormat = message.arguments[1] message.room.say(f"/tour new {tourFormat},elim") if len(message.arguments) > 2: rules = ', '.join(message.arguments[2:]) message.room.say(f"/tour rules {rules}") else: commands = UNO_COMMANDS for command in commands: message.room.say(command)
def addReversioWord(self, message: core.BotMessage) -> None: """Adds a word to the reversio database. Arguments: message {Message} -- the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond( f"Usage: ``{config.commandCharacter}addreversioword {'[room], ' if not message.room else ''}<word>``." ) word = ",".join(message.arguments[1:]) room = message.room if not room and len(message.arguments) > 2: room = message.connection.getRoom(message.arguments[1]) word = ",".join(message.arguments[2:]) if not room: return message.respond("You must specify a valid room.") word = word.strip().lower() if message.sender.can("addfact", room): if room.id not in self.reversioWords.keys(): self.reversioWords[room.id] = [word] else: self.reversioWords[room.id].append(word) data.store("reversioWords", self.reversioWords) return message.respond("Word added!") return message.respond("Permission denied.")
def exportSnippets(self, message: core.BotMessage) -> None: """Exports the snippets to Pastebin Arguments: message {Message} -- the Message object that invoked the command """ kind = 'fact' snippetList = self.factList if "topic" in message.arguments[0]: kind = 'topic' snippetList = self.topicList elif "quote" in message.arguments[0]: kind = 'quote' snippetList = self.quoteList if message.room: room = message.room elif len(message.arguments) > 1: room = message.connection.getRoom(message.arguments[1]) else: return message.respond("You must specify a room.") if not message.sender.can("addfact", room): return message.respond("Permission denied.") if room.id not in snippetList.keys() or len(snippetList[room.id]) == 0: return message.respond( f"There are no {kind}s for the room {room.id}.") pasteData = "\n".join(snippetList[room.id]) return message.respond( str( Pastebin(config.pastebinAPIKey).create_paste( pasteData, # the data 1, # unlisted paste f"{kind.title()}s for room {room.id}" # title )))
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 countSnippets(self, message: core.BotMessage) -> None: """Counts the number of snippets Arguments: message {Message} -- the Message object that invoked the command """ kind = 'fact' snippetList = self.factList if "topic" in message.arguments[0]: kind = 'topic' snippetList = self.topicList elif "quote" in message.arguments[0]: kind = 'quote' snippetList = self.quoteList if message.room: room = message.room elif len(message.arguments) > 1: room = message.connection.getRoom(message.arguments[1]) else: return message.respond("You must specify a room.") num = 0 if snippetList and room.id in snippetList.keys(): num = len(snippetList[room.id]) return message.respond( f"There {'is ' if num == 1 else 'are '} {str(num)} \ {kind}{'' if num == 1 else 's'} for the room {room.id}.")
def listRepeats(self, message: core.BotMessage) -> None: """listrepeats: lists repeats Arguments: message {Message} -- the Message object that invoked the command """ room = message.room if not room: if len(message.arguments) != 2: return message.respond( "You must specify a room when using this command in PMs.") room = message.connection.getRoom(message.arguments[1]) if not room: return message.respond( f"I'm not in the room '{message.arguments[1]}'.") if not message.sender.can('searchlog', room): return message.respond("Permission denied.") repeats = data.get("repeats") if not (repeats and room.id in repeats and repeats[room.id]): return message.respond( f"There are no repeats for the room '{room.id}'") htmlBuf = f"<details><summary>Repeats for the room <strong>{room.id}</strong></summary><ul>" for repeat in repeats[room.id]: htmlBuf += f"<li>Repeated every {list(repeat.values())[0]} minutes: \"{list(repeat.keys())[0]}\"</li>" htmlBuf += "</ul></details>" print(htmlBuf) return message.respondHTML(htmlBuf)
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 memoryStats(self, message: core.BotMessage) -> None: """Gets info on memory usage Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ if not message.sender.id in config.sysops: return message.respond("Permission denied.") buf = [] meminfoPath = pathlib.Path("/proc/meminfo") if meminfoPath.is_file(): for line in open('/proc/meminfo', 'r', encoding='utf-8').readlines(): if 'MemAvailable' in line: buf.append( f"{round(int(line.split(':')[1].split('kB')[0]) / 1024, 2)} MB available" ) if 'MemFree' in line: buf.append( f"{round(int(line.split(':')[1].split('kB')[0]) / 1024, 2)} MB free" ) if 'MemTotal' in line: buf.append( f"{round(int(line.split(':')[1].split('kB')[0]) / 1024, 2)} MB total" ) buf.append( f"{round(psutil.Process().memory_info().rss / 1024 ** 2, 2)} MB used by the bot process" ) return message.respond(f"Memory stats: {', '.join(buf)}")
def timer(self, message: core.BotMessage) -> None: """timer: evaluates the given Python expression Arguments: message {Message} -- the Message object that invoked the command """ if len(message.arguments) not in range(1, 4): message.respond( f"Usage: ``{config.commandCharacter}timer <duration>, <optional message>``" ) return response = "/wall " if message.type == 'pm' or message.connection.this.can( 'wall', message.room) else "" response += message.arguments[2] if len( message.arguments ) > 2 else f"Timer set by {message.sender.name} is up" try: duration = float(message.arguments[1]) except ValueError: message.respond(f"{message.arguments[1]} isn't a valid duration") return t = threading.Timer(duration, message.respond, args=[response]) t.daemon = True t.start()
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 owo(self, message: core.BotMessage) -> None: """owo: replaces vowels with owo faces Arguments: message {Message} -- the Message object that invoked the command """ text = config.separator.join(message.arguments[1:]) for vowel in list("AaEeIiOoUuYy"): text = text.replace(vowel, f"{vowel}w{vowel}") message.respond(text)
def uwu(self, message: core.BotMessage) -> None: """uwu: turns English into weird anime language Arguments: message {Message} -- the Message object that invoked the command """ text = config.separator.join(message.arguments[1:]) uwuRules = {'r': 'w', 'l': 'w', 'R': 'W', 'L': 'W'} for (letter, replacement) in uwuRules.items(): text = text.replace(letter, replacement) message.respond(sanitize(text))
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 kill(self, message: core.BotMessage) -> None: """Kills the bot process Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ if message.sender.id in config.sysops: message.respond("Killing the bot process....") core.log(f"E: admin.kill(): killed by {message.senderName}") sys.exit() return message.respond("Permission denied.")
def uwu(self, message: core.BotMessage) -> None: """uwu: turns English into weird anime language Arguments: message {Message} -- the Message object that invoked the command """ text = config.separator.join(message.arguments[1:]) uwuRules = {'r': 'w', 'l': 'w', 'R': 'W', 'L': 'W'} for key in uwuRules: text = text.replace(key, uwuRules[key]) message.respond(text)
def audio(self, message: core.BotMessage) -> None: """audio: displays audio in the room Arguments: message {Message} -- the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond(f"Usage: ``{ config.commandCharacter}audio <URL to audio file>``.") url = ','.join(message.arguments[1:]).strip() if not isAudioURL(url): return message.respond( "You must specify a valid URL beginning with ``http://`` or ``https://``; the URL must refer to an audio file." ) return message.respondHTMLPatched(f'<audio controls src="{url}"></audio>')
def audio(self, message: core.BotMessage) -> None: """audio: displays audio in the room Arguments: message {Message} -- the Message object that invoked the command """ if len(message.arguments) < 2: return message.respond(f"Usage: ``{ config.commandCharacter}audio <URL to audio file>``.") url = message.arguments[1].strip().lower() if not re.match(r'^https?:\/\/(.*?)\.[a-z]{2,}/(.*?)\.(mp[34]|wav|ogg)$', url): return message.respond( "You must specify a valid URL beginning with ``http://`` or ``https://``; the URL must refer to an audio file." ) return message.respondHTML(f'<audio controls src="{url}"></audio>')
def runCommand(self, message: core.BotMessage) -> None: """runCommand: sends the given command to the given room Arguments: message {Message} -- the Message object that invoked the command """ if not message.arguments or len(message.arguments) < 3: return message.respond( f"Usage: ``{config.commandCharacter}do <room>, <message>``.") room = message.connection.getRoom(message.arguments[1]) if room: if not message.sender.can("manage", room): return message.respond("Permission denied.") command = ",".join(message.arguments[2:]).strip() return room.say(command) return message.respond(f"{message.arguments[1]} isn't a room I'm in.")
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 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 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 resetLB(self, message: core.BotMessage) -> None: """Resets the minigame leaderboard Arguments: message {Message} -- the Message object that invoked the command """ room = message.room if not room: if len(message.arguments) < 2: return message.respond("You must specify a room.") room = message.connection.getRoom(message.arguments[1]) if not room: return message.respond("You must specify a room.") if not message.sender.can("hostgame", message.room): return message.respond("Permission denied.") if room.id not in self.minigamePoints.keys(): return message.respond(f"There are no scores in the leaderboard for the room '{room.id}'.") del self.minigamePoints[room.id] return message.respond("Cleared the minigame leaderboard!")
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 viewModules(self, message: core.BotMessage) -> None: """Lists the currently loaded modules Args: message (message: core.BotMessage) -> None: the Message object that invoked the command """ return message.respond( f"Modules currently known to be loaded: {', '.join([f'``{module}``' for module in message.connection.modules])}" )
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 help(self, message: core.BotMessage) -> None: """Help Arguments: message {Message} -- the Message object that invoked the command """ return message.respond( "Expecto Botronum guide: https://github.com/AnnikaCodes/expecto-botronum/blob/master/README.md#commands" )
def viewModerationSettings(self, message: core.BotMessage) -> None: """Displays information about a room's moderation settings Args: message (core.BotMessage): the Message object that invoked the command """ room = message.connection.getRoom(message.arguments[1]) if len(message.arguments) > 1 else message.room if not room: return message.respond("You must specify a valid room when using this command in PMs.") if not message.sender.can("searchlog", room): return message.respond("Permission denied.") moderation = room.moderation or {} buf = f"Moderation settings for room **``{room.id}``**: " buf += ', '.join( [f"**{thing}**: {'enabled' if moderation.get(thing) else 'disabled'}" for thing in core.VALID_AUTOMOD_TYPES] ) buf += '.' message.respond(buf)