class RtmEventHandler(object):

    bold_pattern = re.compile(
        "(((?<!.)| )\*(?=\S)(?!\*).+?(?<!\*)(?<=\S)\*( |(?!.)))"
        # btw this doesn't work exactly properly
        # if you want to fix it, don't use a regex
        # this is a commit to revive zac
    )

    def __init__(self, slack_clients, msg_writer, markov_chain):
        self.clients = slack_clients
        self.msg_writer = msg_writer
        self.game_manager = GameManager(self.msg_writer)
        self.user_manager = UserManager(self.clients, self.msg_writer)
        self.tictactoe_manager = TicTacToeManager(
            self.msg_writer, self.user_manager, self.game_manager
        )
        self.response_master = Response_master(self.msg_writer)
        self.user_manager = UserManager(self.clients, self.msg_writer)
        self.rude_manager = RudeManager(self.msg_writer)
        self.channel_manager = ChannelManager(slack_clients)

        self.markov_chain = markov_chain
        markov_files = ['hpOne.txt', 'lotrOne.txt', 'memoriesOfIce.txt']
        self.lotrMarkov = Markov(2, msg_writer, markov_files)

    def handle(self, event):
        if 'type' in event:
            self._handle_by_type(event['type'], event)

    def _handle_by_type(self, event_type, event):
        # See https://api.slack.com/rtm for a full list of events
        if event_type == 'error':
            self.msg_writer.write_error(json.dumps(event), event['channel'])
        elif event_type == 'message':
            self._handle_message(event)
        elif event_type == 'channel_joined':
            # you joined a channel
            self.msg_writer.write_help_message(event['channel']['id'])
        elif event_type == 'group_joined':
            # you joined a private group
            self.msg_writer.write_help_message(event['channel'])
        elif (
            event_type == 'reaction_added' and 'user' in event and
            not self.clients.is_message_from_me(event)
        ):
            if 'channel' in event['item']:
                msg = event['item']
                self.response_master.process_reaction(
                    event['reaction'], msg['channel'], msg['ts']
                )
        else:
            pass

    def _is_edited_with_star(self, message):
        return '*' in re.sub(self.bold_pattern, '', message)

    def _is_edited_by_user(self, event):
        if 'subtype' in event:
            if event['subtype'] == 'message_changed':
                if 'message' in event:
                    event_msg = event['message']

                    # Dont allow zac to spam his own message edits
                    if self.clients.is_message_from_me(event_msg):
                        return False

                    if (
                        'user' in event_msg and 'edited' in event_msg and
                        'user' in event_msg['edited'] and
                        not is_bot_message(event_msg['edited'])
                    ):
                        user1 = event_msg['user']
                        user2 = event_msg['edited']['user']
                        return user1 == user2
        return False

    def _handle_message(self, event):
        if 'subtype' in event:
            # if self._is_edited_by_user(event):
            #     self.msg_writer.write_spelling_mistake(
            #         event['channel'], event['message']['ts']
            #     )
            if (
                event['subtype'] == 'channel_join' and
                not self.clients.is_message_from_me(event)
            ):
                self.msg_writer.write_joined_channel(
                    event['channel'], event['user']
                )
            elif event['subtype'] == 'message_deleted':
                self.msg_writer.write_message_deleted(event['channel'])
            elif event['subtype'] == 'channel_leave':
                self.msg_writer.write_left_channel(event['channel'])

        # Filter out messages from the bot itself
        if 'user' in event and not self.clients.is_message_from_me(event):
            logging.info("Handling event")

            msg_txt = event['text']
            channel_id = event['channel']
            user_id = event['user']
            ts = event['ts']

            user_name = self.user_manager.get_user_by_id(user_id)
            lower_txt = msg_txt.lower()

            # Markov chain addition and response
            if should_add_markov(event):
                self.markov_chain.add_single_line(msg_txt)
            if (
                channel_id == self.channel_manager.get_channel_id('markov') or
                lower_txt == 'markov'
            ):
                self.msg_writer.send_message(str(self.lotrMarkov), channel_id)

            # Respond to messages handled by rude_manager and response_manager
            self.rude_manager.run(channel_id, user_id)
            self.response_master.process_message(
                msg_txt, channel_id, user_id, ts
            )

            # Command line
            # logging.info("lower_txt: "+str(lower_txt.split()))
            try:
                token = lower_txt.split()[0]
                if token == '#>' or token == u'#&gt;':
                    # logging.info("entering terminal command mode")
                    self.msg_writer.write_terminal_command(
                        lower_txt, channel_id
                    )
                    return 0  # Do not continue execution
            except:
                pass

            # Return channel and user information
            if lower_txt == 'channelinfo':
                self.msg_writer.send_message(channel_id, channel_id)
            elif lower_txt == 'channelname':
                self.msg_writer.send_message(
                    self.channel_manager.get_channel_by_id(channel_id),
                    channel_id
                )
            elif lower_txt == 'userinfo':
                self.msg_writer.send_message(user_id, channel_id)
            elif lower_txt == 'allusersinfo':
                self.user_manager.print_all_users(channel_id)
            elif lower_txt == 'allchannelinfo':
                self.msg_writer.send_message(
                    str(self.channel_manager.get_all_channel_ids()),
                    channel_id
                )
            elif lower_txt == 'allchannelname':
                self.msg_writer.send_message(
                    str(self.channel_manager.get_all_channel_names()),
                    channel_id
                )
            elif lower_txt == 'ayy':
                self.msg_writer.write_lmao(channel_id)

            # Loud addition and response
            if should_add_loud(event):
                self.msg_writer.write_loud(msg_txt)
                self.msg_writer.respond_loud(msg_txt, channel_id)
            # if self._is_edited_with_star(msg_txt):
            #     self.msg_writer.write_spelling_mistake(channel_id, ts)

            # Respond to message text
            if re.search('i choose you', lower_txt):
                self.msg_writer.write_cast_pokemon(lower_txt, channel_id)
            if re.search('weather', lower_txt):
                self.msg_writer.write_weather(channel_id)
            if re.search('riri', lower_txt):
                self.msg_writer.write_riri_me(msg_txt, channel_id)
            if 'xkcd' in lower_txt:
                self.msg_writer.write_xkcd(lower_txt, channel_id)
            if (
                'tictactoe' in lower_txt or ' ttt' in lower_txt or
                lower_txt.startswith('ttt')
            ):
                self.tictactoe_manager.get_message(
                    channel_id, lower_txt, user_name
                )

            # Respond to message text with `zac` included
            if is_zac_mention(msg_txt) or self.clients.is_bot_mention(msg_txt):
                if re.search('erase|delete', lower_txt):
                    self.msg_writer.erase_history(
                        msg_txt, channel_id, ts
                    )
                if 'help' in lower_txt:
                    self.msg_writer.write_help_message(channel_id)
                if 'joke' in lower_txt:
                    self.msg_writer.write_joke(channel_id)
                if 'french' in lower_txt:
                    self.msg_writer.write_french(msg_txt, channel_id)
                if re.search('who\'?s that pokemon', msg_txt):
                    self.msg_writer.write_whos_that_pokemon(channel_id)
                if re.search(' ?zac it\'?s', lower_txt):
                    self.msg_writer.write_pokemon_guessed_response(
                        msg_txt, channel_id, user_id
                    )
                if re.search('attachment|beep boop link', lower_txt):
                    self.msg_writer.demo_attachment(channel_id)
                if 'sad ' in lower_txt:
                    self.msg_writer.write_sad(channel_id)
                if 'sort me' in lower_txt:
                    self.msg_writer.write_hogwarts_house(
                        msg_txt, channel_id, user_id
                    )
                if re.search('encourage ', lower_txt):
                    self.msg_writer.write_encouragement(msg_txt, channel_id)
                if 'sass ' in lower_txt:
                    self.msg_writer.write_sass(msg_txt, channel_id)
                if 'solve' in lower_txt:
                    self.msg_writer.write_solution(msg_txt, channel_id)
                if re.search('explain|why', lower_txt):
                    self.msg_writer.write_explanation(channel_id)
                if re.search('sweetpotato|sweet potato', lower_txt):
                    self.msg_writer.write_sweetpotato_me(msg_txt, channel_id)
                if re.search('draw me', lower_txt):
                    self.msg_writer.write_draw_me(channel_id)
                if re.search('love|forever|relationship', lower_txt):
                    self.msg_writer.write_forever(channel_id)
                if re.search('unflip', lower_txt):
                    self.msg_writer.write_unflip(channel_id)
                elif re.search('flip|rageflip', lower_txt):
                    self.msg_writer.write_flip(channel_id)
                if re.search('sup son', lower_txt):
                    self.msg_writer.write_sup_son(channel_id)
                if lower_txt == "zac":
                    self.msg_writer.write_prompt(channel_id)
                # The word google must be specified in lower case
                if 'zac google ' in msg_txt:
                    self.msg_writer.google(msg_txt, channel_id)
                if msg_txt.startswith('zac add news '):
                    user_name = self.user_manager.get_user_by_id(user_id)
                    self.msg_writer.link_945(msg_txt, channel_id, user_name)
                if re.search('open the pod bay doors', lower_txt):
                    self.msg_writer.write_hal(channel_id, user_name)
                if re.search('hackernews', lower_txt):
                    self.msg_writer.write_hackernews(channel_id)
                else:
                    # self.msg_writer.write_prompt(channel_id)
                    pass
class TimeTriggeredEventManager(object):

    def __init__(self, clients, msg_writer, markov_chain):
        self.clients = clients
        self.msg_writer = msg_writer
        self.markov_chain = markov_chain
        self.channel_manager = ChannelManager(clients)
        self.trigger_startup_log()
        self.process_recent_messages()

    def send_message(self, msg_txt, channel=None):
        self.msg_writer.send_message(msg_txt, channel)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def clean_channels_history(self):
        result = 'Erased messages: '
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                count = self._erase_channel_messages(channel_id, log_days=3)
                if count != 0:
                    result += '<#{}> ({}), '.format(
                        str(channel_id), str(count)
                    )
        if result.endswith(', '):
            result = result[:-2]
        else:
            result = 'No messages erased during general clean up'
        self.send_message(result)

    def clean_testing_channel_history(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        total_count = 0
        for num in range(10):
            count = self._erase_channel_messages(testing_channel, log_days=1)
            total_count += count
            if count == 0:
                break
        result = 'Erased {} total messages from <#{}> in {} passes'.format(
            str(total_count), str(testing_channel), str(num)
        )
        self.send_message(result)

    def _erase_channel_messages(self, channel_id, log_days=0):
        count = 0
        now_ts = float(time.time())
        response = self.clients.get_message_history(channel_id)
        if 'messages' in response:
            for message in response['messages']:
                if (
                    'ts' in message and 'pinned_to' not in message and
                    self.clients.is_message_from_me(message)
                ):
                    # Delete everything older than `log_days` old
                    # Delete items older than a day old
                    # Unless they are weather posts or startup logs
                    # Always delete the message deletions messages
                    if (
                        (now_ts - (60*60*24*log_days)) > float(message['ts'])
                        or (
                            (now_ts - (60*60*24)) > float(message['ts'])
                            and not re.search(
                                DONT_DELETE, message['text'].lower()
                            )
                            or (
                                re.search(
                                    "I SAW THAT! _Someone_ deleted a message",
                                    message['text']
                                )
                            )
                        )
                    ):
                        self.clients.delete_message(channel_id, message['ts'])
                        count += 1
        return count

    def process_recent_messages(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        count_markov = 0
        count_louds = 0
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                response = self.clients.get_message_history(channel_id)
                if 'messages' in response:
                    for message in response['messages']:
                        if not self.clients.is_message_from_me(message):
                            if 'text' in message:
                                msg_text = message['text']

                                # Add markovs
                                if should_add_markov(message):
                                    self.markov_chain.add_single_line(msg_text)
                                    count_markov += 1

                                # Add louds
                                if should_add_loud(message):
                                    self.msg_writer.write_loud(msg_text)
                                    count_louds += 1
        result = (
            "Added " + str(count_markov) + " messages to markov "
            "and " + str(count_louds) + " loud messages"
        )
        self.send_message(result)

    def trigger_random_markov(self, channel):
        if random.random() < 0.10:
            channel_id = self.channel_manager.get_channel_id(channel)
            now_timestamp = float(time.time())
            response = self.clients.get_message_history(channel_id, 1)
            if 'messages' in response:
                for message in response['messages']:
                    if (
                        'user' in message and 'ts' in message and not
                        self.clients.is_message_from_me(message) and
                        not contains_tag(message['text']) and
                        'markov' not in message['text']
                    ):
                        # Post only 3 - 5 minutes after latest message
                        if (
                            now_timestamp - (60*5) <= float(message['ts']) and
                            now_timestamp - (60*2) >= float(message['ts'])
                        ):
                            try:
                                txt = str(self.markov_chain)
                                self.send_message(txt, channel)
                                self.trigger_method_log('random markov')
                            except Exception:
                                err_msg = traceback.format_exc()
                                logging.error(
                                    'Unexpected error: {}'.format(err_msg)
                                )
                                self.msg_writer.write_error(err_msg)
                                pass

    def trigger_ping(self, day, hour, minute, second):
        msg = ('Ping on ' + day + ' ' + str(hour) + ':' + str(minute) +
               ':' + str(second) + ' :' + str(self.get_emoji()) + ':')
        self.send_message(msg)

    def trigger_method_log(self, method_name):
        msg = 'Event: {}'.format(method_name)
        self.send_message(msg)

    def trigger_startup_log(self):
        day, hour, minute, second = _get_datetime()
        msg = ('I came back to life on ' + day + ' ' + str(hour) + ':' +
               str(minute) + ':' + str(second) + ' :' + str(self.get_emoji()) +
               ':')
        self.send_message(msg)

    def trigger_weather(self):
        response = weather_manager.getCurrentWeather()
        self.send_message(response)

    def trigger_tuesday(self):
        txt = "DON'T FORGET IT'S TUESDAY _ALLL_ DAY TODAY"
        self.msg_writer.send_message_as_other(
            txt, 'random', 'zac', ':rolled_up_newspaper:'
        )

    def trigger_timed_event(self):
        day, hour, minute, second = _get_datetime()

        # leaves 10-ish seconds to trigger since method is called every 10-ish
        # seconds and we want the if statement to trigger once per min only
        if(second >= 5 and second <= 16):
            try:
                # self.trigger_ping(day, hour, minute, second)
                if hour == 1:
                    if minute == 15:
                        self.clean_channels_history()
                    if minute == 0 or minute == 30:
                        self.clean_testing_channel_history()
                if hour % 3 == 0 and minute == 0:
                    self.trigger_weather()
                if hour >= 9 and hour <= 16:
                    self.trigger_random_markov('random')
                    self.trigger_random_markov('work')
                if day == 'Tuesday' and hour == 14 and minute == 43:
                    self.trigger_tuesday()
            except Exception as e:
                self.msg_writer.write_error(e)
Esempio n. 3
0
class Messenger(object):
    def __init__(self, slack_clients):
        self.clients = slack_clients
        self.loud_manager = LoudManager()
        self.whos_that_pokemon_manager = WhosThatPokemonManager()
        self.equation_manager = EquationManager()
        self.explanation_manager = ResourceManager('explanations.txt')
        self.help_manager = ResourceManager('help_text.txt')
        self.sass_manager = ResourceManager('sass.txt')
        self.channel_manager = ChannelManager(slack_clients)

    def erase_history(self, msg_text, channel_id, now_timestamp):
        try:
            tokens = re.findall('[0-9]+', msg_text)
            delete_num = int(tokens[0])
            count = 0
            response = self.clients.get_message_history(channel_id)
            if 'messages' in response:
                for message in response['messages']:
                    if (
                        'ts' in message and 'pinned_to' not in message and
                        self.clients.is_message_from_me(message) and
                        not re.search(DONT_DELETE, message['text'].lower())
                    ):
                        response = self.clients.delete_message(
                            channel_id, message['ts']
                        )
                        count += 1
                        if count >= delete_num:
                            break
            if count < delete_num:
                msg = ("Erased " + str(count) + " messages: I "
                       "can only see the 100 most recent messages")
                self.send_message(msg, channel_id)
        except Exception:
            msg = "Correct usage is `zac erase <num>`"
            self.send_message(msg, channel_id)
            pass

    def __del__(self):
        closing_msgs = ["No!! Don't kill me! I want to live!", "Good BYEEE!!!",
                        "I'm dying again :sob:",
                        "Have you gotten tired of this face :zacefron: ?"]
        txt = random.choice(closing_msgs)
        self.send_message(txt)

    def send_slow_message_as_other(self, msg_text, channel, username, emoji):
        self.send_message_as_other(
            msg_text, channel, username, emoji, slow=True
        )

    def send_message_as_other(
        self, msg_text, channel, username, emoji, slow=False
    ):
        msg_text = msg_text.replace('&', "&amp;")
        channel = self.channel_manager.get_channel_id(channel)
        if (slow):
            self.clients.send_user_typing_pause(channel)

        return self.clients.send_message_as_other(
            msg_text, channel, username, emoji
        )

    def write_slow(self, msg_text, channel=None):
        return self.send_message(msg_text, channel, slow=True)

    def send_message(
        self, msg_text, channel=None, slow=False, react_emoji=None
    ):
        msg_text = msg_text.replace('&', "&amp;")
        msg_text = msg_text.replace('&quot;', "\"")

        # channel = self.channel_manager.get_channel_id(channel)

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.send_message(msg_text, channel)
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])

        return response

    def update_message(
        self, updated_msg_text, ts, channel=None, slow=False, react_emoji=None
    ):
        updated_msg_text = updated_msg_text.replace('&', "&amp;")

        channel = self.channel_manager.get_channel_id(channel)

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.update_message(
            updated_msg_text, channel, ts
        )
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])
        return response

    def send_attachment(self, txt, channel_id, attachment):
        self.clients.send_attachment(txt, channel_id, attachment)

    def write_error(self, err_msg, channel_id=None):
        txt = (":face_with_head_bandage: my maker didn't handle this error "
               "very well:\n>```{}```").format(err_msg)
        self.send_message(txt, channel_id)

    def send_reaction(self, emoji_name, channel_id, timestamp):
        self.clients.send_reaction(emoji_name, channel_id, timestamp)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def write_message_deleted(self, channel_id):
        # Dont post if messages were deleted inside of #zac-testing
        if channel_id != self.channel_manager.get_channel_id('zac-testing'):
            txt = ("I SAW THAT! _Someone_ deleted a message from channel: "
                   "<#{}>").format(channel_id)
            self.send_message(txt)

    def write_left_channel(self, channel_id):
        self.send_message('...well THAT was something', channel_id)

    def write_joined_channel(self, channel_id, user_id):
        if channel_id == self.channel_manager.get_channel_id('zac-testing'):
            txt = ("Hey <@{}>! Welcome to the Testing (aka the Weather) "
                   "channel. Please MUTE this channel or be inundaded with "
                   "notifications!").format(user_id)
            self.write_slow(txt, channel_id)
        else:
            self.write_greeting(channel_id, user_id)

    def write_help_message(self, channel_id):
        help_txt = self.help_manager.get_all()
        count = self.help_manager.get_count()
        txt = (
            "I'm Zac.  I'll *_respond_* to the following {} commands:\n{}"
        ).format(count-1, help_txt)
        info_text = (
            "Hi, I'm Zac Efron. You can find out more information about me "
            "in #zac-testing. Merci :zacefron:"
        )
        self.write_slow(info_text, channel_id)
        self.write_slow(txt, 'zac-testing')

    def write_french(self, msg_text, channel_id):
        target = get_target(FRENCH_FLAG, msg_text).replace('_', '')
        self.write_slow('_le {}_'.format(target), channel_id)

    def write_greeting(self, channel_id, user_id):
        greetings = ['Hi', 'Hello', 'Nice to meet you', 'Howdy', 'Salutations']
        txt = '{}, <@{}>!'.format(random.choice(greetings), user_id)
        self.write_slow(txt, channel_id)

    def write_spelling_mistake(self, channel_id, timestamp):
        emoji_name = "spelft_it_wronbg_again_i_see"
        self.send_reaction(emoji_name, channel_id, timestamp)

    def write_prompt(self, channel_id):
        bot_uid = self.clients.bot_user_id()
        txt = ("I'm sorry, I didn't quite understand... Can I help you? "
               "(e.g. `<@" + bot_uid + "> help`)")
        self.write_slow(txt, channel_id)

    def write_cast_pokemon(self, lower_msg_text, channel_id):
        pkmn = pokemon_i_choose_you(lower_msg_text)
        if pkmn is not None:
            self.send_message(pkmn, channel_id)

    def write_whos_that_pokemon(self, channel_id):
        txt = self.whos_that_pokemon_manager.whos_that_pkmn()
        self.send_message(txt, channel_id)

    def write_pokemon_guessed_response(self, msg_text, channel_id, user_id):
        text = self.whos_that_pokemon_manager.check_response(user_id, msg_text)
        if text is not None:
            self.send_message(text, channel_id)

    def write_weather(self, channel_id):
        self.write_slow(weather_manager.getCurrentWeather(), channel_id)

    def write_loud(self, orig_msg):
        if not is_zac_mention(orig_msg):
            self.loud_manager.write_loud_to_file(orig_msg)

    def respond_loud(self, orig_msg, channel_id):
        if is_zac_mention(orig_msg) or random.random() < 0.8:
            message = self.loud_manager.get_random_loud();
            
            if message:
                self.send_message(self.loud_manager.get_random_loud(), channel_id)

    def write_explanation(self, channel_id):
        self.write_slow(self.explanation_manager.get_response(), channel_id)

    def write_sass(self, msg_txt, channel_id):
        target = get_target(SASS_FLAG, msg_txt)
        sass = 'Hey, {}! {}'.format(target, self.sass_manager.get_response())
        self.write_slow(sass, channel_id)

    def write_solution(self, msg_text, channel_id):
        self.write_slow(self.equation_manager.solve(msg_text), channel_id)

    def write_sweetpotato_me(self, msg_text, channel_id):
        target = get_target(SWEETPOTATO_FLAG, msg_text)
        txt = 'Here, {}! :sweet_potato:'.format(target)
        self.write_slow(txt, channel_id)

    def write_riri_me(self, msg_text, channel_id):
        target = get_target(RIRI_FLAG, msg_text).upper()
        if target != "":
            txt = ' '.join(target for num in range(5))
        else:
            txt = "WHY WOULD YOU JUST TYPE RIRI?\n"
        self.write_slow(txt, channel_id)

    def write_terminal_command(self, lower_msg_text, channel_id):
        response = terminal_manager.run_terminal_command(lower_msg_text)
        self.send_message(response, channel_id)

    def google(self, msg_text, channel_id):
        try:
            tokens = re.split(GOOGLE_FLAG, msg_text)
            query = ""
            for num in range(len(tokens)):
                if num > 0:
                    query += "+" + tokens[num].strip()
            if len(query) > 1:
                query = query[1:]

            url = (
                "https://www.googleapis.com/customsearch/v1?key=AIzaSyDs8-"
                "StJem2hqcu2L-J4PceBPVx5Jk6txk&cx=004179257401026011423:y4"
                "vp3hnjgvo&q="
            ) + query

            response = requests.get(url).json()
            result = response['items'][0]['link']

            self.send_message(result, channel_id)
        except Exception as e:
            self.write_error(str(e))
Esempio n. 4
0
class Messenger(object):
    def __init__(self, slack_clients):
        self.clients = slack_clients
        self.loud_manager = LoudManager()
        self.whos_that_pokemon_manager = WhosThatPokemonManager()
        self.hogwarts_house_sorter = HogwartsHouseSorter()
        self.equation_manager = EquationManager()
        self.explanation_manager = ResourceManager('explanations.txt')
        self.drawing_manager = ResourceManager('draw_me.txt')
        self.forever_manager = ResourceManager('forever.txt')
        self.help_manager = ResourceManager('help_text.txt')
        self.sass_manager = ResourceManager('sass.txt')
        self.channel_manager = ChannelManager(slack_clients)

    def erase_history(self, msg_text, channel_id, now_timestamp):
        try:
            tokens = re.findall('[0-9]+', msg_text)
            delete_num = int(tokens[0])
            count = 0
            response = self.clients.get_message_history(channel_id)
            if 'messages' in response:
                for message in response['messages']:
                    if ('ts' in message and 'pinned_to' not in message
                            and self.clients.is_message_from_me(message)
                            and not re.search(DONT_DELETE,
                                              message['text'].lower())):
                        response = self.clients.delete_message(
                            channel_id, message['ts'])
                        count += 1
                        if count >= delete_num:
                            break
            if count < delete_num:
                msg = ("Erased " + str(count) + " messages: I "
                       "can only see the 100 most recent messages")
                self.send_message(msg, channel_id)
        except Exception:
            msg = "Correct usage is `bolton erase <num>`"
            self.send_message(msg, channel_id)
            pass

    def __del__(self):
        closing_msgs = [
            "No!! Don't kill me! I want to live!", "Good BYEEE!!!",
            "I'm dying again :sob:",
            "Have you gotten tired of this face :boltonefron: ?"
        ]
        txt = random.choice(closing_msgs)
        self.send_message(txt)

    def send_slow_message_as_other(self, msg_text, channel, username, emoji):
        self.clients.send_user_typing_pause(channel)
        self.send_message_as_other(msg_text, channel, username, emoji)

    def send_message_as_other(self, msg_text, channel, username, emoji):
        msg_text = msg_text.replace('&', "&amp;")
        # msg = msg.replace('<', "&lt;")
        # msg = msg.replace('>', "&gt;")
        # msg = msg.decode("utf8", "ignore")

        return self.clients.send_message_as_other(msg_text, channel, username,
                                                  emoji)

    def write_slow(self, msg_text, channel=None):
        return self.send_message(msg_text, channel, slow=True)

    def send_message(self,
                     msg_text,
                     channel=None,
                     slow=False,
                     react_emoji=None):
        msg_text = msg_text.replace('&', "&amp;")
        # msg = msg.replace('<', "&lt;")
        # msg = msg.replace('>', "&gt;")
        # msg = msg.decode("utf8", "ignore")

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.send_message(msg_text, channel)
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])
        return response

    def update_message(self,
                       updated_msg_text,
                       ts,
                       channel=None,
                       slow=False,
                       react_emoji=None):
        updated_msg_text = updated_msg_text.replace('&', "&amp;")

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.update_message(updated_msg_text, channel, ts)
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])
        return response

    def send_attachment(self, txt, channel_id, attachment):
        self.clients.send_attachment(txt, channel_id, attachment)

    def write_error(self, err_msg, channel_id=None):
        txt = (":face_with_head_bandage: my maker didn't handle this error "
               "very well:\n>```{}```").format(err_msg)
        self.send_message(txt, channel_id)

    def send_reaction(self, emoji_name, channel_id, timestamp):
        self.clients.send_reaction(emoji_name, channel_id, timestamp)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def write_message_deleted(self, channel_id):
        # Dont post if messages were deleted inside of #bolton-testing
        if channel_id != self.channel_manager.get_channel_id('bolton-testing'):
            txt = ("I SAW THAT! _Someone_ deleted a message from channel: "
                   "<#{}>").format(channel_id)
            self.send_message(txt)

    def write_left_channel(self, channel_id):
        self.send_message('...well THAT was something', channel_id)

    def write_joined_channel(self, channel_id, user_id):
        if channel_id == self.channel_manager.get_channel_id('bolton-testing'):
            txt = ("Hey <@{}>! Welcome to the Testing (aka the Weather) "
                   "channel. Please MUTE this channel or be inundaded with "
                   "notifications!").format(user_id)
            self.write_slow(txt, channel_id)
            self.write_xkcd(channel_id, "15")
        else:
            self.write_greeting(channel_id, user_id)

    def write_help_message(self, channel_id):
        help_txt = self.help_manager.get_all()
        count = self.help_manager.get_count()
        txt = (
            "I'm bolton.  I'll *_respond_* to the following {} commands:\n{}"
        ).format(count - 1, help_txt)
        self.write_slow(txt, channel_id)

    def write_french(self, msg_text, channel_id):
        target = get_target(FRENCH_FLAG, msg_text).replace('_', '')
        self.write_slow('_le {}_'.format(target), channel_id)

    def write_greeting(self, channel_id, user_id):
        greetings = ['Hi', 'Hello', 'Nice to meet you', 'Howdy', 'Salutations']
        txt = '{}, <@{}>!'.format(random.choice(greetings), user_id)
        self.write_slow(txt, channel_id)

    def write_spelling_mistake(self, channel_id, timestamp):
        emoji_name = "spelft_it_wronbg_again_i_see"
        self.send_reaction(emoji_name, channel_id, timestamp)

    def write_prompt(self, channel_id):
        bot_uid = self.clients.bot_user_id()
        txt = ("I'm sorry, I didn't quite understand... Can I help you? "
               "(e.g. `<@" + bot_uid + "> help`)")
        self.write_slow(txt, channel_id)

    def write_joke(self, channel_id):
        answer = (
            "Wenn ist das Nunstück git und Slotermeyer? Ja! Beiherhund das "
            "Oder die Flipperwaldt gersput!")
        self.write_slow(answer, channel_id)

    def write_encouragement(self, msg_text, channel_id):
        encouragements = [
            "Get your shit together",
            "You can do it",
            "I'm here for you",
            "Do you just think about yourself",
            "You're the best",
        ]
        target = get_target(ENCOURAGE_FLAG, msg_text)
        txt = '{} {}'.format(random.choice(encouragements), target)
        self.write_slow(txt, channel_id)

    def write_cast_pokemon(self, lower_msg_text, channel_id):
        pkmn = pokemon_i_choose_you(lower_msg_text)
        if pkmn is not None:
            self.send_message(pkmn, channel_id)

    def write_whos_that_pokemon(self, channel_id):
        txt = self.whos_that_pokemon_manager.whos_that_pkmn()
        self.send_message(txt, channel_id)

    def write_pokemon_guessed_response(self, msg_text, channel_id, user_id):
        text = self.whos_that_pokemon_manager.check_response(user_id, msg_text)
        if text is not None:
            self.send_message(text, channel_id)

    def write_sad(self, channel_id):
        txt = "I'm crying into my tea. :joy:"
        attachment = {
            "pretext":
            "This always cracks me up. :wink:",
            "title":
            "/giphy bloopin",
            "title_link": ("http://giphy.com/gifs/friday-rebecca-black-hurrr-"
                           "13FsSYo3fzfT2g"),
            "text":
            txt,
            "fallback":
            txt,
            "image_url":
            "http://i.giphy.com/13FsSYo3fzfT2g.gif",
            "color":
            "#7CD197",
        }
        self.send_attachment(txt, channel_id, attachment)

    def demo_attachment(self, channel_id):
        txt = ("Beep Beep Boop is a ridiculously simple hosting platform for "
               "your Slackbots.")
        attachment = {
            "pretext":
            "We bring bots to life. :sunglasses: :thumbsup:",
            "title":
            "Host, deploy and share your bot in seconds.",
            "title_link":
            "https://beepboophq.com/",
            "text":
            txt,
            "fallback":
            txt,
            "image_url": ("https://storage.googleapis.com/beepboophq/_assets/"
                          "bot-1.22f6fb.png"),
            "color":
            "#7CD197",
        }
        self.send_attachment(txt, channel_id, attachment)

    def write_weather(self, channel_id):
        self.write_slow(weather_manager.getCurrentWeather(), channel_id)

    def write_loud(self, orig_msg):
        if not is_bolton_mention(orig_msg):
            self.loud_manager.write_loud_to_file(orig_msg)

    def respond_loud(self, orig_msg, channel_id):
        if is_bolton_mention(orig_msg) or random.random() < 0.25:
            self.send_message(self.loud_manager.get_random_loud(), channel_id)

    def write_hogwarts_house(self, msg_text, channel_id, user_id):
        response = self.hogwarts_house_sorter.sort_into_house(msg_text)
        self.write_slow('<@{}>: {}'.format(user_id, response), channel_id)

    def write_explanation(self, channel_id):
        self.write_slow(self.explanation_manager.get_response(), channel_id)

    def write_sass(self, msg_txt, channel_id):
        target = get_target(SASS_FLAG, msg_txt)
        sass = 'Hey, {}! {}'.format(target, self.sass_manager.get_response())
        self.write_slow(sass, channel_id)

    def write_solution(self, msg_text, channel_id):
        self.write_slow(self.equation_manager.solve(msg_text), channel_id)

    def write_sweetpotato_me(self, msg_text, channel_id):
        target = get_target(SWEETPOTATO_FLAG, msg_text)
        txt = 'Here, {}! :sweet_potato:'.format(target)
        self.write_slow(txt, channel_id)

    def write_draw_me(self, channel_id):
        self.write_slow(self.drawing_manager.get_response(), channel_id)

    def write_forever(self, channel_id):
        original_msg = self.forever_manager.get_response()
        response = self.write_slow(original_msg, channel_id)
        new_msg = '~{}~ Just kidding! :laughing:'.format(original_msg.strip())
        self.update_message(new_msg,
                            response['ts'],
                            channel_id,
                            slow=True,
                            react_emoji='trollface')

    def write_flip(self, channel_id):
        self.send_message(u"(╯°□°)╯︵ ┻━┻", channel_id)

    def write_unflip(self, channel_id):
        self.send_message(u"┬─┬ノ( º _ ºノ)", channel_id)

    def write_sup_son(self, channel_id):
        self.send_message(u"¯\_(ツ)_/¯", channel_id)

    def write_riri_me(self, msg_text, channel_id):
        target = get_target(RIRI_FLAG, msg_text).upper()
        if target != "":
            txt = ' '.join(target for num in range(5))
        else:
            txt = "WHY WOULD YOU JUST TYPE RIRI?\n"
        self.write_slow(txt, channel_id)

    def write_xkcd(self, lower_msg_text, channel_id):
        requestedComic = lower_msg_text[lower_msg_text.find('xkcd') + 4:]
        txt = xkcd_manager.getImageLocation(requestedComic)
        self.write_slow(txt, channel_id)

    def write_terminal_command(self, lower_msg_text, channel_id):
        logger.info("In Messenger for terminal_manager")
        response = terminal_manager.run_terminal_command(lower_msg_text)
        self.send_message(response, channel_id)
Esempio n. 5
0
class RtmEventHandler(object):

    bold_pattern = re.compile(
        "(((?<!.)| )\*(?=\S)(?!\*).+?(?<!\*)(?<=\S)\*( |(?!.)))"
    )

    def __init__(self, slack_clients, msg_writer, markov_chain):
        self.clients = slack_clients
        self.msg_writer = msg_writer
        self.game_manager = GameManager(self.msg_writer)
        self.user_manager = UserManager(self.clients, self.msg_writer)
        self.tictactoe_manager = TicTacToeManager(
            self.msg_writer, self.user_manager, self.game_manager
        )
        self.response_master = Response_master(self.msg_writer)
        self.user_manager = UserManager(self.clients, self.msg_writer)
        self.rude_manager = RudeManager(self.msg_writer)
        self.channel_manager = ChannelManager(slack_clients)

        self.markov_chain = markov_chain
        markov_files = ['hpOne.txt', 'lotrOne.txt', 'memoriesOfIce.txt']
        self.lotrMarkov = Markov(2, msg_writer, markov_files)

    def handle(self, event):
        if 'type' in event:
            self._handle_by_type(event['type'], event)

    def _handle_by_type(self, event_type, event):
        # See https://api.slack.com/rtm for a full list of events
        if event_type == 'error':
            self.msg_writer.write_error(json.dumps(event), event['channel'])
        elif event_type == 'message':
            self._handle_message(event)
        elif event_type == 'channel_joined':
            # you joined a channel
            self.msg_writer.write_help_message(event['channel']['id'])
        elif event_type == 'group_joined':
            # you joined a private group
            self.msg_writer.write_help_message(event['channel'])
        elif (
            event_type == 'reaction_added' and 'user' in event
            and not self.clients.is_message_from_me(event)
        ):
            if 'channel' in event['item']:
                msg = event['item']
                self.response_master.process_reaction(
                    event['reaction'], msg['channel'], msg['ts']
                )
        else:
            pass

    def _is_edited_with_star(self, message):
        return '*' in re.sub(self.bold_pattern, '', message)

    def _is_edited_by_user(self, event):
        if 'subtype' in event:
            if event['subtype'] == 'message_changed':
                if 'message' in event:
                    event_msg = event['message']

                    # Dont allow bolton to spam his own message edits
                    if self.clients.is_message_from_me(event_msg):
                        return False

                    if (
                        'user' in event_msg and 'edited' in event_msg and
                        'user' in event_msg['edited'] and
                        not is_bot_message(event_msg['edited'])
                    ):
                        user1 = event_msg['user']
                        user2 = event_msg['edited']['user']
                        return user1 == user2
        return False

    def _handle_message(self, event):
        if 'subtype' in event:
            if self._is_edited_by_user(event):
                self.msg_writer.write_spelling_mistake(
                    event['channel'], event['message']['ts']
                )
            elif (
                event['subtype'] == 'channel_join' and
                not self.clients.is_message_from_me(event)
            ):
                self.msg_writer.write_joined_channel(
                    event['channel'], event['user']
                )
            elif event['subtype'] == 'message_deleted':
                self.msg_writer.write_message_deleted(event['channel'])
            elif event['subtype'] == 'channel_leave':
                self.msg_writer.write_left_channel(event['channel'])

        # Filter out messages from the bot itself
        if 'user' in event and not self.clients.is_message_from_me(event):
            logging.info("Handling event")

            msg_txt = event['text']
            channel_id = event['channel']
            user_id = event['user']
            ts = event['ts']

            user_name = self.user_manager.get_user_by_id(user_id)
            lower_txt = msg_txt.lower()

            # Markov chain addition and response
            if should_add_markov(event):
                self.markov_chain.add_single_line(msg_txt)
            if (
                channel_id == self.channel_manager.get_channel_id('bolton_weather')
                or lower_txt == 'markov'
            ):
                self.msg_writer.send_message(str(self.lotrMarkov), channel_id)

            # Respond to messages handled by rude_manager and response_manager
            self.rude_manager.run(channel_id, user_id)
            self.response_master.process_message(
                msg_txt, channel_id, user_id, ts
            )

            # Command line
            # logging.info("lower_txt: "+str(lower_txt.split()))
            try:
                token = lower_txt.split()[0]
                if token == '#>' or token == u'#&gt;':
                    # logging.info("entering terminal command mode")
                    self.msg_writer.write_terminal_command(
                        lower_txt, channel_id
                    )
                    return 0  # Do not continue execution
            except:
                pass

            # Return channel and user information
            if lower_txt == 'channelinfo':
                self.msg_writer.send_message(channel_id, channel_id)
            if lower_txt == 'userinfo':
                self.msg_writer.send_message(user_id, channel_id)
            if lower_txt == 'allusersinfo':
                self.user_manager.print_all_users(channel_id)

            # Loud addition and response
            if should_add_loud(event):
                self.msg_writer.write_loud(msg_txt)
                self.msg_writer.respond_loud(msg_txt, channel_id)
            if self._is_edited_with_star(msg_txt):
                self.msg_writer.write_spelling_mistake(channel_id, ts)

            # Respond to message text
            if re.search('i choose you', lower_txt):
                self.msg_writer.write_cast_pokemon(lower_txt, channel_id)
            if re.search('weather', lower_txt):
                self.msg_writer.write_weather(channel_id)
            if re.search('riri', lower_txt):
                self.msg_writer.write_riri_me(msg_txt, channel_id)
            if 'xkcd' in lower_txt:
                self.msg_writer.write_xkcd(lower_txt, channel_id)
            if (
                'tictactoe' in lower_txt or ' ttt' in lower_txt or
                lower_txt.startswith('ttt')
            ):
                self.tictactoe_manager.get_message(
                    channel_id, lower_txt, user_name
                )

            # Respond to message text with `bolton` included
            if is_bolton_mention(msg_txt) or self.clients.is_bot_mention(msg_txt):
                if re.search('erase|delete', lower_txt):
                    self.msg_writer.erase_history(
                        msg_txt, channel_id, ts
                    )
                if 'help' in lower_txt:
                    self.msg_writer.write_help_message(channel_id)
                if 'joke' in lower_txt:
                    self.msg_writer.write_joke(channel_id)
                if 'french' in lower_txt:
                    self.msg_writer.write_french(msg_txt, channel_id)
                if re.search('who\'?s that pokemon', msg_txt):
                    self.msg_writer.write_whos_that_pokemon(channel_id)
                if re.search(' ?bolton it\'?s', lower_txt):
                    self.msg_writer.write_pokemon_guessed_response(
                        msg_txt, channel_id, user_id
                    )
                if re.search('attachment|beep boop link', lower_txt):
                    self.msg_writer.demo_attachment(channel_id)
                if 'sad ' in lower_txt:
                    self.msg_writer.write_sad(channel_id)
                if 'sort me' in lower_txt:
                    self.msg_writer.write_hogwarts_house(
                        msg_txt, channel_id, user_id
                    )
                if re.search('encourage ', lower_txt):
                    self.msg_writer.write_encouragement(msg_txt, channel_id)
                if 'sass ' in lower_txt:
                    self.msg_writer.write_sass(msg_txt, channel_id)
                if 'solve' in lower_txt:
                    self.msg_writer.write_solution(msg_txt, channel_id)
                if re.search('explain|why', lower_txt):
                    self.msg_writer.write_explanation(channel_id)
                if re.search('sweetpotato|sweet potato', lower_txt):
                    self.msg_writer.write_sweetpotato_me(msg_txt, channel_id)
                if re.search('draw me', lower_txt):
                    self.msg_writer.write_draw_me(channel_id)
                if re.search('love|forever|relationship', lower_txt):
                    self.msg_writer.write_forever(channel_id)
                if re.search('unflip', lower_txt):
                    self.msg_writer.write_unflip(channel_id)
                elif re.search('flip|rageflip', lower_txt):
                    self.msg_writer.write_flip(channel_id)
                if re.search('sup son', lower_txt):
                    self.msg_writer.write_sup_son(channel_id)
                if lower_txt == "bolton":
                    self.msg_writer.write_prompt(channel_id)
                else:
                    pass
Esempio n. 6
0
class TimeTriggeredEventManager(object):
    def __init__(self, clients, msg_writer, markov_chain):
        self.clients = clients
        self.msg_writer = msg_writer
        self.markov_chain = markov_chain
        self.channel_manager = ChannelManager(clients)
        self.random_manager = ResourceManager('random_comments.txt')
        self.trigger_startup_log()
        self.process_recent_messages()

    def send_message(self, msg_txt, channel=None):
        self.msg_writer.send_message(msg_txt, channel)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def clean_channels_history(self):
        result = 'Erased messages: '
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                count = self._erase_channel_messages(channel_id, log_days=3)
                if count != 0:
                    result += '<#{}> ({}), '.format(str(channel_id),
                                                    str(count))
        if result.endswith(', '):
            result = result[:-2]
        else:
            result = 'No messages erased during general clean up'
        self.send_message(result)

    def clean_testing_channel_history(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        total_count = 0
        for num in range(10):
            count = self._erase_channel_messages(testing_channel, log_days=1)
            total_count += count
            if count == 0:
                break
        result = 'Erased {} total messages from <#{}> in {} passes'.format(
            str(total_count), str(testing_channel), str(num))
        self.send_message(result)

    def _erase_channel_messages(self, channel_id, log_days=0):
        count = 0
        now_ts = float(time.time())
        response = self.clients.get_message_history(channel_id)
        if 'messages' in response:
            for message in response['messages']:
                if ('ts' in message and 'pinned_to' not in message
                        and self.clients.is_message_from_me(message)):
                    # Delete everything older than `log_days` old
                    # Delete items older than a day old
                    # Unless they are weather posts or startup logs
                    # Always delete the message deletions messages
                    if ((now_ts -
                         (60 * 60 * 24 * log_days)) > float(message['ts']) or
                        ((now_ts - (60 * 60 * 24)) > float(message['ts']) and
                         not re.search(DONT_DELETE, message['text'].lower()) or
                         (re.search("I SAW THAT! _Someone_ deleted a message",
                                    message['text'])))):
                        self.clients.delete_message(channel_id, message['ts'])
                        count += 1
        return count

    def process_recent_messages(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        count_markov = 0
        count_louds = 0
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                response = self.clients.get_message_history(channel_id)
                if 'messages' in response:
                    for message in response['messages']:
                        if not self.clients.is_message_from_me(message):
                            if 'text' in message:
                                msg_text = message['text']

                                # Add markovs
                                if should_add_markov(message):
                                    self.markov_chain.add_single_line(msg_text)
                                    count_markov += 1

                                # Add louds
                                if should_add_loud(message):
                                    self.msg_writer.write_loud(msg_text)
                                    count_louds += 1
        result = ("Added " + str(count_markov) + " messages to markov "
                  "and " + str(count_louds) + " loud messages")
        self.send_message(result)

    def trigger_random_markov(self):
        if random.random() < 0.15:
            channel_id = self.channel_manager.get_channel_id(
                'boltonsexybutt-co')
            now_timestamp = float(time.time())
            response = self.clients.get_message_history(channel_id, 1)
            if 'messages' in response:
                for message in response['messages']:
                    if ('user' in message and 'ts' in message
                            and not self.clients.is_message_from_me(message)
                            and not contains_tag(message['text'])
                            and 'markov' not in message['text']):
                        # Post only 3 - 5 minutes after latest message
                        if (now_timestamp - (60 * 5) <= float(message['ts'])
                                and now_timestamp -
                            (60 * 2) >= float(message['ts'])):
                            try:
                                txt = str(self.markov_chain)
                                self.send_message(txt, 'boltonsexybutt-co')
                                self.trigger_method_log('random markov')
                            except Exception:
                                err_msg = traceback.format_exc()
                                logging.error(
                                    'Unexpected error: {}'.format(err_msg))
                                self.msg_writer.write_error(err_msg)
                                pass

    def trigger_morning(self):
        responses = [
            "Good morning", "Morning", "Guten Morgen", "Bonjour", "Ohayou",
            "Good morning to you", "Aloha", "Konnichiwashington",
            "Buenos dias", "GLUTEN MORNING", ":sunny: Good morning",
            "Where have you been. MORNING"
        ]
        txt = '{}! :{}:'.format(random.choice(responses), self.get_emoji())
        self.msg_writer.send_message_as_other(txt, 'boltonsexybutt-co',
                                              'bolton', ':sunglasses:')
        # self.send_message(txt, 'boltonsexybutt-co')

    def trigger_markov(self):
        try:
            self.msg_writer.send_message(str(self.markov_chain),
                                         'bolton_weather')
        except Exception:
            err_msg = traceback.format_exc()
            logging.error('Unexpected error: {}'.format(err_msg))
            self.msg_writer.write_error(err_msg)
            pass

    def trigger_ping(self, day, hour, minute, second):
        msg = ('Ping on ' + day + ' ' + str(hour) + ':' + str(minute) + ':' +
               str(second) + ' :' + str(self.get_emoji()) + ':')
        self.send_message(msg)

    def trigger_method_log(self, method_name):
        msg = 'Event: {}'.format(method_name)
        self.send_message(msg)

    def trigger_startup_log(self):
        day, hour, minute, second = _get_datetime()
        msg = ('I came back to life on ' + day + ' ' + str(hour) + ':' +
               str(minute) + ':' + str(second) + ' :' + str(self.get_emoji()) +
               ':')
        self.send_message(msg)

    def trigger_wine_club(self):
        tags = ['channel', 'here']
        msg = ("WINE CLUB IN THE LOUNGE :wine_glass: :wine_glass: "
               ":wine_glass: :wine_glass: :wine_glass:")
        txt = '<!{}> {}'.format(random.choice(tags), msg)
        self.msg_writer.send_message_as_other(txt, 'boltonsexybutt-co',
                                              'bolton', ':wine_glass:')

    def trigger_random_phrase(self):
        if random.random() < 0.02:
            comment = self.random_manager.get_response()
            txt = '{} :{}:'.format(comment, self.get_emoji())
            self.send_message(txt, 'boltonsexybutt-co')
            self.trigger_method_log('wine club')

    def trigger_weather(self):
        response = weather_manager.getCurrentWeather()
        self.send_message(response)

    def trigger_tuesday(self):
        txt = "DON'T FORGET IT'S TUESDAY _ALLL_ DAY TODAY"
        self.msg_writer.send_message_as_other(txt, 'boltonsexybutt-co',
                                              'bolton',
                                              ':rolled_up_newspaper:')

    def trigger_945(self):
        kip_msgs = [
            '@945', '945!', '#945', ':paw_prints: 945!', '~945~',
            ':horse: 945! giddyup', '945! :heart:', '945! :sweet_potato:',
            '945!........', '945 time', '945 quickie', '945 o\'clock',
            '945! :sheep: :panda_face: :slowpoke:', '945! :boom:',
            ':eggplant: 945.', '945 :coffee:', '_le 945_', '_le fast 945_'
        ]
        txt = '{} :{}:'.format(random.choice(kip_msgs), self.get_emoji())
        self.msg_writer.send_message_as_other(txt, 'boltonsexybutt-co',
                                              'bolton', ':boltonefron:')
        # self.send_message(txt, 'boltonsexybutt-co')

    def trigger_mochaccino(self):
        msgs = [
            'The mochaccino tastes _amazing_ this morning!',
            'Eh, mochaccino ain\'t so great today...',
            'HELP! MOCHACCINO EVERYWHERE!',
            ('The mochaccino machine won\'t stop dripping help I need an '
             'adult'), 'WHAT! wHY is my mochaccino _decaf_??!',
            'I haven\'t had my mochaccino yet don\'t talk to me',
            'WHERE\'S MY MUG I NEED MOCHACCINO!!',
            'Mochaccino mochaccino mochaccino',
            'Mochaccino is SO GOOD TODAY HOLY HELL',
            ('Today\'s mochaccino is like an angel pooped out a nice hot '
             'cup of coffee mmmmm~'), 'Mochaccino status: passable',
            'MOCHACCINO MOCHACCINO MOCHACCINO!!!',
            'Who\'s ready for a nice cup o\' mochaccino?', '_le mochaccino_'
        ]
        txt = '{} :coffee:'.format(random.choice(msgs))
        self.msg_writer.send_message_as_other(txt, 'boltonsexybutt-co',
                                              'bolton', ':coffee:')

    def trigger_timed_event(self):
        day, hour, minute, second = _get_datetime()

        # leaves 10-ish seconds to trigger since method is called every 10-ish
        # seconds and we wantz the if statement to trigger once per min only
        if (second >= 5 and second <= 15):
            # self.trigger_ping(day, hour, minute, second)
            if hour == 1:
                if minute == 15:
                    self.clean_channels_history()
                if minute == 0 or minute == 30:
                    self.clean_testing_channel_history()
            if hour % 3 == 0 and minute == 0:
                self.trigger_weather()
            if minute == 15:
                self.trigger_markov()
            if hour >= 9 and hour <= 16:
                self.trigger_random_markov()
            if (day != 'Saturday' and day != 'Sunday'):
                if hour == 8 and minute == 45:
                    self.trigger_morning()
                if hour == 9:
                    if minute == 45:
                        self.trigger_945()
                    elif minute == 0:
                        self.trigger_mochaccino()
            if day == 'Friday':
                if hour == 16 and minute == 45:
                    self.trigger_wine_club()
                if hour >= 17 and hour <= 18:
                    self.trigger_random_phrase()
            if day == 'Tuesday':
                if hour == 14 and minute == 7:
                    self.trigger_tuesday()
Esempio n. 7
0
class SlackClients(object):
    def __init__(self, token):
        self.token = token

        # Slacker is a Slack Web API Client
        self.web = Slacker(token)

        # SlackClient is a Slack Websocket RTM API Client
        self.rtm = SlackClient(token)

        self.msg_writer = Messenger(self)
        self.channel_manager = ChannelManager(self)

        # Set up bot_id
        self.bot_id = None
        self.startUp()

    def startUp(self):
        response = self.send_message_as_other('Initializing bot_id...',
                                              TESTING_CHANNEL, 'bolton',
                                              ':boltonefron:')
        if self.bot_id is None and 'message' in response:
            message = response['message']
            if 'bot_id' in message:
                self.bot_id = message['bot_id']
                self.msg_writer.erase_history(
                    'bolton erase 1',
                    self.channel_manager.get_channel_id(TESTING_CHANNEL),
                    float(time.time()))
                # msg_text = 'Initialized bot_id: ' + self.bot_id
                # self.send_message(msg_text, TESTING_CHANNEL)

    def bot_user_id(self):
        return self.rtm.server.login_data['self']['id']

    def is_message_from_me(self, message):
        if 'user' in message and message['user'] == self.bot_user_id():
            return True
        elif (
                'bot_id' in message and self.bot_id is not None and
            (message['bot_id'] == self.bot_id
             # message['bot_id'] == 'B1S057DV0' or
             # message['bot_id'] == 'B15JNU2AU'
             )):
            return True
        return False

    def is_bot_mention(self, message):
        bot_user_name = self.rtm.server.login_data['self']['id']
        if re.search("@{}".format(bot_user_name), message):
            return True
        else:
            return False

    def send_user_typing_pause(self, channel_id, sleep_time=3.0):
        user_typing_json = {"type": "typing", "channel": channel_id}
        self.rtm.server.send_to_websocket(user_typing_json)
        time.sleep(sleep_time)

    # this method only gets a team's custom emojis and NOT all slack emojis
    def get_random_emoji(self):
        response = self.rtm.api_call('emoji.list')
        emojis = response['emoji'].items()
        return emojis[int(random.random() * len(emojis))][0]

    def send_message_as_other(self, msg_text, channel_id, username, emoji):
        response = self.rtm.api_call('chat.postMessage',
                                     token=str(self.token),
                                     channel=channel_id,
                                     text=msg_text,
                                     link_names=1,
                                     username=username,
                                     unfurl_links=True,
                                     icon_emoji=emoji)

        # Make sure the message gets sent to bolton-testing at least
        if 'error' in response:
            response = str(response) + "\nOriginal message:\n" + msg_text
            self.send_message(response, TESTING_CHANNEL)
        return response

    def send_message(self, msg_text, channel):
        response = self.rtm.api_call('chat.postMessage',
                                     token=str(self.token),
                                     channel=channel,
                                     text=msg_text,
                                     as_user=True,
                                     link_names=1,
                                     unfurl_links=True)
        # Make sure the message gets sent to bolton-testing at least
        if 'error' in response:
            response = str(response) + "\nOriginal message:\n" + msg_text
            self.send_message(response, TESTING_CHANNEL)
        return response

    def update_message(self, updated_msg_text, channel, timestamp):
        response = self.rtm.api_call('chat.update',
                                     token=str(self.token),
                                     channel=channel,
                                     text=updated_msg_text,
                                     as_user=True,
                                     link_names=1,
                                     unfurl_links=True,
                                     ts=timestamp)
        # Make sure the message gets sent to bolton-testing at least
        if 'error' in response:
            response = (str(response) + "\nOriginal message:\n" +
                        updated_msg_text)
            self.send_message(response, TESTING_CHANNEL)
        return response

    def get_message_history(self, channel_id, count=None):
        response = self.rtm.api_call('channels.history',
                                     token=str(self.token),
                                     channel=channel_id,
                                     count=count)
        if 'error' in response:
            error_msg = "`get_message_history` error:\n" + str(response)
            self.msg_writer.write_error(error_msg)
        return response

    def delete_message(self, channel_id, timestamp):
        response = self.rtm.api_call('chat.delete',
                                     token=str(self.token),
                                     channel=channel_id,
                                     as_user=True,
                                     ts=timestamp)
        if 'error' in response:
            error_msg = "`delete_message` error:\n" + str(response)
            self.msg_writer.write_error(error_msg)
        return response

    def send_attachment(self, txt, channel_id, attachment):
        # this does not return the response object that rtm does
        return self.web.chat.post_message(channel_id,
                                          txt,
                                          attachments=[attachment],
                                          as_user='******')

    def get_users(self):
        response = self.rtm.api_call('users.list', token=str(self.token))
        if 'error' in response:
            error_msg = "`get_users` error:\n" + str(response)
            self.msg_writer.write_error(error_msg)
        return response

    def get_channels(self):
        response = self.rtm.api_call(
            'channels.list',
            token=str(self.token),
            exclude_archived=1,
        )
        if 'error' in response:
            error_msg = "`get_channels` error:\n" + str(response)
            self.msg_writer.write_error(error_msg)
        return response

    def get_groups(self):
        return self.rtm.api_call('groups.list', token=str(self.token))

    def get_ims(self):
        return self.rtm.api_call('im.list', token=str(self.token))

    def send_reaction(self, emoji_name, channel, timestamp):
        response = self.rtm.api_call("reactions.add",
                                     token=str(self.token),
                                     name=emoji_name,
                                     channel=channel,
                                     timestamp=timestamp)
        if 'error' in response and response['error'] != 'already_reacted':
            error_msg = "`send_reaction` error:\n" + str(response)
            self.msg_writer.write_error(error_msg)
        return response

    def upload_file_to_slack(self, filepath, filename, channel):
        my_file = os.path.join(filepath, filename)
        return self.web.files.upload(my_file, channels=channel)
class TimeTriggeredEventManager(object):

    def __init__(self, clients, msg_writer, markov_chain):
        self.clients = clients
        self.msg_writer = msg_writer
        self.markov_chain = markov_chain
        self.channel_manager = ChannelManager(clients)
        self.random_manager = ResourceManager('random_comments.txt')
        self.trigger_startup_log()
        self.process_recent_messages()
        self.news_links = NewsManager()

    def send_message(self, msg_txt, channel=None):
        self.msg_writer.send_message(msg_txt, channel)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def clean_channels_history(self):
        result = 'Erased messages: '
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                count = self._erase_channel_messages(channel_id, log_days=3)
                if count != 0:
                    result += '<#{}> ({}), '.format(
                        str(channel_id), str(count)
                    )
        if result.endswith(', '):
            result = result[:-2]
        else:
            result = 'No messages erased during general clean up'
        self.send_message(result)

    def clean_testing_channel_history(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        total_count = 0
        for num in range(10):
            count = self._erase_channel_messages(testing_channel, log_days=1)
            total_count += count
            if count == 0:
                break
        result = 'Erased {} total messages from <#{}> in {} passes'.format(
            str(total_count), str(testing_channel), str(num)
        )
        self.send_message(result)

    def _erase_channel_messages(self, channel_id, log_days=0):
        count = 0
        now_ts = float(time.time())
        response = self.clients.get_message_history(channel_id)
        if 'messages' in response:
            for message in response['messages']:
                if (
                    'ts' in message and 'pinned_to' not in message and
                    self.clients.is_message_from_me(message)
                ):
                    # Delete everything older than `log_days` old
                    # Delete items older than a day old
                    # Unless they are weather posts or startup logs
                    # Always delete the message deletions messages
                    if (
                        (now_ts - (60*60*24*log_days)) > float(message['ts'])
                        or (
                            (now_ts - (60*60*24)) > float(message['ts'])
                            and not re.search(
                                DONT_DELETE, message['text'].lower()
                            )
                            or (
                                re.search(
                                    "I SAW THAT! _Someone_ deleted a message",
                                    message['text']
                                )
                            )
                        )
                    ):
                        self.clients.delete_message(channel_id, message['ts'])
                        count += 1
        return count

    def process_recent_messages(self):
        testing_channel = self.channel_manager.get_channel_id(TESTING_CHANNEL)
        count_markov = 0
        count_louds = 0
        for channel_id in self.channel_manager.get_all_channel_ids():
            if channel_id != testing_channel:
                response = self.clients.get_message_history(channel_id)
                if 'messages' in response:
                    for message in response['messages']:
                        if not self.clients.is_message_from_me(message):
                            if 'text' in message:
                                msg_text = message['text']

                                # Add markovs
                                if should_add_markov(message):
                                    self.markov_chain.add_single_line(msg_text)
                                    count_markov += 1

                                # Add louds
                                if should_add_loud(message):
                                    self.msg_writer.write_loud(msg_text)
                                    count_louds += 1
        result = (
            "Added " + str(count_markov) + " messages to markov "
            "and " + str(count_louds) + " loud messages"
        )
        self.send_message(result)

    def trigger_random_markov(self):
        if random.random() < 0.15:
            channel_id = self.channel_manager.get_channel_id('random')
            now_timestamp = float(time.time())
            response = self.clients.get_message_history(channel_id, 1)
            if 'messages' in response:
                for message in response['messages']:
                    if (
                        'user' in message and 'ts' in message and not
                        self.clients.is_message_from_me(message) and
                        not contains_tag(message['text']) and
                        'markov' not in message['text']
                    ):
                        # Post only 3 - 5 minutes after latest message
                        if (
                            now_timestamp - (60*5) <= float(message['ts']) and
                            now_timestamp - (60*2) >= float(message['ts'])
                        ):
                            try:
                                txt = str(self.markov_chain)
                                self.send_message(txt, 'random')
                                self.trigger_method_log('random markov')
                            except Exception:
                                err_msg = traceback.format_exc()
                                logging.error(
                                    'Unexpected error: {}'.format(err_msg)
                                )
                                self.msg_writer.write_error(err_msg)
                                pass

    def trigger_markov(self):
        try:
            self.msg_writer.send_message(str(self.markov_chain), 'markov')
        except Exception:
            err_msg = traceback.format_exc()
            logging.error('Unexpected error: {}'.format(err_msg))
            self.msg_writer.write_error(err_msg)
            pass

    def trigger_ping(self, day, hour, minute, second):
        msg = ('Ping on ' + day + ' ' + str(hour) + ':' + str(minute) +
               ':' + str(second) + ' :' + str(self.get_emoji()) + ':')
        self.send_message(msg)

    def trigger_method_log(self, method_name):
        msg = 'Event: {}'.format(method_name)
        self.send_message(msg)

    def trigger_startup_log(self):
        day, hour, minute, second = _get_datetime()
        msg = ('I came back to life on ' + day + ' ' + str(hour) + ':' +
               str(minute) + ':' + str(second) + ' :' + str(self.get_emoji()) +
               ':')
        self.send_message(msg)

    def trigger_wine_club(self):
        tags = ['here']
        msg = ("WINE CLUB IN THE LOUNGE :wine_glass: :wine_glass: "
               ":wine_glass: :wine_glass: :wine_glass:")
        txt = '<!{}> {}'.format(random.choice(tags), msg)
        self.msg_writer.send_message_as_other(
            txt, 'random', 'zac', ':wine_glass:'
        )

    def trigger_random_phrase(self):
        if random.random() < 0.01:
            comment = self.random_manager.get_response()
            txt = '{} :{}:'.format(comment, self.get_emoji())
            self.send_message(txt, 'random')
            self.trigger_method_log('wine club')

    def trigger_weather(self):
        response = weather_manager.getCurrentWeather()
        self.send_message(response)

    def trigger_tuesday(self):
        txt = "DON'T FORGET IT'S TUESDAY _ALLL_ DAY TODAY"
        self.msg_writer.send_message_as_other(
            txt, 'random', 'zac', ':rolled_up_newspaper:'
        )

    def trigger_945(self):
        emojis = ['sunglasses', 'zacefron', 'coffee']
        mornings = [
            "Good morning", "Morning", "Guten Morgen", "Bonjour", "Ohayou",
            "Good morning to you", "Aloha", "Konnichiwashington",
            "Buenos dias", "GLUTEN MORNING", ":sunny: Good morning",
            "Where have you been. MORNING"
        ]
        coffee_msgs = [
            'The mochaccino tastes _amazing_ this morning!',
            'Eh, mochaccino ain\'t so great today...',
            'HELP! MOCHACCINO EVERYWHERE!',
            'The mochaccino machine won\'t stop dripping help I need an adult',
            'WHAT! wHY is my mochaccino _decaf_??!',
            'I haven\'t had my mochaccino yet don\'t talk to me',
            'WHERE\'S MY MUG I NEED MOCHACCINO!!',
            'Mochaccino mochaccino mochaccino',
            'Mochaccino is SO GOOD TODAY HOLY HELL',
            (
                'Today\'s mochaccino is like an angel pooped out a nice hot '
                'cup of coffee mmmmm~'
            ),
            'Mochaccino status: passable',
            'MOCHACCINO MOCHACCINO MOCHACCINO!!!',
            'Who\'s ready for a nice cup o\' mochaccino?',
            '_le mochaccino_'
        ]
        kip_msgs = [
            '@945', '945!', '#945', ':paw_prints: 945!', '~945~',
            ':horse: 945! giddyup', '945! :heart:', '945! :sweet_potato:',
            '945!........', '945 time',
            '945 quickie', '945 o\'clock',
            '945! :sheep: :panda_face: :slowpoke:', '945! :boom:',
            ':eggplant: 945.', '945 :coffee:', '_le 945_',
            '_le fast 945_'
        ]
        morning_msg = '{}! :{}:'.format(
            random.choice(mornings), self.get_emoji()
        )
        coffee_msg = '{} :coffee:'.format(random.choice(coffee_msgs))
        kip_msg = '{} :{}:'.format(random.choice(kip_msgs), self.get_emoji())
        msg = '{}\n{}'.format(random.choice(morning_msg, coffee_msg), kip_msg)
        self.msg_writer.send_message_as_other(
            msg, 'random', 'zac', ':{}:'.format(random.choice(emojis))
        )

    def post_news(self, iteration):
        link, user_name = self.news_links.get_news()
        if link is not None and link != "":
            if iteration == 0:
                self.trigger_945()
            iteration += 1
            txt = "News: " + link
            if user_name is None or user_name == "":
                user_name = 'zacefron'
            self.msg_writer.send_message_as_other(
                txt, 'random', user_name, ':{}:'.format(user_name)
            )
            self.post_news(iteration)

    def trigger_timed_event(self):
        day, hour, minute, second = _get_datetime()

        # leaves 10-ish seconds to trigger since method is called every 10-ish
        # seconds and we wantz the if statement to trigger once per min only
        if(second >= 5 and second <= 15):
            # self.trigger_ping(day, hour, minute, second)
            if hour == 1:
                if minute == 15:
                    self.clean_channels_history()
                if minute == 0 or minute == 30:
                    self.clean_testing_channel_history()
            if hour % 3 == 0 and minute == 0:
                self.trigger_weather()
            if minute == 15:
                self.trigger_markov()
            if hour >= 9 and hour <= 16:
                self.trigger_random_markov()
            if (day != 'Saturday' and day != 'Sunday'):
                if hour == 9:
                    if minute == 45:
                        self.post_news(0)
            if day == 'Friday':
                if hour == 16 and minute == 45:
                    self.trigger_wine_club()
                if hour == 17:
                    self.trigger_random_phrase()
            if day == 'Tuesday':
                if hour == 14 and minute == 7:
                    self.trigger_tuesday()
class Messenger(object):
    def __init__(self, slack_clients):
        self.clients = slack_clients
        self.loud_manager = LoudManager()
        self.whos_that_pokemon_manager = WhosThatPokemonManager()
        self.hogwarts_house_sorter = HogwartsHouseSorter()
        self.equation_manager = EquationManager()
        self.explanation_manager = ResourceManager('explanations.txt')
        self.drawing_manager = ResourceManager('draw_me.txt')
        self.forever_manager = ResourceManager('forever.txt')
        self.help_manager = ResourceManager('help_text.txt')
        self.sass_manager = ResourceManager('sass.txt')
        self.channel_manager = ChannelManager(slack_clients)
        self.news_links = NewsManager()

    def erase_history(self, msg_text, channel_id, now_timestamp):
        try:
            tokens = re.findall('[0-9]+', msg_text)
            delete_num = int(tokens[0])
            count = 0
            response = self.clients.get_message_history(channel_id)
            if 'messages' in response:
                for message in response['messages']:
                    if (
                        'ts' in message and 'pinned_to' not in message and
                        self.clients.is_message_from_me(message) and
                        not re.search(DONT_DELETE, message['text'].lower())
                    ):
                        response = self.clients.delete_message(
                            channel_id, message['ts']
                        )
                        count += 1
                        if count >= delete_num:
                            break
            if count < delete_num:
                msg = ("Erased " + str(count) + " messages: I "
                       "can only see the 100 most recent messages")
                self.send_message(msg, channel_id)
        except Exception:
            msg = "Correct usage is `zac erase <num>`"
            self.send_message(msg, channel_id)
            pass

    def __del__(self):
        closing_msgs = ["No!! Don't kill me! I want to live!", "Good BYEEE!!!",
                        "I'm dying again :sob:",
                        "Have you gotten tired of this face :zacefron: ?"]
        txt = random.choice(closing_msgs)
        self.send_message(txt)

    def send_slow_message_as_other(self, msg_text, channel, username, emoji):
        self.send_message_as_other(
            msg_text, channel, username, emoji, slow=True
        )

    def send_message_as_other(
        self, msg_text, channel, username, emoji, slow=False
    ):
        msg_text = msg_text.replace('&', "&amp;")
        channel = self.channel_manager.get_channel_id(channel)
        if (slow):
            self.clients.send_user_typing_pause(channel)

        return self.clients.send_message_as_other(
            msg_text, channel, username, emoji
        )

    def write_slow(self, msg_text, channel=None):
        return self.send_message(msg_text, channel, slow=True)

    def send_message(
        self, msg_text, channel=None, slow=False, react_emoji=None
    ):
        msg_text = msg_text.replace('&', "&amp;")
        msg_text = msg_text.replace('&quot;', "\"")

        # channel = self.channel_manager.get_channel_id(channel)

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.send_message(msg_text, channel)
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])

        return response

    def update_message(
        self, updated_msg_text, ts, channel=None, slow=False, react_emoji=None
    ):
        updated_msg_text = updated_msg_text.replace('&', "&amp;")

        channel = self.channel_manager.get_channel_id(channel)

        if channel is None:
            channel = TESTING_CHANNEL
        if slow is True:
            self.clients.send_user_typing_pause(channel)
        response = self.clients.update_message(
            updated_msg_text, channel, ts
        )
        if 'ok' in response and react_emoji is not None:
            self.send_reaction(react_emoji, channel, response['ts'])
        return response

    def send_attachment(self, txt, channel_id, attachment):
        self.clients.send_attachment(txt, channel_id, attachment)

    def write_error(self, err_msg, channel_id=None):
        txt = (":face_with_head_bandage: my maker didn't handle this error "
               "very well:\n>```{}```").format(err_msg)
        self.send_message(txt, channel_id)

    def send_reaction(self, emoji_name, channel_id, timestamp):
        self.clients.send_reaction(emoji_name, channel_id, timestamp)

    def get_emoji(self):
        return self.clients.get_random_emoji()

    def write_message_deleted(self, channel_id):
        # Dont post if messages were deleted inside of #zac-testing
        if channel_id != self.channel_manager.get_channel_id('zac-testing'):
            txt = ("I SAW THAT! _Someone_ deleted a message from channel: "
                   "<#{}>").format(channel_id)
            self.send_message(txt)

    def write_left_channel(self, channel_id):
        self.send_message('...well THAT was something', channel_id)

    def write_joined_channel(self, channel_id, user_id):
        if channel_id == self.channel_manager.get_channel_id('zac-testing'):
            txt = ("Hey <@{}>! Welcome to the Testing (aka the Weather) "
                   "channel. Please MUTE this channel or be inundaded with "
                   "notifications!").format(user_id)
            self.write_slow(txt, channel_id)
            self.write_xkcd("15", channel_id)
        else:
            self.write_greeting(channel_id, user_id)

    def write_help_message(self, channel_id):
        help_txt = self.help_manager.get_all()
        count = self.help_manager.get_count()
        txt = (
            "I'm Zac.  I'll *_respond_* to the following {} commands:\n{}"
        ).format(count-1, help_txt)
        info_text = (
            "Hi, I'm Zac Efron. You can find out more information about me "
            "in #zac-testing. Merci :zacefron:"
        )
        self.write_slow(info_text, channel_id)
        self.write_slow(txt, 'zac-testing')

    def write_french(self, msg_text, channel_id):
        target = get_target(FRENCH_FLAG, msg_text).replace('_', '')
        self.write_slow('_le {}_'.format(target), channel_id)

    def write_greeting(self, channel_id, user_id):
        greetings = ['Hi', 'Hello', 'Nice to meet you', 'Howdy', 'Salutations']
        txt = '{}, <@{}>!'.format(random.choice(greetings), user_id)
        self.write_slow(txt, channel_id)

    def write_spelling_mistake(self, channel_id, timestamp):
        emoji_name = "spelft_it_wronbg_again_i_see"
        self.send_reaction(emoji_name, channel_id, timestamp)

    def write_prompt(self, channel_id):
        bot_uid = self.clients.bot_user_id()
        txt = ("I'm sorry, I didn't quite understand... Can I help you? "
               "(e.g. `<@" + bot_uid + "> help`)")
        self.write_slow(txt, channel_id)

    def write_joke(self, channel_id):
        # answer = (
        #     "Wenn ist das Nunstück git und Slotermeyer? Ja! Beiherhund das "
        #     "Oder die Flipperwaldt gersput!"
        # )
        url = "http://api.icndb.com/jokes/random"
        answer = requests.get(url).json()['value']['joke']
        self.write_slow(answer, channel_id)

    def write_encouragement(self, msg_text, channel_id):
        encouragements = [
            "Get your shit together", "You can do it", "I'm here for you",
            "Do you just think about yourself", "You're the best",
        ]
        target = get_target(ENCOURAGE_FLAG, msg_text)
        txt = '{} {}'.format(random.choice(encouragements), target)
        self.write_slow(txt, channel_id)

    def write_cast_pokemon(self, lower_msg_text, channel_id):
        pkmn = pokemon_i_choose_you(lower_msg_text)
        if pkmn is not None:
            self.send_message(pkmn, channel_id)

    def write_whos_that_pokemon(self, channel_id):
        txt = self.whos_that_pokemon_manager.whos_that_pkmn()
        self.send_message(txt, channel_id)

    def write_pokemon_guessed_response(self, msg_text, channel_id, user_id):
        text = self.whos_that_pokemon_manager.check_response(user_id, msg_text)
        if text is not None:
            self.send_message(text, channel_id)

    def write_sad(self, channel_id):
        txt = "I'm crying into my tea. :joy:"
        attachment = {
            "pretext": "This always cracks me up. :wink:",
            "title": "/giphy bloopin",
            "title_link": ("http://giphy.com/gifs/friday-rebecca-black-hurrr-"
                           "13FsSYo3fzfT2g"),
            "text": txt,
            "fallback": txt,
            "image_url": "http://i.giphy.com/13FsSYo3fzfT2g.gif",
            "color": "#7CD197",
        }
        self.send_attachment(txt, channel_id, attachment)

    def demo_attachment(self, channel_id):
        txt = ("Beep Beep Boop is a ridiculously simple hosting platform for "
               "your Slackbots.")
        attachment = {
            "pretext": "We bring bots to life. :sunglasses: :thumbsup:",
            "title": "Host, deploy and share your bot in seconds.",
            "title_link": "https://beepboophq.com/",
            "text": txt,
            "fallback": txt,
            "image_url": ("https://storage.googleapis.com/beepboophq/_assets/"
                          "bot-1.22f6fb.png"),
            "color": "#7CD197",
        }
        self.send_attachment(txt, channel_id, attachment)

    def write_weather(self, channel_id):
        self.write_slow(weather_manager.getCurrentWeather(), channel_id)

    def write_loud(self, orig_msg):
        if not is_zac_mention(orig_msg):
            self.loud_manager.write_loud_to_file(orig_msg)

    def respond_loud(self, orig_msg, channel_id):
        if is_zac_mention(orig_msg) or random.random() < 0.8:
            message = self.loud_manager.get_random_loud();
            
            if message:
                self.send_message(self.loud_manager.get_random_loud(), channel_id)

    def write_hogwarts_house(self, msg_text, channel_id, user_id):
        response = self.hogwarts_house_sorter.sort_into_house(msg_text)
        self.write_slow('<@{}>: {}'.format(user_id, response), channel_id)

    def write_explanation(self, channel_id):
        self.write_slow(self.explanation_manager.get_response(), channel_id)

    def write_sass(self, msg_txt, channel_id):
        target = get_target(SASS_FLAG, msg_txt)
        sass = 'Hey, {}! {}'.format(target, self.sass_manager.get_response())
        self.write_slow(sass, channel_id)

    def write_solution(self, msg_text, channel_id):
        self.write_slow(self.equation_manager.solve(msg_text), channel_id)

    def write_sweetpotato_me(self, msg_text, channel_id):
        target = get_target(SWEETPOTATO_FLAG, msg_text)
        txt = 'Here, {}! :sweet_potato:'.format(target)
        self.write_slow(txt, channel_id)

    def write_draw_me(self, channel_id):
        self.write_slow(self.drawing_manager.get_response(), channel_id)

    def write_forever(self, channel_id):
        original_msg = self.forever_manager.get_response()
        response = self.write_slow(original_msg, channel_id)
        new_msg = '~{}~ Just kidding! :laughing:'.format(original_msg.strip())
        self.update_message(
            new_msg, response['ts'], channel_id, slow=True,
            react_emoji='trollface'
        )

    def write_flip(self, channel_id):
        self.send_message(u"(╯°□°)╯︵ ┻━┻", channel_id)

    def write_unflip(self, channel_id):
        self.send_message(u"┬─┬ノ( º _ ºノ)", channel_id)

    def write_sup_son(self, channel_id):
        self.send_message(u"¯\_(ツ)_/¯", channel_id)

    def write_riri_me(self, msg_text, channel_id):
        target = get_target(RIRI_FLAG, msg_text).upper()
        if target != "":
            txt = ' '.join(target for num in range(5))
        else:
            txt = "WHY WOULD YOU JUST TYPE RIRI?\n"
        self.write_slow(txt, channel_id)

    def write_xkcd(self, lower_msg_text, channel_id):
        requestedComic = lower_msg_text[lower_msg_text.find('xkcd') + 4:]
        txt = xkcd_manager.getImageLocation(requestedComic)
        self.write_slow(txt, channel_id)

    def write_terminal_command(self, lower_msg_text, channel_id):
        response = terminal_manager.run_terminal_command(lower_msg_text)
        self.send_message(response, channel_id)

    def google(self, msg_text, channel_id):
        try:
            tokens = re.split(GOOGLE_FLAG, msg_text)
            query = ""
            for num in range(len(tokens)):
                if num > 0:
                    query += "+" + tokens[num].strip()
            if len(query) > 1:
                query = query[1:]

            url = (
                "https://www.googleapis.com/customsearch/v1?key=AIzaSyDs8-"
                "StJem2hqcu2L-J4PceBPVx5Jk6txk&cx=004179257401026011423:y4"
                "vp3hnjgvo&q="
            ) + query

            response = requests.get(url).json()
            result = response['items'][0]['link']

            self.send_message(result, channel_id)
        except Exception as e:
            self.write_error(str(e))

    def link_945(self, msg_text, channel_id, user_name):
        my_news = msg_text.partition("zac add news ")[2]
        rank = self.news_links.add_news(my_news, user_name)
        result = 'Added <{}> from `{}` to the news. It\'s rank is #{}'.format(
            my_news, user_name, rank
        )
        self.send_message(result, channel_id)

    def write_lmao(self, channel_id):
        self.send_message("lmao", channel_id)

    def write_hal(self, channel_id, user_name):
        reply = "I'm sorry <@{}>, I'm afraid I can't do that.".format(
            user_name
        )
        self.send_message(reply, channel_id)

    def write_hackernews(self, channel_id):
        article = get_hackernews_article()
        self.send_message(article, channel_id)