Ejemplo n.º 1
0
 async def get_manifest(self, d1: bool = False) -> None:
     """
     Checks if the manifest is up to date and downloads if it's not
     """
     try:
         headers = await self.build_headers()
     except Destiny2MissingAPITokens:
         return
     if d1:
         manifest_data = await self.request_url(
             f"{DESTINY1_BASE_URL}/Manifest/", headers=headers
         )
         locale = get_locale()
         if locale in manifest_data:
             manifest = manifest_data["mobileWorldContentPaths"][locale]
         elif locale[:-3] in manifest_data:
             manifest = manifest_data["mobileWorldContentPaths"][locale[:-3]]
         else:
             manifest = manifest_data["mobileWorldContentPaths"]["en"]
     else:
         manifest_data = await self.request_url(
             f"{BASE_URL}/Destiny2/Manifest/", headers=headers
         )
         locale = get_locale()
         if locale in manifest_data:
             manifest = manifest_data["jsonWorldContentPaths"][locale]
         elif locale[:-3] in manifest_data:
             manifest = manifest_data["jsonWorldContentPaths"][locale[:-3]]
         else:
             manifest = manifest_data["jsonWorldContentPaths"]["en"]
     async with aiohttp.ClientSession() as session:
         async with session.get(
             f"https://bungie.net/{manifest}", headers=headers, timeout=None
         ) as resp:
             if d1:
                 await self.download_d1_manifest(resp)
             else:
                 # response_data = await resp.text()
                 # data = json.loads(response_data)
                 data = await resp.json()
                 for key, value in data.items():
                     path = cog_data_path(self) / f"{key}.json"
                     with path.open(encoding="utf-8", mode="w") as f:
                         if self.bot.user.id in DEV_BOTS:
                             json.dump(
                                 value,
                                 f,
                                 indent=4,
                                 sort_keys=False,
                                 separators=(",", " : "),
                             )
                         else:
                             json.dump(value, f)
                 await self.config.manifest_version.set(manifest_data["version"])
     return manifest_data["version"]
Ejemplo n.º 2
0
    def babel(locale: str = None) -> str:
        """Get the bot's current locale in a format usable by Babel"""
        from babel import UnknownLocaleError, Locale as bLocale

        bot_locale = (locale or get_locale()).replace("-", "_")
        try:
            return str(bLocale.parse(bot_locale))
        except (UnknownLocaleError, ValueError):
            if bot_locale != get_locale().replace("-", "_"):
                return bot_locale

            warnings.warn(
                f"Bot locale {bot_locale} is not recognized by Babel; falling back to en_US.",
                RuntimeWarning,
            )
            return "en_US"
Ejemplo n.º 3
0
 async def get_manifest(self) -> None:
     """
         Checks if the manifest is up to date and downloads if it's not
     """
     await self.bot.wait_until_ready()
     try:
         headers = await self.build_headers()
     except Destiny2MissingAPITokens:
         return
     manifest_data = await self.request_url(f"{BASE_URL}/Destiny2/Manifest/", headers=headers)
     locale = get_locale()
     if locale in manifest_data:
         manifest = manifest_data["jsonWorldContentPaths"][locale]
     elif locale[:-3] in manifest_data:
         manifest = manifest_data["jsonWorldContentPaths"][locale[:-3]]
     else:
         manifest = manifest_data["jsonWorldContentPaths"]["en"]
     if await self.config.manifest_version() != manifest_data["version"]:
         async with aiohttp.ClientSession() as session:
             async with session.get(f"https://bungie.net/{manifest}", headers=headers) as resp:
                 data = await resp.json()
         for key, value in data.items():
             path = cog_data_path(self) / f"{key}.json"
             with path.open(encoding="utf-8", mode="w") as f:
                 json.dump(value, f, indent=4, sort_keys=False, separators=(",", " : "))
         await self.config.manifest_version.set(manifest_data["version"])
     return manifest_data["version"]
Ejemplo n.º 4
0
    def reload_translations(self):
        if self.previous_locale == get_locale():
            return  # Don't care if the locale hasn't changed

        # Embed constants
        self.BDAY_LIST_TITLE = _("Birthday List")

        # Logging message constants

        # Message constants
        self.BDAY_WITH_YEAR = _("<@!{}> is now **{} years old**. :tada:")
        self.BDAY_WITHOUT_YEAR = _("It's <@!{}>'s birthday today! :tada:")
        self.ROLE_SET = _(
            ":white_check_mark: The birthday role on **{g}** has been set to: **{r}**."
        )
        self.BDAY_INVALID = _(
            ":x: The birthday date you entered is invalid. It must be `MM-DD`."
        )
        self.BDAY_SET = _(
            ":white_check_mark: Your birthday has been set to: **{}**.")
        self.CHANNEL_SET = _(
            ":white_check_mark: "
            "The channel for announcing birthdays on **{g}** has been set to: **{c}**."
        )
        self.BDAY_REMOVED = _(
            ":put_litter_in_its_place: Your birthday has been removed.")
Ejemplo n.º 5
0
 async def get_lang(self):
     """Get language for forecastio, based on current's bot language"""
     locale = get_locale()
     special_cases = {
         "lol-US": "x-pig-latin",
         "debugging": "en",
         "zh-TW": "zh-tw"
     }
     lang = special_cases.get(locale, locale[:2])
     if lang in FORECASTIO_SUPPORTED_LANGS:
         return lang
     return "en"
Ejemplo n.º 6
0
 async def _get_wordlist(self, ctx):
     """Get the proper wordlist for the current locale."""
     locale = await self.config.guild(ctx.guild).locale()
     if locale is None:
         locale = get_locale()
     for char in CHARS:
         if locale.lower() == char.lower():
             locale = char  #convert case to the one used by the file
             break
     if locale not in CHARS:  #now case insensitive
         await ctx.send(_('Your locale is not available. Using `en-US`.'))
         locale = 'en-US'
     with open(bundled_data_path(self) / f'{locale}.json') as f:
         wordlist = json.load(f)
     return wordlist, locale
Ejemplo n.º 7
0
    def reload_translations(self):
        if self.previous_locale == get_locale():
            return  # Don't care if the locale hasn't changed

        # Embed constants
        self.REMINDER_TITLE = _("Reminder")

        # Logging message constants

        # Message constants
        self.INVALID_TIME_FORMAT = _(":x: Invalid time format.")
        self.TOO_MUCH_TIME = _(
            ":x: Too long amount of time. Maximum: {} total seconds")
        self.WILL_REMIND = _(
            ":white_check_mark: I will remind you in {} seconds.")
Ejemplo n.º 8
0
    def reload_translations(self):
        new_locale = get_locale()
        if self.previous_locale == new_locale:
            return  # Don't care if the locale hasn't changed
        self.previous_locale = new_locale

        # Embed constants
        self.LINK_LIST_TITLE = _("Role Links")
        self.LINK_LIST_NO_LINKS = _("There are no links in this server")

        # Logging message constants
        self.LOG_MESSAGE_NOT_FOUND = _("Could not find message {msg_id} in {channel}.")
        self.LOG_CHANNEL_NOT_FOUND = _("Could not find channel {channel_id}.")
        self.LOG_SERVER_NOT_FOUND = _("Could not find server with id {guild_id}.")
        self.LOG_PROCESSING_LOOP_ENDED = _("The processing loop has ended.")

        # Message constants
        self.PROGRESS_FORMAT = _("Checked {c} out of {r} reactions out of {t} emojis.")
        self.PROGRESS_COMPLETE_FORMAT = _(""":white_check_mark: Completed! Checked a total of {c} reactions.
Gave a total of {g} roles.""")
        self.MESSAGE_NOT_FOUND = _(":x: Message not found.")
        self.ALREADY_BOUND = _(":x: The emoji is already bound on that message.")
        self.NOT_IN_SERVER = _(":x: The channel must be in a server.")
        self.ROLE_NOT_FOUND = _(":x: Role not found on the given channel's server.")
        self.EMOJI_NOT_FOUND = _(":x: Emoji not found in any of my servers or in unicode emojis.")
        self.CANT_ADD_REACTIONS = _(":x: I don't have the permission to add reactions in that channel.")
        self.CANT_MANAGE_ROLES = _(":x: I don't have the permission to manage users' roles in the channel's server.")
        self.ROLE_SUCCESSFULLY_BOUND = _(":white_check_mark: The role has been bound to {} on the message in {}.")
        self.ROLE_NOT_BOUND = _(":x: The role is not bound to that message.")
        self.INITIALIZING = _("Initializing...")
        self.ROLE_UNBOUND = _(":put_litter_in_its_place: Unbound the role on the message.\n")
        self.REACTION_CLEAN_START = self.ROLE_UNBOUND + _("Removing linked reactions...")
        self.PROGRESS_REMOVED = self.ROLE_UNBOUND + _("Removed **{} / {}** reactions...")
        self.REACTION_CLEAN_DONE = self.ROLE_UNBOUND + _("Removed **{}** reactions.")
        self.LINK_MESSAGE_NOT_FOUND = _("The following messages weren't found: {}")
        self.LINK_CHANNEL_NOT_FOUND = _("The following channels weren't found: {}")
        self.LINK_PAIR_INVALID = _("The following channel-message pairs were invalid: {}")
        self.LINK_MUST_SPECIFY = _("You must specify at least one message to be linked.")
        self.LINK_FAILED = _(":x: Failed to link reactions.\n")
        self.LINK_SUCCESSFUL = _(":white_check_mark: Successfully linked the reactions.")
        self.LINK_NAME_TAKEN = _(":x: That link name is already used in the current server. "
                                 "Remove it before assigning to it.")
        self.UNLINK_NOT_FOUND = _(":x: Could not find a link with that name in this server.")
        self.UNLINK_SUCCESSFUL = _(":white_check_mark: The link has been removed from this server.")
        self.CANT_CHECK_LINKED = _(":x: Cannot run a check on linked messages.")
        self.REACTION_NOT_FOUND = _(":x: Could not find the reaction of that message.")
Ejemplo n.º 9
0
    def bot(self) -> Tuple[str, ...]:
        """Tuple containing the names of all locales the bot is currently configured to load from

        The returned locales have not been modified from what is configured in Red,
        and as such they may or may not actually exist as usable locales on disk.

        The returned tuple may contain duplicates.

        You should use :attr:`LocaleProxy.names` if you want a list of actually usable locale names.

        Currently, this simply returns the return value of ``get_locale()`` in a tuple,
        but is provided as basic foundational work for handling future per-user/guild locales
        if and when it's implemented in Red.
        """
        # If/when multi-locale support in Red lands, this is roughly how we want to
        # prioritize configured locales to load and retrieve strings from:
        #   1. User
        #   2. Channel
        #   3. Guild
        #   4. Global
        #   5. Default translator locale

        # noinspection PyRedundantParentheses
        return (get_locale(), )
Ejemplo n.º 10
0
    async def act(self, ctx: commands.Context, *, target: Union[discord.Member, str] = None):
        """
        Acts on the specified user.
        """
        if not target or isinstance(target, str):
            return  # no help text

        try:
            if not ctx.guild:
                raise KeyError()
            message = await self.config.guild(ctx.guild).get_raw("custom", ctx.invoked_with)
        except KeyError:
            try:
                message = await self.config.get_raw("custom", ctx.invoked_with)
            except KeyError:
                message = NotImplemented

        if message is None:  # ignored command
            return
        elif message is NotImplemented:  # default
            # humanize action text
            action = inflection.humanize(ctx.invoked_with).split()
            iverb = -1

            for cycle in range(2):
                if iverb > -1:
                    break
                for i, act in enumerate(action):
                    act = act.lower()
                    if (
                        act in NOLY_ADV
                        or act in CONJ
                        or (act.endswith("ly") and act not in LY_VERBS)
                        or (not cycle and act in SOFT_VERBS)
                    ):
                        continue
                    action[i] = inflection.pluralize(action[i])
                    iverb = max(iverb, i)

            if iverb < 0:
                return
            action.insert(iverb + 1, target.mention)
            message = italics(" ".join(action))
        else:
            assert isinstance(message, str)
            message = fmt_re.sub(functools.partial(self.repl, target), message)

        # add reaction gif
        if self.try_after and ctx.message.created_at < self.try_after:
            return await ctx.send(message)
        if not await ctx.embed_requested():
            return await ctx.send(message)
        key = (await ctx.bot.get_shared_api_tokens("tenor")).get("api_key")
        if not key:
            return await ctx.send(message)
        async with aiohttp.request(
            "GET",
            "https://api.tenor.com/v1/search",
            params={
                "q": ctx.invoked_with,
                "key": key,
                "anon_id": str(ctx.author.id ^ ctx.me.id),
                "media_filter": "minimal",
                "contentfilter": "off" if getattr(ctx.channel, "nsfw", False) else "low",
                "ar_range": "wide",
                "limit": "8",
                "locale": get_locale(),
            },
        ) as response:
            json: dict
            if response.status == 429:
                self.try_after = ctx.message.created_at + 30
                json = {}
            elif response.status >= 400:
                json = {}
            else:
                json = await response.json()
        if not json.get("results"):
            return await ctx.send(message)
        message = f"{message}\n\n{random.choice(json['results'])['itemurl']}"
        await ctx.send(
            message,
            allowed_mentions=discord.AllowedMentions(
                users=False if target in ctx.message.mentions else [target]
            ),
        )
Ejemplo n.º 11
0
    async def act(self, ctx: commands.Context, *, target: Union[discord.Member, str] = None):
        if not target or isinstance(target, str):
            return  # no help text

        try:
            if not ctx.guild:
                raise KeyError()
            message = await self.config.guild(ctx.guild).get_raw("custom", ctx.invoked_with)
        except KeyError:
            try:
                message = await self.config.get_raw("custom", ctx.invoked_with)
            except KeyError:
                message = NotImplemented

        humanized: Optional[str] = None
        if message is None:  # ignored command
            return
        elif message is NotImplemented:  # default
            # humanize action text
            humanized = inflection.humanize(ctx.invoked_with)
            action = humanized.split()
            iverb = -1

            for cycle in range(2):
                if iverb > -1:
                    break
                for i, act in enumerate(action):
                    act = act.lower()
                    if (
                        act in NOLY_ADV
                        or act in CONJ
                        or (act.endswith("ly") and act not in LY_VERBS)
                        or (not cycle and act in SOFT_VERBS)
                    ):
                        continue
                    action[i] = inflection.pluralize(action[i])
                    iverb = max(iverb, i)

            if iverb < 0:
                return
            action.insert(iverb + 1, target.mention)
            message = italics(" ".join(action))
        else:
            assert isinstance(message, str)
            message = fmt_re.sub(functools.partial(self.repl, target), message)

        send = functools.partial(
            ctx.send,
            allowed_mentions=discord.AllowedMentions(
                users=False if target in ctx.message.mentions else [target]
            ),
        )

        # add reaction gif
        if self.try_after and ctx.message.created_at < self.try_after:
            return await send(message)
        if not await ctx.embed_requested():
            return await send(message)
        key = (await ctx.bot.get_shared_api_tokens("tenor")).get("api_key")
        if not key:
            return await send(message)
        if humanized is None:
            humanized = inflection.humanize(ctx.invoked_with)
        async with aiohttp.request(
            "GET",
            "https://g.tenor.com/v1/search",
            params={
                "q": humanized,
                "key": key,
                "anon_id": str(ctx.author.id ^ ctx.me.id),
                "media_filter": "minimal",
                "contentfilter": "off" if getattr(ctx.channel, "nsfw", False) else "low",
                "ar_range": "wide",
                "limit": 20,
                "locale": i18n.get_locale(),
            },
        ) as response:
            json: dict
            if response.status == 429:
                self.try_after = ctx.message.created_at + timedelta(seconds=30)
                json = {}
            elif response.status >= 400:
                json = {}
            else:
                json = await response.json()
        if not json.get("results"):
            return await send(message)
        # Try to keep gifs more relevant by only grabbing from the top 50% + 1 of results,
        # in case there are only a few results.
        # math.ceiling() is not used since it would be too limiting for smaller lists.
        choice = json["results"][random.randrange(len(json["results"]) // 2 + 1)]
        choice = random.choice(json["results"])
        embed = discord.Embed(
            color=await ctx.embed_color(),
            timestamp=datetime.fromtimestamp(choice["created"], timezone.utc),
            url=choice["itemurl"],
        )
        # This footer is required by Tenor's API: https://tenor.com/gifapi/documentation#attribution
        embed.set_footer(text="Via Tenor")
        embed.set_image(url=choice["media"][0]["gif"]["url"])
        await send(message, embed=embed)
Ejemplo n.º 12
0
 async def forecast(self, ctx, *, place: str):
     """Shows 7 days forecast for provided place"""
     apikeys = await self.bot.get_shared_api_tokens("forecastio")
     async with ctx.typing():
         try:
             async with self.session.get(
                     f"https://nominatim.openstreetmap.org/search?q={place}&format=jsonv2&addressdetails=1&limit=1",
                     headers=
                 {
                     "Accept-Language":
                     get_locale(),
                     "User-Agent":
                     f"Red-DiscordBot/{redbot_ver} Fixator10-Cogs/Weather/{self.__version__}",
                 },
             ) as r:
                 location = await r.json(loads=json.loads)
         except aiohttp.ClientResponseError as e:
             await ctx.send(
                 chat.error(
                     _("Cannot find a place {}. OSM returned {}").format(
                         chat.inline(place), e.status)))
             return
         if not location:
             await ctx.send(
                 chat.error(
                     _("Cannot find a place {}").format(chat.inline(place)))
             )
             return
         location = location[0]
         try:
             forecast = await self.bot.loop.run_in_executor(
                 None,
                 partial(
                     forecastio.load_forecast,
                     apikeys.get("secret"),
                     location.get("lat", 0),
                     location.get("lon", 0),
                     units=await self.get_units(ctx),
                     lang=await self.get_lang(),
                 ),
             )
         except HTTPError:
             await ctx.send(
                 chat.error(
                     _("This command requires API key. "
                       "Use {}forecastapi to get more information").format(
                           ctx.clean_prefix)))
             return
         except (RequestsConnectionError, Timeout):
             await ctx.send(
                 chat.error(_("Unable to get data from forecast.io")))
             return
     by_day = forecast.daily()
     pages = []
     for i in range(0, 8):
         data = by_day.data[i]
         em = discord.Embed(
             title=_("Weather in {}").format(
                 shorten(
                     location.get("display_name", UNKNOWN_EMOJI),
                     244,
                     placeholder="…",
                 )),
             description=f"{by_day.summary}\n" +
             _("[View on Google Maps](https://www.google.com/maps/place/{},{})"
               ).format(
                   location.get("lat", 0),
                   location.get("lon", 0),
               ),
             color=await ctx.embed_color(),
             timestamp=data.time,
         )
         em.set_author(name=_("Powered by Dark Sky"),
                       url="https://darksky.net/poweredby/")
         em.set_footer(text=_("Page {}/8").format(i + 1))
         try:
             # FIXME: find a better way to do that
             summary = data.summary
         except PropertyUnavailable:
             summary = _("No summary for this day")
         em.add_field(
             name=_("Summary"),
             value="{} {}".format(
                 WEATHER_STATES.get(data.icon, UNKNOWN_EMOJI),
                 summary,
             ),
         )
         em.add_field(
             name=_("Temperature"),
             value=
             f"{data.temperatureMin} — {data.temperatureMax} {await self.get_localized_units(ctx, 'temp')}\n"
             f"({data.apparentTemperatureMin} — {data.apparentTemperatureMax}{await self.get_localized_units(ctx, 'temp')})",
         )
         em.add_field(
             name=_("Air pressure"),
             value="{} {}".format(
                 data.pressure, await
                 self.get_localized_units(ctx, "pressure")),
         )
         em.add_field(name=_("Humidity"),
                      value=f"{int(data.humidity * 100)}%")
         em.add_field(
             name=_("Visibility"),
             value="{} {}".format(
                 data.visibility, await
                 self.get_localized_units(ctx, "distance")),
         )
         em.add_field(
             name=_("Wind speed"),
             value="{} {} {}".format(
                 await self.wind_bearing_direction(data.windBearing),
                 data.windSpeed,
                 await self.get_localized_units(ctx, "speed"),
             ),
         )
         em.add_field(name=_("Cloud cover"),
                      value=f"{int(data.cloudCover * 100)}%")
         em.add_field(
             name=_("Ozone density"),
             value="{} [DU](https://en.wikipedia.org/wiki/Dobson_unit)".
             format(data.ozone),
         )
         em.add_field(name=_("UV index"), value=data.uvIndex)
         try:
             preciptype = data.precipType
         except PropertyUnavailable:
             preciptype = None
         try:
             precipaccumulation = data.precipAccumulation
         except PropertyUnavailable:
             precipaccumulation = None
         em.add_field(
             name=_("Precipitation"),
             value=_("Probability: {}%\n").format(
                 int(data.precipProbability * 100)) +
             _("Intensity: {} {}").format(
                 int(data.precipIntensity * 100),
                 await self.get_localized_units(ctx, "intensity"),
             ) + (preciptype and _("\nType: {}").format(
                 _(PRECIP_TYPE_I18N.get(preciptype, preciptype))) or "") +
             (precipaccumulation
              and _("\nSnowfall accumulation: {} {}").format(
                  precipaccumulation,
                  await self.get_localized_units(ctx, "accumulation"),
              ) or ""),
         )
         em.add_field(name=_("Moon phase"),
                      value=await self.num_to_moon(data.moonPhase))
         pages.append(em)
     await menu(ctx, pages, DEFAULT_CONTROLS)
Ejemplo n.º 13
0
    def reload_translations(self):
        new_locale = get_locale()
        if self.previous_locale == new_locale:
            return  # Don't care if the locale hasn't changed
        self.previous_locale = new_locale

        # Embed constants
        self.LINK_LIST_TITLE = _("Role Links")
        self.LINK_LIST_NO_LINKS = _("There are no links in this server")

        # Logging message constants
        self.LOG_MESSAGE_NOT_FOUND = _("Could not find message {msg_id} in {channel}.")
        self.LOG_CHANNEL_NOT_FOUND = _("Could not find channel {channel_id}.")
        self.LOG_SERVER_NOT_FOUND = _("Could not find server with id {guild_id}.")
        self.LOG_PROCESSING_LOOP_ENDED = _("The processing loop has ended.")
        self.LOG_BINDINGS = _("Cached bindings: {bindings}")

        # Message constants
        self.PROGRESS_FORMAT = _("Checked {c} out of {r} reactions out of {t} emojis.")
        self.PROGRESS_COMPLETE_FORMAT = _(
            """:white_check_mark: Completed! Checked a total of {c} reactions.
Gave a total of {g} roles."""
        )
        self.MESSAGE_NOT_FOUND = _(":x: Message not found.")
        self.ALREADY_BOUND = _(":x: The emoji is already bound on that message.")
        self.NOT_IN_SERVER = _(":x: The channel must be in a server.")
        self.ROLE_NOT_FOUND = _(":x: Role not found on the given channel's server.")
        self.EMOJI_NOT_FOUND = _(":x: Emoji not found in any of my servers or in unicode emojis.")
        self.CANT_ADD_REACTIONS = _(
            ":x: I don't have the permission to add reactions in that channel."
        )
        self.CANT_MANAGE_ROLES = _(
            ":x: I don't have the permission to manage users' roles in the channel's server."
        )
        self.ROLE_SUCCESSFULLY_BOUND = _(
            ":white_check_mark: The role has been bound to {} on the message in {}."
        )
        self.ROLE_NOT_BOUND = _(":x: The role is not bound to that message.")
        self.INITIALIZING = _("Initializing...")
        self.ROLE_UNBOUND = _(":put_litter_in_its_place: Unbound the role on the message.\n")
        self.REACTION_CLEAN_START = self.ROLE_UNBOUND + _("Removing linked reactions...")
        self.PROGRESS_REMOVED = self.ROLE_UNBOUND + _("Removed **{} / {}** reactions...")
        self.REACTION_CLEAN_DONE = self.ROLE_UNBOUND + _("Removed **{}** reactions.")
        self.LINK_MESSAGE_NOT_FOUND = _("The following messages weren't found: {}")
        self.LINK_CHANNEL_NOT_FOUND = _("The following channels weren't found: {}")
        self.LINK_PAIR_INVALID = _("The following channel-message pairs were invalid: {}")
        self.LINK_MUST_SPECIFY = _("You must specify at least one message to be linked.")
        self.LINK_FAILED = _(":x: Failed to link reactions.\n")
        self.LINK_SUCCESSFUL = _(":white_check_mark: Successfully linked the reactions.")
        self.LINK_NAME_TAKEN = _(
            ":x: That link name is already used in the current server. "
            "Remove it before assigning to it."
        )
        self.UNLINK_NOT_FOUND = _(":x: Could not find a link with that name in this server.")
        self.UNLINK_SUCCESSFUL = _(
            ":white_check_mark: The link has been removed from this server."
        )
        self.CANT_CHECK_LINKED = _(":x: Cannot run a check on linked messages.")
        self.REACTION_NOT_FOUND = _(":x: Could not find the reaction of that message.")
        self.CANT_GIVE_ROLE = _(
            ":x: I can't give that role! Maybe it's higher than my own highest role?"
        )
Ejemplo n.º 14
0
    async def act(self, ctx, *, target: Union[discord.Member, str] = None):
        """
        Acts on the specified user.
        """
        if not target or isinstance(target, str):
            return  # no help text

        try:
            if not ctx.guild:
                raise KeyError()
            message = await self.config.guild(ctx.guild).get_raw(
                "custom", ctx.invoked_with)
        except KeyError:
            try:
                message = await self.config.get_raw("custom", ctx.invoked_with)
            except KeyError:
                message = NotImplemented

        if message is None:  # ignored command
            return
        elif message is NotImplemented:  # default
            # humanize action text
            action = inflection.humanize(ctx.invoked_with).split()
            iverb = -1

            for cycle in range(2):
                if iverb > -1:
                    break
                for i, act in enumerate(action):
                    act = act.lower()
                    if (act in NOLY_ADV or act in CONJ
                            or (act.endswith("ly") and act not in LY_VERBS)
                            or (not cycle and act in SOFT_VERBS)):
                        continue
                    action[i] = inflection.pluralize(action[i])
                    iverb = max(iverb, i)

            if iverb < 0:
                return
            action.insert(iverb + 1, target.mention)
            message = italics(" ".join(action))
        else:
            message = message.format(target, user=target)

        # add reaction gif
        if not ctx.channel.permissions_for(ctx.me).embed_links:
            return await ctx.send(message)
        key = (await get_shared_api_tokens(ctx.bot, "tenor")).get("api_key")
        if not key:
            return await ctx.send(message)
        async with aiohttp.request(
                "GET",
                "https://api.tenor.com/v1/search",
                params={
                    "q": ctx.invoked_with,
                    "key": key,
                    "anon_id": str(ctx.author.id ^ ctx.me.id),
                    "media_filter": "minimal",
                    "contentfilter": "low",
                    "ar_range": "wide",
                    "locale": get_locale(),
                },
        ) as response:
            if response.status >= 400:
                json: dict = {}
            else:
                json = await response.json()
        if not json.get("results"):
            return await ctx.send(message)
        message = f"{message}\n\n{random.choice(json['results'])['itemurl']}"
        await ctx.send(message)