async def on_message_edit(self, before, message): # Adds the edited message to the table if message.guild is None: return # gets the char difference between the two messages b_cont = before.content a_cont = message.content before_char_count = len(b_cont) before_word_count = len(b_cont.split(" ")) before_emoji_count = b_cont.count(":") // 2 before_spoiler_count = b_cont.count("||") // 2 after_char_count = len(a_cont) after_word_count = len(a_cont.split(" ")) after_emoji_count = a_cont.count(":") // 2 after_spoiler_count = a_cont.count("||") // 2 SUBJECT_ID = self.get_current_subject() SQLFunctions.update_statistics( message.author, SUBJECT_ID, messages_edited=1, characters_sent=after_char_count - before_char_count, words_sent=after_word_count - before_word_count, emojis_sent=after_emoji_count - before_emoji_count, spoilers_sent=after_spoiler_count - before_spoiler_count)
def __init__(self, bot): self.bot = bot self.send_message_to_finn = False self.lecture_updater_version = "v1.0" self.db_path = "./data/discord.db" self.conn = SQLFunctions.connect() self.channel_to_post = SQLFunctions.get_config( "channel_to_post_updates", self.conn) self.background_loop.start() self.current_activity = "" self.sent_updates = { 1: False, 2: False, 3: False, 4: False, 5: False, 6: False } self.lecture_updates_role_ids = { "first": 885810281358446623, "second": 885810349121622056, "third": 885810401969831978 } self.sent_advent = False self.sent_website_updates = False
async def on_message(self, message): # fights against people trying to ruin my images hehe ;) if message.content.startswith(".place setpixel ") and self.image is not None: cont = message.content.split(" ") try: x = int(cont[2]) y = int(cont[3]) except ValueError: return r, g, b, a = self.image.getpixel((x, y)) if a != 0: color = rgb2hex(r, g, b) if color != cont[4].lower(): channel = self.bot.get_channel(819966095070330950) if channel is None: channel = self.bot.get_channel(402563165247766528) await channel.send(f".place setpixel {x} {y} {color} | COUNTERING {message.author.name}") if message.author.id == self.userToCopyTextFrom and message.channel.id != 813430350965375046 and is_valid_msg(message.content): if len(self.queue) > 50: return pil_img, self.last_line, self.last_char = self.draw_text(message.content, self.last_line, self.last_char) SQLFunctions.insert_or_update_config("Draw_Last_Line", self.last_line, self.conn) SQLFunctions.insert_or_update_config("Draw_Last_Char", self.last_char, self.conn) # id to stop specific draw ID = str(random.randint(1000, 10000)) img = im2q.PixPlace(ID, ID, False, pil_img=pil_img) img.left_to_right() self.handle_image(img, 0, ID)
async def on_message_delete(self, message): if message.guild is None: return SUBJECT_ID = self.get_current_subject() SQLFunctions.update_statistics(message.author, SUBJECT_ID, messages_deleted=1)
async def add_rep(self, message, member, author): """ Adds the reputation to the file """ # Can the user rep yet? author_member = SQLFunctions.get_or_create_discord_member( author, conn=self.conn) if not self.check_valid_time(author_member): return False receiver_member = SQLFunctions.get_or_create_discord_member( member, conn=self.conn) # Format the reputation message msg_list = message.content.split(" ") if len(msg_list) > 2: msg = " ".join(msg_list[2:]) else: return False # Check if the rep is positive if msg.startswith("-"): isPositive = False msg = msg[1:].strip() else: isPositive = True # Add to DB SQLFunctions.add_reputation(author_member, receiver_member, msg, isPositive, self.conn) return True
def __init__(self, bot): self.bot = bot self.cancel_all = False self.cancel_draws = [] self.pause_draws = False self.progress = {} self.image = None self.queue = [] self.background_draw.start() self.db_path = "./data/discord.db" self.place_path = "./place/" self.conn = SQLFunctions.connect() self.LINE_HEIGHT = 62 # amount of lines which fit on the place canvas self.CHAR_WIDTH = 166 # amount of chars which fit in a line on the place canvas self.font = ImageFont.truetype("./data/nk57-monospace-cd-rg.ttf", 12) self.userToCopyTextFrom = -1 self.last_line = SQLFunctions.get_config("Draw_Last_Line", self.conn) if len(self.last_line) == 0: self.last_line = 0 else: self.last_line = self.last_line[0] self.last_char = SQLFunctions.get_config("Draw_Last_Char", self.conn) if len(self.last_char) == 0: self.last_char = 0 else: self.last_char = self.last_char[0]
async def on_reaction_remove(self, reaction, member): if reaction.message.guild is None or member.bot: return if member.id == reaction.message.author.id: return SUBJECT_ID = self.get_current_subject() SQLFunctions.update_statistics(member, SUBJECT_ID, reactions_removed=1) SQLFunctions.update_statistics(reaction.message.author, SUBJECT_ID, reactions_taken_away=1)
def remove_drawing(self, ID): # removes the drawing from the sql table SQLFunctions.delete_config(f"%{ID}", self.conn) # removes it from the queue and progress bar if ID in self.progress: self.progress.pop(ID) self.queue.pop(0) while ID in self.cancel_draws: self.cancel_draws.remove(ID) os.remove(f"{self.place_path}{ID}.npy")
async def add_xp(self, member: discord.Member, amount_min, amount_max): """ Adds xp to a specific user in that guild :param member: :param amount_min: :param amount_max: :return: """ rand_amount = random.randrange(amount_min, amount_max) SQLFunctions.insert_or_update_voice_level(member, rand_amount, self.conn)
async def on_reaction_add(self, reaction, member): if reaction.message.guild is None or member.bot: return if member.id == reaction.message.author.id: return SUBJECT_ID = self.get_current_subject() SQLFunctions.update_statistics( member, SUBJECT_ID, reactions_added=1) # reactions added by the user SQLFunctions.update_statistics( reaction.message.author, SUBJECT_ID, reactions_received=1) # reactions received by the user
async def on_member_join(self, member): # adds the user to the db try: SQLFunctions.get_or_create_discord_member(member, conn=self.conn) except Exception as e: print(e) if member.bot: return # if the server is the main server if member.guild.id == 747752542741725244: channel = self.bot.get_channel(815936830779555841) await self.send_welcome_message(channel, member, member.guild)
def __init__(self, bot): self.bot = bot self.db_path = "./data/discord.db" self.conn = SQLFunctions.connect() # gets the button value to watch self.watch_button_value = SQLFunctions.get_config("ButtonValue", self.conn) if len(self.watch_button_value) == 0: self.watch_button_value = 1e6 else: self.watch_button_value = self.watch_button_value[0] self.sent_message = False self.old_value = 1e6 # to ignore fake buttons we store the last value
def __init__(self, bot): self.bot = bot self.newcomers = {} self.ta_request = {} self.bot_prefix_path = "./data/bot_prefix.json" with open(self.bot_prefix_path, "r") as f: self.all_prefix = json.load(f) self.db_path = "./data/discord.db" self.conn = SQLFunctions.connect() self.welcome_message_id = SQLFunctions.get_config( "WelcomeMessage", self.conn) self.requested_help = [ ] # list of DiscordUserIDs of who requested help self.requested_ta = [ ] # list of DiscordUserIDs which requested TA role to avoid spam
async def on_message(self, message): # only count stats in servers if message.guild is None: return # deletes the message if its in #newcomers if message.channel.id == 815881148307210260 and not message.author.bot: try: await message.delete() deleted_messages = SQLFunctions.get_config( "deleted_messages", self.conn) if len(deleted_messages) == 0: deleted_messages = 0 else: deleted_messages = deleted_messages[0] SQLFunctions.insert_or_update_config("deleted_messages", deleted_messages + 1, self.conn) except discord.NotFound: # message was already deleted pass SUBJECT_ID = self.get_current_subject() # Makes it better to work with the message msg = demojize(message.content) char_count = len(msg) word_count = len(msg.split(" ")) emoji_count = msg.count(":") // 2 spoiler_count = msg.count("||") // 2 # File Statistics files_amount = len(message.attachments) file_sizes = 0 images_amt = 0 for f in message.attachments: file_sizes += f.size if f.height is not None and f.height > 0: images_amt += 1 SQLFunctions.update_statistics(message.author, SUBJECT_ID, conn=self.conn, messages_sent=1, characters_sent=char_count, words_sent=word_count, spoilers_sent=spoiler_count, emojis_sent=emoji_count, files_sent=files_amount, file_size_sent=file_sizes, images_sent=images_amt)
async def background_draw(self): await self.bot.wait_until_ready() # opens and readies all the files imgs = self.get_all_queues(self.place_path) for im in imgs: if im.fp not in self.progress: start = SQLFunctions.get_config(f"Start_{im.fp}", self.conn) if len(start) == 0: start = 0 else: start = start[0] self.progress[im.fp] = { "count": start, "img": im, "queue": im.get_queue() } self.queue.append({ "ID": im.fp, "size": im.size, "img": im, "queue": im.get_queue() }) channelID = SQLFunctions.get_config("PlaceChannel", self.conn) if len(channelID) == 0: channelID = 819966095070330950 else: channelID = channelID[0] channel = self.bot.get_channel(channelID) if channel is None: channel = self.bot.get_channel(402551175272202252) # fallback test channel # keeps going through all lists while len(self.queue) > 0 and not self.pause_draws: drawing = self.queue[0] start = SQLFunctions.get_config(f"Start_{drawing['ID']}", self.conn) end = SQLFunctions.get_config(f"End_{drawing['ID']}", self.conn) if len(start) == 0: start = 0 else: start = start[0] if len(end) == 0: end = drawing["img"].size else: end = end[0] done = await self.draw_pixels(drawing["ID"], channel, start, end) if done: self.remove_drawing(drawing["ID"])
async def perm(self, ctx, command=None): """ Can be used to edit or view permissions for a command. For more information view the `add` subcommand. Permissions: Owner """ if ctx.invoked_subcommand is None: if command is None: await ctx.reply("ERROR! No command to view given.") raise discord.ext.commands.BadArgument if command.lower() not in [com.name.lower() for com in self.bot.commands]: await ctx.reply("ERROR! Command not found. Did you maybe mistype a subcommand?") raise discord.ext.commands.BadArgument command_level = SQLFunctions.get_all_command_levels(command.lower(), self.conn) embed = discord.Embed( description=f"Dynamic permissions for `{command.lower()}`:", color=discord.Color.blue() ) user_msg = "\n".join(f"* {k}: {v}" for k, v in command_level.user_levels.items()) role_msg = "\n".join(f"* {k}: {v}" for k, v in command_level.role_levels.items()) channel_msg = "\n".join(f"* {k}: {v}" for k, v in command_level.channel_levels.items()) guild_msg = "\n".join(f"* {k}: {v}" for k, v in command_level.guild_levels.items()) embed.add_field(name="User", value=f"```md\n{user_msg} ```") embed.add_field(name="Role", value=f"```md\n{role_msg} ```") embed.add_field(name="\u200b", value="\u200b", inline=False) embed.add_field(name="Channel", value=f"```md\n{channel_msg} ```") embed.add_field(name="Guild", value=f"```md\n{guild_msg} ```") await ctx.reply(embed=embed)
def __init__(self, bot): self.bot = bot with open("./data/ignored_users.json") as f: self.ignored_users = json.load(f) self.db_path = "./data/discord.db" self.conn = SQLFunctions.connect() self.time_to_wait = 20 * 3600 # Wait 20 hours before repping again
async def rank(self, ctx, user=None): """ This command sends the users voice XP rank. If no user is defined, the command user's rank is sent. """ if user is None: member = ctx.message.author else: user_id = user.replace("<@", "").replace(">", "").replace("!", "") member = None if user_id.isnumeric(): member = ctx.message.guild.get_member(int(user_id)) if member is None: await ctx.send( f"{ctx.message.author.mention}, invalid mention or user ID. Can't display rank for that user." ) raise ValueError # Query User experience voice_level = SQLFunctions.get_voice_level(member, self.conn) level = levefier(voice_level.experience) pre_level = round(voice_level.experience - xpfier(level)) aft_level = round(xpfier(level + 1) - xpfier(level)) embed = discord.Embed( title="Voice Level", description=f"User: <@!{voice_level.member.DiscordUserID}>\n" f"Current Level: `{level}`\n" f"Level XP: `{pre_level}` / `{aft_level}`\n" f"Total XP: `{number_split(voice_level.experience)}`\n" f"Estimated Hours: `{round(voice_level.experience / 3600, 1)}`", color=0x00FF00) await ctx.send(embed=embed)
def check_valid_time(self, member: SQLFunctions.DiscordMember): result = SQLFunctions.get_most_recent_time(member, self.conn) if result is None: return True time_sent = datetime.datetime.fromisoformat(result) if time.time() - time_sent.timestamp() > self.time_to_wait: return True return False
async def point_distribute(self, confirmed_cases): log(f"Starting COVID points distribution", "COVID") lb_messages = [] rank = 1 guessers = SQLFunctions.get_covid_guessers(self.conn, guessed=True) for g in guessers: g.TempPoints = int(calculate_points(confirmed_cases, g.NextGuess)) # Sort the guessers by their gotten points guessers.sort(key=lambda x: x.TempPoints, reverse=True) for g in guessers: msg = f"**{rank}:** <@{g.member.DiscordUserID}> got {g.TempPoints} points *(guess: {g.NextGuess})*" rank += 1 lb_messages.append(msg) SQLFunctions.clear_covid_guesses(users=guessers, increment=True, conn=self.conn) return lb_messages
def handle_image(self, img: im2q, drawn: int, ID: str): self.progress[ID] = { "count": drawn, "img": img, "queue": img.get_queue() } self.queue.append({ "ID": ID, "size": img.size, "img": img, "queue": img.get_queue() }) SQLFunctions.insert_or_update_config(f"Start_{ID}", 0, self.conn) SQLFunctions.insert_or_update_config(f"End_{ID}", img.size, self.conn) # saves the img as a numpy file so it can easily be reload when the bot restarts img.save_array(f"{self.place_path}{ID}")
async def watch(self, ctx, val=None): await ctx.message.delete() if val is None: # yes if we're tracking, no if we're at default value if self.watch_button_value < 1e6: await ctx.send(f"{self.watch_button_value} | {self.sent_message}", delete_after=5) else: await ctx.send("no", delete_after=5) else: if not val.isnumeric(): await ctx.send("not int", delete_after=5) raise discord.ext.commands.errors.BadArgument # saves the value to watch into config SQLFunctions.insert_or_update_config("ButtonValue", int(val), self.conn) self.watch_button_value = int(val) self.old_value = 1e6 self.sent_message = False await ctx.send("ok", delete_after=5)
def __init__(self, bot): self.bot = bot self.script_start = 0 self.waiting = False self.time_counter = 0 # So statistics dont get saved every few seconds, and instead only every 2 mins self.bot_changed_to_yesterday = {} self.background_git_backup.start() self.sent_file = False self.current_subject = [-1, 0] self.conn = SQLFunctions.connect()
def __init__(self, bot): self.bot = bot self.clap_counter = 0 self.time = 0 self.confirmed_cases = 0 self.confirm_msg = None # Confirmed message self.conn = SQLFunctions.connect() self.time_since_task_start = time.time() self.background_check_cases.start() self.sent_covid = False recent_covid_cases = SQLFunctions.get_config("COVID_Cases", self.conn) recent_covid_day = SQLFunctions.get_config("COVID_Day", self.conn) if len(recent_covid_cases) > 0: self.cases_today = recent_covid_cases[0] else: self.cases_today = 0 if len(recent_covid_day) > 0: self.last_cases_day = recent_covid_day[0] else: self.last_cases_day = 0
def get_current_subject(self, semester=2) -> int: """ Minor cache system to only make a subject query if it's a new minute Returns the current subject ID """ minute = datetime.now().minute if self.current_subject[0] != minute: subject_id = SQLFunctions.get_current_subject_id(semester, conn=self.conn) self.current_subject = [minute, subject_id] return self.current_subject[1]
async def ImagesSent(self, ctx, mx=10): """ See ImagesSent Stats. `amount shown` is the amount of users that \ should be displayed in the leaderboard. Min: 1, Max: 20. """ if mx < 0: mx = 1 elif mx > 20: mx = 20 column = SQLFunctions.get_statistic_rows("ImagesSent", mx, self.conn) embed = await self.get_top_users(single_statistic=column, single_statistic_name="Images Sent") await ctx.send(embed=embed)
async def draw_pixels(self, ID, channel, start, end) -> bool: pixels_queue = self.progress[ID]["queue"][start:end] # draws the pixels while len(pixels_queue) > 0: if self.cancel_all or str(ID) in self.cancel_draws: await channel.send(f"Canceled Project {ID}.") return True if self.pause_draws: return False pix = pixels_queue[0] pX = pix[0] pY = pix[1] pHex = rgb2hex(pix[2], pix[3], pix[4]) try: await channel.send(f".place setpixel {pX} {pY} {pHex} | PROJECT {ID}") self.progress[ID]["count"] += 1 pixels_queue.pop(0) if self.progress[ID]["count"] % 10 == 0: SQLFunctions.insert_or_update_config(f"Start_{ID}", self.progress[ID]["count"], self.conn) except Exception: await asyncio.sleep(5) return True
async def VoteCount(self, ctx, mx=10): """ See only ReactionsTakenAway Stats. `amount shown` is the amount of users that \ should be displayed in the leaderboard. Min: 1, Max: 20. """ if mx < 0: mx = 1 elif mx > 20: mx = 20 column = SQLFunctions.get_statistic_rows("VoteCount", mx, self.conn) embed = await self.get_top_users( single_statistic=column, single_statistic_name="Quote Battles voted on") await ctx.send(embed=embed)
async def globally_handle_permissions(ctx): """ Checks if the command can be used in the channel. It's a blacklist system, so by default all commands can be used. Hierarchy is USER > ROLE > CHANNEL > GUILD. 0 is the default value 1 is allowed -1 is not allowed """ guild_id = 0 role_ids = [] if ctx.message.guild is not None: guild_id = ctx.message.guild.id role_ids = [role.id for role in ctx.message.author.roles] command_name = ctx.command.name if ctx.command.root_parent is not None: command_name = ctx.command.root_parent.name permission_level = SQLFunctions.get_command_level(command_name, ctx.message.author.id, role_ids, ctx.message.channel.id, guild_id, conn) return permission_level != -1
async def statistics(self, ctx, user=None): """ Used to call the statistics page of a user or of the server. The user parameter can be another user or "top" to get the top three users \ of each category. """ if ctx.invoked_subcommand is None: statistic_columns = { "MessagesSent": [], "MessagesDeleted": [], "MessagesEdited": [], "CharactersSent": [], "WordsSent": [], "SpoilersSent": [], "EmojisSent": [], "FilesSent": [], "FileSizeSent": [], "ImagesSent": [], "ReactionsAdded": [], "ReactionsRemoved": [], "ReactionsReceived": [], "ReactionsTakenAway": [], "VoteCount": [] } for key in statistic_columns.keys(): statistic_columns[key] = SQLFunctions.get_statistic_rows( key, 5000, self.conn) if user is None: embed = await self.create_embed(ctx.message.author, statistic_columns) await ctx.send(embed=embed) else: try: memberconverter = discord.ext.commands.MemberConverter() member = await memberconverter.convert(ctx, user) except discord.ext.commands.errors.BadArgument: await ctx.send( "Invalid user. Mention the user for this to work.") raise discord.ext.commands.errors.BadArgument embed = await self.create_embed(member, statistic_columns) await ctx.send(embed=embed)