示例#1
0
文件: source.py 项目: zuodh/airbyte
 def check_config(self, logger: AirbyteLogger, config_path: str, config: json) -> AirbyteConnectionStatus:
     try:
         client = WebClient(token=config["token"])
         client.conversations_list()
         return AirbyteConnectionStatus(status=Status.SUCCEEDED)
     except SlackApiError as e:
         logger.error(f"Got an error: {e.args[0]}")
         return AirbyteConnectionStatus(status=Status.FAILED, message=str(e.args[0]))
示例#2
0
def load_channels(archived=False):
    """
    Get a list of all the public channels in Slack

    :param archived: Boolean - Include archived channels

    :returns: Response object (Dictionary)
    """
    if not settings.SLACK_TOKEN:
        return {'ok': False, 'error': 'config_error'}

    client = WebClient(token=settings.SLACK_TOKEN)

    try:
        response = client.conversations_list(exclude_archived=not archived)
        assert response['ok'] is True

        channels = []
        for channel in response['channels']:
            channels.append((channel['id'], channel['name']))

        return {'ok': True, 'channels': channels}
    except SlackApiError as e:
        assert e.response['ok'] is False
        return e.response
def get_slack_conversations():
    try:
        slack_token = config["SLACK"]["oauth_token"]
        client = WebClient(token=slack_token)
        response = client.conversations_list()
        conversations = response["channels"]
        return conversations
    except Exception as error:
        logger.error(str(error))
def get_slack_channel_id(channel_name):
    wc = WebClient(slack_token)

    channels = wc.conversations_list(exclude_archived=True,
                                     types="public_channel")
    for c in channels["channels"]:
        if c["name"] == channel_name:
            channel_id = c["id"]

    return channel_id
示例#5
0
    def deviceStartComm(self, device):
        self.logger.debug(f"{device.name}: Starting Device")

        client = WebClient(token=device.pluginProps['bot_token'])
        auth_info = client.auth_test()
        self.slack_accounts[auth_info['team_id']] = device.id
        self.logger.info(f"{device.name}: Connected to Slack Workspace '{auth_info['team']}'")

        self.channels[device.id] = [
            (channel["id"], channel["name"])
            for channel in client.conversations_list()['channels']
        ]
        self.logger.info("{}: Channel List Updated".format(device.name))
        self.logger.debug("{}: Channels: {}".format(device.name, self.channels[device.id]))
示例#6
0
class SlackBot:
    def __init__(self, token):
        self.client = WebClient(token=token)
        self.channels_dict = dict()
        try:
            for response in self.client.conversations_list():
                self.channels_dict.update({
                    channel["name"]: channel["id"]
                    for channel in response["channels"]
                })
        except SlackApiError as e:
            print(f"Error: {e}")

    def check_channel(self, channel: str):
        return channel in self.channels_dict

    def post_in_channel(self, channel_name: str, message: str,
                        articles: [Article]):
        if articles:
            marked_articles = '>- ' + '\n>- '.join(
                [f'<{art.url}|{art.header}>' for art in articles])

            try:
                self.client.conversations_join(
                    channel=self.channels_dict[channel_name])
                self.client.chat_postMessage(
                    channel=self.channels_dict[channel_name],
                    text=f"{message}",
                    blocks=[{
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": f"{message}"
                        }
                    }, {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": f"{marked_articles}"
                        }
                    }])

            except SlackApiError as e:
                print(f"Error: {e}")
示例#7
0
def return_joining_channels(say, logger, context):
    '''tell channel names where the bot joins
    
    `@BOTNAME where`
    '''

    client = WebClient(token=slack_bot_token)

    # if you want only channels, ch['is_channel'] will be good filter

    channels = []

    for ch in client.conversations_list()['channels']:
        members = client.conversations_members(channel=ch['id'])['members']
        if bot_user_id in members:
            channels.append(ch['name'])
    if len(channels) > 0:
        say(msgs.imin().format('\n'.join(channels)))
    else:
        say(msgs.noplace())
示例#8
0
# Instanciate WebClient
client = WebClient(token=os.environ.get("SLACK_BOT_TOKEN"))

# Instanciate WebClient
app = Flask(__name__)
handler = SlackRequestHandler(bolt_app)

# Slack
# multi channel translation
list_channel_basename = os.environ.get("MULTI_CHANNEL").split(",")

channels = []
# retrieve channel object from Slack API
try:
    # Call the conversations.list method using the built-in WebClient
    for paginated_response in client.conversations_list(limit=10):
        channels += paginated_response['channels']

except SlackApiError as e:
    logging.error("Error fetching conversations: {}".format(e))
    #logger.error("Error fetching conversations: {}".format(e))

name_dict, id_dict = {}, {}
for i in channels:
    name_dict[i['name']] = i['id']
    id_dict[i['id']] = i['name']
# not good way :(
del i

# Logging
if DEBUG == "True":
class TestWebClient(unittest.TestCase):
    def setUp(self):
        setup_mock_web_api_server(self)
        self.client = WebClient(
            token="xoxb-api_test",
            base_url="http://localhost:8888",
        )

    def tearDown(self):
        cleanup_mock_web_api_server(self)

    pattern_for_language = re.compile("python/(\\S+)", re.IGNORECASE)
    pattern_for_package_identifier = re.compile("slackclient/(\\S+)")

    def test_subsequent_requests_with_a_session_succeeds(self):
        resp = self.client.api_test()
        assert resp["ok"]
        resp = self.client.api_test()
        assert resp["ok"]

    def test_api_calls_include_user_agent(self):
        self.client.token = "xoxb-api_test"
        resp = self.client.api_test()
        self.assertEqual(200, resp.status_code)

    def test_builtin_api_methods_send_json(self):
        self.client.token = "xoxb-api_test"
        resp = self.client.api_test(msg="bye")
        self.assertEqual(200, resp.status_code)
        self.assertEqual("bye", resp["args"]["msg"])

    def test_requests_can_be_paginated(self):
        self.client.token = "xoxb-users_list_pagination"
        users = []
        for page in self.client.users_list(limit=2):
            users = users + page["members"]
        self.assertTrue(len(users) == 4)

    def test_response_can_be_paginated_multiple_times(self):
        self.client.token = "xoxb-conversations_list_pagination"
        # This test suite verifies the changes in #521 work as expected
        response = self.client.conversations_list(limit=1)
        ids = []
        for page in response:
            ids.append(page["channels"][0]["id"])
        self.assertEqual(ids, ["C1", "C2", "C3"])

        # The second iteration starting with page 2
        # (page1 is already cached in `response`)
        self.client.token = "xoxb-conversations_list_pagination2"
        ids = []
        for page in response:
            ids.append(page["channels"][0]["id"])
        self.assertEqual(ids, ["C1", "C2", "C3"])

    def test_request_pagination_stops_when_next_cursor_is_missing(self):
        self.client.token = "xoxb-users_list_pagination_1"
        users = []
        for page in self.client.users_list(limit=2):
            users = users + page["members"]
        self.assertTrue(len(users) == 2)

    def test_json_can_only_be_sent_with_post_requests(self):
        with self.assertRaises(err.SlackRequestError):
            self.client.api_call("fake.method", http_verb="GET", json={})

    def test_slack_api_error_is_raised_on_unsuccessful_responses(self):
        self.client.token = "xoxb-api_test_false"
        with self.assertRaises(err.SlackApiError):
            self.client.api_test()
        self.client.token = "xoxb-500"
        with self.assertRaises(err.SlackApiError):
            self.client.api_test()

    def test_slack_api_rate_limiting_exception_returns_retry_after(self):
        self.client.token = "xoxb-rate_limited"
        try:
            self.client.api_test()
        except err.SlackApiError as slack_api_error:
            self.assertFalse(slack_api_error.response["ok"])
            self.assertEqual(429, slack_api_error.response.status_code)
            self.assertEqual(30, int(slack_api_error.response.headers["Retry-After"]))

    def test_the_api_call_files_argument_creates_the_expected_data(self):
        self.client.token = "xoxb-users_setPhoto"
        resp = self.client.users_setPhoto(
            image="tests/slack_sdk_fixture/slack_logo.png"
        )
        self.assertEqual(200, resp.status_code)

    def test_issue_560_bool_in_params_sync(self):
        self.client.token = "xoxb-conversations_list"
        self.client.conversations_list(exclude_archived=1)  # ok
        self.client.conversations_list(exclude_archived="true")  # ok
        self.client.conversations_list(exclude_archived=True)  # ok

    def test_issue_690_oauth_v2_access(self):
        self.client.token = ""
        resp = self.client.oauth_v2_access(
            client_id="111.222", client_secret="secret", code="codeeeeeeeeee"
        )
        self.assertIsNone(resp["error"])
        with self.assertRaises(err.SlackApiError):
            self.client.oauth_v2_access(
                client_id="999.999", client_secret="secret", code="codeeeeeeeeee"
            )

    def test_issue_690_oauth_access(self):
        self.client.token = ""
        resp = self.client.oauth_access(
            client_id="111.222", client_secret="secret", code="codeeeeeeeeee"
        )
        self.assertIsNone(resp["error"])
        with self.assertRaises(err.SlackApiError):
            self.client.oauth_access(
                client_id="999.999", client_secret="secret", code="codeeeeeeeeee"
            )

    def test_issue_705_no_param_request_pagination(self):
        self.client.token = "xoxb-users_list_pagination"
        users = []
        for page in self.client.users_list():
            users = users + page["members"]
        self.assertTrue(len(users) == 4)

    def test_token_param(self):
        client = WebClient(base_url="http://localhost:8888")
        with self.assertRaises(err.SlackApiError):
            client.users_list()
        resp = client.users_list(token="xoxb-users_list_pagination")
        self.assertIsNone(resp["error"])
        with self.assertRaises(err.SlackApiError):
            client.users_list()

    def test_timeout_issue_712(self):
        client = WebClient(base_url="http://localhost:8888", timeout=1)
        with self.assertRaises(socket.timeout):
            client.users_list(token="xoxb-timeout")

    def test_html_response_body_issue_718(self):
        client = WebClient(base_url="http://localhost:8888")
        try:
            client.users_list(token="xoxb-html_response")
            self.fail("SlackApiError expected here")
        except err.SlackApiError as e:
            self.assertTrue(
                str(e).startswith(
                    "Received a response in a non-JSON format: <!DOCTYPE HTML PUBLIC"
                ),
                e,
            )

    def test_user_agent_customization_issue_769(self):
        client = WebClient(
            base_url="http://localhost:8888",
            token="xoxb-user-agent this_is test",
            user_agent_prefix="this_is",
            user_agent_suffix="test",
        )
        resp = client.api_test()
        self.assertTrue(resp["ok"])

    def test_default_team_id(self):
        client = WebClient(base_url="http://localhost:8888", team_id="T_DEFAULT")
        resp = client.users_list(token="xoxb-users_list_pagination")
        self.assertIsNone(resp["error"])
示例#10
0
class Slack(threading.Thread):
    def __init__(self, interval=3, config=None, auto_start=True, **commands):
        """
        Initializes Slack bot, including auto-updating widget if in notebook
        and using multiprocessing.

        Args:
            interval (int): Update interval for widget (must be over 1s).
            config (Optional[dict]): Config dict
                If not given, uses qc.config['user']['slack']
                The config dict must contain the following keys:

                - 'bot_name': Name of the bot
                - 'bot_token': Token from bot (obtained from slack website)
                - 'names': Usernames to periodically check for IM messages

            auto_start (bool): Defaults to True.

        """
        if config is not None:
            self.config = config
        else:
            self.config = qc_config.user.slack

        self.slack = WebClient(token=self.config['token'])
        self.users = self.get_users(self.config['names'])
        self.get_im_ids(self.users)

        self.commands = {
            'plot': self.upload_latest_plot,
            'msmt': self.print_measurement_information,
            'measurement': self.print_measurement_information,
            'notify': self.add_task,
            'help': self.help_message,
            'task': self.add_task,
            **commands
        }
        self.task_commands = {'finished': self.check_msmt_finished}

        self.interval = interval
        self.tasks = []

        # Flag that exits loop when set to True (called via self.exit())
        self._exit = False

        # Flag that enables actions to be performed in the event loop
        # Enabled via self.start(), disabled via self.stop()
        self._is_active = False

        # Call Thread init
        super().__init__()

        if auto_start:
            self.start()

    def start(self):
        self._is_active = True
        try:
            # Start thread, can only be called once
            super().start()
        except RuntimeError:
            # Thread already started, ignoring
            pass

    def run(self):
        """
        Thread event loop that periodically checks for updates.
        Can be stopped via  :meth:`stop` , after which the Thread is stopped.
        Returns:
            None.
        """
        while not self._exit:
            # Continue event loop
            if self._is_active:
                # check for updates
                self.update()
            sleep(self.interval)

    def stop(self):
        """
        Stop checking for updates. Can be started again via :meth:`start`.
        Returns:
            None.
        """
        self._is_active = False

    def exit(self):
        """
        Exit event loop, stop Thread.
        Returns:
            None
        """
        self._stop = True

    def user_from_id(self, user_id):
        """
        Retrieve user from user id.
        Args:
            user_id: Id from which to retrieve user information.

        Returns:
            dict: User information.
        """
        return self.slack.users_info(user=user_id)['user']

    def get_users(self, usernames):
        """
        Extracts user information for users.
        Args:
            usernames: Slack usernames of users.

        Returns:
            dict: {username: user}
        """
        users = {}
        response = self.slack.users_list()
        for member in response['members']:
            if member['name'] in usernames:
                users[member['name']] = member
        if len(users) != len(usernames):
            remaining_names = [name for name in usernames if name not in users]
            raise RuntimeError(f'Could not find names {remaining_names}')
        return users

    def get_im_ids(self, users):
        """
        Adds IM ids of users to users dict.
        Also adds `last_ts` to the latest IM message
        Args:
            users (dict): {username: user}

        Returns:
            None.
        """
        response = self.slack.conversations_list(types='im')
        user_ids = {username: user['id'] for username, user in users.items()}
        im_ids = {chan['user']: chan['id'] for chan in response['channels']}
        for username, user_id in user_ids.items():
            if user_id in im_ids.keys():
                users[username]['im_id'] = im_ids[user_id]
                # update last ts
                messages = self.get_im_messages(username=username, limit=1)
                if messages:
                    users[username]['last_ts'] = float(messages[0]['ts'])
                else:
                    users[username]['last_ts'] = None

    def get_im_messages(self, username, **kwargs):
        """
        Retrieves IM messages from username.
        Args:
            username: Name of user.
            **kwargs: Additional kwargs for retrieving IM messages.

        Returns:
            List of IM messages.
        """
        # provide backward compatibility with 'count' keyword. It still works,
        # but is undocumented. 'count' likely does the same as 'limit', but
        # 'limit' takes precedence
        if 'limit' not in kwargs.keys():
            kwargs['limit'] = kwargs.pop('count', None)

        channel = self.users[username].get('im_id', None)
        if channel is None:
            return []
        else:
            response = self.slack.conversations_history(channel=channel,
                                                        **kwargs)
            return response['messages']

    def get_new_im_messages(self):
        """
        Retrieves new IM messages for each user in self.users.
        Updates user['last_ts'] to ts of newest message.
        Returns:
            im_messages (Dict): {username: [messages list]} newer than last_ts.
        """
        im_messages = {}
        for username, user in self.users.items():
            last_ts = user.get('last_ts', None)
            new_messages = self.get_im_messages(username=username,
                                                oldest=last_ts)
            # Kwarg 'oldest' sometimes also returns message with ts==last_ts
            new_messages = [
                m for m in new_messages if float(m['ts']) != last_ts
            ]
            im_messages[username] = new_messages
            if new_messages:
                self.users[username]['last_ts'] = float(new_messages[0]['ts'])
        return im_messages

    def update(self):
        """
        Performs tasks, and checks for new messages.
        Periodically called from widget update.
        Returns:
            None.
        """
        new_tasks = []
        for task in self.tasks:
            task_finished = task()
            if not task_finished:
                new_tasks.append(task)
        self.tasks = new_tasks

        new_messages = {}
        try:
            new_messages = self.get_new_im_messages()
        except (ReadTimeout, HTTPError, ConnectTimeout, ReadTimeoutError) as e:
            # catch any timeouts caused by network delays
            warnings.warn('error retrieving slack messages',
                          SlackTimeoutWarning)
            logging.info(e)
        self.handle_messages(new_messages)

    def help_message(self):
        """Return simple help message"""
        cc = ", ".join("`" + str(k) + "`" for k in self.commands.keys())
        return "\nAvailable commands: %s" % cc

    def handle_messages(self, messages):
        """
        Performs commands depending on messages.
        This includes adding tasks to be performed during each update.
        """
        for user, user_messages in messages.items():
            for message in user_messages:
                if message.get('user', None) != self.users[user]['id']:
                    # Filter out bot messages
                    continue
                channel = self.users[user]['im_id']
                # Extract command (first word) and possible args
                command, args, kwargs = convert_command(message['text'])
                if command in self.commands:
                    msg = f'Executing {command}'
                    if args:
                        msg += f' {args}'
                    if kwargs:
                        msg += f' {kwargs}'
                    self.slack.chat_postMessage(text=msg, channel=channel)

                    func = self.commands[command]
                    try:
                        if isinstance(func, _BaseParameter):
                            results = func(*args, **kwargs)
                        else:
                            # Only add channel and Slack if they are explicit
                            # kwargs
                            func_sig = inspect.signature(func)
                            if 'channel' in func_sig.parameters:
                                kwargs['channel'] = channel
                            if 'slack' in func_sig.parameters:
                                kwargs['slack'] = self
                            results = func(*args, **kwargs)

                        if results is not None:
                            self.slack.chat_postMessage(
                                text=f'Results: {results}', channel=channel)

                    except Exception:
                        self.slack.chat_postMessage(
                            text=f'Error: {traceback.format_exc()}',
                            channel=channel)
                else:
                    self.slack.chat_postMessage(
                        text='Command {} not understood. Try `help`'.format(
                            command),
                        channel=channel)

    def add_task(self, command, *args, channel, **kwargs):
        """
        Add a task to self.tasks, which will be executed during each update
        Args:
            command: Task command.
            *args: Additional args for command.
            channel: Slack channel (can also be IM channel).
            **kwargs: Additional kwargs for particular.

        Returns:
            None.
        """
        if command in self.task_commands:
            self.slack.chat_postMessage(text=f'Added task "{command}"',
                                        channel=channel)
            func = self.task_commands[command]
            self.tasks.append(partial(func, *args, channel=channel, **kwargs))
        else:
            self.slack.chat_postMessage(
                text=f'Task command {command} not understood', channel=channel)

    def upload_latest_plot(self, channel, **kwargs):
        """
        Uploads latest plot (if any) to slack channel.
        The latest plot is retrieved from
        :class:`qcodes.plots.base.BasePlot`, which is updated
        every time a new qcodes plot is instantiated.
        Args:
            channel: Slack channel (can also be IM channel).
            **kwargs: Not used.

        Returns:
            None.
        """
        # Create temporary filename
        temp_filename = tempfile.mktemp(suffix='.jpg')
        # Retrieve latest plot
        latest_plot = BasePlot.latest_plot
        if latest_plot is not None:
            # Saves latest plot to filename
            latest_plot.save(filename=temp_filename)
            # Upload plot to slack
            self.slack.files_upload(file=temp_filename, channels=channel)
            os.remove(temp_filename)
        else:
            self.slack.chat_postMessage(text='No latest plot', channel=channel)

    def print_measurement_information(self, channel, **kwargs):
        """
        Prints information about the current measurement.
        Information printed is percentage complete, and dataset representation.
        Dataset is retrieved from DataSet.latest_dataset, which updates itself
        every time a new dataset is created
        Args:
            channel: Slack channel (can also be IM channel).
            **kwargs: Not used.

        Returns:
            None.
        """
        dataset = active_data_set()
        if dataset is not None:
            self.slack.chat_postMessage(
                text='Measurement is {:.0f}% complete'.format(
                    100 * dataset.fraction_complete()),
                channel=channel)
            self.slack.chat_postMessage(text=repr(dataset), channel=channel)
        else:
            self.slack.chat_postMessage(text='No latest dataset found',
                                        channel=channel)

    def check_msmt_finished(self, channel, **kwargs):
        """
        Checks if the latest measurement is completed.
        Args:
            channel: Slack channel (can also be IM channel).
            **kwargs: Not used.

        Returns:
            bool: True if measurement is finished, False otherwise.
        """
        if active_loop() is None:
            self.slack.chat_postMessage(text='Measurement complete',
                                        channel=channel)
            return True
        else:
            return False
示例#11
0
class CountStamp:
    startdate = None
    startdatetime = None
    enddatetime = None
    stamp_counter = {}
    user_counter = {}
    recv_counter = {}
    client = None
    bot = None
    message = ""
    chat_list = []

    # インストラクタ
    def __init__(self):
        print("CountStamp Start")
        self.client = WebClient(const.USER_TOKEN)
        self.bot = WebClient(const.BOT_TOKEN)
        #Start of time
        self.startdate = datetime.date.today() - datetime.timedelta(
            days=const.DAYS_AGO)
        self.startdatetime = datetime.datetime.combine(self.startdate,
                                                       datetime.time())
        self.enddatetime = self.startdatetime + datetime.timedelta(
            days=const.DAYS_TERM)
        print("term:{}-{}".format(self.startdatetime, self.enddatetime))

    # チャンネルリストを取得する
    def setChannelList(self):
        self.channel_list = self.client.conversations_list(
            exclude_archived=True, limit=const.CHANNEL_LIMIT)
        print("channels:{}".format(len(self.channel_list["channels"])))

    # スタンプをカウントする
    def cntStamp(self):
        channel_cnt = 0
        for channel in self.channel_list["channels"]:
            channel_cnt = channel_cnt + 1
            channel_id = channel["id"]
            #apiの呼び出し回数の制限(1分間に50回まで)を回避する
            time.sleep(1)
            history = self.client.conversations_history(
                channel=channel_id,
                oldest=self.startdatetime.timestamp(),
                latest=self.enddatetime.timestamp())
            #履歴内のスタンプをカウントする
            self.cntReactions(history=history, channel=channel)

    # 履歴内のスタンプをカウントする
    def cntReactions(self, history, channel):
        if (len(history['messages']) > 0):
            print("channel_name:{} messages:{}".format(
                channel['name'], len(history['messages'])))
        for message in history["messages"]:
            try:
                if (message.get("reactions")):
                    reactions_cnt = 0
                    for reaction in message["reactions"]:
                        # ユーザー別のスタンプ数のカウント
                        self.cntUsers(users=reaction["users"])

                        # スタンプ別のスタンプ数のカウント
                        key = reaction["name"]
                        if (self.stamp_counter.get(key)):
                            self.stamp_counter[key] = self.stamp_counter[
                                key] + reaction["count"]
                        else:
                            self.stamp_counter[key] = reaction["count"]

                        # スタンプを受け取ったユーザー別のスタンプ数のカウント
                        if (message.get("user")):
                            key = message["user"]
                            if (self.recv_counter.get(key)):
                                self.recv_counter[key] = self.recv_counter[
                                    key] + reaction["count"]
                            else:
                                self.recv_counter[key] = reaction["count"]

                        # スレッドについたスタンプ数のカウント
                        reactions_cnt = reactions_cnt + reaction["count"]

                    # スレッド別のスタンプ数
                    self.chat_list.append(
                        [channel['id'], message["ts"], reactions_cnt])
            except KeyError as e:
                print("KeyError:")
                print(e.args)

    #スタンプしたユーザーをカウント
    def cntUsers(self, users):
        for user_id in users:
            if (self.user_counter.get(user_id)):
                self.user_counter[user_id] = self.user_counter[user_id] + 1
            else:
                self.user_counter[user_id] = 1

    # カウントをポストする
    def setMessage(self):
        sorted_stamp = sorted(self.stamp_counter.items(),
                              key=lambda x: x[1],
                              reverse=True)
        sorted_user = sorted(self.user_counter.items(),
                             key=lambda x: x[1],
                             reverse=True)
        sorted_recv = sorted(self.recv_counter.items(),
                             key=lambda x: x[1],
                             reverse=True)
        sorted_chat = sorted(self.chat_list, key=lambda x: x[2], reverse=True)

        w_list = ['月', '火', '水', '木', '金', '土', '日']
        self.message = "{}({})のスタンプランキングTOP{}を発表します。\n".format(
            self.startdate.strftime('%Y年%m月%d日'),
            w_list[self.startdate.weekday()], const.RANK_LIMIT)

        self.message = self.message + "\n\n:+1:このスタンプが良く使われました:+1:\n"
        self.setRankingMessage(sorted_stamp, False)

        self.message = self.message + "\n\n:tera_感謝_赤:このユーザーがたくさんスタンプしました:tera_感謝_赤:\n"
        self.setRankingMessage(sorted_user, True)

        self.message = self.message + "\n\n:gift:このユーザーがたくさんスタンプを受け取りました:gift:\n"
        self.setRankingMessage(sorted_recv, True)

        self.message = self.message + "\n\n:trophy:スタンプを集めたメッセージはこちら:trophy:\n"
        self.setChatRankingMessage(sorted_chat)

        total_stamp = sum(self.stamp_counter.values())
        self.message = self.message + "\n\nすべてのスタンプを合計すると {} でした!".format(
            total_stamp)
        i = 1
        while i <= int(total_stamp / const.CLAP_LOOP):
            self.message = self.message + ":clap:"
            i = i + 1

        today_weekday = datetime.date.today().weekday()
        # 土日はメッセージを変える。
        if (today_weekday != 5 and today_weekday != 6):
            self.message = self.message + "\nそれでは今日もはりきってスタンプしましょう!"
        else:
            self.message = self.message + "\n休日対応おつかれさまです。"

    def postMessage(self):
        self.bot.chat_postMessage(channel=const.CHANNEL_NAME,
                                  text=self.message)

    #ランキング処理
    def setRankingMessage(self, rank_list, user_flag):
        rank = 1
        i = 0
        while i < len(rank_list):
            if (user_flag):
                self.message = self.message + '\n{}位 {} {}'.format(
                    rank, self.getUsername(rank_list[i][0]), rank_list[i][1])
            else:
                self.message = self.message + '\n{}位 :{}: {}'.format(
                    rank, rank_list[i][0], rank_list[i][1])
            #同列順位の処理
            j = 1
            while (i + j) < len(rank_list):
                if (rank_list[i][1] == rank_list[i + j][1]):
                    if (user_flag):
                        self.message = self.message + '  {} {}'.format(
                            self.getUsername(rank_list[i + j][0]),
                            rank_list[i + j][1])
                    else:
                        self.message = self.message + '  :{}: {}'.format(
                            rank_list[i + j][0], rank_list[i + j][1])
                    j = j + 1
                else:
                    break
            i = i + j
            rank = rank + j
            if (rank > const.RANK_LIMIT):
                break
        self.message = self.message + '\n'

    #チャットのランク処理
    def setChatRankingMessage(self, sorted_chat):
        rank = 1
        for chat in sorted_chat:
            link = self.bot.chat_getPermalink(channel=chat[0],
                                              message_ts=chat[1])
            self.message = self.message + link["permalink"] + '\n'
            rank = rank + 1
            if (rank > const.CHAT_RANK_LIMIT):
                break

    #ユーザーの表示名を取得する
    def getUsername(self, user_id):
        user_info = self.bot.users_info(user=user_id)
        return user_info['user']['profile']['real_name']

    # デストラクタ
    def __del__(self):
        print("CountStamp End")
示例#12
0
class CSDigest:
    def __init__(self, config_path) -> None:
        print("loading data")
        # handling files
        with open(config_path) as cfg:
            config = yaml.load(cfg, Loader=yaml.FullLoader)
        with open(config["token_path"]) as tk:
            self.token = tk.readline()
        with open(config["template_path"]) as tp:
            self.temp = BeautifulSoup(tp, "html.parser")
        self.cache_im = os.path.join("cache", "images")
        os.makedirs(self.cache_im, exist_ok=True)
        self.ts_old = dateparser.parse(config["time_span"]).timestamp()
        # initialize objects
        self.foodnet = models.load_model("./foodnet/model")
        self.datagen = ImageDataGenerator(rescale=1.0 / 255)
        self.client = WebClient(token=self.token)
        self.channels = pd.DataFrame(
            self.client.conversations_list(limit=1000)["channels"]
        ).set_index("name")
        self.users = pd.DataFrame(
            self.client.users_list(limit=1000)["members"]
        ).set_index("id")
        self.users["display_name"] = self.users["profile"].apply(
            self.extract_profile, key="display_name"
        )
        print("fetching messages")
        # get messages
        ms_general = self.get_msg("general", same_user=False)
        ms_home = self.get_msg("homesanity", ts_thres=0)
        ms_quote = self.get_msg("quotablequotes", ts_thres=120, same_user=False)
        print("building newsletter")
        # handle carousel
        if len(ms_general) > 0:
            ms_general["class"] = ms_general.apply(self.classify_msg, axis="columns")
            ms_tada = ms_general[ms_general["class"] == "tada"]
            if len(ms_tada) > 0:
                ms_tada["permalink"] = ms_tada.apply(self.get_permalink, axis="columns")
                self.build_carousel(ms_tada)
        # handle food
        ms_files = pd.concat([ms_general, ms_home])
        if len(ms_files) > 0:
            ms_files["file_path"] = ms_files.apply(self.download_images, axis="columns")
            ms_files = ms_files[ms_files["file_path"].astype(bool)]
            ms_files["food_prob"] = ms_files["file_path"].apply(self.classify_food)
            ms_files["food_path"] = ms_files.apply(self.filter_food, axis="columns")
            ms_food = ms_files[ms_files["food_path"].notnull()]
            ms_food["permalink"] = ms_food.apply(self.get_permalink, axis="columns")
            ms_food["aspect"] = ms_food["food_path"].apply(self.get_img_aspect)
            ms_food = ms_food.sort_values("aspect", ascending=True)
            self.build_portfolio(ms_food)
        # handle quotes
        if len(ms_quote) > 0:
            ms_quote = ms_quote[~ms_quote["files"].astype(bool)]
            ms_quote["permalink"] = ms_quote.apply(self.get_permalink, axis="columns")
            self.build_quotes(ms_quote)

    def get_msg(self, channel, ts_thres=5, same_user=True):
        ms = pd.DataFrame(
            self.client.conversations_history(
                channel=self.channels.loc[channel]["id"],
                oldest=self.ts_old,
                limit=1000,
            )["messages"]
        )
        if len(ms) > 0:
            try:
                ms = ms[ms["subtype"].isnull()]
            except KeyError:
                pass
            if len(ms) > 0:
                ms = (
                    self.cluster_msg(ms, ts_thres=ts_thres, same_user=same_user)
                    .groupby("component")
                    .apply(self.merge_msg)
                    .reset_index()
                    .apply(self.translate_msg_user, axis="columns")
                )
                ms["text"] = ms["text"].apply(
                    emojize, use_aliases=True, variant="emoji_type"
                )
                ms["channel"] = self.channels.loc[channel]["id"]
        return ms

    def classify_msg(self, msg_df):
        if msg_df["reactions"]:
            tada = list(filter(lambda r: r["name"] == "tada", msg_df["reactions"]))
            if tada and tada[0]["count"] > 5:
                return "tada"

    def cluster_msg(self, msg_df, ts_thres, same_user):
        ts_dist = pdist(msg_df["ts"].values.astype(float).reshape((-1, 1)))
        txt_dist = pdist(CountVectorizer().fit_transform(msg_df["text"]).toarray())
        adj = squareform(ts_dist) < ts_thres * 60
        if same_user:
            user_dist = pdist(
                msg_df["user"].values.reshape((-1, 1)),
                metric=lambda u, v: 0 if u == v else 1,
            )
            adj = adj * (squareform(user_dist) < 1)
        n_comp, lab = connected_components(adj, directed=False)
        msg_df["component"] = lab
        return msg_df

    def merge_msg(self, msg_df, multiple_users="first"):
        msg_df = msg_df.sort_values("ts")
        if multiple_users == "forbid":
            user = msg_df["user"].unique()
            assert len(user) == 1
            user = user.item()
        elif multiple_users == "first":
            user = msg_df.iloc[0]["user"]
            msg_df = msg_df[msg_df["user"] == user]
        else:
            raise ValueError("multiple_users=={} not understood".format(multiple_users))
        try:
            reactions = msg_df["reactions"].dropna().values
            reactions = sum(reactions, [])
        except KeyError:
            reactions = []
        try:
            files = msg_df["files"].dropna().values
            files = sum(files, [])
        except KeyError:
            files = []
        try:
            attch = msg_df["attachments"].dropna().values
            attch = sum(attch, [])
        except KeyError:
            attch = []
        return pd.Series(
            {
                "user": user,
                "text": "\n".join(msg_df["text"].values),
                "ts": msg_df.iloc[0].loc["ts"],
                "reactions": reactions,
                "files": files,
                "attachments": attch,
            }
        )

    def translate_msg_user(self, msg_row, substitute=["display_name", "name"]):
        try:
            msg_row["user"] = self.translate_user(msg_row["user"], substitute)
            msg_row["text"] = re.sub(
                r"\<\@(.*?)\>",
                lambda u: self.translate_user(u.group(1), substitute),
                msg_row["text"],
            )
        except TypeError:
            pass
        return msg_row

    def translate_user(self, uid, substitute):
        for sub in substitute:
            if sub == "real_name":
                prefix = ""
            else:
                prefix = "@"
            user = self.users.loc[uid, sub]
            if type(user) == str and bool(user):
                return prefix + user

    def extract_profile(self, prof, key):
        try:
            return prof[key]
        except KeyError:
            return np.nan

    def get_permalink(self, msg_row):
        return self.client.chat_getPermalink(
            channel=msg_row["channel"], message_ts=str(msg_row["ts"])
        )["permalink"]

    def download_images(self, msg_row):
        fpaths = []
        for fdict in msg_row["files"]:
            try:
                mimietype = fdict["mimetype"]
            except KeyError:
                continue
            if mimietype.startswith("image"):
                fpath = os.path.join(
                    self.cache_im,
                    ".".join([fdict["id"], fdict["filetype"]]),
                )
                resp = requests.get(
                    fdict["url_private_download"],
                    headers={"Authorization": "Bearer {}".format(self.token)},
                )
                open(fpath, "wb").write(resp.content)
                fpaths.append(fpath)
        for fdict in msg_row["attachments"]:
            try:
                url = fdict["image_url"]
            except KeyError:
                continue
            fpath = os.path.join(self.cache_im, url.split("/")[-1].split("?")[0])
            resp = requests.get(url)
            open(fpath, "wb").write(resp.content)
            fpaths.append(fpath)
        return fpaths

    def build_carousel(self, msg_df):
        indicator = self.temp.find("ol", {"id": "carousel-inds"})
        ind_temp = indicator.find("li", {"id": "carousel-ind-template"}).extract()
        sld_wrapper = self.temp.find("div", {"id": "carousel-slides"})
        tada_temp = self.temp.find("div", {"id": "carousel-slide-template"}).extract()
        for (imsg, msg), icss in zip(
            msg_df.reset_index(drop=True).iterrows(), itt.cycle(np.arange(3) + 1)
        ):
            cur_ind = copy.copy(ind_temp)
            cur_ind["data-slide-to"] = str(imsg)
            cur_tada = copy.copy(tada_temp)
            cur_tada.find("h3", {"id": "carousel-slide-message"}).string = (
                msg["text"] if len(msg["text"]) <= 320 else msg["text"][:320] + "..."
            )
            cur_tada.find(True, {"id": "carousel-slide-author"}).string = " ".join(
                [
                    msg["user"],
                    datetime.fromtimestamp(float(msg["ts"])).strftime("%b %d"),
                ]
            )
            cur_tada.find("a")["href"] = msg["permalink"]
            if re.search("birthday", msg["text"].lower()):
                cur_tada["class"] = [
                    "carousel-birthday" if c == "carousel-tada-1" else c
                    for c in cur_tada["class"]
                ]
            else:
                cur_tada["class"] = [
                    "carousel-tada-{}".format(icss) if c == "carousel-tada-1" else c
                    for c in cur_tada["class"]
                ]
            if not imsg == 0:
                del cur_ind["class"]
                cur_tada["class"] = list(
                    filter(lambda c: c != "active", cur_tada["class"])
                )
            indicator.append(cur_ind)
            sld_wrapper.append(cur_tada)

    def write_html(self):
        with open("csdigest.html", "w", encoding="utf-8") as outf:
            outf.write(str(self.temp))

    def classify_food(self, img_path):
        try:
            imgs = [img_to_array(load_img(imp).resize((512, 512))) for imp in img_path]
        except:
            return np.atleast_1d(np.ones(len(img_path))).tolist()
        predict = self.foodnet.predict(self.datagen.flow(np.stack(imgs)))
        return np.atleast_1d(predict.squeeze()).tolist()

    def filter_food(self, msg_row, thres=0.1):
        minval, minidx = np.min(msg_row["food_prob"]), np.argmin(msg_row["food_prob"])
        if minval < thres:
            return msg_row["file_path"][minidx]
        else:
            return np.nan

    def get_img_aspect(self, path):
        img = load_img(path)
        return img.size[0] / img.size[1]

    def build_portfolio(self, msg_df):
        porto = self.temp.find("div", {"id": "portfolio-container"})
        port_temp = self.temp.find("div", {"id": "portfolio-template"}).extract()
        del port_temp["id"]
        for imsg, msg in msg_df.iterrows():
            cur_temp = copy.copy(port_temp)
            cur_temp.img["src"] = msg["food_path"]
            cur_temp.find("a", {"id": "port-zoom-link"})["href"] = msg["food_path"]
            cur_temp.find("a", {"id": "port-msg-link"})["href"] = msg["permalink"]
            txt = msg["text"]
            if len(txt) > 150:
                txt = txt[:150] + "..."
            cur_temp.find(True, {"id": "port-item-text"}).string = txt
            # cur_temp.find(True, {"id": "port-item-text"}).string = str(
            #     np.min(msg["food_prob"])
            # )
            porto.append(cur_temp)

    def build_quotes(self, msg_df):
        quotes = self.temp.find("div", {"id": "quote-block"})
        quote_temp = quotes.find("div", {"id": "quote-template"}).extract()
        indicators = self.temp.find("ol", {"id": "quote-indicator-wrap"})
        ind_temp = indicators.find("li", {"id": "quote-indicator"}).extract()
        for imsg, msg in msg_df.reset_index(drop=True).iterrows():
            cur_quote = copy.copy(quote_temp)
            cur_quote.find("a", {"id": "quote-link"})["href"] = msg["permalink"]
            cur_quote.find("p", {"id": "quote-content"}).string = (
                msg["text"] if len(msg["text"]) <= 400 else msg["text"][:400] + "..."
            )
            cur_quote.find("span", {"id": "quote-name"}).string = msg["user"]
            cur_ind = copy.copy(ind_temp)
            cur_ind["data-slide-to"] = str(imsg)
            if imsg > 0:
                cur_quote["class"] = list(
                    filter(lambda c: c != "active", cur_quote["class"])
                )
                cur_ind["class"] = list(
                    filter(lambda c: c != "active", cur_ind["class"])
                )
            quotes.append(cur_quote)
            indicators.append(cur_ind)
示例#13
0
class SlackMonitor(Monitor):
    """
    Create a monitoring service that alerts on Task failures / completion in a Slack channel
    """
    def __init__(self,
                 slack_api_token,
                 channel,
                 message_prefix=None,
                 filters=None):
        # type: (str, str, Optional[str], Optional[List[Callable[[Task], bool]]]) -> ()
        """
        Create a Slack Monitoring object.
        It will alert on any Task/Experiment that failed or completed

        :param slack_api_token: Slack bot API Token. Token should start with "xoxb-"
        :param channel: Name of the channel to post alerts to
        :param message_prefix: optional message prefix to add before any message posted
            For example: message_prefix="Hey <!here>,"
        :param filters: An optional collection of callables that will be passed a Task
            object and return True/False if it should be filtered away
        """
        super(SlackMonitor, self).__init__()
        self.channel = "{}".format(channel[1:] if channel[0] ==
                                   "#" else channel)
        self.slack_client = WebClient(token=slack_api_token)
        self.min_num_iterations = 0
        self.filters = filters or list()
        self.status_alerts = [
            "failed",
        ]
        self.include_manual_experiments = False
        self.include_archived = False
        self.verbose = False
        self._channel_id = None
        self._message_prefix = "{} ".format(
            message_prefix) if message_prefix else ""
        self.check_credentials()

    def check_credentials(self):
        # type: () -> ()
        """
        Check we have the correct credentials for the slack channel
        """
        self.slack_client.api_test()

        # Find channel ID
        channels = []
        cursor = None
        while True:
            response = self.slack_client.conversations_list(cursor=cursor)
            channels.extend(response.data["channels"])
            cursor = response.data["response_metadata"].get("next_cursor")
            if not cursor:
                break
        channel_id = [
            channel_info.get("id") for channel_info in channels
            if channel_info.get("name") == self.channel
        ]
        if not channel_id:
            raise ValueError(
                "Error: Could not locate channel name '{}'".format(
                    self.channel))

        # test bot permission (join channel)
        self._channel_id = channel_id[0]
        self.slack_client.conversations_join(channel=self._channel_id)

    def post_message(self, message, retries=1, wait_period=10.0):
        # type: (str, int, float) -> ()
        """
        Post message on our slack channel

        :param message: Message to be sent (markdown style)
        :param retries: Number of retries before giving up
        :param wait_period: wait between retries in seconds
        """
        for i in range(retries):
            if i != 0:
                sleep(wait_period)

            try:
                self.slack_client.chat_postMessage(
                    channel=self._channel_id,
                    blocks=[
                        dict(type="section",
                             text={
                                 "type": "mrkdwn",
                                 "text": message
                             })
                    ],
                )
                return
            except SlackApiError as e:
                print(
                    'While trying to send message: "\n{}\n"\nGot an error: {}'.
                    format(message, e.response["error"]))

    def get_query_parameters(self):
        # type: () -> dict
        """
        Return the query parameters for the monitoring.

        :return dict: Example dictionary: {'status': ['failed'], 'order_by': ['-last_update']}
        """
        filter_tags = list() if self.include_archived else ["-archived"]
        if not self.include_manual_experiments:
            filter_tags.append("-development")
        return dict(status=self.status_alerts,
                    order_by=["-last_update"],
                    system_tags=filter_tags)

    def process_task(self, task):
        """
        # type: (Task) -> ()
        Called on every Task that we monitor.
        This is where we send the Slack alert

        :return: None
        """
        # skipping failed tasks with low number of iterations
        if self.min_num_iterations and task.get_last_iteration(
        ) < self.min_num_iterations:
            print("Skipping {} experiment id={}, number of iterations {} < {}".
                  format(task.status, task.id, task.get_last_iteration(),
                         self.min_num_iterations))
            return
        if any(f(task) for f in self.filters):
            if self.verbose:
                print("Experiment id={} {} did not pass all filters".format(
                    task.id, task.status))
            return

        print('Experiment id={} {}, raising alert on channel "{}"'.format(
            task.id, task.status, self.channel))

        console_output = task.get_reported_console_output(number_of_reports=3)
        message = "{}Experiment ID <{}|{}> *{}*\nProject: *{}*  -  Name: *{}*\n" "```\n{}\n```".format(
            self._message_prefix,
            task.get_output_log_web_page(),
            task.id,
            task.status,
            task.get_project_name(),
            task.name,
            ("\n".join(console_output))[-2048:],
        )
        self.post_message(message, retries=5)
示例#14
0
class ChannelManager(object):

    def __init__(self, token=Path('SLACK_OAUTH_TOKEN').read_text()):
        self.token = token
        self.client = WebClient(token=self.token)
        self.channels = [ ]

    # Sets the internal channels field
    def list(self):
        resp = self.client.conversations_list(exclude_archived=True, types="public_channel,private_channel", limit=500)
        assert resp.data['ok']
        self.channels = []
        for channel in resp.data['channels']:
            self.channels.append({
                'ID': channel['id'],
                'Channel Name': channel['name'],
                'Purpose': channel['purpose']['value']
            })
    
    def exists(self, name):
        for channel in self.channels:
            if channel['Channel Name'] == name:
                return True
        return False

    def get_id(self, name):
        for channel in self.channels:
            if channel['Channel Name'] == name:
                return channel['ID']
        return False
    
    def get_purpose(self, name):
        for channel in self.channels:
            if channel['Channel Name'] == name:
                return channel['Purpose']
        return False


    def create(self, name, is_private):
        print(f'Creating channel {name} ...')
        resp = self.client.conversations_create(name=name, is_private=is_private)
        return resp
    
    def set_purpose(self, name, purpose):
        chan_id = self.get_id(name)
        # TODO(arjun): Hack. The name that comes back has HTML entities for
        # special characters. God help us all.
        if self.get_purpose(name) != "":
            return
        print(f'Setting purpose for {name} ...')
        self.client.conversations_setPurpose(channel=chan_id, purpose=purpose)

    def post(self, name, message):
        channel_id = self.get_id(name)
        if not channel_id:
            return False

        try:
            resp = self.client.chat_postMessage(channel=channel_id, text=message)
            if resp.data['ok']:
                return True
            else:
                print(f"Response was {resp.data}")
                return False    
        except SlackApiError as e:
            if e.response["error"] != "not_in_channel":
                raise e
            self.client.conversations_join(channel=channel_id)
            self.client.chat_postMessage(channel=channel_id, text=message)
            return True
示例#15
0
class Slack:
    def __init__(self, token, channel_name=None, channel_id=None):
        self.client = WebClient(token)
        self.channel_id = channel_id

        channels = self.client.conversations_list(
            types='public_channel,private_channel')

        if channel_name:
            for channel in channels['channels']:
                if channel['name'] == channel_name:
                    self.channel_id = channel['id']
                    break
        if not self.channel_id:
            self.channel_id = self.client.conversations_create(
                name=channel_name.lower(), is_private=True)['channel']['id']
            admins = [
                u['id'] for u in self.client.users_list()['members']
                if u.get('is_admin') or u.get('is_owner')
            ]
            self.client.conversations_invite(channel=self.channel_id,
                                             users=admins)

    def send_snippet(self,
                     title,
                     initial_comment,
                     code,
                     code_type='python',
                     thread_ts=None):
        return self.client.files_upload(
            channels=self.channel_id,
            title=title,
            initial_comment=initial_comment.replace('<br>', ''),
            content=code,
            filetype=code_type,
            thread_ts=thread_ts)['ts']

    def send_exception_snippet(self,
                               domain,
                               event,
                               code_type='python',
                               thread_ts=None):
        message = traceback.format_exc() + '\n\n\n' + dumps(event, indent=2)
        subject = 'Error occurred in ' + domain
        self.send_snippet(subject,
                          subject,
                          message,
                          code_type=code_type,
                          thread_ts=thread_ts)

    def send_raw_message(self, blocks, thread_ts=None):
        return self.client.chat_postMessage(channel=self.channel_id,
                                            blocks=blocks,
                                            thread_ts=thread_ts)['ts']

    def update_raw_message(self, ts, blocks):
        self.client.chat_update(channel=self.channel_id, blocks=blocks, ts=ts)

    def get_perm_link(self, ts):
        return self.client.chat_getPermalink(channel=self.channel_id,
                                             message_ts=ts)['permalink']

    def send_message(self, message, attachment=None, thread_ts=None):
        blocks = [{
            'type': 'section',
            'text': {
                'type': 'mrkdwn',
                'text': message.replace('<br>', '')
            }
        }, {
            'type': 'divider'
        }]
        if attachment:
            blocks[0]['accessory'] = {
                'type': 'button',
                'text': {
                    'type': 'plain_text',
                    'text': attachment['text'],
                    'emoji': True
                },
                'url': attachment['value']
            }

        return self.send_raw_message(blocks, thread_ts)
示例#16
0
# Set Slack tokens
key = sys.argv[2]
slack = WebClient(token=key)

#Define AWS Database connection criteria
mydb = pymysql.connect(host=host,
                       port=port,
                       user=user,
                       password=password,
                       db=db,
                       charset='utf8mb4',
                       cursorclass=pymysql.cursors.DictCursor)

# Get channel list
channels_response = slack.conversations_list(limit=999)
channels = channels_response.data['channels']
channels_df = pd.json_normalize(channels)
channels_df = channels_df[['id', 'name', 'created', 'is_archived']]
channels_df = channels_df.rename(
    columns={
        'id': 'channel_id',
        'name': 'ao',
        'created': 'channel_created',
        'is_archived': 'archived'
    })

# Now connect to the AWS database and insert some rows!
print('Updating Slack channel list / AOs for region...')
try:
    with mydb.cursor() as cursor: