Skip to content

Commit

Permalink
Resolve linked messages issues, improve group functionality (bugfixes),
Browse files Browse the repository at this point in the history
resolves #3041
  • Loading branch information
Jerrie-Aries authored and fourjr committed Aug 7, 2021
1 parent 3407e05 commit 20b31f8
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 83 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ v3.10 adds group conversations while resolving othre bugs and QOL changes. It is
- `close_on_leave_reason` now works properly when `close_on_leave` is enabled. ([GH #3075](https://github.com/kyb3r/modmail/issues/3075))
- Invalid arguments are now properly catched and a proper error message is sent.
- Update database after resetting/purging all plugins. ([GH #3011](https://github.com/kyb3r/modmail/pull/3011))
- `thread_auto_close` timer now only resets on non-note and replies from mods. ([GH #3030](https://github.com/kyb3r/modmail/issues/3030))

## Internal

Expand Down
17 changes: 5 additions & 12 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ async def handle_reaction_events(self, payload):
if not thread.recipient.dm_channel:
await thread.recipient.create_dm()
try:
linked_message = await thread.find_linked_message_from_dm(message, either_direction=True)
linked_messages = await thread.find_linked_message_from_dm(message, either_direction=True)
except ValueError as e:
logger.warning("Failed to find linked message for reactions: %s", e)
return
Expand All @@ -1274,19 +1274,19 @@ async def handle_reaction_events(self, payload):
if not thread:
return
try:
_, *linked_message = await thread.find_linked_messages(message.id, either_direction=True)
_, *linked_messages = await thread.find_linked_messages(message.id, either_direction=True)
except ValueError as e:
logger.warning("Failed to find linked message for reactions: %s", e)
return

if self.config["transfer_reactions"] and linked_message is not [None]:
if self.config["transfer_reactions"] and linked_messages is not [None]:
if payload.event_type == "REACTION_ADD":
for msg in linked_message:
for msg in linked_messages:
await self.add_reaction(msg, reaction)
await self.add_reaction(message, reaction)
else:
try:
for msg in linked_message:
for msg in linked_messages:
await msg.remove_reaction(reaction, self.user)
await message.remove_reaction(reaction, self.user)
except (discord.HTTPException, discord.InvalidArgument) as e:
Expand Down Expand Up @@ -1432,13 +1432,6 @@ async def on_message_delete(self, message):
if not thread:
return

audit_logs = self.modmail_guild.audit_logs(limit=10, action=discord.AuditLogAction.message_delete)

entry = await audit_logs.find(lambda a: a.target == self.user)

if entry is None:
return

try:
await thread.delete_message(message, note=False)
embed = discord.Embed(description="Successfully deleted message.", color=self.main_color)
Expand Down
165 changes: 99 additions & 66 deletions core/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
match_user_id,
match_other_recipients,
truncate,
format_channel_name,
get_top_hoisted_role,
create_thread_channel,
get_joint_id,
)

logger = getLogger(__name__)
Expand All @@ -36,7 +37,7 @@ def __init__(
manager: "ThreadManager",
recipient: typing.Union[discord.Member, discord.User, int],
channel: typing.Union[discord.DMChannel, discord.TextChannel] = None,
other_recipients: typing.List[typing.Union[discord.Member, discord.User]] = [],
other_recipients: typing.List[typing.Union[discord.Member, discord.User]] = None,
):
self.manager = manager
self.bot = manager.bot
Expand All @@ -48,7 +49,7 @@ def __init__(
raise CommandError("Recipient cannot be a bot.")
self._id = recipient.id
self._recipient = recipient
self._other_recipients = other_recipients
self._other_recipients = other_recipients or []
self._channel = channel
self.genesis_message = None
self._ready_event = asyncio.Event()
Expand Down Expand Up @@ -127,9 +128,13 @@ async def from_channel(cls, manager: "ThreadManager", channel: discord.TextChann
else:
recipient = manager.bot.get_user(recipient_id) or await manager.bot.fetch_user(recipient_id)

other_recipients = match_other_recipients(channel.topic)
for n, uid in enumerate(other_recipients):
other_recipients[n] = manager.bot.get_user(uid) or await manager.bot.fetch_user(uid)
other_recipients = []
for uid in match_other_recipients(channel.topic):
try:
other_recipient = manager.bot.get_user(uid) or await manager.bot.fetch_user(uid)
except discord.NotFound:
continue
other_recipients.append(other_recipient)

thread = cls(manager, recipient or recipient_id, channel, other_recipients)

Expand All @@ -149,33 +154,19 @@ async def setup(self, *, creator=None, category=None, initial_message=None):
overwrites = None

try:
channel = await self.bot.modmail_guild.create_text_channel(
name=format_channel_name(self.bot, recipient),
category=category,
overwrites=overwrites,
reason="Creating a thread channel.",
)
except discord.HTTPException as e:
# try again but null-discrim (name could be banned)
try:
channel = await self.bot.modmail_guild.create_text_channel(
name=format_channel_name(self.bot, recipient, force_null=True),
category=category,
overwrites=overwrites,
reason="Creating a thread channel.",
)
except discord.HTTPException as e: # Failed to create due to missing perms.
logger.critical("An error occurred while creating a thread.", exc_info=True)
self.manager.cache.pop(self.id)
channel = await create_thread_channel(self.bot, recipient, category, overwrites)
except discord.HTTPException as e: # Failed to create due to missing perms.
logger.critical("An error occurred while creating a thread.", exc_info=True)
self.manager.cache.pop(self.id)

embed = discord.Embed(color=self.bot.error_color)
embed.title = "Error while trying to create a thread."
embed.description = str(e)
embed.add_field(name="Recipient", value=recipient.mention)
embed = discord.Embed(color=self.bot.error_color)
embed.title = "Error while trying to create a thread."
embed.description = str(e)
embed.add_field(name="Recipient", value=recipient.mention)

if self.bot.log_channel is not None:
await self.bot.log_channel.send(embed=embed)
return
if self.bot.log_channel is not None:
await self.bot.log_channel.send(embed=embed)
return

self._channel = channel

Expand Down Expand Up @@ -209,7 +200,7 @@ async def send_genesis_message():
logger.error("Failed unexpectedly:", exc_info=True)

async def send_recipient_genesis_message():
# Once thread is ready, tell the recipient.
# Once thread is ready, tell the recipient (don't send if using contact on others)
thread_creation_response = self.bot.config["thread_creation_response"]

embed = discord.Embed(
Expand Down Expand Up @@ -585,7 +576,7 @@ async def find_linked_messages(
either_direction: bool = False,
message1: discord.Message = None,
note: bool = True,
) -> typing.Tuple[discord.Message, typing.Optional[discord.Message]]:
) -> typing.Tuple[discord.Message, typing.List[typing.Optional[discord.Message]]]:
if message1 is not None:
if not message1.embeds or not message1.embeds[0].author.url or message1.author != self.bot.user:
raise ValueError("Malformed thread message.")
Expand Down Expand Up @@ -698,49 +689,84 @@ async def delete_message(
if tasks:
await asyncio.gather(*tasks)

async def find_linked_message_from_dm(self, message, either_direction=False):
if either_direction and message.embeds and message.embeds[0].author.url:
compare_url = message.embeds[0].author.url
compare_id = compare_url.split("#")[-1]
else:
compare_url = None
compare_id = None
async def find_linked_message_from_dm(
self, message, either_direction=False
) -> typing.List[discord.Message]:

joint_id = None
if either_direction:
joint_id = get_joint_id(message)
# could be None too, if that's the case we'll reassign this variable from
# thread message we fetch in the next step

linked_messages = []
if self.channel is not None:
async for linked_message in self.channel.history():
if not linked_message.embeds:
async for msg in self.channel.history():
if not msg.embeds:
continue
url = linked_message.embeds[0].author.url
if not url:
continue
if url == compare_url:
return linked_message

msg_id = url.split("#")[-1]
if not msg_id.isdigit():
msg_joint_id = get_joint_id(msg)
if msg_joint_id is None:
continue
msg_id = int(msg_id)
if int(msg_id) == message.id:
return linked_message

if compare_id is not None and compare_id.isdigit():
if int(msg_id) == int(compare_id):
return linked_message
if msg_joint_id == message.id:
linked_messages.append(msg)
break

if joint_id is not None and msg_joint_id == joint_id:
linked_messages.append(msg)
break
else:
raise ValueError("Thread channel message not found.")
else:
raise ValueError("Thread channel message not found.")

if joint_id is None:
joint_id = get_joint_id(linked_messages[0])
if joint_id is None:
# still None, supress this and return the thread message
logger.error("Malformed thread message.")
return linked_messages

for user in self.recipients:
if user.dm_channel == message.channel:
continue
async for other_msg in user.history():
if either_direction:
if other_msg.id == joint_id:
linked_messages.append(other_msg)
break

if not other_msg.embeds:
continue

other_joint_id = get_joint_id(other_msg)
if other_joint_id is not None and other_joint_id == joint_id:
linked_messages.append(other_msg)
break
else:
logger.error("Linked message from recipient %s not found.", user)

return linked_messages

async def edit_dm_message(self, message: discord.Message, content: str) -> None:
try:
linked_message = await self.find_linked_message_from_dm(message)
linked_messages = await self.find_linked_message_from_dm(message)
except ValueError:
logger.warning("Failed to edit message.", exc_info=True)
raise
embed = linked_message.embeds[0]
embed.add_field(name="**Edited, former message:**", value=embed.description)
embed.description = content
await asyncio.gather(self.bot.api.edit_message(message.id, content), linked_message.edit(embed=embed))

async def note(self, message: discord.Message, persistent=False, thread_creation=False) -> None:
for msg in linked_messages:
embed = msg.embeds[0]
if isinstance(msg.channel, discord.TextChannel):
# just for thread channel, we put the old message in embed field
embed.add_field(name="**Edited, former message:**", value=embed.description)
embed.description = content
await asyncio.gather(self.bot.api.edit_message(message.id, content), msg.edit(embed=embed))

async def note(
self, message: discord.Message, persistent=False, thread_creation=False
) -> discord.Message:
if not message.content and not message.attachments:
raise MissingRequiredArgument(SimpleNamespace(name="msg"))

Expand All @@ -758,7 +784,9 @@ async def note(self, message: discord.Message, persistent=False, thread_creation

return msg

async def reply(self, message: discord.Message, anonymous: bool = False, plain: bool = False) -> None:
async def reply(
self, message: discord.Message, anonymous: bool = False, plain: bool = False
) -> typing.Tuple[discord.Message, discord.Message]:
if not message.content and not message.attachments:
raise MissingRequiredArgument(SimpleNamespace(name="msg"))
if not any(g.get_member(self.id) for g in self.bot.guilds):
Expand Down Expand Up @@ -854,7 +882,8 @@ async def send(
thread_creation: bool = False,
) -> None:

self.bot.loop.create_task(self._restart_close_timer()) # Start or restart thread auto close
if not note and from_mod:
self.bot.loop.create_task(self._restart_close_timer()) # Start or restart thread auto close

if self.close_task is not None:
# cancel closing if a thread message is sent.
Expand Down Expand Up @@ -1208,9 +1237,13 @@ async def _find_from_channel(self, channel):
except discord.NotFound:
recipient = None

other_recipients = match_other_recipients(channel.topic)
for n, uid in enumerate(other_recipients):
other_recipients[n] = self.bot.get_user(uid) or await self.bot.fetch_user(uid)
other_recipients = []
for uid in match_other_recipients(channel.topic):
try:
other_recipient = self.bot.get_user(uid) or await self.bot.fetch_user(uid)
except discord.NotFound:
continue
other_recipients.append(other_recipient)

if recipient is None:
thread = Thread(self, user_id, channel, other_recipients)
Expand Down
Loading

0 comments on commit 20b31f8

Please sign in to comment.