diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..8f3f71ad17 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Read(//e/Users/TammyShepherd/Documents/Modmail/core/**)", + "Read(//e/Users/TammyShepherd/Documents/Modmail/**)" + ], + "deny": [], + "ask": [] + } +} diff --git a/.gitignore b/.gitignore index 635e3160e8..e165a06432 100644 --- a/.gitignore +++ b/.gitignore @@ -1,93 +1,29 @@ +#### Modmail +# Plugins +plugins/* +!plugins/registry.json +!plugins/@local + +# Config files +config.json +.env + +# Data +temp/ + +### Python # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - # Installer logs pip-log.txt pip-delete-this-directory.txt -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - # pyenv .python-version -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - # Environments .env .venv @@ -98,43 +34,62 @@ ENV/ env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject +#### PyCharm +.idea/ -# mkdocs documentation -/site +#### VS Code +.vscode/ -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json +### Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db -# Pyre type checker -.pyre/ +# Dump file +*.stackdump -# PyCharm -.idea/ +# Folder config file +[Dd]esktop.ini -# MacOS -.DS_Store +# Recycle Bin used on file shares +$RECYCLE.BIN/ -# VS Code -.vscode/ +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp -# Node -package-lock.json -node_modules/ +# Windows shortcuts +*.lnk -# Modmail -plugins/* -!plugins/registry.json -!plugins/@local -config.json -temp/ -test.py -stack.yml -.github/workflows/build_docker.yml \ No newline at end of file +### macOS +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/core/clients.py b/core/clients.py index 90f09b3b48..b92514a947 100644 --- a/core/clients.py +++ b/core/clients.py @@ -449,8 +449,11 @@ def __init__(self, bot): logger.critical("A Mongo URI is necessary for the bot to function.") raise RuntimeError + # Get database name from config, default to "modmail_bot" + db_name = bot.config.get("db_name", convert=False) or "modmail_bot" + try: - db = AsyncIOMotorClient(mongo_uri).modmail_bot + db = AsyncIOMotorClient(mongo_uri)[db_name] except ConfigurationError as e: logger.critical( "Your MongoDB CONNECTION_URI might be copied wrong, try re-copying from the source again. " @@ -497,6 +500,7 @@ async def validate_database_connection(self, *, ssl_retry=True): mongo_uri = self.bot.config["connection_uri"] if mongo_uri is None: mongo_uri = self.bot.config["mongo_uri"] + db_name = self.bot.config.get("db_name", convert=False) or "modmail_bot" for _ in range(3): logger.warning( "FAILED TO VERIFY SSL CERTIFICATE, ATTEMPTING TO START WITHOUT SSL (UNSAFE)." @@ -506,7 +510,7 @@ async def validate_database_connection(self, *, ssl_retry=True): 'run "Certificate.command" on MacOS, ' 'and check certifi is up to date "pip3 install --upgrade certifi".' ) - self.db = AsyncIOMotorClient(mongo_uri, tlsAllowInvalidCertificates=True).modmail_bot + self.db = AsyncIOMotorClient(mongo_uri, tlsAllowInvalidCertificates=True)[db_name] return await self.validate_database_connection(ssl_retry=False) if "ServerSelectionTimeoutError" in message: logger.critical( diff --git a/core/config.py b/core/config.py index 0e45b00175..f4af799e6d 100644 --- a/core/config.py +++ b/core/config.py @@ -97,6 +97,7 @@ class ConfigManager: "plain_snippets": False, "require_close_reason": False, "show_log_url_button": False, + "public_ticket_logs_send_on_close": False, # group conversations "private_added_to_group_title": "New Thread (Group)", "private_added_to_group_response": "{moderator.name} has added you to a Modmail thread.", @@ -204,6 +205,7 @@ class ConfigManager: "mongo_uri": None, "database_type": "mongodb", "connection_uri": None, # replace mongo uri in the future + "db_name": "modmail_bot", # configurable database name "owners": None, "enable_presence_intent": False, "registry_plugins_only": False, @@ -274,6 +276,7 @@ class ConfigManager: "anonymous_snippets", "plain_snippets", "require_close_reason", + "public_ticket_logs_send_on_close", "recipient_thread_close", "thread_show_roles", "thread_show_account_age", diff --git a/core/config_help.json b/core/config_help.json index b5832935c7..4c1dbfdc10 100644 --- a/core/config_help.json +++ b/core/config_help.json @@ -872,6 +872,19 @@ ], "notes": [] }, + "public_ticket_logs_send_on_close": { + "default": "No", + "description": "When enabled, automatically appends a link to the log/transcript at the end of the thread close message sent to recipients.", + "examples": [ + "`{prefix}config set public_ticket_logs_send_on_close yes`", + "`{prefix}config set public_ticket_logs_send_on_close no`" + ], + "notes": [ + "This adds a \"View a transcript of this communication [here](log_url)\" link to the close message.", + "The log URL must be properly configured for this to work.", + "See also: `thread_close_response`, `thread_self_close_response`, `log_url`." + ] + }, "private_added_to_group_title": { "default": "New Thread (Group)", "description": "This is the message embed title sent to the recipient that is just added to a thread.", @@ -1125,6 +1138,17 @@ "This configuration can only to be set through `.env` file or environment (config) variables." ] }, + "db_name": { + "default": "modmail_bot", + "description": "The name of the MongoDB database to use. This allows you to customize the database name instead of using the default 'modmail_bot'.", + "examples": [ + ], + "notes": [ + "This configuration can only to be set through `.env` file or environment (config) variables.", + "If not specified, defaults to 'modmail_bot'.", + "Useful for running multiple instances or custom database naming schemes." + ] + }, "owners": { "default": "None, required", "description": "A list of definite bot owners, use `{prefix}perms add level OWNER @user` to set flexible bot owners.", diff --git a/core/thread.py b/core/thread.py index bf77180f8c..b9c3bde107 100644 --- a/core/thread.py +++ b/core/thread.py @@ -1200,6 +1200,10 @@ async def _close(self, closer, silent=False, delete_channel=True, message=None, logkey=log_data["key"] if log_data else None, ) + # Append log URL if config is enabled + if self.bot.config.get("public_ticket_logs_send_on_close") and log_url: + message += f" View a transcript of this communication [here]({log_url})." + embed.description = message footer = self.bot.config["thread_close_footer"] embed.set_footer(