From bfb856ed7b04aac134545629cc85290d38fd6f29 Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 17 Oct 2023 22:00:20 -0400 Subject: [PATCH 1/4] adding AI chat feature --- requirements.txt | 4 +++- src/bot.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30950b18..ccf3b435 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,6 @@ pylint==3.0.1 psutil==5.9.5 apscheduler==3.10.4 pyshorteners~=1.0.1 -quickchart.io==1.0.0 \ No newline at end of file +quickchart.io==1.0.0 +bard==0.1 +bardapi==0.1.38 diff --git a/src/bot.py b/src/bot.py index a8b0a9b8..a9c94fde 100644 --- a/src/bot.py +++ b/src/bot.py @@ -32,6 +32,7 @@ import help_command import regrade import utils +from bardapi import Bard os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1' @@ -47,6 +48,13 @@ BOT_VERSION=os.getenv('VERSION') print(BOT_VERSION) Test_bot_application_ID = int(os.getenv('TEST_BOT_APP_ID')) +bard_api_key = os.getenv('BARD_API_KEY') +#print(f'bard_api_key = {bard_api_key} ') +#print('check1') +#print('check2') +bard = Bard(token = bard_api_key) + + TESTING_MODE = None @@ -309,6 +317,33 @@ async def test(ctx): ''' simple sanity check ''' await ctx.send('test successful') +################################## +#Function : bard enabled +# Description: Integrating bard api +################################## +def bard_response(user_input): + #response = bard.query(prompt) + response = bard.get_answer(str(user_input))['content'] + return response +@bot.command() +async def Aichat(ctx): + await ctx.send("You are now in a AI chat session. Type 'exit' to end the chat.") + def check(msg): + return msg.author == ctx.author + while True: + try: + user_input = await bot.wait_for("message", check=check, timeout=300) # Adjust the timeout as needed + + if user_input.content.lower() == 'exit': + await ctx.send("Chat session ended.") + break + chat_reply = bard_response(user_input.content) + await ctx.send(chat_reply) + except asyncio.TimeoutError: + await ctx.send("Chat session timed out. Type `!chat` to start a new session.") + break + + ########################### # Function: get_instructor # Description: Command used to give Instructor role out by instructors From 23b13b10934a4be9626a30f6f0c1427e58685f77 Mon Sep 17 00:00:00 2001 From: abhinav Date: Thu, 19 Oct 2023 12:25:10 -0400 Subject: [PATCH 2/4] pushing again --- spam.txt | 0 src/bot.py | 1 - test/test_aichat.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 spam.txt create mode 100644 test/test_aichat.py diff --git a/spam.txt b/spam.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/bot.py b/src/bot.py index a9c94fde..ac29b18a 100644 --- a/src/bot.py +++ b/src/bot.py @@ -322,7 +322,6 @@ async def test(ctx): # Description: Integrating bard api ################################## def bard_response(user_input): - #response = bard.query(prompt) response = bard.get_answer(str(user_input))['content'] return response @bot.command() diff --git a/test/test_aichat.py b/test/test_aichat.py new file mode 100644 index 00000000..6e095469 --- /dev/null +++ b/test/test_aichat.py @@ -0,0 +1,41 @@ +import unittest +import discord + +class TestAichat(unittest.TestCase): + def setUp(self): + # Create a Discord bot instance with a command prefix + self.bot = discord.ext.commands.Bot(command_prefix='!') + self.ctx = discord.ext.commands.Context(prefix='!', bot=self.bot) + + def test_aichat_exit(self): + # Simulate a user entering "exit" in the chat + user_input = discord.Message(content="exit", author=discord.User(), channel=discord.TextChannel()) + + # Simulate the user context for the bot + self.ctx.author = user_input.author + self.ctx.channel = user_input.channel + + # Get the Aichat command and run it + aichat_command = self.bot.get_command('Aichat') + with self.assertLogs(self.bot, level='INFO') as logs: + self.bot.dispatch('message', user_input) + + # Check for expected log messages + log_messages = [record.getMessage() for record in logs.records] + self.assertIn("You are now in an AI chat session. Type 'exit' to end the chat.", log_messages) + self.assertIn("Chat session ended.", log_messages) + + def test_aichat_timeout(self): + # Simulate a timeout in the chat session + self.ctx.author = discord.User() + self.ctx.channel = discord.TextChannel() + + # Get the Aichat command and run it + aichat_command = self.bot.get_command('Aichat') + with self.assertLogs(self.bot, level='INFO') as logs: + self.bot.dispatch('message', discord.Message()) + + # Check for expected log messages + log_messages = [record.getMessage() for record in logs.records] + self.assertIn("You are now in an AI chat session. Type 'exit' to end the chat.", log_messages) + self.assertIn("Chat session timed out. Type `!chat` to start a new session.", log_messages) From 173a48ebd3156456f1ea567a28fc9712057f4b2d Mon Sep 17 00:00:00 2001 From: abhinav Date: Thu, 19 Oct 2023 14:58:14 -0400 Subject: [PATCH 3/4] updating bot Ai chat feature --- src/bot.py | 68 ++++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/src/bot.py b/src/bot.py index dc138931..2e813336 100644 --- a/src/bot.py +++ b/src/bot.py @@ -21,6 +21,7 @@ from quickchart import QuickChart import pyshorteners +from bardapi import Bard import db import profanity import event_creation @@ -33,7 +34,6 @@ import regrade import utils import spam -from bardapi import Bard os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1' @@ -49,11 +49,10 @@ BOT_VERSION=os.getenv('VERSION') print(BOT_VERSION) Test_bot_application_ID = int(os.getenv('TEST_BOT_APP_ID')) -bard_api_key = os.getenv('BARD_API_KEY') -#print(f'bard_api_key = {bard_api_key} ') -#print('check1') -#print('check2') -bard = Bard(token = bard_api_key) +#bard_api_key = os.getenv('BARD_API_KEY', None) +#bard = None +#if bard_api_key: +# bard = Bard(token = bard_api_key) guild_id = int(os.getenv('TEST_GUILD_ID')) ## needed for spam detection @@ -323,28 +322,30 @@ async def test(ctx): #Function : bard enabled # Description: Integrating bard api ################################## -def bard_response(user_input): - response = bard.get_answer(str(user_input))['content'] - return response @bot.command() async def Aichat(ctx): - await ctx.send("You are now in a AI chat session. Type 'exit' to end the chat.") + bard_api_key = os.getenv('BARD_API_KEY', None) + bard = None + if bard_api_key: + bard = Bard(token = bard_api_key) + await ctx.send("You are now in a AI chat session." + "Type 'exit' to end the chat.") def check(msg): return msg.author == ctx.author while True: try: - user_input = await bot.wait_for("message", check=check, timeout=300) # Adjust the timeout as needed - + user_input = await bot.wait_for("message", check=check, timeout=300) if user_input.content.lower() == 'exit': await ctx.send("Chat session ended.") break - chat_reply = bard_response(user_input.content) - await ctx.send(chat_reply) + if bard: + chat_reply = bard.get_answer(str(user_input.content))['content'] + await ctx.send(chat_reply) + else: + await ctx.send("Bard API is not available.") except asyncio.TimeoutError: await ctx.send("Chat session timed out. Type `!chat` to start a new session.") break - - ########################### # Function: get_instructor # Description: Command used to give Instructor role out by instructors @@ -908,83 +909,51 @@ async def custom_begin_tests(ctx): @custom_help.command('create') async def custom_create(ctx): await help_command.create(ctx) - - @custom_help.command('end-tests') async def custom_end_tests(ctx): await help_command.end_tests(ctx) - - @custom_help.command('oh') async def custom_oh(ctx): await help_command.oh(ctx) - - @custom_help.command('ping') async def custom_ping(ctx): await help_command.ping(ctx) - - @custom_help.command('poll') async def custom_poll(ctx): await help_command.poll(ctx) - - @custom_help.command('setInstructor') async def custom_setInstructor(ctx): await help_command.setInstructor(ctx) - - @custom_help.command('stats') async def custom_stats(ctx): await help_command.stats(ctx) - - @custom_help.command('test') async def custom_test(ctx): await help_command.test(ctx) - - @custom_help.command('regrade-request') async def custom_regrade_request(ctx): await help_command.regrade_request(ctx) - - @custom_help.command('update-request') async def custom_update_request(ctx): await help_command.update_request(ctx) - - @custom_help.command('display-requests') async def custom_display_requests(ctx): await help_command.display_requests(ctx) - - @custom_help.command('remove-request') async def custom_remove_request(ctx): await help_command.remove_request(ctx) - - @custom_help.command('create_email') async def custom_create_email(ctx): await help_command.create_email(ctx) - - @custom_help.command('update_email') async def custom_update_email(ctx): await help_command.update_email(ctx) - - @custom_help.command('remove_email') async def custom_remove_email(ctx): await help_command.remove_email(ctx) - - @custom_help.command('view_email') async def custom_view_email(ctx): await help_command.view_email(ctx) - - ########################### # Function: begin_tests # Description: Start the automated testing @@ -995,12 +964,9 @@ async def custom_view_email(ctx): async def begin_tests(ctx): ''' start test command ''' global TESTING_MODE - if ctx.author.id != Test_bot_application_ID: return - TESTING_MODE = True - test_oh_chan = next((ch for ch in ctx.guild.text_channels if 'office-hour-test' in ch.name), None) if test_oh_chan: From db442097e1166976ebf8d412905fe14fcf0b0a98 Mon Sep 17 00:00:00 2001 From: samuellouiskwiatkowski-martin Date: Thu, 19 Oct 2023 21:22:21 -0400 Subject: [PATCH 4/4] Resolved the merge conflicts --- requirements.txt | 2 ++ src/bot.py | 41 ----------------------------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/requirements.txt b/requirements.txt index af41c602..3b11e824 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,6 @@ psutil==5.9.5 apscheduler==3.10.4 pyshorteners~=1.0.1 quickchart.io==1.0.0 +bard==0.1 +bardapi==0.1.38 easy-pil==0.3.0 \ No newline at end of file diff --git a/src/bot.py b/src/bot.py index c8cfb791..d20c3249 100644 --- a/src/bot.py +++ b/src/bot.py @@ -555,7 +555,6 @@ async def ask_question(ctx, question): else: await ctx.author.send('Please send questions to the #q-and-a channel.') await ctx.message.delete() - ########################### # Function: send_links # Description: command to fetch all the links posted in the group @@ -564,13 +563,11 @@ async def ask_question(ctx, question): # Outputs: # - Bot posts all the links posted in group. ########################### - @bot.command(name='send_links', help='Command will output all the messages which contain url') async def send_links(ctx): """To display all messages which contain url.""" await ctx.send("The below list of messages contains URLs") await ctx.send(file=discord.File('images/links/links.txt')) - ########################### # Function: answer # Description: command to answer question and sends to qna module @@ -590,10 +587,8 @@ async def answer_question(ctx, q_num, answer): else: await ctx.author.send('Please send answers to the #q-and-a channel.') await ctx.message.delete() - @bot.command(name='regrade-request', help='add regrade-request') async def submit_regrade_request(ctx,name:str,questions:str): - """ Function: submit_regrade_request Description: command to add a regrade request @@ -604,7 +599,6 @@ async def submit_regrade_request(ctx,name:str,questions:str): Outputs: - adds the regrade request to the database """ - if ctx.channel.name == 'regrade-requests': await regrade.add_request(ctx,name,questions) else: @@ -643,7 +637,6 @@ async def display_regrade_request(ctx): @bot.command(name='update-request', help='update regrade request') async def update_regrade_request(ctx,name:str,questions:str): - """ Function: update_regrade_request Description: command to display all the regrade requests @@ -652,15 +645,11 @@ async def update_regrade_request(ctx,name:str,questions:str): Output: - updates an existing regrade request with any modifications """ - if ctx.channel.name == 'regrade-requests': await regrade.update_regrade_request(ctx,name,questions) - else: await ctx.author.send('Please submit requests in regrade channel.') await ctx.message.delete() - - @update_regrade_request.error async def update_regrade_request_error(ctx, error): """ @@ -669,10 +658,8 @@ async def update_regrade_request_error(ctx, error): if isinstance(error, commands.MissingRequiredArgument): await ctx.send('Invalid command.\n Use !update-request \n \ ( Example: !update-request "Student 1" q1,q2,q3 )') - @bot.command(name='remove-request', help='remove regrade request') async def remove_regrade_request(ctx,name:str,questions:str): - """ Function: remove_regrade_request Description: command to remove a regrade request @@ -682,15 +669,11 @@ async def remove_regrade_request(ctx,name:str,questions:str): - questions: question numbers to be regraded - output: removes an existing regrade request from the database """ - if ctx.channel.name == 'regrade-requests': await regrade.remove_regrade_request(ctx,name,questions) - else: await ctx.author.send('Please submit requests in regrade channel.') await ctx.message.delete() - - @remove_regrade_request.error async def remove_regrade_request_error(ctx, error): """ @@ -699,22 +682,17 @@ async def remove_regrade_request_error(ctx, error): if isinstance(error, commands.MissingRequiredArgument): await ctx.send('Invalid command.\n Use !remove-request \n \ ( Example: !remove-request "Student 1" q1,q2,q3 )') - ########################### # Function: ping # Description: Shows latency for debugging ########################### - @bot.command(name='ping', help='Returns Latency') - async def ping(ctx): start=time() message=await ctx.send(f"Pong! : {bot.latency*1000:,.0f} ms") end=time() await message.edit(content="Pong! : "+str(int(bot.latency*1000))+" ms."+ " Response time : "+str(int((end-start)*1000))+" ms.") - - @bot.command(name='chart', help='Creates a custom chart') @commands.has_role('Instructor') async def custom_chart(ctx, title: str, chart: str, *args): @@ -728,25 +706,20 @@ async def custom_chart(ctx, title: str, chart: str, *args): Returns: returns a graph in the chat box """ - if len(args) % 2 != 0: print("Make sure every data-label singularly matches a datapoint (A B C 1 2 3") return data_count = int(len(args) / 2) with open('../data/charts/chartstorage.json', 'r', encoding='utf-8') as file: storage = json.load(file) - labels_list = [] dataset_list = [] - for data_label in range(data_count): labels_list.append(args[data_label]) print(args[data_label]) - for data_point in range(data_count, len(args)): dataset_list.append(args[data_point]) print(args[data_point]) - quick_chart = QuickChart() quick_chart.width = 500 quick_chart.height = 300 @@ -764,14 +737,11 @@ async def custom_chart(ctx, title: str, chart: str, *args): link = quick_chart.get_url() shortener = pyshorteners.Shortener() shortened_link = shortener.tinyurl.short(link) - await update_chart(storage, title, shortened_link) with open('../data/charts/chartstorage.json', 'w', encoding='utf-8') as file: json.dump(storage, file, indent=4) await ctx.send("Here is your chart:") await ctx.send(f"{shortened_link}") - - @bot.command(name='check_chart', help='View a custom chart by giving title name') async def checkchart(ctx, name: str): """ @@ -789,7 +759,6 @@ async def checkchart(ctx, name: str): else: await ctx.send(f"Your requested chart:") await ctx.send(f"{storage[name]['URL']}") - async def update_chart(storage, name, link): """ Updates the URL of the chart @@ -801,12 +770,10 @@ async def update_chart(storage, name, link): if not str(name) in storage: storage[str(name)] = {} storage[str(name)]['URL'] = link - ########################### # Function: stats # Description: Shows stats like ########################### - @bot.command(name='stats', help='shows bot stats') async def show_stats(ctx): embed = Embed(title="Bot stats",colour=ctx.author.colour, timestamp=datetime.utcnow()) @@ -842,7 +809,6 @@ async def show_stats(ctx): ########################### polls=[] scheduler = AsyncIOScheduler() - @bot.command(name='poll', help='Set Poll for a specified time and topic.') @commands.has_role('Instructor') async def create_poll(ctx, hours: int, question: str, *options): @@ -864,7 +830,6 @@ async def create_poll(ctx, hours: int, question: str, *options): scheduler.add_job(complete_poll, "interval", minutes=hours,args=(message.channel.id, message.id)) scheduler.start() - async def complete_poll(channel_id, message_id): message = await bot.get_channel(channel_id).fetch_message(message_id) most_voted = max(message.reactions, key=lambda r: r.count) @@ -872,7 +837,6 @@ async def complete_poll(channel_id, message_id): " was the most popular with "+str(most_voted.count-1)+" votes!") polls.remove((message.channel.id, message.id)) scheduler.shutdown() - @bot.event async def on_raw_reaction_add(payload): if payload.message_id in (poll[1] for poll in polls): @@ -882,7 +846,6 @@ async def on_raw_reaction_add(payload): and payload.member in await reaction.users().flatten() and reaction.emoji != payload.emoji.name): await message.remove_reaction(reaction.emoji, payload.member) - ########################### # Function: custom-profanity # Description: Define a word to be added to the profanity filter @@ -904,19 +867,15 @@ async def custom_profanity(ctx, pword): @commands.has_role('Instructor') async def attend(ctx): await attendance.compute(bot, ctx) - @bot.command(name='create_email', help='Configures the specified email address against user.') async def create_email(ctx, email_id): await email_address.create_email(ctx, email_id) - @bot.command(name='update_email', help='Updates the configured email address against user.') async def update_email(ctx, email_id): await email_address.update_email(ctx, email_id) - @bot.command(name='view_email', help='displays the configured email address against user.') async def view_email(ctx): await email_address.view_email(ctx) - @bot.command(name='remove_email', help='deletes the configured email address against user.') async def delete_email(ctx): await email_address.delete_email(ctx)