diff --git a/docker/accessbot/accessbot.py b/docker/accessbot/accessbot.py
index 0459ccdd30..38edc16ff2 100755
--- a/docker/accessbot/accessbot.py
+++ b/docker/accessbot/accessbot.py
@@ -34,7 +34,6 @@ class SetAccess(irc.client.SimpleIRCClient):
 
     def __init__(self, config, noop, nick, password, server, port):
         irc.client.SimpleIRCClient.__init__(self)
-        self.identify_msg_cap = False
         self.config = config
         self.nick = nick
         self.password = password
@@ -44,6 +43,7 @@ class SetAccess(irc.client.SimpleIRCClient):
         self.channels = [x['name'] for x in self.config['channels']]
         self.current_channel = None
         self.current_list = []
+        self.current_mode = ''
         self.changes = []
         self.identified = False
         if self.port == 6697:
@@ -56,30 +56,19 @@ class SetAccess(irc.client.SimpleIRCClient):
     def on_disconnect(self, connection, event):
         sys.exit(0)
 
-    def on_welcome(self, c, e):
-        self.identify_msg_cap = False
-        self.log.debug("Requesting identify-msg capability")
-        c.cap('REQ', 'identify-msg')
-        c.cap('END')
-
-    def on_cap(self, c, e):
-        self.log.debug("Received cap response %s" % repr(e.arguments))
-        if e.arguments[0] == 'ACK' and 'identify-msg' in e.arguments[1]:
-            self.log.debug("identify-msg cap acked")
-            self.identify_msg_cap = True
-            self.log.debug("Identifying to nickserv")
-            c.privmsg("nickserv", "identify %s " % self.password)
-
     def on_privnotice(self, c, e):
-        if not self.identify_msg_cap:
-            self.log.debug("Ignoring message because identify-msg "
-                           "cap not enabled")
-            return
         nick = e.source.split('!')[0]
-        auth = e.arguments[0][0]
-        msg = e.arguments[0][1:]
-        if auth == '+' and nick == 'NickServ' and not self.identified:
-            if msg.startswith('You are now identified'):
+        msg = e.arguments[0]
+        if nick == 'NickServ' and not self.identified:
+            if msg.startswith('authenticate yourself to services'):
+                self.log.debug("Identifying to nickserv")
+                # TODO (fungi): We should protect against sending our
+                # password to a false NickServ, perhaps with
+                # https://www.oftc.net/NickServ/CertFP/ or eventually
+                # SASL once the ircd implements that
+                c.privmsg("nickserv", "identify %s " % self.password)
+                return
+            elif msg.startswith('You are successfully identified'):
                 self.identified = True
                 # Prejoin and set ourselves as op in these channels,
                 # to facilitate +f forwarding.
@@ -88,8 +77,10 @@ class SetAccess(irc.client.SimpleIRCClient):
                     c.privmsg("chanserv", "op #%s" % channel)
                 self.advance()
                 return
-        if auth != '+' or nick != 'ChanServ':
-            self.log.debug("Ignoring message from unauthenticated "
+            else:
+                return
+        if nick not in ('ChanServ', 'NickServ'):
+            self.log.debug("Ignoring message from non-ChanServ "
                            "user %s" % nick)
             return
         self.failed = False
@@ -99,95 +90,78 @@ class SetAccess(irc.client.SimpleIRCClient):
         ret = {}
         alumni = []
         mode = ''
+        level = ''
         channel = None
         for c in self.config['channels']:
             if c['name'] == channel_name:
                 channel = c
         if channel is None:
             raise Exception("Unknown channel %s" % (channel_name,))
-        mask = ''
-        for access, nicks in (list(self.config['global'].items()) +
+        for key, value in (list(self.config['global'].items()) +
                               list(channel.items())):
-            if access == 'mask':
-                mask = self.config['access'].get(nicks)
+            if key == 'alumni':
+                alumni += value
                 continue
-            if access == 'alumni':
-                alumni += nicks
+            if key == 'mode':
+                mode = value
                 continue
-            if access == 'mode':
-                mode = nicks
-                continue
-            flags = self.config['access'].get(access)
-            if flags is None:
-                continue
-            for nick in nicks:
-                ret[nick] = flags
-        return mask, ret, alumni, mode
 
-    def _get_access_change(self, current, target, mask):
-        remove = ''
-        add = ''
-        change = ''
-        for x in current:
-            if x in '+-':
+            # If we get this far, we assume the key is an access
+            # level matching an entry in the access list
+            level = self.config['access'].get(key)
+            if level is None:
+                # Skip if this doesn't match a defined access level
                 continue
-            if target:
-                if x not in target:
-                    remove += x
-            else:
-                if x not in mask:
-                    remove += x
-        for x in target:
-            if x in '+-':
-                continue
-            if x not in current:
-                add += x
-        if remove:
-            change += '-' + remove
-        if add:
-            change += '+' + add
-        return change
+            for nick in value:
+                ret[nick] = level
+        return ret, alumni, mode
+
+    def _get_access_change(self, current, target):
+        if current != target:
+            return target
 
     def _get_access_changes(self):
-        mask, target, alumni, mode = self._get_access_list(self.current_channel)
-        self.log.debug("Mask for %s: %s" % (self.current_channel, mask))
-        self.log.debug("Target for %s: %s" % (self.current_channel, target))
+        target, alumni, mode = self._get_access_list(
+            self.current_channel)
+        self.log.debug("Target #%s ACL: %s" % (self.current_channel, target))
         all_nicks = set()
         global_alumni = self.config.get('alumni', {})
         global_mode = self.config.get('mode', '')
         current = {}
         changes = []
-        for nick, flags, msg in self.current_list:
+        for nick, level, msg in self.current_list:
             if nick in global_alumni or nick in alumni :
                 self.log.debug("%s is an alumni; removing access", nick)
                 changes.append('access #%s del %s' % (self.current_channel, nick))
                 continue
             all_nicks.add(nick)
-            current[nick] = flags
+            current[nick] = level
         for nick in target.keys():
             all_nicks.add(nick)
         for nick in all_nicks:
             change = self._get_access_change(current.get(nick, ''),
-                                             target.get(nick, ''), mask)
+                                             target.get(nick, ''))
             if change:
                 changes.append('access #%s add %s %s' % (self.current_channel,
                                                          nick, change))
 
-        # Set the mode.  Note we always just hard-set the mode for
-        # simplicity (per the man page mlock always clears and sets
-        # anyway).  Channel mode overrides global mode.
-        #
-        # Note for +f you need to be op in the target channel; see
-        # op_channel option.
+        # Set the mode if what we want differs from what's already there.
+        # Channel mode overrides global mode.
         if not mode and global_mode:
             mode = global_mode
-        self.log.debug("Setting mode to : %s" % mode)
-        if mode:
+        if not mode:
+            mode = '+'
+        if sorted(mode) != sorted(self.current_mode):
+            self.log.debug("Current mode for #%s is %s, replacing with %s" % (
+                self.current_channel, self.current_mode, mode))
             changes.append('set #%s mlock %s' % (self.current_channel, mode))
 
         return changes
 
     def advance(self, msg=None):
+        # Some service responses include a number of embedded 0x02 bytes
+        if msg:
+            msg = msg.replace('\x02', '')
         if self.changes:
             if self.noop:
                 for change in self.changes:
@@ -204,19 +178,41 @@ class SetAccess(irc.client.SimpleIRCClient):
                 self.connection.quit()
                 return
             self.current_channel = self.channels.pop()
+            # Clear the mode string before we request it, so if we get
+            # no response we won't have the modes from an earlier channel
+            self.current_mode = ''
+            # Sending a set mlock with no value prompts the service to
+            # respond with the current mlock value so we can compare
+            # against it later
+            self.connection.privmsg('chanserv', 'set #%s mlock' %
+                                    self.current_channel)
+            # Clear the access list before we request it, so if we get
+            # no response we won't have the list from an earlier channel
             self.current_list = []
-            self.connection.privmsg('chanserv', 'access list #%s' %
+            self.connection.privmsg('chanserv', 'access #%s list' %
                                     self.current_channel)
             time.sleep(1)
             return
-        if msg.startswith('End of'):
+        # We tokenize every server message, and perform some rough
+        # heuristics in order to determine what kind of response we're
+        # dealing with and whether it's something we know how to parse
+        parts = msg.split()
+        # If the third word look like an access level, assume this is
+        # an access list entry and that the second word is a
+        # corresponding nick
+        if parts[2] in ('MASTER', 'CHANOP', 'MEMBER'):
+            self.current_list.append((parts[1], parts[2], msg))
+        # If the message starts with "MLOCK is SET to" then assume the
+        # fifth word is the channel's mode string
+        elif msg.startswith('MLOCK is SET to'):
+            self.current_mode = parts[4]
+        # If the message starts with "End of" then assume this marks
+        # the end of an access list
+        elif msg.startswith('End of'):
             self.changes = self._get_access_changes()
             self.current_channel = None
             self.advance()
             return
-        parts = msg.split()
-        if parts[2].startswith('+'):
-            self.current_list.append((parts[1], parts[2], msg))
 
 
 def main():