docs: def message_to_event(direction, message):
      """Prepare an ``RFC1459Message`` for event dispatch.
  
      We do this because we have to handle special things as well, such as CTCP
      and deconstructing verbs properly.
      """
      server = message.server
  
      # change numerics into nice names
      if message.verb in numerics:
          message.verb = numerics[message.verb]
      verb = message.verb.lower()
  
      # modify public/private verbs
      if verb == 'privmsg':
          if server.is_channel(message.params[0]):
              verb = 'pubmsg'
      if verb == 'notice':
          verb = 'privnotice'
          if server.is_channel(message.params[0]):
              verb = 'pubnotice'
      elif verb == 'mode':
          verb = 'umode'
          if server.is_channel(message.params[0]):
              verb = 'cmode'
  
      # this is the same as ircreactor does
      info = message.__dict__
      info['direction'] = direction
      info['verb'] = verb
  
      if 'time' in info['tags']:
          info['server_time'] = time.strptime(info['tags']['time'],
                                              '%Y-%m-%dT%H:%M:%S.%fZ')
  
      infos = [[verb, info], ]
  
      # handle shitty ctcp
      if verb in ('privmsg', 'pubmsg', 'privnotice', 'pubnotice'):
          infos = ctcp_unpack_message(info)
  
      # work on each info object separately
      i = -1
      while i < (len(infos) - 1):
          i += 1
          name = infos[i][NAME_ATTR]
  
          # standard message attributes
          for attr, param_map in _verb_param_map.items():
              # escaping
              escaped = False
              if attr.startswith('escaped_'):
                  attr = attr.lstrip('escaped_')
                  escaped = True
  
              for param_number, verbs in param_map.items():
                  if len(infos[i][INFO_ATTR]['params']) > param_number and name in verbs:
                      value = infos[i][INFO_ATTR]['params'][param_number]
                      if escaped:
                          value = escape(value)
                      infos[i][INFO_ATTR][attr] = value
  
          # custom processing
          if name == 'welcome':
              # for servers where a low nicklen makes them silently truncate our nick
              server.nick = server.istring(infos[i][INFO_ATTR]['nick'])
  
          # custom message attributes
          if name == 'ctcp':
              if infos[i][INFO_ATTR]['ctcp_verb'] == 'action':
                  info = dict(infos[i][INFO_ATTR])
                  info['message'] = info['ctcp_text']
                  if server.is_channel(info['target']):
                      name = 'pubaction'
                      info['channel'] = info['target']
                  else:
                      name = 'privaction'
                  infos.append([name, info])
  
          if name == 'umode' and len(infos[i][INFO_ATTR]['params']) > 1:
              modestring = infos[i][INFO_ATTR]['params'][1:]
              modes = parse_modes(modestring)
  
              infos[i][INFO_ATTR]['modestring'] = ' '.join(modestring).strip()
              infos[i][INFO_ATTR]['modes'] = modes
  
          if name == 'cmode' and len(infos[i][INFO_ATTR]['params']) > 1:
              modestring = infos[i][INFO_ATTR]['params'][1:]
              chanmodes = server.features.get('chanmodes')
              prefixes = list(server.features.get('prefix').keys())
              modes = parse_modes(modestring, chanmodes, prefixes)
  
              infos[i][INFO_ATTR]['modestring'] = ' '.join(modestring).strip()
              infos[i][INFO_ATTR]['modes'] = modes
  
          if name == 'cmodeis':
              if len(infos[i][INFO_ATTR]['params']) > 2:
                  modestring = infos[i][INFO_ATTR]['params'][2:]
                  chanmodes = server.features.get('chanmodes')
                  modes = parse_modes(modestring, chanmodes)
  
                  infos[i][INFO_ATTR]['modestring'] = ' '.join(modestring).strip()
                  infos[i][INFO_ATTR]['modes'] = modes
              else:
                  infos[i][INFO_ATTR]['modestring'] = ''
                  infos[i][INFO_ATTR]['modes'] = []
  
          if name == 'namreply':
              channel_name = infos[i][INFO_ATTR]['params'][2]
              server.info.create_channel(channel_name)
              channel = server.info.channels.get(channel_name)
  
              nice_names = []
              channel_prefixes = {}
              if len(infos[i][INFO_ATTR]['params']) > 3:
                  raw_names = infos[i][INFO_ATTR]['params'][3].split(' ')
              else:
                  raw_names = []
  
              for name in raw_names:
                  # InspIRCd sends us an empty last param because they are cool
                  if not len(name):
                      continue
  
                  prefixes = ''
                  while name[0] in server.features.available['prefix'].values():
                      prefixes += name[0]
                      name = name[1:]
  
                  nick = NickMask(name).nick
  
                  server.info.create_user(nick)
                  nice_names.append(name)
                  server.info.create_user(name)
                  user = server.info.users.get(nick)
                  channel_prefixes[user] = prefixes
                  channel.add_user(nick, prefixes=prefixes)
  
              infos[i][INFO_ATTR]['users'] = ','.join(nice_names)
              infos[i][INFO_ATTR]['prefixes'] = channel_prefixes
  
          # source / target mapping
          for attr in ('source', 'target', 'channel'):
              if attr in infos[i][INFO_ATTR] and infos[i][INFO_ATTR][attr]:
                  source = infos[i][INFO_ATTR][attr]
                  if server.is_channel(source):
                      server.info.create_channel(source)
                      infos[i][INFO_ATTR][attr] = server.info.channels.get(source)
                  elif '.' in source and server.is_server(source):
                      server.info.create_server(source)
                      infos[i][INFO_ATTR][attr] = server.info.servers.get(source)
                  elif server.is_nick(source):
                      server.info.create_user(source)
                      infos[i][INFO_ATTR][attr] = server.info.users.get(NickMask(source).nick)
                  else:  # we assume this is a user with messed up characters
                      server.info.create_user(source)
                      infos[i][INFO_ATTR][attr] = server.info.users.get(NickMask(source).nick)
  
          if 'channels' in infos[i][INFO_ATTR] and infos[i][INFO_ATTR]['channels']:
              channels = []
              for chan in infos[i][INFO_ATTR]['channels'].split(','):
                  server.info.create_channel(chan)
                  channels.append(server.info.channels.get(chan))
              infos[i][INFO_ATTR]['channels'] = channels
  
          if 'users' in infos[i][INFO_ATTR] and infos[i][INFO_ATTR]['users']:
              users = []
              for user in infos[i][INFO_ATTR]['users'].split(','):
                  server.info.create_user(user)
                  users.append(server.info.users.get(NickMask(user).nick))
              infos[i][INFO_ATTR]['users'] = users
  
          # custom from_to attribute for ease in bots
          verb = infos[i][INFO_ATTR]['verb']
          dir = infos[i][INFO_ATTR]['direction']
          source = infos[i][INFO_ATTR].get('source')
          target = infos[i][INFO_ATTR].get('target')
  
          if verb in ['pubmsg', 'pubnotice', 'pubaction']:
              infos[i][INFO_ATTR]['from_to'] = target
          elif verb in ['privmsg', 'privnotice', 'privaction']:
              if dir == 'out':
                  infos[i][INFO_ATTR]['from_to'] = target
              elif dir == 'in':
                  if 'echo-message' in server.capabilities.enabled:
                      infos[i][INFO_ATTR]['from_to'] = target
                  else:
                      infos[i][INFO_ATTR]['from_to'] = source
          if 'from_to' in infos[i][INFO_ATTR] and infos[i][INFO_ATTR]['from_to'] is None:
              del infos[i][INFO_ATTR]['from_to']
  
          # convenience function so unnecessary messages can get ignored easily
          infos[i][INFO_ATTR]['will_be_echod'] = False
          if verb in ['pubmsg', 'pubnotice', 'privmsg', 'privnotice']:
              if dir == 'out' and 'echo-message' in server.capabilities.enabled:
                  infos[i][INFO_ATTR]['will_be_echod'] = True
  
          if 'from_to' in infos[i][INFO_ATTR] and infos[i][INFO_ATTR]['from_to'].is_server:
              del infos[i][INFO_ATTR]['from_to']
  
      return infos