From 89afed5793221d8d55792c50f8dd30b28db4885d Mon Sep 17 00:00:00 2001
From: Taku <45324516+Taaku18@users.noreply.github.com>
Date: Thu, 16 Jan 2025 08:28:36 +0000
Subject: [PATCH 1/7] Update README.md
Signed-off-by: Taku <45324516+Taaku18@users.noreply.github.com>
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 980066ca7e..500978a1f9 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
@@ -24,7 +24,7 @@
-
+
From 359c00df65c8aad23aebf9d9870816f502297c5b Mon Sep 17 00:00:00 2001
From: Martin
Date: Fri, 17 Jan 2025 00:00:16 +0100
Subject: [PATCH 2/7] Check for local git repository
Implements a checking mechanismen for local git repository.
---
bot.py | 33 +++++++++++++++++++++++++++++++++
cogs/utility.py | 8 ++++++++
2 files changed, 41 insertions(+)
diff --git a/bot.py b/bot.py
index 3c6ebe7911..9cd61bc269 100644
--- a/bot.py
+++ b/bot.py
@@ -758,6 +758,33 @@ def check_manual_blocked(self, author: discord.Member) -> bool:
logger.debug("User blocked, user %s.", author.name)
return False
+ def check_local_git(self) -> bool:
+ """
+ Checks if the bot is installed via git.
+ """
+ valid_local_git = False
+ git_folder_path = os.path.join(".git")
+
+ # Check if the .git folder exists and is a directory
+ if os.path.exists(git_folder_path) and os.path.isdir(git_folder_path):
+ required_files = ["config", "HEAD"]
+ required_dirs = ["refs", "objects"]
+
+ # Verify required files exist
+ for file in required_files:
+ if not os.path.isfile(os.path.join(git_folder_path, file)):
+ return valid_local_git
+
+ # Verify required directories exist
+ for directory in required_dirs:
+ if not os.path.isdir(os.path.join(git_folder_path, directory)):
+ return valid_local_git
+
+ # If all checks pass, set valid_local_git to True
+ valid_local_git = True
+
+ return valid_local_git
+
async def _process_blocked(self, message):
_, blocked_emoji = await self.retrieve_emoji()
if await self.is_blocked(message.author, channel=message.channel, send_message=True):
@@ -1721,6 +1748,12 @@ async def before_autoupdate(self):
self.autoupdate.cancel()
return
+ if not self.check_local_git():
+ logger.warning("Bot not installed via git.")
+ logger.warning("Autoupdates disabled.")
+ self.autoupdate.cancel()
+ return
+
@tasks.loop(hours=1, reconnect=False)
async def log_expiry(self):
log_expire_after = self.config.get("log_expiration")
diff --git a/cogs/utility.py b/cogs/utility.py
index 31cb065a28..19147b9134 100644
--- a/cogs/utility.py
+++ b/cogs/utility.py
@@ -2003,6 +2003,14 @@ async def update(self, ctx, *, flag: str = ""):
embed.set_author(name=user["username"], icon_url=user["avatar_url"], url=user["url"])
await ctx.send(embed=embed)
else:
+ if self.bot.check_local_git() is False:
+ embed = discord.Embed(
+ title="Update Command Unavailable",
+ description="The bot cannot be updated due to not being installed via a git."
+ "You need to manually update the bot according to your hosting method.",
+ color=discord.Color.red(),
+ )
+ return await ctx.send(embed=embed)
command = "git pull"
proc = await asyncio.create_subprocess_shell(
command,
From 902082e68b833e97312101fb236bc7563e26f522 Mon Sep 17 00:00:00 2001
From: Martin
Date: Fri, 17 Jan 2025 00:10:45 +0100
Subject: [PATCH 3/7] Fix typo in update command.
---
cogs/utility.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cogs/utility.py b/cogs/utility.py
index 19147b9134..78695bdd57 100644
--- a/cogs/utility.py
+++ b/cogs/utility.py
@@ -2006,7 +2006,7 @@ async def update(self, ctx, *, flag: str = ""):
if self.bot.check_local_git() is False:
embed = discord.Embed(
title="Update Command Unavailable",
- description="The bot cannot be updated due to not being installed via a git."
+ description="The bot cannot be updated due to not being installed via git."
"You need to manually update the bot according to your hosting method.",
color=discord.Color.red(),
)
From e798fb5941e1418839f7805c8851d97913cb06b1 Mon Sep 17 00:00:00 2001
From: Martin
Date: Sat, 8 Feb 2025 21:28:53 +0100
Subject: [PATCH 4/7] Fix incorrect config help
This fixes some incorrect help text of the thread_close_response and thread_self_close_response
---
core/config_help.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/config_help.json b/core/config_help.json
index d301763fe4..ad5241c09f 100644
--- a/core/config_help.json
+++ b/core/config_help.json
@@ -521,7 +521,7 @@
"notes": [
"When `recipient_thread_close` is enabled and the recipient closed their own thread, `thread_self_close_response` is used instead of this configuration.",
"You may use the `{{closer}}` variable for access to the [Member](https://discordpy.readthedocs.io/en/latest/api.html#discord.Member) that closed the thread.",
- "`{{loglink}}` can be used as a placeholder substitute for the full URL linked to the thread in the log viewer and `{{loglink}}` for the unique key (ie. s3kf91a) of the log.",
+ "`{{loglink}}` can be used as a placeholder substitute for the full URL linked to the thread in the log viewer and `{{logkey}}` for the unique key (ie. s3kf91a) of the log.",
"Discord flavoured markdown is fully supported in `thread_close_response`.",
"See also: `thread_close_title`, `thread_close_footer`, `thread_self_close_response`, `thread_creation_response`."
]
@@ -535,7 +535,7 @@
"notes": [
"When `recipient_thread_close` is disabled or the thread wasn't closed by the recipient, `thread_close_response` is used instead of this configuration.",
"You may use the `{{closer}}` variable for access to the [Member](https://discordpy.readthedocs.io/en/latest/api.html#discord.Member) that closed the thread.",
- "`{{loglink}}` can be used as a placeholder substitute for the full URL linked to the thread in the log viewer and `{{loglink}}` for the unique key (ie. s3kf91a) of the log.",
+ "`{{loglink}}` can be used as a placeholder substitute for the full URL linked to the thread in the log viewer and `{{logkey}}` for the unique key (ie. s3kf91a) of the log.",
"Discord flavoured markdown is fully supported in `thread_self_close_response`.",
"See also: `thread_close_title`, `thread_close_footer`, `thread_close_response`."
]
From 3ebb21e4116e753ae727319cdf37ca6cef5ae5e2 Mon Sep 17 00:00:00 2001
From: Martin
Date: Thu, 27 Feb 2025 18:23:17 +0100
Subject: [PATCH 5/7] recipient_thread_close via Buttons
- Updates the recipient_thread_close feature to use a button instead of a reaction.
- New config options: recipient_thread_close_button_label, recipient_thread_close_button_style
- The close_emoji default value is now None to make it possible changing the button to only have a label.
---
bot.py | 23 ++++++++++-------------
core/config.py | 9 ++++++---
core/config_help.json | 33 ++++++++++++++++++++++++++++-----
core/thread.py | 31 +++++++++++++++++++++++++++----
core/utils.py | 36 ++++++++++++++++++++++++++++++++++++
5 files changed, 107 insertions(+), 25 deletions(-)
diff --git a/bot.py b/bot.py
index 9cd61bc269..6d5fe46ab1 100644
--- a/bot.py
+++ b/bot.py
@@ -48,7 +48,15 @@
)
from core.thread import ThreadManager
from core.time import human_timedelta
-from core.utils import extract_block_timestamp, normalize_alias, parse_alias, truncate, tryint, human_join
+from core.utils import (
+ extract_block_timestamp,
+ normalize_alias,
+ parse_alias,
+ truncate,
+ tryint,
+ human_join,
+ ThreadSelfCloseView,
+)
logger = getLogger(__name__)
@@ -617,6 +625,7 @@ async def on_ready(self):
self.post_metadata.start()
self.autoupdate.start()
self.log_expiry.start()
+ self.add_view(ThreadSelfCloseView(self))
self._started = True
async def convert_emoji(self, name: str) -> str:
@@ -1275,19 +1284,7 @@ async def handle_reaction_events(self, payload):
return
reaction = payload.emoji
- close_emoji = await self.convert_emoji(self.config["close_emoji"])
if from_dm:
- if (
- payload.event_type == "REACTION_ADD"
- and message.embeds
- and str(reaction) == str(close_emoji)
- and self.config.get("recipient_thread_close")
- ):
- ts = message.embeds[0].timestamp
- if ts == thread.channel.created_at:
- # the reacted message is the corresponding thread creation embed
- # closing thread
- return await thread.close(closer=user)
if (
message.author == self.user
and message.embeds
diff --git a/core/config.py b/core/config.py
index 5c6b0dd09d..c33deb21be 100644
--- a/core/config.py
+++ b/core/config.py
@@ -49,12 +49,14 @@ class ConfigManager:
# threads
"sent_emoji": "\N{WHITE HEAVY CHECK MARK}",
"blocked_emoji": "\N{NO ENTRY SIGN}",
- "close_emoji": "\N{LOCK}",
+ "close_emoji": None,
"use_user_id_channel_name": False,
"use_timestamp_channel_name": False,
"use_nickname_channel_name": False,
"use_random_channel_name": False,
"recipient_thread_close": False,
+ "recipient_thread_close_button_label": None,
+ "recipient_thread_close_button_style": "red",
"thread_show_roles": True,
"thread_show_account_age": True,
"thread_show_join_age": True,
@@ -65,7 +67,7 @@ class ConfigManager:
"thread_creation_response": "The staff team will get back to you as soon as possible.",
"thread_creation_footer": "Your message has been sent",
"thread_contact_silently": False,
- "thread_self_closable_creation_footer": "Click the lock to close the thread",
+ "thread_self_closable_creation_footer": "Click the button to close the thread",
"thread_creation_contact_title": "New Thread",
"thread_creation_self_contact_response": "You have opened a Modmail thread.",
"thread_creation_contact_response": "{creator.name} has opened a Modmail thread.",
@@ -233,7 +235,8 @@ class ConfigManager:
enums = {
"dm_disabled": DMDisabled,
"status": discord.Status,
- "activity_type": discord.ActivityType,
+ "activity_type": discord.ActivityType
+ #"recipient_thread_close_button_style": discord.ButtonStyle
}
force_str = {"command_permissions", "level_permissions"}
diff --git a/core/config_help.json b/core/config_help.json
index ad5241c09f..144c54e9f1 100644
--- a/core/config_help.json
+++ b/core/config_help.json
@@ -285,8 +285,8 @@
]
},
"close_emoji": {
- "default": "🔒",
- "description": "This is the emoji the recipient can click to close a thread themselves. The emoji is automatically added to the `thread_creation_response` embed.",
+ "default": "None",
+ "description": "This is the emoji for the close button the recipient can click to close a thread themselves. The emoji (attached to the button) is automatically added to the `thread_creation_response` embed.",
"examples": [
"`{prefix}config set close_emoji 👍`"
],
@@ -297,14 +297,37 @@
},
"recipient_thread_close": {
"default": "Disabled",
- "description": "Setting this configuration will allow recipients to use the `close_emoji` to close the thread themselves.",
+ "description": "Setting this configuration will allow recipients to close threads by themselves via a button.",
"examples": [
"`{prefix}config set recipient_thread_close yes`",
"`{prefix}config set recipient_thread_close no`"
],
"notes": [
- "The close emoji is dictated by the configuration `close_emoji`.",
- "See also: `close_emoji`."
+ "The button attached to the `thread_creation_response` can have set a custom label, emoji or even both.",
+ "See also: `close_emoji`, `recipient_thread_close_button_label`, `recipient_thread_close_button_style`."
+ ]
+ },
+ "recipient_thread_close_button_label": {
+ "default": "None",
+ "description": "This configuration changes the label of the button for the `recipient_thread_close` feature.",
+ "examples": [
+ "`{prefix}config set recipient_thread_close_button_label Your label`"
+ ],
+ "notes": [
+ "The label cannot exceed 80 characters.",
+ "See also: `recipient_thread_close`, `close_emoji`, `recipient_thread_close_button_style`."
+ ]
+ },
+ "recipient_thread_close_button_style": {
+ "default": "red",
+ "description": "This configuration changes the style of the button for the `recipient_thread_close` feature.",
+ "examples": [
+ "`{prefix}config set recipient_thread_close_button_style green`",
+ "`{prefix}config set recipient_thread_close_button_style blurple`"
+ ],
+ "notes": [
+ "The style is limited by discord to the following colors: blurple, green, red, gray.",
+ "See also: `recipient_thread_close`, `close_emoji`, `recipient_thread_close_button_label`."
]
},
"thread_show_roles": {
diff --git a/core/thread.py b/core/thread.py
index 81dc03f44d..5cd95a68a7 100644
--- a/core/thread.py
+++ b/core/thread.py
@@ -32,6 +32,7 @@
AcceptButton,
DenyButton,
ConfirmThreadCreationView,
+ ThreadSelfCloseView,
DummyParam,
)
@@ -239,11 +240,33 @@ async def send_recipient_genesis_message():
if creator is None or creator == recipient:
msg = await recipient.send(embed=embed)
-
- if recipient_thread_close:
+ close_emoji = self.bot.config["close_emoji"]
+ close_label = self.bot.config["recipient_thread_close_button_label"]
+ if (
+ recipient_thread_close
+ and self.bot.config["recipient_thread_close_button_style"].lower()
+ in ["red", "green", "blurple", "gray"]
+ and (close_emoji is not None and close_label is not None)
+ ):
close_emoji = self.bot.config["close_emoji"]
- close_emoji = await self.bot.convert_emoji(close_emoji)
- await self.bot.add_reaction(msg, close_emoji)
+ if close_emoji:
+ close_emoji = await self.bot.convert_emoji(close_emoji)
+
+ button_style = discord.ButtonStyle(
+ int(
+ discord.ButtonStyle[
+ self.bot.config["recipient_thread_close_button_style"].lower()
+ ]
+ )
+ )
+ view = ThreadSelfCloseView(
+ button_style,
+ close_label,
+ close_emoji,
+ self.bot,
+ )
+ await msg.edit(view=view)
+ # await self.bot.add_reaction(msg, close_emoji)
async def send_persistent_notes():
notes = await self.bot.api.find_notes(self.recipient)
diff --git a/core/utils.py b/core/utils.py
index 9f9f572f5a..2ca68be346 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -42,6 +42,7 @@
"AcceptButton",
"DenyButton",
"ConfirmThreadCreationView",
+ "ThreadSelfCloseView",
"DummyParam",
]
@@ -599,6 +600,41 @@ def __init__(self):
self.value = None
+class ThreadSelfCloseView(discord.ui.View):
+ def __init__(
+ self,
+ style: typing.Optional[discord.ButtonStyle] = None,
+ label: typing.Optional[str] = None,
+ emoji: typing.Optional[str] = None,
+ bot: typing.Any = None,
+ ):
+ super().__init__(timeout=None)
+ self.bot = bot
+
+ self.self_close_button.label = label
+ self.self_close_button.style = style
+ self.self_close_button.emoji = emoji
+
+ @discord.ui.button(label="default", style=discord.ButtonStyle.secondary, custom_id="SelfCloseView")
+ async def self_close_button(self, interaction: discord.Interaction, button: discord.ui.Button):
+ await interaction.response.defer(ephemeral=False, thinking=True)
+ thread = await self.bot.threads.find(recipient=interaction.user)
+ if not thread:
+ error_embed = discord.Embed(
+ description="A thread could not be found.", color=self.bot.config["error_color"]
+ )
+ return await interaction.followup.send(embed=error_embed)
+
+ message = self.bot.config["thread_self_close_response"]
+ embed = discord.Embed(
+ title="Thread closed", description=message, color=self.bot.config["error_color"]
+ )
+ await thread.close(closer=interaction.user, silent=True)
+ self.self_close_button.disabled = True
+ await interaction.message.edit(view=self)
+ await interaction.followup.send(embed=embed)
+
+
class DummyParam:
"""
A dummy parameter that can be used for MissingRequiredArgument.
From e3af5fa8a06562fd6051934e88cbad30d4c144c3 Mon Sep 17 00:00:00 2001
From: Martin
Date: Thu, 27 Feb 2025 18:28:17 +0100
Subject: [PATCH 6/7] Fix config.py black formatting
---
core/config.py | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/core/config.py b/core/config.py
index c33deb21be..85b6b05fb5 100644
--- a/core/config.py
+++ b/core/config.py
@@ -232,12 +232,7 @@ class ConfigManager:
"registry_plugins_only",
}
- enums = {
- "dm_disabled": DMDisabled,
- "status": discord.Status,
- "activity_type": discord.ActivityType
- #"recipient_thread_close_button_style": discord.ButtonStyle
- }
+ enums = {"dm_disabled": DMDisabled, "status": discord.Status, "activity_type": discord.ActivityType}
force_str = {"command_permissions", "level_permissions"}
From 58954a43839741e9d8a39b25b9823e3a10bc6fc0 Mon Sep 17 00:00:00 2001
From: Martin
Date: Thu, 27 Feb 2025 18:30:02 +0100
Subject: [PATCH 7/7] Merge pull request #4 from
martinbndr/recipient_thread_close_button
recipient_thread_close via Buttons