async def convert(self, ctx: context.CustomContext, argument): if not ctx.bot.mk.NATION_ROLE_PREFIX: raise exceptions.DemocracivBotException( f"{config.NO} You can't use Nation Roles with this bot.") arg = argument.lower() prefix = ctx.bot.mk.NATION_ROLE_PREFIX.lower() if arg.startswith(prefix): real_arg = arg elif arg.startswith(prefix[:-2]): real_arg = arg.replace(prefix[:-2], prefix) else: real_arg = f"{ctx.bot.mk.NATION_ROLE_PREFIX}{argument}" role = await super().convert(ctx, real_arg) if not role.name.lower().startswith( ctx.bot.mk.NATION_ROLE_PREFIX.lower()): raise commands.BadArgument( f"{config.NO} You're not allowed to give someone the `{role.name}` role." ) return role
async def pin(self, ctx, *, message: discord.Message = None): """Pin a message You can either reply to the message you want me to pin, or give me the message's ID like shown below. **Example** `{PREFIX}{COMMAND} 784598328666619934` use the message's ID *(only works if you use the command in the same channel as the message you want to pin)* `{PREFIX}{COMMAND} https://discord.com/channels/208984105310879744/499669824847478785/784598328666619934` use the message's URL """ if not message and ctx.message.reference: message = ctx.message.reference.resolved if not message: message = await ctx.channel.fetch_message( ctx.message.reference.message_id) if not message: return if message.channel.category_id not in self.bot.mk.NATION_CATEGORIES: raise exceptions.DemocracivBotException( f"{config.NO} You're not allowed to pin messages in this channel." ) await message.pin()
async def _search_model(self, ctx, *, model, query: str, return_model=False): if len(query) < 3: raise exceptions.DemocracivBotException( f"{config.NO} The query to search for has to be at least 3 characters long." ) if model is models.Motion: found = await self.bot.db.fetch( "SELECT id from motion WHERE (lower(title) LIKE '%' || $1 || '%') OR" " (lower(description) LIKE '%' || $1 || '%') " "ORDER BY similarity(lower(title), $1) DESC LIMIT 20", query.lower(), ) formatted = [] for record in found: obj = await model.convert(ctx, record["id"]) if return_model: formatted.append(obj) else: formatted.append(obj.formatted) else: is_law = model is models.Law # First, search by name similarity async with self.bot.db.acquire() as con: results = await self._search_bill_by_name( query, connection=con, search_laws=is_law, return_model=return_model ) # Set word similarity threshold for search by tag await self._update_pg_trgm_similarity_threshold(0.4, connection=con) # Then, search by tag similarity # for word in query.split(): # if len(word) < 3 or word in ( # "the", # "author", # "authors", # "date", # "name", # "and", # "d/m/y", # "type", # "description", # "by", # "generated", # ): # continue result = await self._search_bill_by_tag( query, connection=con, search_laws=is_law, return_model=return_model ) if result: results.update(result) formatted = list(results) return formatted
async def tinyurl(self, url: str) -> typing.Optional[str]: async with self.session.post( f"https://api.shrtco.de/v2/shorten?url={url}") as response: if response.status in (200, 201): tiny_url = await response.json() try: return tiny_url["result"]["full_short_link"] except KeyError: raise exceptions.DemocracivBotException( f"{config.NO} The URL shortening service returned an error, try again later." )
async def check(ctx): if ctx.author.id == ctx.bot.owner_id: return True is_allowed = ( ctx.author.guild_permissions.administrator or await ctx.bot.get_guild_setting(ctx.guild.id, "tag_creation_allowed") ) if is_allowed: return True else: raise exceptions.DemocracivBotException( message=f"{config.NO} Only Administrators can add or remove tags on this server." " Administrators can change this setting in " f"`{config.BOT_PREFIX}server tagcreation`." )
def wrapper(ctx): if not ctx.bot.mk.NATION_ROLE_PREFIX: raise exceptions.DemocracivBotException( f"{config.NO} You can't use Nation Roles with this bot.") else: return True
async def create_new_party( self, ctx: CustomContext, *, role=True, leaders=True, invite=True, join_mode=True, commit=True, merge=False, ) -> typing.Union[typing.Dict, PoliticalParty]: result = { "role": None, "invite": None, "leaders": [], "join_mode": None } if role: await ctx.send( f"{config.USER_INTERACTION_REQUIRED} Reply with the name of the new party you want to create." ) role_name = await ctx.converted_input( converter=converter.CaseInsensitiveRole) if isinstance(role_name, str): await ctx.send( f"{config.YES} I will **create a new role** on this server named `{role_name}`" f" for the new party.") try: discord_role = await ctx.guild.create_role(name=role_name) except discord.Forbidden: raise exceptions.ForbiddenError( exceptions.ForbiddenTask.CREATE_ROLE, role_name) else: discord_role = role_name try: match = await PoliticalParty.convert(ctx, role_name) if match and merge: await ctx.send( f"{config.YES} I'll use the **already existing** party `{discord_role.name}` to merge " f"the others into.") return match except exceptions.NotFoundError: pass await ctx.send( f"{config.YES} I'll use the **pre-existing role** `{discord_role.name}` for the new party." ) result["role"] = discord_role if leaders: img = await self.bot.make_file_from_image_link( "https://cdn.discordapp.com/attachments/499669824847478785/784584955921301554/partyjoin.PNG" ) await ctx.send( f"{config.USER_INTERACTION_REQUIRED} Reply with the names or mentions of the party's leaders or " f"representatives. If this party has multiple leaders, separate them with a newline, like in the " f"image below.\n\n " f"{config.HINT} *Party leaders get DM notifications by me when someone joins or leaves their " f"party, and they are the ones that can accept and deny join requests if the party's join mode " f"is request-based.*", file=img, ) leaders_text = (await ctx.input()).splitlines() leaders = [] conv = Fuzzy[converter.CaseInsensitiveMember] for leader in leaders_text: try: converted = await conv.convert(ctx, leader.strip()) if not converted.bot: leaders.append(converted.id) except commands.BadArgument: continue if not leaders: leaders.append(0) result["leaders"] = leaders if invite: party_invite = await ctx.input( f"{config.USER_INTERACTION_REQUIRED} Reply with the invite link to the party's Discord server. " f"If they don't have one, just reply with gibberish.") if not self.discord_invite_pattern.fullmatch(party_invite): party_invite = "None" result["invite"] = party_invite if join_mode: view = SelectJoinModeView(ctx) await ctx.send( f"{config.USER_INTERACTION_REQUIRED} Should this party be public, request-based, or private?", view=view, ) join_mode = await view.prompt() result["join_mode"] = join_mode if commit: async with self.bot.db.acquire() as connection: if result["invite"] == "None": result["invite"] = None async with connection.transaction(): try: await connection.execute( "INSERT INTO party (id, discord_invite, join_mode) VALUES ($1, $2, $3)" "ON CONFLICT (id) DO UPDATE SET discord_invite = $2, join_mode = $3 WHERE party.id = $1", result["role"].id, result["invite"], result["join_mode"], ) except asyncpg.UniqueViolationError: raise exceptions.DemocracivBotException( f"{config.NO} `{result['role'].name}` already is a " f"political party. If you " f"want to edit it, use `{config.BOT_PREFIX}party edit " f"{result['role'].name}` " f"instead. ") await connection.execute( "INSERT INTO party_alias (party_id, alias) VALUES ($1, $2) ON CONFLICT DO NOTHING ", result["role"].id, result["role"].name.lower(), ) for leader in result["leaders"]: await connection.execute( "INSERT INTO party_leader (party_id, leader_id) VALUES ($1, $2) ON CONFLICT DO NOTHING ", result["role"].id, leader, ) return await PoliticalParty.convert(ctx, result["role"].id) return result