diff --git a/cord/accounts.py b/cord/accounts.py index 6ea03ea..1312cdc 100644 --- a/cord/accounts.py +++ b/cord/accounts.py @@ -22,26 +22,37 @@ async def request_user_by_discord_id(discord_id): async def get_account(interaction): try: - get_response = await request_user_by_discord_id(interaction.user.id) + user_response = await request_user_by_discord_id(interaction.user.id) except Exception as exc: - await embedder.error(interaction, """Sorry, -there was an error while checking if you have an account. + await embedder.error(interaction, """Sorry, there was an error while checking if you have an account. Please report this issue to the staff!""", ephemeral=True) raise exc - if get_response.status_code == 404: - return await embedder.error(interaction, """You -don't have an account yet!""", ephemeral=True) + if user_response.status_code == 404: + await embedder.error(interaction, """You don't have an account yet!""", ephemeral=True) + raise ValueError('Account not found.') - await embedder.info(interaction, f"""**Your account** + return user_response.json() + +async def get_info(interaction): + account = await get_account(interaction) + + await embedder.info(interaction, f"""### Your account This is all we have stored about your API account in our database. Feel free to request a removal of your account by contacting the staff. ||```json -{json.dumps(get_response.json(), indent=4)} +{json.dumps(account, indent=4)} ```|| (Click to reveal) Learn more about how to use our API at **https://nova-oss.com**. """, ephemeral=True) + +async def get_credits(interaction): + account = await get_account(interaction) + + await embedder.info(interaction, f"""### Your credits +**Amount:** `{account["credits"]}` +""", ephemeral=True) diff --git a/cord/bot.py b/cord/bot.py index 455dda0..3d13885 100644 --- a/cord/bot.py +++ b/cord/bot.py @@ -8,6 +8,7 @@ import embedder import autochat import accounts import community +import tutorials import credential_manager from dotenv import load_dotenv @@ -64,7 +65,21 @@ async def leaderboard(interaction: nextcord.Interaction): @bot.slash_command(description='Get info and stats about your NovaAI API account.') async def account(interaction: nextcord.Interaction): - return await accounts.get_account(interaction) + return await accounts.get_info(interaction) + +@bot.slash_command(name='credits', description='Get information about the amount of credits you have on your NovaAI API account.') +async def credits_(interaction: nextcord.Interaction): + return await accounts.get_credits(interaction) + +@bot.slash_command(description='View examples and tips for implementing NovaAI\'s API.') +async def tutorial(interaction: nextcord.Interaction, + how_can_i: str = SlashOption(# + description='Read a tutorial on how to...', + required=True, + choices=['fix ModuleNotFoundErrors', 'use the Python library', 'use curl', 'use Node.js', 'program a Python Discord Bot with streaming'] + ) +): + return await tutorials.send(interaction, how_can_i) @bot.slash_command(description='Lookup members by their Discord ID.') async def lookup(interaction: nextcord.Interaction, diff --git a/cord/chatbot.py b/cord/chatbot.py index 79e40d5..a6ab612 100644 --- a/cord/chatbot.py +++ b/cord/chatbot.py @@ -8,10 +8,10 @@ from dotenv import load_dotenv load_dotenv() async def respond(interaction, prompt): - partial_message = await interaction.send('‎') # empty message - message = await partial_message.fetch() + partial_message = await interaction.send('‎') # send an empty message + message = await partial_message.fetch() # gets the message that was send - openai.api_base = os.getenv('OPENAI_BASE', 'https://api.openai.com/v1') + openai.api_base = 'https://nova-oss.com' openai.api_key = os.getenv('OPENAI_KEY') model = os.getenv('OPENAI_MODEL') @@ -52,3 +52,5 @@ async def respond(interaction, prompt): await message.edit(content=text) await message.add_reaction('✅') + + diff --git a/cord/embedder.py b/cord/embedder.py index 916139d..7d51e1d 100644 --- a/cord/embedder.py +++ b/cord/embedder.py @@ -1,5 +1,4 @@ import nextcord -import datetime from typing import Union @@ -24,21 +23,12 @@ async def send( color=color ) - time_difference = datetime.datetime.now(datetime.timezone.utc) - ctx.created_at - milliseconds = int(time_difference.total_seconds() * 1000) - - end = '' - - if milliseconds > 5000: # https://youtu.be/-5wpm-gesOY - end = f' in {milliseconds}ms' - - embed.set_footer(text=f'Powered by NovaAI{end}', icon_url='https://i.ibb.co/LDyFcSh/fav-blurple.png') + embed.set_footer(text='Powered by NovaAI with ❤️', icon_url='https://i.ibb.co/LDyFcSh/fav-blurple.png') embed.set_author(name='NovaCord', url='https://nova-oss.com/novacord') interaction_type = Union[nextcord.Interaction, nextcord.InteractionResponse] # these checks are done so this function is easy to use - if edit: return await ctx.edit(embed=embed, content=content, **kwargs) diff --git a/cord/tutorials.py b/cord/tutorials.py new file mode 100644 index 0000000..c227caf --- /dev/null +++ b/cord/tutorials.py @@ -0,0 +1,130 @@ +import embedder + +async def send(interaction, how_can_i): + if how_can_i == 'fix ModuleNotFoundErrors': + text = """You can install Python packages using `pip`. Here's an example: `pip install openai`. +Don't have `pip` installed? Learn more here: https://pip.pypa.io/en/stable/installation/. +""" + + if how_can_i == 'use the Python library': + text = """For the official `openai` Python library, you just need to set the `openai.api_base` to `https://api.nova-oss.com/v1`. +```py +import openai + +openai.api_key = "PUT_YOUR_NOVA_AI_API_KEY_IN_HERE" +openai.api_base = "https://api.nova-oss.com/v1" + +completion = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"} + ] +) + +print(completion.choices[0].message) +``` +""" + + if how_can_i == 'use curl': + text = """For curl, just follow the official OpenAI documentation: https://platform.openai.com/docs/api-reference/chat/create?lang=curl +And replace ~~`openai.com`~~ with **`nova-oss.com`**. Here's an example: + +```bash +curl https://api.nova-oss.com/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer PUT_YOUR_NOVA_AI_API_KEY_IN_HERE" \ + -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "1+1" + } + ] + }' +``` +""" + + if how_can_i == 'use Node.js': + text = """Currently, the official node.js `openai` package doesn't support custom endpoints in their stable version, +but will probably change after the release of v4.0.0: https://github.com/openai/openai-node/discussions/182#discussioncomment-6335703. +Meanwhile, you can just do "normal" requests like in curl without any package. +""" + + if how_can_i == 'program a Python Discord Bot with streaming': + text = """I'm assuming you know how to run Discord bots in Python and have already set up a bot. +And if not - there are tons of tutorials out there on the internet on how to do that. + +**Warning:** I'm using *Nextcord* as my package of choice for programming Discord bots, because it worked the best for me. +If you get an error saying that Nextcord isn't compatible with another package, +simply uninstall it using a command like `pip uninstall discord.py`. + +Anyways, make sure you have all packages you need installed: +`pip install openai python-dotenv nextcord`. + +We don't want our code to contain the secret API key and Discord bot token. +So create a `.env` file with the following content (obviously replace the values with the actual ones appropriately).: + +``` +NOVA_KEY=PUT_YOUR_NOVA_API_KEY_HERE +DISCORD_TOKEN=PUT_YOUR_DISCORD_BOT_TOKEN_HERE +``` + +And you should be good to go! + +```py +import os +import openai +import nextcord + +from dotenv import load_dotenv + +load_dotenv() # reads our secret of our .env file and makes them accessible using os.getenv() + +# add a new command +@bot.slash_command(description='Chat with AI') +async def chat(interaction: nextcord.Interaction, + # this makes it so people can enter their prompt when using the slash command + prompt: str = SlashOption(description='Chatbot Prompt', required=True) +): + partial_message = await interaction.send('‎') # just writes an empty message + message = await partial_message.fetch() # prepares this message to be used + + # set the configuration for the AI access + openai.api_base = 'https://api.nova-oss.com/v1' + openai.api_key = os.getenv('NOVA_KEY') + + async with interaction.channel.typing(): # so Discord displays the "[Bot name] is typing..." at the bottom + try: + completion = openai.ChatCompletion.create( + model='gpt-3.5-turbo', + messages=[{'role': 'user', 'content': prompt}], + stream=True # this is really important to get the streaming (realtime updates) to work + ) + # from now on, we will get new completions in the background which are saved in the completion variable + except Exception as exc: + await interaction.send(':x: Error - could not generate an AI response. Look in the console.') + raise exc # so the error shows up in the console + + # we want to save the text that was generated using the AI + text = '' + + for event in completion: # loop through word generation in real time + try: + new_text = event['choices'][0]['delta']['content'] # add the newly generated word to the variable + except KeyError: # end or an error occured + break # stop the loop + + text += new_text # ad the new word to the complete text variable + + if text: # we get errors if the new text we're editing the message to is empty + await message.edit(content=text) # finally edit the message to include the entire new text + + # put any code in here if you want anything to happen when the AI is done with the completion + +# starts the Discord bot +bot.run(os.getenv('DISCORD_TOKEN')) +``` +""" + return await embedder.info(interaction, text)