diff --git a/README.md b/README.md
index 44ef0f8..1a807de 100644
--- a/README.md
+++ b/README.md
@@ -2,31 +2,21 @@
 
 Cog Function
 
-| Name | Status | Description
+| Name | Status | Description (Click to see full status)
 | --- | --- | --- | 
-| ccrole | **Beta** | Create custom commands that also assign roles |
-| chatter | **Alpha** | Chat-bot trained to talk like your guild
-| fight | **Incomplete** | Organize bracket tournaments within discord |
-| flag | **Beta** | Create temporary marks on users that expire after specified time |
-| hangman | **Incomplete** | Play a game of hangman |
-| immortal | **Private** | Cog designed for a specific server, not recommended to install |
-| leaver | **Incomplete** | Send a message in a channel when a user leaves the server |
-| reactrestrict | **Alpha** | Removes reactions by role per channel |
-| stealemoji | **Alpha** | Steals any custom emoji it sees |
-| werewolf | **Incomplete** | Play the classic party game Werewolf within discord |
+| ccrole | **Beta** | Create custom commands that also assign roles
May have some bugs, please create an issue if you find any  |
+| chatter | **Alpha** | Chat-bot trained to talk like your guild
Missing some key features, but currently functional  |
+| fight | **Incomplete** | Organize bracket tournaments within discord
Still in-progress, a massive project  |
+| flag | **Incomplete** | Create temporary marks on users that expire after specified time
Not yet ported to v3  |
+| hangman | **Alpha** | Play a game of hangman
Some visual glitches and needs more customization  |
+| howdoi | **Incomplete** | Create temporary marks on users that expire after specified time
Not yet ported to v3  |
+| leaver | **Incomplete** | Send a message in a channel when a user leaves the server
Not yet ported to v3  |
+| reactrestrict | **Alpha** | Removes reactions by role per channel
A bit clunky, but functional  |
+| secrethitler | **Incomplete** | Play the Secret Hitler game
Concept, no work done yet  |
+| stealemoji | **Alpha** | Steals any custom emoji it sees in a reaction
Some planned upgrades for server generation  |
+| werewolf | **Incomplete** | Play the classic party game Werewolf within discord
Another massive project, will be fully customizable  |
 
-Cog Status Descriptions
 
-  - ccrole: May have some bugs, please create an issue if you find any
-  - chatter: Missing some key features, but currently functional
-  - fight: Still in-progress, a massive project
-  - flag: Not yet ported to v3
-  - hangman: Not yet ported to v3
-  - immortal: Designed for a specific server, not recommended to install
-  - leaver: Not yet ported to v3
-  - reactrestrict: A bit clunky, but functional
-  - stealemoji: Some planned upgrades for server generation
-  - werewolf: Another massive project, will be fully customizable
+Check out my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
 
-Many of these are functional in my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
-  
\ No newline at end of file
+Get support on the [Third Party Cog Server](https://discord.gg/GET4DVk)
diff --git a/ccrole/ccrole.py b/ccrole/ccrole.py
index 38cff39..2459b6e 100644
--- a/ccrole/ccrole.py
+++ b/ccrole/ccrole.py
@@ -1,14 +1,11 @@
-import discord
-import asyncio 
-
-from discord.ext import commands
-
-from redbot.core import Config, checks
-
-from redbot.core.utils.chat_formatting import pagify, box
-import os
+import asyncio
 import re
 
+import discord
+from discord.ext import commands
+from redbot.core import Config, checks
+from redbot.core.utils.chat_formatting import pagify, box
+
 
 class CCRole:
     """
@@ -20,23 +17,27 @@ class CCRole:
         self.bot = bot
         self.config = Config.get_conf(self, identifier=9999114111108101)
         default_guild = {
-            "cmdlist" : {}, 
+            "cmdlist": {},
             "settings": {}
         }
-        
-        self.config.register_guild(**default_guild)
 
+        self.config.register_guild(**default_guild)
 
     @commands.group(no_pm=True)
     async def ccrole(self, ctx):
-        """Custom commands management"""
+        """Custom commands management with roles
+
+        Highly customizable custom commands with role management."""
         if not ctx.invoked_subcommand:
             await ctx.send_help()
 
     @ccrole.command(name="add")
     @checks.mod_or_permissions(administrator=True)
-    async def ccrole_add(self, ctx, command : str):
-        """Adds a custom command with roles"""
+    async def ccrole_add(self, ctx, command: str):
+        """Adds a custom command with roles
+
+        When adding text, put arguments in `{}` to eval them
+        Options: `{author}`, `{target}`, `{server}`, `{channel}`, `{message}`"""
         command = command.lower()
         if command in self.bot.all_commands:
             await ctx.send("That command is already a standard command.")
@@ -45,125 +46,164 @@ class CCRole:
         guild = ctx.guild
         author = ctx.author
         channel = ctx.channel
-        
-        cmdlist = self.config.guild(ctx.guild).cmdlist
-        
-        if await cmdlist.get_raw(command, default=None):
+
+        cmd_list = self.config.guild(guild).cmdlist
+
+        if await cmd_list.get_raw(command, default=None):
             await ctx.send("This command already exists. Delete it with `{}ccrole delete` first.".format(ctx.prefix))
             return
 
         # Roles to add
         await ctx.send('What roles should it add? (Must be **comma separated**)\nSay `None` to skip adding roles')
-        
+
         def check(m):
-            return m.author == author and m.channel==channel
-        
+            return m.author == author and m.channel == channel
+
         try:
             answer = await self.bot.wait_for('message', timeout=120, check=check)
         except asyncio.TimeoutError:
             await ctx.send("Timed out, canceling")
-            
+            return
+
         arole_list = []
-        if answer.content.upper()!="NONE":
+        if answer.content.upper() != "NONE":
             arole_list = await self._get_roles_from_content(ctx, answer.content)
             if arole_list is None:
                 await ctx.send("Invalid answer, canceling")
                 return
-        
+
         # Roles to remove
         await ctx.send('What roles should it remove? (Must be comma separated)\nSay `None` to skip removing roles')
         try:
             answer = await self.bot.wait_for('message', timeout=120, check=check)
         except asyncio.TimeoutError:
             await ctx.send("Timed out, canceling")
-        
+            return
+
         rrole_list = []
-        if answer.content.upper()!="NONE":
+        if answer.content.upper() != "NONE":
             rrole_list = await self._get_roles_from_content(ctx, answer.content)
             if rrole_list is None:
                 await ctx.send("Invalid answer, canceling")
                 return
-                
+
         # Roles to use
-        await ctx.send('What roles are allowed to use this command? (Must be comma separated)\nSay `None` to allow all roles')
-        
+        await ctx.send(
+            'What roles are allowed to use this command? (Must be comma separated)\nSay `None` to allow all roles')
+
         try:
             answer = await self.bot.wait_for('message', timeout=120, check=check)
         except asyncio.TimeoutError:
             await ctx.send("Timed out, canceling")
-        
+            return
+
         prole_list = []
-        if answer.content.upper()!="NONE":
+        if answer.content.upper() != "NONE":
             prole_list = await self._get_roles_from_content(ctx, answer.content)
             if prole_list is None:
                 await ctx.send("Invalid answer, canceling")
                 return
-                
+
         # Selfrole
         await ctx.send('Is this a targeted command?(yes/no)\nNo will make this a selfrole command')
-        
+
         try:
             answer = await self.bot.wait_for('message', timeout=120, check=check)
         except asyncio.TimeoutError:
             await ctx.send("Timed out, canceling")
-        
+            return
+
         if answer.content.upper() in ["Y", "YES"]:
             targeted = True
             await ctx.send("This command will be **`targeted`**")
         else:
             targeted = False
             await ctx.send("This command will be **`selfrole`**")
-        
+
         # Message to send
-        await ctx.send('What message should the bot say when using this command?\nSay `None` to send the default `Success!` message')
-        
+        await ctx.send(
+            'What message should the bot say when using this command?\n'
+            'Say `None` to send the default `Success!` message\n'
+            'Eval Options: `{author}`, `{target}`, `{server}`, `{channel}`, `{message}`\n'
+            'For example: `Welcome {target.mention} to {server.name}!`')
+
         try:
             answer = await self.bot.wait_for('message', timeout=120, check=check)
         except asyncio.TimeoutError:
             await ctx.send("Timed out, canceling")
+            return
+
         text = "Success!"
-        if answer.content.upper()!="NONE":
+        if answer.content.upper() != "NONE":
             text = answer.content
 
         # Save the command
-        
+
         out = {'text': text, 'aroles': arole_list, 'rroles': rrole_list, "proles": prole_list, "targeted": targeted}
-        
-        await cmdlist.set_raw(command, value=out)
-        
-        ctx.send("Custom Command **`{}`** successfully added".format(command))
-        
+
+        await cmd_list.set_raw(command, value=out)
+
+        await ctx.send("Custom Command **`{}`** successfully added".format(command))
+
     @ccrole.command(name="delete")
     @checks.mod_or_permissions(administrator=True)
-    async def ccrole_delete(self, ctx, command : str):
+    async def ccrole_delete(self, ctx, command: str):
         """Deletes a custom command
+
         Example:
-        [p]ccrole delete yourcommand"""
+        `[p]ccrole delete yourcommand`"""
         guild = ctx.guild
         command = command.lower()
-        if not await self.config.guild(ctx.guild).cmdlist.get_raw(command, default=None):
+        if not await self.config.guild(guild).cmdlist.get_raw(command, default=None):
             await ctx.send("That command doesn't exist")
         else:
-            await self.config.guild(ctx.guild).cmdlist.set_raw(command, value=None)
+            await self.config.guild(guild).cmdlist.set_raw(command, value=None)
             await ctx.send("Custom command successfully deleted.")
 
+    @ccrole.command(name="details")
+    async def ccrole_details(self, ctx, command: str):
+        """Provide details about passed custom command"""
+        guild = ctx.guild
+        command = command.lower()
+        cmd = await self.config.guild(guild).cmdlist.get_raw(command, default=None)
+        if cmd is None:
+            await ctx.send("That command doesn't exist")
+            return
+
+        embed = discord.Embed(title=command,
+                              description="{} custom command".format("Targeted" if cmd['targeted'] else "Non-Targeted"))
+
+        def process_roles(role_list):
+            if not role_list:
+                return "None"
+            return ", ".join([discord.utils.get(ctx.guild.roles, id=roleid).name for roleid in role_list])
+
+        embed.add_field(name="Text", value="```{}```".format(cmd['text']))
+        embed.add_field(name="Adds Roles", value=process_roles(cmd['aroles']), inline=True)
+        embed.add_field(name="Removes Roles", value=process_roles(cmd['rroles']), inline=True)
+        embed.add_field(name="Role Restrictions", value=process_roles(cmd['proles']), inline=True)
+
+        await ctx.send(embed=embed)
+
     @ccrole.command(name="list")
     async def ccrole_list(self, ctx):
         """Shows custom commands list"""
         guild = ctx.guild
-        commands = await self.config.guild(ctx.guild).cmdlist()
-
-        if not commands:
-            await ctx.send("There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format(ctx.prefix))
+        cmd_list = await self.config.guild(guild).cmdlist()
+        cmd_list = {k: v for k,v in cmd_list.items() if v}
+        if not cmd_list:
+            await ctx.send(
+                "There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format(
+                    ctx.prefix))
             return
 
-        commands = ", ".join([ctx.prefix + c for c in sorted(commands.keys())])
-        commands = "Custom commands:\n\n" + commands
+        cmd_list = ", ".join([ctx.prefix + c for c in sorted(cmd_list.keys())])
+        cmd_list = "Custom commands:\n\n" + cmd_list
 
-        if len(commands) < 1500:
-            await ctx.send(box(commands))
+        if len(cmd_list) < 1500:  # I'm allowed to have arbitrary numbers for when it's too much to dm dammit
+            await ctx.send(box(cmd_list))
         else:
-            for page in pagify(commands, delims=[" ", "\n"]):
+            for page in pagify(cmd_list, delims=[" ", "\n"]):
                 await ctx.author.send(box(page))
             await ctx.send("Command list DM'd")
 
@@ -177,24 +217,22 @@ class CCRole:
         except ValueError:
             return
 
-        
         cmdlist = self.config.guild(guild).cmdlist
-        cmd = message.content[len(prefix):].split()[0]
-        cmd = await cmdlist.get_raw(cmd.lower(), default=None)
-        
-        if cmd:
+        cmd = message.content[len(prefix):].split()[0].lower()
+        cmd = await cmdlist.get_raw(cmd, default=None)
+
+        if cmd is not None:
             await self.eval_cc(cmd, message)
-    
+
     async def _get_roles_from_content(self, ctx, content):
         content_list = content.split(",")
-        role_list = []
         try:
             role_list = [discord.utils.get(ctx.guild.roles, name=role.strip(' ')).id for role in content_list]
-        except:
+        except (discord.HTTPException, AttributeError):  # None.id is attribute error
             return None
         else:
             return role_list
-    
+
     async def get_prefix(self, message: discord.Message) -> str:
         """
         Borrowed from alias cog
@@ -214,27 +252,26 @@ class CCRole:
             if content.startswith(p):
                 return p
         raise ValueError
-        
+
     async def eval_cc(self, cmd, message):
         """Does all the work"""
         if cmd['proles'] and not (set(role.id for role in message.author.roles) & set(cmd['proles'])):
             return  # Not authorized, do nothing
-        
+
         if cmd['targeted']:
             try:
                 target = discord.utils.get(message.guild.members, mention=message.content.split()[1])
-            except:
+            except IndexError:  # .split() return list of len<2
                 target = None
-            
+
             if not target:
-                out_message = "This command is targeted! @mention a target\n`{} `".format(message.content.split()[0])
-                
+                out_message = "This custom command is targeted! @mention a target\n`{} `".format(
+                    message.content.split()[0])
                 await message.channel.send(out_message)
-                
                 return
         else:
             target = message.author
-            
+
         if cmd['aroles']:
             arole_list = [discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd['aroles']]
             # await self.bot.send_message(message.channel, "Adding: "+str([str(arole) for arole in arole_list]))
@@ -243,7 +280,7 @@ class CCRole:
             except discord.Forbidden:
                 await message.channel.send("Permission error: Unable to add roles")
         await asyncio.sleep(1)
-        
+
         if cmd['rroles']:
             rrole_list = [discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd['rroles']]
             # await self.bot.send_message(message.channel, "Removing: "+str([str(rrole) for rrole in rrole_list]))
@@ -251,37 +288,40 @@ class CCRole:
                 await target.remove_roles(*rrole_list)
             except discord.Forbidden:
                 await message.channel.send("Permission error: Unable to remove roles")
-        await message.channel.send(cmd['text'])
-            
-        # {'text': text, 'aroles': arole_list, 'rroles': rrole_list, "proles", prole_list, "targeted": targeted}
 
-    # def format_cc(self, command, message):
-        # results = re.findall("\{([^}]+)\}", command)
-        # for result in results:
-            # param = self.transform_parameter(result, message)
-            # command = command.replace("{" + result + "}", param)
-        # return command
+        out_message = self.format_cc(cmd, message, target)
+        await message.channel.send(out_message)
 
-    # def transform_parameter(self, result, message):
-        # """
-        # For security reasons only specific objects are allowed
-        # Internals are ignored
-        # """
-        # raw_result = "{" + result + "}"
-        # objects = {
-            # "message" : message,
-            # "author"  : message.author,
-            # "channel" : message.channel,
-            # "server"  : message.server
-        # }
-        # if result in objects:
-            # return str(objects[result])
-        # try:
-            # first, second = result.split(".")
-        # except ValueError:
-            # return raw_result
-        # if first in objects and not second.startswith("_"):
-            # first = objects[first]
-        # else:
-            # return raw_result
-        # return str(getattr(first, second, raw_result))
\ No newline at end of file
+    def format_cc(self, cmd, message, target):
+        out = cmd['text']
+        results = re.findall("{([^}]+)\}", out)
+        for result in results:
+            param = self.transform_parameter(result, message, target)
+            out = out.replace("{" + result + "}", param)
+        return out
+
+    def transform_parameter(self, result, message, target):
+        """
+        For security reasons only specific objects are allowed
+        Internals are ignored
+        """
+        raw_result = "{" + result + "}"
+        objects = {
+            "message": message,
+            "author": message.author,
+            "channel": message.channel,
+            "server": message.guild,
+            "guild": message.guild,
+            "target": target
+        }
+        if result in objects:
+            return str(objects[result])
+        try:
+            first, second = result.split(".")
+        except ValueError:
+            return raw_result
+        if first in objects and not second.startswith("_"):
+            first = objects[first]
+        else:
+            return raw_result
+        return str(getattr(first, second, raw_result))
diff --git a/chatter/__init__.py b/chatter/__init__.py
index 2d7a8e8..cc101b7 100644
--- a/chatter/__init__.py
+++ b/chatter/__init__.py
@@ -1,5 +1,11 @@
-from .chatter import Chatter
+from . import chatterbot
+from .chat import Chatter
 
 
 def setup(bot):
     bot.add_cog(Chatter(bot))
+
+
+__all__ = (
+    'chatterbot'
+)
diff --git a/chatter/chatter.py b/chatter/chat.py
similarity index 72%
rename from chatter/chatter.py
rename to chatter/chat.py
index 5e02d39..32d83a3 100644
--- a/chatter/chatter.py
+++ b/chatter/chat.py
@@ -1,16 +1,13 @@
 import asyncio
-from typing import List, Union
+from datetime import datetime, timedelta
 
 import discord
 from discord.ext import commands
+from redbot.core import Config
 
-from redbot.core import Config, RedContext
-from redbot.core.bot import Red
+from chatter.chatterbot import ChatBot
+from chatter.chatterbot.trainers import ListTrainer
 
-from .source import ChatBot
-from .source.trainers import ListTrainer
-
-from datetime import datetime,timedelta
 
 
 class Chatter:
@@ -25,17 +22,21 @@ class Chatter:
         default_guild = {
             "whitelist": None,
             "days": 1
-            }
-        
-        self.chatbot = ChatBot("ChatterBot")
+        }
+
+        self.chatbot = ChatBot(
+            "ChatterBot",
+            storage_adapter='chatter.chatterbot.storage.SQLStorageAdapter',
+            database='./database.sqlite3'
+        )
         self.chatbot.set_trainer(ListTrainer)
 
         self.config.register_global(**default_global)
         self.config.register_guild(**default_guild)
-        
+
         self.loop = asyncio.get_event_loop()
-    
-    async def _get_conversation(self, ctx, in_channel: discord.TextChannel=None):
+
+    async def _get_conversation(self, ctx, in_channel: discord.TextChannel = None):
         """
         Compiles all conversation in the Guild this bot can get it's hands on
         Currently takes a stupid long time
@@ -43,9 +44,8 @@ class Chatter:
         """
         out = []
         after = datetime.today() - timedelta(days=(await self.config.guild(ctx.guild).days()))
-        
 
-        for channel in ctx.guild.text_channels: 
+        for channel in ctx.guild.text_channels:
             if in_channel:
                 channel = in_channel
             await ctx.send("Gathering {}".format(channel.mention))
@@ -53,7 +53,7 @@ class Chatter:
             try:
                 async for message in channel.history(limit=None, reverse=True, after=after):
                     if user == message.author:
-                        out[-1] += "\n"+message.clean_content
+                        out[-1] += "\n" + message.clean_content
                     else:
                         user = message.author
                         out.append(message.clean_content)
@@ -61,12 +61,12 @@ class Chatter:
                 pass
             except discord.HTTPException:
                 pass
-            
+
             if in_channel:
                 break
-        
+
         return out
-        
+
     def _train(self, data):
         try:
             self.chatbot.train(data)
@@ -81,45 +81,59 @@ class Chatter:
         """
         if ctx.invoked_subcommand is None:
             await ctx.send_help()
+
     @chatter.command()
     async def age(self, ctx: RedContext, days: int):
         """
         Sets the number of days to look back
         Will train on 1 day otherwise
         """
-        
+
         await self.config.guild(ctx.guild).days.set(days)
         await ctx.send("Success")
-        
+
     @chatter.command()
-    async def train(self, ctx: RedContext, channel: discord.TextChannel = None):
+    async def backup(self, ctx, backupname):
+        """
+        Backup your training data to a json for later use
+        """
+        await ctx.send("Backing up data, this may take a while")
+        future = await self.loop.run_in_executor(None, self.chatbot.trainer.export_for_training, './{}.json'.format(backupname))
+
+        if future:
+            await ctx.send("Backup successful!")
+        else:
+            await ctx.send("Error occurred :(")
+
+    @chatter.command()
+    async def train(self, ctx: commands.Context, channel: discord.TextChannel):
         """
         Trains the bot based on language in this guild
         """
-        
+
         conversation = await self._get_conversation(ctx, channel)
-        
+
         if not conversation:
             await ctx.send("Failed to gather training data")
             return
-            
+
         await ctx.send("Gather successful! Training begins now\n(**This will take a long time, be patient**)")
-        embed=discord.Embed(title="Loading")
+        embed = discord.Embed(title="Loading")
         embed.set_image(url="http://www.loop.universaleverything.com/animations/1295.gif")
         temp_message = await ctx.send(embed=embed)
         future = await self.loop.run_in_executor(None, self._train, conversation)
-        
+
         try:
             await temp_message.delete()
         except:
             pass
-        
+
         if future:
             await ctx.send("Training successful!")
         else:
             await ctx.send("Error occurred :(")
-            
-    async def on_message(self, message: discord.Message):
+
+    async def on_message(self, message):
         """
         Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
         for on_message recognition of @bot
@@ -135,9 +149,9 @@ class Chatter:
                 return
             text = text.replace(to_strip, "", 1)
             async with channel.typing():
-                response = self.chatbot.get_response(text)
-                if not response:
-                    response = ":thinking:"
-                await channel.send(response)
-                
+                future = await self.loop.run_in_executor(None, self.chatbot.get_response, text)
 
+                if future:
+                    await channel.send(str(future))
+                else:
+                    await channel.send(':thinking:')
diff --git a/chatter/source/__init__.py b/chatter/chatterbot/__init__.py
similarity index 91%
rename from chatter/source/__init__.py
rename to chatter/chatterbot/__init__.py
index 2ea55f6..7a127ee 100644
--- a/chatter/source/__init__.py
+++ b/chatter/chatterbot/__init__.py
@@ -3,7 +3,7 @@ ChatterBot is a machine learning, conversational dialog engine.
 """
 from .chatterbot import ChatBot
 
-__version__ = '0.8.4'
+__version__ = '0.8.5'
 __author__ = 'Gunther Cox'
 __email__ = 'gunthercx@gmail.com'
 __url__ = 'https://github.com/gunthercox/ChatterBot'
diff --git a/chatter/source/__main__.py b/chatter/chatterbot/__main__.py
similarity index 99%
rename from chatter/source/__main__.py
rename to chatter/chatterbot/__main__.py
index a27f483..0322854 100644
--- a/chatter/source/__main__.py
+++ b/chatter/chatterbot/__main__.py
@@ -1,6 +1,5 @@
 import sys
 
-
 if __name__ == '__main__':
     import importlib
 
diff --git a/chatter/source/adapters.py b/chatter/chatterbot/adapters.py
similarity index 96%
rename from chatter/source/adapters.py
rename to chatter/chatterbot/adapters.py
index f99734d..83ce94c 100644
--- a/chatter/source/adapters.py
+++ b/chatter/chatterbot/adapters.py
@@ -16,7 +16,7 @@ class Adapter(object):
         """
         Gives the adapter access to an instance of the ChatBot class.
 
-        :param chatbot: A chat bot instanse.
+        :param chatbot: A chat bot instance.
         :type chatbot: ChatBot
         """
         self.chatbot = chatbot
diff --git a/chatter/source/chatterbot.py b/chatter/chatterbot/chatterbot.py
similarity index 91%
rename from chatter/source/chatterbot.py
rename to chatter/chatterbot/chatterbot.py
index 66a92b9..c7a92cb 100644
--- a/chatter/source/chatterbot.py
+++ b/chatter/chatterbot/chatterbot.py
@@ -1,9 +1,11 @@
 from __future__ import unicode_literals
+
 import logging
-from .storage import StorageAdapter
+
+from . import utils
 from .input import InputAdapter
 from .output import OutputAdapter
-from . import utils
+from .storage import StorageAdapter
 
 
 class ChatBot(object):
@@ -20,15 +22,15 @@ class ChatBot(object):
 
         self.default_session = None
 
-        storage_adapter = kwargs.get('storage_adapter', 'chatter.source.storage.SQLStorageAdapter')
+        storage_adapter = kwargs.get('storage_adapter', 'chatter.chatterbot.storage.SQLStorageAdapter')
 
         logic_adapters = kwargs.get('logic_adapters', [
-            'chatter.source.logic.BestMatch'
+            'chatter.chatterbot.logic.BestMatch'
         ])
 
-        input_adapter = kwargs.get('input_adapter', 'chatter.source.input.VariableInputTypeAdapter')
+        input_adapter = kwargs.get('input_adapter', 'chatter.chatterbot.input.VariableInputTypeAdapter')
 
-        output_adapter = kwargs.get('output_adapter', 'chatter.source.output.OutputAdapter')
+        output_adapter = kwargs.get('output_adapter', 'chatter.chatterbot.output.OutputAdapter')
 
         # Check that each adapter is a valid subclass of it's respective parent
         utils.validate_adapter_class(storage_adapter, StorageAdapter)
@@ -45,7 +47,7 @@ class ChatBot(object):
 
         # Add required system logic adapter
         self.logic.system_adapters.append(
-            utils.initialize_class('chatter.source.logic.NoKnowledgeAdapter', **kwargs)
+            utils.initialize_class('chatter.chatterbot.logic.NoKnowledgeAdapter', **kwargs)
         )
 
         for adapter in logic_adapters:
@@ -59,7 +61,7 @@ class ChatBot(object):
 
         preprocessors = kwargs.get(
             'preprocessors', [
-                'chatter.source.preprocessors.clean_whitespace'
+                'chatter.chatterbot.preprocessors.clean_whitespace'
             ]
         )
 
@@ -69,7 +71,7 @@ class ChatBot(object):
             self.preprocessors.append(utils.import_module(preprocessor))
 
         # Use specified trainer or fall back to the default
-        trainer = kwargs.get('trainer', 'chatter.source.trainers.Trainer')
+        trainer = kwargs.get('trainer', 'chatter.chatterbot.trainers.Trainer')
         TrainerClass = utils.import_module(trainer)
         self.trainer = TrainerClass(self.storage, **kwargs)
         self.training_data = kwargs.get('training_data')
diff --git a/chatter/source/comparisons.py b/chatter/chatterbot/comparisons.py
similarity index 96%
rename from chatter/source/comparisons.py
rename to chatter/chatterbot/comparisons.py
index 816e175..59efa95 100644
--- a/chatter/source/comparisons.py
+++ b/chatter/chatterbot/comparisons.py
@@ -1,5 +1,4 @@
 # -*- coding: utf-8 -*-
-import sys
 
 
 """
@@ -58,19 +57,14 @@ class LevenshteinDistance(Comparator):
         :rtype: float
         """
 
-        PYTHON = sys.version_info[0]
-
         # Return 0 if either statement has a falsy text value
         if not statement.text or not other_statement.text:
             return 0
 
         # Get the lowercase version of both strings
-        if PYTHON < 3:
-            statement_text = unicode(statement.text.lower()) # NOQA
-            other_statement_text = unicode(other_statement.text.lower()) # NOQA
-        else:
-            statement_text = str(statement.text.lower())
-            other_statement_text = str(other_statement.text.lower())
+
+        statement_text = str(statement.text.lower())
+        other_statement_text = str(other_statement.text.lower())
 
         similarity = SequenceMatcher(
             None,
@@ -130,7 +124,7 @@ class SynsetDistance(Comparator):
         """
         from nltk.corpus import wordnet
         from nltk import word_tokenize
-        from . import utils
+        from chatter.chatterbot import utils
         import itertools
 
         tokens1 = word_tokenize(statement.text.lower())
diff --git a/chatter/source/constants.py b/chatter/chatterbot/constants.py
similarity index 100%
rename from chatter/source/constants.py
rename to chatter/chatterbot/constants.py
diff --git a/chatter/source/conversation.py b/chatter/chatterbot/conversation.py
similarity index 96%
rename from chatter/source/conversation.py
rename to chatter/chatterbot/conversation.py
index ea674aa..c9dfcb4 100644
--- a/chatter/source/conversation.py
+++ b/chatter/chatterbot/conversation.py
@@ -25,7 +25,6 @@ class Statement(StatementMixin):
     """
 
     def __init__(self, text, **kwargs):
-        import sys
 
         # Try not to allow non-string types to be passed to statements
         try:
@@ -33,13 +32,6 @@ class Statement(StatementMixin):
         except UnicodeEncodeError:
             pass
 
-        # Prefer decoded utf8-strings in Python 2.7
-        if sys.version_info[0] < 3:
-            try:
-                text = text.decode('utf-8')
-            except UnicodeEncodeError:
-                pass
-
         self.text = text
         self.tags = kwargs.pop('tags', [])
         self.in_response_to = kwargs.pop('in_response_to', [])
diff --git a/chatter/source/corpus.py b/chatter/chatterbot/corpus.py
similarity index 99%
rename from chatter/source/corpus.py
rename to chatter/chatterbot/corpus.py
index 65da8eb..4bf0e4b 100644
--- a/chatter/source/corpus.py
+++ b/chatter/chatterbot/corpus.py
@@ -5,7 +5,6 @@ View the corpus on GitHub at https://github.com/gunthercox/chatterbot-corpus
 
 from chatterbot_corpus import Corpus
 
-
 __all__ = (
     'Corpus',
 )
diff --git a/chatter/source/ext/__init__.py b/chatter/chatterbot/ext/__init__.py
similarity index 100%
rename from chatter/source/ext/__init__.py
rename to chatter/chatterbot/ext/__init__.py
diff --git a/chatter/source/ext/django_chatterbot/management/__init__.py b/chatter/chatterbot/ext/sqlalchemy_app/__init__.py
similarity index 100%
rename from chatter/source/ext/django_chatterbot/management/__init__.py
rename to chatter/chatterbot/ext/sqlalchemy_app/__init__.py
diff --git a/chatter/source/ext/sqlalchemy_app/models.py b/chatter/chatterbot/ext/sqlalchemy_app/models.py
similarity index 88%
rename from chatter/source/ext/sqlalchemy_app/models.py
rename to chatter/chatterbot/ext/sqlalchemy_app/models.py
index 9f1b0d3..6a7dc00 100644
--- a/chatter/source/ext/sqlalchemy_app/models.py
+++ b/chatter/chatterbot/ext/sqlalchemy_app/models.py
@@ -1,11 +1,11 @@
 from sqlalchemy import Table, Column, Integer, DateTime, ForeignKey, PickleType
+from sqlalchemy.ext.declarative import declared_attr, declarative_base
 from sqlalchemy.orm import relationship
 from sqlalchemy.sql import func
-from sqlalchemy.ext.declarative import declared_attr, declarative_base
 
-from ...constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH
-from .types import UnicodeString
-from ...conversation import StatementMixin
+from chatter.chatterbot.constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH
+from chatter.chatterbot.conversation import StatementMixin
+from chatter.chatterbot.ext.sqlalchemy_app.types import UnicodeString
 
 
 class ModelBase(object):
@@ -29,7 +29,6 @@ class ModelBase(object):
 
 Base = declarative_base(cls=ModelBase)
 
-
 tag_association_table = Table(
     'tag_association',
     Base.metadata,
@@ -73,8 +72,8 @@ class Statement(Base, StatementMixin):
         return [tag.name for tag in self.tags]
 
     def get_statement(self):
-        from ...conversation import Statement as StatementObject
-        from ...conversation import Response as ResponseObject
+        from chatter.chatterbot.conversation import Statement as StatementObject
+        from chatter.chatterbot.conversation import Response as ResponseObject
 
         statement = StatementObject(
             self.text,
diff --git a/chatter/source/ext/sqlalchemy_app/types.py b/chatter/chatterbot/ext/sqlalchemy_app/types.py
similarity index 70%
rename from chatter/source/ext/sqlalchemy_app/types.py
rename to chatter/chatterbot/ext/sqlalchemy_app/types.py
index b48f4f6..ee9b123 100644
--- a/chatter/source/ext/sqlalchemy_app/types.py
+++ b/chatter/chatterbot/ext/sqlalchemy_app/types.py
@@ -13,9 +13,4 @@ class UnicodeString(TypeDecorator):
         Coerce Python bytestrings to unicode before
         saving them to the database.
         """
-        import sys
-
-        if sys.version_info[0] < 3:
-            if isinstance(value, str):
-                value = value.decode('utf-8')
         return value
diff --git a/chatter/source/filters.py b/chatter/chatterbot/filters.py
similarity index 100%
rename from chatter/source/filters.py
rename to chatter/chatterbot/filters.py
diff --git a/chatter/source/input/__init__.py b/chatter/chatterbot/input/__init__.py
similarity index 99%
rename from chatter/source/input/__init__.py
rename to chatter/chatterbot/input/__init__.py
index 34d9568..53c53f9 100644
--- a/chatter/source/input/__init__.py
+++ b/chatter/chatterbot/input/__init__.py
@@ -1,12 +1,11 @@
 from .input_adapter import InputAdapter
-from .microsoft import Microsoft
 from .gitter import Gitter
 from .hipchat import HipChat
 from .mailgun import Mailgun
+from .microsoft import Microsoft
 from .terminal import TerminalAdapter
 from .variable_input_type_adapter import VariableInputTypeAdapter
 
-
 __all__ = (
     'InputAdapter',
     'Microsoft',
diff --git a/chatter/source/input/gitter.py b/chatter/chatterbot/input/gitter.py
similarity index 98%
rename from chatter/source/input/gitter.py
rename to chatter/chatterbot/input/gitter.py
index 6ed83db..9018e37 100644
--- a/chatter/source/input/gitter.py
+++ b/chatter/chatterbot/input/gitter.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
+
 from time import sleep
-from . import InputAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
 
 
 class Gitter(InputAdapter):
diff --git a/chatter/source/input/hipchat.py b/chatter/chatterbot/input/hipchat.py
similarity index 96%
rename from chatter/source/input/hipchat.py
rename to chatter/chatterbot/input/hipchat.py
index b251157..b5da731 100644
--- a/chatter/source/input/hipchat.py
+++ b/chatter/chatterbot/input/hipchat.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
+
 from time import sleep
-from . import InputAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
 
 
 class HipChat(InputAdapter):
diff --git a/chatter/source/input/input_adapter.py b/chatter/chatterbot/input/input_adapter.py
similarity index 95%
rename from chatter/source/input/input_adapter.py
rename to chatter/chatterbot/input/input_adapter.py
index 3bc4b08..49c63db 100644
--- a/chatter/source/input/input_adapter.py
+++ b/chatter/chatterbot/input/input_adapter.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
-from ..adapters import Adapter
+
+from chatter.chatterbot.adapters import Adapter
 
 
 class InputAdapter(Adapter):
diff --git a/chatter/source/input/mailgun.py b/chatter/chatterbot/input/mailgun.py
similarity index 93%
rename from chatter/source/input/mailgun.py
rename to chatter/chatterbot/input/mailgun.py
index b1fe705..6fb78a8 100644
--- a/chatter/source/input/mailgun.py
+++ b/chatter/chatterbot/input/mailgun.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
+
 import datetime
-from . import InputAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
 
 
 class Mailgun(InputAdapter):
diff --git a/chatter/source/input/microsoft.py b/chatter/chatterbot/input/microsoft.py
similarity index 92%
rename from chatter/source/input/microsoft.py
rename to chatter/chatterbot/input/microsoft.py
index 395a3de..3a255bf 100644
--- a/chatter/source/input/microsoft.py
+++ b/chatter/chatterbot/input/microsoft.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
+
 from time import sleep
-from . import InputAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
 
 
 class Microsoft(InputAdapter):
@@ -21,10 +23,10 @@ class Microsoft(InputAdapter):
 
         # NOTE: Direct Line client credentials are different from your bot's
         # credentials
-        self.direct_line_token_or_secret = kwargs.\
+        self.direct_line_token_or_secret = kwargs. \
             get('direct_line_token_or_secret')
 
-        authorization_header = 'BotConnector  {}'.\
+        authorization_header = 'BotConnector  {}'. \
             format(self.direct_line_token_or_secret)
 
         self.headers = {
@@ -62,7 +64,7 @@ class Microsoft(InputAdapter):
     def get_most_recent_message(self):
         import requests
 
-        endpoint = '{host}/api/conversations/{id}/messages'\
+        endpoint = '{host}/api/conversations/{id}/messages' \
             .format(host=self.directline_host,
                     id=self.conversation_id)
 
diff --git a/chatter/source/input/terminal.py b/chatter/chatterbot/input/terminal.py
similarity index 70%
rename from chatter/source/input/terminal.py
rename to chatter/chatterbot/input/terminal.py
index e2d7ba2..20cb3c2 100644
--- a/chatter/source/input/terminal.py
+++ b/chatter/chatterbot/input/terminal.py
@@ -1,7 +1,8 @@
 from __future__ import unicode_literals
-from . import InputAdapter
-from ..conversation import Statement
-from ..utils import input_function
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
+from chatter.chatterbot.utils import input_function
 
 
 class TerminalAdapter(InputAdapter):
diff --git a/chatter/source/input/variable_input_type_adapter.py b/chatter/chatterbot/input/variable_input_type_adapter.py
similarity index 86%
rename from chatter/source/input/variable_input_type_adapter.py
rename to chatter/chatterbot/input/variable_input_type_adapter.py
index 9158611..d2d598c 100644
--- a/chatter/source/input/variable_input_type_adapter.py
+++ b/chatter/chatterbot/input/variable_input_type_adapter.py
@@ -1,22 +1,18 @@
 from __future__ import unicode_literals
-from . import InputAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.input import InputAdapter
 
 
 class VariableInputTypeAdapter(InputAdapter):
-
     JSON = 'json'
     TEXT = 'text'
     OBJECT = 'object'
-    VALID_FORMATS = (JSON, TEXT, OBJECT, )
+    VALID_FORMATS = (JSON, TEXT, OBJECT,)
 
     def detect_type(self, statement):
-        import sys
 
-        if sys.version_info[0] < 3:
-            string_types = basestring # NOQA
-        else:
-            string_types = str
+        string_types = str
 
         if hasattr(statement, 'text'):
             return self.OBJECT
diff --git a/chatter/source/logic/__init__.py b/chatter/chatterbot/logic/__init__.py
similarity index 99%
rename from chatter/source/logic/__init__.py
rename to chatter/chatterbot/logic/__init__.py
index ecb1020..1930556 100644
--- a/chatter/source/logic/__init__.py
+++ b/chatter/chatterbot/logic/__init__.py
@@ -1,5 +1,5 @@
-from .logic_adapter import LogicAdapter
 from .best_match import BestMatch
+from .logic_adapter import LogicAdapter
 from .low_confidence import LowConfidenceAdapter
 from .mathematical_evaluation import MathematicalEvaluation
 from .multi_adapter import MultiLogicAdapter
@@ -7,7 +7,6 @@ from .no_knowledge_adapter import NoKnowledgeAdapter
 from .specific_response import SpecificResponseAdapter
 from .time_adapter import TimeLogicAdapter
 
-
 __all__ = (
     'LogicAdapter',
     'BestMatch',
diff --git a/chatter/source/logic/best_match.py b/chatter/chatterbot/logic/best_match.py
similarity index 99%
rename from chatter/source/logic/best_match.py
rename to chatter/chatterbot/logic/best_match.py
index 712c8f9..5c48121 100644
--- a/chatter/source/logic/best_match.py
+++ b/chatter/chatterbot/logic/best_match.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .logic_adapter import LogicAdapter
 
 
diff --git a/chatter/source/logic/logic_adapter.py b/chatter/chatterbot/logic/logic_adapter.py
similarity index 93%
rename from chatter/source/logic/logic_adapter.py
rename to chatter/chatterbot/logic/logic_adapter.py
index df2c143..1239cca 100644
--- a/chatter/source/logic/logic_adapter.py
+++ b/chatter/chatterbot/logic/logic_adapter.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
-from ..adapters import Adapter
-from ..utils import import_module
+
+from chatter.chatterbot.adapters import Adapter
+from chatter.chatterbot.utils import import_module
 
 
 class LogicAdapter(Adapter):
@@ -17,8 +18,8 @@ class LogicAdapter(Adapter):
 
     def __init__(self, **kwargs):
         super(LogicAdapter, self).__init__(**kwargs)
-        from ..comparisons import levenshtein_distance
-        from ..response_selection import get_first_response
+        from chatter.chatterbot.comparisons import levenshtein_distance
+        from chatter.chatterbot.response_selection import get_first_response
 
         # Import string module parameters
         if 'statement_comparison_function' in kwargs:
diff --git a/chatter/source/logic/low_confidence.py b/chatter/chatterbot/logic/low_confidence.py
similarity index 97%
rename from chatter/source/logic/low_confidence.py
rename to chatter/chatterbot/logic/low_confidence.py
index fb5435c..585cf20 100644
--- a/chatter/source/logic/low_confidence.py
+++ b/chatter/chatterbot/logic/low_confidence.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
 from .best_match import BestMatch
 
 
diff --git a/chatter/source/logic/mathematical_evaluation.py b/chatter/chatterbot/logic/mathematical_evaluation.py
similarity index 95%
rename from chatter/source/logic/mathematical_evaluation.py
rename to chatter/chatterbot/logic/mathematical_evaluation.py
index 2a65fdc..af27548 100644
--- a/chatter/source/logic/mathematical_evaluation.py
+++ b/chatter/chatterbot/logic/mathematical_evaluation.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
-from . import LogicAdapter
-from ..conversation import Statement
+
+from chatter.chatterbot.conversation import Statement
+from chatter.chatterbot.logic import LogicAdapter
 
 
 class MathematicalEvaluation(LogicAdapter):
diff --git a/chatter/source/logic/multi_adapter.py b/chatter/chatterbot/logic/multi_adapter.py
similarity index 96%
rename from chatter/source/logic/multi_adapter.py
rename to chatter/chatterbot/logic/multi_adapter.py
index 150f6c3..6cfe30f 100644
--- a/chatter/source/logic/multi_adapter.py
+++ b/chatter/chatterbot/logic/multi_adapter.py
@@ -1,6 +1,8 @@
 from __future__ import unicode_literals
+
 from collections import Counter
-from .. import utils
+
+from chatter.chatterbot import utils
 from .logic_adapter import LogicAdapter
 
 
@@ -13,7 +15,7 @@ class MultiLogicAdapter(LogicAdapter):
     """
 
     def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+        super(MultiLogicAdapter, self).__init__(**kwargs)
 
         # Logic adapters added by the chat bot
         self.adapters = []
@@ -49,7 +51,7 @@ class MultiLogicAdapter(LogicAdapter):
             if adapter.can_process(statement):
 
                 output = adapter.process(statement)
-                results.append((output.confidence, output, ))
+                results.append((output.confidence, output,))
 
                 self.logger.info(
                     '{} selected "{}" as a response with a confidence of {}'.format(
diff --git a/chatter/source/logic/no_knowledge_adapter.py b/chatter/chatterbot/logic/no_knowledge_adapter.py
similarity index 99%
rename from chatter/source/logic/no_knowledge_adapter.py
rename to chatter/chatterbot/logic/no_knowledge_adapter.py
index 59b11fd..55208b4 100644
--- a/chatter/source/logic/no_knowledge_adapter.py
+++ b/chatter/chatterbot/logic/no_knowledge_adapter.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .logic_adapter import LogicAdapter
 
 
diff --git a/chatter/source/logic/specific_response.py b/chatter/chatterbot/logic/specific_response.py
similarity index 94%
rename from chatter/source/logic/specific_response.py
rename to chatter/chatterbot/logic/specific_response.py
index 2ed6da1..101dd3b 100644
--- a/chatter/source/logic/specific_response.py
+++ b/chatter/chatterbot/logic/specific_response.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .logic_adapter import LogicAdapter
 
 
@@ -15,7 +16,7 @@ class SpecificResponseAdapter(LogicAdapter):
 
     def __init__(self, **kwargs):
         super(SpecificResponseAdapter, self).__init__(**kwargs)
-        from ..conversation import Statement
+        from chatter.chatterbot.conversation import Statement
 
         self.input_text = kwargs.get('input_text')
 
diff --git a/chatter/source/logic/time_adapter.py b/chatter/chatterbot/logic/time_adapter.py
similarity index 94%
rename from chatter/source/logic/time_adapter.py
rename to chatter/chatterbot/logic/time_adapter.py
index 3de4001..72902e2 100644
--- a/chatter/source/logic/time_adapter.py
+++ b/chatter/chatterbot/logic/time_adapter.py
@@ -1,5 +1,7 @@
 from __future__ import unicode_literals
+
 from datetime import datetime
+
 from .logic_adapter import LogicAdapter
 
 
@@ -40,8 +42,8 @@ class TimeLogicAdapter(LogicAdapter):
         ])
 
         labeled_data = (
-            [(name, 0) for name in self.negative] +
-            [(name, 1) for name in self.positive]
+                [(name, 0) for name in self.negative] +
+                [(name, 1) for name in self.positive]
         )
 
         train_set = [
@@ -79,7 +81,7 @@ class TimeLogicAdapter(LogicAdapter):
         return features
 
     def process(self, statement):
-        from ..conversation import Statement
+        from chatter.chatterbot.conversation import Statement
 
         now = datetime.now()
 
diff --git a/chatter/source/output/__init__.py b/chatter/chatterbot/output/__init__.py
similarity index 100%
rename from chatter/source/output/__init__.py
rename to chatter/chatterbot/output/__init__.py
index 0d64ca4..80abe4f 100644
--- a/chatter/source/output/__init__.py
+++ b/chatter/chatterbot/output/__init__.py
@@ -1,9 +1,9 @@
-from .output_adapter import OutputAdapter
-from .microsoft import Microsoft
-from .terminal import TerminalAdapter
-from .mailgun import Mailgun
 from .gitter import Gitter
 from .hipchat import HipChat
+from .mailgun import Mailgun
+from .microsoft import Microsoft
+from .output_adapter import OutputAdapter
+from .terminal import TerminalAdapter
 
 __all__ = (
     'OutputAdapter',
diff --git a/chatter/source/output/gitter.py b/chatter/chatterbot/output/gitter.py
similarity index 99%
rename from chatter/source/output/gitter.py
rename to chatter/chatterbot/output/gitter.py
index db654e2..ba01fa8 100644
--- a/chatter/source/output/gitter.py
+++ b/chatter/chatterbot/output/gitter.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .output_adapter import OutputAdapter
 
 
diff --git a/chatter/source/output/hipchat.py b/chatter/chatterbot/output/hipchat.py
similarity index 99%
rename from chatter/source/output/hipchat.py
rename to chatter/chatterbot/output/hipchat.py
index 4eaa9a7..2546092 100644
--- a/chatter/source/output/hipchat.py
+++ b/chatter/chatterbot/output/hipchat.py
@@ -1,5 +1,7 @@
 from __future__ import unicode_literals
+
 import json
+
 from .output_adapter import OutputAdapter
 
 
diff --git a/chatter/source/output/mailgun.py b/chatter/chatterbot/output/mailgun.py
similarity index 99%
rename from chatter/source/output/mailgun.py
rename to chatter/chatterbot/output/mailgun.py
index 6bb4954..71a9a7a 100644
--- a/chatter/source/output/mailgun.py
+++ b/chatter/chatterbot/output/mailgun.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .output_adapter import OutputAdapter
 
 
diff --git a/chatter/source/output/microsoft.py b/chatter/chatterbot/output/microsoft.py
similarity index 99%
rename from chatter/source/output/microsoft.py
rename to chatter/chatterbot/output/microsoft.py
index 177dc35..816fc97 100644
--- a/chatter/source/output/microsoft.py
+++ b/chatter/chatterbot/output/microsoft.py
@@ -1,5 +1,7 @@
 from __future__ import unicode_literals
+
 import json
+
 from .output_adapter import OutputAdapter
 
 
diff --git a/chatter/source/output/output_adapter.py b/chatter/chatterbot/output/output_adapter.py
similarity index 92%
rename from chatter/source/output/output_adapter.py
rename to chatter/chatterbot/output/output_adapter.py
index 880cb18..5d13dd7 100644
--- a/chatter/source/output/output_adapter.py
+++ b/chatter/chatterbot/output/output_adapter.py
@@ -1,4 +1,4 @@
-from ..adapters import Adapter
+from chatter.chatterbot.adapters import Adapter
 
 
 class OutputAdapter(Adapter):
diff --git a/chatter/source/output/terminal.py b/chatter/chatterbot/output/terminal.py
similarity index 99%
rename from chatter/source/output/terminal.py
rename to chatter/chatterbot/output/terminal.py
index f189aba..8ab63e1 100644
--- a/chatter/source/output/terminal.py
+++ b/chatter/chatterbot/output/terminal.py
@@ -1,4 +1,5 @@
 from __future__ import unicode_literals
+
 from .output_adapter import OutputAdapter
 
 
diff --git a/chatter/source/parsing.py b/chatter/chatterbot/parsing.py
similarity index 100%
rename from chatter/source/parsing.py
rename to chatter/chatterbot/parsing.py
index cf955ff..d7ad4d2 100644
--- a/chatter/source/parsing.py
+++ b/chatter/chatterbot/parsing.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
+import calendar
 import re
 from datetime import timedelta, datetime
-import calendar
 
 # Variations of dates that the parser can capture
 year_variations = ['year', 'years', 'yrs']
diff --git a/chatter/source/preprocessors.py b/chatter/chatterbot/preprocessors.py
similarity index 80%
rename from chatter/source/preprocessors.py
rename to chatter/chatterbot/preprocessors.py
index f7043b1..2ab0ee0 100644
--- a/chatter/source/preprocessors.py
+++ b/chatter/chatterbot/preprocessors.py
@@ -27,14 +27,9 @@ def unescape_html(chatbot, statement):
     Convert escaped html characters into unescaped html characters.
     For example: "<b>" becomes "".
     """
-    import sys
 
     # Replace HTML escape characters
-    if sys.version_info[0] < 3:
-        from HTMLParser import HTMLParser
-        html = HTMLParser()
-    else:
-        import html
+    import html
 
     statement.text = html.unescape(statement.text)
 
@@ -47,11 +42,6 @@ def convert_to_ascii(chatbot, statement):
     For example: "pรฅ fรฉdรฉral" becomes "pa federal".
     """
     import unicodedata
-    import sys
-
-    # Normalize unicode characters
-    if sys.version_info[0] < 3:
-        statement.text = unicode(statement.text) # NOQA
 
     text = unicodedata.normalize('NFKD', statement.text)
     text = text.encode('ascii', 'ignore').decode('utf-8')
diff --git a/chatter/source/response_selection.py b/chatter/chatterbot/response_selection.py
similarity index 100%
rename from chatter/source/response_selection.py
rename to chatter/chatterbot/response_selection.py
diff --git a/chatter/source/storage/__init__.py b/chatter/chatterbot/storage/__init__.py
similarity index 73%
rename from chatter/source/storage/__init__.py
rename to chatter/chatterbot/storage/__init__.py
index 2b4e286..77d3e04 100644
--- a/chatter/source/storage/__init__.py
+++ b/chatter/chatterbot/storage/__init__.py
@@ -1,12 +1,9 @@
 from .storage_adapter import StorageAdapter
-from .django_storage import DjangoStorageAdapter
 from .mongodb import MongoDatabaseAdapter
 from .sql_storage import SQLStorageAdapter
 
-
 __all__ = (
     'StorageAdapter',
-    'DjangoStorageAdapter',
     'MongoDatabaseAdapter',
     'SQLStorageAdapter',
 )
diff --git a/chatter/source/storage/mongodb.py b/chatter/chatterbot/storage/mongodb.py
similarity index 97%
rename from chatter/source/storage/mongodb.py
rename to chatter/chatterbot/storage/mongodb.py
index 92ce5a1..1ddb625 100644
--- a/chatter/source/storage/mongodb.py
+++ b/chatter/chatterbot/storage/mongodb.py
@@ -1,10 +1,13 @@
-from . import StorageAdapter
+from chatter.chatterbot.storage import StorageAdapter
 
 
 class Query(object):
 
-    def __init__(self, query={}):
-        self.query = query
+    def __init__(self, query=None):
+        if query is None:
+            self.query = {}
+        else:
+            self.query = query
 
     def value(self):
         return self.query.copy()
@@ -116,7 +119,7 @@ class MongoDatabaseAdapter(StorageAdapter):
         """
         Return the class for the statement model.
         """
-        from ..conversation import Statement
+        from chatter.chatterbot.conversation import Statement
 
         # Create a storage-aware statement
         statement = Statement
@@ -128,7 +131,7 @@ class MongoDatabaseAdapter(StorageAdapter):
         """
         Return the class for the response model.
         """
-        from ..conversation import Response
+        from chatter.chatterbot.conversation import Response
 
         # Create a storage-aware response
         response = Response
diff --git a/chatter/source/storage/sql_storage.py b/chatter/chatterbot/storage/sql_storage.py
similarity index 95%
rename from chatter/source/storage/sql_storage.py
rename to chatter/chatterbot/storage/sql_storage.py
index 21c84e6..32b9535 100644
--- a/chatter/source/storage/sql_storage.py
+++ b/chatter/chatterbot/storage/sql_storage.py
@@ -1,8 +1,8 @@
-from . import StorageAdapter
+from chatter.chatterbot.storage import StorageAdapter
 
 
 def get_response_table(response):
-    from ..ext.sqlalchemy_app.models import Response
+    from chatter.chatterbot.ext.sqlalchemy_app.models import Response
     return Response(text=response.text, occurrence=response.occurrence)
 
 
@@ -86,28 +86,28 @@ class SQLStorageAdapter(StorageAdapter):
         """
         Return the statement model.
         """
-        from ..ext.sqlalchemy_app.models import Statement
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Statement
         return Statement
 
     def get_response_model(self):
         """
         Return the response model.
         """
-        from ..ext.sqlalchemy_app.models import Response
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Response
         return Response
 
     def get_conversation_model(self):
         """
         Return the conversation model.
         """
-        from ..ext.sqlalchemy_app.models import Conversation
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Conversation
         return Conversation
 
     def get_tag_model(self):
         """
         Return the conversation model.
         """
-        from ..ext.sqlalchemy_app.models import Tag
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Tag
         return Tag
 
     def count(self):
@@ -379,14 +379,14 @@ class SQLStorageAdapter(StorageAdapter):
         """
         Drop the database attached to a given adapter.
         """
-        from ..ext.sqlalchemy_app.models import Base
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Base
         Base.metadata.drop_all(self.engine)
 
     def create(self):
         """
         Populate the database with the tables.
         """
-        from ..ext.sqlalchemy_app.models import Base
+        from chatter.chatterbot.ext.sqlalchemy_app.models import Base
         Base.metadata.create_all(self.engine)
 
     def _session_finish(self, session, statement_text=None):
diff --git a/chatter/source/storage/storage_adapter.py b/chatter/chatterbot/storage/storage_adapter.py
similarity index 95%
rename from chatter/source/storage/storage_adapter.py
rename to chatter/chatterbot/storage/storage_adapter.py
index 50beac7..046ae63 100644
--- a/chatter/source/storage/storage_adapter.py
+++ b/chatter/chatterbot/storage/storage_adapter.py
@@ -24,12 +24,12 @@ class StorageAdapter(object):
         # The string must be lowercase
         model_name = model_name.lower()
 
-        kwarg_model_key = '%s_model' % (model_name, )
+        kwarg_model_key = '%s_model' % (model_name,)
 
         if kwarg_model_key in self.kwargs:
             return self.kwargs.get(kwarg_model_key)
 
-        get_model_method = getattr(self, 'get_%s_model' % (model_name, ))
+        get_model_method = getattr(self, 'get_%s_model' % (model_name,))
 
         return get_model_method()
 
@@ -157,7 +157,8 @@ class StorageAdapter(object):
 
     class EmptyDatabaseException(Exception):
 
-        def __init__(self, value='The database currently contains no entries. At least one entry is expected. You may need to train your chat bot to populate your database.'):
+        def __init__(self,
+                     value='The database currently contains no entries. At least one entry is expected. You may need to train your chat bot to populate your database.'):
             self.value = value
 
         def __str__(self):
diff --git a/chatter/source/trainers.py b/chatter/chatterbot/trainers.py
similarity index 96%
rename from chatter/source/trainers.py
rename to chatter/chatterbot/trainers.py
index 1f634d1..42ccd47 100644
--- a/chatter/source/trainers.py
+++ b/chatter/chatterbot/trainers.py
@@ -1,8 +1,9 @@
 import logging
 import os
 import sys
-from .conversation import Statement, Response
+
 from . import utils
+from .conversation import Statement, Response
 
 
 class Trainer(object):
@@ -60,8 +61,8 @@ class Trainer(object):
 
         def __init__(self, value=None):
             default = (
-                'A training class must be specified before calling train(). ' +
-                'See http://chatterbot.readthedocs.io/en/stable/training.html'
+                    'A training class must be specified before calling train(). ' +
+                    'See http://chatterbot.readthedocs.io/en/stable/training.html'
             )
             self.value = value or default
 
@@ -84,7 +85,7 @@ class Trainer(object):
         import json
         export = {'conversations': self._generate_export_data()}
         with open(file_path, 'w+') as jsonfile:
-            json.dump(export, jsonfile, ensure_ascii=False)
+            json.dump(export, jsonfile, ensure_ascii=True)
 
 
 class ListTrainer(Trainer):
@@ -392,10 +393,9 @@ class UbuntuCorpusTrainer(Trainer):
 
         file_kwargs = {}
 
-        if sys.version_info[0] > 2:
-            # Specify the encoding in Python versions 3 and up
-            file_kwargs['encoding'] = 'utf-8'
-            # WARNING: This might fail to read a unicode corpus file in Python 2.x
+        # Specify the encoding in Python versions 3 and up
+        file_kwargs['encoding'] = 'utf-8'
+        # WARNING: This might fail to read a unicode corpus file in Python 2.x
 
         for file in glob.iglob(extracted_corpus_path):
             self.logger.info('Training from: {}'.format(file))
diff --git a/chatter/source/utils.py b/chatter/chatterbot/utils.py
similarity index 93%
rename from chatter/source/utils.py
rename to chatter/chatterbot/utils.py
index 684d7f7..e18549e 100644
--- a/chatter/source/utils.py
+++ b/chatter/chatterbot/utils.py
@@ -75,17 +75,8 @@ def input_function():
     Normalizes reading input between python 2 and 3.
     The function 'raw_input' becomes 'input' in Python 3.
     """
-    import sys
 
-    if sys.version_info[0] < 3:
-        user_input = str(raw_input()) # NOQA
-
-        # Avoid problems using format strings with unicode characters
-        if user_input:
-            user_input = user_input.decode('utf-8')
-
-    else:
-        user_input = input() # NOQA
+    user_input = input()  # NOQA
 
     return user_input
 
@@ -137,7 +128,7 @@ def remove_stopwords(tokens, language):
     Stop words are words like "is, the, a, ..."
 
     Be sure to download the required NLTK corpus before calling this function:
-    - from chatterbot.utils import nltk_download_corpus
+    - from chatter.chatterbot.utils import nltk_download_corpus
     - nltk_download_corpus('corpora/stopwords')
     """
     from nltk.corpus import stopwords
diff --git a/chatter/info.json b/chatter/info.json
index bd4870a..d2ebffd 100644
--- a/chatter/info.json
+++ b/chatter/info.json
@@ -1,10 +1,30 @@
 {
-    "author" : ["Bobloy"],
-    "bot_version" : [3,0,0],
-    "description" : "Create an offline chatbot that talks like your average member using Machine Learning",
-    "hidden" : false,
-    "install_msg" : "Thank you for installing Chatter!",
-    "requirements" : ["sqlalchemy<1.3,>=1.2", "python-twitter<4.0,>=3.0", "python-dateutil<2.7,>=2.6", "pymongo<4.0,>=3.3", "nltk<4.0,>=3.2", "mathparse<0.2,>=0.1", "chatterbot-corpus<1.2,>=1.1"],
-    "short" : "Local Chatbot run on machine learning",
-    "tags" : ["chat", "chatbot", "cleverbot", "clever","bobloy"]
+  "author": [
+    "Bobloy"
+  ],
+  "bot_version": [
+    3,
+    0,
+    0
+  ],
+  "description": "Create an offline chatbot that talks like your average member using Machine Learning",
+  "hidden": false,
+  "install_msg": "Thank you for installing Chatter!",
+  "requirements": [
+    "sqlalchemy<1.3,>=1.2",
+    "python-twitter<4.0,>=3.0",
+    "python-dateutil<2.7,>=2.6",
+    "pymongo<4.0,>=3.3",
+    "nltk<4.0,>=3.2",
+    "mathparse<0.2,>=0.1",
+    "chatterbot-corpus<1.2,>=1.1"
+  ],
+  "short": "Local Chatbot run on machine learning",
+  "tags": [
+    "chat",
+    "chatbot",
+    "cleverbot",
+    "clever",
+    "bobloy"
+  ]
 }
\ No newline at end of file
diff --git a/chatter/source/__pycache__/__init__.cpython-36.pyc b/chatter/source/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index b9aa8f4..0000000
Binary files a/chatter/source/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/adapters.cpython-36.pyc b/chatter/source/__pycache__/adapters.cpython-36.pyc
deleted file mode 100644
index b4f47b3..0000000
Binary files a/chatter/source/__pycache__/adapters.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/chatterbot.cpython-36.pyc b/chatter/source/__pycache__/chatterbot.cpython-36.pyc
deleted file mode 100644
index 4b790ba..0000000
Binary files a/chatter/source/__pycache__/chatterbot.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/comparisons.cpython-36.pyc b/chatter/source/__pycache__/comparisons.cpython-36.pyc
deleted file mode 100644
index e1372db..0000000
Binary files a/chatter/source/__pycache__/comparisons.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/constants.cpython-36.pyc b/chatter/source/__pycache__/constants.cpython-36.pyc
deleted file mode 100644
index e1dd4ef..0000000
Binary files a/chatter/source/__pycache__/constants.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/conversation.cpython-36.pyc b/chatter/source/__pycache__/conversation.cpython-36.pyc
deleted file mode 100644
index e9e644a..0000000
Binary files a/chatter/source/__pycache__/conversation.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/preprocessors.cpython-36.pyc b/chatter/source/__pycache__/preprocessors.cpython-36.pyc
deleted file mode 100644
index 954526b..0000000
Binary files a/chatter/source/__pycache__/preprocessors.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/response_selection.cpython-36.pyc b/chatter/source/__pycache__/response_selection.cpython-36.pyc
deleted file mode 100644
index f1c2ecf..0000000
Binary files a/chatter/source/__pycache__/response_selection.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/trainers.cpython-36.pyc b/chatter/source/__pycache__/trainers.cpython-36.pyc
deleted file mode 100644
index 2c3633d..0000000
Binary files a/chatter/source/__pycache__/trainers.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/utils.cpython-36.pyc b/chatter/source/__pycache__/utils.cpython-36.pyc
deleted file mode 100644
index d4d4097..0000000
Binary files a/chatter/source/__pycache__/utils.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/__pycache__/__init__.cpython-36.pyc b/chatter/source/ext/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 502c4b0..0000000
Binary files a/chatter/source/ext/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/django_chatterbot/__init__.py b/chatter/source/ext/django_chatterbot/__init__.py
deleted file mode 100644
index c683f59..0000000
--- a/chatter/source/ext/django_chatterbot/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-default_app_config = (
-    'chatter.source.ext.django_chatterbot.apps.DjangoChatterBotConfig'
-)
diff --git a/chatter/source/ext/django_chatterbot/abstract_models.py b/chatter/source/ext/django_chatterbot/abstract_models.py
deleted file mode 100644
index 4531186..0000000
--- a/chatter/source/ext/django_chatterbot/abstract_models.py
+++ /dev/null
@@ -1,261 +0,0 @@
-from ...conversation import StatementMixin
-from ... import constants
-from django.db import models
-from django.apps import apps
-from django.utils import timezone
-from django.conf import settings
-
-
-DJANGO_APP_NAME = constants.DEFAULT_DJANGO_APP_NAME
-STATEMENT_MODEL = 'Statement'
-RESPONSE_MODEL = 'Response'
-
-if hasattr(settings, 'CHATTERBOT'):
-    """
-    Allow related models to be overridden in the project settings.
-    Default to the original settings if one is not defined.
-    """
-    DJANGO_APP_NAME = settings.CHATTERBOT.get(
-        'django_app_name',
-        DJANGO_APP_NAME
-    )
-    STATEMENT_MODEL = settings.CHATTERBOT.get(
-        'statement_model',
-        STATEMENT_MODEL
-    )
-    RESPONSE_MODEL = settings.CHATTERBOT.get(
-        'response_model',
-        RESPONSE_MODEL
-    )
-
-
-class AbstractBaseStatement(models.Model, StatementMixin):
-    """
-    The abstract base statement allows other models to
-    be created using the attributes that exist on the
-    default models.
-    """
-
-    text = models.CharField(
-        unique=True,
-        blank=False,
-        null=False,
-        max_length=constants.STATEMENT_TEXT_MAX_LENGTH
-    )
-
-    extra_data = models.CharField(
-        max_length=500,
-        blank=True
-    )
-
-    # This is the confidence with which the chat bot believes
-    # this is an accurate response. This value is set when the
-    # statement is returned by the chat bot.
-    confidence = 0
-
-    class Meta:
-        abstract = True
-
-    def __str__(self):
-        if len(self.text.strip()) > 60:
-            return '{}...'.format(self.text[:57])
-        elif len(self.text.strip()) > 0:
-            return self.text
-        return ''
-
-    def __init__(self, *args, **kwargs):
-        super(AbstractBaseStatement, self).__init__(*args, **kwargs)
-
-        # Responses to be saved if the statement is updated with the storage adapter
-        self.response_statement_cache = []
-
-    @property
-    def in_response_to(self):
-        """
-        Return the response objects that are for this statement.
-        """
-        ResponseModel = apps.get_model(DJANGO_APP_NAME, RESPONSE_MODEL)
-        return ResponseModel.objects.filter(statement=self)
-
-    def add_extra_data(self, key, value):
-        """
-        Add extra data to the extra_data field.
-        """
-        import json
-
-        if not self.extra_data:
-            self.extra_data = '{}'
-
-        extra_data = json.loads(self.extra_data)
-        extra_data[key] = value
-
-        self.extra_data = json.dumps(extra_data)
-
-    def add_tags(self, tags):
-        """
-        Add a list of strings to the statement as tags.
-        (Overrides the method from StatementMixin)
-        """
-        for tag in tags:
-            self.tags.create(
-                name=tag
-            )
-
-    def add_response(self, statement):
-        """
-        Add a response to this statement.
-        """
-        self.response_statement_cache.append(statement)
-
-    def remove_response(self, response_text):
-        """
-        Removes a response from the statement's response list based
-        on the value of the response text.
-
-        :param response_text: The text of the response to be removed.
-        :type response_text: str
-        """
-        is_deleted = False
-        response = self.in_response.filter(response__text=response_text)
-
-        if response.exists():
-            is_deleted = True
-
-        return is_deleted
-
-    def get_response_count(self, statement):
-        """
-        Find the number of times that the statement has been used
-        as a response to the current statement.
-
-        :param statement: The statement object to get the count for.
-        :type statement: chatterbot.conversation.Statement
-
-        :returns: Return the number of times the statement has been used as a response.
-        :rtype: int
-        """
-        return self.in_response.filter(response__text=statement.text).count()
-
-    def serialize(self):
-        """
-        :returns: A dictionary representation of the statement object.
-        :rtype: dict
-        """
-        import json
-        data = {}
-
-        if not self.extra_data:
-            self.extra_data = '{}'
-
-        data['text'] = self.text
-        data['in_response_to'] = []
-        data['extra_data'] = json.loads(self.extra_data)
-
-        for response in self.in_response.all():
-            data['in_response_to'].append(response.serialize())
-
-        return data
-
-
-class AbstractBaseResponse(models.Model):
-    """
-    The abstract base response allows other models to
-    be created using the attributes that exist on the
-    default models.
-    """
-
-    statement = models.ForeignKey(
-        STATEMENT_MODEL,
-        related_name='in_response',
-        on_delete=models.CASCADE
-    )
-
-    response = models.ForeignKey(
-        STATEMENT_MODEL,
-        related_name='responses',
-        on_delete=models.CASCADE
-    )
-
-    created_at = models.DateTimeField(
-        default=timezone.now,
-        help_text='The date and time that this response was created at.'
-    )
-
-    class Meta:
-        abstract = True
-
-    @property
-    def occurrence(self):
-        """
-        Return a count of the number of times this response has occurred.
-        """
-        ResponseModel = apps.get_model(DJANGO_APP_NAME, RESPONSE_MODEL)
-
-        return ResponseModel.objects.filter(
-            statement__text=self.statement.text,
-            response__text=self.response.text
-        ).count()
-
-    def __str__(self):
-        statement = self.statement.text
-        response = self.response.text
-        return '{} => {}'.format(
-            statement if len(statement) <= 20 else statement[:17] + '...',
-            response if len(response) <= 40 else response[:37] + '...'
-        )
-
-    def serialize(self):
-        """
-        :returns: A dictionary representation of the statement object.
-        :rtype: dict
-        """
-        data = {}
-
-        data['text'] = self.response.text
-        data['created_at'] = self.created_at.isoformat()
-        data['occurrence'] = self.occurrence
-
-        return data
-
-
-class AbstractBaseConversation(models.Model):
-    """
-    The abstract base conversation allows other models to
-    be created using the attributes that exist on the
-    default models.
-    """
-
-    responses = models.ManyToManyField(
-        RESPONSE_MODEL,
-        related_name='conversations',
-        help_text='The responses in this conversation.'
-    )
-
-    class Meta:
-        abstract = True
-
-    def __str__(self):
-        return str(self.id)
-
-
-class AbstractBaseTag(models.Model):
-    """
-    The abstract base tag allows other models to
-    be created using the attributes that exist on the
-    default models.
-    """
-
-    name = models.SlugField(
-        max_length=constants.TAG_NAME_MAX_LENGTH
-    )
-
-    statements = models.ManyToManyField(
-        STATEMENT_MODEL,
-        related_name='tags'
-    )
-
-    class Meta:
-        abstract = True
-
-    def __str__(self):
-        return self.name
diff --git a/chatter/source/ext/django_chatterbot/admin.py b/chatter/source/ext/django_chatterbot/admin.py
deleted file mode 100644
index 193c264..0000000
--- a/chatter/source/ext/django_chatterbot/admin.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from django.contrib import admin
-from .models import (
-    Statement, Response, Conversation, Tag
-)
-
-
-class StatementAdmin(admin.ModelAdmin):
-    list_display = ('text', )
-    list_filter = ('text', )
-    search_fields = ('text', )
-
-
-class ResponseAdmin(admin.ModelAdmin):
-    list_display = ('statement', 'response', 'occurrence', )
-    search_fields = ['statement__text', 'response__text']
-
-
-class ConversationAdmin(admin.ModelAdmin):
-    list_display = ('id', )
-
-
-class TagAdmin(admin.ModelAdmin):
-    list_display = ('name', )
-    list_filter = ('name', )
-    search_fields = ('name', )
-
-
-admin.site.register(Statement, StatementAdmin)
-admin.site.register(Response, ResponseAdmin)
-admin.site.register(Conversation, ConversationAdmin)
-admin.site.register(Tag, TagAdmin)
diff --git a/chatter/source/ext/django_chatterbot/apps.py b/chatter/source/ext/django_chatterbot/apps.py
deleted file mode 100644
index b873e3e..0000000
--- a/chatter/source/ext/django_chatterbot/apps.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.apps import AppConfig
-
-
-class DjangoChatterBotConfig(AppConfig):
-
-    name = 'chatter.source.ext.django_chatterbot'
-    label = 'django_chatterbot'
-    verbose_name = 'Django ChatterBot'
diff --git a/chatter/source/ext/django_chatterbot/factories.py b/chatter/source/ext/django_chatterbot/factories.py
deleted file mode 100644
index 7367b58..0000000
--- a/chatter/source/ext/django_chatterbot/factories.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-These factories are used to generate fake data for testing.
-"""
-import factory
-from . import models
-from ... import constants
-from factory.django import DjangoModelFactory
-
-
-class StatementFactory(DjangoModelFactory):
-
-    text = factory.Faker(
-        'text',
-        max_nb_chars=constants.STATEMENT_TEXT_MAX_LENGTH
-    )
-
-    class Meta:
-        model = models.Statement
-
-
-class ResponseFactory(DjangoModelFactory):
-
-    statement = factory.SubFactory(StatementFactory)
-
-    response = factory.SubFactory(StatementFactory)
-
-    class Meta:
-        model = models.Response
-
-
-class ConversationFactory(DjangoModelFactory):
-
-    class Meta:
-        model = models.Conversation
-
-
-class TagFactory(DjangoModelFactory):
-
-    name = factory.Faker('word')
-
-    class Meta:
-        model = models.Tag
diff --git a/chatter/source/ext/django_chatterbot/management/commands/__init__.py b/chatter/source/ext/django_chatterbot/management/commands/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/django_chatterbot/management/commands/train.py b/chatter/source/ext/django_chatterbot/management/commands/train.py
deleted file mode 100644
index d4810b8..0000000
--- a/chatter/source/ext/django_chatterbot/management/commands/train.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from django.core.management.base import BaseCommand
-
-
-class Command(BaseCommand):
-    """
-    A Django management command for calling a
-    chat bot's training method.
-    """
-
-    help = 'Trains the database used by the chat bot'
-    can_import_settings = True
-
-    def handle(self, *args, **options):
-        from ..... import ChatBot
-        from ... import settings
-
-        chatterbot = ChatBot(**settings.CHATTERBOT)
-
-        chatterbot.train(chatterbot.training_data)
-
-        # Django 1.8 does not define SUCCESS
-        if hasattr(self.style, 'SUCCESS'):
-            style = self.style.SUCCESS
-        else:
-            style = self.style.NOTICE
-
-        self.stdout.write(style('Starting training...'))
-        training_class = chatterbot.trainer.__class__.__name__
-        self.stdout.write(style('ChatterBot trained using "%s"' % training_class))
diff --git a/chatter/source/ext/django_chatterbot/migrations/0001_initial.py b/chatter/source/ext/django_chatterbot/migrations/0001_initial.py
deleted file mode 100644
index 9c20907..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0001_initial.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = []
-
-    operations = [
-        migrations.CreateModel(
-            name='Response',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('occurrence', models.PositiveIntegerField(default=0)),
-            ],
-        ),
-        migrations.CreateModel(
-            name='Statement',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('text', models.CharField(max_length=255, unique=True)),
-            ],
-        ),
-        migrations.AddField(
-            model_name='response',
-            name='response',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='django_chatterbot.Statement'),
-        ),
-        migrations.AddField(
-            model_name='response',
-            name='statement',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response_to', to='django_chatterbot.Statement'),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py b/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py
deleted file mode 100644
index 5ed2f4a..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.2 on 2016-10-30 12:13
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='statement',
-            name='extra_data',
-            field=models.CharField(default='{}', max_length=500),
-            preserve_default=False,
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py b/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
deleted file mode 100644
index 8da6869..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.9 on 2016-12-12 00:06
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0002_statement_extra_data'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='response',
-            name='occurrence',
-            field=models.PositiveIntegerField(default=1),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py b/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
deleted file mode 100644
index 7860d49..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2016-12-04 23:52
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0003_change_occurrence_default'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='response',
-            name='statement',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response', to='django_chatterbot.Statement'),
-        ),
-        migrations.AlterField(
-            model_name='response',
-            name='response',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='django_chatterbot.Statement'),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py b/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py
deleted file mode 100644
index 7b38f00..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.1 on 2016-12-29 19:20
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0004_rename_in_response_to'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='statement',
-            name='created_at',
-            field=models.DateTimeField(
-                default=django.utils.timezone.now,
-                help_text='The date and time that this statement was created at.'
-            ),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py b/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py
deleted file mode 100644
index 1cf95d9..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.9 on 2017-01-17 07:02
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0005_statement_created_at'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Conversation',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-            ],
-        ),
-        migrations.AlterField(
-            model_name='statement',
-            name='created_at',
-            field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time that this statement was created at.'),
-        ),
-        migrations.AddField(
-            model_name='conversation',
-            name='statements',
-            field=models.ManyToManyField(help_text='The statements in this conversation.', related_name='conversation', to='django_chatterbot.Statement'),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py b/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py
deleted file mode 100644
index 1a0b5ac..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2017-07-18 00:16
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0006_create_conversation'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='response',
-            name='created_at',
-            field=models.DateTimeField(
-                default=django.utils.timezone.now,
-                help_text='The date and time that this response was created at.'
-            ),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py b/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py
deleted file mode 100644
index f3bd720..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2017-07-18 11:25
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0007_response_created_at'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='conversation',
-            name='statements',
-        ),
-        migrations.RemoveField(
-            model_name='response',
-            name='occurrence',
-        ),
-        migrations.RemoveField(
-            model_name='statement',
-            name='created_at',
-        ),
-        migrations.AddField(
-            model_name='conversation',
-            name='responses',
-            field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0009_tags.py b/chatter/source/ext/django_chatterbot/migrations/0009_tags.py
deleted file mode 100644
index ee71713..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0009_tags.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11a1 on 2017-07-07 00:12
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0008_update_conversations'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Tag',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.SlugField()),
-            ],
-            options={
-                'abstract': False,
-            },
-        ),
-        migrations.AlterField(
-            model_name='statement',
-            name='text',
-            field=models.CharField(max_length=255, unique=True),
-        ),
-        migrations.AddField(
-            model_name='tag',
-            name='statements',
-            field=models.ManyToManyField(related_name='tags', to='django_chatterbot.Statement'),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py b/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py
deleted file mode 100644
index 84940a7..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-08-16 00:56
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0009_tags'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='statement',
-            name='text',
-            field=models.CharField(max_length=400, unique=True),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py b/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py
deleted file mode 100644
index 4f7b327..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-08-20 13:55
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('django_chatterbot', '0010_statement_text'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='statement',
-            name='extra_data',
-            field=models.CharField(blank=True, max_length=500),
-        ),
-    ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/__init__.py b/chatter/source/ext/django_chatterbot/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/django_chatterbot/models.py b/chatter/source/ext/django_chatterbot/models.py
deleted file mode 100644
index ac51c06..0000000
--- a/chatter/source/ext/django_chatterbot/models.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from .abstract_models import (
-    AbstractBaseConversation, AbstractBaseResponse,
-    AbstractBaseStatement, AbstractBaseTag
-)
-
-
-class Statement(AbstractBaseStatement):
-    """
-    A statement represents a single spoken entity, sentence or
-    phrase that someone can say.
-    """
-    pass
-
-
-class Response(AbstractBaseResponse):
-    """
-    A connection between a statement and anther statement
-    that response to it.
-    """
-    pass
-
-
-class Conversation(AbstractBaseConversation):
-    """
-    A sequence of statements representing a conversation.
-    """
-    pass
-
-
-class Tag(AbstractBaseTag):
-    """
-    A label that categorizes a statement.
-    """
-    pass
diff --git a/chatter/source/ext/django_chatterbot/settings.py b/chatter/source/ext/django_chatterbot/settings.py
deleted file mode 100644
index 802b77d..0000000
--- a/chatter/source/ext/django_chatterbot/settings.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-Default ChatterBot settings for Django.
-"""
-from django.conf import settings
-from ... import constants
-
-
-CHATTERBOT_SETTINGS = getattr(settings, 'CHATTERBOT', {})
-
-CHATTERBOT_DEFAULTS = {
-    'name': 'ChatterBot',
-    'storage_adapter': 'chatter.source.storage.DjangoStorageAdapter',
-    'input_adapter': 'chatter.source.input.VariableInputTypeAdapter',
-    'output_adapter': 'chatter.source.output.OutputAdapter',
-    'django_app_name': constants.DEFAULT_DJANGO_APP_NAME
-}
-
-CHATTERBOT = CHATTERBOT_DEFAULTS.copy()
-CHATTERBOT.update(CHATTERBOT_SETTINGS)
diff --git a/chatter/source/ext/django_chatterbot/urls.py b/chatter/source/ext/django_chatterbot/urls.py
deleted file mode 100644
index 079005d..0000000
--- a/chatter/source/ext/django_chatterbot/urls.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.conf.urls import url
-from .views import ChatterBotView
-
-
-urlpatterns = [
-    url(
-        r'^$',
-        ChatterBotView.as_view(),
-        name='chatterbot',
-    ),
-]
diff --git a/chatter/source/ext/django_chatterbot/views.py b/chatter/source/ext/django_chatterbot/views.py
deleted file mode 100644
index d73408e..0000000
--- a/chatter/source/ext/django_chatterbot/views.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import json
-from django.views.generic import View
-from django.http import JsonResponse
-from ... import ChatBot
-from . import settings
-
-
-class ChatterBotViewMixin(object):
-    """
-    Subclass this mixin for access to the 'chatterbot' attribute.
-    """
-
-    chatterbot = ChatBot(**settings.CHATTERBOT)
-
-    def validate(self, data):
-        """
-        Validate the data recieved from the client.
-
-        * The data should contain a text attribute.
-        """
-        from django.core.exceptions import ValidationError
-
-        if 'text' not in data:
-            raise ValidationError('The attribute "text" is required.')
-
-    def get_conversation(self, request):
-        """
-        Return the conversation for the session if one exists.
-        Create a new conversation if one does not exist.
-        """
-        from .models import Conversation, Response
-
-        class Obj(object):
-            def __init__(self):
-                self.id = None
-                self.statements = []
-
-        conversation = Obj()
-
-        conversation.id = request.session.get('conversation_id', 0)
-        existing_conversation = False
-        try:
-            Conversation.objects.get(id=conversation.id)
-            existing_conversation = True
-
-        except Conversation.DoesNotExist:
-            conversation_id = self.chatterbot.storage.create_conversation()
-            request.session['conversation_id'] = conversation_id
-            conversation.id = conversation_id
-
-        if existing_conversation:
-            responses = Response.objects.filter(
-                conversations__id=conversation.id
-            )
-
-            for response in responses:
-                conversation.statements.append(response.statement.serialize())
-                conversation.statements.append(response.response.serialize())
-
-        return conversation
-
-
-class ChatterBotView(ChatterBotViewMixin, View):
-    """
-    Provide an API endpoint to interact with ChatterBot.
-    """
-
-    def post(self, request, *args, **kwargs):
-        """
-        Return a response to the statement in the posted data.
-        """
-        input_data = json.loads(request.read().decode('utf-8'))
-
-        self.validate(input_data)
-
-        conversation = self.get_conversation(request)
-
-        response = self.chatterbot.get_response(input_data, conversation.id)
-        response_data = response.serialize()
-
-        return JsonResponse(response_data, status=200)
-
-    def get(self, request, *args, **kwargs):
-        """
-        Return data corresponding to the current conversation.
-        """
-        conversation = self.get_conversation(request)
-
-        data = {
-            'detail': 'You should make a POST request to this endpoint.',
-            'name': self.chatterbot.name,
-            'conversation': conversation.statements
-        }
-
-        # Return a method not allowed response
-        return JsonResponse(data, status=405)
-
-    def patch(self, request, *args, **kwargs):
-        """
-        The patch method is not allowed for this endpoint.
-        """
-        data = {
-            'detail': 'You should make a POST request to this endpoint.'
-        }
-
-        # Return a method not allowed response
-        return JsonResponse(data, status=405)
-
-    def delete(self, request, *args, **kwargs):
-        """
-        The delete method is not allowed for this endpoint.
-        """
-        data = {
-            'detail': 'You should make a POST request to this endpoint.'
-        }
-
-        # Return a method not allowed response
-        return JsonResponse(data, status=405)
diff --git a/chatter/source/ext/sqlalchemy_app/__init__.py b/chatter/source/ext/sqlalchemy_app/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 2faf4fc..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc
deleted file mode 100644
index feb27f8..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc
deleted file mode 100644
index 93e7532..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/__init__.cpython-36.pyc b/chatter/source/input/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 360dbeb..0000000
Binary files a/chatter/source/input/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/gitter.cpython-36.pyc b/chatter/source/input/__pycache__/gitter.cpython-36.pyc
deleted file mode 100644
index 15765b3..0000000
Binary files a/chatter/source/input/__pycache__/gitter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/hipchat.cpython-36.pyc b/chatter/source/input/__pycache__/hipchat.cpython-36.pyc
deleted file mode 100644
index 9ce7312..0000000
Binary files a/chatter/source/input/__pycache__/hipchat.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc b/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc
deleted file mode 100644
index 59defa7..0000000
Binary files a/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/mailgun.cpython-36.pyc b/chatter/source/input/__pycache__/mailgun.cpython-36.pyc
deleted file mode 100644
index 6ca78af..0000000
Binary files a/chatter/source/input/__pycache__/mailgun.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/microsoft.cpython-36.pyc b/chatter/source/input/__pycache__/microsoft.cpython-36.pyc
deleted file mode 100644
index 58f5b4e..0000000
Binary files a/chatter/source/input/__pycache__/microsoft.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/terminal.cpython-36.pyc b/chatter/source/input/__pycache__/terminal.cpython-36.pyc
deleted file mode 100644
index ed2c5bd..0000000
Binary files a/chatter/source/input/__pycache__/terminal.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc b/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc
deleted file mode 100644
index 593d964..0000000
Binary files a/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/__init__.cpython-36.pyc b/chatter/source/logic/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 56ea4ef..0000000
Binary files a/chatter/source/logic/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/best_match.cpython-36.pyc b/chatter/source/logic/__pycache__/best_match.cpython-36.pyc
deleted file mode 100644
index da71469..0000000
Binary files a/chatter/source/logic/__pycache__/best_match.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc
deleted file mode 100644
index ca2c83d..0000000
Binary files a/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc b/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc
deleted file mode 100644
index a156f97..0000000
Binary files a/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc b/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc
deleted file mode 100644
index 452d69f..0000000
Binary files a/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc
deleted file mode 100644
index e3517c8..0000000
Binary files a/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc
deleted file mode 100644
index 7d286d6..0000000
Binary files a/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc b/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc
deleted file mode 100644
index 179e73d..0000000
Binary files a/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc
deleted file mode 100644
index 9cbf60e..0000000
Binary files a/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/__init__.cpython-36.pyc b/chatter/source/output/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 0f996ed..0000000
Binary files a/chatter/source/output/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/gitter.cpython-36.pyc b/chatter/source/output/__pycache__/gitter.cpython-36.pyc
deleted file mode 100644
index 37a8524..0000000
Binary files a/chatter/source/output/__pycache__/gitter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/hipchat.cpython-36.pyc b/chatter/source/output/__pycache__/hipchat.cpython-36.pyc
deleted file mode 100644
index 7d2dec4..0000000
Binary files a/chatter/source/output/__pycache__/hipchat.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/mailgun.cpython-36.pyc b/chatter/source/output/__pycache__/mailgun.cpython-36.pyc
deleted file mode 100644
index 3295a1a..0000000
Binary files a/chatter/source/output/__pycache__/mailgun.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/microsoft.cpython-36.pyc b/chatter/source/output/__pycache__/microsoft.cpython-36.pyc
deleted file mode 100644
index 73bc7a2..0000000
Binary files a/chatter/source/output/__pycache__/microsoft.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc b/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc
deleted file mode 100644
index 478d6c1..0000000
Binary files a/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/terminal.cpython-36.pyc b/chatter/source/output/__pycache__/terminal.cpython-36.pyc
deleted file mode 100644
index 98a931b..0000000
Binary files a/chatter/source/output/__pycache__/terminal.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/__init__.cpython-36.pyc b/chatter/source/storage/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 2eb08ba..0000000
Binary files a/chatter/source/storage/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc b/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc
deleted file mode 100644
index c8e1f3a..0000000
Binary files a/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc b/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc
deleted file mode 100644
index 4ef2bb1..0000000
Binary files a/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc b/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc
deleted file mode 100644
index b8acba2..0000000
Binary files a/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc b/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc
deleted file mode 100644
index b3b34df..0000000
Binary files a/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/django_storage.py b/chatter/source/storage/django_storage.py
deleted file mode 100644
index 5642b2c..0000000
--- a/chatter/source/storage/django_storage.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from . import StorageAdapter
-from .. import constants
-
-
-class DjangoStorageAdapter(StorageAdapter):
-    """
-    Storage adapter that allows ChatterBot to interact with
-    Django storage backends.
-    """
-
-    def __init__(self, **kwargs):
-        super(DjangoStorageAdapter, self).__init__(**kwargs)
-
-        self.adapter_supports_queries = False
-        self.django_app_name = kwargs.get(
-            'django_app_name',
-            constants.DEFAULT_DJANGO_APP_NAME
-        )
-
-    def get_statement_model(self):
-        from django.apps import apps
-        return apps.get_model(self.django_app_name, 'Statement')
-
-    def get_response_model(self):
-        from django.apps import apps
-        return apps.get_model(self.django_app_name, 'Response')
-
-    def get_conversation_model(self):
-        from django.apps import apps
-        return apps.get_model(self.django_app_name, 'Conversation')
-
-    def get_tag_model(self):
-        from django.apps import apps
-        return apps.get_model(self.django_app_name, 'Tag')
-
-    def count(self):
-        Statement = self.get_model('statement')
-        return Statement.objects.count()
-
-    def find(self, statement_text):
-        Statement = self.get_model('statement')
-        try:
-            return Statement.objects.get(text=statement_text)
-        except Statement.DoesNotExist as e:
-            self.logger.info(str(e))
-            return None
-
-    def filter(self, **kwargs):
-        """
-        Returns a list of statements in the database
-        that match the parameters specified.
-        """
-        from django.db.models import Q
-        Statement = self.get_model('statement')
-
-        order = kwargs.pop('order_by', None)
-
-        RESPONSE_CONTAINS = 'in_response_to__contains'
-
-        if RESPONSE_CONTAINS in kwargs:
-            value = kwargs[RESPONSE_CONTAINS]
-            del kwargs[RESPONSE_CONTAINS]
-            kwargs['in_response__response__text'] = value
-
-        kwargs_copy = kwargs.copy()
-
-        for kwarg in kwargs_copy:
-            value = kwargs[kwarg]
-            del kwargs[kwarg]
-            kwarg = kwarg.replace('in_response_to', 'in_response')
-            kwargs[kwarg] = value
-
-        if 'in_response' in kwargs:
-            responses = kwargs['in_response']
-            del kwargs['in_response']
-
-            if responses:
-                kwargs['in_response__response__text__in'] = []
-                for response in responses:
-                    kwargs['in_response__response__text__in'].append(response)
-            else:
-                kwargs['in_response'] = None
-
-        parameters = {}
-        if 'in_response__response__text' in kwargs:
-            value = kwargs['in_response__response__text']
-            parameters['responses__statement__text'] = value
-
-        statements = Statement.objects.filter(Q(**kwargs) | Q(**parameters))
-
-        if order:
-            statements = statements.order_by(order)
-
-        return statements
-
-    def update(self, statement):
-        """
-        Update the provided statement.
-        """
-        Statement = self.get_model('statement')
-        Response = self.get_model('response')
-
-        response_statement_cache = statement.response_statement_cache
-
-        statement, created = Statement.objects.get_or_create(text=statement.text)
-        statement.extra_data = getattr(statement, 'extra_data', '')
-        statement.save()
-
-        for _response_statement in response_statement_cache:
-
-            response_statement, created = Statement.objects.get_or_create(
-                text=_response_statement.text
-            )
-            response_statement.extra_data = getattr(_response_statement, 'extra_data', '')
-            response_statement.save()
-
-            Response.objects.create(
-                statement=response_statement,
-                response=statement
-            )
-
-        return statement
-
-    def get_random(self):
-        """
-        Returns a random statement from the database
-        """
-        Statement = self.get_model('statement')
-        return Statement.objects.order_by('?').first()
-
-    def remove(self, statement_text):
-        """
-        Removes the statement that matches the input text.
-        Removes any responses from statements if the response text matches the
-        input text.
-        """
-        from django.db.models import Q
-
-        Statement = self.get_model('statement')
-        Response = self.get_model('response')
-
-        statements = Statement.objects.filter(text=statement_text)
-
-        responses = Response.objects.filter(
-            Q(statement__text=statement_text) | Q(response__text=statement_text)
-        )
-
-        responses.delete()
-        statements.delete()
-
-    def get_latest_response(self, conversation_id):
-        """
-        Returns the latest response in a conversation if it exists.
-        Returns None if a matching conversation cannot be found.
-        """
-        Response = self.get_model('response')
-
-        response = Response.objects.filter(
-            conversations__id=conversation_id
-        ).order_by(
-            'created_at'
-        ).last()
-
-        if not response:
-            return None
-
-        return response.response
-
-    def create_conversation(self):
-        """
-        Create a new conversation.
-        """
-        Conversation = self.get_model('conversation')
-        conversation = Conversation.objects.create()
-        return conversation.id
-
-    def add_to_conversation(self, conversation_id, statement, response):
-        """
-        Add the statement and response to the conversation.
-        """
-        Statement = self.get_model('statement')
-        Response = self.get_model('response')
-
-        first_statement, created = Statement.objects.get_or_create(text=statement.text)
-        first_response, created = Statement.objects.get_or_create(text=response.text)
-
-        response = Response.objects.create(
-            statement=first_statement,
-            response=first_response
-        )
-
-        response.conversations.add(conversation_id)
-
-    def drop(self):
-        """
-        Remove all data from the database.
-        """
-        Statement = self.get_model('statement')
-        Response = self.get_model('response')
-        Conversation = self.get_model('conversation')
-        Tag = self.get_model('tag')
-
-        Statement.objects.all().delete()
-        Response.objects.all().delete()
-        Conversation.objects.all().delete()
-        Tag.objects.all().delete()
-
-    def get_response_statements(self):
-        """
-        Return only statements that are in response to another statement.
-        A statement must exist which lists the closest matching statement in the
-        in_response_to field. Otherwise, the logic adapter may find a closest
-        matching statement that does not have a known response.
-        """
-        Statement = self.get_model('statement')
-        Response = self.get_model('response')
-
-        responses = Response.objects.all()
-
-        return Statement.objects.filter(in_response__in=responses)
diff --git a/fight/fight.py b/fight/fight.py
index ff115e1..931c7c9 100644
--- a/fight/fight.py
+++ b/fight/fight.py
@@ -1142,7 +1142,7 @@ class Fight:
         
     #**************** Attempt 2, borrow from Squid*******
 
-    async def on_raw_reaction_add(self, emoji: discord.PartialReactionEmoji,
+    async def on_raw_reaction_add(self, emoji: discord.PartialEmoji,
                                   message_id: int, channel_id: int, user_id: int):
         """
         Event handler for long term reaction watching.
diff --git a/hangman/__init__.py b/hangman/__init__.py
new file mode 100644
index 0000000..2168fdf
--- /dev/null
+++ b/hangman/__init__.py
@@ -0,0 +1,9 @@
+from .hangman import Hangman
+from redbot.core import data_manager
+
+
+def setup(bot):
+    n = Hangman(bot)
+    data_manager.load_bundled_data(n, __file__)
+    bot.add_cog(n)
+    bot.add_listener(n._on_react, "on_reaction_add")
diff --git a/hangman/hangman.py b/hangman/hangman.py
index 987b3a2..766177f 100644
--- a/hangman/hangman.py
+++ b/hangman/hangman.py
@@ -1,356 +1,338 @@
-import discord
-import os
-
-from discord.ext import commands
+from collections import defaultdict
 from random import randint
 
-from .utils.dataIO import dataIO
-from .utils import checks
+import discord
+from discord.ext import commands
+from redbot.core import Config, checks
+from redbot.core.data_manager import cog_data_path, load_basic_configuration
 
 
 class Hangman:
     """Lets anyone play a game of hangman with custom phrases"""
+    navigate = "๐ผ๐ฝ"
+    letters = "๐ฆ๐ง๐จ๐ฉ๐ช๐ซ๐ฌ๐ญ๐ฎ๐ฏ๐ฐ๐ฑ๐ฒ๐ณ๐ด๐ต๐ถ๐ท๐ธ๐น๐บ๐ป๐ผ๐ฝ๐พ๐ฟ"
 
     def __init__(self, bot):
         self.bot = bot
-        self.path = "data/Fox-Cogs/hangman"
-        self.file_path = "data/Fox-Cogs/hangman/hangman.json"
-        self.answer_path = "data/hangman/hanganswers.txt"
-        self.the_data = dataIO.load_json(self.file_path)
-        self.winbool = False
-        self.letters = "๐ฆ๐ง๐จ๐ฉ๐ช๐ซ๐ฌ๐ญ๐ฎ๐ฏ๐ฐ๐ฑ๐ฒ๐ณ๐ด๐ต๐ถ๐ท๐ธ๐น๐บ๐ป๐ผ๐ฝ๐พ๐ฟ"
-        self.navigate = "๐ผ๐ฝ"
-        self._updateHanglist()
+        self.config = Config.get_conf(self, identifier=1049711010310997110)
+        default_guild = {
+            "theface": ':thinking:',
+        }
 
-    def _updateHanglist(self):
-        self.hanglist = (
-            """>
-               \_________
-                |/        
-                |              
-                |                
-                |                 
-                |               
-                |                   
-                |\___                 
-                """,
+        self.config.register_guild(**default_guild)
 
-            """>
-               \_________
-                |/   |      
-                |              
-                |                
-                |                 
-                |               
-                |                   
-                |\___                 
-                H""",
+        self.the_data = defaultdict(
+            lambda: {"running": False, "hangman": 0, "guesses": [], "trackmessage": False, "answer": ''})
+        self.path = str(cog_data_path(self)).replace('\\', '/')
 
-            """>
-               \_________       
-                |/   |              
-                |   """+self.the_data["theface"]+"""
-                |                         
-                |                       
-                |                         
-                |                          
-                |\___                       
-                HA""",
+        self.answer_path = self.path+"/bundled_data/hanganswers.txt"
 
-            """>
-               \________               
-                |/   |                   
-                |   """+self.the_data["theface"]+"""                   
-                |    |                     
-                |    |                    
-                |                           
-                |                            
-                |\___                    
-                HAN""",
+        self.winbool = defaultdict(lambda: False)
 
+        self.hanglist = {}
 
-            """>
-               \_________             
-                |/   |               
-                |   """+self.the_data["theface"]+"""                    
-                |   /|                     
-                |     |                    
-                |                        
-                |                          
-                |\___                          
-                HANG""",
+    async def _update_hanglist(self):
+        for guild in self.bot.guilds:
+            theface = await self.config.guild(guild).theface()
+            self.hanglist[guild] = (
+                """>
+                   \_________
+                    |/        
+                    |              
+                    |                
+                    |                 
+                    |               
+                    |                   
+                    |\___                 
+                    """,
 
+                """>
+                   \_________
+                    |/   |      
+                    |              
+                    |                
+                    |                 
+                    |               
+                    |                   
+                    |\___                 
+                    H""",
 
-            """>
-               \_________              
-                |/   |                     
-                |   """+self.the_data["theface"]+"""                      
-                |   /|\                    
-                |     |                       
-                |                             
-                |                            
-                |\___                          
-                HANGM""",
+                """>
+                   \_________       
+                    |/   |              
+                    |   """ + theface + """
+                    |                         
+                    |                       
+                    |                         
+                    |                          
+                    |\___                       
+                    HA""",
 
+                """>
+                   \________               
+                    |/   |                   
+                    |   """ + theface + """                   
+                    |    |                     
+                    |    |                    
+                    |                           
+                    |                            
+                    |\___                    
+                    HAN""",
 
+                """>
+                   \_________             
+                    |/   |               
+                    |   """ + theface + """                    
+                    |   /|                     
+                    |     |                    
+                    |                        
+                    |                          
+                    |\___                          
+                    HANG""",
 
-            """>
-               \________                   
-                |/   |                         
-                |   """+self.the_data["theface"]+"""                       
-                |   /|\                             
-                |     |                          
-                |   /                            
-                |                                  
-                |\___                              
-                HANGMA""",
+                """>
+                   \_________              
+                    |/   |                     
+                    |   """ + theface + """                      
+                    |   /|\                    
+                    |     |                       
+                    |                             
+                    |                            
+                    |\___                          
+                    HANGM""",
 
+                """>
+                   \________                   
+                    |/   |                         
+                    |   """ + theface + """                       
+                    |   /|\                             
+                    |     |                          
+                    |   /                            
+                    |                                  
+                    |\___                              
+                    HANGMA""",
+
+                """>
+                   \________
+                    |/   |     
+                    |   """ + theface + """     
+                    |   /|\           
+                    |     |        
+                    |   / \        
+                    |               
+                    |\___           
+                    HANGMAN""")
 
-            """>
-               \________
-                |/   |     
-                |   """+self.the_data["theface"]+"""     
-                |   /|\           
-                |     |        
-                |   / \        
-                |               
-                |\___           
-                HANGMAN""")
-              
-    def save_data(self):
-        """Saves the json"""
-        dataIO.save_json(self.file_path, self.the_data)
-        
     @commands.group(aliases=['sethang'], pass_context=True)
     @checks.mod_or_permissions(administrator=True)
     async def hangset(self, ctx):
         """Adjust hangman settings"""
-        if ctx.invoked_subcommand is None:
-            await self.bot.send_cmd_help(ctx)
-            
+        if not ctx.invoked_subcommand:
+            await ctx.send_help()
+
     @hangset.command(pass_context=True)
-    async def face(self, ctx, theface):
+    async def face(self, ctx: commands.Context, theface):
         message = ctx.message
-        #Borrowing FlapJack's emoji validation (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
+        # Borrowing FlapJack's emoji validation
+        # (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
         if theface[:2] == "<:":
-            theface = [r for server in self.bot.servers for r in server.emojis if r.id == theface.split(':')[2][:-1]][0]
-        
+            theface = [r for r in self.bot.emojis if r.id == theface.split(':')[2][:-1]][0]
+
         try:
             # Use the face as reaction to see if it's valid (THANKS FLAPJACK <3)
-            await self.bot.add_reaction(message, theface)
-            self.the_data["theface"] = str(theface)
-            self.save_data()
-            self._updateHanglist()
-            await self.bot.say("Face has been updated!")
-
+            await message.add_reaction(theface)
         except discord.errors.HTTPException:
-            await self.bot.say("That's not an emoji I recognize.")
-            
+            await ctx.send("That's not an emoji I recognize.")
+            return
+
+        await self.config.guild(ctx.guild).theface.set(theface)
+        await self._update_hanglist()
+        await ctx.send("Face has been updated!")
+
     @commands.command(aliases=['hang'], pass_context=True)
-    async def hangman(self, ctx, guess: str=None):
+    async def hangman(self, ctx, guess: str = None):
         """Play a game of hangman against the bot!"""
         if guess is None:
-            if self.the_data["running"]:
-                await self.bot.say("Game of hangman is already running!\nEnter your guess!")
-                self._printgame()
+            if self.the_data[ctx.guild]["running"]:
+                await ctx.send("Game of hangman is already running!\nEnter your guess!")
+                await self._printgame(ctx.channel)
                 """await self.bot.send_cmd_help(ctx)"""
             else:
-                await self.bot.say("Starting a game of hangman!")
-                self._startgame()
-                await self._printgame()
-        elif not self.the_data["running"]:
-            await self.bot.say("Game of hangman is not yet running!\nStarting a game of hangman!")
-            self._startgame()
-            await self._printgame()
-        else:    
-            await self._guessletter(guess)
+                await ctx.send("Starting a game of hangman!")
+                self._startgame(ctx.guild)
+                await self._printgame(ctx.channel)
+        elif not self.the_data[ctx.guild]["running"]:
+            await ctx.send("Game of hangman is not yet running!\nStarting a game of hangman!")
+            self._startgame(ctx.guild)
+            await self._printgame(ctx.channel)
+        else:
+            await ctx.send("Guess by reacting to the message")
+            # await self._guessletter(guess, ctx.channel)
 
-
-                
-    def _startgame(self):
+    def _startgame(self, guild):
         """Starts a new game of hangman"""
-        self.the_data["answer"] = self._getphrase().upper()
-        self.the_data["hangman"] = 0
-        self.the_data["guesses"] = []
-        self.winbool = False
-        self.the_data["running"] = True
-        self.the_data["trackmessage"] = False
-        self.save_data()
-        
-    def _stopgame(self):
+        self.the_data[guild]["answer"] = self._getphrase().upper()
+        self.the_data[guild]["hangman"] = 0
+        self.the_data[guild]["guesses"] = []
+        self.winbool[guild] = False
+        self.the_data[guild]["running"] = True
+        self.the_data[guild]["trackmessage"] = False
+
+    def _stopgame(self, guild):
         """Stops the game in current state"""
-        self.the_data["running"] = False
-        self.save_data()
-    
-    async def _checkdone(self, channel=None):
-        if self.winbool:
-            if channel:
-                await self.bot.send_message(channel, "You Win!")
-            else:
-                await self.bot.say("You Win!")
-            self._stopgame()
-                
-        if self.the_data["hangman"] >= 7:
-            if channel:
-                await self.bot.send_message(channel, "You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
-            else:
-                await self.bot.say("You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
-                
-            self._stopgame()
-                
+        self.the_data[guild]["running"] = False
+        self.the_data[guild]["trackmessage"] = False
+
+    async def _checkdone(self, channel):
+        if self.winbool[channel.guild]:
+            await channel.send("You Win!")
+            self._stopgame(channel.guild)
+        elif self.the_data[channel.guild]["hangman"] >= 7:
+            await channel.send("You Lose!\nThe Answer was: **" + self.the_data[channel.guild]["answer"] + "**")
+
+            self._stopgame(channel.guild)
+
     def _getphrase(self):
         """Get a new phrase for the game and returns it"""
-        phrasefile = open(self.answer_path, 'r')
-        phrases = phrasefile.readlines()
-        
+
+        with open(self.answer_path, 'r') as phrasefile:
+            phrases = phrasefile.readlines()
+
         outphrase = ""
         while outphrase == "":
-            outphrase = phrases[randint(0, len(phrases)-1)].partition(" (")[0]
-#           outphrase = phrases[randint(0,10)].partition(" (")[0]
+            outphrase = phrases[randint(0, len(phrases) - 1)].partition(" (")[0]
         return outphrase
-   
-    def _hideanswer(self):
+
+    def _hideanswer(self, guild):
         """Returns the obscured answer"""
         out_str = ""
-        
-        self.winbool = True
-        for i in self.the_data["answer"]:
+
+        self.winbool[guild] = True
+        for i in self.the_data[guild]["answer"]:
             if i == " " or i == "-":
-                out_str += i*2
-            elif i in self.the_data["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
-                out_str += "__"+i+"__ "
+                out_str += i * 2
+            elif i in self.the_data[guild]["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
+                out_str += "__" + i + "__ "
             else:
                 out_str += "**\_** "
-                self.winbool = False
-                
+                self.winbool[guild] = False
+
         return out_str
-        
-    def _guesslist(self):
+
+    def _guesslist(self, guild):
         """Returns the current letter list"""
         out_str = ""
-        for i in self.the_data["guesses"]:
+        for i in self.the_data[guild]["guesses"]:
             out_str += str(i) + ","
         out_str = out_str[:-1]
-        
+
         return out_str
-        
-    async def _guessletter(self, guess, channel=None):
+
+    async def _guessletter(self, guess, message):
         """Checks the guess on a letter and prints game if acceptable guess"""
-        if not guess.upper() in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or not len(guess) == 1:
-            if channel:
-                await self.bot.send_message(channel, "Invalid guess. Only A-Z is accepted")
-            else:
-                await self.bot.say("Invalid guess. Only A-Z is accepted")
+        channel = message.channel
+        if guess.upper() not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or len(guess) != 1:
+            await channel.send("Invalid guess. Only A-Z is accepted")
             return
 
-        if guess.upper() in self.the_data["guesses"]:
-            if channel:
-                await self.bot.send_message(channel, "Already guessed that! Try again")
-            else:
-                await self.bot.say("Already guessed that! Try again")
+        if guess.upper() in self.the_data[channel.guild]["guesses"]:
+            await channel.send("Already guessed that! Try again")
             return
+        if guess.upper() not in self.the_data[channel.guild]["answer"]:
+            self.the_data[channel.guild]["hangman"] += 1
+
+        self.the_data[channel.guild]["guesses"].append(guess.upper())
+
+        await self._reprintgame(message)
 
-        if not guess.upper() in self.the_data["answer"]:
-            self.the_data["hangman"] += 1
-            
-        self.the_data["guesses"].append(guess.upper())
-        self.save_data()
-        
-        await self._printgame(channel)
-        
     async def _on_react(self, reaction, user):
         """ Thanks to flapjack reactpoll for guidelines
             https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
-        
-        
-        
-        if not self.the_data["trackmessage"]:
+
+        if reaction.message.id != self.the_data[user.guild]["trackmessage"]:
             return
-        
+
         if user == self.bot.user:
             return  # Don't remove bot's own reactions
         message = reaction.message
         emoji = reaction.emoji
-        
-        if not message.id == self.the_data["trackmessage"]:
-            return
-        
+
         if str(emoji) in self.letters:
             letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[self.letters.index(str(emoji))]
-            await self._guessletter(letter, message.channel)
-            
-            
+            await self._guessletter(letter, message)
+            await message.remove_reaction(emoji, user)
+            await message.remove_reaction(emoji, self.bot.user)
+
         if str(emoji) in self.navigate:
             if str(emoji) == self.navigate[0]:
                 await self._reactmessage_am(message)
-            
+
             if str(emoji) == self.navigate[-1]:
                 await self._reactmessage_nz(message)
-    
-    
+
     async def _reactmessage_menu(self, message):
         """React with menu options"""
-        await self.bot.clear_reactions(message)
-        
-        await self.bot.add_reaction(message, self.navigate[0])
-        await self.bot.add_reaction(message, self.navigate[-1])
-        
+        await message.clear_reactions()
+
+        await message.add_reaction(self.navigate[0])
+        await message.add_reaction(self.navigate[-1])
+
     async def _reactmessage_am(self, message):
-        await self.bot.clear_reactions(message)
+        await message.clear_reactions()
 
         for x in range(len(self.letters)):
-            if x in [i for i,b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist()]:
-                await self.bot.add_reaction(message, self.letters[x])
-         
-        await self.bot.add_reaction(message, self.navigate[-1])
+            if x in [i for i, b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist(message.guild)]:
+                await message.add_reaction(self.letters[x])
 
+        await message.add_reaction(self.navigate[-1])
 
     async def _reactmessage_nz(self, message):
-        await self.bot.clear_reactions(message)
+        await message.clear_reactions()
 
         for x in range(len(self.letters)):
-            if x in [i for i,b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist()]:
-                await self.bot.add_reaction(message, self.letters[x+13])
-                
-        await self.bot.add_reaction(message, self.navigate[0])        
+            if x in [i for i, b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist(message.guild)]:
+                await message.add_reaction(self.letters[x + 13])
 
+        await message.add_reaction(self.navigate[0])
 
-    async def _printgame(self, channel=None):
+    def _make_say(self, guild):
+        c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n"
+
+        c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n"
+
+        c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n"
+
+        c_say += self.navigate[0] + " for A-M, " + self.navigate[-1] + " for N-Z"
+
+        return c_say
+
+    async def _reprintgame(self, message):
+        if message.guild not in self.hanglist:
+            await self._update_hanglist()
+
+        c_say = self._make_say(message.guild)
+
+        await message.edit(content=c_say)
+        self.the_data[message.guild]["trackmessage"] = message.id
+
+        await self._checkdone(message.channel)
+
+    async def _printgame(self, channel):
         """Print the current state of game"""
-        cSay = ("Guess this: " + str(self._hideanswer()) + "\n"
-                + "Used Letters: " + str(self._guesslist()) + "\n"
-                + self.hanglist[self.the_data["hangman"]] + "\n"
-                + self.navigate[0]+" for A-M, "+self.navigate[-1]+" for N-Z")
-        if channel:
-            message = await self.bot.send_message(channel, cSay)
-        else:
-            message = await self.bot.say(cSay)
-        
-        self.the_data["trackmessage"] = message.id
-        self.save_data()
+        if channel.guild not in self.hanglist:
+            await self._update_hanglist()
+
+        c_say = self._make_say(channel.guild)
+
+        message = await channel.send(c_say)
+
+        self.the_data[channel.guild]["trackmessage"] = message.id
+
         await self._reactmessage_menu(message)
         await self._checkdone(channel)
-        
-    
-def check_folders():
-    if not os.path.exists("data/Fox-Cogs"):
-        print("Creating data/Fox-Cogs folder...")
-        os.makedirs("data/Fox-Cogs")
 
-    if not os.path.exists("data/Fox-Cogs/hangman"):
-        print("Creating data/Fox-Cogs/hangman folder...")
-        os.makedirs("data/Fox-Cogs/hangman")
-
-        
-def check_files():
-    if not dataIO.is_valid_json("data/Fox-Cogs/hangman/hangman.json"):
-        dataIO.save_json("data/Fox-Cogs/hangman/hangman.json", {"running": False, "hangman": 0, "guesses": [], "theface": "<:never:336861463446814720>", "trackmessage": False})
-    
 
 def setup(bot):
-    check_folders()
-    check_files()
     n = Hangman(bot)
     bot.add_cog(n)
     bot.add_listener(n._on_react, "on_reaction_add")
-    
diff --git a/hangman/info.json b/hangman/info.json
index ac146f1..655f00c 100644
--- a/hangman/info.json
+++ b/hangman/info.json
@@ -1,14 +1,20 @@
 {
-	"AUTHOR": "Bobloy",
-	"DESCRIPTION": "Hangman Cog for Red Discord bot. Play a game of Hangman with your friends!",
-	"INSTALL_MSG": "Thank you for installing Hangman! Play with [p]hangman, edit with [p]hangset",
-	"NAME": "Hangman",
-	"SHORT": "Play a game of Hangman with your friends!",
-	"TAGS": [
-		"fox",
-		"bobloy",
-		"fun",
-		"game"
-	],
-	"HIDDEN": false
+  "author": [
+    "Bobloy"
+  ],
+  "bot_version": [
+    3,
+    0,
+    0
+  ],
+  "description": "Play Hangman with your friends",
+  "hidden": false,
+  "install_msg": "Thank you for installing Hangman!",
+  "requirements": [],
+  "short": "Play Hangman",
+  "tags": [
+    "game",
+    "fun",
+    "bobloy"
+  ]
 }
\ No newline at end of file
diff --git a/immortal/immortal.py b/immortal/immortal.py
deleted file mode 100644
index c20908c..0000000
--- a/immortal/immortal.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import discord
-import asyncio
-import os
-from datetime import datetime
-from discord.ext import commands
-
-from .utils.dataIO import dataIO
-from .utils import checks
-
-
-class Immortal:
-    """Creates a goodbye message when people leave"""
-
-    def __init__(self, bot):
-        self.bot = bot
-        self.path = "data/Fox-Cogs/immortal"
-        self.file_path = "data/Fox-Cogs/immortal/immortal.json"
-        self.the_data = dataIO.load_json(self.file_path)
-
-    def save_data(self):
-        """Saves the json"""
-        dataIO.save_json(self.file_path, self.the_data)
-
-    async def adj_roles(self, server, author, member: discord.Member=None, rrole_names=[], arole_names=[]):
-        # Thank you SML for the addrole code
-        # https://github.com/smlbiobot/SML-Cogs/tree/master/mm
-        
-        rroles = [r for r in server.roles if r.name in rrole_names]
-        aroles = [r for r in server.roles if r.name in arole_names]
-        try:
-            await self.bot.add_roles(member, *aroles)
-            await asyncio.sleep(0.5)
-            await self.bot.remove_roles(member, *rroles)
-            await asyncio.sleep(0.5)
-
-        except discord.Forbidden:
-            await self.bot.say(
-                "{} does not have permission to edit {}โs roles.".format(
-                    author.display_name, member.display_name))
-
-        except discord.HTTPException:
-            await self.bot.say(
-                "Failed to adjust roles.")
-        except:
-            await self.bot.say("Unknown Exception")
-
-        
-
-
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def iresort(self, ctx, member: discord.Member=None):
-        """Sends someone on vacation!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Member", "Immortal", "Eternal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest"]
-            arole_names = ["Resort"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Resort" in [r.name for r in member.roles]:
-                await self.bot.say("You are being sent on Vacation! :tada:" +
-                                   "Please relocate to Immortal Resort (#889L92UQ) when you find the time.")
-                await self.bot.send_message(member, "You are being sent on Vacation! :tada: Please relocate " +
-                                                    "to Immortal Resort (#889L92UQ) when you find the time.\n" +
-                                                    "You'll have limited access to the server until you rejoin a main clan")
-
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def icrypt(self, ctx, member: discord.Member=None):
-        """Sends someone to Crypt!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Revenant", "Undead", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Crypt"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Crypt" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-                
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def irevenant(self, ctx, member: discord.Member=None):
-        """Sends someone to Revenant!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Undead", "Crypt", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Revenant"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Revenant" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-                
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def iundead(self, ctx, member: discord.Member=None):
-        """Sends someone to Undead!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Undead"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Undead" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-                
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def iphantom(self, ctx, member: discord.Member=None):
-        """Sends someone to Phantom!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Immortal", "Eternal", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Phantom"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Phantom" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def ieternal(self, ctx, member: discord.Member=None):
-        """Sends someone to Eternal!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Immortal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Eternal"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Eternal" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-
-    @commands.command(pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(manage_roles=True)
-    async def iimmortal(self, ctx, member: discord.Member=None):
-        """Sends someone to Immortal!"""
-
-        if member is None:
-            await self.bot.send_cmd_help(ctx)
-        else:
-            server = ctx.message.server
-            author = ctx.message.author
-            role_names = ["Eternal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
-            arole_names = ["Member", "Immortal"]
-            await self.adj_roles(server, author, member, role_names, arole_names)
-            if "Immortal" in [r.name for r in member.roles]:
-                await self.bot.say("Success")
-                await self.send_welcome(member)
-
-    @commands.group(aliases=['setimmortal'], pass_context=True, no_pm=True)
-    @checks.mod_or_permissions(administrator=True)
-    async def immortalset(self, ctx):
-        """Adjust immortal settings"""
-
-        server = ctx.message.server
-        if server.id not in self.the_data:
-            self.the_data[server.id] = {}
-            self.save_data()
-
-        if ctx.invoked_subcommand is None:
-            await self.bot.send_cmd_help(ctx)
-
-    @immortalset.command(pass_context=True, no_pm=True)
-    async def welcomechannel(self, ctx):
-        server = ctx.message.server
-        if 'WELCOMECHANNEL' not in self.the_data[server.id]:
-            self.the_data[server.id]['WELCOMECHANNEL'] = ''
-
-        self.the_data[server.id]['WELCOMECHANNEL'] = ctx.message.channel.id
-        self.save_data()
-        await self.bot.say("Welcome Channel set to "+ctx.message.channel.name)
-
-    async def send_welcome(self, member):
-        server = member.server
-        if server.id in self.the_data:
-            await self.bot.send_message(server.get_channel(self.the_data[server.id]['WELCOMECHANNEL']),
-                                        "You now have access to the server, " + member.mention + "\n" +
-                                        "Check " + server.get_channel("257557008662790145").mention + " & " +
-                                        server.get_channel("257560603093106688").mention+" for clan rules etc.\n" +
-                                        "We recommend turning all message notifications on for " + server.get_channel("257560603093106688").mention +
-                                        " if you want to know when tourneys are posted and other important info.\n" +
-                                        "You can also type `!help` for a list of bot commands/features.")
-
-#    @immortalset.command(pass_context=True)
-#    async def channel(self, ctx):
-#        server = ctx.message.server
-#        if 'channel' not in self.the_data[server.id]:
-#            self.the_data[server.id]['channel'] = ''
-
-#        self.the_data[server.id]['channel'] = ctx.message.channel.id
-#        self.save_data()
-
-#    async def _when_leave(self, member):
-#        server = member.server
-#        if server.id not in self.the_data:
-#            return
-
-#        await self.bot.say("YOU LEFT ME "+member.mention)
-#        self.the_data[server.id]
-
-
-def check_folders():
-    if not os.path.exists("data/Fox-Cogs"):
-        print("Creating data/Fox-Cogs folder...")
-        os.makedirs("data/Fox-Cogs")
-
-    if not os.path.exists("data/Fox-Cogs/immortal"):
-        print("Creating data/Fox-Cogs/immortal folder...")
-        os.makedirs("data/Fox-Cogs/immortal")
-
-
-def check_files():
-    if not dataIO.is_valid_json("data/Fox-Cogs/immortal/immortal.json"):
-        dataIO.save_json("data/Fox-Cogs/immortal/immortal.json", {})
-
-
-def setup(bot):
-    check_folders()
-    check_files()
-    q = Immortal(bot)
-    bot.add_cog(q)
diff --git a/immortal/info.json b/immortal/info.json
deleted file mode 100644
index 8668a65..0000000
--- a/immortal/info.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-    "AUTHOR" : "Bobloy",
-    "INSTALL_MSG" : "Thank you for installing Immortal Family Cog",
-    "NAME" : "Immortal",
-    "SHORT" : "Cog for a specific server, will not work on other servers",
-    "DESCRIPTION" : "Cog specifically for the Immortal Family discord server. I do not recommend installing it",
-    "TAGS" : ["fox", "bobloy", "utilities", "tools", "utility", "tool"],
-    "HIDDEN" : false
-}
\ No newline at end of file