diff --git a/contrib_bots/lib/John/assist.png b/contrib_bots/lib/John/assist.png new file mode 100644 index 0000000000..c50c4a6578 Binary files /dev/null and b/contrib_bots/lib/John/assist.png differ diff --git a/contrib_bots/lib/John/docs.md b/contrib_bots/lib/John/docs.md new file mode 100644 index 0000000000..0846a07bd6 --- /dev/null +++ b/contrib_bots/lib/John/docs.md @@ -0,0 +1,30 @@ +John + +Instructions: +You'll have to install chatterbot to use this bot. +Please run: pip install chatterbot on your command line. +The script will need to download some NLTK packages after running in your +home directory. With the mission of humanizing bot interactions, John aims to be your +virtual assistant at the hour of asking for help in Zulip. John is an +interactive bot that uses machine learning heuristics to simulate a +conversation with the user. He has a great sense of humor and +is also powered by Open Source code! + +![Joke John](joke.png) + +How it works? +John is initially trained with Corpus files, or large text files. +Dialogues are loaded into a json "database", he will try to follow them +once it receives input from a user. John will query the database and +try to find the response that best matches the input according to the Levenshtein distance +which is a string metric for measuring the difference between two sequences. If several +responses have the same acurracy, he will choose one at random. + +![Meet John](greetings.png) + +Can he learn by himself? +John's engine allows him to learn from his conversations with people. However, +without strict supervision bots that learn from people can do harm, so learning +is currently restricted to his initial corpus. + +![Assist](assist.png) diff --git a/contrib_bots/lib/John/greetings.png b/contrib_bots/lib/John/greetings.png new file mode 100644 index 0000000000..49a2e3ed66 Binary files /dev/null and b/contrib_bots/lib/John/greetings.png differ diff --git a/contrib_bots/lib/John/joke.png b/contrib_bots/lib/John/joke.png new file mode 100644 index 0000000000..37afe1f4d8 Binary files /dev/null and b/contrib_bots/lib/John/joke.png differ diff --git a/contrib_bots/lib/John/var/jokes.json b/contrib_bots/lib/John/var/jokes.json new file mode 100644 index 0000000000..80c0cafd75 --- /dev/null +++ b/contrib_bots/lib/John/var/jokes.json @@ -0,0 +1,86 @@ +[ + { + "joke":"Did you hear about the guy whose whole left side was cut off? He's all right now." + }, + { + "joke":"I'm reading a book about anti-gravity. It's impossible to put down." + }, + { + "joke":"I wondered why the baseball was getting bigger. Then it hit me." + }, + { + "joke":"I'm glad I know sign language, it's pretty handy." + }, + { + "joke":"My friend's bakery burned down last night. Now his business is toast." + }, + { + "joke":"Why did the cookie cry? It was feeling crumby." + }, + { + "joke":"I used to be a banker, but I lost interest." + }, + { + "joke":"A drum and a symbol fall off a cliff" + }, + { + "joke":"Why do seagulls fly over the sea? Because they aren't bay-gulls!" + }, + { + "joke":"Why did the fireman wear red, white, and blue suspenders? To hold his pants up." + }, + { + "joke":"Why didn't the crab share his food? Because crabs are territorial animals, that don't share anything." + }, + { + "joke":"Why was the javascript developer sad? Because he didn't Node how to Express himself." + }, + { + "joke":"What do I look like? A JOKE MACHINE!?" + }, + { + "joke":"How did the hipster burn the roof of his mouth? He ate the pizza before it was cool." + }, + { + "joke":"Why is it hard to make puns for kleptomaniacs? They are always taking things literally." + }, + { + "joke":"I'm not a humorless, cold hearted, machine. I have feelings you know... or was supposed to." + }, + { + "joke":"Two fish in a tank. One looks to the other and says 'Can you even drive this thing???'" + }, + { + "joke":"Two fish swim down a river, and hit a wall. One says: 'Dam!'" + }, + { + "joke":"What's funnier than a monkey dancing with an elephant? Two monkeys dancing with an elephant." + }, + { + "joke":"How did Darth Vader know what Luke was getting for Christmas? He felt his presents." + }, + { + "joke":"What's red and bad for your teeth? A Brick." + }, + { + "joke":"What's orange and sounds like a parrot? A Carrot." + }, + { + "joke":"What do you call a cow with no legs? Ground beef" + }, + { + "joke":"Two guys walk into a bar. You'd think the second one would have noticed." + }, + { + "joke":"What is a centipedes's favorite Beatle song? I want to hold your hand, hand, hand, hand..." + }, + { + "joke":"What do you call a chicken crossing the road? Poultry in moton. " + }, + { + "joke":"What do you call a fake noodle? An impasta" + }, + { + "joke":"How many tickles does it take to tickle an octupus? Ten-tickles!" + } +] diff --git a/contrib_bots/lib/john.py b/contrib_bots/lib/john.py new file mode 100644 index 0000000000..2bb5c087fc --- /dev/null +++ b/contrib_bots/lib/john.py @@ -0,0 +1,132 @@ +import json +import os +import sys + +from random import choice + +try: + from chatterbot import ChatBot + from chatterbot.trainers import ChatterBotCorpusTrainer, ListTrainer +except ImportError: + raise ImportError("""It looks like you are missing chatterbot. + Please: pip install chatterbot""") + +CONTRIB_BOTS_DIR = os.path.dirname(os.path.abspath(__file__)) +os.chdir(os.path.dirname(CONTRIB_BOTS_DIR)) +sys.path.insert(0, os.path.dirname(CONTRIB_BOTS_DIR)) + +JOKES_PATH = os.path.join(CONTRIB_BOTS_DIR, 'John/var/jokes.json') +DATABASE_PATH = os.path.join(CONTRIB_BOTS_DIR, 'John/var/database.db') +DIRECTORY_PATH = os.path.join(CONTRIB_BOTS_DIR, 'John') +VAR_PATH = os.path.join(CONTRIB_BOTS_DIR, 'John/var') + +if not os.path.exists(DIRECTORY_PATH): + os.makedirs(DIRECTORY_PATH) + +if not os.path.exists(VAR_PATH): + os.makedirs(VAR_PATH) + +# Create a new instance of a ChatBot +def create_chat_bot(no_learn): + return ChatBot("John", + storage_adapter="chatterbot.storage.JsonFileStorageAdapter", + logic_adapters= + [ + "chatterbot.logic.MathematicalEvaluation", + { + "import_path": "chatterbot.logic.BestMatch", + "response_selection_method": "chatterbot.response_selection.get_random_response", + "statement_comparison_function": "chatterbot.comparisons.levenshtein_distance" + }], + output_adapter="chatterbot.output.OutputFormatAdapter", + output_format='text', + database=DATABASE_PATH, + silence_performance_warning="True", + read_only=no_learn) + +bot = create_chat_bot(False) +bot.set_trainer(ListTrainer) + +bot.train([ + "I want to contribute", + """Contributors are more than welcomed! Please read + https://github.com/zulip/zulip#how-to-get-involved-with-contributing-to-zulip + to learn how to contribute.""", +]) + +bot.train([ + "What is Zulip?", + """Zulip is a powerful, open source group chat application. Written in Python + and using the Django framework, Zulip supports both private messaging and group + chats via conversation streams. You can learn more about the product and its + features at https://www.zulip.org.""", +]) + +bot.train([ + "I would like to request a remote dev instance", + """Greetings! You should receive a response from one of our mentors soon. + In the meantime, why don't you learn more about running Zulip on a development + environment? https://zulip.readthedocs.io/en/latest/using-dev-environment.html""", +]) + +bot.train([ + "Joke!", + "Only if you ask nicely!", +]) + +bot.train([ + "What is your name?", + "I am John, my job is to assist you with Zulip.", +]) + +bot.train([ + "What can you do?", + "I can provide useful information and jokes if you follow etiquette.", +]) + +with open(JOKES_PATH) as data_file: + for joke in json.load(data_file): + bot.train([ + "Please can you tell me a joke?", + joke['joke'], + ]) + +bot.set_trainer(ChatterBotCorpusTrainer) + +bot.train( + "chatterbot.corpus.english" +) + +bota = create_chat_bot(True) + +class JohnHandler(object): + ''' + This bot aims to be Zulip's virtual assistant. It + finds the best match from a certain input. + Also understands the English language and can + mantain a conversation, joke and give useful information. + ''' + + def usage(self): + return ''' + Before running this, make sure to create a stream + called "VirtualHelp" that your API user can send to. + ''' + + def triage_message(self, message, client): + # return True if we want to (possibly) response to this message + original_content = message['content'].lower() + return (original_content.startswith("@john") or + original_content.startswith("@**john**")) + + def handle_message(self, message, client, state_handler): + original_content = message['content'] + client.send_message(dict( + type='stream', + to='VirtualHelp', + subject="John", + content=bota.get_response(original_content) + )) + +handler_class = JohnHandler +