commit
c1a6978a9c
3 changed files with 177 additions and 54 deletions
24
CONTRIBUTING.md
Normal file
24
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
# Contributing Guidelines
|
||||||
|
|
||||||
|
Thank you for considering contributing to our project! Please read the following guidelines before making your contribution.
|
||||||
|
|
||||||
|
### New Feature Pull Requests
|
||||||
|
|
||||||
|
- To contribute a new feature, please make sure to send your pull request to the `dev` branch.
|
||||||
|
- Your pull request should include a detailed description of the new feature you are adding and any relevant documentation.
|
||||||
|
- Please also make sure your code follows PEP8 standards.
|
||||||
|
|
||||||
|
### Bugfix Pull Requests
|
||||||
|
|
||||||
|
- If you are fixing a bug or issue, please send your pull request to the `main` branch.
|
||||||
|
- Describe the bug or issue that you are fixing in your pull request and include any relevant information.
|
||||||
|
- Make sure to test your fix thoroughly and include any necessary documentation.
|
||||||
|
|
||||||
|
### General Guidelines
|
||||||
|
|
||||||
|
- Please make sure to be a good person in all interactions and contributions.
|
||||||
|
- Be respectful and polite when communicating with other contributors and maintain a positive and constructive attitude.
|
||||||
|
- If you have any questions or need help with your contribution, feel free to reach out to our team.
|
||||||
|
|
||||||
|
We appreciate your interest in improving our project and look forward to reviewing your contributions!
|
||||||
205
cogs/chatgpt.py
205
cogs/chatgpt.py
|
|
@ -8,6 +8,7 @@ import aiofiles
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands, tasks
|
from discord.ext import commands, tasks
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
class ChatGPT(commands.Cog):
|
class ChatGPT(commands.Cog):
|
||||||
|
|
||||||
|
|
@ -16,7 +17,6 @@ class ChatGPT(commands.Cog):
|
||||||
self.API_KEY = os.getenv("openai.api_key")
|
self.API_KEY = os.getenv("openai.api_key")
|
||||||
self.working_dir = "tmp/chatgpt/"
|
self.working_dir = "tmp/chatgpt/"
|
||||||
self.data_dir = "data/chatgpt/"
|
self.data_dir = "data/chatgpt/"
|
||||||
self.premium_role = 1200943915579228170
|
|
||||||
self.folder_setup()
|
self.folder_setup()
|
||||||
self.remind_me_loop.start()
|
self.remind_me_loop.start()
|
||||||
self.http_session = self.create_aiohttp_session()
|
self.http_session = self.create_aiohttp_session()
|
||||||
|
|
@ -36,7 +36,8 @@ class ChatGPT(commands.Cog):
|
||||||
self.data_dir,
|
self.data_dir,
|
||||||
self.data_dir + "config",
|
self.data_dir + "config",
|
||||||
self.data_dir + "logs",
|
self.data_dir + "logs",
|
||||||
self.data_dir + "dalle"
|
self.data_dir + "dalle",
|
||||||
|
self.data_dir + "costs"
|
||||||
]
|
]
|
||||||
|
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
|
|
@ -46,6 +47,116 @@ class ChatGPT(commands.Cog):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception(f"ChatGPT failed to make directories: {e}")
|
self.logger.exception(f"ChatGPT failed to make directories: {e}")
|
||||||
|
|
||||||
|
def text_cost_calc(self, model, input_tokens, output_tokens):
|
||||||
|
cost_table = {"gpt-3.5-turbo":{"input_tokens":0.0000005,"output_tokens":0.0000015},
|
||||||
|
"gpt-4-turbo-preview":{"input_tokens":0.00001,"output_tokens":0.00003},
|
||||||
|
"gpt-4-vision-preview":{"input_tokens":0.00001,"output_tokens":0.00003}
|
||||||
|
}
|
||||||
|
input_cost = cost_table[model]["input_tokens"] * input_tokens
|
||||||
|
output_cost = cost_table[model]["output_tokens"] * output_tokens
|
||||||
|
cost = input_cost + output_cost
|
||||||
|
return cost
|
||||||
|
|
||||||
|
def add_cost(self, category: str, cost: float):
|
||||||
|
day = time.strftime("%d")
|
||||||
|
month = time.strftime("%B")
|
||||||
|
year = time.strftime("%Y")
|
||||||
|
filepath = f"{self.data_dir}costs/{month}_{day}_{year}.json"
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
json.dump({},f)
|
||||||
|
with open(filepath, "r") as f:
|
||||||
|
costs_dict = json.loads(f.readline())
|
||||||
|
if category not in costs_dict:
|
||||||
|
costs_dict[category] = cost
|
||||||
|
else:
|
||||||
|
costs_dict[category] += cost
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
json.dump(costs_dict,f)
|
||||||
|
|
||||||
|
async def graph_cost(self, graph_title, costs_per_day):
|
||||||
|
plt.plot(costs_per_day.values())
|
||||||
|
plt.title(f"{graph_title} Costs")
|
||||||
|
plt.xlabel("Day")
|
||||||
|
plt.ylabel("Cost")
|
||||||
|
plt.savefig(f"{self.data_dir}costs/{graph_title}_costs.png")
|
||||||
|
plt.close()
|
||||||
|
return f"{self.data_dir}costs/{graph_title}_costs.png"
|
||||||
|
|
||||||
|
async def graph_category(self, graph_title, cost_per_category):
|
||||||
|
bar_container = plt.barh(list(cost_per_category.keys()), cost_per_category.values())
|
||||||
|
plt.title(f"{graph_title} Costs")
|
||||||
|
plt.xlabel("Category")
|
||||||
|
plt.ylabel("Cost")
|
||||||
|
plt.bar_label(bar_container)
|
||||||
|
plt.savefig(f"{self.data_dir}costs/{graph_title}_categories.png")
|
||||||
|
plt.close()
|
||||||
|
return f"{self.data_dir}costs/{graph_title}_categories.png"
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name="costs",
|
||||||
|
brief="Get the costs for a given month and year.",
|
||||||
|
help="Get the costs for a given month and year. If no month or year is given, it will default to the current month and year.",
|
||||||
|
aliases=["cost"],
|
||||||
|
usage="costs [month] [year]",
|
||||||
|
)
|
||||||
|
async def costs(self, ctx, month=time.strftime("%B"), year=time.strftime("%Y")):
|
||||||
|
print('working')
|
||||||
|
total_cost = 0
|
||||||
|
cost_per_day = {}
|
||||||
|
for x in range(1,32):
|
||||||
|
daily_cost = 0
|
||||||
|
if x < 10:
|
||||||
|
x = f"0{x}"
|
||||||
|
filepath = f"{self.data_dir}costs/{month}_{x}_{year}.json"
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
with open(filepath, "r") as f:
|
||||||
|
costs_dict = json.loads(f.readline())
|
||||||
|
for category, cost in costs_dict.items():
|
||||||
|
total_cost += cost
|
||||||
|
daily_cost += cost
|
||||||
|
cost_per_day[x] = daily_cost
|
||||||
|
rounded_total = round(total_cost, 2)
|
||||||
|
if rounded_total == 0:
|
||||||
|
await ctx.send(f"No data for {month} {year}")
|
||||||
|
else:
|
||||||
|
graph_title = f"{month} {year}"
|
||||||
|
await ctx.send(f"Total cost for {month} {year}: ${rounded_total}", file=discord.File(await self.graph_cost(graph_title, cost_per_day)))
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name="category_costs",
|
||||||
|
brief="Get the costs per category for a given month and year.",
|
||||||
|
help="Get the costs per category for a given month and year. If no month or year is given, it will default to the current month and year.",
|
||||||
|
aliases=["category_cost"],
|
||||||
|
usage="category_costs [month] [year] [day]",
|
||||||
|
)
|
||||||
|
async def category_costs(self, ctx, month=time.strftime("%B"), year=time.strftime("%Y"), day=None):
|
||||||
|
total_cost = 0
|
||||||
|
cost_per_category = {}
|
||||||
|
if day == None:
|
||||||
|
day_range = range(1,32)
|
||||||
|
else:
|
||||||
|
day_range = [int(day)]
|
||||||
|
for x in day_range:
|
||||||
|
daily_cost = 0
|
||||||
|
if x < 10:
|
||||||
|
x = f"0{x}"
|
||||||
|
filepath = f"{self.data_dir}costs/{month}_{x}_{year}.json"
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
with open(filepath, "r") as f:
|
||||||
|
costs_dict = json.loads(f.readline())
|
||||||
|
for category, cost in costs_dict.items():
|
||||||
|
if category not in cost_per_category:
|
||||||
|
cost_per_category[category] = cost
|
||||||
|
else:
|
||||||
|
cost_per_category[category] += cost
|
||||||
|
total_cost += cost
|
||||||
|
graph_title = f"{month} {year}"
|
||||||
|
rounded_total = round(total_cost, 2)
|
||||||
|
if rounded_total == 0:
|
||||||
|
await ctx.send(f"No data for {month} {year}")
|
||||||
|
else:
|
||||||
|
await ctx.send(f"Total cost for {month} {year}: ${rounded_total}", file=discord.File(await self.graph_category(graph_title, cost_per_category)))
|
||||||
|
|
||||||
def create_channel_config(self, filepath):
|
def create_channel_config(self, filepath):
|
||||||
config_dict = {
|
config_dict = {
|
||||||
|
|
@ -102,6 +213,10 @@ class ChatGPT(commands.Cog):
|
||||||
async with self.http_session.post(url, json=data, headers=self.headers) as resp:
|
async with self.http_session.post(url, json=data, headers=self.headers) as resp:
|
||||||
response_data = await resp.json()
|
response_data = await resp.json()
|
||||||
response = response_data['choices'][0]['message']['content']
|
response = response_data['choices'][0]['message']['content']
|
||||||
|
input_tokens = response_data['usage']['prompt_tokens']
|
||||||
|
output_tokens = response_data['usage']['completion_tokens']
|
||||||
|
cost = self.text_cost_calc(model, input_tokens, output_tokens)
|
||||||
|
self.add_cost(model, cost)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
|
|
@ -190,15 +305,12 @@ class ChatGPT(commands.Cog):
|
||||||
brief="Get an answer"
|
brief="Get an answer"
|
||||||
)
|
)
|
||||||
async def question_gpt4(self, ctx):
|
async def question_gpt4(self, ctx):
|
||||||
if ctx.author.get_role(self.premium_role):
|
await ctx.send("One moment, let me think...")
|
||||||
await ctx.send("One moment, let me think...")
|
question = ctx.message.content.split(" ", maxsplit=1)[1]
|
||||||
question = ctx.message.content.split(" ", maxsplit=1)[1]
|
answer = await self.answer_question(question, "gpt-4-turbo-preview")
|
||||||
answer = await self.answer_question(question, "gpt-4-turbo-preview")
|
chunks = [answer[i:i+1999] for i in range(0, len(answer), 1999)]
|
||||||
chunks = [answer[i:i+1999] for i in range(0, len(answer), 1999)]
|
for chunk in chunks:
|
||||||
for chunk in chunks:
|
await ctx.send(chunk)
|
||||||
await ctx.send(chunk)
|
|
||||||
else:
|
|
||||||
await ctx.send("Sorry you must be a premium member to use this command. (!donate)")
|
|
||||||
|
|
||||||
async def dalle_api_call(self, prompt: str, model: str="dall-e-2", quality: str="standard", size: str="1024x1024") -> tuple:
|
async def dalle_api_call(self, prompt: str, model: str="dall-e-2", quality: str="standard", size: str="1024x1024") -> tuple:
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -228,25 +340,22 @@ class ChatGPT(commands.Cog):
|
||||||
|
|
||||||
|
|
||||||
async def generate_dalle_image(self, ctx, model, quality="standard", size="1024x1024") -> None:
|
async def generate_dalle_image(self, ctx, model, quality="standard", size="1024x1024") -> None:
|
||||||
if ctx.author.get_role(self.premium_role):
|
prompt = ctx.message.content.split(" ", maxsplit=1)[1]
|
||||||
prompt = ctx.message.content.split(" ", maxsplit=1)[1]
|
await ctx.send(f"Please be patient this may take some time! Generating: {prompt}.")
|
||||||
await ctx.send(f"Please be patient this may take some time! Generating: {prompt}.")
|
resp_status, resp = await self.dalle_api_call(prompt, model=model, quality=quality, size=size)
|
||||||
resp_status, resp = await self.dalle_api_call(prompt, model=model, quality=quality, size=size)
|
if resp_status != 200:
|
||||||
if resp_status != 200:
|
await ctx.send(f"Error generating image: {resp_status}: {resp}")
|
||||||
await ctx.send(f"Error generating image: {resp_status}: {resp}")
|
return
|
||||||
return
|
my_filename = str(time.time_ns()) + ".png"
|
||||||
my_filename = str(time.time_ns()) + ".png"
|
image_filepath = f"{self.data_dir}dalle/{my_filename}"
|
||||||
image_filepath = f"{self.data_dir}dalle/{my_filename}"
|
await self.download_image(resp, image_filepath)
|
||||||
await self.download_image(resp, image_filepath)
|
with open(image_filepath, "rb") as fh:
|
||||||
with open(image_filepath, "rb") as fh:
|
f = discord.File(fh, filename=image_filepath)
|
||||||
f = discord.File(fh, filename=image_filepath)
|
prompt = prompt.replace('\n',' ')
|
||||||
prompt = prompt.replace('\n',' ')
|
log_data = f'Author: {ctx.author.name}, Prompt: {prompt}, Filename: {my_filename}\n'
|
||||||
log_data = f'Author: {ctx.author.name}, Prompt: {prompt}, Filename: {my_filename}\n'
|
with open(f"{self.data_dir}logs/dalle3.log", 'a') as log_filepath:
|
||||||
with open(f"{self.data_dir}logs/dalle3.log", 'a') as log_filepath:
|
log_filepath.writelines(log_data)
|
||||||
log_filepath.writelines(log_data)
|
await ctx.send(f'Generated by: {ctx.author.name}\nPrompt: {prompt}', file=f)
|
||||||
await ctx.send(f'Generated by: {ctx.author.name}\nPrompt: {prompt}', file=f)
|
|
||||||
else:
|
|
||||||
await ctx.send("Sorry you must be a premium member to use this command. (!donate)")
|
|
||||||
|
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
|
|
@ -255,6 +364,7 @@ class ChatGPT(commands.Cog):
|
||||||
brief="Generate Image"
|
brief="Generate Image"
|
||||||
)
|
)
|
||||||
async def dalle2(self, ctx):
|
async def dalle2(self, ctx):
|
||||||
|
self.add_cost("dalle2", 0.02)
|
||||||
await self.generate_dalle_image(ctx, model="dall-e-2")
|
await self.generate_dalle_image(ctx, model="dall-e-2")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -265,6 +375,7 @@ class ChatGPT(commands.Cog):
|
||||||
aliases = ['dalle']
|
aliases = ['dalle']
|
||||||
)
|
)
|
||||||
async def dalle3(self, ctx):
|
async def dalle3(self, ctx):
|
||||||
|
self.add_cost("dalle3", 0.04)
|
||||||
await self.generate_dalle_image(ctx, model="dall-e-3")
|
await self.generate_dalle_image(ctx, model="dall-e-3")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -274,6 +385,7 @@ class ChatGPT(commands.Cog):
|
||||||
brief="Generate HD Image",
|
brief="Generate HD Image",
|
||||||
)
|
)
|
||||||
async def dalle3hd(self, ctx):
|
async def dalle3hd(self, ctx):
|
||||||
|
self.add_cost("dalle3hd", 0.08)
|
||||||
await self.generate_dalle_image(ctx, model="dall-e-3", quality="hd", size="1792x1024")
|
await self.generate_dalle_image(ctx, model="dall-e-3", quality="hd", size="1792x1024")
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
|
|
@ -415,30 +527,17 @@ class ChatGPT(commands.Cog):
|
||||||
async with ctx.channel.typing():
|
async with ctx.channel.typing():
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
prompt = f"You are a {channel_vars['personality']} chat bot named Sparkytron 3000 created by @phixxy.com. Your personality should be {channel_vars['personality']}. You are currently in a {channel_vars['channel_topic']} chatroom. The message history is: {chat_history_string}"
|
prompt = f"You are a {channel_vars['personality']} chat bot named Sparkytron 3000 created by @phixxy.com. Your personality should be {channel_vars['personality']}. You are currently in a {channel_vars['channel_topic']} chatroom. The message history is: {chat_history_string}"
|
||||||
|
response = await self.answer_question(prompt)
|
||||||
|
if "Sparkytron 3000:" in response[0:17]:
|
||||||
|
response = response.replace("Sparkytron 3000:", "")
|
||||||
|
max_len = 1999
|
||||||
|
if len(response) > max_len:
|
||||||
|
messages=[response[y-max_len:y] for y in range(max_len, len(response)+max_len,max_len)]
|
||||||
|
else:
|
||||||
|
messages=[response]
|
||||||
|
for message in messages:
|
||||||
|
await ctx.channel.send(message)
|
||||||
|
|
||||||
data = {
|
|
||||||
"model": "gpt-3.5-turbo",
|
|
||||||
"messages": [{"role": "user", "content": prompt}]
|
|
||||||
}
|
|
||||||
|
|
||||||
url = "https://api.openai.com/v1/chat/completions"
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with self.http_session.post(url, json=data, headers=self.headers) as resp:
|
|
||||||
response_data = await resp.json()
|
|
||||||
response = response_data['choices'][0]['message']['content']
|
|
||||||
if "Sparkytron 3000:" in response[0:17]:
|
|
||||||
response = response.replace("Sparkytron 3000:", "")
|
|
||||||
max_len = 1999
|
|
||||||
if len(response) > max_len:
|
|
||||||
messages=[response[y-max_len:y] for y in range(max_len, len(response)+max_len,max_len)]
|
|
||||||
else:
|
|
||||||
messages=[response]
|
|
||||||
for message in messages:
|
|
||||||
await ctx.channel.send(message)
|
|
||||||
|
|
||||||
except Exception as error:
|
|
||||||
self.logger.exception("Problem with chat_response in chatgpt")
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_reaction_add(self, reaction, user):
|
async def on_reaction_add(self, reaction, user):
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class Donate(commands.Cog):
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.donation_link = "https://patreon.com/soullesssurvival"
|
self.donation_link = "https://patreon.com/phixxy"
|
||||||
self.data_dir = "data/donate/"
|
self.data_dir = "data/donate/"
|
||||||
self.donor_file = "data/donate/supporters.txt"
|
self.donor_file = "data/donate/supporters.txt"
|
||||||
self.folder_setup()
|
self.folder_setup()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue