Skip to content
Permalink
4d22124483
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
92 lines (86 sloc) 7.93 KB
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>chatbot by soperd</title>
<link rel="stylesheet" href="stylesheets/styles.css">
<link rel="stylesheet" href="stylesheets/github-dark.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="javascripts/respond.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!--[if lt IE 8]>
<link rel="stylesheet" href="stylesheets/ie.css">
<![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<div id="header">
<nav>
<li class="fork"><a href="https://github.coventry.ac.uk/soperd/chatbot-docs">View On GitHub</a></li>
</nav>
</div><!-- end header -->
<div class="wrapper">
<section>
<div id="title">
<h1>chatbot</h1>
<p>Year 1 Project</p>
<hr>
<span class="credits left">Project maintained by <a href="https://github.coventry.ac.uk/soperd">soperd</a></span>
<span class="credits right">Hosted on GitHub Pages &mdash; Theme by <a href="https://twitter.com/michigangraham">mattgraham</a></span>
</div>
<h3>
<a id="introduction" class="anchor" href="#introduction" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Introduction</h3>
<p>This is a reflective piece on my Year 1 group project. Please view the code <a href="https://github.coventry.ac.uk/soperd/chatbot">here</a>.</p>
<p>The project is a chatbot written in Python 3 primarily for use with Discord. Current features include:</p>
<ul>
<li>Some natural language understanding with <a href="https://rasa.com/">RasaNLU</a>.</li>
<li>Weather forecast integration with <a href="https://darksky.net/">DarkSky</a>.</li>
<li>Location services with <a href="https://nominatim.openstreetmap.org/">Nominantim by OpenStreetMaps</a>.</li>
<li>Datastore integration with <a href="https://redis.io/">Redis</a>.</li>
</ul>
<h3>
<a id="design" class="anchor" href="#design" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Design</h3>
<p>As the most experienced member of my team, I set down the foundation for the bot's design and structure. At the time, we were unsure what platform which platforms we wanted to target, so I set out by separating the platform-specific implementations from the platform-neutral services.</p>
<p>I did this by putting all code that provided some kind of generic services into the <code>handlers</code> folder of the project. Here you can find a file called <code>services.py</code> which contains a class called <code>Service</code> that acts as a configurable base class. All other services in <code>handlers</code> inherits this <code>Service</code> class and implements its own functionality. For example, the <code>WeatherService</code> class in <code>weather.py</code> contains methods like <code>get_weather_at</code> that takes a given longitude and latitude and queries DarkSky's forecast API.</p>
<p>The services can then be registered using the <code>register_service</code> decorator that can be found in <code>services.py</code>. The decorator takes a name as a parameter, then resolves the config provided to that service with the same name detailed in <code>config.json</code>, and stores it in the dictionary <code>global_services</code>.</p>
<p>For all the platform specific implementations, I put the code in the <code>wrappers</code> folder. This contains a file called <code>bot.py</code> which has a class <code>ChatBot</code>. This acts as an interface that defines common behaviours for every bot with methods such as <code>start</code> and <code>stop</code>, since the reasoning behind it was that every bot must at least start and be able to be stopped. At the moment, the only implemented platform is Discord, the code for which can be seen in <code>discord.py</code>. Here we can see the bot inherits <code>ChatBot</code>, implements the <code>start</code> method and defines its own. <code>ChatBot</code> also declares three class members:</p>
<ul>
<li>
<code>services</code>: A dictionary containing instances of services for the bot to use.</li>
<li>
<code>datastore</code>: A Redis client object for the bot to access the datastore.</li>
<li>
<code>config</code>: A <code>ConfigDict</code> object that acts as a representation of the bot's config in <code>config.json</code> (see below).</li>
</ul>
<p>Both services and wrappers are configurable via the <code>config.json</code> file, which looks something like this:</p>
<div class="highlight highlight-source-json"><pre>{
<span class="pl-s"><span class="pl-pds">"</span>discord<span class="pl-pds">"</span></span>: {
<span class="pl-s"><span class="pl-pds">"</span>token<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>&lt;DISCORD TOKEN HERE&gt;<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>services<span class="pl-pds">"</span></span>: [
<span class="pl-s"><span class="pl-pds">"</span>weather<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>location<span class="pl-pds">"</span></span>
]
},
<span class="pl-s"><span class="pl-pds">"</span>services<span class="pl-pds">"</span></span>: {
<span class="pl-s"><span class="pl-pds">"</span>weather<span class="pl-pds">"</span></span>: {
<span class="pl-s"><span class="pl-pds">"</span>token<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>&lt;DARKSKY TOKEN HERE&gt;<span class="pl-pds">"</span></span>
}
},
<span class="pl-s"><span class="pl-pds">"</span>redis<span class="pl-pds">"</span></span>: {
<span class="pl-s"><span class="pl-pds">"</span>host<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>localhost<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>port<span class="pl-pds">"</span></span>: <span class="pl-c1">6379</span>
}
}</pre></div>
<p>This details that the Discord bot has a token and a set of services it can use. For this example, it can use the weather and location services. When we run the bot using the <code>run_discord.py</code> script, this passes the <code>discord</code> configuration to the <code>DiscordBot</code> class constructor, which in turn calls <code>ChatBot</code>'s constructor. If the optional parameter <code>services</code> :p is <code>None</code> then <code>ChatBot</code>'s constructor then will find the services listed in <code>config.json</code> from the <code>global_services</code> and store them in the <code>services</code> class member. This was my attempt to "inject" services into the bot automatically.</p>
<p><code>DiscordBot</code> is currently the only functioning bot. It uses the Discord.py library which makes for easy integration with Discord's API.</p>
<h3>
<a id="reflection" class="anchor" href="#reflection" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Reflection</h3>
<p>I think my design worked well for the most part. If we decided we wanted to integrate the app into WhatsApp or Messenger, I feel that my design would aid in porting our features over to the new bot. This design also allows for multiple bots with similar features on the same platform, which I am happy with. I do, however, believe that in some areas my code is poorly implemented, such as :p the <code>global_services</code> dictionary. This seems like a bit of a hack, and it wasn't really necessary to achieve my goal. That being said, this is the first time I've used a custom decorator in Python and I can see its potential elsewhere.</p>
</section>
</div>
<!--[if !IE]><script>fixScale(document);</script><![endif]-->
</body>
</html>