def __init__(self): _settings, _ = import_settings() slack_api_token = _settings.get('SLACK_API_TOKEN', None) http_proxy = _settings.get('HTTP_PROXY', None) self.rtm_client = RTMClient(token=slack_api_token, proxy=http_proxy) self.web_client = WebClient(token=slack_api_token, proxy=http_proxy) self._bot_info = {} self._users = {} self._channels = {}
def __init__(self, bot_user_token, bot_id=None): self.name = BOT_NAME self.bot_id = bot_id if not self.bot_id: # Read the bot's id by calling auth.test response = WebClient(token=bot_user_token).api_call('auth.test') self.bot_id = response['user_id'] logger.info(f"My bot_id is {self.bot_id}") self.sc = RTMClient(token=bot_user_token, run_async=True) # Connect our callback events to the RTM client RTMClient.run_on(event="hello")(self.on_hello) RTMClient.run_on(event="message")(self.on_message) RTMClient.run_on(event="goodbye")(self.on_goodbye) # Startup our client event loop self.future = self.sc.start() self.bot_start = dt.now() self.at_bot = f'<@{self.bot_id}>' logger.info("Created new SlackClient Instance")
class LowLevelSlackClient(metaclass=Singleton): def __init__(self): _settings, _ = import_settings() slack_api_token = _settings.get('SLACK_API_TOKEN', None) http_proxy = _settings.get('HTTP_PROXY', None) self.rtm_client = RTMClient(token=slack_api_token, proxy=http_proxy) self.web_client = WebClient(token=slack_api_token, proxy=http_proxy) self._bot_info = {} self._users = {} self._channels = {} @staticmethod def get_instance() -> 'LowLevelSlackClient': return LowLevelSlackClient() def _register_user(self, user_response): user = User.from_api_response(user_response) self._users[user.id] = user return user def _register_channel(self, channel_response): channel = Channel.from_api_response(channel_response) self._channels[channel.id] = channel return channel def ping(self): # Ugly hack because some parts of slackclient > 2.0 are async-only (like the ping function) # and Slack Machine isn't async yet loop = asyncio.new_event_loop() result = self.rtm_client.ping() loop.run_until_complete(result) def _on_open(self, **payload): # Set bot info self._bot_info = payload['data']['self'] # Build user cache all_users = call_paginated_endpoint(self.web_client.users_list, 'members') for u in all_users: self._register_user(u) logger.debug("Number of users found: %s" % len(self._users)) logger.debug("Users: %s" % ", ".join([ f"{u.profile.display_name}|{u.profile.real_name}" for u in self._users.values() ])) # Build channel cache all_channels = call_paginated_endpoint( self.web_client.conversations_list, 'channels', types='public_channel,private_channel') for c in all_channels: self._register_channel(c) logger.debug("Number of channels found: %s" % len(self._channels)) logger.debug("Channels: %s" % ", ".join([c.name for c in self._channels.values()])) def _on_team_join(self, **payload): user = self._register_user(payload['data']['user']) logger.debug("User joined team: %s" % user) def _on_user_change(self, **payload): user = self._register_user(payload['data']['user']) logger.debug("User changed: %s" % user) def _on_channel_created(self, **payload): channel_resp = self.web_client.channels_info( channel=payload['data']['channel']['id']) channel = self._register_channel(channel_resp['channel']) logger.debug("Channel created: %s" % channel) def _on_channel_updated(self, **payload): data = payload['data'] if isinstance(data['channel'], dict): channel_id = data['channel']['id'] else: channel_id = data['channel'] channel_resp = self.web_client.channels_info(channel=channel_id) channel = self._register_channel(channel_resp['channel']) logger.debug("Channel updated: %s" % channel) def _on_channel_deleted(self, **payload): channel = self._channels[payload['data']['channel']] del self._channels[payload['data']['channel']] logger.debug("Channel %s deleted" % channel.name) @property def bot_info(self) -> Dict[str, str]: return self._bot_info def start(self): RTMClient.on(event='open', callback=self._on_open) RTMClient.on(event='team_join', callback=self._on_team_join) RTMClient.on(event='channel_created', callback=self._on_channel_created) RTMClient.on(event='channel_deleted', callback=self._on_channel_deleted) RTMClient.on(event='channel_rename', callback=self._on_channel_updated) RTMClient.on(event='channel_archive', callback=self._on_channel_updated) RTMClient.on(event='channel_unarchive', callback=self._on_channel_updated) RTMClient.on(event='user_change', callback=self._on_user_change) self.rtm_client.start() @property def users(self) -> Dict[str, User]: return self._users @property def channels(self) -> Dict[str, Channel]: return self._channels
def start(self): RTMClient.on(event='open', callback=self._on_open) RTMClient.on(event='team_join', callback=self._on_team_join) RTMClient.on(event='channel_created', callback=self._on_channel_created) RTMClient.on(event='channel_deleted', callback=self._on_channel_deleted) RTMClient.on(event='channel_rename', callback=self._on_channel_updated) RTMClient.on(event='channel_archive', callback=self._on_channel_updated) RTMClient.on(event='channel_unarchive', callback=self._on_channel_updated) RTMClient.on(event='user_change', callback=self._on_user_change) self.rtm_client.start()
class SlackClient: def __init__(self, bot_user_token, bot_id=None): self.name = BOT_NAME self.bot_id = bot_id if not self.bot_id: # Read the bot's id by calling auth.test response = WebClient(token=bot_user_token).api_call('auth.test') self.bot_id = response['user_id'] logger.info(f"My bot_id is {self.bot_id}") self.sc = RTMClient(token=bot_user_token, run_async=True) # Connect our callback events to the RTM client RTMClient.run_on(event="hello")(self.on_hello) RTMClient.run_on(event="message")(self.on_message) RTMClient.run_on(event="goodbye")(self.on_goodbye) # Startup our client event loop self.future = self.sc.start() self.bot_start = dt.now() self.at_bot = f'<@{self.bot_id}>' logger.info("Created new SlackClient Instance") def __repr__(self): return self.at_bot def __str__(self): return self.__repr__() def on_hello(self, **payload): """Slack is confirming our connection request""" logger.info(f"{self} is connected to Slack's RTM server") self.post_message(f"{self.name} is now online") # Callback Methods def on_message(self, **payload): """Slack has sent the bot a message""" data = payload["data"] if "text" in data and self.at_bot in data['text']: # parse everything after <@Bot> mention raw_cmd = data["text"].split(self.at_bot)[1].strip().lower() chan = data['channel'] # handling cmd response = self.handle_command(raw_cmd, chan) self.post_message(response, chan) def handle_command(self, raw_cmd, chan): "Parses a raw_cmd string directed at the bot" # return "To crush your enemies, to see them driven before you," # + "and to hear the lamentations of their people!" response = None args = shlex.split(raw_cmd) cmd = args[0].lower() logger.info(f'{self} received command: "{raw_cmd}"') if cmd not in bot_commands: response = f'Unknown Command: "{cmd}"' logger.error(f'{self} {response}') # Now there is a valid command that must be processed elif cmd == 'help': response = "Available Commands:\n" + formatted_dict(bot_commands) logger.info(response) elif cmd == 'ping': response = f"{self.name} is active, current uptime:" + \ f" {self.get_uptime()}" logger.info(response) elif cmd == 'list': pass elif cmd == 'exit' or cmd == 'quit': response = f"{self.name} Manual exit request at" + \ f"{self.get_uptime()} uptime..." logger.warning(response) # The following is extremely hacky. DO NOT USE IN PRODUCTION!!! self.post_message(response) # Allow some time for sending the impending death message time.sleep(5.0) os.kill(os.getpid(), signal.SIGTERM) return response def on_goodbye(self, **payload): logger.warning(f"{self} is disconnecting now") def post_message(self, msg_text, channel=BOT_CHAN): """Sends a message to a Slack channel""" assert self.sc._web_client is not None if msg_text: self.sc._web_client.chat_postMessage( channel=channel, text=msg_text ) def get_uptime(self): """Returns the duration for how long this client has been connected""" return dt.now() - self.bot_start # Waiting for something method def run(self): logger.info("Waiting for things to happen...") loop = self.future.get_loop() # Forever Waiting for a CTRL+C or a SIGTERM or SIGINT loop.run_until_complete(self.future) logger.info("Things are now done happening.")