async def set_name(self, ctx, *, name: str): """Set new display name""" if not repo_u.exists(ctx.author.id): return await ctx.author.send(f"Register with `{self.p}register`") if repo_u.get_attribute(ctx.author.id, "restricted") == 1: return await ctx.author.send("You are forbidden to alter your settings.") name = self.sanitise(name, limit=32) u = repo_u.get_by_nickname(name) if u is not None: return await ctx.author.send("This name is already used by someone.") # fmt: off disallowed = ( "(", ")", "*", "/", "@", "\\", "_", "`", "\u200B", "\u200C", "\u200D", "\u2028", "\u2060", "\uFEFF", # guild emojis "<:", "<a:", ) # fmt: on for char in disallowed: if char in name: return await ctx.author.send("The name contains forbidden characters.") before = repo_u.get_attribute(ctx.author.id, "nickname") repo_u.set(ctx.author.id, key="nickname", value=name) await ctx.author.send(f"Your nickname was changed to **{name}**") await self.event.user(ctx, f"Nickname changed from **{before}** to **{name}**.")
async def user_edit(self, ctx, member: discord.Member, key: str, value: str): """Edit user""" if (ctx.author.id != config["admin id"] and repo_u.get_attribute(member.id, "mod") == 1): return await ctx.send( "> You do not have permission to alter mod accounts") if ctx.author.id != config["admin id"] and member.id == config[ "admin id"]: return await ctx.send( "> You do not have permission to alter admin account") if key in ("mod", "readonly", "restricted"): try: value = int(value) except ValueError: raise errors.BadArgument("Value has to be integer.") elif key.startswith("home_id:"): try: value = int(value) except ValueError: raise errors.BadArgument("Value has to be integer.") repo_u.set(discord_id=member.id, key=key, value=value) await self.event.sudo(ctx, f"{member.id} updated: {key} = {value}.")
async def on_message(self, message: discord.Message): # ignore non-textchannel sources if not isinstance(message.channel, discord.TextChannel): return # do not act if author is bot if message.author.bot: return # get wormhole db_w = repo_w.get(message.channel.id) if db_w is None: return # get additional information db_b = repo_b.get(db_w.beam) # check for attributes # fmt: off if db_b.active == 0 \ or db_w.active == 0 \ or repo_u.get_attribute(message.author.id, "readonly") == 1: return await self.delete(message) # fmt: on # do not act if message is bot command if message.content.startswith(config["prefix"]): return await self.delete(message) # get wormhole channel objects if db_b.name not in self.wormholes or len( self.wormholes[db_b.name]) == 0: self.reconnect(db_b.name) # process incoming message content = await self._process(message) # convert attachments to links first_line = True if message.attachments: for f in message.attachments: # don't add newline if message has only attachments if first_line: content += " " + f.url first_line = False else: content += "\n" + f.url if len(content) < 1: return # count the message self._update_stats(message) # send the message await self.send(message=message, text=content, files=message.attachments)
async def user_remove(self, ctx, member_id: int): """Remove user""" if ctx.author.id != config["admin id"] and repo_u.get_attribute( member_id, "mod") == 1: return await ctx.send( "> You do not have permission to alter mod accounts") if ctx.author.id != config["admin id"] and member_id == config[ "admin id"]: return await ctx.send( "> You do not have permission to alter admin account") repo_u.delete(member_id) await self.event.sudo(ctx, f"User **{member_id}** removed.")
async def set_home(self, ctx): """Set current channel as your home wormhole""" if not repo_u.exists(ctx.author.id): return await ctx.author.send(f"Register with `{self.p}register`") if repo_u.get_attribute(ctx.author.id, "restricted") == 1: return await ctx.author.send("You are forbidden to alter your settings.") if not isinstance(ctx.channel, discord.TextChannel) or not repo_w.exists(ctx.channel.id): return await ctx.author.send("Home has to be a wormhole") beam_name = repo_w.get(ctx.channel.id).beam repo_u.set(ctx.author.id, key=f"home_id:{beam_name}", value=ctx.channel.id) await ctx.author.send("Home set to " + ctx.channel.mention) await self.event.user( ctx, f"Home in **{beam_name}** set to **{ctx.channel.id}** ({ctx.guild.name})." )
def _update_stats(self, message: discord.Message): """Increment wormhole's statistics""" # try to get author's home wormhole beam_name = repo_w.get_attribute(message.channel.id, "beam") channel_id = repo_u.get_attribute(message.author.id, f"home_id:{beam_name}") if channel_id is None: # user is not registered, use current wormhole channel_id = message.channel.id current = repo_w.get_attribute(channel_id, "messages") repo_w.set(channel_id, "messages", current + 1) beam_name = repo_w.get_attribute(message.channel.id, "beam") if beam_name in self.transferred: self.transferred[beam_name] += 1 else: self.transferred[beam_name] = 1
async def _process(self, message: discord.Message): """Escape mentions and apply anonymity""" content = message.content users = re.findall(r"<@!?[0-9]+>", content) roles = re.findall(r"<@&[0-9]+>", content) channels = re.findall(r"<#[0-9]+>", content) emojis = re.findall(r"<:[a-zA-Z0-9_]+:[0-9]+>", content) # prevent tagging for u in users: try: # Get discord user tags. If they're registered, translate to # their ((nickname)); it will be converted on send. user_id = int( u.replace("<@!", "").replace("<@", "").replace(">", "")) nickname = repo_u.get_attribute(user_id, "nickname") if nickname is not None: user = "******" + nickname + "))" else: user = str(self.bot.get_user(user_id)) except Exception as e: user = "******" await self.event.user(message, "Problem in user retrieval:\n>>>{e}") content = content.replace(u, user) for r in roles: try: role = message.guild.get_role( int(r.replace("<@&", "").replace(">", ""))).name except Exception as e: role = "unknown-role" await self.event.user(message, "Problem in role retrieval:\n>>>{e}") content = content.replace(r, role) # convert channel tags to universal names for channel in channels: try: ch = self.bot.get_channel( int(channel.replace("<#", "").replace(">", ""))) channel_name = self.sanitise(ch.name) guild_name = self.sanitise(ch.guild.name) content = content.replace( channel, f"__**{guild_name}/{channel_name}**__") except Exception as e: await self.event.user(message, "Problem in channel retrieval:\n>>>{e}") # remove unavailable emojis for emoji in emojis: emoji_ = emoji.replace("<:", "").replace(">", "") emoji_name = emoji_.split(":")[0] emoji_id = int(emoji_.split(":")[1]) if self.bot.get_emoji(emoji_id) is None: content = content.replace(emoji, ":" + emoji_name + ":") # line preprocessor for codeblocks if "```" in content: backticks = re.findall(r"```[a-z0-9]*", content) for b in backticks: content = content.replace(f" {b}", f"\n{b}", 1) content = content.replace(f"{b} ", f"{b}\n", 1) # apply prefixes content_ = content.split("\n") content = "" p = self._get_prefix(message) code = False for i in range(len(content_)): if i == 1: # use fill icon instead of guild one p = self._get_prefix(message, first_line=False) line = content_[i] # add prefix if message starts with code block if i == 0 and line.startswith("```"): content += self._get_prefix(message) + "\n" if line.startswith("```"): code = True if code: content += line + "\n" else: content += p + line + "\n" if line.endswith("```") and code: code = False return content.replace("@", "@_")
async def send( self, *, message: discord.Message, text: str, files: list = None, ): """Distribute the message""" deleted_original = False # get variables messages = [message] db_w = repo_w.get(message.channel.id) db_b = repo_b.get(db_w.beam) # access control if db_b.active == 0: return if db_w.active == 0 or db_w.readonly == 1: return if repo_u.get_attribute(message.author.id, "readonly") == 1: return # remove the original, if possible manage_messages_perm = message.guild.me.permissions_in( message.channel).manage_messages if manage_messages_perm and db_b.replace == 1 and not files: try: messages[0] = message.author await self.delete(message) deleted_original = True except discord.Forbidden: pass # limit message length text = text[:1024] # update wormhole list if db_b.name not in self.wormholes.keys(): self.reconnect(db_b.name) wormholes = self.wormholes[db_b.name] users = self._get_users_from_tags(beam_name=db_b.name, text=text) # replicate messages tasks = [] for wormhole in wormholes: task = asyncio.ensure_future( self.replicate( wormhole, message, messages, users, text, files, db_b, manage_messages_perm, )) tasks.append(task) await asyncio.gather(*tasks, return_exceptions=True) # add checkmark to original, if it hasn't been deleted if not deleted_original: await message.add_reaction("✅") # save message objects in case of editing/deletion if db_b.timeout > 0: self.sent.append(messages) await asyncio.sleep(db_b.timeout) self.sent.remove(messages)
def is_mod(ctx: commands.Context): return is_admin(ctx) or repo_u.get_attribute(ctx.author.id, "mod") == 1