コード例 #1
0
ファイル: dev.py プロジェクト: Yaulendil/petal
 async def cmd_bool(self, src, **_):
     m = Menu(self.client,
              src.channel,
              "Choice",
              "Test Function",
              user=src.author)
     m.add_section(repr(await m.get_bool()))
     await m.post()
コード例 #2
0
ファイル: dev.py プロジェクト: Yaulendil/petal
    async def cmd_menu2(self, src, **_):
        m = Menu(self.client,
                 src.channel,
                 "Choice",
                 "Test Function",
                 user=src.author)

        m.add_section("\n".join(await m.get_multi(
            ["asdf", "qwert", "asdfqwert", "qwertyuiop"])) or "(None)")
        await m.post()
コード例 #3
0
ファイル: dev.py プロジェクト: Yaulendil/petal
    async def cmd_poll(self,
                       args,
                       src,
                       _question: str = "",
                       _channel: int = None,
                       _time: int = 0,
                       **_):
        if len(args) < 2:
            return "Must provide at least two options."

        duration = _time if _time > 0 else 3600

        title = "Poll"
        if _question:
            title += ": " + _question

        if _channel:
            targ = self.client.get_channel(_channel)
        else:
            targ = src.channel
        if not targ:
            return "Invalid Channel"

        poll = Menu(self.client, targ, title, "Test Function")
        await poll.get_poll(args, duration)
コード例 #4
0
    async def cmd_purge(self, args, src: Src, **_):
        """Purge up to 200 messages in the current channel.

        Syntax: `{p}purge <number of messages to delete>`
        """
        if len(args) < 1:
            raise CommandArgsError("Please provide a number between 1 and 200")
        try:
            delete_num = int(args[0].strip())
        except ValueError:
            raise CommandInputError("Please make sure your input is a number")
        if not 0 < delete_num <= 200:
            raise CommandInputError(
                "Can only delete between 0 and 200 Messages.")

        confirm_menu: Menu = Menu(
            self.client,
            src.channel,
            "Confirm Purge",
            f"Really delete the last {delete_num} Messages in this Channel?\n"
            f"(This Menu and your Invocation will also be deleted.)",
            src.author,
        )
        confirmed = await confirm_menu.get_bool()

        if confirmed is True:
            try:
                await src.channel.purge(limit=delete_num + 2, check=None)
            except discord.errors.Forbidden:
                raise CommandOperationError(
                    "I don't have permissions to purge messages.")
            else:
                logEmbed = discord.Embed(
                    title="Purge Event",
                    description=f"{delete_num} messages were purged from "
                    f"`#{src.channel.name}` in {src.guild.name} by "
                    f"`{src.author.name}#{src.author.discriminator}`.",
                    color=0x0ACDFF,
                )
                await self.client.log_moderation(embed=logEmbed)

        elif confirmed is False:
            await confirm_menu.add_section("Purge Cancelled.")
            await confirm_menu.post()
        else:
            await confirm_menu.add_section("Confirmation Timed Out.")
            await confirm_menu.post()
コード例 #5
0
ファイル: dev.py プロジェクト: Yaulendil/petal
    async def cmd_vote(self,
                       src,
                       _question: str = None,
                       _channel: int = None,
                       _time: int = 0,
                       **_):
        duration = _time if _time > 0 else 3600
        title = "Vote: " + _question if _question else "Vote"

        if _channel:
            targ = self.client.get_channel(_channel)
        else:
            targ = src.channel
        if not targ:
            return "Invalid Channel"

        poll = Menu(self.client, targ, title, "Test Function")
        await poll.get_vote(duration)
コード例 #6
0
ファイル: dev.py プロジェクト: Yaulendil/petal
    async def cmd_menu(self, src, **_):
        m = Menu(self.client,
                 src.channel,
                 "Choice",
                 "Test Function",
                 user=src.author)

        m.add_section(
            await m.get_one(["asdf", "qwert", "asdfqwert", "qwertyuiop"])
            or "(None)")
        await m.post()
        m.add_section(
            await m.get_one(["zxcv", "qazwsx", "yuiop", "poiuytrewq"])
            or "(None)",
            # overwrite=0,
        )
        await m.post()
        m.add_section(await m.get_one(["aaaaaaaaa", "wysiwyg", "zzz"])
                      or "(None)"  # , overwrite=0,
                      )
        await m.post()
コード例 #7
0
    async def cmd_event(self,
                        src,
                        _image: str = None,
                        _message: str = None,
                        _nomenu: bool = False,
                        **_):
        """Post a message announcing the start of an event.

        Define a message which will be sent to one or more predefined channels. The message may include mass pings by way of including tags `{{e}}` and `{{h}}` for substitution.
        Destination channels may be selected conversationally or by way of a reaction-based menu.

        Options:
        `--message=<msg>` :: Define the message to send ahead of time. This will skip the step where Petal asks you what you want the message to say.
        `--nomenu` :: Forsake the Reaction UI and determine destination channels conversationally.
        """
        channels_list = []
        channels_dict = {}
        msg = ""
        for chan in self.config.get("xPostList"):
            channel = self.client.get_channel(chan)
            if channel is not None:
                msg += (str(len(channels_list)) + ". (" + channel.name +
                        " [{}]".format(channel.guild.name) + ")\n")
                channels_list.append(channel)
                channels_dict[channel.guild.name + "/#" +
                              channel.name] = channel
            else:
                self.log.warn(
                    chan +
                    " is not a valid channel. I'd remove it if I were you.")

        # Get channels to send to.
        if _nomenu:
            # Do it only conversationally.
            menu = None
            while True:
                await self.client.send_message(
                    src.author,
                    src.channel,
                    "Hi there, " + src.author.name +
                    "! Please select the number of " +
                    "each guild you want to post " +
                    "to. (dont separate the numbers)",
                )

                await self.client.send_message(src.author, src.channel, msg)

                chans = await Messages.waitfor(
                    self.client,
                    all_checks(Messages.by_user(src.author),
                               Messages.in_channel(src.channel)),
                    timeout=20,
                )

                if chans is None:
                    return (
                        "Sorry, the request timed out. Please make sure you"
                        " type a valid sequence of numbers.")
                if self.validate_channel(channels_list, chans.content):
                    break
                else:
                    await self.client.send_message(
                        src.author,
                        src.channel,
                        "Invalid channel choices. You may try again immediately.",
                    )
            post_to = []
            for i in chans.content:
                print(channels_list[int(i)])
                post_to.append(channels_list[int(i)])
        else:
            # Use the Reaction-based GUI.
            menu = Menu(
                self.client,
                src.channel,
                "Event Announcement Post (by {})".format(
                    src.author.display_name),
                "Use the Reaction Buttons to fill out the Announcement.",
                user=src.author,
            )
            if _image:
                menu.em.set_thumbnail(url=_image)
            selection = await menu.get_multi(list(channels_dict),
                                             title="Target Channels")
            post_to = [
                channels_dict[c] for c in selection if c in channels_dict
            ]
            if not post_to:
                # menu.add_section(
                #     "No valid target channels selected; Post canceled.", "Verdict"
                # )
                # await menu.close("No valid target channels selected; Post canceled.")
                menu.add_section("No Channels selected; Cancelled.",
                                 "Target Channels",
                                 overwrite=-1)
                await menu.post()
                return
            menu.add_section("\n".join([c.mention for c in post_to]),
                             "Target Channels",
                             overwrite=-1)
            await menu.post()

        try:
            msgstr = (_message or (await Messages.waitfor(
                self.client,
                all_checks(
                    Messages.by_user(src.author),
                    Messages.in_channel(src.channel),
                ),
                timeout=120,
                channel=src.channel,
                prompt="What do you want to send?"
                " (remember: {e} = `@ev` and {h} = `@here`)",
            )).content).format(e="@everyone", h="@here")

        except AttributeError:
            # Likely tried to get `None.content`.
            raise CommandOperationError("Text Input timed out.")

        if _nomenu:
            embed = discord.Embed(title="Message to post",
                                  description=msgstr,
                                  colour=0x0ACDFF)
            embed.add_field(name="Channels",
                            value="\n".join([c.mention for c in post_to]))
            await self.client.embed(src.channel, embed)

            msg2 = await Messages.waitfor(
                self.client,
                all_checks(Messages.by_user(src.author),
                           Messages.in_channel(src.channel)),
                timeout=20,
                channel=src.channel,
                prompt="If this is correct, type `confirm`.",
            )

            if msg2 is None:
                return "Event post timed out."
            elif msg2.content.lower() != "confirm":
                return "Event post cancelled."
        else:
            # confirmer = Menu(self.client, src.channel, "Confirm Post", user=src.author)
            menu.add_section(msgstr, "Message Preview")
            proceed = await menu.get_bool(
                prompt="Send this Event Announcement?", title="Confirmation")
            if proceed is None:
                # menu.em.description = "[ Closed ]"
                menu.add_section("Posting timed out.",
                                 "Confirmation",
                                 overwrite=-1)
                return
            elif proceed is not True:
                # menu.em.description = "[ Closed ]"
                menu.add_section("Posting cancelled.",
                                 "Confirmation",
                                 overwrite=-1)
                return

        posted: List[discord.Message] = []
        # TODO
        # em = discord.Embed()
        # if _image:
        #     em.set_image(url=_image)
        for i in post_to:
            posted.append(await i.send(msgstr))
            await asyncio.sleep(1)

        if menu:
            menu.add_section("Messages have been posted.",
                             "Confirmation",
                             overwrite=-1)
            await menu.post()
        else:
            # menu.em.description = "[ Closed ]"
            await self.client.send_message(src.author, src.channel,
                                           "Messages have been posted.")

        try:
            subkey, subname = self.get_event_subscription(msgstr)
        except AttributeError:
            pass
        else:
            if subkey is None:
                return (
                    "I was unable to auto-detect any game titles in your post."
                    " No subscribers will be notified for this event.")
            else:
                n = await Messages.waitfor(
                    self.client,
                    all_checks(Messages.by_user(src.author),
                               Messages.in_channel(src.channel)),
                    timeout=20,
                    channel=src.channel,
                    prompt=
                    f"I auto-detected a possible game in your announcement:"
                    f" **{subname}**. Would you like to notify subscribers? [y/N]",
                )

                if not n:
                    return "Timed out."
                elif n.content.lower() not in ("y", "yes"):
                    return "Subscribers will not be notified."
                else:
                    response = await self.notify_subscribers(
                        src.channel, posted[0], subkey)
                    todelete = "[{}]".format(subkey)
                    for post in posted:
                        content = post.content
                        #    print(content)
                        #    print(todelete)
                        if todelete in content:
                            # print("replacing")
                            content = content.replace(todelete, "")
                            # print("replaced: " + content)
                            await post.edit(content=content)

                    return response
コード例 #8
0
    async def cmd_update(self,
                         src,
                         _title: str = "",
                         _content: str = "",
                         _platform: str = "",
                         _reddit: bool = False,
                         _twitter: bool = False,
                         _fb: bool = False,
                         _tumblr: bool = False,
                         **_):
        """
        Post updates to various Social Media accounts.

        Syntax: `{p}update [OPTIONS]`

        Options:
        `--title="<title>"` :: Set the post title. Only matters for Reddit and Tumblr. Asked for if not provided.
        `--content="<content>"` :: Fill in the post content. This is the long bit. Asked for if not provided.
        `--platform=<0,1,2,3>` :: Old style numeric platform selection. Exactly the same as what is asked for if not provided.
        `--reddit`, `--twitter`, `--fb`, `--tumblr` :: Determine which platform(s) to post to. Alternative to `--platform`.
        """
        modes = []
        names = []
        using = []
        table = {
            "reddit": self.router.reddit,
            "twitter": self.router.twit,
            "facebook": self.router.fb,
            "tumblr": self.router.tumblr,
        }
        flagged = ""
        if _reddit:
            flagged += "0"
        if _twitter:
            flagged += "1"
        if _fb:
            flagged += "2"
        if _tumblr:
            flagged += "3"

        if table["reddit"]:
            names.append(str(len(modes)) + " reddit")
            modes.append(self.router.reddit)
            using.append("reddit")

        if table["twitter"]:
            names.append(str(len(modes)) + " twitter")
            modes.append(self.router.twit)
            using.append("twitter")

        if table["facebook"]:
            names.append(str(len(modes)) + " facebook")
            modes.append(self.router.fb)
            using.append("facebook")

        if table["tumblr"]:
            names.append(str(len(modes)) + " tumblr")
            modes.append(self.router.tumblr)
            using.append("tumblr")

        if not modes:
            return "No modules enabled for social media posting."

        if True not in (_reddit, _twitter, _fb, _tumblr) and _platform == "":
            # No destinations have been sent as a flag. Ask the user.
            sendto = ""
            ui = Menu(
                self.client,
                src.channel,
                "Platform",
                "Select platform(s) to publish on.",
                user=src.author,
            )
            await ui.post()

            platforms = await ui.get_multi(using)
            platforms.sort()
            ui.em.description += "\nSelection: " + sequence_words(
                [x.capitalize() for x in platforms])
            await ui.post()

            for i, name in enumerate(using):
                if name in platforms:
                    sendto += str(i)
            if not sendto:
                await ui.close()
                return "Cancelled"
        else:
            sendto = flagged + _platform

        # Check whether sendto contains anything illegal.
        if sendto.strip("0123") != "":
            return "Invalid platform set, please try again."

        if not _title:
            # A title has not been sent as a flag. Ask the user.
            await self.client.send_message(
                src.author,
                src.channel,
                "Please type a title for your post (timeout after 1 minute).",
            )
            _title = await self.client.wait_for_message(channel=src.channel,
                                                        author=src.author,
                                                        timeout=60)
            if _title is None:
                return "The process timed out, you need a valid title."
            _title = _title.content

        if not _content:
            # Post content has not been sent as a flag. Ask the user.
            await self.client.send_message(
                src.author,
                src.channel,
                "Please type the content of the post " +
                "below. Limit to 280 characters for " +
                "Twitter posts. This process will " +
                "time out after 2 minutes.",
            )
            _content = await self.client.wait_for_message(channel=src.channel,
                                                          author=src.author,
                                                          timeout=120)

            if _content is None:
                return "The process timed out, you need content to post."
            _content = _content.content

        # Make sure this would not be an overly long tweet.
        if "1" in sendto:
            if len(_content) > 280:
                return "This post is {} characters too long for Twitter.".format(
                    len(_content) - 280)

        # Get final confirmation before sending.
        await self.client.send_message(
            src.author,
            src.channel,
            "Your post is ready. Please type `send` to confirm.",
        )
        confirm = await self.client.wait_for_message(channel=src.channel,
                                                     author=src.author,
                                                     content="send",
                                                     timeout=10)
        if confirm.content.lower() != "send":
            return "Timed out, message not sent."

        # return str([flagged, sendto, platform, title, content])

        # Post to Reddit
        if "0" in sendto:
            sub1 = self.router.reddit.subreddit(
                self.config.get("reddit")["targetSR"])
            try:
                response = sub1.submit(_title.content,
                                       selftext=_content.clean_content,
                                       send_replies=False)
                self.log.f("smupdate", "Reddit Response: " + str(response))
            except APIException:
                await self.client.send_message(
                    src.author,
                    src.channel,
                    "The post did not send, " + "this key has been " +
                    "ratelimited. Please wait for" +
                    " about 8-10 minutes before " + "posting again",
                )
            else:
                await self.client.send_message(
                    src.author,
                    src.channel,
                    "Submitted post to " +
                    self.config.get("reddit")["targetSR"],
                )
                await asyncio.sleep(2)

        # Post to Twitter
        if "1" in sendto:
            self.router.twit.PostUpdate(_content.clean_content)
            await self.client.send_message(src.author, src.channel,
                                           "Submitted tweet")
            await asyncio.sleep(2)

        # Post to Facebook
        if "2" in sendto:
            # Setting up facebook takes a bit of digging around to see how
            # their API works. Basically you have to be admin'd on a page
            # and have its ID as well as generate an OAUTH2 long-term key.
            # Facebook python API was what I googled

            resp = self.router.fb.get_object("me/accounts")
            page_access_token = None
            for page in resp["data"]:
                if page["id"] == self.config.get("facebook")["pageID"]:
                    page_access_token = page["access_token"]
            postpage = facebook.GraphAPI(page_access_token)

            # if postpage is None:
            #     await self.client.send_message(
            #         src.author,
            #         src.channel,
            #         "Invalid page id for " + "facebook, will not post",
            #     )
            # else:
            #     # FIXME: 'postpage.put_wall_post' and 'page' are invalid
            #     # Did you mean to put them in the loop above?
            #     status = postpage.put_wall_post(body.clean_content)
            #     await self.client.send_message(
            #         src.author,
            #         src.channel,
            #         "Posted to facebook under page: " + page["name"],
            #     )
            #     self.log.f("smupdate", "Facebook Response: " + str(status))
            #     await asyncio.sleep(2)

        # Post to Tumblr
        if "3" in sendto:
            self.router.tumblr.create_text(
                self.config.get("tumblr")["targetBlog"],
                state="published",
                slug="post from petalbot",
                title=_title.content,
                body=_content.clean_content,
            )

            await self.client.send_message(
                src.author,
                src.channel,
                "Posted to tumblr: " + self.config.get("tumblr")["targetBlog"],
            )

        return "Done posting"
コード例 #9
0
    async def cmd_send(self,
                       args,
                       src: discord.Message,
                       _everyone: bool = False,
                       _here: bool = False,
                       _identity: str = None,
                       _i: str = None,
                       _image: str = None,
                       _I: str = None,
                       **_):
        """Broadcast an official-looking message into another channel.

        By using the Identity Option, you can specify who the message is from.
        Valid Identities:```\n{}```

        Syntax: `{{p}}send [OPTIONS] <channel-id> ["<message>"]`

        Parameters
        ----------
        _ : dict
            Dict of additional Keyword Args.
        self
            self
        args : List[str]
            List of Positional Arguments supplied after Command.
        src : discord.Message
            The Discord Message that invoked this Command.
        _everyone : bool
            Include an `@everyone` ping in the message. Overrides `--here`.
        _here : bool
            Include a `@here` ping in the message.
        _identity, _i : str
            Select the group/team on whose behalf this message is being sent.
        _image, _I : str
            Provide the URL of an image to be included in the embed.
        """
        if 2 < len(args) < 1:
            raise CommandArgsError(
                "Must provide a Channel ID and, optionally, a quoted message.")
        elif not args[0].isdigit():
            raise CommandArgsError("Channel ID must be integer.")

        destination: discord.TextChannel = self.client.get_channel(
            int(args.pop(0)))
        if not destination:
            raise CommandArgsError("Invalid Channel.")

        if not args:
            await self.client.send_message(
                src.author,
                src.channel,
                "Please give a message to send (just reply below):",
            )
            try:
                msg = await self.client.wait_for(
                    "message",
                    check=checks.all_checks(
                        checks.Messages.by_user(src.author),
                        checks.Messages.in_channel(src.channel),
                    ),
                    timeout=30,
                )
            except asyncio.TimeoutError:
                raise CommandInputError("Timed out while waiting for message.")
            else:
                text = msg.content
        else:
            text = args[0]

        identity = (_identity or _i or default).lower()
        ident = idents.get(identity, idents[list(sorted(idents.keys()))[0]])
        ident["description"] = text
        img = _image or _I

        try:
            preview = discord.Embed(**ident)
            if img:
                preview.set_image(url=img)
            menu = Menu(self.client, src.channel, "", "", user=src.author)
            menu.em = preview

            confirm = await menu.get_bool(
                prompt="Send this message to {} on behalf of {}?\n"
                "(This section will not be sent.)".format(
                    destination.mention, identity) +
                ("\n***NOTE: THIS MESSAGE WILL SEND A MASS PING!***"
                 if _everyone or _here else ""),
                title="Confirm",
            )

            if confirm is True:
                em = discord.Embed(**ident)
                if img:
                    em.set_image(url=img)
                if _everyone:
                    await destination.send("(@everyone)", embed=em)
                elif _here:
                    await destination.send("(@here)", embed=em)
                else:
                    await destination.send(embed=em)
                # await self.client.embed(destination, em)
            elif confirm is False:
                raise CommandExit("Message cancelled.")
            else:
                raise CommandExit("Confirmation timed out.")

        except discord.errors.Forbidden:
            raise CommandOperationError(
                "Failed to send message: Access Denied")
        else:
            return ("{} (ID: `{}`) sent the following message to {}"
                    " on behalf of `{}`:\n{}".format(
                        src.author.name,
                        str(src.author.id),
                        destination.mention,
                        identity,
                        text,
                    ))