async def studijni(self, ctx): link = "https://www.fit.vut.cz/fit/room/C109/.cs" htmlparser = etree.HTMLParser() session = requests.session() result = session.get(link) xDoc2 = etree.fromstring(result.text, htmlparser) hours_div = xDoc2.xpath( "//*[b[contains(text(),'Úřední hodiny')]]//following-sibling::div") embed = disnake.Embed(title=Messages.studijni_title, url=link) if hours_div: hours = etree.tostring(hours_div[0], encoding=str, method="text") additional_info = xDoc2.xpath("//main//section/p") if additional_info: info = etree.tostring(additional_info[0], encoding=str, method="text").split(':', 1) if len(info) > 1: embed.add_field(name=info[0], value=info[1], inline=False) else: hours_div = xDoc2.xpath("//main//section") if len(hours_div): hours = ''.join(hours_div[0].itertext()) hours = hours.strip() else: hours = Messages.studijni_web_error embed.add_field(name=Messages.studijni_office_hours, value=hours, inline=False) add_author_footer(embed, ctx.message.author) await ctx.send(embed=embed)
async def shortcut(self, ctx, shortcut=None): """Informations about subject specified by its shorcut""" if not shortcut: await ctx.send(utils.fill_message("shorcut_format", command=ctx.invoked_with)) return programme = review_repo.get_programme(shortcut.upper()) if programme: embed = discord.Embed(title=programme.shortcut, description=programme.name) embed.add_field(name="Link", value=programme.link) else: subject = review_repo.get_subject_details(shortcut.lower()) if not subject: await ctx.send(messages.review_wrong_subject) return embed = discord.Embed(title=subject.shortcut, description=subject.name) if subject.semester == "L": embed.add_field(name="Semestr", value="Letní") else: embed.add_field(name="Semestr", value="Zimní") embed.add_field(name="Typ", value=subject.type) if subject.year: embed.add_field(name="Ročník", value=subject.year) embed.add_field(name="Kredity", value=subject.credits) embed.add_field(name="Ukončení", value=subject.end) embed.add_field(name="Karta předmětu", value=subject.card, inline=False) embed.add_field( name="Statistika úspěšnosti předmětu", value=f"http://fit.nechutny.net/?detail={subject.shortcut}", inline=False, ) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed)
def urban_embeds(self, author, dict): """Generate embeds from dictionary of resposes""" embed_list = [] for idx, item in enumerate(dict["list"]): definition = item["definition"] example = item["example"] if len(definition) > 1024: definition = definition[0:1021] + "`…`" if len(example) > 1024: example = example[0:1021] + "`…`" embed = disnake.Embed( title=item["word"], url=item["permalink"], ) embed.add_field(name="Definition", value=definition, inline=False) if example: embed.add_field(name="Example", value=example, inline=False) embed.add_field( name="Page", value=f"{idx + 1}/{len(dict['list'])}", inline=False, ) utils.add_author_footer(embed, author) embed_list.append(embed) return embed_list
def create_embed_of_link(self, streamlink: StreamLink, author: Union[discord.User, discord.Member], links_count: int, current_pos: int) -> discord.Embed: embed = discord.Embed(color=0xEEE657) embed.set_author(name="Streamlinks") if streamlink.thumbnail_url is not None: embed.set_image(url=streamlink.thumbnail_url) embed.add_field(name="Předmět", value=streamlink.subject.upper(), inline=True) embed.add_field(name="Od", value=streamlink.member_name, inline=True) embed.add_field(name="Datum vydání", value=streamlink.created_at.strftime("%d. %m. %Y"), inline=True) embed.add_field(name="Odkaz", value=f"[{streamlink.link}]({streamlink.link})", inline=False) embed.add_field(name="Popis", value=streamlink.description, inline=False) embed.timestamp = datetime.utcnow() utils.add_author_footer( embed, author, additional_text=[ f"[{streamlink.subject.upper()}] Page: {current_pos} / {links_count}" f" (#{streamlink.id})" ]) return embed
async def shortcut(self, ctx, shortcut=None): """Informations about subject specified by its shorcut""" if not shortcut: await ctx.send( utils.fill_message("shorcut_format", command=ctx.invoked_with)) return programme = review_repo.get_programme(shortcut.upper()) if programme: embed = disnake.Embed(title=programme.shortcut, description=programme.name) embed.add_field(name="Link", value=programme.link) else: subject = review_repo.get_subject_details(shortcut) if not subject: subject = review_repo.get_subject_details(f"TV-{shortcut}") if not subject: await ctx.send(messages.review_wrong_subject) return embed = disnake.Embed(title=subject.shortcut, description=subject.name) if subject.semester == "L": semester_value = "Letní" if subject.semester == "Z": semester_value = "Zimní" else: semester_value = "Zimní, Letní" embed.add_field(name="Semestr", value=semester_value) embed.add_field(name="Typ", value=subject.type) if subject.year: embed.add_field(name="Ročník", value=subject.year) embed.add_field(name="Kredity", value=subject.credits) embed.add_field(name="Ukončení", value=subject.end) if "*" in subject.name: embed.add_field(name="Upozornění", value="Předmět není v tomto roce otevřen", inline=False) if subject.shortcut.startswith("TV-"): embed.add_field( name="Rozvrh předmětu v IS", value= f"https://www.vut.cz/studis/student.phtml?sn=rozvrhy&action=gm_rozvrh_predmetu&operation=rozvrh&predmet_id={subject.card}&fakulta_id=814", inline=False) else: embed.add_field( name="Karta předmětu", value= f"https://www.fit.vut.cz/study/course/{subject.shortcut}/.cs", inline=False) embed.add_field( name="Statistika úspěšnosti předmětu", value=f"http://fit.nechutny.net/?detail={subject.shortcut}", inline=False, ) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed)
async def message_karma(self, channel_out, msg): author = channel_out.author reactions = msg.reactions colour = 0x6d6a69 output = {'-1': [], '1': [], '0': []} karma = 0 for react in reactions: emoji = react.emoji val = self.repo.emoji_value_raw(emoji) if val == 1: output['1'].append(emoji) karma += react.count async for user in react.users(): if user.id == msg.author.id: karma -= 1 break elif val == -1: output['-1'].append(emoji) karma -= react.count async for user in react.users(): if user.id == msg.author.id: karma += 1 break else: output['0'].append(emoji) embed = discord.Embed(title='Karma zprávy') embed.add_field(name="Zpráva", value=msg.jump_url, inline=False) for key in ['1', '-1', '0']: if output[key]: message = "" for emoji in output[key]: message += str(emoji) + ' ' if key == '1': name = 'Pozitivní' elif key == '0': name = 'Neutrální' else: name = 'Negativní' embed.add_field(name=name, value=message, inline=False) if karma > 0: colour = 0x34cb0b elif karma < 0: colour = 0xcb410b embed.colour = colour embed.add_field(name='Celková karma za zprávu:', value=karma, inline=False) utils.add_author_footer(embed, author) await channel_out.send(embed=embed)
async def weather(self, ctx, *, place: str = "Brno"): token = config.weather_token place = place[:100] if "&" in place: return await ctx.send("Takhle se žádné město určitě nejmenuje.") url = ("http://api.openweathermap.org/data/2.5/weather?q=" + place + "&units=metric&lang=cz&appid=" + token) res = requests.get(url).json() if str(res["cod"]) == "200": description = "Aktuální počasí v městě " + res[ "name"] + ", " + res["sys"]["country"] embed = discord.Embed(title="Počasí", description=description) image = "http://openweathermap.org/img/w/" + res["weather"][0][ "icon"] + ".png" embed.set_thumbnail(url=image) weather = res["weather"][0]["main"] + " ( " + res["weather"][0][ "description"] + " ) " temp = str(res["main"]["temp"]) + "°C" feels_temp = str(res["main"]["feels_like"]) + "°C" humidity = str(res["main"]["humidity"]) + "%" wind = str(res["wind"]["speed"]) + "m/s" clouds = str(res["clouds"]["all"]) + "%" visibility = str( res["visibility"] / 1000) + " km" if "visibility" in res else "bez dat" embed.add_field(name="Počasí", value=weather, inline=False) embed.add_field(name="Teplota", value=temp, inline=True) embed.add_field(name="Pocitová teplota", value=feels_temp, inline=True) embed.add_field(name="Vlhkost", value=humidity, inline=True) embed.add_field(name="Vítr", value=wind, inline=True) embed.add_field(name="Oblačnost", value=clouds, inline=True) embed.add_field(name="Viditelnost", value=visibility, inline=True) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed) elif str(res["cod"]) == "404": await ctx.send("Město nenalezeno") elif str(res["cod"]) == "401": await ctx.send("Rip token -> Rebel pls fix") else: await ctx.send( "Město nenalezeno! <:pepeGun:484470874246742018> (" + res["message"] + ")")
async def week(self, inter: disnake.ApplicationCommandInteraction): """See if the current week is odd or even""" cal_week = date.today().isocalendar()[1] stud_week = cal_week - config.starting_week even, odd = "sudý", "lichý" cal_type = even if cal_week % 2 == 0 else odd embed = disnake.Embed(title="Týden", color=0xE5DC37) embed.add_field(name="Studijní", value=stud_week) embed.add_field(name="Kalendářní", value=f"{cal_type} ({cal_week})") embed.add_field(name="Poznámka", value=Messages.week_warning, inline=False) utils.add_author_footer(embed, inter.author) await inter.response.send_message(embed=embed)
async def week(self, ctx: commands.Context): """See if the current week is odd or even""" cal_week = date.today().isocalendar()[1] stud_week = cal_week - config.starting_week even, odd = "sudý", "lichý" cal_type = even if cal_week % 2 == 0 else odd embed = discord.Embed(title="Týden", color=0xE5DC37) embed.add_field(name="Studijní", value=stud_week) embed.add_field(name="Kalendářní", value=f"{cal_type} ({cal_week})") embed.add_field(name="Poznámka", value=messages.week_warning, inline=False) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed)
async def week(self, ctx: commands.Context): """See if the current week is odd or even""" cal_week = date.today().isocalendar()[1] stud_week = cal_week - config.starting_week even, odd = "sudý", "lichý" cal_type = even if cal_week % 2 == 0 else odd stud_type = even if stud_week % 2 == 0 else odd embed = discord.Embed(title="Týden", color=0xE5DC37) embed.add_field(name="Studijní", value="{} ({})".format(stud_type, stud_week)) embed.add_field(name="Kalendářní", value="{} ({})".format(cal_type, cal_week)) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed)
async def tierboard(self, ctx, type="V", sem="Z", year=""): """Board of suject based on average tier from reviews""" # TODO autochange sem based on week command? degree = None type = type.upper() if type == "HELP": await ctx.send(messages.tierboard_help) return sem = sem.upper() for role in ctx.author.roles: if "BIT" in role.name: degree = "BIT" if not year and type == "P": if role.name == "4BIT+": year = "3BIT" elif role.name == "0BIT": year = "1BIT" else: year = role.name break if "MIT" in role.name: degree = "MIT" if not year and type == "P": year = "" # TODO get programme from DB? or find all MIT P? break if not degree and not year: await ctx.send(messages.tierboard_missing_year) return board = review_repo.get_tierboard(type, sem, degree, year) output = "" cnt = 1 for line in board: output += f"{cnt} - **{line.shortcut}**: {round(line.avg_tier, 1)}\n" cnt += 1 embed = discord.Embed(title="Tierboard", description=output) embed.timestamp = datetime.datetime.now(tz=datetime.timezone.utc) embed.add_field(name="Semester", value=sem) embed.add_field(name="Typ", value=type) if year: degree = year embed.add_field(name="Program", value=degree) utils.add_author_footer(embed, ctx.author, additional_text=("?tierboard help",)) await ctx.send(embed=embed)
async def format_page(self, menu, page: DatabasePage) -> Union[str, disnake.Embed, dict]: board_lines = [] for i, entry in enumerate(page): # type: int, Table board_lines.append(await self._format_row( entry=entry, position=(self.current_page * self.per_page) + i + 1, ctx=menu.ctx )) self.base_embed.description = "\n" + "\n".join(board_lines) # possibility to optimize, author could be set only once utils.add_author_footer( self.base_embed, menu.ctx.author, additional_text=(f"{self.current_page + 1}/{self.get_max_pages()} pages.",), ) return self.base_embed
async def leaderboard(self, ctx: discord.ext.commands.Context, action, order, start=1): if action == 'give': if order == "DESC": column = 'positive' attribute = Database_karma.positive.desc() emote = "<:peepolove:562305740132450359>" title = emote + "KARMA GIVINGBOARD " + emote else: column = 'negative' attribute = Database_karma.negative.desc() emote = "<:ishagrin:638277508651024394>" title = emote + " KARMA ISHABOARD " + emote elif action == 'get': column = 'karma' if order == "DESC": attribute = Database_karma.karma.desc() emote = ":trophy:" title = emote + " KARMA LEADERBOARD " + emote else: attribute = Database_karma.karma emote = "<:coolStoryArcasCZ:489539455271829514>" title = emote + " KARMA BAJKARBOARD " + emote else: raise Exception('Action neni get/give') output = self.gen_leaderboard_content(attribute, start, column) embed = discord.Embed(title=title, description=output) utils.add_author_footer(embed, ctx.author) if action == "get" and order == "DESC": value_num = math.ceil(start / cfg.karma_grillbot_leaderboard_size) value = msg.karma_web if value_num == 1 else f"{msg.karma_web}{value_num}" embed.add_field(name=msg.karma_web_title, value=value) message = await ctx.send(embed=embed) await message.add_reaction("⏪") await message.add_reaction("◀") await message.add_reaction("▶")
def make_embed(self, msg_author, review, subject, description, page): """Create new embed for reviews""" embed = discord.Embed(title=f"{subject.upper()} reviews", description=description) colour = 0x6D6A69 id = 0 if review: id = review.id if review.anonym: author = "Anonym" else: guild = self.bot.get_guild(config.guild_id) author = guild.get_member(int(review.member_ID)) embed.add_field(name="Author", value=author) embed.add_field(name="Tier", value=review.tier) embed.add_field(name="Date", value=review.date) text = review.text_review if text is not None: text_len = len(text) if text_len > 1024: pages = text_len // 1024 + (text_len % 1024 > 0) text = text[:1024] embed.add_field(name="Text page", value=f"1/{pages}", inline=False) embed.add_field(name="Text", value=text, inline=False) likes = review_repo.get_votes_count(review.id, True) embed.add_field(name="Likes", value=f"👍{likes}") dislikes = review_repo.get_votes_count(review.id, False) embed.add_field(name="Dislikes", value=f"👎{dislikes}") diff = likes - dislikes if diff > 0: colour = 0x34CB0B elif diff < 0: colour = 0xCB410B embed.add_field(name="Help", value=messages.reviews_reaction_help, inline=False) embed.colour = colour utils.add_author_footer(embed, msg_author, additional_text=[f"Review: {page} | ID: {id}"]) return embed
async def room(self, ctx: commands.Context, *, room: str): url = f"https://www.fit.vut.cz/fit/map/.cs?show={room.upper()}&big=1" r = requests.get(url) if r.status_code != 200: return await ctx.send(Messages.fit_room_unreach) async with ctx.typing(): try: soup = BeautifulSoup(r.content, 'html.parser') main_body = soup.find("main", {"id": "main"}) floor_list = main_body.find("ul", {"class": "pagination__list"}) active_floor = floor_list.find("a", {"aria-current": "page"}) image = main_body.find("svg") overlay = image.find("g", {"id": "layer3"}) cursor = overlay.find( "polygon", {"style": "fill:red;stroke:none;pointer-events:none"}) except: return await ctx.send(Messages.fit_room_parsing_failed) if image is None or cursor is None: return await ctx.send( utils.fill_message("fit_room_room_not_on_plan", room=room)) image_bytes = BytesIO() svg2png(bytestring=str(image).encode("utf-8"), write_to=image_bytes, parent_width=720, parent_height=1000, background_color="white", dpi=300) image_bytes.seek(0) embed = discord.Embed(title=f"Místnost: {room}", color=discord.Color.dark_blue()) embed.set_image(url="attachment://plan.png") embed.description = f"[Odkaz na plánek]({url})" utils.add_author_footer(embed, ctx.author, additional_text=[str(active_floor.text)]) file = discord.File(fp=image_bytes, filename="plan.png") await ctx.send(embed=embed, file=file)
async def hugs(self, ctx: commands.Context, user: discord.Member = None): """ Get your lovely hug stats. """ if user is None or user == ctx.author: user = ctx.author user_str = utils.get_username(user) title = "{0} Your Lovely Hug Stats {0}" else: user_str = utils.get_username(user) title = f"{{0}} {user_str}'s Lovely Hug Stats {{0}}" async with ctx.typing(): stats = self.hugs_repo.get_members_stats(user.id) positions = self.hugs_repo.get_member_position(stats) avg_position = int((positions[0] + positions[1]) // 2) embed = discord.Embed( title=title.format( self.get_default_emoji("peepoHugger") or "" ), description=" | ".join( ( "**Ranks**", f"Given: **{positions[0]}.**", f"Received: **{positions[1]}.**", f"Avg: **{avg_position}.**", ) ), ) embed.set_author(name=user_str, icon_url=user.avatar_url) utils.add_author_footer(embed, ctx.author) given_emoji = self.get_default_emoji("peepohugs") or "" recv_emoji = self.get_default_emoji("huggers") or "" embed.add_field(name=f"{given_emoji} Given", value=str(stats.given)) embed.add_field(name=f"{recv_emoji} Received", value=str(stats.received)) await ctx.send(embed=embed) await self.check.botroom_check(ctx.message)
async def process_exams(self, ctx: commands.Context, year: Union[str, None]): date = datetime.date.today() semester = "ZS" if 3 < date.month < 9: semester = "LS" cal_year = date.year if semester == "ZS": cal_year -= 1 all_url = f"https://minerva3.fit.vutbr.cz/rozvrhis/{semester}{cal_year}/zkousky" year_url = f"{all_url}/{year}" description = f"[Odkaz na zkoušky ročníku]({year_url})\n" if year else "" description += f"[Odkaz na všechny zkoušky]({all_url})" title = f"Zkoušky {year} {semester}{cal_year}-{cal_year + 1}" \ if year else f"Zkoušky {semester}{cal_year}-{cal_year + 1}" r = requests.get(year_url) if r.status_code == 200: soup = BeautifulSoup(r.content, 'html.parser') try: table = soup.find("table", {"class": "exam"}) body = table.find("tbody") if body is None: # There is no table so no terms embed = discord.Embed(title=title, description=description, color=discord.Color.dark_blue()) utils.add_author_footer(embed, ctx.author) return await ctx.send(embed=embed) exams = body.find_all("tr") number_of_exams = len(exams) bs = config.exams_page_size number_of_batches = math.ceil(number_of_exams / bs) exam_batches = [ exams[i * bs:bs + i * bs] for i in range(number_of_batches) ] pages = [] for exam_batch in exam_batches: embed = discord.Embed(title=title, description=description, color=discord.Color.dark_blue()) utils.add_author_footer(embed, ctx.author) for exam in exam_batch: tag = exam.find("a") cols = exam.find_all("td") del cols[0] subject_tag = tag.find("strong").contents[0] col_count = len(cols) if col_count == 1: # Support for credits col = cols[0] strong_tag = col.find("strong") if strong_tag is None: # There is no term - Only text embed.add_field(name=subject_tag, value=col.contents[0], inline=False) else: term = str(strong_tag.contents[0]).replace( " ", "") date_splits = term.split(".") term_date = datetime.date( int(date_splits[2]), int(date_splits[1]), int(date_splits[0])) name = f"{subject_tag}" term_time = f"{col.contents[0]}\n{term}" if term_date < datetime.date.today(): name = f"~~{name}~~" term_time = f"~~{term_time}~~" embed.add_field(name=name, value=term_time, inline=False) else: # Classic terms whole_term_count = 0 for idx, col in enumerate(cols): terms = col.find_all("strong") times = col.find_all("em") number_of_terms = len(terms) whole_term_count += number_of_terms for idx2, (term, time) in enumerate( zip(terms, times)): term = str(term.contents[0]).replace( " ", "") time_cont = "" for c in time.contents: time_cont += str(c) time_cont = time_cont.replace( "<sup>", ":").replace("</sup>", "") date_splits = term.split(".") term_date = datetime.date( int(date_splits[2]), int(date_splits[1]), int(date_splits[0])) name = f"{idx + 1}. {subject_tag}" if number_of_terms == 1 else \ f"{idx + 1}.{idx2 + 1} {subject_tag}" term_time = f"{term}\n{time_cont}" if term_date < datetime.date.today(): name = f"~~{name}~~" term_time = f"~~{term_time}~~" embed.add_field(name=name, value=term_time) to_add = math.ceil( whole_term_count / 3) * 3 - whole_term_count for _ in range(to_add): embed.add_field(name='\u200b', value='\u200b') pages.append(embed) number_of_pages = len(pages) if number_of_pages > 1: page_sesstion = PaginatorSession( self.bot, ctx, timeout=config.exams_paginator_duration, pages=pages, color=discord.Color.dark_blue(), delete_after=False) await page_sesstion.run() elif number_of_pages == 1: # Only one page, no need paginator await ctx.send(embed=pages[0]) else: # No pages were parsed, so we will post only default embed embed = discord.Embed(title=title, description=description, color=discord.Color.dark_blue()) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed) except: # Parsing failed embed = discord.Embed(title=title, description=description, color=discord.Color.dark_blue()) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed) await ctx.send(Messages.exams_parsing_failed) else: # Site returned fail code embed = discord.Embed(title=title, description=description, color=discord.Color.dark_blue()) utils.add_author_footer(embed, ctx.author) await ctx.send(embed=embed)
async def process_exams( self, ctx: Union[commands.Context, disnake.TextChannel, disnake.Message], year: Union[str, None], author: Optional[disnake.User] = None, ): date = datetime.date.today() semester = "ZS" if 3 < date.month < 9: semester = "LS" cal_year = date.year if date.month < 9: cal_year -= 1 all_url = f"https://minerva3.fit.vutbr.cz/rozvrhis/{semester}{cal_year}/zkousky" year_url = f"{all_url}/{year}" description = f"[Odkaz na zkoušky ročníku]({year_url})\n" if year else "" description += f"[Odkaz na všechny zkoušky]({all_url})" title = (f"Zkoušky {year} {semester}{cal_year}-{cal_year + 1}" if year else f"Zkoušky {semester}{cal_year}-{cal_year + 1}") r = requests.get(year_url) if r.status_code == 200: soup = BeautifulSoup(r.content, "html.parser") table = soup.find("table", {"class": "exam"}) body = table.find("tbody") if body is None: # There is no table so no terms embed = disnake.Embed(title=title, description=description, color=disnake.Color.dark_blue()) utils.add_author_footer( embed, author if author is not None else self.bot.user) if isinstance(ctx, commands.Context): return await ctx.send(embed=embed) # There is body so start parsing table exams = body.find_all("tr") number_of_exams = len(exams) bs = config.exams_page_size number_of_batches = math.ceil(number_of_exams / bs) exam_batches = [ exams[i * bs:bs + i * bs] for i in range(number_of_batches) ] term_strings_dict = {} pages = [] for exam_batch in exam_batches: embed = disnake.Embed(title=title, description=description, color=disnake.Color.dark_blue()) utils.add_author_footer( embed, author if author is not None else self.bot.user) for exam in exam_batch: # Every exams row start with link tag tag = exam.find("a") if str(exam).startswith( "<tr><td><a") else None cols = exam.find_all("td") # Check if tag is not None and get strong and normal subject tag subject_tag = (tag.find("strong") or tag.contents[0] ) if tag is not None else None if subject_tag is None: content = re.sub(CLEANR, "", str(cols[0])) embed.add_field(name="Poznámka", value=content, inline=False) else: del cols[0] if not isinstance(subject_tag, NavigableString): subject_tag = subject_tag.contents[0] col_count = len(cols) if col_count == 1: # Support for credits col = cols[0] strong_tag = col.find("strong") if strong_tag is None: # There is no term - Only text embed.add_field(name=subject_tag, value=col.contents[0], inline=False) else: # Mainly for terms without specified time term_date_string = strong_tag.contents[ 0].replace("\xa0", "").replace(" ", "") date_splits = term_date_string.split(".") # Without actual time set time to end of the day term_datetime = datetime.datetime( int(date_splits[2]), int(date_splits[1]), int(date_splits[0]), 23, 59) term_date = datetime.date( int(date_splits[2]), int(date_splits[1]), int(date_splits[0])) term_content = f"{term_date_string}\n{col.contents[0]}" # Calculate character offsets padded_term_date = datetime.date.strftime( term_date, "%d.%m.%Y") date_offset = " " * (DATE_OFFSET - len(subject_tag)) time_offset = " " * ( TIME_OFFSET - len(padded_term_date) ) # Here used aas data offset term_string = f"{subject_tag}{date_offset}{padded_term_date}" term_string += f"{time_offset}{col.contents[0]}" if term_date == datetime.date.today(): term_strings_dict[ term_datetime] = f"- {term_string}" elif term_datetime < datetime.datetime.now(): subject_tag = f"~~{subject_tag}~~" term_content = f"~~{term_content}~~" else: term_strings_dict[ term_datetime] = f"+ {term_string}" embed.add_field(name=subject_tag, value=term_content, inline=False) else: # Classic terms whole_term_count = 0 for idx, col in enumerate(cols): terms = col.find_all("strong") times = col.find_all("em") number_of_terms = len(terms) whole_term_count += number_of_terms for idx2, (term_date_string, time) in enumerate(zip( terms, times)): term_date_string = ( term_date_string.contents[0].replace( "\xa0", "").replace(" ", "")) term_time_string = "" for c in time.contents: term_time_string += str(c) term_time_string = term_time_string.replace( "<sup>", ":").replace("</sup>", "") date_splits = term_date_string.split(".") start_time_string_parts = ( term_time_string.split("-")[0].replace( " ", "").split(":")) end_time_string_parts = ( term_time_string.split("-")[1].replace( " ", "").split(":")) term_datetime = datetime.datetime( int(date_splits[2]), int(date_splits[1]), int(date_splits[0]), int(start_time_string_parts[0]), int(start_time_string_parts[1]), ) term_date = datetime.date( int(date_splits[2]), int(date_splits[1]), int(date_splits[0])) name = ( f"{idx + 1}. {subject_tag}" if number_of_terms == 1 else f"{idx + 1}.{idx2 + 1} {subject_tag}") if (start_time_string_parts is not None and end_time_string_parts is not None): start_time = datetime.time( int(start_time_string_parts[0]), int(start_time_string_parts[1])) start_time_string = datetime.time.strftime( start_time, "%H:%M") end_time = datetime.time( int(end_time_string_parts[0]), int(end_time_string_parts[1])) end_time_string = datetime.time.strftime( end_time, "%H:%M") term_time_string = f"{start_time_string} - {end_time_string}" padded_term_date = datetime.date.strftime( term_date, "%d.%m.%Y") term_date_time_string = f"{padded_term_date} {term_time_string}" # Calculate character offsets date_offset = " " * (DATE_OFFSET - len(name)) time_offset = " " * (TIME_OFFSET - len(padded_term_date)) term_string = f"{name}{date_offset}{padded_term_date}" term_string += f"{time_offset}{term_time_string}" if term_date == datetime.date.today(): term_strings_dict[ term_datetime] = f"- {term_string}" elif term_datetime < datetime.datetime.now( ): name = f"~~{name}~~" term_date_time_string = f"~~{term_date_time_string}~~" else: term_strings_dict[ term_datetime] = f"+ {term_string}" embed.add_field( name=name, value=term_date_time_string) to_add = math.ceil( whole_term_count / 3) * 3 - whole_term_count for _ in range(to_add): embed.add_field(name="\u200b", value="\u200b") pages.append(embed) if len(pages) == 0: embed = disnake.Embed(title=title, description=description, color=disnake.Color.dark_blue()) utils.add_author_footer( embed, author if author is not None else self.bot.user) pages.append(embed) if isinstance(ctx, commands.Context): view = EmbedView(pages) view.message = await ctx.reply(embed=pages[0], view=view) else: header = disnake.Embed(title=title, description=description, color=disnake.Color.dark_blue()) await self.handle_exams_with_database_access( term_strings_dict, header, ctx) else: # Site returned fail code embed = disnake.Embed(title=title, description=description, color=disnake.Color.dark_blue()) utils.add_author_footer( embed, author if author is not None else self.bot.user) if isinstance(ctx, commands.Context): await ctx.send(embed=embed)
async def tierboard( self, inter: disnake.ApplicationCommandInteraction, type: str = commands.Param(name='typ', choices=['P', 'PVT', 'PVA', 'V']), sem: str = commands.Param(name='semestr', choices=['Z', 'L']), year: str = commands.Param( name='rocnik', choices=["1BIT", "2BIT", "3BIT", "1MIT", "2MIT"], default='' ) ): """Board of suject based on average tier from reviews""" degree = None author = inter.author if not inter.guild: # DM guild = self.bot.get_guild(config.guild_id) author = guild.get_member(author.id) if not year: for role in author.roles: if any(deg in role.name for deg in ["BIT", "MIT"]): if role.name == "4BIT+": year = "3BIT" elif role.name == "0BIT": year = "1BIT" elif role.name == "0MIT": year = "1MIT" elif role.name == "3MIT+": year = "2MIT" else: year = role.name break if "BIT" in year: degree = "BIT" if "MIT" in year: degree = "MIT" if not degree and not year: await inter.send(Messages.tierboard_missing_year, ephemeral=True) return embeds = [] embed = disnake.Embed(title="Tierboard") embed.timestamp = datetime.datetime.now(tz=datetime.timezone.utc) embed.add_field(name="Typ", value=type) embed.add_field(name="Semestr", value="Letní" if sem == "L" else "Zimní") if type != "P": embed.add_field(name="Program", value=degree) year = "" else: embed.add_field(name="Ročník", value=year) utils.add_author_footer(embed, author) pages_total = self.repo.get_tierboard_page_count(type, sem, degree, year) for page in range(pages_total): board = self.repo.get_tierboard(type, sem, degree, year, page*10) output = "" cnt = 1 for line in board: output += f"{cnt} - **{line.shortcut}**: {round(line.avg_tier, 1)}\n" cnt += 1 embed.description = output embeds.append(copy.copy(embed)) if pages_total == 0: embed.description = "" embeds.append(embed) view = EmbedView(embeds) view.message = await inter.response.send_message(embed=embeds[0], view=view)
async def __repost_message(self, ctx: ReactionContext, reactions: List[discord.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: tmp_string = title_string + f"{reaction.count}x{reaction.emoji} " if len(tmp_string) >= 255: break title_string = tmp_string embed = discord.Embed(color=discord.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 more_images = False attachment_urls = [] for attachment in ctx.message.attachments: content_type = attachment.content_type if content_type is not None and content_type.split( "/")[0] == "image": if main_image is None: main_image = await attachment.to_file() else: more_images = True else: if len(attachment.proxy_url) < 1023: attachment_urls.append(attachment.proxy_url) # 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: more_images = True break content = ctx.message.content[:900] if more_images: content += "\n\nVíce obrázků v původním postu" embed.add_field(name="Obsah", value=content) elif more_images: embed.add_field(name="Obsah", value="Více obrázků v původním postu") # Set main image if present if main_image is not None: if isinstance(main_image, discord.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 # Add all attachments as fields for idx, attachment_url in enumerate(attachment_urls): embed.add_field(name=f"Příloha {idx + 1}", value=attachment_url, inline=False) repost_message_id = -1 if len(embed) < 6000: repost_message = await self.repost_channel.send( embed=embed, file=main_image) repost_message_id = repost_message.id self.repost_repo.create_repost(ctx.message.id, repost_message_id, ctx.member.id)
async def tierboard(self, ctx, type="V", sem="Z", year=""): """Board of suject based on average tier from reviews""" # TODO autochange sem based on week command? degree = None type = type.upper() sem = sem.upper() if type == "HELP" or sem not in ['Z', 'L'] or type not in ['P', 'PVT', 'PVA', 'V']: await ctx.send(f"`{utils.get_command_signature(ctx)}`\n{messages.tierboard_help}") return for role in ctx.author.roles: if "BIT" in role.name: degree = "BIT" if not year and type == "P": if role.name == "4BIT+": year = "3BIT" elif role.name == "0BIT": year = "1BIT" else: year = role.name break if "MIT" in role.name: degree = "MIT" if not year and type == "P": year = "" # TODO get programme from DB? or find all MIT P? break if not degree and not year: await ctx.send(messages.tierboard_missing_year) return board = review_repo.get_tierboard(type, sem, degree, year) output = "" cnt = 1 for line in board: output += f"{cnt} - **{line.shortcut}**: {round(line.avg_tier, 1)}\n" cnt += 1 embed = discord.Embed(title="Tierboard", description=output) embed.timestamp = datetime.datetime.now(tz=datetime.timezone.utc) embed.add_field(name="Typ", value=type) embed.add_field(name="Semestr", value="Letní" if sem == "L" else "Zimní") if year: degree = year embed.add_field(name="Program", value=degree) utils.add_author_footer(embed, ctx.author, additional_text=("?tierboard help",)) msg = await ctx.send(embed=embed) page_num = 0 pages_total = review_repo.get_tierboard_page_count(type, sem, degree, year) if pages_total == 0: return await msg.add_reaction("⏪") await msg.add_reaction("◀") await msg.add_reaction("▶") while True: def check(reaction, user): return reaction.message.id == msg.id and not user.bot try: reaction, user = await self.bot.wait_for("reaction_add", check=check, timeout=300.0) except asyncio.TimeoutError: return emoji = str(reaction.emoji) if emoji in ["⏪", "◀", "▶"] and user.id == ctx.author.id: if emoji == "⏪": page_num = 0 elif emoji == "◀": page_num -= 1 if page_num < 0: page_num = pages_total - 1 elif emoji == "▶": page_num += 1 if page_num >= pages_total: page_num = 0 offset = page_num * 10 board = review_repo.get_tierboard(type, sem, degree, year, offset) output = "" cnt = 1 + offset for line in board: output += f"{cnt} - **{line.shortcut}**: {round(line.avg_tier, 1)}\n" cnt += 1 embed.description = output await msg.edit(embed=embed) try: await msg.remove_reaction(emoji, user) except discord.errors.Forbidden: pass
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)