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.

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