Compare commits

...

33 Commits

Author SHA1 Message Date
jack1142
734ab49f6a Version bump 2020-08-18 01:08:48 +02:00
jack1142
204d82db8f Red 3.3.12 - Changelog (#4194) 2020-08-18 01:01:53 +02:00
zephyrkul
77a5b4c742 [Filter] Add missing parentheses (#4185) 2020-08-17 23:40:41 +02:00
Douglas
7707fec199 Don't send message about DMing ACL when command's called from DM (#4188)
* Only send a message telling the user the file will be send via DM if the original message didn't come from a DM

* Remove unused import for AsyncIter

* Changed channel instance comparison to discord.DMChannel

Co-authored-by: douglas-cpp <douglasc.dev@gmail.com>
2020-08-17 23:40:41 +02:00
PredaaA
4ef51a165f [Audio] Send an more user friendly message at "Track Stuck" errors (#4061)
Co-authored-by: Draper <27962761+Drapersniper@users.noreply.github.com>
2020-08-17 23:40:40 +02:00
Dav
574dabbeac [Reports] Give friendly error if no report channel set (#4136) 2020-08-17 23:40:40 +02:00
Dav
e463c307ea [Core] Typo fix (#4035) 2020-08-17 23:40:40 +02:00
Jamie
b8e56f3632 [Streams] Attempt to fix unclear error in youtubestream (#4148) 2020-08-17 23:40:40 +02:00
MeatyChunks
dc3fe1f8f8 Fix amount of messages in log message of [p]cleanup message (#4156)
Fixes #4155
2020-08-17 23:40:39 +02:00
Kowlin
1dda0f95de [Streams] Allow for consume all on messages (#4183)
* Added consume all to streams.

* Updated help doc

* Styling checks, man...
2020-08-17 23:40:39 +02:00
Predeactor
5e5687d0bb Propose more info on simple serverinfo (#4121)
* Propose more info on simple serverinfo

* Fixed black + serverinfo

* Fix black (Sorry)

* Merge jack's suggestions

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Change separator

👍

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Black

* Update general.py

Co-authored-by: Ubuntu <ubuntu@vps-35e65bf5.vps.ovh.net>
Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
2020-08-17 23:40:39 +02:00
Jyu Viole Grace
e0ad67aa3d [Trivia] whosthatpokemon2 trivia - Generation 2 Pokémons (#4102)
* Generation 2 Pokemons for whosthatpokemon trivia

* replace "pokemon" with "Pokémon"
2020-08-17 23:40:39 +02:00
Jyu Viole Grace
c3eb4abd7a use "Pokémon" instead of "pokemon" 2020-08-17 23:40:38 +02:00
jack1142
d97760997e Give friendlier error when package can't be found during bot startup (#4079) 2020-08-17 23:40:38 +02:00
Vexed
81193d17c7 [Mod] [p]mute|unmute voice now take action instantly (#4064)
* make mute and unmute have the same fail string

* now add the jucy bits

* hmm no

This reverts commit a445bd8415.

* Apply suggestions from code review

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
2020-08-17 23:40:38 +02:00
Dav
cf78ea06e2 [Mute] Indicate that a guild mute/unmute is currently being processed. (#4172)
Co-authored-by: fixator10
2020-08-17 23:40:38 +02:00
PredaaA
26122e56a0 [General] Change PUBLIC feature to COMMUNITY in serverinfo (#4116) 2020-08-17 23:40:37 +02:00
Flame442
d8d3e9fceb [Trivia] Remove an unnecessary .format (#4175) 2020-08-10 01:17:44 +02:00
jack1142
b3281385e9 3.3.11 changelog (#4173)
* 3.3.11 changelog

* Update docs/changelog_3_3_0.rst

Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>

* Update docs/changelog_3_3_0.rst

Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>

* Update docs/changelog_3_3_0.rst

Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>

* Update docs/changelog_3_3_0.rst

Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>

Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>
2020-08-10 01:17:37 +02:00
jack1142
e6947bdbf6 Bump to 3.3.11 2020-08-10 01:10:35 +02:00
Douglas
a6d924221d [Trivia] Fix unresolved reference to bank.BalanceTooHigh (#4170)
Co-authored-by: douglas-cpp <douglasc.dev@gmail.com>
2020-08-10 00:50:22 +02:00
Vexed
cf7db1e891 Docs improvements after watching two self-proclaimed incompetent people install it (#4119)
* the thing

* right

* hmm

* review
2020-08-10 00:50:03 +02:00
jack1142
bc544d476b Fix the errors related to installed module having invalid commit data (#4086) 2020-08-10 00:49:17 +02:00
MeatyChunks
10976f218b [Economy] Prevent forbidden error when blocked by user (#4120)
Stop `[p]payouts` throwing a console error if the user has blocked the bot. Probably too spammy to put the payout message in chat.
2020-08-10 00:48:34 +02:00
jack1142
a589838a41 Only accept positive integers in [p]cleanup commands (#4115) 2020-08-10 00:48:26 +02:00
Ryan
e36d1f143d [CustomCom] Add missing await (#4108) 2020-08-10 00:47:36 +02:00
jack1142
9a6f78e62e Update chocolatey install commands per chocolatey.org (#4098) 2020-08-10 00:47:28 +02:00
Draper
649db87a8a [Audio] Ensure TrackEnqueueError is always handled (#3879)
Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com>
2020-08-10 00:43:20 +02:00
jack1142
c6f9a78d57 Update pyenv instructions to install Python 3.8.5 (#4094) 2020-08-10 00:35:30 +02:00
jack1142
bd89e4386d Fix no message when unregistered reason is used in [p]warn (#3840) 2020-08-10 00:34:26 +02:00
jack1142
a962f94a58 Ignore that the rule for model doesn't exist when trying to remove it (#4036) 2020-08-10 00:34:10 +02:00
jack1142
3471011f85 Fix the error for empty author list in [p]findcog (#4042) 2020-08-10 00:34:01 +02:00
Draper
186dbe6118 Lavalink.jar bump for internal manager (#4168) 2020-08-10 00:27:45 +02:00
30 changed files with 754 additions and 277 deletions

View File

@@ -1,5 +1,86 @@
.. 3.3.x Changelogs .. 3.3.x Changelogs
Redbot 3.3.12 (2020-08-18)
==========================
| Thanks to all these amazing people that contributed to this release:
| :ghuser:`Dav-Git`, :ghuser:`douglas-cpp`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`MeatyChunks`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`thisisjvgrace`, :ghuser:`Vexed01`, :ghuser:`zephyrkul`
End-user changelog
------------------
Core Bot
********
- Red now logs clearer error if it can't find package to load in any cog path during bot startup (:issue:`4079`)
Mod
***
- ``[p]mute voice`` and ``[p]unmute voice`` now take action instantly if bot has Move Members permission (:issue:`4064`)
- Added typing to ``[p](un)mute guild`` to indicate that mute is being processed (:issue:`4066`, :issue:`4172`)
Streams
*******
- Improve error messages for invalid channel names/IDs (:issue:`4147`, :issue:`4148`)
Trivia Lists
************
- Added ``whosthatpokemon2`` trivia containing Pokémons from 2nd generation (:issue:`4102`)
- Added ``whosthatpokemon3`` trivia containing Pokémons from 3rd generation (:issue:`4141`)
Miscellaneous
-------------
- Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4116`)
- Simple version of ``[p]serverinfo`` now shows info about more detailed ``[p]serverinfo 1`` (:issue:`4121`)
Redbot 3.3.11 (2020-08-10)
==========================
| Thanks to all these amazing people that contributed to this release:
| :ghuser:`douglas-cpp`, :ghuser:`Drapersniper`, :ghuser:`jack1142`, :ghuser:`MeatyChunks`, :ghuser:`Vexed01`, :ghuser:`yamikaitou`
End-user changelog
------------------
Audio
*****
- Audio should now work again on all voice regions (:issue:`4162`, :issue:`4168`)
- Removed an edge case where an unfriendly error message was sent in Audio cog (:issue:`3879`)
Cleanup
*******
- Fixed a bug causing ``[p]cleanup`` commands to clear all messages within last 2 weeks when ``0`` is passed as the amount of messages to delete (:issue:`4114`, :issue:`4115`)
CustomCommands
**************
- ``[p]cc show`` now sends an error message when command with the provided name couldn't be found (:issue:`4108`)
Downloader
**********
- ``[p]findcog`` no longer fails for 3rd-party cogs without any author (:issue:`4032`, :issue:`4042`)
- Update commands no longer crash when a different repo is added under a repo name that was once used (:issue:`4086`)
Permissions
***********
- ``[p]permissions removeserverrule`` and ``[p]permissions removeglobalrule`` no longer error when trying to remove a rule that doesn't exist (:issue:`4028`, :issue:`4036`)
Warnings
********
- ``[p]warn`` now sends an error message (instead of no feedback) when an unregistered reason is used by someone who doesn't have Administrator permission (:issue:`3839`, :issue:`3840`)
Redbot 3.3.10 (2020-07-09) Redbot 3.3.10 (2020-07-09)
=================================== ===================================

View File

@@ -397,7 +397,7 @@ Then run the following command:
.. code-block:: none .. code-block:: none
CONFIGURE_OPTS=--enable-optimizations pyenv install 3.8.3 -v CONFIGURE_OPTS=--enable-optimizations pyenv install 3.8.5 -v
This may take a long time to complete, depending on your hardware. For some machines (such as This may take a long time to complete, depending on your hardware. For some machines (such as
Raspberry Pis and micro-tier VPSes), it may take over an hour; in this case, you may wish to remove Raspberry Pis and micro-tier VPSes), it may take over an hour; in this case, you may wish to remove
@@ -409,7 +409,7 @@ After that is finished, run:
.. code-block:: none .. code-block:: none
pyenv global 3.8.3 pyenv global 3.8.5
Pyenv is now installed and your system should be configured to run Python 3.8. Pyenv is now installed and your system should be configured to run Python 3.8.

View File

@@ -29,13 +29,14 @@ Using PowerShell and Chocolatey (recommended)
********************************************* *********************************************
To install via PowerShell, search "powershell" in the Windows start menu, To install via PowerShell, search "powershell" in the Windows start menu,
right-click on it and then click "Run as administrator" right-click on it and then click "Run as administrator".
Then run each of the following commands: Then run each of the following commands:
.. code-block:: none .. code-block:: none
Set-ExecutionPolicy Bypass -Scope Process -Force Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco upgrade git --params "/GitOnlyOnPath /WindowsTerminal" -y choco upgrade git --params "/GitOnlyOnPath /WindowsTerminal" -y
choco upgrade visualstudio2019-workload-vctools -y choco upgrade visualstudio2019-workload-vctools -y
@@ -63,7 +64,7 @@ Manually installing dependencies
* `MSVC Build tools <https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019>`_ * `MSVC Build tools <https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019>`_
* `Python 3.8.1 <https://www.python.org/downloads/>`_ - Red needs Python 3.8.1 or greater * `Python 3.8.1 or greater <https://www.python.org/downloads/>`_
.. attention:: Please make sure that the box to add Python to PATH is CHECKED, otherwise .. attention:: Please make sure that the box to add Python to PATH is CHECKED, otherwise
you may run into issues when trying to run Red. you may run into issues when trying to run Red.
@@ -86,7 +87,7 @@ Creating a Virtual Environment
.. tip:: .. tip::
If you want to learn more about virtual environments, see page: `about-venvs` If you want to learn more about virtual environments, see page: `about-venvs`.
We require installing Red into a virtual environment. Don't be scared, it's very We require installing Red into a virtual environment. Don't be scared, it's very
straightforward. straightforward.
@@ -95,7 +96,12 @@ First, choose a directory where you would like to create your virtual environmen
to keep it in a location which is easy to type out the path to. From now, we'll call it to keep it in a location which is easy to type out the path to. From now, we'll call it
``redenv`` and it will be located in your home directory. ``redenv`` and it will be located in your home directory.
Start with opening a command prompt (open Start, search for "command prompt", then click it) Start with opening a command prompt (open Start, search for "command prompt", then click it).
.. note::
You shouldn't run command prompt as administrator when creating your virtual environment, or
running Red.
.. warning:: .. warning::
@@ -144,11 +150,6 @@ Run **one** of the following set of commands, depending on what extras you want
python -m pip install -U pip setuptools wheel python -m pip install -U pip setuptools wheel
python -m pip install -U Red-DiscordBot[postgres] python -m pip install -U Red-DiscordBot[postgres]
.. note::
These commands are also used for updating Red
-------------------------- --------------------------
Setting Up and Running Red Setting Up and Running Red
-------------------------- --------------------------

View File

@@ -191,7 +191,7 @@ def _update_event_loop_policy():
_asyncio.set_event_loop_policy(_uvloop.EventLoopPolicy()) _asyncio.set_event_loop_policy(_uvloop.EventLoopPolicy())
__version__ = "3.3.11.dev1" __version__ = "3.3.12"
version_info = VersionInfo.from_str(__version__) version_info = VersionInfo.from_str(__version__)
# Filter fuzzywuzzy slow sequence matcher warning # Filter fuzzywuzzy slow sequence matcher warning

View File

@@ -576,6 +576,16 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
notify_channel = self.bot.get_channel(notify_channel) notify_channel = self.bot.get_channel(notify_channel)
await self.send_embed_msg(notify_channel, title=_("Couldn't get a valid track.")) await self.send_embed_msg(notify_channel, title=_("Couldn't get a valid track."))
return return
except TrackEnqueueError:
self.update_player_lock(ctx, False)
return await self.send_embed_msg(
ctx,
title=_("Unable to Get Track"),
description=_(
"I'm unable get a track from Lavalink at the moment, try again in a few "
"minutes."
),
)
if not guild_data["auto_play"]: if not guild_data["auto_play"]:
await ctx.invoke(self.command_audioset_autoplay_toggle) await ctx.invoke(self.command_audioset_autoplay_toggle)

View File

@@ -22,7 +22,7 @@ from ...apis.playlist_interface import create_playlist, delete_playlist, get_all
from ...audio_dataclasses import LocalPath, Query from ...audio_dataclasses import LocalPath, Query
from ...audio_logging import IS_DEBUG, debug_exc_log from ...audio_logging import IS_DEBUG, debug_exc_log
from ...converters import ComplexScopeParser, ScopeParser from ...converters import ComplexScopeParser, ScopeParser
from ...errors import MissingGuild, TooManyMatches from ...errors import MissingGuild, TooManyMatches, TrackEnqueueError
from ...utils import PlaylistScope from ...utils import PlaylistScope
from ..abc import MixinMeta from ..abc import MixinMeta
from ..cog_utils import CompositeMetaClass, LazyGreedyConverter, PlaylistConverter, _ from ..cog_utils import CompositeMetaClass, LazyGreedyConverter, PlaylistConverter, _
@@ -1817,6 +1817,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
uploaded_playlist_name = uploaded_playlist.get( uploaded_playlist_name = uploaded_playlist.get(
"name", (file_url.split("/")[6]).split(".")[0] "name", (file_url.split("/")[6]).split(".")[0]
) )
try:
if self.api_interface is not None and ( if self.api_interface is not None and (
not uploaded_playlist_url not uploaded_playlist_url
or not self.match_yt_playlist(uploaded_playlist_url) or not self.match_yt_playlist(uploaded_playlist_url)
@@ -1854,6 +1855,16 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
playlist_url=uploaded_playlist_url, playlist_url=uploaded_playlist_url,
scope_data=(scope, author, guild, specified_user), scope_data=(scope, author, guild, specified_user),
) )
except TrackEnqueueError:
self.update_player_lock(ctx, False)
return await self.send_embed_msg(
ctx,
title=_("Unable to Get Track"),
description=_(
"I'm unable get a track from Lavalink at the moment, try again in a few "
"minutes."
),
)
@commands.cooldown(1, 60, commands.BucketType.member) @commands.cooldown(1, 60, commands.BucketType.member)
@command_playlist.command( @command_playlist.command(

View File

@@ -5,7 +5,7 @@ import logging
import discord import discord
import lavalink import lavalink
from ...errors import DatabaseError from ...errors import DatabaseError, TrackEnqueueError
from ..abc import MixinMeta from ..abc import MixinMeta
from ..cog_utils import CompositeMetaClass, _ from ..cog_utils import CompositeMetaClass, _
@@ -62,12 +62,25 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
await self.api_interface.autoplay(player, self.playlist_api) await self.api_interface.autoplay(player, self.playlist_api)
except DatabaseError: except DatabaseError:
notify_channel = player.fetch("channel") notify_channel = player.fetch("channel")
if notify_channel:
notify_channel = self.bot.get_channel(notify_channel) notify_channel = self.bot.get_channel(notify_channel)
if notify_channel:
await self.send_embed_msg( await self.send_embed_msg(
notify_channel, title=_("Couldn't get a valid track.") notify_channel, title=_("Couldn't get a valid track.")
) )
return return
except TrackEnqueueError:
notify_channel = player.fetch("channel")
notify_channel = self.bot.get_channel(notify_channel)
if notify_channel:
await self.send_embed_msg(
notify_channel,
title=_("Unable to Get Track"),
description=_(
"I'm unable get a track from Lavalink at the moment, try again in a few "
"minutes."
),
)
return
if event_type == lavalink.LavalinkEvents.TRACK_START and notify: if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
notify_channel = player.fetch("channel") notify_channel = player.fetch("channel")
if notify_channel: if notify_channel:
@@ -180,7 +193,9 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
embed = discord.Embed( embed = discord.Embed(
colour=await self.bot.get_embed_color(message_channel), colour=await self.bot.get_embed_color(message_channel),
title=_("Track Stuck"), title=_("Track Stuck"),
description="{}".format(description), description=_(
"Playback of the song has stopped due to an unexcepted error.\n{error}"
).format(error=description),
) )
else: else:
embed = discord.Embed( embed = discord.Embed(

View File

@@ -20,7 +20,7 @@ from .errors import LavalinkDownloadFailed
log = logging.getLogger("red.audio.manager") log = logging.getLogger("red.audio.manager")
JAR_VERSION: Final[str] = "3.3.1" JAR_VERSION: Final[str] = "3.3.1"
JAR_BUILD: Final[int] = 1068 JAR_BUILD: Final[int] = 1069
LAVALINK_DOWNLOAD_URL: Final[str] = ( LAVALINK_DOWNLOAD_URL: Final[str] = (
"https://github.com/Cog-Creators/Lavalink-Jars/releases/download/" "https://github.com/Cog-Creators/Lavalink-Jars/releases/download/"
f"{JAR_VERSION}_{JAR_BUILD}/" f"{JAR_VERSION}_{JAR_BUILD}/"

View File

@@ -1,6 +1,6 @@
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union, List, Callable, Set from typing import Callable, List, Optional, Set, Union
import discord import discord
@@ -10,7 +10,7 @@ from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import humanize_number from redbot.core.utils.chat_formatting import humanize_number
from redbot.core.utils.mod import slow_deletion, mass_purge from redbot.core.utils.mod import slow_deletion, mass_purge
from redbot.core.utils.predicates import MessagePredicate from redbot.core.utils.predicates import MessagePredicate
from .converters import RawMessageIds from .converters import PositiveInt, RawMessageIds, positive_int
_ = Translator("Cleanup", __file__) _ = Translator("Cleanup", __file__)
@@ -60,9 +60,9 @@ class Cleanup(commands.Cog):
async def get_messages_for_deletion( async def get_messages_for_deletion(
*, *,
channel: discord.TextChannel, channel: discord.TextChannel,
number: int = None, number: Optional[PositiveInt] = None,
check: Callable[[discord.Message], bool] = lambda x: True, check: Callable[[discord.Message], bool] = lambda x: True,
limit: int = None, limit: Optional[PositiveInt] = None,
before: Union[discord.Message, datetime] = None, before: Union[discord.Message, datetime] = None,
after: Union[discord.Message, datetime] = None, after: Union[discord.Message, datetime] = None,
delete_pinned: bool = False, delete_pinned: bool = False,
@@ -105,7 +105,7 @@ class Cleanup(commands.Cog):
break break
if message_filter(message): if message_filter(message):
collected.append(message) collected.append(message)
if number and number <= len(collected): if number is not None and number <= len(collected):
break break
return collected return collected
@@ -120,7 +120,7 @@ class Cleanup(commands.Cog):
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
async def text( async def text(
self, ctx: commands.Context, text: str, number: int, delete_pinned: bool = False self, ctx: commands.Context, text: str, number: positive_int, delete_pinned: bool = False
): ):
"""Delete the last X messages matching the specified text. """Delete the last X messages matching the specified text.
@@ -169,7 +169,7 @@ class Cleanup(commands.Cog):
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
async def user( async def user(
self, ctx: commands.Context, user: str, number: int, delete_pinned: bool = False self, ctx: commands.Context, user: str, number: positive_int, delete_pinned: bool = False
): ):
"""Delete the last X messages from a specified user. """Delete the last X messages from a specified user.
@@ -270,7 +270,7 @@ class Cleanup(commands.Cog):
self, self,
ctx: commands.Context, ctx: commands.Context,
message_id: RawMessageIds, message_id: RawMessageIds,
number: int, number: positive_int,
delete_pinned: bool = False, delete_pinned: bool = False,
): ):
"""Deletes X messages before specified message. """Deletes X messages before specified message.
@@ -351,7 +351,9 @@ class Cleanup(commands.Cog):
@cleanup.command() @cleanup.command()
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
async def messages(self, ctx: commands.Context, number: int, delete_pinned: bool = False): async def messages(
self, ctx: commands.Context, number: positive_int, delete_pinned: bool = False
):
"""Delete the last X messages. """Delete the last X messages.
Example: Example:
@@ -372,7 +374,7 @@ class Cleanup(commands.Cog):
to_delete.append(ctx.message) to_delete.append(ctx.message)
reason = "{}({}) deleted {} messages in channel {}.".format( reason = "{}({}) deleted {} messages in channel {}.".format(
author.name, author.id, number, channel.name author.name, author.id, len(to_delete), channel.name
) )
log.info(reason) log.info(reason)
@@ -381,7 +383,9 @@ class Cleanup(commands.Cog):
@cleanup.command(name="bot") @cleanup.command(name="bot")
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
async def cleanup_bot(self, ctx: commands.Context, number: int, delete_pinned: bool = False): async def cleanup_bot(
self, ctx: commands.Context, number: positive_int, delete_pinned: bool = False
):
"""Clean up command messages and messages from the bot.""" """Clean up command messages and messages from the bot."""
channel = ctx.channel channel = ctx.channel
@@ -458,7 +462,7 @@ class Cleanup(commands.Cog):
async def cleanup_self( async def cleanup_self(
self, self,
ctx: commands.Context, ctx: commands.Context,
number: int, number: positive_int,
match_pattern: str = None, match_pattern: str = None,
delete_pinned: bool = False, delete_pinned: bool = False,
): ):
@@ -531,7 +535,7 @@ class Cleanup(commands.Cog):
@cleanup.command(name="spam") @cleanup.command(name="spam")
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
async def cleanup_spam(self, ctx: commands.Context, number: int = 50): async def cleanup_spam(self, ctx: commands.Context, number: positive_int = PositiveInt(50)):
"""Deletes duplicate messages in the channel from the last X messages and keeps only one copy. """Deletes duplicate messages in the channel from the last X messages and keeps only one copy.
Defaults to 50. Defaults to 50.

View File

@@ -1,12 +1,30 @@
from redbot.core.commands import Converter, BadArgument from typing import NewType, TYPE_CHECKING
from redbot.core.commands import BadArgument, Context, Converter
from redbot.core.i18n import Translator from redbot.core.i18n import Translator
from redbot.core.utils.chat_formatting import inline
_ = Translator("Cleanup", __file__) _ = Translator("Cleanup", __file__)
class RawMessageIds(Converter): class RawMessageIds(Converter):
async def convert(self, ctx, argument): async def convert(self, ctx: Context, argument: str) -> int:
if argument.isnumeric() and len(argument) >= 17: if argument.isnumeric() and len(argument) >= 17:
return int(argument) return int(argument)
raise BadArgument(_("{} doesn't look like a valid message ID.").format(argument)) raise BadArgument(_("{} doesn't look like a valid message ID.").format(argument))
PositiveInt = NewType("PositiveInt", int)
if TYPE_CHECKING:
positive_int = PositiveInt
else:
def positive_int(arg: str) -> int:
try:
ret = int(arg)
except ValueError:
raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg)))
if ret <= 0:
raise BadArgument(_("{arg} is not a positive integer.").format(arg=inline(arg)))
return ret

View File

@@ -464,7 +464,7 @@ class CustomCommands(commands.Cog):
try: try:
cmd = await self.commandobj.get_full(ctx.message, command_name) cmd = await self.commandobj.get_full(ctx.message, command_name)
except NotFound: except NotFound:
ctx.send(_("I could not not find that custom command.")) await ctx.send(_("I could not not find that custom command."))
return return
responses = cmd["response"] responses = cmd["response"]

View File

@@ -303,9 +303,21 @@ class Downloader(commands.Cog):
hashes: Dict[Tuple[Repo, str], Set[InstalledModule]] = defaultdict(set) hashes: Dict[Tuple[Repo, str], Set[InstalledModule]] = defaultdict(set)
for module in modules: for module in modules:
module.repo = cast(Repo, module.repo) module.repo = cast(Repo, module.repo)
if module.repo.commit != module.commit and await module.repo.is_ancestor( if module.repo.commit != module.commit:
module.commit, module.repo.commit try:
): should_add = await module.repo.is_ancestor(module.commit, module.repo.commit)
except errors.UnknownRevision:
# marking module for update if the saved commit data is invalid
last_module_occurrence = await module.repo.get_last_module_occurrence(
module.name
)
if last_module_occurrence is not None and not last_module_occurrence.disabled:
if last_module_occurrence.type == InstallableType.COG:
cogs_to_update.add(last_module_occurrence)
elif last_module_occurrence.type == InstallableType.SHARED_LIBRARY:
libraries_to_update.add(last_module_occurrence)
else:
if should_add:
hashes[(module.repo, module.commit)].add(module) hashes[(module.repo, module.commit)].add(module)
update_commits = [] update_commits = []
@@ -1387,7 +1399,11 @@ class Downloader(commands.Cog):
cog_name = self.cog_name_from_instance(cog) cog_name = self.cog_name_from_instance(cog)
installed, cog_installable = await self.is_installed(cog_name) installed, cog_installable = await self.is_installed(cog_name)
if installed: if installed:
made_by = humanize_list(cog_installable.author) or _("Missing from info.json") made_by = (
humanize_list(cog_installable.author)
if cog_installable.author
else _("Missing from info.json")
)
repo_url = ( repo_url = (
_("Missing from installed repos") _("Missing from installed repos")
if cog_installable.repo is None if cog_installable.repo is None

View File

@@ -198,6 +198,11 @@ class Repo(RepoJSONMixin):
descendant_rev : `str` descendant_rev : `str`
Descendant revision Descendant revision
Raises
------
.UnknownRevision
When git cannot find one of the provided revisions.
Returns Returns
------- -------
bool bool
@@ -212,10 +217,17 @@ class Repo(RepoJSONMixin):
maybe_ancestor_rev=maybe_ancestor_rev, maybe_ancestor_rev=maybe_ancestor_rev,
descendant_rev=descendant_rev, descendant_rev=descendant_rev,
) )
p = await self._run(git_command, valid_exit_codes=valid_exit_codes) p = await self._run(git_command, valid_exit_codes=valid_exit_codes, debug_only=True)
if p.returncode in valid_exit_codes: if p.returncode in valid_exit_codes:
return not bool(p.returncode) return not bool(p.returncode)
# this is a plumbing command so we're safe here
stderr = p.stderr.decode(**DECODE_PARAMS).strip()
if stderr.startswith(("fatal: Not a valid object name", "fatal: Not a valid commit name")):
rev, *__ = stderr[31:].split(maxsplit=1)
raise errors.UnknownRevision(f"Revision {rev} cannot be found.", git_command)
raise errors.GitException( raise errors.GitException(
f"Git failed to determine if commit {maybe_ancestor_rev}" f"Git failed to determine if commit {maybe_ancestor_rev}"
f" is ancestor of {descendant_rev} for repo at path: {self.folder_path}", f" is ancestor of {descendant_rev} for repo at path: {self.folder_path}",

View File

@@ -532,7 +532,10 @@ class Economy(commands.Cog):
@guild_only_check() @guild_only_check()
async def payouts(self, ctx: commands.Context): async def payouts(self, ctx: commands.Context):
"""Show the payouts for the slot machine.""" """Show the payouts for the slot machine."""
try:
await ctx.author.send(SLOT_PAYOUTS_MSG) await ctx.author.send(SLOT_PAYOUTS_MSG)
except discord.Forbidden:
await ctx.send(_("I can't send direct messages to you."))
@commands.command() @commands.command()
@guild_only_check() @guild_only_check()

View File

@@ -255,7 +255,7 @@ class Filter(commands.Cog):
elif isinstance(server_or_channel, discord.TextChannel): elif isinstance(server_or_channel, discord.TextChannel):
async with self.config.channel(server_or_channel).filter() as cur_list: async with self.config.channel(server_or_channel).filter() as cur_list:
for w in words: for w in words:
if w.lower not in cur_list and w: if w.lower() not in cur_list and w:
cur_list.append(w.lower()) cur_list.append(w.lower())
added = True added = True

View File

@@ -267,7 +267,13 @@ class General(commands.Cog):
data.add_field(name=_("Voice Channels"), value=voice_channels) data.add_field(name=_("Voice Channels"), value=voice_channels)
data.add_field(name=_("Roles"), value=humanize_number(len(guild.roles))) data.add_field(name=_("Roles"), value=humanize_number(len(guild.roles)))
data.add_field(name=_("Owner"), value=str(guild.owner)) data.add_field(name=_("Owner"), value=str(guild.owner))
data.set_footer(text=_("Server ID: ") + str(guild.id)) data.set_footer(
text=_("Server ID: ")
+ str(guild.id)
+ _(" • Use {command} for more info on the server.").format(
command=f"{ctx.clean_prefix}serverinfo 1"
)
)
if guild.icon_url: if guild.icon_url:
data.set_author(name=guild.name, url=guild.icon_url) data.set_author(name=guild.name, url=guild.icon_url)
data.set_thumbnail(url=guild.icon_url) data.set_thumbnail(url=guild.icon_url)
@@ -364,7 +370,7 @@ class General(commands.Cog):
"VERIFIED": _("Verified"), "VERIFIED": _("Verified"),
"DISCOVERABLE": _("Server Discovery"), "DISCOVERABLE": _("Server Discovery"),
"FEATURABLE": _("Featurable"), "FEATURABLE": _("Featurable"),
"PUBLIC": _("Public"), "COMMUNITY": _("Community"),
"PUBLIC_DISABLED": _("Public disabled"), "PUBLIC_DISABLED": _("Public disabled"),
"INVITE_SPLASH": _("Splash Invite"), "INVITE_SPLASH": _("Splash Invite"),
"VIP_REGIONS": _("VIP Voice Servers"), "VIP_REGIONS": _("VIP Voice Servers"),

View File

@@ -3,6 +3,7 @@ from typing import cast, Optional
import discord import discord
from redbot.core import commands, checks, i18n, modlog from redbot.core import commands, checks, i18n, modlog
from redbot.core.utils import AsyncIter
from redbot.core.utils.chat_formatting import format_perms_list from redbot.core.utils.chat_formatting import format_perms_list
from redbot.core.utils.mod import get_audit_reason, is_allowed_by_hierarchy from redbot.core.utils.mod import get_audit_reason, is_allowed_by_hierarchy
from .abc import MixinMeta from .abc import MixinMeta
@@ -207,6 +208,17 @@ class MuteMixin(MixinMeta):
await ctx.send( await ctx.send(
_("Muted {user} in channel {channel.name}").format(user=user, channel=channel) _("Muted {user} in channel {channel.name}").format(user=user, channel=channel)
) )
try:
if channel.permissions_for(ctx.me).move_members:
await user.move_to(channel)
else:
raise RuntimeError
except (discord.Forbidden, RuntimeError):
await ctx.send(
_(
"Because I don't have the Move Members permission, this will take into effect when the user rejoins."
)
)
else: else:
await ctx.send(issue) await ctx.send(issue)
@@ -255,11 +267,10 @@ class MuteMixin(MixinMeta):
audit_reason = get_audit_reason(author, reason) audit_reason = get_audit_reason(author, reason)
mute_success = [] mute_success = []
async with ctx.typing():
for channel in guild.channels: for channel in guild.channels:
success, issue = await self.mute_user(guild, channel, author, user, audit_reason) success, issue = await self.mute_user(guild, channel, author, user, audit_reason)
mute_success.append((success, issue)) mute_success.append((success, issue))
await asyncio.sleep(0.1)
try:
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
@@ -271,8 +282,6 @@ class MuteMixin(MixinMeta):
until=None, until=None,
channel=None, channel=None,
) )
except RuntimeError as e:
await ctx.send(e)
await ctx.send(_("User has been muted in this server.")) await ctx.send(_("User has been muted in this server."))
@commands.group() @commands.group()
@@ -322,6 +331,17 @@ class MuteMixin(MixinMeta):
await ctx.send( await ctx.send(
_("Unmuted {user} in channel {channel.name}").format(user=user, channel=channel) _("Unmuted {user} in channel {channel.name}").format(user=user, channel=channel)
) )
try:
if channel.permissions_for(ctx.me).move_members:
await user.move_to(channel)
else:
raise RuntimeError
except (discord.Forbidden, RuntimeError):
await ctx.send(
_(
"Because I don't have the Move Members permission, this will take into effect when the user rejoins."
)
)
else: else:
await ctx.send(_("Unmute failed. Reason: {}").format(message)) await ctx.send(_("Unmute failed. Reason: {}").format(message))
@@ -372,11 +392,12 @@ class MuteMixin(MixinMeta):
audit_reason = get_audit_reason(author, reason) audit_reason = get_audit_reason(author, reason)
unmute_success = [] unmute_success = []
async with ctx.typing():
for channel in guild.channels: for channel in guild.channels:
success, message = await self.unmute_user(guild, channel, author, user, audit_reason) success, message = await self.unmute_user(
guild, channel, author, user, audit_reason
)
unmute_success.append((success, message)) unmute_success.append((success, message))
await asyncio.sleep(0.1)
try:
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
@@ -387,8 +408,6 @@ class MuteMixin(MixinMeta):
reason, reason,
until=None, until=None,
) )
except RuntimeError as e:
await ctx.send(e)
await ctx.send(_("User has been unmuted in this server.")) await ctx.send(_("User has been unmuted in this server."))
async def mute_user( async def mute_user(

View File

@@ -282,6 +282,7 @@ class Permissions(commands.Cog):
except discord.Forbidden: except discord.Forbidden:
await ctx.send(_("I'm not allowed to DM you.")) await ctx.send(_("I'm not allowed to DM you."))
else: else:
if not isinstance(ctx.channel, discord.DMChannel):
await ctx.send(_("I've just sent the file to you via DM.")) await ctx.send(_("I've just sent the file to you via DM."))
finally: finally:
file.close() file.close()
@@ -599,8 +600,8 @@ class Permissions(commands.Cog):
cog_or_cmd.obj.clear_rule_for(model_id, guild_id=guild_id) cog_or_cmd.obj.clear_rule_for(model_id, guild_id=guild_id)
guild_id, model_id = str(guild_id), str(model_id) guild_id, model_id = str(guild_id), str(model_id)
async with self.config.custom(cog_or_cmd.type, cog_or_cmd.name).all() as rules: async with self.config.custom(cog_or_cmd.type, cog_or_cmd.name).all() as rules:
if guild_id in rules and rules[guild_id]: if (guild_rules := rules.get(guild_id)) is not None:
del rules[guild_id][model_id] guild_rules.pop(model_id, None)
async def _set_default_rule( async def _set_default_rule(
self, rule: Optional[bool], cog_or_cmd: CogOrCommand, guild_id: int self, rule: Optional[bool], cog_or_cmd: CogOrCommand, guild_id: int

View File

@@ -271,6 +271,13 @@ class Reports(commands.Cog):
with contextlib.suppress(discord.Forbidden, discord.HTTPException): with contextlib.suppress(discord.Forbidden, discord.HTTPException):
if val is None: if val is None:
if await self.config.guild(ctx.guild).output_channel() is None:
await author.send(
_(
"This server has no reports channel set up. Please contact a server admin."
)
)
else:
await author.send( await author.send(
_("There was an error sending your report, please contact a server admin.") _("There was an error sending your report, please contact a server admin.")
) )

View File

@@ -505,14 +505,13 @@ class Streams(commands.Cog):
@message.command(name="mention") @message.command(name="mention")
@commands.guild_only() @commands.guild_only()
async def with_mention(self, ctx: commands.Context, message: str = None): async def with_mention(self, ctx: commands.Context, *, message: str = None):
"""Set stream alert message when mentions are enabled. """Set stream alert message when mentions are enabled.
Use `{mention}` in the message to insert the selected mentions. Use `{mention}` in the message to insert the selected mentions.
Use `{stream}` in the message to insert the channel or user name.
Use `{stream.name}` in the message to insert the channel or user name. For example: `[p]streamset message mention {mention}, {stream} is live!`
For example: `[p]streamset message mention "{mention}, {stream.name} is live!"`
""" """
if message is not None: if message is not None:
guild = ctx.guild guild = ctx.guild
@@ -523,12 +522,12 @@ class Streams(commands.Cog):
@message.command(name="nomention") @message.command(name="nomention")
@commands.guild_only() @commands.guild_only()
async def without_mention(self, ctx: commands.Context, message: str = None): async def without_mention(self, ctx: commands.Context, *, message: str = None):
"""Set stream alert message when mentions are disabled. """Set stream alert message when mentions are disabled.
Use `{stream.name}` in the message to insert the channel or user name. Use `{stream}` in the message to insert the channel or user name.
For example: `[p]streamset message nomention "{stream.name} is live!"` For example: `[p]streamset message nomention {stream} is live!`
""" """
if message is not None: if message is not None:
guild = ctx.guild guild = ctx.guild
@@ -724,7 +723,12 @@ class Streams(commands.Cog):
channel.guild channel.guild
).live_message_mention() ).live_message_mention()
if alert_msg: if alert_msg:
content = alert_msg.format(mention=mention_str, stream=stream) content = alert_msg # Stop bad things from happening here...
content = content.replace(
"{stream.name}", str(stream.name)
) # Backwards compatability
content = content.replace("{stream}", str(stream.name))
content = content.replace("{mention}", mention_str)
else: else:
content = _("{mention}, {stream} is live!").format( content = _("{mention}, {stream} is live!").format(
mention=mention_str, mention=mention_str,
@@ -737,7 +741,11 @@ class Streams(commands.Cog):
channel.guild channel.guild
).live_message_nomention() ).live_message_nomention()
if alert_msg: if alert_msg:
content = alert_msg.format(stream=stream) content = alert_msg # Stop bad things from happening here...
content = content.replace(
"{stream.name}", str(stream.name)
) # Backwards compatability
content = content.replace("{stream}", str(stream.name))
else: else:
content = _("{stream} is live!").format( content = _("{stream} is live!").format(
stream=escape( stream=escape(

View File

@@ -190,6 +190,12 @@ class YoutubeStream(Stream):
raise StreamNotFound() raise StreamNotFound()
elif "items" in data: elif "items" in data:
return data["items"][0][resource] return data["items"][0][resource]
elif (
"pageInfo" in data
and "totalResults" in data["pageInfo"]
and data["pageInfo"]["totalResults"] < 1
):
raise StreamNotFound()
raise APIError() raise APIError()
def __repr__(self): def __repr__(self):

View File

@@ -1,303 +1,303 @@
AUTHOR: aikaterna AUTHOR: aikaterna
Who's that pokemon? https://cdn.discord.red/i/JhaHtAM.png: Who's that Pokémon? https://cdn.discord.red/i/JhaHtAM.png:
- Bulbasaur - Bulbasaur
Who's that pokemon? https://cdn.discord.red/i/J42krSc.png: Who's that Pokémon? https://cdn.discord.red/i/J42krSc.png:
- Ivysaur - Ivysaur
Who's that pokemon? https://cdn.discord.red/i/Y6aQTvu.png: Who's that Pokémon? https://cdn.discord.red/i/Y6aQTvu.png:
- Venusaur - Venusaur
Who's that pokemon? https://cdn.discord.red/i/GzGY0m5.png: Who's that Pokémon? https://cdn.discord.red/i/GzGY0m5.png:
- Charmander - Charmander
Who's that pokemon? https://cdn.discord.red/i/GPqbMp9.png: Who's that Pokémon? https://cdn.discord.red/i/GPqbMp9.png:
- Charmeleon - Charmeleon
Who's that pokemon? https://cdn.discord.red/i/Dpx11Hv.png: Who's that Pokémon? https://cdn.discord.red/i/Dpx11Hv.png:
- Charizard - Charizard
Who's that pokemon? https://cdn.discord.red/i/hWxoVFP.png: Who's that Pokémon? https://cdn.discord.red/i/hWxoVFP.png:
- Squirtle - Squirtle
Who's that pokemon? https://cdn.discord.red/i/FAXNExP.png: Who's that Pokémon? https://cdn.discord.red/i/FAXNExP.png:
- Wartortle - Wartortle
Who's that pokemon? https://cdn.discord.red/i/b8dIcMs.png: Who's that Pokémon? https://cdn.discord.red/i/b8dIcMs.png:
- Blastoise - Blastoise
Who's that pokemon? https://cdn.discord.red/i/5THSh9F.png: Who's that Pokémon? https://cdn.discord.red/i/5THSh9F.png:
- Caterpie - Caterpie
Who's that pokemon? https://cdn.discord.red/i/g7J4azT.png: Who's that Pokémon? https://cdn.discord.red/i/g7J4azT.png:
- Metapod - Metapod
Who's that pokemon? https://cdn.discord.red/i/Gln47Ns.png: Who's that Pokémon? https://cdn.discord.red/i/Gln47Ns.png:
- Butterfree - Butterfree
Who's that pokemon? https://cdn.discord.red/i/Nurfd3B.png: Who's that Pokémon? https://cdn.discord.red/i/Nurfd3B.png:
- Weedle - Weedle
Who's that pokemon? https://cdn.discord.red/i/RbTimG9.png: Who's that Pokémon? https://cdn.discord.red/i/RbTimG9.png:
- Kakuna - Kakuna
Who's that pokemon? https://cdn.discord.red/i/4hqx40k.png: Who's that Pokémon? https://cdn.discord.red/i/4hqx40k.png:
- Beedrill - Beedrill
Who's that pokemon? https://cdn.discord.red/i/SH9ogxU.png: Who's that Pokémon? https://cdn.discord.red/i/SH9ogxU.png:
- Pidgey - Pidgey
Who's that pokemon? https://cdn.discord.red/i/ciZdrwt.png: Who's that Pokémon? https://cdn.discord.red/i/ciZdrwt.png:
- Pidgeotto - Pidgeotto
Who's that pokemon? https://cdn.discord.red/i/8pTnBh7.png: Who's that Pokémon? https://cdn.discord.red/i/8pTnBh7.png:
- Pidgeot - Pidgeot
Who's that pokemon? https://cdn.discord.red/i/Q9dISQq.png: Who's that Pokémon? https://cdn.discord.red/i/Q9dISQq.png:
- Rattata - Rattata
Who's that pokemon? https://cdn.discord.red/i/qu5PdyR.png: Who's that Pokémon? https://cdn.discord.red/i/qu5PdyR.png:
- Raticate - Raticate
Who's that pokemon? https://cdn.discord.red/i/FcU64Fe.png: Who's that Pokémon? https://cdn.discord.red/i/FcU64Fe.png:
- Spearow - Spearow
Who's that pokemon? https://cdn.discord.red/i/3RPuh1B.png: Who's that Pokémon? https://cdn.discord.red/i/3RPuh1B.png:
- Fearow - Fearow
Who's that pokemon? https://cdn.discord.red/i/waJYATU.png: Who's that Pokémon? https://cdn.discord.red/i/waJYATU.png:
- Ekans - Ekans
Who's that pokemon? https://cdn.discord.red/i/xpfF1DV.png: Who's that Pokémon? https://cdn.discord.red/i/xpfF1DV.png:
- Arbok - Arbok
Who's that pokemon? https://cdn.discord.red/i/A6KEQ75.png: Who's that Pokémon? https://cdn.discord.red/i/A6KEQ75.png:
- Pikachu - Pikachu
Who's that pokemon? https://cdn.discord.red/i/TqzNM9a.png: Who's that Pokémon? https://cdn.discord.red/i/TqzNM9a.png:
- Raichu - Raichu
Who's that pokemon? https://cdn.discord.red/i/8XJouBF.png: Who's that Pokémon? https://cdn.discord.red/i/8XJouBF.png:
- Sandshrew - Sandshrew
Who's that pokemon? https://cdn.discord.red/i/x0bbRtg.png: Who's that Pokémon? https://cdn.discord.red/i/x0bbRtg.png:
- Sandslash - Sandslash
Who's that pokemon? https://cdn.discord.red/i/jkcrKwF.png: Who's that Pokémon? https://cdn.discord.red/i/jkcrKwF.png:
- Nidoran - Nidoran
Who's that pokemon? https://cdn.discord.red/i/3EmECpN.png: Who's that Pokémon? https://cdn.discord.red/i/3EmECpN.png:
- Nidorina - Nidorina
Who's that pokemon? https://cdn.discord.red/i/r8X77i2.png: Who's that Pokémon? https://cdn.discord.red/i/r8X77i2.png:
- Nidoqueen - Nidoqueen
Who's that pokemon? https://cdn.discord.red/i/5Ny72io.png: Who's that Pokémon? https://cdn.discord.red/i/5Ny72io.png:
- Nidoran - Nidoran
Who's that pokemon? https://cdn.discord.red/i/ZhsSCE4.png: Who's that Pokémon? https://cdn.discord.red/i/ZhsSCE4.png:
- Nidorino - Nidorino
Who's that pokemon? https://cdn.discord.red/i/YVjcgD9.png: Who's that Pokémon? https://cdn.discord.red/i/YVjcgD9.png:
- Nidoking - Nidoking
Who's that pokemon? https://cdn.discord.red/i/C0jvtgd.png: Who's that Pokémon? https://cdn.discord.red/i/C0jvtgd.png:
- Clefairy - Clefairy
Who's that pokemon? https://cdn.discord.red/i/JAvpC50.png: Who's that Pokémon? https://cdn.discord.red/i/JAvpC50.png:
- Clefable - Clefable
Who's that pokemon? https://cdn.discord.red/i/QcxFvi7.png: Who's that Pokémon? https://cdn.discord.red/i/QcxFvi7.png:
- Vulpix - Vulpix
Who's that pokemon? https://cdn.discord.red/i/Q3SRhH3.png: Who's that Pokémon? https://cdn.discord.red/i/Q3SRhH3.png:
- Ninetales - Ninetales
Who's that pokemon? https://cdn.discord.red/i/hKwx2UP.png: Who's that Pokémon? https://cdn.discord.red/i/hKwx2UP.png:
- Jigglypuff - Jigglypuff
Who's that pokemon? https://cdn.discord.red/i/HI6GLIM.png: Who's that Pokémon? https://cdn.discord.red/i/HI6GLIM.png:
- Wigglytuff - Wigglytuff
Who's that pokemon? https://cdn.discord.red/i/ygyr0wR.png: Who's that Pokémon? https://cdn.discord.red/i/ygyr0wR.png:
- Zubat - Zubat
Who's that pokemon? https://cdn.discord.red/i/7amMKJd.png: Who's that Pokémon? https://cdn.discord.red/i/7amMKJd.png:
- Golbat - Golbat
Who's that pokemon? https://cdn.discord.red/i/NMd1UkL.png: Who's that Pokémon? https://cdn.discord.red/i/NMd1UkL.png:
- Oddish - Oddish
Who's that pokemon? https://cdn.discord.red/i/0Pm1MZ6.png: Who's that Pokémon? https://cdn.discord.red/i/0Pm1MZ6.png:
- Gloom - Gloom
Who's that pokemon? https://cdn.discord.red/i/WGjuCBJ.png: Who's that Pokémon? https://cdn.discord.red/i/WGjuCBJ.png:
- Vileplume - Vileplume
Who's that pokemon? https://cdn.discord.red/i/fHkDxEI.png: Who's that Pokémon? https://cdn.discord.red/i/fHkDxEI.png:
- Paras - Paras
Who's that pokemon? https://cdn.discord.red/i/JgC6p0O.png: Who's that Pokémon? https://cdn.discord.red/i/JgC6p0O.png:
- Parasect - Parasect
Who's that pokemon? https://cdn.discord.red/i/xgElemy.png: Who's that Pokémon? https://cdn.discord.red/i/xgElemy.png:
- Venonat - Venonat
Who's that pokemon? https://cdn.discord.red/i/n69EDYY.png: Who's that Pokémon? https://cdn.discord.red/i/n69EDYY.png:
- Venomoth - Venomoth
Who's that pokemon? https://cdn.discord.red/i/gncAD9E.png: Who's that Pokémon? https://cdn.discord.red/i/gncAD9E.png:
- Diglett - Diglett
Who's that pokemon? https://cdn.discord.red/i/XgzUqSP.png: Who's that Pokémon? https://cdn.discord.red/i/XgzUqSP.png:
- Dugtrio - Dugtrio
Who's that pokemon? https://cdn.discord.red/i/abjRwp0.png: Who's that Pokémon? https://cdn.discord.red/i/abjRwp0.png:
- Meowth - Meowth
Who's that pokemon? https://cdn.discord.red/i/nnDIR3w.png: Who's that Pokémon? https://cdn.discord.red/i/nnDIR3w.png:
- Persian - Persian
Who's that pokemon? https://cdn.discord.red/i/fAXPqen.png: Who's that Pokémon? https://cdn.discord.red/i/fAXPqen.png:
- Psyduck - Psyduck
Who's that pokemon? https://cdn.discord.red/i/rq8Q3HN.png: Who's that Pokémon? https://cdn.discord.red/i/rq8Q3HN.png:
- Golduck - Golduck
Who's that pokemon? https://cdn.discord.red/i/vMjgxYs.png: Who's that Pokémon? https://cdn.discord.red/i/vMjgxYs.png:
- Mankey - Mankey
Who's that pokemon? https://cdn.discord.red/i/rLnDp0u.png: Who's that Pokémon? https://cdn.discord.red/i/rLnDp0u.png:
- Primeape - Primeape
Who's that pokemon? https://cdn.discord.red/i/IeyfkCU.png: Who's that Pokémon? https://cdn.discord.red/i/IeyfkCU.png:
- Growlithe - Growlithe
Who's that pokemon? https://cdn.discord.red/i/35xsszF.png: Who's that Pokémon? https://cdn.discord.red/i/35xsszF.png:
- Arcanine - Arcanine
Who's that pokemon? https://cdn.discord.red/i/M5KDU86.png: Who's that Pokémon? https://cdn.discord.red/i/M5KDU86.png:
- Poliwag - Poliwag
Who's that pokemon? https://cdn.discord.red/i/K033cK1.png: Who's that Pokémon? https://cdn.discord.red/i/K033cK1.png:
- Poliwhirl - Poliwhirl
Who's that pokemon? https://cdn.discord.red/i/o5Hv7AK.png: Who's that Pokémon? https://cdn.discord.red/i/o5Hv7AK.png:
- Poliwrath - Poliwrath
Who's that pokemon? https://cdn.discord.red/i/AhNIqr8.png: Who's that Pokémon? https://cdn.discord.red/i/AhNIqr8.png:
- Abra - Abra
Who's that pokemon? https://cdn.discord.red/i/2L2aPtx.png: Who's that Pokémon? https://cdn.discord.red/i/2L2aPtx.png:
- Kadabra - Kadabra
Who's that pokemon? https://cdn.discord.red/i/kGuFJDY.png: Who's that Pokémon? https://cdn.discord.red/i/kGuFJDY.png:
- Alakazam - Alakazam
Who's that pokemon? https://cdn.discord.red/i/YQUTREX.png: Who's that Pokémon? https://cdn.discord.red/i/YQUTREX.png:
- Machop - Machop
Who's that pokemon? https://cdn.discord.red/i/YL7C8i8.png: Who's that Pokémon? https://cdn.discord.red/i/YL7C8i8.png:
- Machoke - Machoke
Who's that pokemon? https://cdn.discord.red/i/1355Vod.png: Who's that Pokémon? https://cdn.discord.red/i/1355Vod.png:
- Machamp - Machamp
Who's that pokemon? https://cdn.discord.red/i/VxA12Ot.png: Who's that Pokémon? https://cdn.discord.red/i/VxA12Ot.png:
- Bellsprout - Bellsprout
Who's that pokemon? https://cdn.discord.red/i/D6mgIJJ.png: Who's that Pokémon? https://cdn.discord.red/i/D6mgIJJ.png:
- Weepinbell - Weepinbell
Who's that pokemon? https://cdn.discord.red/i/NMSvlCz.png: Who's that Pokémon? https://cdn.discord.red/i/NMSvlCz.png:
- Victreebel - Victreebel
Who's that pokemon? https://cdn.discord.red/i/IoJmVzO.png: Who's that Pokémon? https://cdn.discord.red/i/IoJmVzO.png:
- Tentacool - Tentacool
Who's that pokemon? https://cdn.discord.red/i/0O9TNYv.png: Who's that Pokémon? https://cdn.discord.red/i/0O9TNYv.png:
- Tentacruel - Tentacruel
Who's that pokemon? https://cdn.discord.red/i/tFK8d3z.png: Who's that Pokémon? https://cdn.discord.red/i/tFK8d3z.png:
- Geodude - Geodude
Who's that pokemon? https://cdn.discord.red/i/lgew3Ca.png: Who's that Pokémon? https://cdn.discord.red/i/lgew3Ca.png:
- Graveler - Graveler
Who's that pokemon? https://cdn.discord.red/i/mG8wAHs.png: Who's that Pokémon? https://cdn.discord.red/i/mG8wAHs.png:
- Golem - Golem
Who's that pokemon? https://cdn.discord.red/i/N6un8iO.png: Who's that Pokémon? https://cdn.discord.red/i/N6un8iO.png:
- Ponyta - Ponyta
Who's that pokemon? https://cdn.discord.red/i/T7ADHJo.png: Who's that Pokémon? https://cdn.discord.red/i/T7ADHJo.png:
- Rapidash - Rapidash
Who's that pokemon? https://cdn.discord.red/i/6RBzLij.png: Who's that Pokémon? https://cdn.discord.red/i/6RBzLij.png:
- Slowpoke - Slowpoke
Who's that pokemon? https://cdn.discord.red/i/7ju6OBY.png: Who's that Pokémon? https://cdn.discord.red/i/7ju6OBY.png:
- Slowbro - Slowbro
Who's that pokemon? https://cdn.discord.red/i/KnhQMxQ.png: Who's that Pokémon? https://cdn.discord.red/i/KnhQMxQ.png:
- Magnemite - Magnemite
Who's that pokemon? https://cdn.discord.red/i/bJDh47R.png: Who's that Pokémon? https://cdn.discord.red/i/bJDh47R.png:
- Magneton - Magneton
Who's that pokemon? https://cdn.discord.red/i/WMJFI92.png: Who's that Pokémon? https://cdn.discord.red/i/WMJFI92.png:
- Farfetch'd - Farfetch'd
Who's that pokemon? https://cdn.discord.red/i/pw6UYNR.png: Who's that Pokémon? https://cdn.discord.red/i/pw6UYNR.png:
- Doduo - Doduo
Who's that pokemon? https://cdn.discord.red/i/WE8YHaR.png: Who's that Pokémon? https://cdn.discord.red/i/WE8YHaR.png:
- Dodrio - Dodrio
Who's that pokemon? https://cdn.discord.red/i/vZlSSqS.png: Who's that Pokémon? https://cdn.discord.red/i/vZlSSqS.png:
- Seel - Seel
Who's that pokemon? https://cdn.discord.red/i/3erUdvZ.png: Who's that Pokémon? https://cdn.discord.red/i/3erUdvZ.png:
- Dewgong - Dewgong
Who's that pokemon? https://cdn.discord.red/i/FkecTd4.png: Who's that Pokémon? https://cdn.discord.red/i/FkecTd4.png:
- Grimer - Grimer
Who's that pokemon? https://cdn.discord.red/i/nHl2BrL.png: Who's that Pokémon? https://cdn.discord.red/i/nHl2BrL.png:
- Muk - Muk
Who's that pokemon? https://cdn.discord.red/i/o9xX4uR.png: Who's that Pokémon? https://cdn.discord.red/i/o9xX4uR.png:
- Shellder - Shellder
Who's that pokemon? https://cdn.discord.red/i/oC0mdzt.png: Who's that Pokémon? https://cdn.discord.red/i/oC0mdzt.png:
- Cloyster - Cloyster
Who's that pokemon? https://cdn.discord.red/i/xhPTiG2.png: Who's that Pokémon? https://cdn.discord.red/i/xhPTiG2.png:
- Gastly - Gastly
Who's that pokemon? https://cdn.discord.red/i/eLwmupW.png: Who's that Pokémon? https://cdn.discord.red/i/eLwmupW.png:
- Haunter - Haunter
Who's that pokemon? https://cdn.discord.red/i/SpBQNMG.png: Who's that Pokémon? https://cdn.discord.red/i/SpBQNMG.png:
- Gengar - Gengar
Who's that pokemon? https://cdn.discord.red/i/oP8J2tJ.png: Who's that Pokémon? https://cdn.discord.red/i/oP8J2tJ.png:
- Onix - Onix
Who's that pokemon? https://cdn.discord.red/i/oJ3ZHfP.png: Who's that Pokémon? https://cdn.discord.red/i/oJ3ZHfP.png:
- Drowzee - Drowzee
Who's that pokemon? https://cdn.discord.red/i/eutXYlt.png: Who's that Pokémon? https://cdn.discord.red/i/eutXYlt.png:
- Hypno - Hypno
Who's that pokemon? https://cdn.discord.red/i/nd7n8su.png: Who's that Pokémon? https://cdn.discord.red/i/nd7n8su.png:
- Krabby - Krabby
Who's that pokemon? https://cdn.discord.red/i/GE9Ddfg.png: Who's that Pokémon? https://cdn.discord.red/i/GE9Ddfg.png:
- Kingler - Kingler
Who's that pokemon? https://cdn.discord.red/i/ZTgtF9V.png: Who's that Pokémon? https://cdn.discord.red/i/ZTgtF9V.png:
- Voltorb - Voltorb
Who's that pokemon? https://cdn.discord.red/i/pQASpgA.png: Who's that Pokémon? https://cdn.discord.red/i/pQASpgA.png:
- Electrode - Electrode
Who's that pokemon? https://cdn.discord.red/i/Fk9QAG4.png: Who's that Pokémon? https://cdn.discord.red/i/Fk9QAG4.png:
- Exeggcute - Exeggcute
Who's that pokemon? https://cdn.discord.red/i/d9VcfpV.png: Who's that Pokémon? https://cdn.discord.red/i/d9VcfpV.png:
- Exeggutor - Exeggutor
Who's that pokemon? https://cdn.discord.red/i/GZMPoPQ.png: Who's that Pokémon? https://cdn.discord.red/i/GZMPoPQ.png:
- Cubone - Cubone
Who's that pokemon? https://cdn.discord.red/i/v2pv554.png: Who's that Pokémon? https://cdn.discord.red/i/v2pv554.png:
- Marowak - Marowak
Who's that pokemon? https://cdn.discord.red/i/cg8BnLd.png: Who's that Pokémon? https://cdn.discord.red/i/cg8BnLd.png:
- Hitmonlee - Hitmonlee
Who's that pokemon? https://cdn.discord.red/i/26iLMlQ.png: Who's that Pokémon? https://cdn.discord.red/i/26iLMlQ.png:
- Hitmonchan - Hitmonchan
Who's that pokemon? https://cdn.discord.red/i/GRxldku.png: Who's that Pokémon? https://cdn.discord.red/i/GRxldku.png:
- Lickitung - Lickitung
Who's that pokemon? https://cdn.discord.red/i/y5dbZrK.png: Who's that Pokémon? https://cdn.discord.red/i/y5dbZrK.png:
- Koffing - Koffing
Who's that pokemon? https://cdn.discord.red/i/LFWBBMh.png: Who's that Pokémon? https://cdn.discord.red/i/LFWBBMh.png:
- Weezing - Weezing
Who's that pokemon? https://cdn.discord.red/i/JlDE7HF.png: Who's that Pokémon? https://cdn.discord.red/i/JlDE7HF.png:
- Rhyhorn - Rhyhorn
Who's that pokemon? https://cdn.discord.red/i/CtdI8PO.png: Who's that Pokémon? https://cdn.discord.red/i/CtdI8PO.png:
- Rhydon - Rhydon
Who's that pokemon? https://cdn.discord.red/i/caw6rxF.png: Who's that Pokémon? https://cdn.discord.red/i/caw6rxF.png:
- Chansey - Chansey
Who's that pokemon? https://cdn.discord.red/i/L2MkNsi.png: Who's that Pokémon? https://cdn.discord.red/i/L2MkNsi.png:
- Tangela - Tangela
Who's that pokemon? https://cdn.discord.red/i/QrbUc6x.png: Who's that Pokémon? https://cdn.discord.red/i/QrbUc6x.png:
- Kangaskhan - Kangaskhan
Who's that pokemon? https://cdn.discord.red/i/rHITC4u.png: Who's that Pokémon? https://cdn.discord.red/i/rHITC4u.png:
- Horsea - Horsea
Who's that pokemon? https://cdn.discord.red/i/gmw99bY.png: Who's that Pokémon? https://cdn.discord.red/i/gmw99bY.png:
- Seadra - Seadra
Who's that pokemon? https://cdn.discord.red/i/nlXApwH.png: Who's that Pokémon? https://cdn.discord.red/i/nlXApwH.png:
- Goldeen - Goldeen
Who's that pokemon? https://cdn.discord.red/i/IOxPciL.png: Who's that Pokémon? https://cdn.discord.red/i/IOxPciL.png:
- Seaking - Seaking
Who's that pokemon? https://cdn.discord.red/i/rEJK5dk.png: Who's that Pokémon? https://cdn.discord.red/i/rEJK5dk.png:
- Staryu - Staryu
Who's that pokemon? https://cdn.discord.red/i/nKMh7m4.png: Who's that Pokémon? https://cdn.discord.red/i/nKMh7m4.png:
- Starmie - Starmie
Who's that pokemon? https://cdn.discord.red/i/zZYsvYZ.png: Who's that Pokémon? https://cdn.discord.red/i/zZYsvYZ.png:
- Mr. Mime - Mr. Mime
Who's that pokemon? https://cdn.discord.red/i/987JigM.png: Who's that Pokémon? https://cdn.discord.red/i/987JigM.png:
- Scyther - Scyther
Who's that pokemon? https://cdn.discord.red/i/71vLjQC.png: Who's that Pokémon? https://cdn.discord.red/i/71vLjQC.png:
- Jynx - Jynx
Who's that pokemon? https://cdn.discord.red/i/gyVHrsJ.png: Who's that Pokémon? https://cdn.discord.red/i/gyVHrsJ.png:
- Electabuzz - Electabuzz
Who's that pokemon? https://cdn.discord.red/i/1WTGued.png: Who's that Pokémon? https://cdn.discord.red/i/1WTGued.png:
- Magmar - Magmar
Who's that pokemon? https://cdn.discord.red/i/V8y1cHT.png: Who's that Pokémon? https://cdn.discord.red/i/V8y1cHT.png:
- Pinsir - Pinsir
Who's that pokemon? https://cdn.discord.red/i/OG6geA9.png: Who's that Pokémon? https://cdn.discord.red/i/OG6geA9.png:
- Tauros - Tauros
Who's that pokemon? https://cdn.discord.red/i/LsfwWH7.png: Who's that Pokémon? https://cdn.discord.red/i/LsfwWH7.png:
- Magikarp - Magikarp
Who's that pokemon? https://cdn.discord.red/i/3FzgA70.png: Who's that Pokémon? https://cdn.discord.red/i/3FzgA70.png:
- Gyarados - Gyarados
Who's that pokemon? https://cdn.discord.red/i/Dz7EXjw.png: Who's that Pokémon? https://cdn.discord.red/i/Dz7EXjw.png:
- Lapras - Lapras
Who's that pokemon? https://cdn.discord.red/i/LYL4rX1.png: Who's that Pokémon? https://cdn.discord.red/i/LYL4rX1.png:
- Ditto - Ditto
Who's that pokemon? https://cdn.discord.red/i/c9KOJH1.png: Who's that Pokémon? https://cdn.discord.red/i/c9KOJH1.png:
- Eevee - Eevee
Who's that pokemon? https://cdn.discord.red/i/F8uPBoY.png: Who's that Pokémon? https://cdn.discord.red/i/F8uPBoY.png:
- Vaporeon - Vaporeon
Who's that pokemon? https://cdn.discord.red/i/ChlK8yg.png: Who's that Pokémon? https://cdn.discord.red/i/ChlK8yg.png:
- Jolteon - Jolteon
Who's that pokemon? https://cdn.discord.red/i/EjAuEZM.png: Who's that Pokémon? https://cdn.discord.red/i/EjAuEZM.png:
- Flareon - Flareon
Who's that pokemon? https://cdn.discord.red/i/lCWdbLS.png: Who's that Pokémon? https://cdn.discord.red/i/lCWdbLS.png:
- Porygon - Porygon
Who's that pokemon? https://cdn.discord.red/i/ajEgvvx.png: Who's that Pokémon? https://cdn.discord.red/i/ajEgvvx.png:
- Omanyte - Omanyte
Who's that pokemon? https://cdn.discord.red/i/B7wu0zJ.png: Who's that Pokémon? https://cdn.discord.red/i/B7wu0zJ.png:
- Omastar - Omastar
Who's that pokemon? https://cdn.discord.red/i/7fO1Js6.png: Who's that Pokémon? https://cdn.discord.red/i/7fO1Js6.png:
- Kabuto - Kabuto
Who's that pokemon? https://cdn.discord.red/i/g0h7oas.png: Who's that Pokémon? https://cdn.discord.red/i/g0h7oas.png:
- Kabutops - Kabutops
Who's that pokemon? https://cdn.discord.red/i/XgGhtIu.png: Who's that Pokémon? https://cdn.discord.red/i/XgGhtIu.png:
- Aerodactyl - Aerodactyl
Who's that pokemon? https://cdn.discord.red/i/q1vGvnR.png: Who's that Pokémon? https://cdn.discord.red/i/q1vGvnR.png:
- Snorlax - Snorlax
Who's that pokemon? https://cdn.discord.red/i/7Lb8533.png: Who's that Pokémon? https://cdn.discord.red/i/7Lb8533.png:
- Articuno - Articuno
Who's that pokemon? https://cdn.discord.red/i/Dk1StF5.png: Who's that Pokémon? https://cdn.discord.red/i/Dk1StF5.png:
- Zapdos - Zapdos
Who's that pokemon? https://cdn.discord.red/i/rrcCaAR.png: Who's that Pokémon? https://cdn.discord.red/i/rrcCaAR.png:
- Moltres - Moltres
Who's that pokemon? https://cdn.discord.red/i/JU0mWkj.png: Who's that Pokémon? https://cdn.discord.red/i/JU0mWkj.png:
- Dratini - Dratini
Who's that pokemon? https://cdn.discord.red/i/SsGWg1B.png: Who's that Pokémon? https://cdn.discord.red/i/SsGWg1B.png:
- Dragonair - Dragonair
Who's that pokemon? https://cdn.discord.red/i/snr8HO7.png: Who's that Pokémon? https://cdn.discord.red/i/snr8HO7.png:
- Dragonite - Dragonite
Who's that pokemon? https://cdn.discord.red/i/RW89rJR.png: Who's that Pokémon? https://cdn.discord.red/i/RW89rJR.png:
- Mewtwo - Mewtwo
Who's that pokemon? https://cdn.discord.red/i/dezUCAt.png: Who's that Pokémon? https://cdn.discord.red/i/dezUCAt.png:
- Mew - Mew

View File

@@ -0,0 +1,201 @@
AUTHOR: aikaterna, owo
Who's that Pokémon? https://cdn.discord.red/i/161fDs6.png:
- Chikorita
Who's that Pokémon? https://cdn.discord.red/i/9mBd4k5.png:
- Bayleef
Who's that Pokémon? https://cdn.discord.red/i/JK6EdFS.png:
- Meganium
Who's that Pokémon? https://cdn.discord.red/i/VANqEey.png:
- Cyndaquil
Who's that Pokémon? https://cdn.discord.red/i/wo3W5f8.png:
- Quilava
Who's that Pokémon? https://cdn.discord.red/i/la2mCq5.png:
- Typhlosion
Who's that Pokémon? https://cdn.discord.red/i/NOE3zVe.png:
- Totodile
Who's that Pokémon? https://cdn.discord.red/i/7mIaKvA.png:
- Croconaw
Who's that Pokémon? https://cdn.discord.red/i/pkRCy4p.png:
- Feraligatr
Who's that Pokémon? https://cdn.discord.red/i/R2RQJJD.png:
- Sentret
Who's that Pokémon? https://cdn.discord.red/i/zVeD6jY.png:
- Furret
Who's that Pokémon? https://cdn.discord.red/i/tu0igAj.png:
- Hoothoot
Who's that Pokémon? https://cdn.discord.red/i/qokx2sz.png:
- Noctowl
Who's that Pokémon? https://cdn.discord.red/i/LqBvwkd.png:
- Ledyba
Who's that Pokémon? https://cdn.discord.red/i/Ri77MSO.png:
- Ledian
Who's that Pokémon? https://cdn.discord.red/i/GvFKVrG.png:
- Spinarak
Who's that Pokémon? https://cdn.discord.red/i/MN7OIfR.png:
- Ariados
Who's that Pokémon? https://cdn.discord.red/i/e5hYFQF.png:
- Crobat
Who's that Pokémon? https://cdn.discord.red/i/YOyKw1M.png:
- Chinchou
Who's that Pokémon? https://cdn.discord.red/i/WZEHcW2.png:
- Lanturn
Who's that Pokémon? https://cdn.discord.red/i/1YqVBUU.png:
- Pichu
Who's that Pokémon? https://cdn.discord.red/i/BzyRRM6.png:
- Cleffa
Who's that Pokémon? https://cdn.discord.red/i/HLzHnoR.png:
- Igglybuff
Who's that Pokémon? https://cdn.discord.red/i/7eIACOx.png:
- Togepi
Who's that Pokémon? https://cdn.discord.red/i/Wihw9ed.png:
- Togetic
Who's that Pokémon? https://cdn.discord.red/i/sEcT1wG.png:
- Natu
Who's that Pokémon? https://cdn.discord.red/i/WP50IsH.png:
- Xatu
Who's that Pokémon? https://cdn.discord.red/i/aeU3eLQ.png:
- Mareep
Who's that Pokémon? https://cdn.discord.red/i/CMjVBM2.png:
- Flaaffy
Who's that Pokémon? https://cdn.discord.red/i/0rL21y3.png:
- Ampharos
Who's that Pokémon? https://cdn.discord.red/i/r83gWqG.png:
- Bellossom
Who's that Pokémon? https://cdn.discord.red/i/nDX9cFZ.png:
- Marill
Who's that Pokémon? https://cdn.discord.red/i/i8MzcBG.png:
- Azumarill
Who's that Pokémon? https://cdn.discord.red/i/9lvsmrK.png:
- Sudowoodo
Who's that Pokémon? https://cdn.discord.red/i/1MOC4py.png:
- Politoed
Who's that Pokémon? https://cdn.discord.red/i/MtHPKHY.png:
- Hoppip
Who's that Pokémon? https://cdn.discord.red/i/G3B7gou.png:
- Skiploom
Who's that Pokémon? https://cdn.discord.red/i/3PqW6d4.png:
- Jumpluff
Who's that Pokémon? https://cdn.discord.red/i/7E4uSrM.png:
- Aipom
Who's that Pokémon? https://cdn.discord.red/i/TQp3Nm6.png:
- Sunkern
Who's that Pokémon? https://cdn.discord.red/i/uz2MQ0c.png:
- Sunflora
Who's that Pokémon? https://cdn.discord.red/i/NiC8tpF.png:
- Yanma
Who's that Pokémon? https://cdn.discord.red/i/yvgjiCQ.png:
- Wooper
Who's that Pokémon? https://cdn.discord.red/i/7NSgVte.png:
- Quagsire
Who's that Pokémon? https://cdn.discord.red/i/d6xPUYS.png:
- Espeon
Who's that Pokémon? https://cdn.discord.red/i/1D2T81X.png:
- Umbreon
Who's that Pokémon? https://cdn.discord.red/i/mdOMOgS.png:
- Murkrow
Who's that Pokémon? https://cdn.discord.red/i/6ReUa0W.png:
- Slowking
Who's that Pokémon? https://cdn.discord.red/i/JA1k4GF.png:
- Misdreavus
Who's that Pokémon? https://cdn.discord.red/i/sLKauTP.png:
- Unown
Who's that Pokémon? https://cdn.discord.red/i/gtzSsQh.png:
- Wobbuffet
Who's that Pokémon? https://cdn.discord.red/i/Zz1hdae.png:
- Girafarig
Who's that Pokémon? https://cdn.discord.red/i/7v0ktVt.png:
- Pineco
Who's that Pokémon? https://cdn.discord.red/i/dGt2LtM.png:
- Forretress
Who's that Pokémon? https://cdn.discord.red/i/3d7i0nA.png:
- Dunsparce
Who's that Pokémon? https://cdn.discord.red/i/EZFfXGQ.png:
- Gligar
Who's that Pokémon? https://cdn.discord.red/i/FicMmX2.png:
- Steelix
Who's that Pokémon? https://cdn.discord.red/i/EWlbb3g.png:
- Snubbull
Who's that Pokémon? https://cdn.discord.red/i/Es7SFIu.png:
- Granbull
Who's that Pokémon? https://cdn.discord.red/i/qWvJSNs.png:
- Qwilfish
Who's that Pokémon? https://cdn.discord.red/i/zl9gJ8s.png:
- Scizor
Who's that Pokémon? https://cdn.discord.red/i/ZSkIWg1.png:
- Shuckle
Who's that Pokémon? https://cdn.discord.red/i/ivmNVBQ.png:
- Heracross
Who's that Pokémon? https://cdn.discord.red/i/cVNGBvn.png:
- Sneasel
Who's that Pokémon? https://cdn.discord.red/i/gwKceRk.png:
- Teddiursa
Who's that Pokémon? https://cdn.discord.red/i/mShUvcr.png:
- Ursaring
Who's that Pokémon? https://cdn.discord.red/i/QnOMvBY.png:
- Slugma
Who's that Pokémon? https://cdn.discord.red/i/rHUlbZ7.png:
- Magcargo
Who's that Pokémon? https://cdn.discord.red/i/SLVkdUf.png:
- Swinub
Who's that Pokémon? https://cdn.discord.red/i/rMgYSAy.png:
- Piloswine
Who's that Pokémon? https://cdn.discord.red/i/fYLQexl.png:
- Corsola
Who's that Pokémon? https://cdn.discord.red/i/LFKLE7w.png:
- Remoraid
Who's that Pokémon? https://cdn.discord.red/i/YxMYn4W.png:
- Octillery
Who's that Pokémon? https://cdn.discord.red/i/QFaIdNN.png:
- Delibird
Who's that Pokémon? https://cdn.discord.red/i/tRT4c6d.png:
- Mantine
Who's that Pokémon? https://cdn.discord.red/i/zaFbLo8.png:
- Skarmory
Who's that Pokémon? https://cdn.discord.red/i/sqAekho.png:
- Houndour
Who's that Pokémon? https://cdn.discord.red/i/mijoGZI.png:
- Houndoom
Who's that Pokémon? https://cdn.discord.red/i/XNCoyFy.png:
- Kingdra
Who's that Pokémon? https://cdn.discord.red/i/2yXKuEh.png:
- Phanpy
Who's that Pokémon? https://cdn.discord.red/i/eAodrXb.png:
- Donphan
Who's that Pokémon? https://cdn.discord.red/i/JJkzySs.png:
- Porygon2
Who's that Pokémon? https://cdn.discord.red/i/FOK8Yla.png:
- Stantler
Who's that Pokémon? https://cdn.discord.red/i/o2Hb9ck.png:
- Smeargle
Who's that Pokémon? https://cdn.discord.red/i/NjCcsZh.png:
- Tyrogue
Who's that Pokémon? https://cdn.discord.red/i/mpUCBUn.png:
- Hitmontop
Who's that Pokémon? https://cdn.discord.red/i/GPpT5pn.png:
- Smoochum
Who's that Pokémon? https://cdn.discord.red/i/ELqP5eZ.png:
- Elekid
Who's that Pokémon? https://cdn.discord.red/i/V7iMoJ4.png:
- Magby
Who's that Pokémon? https://cdn.discord.red/i/xXU0CvW.png:
- Miltank
Who's that Pokémon? https://cdn.discord.red/i/QUO0wOi.png:
- Blissey
Who's that Pokémon? https://cdn.discord.red/i/lC2VuSh.png:
- Raikou
Who's that Pokémon? https://cdn.discord.red/i/5eIF2yf.png:
- Entei
Who's that Pokémon? https://cdn.discord.red/i/wRv6jf7.png:
- Suicune
Who's that Pokémon? https://cdn.discord.red/i/tJJvIYK.png:
- Larvitar
Who's that Pokémon? https://cdn.discord.red/i/9GZXyuU.png:
- Pupitar
Who's that Pokémon? https://cdn.discord.red/i/A2ZWKBK.png:
- Tyranitar
Who's that Pokémon? https://cdn.discord.red/i/QQGb5yJ.png:
- Lugia
Who's that Pokémon? https://cdn.discord.red/i/d3x47sk.png:
- Ho-Oh
Who's that Pokémon? https://cdn.discord.red/i/B6oIkT4.png:
- Celebi

View File

@@ -4,7 +4,7 @@ import time
import random import random
from collections import Counter from collections import Counter
import discord import discord
from redbot.core import bank from redbot.core import bank, errors
from redbot.core.i18n import Translator from redbot.core.i18n import Translator
from redbot.core.utils.chat_formatting import box, bold, humanize_list, humanize_number from redbot.core.utils.chat_formatting import box, bold, humanize_list, humanize_number
from redbot.core.utils.common_filters import normalize_smartquotes from redbot.core.utils.common_filters import normalize_smartquotes
@@ -305,7 +305,7 @@ class TriviaSession:
LOG.debug("Paying trivia winner: %d credits --> %s", amount, str(winner)) LOG.debug("Paying trivia winner: %d credits --> %s", amount, str(winner))
try: try:
await bank.deposit_credits(winner, int(multiplier * score)) await bank.deposit_credits(winner, int(multiplier * score))
except bank.BalanceTooHigh as e: except errors.BalanceTooHigh as e:
await bank.set_balance(winner, e.max_balance) await bank.set_balance(winner, e.max_balance)
await self.ctx.send( await self.ctx.send(
_( _(

View File

@@ -523,7 +523,7 @@ class Trivia(commands.Cog):
) )
padding = [" " * (len(h) - len(f)) for h, f in zip(headers, fields)] padding = [" " * (len(h) - len(f)) for h, f in zip(headers, fields)]
fields = tuple(f + padding[i] for i, f in enumerate(fields)) fields = tuple(f + padding[i] for i, f in enumerate(fields))
lines.append(" | ".join(fields).format(member=member, **m_data)) lines.append(" | ".join(fields))
if rank == top: if rank == top:
break break
return "\n".join(lines) return "\n".join(lines)

View File

@@ -1,5 +1,6 @@
import contextlib import contextlib
from collections import namedtuple from collections import namedtuple
from copy import copy
from typing import Union, Optional from typing import Union, Optional
import discord import discord
@@ -350,22 +351,27 @@ class Warnings(commands.Cog):
reason_type = None reason_type = None
async with self.config.guild(ctx.guild).reasons() as registered_reasons: async with self.config.guild(ctx.guild).reasons() as registered_reasons:
if reason.lower() not in registered_reasons: if (reason_type := registered_reasons.get(reason.lower())) is None:
msg = _("That is not a registered reason!") msg = _("That is not a registered reason!")
if custom_allowed: if custom_allowed:
reason_type = {"description": reason, "points": points} reason_type = {"description": reason, "points": points}
elif ( else:
ctx.guild.owner == ctx.author # logic taken from `[p]permissions canrun`
or ctx.channel.permissions_for(ctx.author).administrator fake_message = copy(ctx.message)
or await ctx.bot.is_owner(ctx.author) fake_message.content = f"{ctx.prefix}warningset allowcustomreasons"
): fake_context = await ctx.bot.get_context(fake_message)
try:
can = await self.allowcustomreasons.can_run(
fake_context, check_all_parents=True, change_permission_state=False
)
except commands.CommandError:
can = False
if can:
msg += " " + _( msg += " " + _(
"Do `{prefix}warningset allowcustomreasons true` to enable custom " "Do `{prefix}warningset allowcustomreasons true` to enable custom "
"reasons." "reasons."
).format(prefix=ctx.clean_prefix) ).format(prefix=ctx.clean_prefix)
return await ctx.send(msg) return await ctx.send(msg)
else:
reason_type = registered_reasons[reason.lower()]
if reason_type is None: if reason_type is None:
return return
member_settings = self.config.member(user) member_settings = self.config.member(user)

View File

@@ -657,12 +657,20 @@ class RedBase(
for package in packages: for package in packages:
try: try:
spec = await self._cog_mgr.find_cog(package) spec = await self._cog_mgr.find_cog(package)
if spec is None:
log.error(
"Failed to load package %s (package was not found in any cog path)",
package,
)
await self.remove_loaded_package(package)
to_remove.append(package)
continue
await asyncio.wait_for(self.load_extension(spec), 30) await asyncio.wait_for(self.load_extension(spec), 30)
except asyncio.TimeoutError: except asyncio.TimeoutError:
log.exception("Failed to load package %s (timeout)", package) log.exception("Failed to load package %s (timeout)", package)
to_remove.append(package) to_remove.append(package)
except Exception as e: except Exception as e:
log.exception("Failed to load package {}".format(package), exc_info=e) log.exception("Failed to load package %s", package, exc_info=e)
await self.remove_loaded_package(package) await self.remove_loaded_package(package)
to_remove.append(package) to_remove.append(package)
for package in to_remove: for package in to_remove:

View File

@@ -2629,7 +2629,7 @@ class Core(commands.Cog, CoreLogic):
ctx: commands.Context, ctx: commands.Context,
channel: Optional[Union[discord.TextChannel, discord.CategoryChannel]] = None, channel: Optional[Union[discord.TextChannel, discord.CategoryChannel]] = None,
): ):
"""Remove a channel or category from ignore the list. """Remove a channel or category from the ignore list.
Defaults to the current channel. Defaults to the current channel.
""" """

View File

@@ -81,14 +81,15 @@ async def test_is_ancestor(mocker, repo, maybe_ancestor_rev, descendant_rev, ret
descendant_rev=descendant_rev, descendant_rev=descendant_rev,
), ),
valid_exit_codes=(0, 1), valid_exit_codes=(0, 1),
debug_only=True,
) )
assert ret is expected assert ret is expected
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_is_ancestor_raise(mocker, repo): async def test_is_ancestor_object_raise(mocker, repo):
m = _mock_run(mocker, repo, 128) m = _mock_run(mocker, repo, 128, b"", b"fatal: Not a valid object name invalid1")
with pytest.raises(GitException): with pytest.raises(UnknownRevision):
await repo.is_ancestor("invalid1", "invalid2") await repo.is_ancestor("invalid1", "invalid2")
m.assert_called_once_with( m.assert_called_once_with(
@@ -99,6 +100,33 @@ async def test_is_ancestor_raise(mocker, repo):
descendant_rev="invalid2", descendant_rev="invalid2",
), ),
valid_exit_codes=(0, 1), valid_exit_codes=(0, 1),
debug_only=True,
)
@pytest.mark.asyncio
async def test_is_ancestor_commit_raise(mocker, repo):
m = _mock_run(
mocker,
repo,
128,
b"",
b"fatal: Not a valid commit name 0123456789abcde0123456789abcde0123456789",
)
with pytest.raises(UnknownRevision):
await repo.is_ancestor(
"0123456789abcde0123456789abcde0123456789", "c950fc05a540dd76b944719c2a3302da2e2f3090"
)
m.assert_called_once_with(
ProcessFormatter().format(
repo.GIT_IS_ANCESTOR,
path=repo.folder_path,
maybe_ancestor_rev="0123456789abcde0123456789abcde0123456789",
descendant_rev="c950fc05a540dd76b944719c2a3302da2e2f3090",
),
valid_exit_codes=(0, 1),
debug_only=True,
) )

View File

@@ -381,7 +381,7 @@ async def test_git_is_ancestor_false(git_repo):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_git_is_ancestor_invalid_ref(git_repo): async def test_git_is_ancestor_invalid_object(git_repo):
p = await git_repo._run( p = await git_repo._run(
ProcessFormatter().format( ProcessFormatter().format(
git_repo.GIT_IS_ANCESTOR, git_repo.GIT_IS_ANCESTOR,
@@ -394,6 +394,22 @@ async def test_git_is_ancestor_invalid_ref(git_repo):
assert p.stderr.decode().strip() == "fatal: Not a valid object name invalid1" assert p.stderr.decode().strip() == "fatal: Not a valid object name invalid1"
@pytest.mark.asyncio
async def test_git_is_ancestor_invalid_commit(git_repo):
p = await git_repo._run(
ProcessFormatter().format(
git_repo.GIT_IS_ANCESTOR,
path=git_repo.folder_path,
maybe_ancestor_rev="0123456789abcde0123456789abcde0123456789",
descendant_rev="c950fc05a540dd76b944719c2a3302da2e2f3090",
)
)
assert p.returncode == 128
assert p.stderr.decode().strip() == (
"fatal: Not a valid commit name 0123456789abcde0123456789abcde0123456789"
)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_git_check_if_module_exists_true(git_repo): async def test_git_check_if_module_exists_true(git_repo):
p = await git_repo._run( p = await git_repo._run(