diff --git a/.env.example b/.env.example index d1d539d..609c495 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,10 @@ +# Important (rquired) variables: TOKEN = +MONGO_DB_URI = +# Optional variables for additional functionality: SPOTIFY_CLIENT_ID = SPOTIFY_CLIENT_SECRET = + + diff --git a/cogs/developer.py b/cogs/developer.py index 6c9f697..e8ea034 100644 --- a/cogs/developer.py +++ b/cogs/developer.py @@ -1,4 +1,5 @@ from __future__ import annotations +from email import message from io import BytesIO import os @@ -18,6 +19,8 @@ class Developer(commands.Cog, command_attrs=dict(hidden=True)): hidden = True def __init__(self, bot: CodingBot) -> None: self.bot = bot + self.metrics = bot.database.metrics + self.thanks = bot.database.thanks @commands.command(name="sync") @commands.is_owner() @@ -153,46 +156,52 @@ async def _reloadall(self, ctx: commands.Context[CodingBot]): @commands.command(name='getusermetric', aliases=['gum'], hidden=True) @commands.is_owner() async def _getusermetric(self, ctx: commands.Context[CodingBot], member: discord.Member): - record = await self.bot.conn.select_record( - 'thanks', - table='thanks_info', - arguments=['thanks_count'], - where=['guild_id', 'user_id'], - values=[ctx.guild.id, member.id] - ) - total_thank_count = record[0].thanks_count if record else 0 - revoked_thank_count = await self.bot.conn.select_record( - 'thanks', - table='thanks_data', - arguments=['count(*)'], - where=['guild_id', 'user_id', 'thank_revoked'], - values=[ctx.guild.id, member.id, 1] - ) - revoked_thank_count = revoked_thank_count[0].count - surviving_thank_count = total_thank_count - revoked_thank_count - - message_metric = await self.bot.conn.select_record( - 'metrics', - table='message_metric', - arguments=['user_id', 'guild_id', 'message_count', 'deleted_message_count', 'offline', 'online', 'dnd', 'idle'], - where=['user_id', 'guild_id'], - values=[member.id, ctx.guild.id] + + record = await self.metrics.message_metric.find_one({'u_id': member.id, 'g_id': ctx.guild.id}) + + thank_data = await self.thanks.thank_data.find_one({'u_id': member.id, 'g_id': ctx.guild.id}) + total_thank_count = thank_data['thanks_count'] if thank_data else 0 + revoked_thank_count = await self.thanks.thank_data.count_documents( + {'u_id': member.id, 'g_id': ctx.guild.id, 'revoked': True} ) - record = message_metric[0] if message_metric else None - if record: + + surviving_thank_count = total_thank_count - revoked_thank_count + + message_metric = record + if message_metric: + message_count = message_metric.get('message_count', 0) + + deleted_message_count = message_metric.get('deleted_message_count', 0) + deleted_message_count_percent = deleted_message_count / message_count * 100 + + actual_message_count = message_count - deleted_message_count + actual_message_count_percent = actual_message_count / message_count * 100 + + offline_message_count = message_metric.get('offline', 0) + offline_message_count_percent = offline_message_count / message_count * 100 + + online_message_count = message_metric.get('online', 0) + online_message_count_percent = online_message_count / message_count * 100 + + dnd_message_count = message_metric.get('dnd', 0) + dnd_message_count_percent = dnd_message_count / message_count * 100 + + idle_message_count = message_metric.get('idle', 0) + idle_message_count_percent = idle_message_count / message_count * 100 + formatted_message = f""" **__Message metrics__** For {member.mention}: - \u3164 • **__Total message count__**: {record.message_count} - \u3164 • **__Deleted message count__**: {record.deleted_message_count} (`{record.deleted_message_count / record.message_count * 100:.2f}%`) - \u3164 • **__Actual message count__**: {record.message_count - record.deleted_message_count} (`{(record.message_count - record.deleted_message_count) / record.message_count * 100:.2f}%`) - \u3164 • **__Offline message count__**: {record.offline} (`{record.offline / record.message_count * 100:.2f}%`) - \u3164 • **__Online message count__**: {record.online} (`{record.online / record.message_count * 100:.2f}%`) - \u3164 • **__Dnd message count__**: {record.dnd} (`{record.dnd / record.message_count * 100:.2f}%`) - \u3164 • **__Idle message count__**: {record.idle} (`{record.idle / record.message_count * 100:.2f}%`) + \u3164 • **__Total message count__**: {message_count} + \u3164 • **__Deleted message count__**: {deleted_message_count} (`{deleted_message_count_percent:.2f}%`) + \u3164 • **__Actual message count__**: {actual_message_count} (`{actual_message_count_percent:.2f}%`) + \u3164 • **__Offline message count__**: {offline_message_count} (`{offline_message_count_percent:.2f}%`) + \u3164 • **__Online message count__**: {online_message_count} (`{online_message_count_percent:.2f}%`) + \u3164 • **__Dnd message count__**: {dnd_message_count} (`{dnd_message_count_percent:.2f}%`) + \u3164 • **__Idle message count__**: {idle_message_count} (`{idle_message_count_percent:.2f}%`) """ embed = discord.Embed( - title=f'{member.name}#{member.discriminator} Detailed anaylysis', + title=f'{member.name}#{member.discriminator} Detailed metrics', description= f'Total thanks this month: {total_thank_count}\n' f'Revoked thanks this month: {revoked_thank_count} (`{revoked_thank_count/total_thank_count*100 if total_thank_count > 0 else 0:.2f}%`)\n' diff --git a/cogs/fun.py b/cogs/fun.py index 0bcd1d2..8f999af 100644 --- a/cogs/fun.py +++ b/cogs/fun.py @@ -55,7 +55,7 @@ async def number( number = await ( self.http.api["numbers"]["random"]() if (number is None) - else self.api["numbers"]["number"](number) + else self.http.api["numbers"]["number"](number) ) embed = await self.bot.embed( title=f"**{number}**", diff --git a/cogs/general.py b/cogs/general.py index f9e3508..c3c864a 100644 --- a/cogs/general.py +++ b/cogs/general.py @@ -7,7 +7,7 @@ import discord from discord.ext import commands -from ext.helpers import UrbanDefinition, UrbanDictionary, find_anime_source +from ext.helpers import UrbanDefinition, UrbanDictionary if TYPE_CHECKING: from ext.models import CodingBot diff --git a/cogs/helper.py b/cogs/helper.py index 393733c..c1ca883 100644 --- a/cogs/helper.py +++ b/cogs/helper.py @@ -11,6 +11,7 @@ from ext.consts import (HELP_BAN_ROLE_ID, OFFICIAL_HELPER_ROLE_ID, READ_HELP_RULES_ROLE_ID, TCR_GUILD_ID) from ext.ui.view import ConfirmButton +from pymongo import DESCENDING if TYPE_CHECKING: from ext.models import CodingBot @@ -19,39 +20,41 @@ class Helper(commands.Cog, command_attrs=dict(hidden=False)): hidden = False + def __init__(self, bot): self.bot = bot + self.help_warning = bot.database.warnings.help_warning - async def cog_check(self, ctx: commands.Context[CodingBot]) -> bool: - """ - Restricts the use of the cog to the official helper role + # async def cog_check(self, ctx: commands.Context[CodingBot]) -> bool: + # """ + # Restricts the use of the cog to the official helper role - Parameters - ---------- - ctx : commands.Context[CodingBot] - The context of the command - - Returns - ------- - bool - Whether the user has the required role - - Raises - ------ - commands.CheckFailure - If the user doesn't have the required role - - """ - if isinstance(ctx.channel, discord.DMChannel): - return False - if ctx.guild.id != TCR_GUILD_ID: - return False + # Parameters + # ---------- + # ctx : commands.Context[CodingBot] + # The context of the command + + # Returns + # ------- + # bool + # Whether the user has the required role - official_helper_role = ctx.guild.get_role(OFFICIAL_HELPER_ROLE_ID) + # Raises + # ------ + # commands.CheckFailure + # If the user doesn't have the required role - if official_helper_role not in ctx.author.roles: - return False - return True + # """ + # if isinstance(ctx.channel, discord.DMChannel): + # return False + # if ctx.guild.id != TCR_GUILD_ID: + # return False + + # official_helper_role = ctx.guild.get_role(OFFICIAL_HELPER_ROLE_ID) + + # if official_helper_role not in ctx.author.roles: + # return False + # return True async def capture_evidence(self, ctx: commands.Context[CodingBot]) -> Optional[discord.Attachment]: """ @@ -61,12 +64,12 @@ async def capture_evidence(self, ctx: commands.Context[CodingBot]) -> Optional[d ---------- ctx : commands.Context[CodingBot] The context of the command - + Returns ------- Optional[discord.Attachment] The evidence that was captured - + """ view = ConfirmButton(ctx) view.message = await ctx.author.send(f'Do you want to provide an evidence for your action?', view=view) @@ -173,7 +176,6 @@ async def log( embed.set_thumbnail(url=member.display_avatar.url) logs = self.bot.get_channel(964165082437263361) # 816512034228666419 await logs.send(embed=embed, file=file) # type: ignore - @commands.hybrid_group(name="helper") async def helper(self, ctx: commands.Context[CodingBot]) -> None: @@ -192,7 +194,7 @@ async def help_warn( ) -> None: """ Warns a member breaking rules in help channels. - + Usage: {prefix}helper warn @@ -200,107 +202,93 @@ async def help_warn( {prefix}helper warn {member} "Breaking rules" """ if len(reason) > 256: - return await self.bot.reply(ctx,"The reason must be less than 256 characters.") - - await self.bot.conn.insert_record( - 'warnings', - table='help_warns', - columns=('guild_id', 'user_id', 'helper_id', 'reason', 'date'), - values=(ctx.guild.id, member.id, ctx.author.id, - reason, ctx.message.created_at.timestamp()) - ) - await self.bot.reply(ctx,f'Help-warned {member.mention}') + return await self.bot.reply(ctx, "The reason must be less than 256 characters.") + + await self.help_warning.insert_one({ + 'g_id': ctx.guild.id, 'u_id': member.id, 'h_id': ctx.author.id, + 'reason': reason, 'date': ctx.message.created_at.timestamp() + }) + await self.bot.reply(ctx, f'Help-warned {member.mention}') evidence = await self.capture_evidence(ctx) await self.log( - action='warn', - member=member, - helper=ctx.author, - reason=reason, + action='warn', + member=member, + helper=ctx.author, + reason=reason, evidence=evidence ) - - @helper.command(name="warnings") async def help_warnings( - self, - ctx: commands.Context[CodingBot], + self, + ctx: commands.Context[CodingBot], member: discord.Member ) -> None: - + embed = discord.Embed( title=f"{member} Help warnings List", color=discord.Color.red()) - records = await self.bot.conn.select_record( - 'warnings', - arguments=('reason', 'helper_id', 'date'), - table='help_warns', - where=('guild_id', 'user_id'), - values=(ctx.guild.id, member.id), - extras=['ORDER BY date DESC'] + records = self.help_warning.find( + {'u_id': member.id, 'g_id': ctx.guild.id}, + sort=[('date', DESCENDING)] ) if not records: - return await self.bot.reply(ctx,f'{member.mention} has no help-warnings.') + return await self.bot.reply(ctx, f'{member.mention} has no help-warnings.') - for i, warning in enumerate(records, 1): - helper = ctx.guild.get_member(warning.helper_id) + i = 1 + async for warning in records: + helper = ctx.guild.get_member(warning['h_id']) + reason = warning['reason'] + date = warning['date'] if helper: helper = helper.mention else: helper = 'Unknown' - embed.add_field(name="`{}.` Reason: {}".format( - i, warning.reason), value=f"Issued by: {helper} - ", inline=False) - - await self.bot.reply(ctx,embed=embed) + embed.add_field( + name="`{}.` Reason: {}".format(i, reason), + value=f"Issued by: {helper} - ", + inline=False + ) + i += 1 + await self.bot.reply(ctx, embed=embed) @helper.command(name="clearwarning") async def help_clearwarning( - self, - ctx: commands.Context[CodingBot], - member: discord.Member, + self, + ctx: commands.Context[CodingBot], + member: discord.Member, index: int = None ) -> None: warn = None target = member or ctx.author if index is None: - await self.bot.conn.delete_record( - 'warnings', - table='help_warns', - where=('guild_id', 'user_id'), - values=(ctx.guild.id, target.id) - ) + await self.help_warning.delete_many({'g_id': ctx.guild.id, 'u_id': target.id}) else: - records = await self.bot.conn.select_record( - 'warnings', - arguments=('date', 'reason'), - table='help_warns', - where=('guild_id', 'user_id'), - values=(ctx.guild.id, target.id), - extras=['ORDER BY date DESC'] + records = self.help_warning.find( + {'g_id': ctx.guild.id, 'u_id': target.id}, + sort=[('date', DESCENDING)] ) - if not records: - return await self.bot.reply(ctx,f'{target.mention} has no warnings.') - - for i, sublist in enumerate(records, 1): + return await self.bot.reply(ctx, f'{target.mention} has no warnings.') + i = 1 + async for sublist in records: if index == i: - warn = sublist.reason - await self.bot.conn.delete_record( - 'warnings', - table='help_warns', - where=('guild_id', 'user_id', 'date'), - values=(ctx.guild.id, target.id, sublist.date) + warn = sublist['reason'] + date = sublist['date'] + await self.help_warning.delete_one( + {'g_id': ctx.guild.id, 'u_id': target.id, + 'reason': warn, 'date': date} ) break - - await self.bot.reply(ctx,f'{target.mention}\'s warning was cleared.') + i += 1 + await self.bot.reply(ctx, f'{target.mention}\'s warning was cleared.') await self.log(action='warn', undo=True, member=target, helper=ctx.author, warn=warn) @helper.command(name="ban") async def help_ban( - self, - ctx: commands.Context[CodingBot], - member:discord.Member, + self, + ctx: commands.Context[CodingBot], + member: discord.Member, *, reason: str ) -> None: @@ -308,61 +296,65 @@ async def help_ban( help_ban_role = ctx.guild.get_role(HELP_BAN_ROLE_ID) read_help_rules_role = ctx.guild.get_role(READ_HELP_RULES_ROLE_ID) if help_ban_role in member.roles: - return await self.bot.reply(ctx,f'{member.mention} is already help-banned') + return await self.bot.reply(ctx, f'{member.mention} is already help-banned') if read_help_rules_role in member.roles: await member.remove_roles(read_help_rules_role) if not help_ban_role in member.roles: await member.add_roles(help_ban_role) - await self.bot.reply(ctx,f'help-banned {member.mention} with reason: {reason}') + await self.bot.reply(ctx, f'help-banned {member.mention} with reason: {reason}') try: await member.send(f"You have been help-banned with reason: {reason}") except discord.Forbidden: pass + await self.log(action='ban', member=member, helper=ctx.author, reason=reason) @helper.command(name="unban") async def help_unban( - self, - ctx: commands.Context[CodingBot], + self, + ctx: commands.Context[CodingBot], member: discord.Member ) -> None: help_ban_role = ctx.guild.get_role(HELP_BAN_ROLE_ID) read_help_rules_role = ctx.guild.get_role(READ_HELP_RULES_ROLE_ID) if not help_ban_role in member.roles: - return await self.bot.reply(ctx,f'{member.mention} is not help-banned') - + return await self.bot.reply(ctx, f'{member.mention} is not help-banned') if not read_help_rules_role in member.roles: await member.add_roles(read_help_rules_role) if help_ban_role in member.roles: await member.remove_roles(help_ban_role) - - await self.bot.reply(ctx,f'help-unbanned {member.mention}') + await self.bot.reply(ctx, f'help-unbanned {member.mention}') try: - await member.send(f"You have been help-unbanned") - await self.log(action='ban', undo=True, member=member, helper=ctx.author) + await member.send(f"You have been help-unbanned") except discord.Forbidden: pass + await self.log(action='ban', undo=True, member=member, helper=ctx.author) @helper.command(name="verify") async def help_verify( - self, - ctx: commands.Context[CodingBot], + self, + ctx: commands.Context[CodingBot], target: discord.Member ) -> None: read_help_rules_role = ctx.guild.get_role(READ_HELP_RULES_ROLE_ID) if read_help_rules_role in target.roles: - embed = discord.Embed(title="ERROR!", description=f"{target.mention} is already verified") - embed.set_footer(text=f"Command executed by {ctx.author}", icon_url=ctx.author.display_avatar.url) + embed = discord.Embed( + title="ERROR!", description=f"{target.mention} is already verified") + embed.set_footer( + text=f"Command executed by {ctx.author}", icon_url=ctx.author.display_avatar.url) else: - embed = discord.Embed(title="Member verified", description=f"{target.mention} was successfully verified") - embed.set_footer(text=f"Command executed by {ctx.author}", icon_url=ctx.author.display_avatar.url) + embed = discord.Embed( + title="Member verified", description=f"{target.mention} was successfully verified") + embed.set_footer( + text=f"Command executed by {ctx.author}", icon_url=ctx.author.display_avatar.url) await target.add_roles(read_help_rules_role) - await self.bot.reply(ctx,embed=embed) + await self.bot.reply(ctx, embed=embed) + async def setup(bot): await bot.add_cog(Helper(bot)) diff --git a/cogs/listeners.py b/cogs/listeners.py index 3f33b28..7ff505a 100644 --- a/cogs/listeners.py +++ b/cogs/listeners.py @@ -21,6 +21,8 @@ class ListenerCog(commands.Cog, command_attrs=dict(hidden=True)): hidden = True def __init__(self, bot: CodingBot) -> None: self.bot = bot + self.afk = bot.database.extras.afk + self.message_metric = bot.database.metrics.message_metric @@ -42,13 +44,7 @@ async def afk_user_messaage(self, message: discord.Message): if record: record = record.get(message.author.id) if record: - _, time = record - await self.bot.conn.delete_record( - 'afk', - table='afk', - where=('user_id',), - values=(message.author.id,), - ) + await self.afk.delete_one({'u_id': message.author.id}) try: if "[AFK]" in message.author.display_name: name = message.author.display_name.split(' ')[1:] @@ -163,24 +159,18 @@ async def track_sent_message(self, message: discord.Message): if message.author.bot or not message.guild: return - values = [message.author.id, message.guild.id, 1, 1] - columns = ['user_id', 'guild_id', 'message_count'] - - columns.append(status := message.author.status.name) - + status = message.author.status.name + to_upsert = {'message_count': 1, status: 1} staff_role = message.guild.get_role(TCR_STAFF_ROLE_ID) if staff_role and staff_role in message.author.roles: # type: ignore - columns.append('is_staff') - values.append(1) + to_upsert['is_staff'] = 1 else: pass - return await self.bot.conn.insert_record( - 'metrics', - table='message_metric', - columns=columns, - values=values, - extras=[f'ON CONFLICT (user_id, guild_id) DO UPDATE SET message_count = message_count + 1, {status} = {status} + 1'], + return await self.message_metric.find_one_and_update( + {'u_id': message.author.id, 'g_id': message.guild.id}, + {'$inc': to_upsert}, + upsert=True ) @commands.Cog.listener('on_message_delete') @@ -192,16 +182,9 @@ async def track_deleted_message(self, message: discord.Message): if message.author.bot or not message.guild: return - values = [message.author.id, message.guild.id] - - columns = ['deleted_message_count = deleted_message_count + 1'] - await self.bot.conn.update_record( - 'metrics', - table='message_metric', - to_update=columns, - where=['user_id', 'guild_id'], - values=values, + return await self.message_metric.update_one( + {'user_id': message.author.id, 'guild_id': message.guild.id}, {'$inc': {'deleted_message': 1}} ) @commands.Cog.listener('on_message') diff --git a/cogs/misc.py b/cogs/misc.py index 323180d..8585401 100644 --- a/cogs/misc.py +++ b/cogs/misc.py @@ -5,7 +5,7 @@ import random import re import string -from datetime import datetime, timedelta +from datetime import timedelta from typing import TYPE_CHECKING, Optional import button_paginator as pg @@ -14,6 +14,7 @@ from ext.helpers import Spotify, grouper, ordinal_suffix_of, find_anime_source from ext.http import Http from ext.ui.view import Piston +from pymongo import ASCENDING, DESCENDING if TYPE_CHECKING: from ext.models import CodingBot @@ -29,6 +30,8 @@ def __init__(self, bot: CodingBot) -> None: self.regex = { "codeblock": re.compile(r"(\w*)\s*(?:```)(\w*)?([\s\S]*)(?:```$)") } + self.afk = bot.database.extras.afk + self.thanks_db = bot.database.thanks async def cog_check(self, ctx: commands.Context[CodingBot]) -> bool: if ctx.guild: @@ -64,11 +67,8 @@ async def afk(self, ctx: commands.Context[CodingBot], *, reason: Optional[str] = if ctx.guild.id not in self.bot.afk_cache: self.bot.afk_cache[ctx.guild.id] = {} if member.id not in self.bot.afk_cache.get(ctx.guild.id): - await self.bot.conn.insert_record( - 'afk', - table='afk', - values=(member.id, reason, int(ctx.message.created_at.timestamp())), - columns=['user_id', 'reason', 'afk_time'] + await self.afk.insert_one( + {'u_id': member.id, 'reason': reason, 'afk_time': int(ctx.message.created_at.timestamp())} ) try: await member.edit(nick=f"[AFK] {member.display_name}") @@ -130,17 +130,16 @@ async def run(self, ctx, *, codeblock: str): @commands.hybrid_group(name="thanks", invoke_without_command=True) @commands.cooldown(1, 10, commands.BucketType.member) async def thanks(self, ctx: commands.Context[CodingBot], member: discord.Member): - record = await self.bot.conn.select_record( - 'thanks', - table='thanks_info', - arguments=('thanks_count',), - where=['guild_id', 'user_id'], - values=[ctx.guild.id, member.id] + + record = await self.thanks_dn.thank_info.find_one( + {'g_id': ctx.guild.id, 'u_id': member.id} ) + if not record: return await ctx.send(f"{member.display_name} does not have any thanks") - thanks = record[0] - await ctx.send(f"{member.display_name} has `{thanks.thanks_count}` thanks") + thanks_count = record['thanks_count'] + + await ctx.send(f"{member.display_name} has `{thanks_count}` thanks") @commands.hybrid_group(name="thank", invoke_without_command=True) @commands.cooldown(1, 10, commands.BucketType.member) @@ -159,61 +158,50 @@ async def thank(self, ctx: commands.Context[CodingBot], member: discord.Member, elif member.id == self.bot.user.id: return await ctx.reply("You can't thank me.", ephemeral=True) - await self.bot.conn.insert_record( - 'thanks', - table='thanks_info', - values=(member.id, ctx.guild.id, 1), - columns=['user_id', 'guild_id', 'thanks_count'], - extras=['ON CONFLICT (user_id) DO UPDATE SET thanks_count = thanks_count + 1'] + + await self.bot.database.thanks.thank_info.find_one_and_update( + {'g_id': ctx.guild.id, 'u_id': member.id}, + {'$inc': {'thanks_count': 1}}, + upsert=True ) staff_role = ctx.guild.get_role(795145820210462771) member_is_staff = 1 if staff_role and staff_role in member.roles else 0 characters = string.ascii_letters + string.digits - await self.bot.conn.insert_record( - 'thanks', - table='thanks_data', - columns=( - 'is_staff', 'user_id', 'giver_id', 'guild_id', - 'message_id', 'channel_id', 'reason', 'thank_id', - 'date' - ), - values=(member_is_staff, member.id, ctx.author.id, ctx.guild.id, - ctx.message.id, ctx.channel.id, reason or "No reason given", - "". join(random.choice(characters) for _ in range(7)), - int(ctx.message.created_at.timestamp()) - ) + await self.bot.database.thanks.thanks_data.insert_one( + {'g_id': ctx.guild.id, 'u_id': member.id, 'is_staff': member_is_staff, + 'giver_id': ctx.author.id, 'm_id': ctx.message.id, 'c_id': ctx.channel.id, + 'reason': reason, 'thank_id': "". join(random.choice(characters) for _ in range(7)), + 'date': int(ctx.message.created_at.timestamp())}, ) await ctx.reply(f"{ctx.author.mention} you thanked {member.mention}!", ephemeral=True) - # NOTE: add check which allows only head helpers and admins to use this command + @thank.command(name="show") + @commands.is_owner() async def thank_show(self, ctx: commands.Context[CodingBot], member: discord.Member): - records = await self.bot.conn.select_record( - 'thanks', - table='thanks_data', - arguments=( - 'giver_id', 'message_id','channel_id','reason','thank_id', - 'date' - ), - where=['user_id'], - values=[member.id] - ) - if not records: - return await ctx.reply(f"{member.mention} does not have any thanks.", ephemeral=True) + records = self.bot.database.thanks.thank_data.find( + {'u_id': member.id, 'g_id': ctx.guild.id}, + sort=[('date', DESCENDING)] + ) - information = tuple(grouper(5, records)) embeds = [] - for info in information: + + information = tuple(grouper(5, await records.to_list(None))) + + if not information: + return await ctx.reply(f"{member.mention} does not have any thanks.", ephemeral=True) + + for _ in information: embed = discord.Embed(title=f'Showing {member.display_name}\'s data') - for data in info: - giver_id = data.giver_id - msg_id = data.message_id - channel_id = data.channel_id - reason = data.reason - thank_id = data.thank_id - timestamp = data.date + for info in information: + giver_id = info['giver_id'] + msg_id = info['d_id'] + channel_id = info['c_id'] + reason = info['reason'] + thank_id = info['thank_id'] + date = info['date'] channel = ctx.guild.get_channel(channel_id) msg_link = f'https://discord.com/channels/{ctx.guild.id}/{channel.id}/{msg_id}' @@ -236,31 +224,23 @@ async def thank_show(self, ctx: commands.Context[CodingBot], member: discord.Mem # NOTE: add check which allows only head helpers and admins to use this command @thank.command(name="delete") async def thank_delete(self, ctx: commands.Context[CodingBot], thank_id: str): - record = await self.bot.conn.select_record( - 'thanks', - table='thanks_data', - arguments=['user_id'], - where=['thank_id'], - values=[thank_id] + + record = await self.bot.database.thanks.thank_data.find_one( + {'thank_id': thank_id, 'g_id': ctx.guild.id} ) - if not record: - return await ctx.send("No thank with that id") - user_id = record[0].user_id + if not record: + return await ctx.reply(f"Thank with id `{thank_id}` does not exist.", ephemeral=True) - await self.bot.conn.delete_record( - 'thanks', - table='thanks_data', - where=['thank_id'], - values=[thank_id] + user_id = record['u_id'] + await self.bot.database.thanks.thank_data.find_one_and_delete( + {'g_id': ctx.guild.id, 'thank_id': thank_id, 'u_id': user_id} ) - await self.bot.conn.insert_record( - 'thanks', - table='thanks_info', - values=(user_id, ctx.guild.id, -1), - columns=['user_id', 'guild_id', 'thanks_count'], - extras=['ON CONFLICT (user_id) DO UPDATE SET thanks_count = thanks_count - 1'] + await self.thanks_db.thanks_info.find_one_and_update( + {'g_id': ctx.guild.id, 'thank_id': thank_id}, + {'$inc': {'thanks_count': -1}}, + upsert=True ) await ctx.send(f"Remove thank from <@{user_id}> with id {thank_id}") @@ -274,15 +254,13 @@ async def thank_leaderboard(self, ctx: commands.Context[CodingBot]): `{prefix}thanks leaderboard`: *will show the thanks leaderboard* """ - - records = await self.bot.conn.select_record( - 'thanks', - table='thanks_info', - arguments=('user_id', 'thanks_count'), - where=['guild_id'], - values=[ctx.guild.id], - extras=['ORDER BY thanks_count DESC, user_id ASC LIMIT 100'], + records = self.thanks_db.thanks_info.find( + {}, + sort=[('thanks_count', DESCENDING), ('user_id', ASCENDING)] ) + + records = await records.to_list(None) + if not records: return await ctx.reply("No thanks leaderboard yet.", ephemeral=True) @@ -290,11 +268,11 @@ async def thank_leaderboard(self, ctx: commands.Context[CodingBot]): embeds = [] for info in information: - user = [ctx.guild.get_member(i.user_id) for i in info] + user = [ctx.guild.get_member(i['u_id']) for i in info] embed = discord.Embed( title=f"Thank points leaderboard", description="\n\n".join( - [f"`{i}{ordinal_suffix_of(i)}` is {user.mention} with `{thanks_count.thanks_count}` Thank point(s)" for i, (user, thanks_count) + [f"`{i}{ordinal_suffix_of(i)}` is {user.mention} with `{thanks_count['thamks_count']}` Thank point(s)" for i, (user, thanks_count) in enumerate(zip(user, info), 1) ] ), diff --git a/cogs/moderation.py b/cogs/moderation.py index 1181f0d..60f4c57 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -12,12 +12,12 @@ from ext.models import CodingBot, TimeConverter from ext.ui.view import ConfirmButton from ext import consts +from pymongo import DESCENDING if TYPE_CHECKING: from ext.models import CodingBot - def trainee_check(): def wrapper(ctx: commands.Context[CodingBot]): trainee_role = ctx.guild.get_role(729537643951554583) # type: ignore @@ -170,7 +170,7 @@ async def log(self, logs = self.bot.get_channel(964165082437263361) # 816512034228666419 await logs.send(embed=embed, file=file) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="kick") @commands.has_permissions(kick_members=True) async def kick(self, ctx: commands.Context[CodingBot], member: discord.Member, *, reason: Optional[str] = None): @@ -198,7 +198,7 @@ async def kick(self, ctx: commands.Context[CodingBot], member: discord.Member, * evidence = await self.capture_evidence(ctx) await self.log(action='kick', moderator=ctx.author, member=member, reason=reason, evidence=evidence) # type: ignore -# @trainee_check() +# # @trainee_check() @commands.hybrid_command(name="ban") @commands.has_permissions(ban_members=True) async def ban(self, ctx: commands.Context[CodingBot], member: discord.Member, *, reason: Optional[str] = None): @@ -223,7 +223,7 @@ async def ban(self, ctx: commands.Context[CodingBot], member: discord.Member, *, evidence = await self.capture_evidence(ctx) await self.log(action='ban', moderator=ctx.author, member=member, undo=False, reason=reason, duration=None, evidence=evidence) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="unban") @commands.has_permissions(ban_members=True) async def unban(self, ctx: commands.Context[CodingBot], user: discord.User, *, reason: Optional[str] = None): @@ -245,7 +245,7 @@ async def unban(self, ctx: commands.Context[CodingBot], user: discord.User, *, r await self.bot.reply(ctx,f'Unbanned {user.mention}') await self.log(action='ban', moderator=ctx.author, member=user, undo=True, reason=reason, duration=None) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="mute", aliases=['timeout']) @commands.has_permissions(manage_roles=True) async def mute(self, ctx: commands.Context[CodingBot], member: discord.Member, duration: TimeConverter, *, reason: Optional[str] = None): @@ -272,7 +272,7 @@ async def mute(self, ctx: commands.Context[CodingBot], member: discord.Member, d evidence = await self.capture_evidence(ctx) await self.log(action='mute', moderator=ctx.author, member=member, undo=False, reason=reason, duration=duration, evidence=evidence) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="unmute") @commands.has_permissions(manage_roles=True) async def unmute(self, ctx: commands.Context[CodingBot], member: discord.Member, *, reason: Optional[str] = None): @@ -296,7 +296,7 @@ async def unmute(self, ctx: commands.Context[CodingBot], member: discord.Member, await self.bot.reply(ctx,f'Unmuted {member.mention}') await self.log(action='mute', moderator=ctx.author, member=member, undo=True, reason=reason) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command() async def massban(self, ctx: commands.Context[CodingBot], users: commands.Greedy[Union[discord.Member, discord.User]]): """ @@ -333,7 +333,7 @@ async def massban(self, ctx: commands.Context[CodingBot], users: commands.Greedy embed.description = description await self.bot.reply(ctx,embed=embed) - @trainee_check() + # @trainee_check() @commands.hybrid_command() async def warn(self, ctx: commands.Context[CodingBot], member: discord.Member, *, reason: Optional[str] = None): """ @@ -348,18 +348,15 @@ async def warn(self, ctx: commands.Context[CodingBot], member: discord.Member, * assert ctx.guild is not None if not reason: return await self.bot.reply(ctx,"Please provide a reason for the warning.") - await self.bot.conn.insert_record( - 'warnings', - table='warnings', - columns=('guild_id', 'user_id', 'moderator_id', 'reason', 'date'), - values=(ctx.guild.id, member.id, ctx.author.id, - reason, ctx.message.created_at.timestamp()) + + await self.bot.database.warnings.warning.insert_one( + {'g_id': ctx.guild.id, 'u_id': member.id, 'mod_id': ctx.author.id, 'reason': reason, 'date': ctx.message.created_at.timestamp()} ) await self.bot.reply(ctx,f'Warned {member.mention}') evidence = await self.capture_evidence(ctx) await self.log(action='warn', moderator=ctx.author, member=member, reason=reason, evidence=evidence) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="purge") @commands.has_permissions(manage_messages=True) async def purge(self, ctx: commands.Context[CodingBot], amount: int = 1): @@ -375,7 +372,7 @@ async def purge(self, ctx: commands.Context[CodingBot], amount: int = 1): purged_amt = len(await ctx.channel.purge(limit=amount + 1)) # type: ignore await self.bot.reply(ctx,f'Purged {purged_amt} messages in {ctx.channel.mention}') # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="warnings") async def warnings(self, ctx: commands.Context[CodingBot], member: discord.Member): """ @@ -390,32 +387,25 @@ async def warnings(self, ctx: commands.Context[CodingBot], member: discord.Membe assert ctx.guild is not None embed = discord.Embed( title=f"{member} Warnings List", color=discord.Color.red()) - records = await self.bot.conn.select_record('warnings', - arguments=( - 'reason', 'moderator_id', 'date'), - table='warnings', - where=( - 'guild_id', 'user_id'), - values=( - ctx.guild.id, member.id), # type: ignore - extras=[ - 'ORDER BY date DESC'] - ) + + records = await self.bot.database.warnings.warning.find( + {'g_id': ctx.guild.id, 'u_id': member.id} + ).sort('date', DESCENDING).to_list(None) if not records: return await self.bot.reply(ctx,f'{member.mention} has no warnings.') for i, warning in enumerate(records, 1): - moderator = ctx.guild.get_member(warning.moderator_id) + moderator = ctx.guild.get_member(warning['mod_id']) if moderator: moderator = moderator.mention else: moderator = 'Unknown' embed.add_field(name="`{}.` Reason: {}".format( - i, warning.reason), value=f"Issued by: {moderator} - ", inline=False) + i, warning['reason']), value=f"Issued by: {moderator} - ", inline=False) await self.bot.reply(ctx,embed=embed) - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="clearwarning") @commands.has_permissions(manage_messages=True) async def clearwarning( @@ -438,39 +428,29 @@ async def clearwarning( assert ctx.guild is not None target: discord.Member = member or ctx.author # type: ignore if index is None: - await self.bot.conn.delete_record( - 'warnings', - table='warnings', - where=('guild_id', 'user_id'), - values=(ctx.guild.id, target.id) + await self.bot.database.warnings.warning.delete_many( + {'g_id': ctx.guild.id, 'u_id': target.id} ) else: - records = await self.bot.conn.select_record( - 'warnings', - arguments=('date',), - table='warnings', - where=('guild_id', 'user_id'), - values=(ctx.guild.id, target.id), - extras=['ORDER BY date DESC'] - ) + records = await self.bot.database.warnings.warning.find( + {'g_id': ctx.guild.id, 'u_id': target.id}, + sort=[('date', DESCENDING)] + ).to_list(None) if not records: return await self.bot.reply(ctx,f'{target.mention} has no warnings.') for i, sublist in enumerate(records, 1): if index == i: - await self.bot.conn.delete_record( - 'warnings', - table='warnings', - where=('guild_id', 'user_id', 'date'), - values=(ctx.guild.id, target.id, sublist.date) + await self.bot.database.warnings.warning.delete_one( + {'g_id': ctx.guild.id, 'u_id': target.id, 'date': sublist['date']} ) break await ctx.reply(f'{target.mention}\'s warning was cleared.', allowed_mentions=discord.AllowedMentions(users=False)) await self.log(action='warn', moderator=ctx.author, member=target, undo=True) # type: ignore - @trainee_check() + # @trainee_check() @commands.hybrid_command(name="verify") @commands.has_permissions(manage_roles=True) # Luz : I don't know what permission is required for this command async def verify_member(self, ctx: commands.Context[CodingBot], target: discord.Member): diff --git a/cogs/tasks.py b/cogs/tasks.py index 233f36c..276a8c2 100644 --- a/cogs/tasks.py +++ b/cogs/tasks.py @@ -59,17 +59,25 @@ async def remove_inactive_warns(self): arguments=('date','user_id'), table='warnings', where=('guild_id',), - alues=(TCR_GUILD_ID,) + values=(TCR_GUILD_ID,) ) + + records = self.bot.database.warnings.warning.find( + {'g_id': TCR_GUILD_ID} + ) + now = datetime.datetime.utcnow().timestamp() - for record in records: - if record.date+(60*60*24*31) < now: + async for record in records: + if record['date'] + (60*60*24*31) < now: await self.bot.conn.delete_record( 'warnings', table='warnings', where=('guild_id', 'user_id', 'date'), values=(TCR_GUILD_ID, record.user_id, record.date) ) + await self.bot.database.warnings.warning.delete_one( + {'g_id': TCR_GUILD_ID, 'user_id': record.user_id, 'date': record.date} + ) async def setup(bot: CodingBot): await bot.add_cog(TaskCog(bot)) diff --git a/ext/consts.py b/ext/consts.py index b634844..2d508d0 100644 --- a/ext/consts.py +++ b/ext/consts.py @@ -2,20 +2,12 @@ __all__ = ( 'INTENTS', - 'PREFIX_CONFIG_SCHEMA', - 'COMMANDS_CONFIG_SCHEMA', - 'WARNINGS_CONFIG_SCHEMA', - 'AFK_CONFIG_SCHEMA', - 'HELP_WARNINGS_CONFIG_SCHEMA', 'HELP_COMMAND', 'OFFICIAL_HELPER_ROLE_ID', 'TCR_GUILD_ID', 'HELP_BAN_ROLE_ID', 'READ_HELP_RULES_ROLE_ID', - 'THANK_INFO_CONFIG_SCHEMA', - 'THANK_DATA_CONFIG_SCHEMA', - 'MESSAGE_METRIC_SCHEMA', - 'TCR_STAFF_ROLE_ID', + 'TCR_STAFF_ROLE_ID' ) INTENTS = discord.Intents( @@ -34,77 +26,6 @@ ) -PREFIX_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS prefixconf ( - id BIGINT, - prefix TEXT - ); - """ - -COMMANDS_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS commandconf ( - id BIGINT, - command TEXT - ); - """ - -WARNINGS_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS warnings ( - user_id BIGINT, - guild_id BIGINT, - moderator_id BIGINT, - reason TEXT, - date BIGINT - ); - """ - -AFK_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS afk ( - user_id BIGINT, - reason TEXT, - afk_time BIGINT - ); - """ - -HELP_WARNINGS_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS help_warns ( - user_id BIGINT, - guild_id BIGINT, - helper_id BIGINT, - reason TEXT, - date BIGINT - ); - """ - -THANK_INFO_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS thanks_info ( - user_id BIGINT UNIQUE, - guild_id BIGINT, - thanks_count INT - ); - """ - -THANK_DATA_CONFIG_SCHEMA = """CREATE TABLE IF NOT EXISTS thanks_data ( - user_id BIGINT, - giver_id BIGINT, - guild_id BIGINT, - message_id BIGINT, - channel_id BIGINT, - thank_id TEXT, - date BIGINT, - reason TEXT DEFAULT "No reason given", - is_staff BOOLEAN CHECK(is_staff IN (0, 1)) DEFAULT 0, - thank_revoked BOOLEAN CHECK(thank_revoked IN (0, 1)) DEFAULT 0 - ); - """ - -MESSAGE_METRIC_SCHEMA = """CREATE TABLE IF NOT EXISTS message_metric ( - user_id BIGINT, - guild_id BIGINT, - message_count INT, - deleted_message_count INT DEFAULT 0, - offline INT DEFAULT 0, - online INT DEFAULT 0, - dnd INT DEFAULT 0, - idle INT DEFAULT 0, - is_staff BOOLEAN CHECK(is_staff IN (0, 1)) DEFAULT 0, - UNIQUE(user_id, guild_id) - );""" - HELP_COMMAND = """ Help command for Coding Bot diff --git a/ext/http.py b/ext/http.py index 6a5017e..91b46f6 100644 --- a/ext/http.py +++ b/ext/http.py @@ -60,6 +60,15 @@ def __init__(self, session: aiohttp.ClientSession): # self.update_data.start() + async def send_request(self, endpoint: str, random: bool, **kwargs): + base_url = "http://numbersapi.com/" + if random: + base_url += f"random/{endpoint}" + else: + base_url += endpoint.format(**kwargs) + await self.get(base_url) + + @tasks.loop(minutes=5) async def update_data(self): self.cache["piston"]["runtimes"] = await self.api["piston"]["runtimes"]() diff --git a/ext/models.py b/ext/models.py index ac7fa8b..4dccdf5 100644 --- a/ext/models.py +++ b/ext/models.py @@ -2,63 +2,21 @@ import datetime as dt import os -import string from dataclasses import dataclass, field -from typing import (Any, Dict, Iterable, List, Mapping, Optional, Set, Tuple, - Union) +from typing import Any, Dict, List, Mapping, Optional, Set, Tuple import aiohttp -import aiosqlite import discord -import sr_api from discord.ext import commands from DiscordUtils import InviteTracker from dotenv import load_dotenv +from motor.motor_asyncio import AsyncIOMotorClient from pytimeparse import parse -from .consts import (AFK_CONFIG_SCHEMA, COMMANDS_CONFIG_SCHEMA, HELP_COMMAND, - HELP_WARNINGS_CONFIG_SCHEMA, INTENTS, - MESSAGE_METRIC_SCHEMA, PREFIX_CONFIG_SCHEMA, - THANK_DATA_CONFIG_SCHEMA, THANK_INFO_CONFIG_SCHEMA, - WARNINGS_CONFIG_SCHEMA) +from .consts import HELP_COMMAND, INTENTS from .helpers import AntiRaid, WelcomeBanner, log_error load_dotenv('.env', verbose=True) -class Record: - __slots__ = ("arguments",) - - def __init__(self, arguments: Dict[str, Any]) -> None: - self.arguments = arguments - - def __getitem__(self, __item: Union[str, int]) -> Any: - if isinstance(__item, str): - if __item in self.arguments: - return self.arguments[__item] - raise AttributeError( - f'Dynamic object has no attribute \'{__item}\'') - elif isinstance(__item, int): # type: ignore - return tuple(self.arguments.values())[__item] - - def __getattr__(self, __item: str): - if __item in self.arguments: - return self.arguments[__item] - raise AttributeError(f"Dynamic object has no attribute '{__item}'") - - def __len__(self): - return len(self.arguments.keys()) - - def __repr__(self) -> str: - argument = ", ".join( - f"{key}={value}" for key, value in self.arguments.items() - ) - return f"" - - @classmethod - def from_tuple(cls, arguments: Iterable[Any], tuple_: Iterable[Any]) -> Record: - arguments = ["".join(map(lambda x: x if x in string.ascii_letters + - string.digits + '_' else "", arg)) for arg in arguments] - return cls(dict(zip(arguments, tuple_))) - @dataclass(slots=True, kw_only=True, repr=True) class Cache: @@ -76,192 +34,6 @@ class Cache: commands: Set[str] = field(default_factory=set) -class Database: - """ - Database class for storing opened connections - - Attributes - ---------- - conn : Dict[str, aiosqlite.Connection] - A dictionary of connections - is_closed : bool - Whether the connections are closed - """ - - def __init__(self, bot: CodingBot): - self.conn: Dict[str, aiosqlite.Connection] = {} - self.is_closed: bool = False - self.bot: CodingBot = bot - - - def __getattr__(self, __name: str) -> Any: - if __name in self.conn: - return self.conn[__name] - return super().__getattribute__(__name) - - async def __aenter__(self) -> "Database": - self.conn["config"] = await aiosqlite.connect("./database/config.db") - self.conn["warnings"] = await aiosqlite.connect( - "./database/warnings.db" - ) - self.conn["afk"] = await aiosqlite.connect("./database/afk.db") - self.conn["thanks"] = await aiosqlite.connect("./database/thanks.db") - self.conn["metrics"] = await aiosqlite.connect("./database/metrics.db") - await self.init_dbs() - return self - - async def fill_cache(self): - record = await self.select_record( - 'afk', - table='afk', - arguments=['user_id', 'reason', 'afk_time'] - ) - - for row in record: - self.bot.afk_cache[row.user_id] = (row.reason, row.afk_time) - - async def init_dbs(self): - async with self.cursor("config") as cursor: - await cursor.execute(PREFIX_CONFIG_SCHEMA) - await cursor.execute(COMMANDS_CONFIG_SCHEMA) - - async with self.cursor("warnings") as cursor: - await cursor.execute(WARNINGS_CONFIG_SCHEMA) - await cursor.execute(HELP_WARNINGS_CONFIG_SCHEMA) - - async with self.cursor("afk") as cursor: - await cursor.execute(AFK_CONFIG_SCHEMA) - - async with self.cursor("thanks") as cursor: - await cursor.execute(THANK_DATA_CONFIG_SCHEMA) - await cursor.execute(THANK_INFO_CONFIG_SCHEMA) - - async with self.cursor("metrics") as cursor: - await cursor.execute(MESSAGE_METRIC_SCHEMA) - - await self.commit() - - async def __aexit__(self, *args: Any) -> None: - await self.commit() - await self.close() - - def cursor(self, conn: str) -> aiosqlite.Cursor: - return getattr(self, conn).cursor() - - def __repr__(self) -> str: - return f"" - - async def select_record( - self, - connection: str, - /, - *, - arguments: Tuple[str, ...], - table: str, - where: Optional[Tuple[str, ...]] = None, - values: Optional[Tuple[Any, ...]] = None, - extras: Optional[List[str]] = None, - ) -> Optional[List[Record]]: - statement = """SELECT {} FROM {}""".format( - ", ".join(arguments), table - ) - if where is not None: - assign_question = map(lambda x: f"{x} = ?", where) - statement += " WHERE {}".format( - " AND ".join(assign_question) - ) - if extras: - for stuff in extras: - statement += f" {stuff}" - async with self.cursor(connection) as cursor: - await cursor.execute(statement, values or ()) - # type: ignore # Type checker cries unnecessarily. - rows: List[aiosqlite.Row[Any]] = [i async for i in cursor] - if rows: - return [Record.from_tuple(arguments, row) for row in rows] - return None - - async def delete_record( - self, - connection: str, - /, - *, - table: str, - where: Tuple[str, ...], - values: Optional[Tuple[Any, ...]] = None - ) -> None: - delete_statement = f"DELETE FROM {table}" - if where is not None: - assign_question = map(lambda x: f"{x} = ?", where) - delete_statement += " WHERE {}".format( - " AND ".join(assign_question)) - - async with self.cursor(connection) as cursor: - await cursor.execute(delete_statement, values or ()) - await getattr(self, connection).commit() - - async def insert_record( - self, - connection: str, - /, - *, - table: str, - values: Tuple[Any, ...], - columns: Tuple[str, ...], - extras: Optional[List[str]] = None - ) -> None: - - insert_statement = """ - INSERT INTO {}({}) VALUES ({}) - """.format( - table, ", ".join(columns), ", ".join(["?"] * len(columns)) - ) - if extras: - for stuff in extras: - insert_statement += f" {stuff}" - async with self.cursor(connection) as cursor: - await cursor.execute(insert_statement, values) - await getattr(self, connection).commit() - - async def update_record( - self, - connection: str, - /, - *, - table: str, - to_update: Tuple[str, ...], - where: Tuple[str, ...], - values: Tuple[Any, ...], - extras: Optional[List[str]] = None - ) -> None: - update_statement = """ - UPDATE {} SET {} WHERE {} - """.format( - table, ", ".join( - map(lambda x: f"{x} = ?" if "=" not in x else x, to_update)), - " AND ".join(map(lambda x: f"{x} = ?", where)) - ) - if extras: - for stuff in extras: - update_statement += f" {stuff}" - async with self.cursor(connection) as cursor: - await cursor.execute(update_statement, values) - await getattr(self, connection).commit() - - @property - def closed(self): - return self.is_closed - - async def commit(self) -> None: - for conn in self.conn.values(): - await conn.commit() - - async def close(self) -> None: - self.is_closed = True - for conn in self.conn.values(): - await conn.close() - - class CodingHelp(commands.HelpCommand): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) @@ -327,16 +99,15 @@ def __init__(self) -> None: command_prefix=[')'], intents=INTENTS, case_insensitive=True, help_command=help_command ) - self.conn: Database = discord.utils.MISSING + self.database: AsyncIOMotorClient = None self.tracker = InviteTracker(self) self.default_prefixes = [")"] self.welcomer = WelcomeBanner(self) self.processing_commands = 0 self.message_cache = {} - self.sr_api = sr_api.Client() self.spotify_session: Optional[tuple] = None - self.spotify_client_id: str = os.environ["SPOTIFY_CLIENT_ID"] - self.spotify_client_secret: str = os.environ["SPOTIFY_CLIENT_SECRET"] + self.spotify_client_id: str = os.environ.get("SPOTIFY_CLIENT_ID") + self.spotify_client_secret: str = os.environ.get("SPOTIFY_CLIENT_SECRET") self.welcomer_enabled = True self.welcomer_channel_id = 743817386792058971 self.raid_mode_enabled = False @@ -344,6 +115,7 @@ def __init__(self) -> None: self.afk_cache: Dict[int, Dict[int, Tuple[str, int]]] = {} async def setup_hook(self) -> None: + self.database = AsyncIOMotorClient(os.environ.get("MONGO_DB_URI")) self.raid_checker.check_for_raid.start() for filename in os.listdir("./cogs"): if filename.endswith(".py"): @@ -355,9 +127,8 @@ async def setup_hook(self) -> None: jishaku.hidden = True async def start(self, token: str, *, reconnect: bool = True) -> None: - async with Database(self) as self.conn: - async with aiohttp.ClientSession() as self.session: - return await super().start(token, reconnect=reconnect) + async with aiohttp.ClientSession() as self.session: + return await super().start(token, reconnect=reconnect) async def on_ready(self) -> None: await self.wait_until_ready() @@ -371,7 +142,7 @@ async def on_invite_delete(self, invite: discord.Invite) -> None: await self.tracker.remove_invite_cache(invite) async def on_guild_join(self, guild: discord.Guild) -> None: - await self.tracker.update_guild_cache(guild) + await self.tracker.add_guild_cache(guild) async def on_guild_remove(self, guild: discord.Guild) -> None: await self.tracker.remove_guild_cache(guild) @@ -379,11 +150,9 @@ async def on_guild_remove(self, guild: discord.Guild) -> None: async def on_member_join(self, member: discord.Member) -> None: if not self.welcomer_enabled: return - banned = await self.raid_checker.cache_insert_or_ban(member) if banned: return - rules = member.guild.rules_channel if rules: rules_channel = rules.mention diff --git a/main.py b/main.py index ed9ab32..a0fc80e 100644 --- a/main.py +++ b/main.py @@ -12,16 +12,16 @@ bot = CodingBot() @bot.before_invoke -async def before_invoke(ctx: commands.Context[CodingBot]): +async def before_invoke(_: commands.Context[CodingBot]): bot.processing_commands += 1 @bot.after_invoke -async def after_invoke(ctx: commands.Context[CodingBot]): +async def after_invoke(_: commands.Context[CodingBot]): bot.processing_commands -= 1 @bot.check -async def check_processing_commands(ctx: commands.Context[CodingBot]): +async def check_processing_commands(_: commands.Context[CodingBot]): await bot.wait_until_ready() return True diff --git a/requirements.txt b/requirements.txt index a81e5c4..b9d45a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ aiohttp==3.8.1 aiosignal==1.2.0 -aiosqlite==0.17.0 async-timeout==4.0.2 attrs==21.4.0 autopep8==1.6.0 @@ -13,7 +12,9 @@ DiscordUtils==1.3.4 frozenlist==1.3.0 humanize==4.1.0 idna==3.3 +jishaku==2.5.0 lxml==4.9.0 +motor==3.0.0 multidict==6.0.2 Pillow==9.1.1 pycodestyle==2.8.0