aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dennis Pham <[email protected]>2020-08-09 13:48:46 -0400
committerGravatar GitHub <[email protected]>2020-08-09 13:48:46 -0400
commita59cdea6e2610abc2752fd253897ae01285d61b8 (patch)
tree44a9787f8d6b834095568035617a664fa49b167d
parentUtils: show error message for long poll titles (diff)
parentRemove unnecessary edits during pagination (diff)
Merge branch 'master' into bug/util/1079/long-poll-titles
-rw-r--r--Dockerfile5
-rw-r--r--Pipfile2
-rw-r--r--Pipfile.lock123
-rw-r--r--bot/cogs/filtering.py4
-rw-r--r--bot/cogs/help_channels.py89
-rw-r--r--bot/cogs/moderation/scheduler.py4
-rw-r--r--bot/cogs/moderation/silence.py3
-rw-r--r--bot/cogs/reminders.py4
-rw-r--r--bot/cogs/source.py22
-rw-r--r--bot/pagination.py10
-rw-r--r--bot/utils/redis_cache.py17
-rw-r--r--bot/utils/regex.py2
-rw-r--r--config-default.yml4
13 files changed, 153 insertions, 136 deletions
diff --git a/Dockerfile b/Dockerfile
index 0b1674e7a..06a538b2a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,11 +6,6 @@ ENV PIP_NO_CACHE_DIR=false \
PIPENV_IGNORE_VIRTUALENVS=1 \
PIPENV_NOSPIN=1
-RUN apt-get -y update \
- && apt-get install -y \
- git \
- && rm -rf /var/lib/apt/lists/*
-
# Install pipenv
RUN pip install -U pipenv
diff --git a/Pipfile b/Pipfile
index 4db8a238b..6fff2223e 100644
--- a/Pipfile
+++ b/Pipfile
@@ -12,7 +12,7 @@ beautifulsoup4 = "~=4.9"
colorama = {version = "~=0.4.3",sys_platform = "== 'win32'"}
coloredlogs = "~=14.0"
deepdiff = "~=4.0"
-discord-py = {git = "https://github.com/Rapptz/discord.py.git",ref = "0bc15fa130b8f01fe2d67446a2184d474b0d0ba7"}
+discord.py = "~=1.4.0"
fakeredis = "~=1.4"
feedparser = "~=5.2"
fuzzywuzzy = "~=0.17"
diff --git a/Pipfile.lock b/Pipfile.lock
index c8cd96d3d..50ddd478c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "eab4852974d26bd2c10362540c3e01d34af62446cb4e1915ec9a0bf2bddf4d94"
+ "sha256": "1905fd7eb15074ddbf04f2177b6cdd65edc4c74cb5fcbf4e6ca08ef649ba8a3c"
},
"pipfile-spec": 6,
"requires": {
@@ -60,11 +60,11 @@
},
"aiormq": {
"hashes": [
- "sha256:41a9d4eb17db805f30ed172f3f609fe0c2b16657fb15b1b67df19d251dd93c0d",
- "sha256:7c19477a9450824cb79f9949fd238f4148e2c0dca67756a2868863c387209f04"
+ "sha256:106695a836f19c1af6c46b58e8aac80e00f86c5b3287a3c6483a1ee369cc95c9",
+ "sha256:9f6dbf6155fe2b7a3d24bf68de97fb812db0fac0a54e96bc1af14ea95078ba7f"
],
"markers": "python_version >= '3.6'",
- "version": "==3.2.2"
+ "version": "==3.2.3"
},
"alabaster": {
"hashes": [
@@ -177,9 +177,22 @@
"index": "pypi",
"version": "==4.3.2"
},
- "discord-py": {
- "git": "https://github.com/Rapptz/discord.py.git",
- "ref": "0bc15fa130b8f01fe2d67446a2184d474b0d0ba7"
+ "discord": {
+ "hashes": [
+ "sha256:9d4debb4a37845543bd4b92cb195bc53a302797333e768e70344222857ff1559",
+ "sha256:ff6653655e342e7721dfb3f10421345fd852c2a33f2cca912b1c39b3778a9429"
+ ],
+ "index": "pypi",
+ "py": "~=1.4.0",
+ "version": "==1.0.1"
+ },
+ "discord.py": {
+ "hashes": [
+ "sha256:2b1846bfa382b54f4eace8e437a9f59f185388c5b08749ac0e1bbd98e05bfde5",
+ "sha256:f3db9531fccc391f51de65cfa46133106a9ba12ff2927aca6c14bffd3b7f17b5"
+ ],
+ "markers": "python_full_version >= '3.5.3'",
+ "version": "==1.4.0"
},
"docutils": {
"hashes": [
@@ -191,11 +204,11 @@
},
"fakeredis": {
"hashes": [
- "sha256:4d170886865a91dbc8b7f8cbd4e5d488f4c5f2f25dfae127f001617bbe9e8f97",
- "sha256:647b2593d349d9d4e566c8dadb2e4c71ba35be5bdc4f1f7ac2d565a12a965053"
+ "sha256:790c85ad0f3b2967aba1f51767021bc59760fcb612159584be018ea7384f7fd2",
+ "sha256:fdfe06f277092d022c271fcaefdc1f0c8d9bfa8cb15374cae41d66a20bd96d2b"
],
"index": "pypi",
- "version": "==1.4.1"
+ "version": "==1.4.2"
},
"feedparser": {
"hashes": [
@@ -542,11 +555,11 @@
},
"sentry-sdk": {
"hashes": [
- "sha256:2de15b13836fa3522815a933bd9c887c77f4868071043349f94f1b896c1bcfb8",
- "sha256:38bb09d0277117f76507c8728d9a5156f09a47ac5175bb8072513859d19a593b"
+ "sha256:21b17d6aa064c0fb703a7c00f77cf6c9c497cf2f83345c28892980a5e742d116",
+ "sha256:4fc97114c77d005467b9b1a29f042e2bc01923cb683b0ef0bbda46e79fa12532"
],
"index": "pypi",
- "version": "==0.16.2"
+ "version": "==0.16.3"
},
"six": {
"hashes": [
@@ -642,14 +655,6 @@
"index": "pypi",
"version": "==3.3.0"
},
- "typing-extensions": {
- "hashes": [
- "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
- "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
- "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
- ],
- "version": "==3.7.4.2"
- },
"urllib3": {
"hashes": [
"sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a",
@@ -658,56 +663,28 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.25.10"
},
- "websockets": {
- "hashes": [
- "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5",
- "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5",
- "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308",
- "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb",
- "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a",
- "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c",
- "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170",
- "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422",
- "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8",
- "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485",
- "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f",
- "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8",
- "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc",
- "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779",
- "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989",
- "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1",
- "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092",
- "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824",
- "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d",
- "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55",
- "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36",
- "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"
- ],
- "markers": "python_full_version >= '3.6.1'",
- "version": "==8.1"
- },
"yarl": {
"hashes": [
- "sha256:1707230e1ea48ea06a3e20acb4ce05a38d2465bd9566c21f48f6212a88e47536",
- "sha256:1f269e8e6676193a94635399a77c9059e1826fb6265c9204c9e5a8ccd36006e1",
- "sha256:2657716c1fc998f5f2675c0ee6ce91282e0da0ea9e4a94b584bb1917e11c1559",
- "sha256:431faa6858f0ea323714d8b7b4a7da1db2eeb9403607f0eaa3800ab2c5a4b627",
- "sha256:5bbcb195da7de57f4508b7508c33f7593e9516e27732d08b9aad8586c7b8c384",
- "sha256:5c82f5b1499342339f22c83b97dbe2b8a09e47163fab86cd934a8dd46620e0fb",
- "sha256:5d410f69b4f92c5e1e2a8ffb73337cd8a274388c6975091735795588a538e605",
- "sha256:66b4f345e9573e004b1af184bc00431145cf5e089a4dcc1351505c1f5750192c",
- "sha256:875b2a741ce0208f3b818008a859ab5d0f461e98a32bbdc6af82231a9e761c55",
- "sha256:9a3266b047d15e78bba38c8455bf68b391c040231ca5965ef867f7cbbc60bde5",
- "sha256:9a592c4aa642249e9bdaf76897d90feeb08118626b363a6be8788a9b300274b5",
- "sha256:a1772068401d425e803999dada29a6babf041786e08be5e79ef63c9ecc4c9575",
- "sha256:b065a5c3e050395ae563019253cc6c769a50fd82d7fa92d07476273521d56b7c",
- "sha256:b325fefd574ebef50e391a1072d1712a60348ca29c183e1d546c9d87fec2cd32",
- "sha256:cf5eb664910d759bbae0b76d060d6e21f8af5098242d66c448bbebaf2a7bfa70",
- "sha256:f058b6541477022c7b54db37229f87dacf3b565de4f901ff5a0a78556a174fea",
- "sha256:f5cfed0766837303f688196aa7002730d62c5cc802d98c6395ea1feb87252727"
+ "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409",
+ "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593",
+ "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2",
+ "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8",
+ "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d",
+ "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692",
+ "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02",
+ "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a",
+ "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8",
+ "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6",
+ "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511",
+ "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e",
+ "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a",
+ "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb",
+ "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f",
+ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317",
+ "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"
],
"markers": "python_version >= '3.5'",
- "version": "==1.5.0"
+ "version": "==1.5.1"
}
},
"develop": {
@@ -728,11 +705,11 @@
},
"cfgv": {
"hashes": [
- "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53",
- "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513"
+ "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d",
+ "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"
],
"markers": "python_full_version >= '3.6.1'",
- "version": "==3.1.0"
+ "version": "==3.2.0"
},
"coverage": {
"hashes": [
@@ -968,11 +945,11 @@
},
"virtualenv": {
"hashes": [
- "sha256:688a61d7976d82b92f7906c367e83bb4b3f0af96f8f75bfcd3da95608fe8ac6c",
- "sha256:8f582a030156282a9ee9d319984b759a232b07f86048c1d6a9e394afa44e78c8"
+ "sha256:7b54fd606a1b85f83de49ad8d80dbec08e983a2d2f96685045b262ebc7481ee5",
+ "sha256:8cd7b2a4850b003a11be2fc213e206419efab41115cc14bca20e69654f2ac08e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.0.28"
+ "version": "==20.0.30"
}
}
}
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 64afd184d..4ec95ad73 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -99,6 +99,10 @@ class Filtering(Cog):
self.bot.loop.create_task(self.reschedule_offensive_msg_deletion())
+ def cog_unload(self) -> None:
+ """Cancel scheduled tasks."""
+ self.scheduler.cancel_all()
+
def _get_filterlist_items(self, list_type: str, *, allowed: bool) -> list:
"""Fetch items from the filter_list_cache."""
return self.bot.filter_list_cache[f"{list_type.upper()}.{allowed}"].keys()
diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py
index 1be980472..57094751e 100644
--- a/bot/cogs/help_channels.py
+++ b/bot/cogs/help_channels.py
@@ -215,9 +215,6 @@ class HelpChannels(commands.Cog):
log.trace("close command invoked; checking if the channel is in-use.")
if ctx.channel.category == self.in_use_category:
if await self.dormant_check(ctx):
-
- # Remove the claimant and the cooldown role
- await self.help_channel_claimants.delete(ctx.channel.id)
await self.remove_cooldown_role(ctx.author)
# Ignore missing task when cooldown has passed but the channel still isn't dormant.
@@ -551,20 +548,9 @@ class HelpChannels(commands.Cog):
A caller argument is provided for metrics.
"""
- msg_id = await self.question_messages.pop(channel.id)
-
- try:
- await self.bot.http.unpin_message(channel.id, msg_id)
- except discord.HTTPException as e:
- if e.code == 10008:
- log.trace(f"Message {msg_id} don't exist, can't unpin.")
- else:
- log.warn(f"Got unexpected status {e.code} when unpinning message {msg_id}: {e.text}")
- else:
- log.trace(f"Unpinned message {msg_id}.")
-
log.info(f"Moving #{channel} ({channel.id}) to the Dormant category.")
+ await self.help_channel_claimants.delete(channel.id)
await self.move_to_bottom_position(
channel=channel,
category_id=constants.Categories.help_dormant,
@@ -587,6 +573,8 @@ class HelpChannels(commands.Cog):
embed = discord.Embed(description=DORMANT_MSG)
await channel.send(embed=embed)
+ await self.unpin(channel)
+
log.trace(f"Pushing #{channel} ({channel.id}) into the channel queue.")
self.channel_queue.put_nowait(channel)
self.report_stats()
@@ -704,15 +692,8 @@ class HelpChannels(commands.Cog):
log.info(f"Channel #{channel} was claimed by `{message.author.id}`.")
await self.move_to_in_use(channel)
await self.revoke_send_permissions(message.author)
- # Pin message for better access and store this to cache
- try:
- await message.pin()
- except discord.NotFound:
- log.info(f"Pinning message {message.id} ({channel}) failed because message got deleted.")
- except discord.HTTPException as e:
- log.info(f"Pinning message {message.id} ({channel.id}) failed with code {e.code}", exc_info=e)
- else:
- await self.question_messages.set(channel.id, message.id)
+
+ await self.pin(message)
# Add user with channel for dormant check.
await self.help_channel_claimants.set(channel.id, message.author.id)
@@ -754,9 +735,22 @@ class HelpChannels(commands.Cog):
self.scheduler.schedule_later(delay, msg.channel.id, self.move_idle_channel(msg.channel))
async def is_empty(self, channel: discord.TextChannel) -> bool:
- """Return True if the most recent message in `channel` is the bot's `AVAILABLE_MSG`."""
- msg = await self.get_last_message(channel)
- return self.match_bot_embed(msg, AVAILABLE_MSG)
+ """Return True if there's an AVAILABLE_MSG and the messages leading up are bot messages."""
+ log.trace(f"Checking if #{channel} ({channel.id}) is empty.")
+
+ # A limit of 100 results in a single API call.
+ # If AVAILABLE_MSG isn't found within 100 messages, then assume the channel is not empty.
+ # Not gonna do an extensive search for it cause it's too expensive.
+ async for msg in channel.history(limit=100):
+ if not msg.author.bot:
+ log.trace(f"#{channel} ({channel.id}) has a non-bot message.")
+ return False
+
+ if self.match_bot_embed(msg, AVAILABLE_MSG):
+ log.trace(f"#{channel} ({channel.id}) has the available message embed.")
+ return True
+
+ return False
async def check_cooldowns(self) -> None:
"""Remove expired cooldowns and re-schedule active ones."""
@@ -863,6 +857,47 @@ class HelpChannels(commands.Cog):
log.trace(f"Channel #{channel} ({channel_id}) retrieved.")
return channel
+ async def pin_wrapper(self, msg_id: int, channel: discord.TextChannel, *, pin: bool) -> bool:
+ """
+ Pin message `msg_id` in `channel` if `pin` is True or unpin if it's False.
+
+ Return True if successful and False otherwise.
+ """
+ channel_str = f"#{channel} ({channel.id})"
+ if pin:
+ func = self.bot.http.pin_message
+ verb = "pin"
+ else:
+ func = self.bot.http.unpin_message
+ verb = "unpin"
+
+ try:
+ await func(channel.id, msg_id)
+ except discord.HTTPException as e:
+ if e.code == 10008:
+ log.debug(f"Message {msg_id} in {channel_str} doesn't exist; can't {verb}.")
+ else:
+ log.exception(
+ f"Error {verb}ning message {msg_id} in {channel_str}: {e.status} ({e.code})"
+ )
+ return False
+ else:
+ log.trace(f"{verb.capitalize()}ned message {msg_id} in {channel_str}.")
+ return True
+
+ async def pin(self, message: discord.Message) -> None:
+ """Pin an initial question `message` and store it in a cache."""
+ if await self.pin_wrapper(message.id, message.channel, pin=True):
+ await self.question_messages.set(message.channel.id, message.id)
+
+ async def unpin(self, channel: discord.TextChannel) -> None:
+ """Unpin the initial question message sent in `channel`."""
+ msg_id = await self.question_messages.pop(channel.id)
+ if msg_id is None:
+ log.debug(f"#{channel} ({channel.id}) doesn't have a message pinned.")
+ else:
+ await self.pin_wrapper(msg_id, channel, pin=False)
+
async def wait_for_dormant_channel(self) -> discord.TextChannel:
"""Wait for a dormant channel to become available in the queue and return it."""
log.trace("Waiting for a dormant channel.")
diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py
index 601e238c9..75028d851 100644
--- a/bot/cogs/moderation/scheduler.py
+++ b/bot/cogs/moderation/scheduler.py
@@ -31,6 +31,10 @@ class InfractionScheduler:
self.bot.loop.create_task(self.reschedule_infractions(supported_infractions))
+ def cog_unload(self) -> None:
+ """Cancel scheduled tasks."""
+ self.scheduler.cancel_all()
+
@property
def mod_log(self) -> ModLog:
"""Get the currently loaded ModLog cog instance."""
diff --git a/bot/cogs/moderation/silence.py b/bot/cogs/moderation/silence.py
index ae4fb7b64..f8a6592bc 100644
--- a/bot/cogs/moderation/silence.py
+++ b/bot/cogs/moderation/silence.py
@@ -152,7 +152,8 @@ class Silence(commands.Cog):
return False
def cog_unload(self) -> None:
- """Send alert with silenced channels on unload."""
+ """Send alert with silenced channels and cancel scheduled tasks on unload."""
+ self.scheduler.cancel_all()
if self.muted_channels:
channels_string = ''.join(channel.mention for channel in self.muted_channels)
message = f"<@&{Roles.moderators}> channels left silenced on cog unload: {channels_string}"
diff --git a/bot/cogs/reminders.py b/bot/cogs/reminders.py
index b5998cc0e..670493bcf 100644
--- a/bot/cogs/reminders.py
+++ b/bot/cogs/reminders.py
@@ -37,6 +37,10 @@ class Reminders(Cog):
self.bot.loop.create_task(self.reschedule_reminders())
+ def cog_unload(self) -> None:
+ """Cancel scheduled tasks."""
+ self.scheduler.cancel_all()
+
async def reschedule_reminders(self) -> None:
"""Get all current reminders from the API and reschedule them."""
await self.bot.wait_until_guild_available()
diff --git a/bot/cogs/source.py b/bot/cogs/source.py
index f1db745cd..205e0ba81 100644
--- a/bot/cogs/source.py
+++ b/bot/cogs/source.py
@@ -60,11 +60,12 @@ class BotSource(commands.Cog):
await ctx.send(embed=embed)
def get_source_link(self, source_item: SourceType) -> Tuple[str, str, Optional[int]]:
- """Build GitHub link of source item, return this link, file location and first line number."""
- if isinstance(source_item, commands.HelpCommand):
- src = type(source_item)
- filename = inspect.getsourcefile(src)
- elif isinstance(source_item, commands.Command):
+ """
+ Build GitHub link of source item, return this link, file location and first line number.
+
+ Raise BadArgument if `source_item` is a dynamically-created object (e.g. via internal eval).
+ """
+ if isinstance(source_item, commands.Command):
if source_item.cog_name == "Alias":
cmd_name = source_item.callback.__name__.replace("_alias", "")
cmd = self.bot.get_command(cmd_name.replace("_", " "))
@@ -78,10 +79,17 @@ class BotSource(commands.Cog):
filename = tags_cog._cache[source_item]["location"]
else:
src = type(source_item)
- filename = inspect.getsourcefile(src)
+ try:
+ filename = inspect.getsourcefile(src)
+ except TypeError:
+ raise commands.BadArgument("Cannot get source for a dynamically-created object.")
if not isinstance(source_item, str):
- lines, first_line_no = inspect.getsourcelines(src)
+ try:
+ lines, first_line_no = inspect.getsourcelines(src)
+ except OSError:
+ raise commands.BadArgument("Cannot get source for a dynamically-created object.")
+
lines_extension = f"#L{first_line_no}-L{first_line_no+len(lines)-1}"
else:
first_line_no = None
diff --git a/bot/pagination.py b/bot/pagination.py
index 94c2d7c0c..bab98cacf 100644
--- a/bot/pagination.py
+++ b/bot/pagination.py
@@ -313,8 +313,6 @@ class LinePaginator(Paginator):
log.debug(f"Got first page reaction - changing to page 1/{len(paginator.pages)}")
- embed.description = ""
- await message.edit(embed=embed)
embed.description = paginator.pages[current_page]
if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
@@ -328,8 +326,6 @@ class LinePaginator(Paginator):
log.debug(f"Got last page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")
- embed.description = ""
- await message.edit(embed=embed)
embed.description = paginator.pages[current_page]
if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
@@ -347,8 +343,6 @@ class LinePaginator(Paginator):
current_page -= 1
log.debug(f"Got previous page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")
- embed.description = ""
- await message.edit(embed=embed)
embed.description = paginator.pages[current_page]
if footer_text:
@@ -368,8 +362,6 @@ class LinePaginator(Paginator):
current_page += 1
log.debug(f"Got next page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")
- embed.description = ""
- await message.edit(embed=embed)
embed.description = paginator.pages[current_page]
if footer_text:
@@ -532,8 +524,6 @@ class ImagePaginator(Paginator):
reaction_type = "next"
# Magic happens here, after page and reaction_type is set
- embed.description = ""
- await message.edit(embed=embed)
embed.description = paginator.pages[current_page]
image = paginator.images[current_page]
diff --git a/bot/utils/redis_cache.py b/bot/utils/redis_cache.py
index 58cfe1df5..52b689b49 100644
--- a/bot/utils/redis_cache.py
+++ b/bot/utils/redis_cache.py
@@ -226,7 +226,6 @@ class RedisCache:
for attribute in vars(instance).values():
if isinstance(attribute, Bot):
self.bot = attribute
- self._redis = self.bot.redis_session
return self
else:
error_message = (
@@ -251,7 +250,7 @@ class RedisCache:
value = self._value_to_typestring(value)
log.trace(f"Setting {key} to {value}.")
- await self._redis.hset(self._namespace, key, value)
+ await self.bot.redis_session.hset(self._namespace, key, value)
async def get(self, key: RedisKeyType, default: Optional[RedisValueType] = None) -> Optional[RedisValueType]:
"""Get an item from the Redis cache."""
@@ -259,7 +258,7 @@ class RedisCache:
key = self._key_to_typestring(key)
log.trace(f"Attempting to retrieve {key}.")
- value = await self._redis.hget(self._namespace, key)
+ value = await self.bot.redis_session.hget(self._namespace, key)
if value is None:
log.trace(f"Value not found, returning default value {default}")
@@ -281,7 +280,7 @@ class RedisCache:
key = self._key_to_typestring(key)
log.trace(f"Attempting to delete {key}.")
- return await self._redis.hdel(self._namespace, key)
+ return await self.bot.redis_session.hdel(self._namespace, key)
async def contains(self, key: RedisKeyType) -> bool:
"""
@@ -291,7 +290,7 @@ class RedisCache:
"""
await self._validate_cache()
key = self._key_to_typestring(key)
- exists = await self._redis.hexists(self._namespace, key)
+ exists = await self.bot.redis_session.hexists(self._namespace, key)
log.trace(f"Testing if {key} exists in the RedisCache - Result is {exists}")
return exists
@@ -314,7 +313,7 @@ class RedisCache:
"""
await self._validate_cache()
items = self._dict_from_typestring(
- await self._redis.hgetall(self._namespace)
+ await self.bot.redis_session.hgetall(self._namespace)
).items()
log.trace(f"Retrieving all key/value pairs from cache, total of {len(items)} items.")
@@ -323,7 +322,7 @@ class RedisCache:
async def length(self) -> int:
"""Return the number of items in the Redis cache."""
await self._validate_cache()
- number_of_items = await self._redis.hlen(self._namespace)
+ number_of_items = await self.bot.redis_session.hlen(self._namespace)
log.trace(f"Returning length. Result is {number_of_items}.")
return number_of_items
@@ -335,7 +334,7 @@ class RedisCache:
"""Deletes the entire hash from the Redis cache."""
await self._validate_cache()
log.trace("Clearing the cache of all key/value pairs.")
- await self._redis.delete(self._namespace)
+ await self.bot.redis_session.delete(self._namespace)
async def pop(self, key: RedisKeyType, default: Optional[RedisValueType] = None) -> RedisValueType:
"""Get the item, remove it from the cache, and provide a default if not found."""
@@ -364,7 +363,7 @@ class RedisCache:
"""
await self._validate_cache()
log.trace(f"Updating the cache with the following items:\n{items}")
- await self._redis.hmset_dict(self._namespace, self._dict_to_typestring(items))
+ await self.bot.redis_session.hmset_dict(self._namespace, self._dict_to_typestring(items))
async def increment(self, key: RedisKeyType, amount: Optional[int, float] = 1) -> None:
"""
diff --git a/bot/utils/regex.py b/bot/utils/regex.py
index d194f93cb..0d2068f90 100644
--- a/bot/utils/regex.py
+++ b/bot/utils/regex.py
@@ -7,6 +7,6 @@ INVITE_RE = re.compile(
r"discord(?:[\.,]|dot)me|" # or discord.me
r"discord(?:[\.,]|dot)io" # or discord.io.
r")(?:[\/]|slash)" # / or 'slash'
- r"([a-zA-Z0-9]+)", # the invite code itself
+ r"([a-zA-Z0-9\-]+)", # the invite code itself
flags=re.IGNORECASE
)
diff --git a/config-default.yml b/config-default.yml
index aacbe170f..4bd90511c 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -432,8 +432,8 @@ help_channels:
# Allowed duration of inactivity before making a channel dormant
idle_minutes: 30
- # Allowed duration of inactivity when question message deleted
- # and no one other sent before message making channel dormant.
+ # Allowed duration of inactivity when channel is empty (due to deleted messages)
+ # before message making a channel dormant
deleted_idle_minutes: 5
# Maximum number of channels to put in the available category