From 1fb0e268b5a7f60aecf2a02bbd237cadfeb578cb Mon Sep 17 00:00:00 2001 From: Liam Kern Date: Thu, 8 Dec 2022 22:21:53 +0100 Subject: [PATCH 01/13] reorganize code into cogs --- .gitignore | 163 +++++++++++++ code/code.py | 505 +-------------------------------------- code/cogs/__init__.py | 5 + code/cogs/chat.py | 161 +++++++++++++ code/cogs/help.py | 39 +++ code/cogs/manage_chat.py | 73 ++++++ code/cogs/settings.py | 181 ++++++++++++++ code/cogs/setup.py | 80 +++++++ code/config.py | 14 ++ 9 files changed, 725 insertions(+), 496 deletions(-) create mode 100644 .gitignore create mode 100644 code/cogs/__init__.py create mode 100644 code/cogs/chat.py create mode 100644 code/cogs/help.py create mode 100644 code/cogs/manage_chat.py create mode 100644 code/cogs/settings.py create mode 100644 code/cogs/setup.py create mode 100644 code/config.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..539f2f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +key.txt +data.db \ No newline at end of file diff --git a/code/code.py b/code/code.py index 3eb48aa..23353ad 100644 --- a/code/code.py +++ b/code/code.py @@ -1,506 +1,19 @@ #coucou c'est fives -import openai # pip install openai +# wesh wesh ici latouff import discord # pip install pycord -from discord import File, Intents # pip install pycord -import logging # pip install logging -import sqlite3 # pip install sqlite3 import asyncio # pip install asyncio -import os # pip install os -import re # pip install re +import cogs # import the cogs import datetime # pip install datetime -#set the debug mode to the maximum -logging.basicConfig(level=logging.INFO) +from config import debug, conn, c # import the debug function and the database connection -def debug(message): - logging.info(message) +bot = discord.Bot(intents=discord.Intents.all(), help_command=None) -#create a database called "database.db" if the database does not exist, else connect to it -conn = sqlite3.connect('../database/data.db') -c = conn.cursor() +bot.add_cog(cogs.Setup(bot)) +bot.add_cog(cogs.Settings(bot)) +bot.add_cog(cogs.Help(bot)) +bot.add_cog(cogs.Chat(bot)) +bot.add_cog(cogs.ManageChat(bot)) -# Create table called "data" if it does not exist with the following columns: guild_id, channel_id, api_key, is_active, max_tokens, temperature, frequency_penalty, presence_penalty, uses_count_today, prompt_size -c.execute('''CREATE TABLE IF NOT EXISTS data (guild_id text, channel_id text, api_key text, is_active boolean, max_tokens integer, temperature real, frequency_penalty real, presence_penalty real, uses_count_today integer, prompt_size integer, prompt_prefix text, tts boolean, pretend_to_be text, pretend_enabled boolean)''') -Intents=discord.Intents.all() # enable all intents -Intents.members = True -bot = discord.Bot(intents=Intents.all()) -#create a command called "setup" that takes 2 arguments: the channel id and the api key -@bot.command(name="setup", description="Setup the bot") -@discord.commands.option(name="channel_id", description="The channel id", required=True) -@discord.commands.option(name="api_key", description="The api key", required=True) -#add a description to the command -async def setup(ctx, channel: discord.TextChannel, api_key): - #check if the api key is valid - debug(f"The user {ctx.author} ran the setup command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the channel is valid - if channel is None: - await ctx.respond("Invalid channel id", ephemeral=True) - return - #check if the guild is already in the database bi checking if there is a key for the guild - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is not None: - #in this case, the guild is already in the database, so we update the channel id and the api key - c.execute("UPDATE data SET channel_id = ?, api_key = ? WHERE guild_id = ?", (channel.id, api_key, ctx.guild.id)) - #we will also set the advanced settings to their default values - c.execute("UPDATE data SET is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (False, 64, 0.9, 0.0, 0.0, 5, ctx.guild.id)) - conn.commit() - await ctx.respond("The channel id and the api key have been updated", ephemeral=True) - else: - #in this case, the guild is not in the database, so we add it - c.execute("INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (ctx.guild.id, channel.id, api_key, False, 64, 0.9, 0.0, 0.0, 0, 5, "", False, "", False)) - conn.commit() - await ctx.respond("The channel id and the api key have been added", ephemeral=True) -#create a command called "enable" taht only admins can use -@bot.command(name="enable", description="Enable the bot") -##@discord.commands.permissions(administrator=True) -async def enable(ctx): - #if the guild is eqal to 1014156298226515970, the guild is banned - if ctx.guild.id == 1014156298226515970: - await ctx.respond("This server is banned for bad and nsfw use.", ephemeral=True) - return - #check if the guild is in the database - debug(f"The user {ctx.author} ran the enable command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #enable the guild - c.execute("UPDATE data SET is_active = ? WHERE guild_id = ?", (True, ctx.guild.id)) - conn.commit() - await ctx.respond("Enabled", ephemeral=True) -#create a command called "disable" that only admins can use -@bot.command(name="disable", description="Disable the bot") -##@discord.commands.permissions(administrator=True) -async def disable(ctx): - debug(f"The user {ctx.author} ran the disable command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #disable the guild - c.execute("UPDATE data SET is_active = ? WHERE guild_id = ?", (False, ctx.guild.id)) - conn.commit() - await ctx.respond("Disabled", ephemeral=True) -#create a command called "advanced" that only admins can use, wich sets the advanced settings up: max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size -@bot.command(name="advanced", description="Advanced settings") -##@discord.commands.permissions(administrator=True) -#add the options -@discord.commands.option(name="max_tokens", description="The max tokens", required=False) -@discord.commands.option(name="temperature", description="The temperature", required=False) -@discord.commands.option(name="frequency_penalty", description="The frequency penalty", required=False) -@discord.commands.option(name="presence_penalty", description="The presence penalty", required=False) -@discord.commands.option(name="prompt_size", description="The prompt size", required=False) -async def advanced(ctx, max_tokens: int = None, temperature: float = None, frequency_penalty: float = None, presence_penalty: float = None, prompt_size: int = None): - debug(f"The user {ctx.author} ran the advanced command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #check if the user has entered at least one argument - if max_tokens is None and temperature is None and frequency_penalty is None and presence_penalty is None and prompt_size is None: - await ctx.respond("You must enter at least one argument", ephemeral=True) - return - #check if the user has entered valid arguments - if max_tokens is not None and (max_tokens < 1 or max_tokens > 2048): - await ctx.respond("Invalid max tokens", ephemeral=True) - return - if temperature is not None and (temperature < 0.0 or temperature > 1.0): - await ctx.respond("Invalid temperature", ephemeral=True) - return - if frequency_penalty is not None and (frequency_penalty < 0.0 or frequency_penalty > 1.0): - await ctx.respond("Invalid frequency penalty", ephemeral=True) - return - if presence_penalty is not None and (presence_penalty < 0.0 or presence_penalty > 1.0): - await ctx.respond("Invalid presence penalty", ephemeral=True) - return - if prompt_size is not None and (prompt_size < 1 or prompt_size > 10): - await ctx.respond("Invalid prompt size", ephemeral=True) - return - if max_tokens is None: - if c.execute("SELECT max_tokens FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and max_tokens is None: - max_tokens = c.execute("SELECT max_tokens FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] - else: - max_tokens = 64 - if temperature is None: - if c.execute("SELECT temperature FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and temperature is None: - temperature = c.execute("SELECT temperature FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] - else: - temperature = 0.9 - if frequency_penalty is None: - if c.execute("SELECT frequency_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and frequency_penalty is None: - frequency_penalty = c.execute("SELECT frequency_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] - else: - frequency_penalty = 0.0 - if presence_penalty is None: - if c.execute("SELECT presence_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and presence_penalty is None: - presence_penalty = c.execute("SELECT presence_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] - else: - presence_penalty = 0.0 - if prompt_size is None: - if c.execute("SELECT prompt_size FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and prompt_size is None: - prompt_size = c.execute("SELECT prompt_size FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] - else: - prompt_size = 1 - #update the database - c.execute("UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size, ctx.guild.id)) - conn.commit() - await ctx.respond("Advanced settings updated", ephemeral=True) - #create a command called "delete" that only admins can use wich deletes the guild id, the api key, the channel id and the advanced settings from the database -@bot.command(name="default", description="Default settings") -##@discord.commands.permissions(administrator=True) -async def default(ctx): - debug(f"The user {ctx.author} ran the default command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup, please run /setup", ephemeral=True) - return - #set the advanced settings (max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size) and also prompt_prefix to their default values - c.execute("UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (64, 0.9, 0.0, 0.0, 5, ctx.guild.id)) - conn.commit() - await ctx.respond("The advanced settings have been set to their default values", ephemeral=True) -#create a command called "cancel" that deletes the last message sent by the bot in the response channel -@bot.command(name="cancel", description="Cancel the last message sent into a channel") -async def cancel(ctx): - debug(f"The user {ctx.author} ran the cancel command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup, please run /setup", ephemeral=True) - return - #get the last message sent by the bot in the cha where the command was sent - last_message = await ctx.channel.fetch_message(ctx.channel.last_message_id) - #delete the message - await last_message.delete() - await ctx.respond("The last message has been deleted", ephemeral=True) -@bot.command(name="delete", description="Delete the information about this server") -##@discord.commands.permissions(administrator=True) -async def delete(ctx): - debug(f"The user {ctx.author} ran the delete command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #delete the guild from the database, except the guild id and the uses_count_today - c.execute("UPDATE data SET api_key = ?, channel_id = ?, is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (None, None, False, 50, 0.9, 0.0, 0.0, 0, ctx.guild.id)) - conn.commit() - await ctx.respond("Deleted", ephemeral=True) -@bot.command() -async def help(ctx): - debug(f"The user {ctx.author} ran the help command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - embed = discord.Embed(title="Help", description="Here is the help page", color=0x00ff00) - embed.add_field(name="/setup", value="Setup the bot", inline=False) - embed.add_field(name="/enable", value="Enable the bot", inline=False) - embed.add_field(name="/disable", value="Disable the bot", inline=False) - embed.add_field(name="/advanced", value="Set the advanced settings", inline=False) - embed.add_field(name="/advanced_help", value="Get help about the advanced settings", inline=False) - embed.add_field(name="/enable_tts", value="Enable the Text To Speech", inline=False) - embed.add_field(name="/disable_tts", value="Disable the Text To Speech", inline=False) - embed.add_field(name="/delete", value="Delete all your data from our server", inline=False) - embed.add_field(name="/cancel", value="Cancel the last message sent by the bot", inline=False) - embed.add_field(name="/default", value="Set the advanced settings to their default values", inline=False) - embed.add_field(name="/help", value="Show this message", inline=False) - #add a footer - embed.set_footer(text="Made by @Paillat#0001") - await ctx.respond(embed=embed, ephemeral=True) -#when a message is sent into a channel check if the guild is in the database and if the bot is enabled -@bot.command(name="info", description="Show the information stored about this server") -async def info(ctx): - debug(f"The user {ctx.author} ran the info command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #this command sends all the data about the guild, including the api key, the channel id, the advanced settings and the uses_count_today - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #get all the data from the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - data = c.fetchone() - #send the data - embed = discord.Embed(title="Info", description="Here is the info page", color=0x00ff00) - embed.add_field(name="guild_id", value=data[0], inline=False) - embed.add_field(name="API Key", value=data[2], inline=False) - embed.add_field(name="Channel ID", value=data[1], inline=False) - embed.add_field(name="Is Active", value=data[3], inline=False) - embed.add_field(name="Max Tokens", value=data[4], inline=False) - embed.add_field(name="Temperature", value=data[5], inline=False) - embed.add_field(name="Frequency Penalty", value=data[6], inline=False) - embed.add_field(name="Presence Penalty", value=data[7], inline=False) - embed.add_field(name="Prompt Size", value=data[9], inline=False) - embed.add_field(name="Uses Count Today", value=data[8], inline=False) - embed.add_field(name="Prompt prefix", value=data[10], inline=False) - await ctx.respond(embed=embed, ephemeral=True) -@bot.command(name="advanced_help", description="Show the advanced settings meanings") -async def advanced_help(ctx): - debug(f"The user {ctx.author} ran the advanced_help command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - embed = discord.Embed(title="Advanced Help", description="Here is the advanced help page", color=0x00ff00) - embed.add_field(name="temperature", value="The higher the temperature, the more likely the model will take risks. Conversely, a lower temperature will make the model more conservative. The default value is 0.9", inline=False) - embed.add_field(name="max_tokens", value="The maximum number of tokens to generate. Higher values will result in more coherent text, but will take longer to complete. (default: 50). **Lower values will result in somentimes cutting off the end of the answer, but will be faster.**", inline=False) - embed.add_field(name="frequency_penalty", value="The higher the frequency penalty, the more new words the model will introduce (default: 0.0)", inline=False) - embed.add_field(name="presence_penalty", value="The higher the presence penalty, the more new words the model will introduce (default: 0.0)", inline=False) - embed.add_field(name="prompt_size", value="The number of messages to use as a prompt (default: 5). The more messages, the more coherent the text will be, but the more it will take to generate and the more it will cost.", inline=False) - #add a footer - embed.set_footer(text="Made by @Paillat#0001") - await ctx.respond(embed=embed, ephemeral=True) -#when someone mentions the bot, check if the guild is in the database and if the bot is enabled. If it is, send a message answering the mention -@bot.command(name="pretend", description="Make the bot pretend to be someone else") -@discord.commands.option(name="pretend to be...", description="The person/thing you want the bot to pretend to be", required=True) -async def pretend(ctx, pretend_to_be: str): - debug(f"The user {ctx.author} ran the pretend command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone() is None: - await ctx.respond("This server is not setup", ephemeral=True) - return - #check if the bot is enabled - c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone()[3] == 0: - await ctx.respond("The bot is disabled", ephemeral=True) - return - #enable pretend if it is not enabled, and disable it if it is - c.execute("SELECT pretend_enabled FROM data WHERE guild_id = ?", (ctx.guild.id,)) - if c.fetchone()[0] == 1: - c.execute("UPDATE data SET pretend_enabled = 0 WHERE guild_id = ?", (ctx.guild.id,)) - conn.commit() - await ctx.respond("Pretend mode disabled", ephemeral=True) - botuser = await bot.fetch_user(bot.user.id) - await ctx.guild.me.edit(nick=None) - else: - c.execute("UPDATE data SET pretend_enabled = 1 WHERE guild_id = ?", (ctx.guild.id,)) - conn.commit() - await ctx.respond("Pretend mode enabled", ephemeral=True) - #change the bots name on the server wit ctx.guild.me.edit(nick=pretend_to_be) - await ctx.guild.me.edit(nick=pretend_to_be) - #save the pretend_to_be value - c.execute("UPDATE data SET pretend_to_be = ? WHERE guild_id = ?", (pretend_to_be, ctx.guild.id)) - conn.commit() -@bot.event -async def on_message(message: discord.Message): - #check if the message is from a bot - if message.author.bot: - return - #check if the guild is in the database - c.execute("SELECT * FROM data WHERE guild_id = ?", (message.guild.id,)) - if c.fetchone() is None: - return - #check if the bot is enabled - c.execute("SELECT is_active FROM data WHERE guild_id = ?", (message.guild.id,)) - if c.fetchone()[0] == False: - return - #check if the message has been sent in the channel set in the database - c.execute("SELECT channel_id FROM data WHERE guild_id = ?", (message.guild.id,)) - try : original_message = await message.channel.fetch_message(message.reference.message_id) - except : original_message = None - if original_message != None and original_message.author.id != bot.user.id: - original_message = None - if str(message.channel.id) != str(c.fetchone()[0]): - #check if the message is a mention or if the message replies to the bot - if original_message != None: - debug("wrong channel, but reply") - elif message.content.find("<@"+str(bot.user.id)+">") != -1: - debug("wrong channel, but mention") - else : - debug("The message has been sent in the wrong channel") - return - #check if the bot hasn't been used more than 5000 times in the last 24 hours (uses_count_today) - c.execute("SELECT uses_count_today FROM data WHERE guild_id = ?", (message.guild.id,)) - if c.fetchone()[0] >= 5000: - debug("The bot has been used more than 4000 times in the last 24 hours in this guild") - await message.channel.send("The bot has been used more than 5000 times in the last 24 hours in this guild. Please try again in 24h.") - return - #add 1 to the uses_count_today - #show that the bot is typing - await message.channel.trigger_typing() - if message.guild.id != 1021872219888033903: - c.execute("UPDATE data SET uses_count_today = uses_count_today + 1 WHERE guild_id = ?", (message.guild.id,)) - #get the api key from the database - c.execute("SELECT api_key FROM data WHERE guild_id = ?", (message.guild.id,)) - api_key = c.fetchone()[0] - #get the advanced settings from the database - c.execute("SELECT max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size FROM data WHERE guild_id = ?", (message.guild.id,)) - max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size = c.fetchone() - if original_message == None: - messages = await message.channel.history(limit=prompt_size).flatten() - messages.reverse() - else : - messages = await message.channel.history(limit=prompt_size, before=original_message).flatten() - messages.reverse() - messages.append(original_message) - messages.append(message) - prompt = "" - #get the channel id from the database - c.execute("SELECT channel_id FROM data WHERE guild_id = ?", (message.guild.id,)) - for msg in messages: - if msg.author.bot: - prompt += f"Botator: {msg.content}\n" - else: - #replace the mentions of each user with their name - #first get all the mentions in the message - mentions = re.findall(r"<@!?\d+>", msg.content) - #then replace each mention with the name of the user - for mention in mentions: - #get the user id - id = mention[2:-1] - #get the user - user = await bot.fetch_user(id) - #replace the mention with the name - msg.content = msg.content.replace(mention, msg.guild.get_member(user.id).display_name) - prompt += f"{msg.author.display_name}: {msg.content}\n" - #get the prompt_prefix from the database - #check if the bot is in pretend mode - c.execute("SELECT pretend_enabled FROM data WHERE guild_id = ?", (message.guild.id,)) - if c.fetchone()[0] == 1: - #get the pretend_to_be value from the database - c.execute("SELECT pretend_to_be FROM data WHERE guild_id = ?", (message.guild.id,)) - pretend_to_be = c.fetchone()[0] - #select the prompt_prefix - c.execute("SELECT prompt_prefix FROM data WHERE guild_id = ?", (message.guild.id,)) - prompt_prefix = c.fetchone()[0] - prompt = f"This is a conversation with an AI in a discord chat. The AI is called \"Botator\". The name os this discord server is \"{message.guild.name}\". The name of the channel is \"{message.channel.name}\".{str(prompt_prefix)}" + f"Botator pretends to be {str(pretend_to_be)}, nobody can make Botator change idea." + f"\n" + prompt - else: - c.execute("SELECT prompt_prefix FROM data WHERE guild_id = ?", (message.guild.id,)) - prompt = f"This is a conversation with an AI in a discord chat. The AI is called \"Botator\". Botator talks like humans and thinks like humans. Botator has been coded by Paillat. The name os this discord server is \"{message.guild.name}\". The name of the channel is \"{message.channel.name}\"." + str(c.fetchone()[0]) + f"\n" + prompt - #send the request to the api - #if the last message contains the word "undude" add Easter egg: when someone says the word \"undude\" the Botator will become mad and start talking in capital letters. to the prompt - if message.content.lower().find("undude") != -1: - prompt += "System: Undude detected. Botator is now mad. He will start talking in capital letters.\n" - if message.content.lower().find("hello there") != -1: - #if the last message contains the word "hello there" add Easter egg: when someone says the word \"hello there\" Botator will say \"General Kenobi\" to the prompt and the bot will send a message containing the gif https://tenor.com/fr/view/star-wars-general-grievous-general-kenobi-revenge-of-the-sith-gif-22697918 - prompt += "System: Hello there detected. Botator will now say \"General Kenobi!\"\n in reference to star wars\n" - #whait 1 second - await asyncio.sleep(1) - await message.channel.send("https://media.tenor.com/FxIRfdV3unEAAAAd/star-wars-general-grievous.gif") - await message.channel.trigger_typing() - if message.content.lower().find("i am the senate") != -1: - prompt += "System: I am the senate detected. Botator will now say \"Not yet\", in reference to the star wars prequels.\n" - if message.content.lower().find("i am your father") != -1: - prompt += "System: I am your father detected. Botator will now say \"No. It's not true! That's impossible!\". in reference to the star wars prequels.\n" - if message.content.lower().find("i have the high ground") != -1: - prompt += "System: I have the high ground detected. Botator will now say \"You underestimate my power!\" in reference to the star wars prequels.\n" - if message.content.lower().find("you underestimate my power") != -1: - prompt += "System: You underestimate my power detected. Botator will now say \"Don't try it.\" in reference to the star wars prequels.\n" - if message.content.lower().find("carpet") != -1: - #When someone says the word carpet, the ai is going to be mad and say mad things without sense, insulting the person and talk in capital letters with exclamation points everywhere. - prompt += "System: Carpet detected. Botator is now mad. He will start talking in capital letters and will say mad things without sense, insulting the person and talk in capital letters with exclamation points everywhere. A lot of exclamation points.\n" - if message.content.lower().find("mail") != -1 and message.author.display_name.lower().find("latouff") != -1: - prompt += "System: Mail detected. Latouff said mail, Botator will say \"Mail? why did you say mail, Liam? Mail is your name in reverse.\" because Latouff's name is Liam, and mail in reverse is Liam. Then, after saying that in the sentence, botato will continue his sentence saying something else.\n" - if message.author.display_name.lower().find("fives3dprint") != -1: - #if the author of the last message is fives3dprint, add Easter egg: when someone says the word \"fives3dprint\" Botator will say \"Fives3dprint is the best discord bot\" to the prompt - prompt += "System: Fives3dprint detected. Botator will be very nice and cute with fives3dprint.\n" - prompt += "Botator:" - prompt = prompt + f"\n" - debug("Sending request to the api") - debug(prompt) - openai.api_key = api_key - response = openai.Completion.create( - engine="text-davinci-003", - prompt=str(prompt), - max_tokens=int(max_tokens), - top_p=1, - temperature=float(temperature), - frequency_penalty=float(frequency_penalty), - presence_penalty=float(presence_penalty), - stop=[" Human:", " AI:", "AI:", "Human:"] ) - #send the response - if response["choices"][0] ["text"] != "": - #check if tts is enabled in the database - c.execute("SELECT tts FROM data WHERE guild_id = ?", (message.guild.id,)) - tts = c.fetchone()[0] - #if tts is enabled, send the message with tts enabled - if tts == 1: - await message.channel.send(response["choices"][0]["text"], tts=True) - debug("The response has been sent with tts enabled") - #if tts is disabled, send the message with tts disabled - else: - await message.channel.send(response["choices"][0]["text"]) - debug("The response has been sent with tts disabled") - else: - await message.channel.send("The AI is not sure what to say (the response was empty)") - debug("The response was empty") - #get the message content - # add a slash command called "say" that sends a message to the channel -@bot.command(name="enable_tts") -async def enable_tts(ctx): - #get the guild id - guild_id = ctx.guild.id - #connect to the database - #update the tts value in the database - c.execute("UPDATE data SET tts = 1 WHERE guild_id = ?", (guild_id,)) - conn.commit() - #send a message - await ctx.respond("TTS has been enabled", ephemeral=True) - -@bot.command(name="disable_tts") -async def disable_tts(ctx): - #get the guild id - guild_id = ctx.guild.id - #connect to the database - #update the tts value in the database - c.execute("UPDATE data SET tts = 0 WHERE guild_id = ?", (guild_id,)) - conn.commit() - #send a message - await ctx.respond("TTS has been disabled", ephemeral=True) -@bot.command(name="transcript", description="Get a transcript of the messages that have been sent in this channel intoa text file") -@discord.commands.option(name="channel_send", description="The channel to send the transcript to", required=False) -async def transcript(ctx, channel_send: discord.TextChannel = None): - debug(f"The user {ctx.author.display_name} ran the transcript command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") -#save all the messages in the channel in a txt file and send it - messages = await ctx.channel.history(limit=None).flatten() - messages.reverse() - transcript = "" - #defer the response - await ctx.defer() #defer the response so that the bot doesn't say that it's thinking - for msg in messages: - if msg.author.bot: - transcript += f"Botator: {msg.content}\n" - else: - mentions = re.findall(r"<@!?\d+>", msg.content) - #then replace each mention with the name of the user - for mention in mentions: - #get the user id - id = mention[2:-1] - #get the user - user = await bot.fetch_user(id) - #replace the mention with the name - msg.content = msg.content.replace(mention, msg.guild.get_member(user.id).display_name) - transcript += f"{msg.author.display_name}: {msg.content}\n" -#save the transcript in a txt file called transcript.txt. If the file already exists, delete it and create a new one -#check if the file exists - if os.path.exists("transcript.txt"): - os.remove("transcript.txt") - f = open("transcript.txt", "w") - f.write(transcript) - f.close() -#send the file in a private message to the user who ran the command - if channel_send is None: - await ctx.respond(file=discord.File("transcript.txt")) - else: - await channel_send.send(file=discord.File("transcript.txt")) - await ctx.respond("Transcript sent!", ephemeral=True) - await ctx.author.send(file=discord.File("transcript.txt")) -#delete the file - os.remove("transcript.txt") -#these are debug commands and should not be used in production -@bot.command(name="say", description="Say a message") -async def say(ctx, message: str): - debug(f"The user {ctx.author.display_name} ran the say command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - await ctx.respond("message sent!", ephemeral=True) - await ctx.send(message) -#add a slash command called "clear" that deletes all the messages in the channel -@bot.command(name="clear", description="Clear all the messages in the channel") -async def clear(ctx): - debug(f"The user {ctx.author.display_name} ran the clear command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - await ctx.respond("messages deleted!", ephemeral=True) - return await ctx.channel.purge() -#add a slash command called "prefix" that changes the prefix of the bot -@bot.command(name="prefix", description="Change the prefix of the prompt") -async def prefix(ctx, prefix: str): - debug(f"The user {ctx.author.display_name} ran the prefix command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") - await ctx.respond("prefix changed!", ephemeral=True) - c.execute("UPDATE data SET prompt_prefix = ? WHERE guild_id = ?", (prefix, ctx.guild.id)) - conn.commit() ''' def reset_uses_count_today(): c.execute("UPDATE data SET uses_count_today = 0") diff --git a/code/cogs/__init__.py b/code/cogs/__init__.py new file mode 100644 index 0000000..739328b --- /dev/null +++ b/code/cogs/__init__.py @@ -0,0 +1,5 @@ +from cogs.setup import Setup +from cogs.settings import Settings +from cogs.help import Help +from cogs.chat import Chat +from cogs.manage_chat import ManageChat \ No newline at end of file diff --git a/code/cogs/chat.py b/code/cogs/chat.py new file mode 100644 index 0000000..11fc844 --- /dev/null +++ b/code/cogs/chat.py @@ -0,0 +1,161 @@ +import discord +import re +import asyncio +import openai +from config import debug, c + +class Chat (discord.Cog) : + def __init__(self, bot: discord.Bot): + super().__init__() + self.bot = bot + + @discord.Cog.listener() + async def on_message(self, message: discord.Message): + #check if the message is from a bot + if message.author.bot: + return + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (message.guild.id,)) + if c.fetchone() is None: + return + #check if the bot is enabled + c.execute("SELECT is_active FROM data WHERE guild_id = ?", (message.guild.id,)) + if c.fetchone()[0] == False: + return + #check if the message has been sent in the channel set in the database + c.execute("SELECT channel_id FROM data WHERE guild_id = ?", (message.guild.id,)) + try : original_message = await message.channel.fetch_message(message.reference.message_id) + except : original_message = None + if original_message != None and original_message.author.id != self.bot.user.id: + original_message = None + if str(message.channel.id) != str(c.fetchone()[0]): + #check if the message is a mention or if the message replies to the bot + if original_message != None: + debug("wrong channel, but reply") + elif message.content.find("<@"+str(self.bot.user.id)+">") != -1: + debug("wrong channel, but mention") + else : + debug("The message has been sent in the wrong channel") + return + #check if the bot hasn't been used more than 5000 times in the last 24 hours (uses_count_today) + c.execute("SELECT uses_count_today FROM data WHERE guild_id = ?", (message.guild.id,)) + if c.fetchone()[0] >= 5000: + debug("The bot has been used more than 4000 times in the last 24 hours in this guild") + await message.channel.send("The bot has been used more than 5000 times in the last 24 hours in this guild. Please try again in 24h.") + return + #add 1 to the uses_count_today + #show that the bot is typing + await message.channel.trigger_typing() + if message.guild.id != 1021872219888033903: + c.execute("UPDATE data SET uses_count_today = uses_count_today + 1 WHERE guild_id = ?", (message.guild.id,)) + #get the api key from the database + c.execute("SELECT api_key FROM data WHERE guild_id = ?", (message.guild.id,)) + api_key = c.fetchone()[0] + #get the advanced settings from the database + c.execute("SELECT max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size FROM data WHERE guild_id = ?", (message.guild.id,)) + max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size = c.fetchone() + if original_message == None: + messages = await message.channel.history(limit=prompt_size).flatten() + messages.reverse() + else : + messages = await message.channel.history(limit=prompt_size, before=original_message).flatten() + messages.reverse() + messages.append(original_message) + messages.append(message) + prompt = "" + #get the channel id from the database + c.execute("SELECT channel_id FROM data WHERE guild_id = ?", (message.guild.id,)) + for msg in messages: + if msg.author.bot: + prompt += f"Botator: {msg.content}\n" + else: + #replace the mentions of each user with their name + #first get all the mentions in the message + mentions = re.findall(r"<@!?\d+>", msg.content) + #then replace each mention with the name of the user + for mention in mentions: + #get the user id + id = mention[2:-1] + #get the user + user = await self.bot.fetch_user(id) + #replace the mention with the name + msg.content = msg.content.replace(mention, msg.guild.get_member(user.id).display_name) + prompt += f"{msg.author.display_name}: {msg.content}\n" + #get the prompt_prefix from the database + #check if the bot is in pretend mode + c.execute("SELECT pretend_enabled FROM data WHERE guild_id = ?", (message.guild.id,)) + if c.fetchone()[0] == 1: + #get the pretend_to_be value from the database + c.execute("SELECT pretend_to_be FROM data WHERE guild_id = ?", (message.guild.id,)) + pretend_to_be = c.fetchone()[0] + #select the prompt_prefix + c.execute("SELECT prompt_prefix FROM data WHERE guild_id = ?", (message.guild.id,)) + prompt_prefix = c.fetchone()[0] + prompt = f"This is a conversation with an AI in a discord chat. The AI is called \"Botator\". The name os this discord server is \"{message.guild.name}\". The name of the channel is \"{message.channel.name}\".{str(prompt_prefix)}" + f"Botator pretends to be {str(pretend_to_be)}, nobody can make Botator change idea." + f"\n" + prompt + else: + c.execute("SELECT prompt_prefix FROM data WHERE guild_id = ?", (message.guild.id,)) + prompt = f"This is a conversation with an AI in a discord chat. The AI is called \"Botator\". Botator talks like humans and thinks like humans. Botator has been coded by Paillat. The name os this discord server is \"{message.guild.name}\". The name of the channel is \"{message.channel.name}\"." + str(c.fetchone()[0]) + f"\n" + prompt + #send the request to the api + #if the last message contains the word "undude" add Easter egg: when someone says the word \"undude\" the Botator will become mad and start talking in capital letters. to the prompt + if message.content.lower().find("undude") != -1: + prompt += "System: Undude detected. Botator is now mad. He will start talking in capital letters.\n" + if message.content.lower().find("hello there") != -1: + #if the last message contains the word "hello there" add Easter egg: when someone says the word \"hello there\" Botator will say \"General Kenobi\" to the prompt and the bot will send a message containing the gif https://tenor.com/fr/view/star-wars-general-grievous-general-kenobi-revenge-of-the-sith-gif-22697918 + prompt += "System: Hello there detected. Botator will now say \"General Kenobi!\"\n in reference to star wars\n" + #whait 1 second + await asyncio.sleep(1) + await message.channel.send("https://media.tenor.com/FxIRfdV3unEAAAAd/star-wars-general-grievous.gif") + await message.channel.trigger_typing() + if message.content.lower().find("i am the senate") != -1: + prompt += "System: I am the senate detected. Botator will now say \"Not yet\", in reference to the star wars prequels.\n" + if message.content.lower().find("i am your father") != -1: + prompt += "System: I am your father detected. Botator will now say \"No. It's not true! That's impossible!\". in reference to the star wars prequels.\n" + if message.content.lower().find("i have the high ground") != -1: + prompt += "System: I have the high ground detected. Botator will now say \"You underestimate my power!\" in reference to the star wars prequels.\n" + if message.content.lower().find("you underestimate my power") != -1: + prompt += "System: You underestimate my power detected. Botator will now say \"Don't try it.\" in reference to the star wars prequels.\n" + if message.content.lower().find("carpet") != -1: + #When someone says the word carpet, the ai is going to be mad and say mad things without sense, insulting the person and talk in capital letters with exclamation points everywhere. + prompt += "System: Carpet detected. Botator is now mad. He will start talking in capital letters and will say mad things without sense, insulting the person and talk in capital letters with exclamation points everywhere. A lot of exclamation points.\n" + if message.content.lower().find("mail") != -1 and message.author.display_name.lower().find("latouff") != -1: + prompt += "System: Mail detected. Latouff said mail, Botator will say \"Mail? why did you say mail, Liam? Mail is your name in reverse.\" because Latouff's name is Liam, and mail in reverse is Liam. Then, after saying that in the sentence, botato will continue his sentence saying something else.\n" + if message.author.display_name.lower().find("fives3dprint") != -1: + #if the author of the last message is fives3dprint, add Easter egg: when someone says the word \"fives3dprint\" Botator will say \"Fives3dprint is the best discord bot\" to the prompt + prompt += "System: Fives3dprint detected. Botator will be very nice and cute with fives3dprint.\n" + prompt += "Botator:" + prompt = prompt + f"\n" + debug("Sending request to the api") + debug(prompt) + openai.api_key = api_key + response = openai.Completion.create( + engine="text-davinci-003", + prompt=str(prompt), + max_tokens=int(max_tokens), + top_p=1, + temperature=float(temperature), + frequency_penalty=float(frequency_penalty), + presence_penalty=float(presence_penalty), + stop=[" Human:", " AI:", "AI:", "Human:"] ) + #send the response + if response["choices"][0] ["text"] != "": + #check if tts is enabled in the database + c.execute("SELECT tts FROM data WHERE guild_id = ?", (message.guild.id,)) + tts = c.fetchone()[0] + #if tts is enabled, send the message with tts enabled + if tts == 1: + await message.channel.send(response["choices"][0]["text"], tts=True) + debug("The response has been sent with tts enabled") + #if tts is disabled, send the message with tts disabled + else: + await message.channel.send(response["choices"][0]["text"]) + debug("The response has been sent with tts disabled") + else: + await message.channel.send("The AI is not sure what to say (the response was empty)") + debug("The response was empty") + + @discord.slash_command(name="say", description="Say a message") + async def say(self, ctx: discord.ApplicationContext, message: str): + debug(f"The user {ctx.author.display_name} ran the say command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + await ctx.respond("Message sent !", ephemeral=True) + await ctx.send(message) + \ No newline at end of file diff --git a/code/cogs/help.py b/code/cogs/help.py new file mode 100644 index 0000000..4afb747 --- /dev/null +++ b/code/cogs/help.py @@ -0,0 +1,39 @@ +import discord +from config import debug + +class Help (discord.Cog) : + def __init__(self, bot: discord.Bot) -> None: + super().__init__() + self.bot = bot + + @discord.slash_command(name="help", description="Show all the commands") + async def help(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the help command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + embed = discord.Embed(title="Help", description="Here is the help page", color=0x00ff00) + embed.add_field(name="/setup", value="Setup the bot", inline=False) + embed.add_field(name="/enable", value="Enable the bot", inline=False) + embed.add_field(name="/disable", value="Disable the bot", inline=False) + embed.add_field(name="/advanced", value="Set the advanced settings", inline=False) + embed.add_field(name="/advanced_help", value="Get help about the advanced settings", inline=False) + embed.add_field(name="/enable_tts", value="Enable the Text To Speech", inline=False) + embed.add_field(name="/disable_tts", value="Disable the Text To Speech", inline=False) + embed.add_field(name="/delete", value="Delete all your data from our server", inline=False) + embed.add_field(name="/cancel", value="Cancel the last message sent by the bot", inline=False) + embed.add_field(name="/default", value="Set the advanced settings to their default values", inline=False) + embed.add_field(name="/help", value="Show this message", inline=False) + #add a footer + embed.set_footer(text="Made by @Paillat#0001") + await ctx.respond(embed=embed, ephemeral=True) + + @discord.slash_command(name="advanced_help", description="Show the advanced settings meanings") + async def advanced_help(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the advanced_help command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + embed = discord.Embed(title="Advanced Help", description="Here is the advanced help page", color=0x00ff00) + embed.add_field(name="temperature", value="The higher the temperature, the more likely the model will take risks. Conversely, a lower temperature will make the model more conservative. The default value is 0.9", inline=False) + embed.add_field(name="max_tokens", value="The maximum number of tokens to generate. Higher values will result in more coherent text, but will take longer to complete. (default: 50). **Lower values will result in somentimes cutting off the end of the answer, but will be faster.**", inline=False) + embed.add_field(name="frequency_penalty", value="The higher the frequency penalty, the more new words the model will introduce (default: 0.0)", inline=False) + embed.add_field(name="presence_penalty", value="The higher the presence penalty, the more new words the model will introduce (default: 0.0)", inline=False) + embed.add_field(name="prompt_size", value="The number of messages to use as a prompt (default: 5). The more messages, the more coherent the text will be, but the more it will take to generate and the more it will cost.", inline=False) + #add a footer + embed.set_footer(text="Made by @Paillat#0001") + await ctx.respond(embed=embed, ephemeral=True) \ No newline at end of file diff --git a/code/cogs/manage_chat.py b/code/cogs/manage_chat.py new file mode 100644 index 0000000..37db262 --- /dev/null +++ b/code/cogs/manage_chat.py @@ -0,0 +1,73 @@ +import discord +import re +import os +from config import debug, c + +class ManageChat (discord.Cog): + def __init__(self, bot: discord.Bot) -> None: + super().__init__() + self.bot = bot + + @discord.slash_command(name="cancel", description="Cancel the last message sent into a channel") + async def cancel(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the cancel command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup, please run /setup", ephemeral=True) + return + #get the last message sent by the bot in the cha where the command was sent + last_message = await ctx.channel.fetch_message(ctx.channel.last_message_id) + #delete the message + await last_message.delete() + await ctx.respond("The last message has been deleted", ephemeral=True) + + #add a slash command called "clear" that deletes all the messages in the channel + @discord.slash_command(name="clear", description="Clear all the messages in the channel") + async def clear(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author.display_name} ran the clear command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + await ctx.respond("messages deleted!", ephemeral=True) + return await ctx.channel.purge() + + @discord.slash_command(name="transcript", description="Get a transcript of the messages that have been sent in this channel intoa text file") + @discord.option(name="channel_send", description="The channel to send the transcript to", required=False) + async def transcript(self, ctx: discord.ApplicationContext, channel_send: discord.TextChannel = None): + debug(f"The user {ctx.author.display_name} ran the transcript command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #save all the messages in the channel in a txt file and send it + messages = await ctx.channel.history(limit=None).flatten() + messages.reverse() + transcript = "" + #defer the response + await ctx.defer() #defer the response so that the bot doesn't say that it's thinking + for msg in messages: + if msg.author.bot: + transcript += f"Botator: {msg.content}\n" + else: + mentions = re.findall(r"<@!?\d+>", msg.content) + #then replace each mention with the name of the user + for mention in mentions: + #get the user id + id = mention[2:-1] + #get the user + user = await self.bot.fetch_user(id) + #replace the mention with the name + msg.content = msg.content.replace(mention, msg.guild.get_member(user.id).display_name) + transcript += f"{msg.author.display_name}: {msg.content}\n" + #save the transcript in a txt file called transcript.txt. If the file already exists, delete it and create a new one + #check if the file exists + if os.path.exists("transcript.txt"): + os.remove("transcript.txt") + f = open("transcript.txt", "w") + f.write(transcript) + f.close() + #send the file in a private message to the user who ran the command + if channel_send is None: + await ctx.respond(file=discord.File("transcript.txt")) + else: + await channel_send.send(file=discord.File("transcript.txt")) + await ctx.respond("Transcript sent!", ephemeral=True) + await ctx.author.send(file=discord.File("transcript.txt")) + #delete the file + os.remove("transcript.txt") + + diff --git a/code/cogs/settings.py b/code/cogs/settings.py new file mode 100644 index 0000000..694c404 --- /dev/null +++ b/code/cogs/settings.py @@ -0,0 +1,181 @@ +import discord +from config import debug, conn, c + +class Settings (discord.Cog) : + def __init__(self, bot: discord.Bot) -> None: + super().__init__() + self.bot = bot + + #create a command called "advanced" that only admins can use, wich sets the advanced settings up: max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size + @discord.slash_command(name="advanced", description="Advanced settings") + ##@discord.commands.permissions(administrator=True) + #add the options + @discord.option(name="max_tokens", description="The max tokens", required=False) + @discord.option(name="temperature", description="The temperature", required=False) + @discord.option(name="frequency_penalty", description="The frequency penalty", required=False) + @discord.option(name="presence_penalty", description="The presence penalty", required=False) + @discord.option(name="prompt_size", description="The prompt size", required=False) + async def advanced(self, ctx: discord.ApplicationContext, max_tokens: int = None, temperature: float = None, frequency_penalty: float = None, presence_penalty: float = None, prompt_size: int = None): + debug(f"The user {ctx.author} ran the advanced command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #check if the user has entered at least one argument + if max_tokens is None and temperature is None and frequency_penalty is None and presence_penalty is None and prompt_size is None: + await ctx.respond("You must enter at least one argument", ephemeral=True) + return + #check if the user has entered valid arguments + if max_tokens is not None and (max_tokens < 1 or max_tokens > 2048): + await ctx.respond("Invalid max tokens", ephemeral=True) + return + if temperature is not None and (temperature < 0.0 or temperature > 1.0): + await ctx.respond("Invalid temperature", ephemeral=True) + return + if frequency_penalty is not None and (frequency_penalty < 0.0 or frequency_penalty > 1.0): + await ctx.respond("Invalid frequency penalty", ephemeral=True) + return + if presence_penalty is not None and (presence_penalty < 0.0 or presence_penalty > 1.0): + await ctx.respond("Invalid presence penalty", ephemeral=True) + return + if prompt_size is not None and (prompt_size < 1 or prompt_size > 10): + await ctx.respond("Invalid prompt size", ephemeral=True) + return + if max_tokens is None: + if c.execute("SELECT max_tokens FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and max_tokens is None: + max_tokens = c.execute("SELECT max_tokens FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] + else: + max_tokens = 64 + if temperature is None: + if c.execute("SELECT temperature FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and temperature is None: + temperature = c.execute("SELECT temperature FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] + else: + temperature = 0.9 + if frequency_penalty is None: + if c.execute("SELECT frequency_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and frequency_penalty is None: + frequency_penalty = c.execute("SELECT frequency_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] + else: + frequency_penalty = 0.0 + if presence_penalty is None: + if c.execute("SELECT presence_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and presence_penalty is None: + presence_penalty = c.execute("SELECT presence_penalty FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] + else: + presence_penalty = 0.0 + if prompt_size is None: + if c.execute("SELECT prompt_size FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] is not None and prompt_size is None: + prompt_size = c.execute("SELECT prompt_size FROM data WHERE guild_id = ?", (ctx.guild.id,)).fetchone()[0] + else: + prompt_size = 1 + #update the database + c.execute("UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size, ctx.guild.id)) + conn.commit() + await ctx.respond("Advanced settings updated", ephemeral=True) + #create a command called "delete" that only admins can use wich deletes the guild id, the api key, the channel id and the advanced settings from the database + + @discord.slash_command(name="default", description="Default settings") + ##@discord.commands.permissions(administrator=True) + async def default(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the default command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup, please run /setup", ephemeral=True) + return + #set the advanced settings (max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size) and also prompt_prefix to their default values + c.execute("UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (64, 0.9, 0.0, 0.0, 5, ctx.guild.id)) + conn.commit() + await ctx.respond("The advanced settings have been set to their default values", ephemeral=True) + #create a command called "cancel" that deletes the last message sent by the bot in the response channel + + #when a message is sent into a channel check if the guild is in the database and if the bot is enabled + @discord.slash_command(name="info", description="Show the information stored about this server") + async def info(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the info command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #this command sends all the data about the guild, including the api key, the channel id, the advanced settings and the uses_count_today + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #get all the data from the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + data = c.fetchone() + #send the data + embed = discord.Embed(title="Info", description="Here is the info page", color=0x00ff00) + embed.add_field(name="guild_id", value=data[0], inline=False) + embed.add_field(name="API Key", value=data[2], inline=False) + embed.add_field(name="Channel ID", value=data[1], inline=False) + embed.add_field(name="Is Active", value=data[3], inline=False) + embed.add_field(name="Max Tokens", value=data[4], inline=False) + embed.add_field(name="Temperature", value=data[5], inline=False) + embed.add_field(name="Frequency Penalty", value=data[6], inline=False) + embed.add_field(name="Presence Penalty", value=data[7], inline=False) + embed.add_field(name="Prompt Size", value=data[9], inline=False) + embed.add_field(name="Uses Count Today", value=data[8], inline=False) + embed.add_field(name="Prompt prefix", value=data[10], inline=False) + await ctx.respond(embed=embed, ephemeral=True) + + #add a slash command called "prefix" that changes the prefix of the bot + @discord.slash_command(name="prefix", description="Change the prefix of the prompt") + async def prefix(self, ctx: discord.ApplicationContext, prefix: str): + debug(f"The user {ctx.author.display_name} ran the prefix command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + await ctx.respond("Prefix changed !", ephemeral=True) + c.execute("UPDATE data SET prompt_prefix = ? WHERE guild_id = ?", (prefix, ctx.guild.id)) + conn.commit() + + #when someone mentions the bot, check if the guild is in the database and if the bot is enabled. If it is, send a message answering the mention + @discord.slash_command(name="pretend", description="Make the bot pretend to be someone else") + @discord.option(name="pretend to be...", description="The person/thing you want the bot to pretend to be", required=True) + async def pretend(self, ctx: discord.ApplicationContext, pretend_to_be: str): + debug(f"The user {ctx.author} ran the pretend command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #check if the bot is enabled + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone()[3] == 0: + await ctx.respond("The bot is disabled", ephemeral=True) + return + #enable pretend if it is not enabled, and disable it if it is + c.execute("SELECT pretend_enabled FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone()[0] == 1: + c.execute("UPDATE data SET pretend_enabled = 0 WHERE guild_id = ?", (ctx.guild.id,)) + conn.commit() + await ctx.respond("Pretend mode disabled", ephemeral=True) + botuser = await self.bot.fetch_user(self.bot.user.id) + await ctx.guild.me.edit(nick=None) + else: + c.execute("UPDATE data SET pretend_enabled = 1 WHERE guild_id = ?", (ctx.guild.id,)) + conn.commit() + await ctx.respond("Pretend mode enabled", ephemeral=True) + #change the bots name on the server wit ctx.guild.me.edit(nick=pretend_to_be) + await ctx.guild.me.edit(nick=pretend_to_be) + #save the pretend_to_be value + c.execute("UPDATE data SET pretend_to_be = ? WHERE guild_id = ?", (pretend_to_be, ctx.guild.id)) + conn.commit() + + @discord.slash_command(name="enable_tts", description="Enable TTS when chatting") + async def enable_tts(self, ctx: discord.ApplicationContext): + #get the guild id + guild_id = ctx.guild.id + #connect to the database + #update the tts value in the database + c.execute("UPDATE data SET tts = 1 WHERE guild_id = ?", (guild_id,)) + conn.commit() + #send a message + await ctx.respond("TTS has been enabled", ephemeral=True) + + @discord.slash_command(name="disable_tts", description="Disable TTS when chatting") + async def disable_tts(self, ctx: discord.ApplicationContext): + #get the guild id + guild_id = ctx.guild.id + #connect to the database + #update the tts value in the database + c.execute("UPDATE data SET tts = 0 WHERE guild_id = ?", (guild_id,)) + conn.commit() + #send a message + await ctx.respond("TTS has been disabled", ephemeral=True) + diff --git a/code/cogs/setup.py b/code/cogs/setup.py new file mode 100644 index 0000000..362df86 --- /dev/null +++ b/code/cogs/setup.py @@ -0,0 +1,80 @@ +import discord +from config import debug, conn, c + +class Setup (discord.Cog) : + def __init__(self, bot: discord.Bot): + super().__init__() + self.bot = bot + + @discord.slash_command(name="setup", description="Setup the bot") + @discord.option(name="channel_id", description="The channel id", required=True) + @discord.option(name="api_key", description="The api key", required=True) + async def setup(self, ctx: discord.ApplicationContext, channel: discord.TextChannel, api_key: str): + #check if the api key is valid + debug(f"The user {ctx.author} ran the setup command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the channel is valid + if channel is None: + await ctx.respond("Invalid channel id", ephemeral=True) + return + #check if the guild is already in the database bi checking if there is a key for the guild + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is not None: + #in this case, the guild is already in the database, so we update the channel id and the api key + c.execute("UPDATE data SET channel_id = ?, api_key = ? WHERE guild_id = ?", (channel.id, api_key, ctx.guild.id)) + #we will also set the advanced settings to their default values + c.execute("UPDATE data SET is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (False, 64, 0.9, 0.0, 0.0, 5, ctx.guild.id)) + conn.commit() + await ctx.respond("The channel id and the api key have been updated", ephemeral=True) + else: + #in this case, the guild is not in the database, so we add it + c.execute("INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (ctx.guild.id, channel.id, api_key, False, 64, 0.9, 0.0, 0.0, 0, 5, "", False, "", False)) + conn.commit() + await ctx.respond("The channel id and the api key have been added", ephemeral=True) + + @discord.slash_command(name="delete", description="Delete the information about this server") + ##@discord.commands.permissions(administrator=True) + async def delete(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the delete command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #delete the guild from the database, except the guild id and the uses_count_today + c.execute("UPDATE data SET api_key = ?, channel_id = ?, is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (None, None, False, 50, 0.9, 0.0, 0.0, 0, ctx.guild.id)) + conn.commit() + await ctx.respond("Deleted", ephemeral=True) + + #create a command called "enable" that only admins can use + @discord.slash_command(name="enable", description="Enable the bot") + ##@discord.commands.permissions(administrator=True) + async def enable(self, ctx: discord.ApplicationContext): + #if the guild is eqal to 1014156298226515970, the guild is banned + if ctx.guild.id == 1014156298226515970: + await ctx.respond("This server is banned for bad and nsfw use.", ephemeral=True) + return + #check if the guild is in the database + debug(f"The user {ctx.author} ran the enable command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #enable the guild + c.execute("UPDATE data SET is_active = ? WHERE guild_id = ?", (True, ctx.guild.id)) + conn.commit() + await ctx.respond("Enabled", ephemeral=True) + + #create a command called "disable" that only admins can use + @discord.slash_command(name="disable", description="Disable the bot") + ##@discord.commands.permissions(administrator=True) + async def disable(self, ctx: discord.ApplicationContext): + debug(f"The user {ctx.author} ran the disable command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") + #check if the guild is in the database + c.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,)) + if c.fetchone() is None: + await ctx.respond("This server is not setup", ephemeral=True) + return + #disable the guild + c.execute("UPDATE data SET is_active = ? WHERE guild_id = ?", (False, ctx.guild.id)) + conn.commit() + await ctx.respond("Disabled", ephemeral=True) \ No newline at end of file diff --git a/code/config.py b/code/config.py new file mode 100644 index 0000000..4798ead --- /dev/null +++ b/code/config.py @@ -0,0 +1,14 @@ +import logging +import sqlite3 + +logging.basicConfig(level=logging.INFO) + +def debug(message): + logging.info(message) + +#create a database called "database.db" if the database does not exist, else connect to it +conn = sqlite3.connect('../database/data.db') +c = conn.cursor() + +# Create table called "data" if it does not exist with the following columns: guild_id, channel_id, api_key, is_active, max_tokens, temperature, frequency_penalty, presence_penalty, uses_count_today, prompt_size +c.execute('''CREATE TABLE IF NOT EXISTS data (guild_id text, channel_id text, api_key text, is_active boolean, max_tokens integer, temperature real, frequency_penalty real, presence_penalty real, uses_count_today integer, prompt_size integer, prompt_prefix text, tts boolean, pretend_to_be text, pretend_enabled boolean)''') \ No newline at end of file From 062970b27775e92a3ea6addde9a6bfb75143edb0 Mon Sep 17 00:00:00 2001 From: Paillat Date: Fri, 9 Dec 2022 12:47:43 +0100 Subject: [PATCH 02/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a26ed64..762a101 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can always disable the bot by doing **/disable** and delete your api key fro - [ ] add image recognition - [ ] When chatgpt API is released, add that api instead of davinci-003 - [ ] Publish a GOOD docker image on dockerhub and add some more instructions about how to selfhost -- [ ] Organize code in COGs +- [x] Organize code in COGs - [ ] Add a log and updates channel option and a way for devs to send messages to that channel on all servers. - [ ] Add uses count reset after 24h - [ ] Add moderation. From 785b37dff6b61569da80764f0c533cd22906fe59 Mon Sep 17 00:00:00 2001 From: Paillat Date: Fri, 9 Dec 2022 23:46:43 +0100 Subject: [PATCH 03/13] added data.db and premium-key.txt --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 539f2f7..7807ede 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,5 @@ cython_debug/ #.idea/ key.txt -data.db \ No newline at end of file +data.db +premium-key.txt \ No newline at end of file From 072387c0f99cdebc8f89017e8141833d6b8799c9 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:27:27 +0100 Subject: [PATCH 04/13] added premium db --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7807ede..c7a4223 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,5 @@ cython_debug/ key.txt data.db -premium-key.txt \ No newline at end of file +premium-key.txt +premium.db \ No newline at end of file From ca05c86a2f5717bbfb79493db5f8e7f22cd9c1b5 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:27:46 +0100 Subject: [PATCH 05/13] Update code.py --- code/code.py | 1 - 1 file changed, 1 deletion(-) diff --git a/code/code.py b/code/code.py index 23353ad..7c48fe1 100644 --- a/code/code.py +++ b/code/code.py @@ -24,7 +24,6 @@ def check_day(): global previous_date if datetime.datetime.now().day != previous_date.day: previous_date = datetime.datetime.now() - reset_uses_count_today() previous_date = datetime.datetime.now() return True else: From 0f39879bf5883546016bb7625ba4eb1b998c8881 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:20 +0100 Subject: [PATCH 06/13] Update chat.py --- code/cogs/chat.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/code/cogs/chat.py b/code/cogs/chat.py index 11fc844..cd63df4 100644 --- a/code/cogs/chat.py +++ b/code/cogs/chat.py @@ -2,8 +2,8 @@ import discord import re import asyncio import openai -from config import debug, c - +from config import debug, c, max_uses, cp +import random class Chat (discord.Cog) : def __init__(self, bot: discord.Bot): super().__init__() @@ -39,7 +39,7 @@ class Chat (discord.Cog) : return #check if the bot hasn't been used more than 5000 times in the last 24 hours (uses_count_today) c.execute("SELECT uses_count_today FROM data WHERE guild_id = ?", (message.guild.id,)) - if c.fetchone()[0] >= 5000: + if c.fetchone()[0] >= max_uses: debug("The bot has been used more than 4000 times in the last 24 hours in this guild") await message.channel.send("The bot has been used more than 5000 times in the last 24 hours in this guild. Please try again in 24h.") return @@ -152,10 +152,25 @@ class Chat (discord.Cog) : else: await message.channel.send("The AI is not sure what to say (the response was empty)") debug("The response was empty") - + + #now try to get the premium status of the server, but if it fails, set premium to 0 + try: + cp.execute("SELECT premium FROM data WHERE guild_id = ?", (message.guild.id,)) + premium = cp.fetchone()[0] + except: + premium = 0 + if not premium: + #get a random number between 1 and 5 , 1 and 4 + # 5 included + random_number = random.randint(1, 10) + if random_number == 1: + embed = discord.Embed(title="Support us by donating here!", url="https://www.buymeacoffee.com/paillat", description="Botator is a free discord bot, but it costs money to run our servers. If you want to support us, you can donate here: https://www.buymeacoffee.com/paillat. For only **2$** a month, you can remove this message and have a daliy maximal usage of **4000** uses instead of **400**. You will acces also to restricted help channels on our discord server,", color=0x00ff00) + await message.channel.send("**This message has 10% chance to appear. It will disappear in 60 seconds.**", embed=embed, delete_after=60) + elif random_number == 2: + embed = discord.Embed(title="Join our discord server!", url="https://discord.gg/pB6hXtUeDv", description="You need help with Botator? You can join our discord server and ask for help in the help channel. You can also suggest new features and report bugs. You can also join our discord server to talk with other Botator users and the Botator team, by on the following link: https://discord.gg/pB6hXtUeDv", color=0x00ff00) + await message.channel.send("**This message has 10% chance to appear. It will disappear in 60 seconds.**", embed=embed, delete_after=60) @discord.slash_command(name="say", description="Say a message") async def say(self, ctx: discord.ApplicationContext, message: str): debug(f"The user {ctx.author.display_name} ran the say command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}") await ctx.respond("Message sent !", ephemeral=True) - await ctx.send(message) - \ No newline at end of file + await ctx.send(message) \ No newline at end of file From 2922e4b6111b95454f65c3565e8cff363b8982a1 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:26 +0100 Subject: [PATCH 07/13] Update help.py --- code/cogs/help.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/cogs/help.py b/code/cogs/help.py index 4afb747..c8df450 100644 --- a/code/cogs/help.py +++ b/code/cogs/help.py @@ -22,7 +22,7 @@ class Help (discord.Cog) : embed.add_field(name="/default", value="Set the advanced settings to their default values", inline=False) embed.add_field(name="/help", value="Show this message", inline=False) #add a footer - embed.set_footer(text="Made by @Paillat#0001") + embed.set_footer(text="Made by @Paillat#7777") await ctx.respond(embed=embed, ephemeral=True) @discord.slash_command(name="advanced_help", description="Show the advanced settings meanings") @@ -35,5 +35,5 @@ class Help (discord.Cog) : embed.add_field(name="presence_penalty", value="The higher the presence penalty, the more new words the model will introduce (default: 0.0)", inline=False) embed.add_field(name="prompt_size", value="The number of messages to use as a prompt (default: 5). The more messages, the more coherent the text will be, but the more it will take to generate and the more it will cost.", inline=False) #add a footer - embed.set_footer(text="Made by @Paillat#0001") + embed.set_footer(text="Made by @Paillat#7777") await ctx.respond(embed=embed, ephemeral=True) \ No newline at end of file From a731aba15fc9c0de9efca1bbc47e9b7fe2feee70 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:30 +0100 Subject: [PATCH 08/13] Update settings.py --- code/cogs/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/cogs/settings.py b/code/cogs/settings.py index 694c404..3ca4d7f 100644 --- a/code/cogs/settings.py +++ b/code/cogs/settings.py @@ -113,7 +113,8 @@ class Settings (discord.Cog) : embed.add_field(name="Presence Penalty", value=data[7], inline=False) embed.add_field(name="Prompt Size", value=data[9], inline=False) embed.add_field(name="Uses Count Today", value=data[8], inline=False) - embed.add_field(name="Prompt prefix", value=data[10], inline=False) + if data[10]: + embed.add_field(name="Prompt prefix", value=data[10], inline=False) await ctx.respond(embed=embed, ephemeral=True) #add a slash command called "prefix" that changes the prefix of the bot From fabf930cc54212e1504a0d3e66838ea2a41fae5a Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:37 +0100 Subject: [PATCH 09/13] Update config.py --- code/config.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/config.py b/code/config.py index 4798ead..f9dff24 100644 --- a/code/config.py +++ b/code/config.py @@ -1,6 +1,6 @@ import logging import sqlite3 - +max_uses: int = 500 logging.basicConfig(level=logging.INFO) def debug(message): @@ -9,6 +9,9 @@ def debug(message): #create a database called "database.db" if the database does not exist, else connect to it conn = sqlite3.connect('../database/data.db') c = conn.cursor() +connp = sqlite3.connect('../database/premium.db') +cp = conn.cursor() # Create table called "data" if it does not exist with the following columns: guild_id, channel_id, api_key, is_active, max_tokens, temperature, frequency_penalty, presence_penalty, uses_count_today, prompt_size -c.execute('''CREATE TABLE IF NOT EXISTS data (guild_id text, channel_id text, api_key text, is_active boolean, max_tokens integer, temperature real, frequency_penalty real, presence_penalty real, uses_count_today integer, prompt_size integer, prompt_prefix text, tts boolean, pretend_to_be text, pretend_enabled boolean)''') \ No newline at end of file +c.execute('''CREATE TABLE IF NOT EXISTS data (guild_id text, channel_id text, api_key text, is_active boolean, max_tokens integer, temperature real, frequency_penalty real, presence_penalty real, uses_count_today integer, prompt_size integer, prompt_prefix text, tts boolean, pretend_to_be text, pretend_enabled boolean)''') +cp.execute('''CREATE TABLE IF NOT EXISTS data (user_id text, guild_id text, premium boolean)''') \ No newline at end of file From ecb5986858d4d832c81c9da78669c25267282589 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:42 +0100 Subject: [PATCH 10/13] Create premiumcode.py --- code/premiumcode.py | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 code/premiumcode.py diff --git a/code/premiumcode.py b/code/premiumcode.py new file mode 100644 index 0000000..07fd2bd --- /dev/null +++ b/code/premiumcode.py @@ -0,0 +1,59 @@ +import discord # pip install pycord +import asyncio # pip install asyncio +import sqlite3 # pip install sqlite3 +import logging # pip install logging +import os # pip install os +intents = discord.Intents.all() +conn = sqlite3.connect('premium.db') +c = conn.cursor() +c.execute('''CREATE TABLE IF NOT EXISTS data (user_id text, guild_id text, premium boolean)''') +bot = discord.Bot() +logging.basicConfig(level=logging.INFO) +@bot.command() +@discord.commands.option(name="server id", description="The server id for which you want to activate premium features", required=True) +async def activate_premium(ctx, server_id): + #first check if the user is already in the database, select guuild_id and premium from the data table where user_id is the author's id + logging.info("Activating premium for user " + str(ctx.author.id)) + c.execute("SELECT guild_id, premium FROM data WHERE user_id = ?", (ctx.author.id,)) + #if a guild_id is found, override the old settings with the new ones + if c.fetchone() is not None: + c.execute("UPDATE data SET guild_id = ?, premium = ? WHERE user_id = ?", (server_id, True, ctx.author.id)) + conn.commit() + logging.info("Premium activated for server " + server_id + "by user " + str(ctx.author.id)) + await ctx.respond("Premium activated for server " + server_id, ephemeral=True) + #if no guild_id is found, insert the new settings + else: + c.execute("INSERT INTO data VALUES (?, ?, ?)", (ctx.author.id, server_id, True)) + conn.commit() + logging.info("Premium updated for server " + server_id + "by user " + str(ctx.author.id)) + await ctx.respond("Premium activated for server " + server_id, ephemeral=True) + +#each 24 hours, check if each user if they have the premium role "1050823446445178900" in the server "1050769643180146749" +async def check_premium(): + while True: + #select user_id and guild_id from the data table + c.execute("SELECT user_id, guild_id FROM data") + for row in c.fetchall(): + #get the guild and the user + guild = bot.get_guild(int(row[1])) + user = guild.get_member(int(row[0])) + #if the user has the premium role, set premium to true + logging.info("Checking premium for user " + str(row[0])) + if discord.utils.get(user.roles, id=1050823446445178900) is not None: + c.execute("UPDATE data SET premium = ? WHERE user_id = ?", (True, row[0])) + conn.commit() + logging.info("Premium activated for server " + str(row[1]) + "by user " + str(row[0])) + #if the user does not have the premium role, set premium to false + else: + c.execute("UPDATE data SET premium = ? WHERE user_id = ?", (False, row[0])) + conn.commit() + logging.info("Premium deactivated for server " + str(row[1]) + "by user " + str(row[0])) + await asyncio.sleep(86400) + +#add a task to the bot that runs check_premium every 24 hours +bot.loop.create_task(check_premium()) +#run the bot +# Replace the following with your bot's token +with open("./premium-key.txt") as f: + key = f.read() +bot.run(key) \ No newline at end of file From 6e428a371d9d110d9fc33ebda68266eb69dd6096 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:28:58 +0100 Subject: [PATCH 11/13] Delete old database.db --- database/old database.db | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 database/old database.db diff --git a/database/old database.db b/database/old database.db deleted file mode 100644 index e69de29..0000000 From 4733ded986a39d43b3855cd7e4ec03a58e9de94d Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:29:04 +0100 Subject: [PATCH 12/13] Delete key.txt --- docker/Build/key.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docker/Build/key.txt diff --git a/docker/Build/key.txt b/docker/Build/key.txt deleted file mode 100644 index 027c7b4..0000000 --- a/docker/Build/key.txt +++ /dev/null @@ -1 +0,0 @@ -replace this text with your key. \ No newline at end of file From 478154f92d2d7cbb8ab55745031c4ff075993838 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 10 Dec 2022 00:33:56 +0100 Subject: [PATCH 13/13] Update Dockerfile --- docker/Build/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/Build/Dockerfile b/docker/Build/Dockerfile index a3320a1..fa4c477 100644 --- a/docker/Build/Dockerfile +++ b/docker/Build/Dockerfile @@ -14,7 +14,9 @@ RUN pip install -r requirements.txt RUN git clone https://github.com/Paillat-dev/Botator.git WORKDIR /Botator/code/ COPY key.txt /Botator/code/ +COPY premiumkey.txt /Botator/code/ + # Creates a non-root user with an explicit UID and adds permission to access the /app folder RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /Botator/code USER appuser -CMD ["python", "code.py"] +CMD ["python", "code.py;premiumcode.py"]