async def uhoh(self, ctx): await ctx.send(utils.fill_message("uhoh_counter", uhohs=uhoh_counter))
async def cog_after_invoke(self, ctx): if ctx.channel.id not in config.allowed_channels: await ctx.message.channel.send( utils.fill_message("bot_room_redirect", user=ctx.message.author.id, bot_room=config.bot_room))
async def diplom_error(self, ctx, error): await ctx.send(utils.fill_message("absolvent_help", command=ctx.invoked_with))
async def bonk_error(self, ctx, error): if isinstance(error, commands.BadArgument): await ctx.send( utils.fill_message("member_not_found", user=ctx.author.id))
async def karma_error(self, ctx, error): if isinstance(error, commands.CheckFailure): await ctx.send( utils.fill_message("insufficient_rights", user=ctx.author.id))
class Absolvent(commands.Cog): def __init__(self, bot): self.bot = bot @commands.command( aliases=["absolvent"], brief=Messages.absolvent_brief, description=utils.fill_message("absolvent_help", command="diplom") ) async def diplom(self, ctx, degree, name, surname, diploma_number, thesis_web_id): """Command for diploma verification and honourable role addition :param ctx: discord context :param name: first name (case-sensitive) :param surname: last name (case-sensitive) :param degree: strictly either "Bc." or "Ing." (case-sensitive) :param diploma_number: ID of diploma, in format NNNNNN/YYYY :param thesis_web_id: ID from URL https://dspace.vutbr.cz/handle/11012/<num> can be discovered via https://dspace.vutbr.cz/handle/11012/19121 """ # prepare htmlparser = etree.HTMLParser() diploma_year = re.search(r"\d+/(\d+)", diploma_number) if not diploma_year: await ctx.send(Messages.absolvent_wrong_diploma_format) return diploma_year = diploma_year.group(1) full_name_without_degree = f"{name} {surname}" full_name_without_degree_surname_first = f"{surname}, {name}" # CHECK WHETHER THE PROVIDED NAME MATCHES THE ONE STORED FOR FIT VUT VERIFICATION def remove_accents(input_str): nfkd_form = unicodedata.normalize("NFKD", input_str) only_ascii = nfkd_form.encode("ASCII", "ignore").decode("ASCII") return only_ascii # get "surname name" for bot database fot the current command caller name_from_db = user_r.get_user_by_id(ctx.author.id).name # remove diacritics from the user-supplied name name_from_user_without_diacritics = remove_accents(f"{surname} {name}") if ctx.guild: await ctx.message.delete() if name_from_db != name_from_user_without_diacritics: await ctx.send(Messages.absolvent_wrong_name) return # CHECK OWNERSHIP, TYPE AND YEAR OF THE QUALIFICATION WORK / THESIS thesis_url = f"https://dspace.vutbr.cz/handle/11012/{thesis_web_id}?locale-attribute=cs" # download the page result_thesis = requests.get(thesis_url) # parse it using lxml xDoc_thesis = etree.fromstring(result_thesis.text, htmlparser) not_found = "".join( xDoc_thesis.xpath('//*[@id="main-container"]//h2/text()') ) master_thesis = "".join( xDoc_thesis.xpath( "/html/body/div[1]/div/div/div/ul[contains(@class,'breadcrumb')]/li[3]/a[.='diplomové práce']/text()" ) ) bachelor_thesis = "".join( xDoc_thesis.xpath( "/html/body/div[1]/div/div/div/ul[contains(@class,'breadcrumb')]/li[3]/a[.='bakalářské práce']/text()" ) ) thesis_author_without_degree_surname_first = "".join( xDoc_thesis.xpath( '//*[@id="aspect_artifactbrowser_ItemViewer_div_item-view"]/div/div[2]/div[1]/div[./h5/b="Autor"]/div/a/text()' ) ) habilitation_date = "".join( xDoc_thesis.xpath( '//*[@id="aspect_artifactbrowser_ItemViewer_div_item-view"]/div/div[2]/div[2]/div[./h5="Termín obhajoby"]/span/text()' ) ) result = "".join( xDoc_thesis.xpath( '//*[@id="aspect_artifactbrowser_ItemViewer_div_item-view"]/div/div[2]/div[2]/div[./h5="Výsledek obhajoby"]/span/text()' ) ) faculty = "".join( xDoc_thesis.xpath( "/html/body/div[1]/div/div/div/ul[contains(@class,'breadcrumb')]/li[4]/a[.='Fakulta informačních technologií']/text()" ) ) #await ctx.send(f""" #DEBUG: #nf: {not_found} #mt: {master_thesis} #bt: {bachelor_thesis} #ta: {thesis_author_without_degree_surname_first} #hd: {habilitation_date} #re: {result} #fa: {faculty} #""") if "Page cannot be found" in not_found: await ctx.send(Messages.absolvent_thesis_not_found_error) return habilitation_year = re.search(r"(\d+)-\d+-\d+", habilitation_date).group(1) if not ( ((degree == "Ing." and master_thesis != "") or (degree == "Bc." and bachelor_thesis != "")) and diploma_year == habilitation_year and faculty != "" and result == "práce byla úspěšně obhájena" and thesis_author_without_degree_surname_first == full_name_without_degree_surname_first ): await ctx.send(Messages.absolvent_web_error) return # DIPLOMA VALIDITY CHECK diplom_url1 = "https://www.vutbr.cz/overeni-diplomu" diplom_url2 = "https://www.vutbr.cz/overeni-diplomu?aid_redir=1" diplom_url3 = "https://www.vutbr.cz/overeni-diplomu" data = {} session = requests.session() # load initial cookies + download the HTML-security codes in hidden input fields session.get(diplom_url1) result2 = session.get(diplom_url2) # parse it using lxml xDoc2 = etree.fromstring(result2.text, htmlparser) # get all hidden server-generated inputs inputs = xDoc2.xpath("//*[@id='over_studenta']/input[@type='hidden']") for input in inputs: data[input.attrib["name"]] = input.attrib["value"] # add user-supplied values data["data[form_el_cislo]"] = diploma_number data["data[form_el_jmeno]"] = name data["data[form_el_prijmeni]"] = surname data["data[form_el_overit]"] = "Ověřit" # send the POST to check the diploma validity result3 = session.post(diplom_url3, data) xDoc3 = etree.fromstring(result3.text, htmlparser) # parse the success text (in czech) absolventText = "".join( xDoc3.xpath( "//*[@id='main']/div[contains(@class,'alert-success')]/div[@class='alert-text']/div//text()" ) ) if not ( absolventText != "" and "úspěšně ověřen" in absolventText and absolventText.endswith(", Fakulta informačních technologií") ): await ctx.send(Messages.absolvent_diploma_error) return guild = self.bot.get_guild(Config.guild_id) role = None if degree == "Bc.": role = discord.utils.get(guild.roles, id=Config.bc_role_id) if degree == "Ing.": role = discord.utils.get(guild.roles, id=Config.ing_role_id) if role: member = guild.get_member(ctx.author.id) for drop_role in member.roles: if "Dropout" in drop_role.name: await member.remove_roles(drop_role, reason="Diploma verification") await member.add_roles(role) await ctx.send(Messages.absolvent_success) @diplom.error async def diplom_error(self, ctx, error): await ctx.send(utils.fill_message("absolvent_help", command=ctx.invoked_with))
async def clone(self, ctx, src: Union[discord.TextChannel, discord.VoiceChannel], name): """Clone channel with same permissions as src.""" new = await src.clone(name=name) await ctx.send(utils.fill_message("channel_clone_done", id=new.id))
async def leaderboard_error(self, ctx, error): if isinstance(error, commands.BadArgument): await ctx.send( utils.fill_message("karma_lederboard_offser_error", user=ctx.author.id))
async def review_error(self, ctx, error): if isinstance(error, commands.BadArgument): await ctx.send(messages.review_add_format) if isinstance(error, commands.CheckFailure): await ctx.send( utils.fill_message("insufficient_rights", user=ctx.author.id))
async def send_code(self, message): if len(str(message.content).split(" ")) != 2: await message.channel.send(Messages.verify_send_format) return # Check if the user doesn't have the verify role if not await self.has_role(message.author, Config.verification_role): login = str(message.content).split(" ")[1] # Some of them will use 'xlogin00' as stated in help, # cuz they dumb if login == "xlogin00": guild = self.bot.get_guild(Config.guild_id) fp = await guild.fetch_emoji(585915845146968093) await message.channel.send( utils.fill_message("verify_send_dumbshit", user=message.author.id, emote=str(fp))) return if login[0] == 'x': # VUT # Check if the login we got is in the database if self.repo.has_unverified_login(login): await self.gen_code_and_send_mail(message, login, "@stud.fit.vutbr.cz") else: await message.channel.send( utils.fill_message("verify_send_not_found", user=message.author.id, admin=Config.admin_id)) embed = discord.Embed(title="Neuspesny pokus o verify", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) else: # MUNI try: int(login) except ValueError: await message.channel.send( utils.fill_message("verify_send_not_found", user=message.author.id, admin=Config.admin_id)) embed = discord.Embed(title="Neuspesny pokus o verify", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) try: await message.delete() return except discord.errors.Forbidden: return if self.repo.get_user(login, status=2) is None and\ self.repo.get_user(login, status=0) is None: if self.repo.get_user(login, status=1) is None: self.repo.add_user(login, "MUNI", status=1) await self.gen_code_and_send_mail(message, login, "@mail.muni.cz") else: await message.channel.send( utils.fill_message("verify_send_not_found", user=message.author.id, admin=Config.admin_id)) embed = discord.Embed(title="Neuspesny pokus o verify", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) else: await message.channel.send( utils.fill_message("verify_already_verified", user=message.author.id, admin=Config.admin_id)) try: await message.delete() except discord.errors.Forbidden: return
async def verify(self, message): """"Verify if VUT login is from database""" if len(str(message.content).split(" ")) != 3: await message.channel.send(Messages.verify_verify_format) return login = str(message.content).split(" ")[1] code = str(message.content).split(" ")[2] # Check if the user doesn't have the verify role # otherwise they wouldn't need to verify, right? if not await self.has_role(message.author, Config.verification_role): # Some of them will use 'xlogin00' as stated in help # yet again, cuz they dumb if login == "xlogin00": guild = self.bot.get_guild(Config.guild_id) fp = await guild.fetch_emoji(585915845146968093) await message.channel.send( utils.fill_message("verify_send_dumbshit", user=message.author.id, emote=str(fp))) return # Same here if code == "kód" or code == "[kód]": guild = self.bot.get_guild(Config.guild_id) fp = await guild.fetch_emoji(585915845146968093) await message.channel.send( utils.fill_message("verify_verify_dumbshit", user=message.author.id, emote=str(fp))) return new_user = self.repo.get_user(login) if new_user is not None: # Check the code if code != new_user.code: await message.channel.send( utils.fill_message("verify_verify_wrong_code", user=message.author.id)) embed = discord.Embed( title="Neuspesny pokus o verify(kod)", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) return # Try and transform the year into the role name year = self.transform_year(new_user.year) if year is None: await message.channel.send( utils.fill_message("verify_verify_manual", user=message.author.id, admin=Config.admin_id, year=str(new_user.year))) embed = discord.Embed( title="Neuspesny pokus o verify(manual)", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) return try: # Get server verify role verify = discord.utils.get(message.guild.roles, name=Config.verification_role) year = discord.utils.get(message.guild.roles, name=year) member = message.author except AttributeError: # jsme v PM guild = self.bot.get_guild(Config.guild_id) verify = discord.utils.get(guild.roles, name=Config.verification_role) year = discord.utils.get(guild.roles, name=year) member = guild.get_member(message.author.id) await member.add_roles(verify) await member.add_roles(year) self.repo.save_verified(login, message.author.id) await member.send( utils.fill_message("verify_verify_success", user=message.author.id)) await member.send(Messages.verify_post_verify_info) if message.channel.type is not discord.ChannelType.private: await message.channel.send( utils.fill_message("verify_verify_success", user=message.author.id)) else: await message.channel.send( utils.fill_message("verify_verify_not_found", user=message.author.id, admin=Config.admin_id)) embed = discord.Embed(title="Neuspesny pokus o verify", color=0xeee657) embed.add_field(name="User", value=utils.generate_mention( message.author.id)) embed.add_field(name="Message", value=message.content, inline=False) channel = self.bot.get_channel(Config.log_channel) await channel.send(embed=embed) else: await message.channel.send( utils.fill_message("verify_already_verified", user=message.author.id, admin=Config.admin_id)) try: await message.delete() except discord.errors.Forbidden: return
async def __repost_message(self, ctx: ReactionContext, reactions: List[disnake.Reaction]): if self.repost_channel is None and config.meme_repost_room != 0: self.repost_channel = await self.bot.fetch_channel( config.meme_repost_room) # Invalid ID if self.repost_channel is None: return async with self.repost_lock: if self.repost_repo.find_repost_by_original_message_id( ctx.message.id) is not None: return # Generate string with all reactions on post at the time title_string = "" for reaction in reactions: if not isinstance(reaction.emoji, str): # Remove all emoji reactions that are not from current server if disnake.utils.get(ctx.guild.emojis, id=reaction.emoji.id) is None: continue tmp_string = title_string + f"{reaction.count}x{reaction.emoji} " if len(tmp_string) >= 255: break title_string = tmp_string embed = disnake.Embed(color=disnake.Color.dark_blue(), title=title_string) utils.add_author_footer(embed, author=ctx.message.author) embed.timestamp = ctx.message.created_at # Create link to original post link = utils.fill_message( "meme_repost_link", original_message_url=ctx.message.jump_url, original_channel=config.meme_room) embed.add_field(name="Link", value=link, inline=False) # Get all attachments of original post main_image = None other_attachments = [] for attachment in ctx.message.attachments: content_type = attachment.content_type if content_type is not None and content_type.split( "/")[0] == "image" and main_image is None: # Set main image if its image and main image is not set main_image = await attachment.to_file() else: # Other attachments convert to file and append to list of attachments attachment_file = await attachment.to_file() if attachment_file is not None: other_attachments.append(attachment_file) # Set content from original message if present if ctx.message.content: content_splits = ctx.message.content.split(" ") for content_split in content_splits: if content_split.startswith("https://"): # Its attachement URL for extension in config.meme_repost_image_extensions: # Check for extension in URL if f".{extension}" in content_split: if main_image is None: main_image = content_split else: other_attachments.append(content_split) break else: other_attachments.append(content_split) content = ctx.message.content[:900] embed.add_field(name="Obsah", value=content) # Set main image if present if main_image is not None: if isinstance(main_image, disnake.File): embed.set_image(url=f"attachment://{main_image.filename}") elif isinstance(main_image, str): embed.set_image(url=main_image) main_image = None else: main_image = None repost_message_id = -1 secondary_message_id = None if len(embed) < 6000: repost_message = await self.repost_channel.send( embed=embed, file=main_image) repost_message_id = repost_message.id if len(other_attachments) > 0: # Files are getting send as files files = [ file for file in other_attachments if isinstance(file, disnake.File) ] files = files[:10] if files else None # And urls as string in separated message urls = [ file for file in other_attachments if isinstance(file, str) ] urls = "\n".join(urls) if urls else None secondary_message = await self.repost_channel.send( urls, files=files) secondary_message_id = secondary_message.id self.repost_repo.create_repost(ctx.message.id, repost_message_id, ctx.member.id, secondary_message_id)
async def reviews(self, ctx, subcommand=None, subject=None, tier: int = None, *args): if subcommand is None: await ctx.send(messages.review_format) else: if isinstance(ctx.message.channel, discord.DMChannel): guild = self.bot.get_guild(config.guild_id) roles = guild.get_member(ctx.message.author.id).roles else: roles = ctx.message.author.roles if subcommand == 'add': for role in roles: if role.name in config.reviews_forbidden_roles: await ctx.send( utils.fill_message("review_add_denied", user=ctx.message.author.id)) return if subject is None or tier is None: await ctx.send(messages.review_add_format) return author = ctx.message.author.id anonym = False if tier < 0 or tier > 4: await ctx.send(messages.review_tier) return if args: if args[0] == "anonym": anonym = True args = args[1:] args = ' '.join(args) args_len = len(args) if args_len == 0: args = None try: self.rev.add_review(author, subject.lower(), tier, anonym, args) except Exception: await ctx.send(messages.review_wrong_subject) return await ctx.send(messages.review_added) elif subcommand == 'remove': if subject is None: if ctx.author.id == config.admin_id: await ctx.send(messages.review_remove_format_admin) else: await ctx.send(messages.review_remove_format) elif subject == 'id': if ctx.author.id == config.admin_id: if tier is None: await ctx.send(messages.review_remove_id_format) else: review_repo.remove(tier) # tier => ID of review await ctx.send(messages.review_remove_success) else: await ctx.send( utils.fill_message("insufficient_rights", user=ctx.author.id)) else: subject = subject.lower() if self.rev.remove(str(ctx.message.author.id), subject): await ctx.send(messages.review_remove_success) else: await ctx.send(messages.review_remove_error) else: subject = subcommand embed = self.rev.list_reviews(subject.lower()) if not embed: await ctx.send(messages.review_wrong_subject) return msg = await ctx.send(embed=embed) footer = msg.embeds[0].footer.text.split('|')[0] if msg.embeds[0].description[-1].isnumeric(): if footer != "Review: 1/1 ": await msg.add_reaction("⏪") await msg.add_reaction("◀") await msg.add_reaction("▶") await msg.add_reaction("👍") await msg.add_reaction("🛑") await msg.add_reaction("👎") if msg.embeds[0].fields[3].name == "Text page": await msg.add_reaction("🔼") await msg.add_reaction("🔽")