class SlackBot: def __init__(self, loop, config_file: str = "config.yaml"): self.loop = loop self.config = Config.from_file(config_file) self.session = ClientSession() self.slack_client = SlackAPI(token=self.config.bot_token, session=self.session) self.waiters = [] self.commands = self.config.commands self.prefix = self.config.prefix self.command_types = { "get_id": self.get_id, "read_web": self.read_web, "run_cmd": self.run_cmd } async def run(self): print("Running") await self.rtm() async def get_id(self, command, message: Message): text = message["text"] mentioned_ids = re.findall(mention, text) await self.send_message(message["channel"], " ".join(mentioned_ids)) async def read_web(self, command, message: Message): async with self.session.get(command["url"]) as resp: resp = await resp.text() await self.send_message(message["channel"], resp) async def run_cmd(self, command, message: Message): output = subprocess.run(command["cmd"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if command["display_output"]: resp = (f"```\n{output.stdout.decode().strip()}\n```" if output.stdout else "") + \ (f"```\n{output.stderr.decode().strip()}\n```" if output.stderr else "") await self.send_message(message["channel"], resp) async def run_command(self, command, message: Message): """ Run a command after checking permissions. """ channel = message["channel"] if "acl1" in command: if not command["acl1"] or message["user"] not in command["acl1"]: return await self.send_message( channel, "You are not in the acl list for this command.") if "reply1" in command: await self.send_message(channel, command["reply1"]) if "acl2" in command and command["acl2"]: message2 = await self.wait_for_message( f"{self.prefix}{command['trigger2']}", timeout=command["timeout"]) if message2 is None: return await self.send_message(channel, "Timed out.") if message2["user"] not in command["acl2"]: return await self.send_message( channel, "You are not in the acl list for this command.") if message2["user"] == message["user"]: return await self.send_message( channel, "The authorising user cannot be the initiating user.") if "reply2" in command: await self.send_message(channel, command["reply2"]) await self.command_types[command["type"]](command, message) if "complete" in command: await self.send_message(channel, command["complete"]) async def wait_for_message(self, text: str, timeout: int) -> Message: """ Wait until receive a message `text` within `timeout`. Returns None if timed out or the message. """ future = loop.create_future() check = lambda event: event.event["text"] == text self.waiters.append((future, check)) try: event = await asyncio.wait_for(future, timeout=timeout) except asyncio.TimeoutError: return None return event async def send_message(self, channel: str, message: str): """ Send `message` to `channel` """ await self.slack_client.query(slack.methods.CHAT_POST_MESSAGE, data={ "channel": channel, "text": message, }) async def rtm(self): """ Main loop, parses Slack messages and runs them """ async for event in self.slack_client.rtm(): if isinstance(event, Message): for future, check in self.waiters: if check(event): future.set_result(event) if not event.event["text"].startswith(self.prefix): continue command = event.event["text"].replace(self.prefix, "", 1).split(" ", 1)[0] if command in self.commands: asyncio.ensure_future( self.run_command(self.commands[command], event.event))
class SlackHandler(GenericHandler): def __init__(self): super(SlackHandler, self).__init__() session = ClientSession() self.slack_client = SlackAPI(token=None, session=session) self.channels = WeakValueDictionary() async def setup(self, token): self.slack_client._token = token await self.update_team_info() await self.update_channels() asyncio.ensure_future(self.rtm()) print("Logged into Slack") def get_channel(self, serialised) -> Optional[SlackChannel]: channel_id = serialised["id"] try: return self.channels[channel_id] except KeyError: pass rtn = SlackChannel(channel_id, self.slack_client) self.channels[channel_id] = rtn return rtn async def rtm(self): async for event in self.slack_client.rtm(): if isinstance(event, Message): await asyncio.gather(*[channel.on_message_handler(event) for channel in self.channels.values()]) if isinstance(event, Event): if event["type"].startswith("channel_"): await self.update_channels() @property def serialised_channels(self): return self._serialised_channels async def update_channels(self): channels = await self.slack_client.query( slack.methods.CONVERSATIONS_LIST, data={ "exclude_archived": True } ) self._serialised_channels = [ { "type": "slack", "id": channel["id"], "name": channel["name"], "server": self.team_info } for channel in channels["channels"] if channel["is_member"] ] async def update_team_info(self): team_info = await self.slack_client.query("https://slack.com/api/team.info") self.team_info = { "id": team_info["team"]["id"], "name": team_info["team"]["name"], "icon": team_info["team"]["icon"]["image_132"] }