Skip to content

Commit 0bc17a8

Browse files
committed
Misc
1 parent cbdf61f commit 0bc17a8

12 files changed

+297
-198
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
- `?snippet` is now the default command name instead of `?snippets` (`?snippets` is still usable). This is to make this consistent with `?alias`/`?aliases`.
2727
- `colorama` is no longer a necessity, this is due to some unsupported OS.
2828
- Changelog command can now take a version argument to jump straight to specified version.
29+
- `?plugin enabled` results are now sorted alphabetically.
30+
- `?plugin registry` results are now sorted alphabetically, helps user find plugins more easily.
31+
- `?plugin registry page-number` plugin registry can specify a page number for quick access.
2932

3033
### Fixes
3134

@@ -44,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4447
- Removed helper functions `info()` and `error()` for formatting logging, it's formatted automatically now.
4548
- Bumped discord.py version to 1.2.3.
4649
- Use discord tasks for metadata loop.
50+
- More debug based logging.
4751

4852
# v3.0.3
4953

bot.py

+82-44
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from aiohttp import ClientSession
2020
from emoji import UNICODE_EMOJI
2121
from motor.motor_asyncio import AsyncIOMotorClient
22+
from pymongo.errors import ConfigurationError
2223

2324
try:
2425
from colorama import init
@@ -65,6 +66,8 @@ def __init__(self):
6566
super().__init__(command_prefix=None) # implemented in `get_prefix`
6667
self._session = None
6768
self._api = None
69+
self.metadata_loop = None
70+
6871
self._connected = asyncio.Event()
6972
self.start_time = datetime.utcnow()
7073

@@ -77,13 +80,17 @@ def __init__(self):
7780

7881
mongo_uri = self.config["mongo_uri"]
7982
if mongo_uri is None:
80-
raise ValueError("A Mongo URI is necessary for the bot to function.")
81-
82-
self.db = AsyncIOMotorClient(mongo_uri).modmail_bot
83-
self.plugin_db = PluginDatabaseClient(self)
83+
logger.critical("A Mongo URI is necessary for the bot to function.")
84+
raise RuntimeError
8485

85-
self.metadata_loop = None
86+
try:
87+
self.db = AsyncIOMotorClient(mongo_uri).modmail_bot
88+
except ConfigurationError as e:
89+
logger.critical("Your MONGO_URI is copied wrong, try re-copying from the source again.")
90+
logger.critical(str(e))
91+
sys.exit(0)
8692

93+
self.plugin_db = PluginDatabaseClient(self)
8794
self._load_extensions()
8895

8996
@property
@@ -134,7 +141,8 @@ def _configure_logging(self):
134141
logger.info("Logging level: %s", level_text)
135142
else:
136143
logger.info("Invalid logging level set.")
137-
logger.warning("Using default logging level: INFO.")
144+
logger.warning("Using default logging level: %s.", level_text)
145+
logger.debug("Successfully configured logging.")
138146

139147
@property
140148
def version(self) -> str:
@@ -198,11 +206,21 @@ def run(self, *args, **kwargs):
198206
self.loop.run_until_complete(self.session.close())
199207
logger.error(" - Shutting down bot - ")
200208

209+
@property
210+
def owner_ids(self):
211+
owner_ids = self.config["owners"]
212+
if owner_ids is not None:
213+
owner_ids = set(map(int, str(owner_ids).split(",")))
214+
if self.owner_id is not None:
215+
owner_ids.add(self.owner_id)
216+
permissions = self.config["level_permissions"].get(PermissionLevel.OWNER.name, [])
217+
for perm in permissions:
218+
owner_ids.add(int(perm))
219+
return owner_ids
220+
201221
async def is_owner(self, user: discord.User) -> bool:
202-
owners = self.config["owners"]
203-
if owners is not None:
204-
if user.id in set(map(int, str(owners).split(","))):
205-
return True
222+
if user.id in self.owner_ids:
223+
return True
206224
return await super().is_owner(user)
207225

208226
@property
@@ -212,18 +230,18 @@ def log_channel(self) -> typing.Optional[discord.TextChannel]:
212230
channel = self.get_channel(int(channel_id))
213231
if channel is not None:
214232
return channel
233+
logger.debug('LOG_CHANNEL_ID was invalid, removed.')
215234
self.config.remove("log_channel_id")
216235
if self.main_category is not None:
217236
try:
218237
channel = self.main_category.channels[0]
219238
self.config["log_channel_id"] = channel.id
220-
logger.debug("No log channel set, however, one was found. Setting...")
239+
logger.warning("No log channel set, setting #%s to be the log channel.", channel.name)
221240
return channel
222241
except IndexError:
223242
pass
224-
logger.info(
225-
"No log channel set, set one with `%ssetup` or "
226-
"`%sconfig set log_channel_id <id>`.",
243+
logger.warning(
244+
"No log channel set, set one with `%ssetup` or `%sconfig set log_channel_id <id>`.",
227245
self.prefix,
228246
self.prefix,
229247
)
@@ -250,7 +268,8 @@ def aliases(self) -> typing.Dict[str, str]:
250268
def token(self) -> str:
251269
token = self.config["token"]
252270
if token is None:
253-
raise ValueError("TOKEN must be set, this is your bot token.")
271+
logger.critical("TOKEN must be set, set this as bot token found on the Discord Dev Portal.")
272+
sys.exit(0)
254273
return token
255274

256275
@property
@@ -260,7 +279,7 @@ def guild_id(self) -> typing.Optional[int]:
260279
try:
261280
return int(str(guild_id))
262281
except ValueError:
263-
raise ValueError("Invalid guild_id set.")
282+
logger.critical("Invalid GUILD_ID set.")
264283
return None
265284

266285
@property
@@ -284,7 +303,7 @@ def modmail_guild(self) -> typing.Optional[discord.Guild]:
284303
if guild is not None:
285304
return guild
286305
self.config.remove("modmail_guild_id")
287-
logger.error("Invalid modmail_guild_id set.")
306+
logger.critical("Invalid MODMAIL_GUILD_ID set.")
288307
return self.guild
289308

290309
@property
@@ -302,10 +321,11 @@ def main_category(self) -> typing.Optional[discord.CategoryChannel]:
302321
if cat is not None:
303322
return cat
304323
self.config.remove("main_category_id")
324+
logger.debug('MAIN_CATEGORY_ID was invalid, removed.')
305325
cat = discord.utils.get(self.modmail_guild.categories, name="Modmail")
306326
if cat is not None:
307327
self.config["main_category_id"] = cat.id
308-
logger.debug("No main category set, however, one was found. Setting...")
328+
logger.debug("No main category set explicitly, setting category \"Modmail\" as the main category.")
309329
return cat
310330
return None
311331

@@ -384,28 +404,34 @@ async def setup_indexes(self):
384404
("key", "text"),
385405
]
386406
)
407+
logger.debug('Successfully set up database indexes.')
387408

388409
async def on_ready(self):
389410
"""Bot startup, sets uptime."""
390411

391412
# Wait until config cache is populated with stuff from db and on_connect ran
392413
await self.wait_for_connected()
393414

415+
if self.guild is None:
416+
logger.debug('Logging out due to invalid GUILD_ID.')
417+
return await self.logout()
418+
394419
logger.line()
395420
logger.info("Client ready.")
396421
logger.line()
397422
logger.info("Logged in as: %s", self.user)
398423
logger.info("User ID: %s", self.user.id)
399424
logger.info("Prefix: %s", self.prefix)
400-
logger.info("Guild Name: %s", self.guild.name if self.guild else "Invalid")
401-
logger.info("Guild ID: %s", self.guild.id if self.guild else "Invalid")
425+
logger.info("Guild Name: %s", self.guild.name)
426+
logger.info("Guild ID: %s", self.guild.id)
402427
logger.line()
403428

404429
await self.threads.populate_cache()
405430

406431
# closures
407432
closures = self.config["closures"]
408433
logger.info("There are %d thread(s) pending to be closed.", len(closures))
434+
logger.line()
409435

410436
for recipient_id, items in tuple(closures.items()):
411437
after = (
@@ -418,10 +444,13 @@ async def on_ready(self):
418444

419445
if not thread:
420446
# If the channel is deleted
447+
logger.debug('Failed to close thread for recipient %s.', recipient_id)
421448
self.config["closures"].pop(recipient_id)
422449
await self.config.update()
423450
continue
424451

452+
logger.debug('Closing thread for recipient %s.', recipient_id)
453+
425454
await thread.close(
426455
closer=self.get_user(items["closer_id"]),
427456
after=after,
@@ -431,8 +460,6 @@ async def on_ready(self):
431460
auto_close=items.get("auto_close", False),
432461
)
433462

434-
logger.line()
435-
436463
self.metadata_loop = tasks.Loop(
437464
self.post_metadata,
438465
seconds=0,
@@ -552,6 +579,7 @@ async def _process_blocked(self, message: discord.Message) -> bool:
552579
reaction = blocked_emoji
553580
changed = False
554581
delta = human_timedelta(min_account_age)
582+
logger.debug('Blocked due to account age, user %s.', message.author.name)
555583

556584
if str(message.author.id) not in self.blocked_users:
557585
new_reason = (
@@ -565,7 +593,7 @@ async def _process_blocked(self, message: discord.Message) -> bool:
565593
embed=discord.Embed(
566594
title="Message not sent!",
567595
description=f"Your must wait for {delta} "
568-
f"before you can contact {self.user.mention}.",
596+
f"before you can contact me.",
569597
color=discord.Color.red(),
570598
)
571599
)
@@ -575,6 +603,7 @@ async def _process_blocked(self, message: discord.Message) -> bool:
575603
reaction = blocked_emoji
576604
changed = False
577605
delta = human_timedelta(min_guild_age)
606+
logger.debug('Blocked due to guild age, user %s.', message.author.name)
578607

579608
if str(message.author.id) not in self.blocked_users:
580609
new_reason = (
@@ -588,29 +617,33 @@ async def _process_blocked(self, message: discord.Message) -> bool:
588617
embed=discord.Embed(
589618
title="Message not sent!",
590619
description=f"Your must wait for {delta} "
591-
f"before you can contact {self.user.mention}.",
620+
f"before you can contact me.",
592621
color=discord.Color.red(),
593622
)
594623
)
595624

596625
elif str(message.author.id) in self.blocked_users:
597-
reaction = blocked_emoji
598626
if reason.startswith("System Message: New Account.") or reason.startswith(
599627
"System Message: Recently Joined."
600628
):
601629
# Met the age limit already, otherwise it would've been caught by the previous if's
602630
reaction = sent_emoji
631+
logger.debug('No longer internally blocked, user %s.', message.author.name)
603632
self.blocked_users.pop(str(message.author.id))
604633
else:
634+
reaction = blocked_emoji
605635
end_time = re.search(r"%(.+?)%$", reason)
606636
if end_time is not None:
637+
logger.debug('No longer blocked, user %s.', message.author.name)
607638
after = (
608639
datetime.fromisoformat(end_time.group(1)) - now
609640
).total_seconds()
610641
if after <= 0:
611642
# No longer blocked
612643
reaction = sent_emoji
613644
self.blocked_users.pop(str(message.author.id))
645+
else:
646+
logger.debug('User blocked, user %s.', message.author.name)
614647
else:
615648
reaction = sent_emoji
616649

@@ -619,7 +652,7 @@ async def _process_blocked(self, message: discord.Message) -> bool:
619652
try:
620653
await message.add_reaction(reaction)
621654
except (discord.HTTPException, discord.InvalidArgument):
622-
pass
655+
logger.warning('Failed to add reaction %s.', reaction, exc_info=True)
623656
return str(message.author.id) in self.blocked_users
624657

625658
async def process_modmail(self, message: discord.Message) -> None:
@@ -805,20 +838,17 @@ async def on_raw_reaction_add(self, payload):
805838

806839
if isinstance(channel, discord.DMChannel):
807840
if str(reaction) == str(close_emoji): # closing thread
841+
try:
842+
recipient_thread_close = strtobool(self.config["recipient_thread_close"])
843+
except ValueError:
844+
recipient_thread_close = self.config.remove("recipient_thread_close")
845+
if not recipient_thread_close:
846+
return
808847
thread = await self.threads.find(recipient=user)
809848
ts = message.embeds[0].timestamp if message.embeds else None
810849
if thread and ts == thread.channel.created_at:
811850
# the reacted message is the corresponding thread creation embed
812-
try:
813-
recipient_thread_close = strtobool(
814-
self.config["recipient_thread_close"]
815-
)
816-
except ValueError:
817-
recipient_thread_close = self.config.remove(
818-
"recipient_thread_close"
819-
)
820-
if recipient_thread_close:
821-
await thread.close(closer=user)
851+
await thread.close(closer=user)
822852
else:
823853
if not message.embeds:
824854
return
@@ -845,6 +875,7 @@ async def on_guild_channel_delete(self, channel):
845875

846876
if isinstance(channel, discord.CategoryChannel):
847877
if self.main_category.id == channel.id:
878+
logger.debug('Main category was deleted.')
848879
self.config.remove("main_category_id")
849880
await self.config.update()
850881
return
@@ -853,17 +884,19 @@ async def on_guild_channel_delete(self, channel):
853884
return
854885

855886
if self.log_channel is None or self.log_channel.id == channel.id:
887+
logger.info('Log channel deleted.')
856888
self.config.remove("log_channel_id")
857889
await self.config.update()
858890
return
859891

860892
thread = await self.threads.find(channel=channel)
861-
if not thread:
862-
return
863-
864-
await thread.close(closer=mod, silent=True, delete_channel=False)
893+
if thread:
894+
logger.debug('Manually closed channel %s.', channel.name)
895+
await thread.close(closer=mod, silent=True, delete_channel=False)
865896

866897
async def on_member_remove(self, member):
898+
if member.guild != self.guild:
899+
return
867900
thread = await self.threads.find(recipient=member)
868901
if thread:
869902
embed = discord.Embed(
@@ -873,6 +906,8 @@ async def on_member_remove(self, member):
873906
await thread.channel.send(embed=embed)
874907

875908
async def on_member_join(self, member):
909+
if member.guild != self.guild:
910+
return
876911
thread = await self.threads.find(recipient=member)
877912
if thread:
878913
embed = discord.Embed(
@@ -980,11 +1015,14 @@ async def validate_database_connection(self):
9801015

9811016
if "OperationFailure" in message:
9821017
logger.critical(
983-
"This is due to having invalid credentials in your MONGO_URI."
1018+
"This is due to having invalid credentials in your MONGO_URI. "
1019+
"Remember you need to substitute `<password>` with your actual password."
9841020
)
9851021
logger.critical(
986-
"Recheck the username/password and make sure to url encode them. "
987-
"https://www.urlencoder.io/"
1022+
"Be sure to URL encode your username and password (not the entire URL!!), "
1023+
"https://www.urlencoder.io/, if this issue persists, try changing your username and password "
1024+
"to only include alphanumeric characters, no symbols."
1025+
""
9881026
)
9891027
raise
9901028
else:
@@ -1024,7 +1062,7 @@ async def after_post_metadata(self):
10241062
if __name__ == "__main__":
10251063
try:
10261064
import uvloop
1027-
1065+
logger.debug('Setting up with uvloop.')
10281066
uvloop.install()
10291067
except ImportError:
10301068
pass

0 commit comments

Comments
 (0)