diff --git a/.gitignore b/.gitignore index cda2e01..e1ff76f 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,4 @@ google-calendar-quickstart.py credentials.json token.json ok.json +faq.json diff --git a/mentalHealthBot.py b/FellowBot.py similarity index 78% rename from mentalHealthBot.py rename to FellowBot.py index cdd876b..6466fa7 100644 --- a/mentalHealthBot.py +++ b/FellowBot.py @@ -1,4 +1,12 @@ #Google Calendar API imports +#!/usr/bin/env python3 +from motivation import get_motivation, refresh_reddit_token +from connect_database import update_mood, get_moods +from mood import tone_result +from weekly_mood import weekly_moods +from google_translate import google_translate +from watson import reload_watson_api + from __future__ import print_function import datetime import os.path @@ -9,33 +17,36 @@ import json import os -import discord import requests from discord.ext import commands import random from keepalive import keepalive +from discord.ext import commands # enter token for the server -TOKEN = os.getenv('DISCORD_TOKEN') -# enter application id - -# client = commands.Bot(command_prefix='.') +# TOKEN = os.environ['DISCORD_TOKEN'] # to use in repl import discord -from discord.ext import commands -# from dotenv import load_dotenv -# load_dotenv() -# TOKEN = os.getenv('DISCORD_TOKEN') +from discord.ext import commands, tasks -# If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] +import datetime +import json +import random +import os +TEST = True # ---------------------BOT COMMAND bot = commands.Bot(command_prefix='!') # ---------------------WEB SCRAPING -scraping = open("ok.json", encoding="UTF-8") +scraping = open("faq.json", encoding="UTF-8") scraped = json.load(scraping) +announcement_channels_list = [] + +# If modifying these scopes, delete the file token.json. +SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] + + # ----------------FOR HELPLINE helplines = ["Family Violence Prevention Center 1-800-313-1310", "National Sexual Assault Hotline 1-800-656-HOPE (4673)", @@ -45,7 +56,7 @@ "GriefShare 1-800-395-5755", "Suicide Hotline 1-800-SUICIDE (784-2433)"] -@bot.command(aliases=['event', 'nextevent', 'nextevents', 'upcomingevent', 'upcomingevents'], help='Get a list of the ongoing and upcoming events of the pod') +@bot.command(aliases=['event', 'nextevent', 'nextevents', 'upcomingevent', 'upcomingevents'], help='Get a list of the ongoing and upcoming events of the pod. Enter the count of events to retrieve following the command. It\'s default value is 1.') async def events(ctx, count='1'): creds = None # The file token.json stores the user's access and refresh tokens, and is @@ -107,7 +118,22 @@ async def events(ctx, count='1'): # await ctx.send(event['description']) -@bot.command(name='faq', help='Searches the FAQ for the most relevant section corresponding to the provided keyword.') +@bot.command(name='faq', help="\ + Searches the FAQ for the most relevant section corresponding to the provided keyword. List of keywords: \ + - ๐Ÿ˜„ **MLHintro** - For in depth introduction to MLH fellowship. \ + - ๐ŸŒฒ **MLHevents**- What are MLH events? Know everything about them. \ + - ๐Ÿš‚ **pods** - What are pods? Know everything here \ + - ๐Ÿ“ฑ **discord** -How to use discord and which channel serves what purpose \ + - ๐Ÿ“ž **zoom**- What is zoom used for? \ + - ๐Ÿ“ข **feedback** - What is feedback in MLH? \ + - โฐ **remote** - How to work remotely and give your 100% in MLH. \ + - ๐Ÿ“ **hackathonrules** - Rules for hackathons \ + - ๐Ÿฅ‡ **hackjudge** - How do judges make a decision for hackathon winner? What is taken into consideration? \ + - ๐Ÿ’ฏ **hackdemo** - How to prepare a good demo for hackathon? \ + - ๐Ÿ™‹ **attendance** - What is the importance of attendance in MLH and how is it monitored?\ + - ๐Ÿ’ **expectations** - What MLH expects from you as fellows\ + - โ›‘๏ธ **help** - Facing problems in MLH? Reach out to us\ +") async def faq(ctx, keyword): for i in scraped["intents"]: if(keyword==i["tag"]): @@ -118,37 +144,37 @@ async def faq(ctx, keyword): await ctx.send(response) -@bot.command(aliases=['al']) +@bot.command(aliases=['al'], help="Get all helpline numbers related to mental health") async def all(ctx): for i in helplines: res = i await ctx.send(res) -@bot.command(aliases=['depression']) +@bot.command(aliases=['depression'], help="Get helpline number to tackle depression and suicidal thoughts") async def suicide(ctx): await ctx.send(helplines[6]) -@bot.command(aliases=['violenT']) +@bot.command(aliases=['violent'], help="Get helpline number to tackle violence") async def violence(ctx): await ctx.send(helplines[0]) -@bot.command(aliases=['drugs']) +@bot.command(aliases=['drugs'], help="Get helpline number to tackle drug addiction") async def drugabuse(ctx): await ctx.send(helplines[2]) -@bot.command(aliases=['assault']) +@bot.command(aliases=['assault'], help="Get helpline number to tackle assaults") async def sexualAssault(ctx): await ctx.send(helplines[1]) -@bot.command(aliases=['ILLNESS']) +@bot.command(aliases=['ILLNESS'], help="Get helpline number to tackle illness") async def cancer(ctx): await ctx.send(helplines[3]) -@bot.command(aliases=['grie']) +@bot.command(aliases=['sorrow'], help="Get helpline number to tackle grief") async def grief(ctx): await ctx.send(helplines[5]) -@bot.command(aliases=['eating']) +@bot.command(aliases=['eating'], help="Get helpline number to tackle eating disorder") async def eatingdisorder(ctx): await ctx.send(helplines[4]) @@ -189,9 +215,7 @@ async def eatingdisorder(ctx): ] # -------------------------TRIGGERING BOT EVENT -@bot.event -async def on_ready(): - print('Bot is ready.') + #-------------------------RANDOM QUOTE SUGGESTION def get_qoute(): response = requests.get("https://zenquotes.io/api/random") @@ -249,5 +273,8 @@ async def on_message(message): # REPEATEDLY PING SERVER TO KEEP ALIVE #keepalive() +checkin_announcement.start() +reload_tokens.start() + # ---------------------INITIALIZING THE BOT bot.run(TOKEN) diff --git a/README.md b/README.md index 84859d2..ba0053c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -# FellowBot # FellowBot ๐Ÿค– ### MLH kick-off hackathon Project - FellowBuddy Discord Bot - +#### Check out the FellowBot website [here](https://mlh-fellowship.github.io/FellowBot/) ## **Hi there ! :wave:** ![Cover](Fb.gif) @@ -9,6 +8,9 @@ Talk ๐Ÿ’  Get help ๐Ÿ’  Scrape! +## Note : Ruby version of this bot resides in ./fellowbot.rb file +## Note : movierecsys feature of the discord bot can be found in this [repo](https://github.com/joeyouss/Movierecsys_Joebot) +## Note : sentimental analysis to make bot more dynamic is now supported. ### **This is FellowBot - an MLH kickoff project, a bot and your friend!.** We understand how sometimes life in particular can be difficult and since we are now MLH Fellows, we thought of making something which can be used by every fellow in the server and which makes their life easy. @@ -26,6 +28,7 @@ We understand how sometimes life in particular can be difficult and since we are The project is a discord bot with the following features: - ๐Ÿง  Mental health support - We understand how stressful it can be to manage everything and keep a positive attitude. Come talk to FellowBot about stress, life and unwanted feelings and it will help you!. Have difficulty managinng time? It will recommend you techniques to stay focused. Having a bad day? It will tell you a joke too!. Want motivation? Well, FellowBot will send you an inspiational quote too!. - โœจ Handbook scraping - We understand that you might not remember every page of the handbook but we also agree it is your best friend in this fellowship!. So, need to search for a particular page? Just type in a relevant keyword from our list of keywords and have the information directly in your discord. Say goodbye to searches again! +- ๐Ÿ“… Next events - There are many events which fellows in MLH have to attend. Spending most of their time in Discord, there must be a way to get it directly there. Voila! Just use our FellowBot to get a list of a few ongoing/upcoming events. ## Techstack @@ -34,6 +37,8 @@ The project is a discord bot with the following features: - ๐Ÿฅฃ Beautiful Soup- To scrape the data beautifully. - ๐Ÿ›Œ REST APIs - to fetch quotes and jokes for the fellows - ๐Ÿ Python - The beautiful language to write our dynamic code. +- ๐ŸŽ† Google API: The calendar API for getting the events +- ๐Ÿ“„ GitHub Pages: For hosting the FellowBot wesbite ## Bot commands ### Functionality 1 -> answering your MLH handbook related questions @@ -59,6 +64,9 @@ The project is a discord bot with the following features: ### Functionality 3 -> Emergency Hotline numbers Use ![your emergency] to trigger out helper bot or just enter **!all** to get every helpline number. +### Functionality 4 -> Get upcoming event(s) info +Use !event [count] to get a list of the ongoing and upcoming events of the pod. Enter the count of events to retrieve following the command. It's default value is 1. + ## Scalability ๐ŸŒบ The project can be scaled to fulfill huge possibilities. The current scalable paths comprise of : - Extending the functionality to make the bot more dynamic i.e. text summarization for cosine similarity between asked questions. This will enable us to make our bot more useful. @@ -75,6 +83,11 @@ The project can be scaled to fulfill huge possibilities. The current scalable pa - copy the link in your own browser and authorize the bot. - Voila! Your bot is ready! +## Adding the bot to your server +- Enter your server token in the FellowBot website to add the bot your server +- Make a PR with your details in `Fellows.csv` to get access to confidential information restricted to MLH Fellows +- Yippee! You are done. + ## Contributing ๐Ÿ‘ฌ Being open-source fellows, we used this opportunity to invite contributions from the whole MLH family!. The project is hence hosted as open source and you are welcome to make it more scalable and better ! @@ -83,3 +96,5 @@ We agree that the handbook should be of use to the MLH fellows hence the bot sho ## What we are proud of ? ๐Ÿฅณ Since none of us had any idea about creating Discord bot, the hard work we have put in and the hours we spent on coding made us believe what we as a team can do. Even though we were running late by 2 days, we still decided to start a new project from scratch since it was more useful and ambitious. The experience was thrilling and we had/still having fun coding the project ! + +Created by Jyoti Bisht, Saurabh Agarwala and George A. McCarthy as part of the MLH summer 2021 orientation week hackathon. diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..e58adb6 --- /dev/null +++ b/config.txt @@ -0,0 +1,3 @@ +ODgyNjk3NjU0MzQ4NDQ3Nzc0.YS_KQA.D9dTYYAKFm2bLTkpZ8hI8bN5NMs +882697654348447774 +! \ No newline at end of file diff --git a/fellowbot.rb b/fellowbot.rb new file mode 100644 index 0000000..b384674 --- /dev/null +++ b/fellowbot.rb @@ -0,0 +1,251 @@ +require 'discordrb' +require 'json' + +config = File.foreach('config.txt').map { |line| line.split(' ').join(' ') } +token = config[0].to_s +bot = Discordrb::Commands::CommandBot.new token: "#{token}", client_id: "#{config[1].to_s}", prefix: "#{config[2].to_s}" + +# -----> CONSTANTS + +file = File.read('ok.json') +data_hash = JSON.parse(file) + + +helplines = ["Family Violence Prevention Center 1-800-313-1310", + "National Sexual Assault Hotline 1-800-656-HOPE (4673)", + "Drug Abuse National Helpline 1-800-662-4357", + "American Cancer Society 1-800-227-2345", + "Eating Disorders Awareness and Prevention 1-800-931-2237", + "GriefShare 1-800-395-5755", + "Suicide Hotline 1-800-SUICIDE (784-2433)"] + +# -------------------------MENTAL WELL BEING BOT +greetings = ["hi", "hello","how are you", "whats up","hey"] +sad_words = ["sad", "depressed", "unhappy", "angry", "miserable", "stressed", "hopeless", "unhappy","worthless"] +problems = ["time management", "busy", "impostor", "coding"] +suicide = ["kill myself", "kill yourself","suicidal","death"] + +# ------------replies +greet = [ + "Welcome !. What can I do for you?", + "Hi!!", + "I am so excited to have you here!", + "Hello there, I am an MLH bot here for your help", + "No I am sleeping right now. Just kidding !! how may I help you?" +] + +starter_encouragements = [ + "At times you may feel like this and I understand that, but it won't be for long I can assure you", + "Trying going for a walk and disconnecting with your surroundings and listen to music. The best way to cope up is to take a break", + "I hear you, MLH is there for you. Please talk to your pod leader and tell him in detail what problems are you facing in, he/she will be dhtere for you like we all are", + "You are doing great. If it counts, I am proud of you ! Don't feel like you cannot do better, you ofcourse can!" +] + +copeup=[ + "I can understand totally. But do you know how can you cope up?. Let me help you : 1. Divide your time into blocks", + "and make sure you are being generous to yourself.", + "2. Make sure you drink enough water and go out for a walk when your mind gets cluttered.", + "3. A little music does not hurt anyone.", + "4. We as coders feel as impostors all the time, don't worry just make sure you have faith on yourself", + "I can totally understand, have you ever tried the pomodore technique.? Read about it here - https://en.wikipedia.org/wiki/Pomodoro_Technique#:~:text=The%20Pomodoro%20Technique%20is%20a%20time%20management%20method,25%20minutes%20in%20length%2C%20separated%20by%20short%20breaks. " +] + +positivereply = [ + "We are extremely sorry to hear that you feel like this , but you are not alone in this. We as MLH put your well being as the top priority. Come talk to us what is bothering you. Help is available, speak with a counselor today by calling the National Suicide Prevention Lifeline at 800-273-8255", + "WE ARE THERE FOR YOU!!. When you think life isn't worth it, search for some new options. Help is available, speak with a counselor today by calling the National Suicide Prevention Lifeline at 800-273-8255" +] +MLH_keys_traversal =["MLHintro", "MLHevents", "pods", "discord", "zoom", "feedback", "remote", "hackathonrules","hackjudge", "hackdemo", "attendance", "expectations", "help"] + +MLH_keys = [ + "๐Ÿ˜„ **MLHintro** - For in depth introduction to MLH fellowship.", + "๐ŸŒฒ **MLHevents**- What are MLH events? Know everything about them.", + "- ๐Ÿš‚ **pods** - What are pods? Know everything here", + "- ๐Ÿ“ฑ **discord** -How to use discord and which channel serves what purpose", + "- ๐Ÿ“ž **zoom**- What is zoom used for?", + "- ๐Ÿ“ข **feedback** - What is feedback in MLH?", + "- โฐ **remote** - How to work remotely and give your 100% in MLH.", + "- ๐Ÿ“ **hackathonrules** - Rules for hackathons", + "- ๐Ÿฅ‡ **hackjudge** - How do judges make a decision for hackathon winner? What is taken into consideration?", + "- ๐Ÿ’ฏ **hackdemo** - How to prepare a good demo for hackathon?", + "- ๐Ÿ™‹ **attendance** - What is the importance of attendance in MLH and how is it monitored?", + "- ๐Ÿ’ **expectations** - What MLH expects from you as fellows", + "- โ›‘๏ธ **help** - Facing problems in MLH? Reach out to us" +] + +# ------> function zone +def jokes() + url = "https://v2.jokeapi.dev/joke/Programming,Miscellaneous,Christmas?blacklistFlags=nsfw,political,racist,sexist,explicit&type=twopart" + res = RestClient.get(url) + res = JSON.parse(res) + puts(res) + joke= "" + joke= res["setup"] + " - " + res["delivery"] + return joke +end + +def quotes() + url = "https://zenquotes.io/api/random" + res = RestClient.get(url) + json_data = JSON.parse(res) + quote = "" + quote = json_data[0]['q'] + " -" + json_data[0]['a'] + return quote +end + + +# ------> function zone ends + +# -------> command zone + +bot.command :MLHintro do |event| + for i in data_hash["intents"] do + if i["tag"] == "MLHintro" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :MLHevents do |event| + for i in data_hash["intents"] do + if i["tag"] == "MLHevents" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :pods do |event| + for i in data_hash["intents"] do + if i["tag"] == "pods" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :discord do |event| + for i in data_hash["intents"] do + if i["tag"] == "discord" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :attendance do |event| + for i in data_hash["intents"] do + if i["tag"] == "attendance" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :remote do |event| + for i in data_hash["intents"] do + if i["tag"] == "remote" + for k in i["responses"] do + event.respond k + end + end + end +end + +bot.command :mlh do |event| + for i in MLH_keys do + event.respond i + end +end + +bot.command :ping do |msg| + msg.respond "pong." +end + +bot.command :test do |event| + event.respond helplines.sample +end + + +bot.command :jokes do |event| + event.respond jokes() +end + +bot.command :quote do |event| + event.respond quotes() +end +# ----------> command zone ends +# ----- game section +bot.message(start_with: '!game') do |event| + + magic = rand(1..10) + + event.respond "Can you guess my secret number? It's between 1 and 10!" + event.user.await!(timeout: 300) do |guess_event| + guess = guess_event.message.content.to_i + + if guess == magic + guess_event.respond 'you win!' + true + else + guess_event.respond(guess > magic ? 'too high' : 'too low') + false + end + end + event.respond "My number was: `#{magic}`." + end + +# --------> trivial msg section + +if bot.message(contains: "intro") do |event| + for i in MLH_keys do + event.respond i + end +end +end +if bot.message(contains: sad_words) do |event| + event.respond positivereply.sample + event.respond "here is a joke to make you feel better" + event.respond jokes() + end +end + +if bot.message(contains: greetings) do |event| + event.respond greet.sample + end +end + +if bot.message(contains: problems) do |event| + event.respond copeup.sample + end +end + +if bot.message(contains: suicide ) do |event| + event.respond positivereply.sample + end +end +# ------> time zone section +CROSS_MARK = "\u274c" +bot.message(content: '!time') do |event| + message = event.respond "The current time is: #{Time.now.strftime('%F %T %Z')}" + + message.react CROSS_MARK + bot.add_await!(Discordrb::Events::ReactionAddEvent, message: message, emoji: CROSS_MARK, timeout: 30) do |_reaction_event| + message.delete + end + puts 'Await destroyed.' +end + +# ------รท exit bot section +bot.command(:exit, help_available: false) do |event| + break unless event.user.id == 882697654348447774 + bot.send_message(event.channel.id, 'Bot is shutting down') + exit +end + +bot.run \ No newline at end of file diff --git a/moods.py b/moods.py new file mode 100644 index 0000000..c40cb02 --- /dev/null +++ b/moods.py @@ -0,0 +1,66 @@ +from PIL import Image, ImageDraw, ImageFont +import datetime + + +def draw_block(start, difference, moods): + daily_moods = [] # list of moods (no repeats) + mood_count = {} # count of each mood + highest = 0 # highest count of a mood + + for mood in moods: # just sorts through the day's moods for above vars + if mood not in daily_moods: + daily_moods.append(mood) + mood_count[mood] = moods.count(mood) + if moods.count(mood) > highest: + highest = moods.count(mood) + + daily_moods = sorted(daily_moods) # alpha sort so the bars have some order + + increment = difference / len(daily_moods) # separation of bars + coord_list = [[start]] # beginning left coord of box + + for i in range(len(daily_moods)): # makes list of x coords between all increments and y displacement + lower_x = start + (increment * (i + 1)) + y_coord = (1 - (mood_count[daily_moods[i]] / highest)) * 93 + coord_list.append([lower_x, y_coord]) + + return coord_list, daily_moods + + +def weekly_moods(moods: list, uuid: int): + # defines colours of bars (missing 'confident': 'orange', 'tentative': 'green', 'analytical': 'pink') + colours = { + 'anger': (251, 105, 98), + 'fear': (168, 228, 239), + 'joy': (252, 252, 153), + 'sadness': (121, 222, 121) + } + lower = 260 # lower is the bottom coord of bar, upper is top but it changes + upper = 167 + difference = 93 # difference between the 2 sides of the boxes + blocks = [857, 722, 586, 450, 313, 176, 44] # left coord of each box + + back = Image.open("resource/grey_back.png") # grey background with the boxes + back_draw = ImageDraw.Draw(back) + + for i in range(len(blocks)): + if len(moods[i]) != 0: + coords, daily_moods = draw_block(blocks[i], difference, moods[i]) # for each box, the function is called + + for j in range(len(daily_moods)): + back_draw.rectangle((coords[j][0] + 1, upper + coords[j + 1][1], coords[j + 1][0], lower), + fill=colours[daily_moods[j]]) # draws each mood for each box + + # date text + today = datetime.datetime.now().date() + initial_coord = 860 + difference = 134 + scale = 1.2 + font = ImageFont.truetype("resource/Roboto-Regular.ttf", 17) + + for i in range(7): + back_draw.text((initial_coord - (difference * i) - (i * scale), 270), (today - datetime.timedelta(i + 1) + ).strftime('%m/%d/%Y'), + fill=(119, 131, 211), font=font) + + back.save(f"process/{uuid}.png") diff --git a/requirements.txt b/requirements.txt index 0b1fd46..844ab00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,9 +4,16 @@ attrs==21.2.0 chardet==4.0.0 discord==1.0.1 discord.py==1.7.2 +google-api-core==1.30.0 +google-api-python-client==2.8.0 +google-auth==1.30.1 +google-auth-httplib2==0.1.0 +google-auth-oauthlib==0.4.4 +google-pasta==0.2.0 +googleapis-common-protos==1.53.0 idna==3.2 multidict==5.1.0 python-dotenv==0.17.1 requests==2.25.1 typing-extensions==3.10.0.0 -yarl==1.6.3 +yarl==1.6.3 \ No newline at end of file diff --git a/sentiments.py b/sentiments.py new file mode 100644 index 0000000..8eaa535 --- /dev/null +++ b/sentiments.py @@ -0,0 +1,26 @@ +# Imports the Google Cloud client library +from google.cloud import language_v1 +import os +import json + + +with open('config/config.json') as json_file: + config = json.load(json_file) + +os.environ["GOOGLE_APPLICATION_CREDENTIALS"]=config["G_CLOUD_SERVICE_KEYFILE"] + +client = language_v1.LanguageServiceClient() + + +def google_sentiment_analysis(text: str): + try: + document = language_v1.Document(content=text, type_=language_v1.Document.Type.PLAIN_TEXT) + sentiment = client.analyze_sentiment(request={'document': document}).document_sentiment + sentiment_score = { + "score": sentiment.score, + "magnitude": sentiment.magnitude + } + except Exception: + return None + output = sentiment_score if sentiment_score else None + return output diff --git a/translate.py b/translate.py new file mode 100644 index 0000000..3503b05 --- /dev/null +++ b/translate.py @@ -0,0 +1,25 @@ +from google.cloud import translate_v2 as translate +import json +import os +import six +import html + +with open('config/config.json') as json_file: + config = json.load(json_file) + +os.environ["GOOGLE_APPLICATION_CREDENTIALS"]=config["G_CLOUD_SERVICE_KEYFILE"] + + +def google_translate(text, language="en"): + """ + Translates text into the target language + Target must be an ISO 639-1 language code. + See https://g.co/cloud/translate/v2/translate-reference#supported_languages + """ + translate_client = translate.Client() + + if isinstance(text, six.binary_type): + text = text.decode("utf-8") + result = translate_client.translate(text, target_language=language) + result['translatedText'] = html.unescape(result['translatedText']) # + return result diff --git a/bot.py b/trialBot.py similarity index 100% rename from bot.py rename to trialBot.py