diff --git a/cogs/config.py b/cogs/config.py index 1f11bc9..68ce98d 100644 --- a/cogs/config.py +++ b/cogs/config.py @@ -15,22 +15,25 @@ def __init__(self, client: commands.Bot): self.config = {"guilds": {}, "optout": [], "question_limit": 100000, "blacklist": []} self.question_map = {} - self.logger.debug("loading questions...") - with open("data.json") as file: - questions = qna.json.json_to_questions(file.read()) - for question in questions: - self.question_map.update({question.text: question}) - del questions - self.logger.debug(f"loaded {len(self.question_map.keys())} questions.") + if os.path.exists("data.json"): + self.logger.debug("loading questions...") + with open("data.json") as file: + questions = qna.json.json_to_questions(file.read()) + for question in questions: + self.question_map.update({question.text: question}) + del questions + self.logger.debug(f"loaded {len(self.question_map.keys())} questions.") - self.logger.debug("loading config...") if os.path.exists("config.json"): + self.logger.debug("loading config...") with open("config.json") as file: - self.config = json.load(file) + self.config: dict = json.load(file) if "guilds" not in self.config.keys(): self.config.update({"guilds": {}}) - if "optout" not in self.config.keys(): - self.config.update({"optout": []}) + if "optin" not in self.config.keys(): + self.config.update({"optin": []}) + if "optout" in self.config.keys(): + self.config.pop("optout") if "question_limit" not in self.config.keys(): self.config.update({"question_limit": 100000}) if "blacklist" not in self.config.keys(): diff --git a/cogs/help.py b/cogs/help.py index dd0e3d6..77ef80c 100644 --- a/cogs/help.py +++ b/cogs/help.py @@ -1,6 +1,7 @@ import bob import discord import logging +import datetime from discord.ext import commands @@ -24,18 +25,20 @@ async def help(self, ctx: commands.Context, requested_command: str = None): embed = discord.Embed( title=f"bob help | {self.client.command_prefix}{target_command.name}", description=target_command.description or target_command.brief, - color=bob.blue_color + color=bob.blue_color, + timestamp=datetime.datetime.now() ) embed.add_field( name="usage", value=target_command.usage or f"{self.client.command_prefix}{target_command.name} " f"{target_command.signature}" ) + embed.set_footer(text=f"bob v{bob.__version__}", icon_url=self.client.user.avatar_url) await ctx.reply(embed=embed) return - embed = discord.Embed(title="bob help", color=bob.blue_color) + embed = discord.Embed(title="bob help", color=bob.blue_color, timestamp=datetime.datetime.now()) for command in sorted(self.client.commands, key=lambda c: c.name): if command.hidden: continue @@ -51,6 +54,8 @@ async def help(self, ctx: commands.Context, requested_command: str = None): value=command.brief or "no description.", inline=False ) + embed.set_footer(text=f"bob v{bob.__version__}", icon_url=self.client.user.avatar_url) + await ctx.reply(embed=embed) diff --git a/cogs/invite.py b/cogs/invite.py deleted file mode 100644 index 2162821..0000000 --- a/cogs/invite.py +++ /dev/null @@ -1,26 +0,0 @@ -import bob -import discord -import logging -import subprocess -from discord.ext import commands - - -class Invite(commands.Cog): - def __init__(self, client: commands.Bot): - self.client = client - self.logger = logging.getLogger("cogs.Invite") - self.logger.debug("registered.") - - @commands.command(brief="invite me to your server!") - async def invite(self, ctx: commands.Context): - embed = discord.Embed( - title="here you go!", - description=f"you can invite me to your server by [clicking on this link](https://discord.com/api/oauth2/" - f"authorize?client_id={self.client.user.id}&permissions=3072&scope=bot)", - color=bob.blue_color - ) - await ctx.reply(embed=embed) - - -def setup(client: commands.Bot): - client.add_cog(Invite(client)) diff --git a/cogs/lar.py b/cogs/lar.py index 6b06218..171aee0 100644 --- a/cogs/lar.py +++ b/cogs/lar.py @@ -15,7 +15,7 @@ def __init__(self, client: commands.Bot): self.logger.debug("registered.") async def learn(self, message: discord.Message): - if message.author.id in self.config.config["optout"] or message.author.id in self.config.config["blacklist"]: + if message.author.id not in self.config.config["optin"] or message.author.id in self.config.config["blacklist"]: return if message.reference: diff --git a/cogs/modpanel.py b/cogs/modpanel.py index 00c4908..efa2c84 100644 --- a/cogs/modpanel.py +++ b/cogs/modpanel.py @@ -30,6 +30,7 @@ def index(): questions=len(self.config.question_map.keys()), responses=responses, guilds=len(self.client.guilds), + users=len(self.config.config["optin"]), shards=self.client.shard_count ) diff --git a/cogs/optin.py b/cogs/optin.py new file mode 100644 index 0000000..6789fa0 --- /dev/null +++ b/cogs/optin.py @@ -0,0 +1,38 @@ +import logging +from cogs.config import Config +from discord.ext import commands + + +def blacklist_check(ctx: commands.Context): + return ctx.author.id not in ctx.cog.config.config["blacklist"] + + +class OptIn(commands.Cog): + def __init__(self, client: commands.Bot): + self.client = client + self.logger = logging.getLogger("cogs.OptIn") + self.config: Config = client.get_cog("Config") + self.logger.debug("registered.") + + @commands.check(blacklist_check) + @commands.command(brief="opt-out of bob message collection") + async def optout(self, ctx: commands.Context): + if ctx.author.id not in self.config.config["optin"]: + return await ctx.reply("you're already opted out!") + + self.config.config["optin"].remove(ctx.author.id) + await ctx.reply("you're now opted out of bob's message collection.") + + @commands.check(blacklist_check) + @commands.command(brief="opt-in to bob message collection") + async def optin(self, ctx: commands.Context): + if ctx.author.id in self.config.config["optin"]: + return await ctx.reply("you're already opted in!") + + self.config.config["optin"].append(ctx.author.id) + await ctx.reply("you're now opted into bob's message collection.\n" + "by opting in, you agree to bob's privacy policy. run `b.privacy` for more info.") + + +def setup(client: commands.Bot): + client.add_cog(OptIn(client)) diff --git a/cogs/optout.py b/cogs/optout.py deleted file mode 100644 index 6677488..0000000 --- a/cogs/optout.py +++ /dev/null @@ -1,31 +0,0 @@ -import logging -from cogs.config import Config -from discord.ext import commands - - -class OptOut(commands.Cog): - def __init__(self, client: commands.Bot): - self.client = client - self.logger = logging.getLogger("cogs.OptOut") - self.config: Config = client.get_cog("Config") - self.logger.debug("registered.") - - @commands.command(brief="opt-out of bob message collection (opted in by default)") - async def optout(self, ctx: commands.Context): - if ctx.author.id in self.config.config["optout"]: - return await ctx.reply("you're already opted out!") - - self.config.config["optout"].append(ctx.author.id) - await ctx.reply("you're now opted out of bob's message collection.\nbob will not use your messages to learn.") - - @commands.command(brief="opt-in to bob message collection") - async def optin(self, ctx: commands.Context): - if ctx.author.id not in self.config.config["optout"]: - return await ctx.reply("you're already opted in!") - - self.config.config["optout"].remove(ctx.author.id) - await ctx.reply("you're now opted into bob's message collection.") - - -def setup(client: commands.Bot): - client.add_cog(OptOut(client)) diff --git a/cogs/usercommands.py b/cogs/usercommands.py new file mode 100644 index 0000000..dc5316c --- /dev/null +++ b/cogs/usercommands.py @@ -0,0 +1,86 @@ +import bob +import discord +import logging +import datetime +from discord.ext import commands + + +class UserCommands(commands.Cog): + def __init__(self, client: commands.Bot): + self.client = client + self.logger = logging.getLogger("cogs.UserCommands") + self.logger.debug("registered.") + + @commands.command(brief="invite me to your server!") + async def invite(self, ctx: commands.Context): + embed = discord.Embed( + title="here you go!", + description=f"you can invite me to your server by [clicking on this link](https://discord.com/api/oauth2/" + f"authorize?client_id={self.client.user.id}&permissions=3072&scope=bot)", + color=bob.blue_color, + timestamp=datetime.datetime.now() + ) + embed.set_footer(text=f"bob v{bob.__version__}", icon_url=self.client.user.avatar_url) + + await ctx.reply(embed=embed) + + @commands.command(brief="need help? join the support server!") + async def support(self, ctx: commands.Context): + embed = discord.Embed( + title="here you go!", + description=f"you can join the support server by [clicking on this link](https://discord.gg/uuqZYPYrMj)", + color=bob.blue_color, + timestamp=datetime.datetime.now() + ) + embed.set_footer(text=f"bob v{bob.__version__}", icon_url=self.client.user.avatar_url) + + await ctx.reply(embed=embed) + + @commands.command(brief="view bob's privacy policy.") + async def privacy(self, ctx: commands.Context): + embed = discord.Embed( + title="privacy policy", + color=bob.blue_color, + timestamp=datetime.datetime.now() + ) + + embed.add_field( + name="1. message collection", + value="if opted-in, bob will use your messages, including attachments, to learn and expand its database.\n" + "bob will additionally log guild, channel, message and author IDs.", + inline=False + ) + embed.add_field( + name="2. message usage", + value="bob will use the messages in its database to respond to messages from other users.", + inline=False + ) + embed.add_field( + name="3. data protection", + value="bob will keep sensitive data (such as guild, channel, message and author IDs) private, and will be " + "visible only to the bot owner.\n" + "bob will never send sensitive data in a reply to a user, unless it is a reply to a message in " + "the database.", + inline=False + ) + embed.add_field( + name="4. data removal", + value="currently you cannot request data removal for your account.\n" + "if you had accidentally sent a sensitive message and bob had learned it as a reply, you can contact " + "the bot owner privately through the support server to get the reply removed.", + inline=False + ) + embed.add_field( + name="5. blacklisting", + value="if the bot owner sees bob learn inappropriate messages from your replies, they may remove your " + "replies and add you to the blacklist.\nthe blacklist stops bob from learning your replies, but " + "it won't stop bob from replying to you.", + inline=False + ) + embed.set_footer(text=f"bob v{bob.__version__}", icon_url=self.client.user.avatar_url) + + await ctx.reply(embed=embed) + + +def setup(client: commands.Bot): + client.add_cog(UserCommands(client)) diff --git a/main.py b/main.py index 7c74ae9..262284b 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import discord import logging import argparse +import traceback from discord.ext import commands parser = argparse.ArgumentParser(description=f"bob {bob.__version__}") @@ -62,15 +63,21 @@ async def on_command_error(ctx: commands.Context, error): ) return await ctx.reply(embed=embed) + else: + traceback.print_exception(type(error), error, error.__traceback__) + @client.event async def on_ready(): client.load_extension("cogs.config") + client.load_extension("cogs.lar") - client.load_extension("cogs.invite") client.load_extension("cogs.configuration") + client.load_extension("cogs.optin") + + client.load_extension("cogs.usercommands") client.load_extension("cogs.help") - client.load_extension("cogs.optout") + client.load_extension("cogs.modpanel") logger.info(f"bob v{bob.__version__} is ready!") diff --git a/templates/index.html b/templates/index.html index aa7dca8..15b281c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -42,6 +42,7 @@

statistics

  • {{ questions }} questions
  • {{ responses }} responses
  • {{ guilds }} guilds
  • +
  • {{ users }} opted-in users
  • {{ shards }} shards