Example #1
0
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))
Example #2
0
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"]
        }