Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
21f4766
Merge development v4.2.0 (#3390)
Taaku18 Nov 11, 2025
c11e32f
Update readme version
Taaku18 Nov 11, 2025
1517693
Relock pipfile
Taaku18 Nov 11, 2025
98cb126
Bump aiohttp and motor deps, fixed pipenv dependency errors
Taaku18 Nov 11, 2025
829a364
fix: Remove thread-only restriction from logs command (#3397)
lorenzo132 Nov 20, 2025
124669d
modmail update QOL (#3392)
lorenzo132 Nov 28, 2025
17dd885
Fix GH Workflow with black Formatting
Taaku18 Nov 28, 2025
bcd1193
Add new sponsor to json
Taaku18 Nov 28, 2025
b0d9f23
Merge branch 'development'
Taaku18 Nov 30, 2025
665e67e
Update version to v4.2.1
Taaku18 Nov 30, 2025
c26af97
Feat: Renaming of snippets and aliases (#3383)
martinbndr Nov 30, 2025
a7ff289
Fixes config_help notes (#3407)
martinbndr Dec 5, 2025
6e75c44
Git Repository check for bot update (#3406)
martinbndr Dec 5, 2025
cfedecb
Fix Plugin Help (#3322)
martinbndr Dec 7, 2025
d2b5042
Added submenu functionality to threadmenu. Fixes #3403 (#3404)
sebkuip Dec 8, 2025
6e094af
Fix: Blackformatting, workflows. Add snooze/unsnooze events. (#3412)
lorenzo132 Dec 8, 2025
3c3608f
Add threadmenu toggle notice (#3411)
martinbndr Dec 13, 2025
ab458f5
Improvements for alias creation/editing (#3422)
martinbndr Dec 20, 2025
ad801b1
Fixes thread_auto_close execution when disabled. (#3423)
martinbndr Dec 20, 2025
040195d
fix: plain replies deletion and edits (#3416)
lorenzo132 Jan 16, 2026
0df325d
Update gitignore
Taaku18 Dec 10, 2025
da6dff2
feat: bulk override perms for commands
lorenzo132 Dec 13, 2025
3ea1b5e
fix linting
lorenzo132 Dec 13, 2025
2f0c585
change reflection upon review
lorenzo132 Dec 14, 2025
df15ad8
linting
lorenzo132 Dec 14, 2025
9be3c4b
resolve review.
lorenzo132 Dec 18, 2025
ef8a5c2
fix: strip mention prefixes - show valid commands before apply.
lorenzo132 Dec 18, 2025
7758dd2
Implement requested change.
lorenzo132 Dec 20, 2025
72d2b98
improve invalid commmand proccesing.
lorenzo132 Feb 2, 2026
dec20f8
logging: bulk override
lorenzo132 Feb 4, 2026
cad5122
fix: show forwarded message in logviewer.
lorenzo132 Dec 5, 2025
74a2969
fix: show forwarded message in logviewer.
lorenzo132 Dec 5, 2025
47e30c5
fix: show forwarded message in logviewer.
lorenzo132 Dec 5, 2025
db9b81a
black formatting
lorenzo132 Dec 5, 2025
8b4dfcb
remove jump_url
lorenzo132 Dec 18, 2025
d9c7d15
feat: adds attachment support to forwarded content.
lorenzo132 Dec 18, 2025
58bb1ee
black formatting
lorenzo132 Dec 18, 2025
64e33f4
use: hassattr(), remove dedunant package
lorenzo132 Dec 27, 2025
d8e91c0
rebase import.
lorenzo132 Feb 2, 2026
c31c024
fix: linting
lorenzo132 Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ LOG_URL=https://logviewername.herokuapp.com/
GUILD_ID=1234567890
OWNERS=Owner1ID,Owner2ID,Owner3ID
CONNECTION_URI=mongodb+srv://mongodburi
DISABLE_AUTOUPDATES=true
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
patreon: kyber
buy_me_a_coffee: modmaildev
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ body:
- Heroku
- Systemd
- PM2
- Patreon
- Buy Me A Coffee / Patreon
- Other
validations:
required: true
Expand Down
175 changes: 65 additions & 110 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
### 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
97 changes: 97 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,103 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
This project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
however, insignificant breaking changes do not guarantee a major version bump, see the reasoning [here](https://github.com/modmail-dev/modmail/issues/319). If you're a plugin developer, note the "BREAKING" section.

# v4.2.1

### Added

**New Configuration Options:**
* `unsnooze_history_limit`: Limits the number of messages replayed when unsnoozing (genesis message and notes are always shown).
* `snooze_behavior`: Choose between `delete` (legacy) or `move` behavior for snoozing.
* `snoozed_category_id`: Target category for `move` snoozing; required when `snooze_behavior` is `move`.
* `snooze_store_attachments`: When enabled, image attachments are stored as base64 when snoozing with delete behavior, allowing them to be re-uploaded on unsnooze.
* `snooze_attachment_max_bytes`: Maximum size per attachment to store as base64 (default: 4 MiB).
* `thread_creation_menu_timeout`: Timeout duration for user interaction with the menu (default: 30 seconds).
* `thread_creation_menu_close_on_timeout`: Silently abort thread creation if user doesn't select an option.
* `thread_creation_menu_anonymous_menu`: Anonymize the initial menu prompt relayed to staff.
* `thread_creation_menu_embed_text`: Text shown in the embed above the selection dropdown.
* `thread_creation_menu_dropdown_placeholder`: Placeholder text in the dropdown before selection.
* `thread_creation_menu_selection_log`: Log the chosen menu option in the newly created thread channel.
* `thread_creation_menu_precreate_channel`: Create thread channel immediately upon first DM even if menu is enabled.
* `thread_creation_menu_embed_title`: Optional title for the thread-creation menu embed.
* `thread_creation_menu_embed_footer`: Optional footer text for the menu embed.
* `thread_creation_menu_embed_footer_icon_url`: Optional URL for the footer icon.
* `thread_creation_menu_embed_thumbnail_url`: Optional thumbnail image URL.
* `thread_creation_menu_embed_image_url`: Optional large hero image URL for the menu embed.
* `thread_creation_menu_embed_large_image`: Promote thumbnail to large hero image if no separate image URL is set.
* `thread_creation_menu_embed_color`: Color for the menu embed's side strip.

**Thread-Creation Menu Feature:**
* Full thread-creation menu system with interactive select menus:
* `?threadmenu toggle`: Enable/disable the menu globally.
* `?threadmenu show`: List current top-level options.
* `?threadmenu option add`: Interactive wizard to create an option.
* `?threadmenu option edit/remove/show`: Manage or inspect existing options.
* `?threadmenu submenu create/delete/list/show`: Manage submenus (nested menu levels).
* `?threadmenu submenu option add/edit/remove`: Manage options inside submenus.
* `?threadmenu dump_config`: Export current configuration to a file.
* `?threadmenu load_config`: Import configuration from a file.
* `?threadmenu reset`: Reset all thread-creation menu settings to defaults.
* Per-option category targeting: Each menu option can specify a target category where threads are created.
* Submenu support: Create up to 25 main-level options, each with up to 24 nested options.
* Optional selection logging: Log which menu option was chosen in the newly created thread channel.
* Anonymous menu support: Hide original prompt author context from staff when menu is anonymized.
* Category fallback: If an option's category is invalid/missing, creation falls back to `main_category_id`.

**Snooze Enhancements:**
* Attachment persistence for delete-behavior snoozing: Image attachments can now be stored as base64 data.
* Enhanced unsnooze functionality with configurable message replay limits.
* Auto-unsnooze task continuously monitors and automatically unsnoozes threads when duration expires.

### Changed
- Renamed `max_snooze_time` to `snooze_default_duration` (accepts seconds or human-readable time like "7 days").
- When `snooze_behavior` is set to `move`, the snoozed category now has a hard limit of 49 channels. New snoozes are blocked once it’s full until space is freed.
- When switching `snooze_behavior` to `move` via `?config set`, the bot reminds admins to set `snoozed_category_id` if it’s missing.
- Thread-creation menu options and submenu options now support per-option `category` targeting.
- Category selection in menu option wizards allows specifying ID, name, or mention format.
- Snoozed thread restoration now respects `unsnooze_history_limit` (if set) to replay only the last N messages.
- Enhanced auto-unsnooze task monitors and automatically unsnoozes threads when their snooze duration expires.
- Snoozed threads can now be moved to a dedicated category instead of being deleted (via `snooze_behavior: move`).

### Fixed

- Corrected behavior when snooze channel count reaches the 49-channel limit in move-based snoozing.
- Improved category resolution in threadmenu wizards (handles ID, name, and mention formats reliably).
- Enhanced thread state restoration after unsnoozing to properly re-add all recipients.


# v4.2.0

Upgraded discord.py to version 2.6.3, added support for CV2.
Forwarded messages now properly show in threads, rather than showing as an empty embed.

### Fixed
- Make Modmail keep working when typing is disabled due to an outage caused by Discord.
- Resolved an issue where forwarded messages appeared as empty embeds.
- Fixed internal message handling and restoration processes.
- Eliminated duplicate logs and notes.
- Addressed inconsistent use of `logkey` after ticket restoration.
- Fixed issues with identifying the user who sent internal messages.
- Solved an ancient bug where closing with words like `evening` wouldn't work.
- Fixed the command from being included in the reply in rare conditions.

### Added
Commands:
* `snooze`: Initiates a snooze action.
* `snoozed`: Displays snoozed items.
* `unsnooze`: Reverses the snooze action.
* `clearsnoozed`: Clears all snoozed items.

Configuration Options:
* `max_snooze_time`: Sets the maximum duration for snooze.
* `snooze_title`: Customizes the title for snooze notifications.
* `snooze_text`: Customizes the text for snooze notifications.
* `unsnooze_text`: Customizes the text for unsnooze notifications.
* `unsnooze_notify_channel`: Specifies the channel for unsnooze notifications.
* `thread_min_characters`: Minimum number of characters required.
* `thread_min_characters_title`: Title shown when the message is too short.
* `thread_min_characters_response`: Response shown to the user if their message is too short.
* `thread_min_characters_footer`: Footer displaying the minimum required characters.

# v4.1.2

### Fixed
Expand Down
21 changes: 13 additions & 8 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,29 @@ verify_ssl = true
bandit = ">=1.7.5"
black = "==23.11.0"
pylint = "==3.0.2"
typing-extensions = "==4.8.0"
tomli = "==2.2.1"

[packages]
aiohttp = "==3.9.0"
aiohttp = "==3.13.2"
async-timeout = {version = "==5.0.1", markers = "python_version < '3.11'"}
typing-extensions = "==4.15.0"
colorama = "==0.4.6"
"discord.py" = {version = "==2.3.2", extras = ["speed"]}
"discord.py" = {version = "==2.6.3", extras = ["speed"]}
emoji = "==2.8.0"
isodate = "==0.6.1"
motor = "==3.3.2"
natural = "==0.2.0" # Why is this needed?
motor = "==3.7.1"
natural = "==0.2.0"
packaging = "==23.2"
parsedatetime = "==2.6"
pymongo = {extras = ["srv"], version = "*"} # Required by motor
dnspython = "==2.8.0"
pymongo = "==4.15.3"
python-dateutil = "==2.8.2"
python-dotenv = "==1.0.0"
uvloop = {version = ">=0.19.0", markers = "sys_platform != 'win32'"}
lottie = {version = "==0.7.0", extras = ["pdf"]}
uvloop = {version = "==0.22.1", markers = "sys_platform != 'win32'"}
lottie = {version = "==0.7.2", extras = ["pdf"]}
setuptools = "==80.9.0"
requests = "==2.31.0"
orjson = "==3.11.4"

[scripts]
bot = "python bot.py"
Loading