slack_token = string
        trigger_string = string
        input_valid_users = set
        input_valid_commands = set

        When the trigger_string prefixes the message text, yields a dictionary
        of::

            {
                'message_data': m_data,
                'cmdline': cmdline_list, # this is a list
                'channel': channel,
                'user': m_data['user'],
                'slack_client': sc
            }

        else yields {'message_data': m_data} and the caller can handle that

        When encountering an error (e.g. invalid message), yields {}, the caller can proceed to the next message

        When the websocket being read from has given up all its messages, yields {'done': True} to
        indicate that the caller has read all of the relevant data for now, and should continue
        its own processing and check back for more data later.

        This relies on the caller sleeping between checks, otherwise this could flood

def generate_triggered_messages(self, token, trigger_string, groups, groups_pillar_name):
        '''
        slack_token = string
        trigger_string = string
        input_valid_users = set
        input_valid_commands = set

        When the trigger_string prefixes the message text, yields a dictionary
        of::

            {
                'message_data': m_data,
                'cmdline': cmdline_list, # this is a list
                'channel': channel,
                'user': m_data['user'],
                'slack_client': sc
            }

        else yields {'message_data': m_data} and the caller can handle that

        When encountering an error (e.g. invalid message), yields {}, the caller can proceed to the next message

        When the websocket being read from has given up all its messages, yields {'done': True} to
        indicate that the caller has read all of the relevant data for now, and should continue
        its own processing and check back for more data later.

        This relies on the caller sleeping between checks, otherwise this could flood
        '''
        all_slack_users = self.get_slack_users(token)  # re-checks this if we have an negative lookup result
        all_slack_channels = self.get_slack_channels(token)  # re-checks this if we have an negative lookup result

        def just_data(m_data):
            '''Always try to return the user and channel anyway'''
            if 'user' not in m_data:
                if 'message' in m_data and 'user' in m_data['message']:
                    log.debug('Message was edited, '
                              'so we look for user in '
                              'the original message.')
                    user_id = m_data['message']['user']
                elif 'comment' in m_data and 'user' in m_data['comment']:
                    log.debug('Comment was added, '
                              'so we look for user in '
                              'the comment.')
                    user_id = m_data['comment']['user']
            else:
                user_id = m_data.get('user')
            channel_id = m_data.get('channel')
            if channel_id.startswith('D'):  # private chate with bot user
                channel_name = 'private chat'
            else:
                channel_name = all_slack_channels.get(channel_id)
            data = {
                'message_data': m_data,
                'user_id': user_id,
                'user_name': all_slack_users.get(user_id),
                'channel_name': channel_name
            }
            if not data['user_name']:
                all_slack_users.clear()
                all_slack_users.update(self.get_slack_users(token))
                data['user_name'] = all_slack_users.get(user_id)
            if not data['channel_name']:
                all_slack_channels.clear()
                all_slack_channels.update(self.get_slack_channels(token))
                data['channel_name'] = all_slack_channels.get(channel_id)
            return data

        for sleeps in (5, 10, 30, 60):
            if self.slack_connect:
                break
            else:
                # see https://api.slack.com/docs/rate-limits
                log.warning('Slack connection is invalid. Server: %s, sleeping %s', self.sc.server, sleeps)
                time.sleep(sleeps)  # respawning too fast makes the slack API unhappy about the next reconnection
        else:
            raise UserWarning('Connection to slack is still invalid, giving up: {}'.format(self.slack_connect))  # Boom!
        while True:
            msg = self.sc.rtm_read()
            for m_data in msg:
                try:
                    msg_text = self.message_text(m_data)
                except (ValueError, TypeError) as msg_err:
                    log.debug('Got an error from trying to get the message text %s', msg_err)
                    yield {'message_data': m_data}  # Not a message type from the API?
                    continue

                # Find the channel object from the channel name
                channel = self.sc.server.channels.find(m_data['channel'])
                data = just_data(m_data)
                if msg_text.startswith(trigger_string):
                    loaded_groups = self.get_config_groups(groups, groups_pillar_name)
                    if not data.get('user_name'):
                        log.error(
                            'The user %s can not be looked up via slack. What has happened here?',
                            m_data.get('user')
                        )
                        channel.send_message('The user {} can not be looked up via slack.  Not running {}'.format(
                            data['user_id'], msg_text))
                        yield {'message_data': m_data}
                        continue
                    (allowed, target, cmdline) = self.control_message_target(
                            data['user_name'], msg_text, loaded_groups, trigger_string)
                    log.debug('Got target: %s, cmdline: %s', target, cmdline)
                    if allowed:
                        yield {
                            'message_data': m_data,
                            'channel': m_data['channel'],
                            'user': data['user_id'],
                            'user_name': data['user_name'],
                            'cmdline': cmdline,
                            'target': target
                        }
                        continue
                    else:
                        channel.send_message('{0} is not allowed to use command {1}.'.format(
                            data['user_name'], cmdline))
                        yield data
                        continue
                else:
                    yield data
                    continue
            yield {'done': True}