async def get_player_name(self, pid): pid = int(pid) friend = self.friends.get_friend(pid) if friend is None: name = await self.fetch_player_name(pid) if name is None: return try: friend = await self.friends.add(name) except Exception: return normalize_name(name) return normalize_name(friend.name)
async def handle_module_packet(self, tid, packet): if tid == SYNCHRONIZE: # Synchronizes the game and the bot now = time.time() lua_time, ranks, staff = packet.split("\x00", 2) self.time_diff = int(lua_time) // 1000 - now self.player_ranks = {} self.ranks = {} for rank in ranks.split("\x01"): self.ranks[rank] = False for data in staff.split("\x00"): player, *ranks = data.split("\x01") player = normalize_name(player) self.player_ranks[player] = self.ranks.copy() for rank in ranks: self.player_ranks[player][rank] = True self.dispatch("module_synchronization") else: return False return True
async def on_channel_message(self, msg): # send the message to discord for chat in (self.mod_chat, self.mapper_chat): if msg.channel != chat.channel: continue content = re.sub( r"`(https?://(?:-\.)?(?:[^\s/?\.#-]+\.?)+(?:/[^\s]*)?)`", r"\1", "`" + msg.content.replace("`", "'") .replace("<", "<") .replace("&", "&") .replace(" ", "` `") + "`" ) author = normalize_name(msg.author) await self.send_webhook( chat.chat_webhook, "`[{}]` `[{}]` {}".format( msg.community.name, author, content ) ) if author != "Parkour#8558" and msg.content[0] == ".": args = msg.content.split(" ") cmd = args.pop(0).lower()[1:] ranks = self.get_player_rank(author) self.dispatch( "channel_command", msg.channel, chat.name, author, ranks, cmd, args ) break
async def on_friend_connected(self, friend): if friend.isSoulmate: return name = normalize_name(friend.name) # Check which chats this player should be in ranks = self.get_player_rank(name) chats = [] for chat in (self.mod_chat, self.mapper_chat): for rank in chat.ranks: if ranks[rank]: break else: continue chats.append(chat) # Wait for the chats to loop and update their players/members while chats: updated = await self.wait_for("on_chat_loop") for chat in updated: if chat in chats: chats.remove(chat) if name not in chat.players: await self.whisper( name, "Please join the {} chat: /chat {}" .format(chat.name, chat.channel_name) )
async def limit_whois_cache(self): while not self.main.open: await asyncio.sleep(3.0) while self.main.open: await asyncio.sleep(300.0) if self.friends is None or len(self.friends.friends) < 400: continue count, friends, required = 0, [], len(self.friends.friends) - 350 for friend in self.friends.friends: if (friend.isSoulmate or normalize_name(friend.name) in self.player_ranks): continue count += 1 friends.append(friend) if count >= required: break for friend in friends: await friend.remove() await asyncio.sleep(1.0)
async def on_whisper(self, whisper): await super().on_whisper(whisper) if whisper.content.startswith("tfm"): await self.proxy.sendTo( { "type": "verification", "username": normalize_name(whisper.author), "token": whisper.content }, "discord")
def prettify_message(self, author, content, community=None): content = re.sub( r"`(https?://(?:-\.)?(?:[^\s/?\.#-]+\.?)+(?:/[^\s]*)?)`", r"\1", "`" + content.replace("`", "'").replace("<", "<").replace( "&", "&").replace(" ", "` `") + "`") author = normalize_name(author) if community is not None: return "`[{}]` `[{}]` {}".format(community, author, content) else: return "`[{}]` {}".format(author, content)
async def save_player_file(self, name, file, update, online_check=True): name = normalize_name(name) if online_check: pid, name, online = await self.get_player_info(name) if not online: return False await self.send_callback( SAVE_PLAYER_FILE, "{}\x00{}\x00{}".format( name, json.dumps(file), "\x01".join(update) if isinstance(update, tuple) else update)) return True
async def load_player_file(self, name, online_check=True): name = normalize_name(name) if online_check: pid, name, online = await self.get_player_info(name) if not online: return await self.send_callback(LOAD_PLAYER_FILE, name) try: name, data = await self.wait_for( "on_player_file_loaded", lambda player, data: name == player, timeout=1.0) except Exception: return return json.loads(data) if data else None
async def get_player_info(self, query): if isinstance(query, str) and query.isdigit(): query = int(query) friend = self.friends.get_friend(query) if friend is None: if isinstance(query, int): name = await self.fetch_player_name(query) if name is None: return (None, None, None) else: name = query try: friend = await self.friends.add(name) except Exception: return (None, None, None) return (friend.id, normalize_name(friend.name), friend.isConnected)
async def fetch_player_name(self, pid): pid = int(pid) for attempt in range(3): try: async with self.forum_session.get( "https://atelier801.com/profile?pr={}".format( pid)) as resp: match = re.search( rb'> ([^<]+)<span class="nav-header-hashtag">' rb'(#\d{4})<\/span>', await resp.read()) if match is None: return return normalize_name( (match.group(1) + match.group(2)).decode()) except Exception: await self.forum_session.close() self.forum_session = aiohttp.ClientSession()
async def on_channel_message(self, msg): # send the message to discord for chat in (self.mod_chat, self.mapper_chat): if msg.channel != chat.channel: continue await self.send_webhook( chat.chat_webhook, self.prettify_message(msg.author, msg.content, msg.community.name)) author = normalize_name(msg.author) if author != "Parkour#8558" and msg.content[0] == ".": args = msg.content.split(" ") cmd = args.pop(0).lower()[1:] ranks = self.get_player_rank(author) self.dispatch("channel_command", msg.channel, chat.name, author, ranks, cmd, args) break
async def check_intruders(self): while not self.main.open: await asyncio.sleep(3.0) while self.main.open: await asyncio.sleep(15.0) updated = [] for chat in (self.mod_chat, self.mapper_chat): if not chat.loaded: continue try: players = await chat.channel.who() except Exception: # timeout continue chat.players = [] updated.append(chat) for player in players: player = normalize_name(player.username) chat.players.append(player) # append normalized names if player == "Parkour#8558": continue ranks = self.get_player_rank(player) for rank in chat.ranks: if ranks[rank]: break else: # intruder! await chat.channel.send( "Intruder alert: {}".format(player) ) await asyncio.sleep(3.0) await self.generate_new_chat(chat) self.dispatch("chat_loop", updated)
async def on_whisper_command(self, whisper, author, ranks, cmd, args): if await super().on_whisper_command(whisper, author, ranks, cmd, args): return True if cmd == "norep": if not ranks["admin"] and not ranks["mod"]: return True if not args: await whisper.reply("Usage: .norep Username#0000") return True target = normalize_name(args[0]) pid, name, online = await self.get_player_info(target) if name is None or not online: await whisper.reply( "That player ({}) is not online.".format(target)) return True file = await self.load_player_file(name, online_check=False) if file is None: await whisper.reply("Could not load {}'s file.".format(name)) return True file["report"] = not file["report"] if not await self.save_player_file( name, file, "report", online_check=False): await whisper.reply("Could not modify {}'s file.".format(name)) return True action = "enabled" if file["report"] else "disabled" await self.send_webhook( env.webhooks.sanctions, "**`[NOREP]:`** `{}` has {} reports from `{}` (ID: `{}`)". format(author, action, name, pid)) await whisper.reply( "Reports from {} (ID: {}) have been {}.".format( name, pid, action)) elif cmd == "report": # Argument check if not args: await whisper.reply("Usage: .report Username#0000") return True reported = normalize_name(args[0]) if reported == author: await whisper.reply("Why are you trying to report yourself?") return True pid, name, online = await self.get_player_info(reported) if name is None or not online: await whisper.reply( "That player ({}) is not online.".format(reported)) return True await whisper.reply( "Your report of the player {} will be handled shortly.".format( reported)) # Player information check if self.report_cooldown(author): return True if reported in self.reported: return True file = await self.load_player_file(author, online_check=False) if file is None or not file["report"]: return True file = await self.load_player_file(reported, online_check=False) if file is None: return True now = self.tfm_time() if now < file.get("killed", 0): return ban = file.get("banned", 0) if ban == 2 or now < ban: return True # Create report report = self.rep_id self.rep_id += 1 online = len(self.mod_chat.players) - 1 now = time.time() self.reports[report] = [ author, reported, online == 0, now + 60 * 5, now + 60 * 30 ] self.reported.append(reported) self.reporters.append((now + 60 * 5, author)) if online == 0: await self.report_discord(report) else: await self.mod_chat.channel.send( "{} reported {} (report id: {}) (room: {}) " "(use the handle command here before handling it)".format( author, reported, report, file["room"])) else: return False return True
def get_player_rank(self, player): return self.player_ranks.get( normalize_name(player), self.ranks )