Skip to content

Commit 3cfba28

Browse files
committed
Plain replies (#2872)
1 parent 2e3def8 commit 3cfba28

File tree

7 files changed

+99
-14
lines changed

7 files changed

+99
-14
lines changed

Diff for: CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
This project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
77
however, insignificant breaking changes do not guarantee a major version bump, see the reasoning [here](https://github.com/kyb3r/modmail/issues/319). If you're a plugins developer, note the "BREAKING" section.
88

9-
# v3.6.3-dev0
9+
# v3.6.3-dev1
10+
11+
### Added
12+
- Plain replies: added commands `preply`, `pareply`, added config `plain_reply_without_command`. Only works from mod to users.
1013

1114
### Improved
1215

Diff for: bot.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "3.6.3-dev0"
1+
__version__ = "3.6.3-dev1"
22

33

44
import asyncio
@@ -896,10 +896,19 @@ async def process_commands(self, message):
896896

897897
thread = await self.threads.find(channel=ctx.channel)
898898
if thread is not None:
899+
anonymous = False
900+
plain = False
899901
if self.config.get("anon_reply_without_command"):
900-
await thread.reply(message, anonymous=True)
901-
elif self.config.get("reply_without_command"):
902-
await thread.reply(message)
902+
anonymous = True
903+
if self.config.get("plain_reply_without_command"):
904+
plain = True
905+
906+
if (
907+
self.config.get("reply_without_command")
908+
or self.config.get("anon_reply_without_command")
909+
or self.config.get("plain_reply_without_command")
910+
):
911+
await thread.reply(message, anonymous=anonymous, plain=plain)
903912
else:
904913
await self.api.append_log(message, type_="internal")
905914
elif ctx.invoked_with:

Diff for: cogs/modmail.py

+28
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,34 @@ async def areply(self, ctx, *, msg: str = ""):
831831
async with ctx.typing():
832832
await ctx.thread.reply(ctx.message, anonymous=True)
833833

834+
@commands.command(aliases=["plainreply"])
835+
@checks.has_permissions(PermissionLevel.SUPPORTER)
836+
@checks.thread_only()
837+
async def preply(self, ctx, *, msg: str = ""):
838+
"""
839+
Reply to a Modmail thread with a plain message.
840+
841+
Supports attachments and images as well as
842+
automatically embedding image URLs.
843+
"""
844+
ctx.message.content = msg
845+
async with ctx.typing():
846+
await ctx.thread.reply(ctx.message, plain=True)
847+
848+
@commands.command(aliases=["plainanonreply", "plainanonymousreply"])
849+
@checks.has_permissions(PermissionLevel.SUPPORTER)
850+
@checks.thread_only()
851+
async def pareply(self, ctx, *, msg: str = ""):
852+
"""
853+
Reply to a Modmail thread with a plain message and anonmymously.
854+
855+
Supports attachments and images as well as
856+
automatically embedding image URLs.
857+
"""
858+
ctx.message.content = msg
859+
async with ctx.typing():
860+
await ctx.thread.reply(ctx.message, anonymous=True, plain=True)
861+
834862
@commands.command()
835863
@checks.has_permissions(PermissionLevel.SUPPORTER)
836864
@checks.thread_only()

Diff for: cogs/plugins.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,10 @@ async def download_plugin(self, plugin, force=False):
172172
except UnicodeDecodeError:
173173
pass
174174
else:
175-
if raw == 'Not Found':
176-
raise InvalidPluginError('Plugin not found')
175+
if raw == "Not Found":
176+
raise InvalidPluginError("Plugin not found")
177177
else:
178-
raise InvalidPluginError('Invalid download recieved, non-bytes object')
178+
raise InvalidPluginError("Invalid download recieved, non-bytes object")
179179

180180
plugin_io = io.BytesIO(raw)
181181
if not plugin.cache_path.parent.exists():

Diff for: core/config.py

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ConfigManager:
3939
"thread_cooldown": isodate.Duration(),
4040
"reply_without_command": False,
4141
"anon_reply_without_command": False,
42+
"plain_reply_without_command": False,
4243
# logging
4344
"log_channel_id": None,
4445
# threads
@@ -130,6 +131,7 @@ class ConfigManager:
130131
"mod_typing",
131132
"reply_without_command",
132133
"anon_reply_without_command",
134+
"plain_reply_without_command",
133135
"recipient_thread_close",
134136
"thread_auto_close_silently",
135137
"thread_move_notify",

Diff for: core/config_help.json

+13-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
"`{prefix}config set reply_without_command no`"
140140
],
141141
"notes": [
142-
"See also: `anon_reply_without_command`."
142+
"See also: `anon_reply_without_command`, `plain_reply_without_command`."
143143
]
144144
},
145145
"anon_reply_without_command": {
@@ -150,7 +150,18 @@
150150
"`{prefix}config set anon_reply_without_command no`"
151151
],
152152
"notes": [
153-
"See also: `reply_without_command`."
153+
"See also: `reply_without_command`, `plain_reply_without_command`."
154+
]
155+
},
156+
"plain_reply_without_command": {
157+
"default": "Disabled",
158+
"description": "Setting this configuration will make all non-command messages sent in the thread channel to be forwarded to the recipient in a plain form without the need of `{prefix}reply`.",
159+
"examples": [
160+
"`{prefix}config set plain_reply_without_command yes`",
161+
"`{prefix}config set plain_reply_without_command no`"
162+
],
163+
"notes": [
164+
"See also: `reply_without_command`, `anon_reply_without_command`."
154165
]
155166
},
156167
"log_channel_id": {

Diff for: core/thread.py

+36-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import io
23
import re
34
import typing
45
from datetime import datetime, timedelta
@@ -619,7 +620,9 @@ async def note(self, message: discord.Message) -> None:
619620

620621
return msg
621622

622-
async def reply(self, message: discord.Message, anonymous: bool = False) -> None:
623+
async def reply(
624+
self, message: discord.Message, anonymous: bool = False, plain: bool = False
625+
) -> None:
623626
if not message.content and not message.attachments:
624627
raise MissingRequiredArgument(SimpleNamespace(name="msg"))
625628
if not any(g.get_member(self.id) for g in self.bot.guilds):
@@ -635,7 +638,11 @@ async def reply(self, message: discord.Message, anonymous: bool = False) -> None
635638

636639
try:
637640
await self.send(
638-
message, destination=self.recipient, from_mod=True, anonymous=anonymous
641+
message,
642+
destination=self.recipient,
643+
from_mod=True,
644+
anonymous=anonymous,
645+
plain=plain,
639646
)
640647
except Exception:
641648
logger.error("Message delivery failed:", exc_info=True)
@@ -653,7 +660,7 @@ async def reply(self, message: discord.Message, anonymous: bool = False) -> None
653660
else:
654661
# Send the same thing in the thread channel.
655662
msg = await self.send(
656-
message, destination=self.channel, from_mod=True, anonymous=anonymous
663+
message, destination=self.channel, from_mod=True, anonymous=anonymous, plain=plain
657664
)
658665

659666
tasks.append(
@@ -688,6 +695,7 @@ async def send(
688695
from_mod: bool = False,
689696
note: bool = False,
690697
anonymous: bool = False,
698+
plain: bool = False,
691699
) -> None:
692700

693701
self.bot.loop.create_task(
@@ -851,7 +859,31 @@ async def send(
851859
else:
852860
mentions = None
853861

854-
msg = await destination.send(mentions, embed=embed)
862+
if plain:
863+
if from_mod and not isinstance(destination, discord.TextChannel):
864+
# Plain to user
865+
if embed.footer.text:
866+
plain_message = f"**({embed.footer.text}) "
867+
else:
868+
plain_message = "**"
869+
plain_message += f"{embed.author.name}:** {embed.description}"
870+
files = []
871+
for i in embed.fields:
872+
if "Image" in i.name:
873+
async with self.bot.session.get(
874+
i.field[i.field.find("http") : -1]
875+
) as resp:
876+
stream = io.BytesIO(await resp.read())
877+
files.append(discord.File(stream))
878+
879+
msg = await destination.send(plain_message, files=files)
880+
else:
881+
# Plain to mods
882+
embed.set_footer(text="[PLAIN] " + embed.footer.text)
883+
msg = await destination.send(mentions, embed=embed)
884+
885+
else:
886+
msg = await destination.send(mentions, embed=embed)
855887

856888
if additional_images:
857889
self.ready = False

0 commit comments

Comments
 (0)