コード例 #1
0
    async def charinfo(self, ctx: commands.Context, *characters: Union[discord.PartialEmoji, str]):
        if not characters:
            await ctx.send_help()
            return

        characters = flatten(
            [x if isinstance(x, discord.PartialEmoji) else list(x) for x in characters]
        )
        characters = deduplicate_iterables(
            [
                x
                for x in characters
                # filter out space characters, as these usually aren't what we're being requested
                # to retrieve info on, and it's almost entirely useless to use an escape
                # sequence to get a mere space character
                if x != " "
            ]
        )

        if len(characters) > 25:
            await ctx.send_help()
            return

        # the following is - for all intents and purposes - ripped directly from RDanny:
        # https://github.com/Rapptz/RoboDanny/blob/ee101d1/cogs/meta.py#L209-L223

        def to_str(char: Union[commands.PartialEmojiConverter, str]):
            if isinstance(char, discord.PartialEmoji):
                return f"{char} \N{EM DASH} `{char.id}`"
            else:
                digit = ord(char)
                name = unicodedata.name(char, translate("name_not_found"))
                return f"{char} \N{EM DASH} `{name}` (`\\U{digit:>08}`)"

        await ctx.send("\n".join(map(to_str, characters)))
コード例 #2
0
    async def rift_link(self,
                        ctx: commands.Context,
                        one_way: Optional[bool] = None,
                        *rifts: Messageable):
        """
        Links this channel to the specified destination(s).

        Anything anyone says in this channel will be forwarded.
        All replies will be relayed back here.
        """
        if not rifts:
            raise commands.UserInputError()
        unique_rifts: List[Messageable] = deduplicate_iterables(rifts)
        source = ctx.channel if ctx.guild else ctx.author
        no_notify = await self.bot.is_owner(
            ctx.author) and not await self.config.notify()
        for rift in unique_rifts:
            if (no_notify and getattr(rift, "guild", None)
                    and not isinstance(rift, discord.abc.User)):
                mem = rift.guild.get_member(ctx.author.id)
                if mem and rift.permissions_for(mem).read_messages:
                    notify = False
                else:
                    notify = True
            else:
                notify = True
            self.rifts.add_vectors(source, rift, two_way=not one_way)
            if notify:
                asyncio.ensure_future(rift.send(_("").format()))
        await ctx.send(
            _("A link has been created to {}! Everything said in this channel will be relayed there.\n"
              "Responses will be relayed here.\n"
              "Type `exit` to quit.").format(
                  humanize_list(list(map(str, unique_rifts)))))
コード例 #3
0
 async def on_message_without_command(self, message: discord.Message):
     if message.author.bot:
         return
     if message.type != discord.MessageType.default:
         return
     if not message.content and not message.attachments:
         return
     if not await self.bot.message_eligible_as_command(message):
         return
     channel = message.channel if message.guild else message.author
     destinations = deduplicate_iterables(
         self.rifts.get(Limited(message=message), ()),
         self.rifts.get(channel, ()))
     if not destinations:
         return
     if message.content.casefold() == "exit":
         if await can_close(message, self.bot):
             if num := await self.close_rifts(message.author, channel):
                 return await message.channel.send(
                     _("{num} rifts closed.").format(num=num))
         else:
             if num := await self.close_rifts(message.author,
                                              Limited(message=message)):
                 return await message.channel.send(
                     _("{num} rifts closed.").format(num=num))
コード例 #4
0
    async def rift_web(self, ctx: commands.Context, *rifts: Messageable):
        """
        Opens up all possible connections between this channel and the specified rifts.

        See the helptext of `[p]rift link` for more info.
        """
        unique_rifts: List[Messageable] = deduplicate_iterables(rifts)
        source = ctx.channel if ctx.guild else ctx.author
        no_notify = await self.bot.is_owner(
            ctx.author) and not await self.config.notify()
        self.rifts.add_web(source, *unique_rifts)
        humanized = humanize_list(list(map(str, (source, *unique_rifts))))
        for rift in unique_rifts:
            if (no_notify and getattr(rift, "guild", None)
                    and not isinstance(rift, discord.abc.User)):
                assert isinstance(rift, discord.TextChannel)
                mem = rift.guild.get_member(ctx.author.id)
                if mem and rift.permissions_for(mem).read_messages:
                    notify = False
                else:
                    notify = True
            else:
                notify = True
            if notify:
                asyncio.ensure_future(
                    rift.send(
                        _("{} has opened a web to here, connecting you to {}."
                          ).format(ctx.author, humanized)))
        await ctx.send(
            _("A web has been opened to {}! Everything you say will be relayed there.\n"
              "Responses will be relayed here.\n"
              "Type `exit` to quit.").format(
                  humanize_list(list(map(str, unique_rifts)))))
コード例 #5
0
 async def on_typing(self, channel: UnionChannel, user: UnionUser,
                     when: datetime):
     if user.bot:
         return
     destinations = deduplicate_iterables(
         self.rifts.get(Limited(author=user, channel=channel), ()),
         self.rifts.get(channel, ()))
     await asyncio.gather(*(channel.trigger_typing()
                            for channel in destinations),
                          return_exceptions=True)
コード例 #6
0
    async def user_defined_paths(self) -> List[Path]:
        """Get a list of user-defined cog paths.

        All paths will be absolute and unique, in order of priority.

        Returns
        -------
        List[pathlib.Path]
            A list of user-defined paths.

        """
        return list(map(Path, deduplicate_iterables(await self.conf.paths())))
コード例 #7
0
ファイル: rift.py プロジェクト: Thalamuszen/FluffyCogs
    async def send(self, ctx: commands.Context, *rifts: Messageable):
        """
        Send a message to the specified destinations.

        Editing or deleting the message you send will still forward
        to the bot's reposts, as in normal rifts.
        """
        if not rifts:
            raise commands.UserInputError()
        unique_rifts = deduplicate_iterables(rifts)
        await ctx.send("What would you like to say?")
        p = MessagePredicate.same_context(ctx=ctx)
        message = await ctx.bot.wait_for("message", check=p)
        await self._send(message, unique_rifts)
コード例 #8
0
ファイル: translator.py プロジェクト: skylarr1227/skybizzle
    def names(self) -> Tuple[str, ...]:
        """Tuple containing the names of all usable locales the bot is configured to load from

        Locales in this tuple are matched from the return value of :attr:`LocaleProxy.bot`
        to the closest available on disk, and as such are guaranteed to exist on disk,
        but cannot be guaranteed to be actually loadable or contain any useful strings.

        The returned tuple is guaranteed to be free of duplicates.
        """
        available = self.loader.available_locales()
        return tuple(x for x in deduplicate_iterables([
            closest_locale(x, available, default=None)
            for x in (*self.bot, self.default)
        ]) if x is not None)
コード例 #9
0
ファイル: setup.py プロジェクト: skylarr1227/skybizzle
    async def sl_reload(self,
                        ctx: commands.Context,
                        skip_confirmation: bool = False):
        self.dump(".".join(self.__module__.split(".")[:-1]))
        self.dump("swift_i18n")
        to_reload = [x.lower() for x in dependents if x in ctx.bot.cogs]
        to_reload.extend(map(lambda x: x.lower(), require_update))
        to_reload = deduplicate_iterables(to_reload)
        names = " ".join(inline(x) for x in to_reload)

        if skip_confirmation or await confirm(
                ctx, content=translate("reload_confirm", cogs=names)):
            await ctx.invoke(ctx.bot.get_cog("Core").reload, *to_reload)
        else:
            await ctx.send(translate("reload_manual", cogs=names))
コード例 #10
0
    async def paths(self) -> List[Path]:
        """Get all currently valid path directories, in order of priority

        Returns
        -------
        List[pathlib.Path]
            A list of paths where cog packages can be found. The
            install path is highest priority, followed by the
            user-defined paths, and the core path has the lowest
            priority.

        """
        return deduplicate_iterables(
            [await self.install_path()], await self.user_defined_paths(), [self.CORE_PATH]
        )
コード例 #11
0
ファイル: cog_manager.py プロジェクト: Brutalbic/Brutalbot
    async def paths(self) -> Tuple[Path, ...]:
        """Get all currently valid path directories.

        Returns
        -------
        `tuple` of `pathlib.Path`
            All valid cog paths.

        """
        conf_paths = [Path(p) for p in await self.conf.paths()]
        other_paths = self._paths

        all_paths = deduplicate_iterables(conf_paths, other_paths,
                                          [self.CORE_PATH])

        if self.install_path not in all_paths:
            all_paths.insert(0, await self.install_path())
        return tuple(p.resolve() for p in all_paths if p.is_dir())
コード例 #12
0
ファイル: rift.py プロジェクト: Thalamuszen/FluffyCogs
    async def rift_open(
        self, ctx: commands.Context, one_way: Optional[bool] = None, *rifts: Messageable
    ):
        """
        Opens a rift to the specified destination(s).

        Only your messages will be forwarded to the specified destinations,
        and all replies will be sent back to you.
        """
        if not rifts:
            raise commands.UserInputError()
        unique_rifts: List[Messageable] = deduplicate_iterables(rifts)
        source = Limited(message=ctx.message) if ctx.guild else ctx.author
        no_notify = await self.bot.is_owner(ctx.author) and not await self.config.notify()
        for rift in unique_rifts:
            if (
                no_notify
                and getattr(rift, "guild", None)
                and not isinstance(rift, discord.abc.User)
            ):
                mem = rift.guild.get_member(ctx.author.id)
                if mem and rift.permissions_for(mem).read_messages:
                    notify = False
                else:
                    notify = True
            else:
                notify = True
            self.rifts.add_vectors(source, rift, two_way=not one_way)
            if notify:
                asyncio.ensure_future(
                    rift.send(
                        _("{} has opened a rift to here from {}.").format(ctx.author, ctx.channel)
                    )
                )
        await ctx.send(
            _(
                "A rift has been opened to {}! Everything you say will be relayed there.\n"
                "Responses will be relayed here.\n"
                "Type `exit` to quit."
            ).format(humanize_list(list(map(str, unique_rifts))))
        )
コード例 #13
0
ファイル: test_utils.py プロジェクト: Brutalbic/Brutalbot
def test_deduplicate_iterables():
    expected = [1, 2, 3, 4, 5]
    inputs = [[1, 2, 1], [3, 1, 2, 4], [5, 1, 2]]
    assert deduplicate_iterables(*inputs) == expected
コード例 #14
0
ファイル: ao3.py プロジェクト: grayconcaves/FanCogs
    async def ao3(self, ctx, ficlink, *, notes=""):
        """Returns details of a fic from a link

        If the fic you inputted is wrong, just click the ❎ emoji to delete the message (Needs Manage Messages permissions)."""

        # SET NOTES
        if notes == "":
            notes = "None."
        else:
            nlimit = await self.config.guild(ctx.guild).noteslimit()
            notes = notes[:nlimit]

        # GET URL
        if "chapter" in ficlink:
            newlink = ficlink.split("chapters")[0]
            ficlink = str(newlink)
        if "collections" in ficlink:
            newlink = ficlink.split("/works/")[1]
            ficlink = str(f"https://archiveofourown.org/works/{newlink}")
        if "?view_full_work=true" in ficlink:
            newlink = ficlink.split("?")[0]
            ficlink = str(newlink)

        firstchap = f"{ficlink}/navigate"
        async with self.session.get(firstchap) as ao3navigation:
            navigate = BeautifulSoup(await ao3navigation.text(),
                                     'html.parser',
                                     parse_only=SoupStrainer("ol"))
        try:
            firstchap = navigate.find("li").a['href']
            url = f"https://archiveofourown.org{firstchap}?view_adult=true"
        except AttributeError:
            return await ctx.send(
                "Error loading work info. Please ensure that the work is not locked."
            )

        # START SCRAPING
        async with self.session.get(url) as ao3session:
            result = BeautifulSoup(await ao3session.text(), 'html.parser')

        # GET AUTHORS
        try:
            a = result.find_all("a", {'rel': 'author'})
            author_list = []
            for author in a:
                author_list.append(author.string.strip())
            try:
                authors = humanize_list(deduplicate_iterables(author_list))
            except Exception:
                authors = "Anonymous"
        except Exception:
            return await ctx.send("Error loading author list.")

        # GET TITLE
        try:
            preface = result.find("div", {'class': 'preface group'}).h2.string
            title = str(preface.strip())
        except Exception:
            title = "No title found."

        # GET FANDOM
        try:
            fan = result.find("dd", {'class': 'fandom tags'})
            fan_list = []
            fandomlimit = await self.config.guild(ctx.guild).fandomlimit()
            for fandom in fan.find_all("li", limit=fandomlimit):
                fan_list.append(fandom.a.string)
            fandom = humanize_list(fan_list)
        except Exception:
            fandom = "No fandom found."

        # GET PAIRING
        try:
            reltags = result.find("dd", {'class': 'relationship tags'})
            pair_list = []
            pairlimit = await self.config.guild(ctx.guild).pairlimit()
            for rel in reltags.find_all("li", limit=pairlimit):
                pair_list.append(rel.a.string)
            pairing = humanize_list(pair_list)
        except Exception:
            pairing = "No Pairing."

        # GET CHAPTERS
        chapters = result.find("dd", {'class': 'chapters'})
        totalchapters = str(BeautifulSoup.getText(chapters))

        # GET STATUS
        chap_list = totalchapters.split("/")
        if "?" in chap_list[1]:
            status = "Work in Progress"
        elif chap_list[0] != chap_list[1]:
            status = "Work in Progress"
        else:
            status = "Complete"

        # GET RATING
        try:
            rate = result.find("dd", {'class': 'rating tags'})
            rating = rate.a.string
        except Exception:
            rating = "Not Rated"

        # GET SUMMARY
        try:
            div = result.find("div", {'class': 'preface group'})
            userstuff = div.find("blockquote", {'class': 'userstuff'})
            stuff = str(BeautifulSoup.getText(userstuff))
            summarytest = f"{stuff}".replace('. ', '**').replace('.', '. ')
            summ = f"{summarytest}".replace('**', '. \n\n')
            slimit = await self.config.guild(ctx.guild).sumlimit()
            summary = summ[:slimit]
        except Exception:
            summary = "No work summary found."

        # GET TAGS
        try:
            use_censor = await self.config.guild(ctx.guild).censor()
            freeform = result.find("dd", {'class': 'freeform tags'})
            tag_list = []
            taglimit = await self.config.guild(ctx.guild).taglimit()
            for tag in freeform.find_all("li", limit=taglimit):
                tag_list.append(tag.a.string)
            if "Explicit" in rating and use_censor:
                tags = f"||{(humanize_list(tag_list))}||"
            else:
                tags = humanize_list(tag_list)

        except Exception:
            tags = "No tags found."

        # GET DATE PUBLISHED AND UPDATED
        published = result.find("dd", {'class': 'published'}).string.strip()
        try:
            updated = result.find("dd", {'class': 'status'}).string.strip()
        except Exception:
            updated = published

        # GET LANGUAGE
        language = result.find("dd", {'class': 'language'}).string.strip()

        # GET WORDS
        words = int(
            result.find("dd", {
                'class': 'words'
            }).string.replace(",", ""))

        # GET KUDOS
        try:
            kudos = int(
                result.find("dd", {
                    'class': 'kudos'
                }).string.replace(",", ""))
        except AttributeError:
            kudos = 0

        # GET HITS
        try:
            hits = int(
                result.find("dd", {
                    'class': 'hits'
                }).string.replace(",", ""))
        except AttributeError:
            hits = 0

        # GET WARNINGS
        warntags = result.find("dd", {'class': 'warning tags'})
        warn_list = []
        try:
            for warning in warntags.find_all("li"):
                warn_list.append(warning.a.string)
            warnings = humanize_list(warn_list)
        except Exception:
            warnings = "No warnings found."

        # CHECK INFO FORMAT
        use_embed = await self.config.guild(ctx.guild).embed()
        data = await self.config.guild(ctx.guild).formatting()

        if use_embed:
            data = discord.Embed(description=summary,
                                 title=title,
                                 url=ficlink,
                                 colour=3553599)
            data.add_field(name="Author:", value=authors, inline=False)
            data.add_field(name="Fandom:", value=fandom, inline=False)
            data.add_field(name="Rating:", value=rating, inline=False)
            data.add_field(name="Pairings:", value=pairing, inline=False)
            data.add_field(name="Tags:", value=tags, inline=False)
            data.add_field(name=f"Rec Notes by {ctx.author}: ",
                           value=notes,
                           inline=False)
            data.set_footer(
                text=
                f"Language: {language}     |       Words: {words}       |       Date Updated: {updated}        |       Status: {status}        "
            )
            ao3msg = await ctx.send(embed=data)

        else:
            params = {
                "title": title,
                "authors": authors,
                "rating": rating,
                "warnings": warnings,
                "language": language,
                "fandom": fandom,
                "pairing": pairing,
                "tags": tags,
                "summary": summary,
                "totalchapters": totalchapters,
                "status": status,
                "words": words,
                "kudos": kudos,
                "hits": hits,
                "reccer": ctx.author.mention,
                "notes": notes,
                "url": f"<{ficlink}>",
                "published": published,
                "updated": updated
            }
            ao3msg = await ctx.send(data.format(**params))

        start_adding_reactions(ao3msg, ReactionPredicate.YES_OR_NO_EMOJIS)

        pred = ReactionPredicate.yes_or_no(ao3msg, ctx.author)
        try:
            await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            await self._clear_react(ao3msg)

            if pred.result is False:
                await ao3msg.delete()
                return

        except asyncio.TimeoutError:
            await self._clear_react(ao3msg)

        autodel = await self.config.guild(ctx.guild).autodelete()

        try:
            if autodel is True:
                await ctx.message.delete()
            return
        except Exception:
            return
コード例 #15
0
ファイル: googletrends.py プロジェクト: Vexed01/Vex-Cogs
    async def trends(
        self,
        ctx: commands.Context,
        timeframe: Optional[TimeframeConverter] = "now 7-d",
        geo: Optional[GeoConverter] = "",
        *query: str,
    ):
        """
        Find what the world is searching, right from Discord.

        **Get started with `[p]trends discord` for a basic example!**

        **Optional**

        `timeframe`:
            You can specify either the long (eg `4hours`) or short (`eg 4h`) version of the
            timeframes. All other values not listed below are invalid.

            `hour`/`1h`
            `4hours`/`4h`
            `day`/`1d`
            `week`/`7d`
            `month`/`1m`
            `3months`/`3m`
            `12months`/`12m`
            `5years`/`5y`
            `all`

        `geo`:
            Defaults to `world`
            You can specify a two-letter geographic code, such as `US`, `GB` or `FR`.
            Sometimes, you can also add a sub-region. See
            https://go.vexcodes.com/trends_geo for a list.

        **Required**

        `trends`:
            Whatever you want! You can add multiple trends, and separate them with a space.
            If your trend has spaces in it, you can use `+` instead of a space or enclose it
            in quotes, for example `Card games` to `Card+games` or `"Card games"`.

        **Examples:**
            The help message is so long that examples wouldn't fit! Run `[p]trendsexamples`
            to see some.
        """
        if timeframe is None or geo is None:
            # should never happen
            return

        query = deduplicate_iterables(query)

        if len(query
               ) == 0 and geo != "" and timeframe != "now 7-d":  # not defaults
            await ctx.send(
                "You must specify at least one query. For example, `[p]trends discord`"
            )
            return
        elif len(query) == 0:
            await ctx.send_help()
            return

        if len(query) > 5:
            await ctx.send("Sorry, the maximum about of queries is 5")
            return

        async with ctx.typing():
            try:
                request = await self.get_trends_request(
                    list(query), timeframe, geo)
            except ResponseError as e:
                if e.response.status_code == 400:
                    await ctx.send(
                        "Your request failed. It looks like something's invalid."
                    )
                elif e.response.status_code in (403, 429):
                    await ctx.send(
                        "Your request failed. It looks like we've hit a rate limit. "
                        "Try again in a minute.")
                else:
                    await ctx.send(
                        "Your request failed for an unexpected reason.")
                return

            try:
                file = await self.plot_graph(request, timeframe, geo)
            except NoData:
                await ctx.send(
                    "Sorry, there's no significant data for that. Check your spelling or choose "
                    "another query.")
                return

        full_location = [k for k, v in GEOS.items() if v == geo][0]
        full_timeframe = TIMEFRAMES.get(timeframe, "")

        url = self.get_trends_url(timeframe, geo, query)

        embed = discord.Embed(
            title=f"Interest over time, {full_location}, {full_timeframe}",
            colour=await ctx.embed_colour(),
        )
        embed.set_footer(
            text=
            f"Times are in UTC\nSee {ctx.clean_prefix}help trends for advanced usage\n"
            + "Data sourced from Google Trends.")
        embed.set_image(url="attachment://plot.png")

        button = url_buttons.URLButton("View on Google Trends", url)
        await url_buttons.send_message(self.bot,
                                       ctx.channel.id,
                                       embed=embed,
                                       file=file,
                                       url_button=button)
コード例 #16
0
ファイル: aliases.py プロジェクト: sourcery-ai-bot/Vex-Cogs
    async def aliases(self, ctx: commands.Context, *, strcommand: str):
        """
        Get all the alias information you could ever want about a command.

        This will show the main command, built-in aliases, global aliases and
        server aliases.
        """
        command = self.bot.get_command(strcommand)

        alias_cog = self.bot.get_cog("Alias")
        if alias_cog is None:
            if command is None:
                return await ctx.send("Hmm, I can't find that command.")
            full_com = command.qualified_name
            builtin_aliases = command.aliases
            com_parent = command.parent or ""

            com_builtin_aliases = [
                inline(f"{com_parent} {builtin_aliases[i]}")
                for i in range(len(builtin_aliases))
            ]

            msg = "I was unable to get information from the alias cog. It's probably not loaded.\n"
            msg += f"Main command: `{full_com}`\nBuilt in aliases: "
            msg += humanize_list(com_builtin_aliases)
            return await ctx.send(msg)

        alias_conf: Config = alias_cog.config  # type:ignore
        all_global_aliases: List[dict] = await alias_conf.entries()

        if ctx.guild:
            all_guild_aliases: List[dict] = await alias_conf.guild(ctx.guild
                                                                   ).entries()
        else:
            all_guild_aliases = []

        # check if command is actually from alias cog
        if command is None:
            for alias in all_guild_aliases:
                if alias["name"] == strcommand:
                    command = self.bot.get_command(alias["command"])

            for alias in all_global_aliases:
                if alias["name"] == strcommand:
                    command = self.bot.get_command(alias["command"])

        if command is None:
            return await ctx.send("That's not a command or alias.")

        builtin_aliases = command.aliases
        com_parent = command.parent or ""

        guild_aliases = [
            alias["name"] for alias in all_guild_aliases
            if strcommand in [alias["command"], alias["name"]]
        ]

        global_aliases = [
            alias["name"] for alias in all_global_aliases
            if strcommand in [alias["command"], alias["name"]]
        ]

        # and probs picked up duplicates on second run so:
        guild_aliases = deduplicate_iterables(guild_aliases)
        guild_aliases = [
            i for i in guild_aliases if not self.bot.get_command(i)
        ]
        global_aliases = deduplicate_iterables(global_aliases)
        global_aliases = [
            i for i in global_aliases if not self.bot.get_command(i)
        ]

        # make everything inline + make built in aliases
        hum_builtin_aliases = inline_hum_list(
            [f"{com_parent} {i}" for i in builtin_aliases])
        hum_global_aliases = inline_hum_list(global_aliases)
        hum_guild_aliases = inline_hum_list(guild_aliases)

        aliases = ""
        none = []
        if hum_builtin_aliases:
            aliases += f"Built-in aliases: {hum_builtin_aliases}\n"
        else:
            none.append("built-in")

        if hum_global_aliases:
            aliases += f"Global aliases: {hum_global_aliases}\n"
        else:
            none.append("global")

        if hum_guild_aliases:
            aliases += f"Server aliases: {hum_guild_aliases}\n"
        else:
            if ctx.guild:
                none.append("guild")
            else:
                aliases += "You're in DMs, so there aren't any server aliases."
        str_none = humanize_list(none, style="or")

        msg = f"Main command: `{strcommand}`\n{aliases}"

        if str_none:
            msg += f"This command has no {str_none} aliases."

        pages = pagify(msg, delims=["\n", ", "])
        for page in pages:
            await ctx.send(page)
コード例 #17
0
 def get_update_ids(self) -> list[str]:
     """Get the group IDs for this feed, in order."""
     return deduplicate_iterables(
         [field.update_id for field in self.fields])