diff --git a/.gitignore b/.gitignore
index 0cf91e4..55ab627 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,5 @@ nosetests.xml
 .idea
 TODO
 *.log
+
+venv
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..84d825e
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,5 @@
+[MESSAGES CONTROL]
+disable=R,C,W,import-error,broad-except
+
+[TYPECHECK]
+ignored-classes=_socketobject
diff --git a/.travis.yml b/.travis.yml
index d21daa0..a280a8d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,4 +27,7 @@ install:
   - pip install -r requirements-dev.txt
 
 script:
+  - pylint integrations/*/*.py
+  - pylint plugins/*/*.py
+  - pylint webhooks/*/*.py
   - pytest -v webhooks/*/test*
diff --git a/integrations/consul/consulalerta.py b/integrations/consul/consulalerta.py
index 71d0355..af9f57e 100755
--- a/integrations/consul/consulalerta.py
+++ b/integrations/consul/consulalerta.py
@@ -1,12 +1,12 @@
 #!/usr/bin/env python
 
-from alertaclient.api import Client
-import sys
-import os
-import time
 import json
+import os
+
 import consul
+import sys
 import time
+from alertaclient.api import Client
 
 CONSUL_HOST = os.environ.get('CONSUL_HOST', '127.0.0.1')
 CONSUL_PORT = int(os.environ.get('CONSUL_PORT', 8500))
@@ -19,13 +19,13 @@ print(j)
 
 try:
     url = client.kv.get('alerta/apiurl')[1]['Value']
-except:
+except Exception:
     print("No URL defined, exiting")
     sys.exit(1)
 
 try:
     key = client.kv.get('alerta/apikey')[1]['Value']
-except:
+except Exception:
     print("No key defined, exiting")
     sys.exit(1)
 
@@ -72,13 +72,13 @@ SEVERITY_MAP = {
 def createalert( data ):
     try:
         environment = client.kv.get('alerta/env/{0}'.format(data['Node']))[1]['Value']
-    except:
+    except Exception:
         try:
-             environment = client.kv.get('alerta/defaultenv')[1]['Value']
-        except:
-             environment = "Production"
+            environment = client.kv.get('alerta/defaultenv')[1]['Value']
+        except Exception:
+            environment = "Production"
 
-    for i in range(max_retries):
+    for _ in range(max_retries):
         try:
             print("Response:")
             response = api.send_alert(
diff --git a/integrations/consul/consulheartbeat.py b/integrations/consul/consulheartbeat.py
index d6c9cd1..9caa52c 100755
--- a/integrations/consul/consulheartbeat.py
+++ b/integrations/consul/consulheartbeat.py
@@ -17,7 +17,7 @@ origin = client.kv.get('alerta/origin')[1]['Value']
 api = Client(endpoint=url, key=key)
 
 def createheartbeat():
-    for i in range(max_retries):
+    for _ in range(max_retries):
         try:
             print(api.heartbeat(origin=origin, timeout=timeout))
         except Exception as e:
diff --git a/integrations/consul/setup.py b/integrations/consul/setup.py
index 57467a6..cda1762 100644
--- a/integrations/consul/setup.py
+++ b/integrations/consul/setup.py
@@ -1,5 +1,5 @@
 
-from setuptools import setup, find_packages
+from setuptools import setup
 
 version = '1.1.1'
 
diff --git a/integrations/mailer/mailer.py b/integrations/mailer/mailer.py
index 27fa935..e59ff54 100755
--- a/integrations/mailer/mailer.py
+++ b/integrations/mailer/mailer.py
@@ -9,6 +9,8 @@ import re
 import signal
 import smtplib
 import socket
+from functools import reduce
+
 import sys
 import threading
 import time
@@ -213,7 +215,7 @@ class MailSender(threading.Thread):
                     return True
             LOG.debug('Regex %s matches nothing', regex)
             return False
-        elif isinstance(value, str) or isinstance(value, unicode):
+        elif isinstance(value, str) or isinstance(value, unicode):  # pylint: disable=undefined-variable
             LOG.debug('Trying to match %s to %s',
                       value, regex)
             return re.search(regex, value) is not None
@@ -302,14 +304,14 @@ class MailSender(threading.Thread):
             LOG.debug('%s : Email sent to %s' % (alert.get_id(),
                                                  ','.join(contacts)))
             return (msg, contacts)
-        except (socket.error, socket.herror, socket.gaierror) as e:
-            LOG.error('Mail server connection error: %s', e)
-            return None
         except smtplib.SMTPException as e:
             LOG.error('Failed to send mail to %s on %s:%s : %s',
                       ", ".join(contacts),
                       OPTIONS['smtp_host'], OPTIONS['smtp_port'], e)
             return None
+        except (socket.error, socket.herror, socket.gaierror) as e:
+            LOG.error('Mail server connection error: %s', e)
+            return None
         except Exception as e:
             LOG.error('Unexpected error while sending email: {}'.format(str(e)))  # nopep8
             return None
diff --git a/integrations/urlmon/settings.py b/integrations/urlmon/settings.py
index 8fc7fb5..432b54f 100644
--- a/integrations/urlmon/settings.py
+++ b/integrations/urlmon/settings.py
@@ -8,7 +8,7 @@ checks = [
         "resource": "www.google.com",
         "url": "http://www.google.com?q=foo#q=foo",
         "environment": "Production",
-        "service": ["Google", "Search"]
+        "service": ["Google", "Search"],
         "api_endpoint": "http://localhost:8080",
         "api_key": None,
     },
diff --git a/integrations/urlmon/urlmon.py b/integrations/urlmon/urlmon.py
index 83bc8de..cf5b30d 100644
--- a/integrations/urlmon/urlmon.py
+++ b/integrations/urlmon/urlmon.py
@@ -1,20 +1,20 @@
-import platform
-import sys
-import time
-import urllib.request, urllib.error, urllib.parse
+import datetime
 import json
-import threading
+import logging
+import platform
 import queue
 import re
-import logging
-
-import datetime
-import ssl
 import socket
-
-from alertaclient.api import Client
-
+import ssl
+import threading
 from http.server import BaseHTTPRequestHandler as BHRH
+from urllib.error import URLError  # pylint: disable=no-name-in-module
+from urllib.parse import urlparse  # pylint: disable=no-name-in-module
+from urllib.request import build_opener, ProxyHandler, HTTPBasicAuthHandler, install_opener, Request, urlopen  # pylint: disable=no-name-in-module
+
+import sys
+import time
+from alertaclient.api import Client
 
 HTTP_RESPONSES = dict([(k, v[0]) for k, v in list(BHRH.responses.items())])
 
@@ -240,8 +240,8 @@ class WorkerThread(threading.Thread):
             if check_ssl:
                 ssl_date_fmt = r'%b %d %H:%M:%S %Y %Z'
                 context = ssl.create_default_context()
-                domain = '{uri.netloc}'.format(uri=urllib.parse.urlparse(check.get('url')))
-                port = urllib.parse.urlparse(check.get('url')).port or 443
+                domain = '{uri.netloc}'.format(uri=urlparse(check.get('url')))
+                port = urlparse(check.get('url')).port or 443
                 conn = context.wrap_socket(
                     socket.socket(socket.AF_INET),
                     server_hostname=domain
@@ -311,42 +311,42 @@ class WorkerThread(threading.Thread):
             start = time.time()
 
             if username and password:
-                auth_handler = urllib.request.HTTPBasicAuthHandler()
+                auth_handler = HTTPBasicAuthHandler()
                 auth_handler.add_password(realm=realm,
                                           uri=uri,
                                           user=username,
                                           passwd=password)
                 if proxy:
-                    opener = urllib.request.build_opener(auth_handler, urllib.request.ProxyHandler(proxy))
+                    opener = build_opener(auth_handler, ProxyHandler(proxy))
                 else:
-                    opener = urllib.request.build_opener(auth_handler)
+                    opener = build_opener(auth_handler)
             else:
                 if proxy:
-                    opener = urllib.request.build_opener(urllib.request.ProxyHandler(proxy))
+                    opener = build_opener(ProxyHandler(proxy))
                 else:
-                    opener = urllib.request.build_opener()
-            urllib.request.install_opener(opener)
+                    opener = build_opener()
+            install_opener(opener)
 
             if 'User-agent' not in headers:
                 headers['User-agent'] = 'alert-urlmon/%s' % (__version__)
 
             try:
                 if post:
-                    req = urllib.request.Request(url, json.dumps(post), headers=headers)
+                    req = Request(url, json.dumps(post), headers=headers)
                 else:
-                    req = urllib.request.Request(url, headers=headers)
-                response = urllib.request.urlopen(req, None, MAX_TIMEOUT)
+                    req = Request(url, headers=headers)
+                response = urlopen(req, None, MAX_TIMEOUT)
             except ValueError as e:
-                LOG.error('Request failed: %s', e)
-            except urllib.error.URLError as e:
+                LOG.error('Request failed: %s' % e)
+            except URLError as e:
                 if hasattr(e, 'reason'):
                     reason = str(e.reason)
                     status = None
                 elif hasattr(e, 'code'):
                     reason = None
-                    status = e.code
+                    status = e.code  # pylint: disable=no-member
             except Exception as e:
-                LOG.warning('Unexpected error: %s', e)
+                LOG.warning('Unexpected error: %s' % e)
             else:
                 status = response.getcode()
                 body = response.read()
@@ -412,7 +412,7 @@ class UrlmonDaemon(object):
                         event='big queue for http checks',
                         value=self.queue.qsize(),
                         severity=severity,
-                        text='URL check queue length is %d', self.queue.qsize(),
+                        text='URL check queue length is %d' % self.queue.qsize(),
                         event_type='serviceAlert',
                     )
                 except Exception as e:
diff --git a/plugins/alertops/alerta_alertops.py b/plugins/alertops/alerta_alertops.py
index b3e687b..2c2d971 100644
--- a/plugins/alertops/alerta_alertops.py
+++ b/plugins/alertops/alerta_alertops.py
@@ -22,22 +22,24 @@ class TriggerEvent(PluginBase):
 
 
     def pre_receive(self, alert, **kwargs):
-        
         return alert
 
+    @staticmethod
+    def _event_type(severity):
+        if severity in ['cleared', 'normal', 'ok']:
+                return "close"
+        else:
+                return "open"
+
     def post_receive(self, alert, **kwargs):
         if alert.repeat:
                 return
+
         message = "%s: %s alert for %s - %s" %( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource)
         
-        if alert.severity in ['cleared', 'normal', 'ok']:
-                event_type = "close"
-        else:
-                event_type = "open"                
-
         payload = {
             "source_id": alert.id,
-            "source_status": event_type,
+            "source_status": TriggerEvent._event_type(alert.severity),
             "description": message,
             "resource": alert.resource,
             "source": "alerta",
@@ -54,11 +56,12 @@ class TriggerEvent(PluginBase):
 
     def status_change(self, alert, status, text, **kwargs):
         if status not in ['ack', 'assign']:
-                return 
+                return
+
         message = "%s: %s alert for %s - %s" %( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource)
         payload = {
             "source_id": alert.id,
-            "source_status": event_type,
+            "source_status": TriggerEvent._event_type(alert.severity),
             "description": message,
             "resource": alert.resource,
             "source": "alerta",
diff --git a/plugins/mattermost/alerta_mattermost.py b/plugins/mattermost/alerta_mattermost.py
index 7df44fa..9c80a84 100644
--- a/plugins/mattermost/alerta_mattermost.py
+++ b/plugins/mattermost/alerta_mattermost.py
@@ -22,15 +22,12 @@ MATTERMOST_USERNAME = os.environ.get(
 
 class ServiceIntegration(PluginBase):
 
-    def __init__(self, name=None):
-
-        super().__init__(name)
-
     def pre_receive(self, alert):
-        return alert
         LOG.debug('Mattermost: %s', alert)
+        return alert
 
     def get_icon(self, status):
+        LOG.debug('Mattermost: %s', status)
         return {
             'security': ':closed_lock_with_key:',
             'critical': ':bangbang:',
@@ -42,9 +39,9 @@ class ServiceIntegration(PluginBase):
             'trace': ':signal_strength:',
             'ok': ':ok:'
         }.get(status, ':ok:')
-        LOG.debug('Mattermost: %s', status)
 
     def _prepare_payload(self, alert):
+        LOG.debug('Mattermost: %s', alert)
         return "{} **{}** **{}**\n`{}` ```{}```".format(
             self.get_icon(alert.severity),
             alert.severity,
@@ -52,7 +49,6 @@ class ServiceIntegration(PluginBase):
             alert.event,
             alert.text,
         )
-        LOG.debug('Mattermost: %s', alert)
 
     def post_receive(self, alert):
         if alert.repeat:
diff --git a/plugins/slack/alerta_slack.py b/plugins/slack/alerta_slack.py
index bc47618..b21793d 100644
--- a/plugins/slack/alerta_slack.py
+++ b/plugins/slack/alerta_slack.py
@@ -5,6 +5,8 @@ import os
 import requests
 import traceback
 
+LOG = logging.getLogger('alerta.plugins.slack')
+
 try:
     from jinja2 import Template
 except Exception as e:
@@ -56,6 +58,7 @@ SLACK_HEADERS = {
     'Content-Type': 'application/json'
 }
 
+
 class ServiceIntegration(PluginBase):
 
     def __init__(self, name=None):
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1545d2f..2e2e383 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,2 +1,3 @@
+pylint
 pytest
 alerta-server[postgres]