Skip to content

feat(new_thread): open and close silently when staff hasn't engaged the user - #406

Merged
Akinator31 merged 2 commits into
mainfrom
400-add-possbility-to-open-a-ticket-silently
Apr 29, 2026
Merged

feat(new_thread): open and close silently when staff hasn't engaged the user#406
Akinator31 merged 2 commits into
mainfrom
400-add-possbility-to-open-a-ticket-silently

Conversation

@Akinator31

@Akinator31 Akinator31 commented Apr 29, 2026

Copy link
Copy Markdown
Member

No description provided.

@Akinator31 Akinator31 linked an issue Apr 29, 2026 that may be closed by this pull request
@Akinator31 Akinator31 changed the title feat(new_thread): open and close silently when staff hasn't engaged t feat(new_thread): open and close silently when staff hasn't engaged the user Apr 29, 2026
@Akinator31
Akinator31 requested a review from Copilot April 29, 2026 18:10
@Akinator31 Akinator31 self-assigned this Apr 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a “silent thread” mode so staff can open a support thread without notifying the user, and ensures close notifications are only sent once staff has actually engaged the user (i.e., successfully delivered a DM reply).

Changes:

  • Add a threads.silent database flag and plumb it through thread creation.
  • Mark threads as “engaged” (clear silent) once a staff reply DM is successfully sent; suppress close DMs while still silent.
  • Update new-thread success messaging in EN/FR to reflect silent opening behavior.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
migrations/20260429120000_thread_silent.sql Adds silent column to threads to persist the silent state.
crates/rustmail/src/db/operations/threads.rs Extends create_thread_for_user with silent, adds is_thread_silent + mark_thread_engaged.
crates/rustmail/src/commands/new_thread/text_command/new_thread.rs Opens threads silently (no DM), sets DB silent flag accordingly.
crates/rustmail/src/commands/new_thread/slash_command/new_thread.rs Opens threads silently (no DM), updates success message key.
crates/rustmail/src/commands/new_thread/common.rs Adds notify_user switch to welcome message handling.
crates/rustmail/src/commands/reply/text_command/reply.rs Clears silent flag after a successful DM reply.
crates/rustmail/src/commands/reply/slash_command/reply.rs Clears silent flag after a successful DM reply.
crates/rustmail/src/commands/anonreply/text_command/anonreply.rs Clears silent flag only when the DM portion succeeds.
crates/rustmail/src/commands/close/text_command/close.rs Suppresses close DM when thread is still silent (including scheduled closures).
crates/rustmail/src/commands/close/slash_command/close.rs Same suppression logic as text close.
crates/rustmail/src/modules/scheduled_closures.rs Avoids sending close DMs when thread remains silent.
crates/rustmail/src/modules/threads.rs Updates thread record creation calls for new silent parameter.
crates/rustmail/src/api/handler/externals/tickets/create.rs Updates thread creation call signature and welcome message call signature.
crates/rustmail/src/i18n/language/en.rs Updates new_thread.success_without_dm text to indicate silent open.
crates/rustmail/src/i18n/language/fr.rs Same as EN update for French.
Comments suppressed due to low confidence (1)

crates/rustmail/src/db/operations/threads.rs:149

  • create_thread_for_user returns res based on inserting into threads, but the subsequent insert into thread_status uses the newly generated thread_id regardless of whether the first insert returned an existing thread ID (unique open-thread constraint). Also, any failure to insert into thread_status is currently ignored, which can leave a thread without a thread_status row and break later operations that expect it. Consider restructuring so you compute the effective thread id first (inserted vs existing), then ensure thread_status exists for that id (propagate errors or handle the expected “already exists” case explicitly).
    let channel_id = channel.id.to_string();
    let thread_id = Uuid::new_v4().to_string();
    let silent_int: i64 = if silent { 1 } else { 0 };

    let res = match sqlx::query(
        "INSERT INTO threads (id, user_id, user_name, channel_id, silent) VALUES (?, ?, ?, ?, ?)",
    )
    .bind(&thread_id)
    .bind(user_id)
    .bind(user_name)
    .bind(&channel_id)
    .bind(silent_int)
    .execute(&pool.clone())
    .await
    {
        Ok(_) => Ok(thread_id.clone()),
        Err(Error::Database(db_err))
            if db_err.code() == Some(std::borrow::Cow::Borrowed("2067")) =>
        {
            if let Some(existing_thread_id) =
                sqlx::query_scalar("SELECT id FROM threads WHERE user_id = ? AND status = 1")
                    .bind(user_id)
                    .fetch_optional(pool)
                    .await?
            {
                Ok(existing_thread_id)
            } else {
                Err(Error::Database(db_err))
            }
        }
        Err(e) => Err(e),
    };

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +66 to +77
if notify_user {
let _ = MessageBuilder::system_message(ctx, config)
.translated_content(
"new_thread.welcome_message",
Some(&params),
None,
Some(channel.guild_id.get()),
)
.await
.to_user(user.id)
.send(true)
.await;

Copilot AI Apr 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the DM branch (if notify_user { ... }), translated_content is called with user_id: None, so get_translated_message will always fall back to the default language (it ignores guild_id for language selection when user_id is None). This means the welcome DM won’t respect the recipient’s language preferences. Pass Some(user.id) when translating content intended for the user (and keep guild_id as needed for server-scoped strings).

Copilot uses AI. Check for mistakes.
.ok()
.flatten()
.map(|v| v != 0)
.unwrap_or(false)

Copilot AI Apr 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_thread_silent currently swallows database errors / missing rows and defaults to false. For the new “silent by default” behavior, this default can cause the bot to send user notifications when it can’t verify the silent flag (e.g., transient DB error), which undermines the feature’s intent. Consider returning Result<bool, sqlx::Error> (and handling it at call sites) or defaulting to true on read errors so you fail closed (no user notification).

Suggested change
.unwrap_or(false)
.unwrap_or(true)

Copilot uses AI. Check for mistakes.
@Akinator31

Copy link
Copy Markdown
Member Author

@copilot apply changes based on the comments in this thread

- Pass Some(user.id) when translating DM content so welcome DM respects
  the recipient's language preferences instead of always falling back to
  the default language
- Change is_thread_silent to unwrap_or(true) so DB errors fail closed,
  preventing accidental user notifications when the silent flag cannot
  be read

Agent-Logs-Url: https://github.com/Rustmail/rustmail/sessions/611e4c0f-7bca-4626-b22f-8070ccd7676c

Co-authored-by: Akinator31 <99099121+Akinator31@users.noreply.github.com>

Copilot AI commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied both changes from the review thread in commit af556be:

  1. common.rs: Changed NoneSome(user.id) when translating the DM welcome message, so get_translated_message uses the recipient's language preferences instead of always falling back to the default.
  2. threads.rs: Changed .unwrap_or(false).unwrap_or(true) in is_thread_silent so DB errors fail closed — preventing user notifications when the silent flag can't be read.

@Akinator31
Akinator31 merged commit 0a30683 into main Apr 29, 2026
2 checks passed
@Akinator31
Akinator31 deleted the 400-add-possbility-to-open-a-ticket-silently branch April 29, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add possbility to open a ticket silently

3 participants