diff --git a/cord/autochat.py b/cord/autochat.py new file mode 100644 index 0000000..55d1e7f --- /dev/null +++ b/cord/autochat.py @@ -0,0 +1,18 @@ +import random +import embedder + +async def process(message): + if message.content == '/key': + responses = [ + 'https://media.tenor.com/t9f91LQWsM4AAAAS/breaking-bad-funny.gif', + 'NPC detected, command rejected.', + 'https://images-ext-1.discordapp.net/external/DXc3r4PyRR3m_4AzevpxWhfFtavdcMeDpZqFj3Ig4hc/https/media.tenor.com/b7swbvaVKhUAAAPo/seriously-laugh.mp4', + 'there👏is👏no👏/key👏command👏', + 'https://i.imgflip.com/7tuhc6.jpg' + ] + + await message.reply(random.choice(responses)) + await message.channel.send('Jokes aside - the project is still under development. There\'s no **`/key`** command.') + + if message.content.startswith('/') and ('commands' not in message.channel.name) and (not message.author.guild_permissions.manage_messages): + await embedder.warn(message, 'Please only run commands in `/commands`.') diff --git a/cord/bot.py b/cord/bot.py index 1b58977..5d36765 100644 --- a/cord/bot.py +++ b/cord/bot.py @@ -1,13 +1,13 @@ # This example requires the 'members' and 'message_content' privileged intents to function. import os -import asyncio import nextcord -import requests -import keys +import system import chatbot import embedder +import autochat +import community from dotenv import load_dotenv from nextcord.ext import commands @@ -17,7 +17,7 @@ load_dotenv() bot = commands.Bot( intents=nextcord.Intents.all(), - default_guild_ids=[int(guild_id) for guild_id in os.getenv('DISCORD_GUILD_IDS').split()] + default_guild_ids=[int(guild_id) for guild_id in os.getenv('DISCORD_GUILD_IDS').split()] # so slash commands work ) @bot.event @@ -27,6 +27,10 @@ async def on_ready(): @bot.event async def on_message(message): + if message.author.bot: # block bots + return + + await autochat.process(message) await bot.process_commands(message) @bot.slash_command(description='Chat with AI') @@ -41,62 +45,17 @@ async def dm(interaction: nextcord.Interaction): await interaction.user.create_dm() await embedder.info(interaction.user.dm_channel, 'Hello!') except nextcord.Forbidden: - await embedder.error(interaction, text='Please open this server\'s options, go to `Privacy Settings` and enable `Direct Messages` and `Message Requests`.') + await embedder.error(interaction, text="""Please open this server\'s options, +go to `Privacy Settings` and enable `Direct Messages` as well as `Message Requests`.""") else: await embedder.ok(interaction, 'Great, DMs are set up successfully!') -@bot.slash_command(description='Get your secret NovaAI API key.') -async def key(interaction: nextcord.Interaction): - try: - resp = requests.post( - url='https://nova-oss.com/api/tos-verification', - timeout=5, - headers={'Content-Type': 'application/json', 'Authorization': os.getenv('TOS_VERIFICATION_KEY')} - ).json() - except Exception as exc: - await embedder.error(interaction, """Sorry, the API server for the verification system is not functioning, -which means you can\'t create a new key right now. Please report this issue to the staff!""") - raise exc +@bot.slash_command(description='Get your secret NovaAI API credentials.') +async def credentials(interaction: nextcord.Interaction): + return await system.get_credentials(interaction) - tos_code = resp['code'] - tos_emoji = resp['emoji'] - - tos_message = await embedder.warn(interaction, f"""# THIS IS JUST A DEMO! -# THE KEY DOESN'T WORK! -You have to read the privacy policy and terms of service first. -In the latter, there is a hidden emoji which you'll have to send (NOT react!) in here. - -https://nova-oss.com/legal/privacy -https://nova-oss.com/legal/terms?verify={tos_code} - -I know it's annoying, but it really helps combat spam bots and abuse. - -This message will be deleted and your code will run out **after about 10 minutes** -if you don't pass the verification, but **feel free to run this command again** at any time. -""", ephemeral=True) - - def check(message): return interaction.user.id == message.author.id and message.content == tos_emoji - - try: - answer = await bot.wait_for('message', timeout=666, check=check) - except asyncio.TimeoutError: - await tos_message.delete() - requests.delete( - url=f'https://nova-oss.com/api/tos-verification/{tos_code}', - timeout=5, - headers={'Content-Type': 'application/json', 'Authorization': os.getenv('TOS_VERIFICATION_KEY')} - ) - - else: - await answer.delete() - api_key = await keys.create(interaction.user) - await embedder.ok(interaction, f"""This is your **secret** API key. Don't paste it on untrusted websites, apps or programs. -Store it securely using a `.env` file in the environment variables or use a secure password manager like *KeePass*, *ProtonPass* or *Bitwarden*. -We reserve the right to __disable your API key at any time__ if you violate our terms of service. -If you accept the terms of service and privacy policy, feel free to use the following API key: - -## ||`{api_key}`|| - -""", ephemeral=True) +@bot.slash_command(description='Leaderboard.') +async def leaderboard(interaction: nextcord.Interaction): + await community.leaderboard(interaction) bot.run(os.getenv('DISCORD_TOKEN')) diff --git a/cord/community.py b/cord/community.py new file mode 100644 index 0000000..717c4cd --- /dev/null +++ b/cord/community.py @@ -0,0 +1,37 @@ +import asyncio +import datetime + +import embedder + +async def process_channel(channel, scores): + if channel.name in ['general', 'support', 'suggestions', 'showcase', 'team-discussion', 'prompts', 'research-resources']: + after = datetime.datetime.now() - datetime.timedelta(days=7) + + async for message in channel.history(limit=1000, after=after): + if not '```' in message.content: # no code + if not scores.get(message.author.id): + scores[message.author.id] = 0 + + scores[message.author.id] += message.content.strip().count(' ') + +async def leaderboard(interaction): + msg = await interaction.send('Loading the leaderboard... Go grab a mug of bleach.. ||**[for legal reasons that a joke]**|| \nhttps://media.tenor.com/M67VmLlocdMAAAAS/spinning-seal.gif') + + scores = {} + + channels = interaction.guild.text_channels + tasks = [process_channel(channel, scores) for channel in channels] # go fast like sonik by doing splitting it into tasks + await asyncio.gather(*tasks) + + board = dict(sorted(scores.items(), key=lambda x: x[1], reverse=True)[:10]) # sort dict by value and get only first 10 + + emojis = [':first_place:', ':second_place:', ':third_place:', ':four:', ':five:', ':six:', ':seven:', ':eight:', ':nine:', ':keycap_ten:'] + + text = 'Words (excluding code) typed in selected channels in the last 7 days with a limit of 1000 messages per channel:\n' + place = 0 + + for user in list(board.keys()): + text += f'{emojis[place]} {interaction.guild.get_member(user).mention} **{scores[user]}**\n' + place += 1 + + await embedder.info(msg, title='Leaderboard (7 days)', text=text) diff --git a/cord/embedder.py b/cord/embedder.py index 65d2488..b48fa0a 100644 --- a/cord/embedder.py +++ b/cord/embedder.py @@ -1,5 +1,5 @@ import nextcord -import traceback +import datetime from typing import Union @@ -7,34 +7,59 @@ async def send( ctx, title: str, text: str, + content: str = '', ephemeral: bool = False, color: nextcord.Color = nextcord.Color.blue() ): + edit = False + + if isinstance(ctx, nextcord.PartialInteractionMessage): + ctx = await ctx.fetch() + edit = True + embed = nextcord.Embed( title=title, description=text, color=color ) - embed.set_footer(text='Powered by NovaAI', icon_url='https://i.ibb.co/LDyFcSh/fav-blurple.png') + + time_difference = datetime.datetime.now(datetime.timezone.utc) - ctx.created_at + milliseconds = int(time_difference.total_seconds() * 1000) + + end = '' + + if milliseconds > 10000: # 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_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 as fuck to use + + if edit: + return await ctx.edit(embed=embed, content=content) + if isinstance(ctx, nextcord.Message): - response = await ctx.reply(embed=embed) - elif isinstance(ctx, Union[nextcord.Interaction, nextcord.InteractionResponse]): - response = await ctx.send(embed=embed, ephemeral=ephemeral) + response = await ctx.reply(embed=embed, content=content) + + elif isinstance(ctx, interaction_type): + response = await ctx.send(embed=embed, ephemeral=ephemeral, content=content) + else: - response = await ctx.send(embed=embed) + response = await ctx.send(embed=embed, content=content) return response -async def ok(ctx, text: str, title: str='Success', *args, **kwargs): +async def ok(ctx, text: str, title: str=':white_check_mark: Success', *args, **kwargs): return await send(ctx, title, text, color=nextcord.Color.green(), *args, **kwargs) -async def info(ctx, text: str, title: str='Information', *args, **kwargs): +async def info(ctx, text: str, title: str=':information_source: Information', *args, **kwargs): return await send(ctx, title, text, color=nextcord.Color.blue(), *args, **kwargs) -async def warn(ctx, text: str, title: str='Warning', *args, **kwargs): +async def warn(ctx, text: str, title: str=':warning: Warning', *args, **kwargs): return await send(ctx, title, text, color=nextcord.Color.orange(), *args, **kwargs) -async def error(ctx, text: str, title: str='Error - Command Failed', *args, **kwargs): +async def error(ctx, text: str, title: str=':x: Error - Command Failed', *args, **kwargs): return await send(ctx, title, text, color=nextcord.Color.red(), *args, **kwargs) diff --git a/cord/system.py b/cord/system.py new file mode 100644 index 0000000..685ab6c --- /dev/null +++ b/cord/system.py @@ -0,0 +1,65 @@ +import os +import asyncio +import requests + +import keys +import embedder + +from dotenv import load_dotenv + +load_dotenv() + +async def get_credentials(interaction): + try: + resp = requests.post( + url='https://nova-oss.com/api/tos-verification', + timeout=5, + headers={'Content-Type': 'application/json', 'Authorization': os.getenv('TOS_VERIFICATION_KEY')} + ).json() + except Exception as exc: + await embedder.error(interaction, """Sorry, the API server for the verification system is not functioning, +which means you can\'t create a new key right now. Please report this issue to the staff!""") + raise exc + + tos_code = resp['code'] + tos_emoji = resp['emoji'] + + tos_message = await embedder.warn(interaction, f"""# THIS IS JUST A DEMO/EXAMPLE! +# THE KEY WON'T WORK! +# THE SYSTEM ISN'T READY YET. +# DON'T SAVE THE KEY!!! +You have to read the privacy policy and terms of service first. +In the latter, there is a hidden emoji which you'll have to send (NOT react!) in here. + +https://nova-oss.com/legal/privacy +https://nova-oss.com/legal/terms?verify={tos_code} + +I know it's annoying, but it really helps combat spam bots and abuse. + +This message will be deleted and your code will run out **after about 10 minutes** +if you don't pass the verification, but **feel free to run this command again** at any time. +""", ephemeral=True) + + def check(message): return interaction.user.id == message.author.id and message.content == tos_emoji + + try: + answer = await interaction.client.wait_for('message', timeout=666, check=check) + except asyncio.TimeoutError: + await tos_message.delete() + requests.delete( + url=f'https://nova-oss.com/api/tos-verification/{tos_code}', + timeout=5, + headers={'Content-Type': 'application/json', 'Authorization': os.getenv('TOS_VERIFICATION_KEY')} + ) + + else: + await answer.delete() + api_key = await keys.create(interaction.user) + await embedder.ok(interaction, f"""This is your **secret** API key. Don't paste it on untrusted websites, apps or programs. +Store it securely using a `.env` file in the environment variables or use a secure password manager like *KeePass*, *ProtonPass* or *Bitwarden*. +We reserve the right to __disable your API key at any time__ if you violate our terms of service. +If you accept the terms of service and privacy policy, feel free to use the following API key: + +## ||`{api_key}`|| + +""", ephemeral=True)