diff --git a/.github/workflows/dotnet-dev-build.yml b/.github/workflows/dotnet-dev-build.yml index f03a5c5b..e7772638 100644 --- a/.github/workflows/dotnet-dev-build.yml +++ b/.github/workflows/dotnet-dev-build.yml @@ -1,4 +1,4 @@ -name: Build on Push +name: Build Dev on Push on: push: diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f2e60167..c85509fa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,38 @@ # **Changelogs** +## **v2.3** +### Features + +- **Tag System Implementation:** Introduced a tag system, allowing users to trigger pre-defined responses from the bot using a 'tag' slash command. + - Implemented web UI and database infrastructure for managing tags (create, view, update, delete). + - Added command handlers for tag retrieval and execution. + - Implemented a `TagProvider` class to dynamically generate slash commands for available tags. + - Implemented a `TagBotMessages` helper class to centralize the creation of messages sent by the bot when using tags. + +- **Enhanced User Engagement with Mirrored Reactions:** Reactions added by moderators in ticket channels are now automatically mirrored to the corresponding user's direct messages, and vice versa, creating a more interactive communication experience. + +- **Implement mirrored message deletion** The bot now removes original user messages from DMs after they are removed to maintain organization and clarity of communications. + +- **Feedback implementation:** The feedback submission system is improved and a data list has been added to improve visibility of user interaction. + +- **Automatic Ticket Data Deletion and Optional Timeout**: Added support for automatically deleting timeout tickets. + +- **Add preliminary permission policies (currently inactive)** Added the configuration for authorization to be tested and applied by admins once the implementation is finished. + +- **Enhance Guild Option UI with visual separators and descriptions:** the visual clarity is improved by seperating groups of guild options through horizontal lines. + +### Bug Fixes + +- Fixed minor UI text issues +- Fixed incorrect metric calculation +- Fixed bot intents +- Fixed multiple feedback submissions +### Dependency Updates + +- Updated DSharpPlus to the latest NuGet nightly version. + + + ## **v2.2** diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index b36059db..8d1a19f7 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -96,4 +96,13 @@ This set of commands allows moderators or higher-level users to manage the black - **Description**: Check if a user is blacklisted. - **Parameters**: - `user`: The user to check. -- **Usage**: `/blacklist status [user]` \ No newline at end of file +- **Usage**: `/blacklist status [user]` + +## Tag Slash Commands + +### `/tag` + +- **Description**: Get tag content. +- **Parameters**: + - `name`: Tag name. +- **Usage**: `/tag [name]` \ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Components/App.razor b/src/Modmail.NET.Web.Blazor/Components/App.razor index 5d197af0..99d10b44 100644 --- a/src/Modmail.NET.Web.Blazor/Components/App.razor +++ b/src/Modmail.NET.Web.Blazor/Components/App.razor @@ -1,4 +1,4 @@ -@using Modmail.NET.Static +@using Modmail.NET.Common.Static @using Modmail.NET.Web.Blazor.Components.Layout.Shared @inject ThemeService ThemeService diff --git a/src/Modmail.NET.Web.Blazor/Components/Layout/AuthLayout.razor b/src/Modmail.NET.Web.Blazor/Components/Layout/AuthLayout.razor index d44e972c..b4cc4c96 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Layout/AuthLayout.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Layout/AuthLayout.razor @@ -1,7 +1,7 @@ -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Permission -@using Modmail.NET.Static +@using Modmail.NET.Common.Static +@using Modmail.NET.Features.Permission.Queries @using Modmail.NET.Web.Blazor.Components.Layout.Shared +@using Modmail.NET.Web.Blazor.Extensions @using Modmail.NET.Web.Blazor.Providers @inherits LayoutComponentBase @inject NavigationManager NavigationManager @@ -36,9 +36,17 @@ + + @* *@ + @if (_hasAccessTickets) { } + + + + + @if (_hasAccessBlacklist) { } @@ -47,13 +55,15 @@ } @if (_hasAccessTeams) { + @* *@ + } @if (_isOwner) { } @if (_hasAccessHangfire) { - + } @*
*@ diff --git a/src/Modmail.NET.Web.Blazor/Components/Layout/Shared/AppFooter.razor b/src/Modmail.NET.Web.Blazor/Components/Layout/Shared/AppFooter.razor index 6eb8ca20..9c5c158a 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Layout/Shared/AppFooter.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Layout/Shared/AppFooter.razor @@ -1,4 +1,4 @@ -@using Modmail.NET.Utils +@using Modmail.NET.Common.Utils diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Blacklist.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Blacklist.razor index 006fee7c..7482a28a 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/Blacklist.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Blacklist.razor @@ -1,10 +1,11 @@ @page "/blacklist" @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Blacklist -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Blacklist.Commands @using Modmail.NET.Web.Blazor.Components.Shared.Blacklist +@using Modmail.NET.Web.Blazor.Extensions @using Modmail.NET.Web.Blazor.Providers @using Serilog @inject IDbContextFactory DbContextFactory @@ -91,7 +92,7 @@ "User removed from blacklist"); await ReloadDataAsync(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, blacklist.DiscordUserId); diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Dashboard.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Dashboard.razor index 210c65fc..34cb3fba 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/Dashboard.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Dashboard.razor @@ -1,17 +1,18 @@ @page "/dashboard" -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Guild -@using Modmail.NET.Features.Metric -@using Modmail.NET.Features.Permission -@using Modmail.NET.Queues -@using Modmail.NET.Static -@using Modmail.NET.Utils +@using Modmail.NET.Common.Static +@using Modmail.NET.Common.Utils +@using Modmail.NET.Features.Guild.Queries +@using Modmail.NET.Features.Metric.Models +@using Modmail.NET.Features.Metric.Queries +@using Modmail.NET.Features.Permission.Queries +@using Modmail.NET.Features.Ticket.Services +@using Modmail.NET.Web.Blazor.Extensions @using Modmail.NET.Web.Blazor.Providers @inject NavigationManager NavigationManager @inject DialogService DialogService @inject NotificationService NotificationService @inject ModmailBot Bot -@inject TicketMessageQueue TicketMessageQueue +@inject TicketMessage TicketMessage @inject IServiceScopeFactory ScopeFactory @attribute [AuthorizeTeam] @@ -235,7 +236,7 @@ - @(UtilReadable.ConvertNumberToReadableString(TicketMessageQueue.GetChannelCount())) + @(UtilReadable.ConvertNumberToReadableString(TicketMessage.GetChannelCount())) Processing Message Queues diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Feedback.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Feedback.razor new file mode 100644 index 00000000..a1a2043a --- /dev/null +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Feedback.razor @@ -0,0 +1,141 @@ +@page "/feedback" +@using Microsoft.EntityFrameworkCore +@using Modmail.NET.Features.Ticket.Mappers +@using Modmail.NET.Features.Ticket.Models +@inject IDbContextFactory DbContextFactory + +@* @attribute [AuthorizeTeam(nameof(AuthPolicy.ViewFeedbacks))] *@ + +@code { + +//TODO: Feedback page authentication + [CascadingParameter] + public Task AuthContext { get; set; } + + private IQueryable _data; + + bool _isLoading; + + async Task ShowLoading() { + _isLoading = true; + + await Task.Yield(); + + _isLoading = false; + } + + + private async Task ReloadDataAsync(LoadDataArgs args = null) { + await ShowLoading(); + + var dbContext = await DbContextFactory.CreateDbContextAsync(); + var query = dbContext.Tickets + .AsNoTracking() + .OrderByDescending(x => x.ClosedDateUtc) + .AsQueryable() + .ProjectToFeedbackDto(); + + + if (args is not null) { + query = query.ApplyDataGridFilter(args); + } + + _count = await query.CountAsync(); + + _data = args is not null + ? query.ApplyPagination(args) + : query.Skip(0).Take(10).AsQueryable(); + + StateHasChanged(); + } + + + protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); + await ReloadDataAsync(); + } + + + private async Task LoadDataAsync(LoadDataArgs args) { + await ReloadDataAsync(args); + } + + private int _count; + + +} + + +
+ + + + + + Feedbacks + +

+ This page shows the list of feedbacks given by users after ticket is closed. +

+ @*
*@ +
+
+ + @if (_data is null) { + + } + else { + + + + + + + + + + + + + + + + + + + } + + +
+
+
\ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Options.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Options.razor index 0c7ddf32..d1a2c38d 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/Options.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Options.razor @@ -1,8 +1,12 @@ @page "/options" @using Microsoft.EntityFrameworkCore @using Microsoft.Extensions.Options -@using Modmail.NET.Abstract -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Metric.Static +@using Modmail.NET.Features.Permission.Static +@using Modmail.NET.Features.Ticket.Static @using Modmail.NET.Web.Blazor.Providers @using Serilog @inject NavigationManager NavigationManager @@ -29,13 +33,23 @@ return; } + InitGuildOptionValues(); + } + + private void InitGuildOptionValues() { if (_guildOption.TicketDataDeleteWaitDays > 0) { _enableTicketAutoDelete = true; } + else { + _guildOption.TicketDataDeleteWaitDays = TicketConstants.TicketDataDeleteWaitDaysMax; + } if (_guildOption.TicketTimeoutHours > 0) { _enableTicketTimeout = true; } + else { + _guildOption.TicketTimeoutHours = TicketConstants.TicketTimeoutMaxAllowedHours; + } } @@ -46,12 +60,12 @@ throw new InvalidOperationException("Guild option is null"); } - if (_guildOption.TicketTimeoutHours is > Const.TicketTimeoutMaxAllowedHours or < Const.TicketTimeoutMinAllowedHours && _enableTicketTimeout) { + if (_guildOption.TicketTimeoutHours is > TicketConstants.TicketTimeoutMaxAllowedHours or < TicketConstants.TicketTimeoutMinAllowedHours && _enableTicketTimeout) { NotificationService.Notify(NotificationSeverity.Warning, "Warning", "Ticket Timeout Hours is not in valid range."); return; } - if (_guildOption.TicketDataDeleteWaitDays is > Const.TicketDataDeleteWaitDaysMax or < Const.TicketDataDeleteWaitDaysMin && _enableTicketAutoDelete) { + if (_guildOption.TicketDataDeleteWaitDays is > TicketConstants.TicketDataDeleteWaitDaysMax or < TicketConstants.TicketDataDeleteWaitDaysMin && _enableTicketAutoDelete) { NotificationService.Notify(NotificationSeverity.Warning, "Warning", "Auto Delete Ticket Data Wait Days is not in valid range."); return; } @@ -69,16 +83,19 @@ } + var dbContext = await DbContextFactory.CreateDbContextAsync(); dbContext.Update(_guildOption); var affected = await dbContext.SaveChangesAsync(); + InitGuildOptionValues(); if (affected == 0) { NotificationService.Notify(NotificationSeverity.Error, "Failed", "Guild options could not be updated"); + return; } NotificationService.Notify(NotificationSeverity.Success, "Success", "Guild options updated successfully."); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { ex.NotifyException(NotificationService); Log.Warning(ex, logMessage); } @@ -121,19 +138,33 @@ +
Take feedback from user + + + After ticket is closed a message is sent to user to take text and star feedback + + + +
Force anonymous messages + + + Forces anonymous messages for all tickets, ignoring the tickets anonymous option + + +
@@ -151,9 +182,7 @@ } - - - +
@@ -166,17 +195,20 @@ + Min="TicketConstants.TicketTimeoutMinAllowedHours" + Max="TicketConstants.TicketTimeoutMaxAllowedHours"/> - Set the number of hours (@Const.TicketTimeoutMinAllowedHours~@Const.TicketTimeoutMaxAllowedHours) a + Set the number of hours + (@TicketConstants.TicketTimeoutMinAllowedHours~@TicketConstants.TicketTimeoutMaxAllowedHours) a ticket can be inactive before it's automatically closed. } +
@@ -188,30 +220,37 @@ + Min="TicketConstants.TicketDataDeleteWaitDaysMin" + Max="TicketConstants.TicketDataDeleteWaitDaysMax"/> - Set the number of days (@Const.TicketDataDeleteWaitDaysMin~@Const.TicketDataDeleteWaitDaysMax) after + Set the number of days + (@TicketConstants.TicketDataDeleteWaitDaysMin~@TicketConstants.TicketDataDeleteWaitDaysMax) after which ticket data is deleted. } +
+ + Min="MetricConstants.StatisticsCalculateDaysMin" + Max="MetricConstants.StatisticsCalculateDaysMax"/> Days to include in statistics calculation - (@Const.StatisticsCalculateDaysMin~@Const.StatisticsCalculateDaysMax). + (@MetricConstants.StatisticsCalculateDaysMin~@MetricConstants.StatisticsCalculateDaysMax). +
+ DbContextFactory +@inject DialogService DialogService +@inject NotificationService NotificationService +@inject TooltipService TooltipService +@inject ISender Sender +@attribute [AuthorizeTeam(nameof(AuthPolicy.ManageTicketTypes))] + + +@code { + + [CascadingParameter] + public Task AuthContext { get; set; } + + private IQueryable _data; + + bool _isLoading; + private int _count; + + async Task ShowLoading() { + _isLoading = true; + + await Task.Yield(); + + _isLoading = false; + } + + private async Task ReloadDataAsync(LoadDataArgs args = null) { + await ShowLoading(); + + var dbContext = await DbContextFactory.CreateDbContextAsync(); + var query = dbContext.Tags + .AsNoTracking() + .OrderByDescending(x => x.RegisterDateUtc) + .AsQueryable(); + + if (args is not null) { + query = query.ApplyDataGridFilter(args); + } + + _count = await query.CountAsync(); + + + _data = args is not null + ? query.ApplyPagination(args) + : query.Skip(0).Take(10).AsQueryable(); + + StateHasChanged(); + } + + + protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); + + await ReloadDataAsync(); + } + + + private async Task LoadDataAsync(LoadDataArgs args) { + await ReloadDataAsync(args); + } + + + private async Task RemoveAsync(Tag data) { + var dialogResult = await DialogService.Confirm("You are deleting a tag, this action can not be undone.", + "Are you sure ?", + new ConfirmOptions { + OkButtonText = "Yes", + CancelButtonText = "No", + CloseDialogOnOverlayClick = true, + CloseDialogOnEsc = true + }); + + + if (dialogResult == true) { + const string logMessage = $"[{nameof(Tag)}]{nameof(RemoveAsync)}({{@data}})"; + try { + var state = await AuthContext; + var userId = state.User.GetUserId(); + + await Sender.Send(new ProcessRemoveTagCommand(userId, data.Id)); + Log.Information(logMessage, + data); + NotificationService.Notify(NotificationSeverity.Success, + "Ticket Type deleted successfully"); + + await ReloadDataAsync(); + } + catch (ModmailBotException ex) { + Log.Warning(ex, + logMessage, + data); + ex.NotifyException(NotificationService); + } + catch (Exception ex) { + Log.Fatal(ex, + logMessage, + data); + ex.NotifyException(NotificationService); + } + } + } + + private async Task ShowAddDialog() { + var dialog = await DialogService.OpenAsync("Create Tag", + _ => + @, + new DialogOptions { + Width = "450px" + }); + if (dialog is true) { + await ReloadDataAsync(); + } + } + + private async Task ShowEditDialog(Tag data) { + var dialog = await DialogService.OpenAsync("Edit Tag", + _ => + @, + new DialogOptions { + Width = "450px" + }); + if (dialog is true) { + await ReloadDataAsync(); + } + } + +} + + +
+ + + + + + Tags + + @*
*@ +

+ Tags allow you to set short names to trigger pre-defined content responses from the bot. +

+
+
+ + @if (_data is null) { + + } + else { + + + + + + + Create Tag + + + + + + + + + + + + + + + + + + } + + +
+
+
\ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Teams.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Teams.razor index 58afce87..fe95d9a1 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/Teams.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Teams.razor @@ -1,10 +1,11 @@ @page "/teams" @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Teams -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Teams.Commands @using Modmail.NET.Web.Blazor.Components.Shared.Teams +@using Modmail.NET.Web.Blazor.Extensions @using Modmail.NET.Web.Blazor.Providers @using Serilog @inject IDbContextFactory DbContextFactory @@ -224,7 +225,7 @@ await ReloadDataAsync(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, team.Name); diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/TicketTypes.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/TicketTypes.razor index 08d30ba2..62b70c27 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/TicketTypes.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/TicketTypes.razor @@ -1,10 +1,11 @@ @page "/ticket-types" @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.TicketType -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Ticket.Commands @using Modmail.NET.Web.Blazor.Components.Shared.TicketType +@using Modmail.NET.Web.Blazor.Extensions @using Modmail.NET.Web.Blazor.Providers @using Serilog @inject IDbContextFactory DbContextFactory @@ -94,7 +95,7 @@ await ReloadDataAsync(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, ticketType); diff --git a/src/Modmail.NET.Web.Blazor/Components/Pages/Tickets.razor b/src/Modmail.NET.Web.Blazor/Components/Pages/Tickets.razor index be739e48..2c98e5f8 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Pages/Tickets.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Pages/Tickets.razor @@ -1,8 +1,11 @@ @page "/tickets" @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Features.Ticket -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Features.Ticket.Commands +@using Modmail.NET.Features.Ticket.Mappers +@using Modmail.NET.Features.Ticket.Models +@using Modmail.NET.Language @using Modmail.NET.Web.Blazor.Components.Shared.Tickets @using Modmail.NET.Web.Blazor.Providers @using Modmail.NET.Web.Blazor.Static @@ -116,7 +119,7 @@ NotificationService.Notify(NotificationSeverity.Success, "Success", "The ticket has been closed successfully."); await ReloadDataAsync(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { ex.NotifyException(NotificationService); Log.Warning(ex, logMessage, _forceCloseReason); } @@ -152,7 +155,43 @@ // private Task ShowAddNoteDialog(TicketDto ticketDto) { // throw new NotImplementedException(); - // } + // } @* @ticketDto.FeedbackStar *@ + + + private async Task ShowFeedback(TicketDto ticketDto) { + _ = await DialogService.OpenAsync("Ticket Feedback", + _ => + @
+ + + + @for (var i = 0; i < ticketDto.FeedbackStar; i++) { + + } + @for (var i = 0; i < 5 - ticketDto.FeedbackStar; i++) { + + } + + + + @if (string.IsNullOrEmpty(ticketDto.FeedbackMessage)) { + @LangKeys.NoFeedbackProvided.GetTranslation() + } + else { + @ticketDto.FeedbackMessage + } + + + + +
, + new DialogOptions { + Width = "500px", + CloseDialogOnEsc = true, + CloseDialogOnOverlayClick = true + }); + } + } @@ -308,6 +347,19 @@ MouseEnter="@(args => TooltipService.Open(args, "Show Notes"))" @onclick:stopPropagation="true"> + @if (data.FeedbackStar.HasValue) { + + + } @* @message.RegisterDateUtc.ToString("MM/dd/yyyy HH:mm") + "rz-color-secondary", + TicketMessageChangeStatus.Deleted => "rz-color-danger", + _ => "" + })> + @(message.ChangeStatus switch { + TicketMessageChangeStatus.Updated => " - Edited", + TicketMessageChangeStatus.Deleted => " - Deleted", + _ => "" + }) + +
@@ -127,6 +141,7 @@ } + //TODO: implement message history view for edited messages, can be directly rendered or can be a dialog _id = ShortGuid.Encode(TicketId); _users = await Sender.Send(new GetDiscordUserInfoDictQuery()); _data = await _context.TicketMessages @@ -141,16 +156,18 @@ x.RegisterDateUtc.Hour, x.RegisterDateUtc.Minute, x.SenderUserId, - x.SentByMod + x.SentByMod, + x.ChangeStatus }) .Select(g => new TicketMessage { RegisterDateUtc = g.Min(x => x.RegisterDateUtc), SenderUserId = g.Key.SenderUserId, - MessageContent = string.Join("
", g.Select(x => x.MessageContent)), + MessageContent = string.Join("
", g.OrderBy(x => x.RegisterDateUtc).Select(x => x.MessageContent)), Attachments = g.SelectMany(x => x.Attachments).ToList(), // Aggregate attachments TicketId = TicketId, MessageDiscordId = 0, - SentByMod = g.Key.SentByMod + SentByMod = g.Key.SentByMod, + ChangeStatus = g.Key.ChangeStatus }) .ToArrayAsync(); diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/AccountDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/AccountDialog.razor index 96c97533..bb8ec020 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/AccountDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/AccountDialog.razor @@ -1,6 +1,6 @@ @using System.Security.Claims +@using Modmail.NET.Features.Permission.Static @using Modmail.NET.Language -@using Modmail.NET.Static @inject NavigationManager NavigationManager @code { diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Blacklist/AddBlacklistDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Blacklist/AddBlacklistDialog.razor index bf2ffaa2..23e302b6 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Blacklist/AddBlacklistDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Blacklist/AddBlacklistDialog.razor @@ -1,8 +1,9 @@ @using Microsoft.EntityFrameworkCore @using Microsoft.Extensions.Options -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Blacklist +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Blacklist.Commands +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject IDbContextFactory DbContextFactory @inject NotificationService NotificationService @@ -67,7 +68,7 @@ _reason); DialogService.Close(true); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { ex.NotifyException(NotificationService); Log.Warning(logMessage, _selectedUserId, diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Tag/CreateOrUpdateTagDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Tag/CreateOrUpdateTagDialog.razor new file mode 100644 index 00000000..98db3b30 --- /dev/null +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Tag/CreateOrUpdateTagDialog.razor @@ -0,0 +1,131 @@ +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Common.Static +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Tag.Commands +@using Modmail.NET.Web.Blazor.Extensions +@using Serilog +@inject DialogService DialogService +@inject NotificationService NotificationService +@inject ModmailBot Bot +@inject ISender Sender + +@code { + + [CascadingParameter] + public Task AuthContext { get; set; } + + [Parameter] + public Tag Tag { get; set; } + + bool IsUpdate => Tag != null; + string _name = ""; + + private string FixedName { + get => _name; + set => _name = value.Trim().Replace(" ", "-"); + } + + string _title = ""; + string _content = ""; + + protected override Task OnInitializedAsync() { + if (Tag is not null) { + _name = Tag.Name; + _title = Tag.Title; + _content = Tag.Content; + } + + return base.OnInitializedAsync(); + } + + private async Task SubmitAsync() { + if (string.IsNullOrEmpty(_name)) { + NotificationService.Notify(NotificationSeverity.Warning, "Warning", "Please enter a name."); + return; + } + + // if (string.IsNullOrEmpty(_title)) { + // NotificationService.Notify(NotificationSeverity.Warning, "Warning", "Please enter a title."); + // return; + // } + + if (string.IsNullOrEmpty(_content)) { + NotificationService.Notify(NotificationSeverity.Warning, "Warning", "Please enter a content."); + return; + } + + var dialogResult = await DialogService.Confirm(IsUpdate + ? "Are you sure you want to update this tag ?" + : "Are you sure you want to create new tag ?", + options: new ConfirmOptions { + OkButtonText = "Yes", + CancelButtonText = "No", + CloseDialogOnOverlayClick = true, + CloseDialogOnEsc = true + }); + if (dialogResult == true) { + const string logMessage = $"[{nameof(CreateOrUpdateTagDialog)}]{nameof(SubmitAsync)}({{Name}})"; + try { + var state = await AuthContext; + var userId = state.User.GetUserId(); + + if (IsUpdate && Tag is not null) { + await Sender.Send(new ProcessUpdateTagCommand(userId, Tag.Id, _name, _title, _content)); + Log.Information(logMessage, _name); + NotificationService.Notify(NotificationSeverity.Success, "Success", "Tag updated successfully."); + } + else { + await Sender.Send(new ProcessCreateTagCommand(userId, + _name, + _title, + _content)); + Log.Information(logMessage, _name); + NotificationService.Notify(NotificationSeverity.Success, "Success", "Tag created successfully."); + } + + DialogService.Close(true); + } + catch (ModmailBotException ex) { + Log.Warning(ex, logMessage, _name); + ex.NotifyException(NotificationService); + } + catch (Exception ex) { + Log.Fatal(ex, logMessage, _name); + ex.NotifyException(NotificationService); + } + } + } + + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddRoleToTeamDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddRoleToTeamDialog.razor index 0d08043e..20bf66ba 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddRoleToTeamDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddRoleToTeamDialog.razor @@ -1,10 +1,11 @@ @using DSharpPlus.Entities @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Bot -@using Modmail.NET.Features.Teams -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.DiscordBot.Queries +@using Modmail.NET.Features.Teams.Commands +@using Modmail.NET.Features.Teams.Static +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject IDbContextFactory DbContextFactory @inject NotificationService NotificationService @@ -60,7 +61,7 @@ NotificationService.Notify(NotificationSeverity.Success, "Success", "Role added to team successfully."); DialogService.Close(true); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(logMessage, _selectedRoleId); ex.NotifyException(NotificationService); } diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddUserToTeamDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddUserToTeamDialog.razor index f2d6fe25..657bfd6a 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddUserToTeamDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/AddUserToTeamDialog.razor @@ -1,8 +1,9 @@ @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Teams -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Teams.Commands +@using Modmail.NET.Features.Teams.Static +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject IDbContextFactory DbContextFactory @inject NotificationService NotificationService @@ -61,7 +62,7 @@ NotificationService.Notify(NotificationSeverity.Success, "Success", "User added to team successfully."); DialogService.Close(true); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(logMessage, _selectedUserId); ex.NotifyException(NotificationService); } diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/CreateOrUpdateTeamDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/CreateOrUpdateTeamDialog.razor index 5b5fc9e4..ed4308a0 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/CreateOrUpdateTeamDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/CreateOrUpdateTeamDialog.razor @@ -1,10 +1,11 @@ @using Microsoft.EntityFrameworkCore @using Microsoft.Extensions.Options -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Permission -@using Modmail.NET.Features.Teams -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Permission.Queries +@using Modmail.NET.Features.Permission.Static +@using Modmail.NET.Features.Teams.Commands +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject IDbContextFactory DbContextFactory @inject NotificationService NotificationService @@ -70,7 +71,9 @@ return; } - var dialogResult = await DialogService.Confirm("Are you sure you want to create new team ?", + var dialogResult = await DialogService.Confirm(IsUpdate + ? "Are you sure you want to update this team ?" + : "Are you sure you want to create new team ?", options: new ConfirmOptions { OkButtonText = "Yes", CancelButtonText = "No", @@ -96,7 +99,7 @@ DialogService.Close(true); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, _teamName, _permissionLevel); ex.NotifyException(NotificationService); } diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/TeamDetailsDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/TeamDetailsDialog.razor index 151407c3..5c4c654f 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/TeamDetailsDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Teams/TeamDetailsDialog.razor @@ -1,10 +1,11 @@ @using DSharpPlus.Entities @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.Bot -@using Modmail.NET.Features.Teams -@using Modmail.NET.Static +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.DiscordBot.Queries +@using Modmail.NET.Features.Teams.Commands +@using Modmail.NET.Features.Teams.Static +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject IDbContextFactory DbContextFactory @inject TooltipService TooltipService @@ -83,7 +84,7 @@ NotificationService.Notify(NotificationSeverity.Success, "Success", "Team member removed"); await ReloadDataAsync(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, guildTeamMember); ex.NotifyException(NotificationService); } diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/TicketType/CreateOrUpdateTicketTypeDialog.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/TicketType/CreateOrUpdateTicketTypeDialog.razor index 512b99b5..2d0be4a0 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/TicketType/CreateOrUpdateTicketTypeDialog.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/TicketType/CreateOrUpdateTicketTypeDialog.razor @@ -1,7 +1,8 @@ @using DSharpPlus.Entities -@using Modmail.NET.Abstract -@using Modmail.NET.Extensions -@using Modmail.NET.Features.TicketType +@using Modmail.NET.Common.Exceptions +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.Ticket.Commands +@using Modmail.NET.Web.Blazor.Extensions @using Serilog @inject DialogService DialogService @inject NotificationService NotificationService @@ -65,7 +66,9 @@ : _emoji; - var dialogResult = await DialogService.Confirm("Are you sure you want to create new ticket type ?", + var dialogResult = await DialogService.Confirm(IsUpdate + ? "Are you sure you want to update this ticket type ?" + : "Are you sure you want to create new ticket type ?", options: new ConfirmOptions { OkButtonText = "Yes", CancelButtonText = "No", @@ -104,7 +107,7 @@ DialogService.Close(true); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, _name); ex.NotifyException(NotificationService); } diff --git a/src/Modmail.NET.Web.Blazor/Components/Shared/Tickets/TicketNotes.razor b/src/Modmail.NET.Web.Blazor/Components/Shared/Tickets/TicketNotes.razor index 0974b0fb..a27a04a8 100644 --- a/src/Modmail.NET.Web.Blazor/Components/Shared/Tickets/TicketNotes.razor +++ b/src/Modmail.NET.Web.Blazor/Components/Shared/Tickets/TicketNotes.razor @@ -1,5 +1,6 @@ @using Microsoft.EntityFrameworkCore -@using Modmail.NET.Features.UserInfo +@using Modmail.NET.Database.Entities +@using Modmail.NET.Features.User.Queries @inject IDbContextFactory DbContextFactory @inject ISender Sender diff --git a/src/Modmail.NET.Web.Blazor/Components/_Imports.razor b/src/Modmail.NET.Web.Blazor/Components/_Imports.razor index d90563f6..605e521a 100644 --- a/src/Modmail.NET.Web.Blazor/Components/_Imports.razor +++ b/src/Modmail.NET.Web.Blazor/Components/_Imports.razor @@ -14,9 +14,4 @@ @using Radzen.Blazor.Rendering @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization -@using Modmail.NET.Models.Dto -@using Modmail.NET.Models -@using Modmail.NET.Entities @using Modmail.NET.Database -@using Modmail.NET.Exceptions -@using Modmail.NET.Mappers \ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Controllers/AttachmentController.cs b/src/Modmail.NET.Web.Blazor/Controllers/AttachmentController.cs index d292777e..a7cac110 100644 --- a/src/Modmail.NET.Web.Blazor/Controllers/AttachmentController.cs +++ b/src/Modmail.NET.Web.Blazor/Controllers/AttachmentController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Static; +using Modmail.NET.Features.Ticket.Services; namespace Modmail.NET.Web.Blazor.Controllers; @@ -27,7 +27,7 @@ public async Task Get(Guid id) { if (attachment is null) return NotFound(); var extension = Path.GetExtension(attachment.FileName) ?? throw new NullReferenceException("extension"); //starts with . - var filePath = Path.Combine(Const.AttachmentDownloadDirectory, id + extension); + var filePath = Path.Combine(TicketAttachmentDownloadService.AttachmentDownloadDirectory, id + extension); var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); return new FileStreamResult(stream, attachment.MediaType); } diff --git a/src/Modmail.NET.Web.Blazor/Dependency/AuthDependency.cs b/src/Modmail.NET.Web.Blazor/Dependency/AuthDependency.cs index 5723c503..08bcee3b 100644 --- a/src/Modmail.NET.Web.Blazor/Dependency/AuthDependency.cs +++ b/src/Modmail.NET.Web.Blazor/Dependency/AuthDependency.cs @@ -4,10 +4,10 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components.Authorization; -using Modmail.NET.Abstract; -using Modmail.NET.Features.Permission; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Permission.Queries; using Modmail.NET.Language; -using Modmail.NET.Static; using Modmail.NET.Web.Blazor.Providers; using Serilog; @@ -79,7 +79,7 @@ public static void Configure(WebApplicationBuilder builder) { Log.Information("Discord.OAuth access granted {UserId} {UserName} {Permission}", userId, context.Principal.FindFirst(ClaimTypes.Name), permission.ToString()); context.Success(); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, "Discord.OAuth Access failed {UserId}", userId); context.Fail(ex.TitleMessage + " : " + ex.ContentMessage); } diff --git a/src/Modmail.NET.Web.Blazor/Dependency/BlazorDependency.cs b/src/Modmail.NET.Web.Blazor/Dependency/BlazorDependency.cs index 7b1630df..d40392e9 100644 --- a/src/Modmail.NET.Web.Blazor/Dependency/BlazorDependency.cs +++ b/src/Modmail.NET.Web.Blazor/Dependency/BlazorDependency.cs @@ -1,4 +1,4 @@ -using Modmail.NET.Static; +using Modmail.NET.Common.Static; using Radzen; namespace Modmail.NET.Web.Blazor.Dependency; diff --git a/src/Modmail.NET.Web.Blazor/Dependency/BusinessDependency.cs b/src/Modmail.NET.Web.Blazor/Dependency/BusinessDependency.cs index afaad400..aa8b2ea1 100644 --- a/src/Modmail.NET.Web.Blazor/Dependency/BusinessDependency.cs +++ b/src/Modmail.NET.Web.Blazor/Dependency/BusinessDependency.cs @@ -1,11 +1,10 @@ using EntityFramework.Exceptions.SqlServer; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Static; using Modmail.NET.Database; using Modmail.NET.Database.Triggers; +using Modmail.NET.Features.Ticket.Services; using Modmail.NET.Language; -using Modmail.NET.Queues; -using Modmail.NET.Services; -using Modmail.NET.Static; using Modmail.NET.Web.Blazor.Services; using Serilog; using Serilog.Extensions.Logging; @@ -26,8 +25,7 @@ public static void Configure(WebApplicationBuilder builder) { builder.Services.Configure(botConfig); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); diff --git a/src/Modmail.NET.Web.Blazor/Dependency/DiscordBotDependency.cs b/src/Modmail.NET.Web.Blazor/Dependency/DiscordBotDependency.cs index 44066dad..d97f753e 100644 --- a/src/Modmail.NET.Web.Blazor/Dependency/DiscordBotDependency.cs +++ b/src/Modmail.NET.Web.Blazor/Dependency/DiscordBotDependency.cs @@ -3,8 +3,8 @@ using DSharpPlus.Commands.Processors.TextCommands; using DSharpPlus.Commands.Processors.TextCommands.Parsing; using DSharpPlus.Extensions; -using Modmail.NET.Commands; -using Modmail.NET.Commands.Slash; +using Modmail.NET.Features.DiscordBot.Events; +using Modmail.NET.Features.DiscordCommands.Handlers; using Serilog; namespace Modmail.NET.Web.Blazor.Dependency; @@ -18,13 +18,14 @@ public static void Configure(WebApplicationBuilder builder) { | DiscordIntents.GuildMessages | DiscordIntents.GuildMembers | DiscordIntents.DirectMessages - | DiscordIntents.GuildMessages + | DiscordIntents.GuildMessageReactions | DiscordIntents.DirectMessageReactions); builder.Services.AddCommandsExtension((_, extension) => { extension.AddCommands(); extension.AddCommands(); extension.AddCommands(); + extension.AddCommands(); extension.AddChecks(typeof(ModmailBotProjectMarker).Assembly); TextCommandProcessor textCommandProcessor = new(new TextCommandConfiguration { @@ -44,25 +45,31 @@ public static void Configure(WebApplicationBuilder builder) { builder.Services.ConfigureEventHandlers(eventHandlingBuilder => { - eventHandlingBuilder.HandleMessageCreated(ModmailEventHandlers.OnMessageCreated); - eventHandlingBuilder.HandleChannelDeleted(ModmailEventHandlers.OnChannelDeleted); + eventHandlingBuilder.HandleMessageCreated(OnMessageCreatedEvent.OnMessageCreated); + eventHandlingBuilder.HandleMessageDeleted(OnMessageDeletedEvent.OnMessageDeleted); + eventHandlingBuilder.HandleMessageUpdated(OnMessageUpdatedEvent.OnMessageUpdated); + + eventHandlingBuilder.HandleMessageReactionAdded(OnMessageReactionAddedEvent.OnMessageReactionAdded); + eventHandlingBuilder.HandleMessageReactionRemoved(OnMessageReactionRemovedEvent.OnMessageReactionRemoved); + + eventHandlingBuilder.HandleChannelDeleted(OnChannelDeletedEvent.OnChannelDeleted); - eventHandlingBuilder.HandleInteractionCreated(ModmailEventHandlers.InteractionCreated); - eventHandlingBuilder.HandleComponentInteractionCreated(ModmailEventHandlers.ComponentInteractionCreated); - eventHandlingBuilder.HandleModalSubmitted(ModmailEventHandlers.ModalSubmitted); + eventHandlingBuilder.HandleComponentInteractionCreated(ComponentInteractionCreatedEvent.ComponentInteractionCreated); - eventHandlingBuilder.HandleGuildMemberAdded(ModmailEventHandlers.OnGuildMemberAdded); - eventHandlingBuilder.HandleGuildMemberRemoved(ModmailEventHandlers.OnGuildMemberRemoved); - eventHandlingBuilder.HandleGuildBanAdded(ModmailEventHandlers.OnGuildBanAdded); - eventHandlingBuilder.HandleGuildBanAdded(ModmailEventHandlers.OnGuildBanAdded); - eventHandlingBuilder.HandleGuildBanRemoved(ModmailEventHandlers.OnGuildBanRemoved); + eventHandlingBuilder.HandleModalSubmitted(ModalSubmittedEvent.ModalSubmitted); - eventHandlingBuilder.HandleUserUpdated(ModmailEventHandlers.OnUserUpdated); - eventHandlingBuilder.HandleUserSettingsUpdated(ModmailEventHandlers.OnUserSettingsUpdated); + //TODO: investigate the need to implement handling of other reaction events + // eventHandlingBuilder.HandleMessageReactionsCleared(); + // eventHandlingBuilder.HandleMessageReactionRemovedEmoji(); - eventHandlingBuilder.HandleMessageReactionAdded(ModmailEventHandlers.OnMessageReactionAdded); - eventHandlingBuilder.HandleMessageDeleted(ModmailEventHandlers.OnMessageDeleted); - eventHandlingBuilder.HandleMessageUpdated(ModmailEventHandlers.OnMessageUpdated); + //User update + eventHandlingBuilder.HandleInteractionCreated(UserUpdateEvents.InteractionCreated); + eventHandlingBuilder.HandleGuildMemberAdded(UserUpdateEvents.OnGuildMemberAdded); + eventHandlingBuilder.HandleGuildMemberRemoved(UserUpdateEvents.OnGuildMemberRemoved); + eventHandlingBuilder.HandleGuildBanAdded(UserUpdateEvents.OnGuildBanAdded); + eventHandlingBuilder.HandleGuildBanRemoved(UserUpdateEvents.OnGuildBanRemoved); + eventHandlingBuilder.HandleUserUpdated(UserUpdateEvents.OnUserUpdated); + eventHandlingBuilder.HandleUserSettingsUpdated(UserUpdateEvents.OnUserSettingsUpdated); }); } } \ No newline at end of file diff --git a/src/Modmail.NET.Web.Blazor/Dependency/HangfireDependency.cs b/src/Modmail.NET.Web.Blazor/Dependency/HangfireDependency.cs index a2af9441..49b9d718 100644 --- a/src/Modmail.NET.Web.Blazor/Dependency/HangfireDependency.cs +++ b/src/Modmail.NET.Web.Blazor/Dependency/HangfireDependency.cs @@ -1,6 +1,6 @@ using Hangfire; using Modmail.NET.Abstract; -using Modmail.NET.Static; +using Modmail.NET.Common.Static; using Modmail.NET.Web.Blazor.Providers; using Serilog; diff --git a/src/Modmail.NET/Extensions/ExtWeb.cs b/src/Modmail.NET.Web.Blazor/Extensions/WebExtensions.cs similarity index 82% rename from src/Modmail.NET/Extensions/ExtWeb.cs rename to src/Modmail.NET.Web.Blazor/Extensions/WebExtensions.cs index dcd880ce..94d4088f 100644 --- a/src/Modmail.NET/Extensions/ExtWeb.cs +++ b/src/Modmail.NET.Web.Blazor/Extensions/WebExtensions.cs @@ -1,8 +1,8 @@ using System.Security.Claims; -namespace Modmail.NET.Extensions; +namespace Modmail.NET.Web.Blazor.Extensions; -public static class ExtWeb +public static class WebExtensions { public static ulong GetUserId(this ClaimsPrincipal principal) { var nameId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; diff --git a/src/Modmail.NET.Web.Blazor/Modmail.NET.Web.Blazor.csproj b/src/Modmail.NET.Web.Blazor/Modmail.NET.Web.Blazor.csproj index d6b7f4e4..78cd2ee7 100644 --- a/src/Modmail.NET.Web.Blazor/Modmail.NET.Web.Blazor.csproj +++ b/src/Modmail.NET.Web.Blazor/Modmail.NET.Web.Blazor.csproj @@ -5,7 +5,7 @@ disable enable 13 - 2.2.0 + 2.3.0 diff --git a/src/Modmail.NET.Web.Blazor/Program.cs b/src/Modmail.NET.Web.Blazor/Program.cs index b230d361..f70a16db 100644 --- a/src/Modmail.NET.Web.Blazor/Program.cs +++ b/src/Modmail.NET.Web.Blazor/Program.cs @@ -1,6 +1,6 @@ using Modmail.NET; +using Modmail.NET.Common.Utils; using Modmail.NET.Language; -using Modmail.NET.Utils; using Modmail.NET.Web.Blazor.Components; using Modmail.NET.Web.Blazor.Dependency; using Serilog; diff --git a/src/Modmail.NET.Web.Blazor/Providers/AuthorizeTeamAttribute.cs b/src/Modmail.NET.Web.Blazor/Providers/AuthorizeTeamAttribute.cs index 849154c2..f9a9273f 100644 --- a/src/Modmail.NET.Web.Blazor/Providers/AuthorizeTeamAttribute.cs +++ b/src/Modmail.NET.Web.Blazor/Providers/AuthorizeTeamAttribute.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; -using Modmail.NET.Static; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Permission.Static; namespace Modmail.NET.Web.Blazor.Providers; diff --git a/src/Modmail.NET.Web.Blazor/Providers/HangfireAuthorizationProvider.cs b/src/Modmail.NET.Web.Blazor/Providers/HangfireAuthorizationProvider.cs index 524f9cd0..28e222a5 100644 --- a/src/Modmail.NET.Web.Blazor/Providers/HangfireAuthorizationProvider.cs +++ b/src/Modmail.NET.Web.Blazor/Providers/HangfireAuthorizationProvider.cs @@ -1,6 +1,6 @@ using Hangfire.Dashboard; using Microsoft.AspNetCore.Authorization; -using Modmail.NET.Static; +using Modmail.NET.Common.Static; namespace Modmail.NET.Web.Blazor.Providers; diff --git a/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckHandler.cs b/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckHandler.cs index 07b798f9..b755037f 100644 --- a/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckHandler.cs +++ b/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckHandler.cs @@ -1,8 +1,8 @@ using System.Security.Claims; using Microsoft.AspNetCore.Authorization; -using Modmail.NET.Extensions; -using Modmail.NET.Features.Permission; -using Modmail.NET.Static; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Web.Blazor.Extensions; namespace Modmail.NET.Web.Blazor.Providers; diff --git a/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckRequirement.cs b/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckRequirement.cs index 5e41bcff..4a26d21f 100644 --- a/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckRequirement.cs +++ b/src/Modmail.NET.Web.Blazor/Providers/TeamPermissionCheckRequirement.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Authorization; -using Modmail.NET.Static; +using Modmail.NET.Common.Static; namespace Modmail.NET.Web.Blazor.Providers; diff --git a/src/Modmail.NET.Web.Blazor/RadzenTools.cs b/src/Modmail.NET.Web.Blazor/RadzenTools.cs index 26336e72..90d92a76 100644 --- a/src/Modmail.NET.Web.Blazor/RadzenTools.cs +++ b/src/Modmail.NET.Web.Blazor/RadzenTools.cs @@ -1,5 +1,5 @@ using System.Linq.Dynamic.Core; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Exceptions; using Radzen; namespace Modmail.NET.Web.Blazor; @@ -34,7 +34,7 @@ public static IQueryable ApplyPagination(this IQueryable queryable, int } public static void NotifyException(this T exception, NotificationService service, bool showExceptionMessage = false) where T : Exception { - if (exception is BotExceptionBase botException) { + if (exception is ModmailBotException botException) { service.Notify(NotificationSeverity.Warning, "Failed", botException.TitleMessage); return; } diff --git a/src/Modmail.NET.Web.Blazor/wwwroot/resources/en.json b/src/Modmail.NET.Web.Blazor/wwwroot/resources/en.json index e9b928f6..b4146b02 100644 --- a/src/Modmail.NET.Web.Blazor/wwwroot/resources/en.json +++ b/src/Modmail.NET.Web.Blazor/wwwroot/resources/en.json @@ -200,5 +200,15 @@ "ErrorNotFound": "Page you looking for doesnt exist or moved", "NotJoinedMainServer": "Bot is not added to main server", "TicketPriority": "Ticket Priority", - "Transcript": "Transcript" + "Transcript": "Transcript", + "MessageEdited": "Message Edited", + "Edited": "Edited", + "NoFeedbackProvided": "No feedback provided", + "FeedbackAlreadySubmitted": "Feedback already submitted", + "Tag": "Tag", + "TagWithSameNameAlreadyExists": "Tag with same name already exists", + "TagDoesntExists": "Tag doesnt exists", + "TagCreatedSuccessfully": "Tag created successfully", + "TagRemovedSuccessfully": "Tag removed successfully", + "TagUpdatedSuccessfully": "Tag updated successfully" } diff --git a/src/Modmail.NET/Abstract/BotExceptionBase.cs b/src/Modmail.NET/Abstract/BotExceptionBase.cs deleted file mode 100644 index dc0dc53b..00000000 --- a/src/Modmail.NET/Abstract/BotExceptionBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using DSharpPlus.Entities; - -namespace Modmail.NET.Abstract; - -public abstract class BotExceptionBase : Exception -{ - protected BotExceptionBase(string titleMessage, string contentMessage = null) { - TitleMessage = titleMessage; - ContentMessage = contentMessage; - } - - public string TitleMessage { get; } - public string ContentMessage { get; } - - public DiscordWebhookBuilder GetWebhookResponse() { - return Webhooks.Warning(TitleMessage, ContentMessage ?? ""); - } - - public DiscordEmbedBuilder GetEmbedResponse() { - return Embeds.Warning(TitleMessage, ContentMessage ?? ""); - } - - public DiscordInteractionResponseBuilder GetInteractionResponse() { - return Interactions.Warning(TitleMessage, ContentMessage ?? ""); - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Abstract/IEntity.cs b/src/Modmail.NET/Abstract/IEntity.cs deleted file mode 100644 index cb35a6f6..00000000 --- a/src/Modmail.NET/Abstract/IEntity.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Modmail.NET.Abstract; - -public interface IEntity { } \ No newline at end of file diff --git a/src/Modmail.NET/Abstract/BaseQueue.cs b/src/Modmail.NET/Abstract/MemoryQueueBase.cs similarity index 92% rename from src/Modmail.NET/Abstract/BaseQueue.cs rename to src/Modmail.NET/Abstract/MemoryQueueBase.cs index b995fc53..9a708598 100644 --- a/src/Modmail.NET/Abstract/BaseQueue.cs +++ b/src/Modmail.NET/Abstract/MemoryQueueBase.cs @@ -1,17 +1,17 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Threading.Channels; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; namespace Modmail.NET.Abstract; -public abstract class BaseQueue where TKey : notnull +public abstract class MemoryQueueBase where TKey : notnull { private readonly TimeSpan _idleTimeout; private readonly ConcurrentDictionary _lastActiveTime = new(); private readonly ConcurrentDictionary> _queues = new(); - protected BaseQueue(TimeSpan idleTimeout) { + protected MemoryQueueBase(TimeSpan idleTimeout) { _idleTimeout = idleTimeout; } diff --git a/src/Modmail.NET/BotConfig.cs b/src/Modmail.NET/BotConfig.cs index b4af8e0a..d7292e34 100644 --- a/src/Modmail.NET/BotConfig.cs +++ b/src/Modmail.NET/BotConfig.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Modmail.NET.Common.Static; namespace Modmail.NET; diff --git a/src/Modmail.NET/Aspects/PerformanceLoggerAspect.cs b/src/Modmail.NET/Common/Aspects/PerformanceLoggerAspect.cs similarity index 96% rename from src/Modmail.NET/Aspects/PerformanceLoggerAspect.cs rename to src/Modmail.NET/Common/Aspects/PerformanceLoggerAspect.cs index 5faef0fd..89a065bd 100644 --- a/src/Modmail.NET/Aspects/PerformanceLoggerAspect.cs +++ b/src/Modmail.NET/Common/Aspects/PerformanceLoggerAspect.cs @@ -2,7 +2,7 @@ using AspectInjector.Broker; using Serilog; -namespace Modmail.NET.Aspects; +namespace Modmail.NET.Common.Aspects; /// /// Performance Logger Aspect for logging long running actions, with a default threshold of 1000 ms. Desired threshold diff --git a/src/Modmail.NET/Common/Exceptions/AnotherServerAlreadySetupException.cs b/src/Modmail.NET/Common/Exceptions/AnotherServerAlreadySetupException.cs new file mode 100644 index 00000000..730083c9 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/AnotherServerAlreadySetupException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class AnotherServerAlreadySetupException : ModmailBotException +{ + public AnotherServerAlreadySetupException() : base(LangProvider.This.GetTranslation(LangKeys.AnotherServerAlreadySetup)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs b/src/Modmail.NET/Common/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs similarity index 69% rename from src/Modmail.NET/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs rename to src/Modmail.NET/Common/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs index 90e55ff1..efd9cf55 100644 --- a/src/Modmail.NET/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs +++ b/src/Modmail.NET/Common/Exceptions/CanNotDeleteTicketTypeWhenActiveTicketsException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class CanNotDeleteTicketTypeWhenActiveTicketsException : BotExceptionBase +public class CanNotDeleteTicketTypeWhenActiveTicketsException : ModmailBotException { public CanNotDeleteTicketTypeWhenActiveTicketsException() : base(LangKeys.CanNotDeleteTicketTypeWhenActiveTickets.GetTranslation()) { } } \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/DbInternalException.cs b/src/Modmail.NET/Common/Exceptions/DbInternalException.cs new file mode 100644 index 00000000..b3015f0d --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/DbInternalException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class DbInternalException : ModmailBotException +{ + public DbInternalException() : base(LangProvider.This.GetTranslation(LangKeys.DbInternalError)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/EmptyListResultException.cs b/src/Modmail.NET/Common/Exceptions/EmptyListResultException.cs similarity index 54% rename from src/Modmail.NET/Exceptions/EmptyListResultException.cs rename to src/Modmail.NET/Common/Exceptions/EmptyListResultException.cs index 7252d9ef..ef8e3841 100644 --- a/src/Modmail.NET/Exceptions/EmptyListResultException.cs +++ b/src/Modmail.NET/Common/Exceptions/EmptyListResultException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class EmptyListResultException : BotExceptionBase +public class EmptyListResultException : ModmailBotException { public EmptyListResultException(LangKeys name) : base(LangKeys.NoXFound.GetTranslation(name)) { Name = name; diff --git a/src/Modmail.NET/Common/Exceptions/FeedbackAlreadySubmittedException.cs b/src/Modmail.NET/Common/Exceptions/FeedbackAlreadySubmittedException.cs new file mode 100644 index 00000000..ac931258 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/FeedbackAlreadySubmittedException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class FeedbackAlreadySubmittedException : ModmailBotException +{ + public FeedbackAlreadySubmittedException() : base(LangProvider.This.GetTranslation(LangKeys.FeedbackAlreadySubmitted)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/InvalidInteractionKeyException.cs b/src/Modmail.NET/Common/Exceptions/InvalidInteractionKeyException.cs new file mode 100644 index 00000000..3f1a040e --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/InvalidInteractionKeyException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class InvalidInteractionKeyException : ModmailBotException +{ + public InvalidInteractionKeyException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidInteractionKey)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/InvalidMessageIdException.cs b/src/Modmail.NET/Common/Exceptions/InvalidMessageIdException.cs new file mode 100644 index 00000000..7f6c3ea3 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/InvalidMessageIdException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class InvalidMessageIdException : ModmailBotException +{ + public InvalidMessageIdException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidMessageId)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/InvalidNameException.cs b/src/Modmail.NET/Common/Exceptions/InvalidNameException.cs similarity index 56% rename from src/Modmail.NET/Exceptions/InvalidNameException.cs rename to src/Modmail.NET/Common/Exceptions/InvalidNameException.cs index a7a3ad21..145e0f71 100644 --- a/src/Modmail.NET/Exceptions/InvalidNameException.cs +++ b/src/Modmail.NET/Common/Exceptions/InvalidNameException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class InvalidNameException : BotExceptionBase +public class InvalidNameException : ModmailBotException { public InvalidNameException(string name) : base(LangProvider.This.GetTranslation(LangKeys.InvalidName)) { Name = name; diff --git a/src/Modmail.NET/Common/Exceptions/InvalidUserIdException.cs b/src/Modmail.NET/Common/Exceptions/InvalidUserIdException.cs new file mode 100644 index 00000000..ce5fcf81 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/InvalidUserIdException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class InvalidUserIdException : ModmailBotException +{ + public InvalidUserIdException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidUser)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/MainServerAlreadySetupException.cs b/src/Modmail.NET/Common/Exceptions/MainServerAlreadySetupException.cs new file mode 100644 index 00000000..bd5eac04 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/MainServerAlreadySetupException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class MainServerAlreadySetupException : ModmailBotException +{ + public MainServerAlreadySetupException() : base(LangProvider.This.GetTranslation(LangKeys.MainServerAlreadySetup)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/MemberAlreadyInTeamException.cs b/src/Modmail.NET/Common/Exceptions/MemberAlreadyInTeamException.cs new file mode 100644 index 00000000..ea6ec5eb --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/MemberAlreadyInTeamException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class MemberAlreadyInTeamException : ModmailBotException +{ + public MemberAlreadyInTeamException() : base(LangProvider.This.GetTranslation(LangKeys.MemberAlreadyInTeam)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/ModmailBotException.cs b/src/Modmail.NET/Common/Exceptions/ModmailBotException.cs new file mode 100644 index 00000000..5d7f64d0 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/ModmailBotException.cs @@ -0,0 +1,39 @@ +using DSharpPlus.Entities; +using Modmail.NET.Common.Static; +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class ModmailBotException : Exception +{ + [Obsolete("String message constructor is obsolete and will be removed, use constructors that takes Language keys")] + protected ModmailBotException(string titleMessage, string contentMessage = null) { + TitleMessage = titleMessage; + ContentMessage = contentMessage; + } + + public ModmailBotException(LangKeys titleMessage) { + TitleMessage = titleMessage.GetTranslation(); + } + + public ModmailBotException(LangKeys titleMessage, LangKeys contentMessage) { + TitleMessage = titleMessage.GetTranslation(); + ContentMessage = contentMessage.GetTranslation(); + } + + + public string TitleMessage { get; } + public string ContentMessage { get; } + + public DiscordWebhookBuilder GetWebhookResponse() { + return ModmailWebhooks.Warning(TitleMessage, ContentMessage ?? ""); + } + + public DiscordEmbedBuilder GetEmbedResponse() { + return ModmailEmbeds.Warning(TitleMessage, ContentMessage ?? ""); + } + + public DiscordInteractionResponseBuilder GetInteractionResponse() { + return ModmailInteractions.Warning(TitleMessage, ContentMessage ?? ""); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/NotFoundException.cs b/src/Modmail.NET/Common/Exceptions/NotFoundException.cs similarity index 57% rename from src/Modmail.NET/Exceptions/NotFoundException.cs rename to src/Modmail.NET/Common/Exceptions/NotFoundException.cs index 221cc597..7d55e22a 100644 --- a/src/Modmail.NET/Exceptions/NotFoundException.cs +++ b/src/Modmail.NET/Common/Exceptions/NotFoundException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class NotFoundException : BotExceptionBase +public class NotFoundException : ModmailBotException { public NotFoundException(LangKeys name) : base(LangProvider.This.GetTranslation(LangKeys.XNotFound, name)) { Name = name; diff --git a/src/Modmail.NET/Exceptions/NotFoundInException.cs b/src/Modmail.NET/Common/Exceptions/NotFoundInException.cs similarity index 67% rename from src/Modmail.NET/Exceptions/NotFoundInException.cs rename to src/Modmail.NET/Common/Exceptions/NotFoundInException.cs index 2c302935..4329ddc5 100644 --- a/src/Modmail.NET/Exceptions/NotFoundInException.cs +++ b/src/Modmail.NET/Common/Exceptions/NotFoundInException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class NotFoundInException : BotExceptionBase +public class NotFoundInException : ModmailBotException { public NotFoundInException(LangKeys name, LangKeys inName) : base(LangProvider.This.GetTranslation(LangKeys.XNotFoundInY, name, inName)) { Name = name; diff --git a/src/Modmail.NET/Exceptions/NotFoundWithException.cs b/src/Modmail.NET/Common/Exceptions/NotFoundWithException.cs similarity index 64% rename from src/Modmail.NET/Exceptions/NotFoundWithException.cs rename to src/Modmail.NET/Common/Exceptions/NotFoundWithException.cs index 786adff6..d79a8960 100644 --- a/src/Modmail.NET/Exceptions/NotFoundWithException.cs +++ b/src/Modmail.NET/Common/Exceptions/NotFoundWithException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class NotFoundWithException : BotExceptionBase +public class NotFoundWithException : ModmailBotException { public NotFoundWithException(LangKeys name, object id) : base(LangProvider.This.GetTranslation(LangKeys.XNotFound, name, id)) { Name = name; diff --git a/src/Modmail.NET/Common/Exceptions/NotJoinedMainServerException.cs b/src/Modmail.NET/Common/Exceptions/NotJoinedMainServerException.cs new file mode 100644 index 00000000..b77fd0f1 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/NotJoinedMainServerException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class NotJoinedMainServerException : ModmailBotException +{ + public NotJoinedMainServerException() : base(LangProvider.This.GetTranslation(LangKeys.NotJoinedMainServer)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/RoleAlreadyInTeamException.cs b/src/Modmail.NET/Common/Exceptions/RoleAlreadyInTeamException.cs new file mode 100644 index 00000000..e9ecb76e --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/RoleAlreadyInTeamException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class RoleAlreadyInTeamException : ModmailBotException +{ + public RoleAlreadyInTeamException() : base(LangProvider.This.GetTranslation(LangKeys.RoleAlreadyInTeam)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/ServerIsNotSetupException.cs b/src/Modmail.NET/Common/Exceptions/ServerIsNotSetupException.cs new file mode 100644 index 00000000..b183e825 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/ServerIsNotSetupException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class ServerIsNotSetupException : ModmailBotException +{ + public ServerIsNotSetupException() : base(LangProvider.This.GetTranslation(LangKeys.RoleNotFoundInTeam)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/TeamAlreadyExistsException.cs b/src/Modmail.NET/Common/Exceptions/TeamAlreadyExistsException.cs new file mode 100644 index 00000000..8d345f97 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/TeamAlreadyExistsException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class TeamAlreadyExistsException : ModmailBotException +{ + public TeamAlreadyExistsException() : base(LangProvider.This.GetTranslation(LangKeys.TeamAlreadyExists)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/TeamNotExistsException.cs b/src/Modmail.NET/Common/Exceptions/TeamNotExistsException.cs new file mode 100644 index 00000000..5949a98f --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/TeamNotExistsException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class TeamNotExistsException : ModmailBotException +{ + public TeamNotExistsException() : base(LangProvider.This.GetTranslation(LangKeys.TeamNotExists)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/TicketAlreadyClosedException.cs b/src/Modmail.NET/Common/Exceptions/TicketAlreadyClosedException.cs new file mode 100644 index 00000000..8ed17e6a --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/TicketAlreadyClosedException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class TicketAlreadyClosedException : ModmailBotException +{ + public TicketAlreadyClosedException() : base(LangProvider.This.GetTranslation(LangKeys.TicketAlreadyClosed)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/TicketMustBeClosedException.cs b/src/Modmail.NET/Common/Exceptions/TicketMustBeClosedException.cs new file mode 100644 index 00000000..1a77c3bc --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/TicketMustBeClosedException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class TicketMustBeClosedException : ModmailBotException +{ + public TicketMustBeClosedException() : base(LangProvider.This.GetTranslation(LangKeys.TicketMustBeClosed)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/TicketTimeoutOutOfRangeException.cs b/src/Modmail.NET/Common/Exceptions/TicketTimeoutOutOfRangeException.cs new file mode 100644 index 00000000..26c33d26 --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/TicketTimeoutOutOfRangeException.cs @@ -0,0 +1,11 @@ +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class TicketTimeoutOutOfRangeException : ModmailBotException +{ + public TicketTimeoutOutOfRangeException() : base(LangKeys.TicketTimeoutValueIsOutOfRange.GetTranslation(), + LangKeys.TicketTimeoutValueMustBeBetweenXAndY.GetTranslation(TicketConstants.TicketTimeoutMinAllowedHours, + TicketConstants.TicketTimeoutMaxAllowedHours)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TicketTypeAlreadyExistsException.cs b/src/Modmail.NET/Common/Exceptions/TicketTypeAlreadyExistsException.cs similarity index 57% rename from src/Modmail.NET/Exceptions/TicketTypeAlreadyExistsException.cs rename to src/Modmail.NET/Common/Exceptions/TicketTypeAlreadyExistsException.cs index a71eec0c..e73b1881 100644 --- a/src/Modmail.NET/Exceptions/TicketTypeAlreadyExistsException.cs +++ b/src/Modmail.NET/Common/Exceptions/TicketTypeAlreadyExistsException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class TicketTypeAlreadyExistsException : BotExceptionBase +public class TicketTypeAlreadyExistsException : ModmailBotException { public TicketTypeAlreadyExistsException(string name) : base(LangProvider.This.GetTranslation(LangKeys.TicketTypeAlreadyExists)) { Name = name; diff --git a/src/Modmail.NET/Exceptions/TicketTypeNotExistsException.cs b/src/Modmail.NET/Common/Exceptions/TicketTypeNotExistsException.cs similarity index 58% rename from src/Modmail.NET/Exceptions/TicketTypeNotExistsException.cs rename to src/Modmail.NET/Common/Exceptions/TicketTypeNotExistsException.cs index 50cd2484..4ba2aeb1 100644 --- a/src/Modmail.NET/Exceptions/TicketTypeNotExistsException.cs +++ b/src/Modmail.NET/Common/Exceptions/TicketTypeNotExistsException.cs @@ -1,8 +1,8 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Language; -namespace Modmail.NET.Exceptions; +namespace Modmail.NET.Common.Exceptions; -public class TicketTypeNotExistsException : BotExceptionBase +public class TicketTypeNotExistsException : ModmailBotException { public TicketTypeNotExistsException(string name = null) : base(LangProvider.This.GetTranslation(LangKeys.TicketTypeNotExists)) { Name = name; diff --git a/src/Modmail.NET/Common/Exceptions/UserAlreadyBlacklistedException.cs b/src/Modmail.NET/Common/Exceptions/UserAlreadyBlacklistedException.cs new file mode 100644 index 00000000..883098ec --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/UserAlreadyBlacklistedException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class UserAlreadyBlacklistedException : ModmailBotException +{ + public UserAlreadyBlacklistedException() : base(LangProvider.This.GetTranslation(LangKeys.UserAlreadyBlacklisted)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Exceptions/UserIsNotBlacklistedException.cs b/src/Modmail.NET/Common/Exceptions/UserIsNotBlacklistedException.cs new file mode 100644 index 00000000..2f72a2ab --- /dev/null +++ b/src/Modmail.NET/Common/Exceptions/UserIsNotBlacklistedException.cs @@ -0,0 +1,8 @@ +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Exceptions; + +public class UserIsNotBlacklistedException : ModmailBotException +{ + public UserIsNotBlacklistedException() : base(LangProvider.This.GetTranslation(LangKeys.UserIsNotBlacklisted)) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Extensions/ExtDiscordUser.cs b/src/Modmail.NET/Common/Extensions/DiscordUserExtensions.cs similarity index 87% rename from src/Modmail.NET/Extensions/ExtDiscordUser.cs rename to src/Modmail.NET/Common/Extensions/DiscordUserExtensions.cs index 2b3cd13e..7df1d3d9 100644 --- a/src/Modmail.NET/Extensions/ExtDiscordUser.cs +++ b/src/Modmail.NET/Common/Extensions/DiscordUserExtensions.cs @@ -1,8 +1,8 @@ using DSharpPlus.Entities; -namespace Modmail.NET.Extensions; +namespace Modmail.NET.Common.Extensions; -public static class ExtDiscordUser +public static class DiscordUserExtensions { public static string GetUsername(this DiscordUser user) { if (user == null) return string.Empty; diff --git a/src/Modmail.NET/Extensions/ExtEmbed.cs b/src/Modmail.NET/Common/Extensions/EmbedExtensions.cs similarity index 89% rename from src/Modmail.NET/Extensions/ExtEmbed.cs rename to src/Modmail.NET/Common/Extensions/EmbedExtensions.cs index 396d4e53..9f3083f2 100644 --- a/src/Modmail.NET/Extensions/ExtEmbed.cs +++ b/src/Modmail.NET/Common/Extensions/EmbedExtensions.cs @@ -1,11 +1,11 @@ using DSharpPlus.Entities; -using Modmail.NET.Entities; -using Modmail.NET.Features.Bot; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.DiscordBot.Queries; -namespace Modmail.NET.Extensions; +namespace Modmail.NET.Common.Extensions; -public static class ExtEmbed +public static class EmbedExtensions { public static DiscordMessageBuilder AddAttachments(this DiscordMessageBuilder builder, TicketMessageAttachment[] attachments) { if (attachments == null || attachments.Length == 0) return builder; diff --git a/src/Modmail.NET/Common/Extensions/ExceptionExtensions.cs b/src/Modmail.NET/Common/Extensions/ExceptionExtensions.cs new file mode 100644 index 00000000..414df8a1 --- /dev/null +++ b/src/Modmail.NET/Common/Extensions/ExceptionExtensions.cs @@ -0,0 +1,45 @@ +using DSharpPlus.Entities; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Static; +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Extensions; + +public static class ExceptionExtensions +{ + public static DiscordWebhookBuilder ToWebhookResponse(this ModmailBotException exception) { + return ModmailWebhooks.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); + } + + public static DiscordEmbedBuilder ToEmbedResponse(this ModmailBotException exception) { + return ModmailEmbeds.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); + } + + public static DiscordInteractionResponseBuilder ToInteractionResponse(this ModmailBotException exception) { + return ModmailInteractions.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); + } + + public static DiscordWebhookBuilder ToWebhookResponse(this Exception exception) { + var config = ServiceLocator.GetBotConfig(); + + if (config.Environment == EnvironmentType.Development) return ModmailWebhooks.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); + + return ModmailWebhooks.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); + } + + public static DiscordEmbedBuilder ToEmbedResponse(this Exception exception) { + var config = ServiceLocator.GetBotConfig(); + + if (config.Environment == EnvironmentType.Development) return ModmailEmbeds.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); + + return ModmailEmbeds.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); + } + + public static DiscordInteractionResponseBuilder ToInteractionResponse(this Exception exception) { + var config = ServiceLocator.GetBotConfig(); + + if (config.Environment == EnvironmentType.Development) return ModmailInteractions.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); + + return ModmailInteractions.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Extensions/ExtString.cs b/src/Modmail.NET/Common/Extensions/StringExtensions.cs similarity index 68% rename from src/Modmail.NET/Extensions/ExtString.cs rename to src/Modmail.NET/Common/Extensions/StringExtensions.cs index 0399d7b2..7ad821b1 100644 --- a/src/Modmail.NET/Extensions/ExtString.cs +++ b/src/Modmail.NET/Common/Extensions/StringExtensions.cs @@ -1,6 +1,6 @@ -namespace Modmail.NET.Extensions; +namespace Modmail.NET.Common.Extensions; -public static class ExtString +public static class StringExtensions { public static string GetStringOrNaN(this string value) { if (string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value)) return "NaN"; diff --git a/src/Modmail.NET/Common/Static/AuthPolicy.cs b/src/Modmail.NET/Common/Static/AuthPolicy.cs new file mode 100644 index 00000000..30e10c1e --- /dev/null +++ b/src/Modmail.NET/Common/Static/AuthPolicy.cs @@ -0,0 +1,47 @@ +using Ardalis.SmartEnum; + +namespace Modmail.NET.Common.Static; + +/// +/// AuthPolicy smart enum +/// +public sealed class AuthPolicy : SmartEnum +{ + public static readonly AuthPolicy Support = new(nameof(Support), 1); + public static readonly AuthPolicy Moderator = new(nameof(Moderator), 2); + public static readonly AuthPolicy Admin = new(nameof(Admin), 3); + public static readonly AuthPolicy Owner = new(nameof(Owner), 4); + public static readonly AuthPolicy ManageTickets = new(nameof(ManageTickets), 6); + public static readonly AuthPolicy ManageTicketTypes = new(nameof(ManageTicketTypes), 7); + public static readonly AuthPolicy ManageTeams = new(nameof(ManageTeams), 8); + public static readonly AuthPolicy ManageBlacklist = new(nameof(ManageBlacklist), 9); + public static readonly AuthPolicy ManageHangfire = new(nameof(ManageHangfire), 10); + + // + // public static readonly AuthPolicy ManageDiscordClient = new(nameof(ManageDiscordClient), 100); + // public static readonly AuthPolicy ViewDashboardMetrics = new(nameof(ViewDashboardMetrics), 101); + // public static readonly AuthPolicy ViewAnalytics = new(nameof(ViewAnalytics), 102); + // + // public static readonly AuthPolicy ViewTickets = new(nameof(ViewTickets), 202); + // public static readonly AuthPolicy ViewTicketTranscript = new(nameof(ViewTicketTranscript), 203); + // public static readonly AuthPolicy ViewTicketNotes = new(nameof(ViewTicketNotes), 204); + // public static readonly AuthPolicy ViewTicketFeedbacks = new(nameof(ViewTicketFeedbacks), 205); + // public static readonly AuthPolicy ViewTicketDetailMetrics = new(nameof(ViewTicketDetailMetrics), 207); + // + // public static readonly AuthPolicy BlockUser = new(nameof(BlockUser), 301); + // public static readonly AuthPolicy RemoveBlock = new(nameof(RemoveBlock), 302); + // + // public static readonly AuthPolicy ViewTicketTypes = new(nameof(ViewTicketTypes), 400); + // public static readonly AuthPolicy ManageTicketTypes = new(nameof(ManageTicketTypes), 401); + // + // public static readonly AuthPolicy ViewTeams = new(nameof(ViewTeams), 500); + // public static readonly AuthPolicy CreateTeam = new(nameof(CreateTeam), 501); + // public static readonly AuthPolicy ManageTeamsAndPermissions = new(nameof(ManageTeams), 502); + // + // public static readonly AuthPolicy ManageGuildOption = new(nameof(ManageGuildOption), 600); + // + // public static readonly AuthPolicy ManageHangfire = new(nameof(ManageHangfire), 700); + + + private AuthPolicy(string name, int value) : base(name, value) { } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Static/Const.cs b/src/Modmail.NET/Common/Static/Const.cs new file mode 100644 index 00000000..5d91f193 --- /dev/null +++ b/src/Modmail.NET/Common/Static/Const.cs @@ -0,0 +1,10 @@ +using DSharpPlus.Entities; +using Modmail.NET.Language; + +namespace Modmail.NET.Common.Static; + +public static class Const +{ + public const string ThemeCookieName = "Modmail.NET.Theme"; + public static readonly DiscordActivity DiscordActivity = new(LangProvider.This.GetTranslation(LangKeys.ModerationConcerns), DiscordActivityType.ListeningTo); +} \ No newline at end of file diff --git a/src/Modmail.NET/Static/DbLength.cs b/src/Modmail.NET/Common/Static/DbLength.cs similarity index 83% rename from src/Modmail.NET/Static/DbLength.cs rename to src/Modmail.NET/Common/Static/DbLength.cs index 8e44977a..f44d4880 100644 --- a/src/Modmail.NET/Static/DbLength.cs +++ b/src/Modmail.NET/Common/Static/DbLength.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; public static class DbLength { @@ -16,4 +16,6 @@ public static class DbLength public const int KeyString = 128; public const int Emoji = 100; public const int Description = 1000; + public const int TagName = 32; + public const int TagTitle = 64; } \ No newline at end of file diff --git a/src/Modmail.NET/Static/DbType.cs b/src/Modmail.NET/Common/Static/DbType.cs similarity index 91% rename from src/Modmail.NET/Static/DbType.cs rename to src/Modmail.NET/Common/Static/DbType.cs index 6a6b1e1e..ee88e6f2 100644 --- a/src/Modmail.NET/Static/DbType.cs +++ b/src/Modmail.NET/Common/Static/DbType.cs @@ -1,6 +1,6 @@ using Ardalis.SmartEnum; -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; public sealed class DbType : SmartEnum { diff --git a/src/Modmail.NET/Static/EnvironmentType.cs b/src/Modmail.NET/Common/Static/EnvironmentType.cs similarity index 60% rename from src/Modmail.NET/Static/EnvironmentType.cs rename to src/Modmail.NET/Common/Static/EnvironmentType.cs index 725c5869..31ea242c 100644 --- a/src/Modmail.NET/Static/EnvironmentType.cs +++ b/src/Modmail.NET/Common/Static/EnvironmentType.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; public enum EnvironmentType { diff --git a/src/Modmail.NET/Static/HangfireQueueName.cs b/src/Modmail.NET/Common/Static/HangfireQueueName.cs similarity index 90% rename from src/Modmail.NET/Static/HangfireQueueName.cs rename to src/Modmail.NET/Common/Static/HangfireQueueName.cs index f514e5c1..02352505 100644 --- a/src/Modmail.NET/Static/HangfireQueueName.cs +++ b/src/Modmail.NET/Common/Static/HangfireQueueName.cs @@ -1,6 +1,6 @@ using Ardalis.SmartEnum; -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; /// /// Hangfire queue names must be lower case diff --git a/src/Modmail.NET/Static/Colors.cs b/src/Modmail.NET/Common/Static/ModmailColors.cs similarity index 87% rename from src/Modmail.NET/Static/Colors.cs rename to src/Modmail.NET/Common/Static/ModmailColors.cs index 8e1c9e0c..1f2fcbd3 100644 --- a/src/Modmail.NET/Static/Colors.cs +++ b/src/Modmail.NET/Common/Static/ModmailColors.cs @@ -1,8 +1,8 @@ using DSharpPlus.Entities; -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; -public static class Colors +public static class ModmailColors { //Simple public static readonly DiscordColor ErrorColor = DiscordColor.DarkRed; @@ -13,6 +13,7 @@ public static class Colors //Message public static readonly DiscordColor MessageReceivedColor = DiscordColor.Blue; + public static readonly DiscordColor TagReceivedColor = DiscordColor.Blue; //Ticket public static readonly DiscordColor TicketCreatedColor = DiscordColor.Blue; diff --git a/src/Modmail.NET/Static/Embeds.cs b/src/Modmail.NET/Common/Static/ModmailEmbeds.cs similarity index 74% rename from src/Modmail.NET/Static/Embeds.cs rename to src/Modmail.NET/Common/Static/ModmailEmbeds.cs index 151dcbf0..ff85a255 100644 --- a/src/Modmail.NET/Static/Embeds.cs +++ b/src/Modmail.NET/Common/Static/ModmailEmbeds.cs @@ -1,34 +1,34 @@ using DSharpPlus.Entities; -namespace Modmail.NET.Static; +namespace Modmail.NET.Common.Static; -public static class Embeds +public static class ModmailEmbeds { public static DiscordEmbedBuilder Error(string title, string message = "") { return new DiscordEmbedBuilder() .WithTitle(title) .WithDescription(message) - .WithColor(Colors.ErrorColor); + .WithColor(ModmailColors.ErrorColor); } public static DiscordEmbedBuilder Success(string title, string message = "") { return new DiscordEmbedBuilder() .WithTitle(title) .WithDescription(message) - .WithColor(Colors.SuccessColor); + .WithColor(ModmailColors.SuccessColor); } public static DiscordEmbedBuilder Info(string title, string message = "") { return new DiscordEmbedBuilder() .WithTitle(title) .WithDescription(message) - .WithColor(Colors.InfoColor); + .WithColor(ModmailColors.InfoColor); } public static DiscordEmbedBuilder Warning(string title, string message = "") { return new DiscordEmbedBuilder() .WithTitle(title) .WithDescription(message) - .WithColor(Colors.WarningColor); + .WithColor(ModmailColors.WarningColor); } } \ No newline at end of file diff --git a/src/Modmail.NET/Common/Static/ModmailInteractions.cs b/src/Modmail.NET/Common/Static/ModmailInteractions.cs new file mode 100644 index 00000000..1e2ea030 --- /dev/null +++ b/src/Modmail.NET/Common/Static/ModmailInteractions.cs @@ -0,0 +1,22 @@ +using DSharpPlus.Entities; + +namespace Modmail.NET.Common.Static; + +public static class ModmailInteractions +{ + public static DiscordInteractionResponseBuilder Error(string title, string message = "") { + return new DiscordInteractionResponseBuilder().AddEmbed(ModmailEmbeds.Error(title, message)); + } + + public static DiscordInteractionResponseBuilder Success(string title, string message = "") { + return new DiscordInteractionResponseBuilder().AddEmbed(ModmailEmbeds.Success(title, message)); + } + + public static DiscordInteractionResponseBuilder Info(string title, string message = "") { + return new DiscordInteractionResponseBuilder().AddEmbed(ModmailEmbeds.Info(title, message)); + } + + public static DiscordInteractionResponseBuilder Warning(string title, string message = "") { + return new DiscordInteractionResponseBuilder().AddEmbed(ModmailEmbeds.Warning(title, message)); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Common/Static/ModmailWebhooks.cs b/src/Modmail.NET/Common/Static/ModmailWebhooks.cs new file mode 100644 index 00000000..6f91e4d0 --- /dev/null +++ b/src/Modmail.NET/Common/Static/ModmailWebhooks.cs @@ -0,0 +1,22 @@ +using DSharpPlus.Entities; + +namespace Modmail.NET.Common.Static; + +public static class ModmailWebhooks +{ + public static DiscordWebhookBuilder Error(string title, string message = "") { + return new DiscordWebhookBuilder().AddEmbed(ModmailEmbeds.Error(title, message)); + } + + public static DiscordWebhookBuilder Success(string title, string message = "") { + return new DiscordWebhookBuilder().AddEmbed(ModmailEmbeds.Success(title, message)); + } + + public static DiscordWebhookBuilder Info(string title, string message = "") { + return new DiscordWebhookBuilder().AddEmbed(ModmailEmbeds.Info(title, message)); + } + + public static DiscordWebhookBuilder Warning(string title, string message = "") { + return new DiscordWebhookBuilder().AddEmbed(ModmailEmbeds.Warning(title, message)); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Utils/UtilAttachment.cs b/src/Modmail.NET/Common/Utils/UtilAttachment.cs similarity index 56% rename from src/Modmail.NET/Utils/UtilAttachment.cs rename to src/Modmail.NET/Common/Utils/UtilAttachment.cs index c641b103..02540742 100644 --- a/src/Modmail.NET/Utils/UtilAttachment.cs +++ b/src/Modmail.NET/Common/Utils/UtilAttachment.cs @@ -1,6 +1,7 @@ -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Services; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilAttachment { @@ -13,13 +14,13 @@ public static string GetUri(Guid id) { } public static string GetLocalPath(TicketMessageAttachment attachment) { - var uri = new Uri(Const.AttachmentDownloadDirectory, UriKind.RelativeOrAbsolute); + var uri = new Uri(TicketAttachmentDownloadService.AttachmentDownloadDirectory, UriKind.RelativeOrAbsolute); return uri + "/" + attachment.Id + Path.GetExtension(attachment.FileName); } public static string[] GetAllFiles() { - return !Directory.Exists(Const.AttachmentDownloadDirectory) + return !Directory.Exists(TicketAttachmentDownloadService.AttachmentDownloadDirectory) ? [] - : Directory.GetFiles(Const.AttachmentDownloadDirectory); + : Directory.GetFiles(TicketAttachmentDownloadService.AttachmentDownloadDirectory); } } \ No newline at end of file diff --git a/src/Modmail.NET/Utils/UtilCache.cs b/src/Modmail.NET/Common/Utils/UtilCache.cs similarity index 96% rename from src/Modmail.NET/Utils/UtilCache.cs rename to src/Modmail.NET/Common/Utils/UtilCache.cs index 59a31321..034afed3 100644 --- a/src/Modmail.NET/Utils/UtilCache.cs +++ b/src/Modmail.NET/Common/Utils/UtilCache.cs @@ -1,7 +1,7 @@ using System.Text; using System.Text.Json; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilCache { diff --git a/src/Modmail.NET/Utils/UtilChannelTopic.cs b/src/Modmail.NET/Common/Utils/UtilChannelTopic.cs similarity index 94% rename from src/Modmail.NET/Utils/UtilChannelTopic.cs rename to src/Modmail.NET/Common/Utils/UtilChannelTopic.cs index 058432d7..64d6b596 100644 --- a/src/Modmail.NET/Utils/UtilChannelTopic.cs +++ b/src/Modmail.NET/Common/Utils/UtilChannelTopic.cs @@ -1,6 +1,6 @@ using Serilog; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilChannelTopic diff --git a/src/Modmail.NET/Utils/UtilDate.cs b/src/Modmail.NET/Common/Utils/UtilDate.cs similarity index 72% rename from src/Modmail.NET/Utils/UtilDate.cs rename to src/Modmail.NET/Common/Utils/UtilDate.cs index 39484540..4aff16bb 100644 --- a/src/Modmail.NET/Utils/UtilDate.cs +++ b/src/Modmail.NET/Common/Utils/UtilDate.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilDate { diff --git a/src/Modmail.NET/Utils/UtilHash.cs b/src/Modmail.NET/Common/Utils/UtilHash.cs similarity index 95% rename from src/Modmail.NET/Utils/UtilHash.cs rename to src/Modmail.NET/Common/Utils/UtilHash.cs index 9a821d11..1ce1fae1 100644 --- a/src/Modmail.NET/Utils/UtilHash.cs +++ b/src/Modmail.NET/Common/Utils/UtilHash.cs @@ -1,7 +1,7 @@ using System.Security.Cryptography; using System.Text; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilHash { diff --git a/src/Modmail.NET/Utils/UtilInteraction.cs b/src/Modmail.NET/Common/Utils/UtilInteraction.cs similarity index 87% rename from src/Modmail.NET/Utils/UtilInteraction.cs rename to src/Modmail.NET/Common/Utils/UtilInteraction.cs index b03c4312..c82bd739 100644 --- a/src/Modmail.NET/Utils/UtilInteraction.cs +++ b/src/Modmail.NET/Common/Utils/UtilInteraction.cs @@ -1,6 +1,6 @@ -using Modmail.NET.Exceptions; +using Modmail.NET.Common.Exceptions; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilInteraction { diff --git a/src/Modmail.NET/Common/Utils/UtilMention.cs b/src/Modmail.NET/Common/Utils/UtilMention.cs new file mode 100644 index 00000000..5e30ec38 --- /dev/null +++ b/src/Modmail.NET/Common/Utils/UtilMention.cs @@ -0,0 +1,14 @@ +using System.Text; +using Modmail.NET.Features.Permission.Models; + +namespace Modmail.NET.Common.Utils; + +public static class UtilMention +{ + public static string GetMentionsMessageString(IEnumerable permissions) { + var sb = new StringBuilder(); + foreach (var perm in permissions) sb.AppendLine(perm.GetMention()); + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Utils/UtilPermission.cs b/src/Modmail.NET/Common/Utils/UtilPermission.cs similarity index 96% rename from src/Modmail.NET/Utils/UtilPermission.cs rename to src/Modmail.NET/Common/Utils/UtilPermission.cs index 39e1ce98..f962fd81 100644 --- a/src/Modmail.NET/Utils/UtilPermission.cs +++ b/src/Modmail.NET/Common/Utils/UtilPermission.cs @@ -1,7 +1,8 @@ using DSharpPlus.Entities; -using Modmail.NET.Models; +using Modmail.NET.Features.Permission.Models; +using Modmail.NET.Features.Teams.Static; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilPermission { diff --git a/src/Modmail.NET/Utils/UtilReadable.cs b/src/Modmail.NET/Common/Utils/UtilReadable.cs similarity index 98% rename from src/Modmail.NET/Utils/UtilReadable.cs rename to src/Modmail.NET/Common/Utils/UtilReadable.cs index 705572ec..950e613d 100644 --- a/src/Modmail.NET/Utils/UtilReadable.cs +++ b/src/Modmail.NET/Common/Utils/UtilReadable.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilReadable { diff --git a/src/Modmail.NET/Utils/UtilVersion.cs b/src/Modmail.NET/Common/Utils/UtilVersion.cs similarity index 95% rename from src/Modmail.NET/Utils/UtilVersion.cs rename to src/Modmail.NET/Common/Utils/UtilVersion.cs index 3e461c5d..58eae1f3 100644 --- a/src/Modmail.NET/Utils/UtilVersion.cs +++ b/src/Modmail.NET/Common/Utils/UtilVersion.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Modmail.NET.Utils; +namespace Modmail.NET.Common.Utils; public static class UtilVersion { diff --git a/src/Modmail.NET/Database/Abstract/IEntity.cs b/src/Modmail.NET/Database/Abstract/IEntity.cs new file mode 100644 index 00000000..86f11027 --- /dev/null +++ b/src/Modmail.NET/Database/Abstract/IEntity.cs @@ -0,0 +1,3 @@ +namespace Modmail.NET.Database.Abstract; + +public interface IEntity { } \ No newline at end of file diff --git a/src/Modmail.NET/Abstract/IGuidId.cs b/src/Modmail.NET/Database/Abstract/IGuidId.cs similarity index 59% rename from src/Modmail.NET/Abstract/IGuidId.cs rename to src/Modmail.NET/Database/Abstract/IGuidId.cs index 92f871f7..b15a8199 100644 --- a/src/Modmail.NET/Abstract/IGuidId.cs +++ b/src/Modmail.NET/Database/Abstract/IGuidId.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Abstract; +namespace Modmail.NET.Database.Abstract; public interface IGuidId { diff --git a/src/Modmail.NET/Abstract/IHasRegisterDate.cs b/src/Modmail.NET/Database/Abstract/IHasRegisterDate.cs similarity index 65% rename from src/Modmail.NET/Abstract/IHasRegisterDate.cs rename to src/Modmail.NET/Database/Abstract/IHasRegisterDate.cs index 7ed360d0..0d593d89 100644 --- a/src/Modmail.NET/Abstract/IHasRegisterDate.cs +++ b/src/Modmail.NET/Database/Abstract/IHasRegisterDate.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Abstract; +namespace Modmail.NET.Database.Abstract; public interface IHasRegisterDate { diff --git a/src/Modmail.NET/Abstract/IHasUpdateDate.cs b/src/Modmail.NET/Database/Abstract/IHasUpdateDate.cs similarity index 64% rename from src/Modmail.NET/Abstract/IHasUpdateDate.cs rename to src/Modmail.NET/Database/Abstract/IHasUpdateDate.cs index 298e0bba..b32fc1d8 100644 --- a/src/Modmail.NET/Abstract/IHasUpdateDate.cs +++ b/src/Modmail.NET/Database/Abstract/IHasUpdateDate.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Abstract; +namespace Modmail.NET.Database.Abstract; public interface IHasUpdateDate { diff --git a/src/Modmail.NET/Database/Configuration/DiscordUserInfoConfiguration.cs b/src/Modmail.NET/Database/Configuration/DiscordUserInfoConfiguration.cs index 9c761c80..f8dc2d14 100644 --- a/src/Modmail.NET/Database/Configuration/DiscordUserInfoConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/DiscordUserInfoConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/GuildOptionConfiguration.cs b/src/Modmail.NET/Database/Configuration/GuildOptionConfiguration.cs index 99da2377..58dc4c80 100644 --- a/src/Modmail.NET/Database/Configuration/GuildOptionConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/GuildOptionConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/GuildTeamConfiguration.cs b/src/Modmail.NET/Database/Configuration/GuildTeamConfiguration.cs index 16b1a88b..50c68ba3 100644 --- a/src/Modmail.NET/Database/Configuration/GuildTeamConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/GuildTeamConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/GuildTeamMemberConfiguration.cs b/src/Modmail.NET/Database/Configuration/GuildTeamMemberConfiguration.cs index fd342f14..4bd7b252 100644 --- a/src/Modmail.NET/Database/Configuration/GuildTeamMemberConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/GuildTeamMemberConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketBlacklistConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketBlacklistConfiguration.cs index ea233ce6..8bc1bb52 100644 --- a/src/Modmail.NET/Database/Configuration/TicketBlacklistConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketBlacklistConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketConfiguration.cs index c5502ec2..56f75009 100644 --- a/src/Modmail.NET/Database/Configuration/TicketConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketMessageAttachmentConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketMessageAttachmentConfiguration.cs index 0930183b..2297eadd 100644 --- a/src/Modmail.NET/Database/Configuration/TicketMessageAttachmentConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketMessageAttachmentConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketMessageConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketMessageConfiguration.cs index eb98a13d..57874700 100644 --- a/src/Modmail.NET/Database/Configuration/TicketMessageConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketMessageConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketNoteConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketNoteConfiguration.cs index 92e4c0fd..b71b3ade 100644 --- a/src/Modmail.NET/Database/Configuration/TicketNoteConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketNoteConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Database/Configuration/TicketTypeConfiguration.cs b/src/Modmail.NET/Database/Configuration/TicketTypeConfiguration.cs index fdaf3148..3bb7daa9 100644 --- a/src/Modmail.NET/Database/Configuration/TicketTypeConfiguration.cs +++ b/src/Modmail.NET/Database/Configuration/TicketTypeConfiguration.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Database.Configuration; diff --git a/src/Modmail.NET/Entities/DiscordUserInfo.cs b/src/Modmail.NET/Database/Entities/DiscordUserInfo.cs similarity index 91% rename from src/Modmail.NET/Entities/DiscordUserInfo.cs rename to src/Modmail.NET/Database/Entities/DiscordUserInfo.cs index 2cc17481..ef3eca10 100644 --- a/src/Modmail.NET/Entities/DiscordUserInfo.cs +++ b/src/Modmail.NET/Database/Entities/DiscordUserInfo.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using DSharpPlus.Entities; -using Modmail.NET.Abstract; -using Modmail.NET.Extensions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class DiscordUserInfo : IHasRegisterDate, IHasUpdateDate, diff --git a/src/Modmail.NET/Entities/GuildOption.cs b/src/Modmail.NET/Database/Entities/GuildOption.cs similarity index 73% rename from src/Modmail.NET/Entities/GuildOption.cs rename to src/Modmail.NET/Database/Entities/GuildOption.cs index a6bdf8bc..df9e946b 100644 --- a/src/Modmail.NET/Entities/GuildOption.cs +++ b/src/Modmail.NET/Database/Entities/GuildOption.cs @@ -1,7 +1,11 @@ using System.ComponentModel.DataAnnotations; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; +using Modmail.NET.Features.Metric.Static; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Ticket.Static; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class GuildOption : IHasRegisterDate, IHasUpdateDate, @@ -24,7 +28,7 @@ public class GuildOption : IHasRegisterDate, public required ulong CategoryId { get; set; } public bool IsEnabled { get; set; } = true; - [Range(Const.TicketTimeoutMinAllowedHours, Const.TicketTimeoutMaxAllowedHours)] + [Range(-1, TicketConstants.TicketTimeoutMaxAllowedHours)] public long TicketTimeoutHours { get; set; } = -1; public bool TakeFeedbackAfterClosing { get; set; } @@ -32,11 +36,11 @@ public class GuildOption : IHasRegisterDate, public bool PublicTranscripts { get; set; } public bool SendTranscriptLinkToUser { get; set; } - [Range(-1, Const.TicketDataDeleteWaitDaysMax)] + [Range(-1, TicketConstants.TicketDataDeleteWaitDaysMax)] public int TicketDataDeleteWaitDays { get; set; } = -1; - [Range(-1, Const.StatisticsCalculateDaysMax)] - public int StatisticsCalculateDays { get; set; } = Const.DefaultStatisticsCalculateDays; + [Range(MetricConstants.StatisticsCalculateDaysMin, MetricConstants.StatisticsCalculateDaysMax)] + public int StatisticsCalculateDays { get; set; } = MetricConstants.DefaultStatisticsCalculateDays; public TeamPermissionLevel ManageTicketMinAccessLevel { get; set; } = TeamPermissionLevel.Moderator; public TeamPermissionLevel ManageTeamsMinAccessLevel { get; set; } = TeamPermissionLevel.Admin; diff --git a/src/Modmail.NET/Entities/GuildTeam.cs b/src/Modmail.NET/Database/Entities/GuildTeam.cs similarity index 82% rename from src/Modmail.NET/Entities/GuildTeam.cs rename to src/Modmail.NET/Database/Entities/GuildTeam.cs index 1081e672..5d8ce59f 100644 --- a/src/Modmail.NET/Entities/GuildTeam.cs +++ b/src/Modmail.NET/Database/Entities/GuildTeam.cs @@ -1,7 +1,9 @@ using System.ComponentModel.DataAnnotations; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; +using Modmail.NET.Features.Permission.Static; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class GuildTeam : IHasRegisterDate, IHasUpdateDate, diff --git a/src/Modmail.NET/Entities/GuildTeamMember.cs b/src/Modmail.NET/Database/Entities/GuildTeamMember.cs similarity index 76% rename from src/Modmail.NET/Entities/GuildTeamMember.cs rename to src/Modmail.NET/Database/Entities/GuildTeamMember.cs index febac2e6..f13b9f37 100644 --- a/src/Modmail.NET/Entities/GuildTeamMember.cs +++ b/src/Modmail.NET/Database/Entities/GuildTeamMember.cs @@ -1,6 +1,7 @@ -using Modmail.NET.Abstract; +using Modmail.NET.Database.Abstract; +using Modmail.NET.Features.Teams.Static; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class GuildTeamMember : IHasRegisterDate, IEntity, diff --git a/src/Modmail.NET/Entities/Statistic.cs b/src/Modmail.NET/Database/Entities/Statistic.cs similarity index 92% rename from src/Modmail.NET/Entities/Statistic.cs rename to src/Modmail.NET/Database/Entities/Statistic.cs index 49bcb4e5..79d35fa1 100644 --- a/src/Modmail.NET/Entities/Statistic.cs +++ b/src/Modmail.NET/Database/Entities/Statistic.cs @@ -1,7 +1,7 @@ using Microsoft.EntityFrameworkCore; -using Modmail.NET.Abstract; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class Statistic : IHasRegisterDate, IEntity, diff --git a/src/Modmail.NET/Database/Entities/Tag.cs b/src/Modmail.NET/Database/Entities/Tag.cs new file mode 100644 index 00000000..7119fa90 --- /dev/null +++ b/src/Modmail.NET/Database/Entities/Tag.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; + +namespace Modmail.NET.Database.Entities; + +[Index(nameof(Name), IsUnique = true)] +public class Tag : IHasRegisterDate, + IHasUpdateDate, + IEntity, + IGuidId +{ + [MaxLength(DbLength.TagName)] + [Required] + public required string Name { get; set; } + + [StringLength(DbLength.TagTitle)] + public required string Title { get; set; } = null; + + [Required] + public required string Content { get; set; } + + public Guid Id { get; set; } + public DateTime RegisterDateUtc { get; set; } + public DateTime? UpdateDateUtc { get; set; } +} \ No newline at end of file diff --git a/src/Modmail.NET/Entities/Ticket.cs b/src/Modmail.NET/Database/Entities/Ticket.cs similarity index 89% rename from src/Modmail.NET/Entities/Ticket.cs rename to src/Modmail.NET/Database/Entities/Ticket.cs index d5fabfa6..29068e33 100644 --- a/src/Modmail.NET/Entities/Ticket.cs +++ b/src/Modmail.NET/Database/Entities/Ticket.cs @@ -1,9 +1,11 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Modmail.NET.Abstract; -using Modmail.NET.Utils; +using Modmail.NET.Common.Static; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Abstract; +using Modmail.NET.Features.Ticket.Static; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class Ticket : IHasRegisterDate, IEntity, diff --git a/src/Modmail.NET/Entities/TicketBlacklist.cs b/src/Modmail.NET/Database/Entities/TicketBlacklist.cs similarity index 79% rename from src/Modmail.NET/Entities/TicketBlacklist.cs rename to src/Modmail.NET/Database/Entities/TicketBlacklist.cs index 9c3760f6..9fd17c6e 100644 --- a/src/Modmail.NET/Entities/TicketBlacklist.cs +++ b/src/Modmail.NET/Database/Entities/TicketBlacklist.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class TicketBlacklist : IHasRegisterDate, IEntity, diff --git a/src/Modmail.NET/Entities/TicketMessage.cs b/src/Modmail.NET/Database/Entities/TicketMessage.cs similarity index 79% rename from src/Modmail.NET/Entities/TicketMessage.cs rename to src/Modmail.NET/Database/Entities/TicketMessage.cs index a112b8f6..c3fc1d77 100644 --- a/src/Modmail.NET/Entities/TicketMessage.cs +++ b/src/Modmail.NET/Database/Entities/TicketMessage.cs @@ -1,10 +1,12 @@ using System.ComponentModel.DataAnnotations; using DSharpPlus.Entities; -using Modmail.NET.Abstract; -using Modmail.NET.Exceptions; -using Modmail.NET.Utils; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Static; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Abstract; +using Modmail.NET.Features.Ticket.Static; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class TicketMessage : IHasRegisterDate, IEntity @@ -20,8 +22,11 @@ public class TicketMessage : IHasRegisterDate, public required bool SentByMod { get; set; } + public TicketMessageChangeStatus ChangeStatus { get; set; } = TicketMessageChangeStatus.None; + //FK - public List Attachments { get; set; } + public virtual List Attachments { get; set; } + public virtual List History { get; set; } public ulong BotMessageId { get; set; } public DateTime RegisterDateUtc { get; set; } diff --git a/src/Modmail.NET/Entities/TicketMessageAttachment.cs b/src/Modmail.NET/Database/Entities/TicketMessageAttachment.cs similarity index 91% rename from src/Modmail.NET/Entities/TicketMessageAttachment.cs rename to src/Modmail.NET/Database/Entities/TicketMessageAttachment.cs index f72e42dd..17abc2f7 100644 --- a/src/Modmail.NET/Entities/TicketMessageAttachment.cs +++ b/src/Modmail.NET/Database/Entities/TicketMessageAttachment.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations; using DSharpPlus.Entities; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class TicketMessageAttachment : IEntity, IGuidId diff --git a/src/Modmail.NET/Database/Entities/TicketMessageHistory.cs b/src/Modmail.NET/Database/Entities/TicketMessageHistory.cs new file mode 100644 index 00000000..c5741198 --- /dev/null +++ b/src/Modmail.NET/Database/Entities/TicketMessageHistory.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; + +namespace Modmail.NET.Database.Entities; + +public class TicketMessageHistory : IHasRegisterDate, + IEntity +{ + public Guid Id { get; set; } + + [MaxLength(DbLength.Message)] + public required string MessageContentBefore { get; set; } = null; + + [MaxLength(DbLength.Message)] + public required string MessageContentAfter { get; set; } = null; + + public required Guid TicketMessageId { get; set; } + + //FK + public virtual TicketMessage TicketMessage { get; set; } + public DateTime RegisterDateUtc { get; set; } +} \ No newline at end of file diff --git a/src/Modmail.NET/Entities/TicketNote.cs b/src/Modmail.NET/Database/Entities/TicketNote.cs similarity index 79% rename from src/Modmail.NET/Entities/TicketNote.cs rename to src/Modmail.NET/Database/Entities/TicketNote.cs index 2932fb40..5f40390f 100644 --- a/src/Modmail.NET/Entities/TicketNote.cs +++ b/src/Modmail.NET/Database/Entities/TicketNote.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class TicketNote : IHasRegisterDate, IEntity, diff --git a/src/Modmail.NET/Entities/TicketType.cs b/src/Modmail.NET/Database/Entities/TicketType.cs similarity index 89% rename from src/Modmail.NET/Entities/TicketType.cs rename to src/Modmail.NET/Database/Entities/TicketType.cs index ffef8c97..426ea45e 100644 --- a/src/Modmail.NET/Entities/TicketType.cs +++ b/src/Modmail.NET/Database/Entities/TicketType.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Abstract; -namespace Modmail.NET.Entities; +namespace Modmail.NET.Database.Entities; public class TicketType : IHasRegisterDate, IHasUpdateDate, diff --git a/src/Modmail.NET/Migrations/20240827120247_Initial.Designer.cs b/src/Modmail.NET/Database/Migrations/20240827120247_Initial.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827120247_Initial.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240827120247_Initial.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240827120247_Initial.cs b/src/Modmail.NET/Database/Migrations/20240827120247_Initial.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827120247_Initial.cs rename to src/Modmail.NET/Database/Migrations/20240827120247_Initial.cs diff --git a/src/Modmail.NET/Migrations/20240827121750_Ticket_AssignedUser.Designer.cs b/src/Modmail.NET/Database/Migrations/20240827121750_Ticket_AssignedUser.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827121750_Ticket_AssignedUser.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240827121750_Ticket_AssignedUser.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240827121750_Ticket_AssignedUser.cs b/src/Modmail.NET/Database/Migrations/20240827121750_Ticket_AssignedUser.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827121750_Ticket_AssignedUser.cs rename to src/Modmail.NET/Database/Migrations/20240827121750_Ticket_AssignedUser.cs diff --git a/src/Modmail.NET/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.Designer.cs b/src/Modmail.NET/Database/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.cs b/src/Modmail.NET/Database/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.cs rename to src/Modmail.NET/Database/Migrations/20240827153553_GuildOption_Removed_GreetingAndClosingMessage.cs diff --git a/src/Modmail.NET/Migrations/20240828104105_RemovedUnusedFK.Designer.cs b/src/Modmail.NET/Database/Migrations/20240828104105_RemovedUnusedFK.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240828104105_RemovedUnusedFK.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240828104105_RemovedUnusedFK.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240828104105_RemovedUnusedFK.cs b/src/Modmail.NET/Database/Migrations/20240828104105_RemovedUnusedFK.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240828104105_RemovedUnusedFK.cs rename to src/Modmail.NET/Database/Migrations/20240828104105_RemovedUnusedFK.cs diff --git a/src/Modmail.NET/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.Designer.cs b/src/Modmail.NET/Database/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.cs b/src/Modmail.NET/Database/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.cs rename to src/Modmail.NET/Database/Migrations/20240830090726_GuildTeamMember_Removed_UpdateDateUtc.cs diff --git a/src/Modmail.NET/Migrations/20240901191906_TicketType_Added_InEnabled.Designer.cs b/src/Modmail.NET/Database/Migrations/20240901191906_TicketType_Added_InEnabled.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240901191906_TicketType_Added_InEnabled.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240901191906_TicketType_Added_InEnabled.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240901191906_TicketType_Added_InEnabled.cs b/src/Modmail.NET/Database/Migrations/20240901191906_TicketType_Added_InEnabled.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240901191906_TicketType_Added_InEnabled.cs rename to src/Modmail.NET/Database/Migrations/20240901191906_TicketType_Added_InEnabled.cs diff --git a/src/Modmail.NET/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.Designer.cs b/src/Modmail.NET/Database/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.cs b/src/Modmail.NET/Database/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.cs rename to src/Modmail.NET/Database/Migrations/20240907082958_TicketType_Updated_EmbedIsNullable.cs diff --git a/src/Modmail.NET/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.Designer.cs b/src/Modmail.NET/Database/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.cs b/src/Modmail.NET/Database/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.cs rename to src/Modmail.NET/Database/Migrations/20240907090419_TicketType_Update_DescriptionIsRequired.cs diff --git a/src/Modmail.NET/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.Designer.cs b/src/Modmail.NET/Database/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.Designer.cs rename to src/Modmail.NET/Database/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.Designer.cs diff --git a/src/Modmail.NET/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.cs b/src/Modmail.NET/Database/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.cs similarity index 100% rename from src/Modmail.NET/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.cs rename to src/Modmail.NET/Database/Migrations/20240907091824_GuildOption_AddedCol_IsEnableDiscordChannelLogging.cs diff --git a/src/Modmail.NET/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.Designer.cs b/src/Modmail.NET/Database/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.cs b/src/Modmail.NET/Database/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.cs rename to src/Modmail.NET/Database/Migrations/20250317224506_GuildOption_AddedColumn_AlwaysAnonymous.cs diff --git a/src/Modmail.NET/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.Designer.cs b/src/Modmail.NET/Database/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.cs b/src/Modmail.NET/Database/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.cs rename to src/Modmail.NET/Database/Migrations/20250317230425_GuildOption_ColumnAdded_SlashCommandDisableColumns.cs diff --git a/src/Modmail.NET/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.Designer.cs b/src/Modmail.NET/Database/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.cs b/src/Modmail.NET/Database/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.cs rename to src/Modmail.NET/Database/Migrations/20250319000242_GuildOption_ColAdded_AllowUsersToCloseTickets.cs diff --git a/src/Modmail.NET/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.Designer.cs b/src/Modmail.NET/Database/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.cs b/src/Modmail.NET/Database/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.cs rename to src/Modmail.NET/Database/Migrations/20250319013409_GuildOption_AddedAvarageResponseTime.cs diff --git a/src/Modmail.NET/Migrations/20250319030317_GuildOption_AddedCols_AvgData.Designer.cs b/src/Modmail.NET/Database/Migrations/20250319030317_GuildOption_AddedCols_AvgData.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319030317_GuildOption_AddedCols_AvgData.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250319030317_GuildOption_AddedCols_AvgData.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250319030317_GuildOption_AddedCols_AvgData.cs b/src/Modmail.NET/Database/Migrations/20250319030317_GuildOption_AddedCols_AvgData.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250319030317_GuildOption_AddedCols_AvgData.cs rename to src/Modmail.NET/Database/Migrations/20250319030317_GuildOption_AddedCols_AvgData.cs diff --git a/src/Modmail.NET/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.Designer.cs b/src/Modmail.NET/Database/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.cs b/src/Modmail.NET/Database/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.cs rename to src/Modmail.NET/Database/Migrations/20250321163931_TicketMessageAttachment_ByteContent_Removed.cs diff --git a/src/Modmail.NET/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.Designer.cs b/src/Modmail.NET/Database/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.cs b/src/Modmail.NET/Database/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.cs rename to src/Modmail.NET/Database/Migrations/20250321164005_GuildOption_IconUrl_Nullable_BugFix.cs diff --git a/src/Modmail.NET/Migrations/20250322125230_GuildOption_RemovedSomeColumns.Designer.cs b/src/Modmail.NET/Database/Migrations/20250322125230_GuildOption_RemovedSomeColumns.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322125230_GuildOption_RemovedSomeColumns.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250322125230_GuildOption_RemovedSomeColumns.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250322125230_GuildOption_RemovedSomeColumns.cs b/src/Modmail.NET/Database/Migrations/20250322125230_GuildOption_RemovedSomeColumns.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322125230_GuildOption_RemovedSomeColumns.cs rename to src/Modmail.NET/Database/Migrations/20250322125230_GuildOption_RemovedSomeColumns.cs diff --git a/src/Modmail.NET/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.Designer.cs b/src/Modmail.NET/Database/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.cs b/src/Modmail.NET/Database/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.cs rename to src/Modmail.NET/Database/Migrations/20250322184829_GuildOption_AddedCol_PermissionLevel.cs diff --git a/src/Modmail.NET/Migrations/20250322225844_TicketMessageSentByModColAdded.Designer.cs b/src/Modmail.NET/Database/Migrations/20250322225844_TicketMessageSentByModColAdded.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322225844_TicketMessageSentByModColAdded.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250322225844_TicketMessageSentByModColAdded.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250322225844_TicketMessageSentByModColAdded.cs b/src/Modmail.NET/Database/Migrations/20250322225844_TicketMessageSentByModColAdded.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250322225844_TicketMessageSentByModColAdded.cs rename to src/Modmail.NET/Database/Migrations/20250322225844_TicketMessageSentByModColAdded.cs diff --git a/src/Modmail.NET/Migrations/20250323013715_StatisticTableAdded.Designer.cs b/src/Modmail.NET/Database/Migrations/20250323013715_StatisticTableAdded.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323013715_StatisticTableAdded.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250323013715_StatisticTableAdded.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250323013715_StatisticTableAdded.cs b/src/Modmail.NET/Database/Migrations/20250323013715_StatisticTableAdded.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323013715_StatisticTableAdded.cs rename to src/Modmail.NET/Database/Migrations/20250323013715_StatisticTableAdded.cs diff --git a/src/Modmail.NET/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.Designer.cs b/src/Modmail.NET/Database/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.cs b/src/Modmail.NET/Database/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.cs rename to src/Modmail.NET/Database/Migrations/20250323173554_GuildTeamAddedColAllowAccessToWebPanel.cs diff --git a/src/Modmail.NET/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.Designer.cs b/src/Modmail.NET/Database/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.cs b/src/Modmail.NET/Database/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.cs rename to src/Modmail.NET/Database/Migrations/20250323192315_GuildOption_RemovedFeatureOptions.cs diff --git a/src/Modmail.NET/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.Designer.cs b/src/Modmail.NET/Database/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.cs b/src/Modmail.NET/Database/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.cs rename to src/Modmail.NET/Database/Migrations/20250323192910_GuildOptionRemoveOptionPermAccessLevel.cs diff --git a/src/Modmail.NET/Migrations/20250326132054_StatisticUpdatedColNames.Designer.cs b/src/Modmail.NET/Database/Migrations/20250326132054_StatisticUpdatedColNames.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250326132054_StatisticUpdatedColNames.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250326132054_StatisticUpdatedColNames.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250326132054_StatisticUpdatedColNames.cs b/src/Modmail.NET/Database/Migrations/20250326132054_StatisticUpdatedColNames.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250326132054_StatisticUpdatedColNames.cs rename to src/Modmail.NET/Database/Migrations/20250326132054_StatisticUpdatedColNames.cs diff --git a/src/Modmail.NET/Migrations/20250326235413_StatisticRenameResolvedTickets.Designer.cs b/src/Modmail.NET/Database/Migrations/20250326235413_StatisticRenameResolvedTickets.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250326235413_StatisticRenameResolvedTickets.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250326235413_StatisticRenameResolvedTickets.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250326235413_StatisticRenameResolvedTickets.cs b/src/Modmail.NET/Database/Migrations/20250326235413_StatisticRenameResolvedTickets.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250326235413_StatisticRenameResolvedTickets.cs rename to src/Modmail.NET/Database/Migrations/20250326235413_StatisticRenameResolvedTickets.cs diff --git a/src/Modmail.NET/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.Designer.cs b/src/Modmail.NET/Database/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.cs b/src/Modmail.NET/Database/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.cs rename to src/Modmail.NET/Database/Migrations/20250329182313_GuildOptionRemovedShowConfirmationOnCloseCol.cs diff --git a/src/Modmail.NET/Migrations/20250329204247_GuildOptionPublicTranscripts.Designer.cs b/src/Modmail.NET/Database/Migrations/20250329204247_GuildOptionPublicTranscripts.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329204247_GuildOptionPublicTranscripts.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250329204247_GuildOptionPublicTranscripts.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250329204247_GuildOptionPublicTranscripts.cs b/src/Modmail.NET/Database/Migrations/20250329204247_GuildOptionPublicTranscripts.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329204247_GuildOptionPublicTranscripts.cs rename to src/Modmail.NET/Database/Migrations/20250329204247_GuildOptionPublicTranscripts.cs diff --git a/src/Modmail.NET/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.Designer.cs b/src/Modmail.NET/Database/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.cs b/src/Modmail.NET/Database/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.cs rename to src/Modmail.NET/Database/Migrations/20250329213018_TicketMessageLengthExtendedToNoLimit.cs diff --git a/src/Modmail.NET/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.Designer.cs b/src/Modmail.NET/Database/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.cs b/src/Modmail.NET/Database/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.cs rename to src/Modmail.NET/Database/Migrations/20250329223813_GuildOptionSendTranscriptLinkToUserColAdded.cs diff --git a/src/Modmail.NET/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.Designer.cs b/src/Modmail.NET/Database/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.cs b/src/Modmail.NET/Database/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.cs rename to src/Modmail.NET/Database/Migrations/20250406154808_TicketMessageAddedColumnBotMessageId.cs diff --git a/src/Modmail.NET/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.Designer.cs b/src/Modmail.NET/Database/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.cs b/src/Modmail.NET/Database/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.cs rename to src/Modmail.NET/Database/Migrations/20250406163216_GuildOptionAddedColTicketAutoDeleteWaitDays.cs diff --git a/src/Modmail.NET/Migrations/20250406180016_Constraints.Designer.cs b/src/Modmail.NET/Database/Migrations/20250406180016_Constraints.Designer.cs similarity index 99% rename from src/Modmail.NET/Migrations/20250406180016_Constraints.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250406180016_Constraints.Designer.cs index 0b13bdb1..0e296024 100644 --- a/src/Modmail.NET/Migrations/20250406180016_Constraints.Designer.cs +++ b/src/Modmail.NET/Database/Migrations/20250406180016_Constraints.Designer.cs @@ -137,7 +137,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); - t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN 1 AND 365"); + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); }); }); diff --git a/src/Modmail.NET/Migrations/20250406180016_Constraints.cs b/src/Modmail.NET/Database/Migrations/20250406180016_Constraints.cs similarity index 98% rename from src/Modmail.NET/Migrations/20250406180016_Constraints.cs rename to src/Modmail.NET/Database/Migrations/20250406180016_Constraints.cs index cdbb546f..3d7e5c5e 100644 --- a/src/Modmail.NET/Migrations/20250406180016_Constraints.cs +++ b/src/Modmail.NET/Database/Migrations/20250406180016_Constraints.cs @@ -73,7 +73,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.AddCheckConstraint( name: "CK_GuildOptions_TicketDataDeleteWaitDays_Range", table: "GuildOptions", - sql: "[TicketDataDeleteWaitDays] BETWEEN 1 AND 365"); + sql: "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); migrationBuilder.AddCheckConstraint( name: "CK_DiscordUserInfos_Username_MinLength", diff --git a/src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs b/src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs similarity index 99% rename from src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs index 69f678db..4b921123 100644 --- a/src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs +++ b/src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.Designer.cs @@ -142,7 +142,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); - t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN 1 AND 365"); + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); }); }); diff --git a/src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs b/src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs similarity index 88% rename from src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs rename to src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs index e0206cb4..62ecb0a9 100644 --- a/src/Modmail.NET/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs +++ b/src/Modmail.NET/Database/Migrations/20250406180146_GuildOptionAddedColStatisticsCalculateDays.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Metric.Static; #nullable disable @@ -15,7 +17,7 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "GuildOptions", type: "int", nullable: false, - defaultValue: Const.DefaultStatisticsCalculateDays); + defaultValue: MetricConstants.DefaultStatisticsCalculateDays); migrationBuilder.AddCheckConstraint( name: "CK_GuildOptions_StatisticsCalculateDays_Range", diff --git a/src/Modmail.NET/Migrations/20250406181350_GuildOptionConstraintFixMinValues.Designer.cs b/src/Modmail.NET/Database/Migrations/20250406181350_GuildOptionConstraintFixMinValues.Designer.cs similarity index 100% rename from src/Modmail.NET/Migrations/20250406181350_GuildOptionConstraintFixMinValues.Designer.cs rename to src/Modmail.NET/Database/Migrations/20250406181350_GuildOptionConstraintFixMinValues.Designer.cs diff --git a/src/Modmail.NET/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs b/src/Modmail.NET/Database/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs similarity index 96% rename from src/Modmail.NET/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs rename to src/Modmail.NET/Database/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs index 5ec283c9..7bb833eb 100644 --- a/src/Modmail.NET/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs +++ b/src/Modmail.NET/Database/Migrations/20250406181350_GuildOptionConstraintFixMinValues.cs @@ -48,7 +48,7 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.AddCheckConstraint( name: "CK_GuildOptions_TicketDataDeleteWaitDays_Range", table: "GuildOptions", - sql: "[TicketDataDeleteWaitDays] BETWEEN 1 AND 365"); + sql: "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); } } } diff --git a/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.Designer.cs b/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.Designer.cs new file mode 100644 index 00000000..a06d0ac7 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.Designer.cs @@ -0,0 +1,679 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250407174400_TicketMessageHistoryTableAdded")] + partial class TicketMessageHistoryTableAdded + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN -1 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany() + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.cs b/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.cs new file mode 100644 index 00000000..ebe27a0b --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250407174400_TicketMessageHistoryTableAdded.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TicketMessageHistoryTableAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TicketMessageHistory", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + MessageContentBefore = table.Column(type: "nvarchar(max)", maxLength: 2147483647, nullable: true), + MessageContentAfter = table.Column(type: "nvarchar(max)", maxLength: 2147483647, nullable: true), + TicketMessageId = table.Column(type: "uniqueidentifier", nullable: false), + RegisterDateUtc = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TicketMessageHistory", x => x.Id); + table.ForeignKey( + name: "FK_TicketMessageHistory_TicketMessages_TicketMessageId", + column: x => x.TicketMessageId, + principalTable: "TicketMessages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_TicketMessageHistory_TicketMessageId", + table: "TicketMessageHistory", + column: "TicketMessageId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TicketMessageHistory"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.Designer.cs b/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.Designer.cs new file mode 100644 index 00000000..23d3181f --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.Designer.cs @@ -0,0 +1,684 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250407174928_TicketMessageAddedColChangeStatus")] + partial class TicketMessageAddedColChangeStatus + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN -1 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.cs b/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.cs new file mode 100644 index 00000000..8c261a17 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250407174928_TicketMessageAddedColChangeStatus.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TicketMessageAddedColChangeStatus : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ChangeStatus", + table: "TicketMessages", + type: "int", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ChangeStatus", + table: "TicketMessages"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.Designer.cs b/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.Designer.cs new file mode 100644 index 00000000..35532624 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.Designer.cs @@ -0,0 +1,684 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250408104845_GuildOptionFixConstrainMinMaxValues")] + partial class GuildOptionFixConstrainMinMaxValues + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.cs b/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.cs new file mode 100644 index 00000000..a11f1ba8 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408104845_GuildOptionFixConstrainMinMaxValues.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class GuildOptionFixConstrainMinMaxValues : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_GuildOptions_StatisticsCalculateDays_Range", + table: "GuildOptions"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GuildOptions_StatisticsCalculateDays_Range", + table: "GuildOptions", + sql: "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_GuildOptions_StatisticsCalculateDays_Range", + table: "GuildOptions"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GuildOptions_StatisticsCalculateDays_Range", + table: "GuildOptions", + sql: "[StatisticsCalculateDays] BETWEEN -1 AND 365"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.Designer.cs b/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.Designer.cs new file mode 100644 index 00000000..c2a53b2e --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.Designer.cs @@ -0,0 +1,708 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250408155322_TagTable")] + partial class TagTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Shortcut") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.cs b/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.cs new file mode 100644 index 00000000..127ddf8b --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408155322_TagTable.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TagTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Tags", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Shortcut = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: true), + Content = table.Column(type: "nvarchar(max)", nullable: true), + RegisterDateUtc = table.Column(type: "datetime2", nullable: false), + UpdateDateUtc = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tags", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tags"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.Designer.cs b/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.Designer.cs new file mode 100644 index 00000000..f31fae73 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.Designer.cs @@ -0,0 +1,715 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName")] + partial class TagsUpdated_AddedColTitle_RenamedShortcutToName + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Title") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Tags", t => + { + t.HasCheckConstraint("CK_Tags_Title_MinLength", "LEN([Title]) >= 0"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.cs b/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.cs new file mode 100644 index 00000000..d646d04a --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408162652_TagsUpdated_AddedColTitle_RenamedShortcutToName.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TagsUpdated_AddedColTitle_RenamedShortcutToName : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Shortcut", + table: "Tags", + newName: "Name"); + + migrationBuilder.AddColumn( + name: "Title", + table: "Tags", + type: "nvarchar(64)", + maxLength: 64, + nullable: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tags_Title_MinLength", + table: "Tags", + sql: "LEN([Title]) >= 0"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_Tags_Title_MinLength", + table: "Tags"); + + migrationBuilder.DropColumn( + name: "Title", + table: "Tags"); + + migrationBuilder.RenameColumn( + name: "Name", + table: "Tags", + newName: "Shortcut"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.Designer.cs b/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.Designer.cs new file mode 100644 index 00000000..0f0f3af3 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.Designer.cs @@ -0,0 +1,719 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250408171908_TagNameUniqueIndex")] + partial class TagNameUniqueIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Title") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("Tags", t => + { + t.HasCheckConstraint("CK_Tags_Title_MinLength", "LEN([Title]) >= 0"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.cs b/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.cs new file mode 100644 index 00000000..926f024b --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408171908_TagNameUniqueIndex.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TagNameUniqueIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_Tags_Name", + table: "Tags", + column: "Name", + unique: true, + filter: "[Name] IS NOT NULL"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Tags_Name", + table: "Tags"); + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.Designer.cs b/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.Designer.cs new file mode 100644 index 00000000..7a1fb6f4 --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.Designer.cs @@ -0,0 +1,724 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Modmail.NET.Database; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + [DbContext(typeof(ModmailDbContext))] + [Migration("20250408171950_TagRequiredAttribute")] + partial class TagRequiredAttribute + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Property("Id") + .HasColumnType("decimal(20,0)"); + + b.Property("AvatarUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Locale") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DiscordUserInfos", t => + { + t.HasCheckConstraint("CK_DiscordUserInfos_Username_MinLength", "LEN([Username]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => + { + b.Property("GuildId") + .HasColumnType("decimal(20,0)"); + + b.Property("AlwaysAnonymous") + .HasColumnType("bit"); + + b.Property("BannerUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CategoryId") + .HasColumnType("decimal(20,0)"); + + b.Property("IconUrl") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LogChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("ManageBlacklistMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageHangfireMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTeamsMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketMinAccessLevel") + .HasColumnType("int"); + + b.Property("ManageTicketTypeMinAccessLevel") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTranscripts") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SendTranscriptLinkToUser") + .HasColumnType("bit"); + + b.Property("StatisticsCalculateDays") + .HasColumnType("int"); + + b.Property("TakeFeedbackAfterClosing") + .HasColumnType("bit"); + + b.Property("TicketDataDeleteWaitDays") + .HasColumnType("int"); + + b.Property("TicketTimeoutHours") + .HasColumnType("bigint"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("GuildId"); + + b.ToTable("GuildOptions", t => + { + t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); + + t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAccessToWebPanel") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PermissionLevel") + .HasColumnType("int"); + + b.Property("PingOnNewMessage") + .HasColumnType("bit"); + + b.Property("PingOnNewTicket") + .HasColumnType("bit"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("GuildTeams", t => + { + t.HasCheckConstraint("CK_GuildTeams_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("GuildTeamId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GuildTeamId"); + + b.ToTable("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvgResponseTimeSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketClosedSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsClosedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("AvgTicketsOpenedPerDay") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("FastestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SlowestClosedTicketSeconds") + .HasPrecision(2) + .HasColumnType("float(2)"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Title") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tags", t => + { + t.HasCheckConstraint("CK_Tags_Content_MinLength", "LEN([Content]) >= 1"); + + t.HasCheckConstraint("CK_Tags_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_Tags_Title_MinLength", "LEN([Title]) >= 0"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Anonymous") + .HasColumnType("bit"); + + b.Property("AssignedUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("BotTicketCreatedMessageInDmId") + .HasColumnType("decimal(20,0)"); + + b.Property("CloseReason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ClosedDateUtc") + .HasColumnType("datetime2"); + + b.Property("CloserUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("FeedbackMessage") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("FeedbackStar") + .HasColumnType("int"); + + b.Property("InitialMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("IsForcedClosed") + .HasColumnType("bit"); + + b.Property("LastMessageDateUtc") + .HasColumnType("datetime2"); + + b.Property("ModMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("OpenerUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PrivateMessageChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketTypeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("CloserUserId"); + + b.HasIndex("OpenerUserId"); + + b.HasIndex("TicketTypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("Reason") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DiscordUserId") + .IsUnique(); + + b.ToTable("TicketBlacklists"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BotMessageId") + .HasColumnType("decimal(20,0)"); + + b.Property("ChangeStatus") + .HasColumnType("int"); + + b.Property("MessageContent") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageDiscordId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("SenderUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("SentByMod") + .HasColumnType("bit"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SenderUserId"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketMessages"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("MediaType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ProxyUrl") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageAttachments", t => + { + t.HasCheckConstraint("CK_TicketMessageAttachments_FileName_MinLength", "LEN([FileName]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_MediaType_MinLength", "LEN([MediaType]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_ProxyUrl_MinLength", "LEN([ProxyUrl]) >= 1"); + + t.HasCheckConstraint("CK_TicketMessageAttachments_Url_MinLength", "LEN([Url]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DiscordUserId") + .HasColumnType("decimal(20,0)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketId"); + + b.ToTable("TicketNotes", t => + { + t.HasCheckConstraint("CK_TicketNotes_Content_MinLength", "LEN([Content]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageContent") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmbedMessageTitle") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Emoji") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("TicketTypes", t => + { + t.HasCheckConstraint("CK_TicketTypes_Description_MinLength", "LEN([Description]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Key_MinLength", "LEN([Key]) >= 1"); + + t.HasCheckConstraint("CK_TicketTypes_Name_MinLength", "LEN([Name]) >= 1"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => + { + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") + .WithMany("GuildTeamMembers") + .HasForeignKey("GuildTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuildTeam"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") + .WithMany("AssignedTickets") + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") + .WithMany("ClosedTickets") + .HasForeignKey("CloserUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") + .WithMany("OpenedTickets") + .HasForeignKey("OpenerUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") + .WithMany() + .HasForeignKey("TicketTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedUser"); + + b.Navigation("CloserUser"); + + b.Navigation("OpenerUser"); + + b.Navigation("TicketType"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") + .WithMany() + .HasForeignKey("DiscordUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscordUser"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) + .WithMany() + .HasForeignKey("SenderUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) + .WithMany("Attachments") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) + .WithMany("TicketNotes") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => + { + b.Navigation("AssignedTickets"); + + b.Navigation("ClosedTickets"); + + b.Navigation("OpenedTickets"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => + { + b.Navigation("GuildTeamMembers"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => + { + b.Navigation("Messages"); + + b.Navigation("TicketNotes"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.cs b/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.cs new file mode 100644 index 00000000..f258666a --- /dev/null +++ b/src/Modmail.NET/Database/Migrations/20250408171950_TagRequiredAttribute.cs @@ -0,0 +1,97 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Modmail.NET.Database.Migrations +{ + /// + public partial class TagRequiredAttribute : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Tags_Name", + table: "Tags"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Tags", + type: "nvarchar(32)", + maxLength: 32, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(32)", + oldMaxLength: 32, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Content", + table: "Tags", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Tags_Name", + table: "Tags", + column: "Name", + unique: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tags_Content_MinLength", + table: "Tags", + sql: "LEN([Content]) >= 1"); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tags_Name_MinLength", + table: "Tags", + sql: "LEN([Name]) >= 1"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Tags_Name", + table: "Tags"); + + migrationBuilder.DropCheckConstraint( + name: "CK_Tags_Content_MinLength", + table: "Tags"); + + migrationBuilder.DropCheckConstraint( + name: "CK_Tags_Name_MinLength", + table: "Tags"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Tags", + type: "nvarchar(32)", + maxLength: 32, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(32)", + oldMaxLength: 32); + + migrationBuilder.AlterColumn( + name: "Content", + table: "Tags", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.CreateIndex( + name: "IX_Tags_Name", + table: "Tags", + column: "Name", + unique: true, + filter: "[Name] IS NOT NULL"); + } + } +} diff --git a/src/Modmail.NET/Migrations/ModmailDbContextModelSnapshot.cs b/src/Modmail.NET/Database/Migrations/ModmailDbContextModelSnapshot.cs similarity index 78% rename from src/Modmail.NET/Migrations/ModmailDbContextModelSnapshot.cs rename to src/Modmail.NET/Database/Migrations/ModmailDbContextModelSnapshot.cs index f2e98e8d..6af6cb0d 100644 --- a/src/Modmail.NET/Migrations/ModmailDbContextModelSnapshot.cs +++ b/src/Modmail.NET/Database/Migrations/ModmailDbContextModelSnapshot.cs @@ -22,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("Modmail.NET.Entities.DiscordUserInfo", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => { b.Property("Id") .HasColumnType("decimal(20,0)"); @@ -62,7 +62,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); - modelBuilder.Entity("Modmail.NET.Entities.GuildOption", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildOption", b => { b.Property("GuildId") .HasColumnType("decimal(20,0)"); @@ -137,13 +137,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) { t.HasCheckConstraint("CK_GuildOptions_Name_MinLength", "LEN([Name]) >= 1"); - t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN -1 AND 365"); + t.HasCheckConstraint("CK_GuildOptions_StatisticsCalculateDays_Range", "[StatisticsCalculateDays] BETWEEN 30 AND 365"); t.HasCheckConstraint("CK_GuildOptions_TicketDataDeleteWaitDays_Range", "[TicketDataDeleteWaitDays] BETWEEN -1 AND 365"); }); }); - modelBuilder.Entity("Modmail.NET.Entities.GuildTeam", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -183,7 +183,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); - modelBuilder.Entity("Modmail.NET.Entities.GuildTeamMember", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -208,7 +208,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("GuildTeamMembers"); }); - modelBuilder.Entity("Modmail.NET.Entities.Statistic", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.Statistic", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -246,7 +246,47 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Statistics"); }); - modelBuilder.Entity("Modmail.NET.Entities.Ticket", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("Title") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdateDateUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tags", t => + { + t.HasCheckConstraint("CK_Tags_Content_MinLength", "LEN([Content]) >= 1"); + + t.HasCheckConstraint("CK_Tags_Name_MinLength", "LEN([Name]) >= 1"); + + t.HasCheckConstraint("CK_Tags_Title_MinLength", "LEN([Title]) >= 0"); + }); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -318,7 +358,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Tickets"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketBlacklist", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -342,7 +382,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("TicketBlacklists"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketMessage", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -351,6 +391,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("BotMessageId") .HasColumnType("decimal(20,0)"); + b.Property("ChangeStatus") + .HasColumnType("int"); + b.Property("MessageContent") .HasMaxLength(2147483647) .HasColumnType("nvarchar(max)"); @@ -379,7 +422,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("TicketMessages"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketMessageAttachment", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -433,7 +476,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketNote", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("MessageContentAfter") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("MessageContentBefore") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("RegisterDateUtc") + .HasColumnType("datetime2"); + + b.Property("TicketMessageId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TicketMessageId"); + + b.ToTable("TicketMessageHistory"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -463,7 +533,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketType", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketType", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -520,9 +590,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); - modelBuilder.Entity("Modmail.NET.Entities.GuildTeamMember", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeamMember", b => { - b.HasOne("Modmail.NET.Entities.GuildTeam", "GuildTeam") + b.HasOne("Modmail.NET.Database.Entities.GuildTeam", "GuildTeam") .WithMany("GuildTeamMembers") .HasForeignKey("GuildTeamId") .OnDelete(DeleteBehavior.Cascade) @@ -531,25 +601,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("GuildTeam"); }); - modelBuilder.Entity("Modmail.NET.Entities.Ticket", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => { - b.HasOne("Modmail.NET.Entities.DiscordUserInfo", "AssignedUser") + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "AssignedUser") .WithMany("AssignedTickets") .HasForeignKey("AssignedUserId") .OnDelete(DeleteBehavior.Restrict); - b.HasOne("Modmail.NET.Entities.DiscordUserInfo", "CloserUser") + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "CloserUser") .WithMany("ClosedTickets") .HasForeignKey("CloserUserId") .OnDelete(DeleteBehavior.Restrict); - b.HasOne("Modmail.NET.Entities.DiscordUserInfo", "OpenerUser") + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "OpenerUser") .WithMany("OpenedTickets") .HasForeignKey("OpenerUserId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Modmail.NET.Entities.TicketType", "TicketType") + b.HasOne("Modmail.NET.Database.Entities.TicketType", "TicketType") .WithMany() .HasForeignKey("TicketTypeId") .OnDelete(DeleteBehavior.Restrict); @@ -563,9 +633,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("TicketType"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketBlacklist", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketBlacklist", b => { - b.HasOne("Modmail.NET.Entities.DiscordUserInfo", "DiscordUser") + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", "DiscordUser") .WithMany() .HasForeignKey("DiscordUserId") .OnDelete(DeleteBehavior.Restrict) @@ -574,40 +644,51 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("DiscordUser"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketMessage", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => { - b.HasOne("Modmail.NET.Entities.DiscordUserInfo", null) + b.HasOne("Modmail.NET.Database.Entities.DiscordUserInfo", null) .WithMany() .HasForeignKey("SenderUserId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Modmail.NET.Entities.Ticket", null) + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) .WithMany("Messages") .HasForeignKey("TicketId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketMessageAttachment", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageAttachment", b => { - b.HasOne("Modmail.NET.Entities.TicketMessage", null) + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", null) .WithMany("Attachments") .HasForeignKey("TicketMessageId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketNote", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessageHistory", b => { - b.HasOne("Modmail.NET.Entities.Ticket", null) + b.HasOne("Modmail.NET.Database.Entities.TicketMessage", "TicketMessage") + .WithMany("History") + .HasForeignKey("TicketMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketMessage"); + }); + + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketNote", b => + { + b.HasOne("Modmail.NET.Database.Entities.Ticket", null) .WithMany("TicketNotes") .HasForeignKey("TicketId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Modmail.NET.Entities.DiscordUserInfo", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.DiscordUserInfo", b => { b.Navigation("AssignedTickets"); @@ -616,21 +697,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("OpenedTickets"); }); - modelBuilder.Entity("Modmail.NET.Entities.GuildTeam", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.GuildTeam", b => { b.Navigation("GuildTeamMembers"); }); - modelBuilder.Entity("Modmail.NET.Entities.Ticket", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.Ticket", b => { b.Navigation("Messages"); b.Navigation("TicketNotes"); }); - modelBuilder.Entity("Modmail.NET.Entities.TicketMessage", b => + modelBuilder.Entity("Modmail.NET.Database.Entities.TicketMessage", b => { b.Navigation("Attachments"); + + b.Navigation("History"); }); #pragma warning restore 612, 618 } diff --git a/src/Modmail.NET/Database/ModmailDbContext.cs b/src/Modmail.NET/Database/ModmailDbContext.cs index 73e8a36e..ea663197 100644 --- a/src/Modmail.NET/Database/ModmailDbContext.cs +++ b/src/Modmail.NET/Database/ModmailDbContext.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; using SmartEnum.EFCore; namespace Modmail.NET.Database; @@ -11,6 +11,7 @@ public ModmailDbContext(DbContextOptions options) : base(optio public DbSet Tickets { get; set; } = null!; public DbSet TicketMessageAttachments { get; set; } = null!; public DbSet TicketMessages { get; set; } = null!; + public DbSet TicketMessageHistory { get; set; } = null!; public DbSet GuildOptions { get; set; } = null!; public DbSet GuildTeams { get; set; } = null!; public DbSet GuildTeamMembers { get; set; } = null!; @@ -19,6 +20,7 @@ public ModmailDbContext(DbContextOptions options) : base(optio public DbSet TicketBlacklists { get; set; } = null!; public DbSet TicketTypes { get; set; } = null!; public DbSet Statistics { get; set; } = null!; + public DbSet Tags { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(ModmailDbContext).Assembly); diff --git a/src/Modmail.NET/Database/Triggers/IdentityV7Trigger.cs b/src/Modmail.NET/Database/Triggers/IdentityV7Trigger.cs index dedadf2c..b1acda17 100644 --- a/src/Modmail.NET/Database/Triggers/IdentityV7Trigger.cs +++ b/src/Modmail.NET/Database/Triggers/IdentityV7Trigger.cs @@ -1,5 +1,5 @@ using EntityFrameworkCore.Triggered; -using Modmail.NET.Abstract; +using Modmail.NET.Database.Abstract; namespace Modmail.NET.Database.Triggers; diff --git a/src/Modmail.NET/Database/Triggers/RegisterDateTrigger.cs b/src/Modmail.NET/Database/Triggers/RegisterDateTrigger.cs index 54810c01..672a56a8 100644 --- a/src/Modmail.NET/Database/Triggers/RegisterDateTrigger.cs +++ b/src/Modmail.NET/Database/Triggers/RegisterDateTrigger.cs @@ -1,6 +1,6 @@ using EntityFrameworkCore.Triggered; -using Modmail.NET.Abstract; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Abstract; namespace Modmail.NET.Database.Triggers; diff --git a/src/Modmail.NET/Database/Triggers/UpdateDateTrigger.cs b/src/Modmail.NET/Database/Triggers/UpdateDateTrigger.cs index 881ce8f8..514e1841 100644 --- a/src/Modmail.NET/Database/Triggers/UpdateDateTrigger.cs +++ b/src/Modmail.NET/Database/Triggers/UpdateDateTrigger.cs @@ -1,6 +1,6 @@ using EntityFrameworkCore.Triggered; -using Modmail.NET.Abstract; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Abstract; namespace Modmail.NET.Database.Triggers; diff --git a/src/Modmail.NET/Exceptions/AnotherServerAlreadySetupException.cs b/src/Modmail.NET/Exceptions/AnotherServerAlreadySetupException.cs deleted file mode 100644 index a4926d41..00000000 --- a/src/Modmail.NET/Exceptions/AnotherServerAlreadySetupException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class AnotherServerAlreadySetupException : BotExceptionBase -{ - public AnotherServerAlreadySetupException() : base(LangProvider.This.GetTranslation(LangKeys.AnotherServerAlreadySetup)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/DbInternalException.cs b/src/Modmail.NET/Exceptions/DbInternalException.cs deleted file mode 100644 index 62df8215..00000000 --- a/src/Modmail.NET/Exceptions/DbInternalException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class DbInternalException : BotExceptionBase -{ - public DbInternalException() : base(LangProvider.This.GetTranslation(LangKeys.DbInternalError)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/InvalidInteractionKeyException.cs b/src/Modmail.NET/Exceptions/InvalidInteractionKeyException.cs deleted file mode 100644 index 4ddf7a58..00000000 --- a/src/Modmail.NET/Exceptions/InvalidInteractionKeyException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class InvalidInteractionKeyException : BotExceptionBase -{ - public InvalidInteractionKeyException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidInteractionKey)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/InvalidMessageIdException.cs b/src/Modmail.NET/Exceptions/InvalidMessageIdException.cs deleted file mode 100644 index d377ed31..00000000 --- a/src/Modmail.NET/Exceptions/InvalidMessageIdException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class InvalidMessageIdException : BotExceptionBase -{ - public InvalidMessageIdException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidMessageId)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/InvalidUserIdException.cs b/src/Modmail.NET/Exceptions/InvalidUserIdException.cs deleted file mode 100644 index 897e6256..00000000 --- a/src/Modmail.NET/Exceptions/InvalidUserIdException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class InvalidUserIdException : BotExceptionBase -{ - public InvalidUserIdException() : base(LangProvider.This.GetTranslation(LangKeys.InvalidUser)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/MainServerAlreadySetupException.cs b/src/Modmail.NET/Exceptions/MainServerAlreadySetupException.cs deleted file mode 100644 index 023fe67c..00000000 --- a/src/Modmail.NET/Exceptions/MainServerAlreadySetupException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class MainServerAlreadySetupException : BotExceptionBase -{ - public MainServerAlreadySetupException() : base(LangProvider.This.GetTranslation(LangKeys.MainServerAlreadySetup)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/MemberAlreadyInTeamException.cs b/src/Modmail.NET/Exceptions/MemberAlreadyInTeamException.cs deleted file mode 100644 index 7ede63f7..00000000 --- a/src/Modmail.NET/Exceptions/MemberAlreadyInTeamException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class MemberAlreadyInTeamException : BotExceptionBase -{ - public MemberAlreadyInTeamException() : base(LangProvider.This.GetTranslation(LangKeys.MemberAlreadyInTeam)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/NotJoinedMainServerException.cs b/src/Modmail.NET/Exceptions/NotJoinedMainServerException.cs deleted file mode 100644 index 0d826072..00000000 --- a/src/Modmail.NET/Exceptions/NotJoinedMainServerException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class NotJoinedMainServerException : BotExceptionBase -{ - public NotJoinedMainServerException() : base(LangProvider.This.GetTranslation(LangKeys.NotJoinedMainServer)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/RoleAlreadyInTeamException.cs b/src/Modmail.NET/Exceptions/RoleAlreadyInTeamException.cs deleted file mode 100644 index ddc8f66f..00000000 --- a/src/Modmail.NET/Exceptions/RoleAlreadyInTeamException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class RoleAlreadyInTeamException : BotExceptionBase -{ - public RoleAlreadyInTeamException() : base(LangProvider.This.GetTranslation(LangKeys.RoleAlreadyInTeam)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/ServerIsNotSetupException.cs b/src/Modmail.NET/Exceptions/ServerIsNotSetupException.cs deleted file mode 100644 index 96d93f2e..00000000 --- a/src/Modmail.NET/Exceptions/ServerIsNotSetupException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class ServerIsNotSetupException : BotExceptionBase -{ - public ServerIsNotSetupException() : base(LangProvider.This.GetTranslation(LangKeys.RoleNotFoundInTeam)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TeamAlreadyExistsException.cs b/src/Modmail.NET/Exceptions/TeamAlreadyExistsException.cs deleted file mode 100644 index e69075ec..00000000 --- a/src/Modmail.NET/Exceptions/TeamAlreadyExistsException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class TeamAlreadyExistsException : BotExceptionBase -{ - public TeamAlreadyExistsException() : base(LangProvider.This.GetTranslation(LangKeys.TeamAlreadyExists)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TeamNotExistsException.cs b/src/Modmail.NET/Exceptions/TeamNotExistsException.cs deleted file mode 100644 index 3f2bc57d..00000000 --- a/src/Modmail.NET/Exceptions/TeamNotExistsException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class TeamNotExistsException : BotExceptionBase -{ - public TeamNotExistsException() : base(LangProvider.This.GetTranslation(LangKeys.TeamNotExists)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TicketAlreadyClosedException.cs b/src/Modmail.NET/Exceptions/TicketAlreadyClosedException.cs deleted file mode 100644 index 4486e014..00000000 --- a/src/Modmail.NET/Exceptions/TicketAlreadyClosedException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class TicketAlreadyClosedException : BotExceptionBase -{ - public TicketAlreadyClosedException() : base(LangProvider.This.GetTranslation(LangKeys.TicketAlreadyClosed)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TicketMustBeClosedException.cs b/src/Modmail.NET/Exceptions/TicketMustBeClosedException.cs deleted file mode 100644 index d5e9bc17..00000000 --- a/src/Modmail.NET/Exceptions/TicketMustBeClosedException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class TicketMustBeClosedException : BotExceptionBase -{ - public TicketMustBeClosedException() : base(LangProvider.This.GetTranslation(LangKeys.TicketMustBeClosed)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/TicketTimeoutOutOfRangeException.cs b/src/Modmail.NET/Exceptions/TicketTimeoutOutOfRangeException.cs deleted file mode 100644 index fc4b5065..00000000 --- a/src/Modmail.NET/Exceptions/TicketTimeoutOutOfRangeException.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class TicketTimeoutOutOfRangeException : BotExceptionBase -{ - public TicketTimeoutOutOfRangeException() : base(LangKeys.TicketTimeoutValueIsOutOfRange.GetTranslation(), - LangKeys.TicketTimeoutValueMustBeBetweenXAndY.GetTranslation(Const.TicketTimeoutMinAllowedHours, - Const.TicketTimeoutMaxAllowedHours)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/UserAlreadyBlacklistedException.cs b/src/Modmail.NET/Exceptions/UserAlreadyBlacklistedException.cs deleted file mode 100644 index 01e3421f..00000000 --- a/src/Modmail.NET/Exceptions/UserAlreadyBlacklistedException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class UserAlreadyBlacklistedException : BotExceptionBase -{ - public UserAlreadyBlacklistedException() : base(LangProvider.This.GetTranslation(LangKeys.UserAlreadyBlacklisted)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Exceptions/UserIsNotBlacklistedException.cs b/src/Modmail.NET/Exceptions/UserIsNotBlacklistedException.cs deleted file mode 100644 index 10b02605..00000000 --- a/src/Modmail.NET/Exceptions/UserIsNotBlacklistedException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Modmail.NET.Abstract; - -namespace Modmail.NET.Exceptions; - -public class UserIsNotBlacklistedException : BotExceptionBase -{ - public UserIsNotBlacklistedException() : base(LangProvider.This.GetTranslation(LangKeys.UserIsNotBlacklisted)) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Extensions/ExtException.cs b/src/Modmail.NET/Extensions/ExtException.cs deleted file mode 100644 index 7928783c..00000000 --- a/src/Modmail.NET/Extensions/ExtException.cs +++ /dev/null @@ -1,43 +0,0 @@ -using DSharpPlus.Entities; -using Modmail.NET.Abstract; - -namespace Modmail.NET.Extensions; - -public static class ExtException -{ - public static DiscordWebhookBuilder ToWebhookResponse(this BotExceptionBase exception) { - return Webhooks.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); - } - - public static DiscordEmbedBuilder ToEmbedResponse(this BotExceptionBase exception) { - return Embeds.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); - } - - public static DiscordInteractionResponseBuilder ToInteractionResponse(this BotExceptionBase exception) { - return Interactions.Warning(exception.TitleMessage, exception.ContentMessage ?? ""); - } - - public static DiscordWebhookBuilder ToWebhookResponse(this Exception exception) { - var config = ServiceLocator.GetBotConfig(); - - if (config.Environment == EnvironmentType.Development) return Webhooks.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); - - return Webhooks.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); - } - - public static DiscordEmbedBuilder ToEmbedResponse(this Exception exception) { - var config = ServiceLocator.GetBotConfig(); - - if (config.Environment == EnvironmentType.Development) return Embeds.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); - - return Embeds.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); - } - - public static DiscordInteractionResponseBuilder ToInteractionResponse(this Exception exception) { - var config = ServiceLocator.GetBotConfig(); - - if (config.Environment == EnvironmentType.Development) return Interactions.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred), exception.Message); - - return Interactions.Error(LangProvider.This.GetTranslation(LangKeys.AnExceptionOccurred)); - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Commands.cs b/src/Modmail.NET/Features/Blacklist/Commands/ProcessAddUserToBlacklistCommand.cs similarity index 51% rename from src/Modmail.NET/Features/Blacklist/Commands.cs rename to src/Modmail.NET/Features/Blacklist/Commands/ProcessAddUserToBlacklistCommand.cs index 8307714a..9ea55165 100644 --- a/src/Modmail.NET/Features/Blacklist/Commands.cs +++ b/src/Modmail.NET/Features/Blacklist/Commands/ProcessAddUserToBlacklistCommand.cs @@ -1,14 +1,11 @@ using MediatR; using Modmail.NET.Abstract; using Modmail.NET.Attributes; -using Modmail.NET.Entities; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; -namespace Modmail.NET.Features.Blacklist; +namespace Modmail.NET.Features.Blacklist.Commands; [PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] public sealed record ProcessAddUserToBlacklistCommand(ulong AuthorizedUserId, ulong UserId, string Reason = null) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] -public sealed record ProcessRemoveUserFromBlacklistCommand(ulong AuthorizedUserId, ulong UserId) : IRequest, - IPermissionCheck; \ No newline at end of file + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Commands/ProcessRemoveUserFromBlacklistCommand.cs b/src/Modmail.NET/Features/Blacklist/Commands/ProcessRemoveUserFromBlacklistCommand.cs new file mode 100644 index 00000000..8f681187 --- /dev/null +++ b/src/Modmail.NET/Features/Blacklist/Commands/ProcessRemoveUserFromBlacklistCommand.cs @@ -0,0 +1,11 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Blacklist.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] +public sealed record ProcessRemoveUserFromBlacklistCommand(ulong AuthorizedUserId, ulong UserId) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Handlers/CheckUserBlacklistStatusHandler.cs b/src/Modmail.NET/Features/Blacklist/Handlers/CheckUserBlacklistStatusHandler.cs index d2c3b7ab..b34f857a 100644 --- a/src/Modmail.NET/Features/Blacklist/Handlers/CheckUserBlacklistStatusHandler.cs +++ b/src/Modmail.NET/Features/Blacklist/Handlers/CheckUserBlacklistStatusHandler.cs @@ -1,7 +1,8 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Blacklist.Queries; namespace Modmail.NET.Features.Blacklist.Handlers; diff --git a/src/Modmail.NET/Features/Blacklist/Handlers/GetAllBlacklistHandler.cs b/src/Modmail.NET/Features/Blacklist/Handlers/GetAllBlacklistHandler.cs index 5791859e..5fc3d7ac 100644 --- a/src/Modmail.NET/Features/Blacklist/Handlers/GetAllBlacklistHandler.cs +++ b/src/Modmail.NET/Features/Blacklist/Handlers/GetAllBlacklistHandler.cs @@ -1,8 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Blacklist.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Blacklist.Handlers; diff --git a/src/Modmail.NET/Features/Blacklist/Handlers/GetBlacklistHandler.cs b/src/Modmail.NET/Features/Blacklist/Handlers/GetBlacklistHandler.cs index 0e0f9ec5..2ad07366 100644 --- a/src/Modmail.NET/Features/Blacklist/Handlers/GetBlacklistHandler.cs +++ b/src/Modmail.NET/Features/Blacklist/Handlers/GetBlacklistHandler.cs @@ -1,8 +1,9 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Blacklist.Queries; namespace Modmail.NET.Features.Blacklist.Handlers; diff --git a/src/Modmail.NET/Features/Blacklist/Handlers/ProcessAddUserToBlacklistHandler.cs b/src/Modmail.NET/Features/Blacklist/Handlers/ProcessAddUserToBlacklistHandler.cs index f857b5bf..ebe56650 100644 --- a/src/Modmail.NET/Features/Blacklist/Handlers/ProcessAddUserToBlacklistHandler.cs +++ b/src/Modmail.NET/Features/Blacklist/Handlers/ProcessAddUserToBlacklistHandler.cs @@ -1,11 +1,17 @@ using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.Ticket; -using Modmail.NET.Features.UserInfo; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Blacklist.Commands; +using Modmail.NET.Features.Blacklist.Queries; +using Modmail.NET.Features.Blacklist.Static; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.User.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Blacklist.Handlers; @@ -51,13 +57,13 @@ await _sender.Send(new ProcessCloseTicketCommand(activeTicket.Id, var user = await _sender.Send(new GetDiscordUserInfoQuery(request.UserId), cancellationToken); var modUser = await _sender.Send(new GetDiscordUserInfoQuery(request.AuthorizedUserId), cancellationToken); - var embedLog = LogResponses.BlacklistAdded(modUser, user, reason); + var embedLog = LogBotMessages.BlacklistAdded(modUser, user, reason); var logChannel = await _sender.Send(new GetDiscordLogChannelQuery(), cancellationToken); await logChannel.SendMessageAsync(embedLog); var member = await _sender.Send(new GetDiscordMemberQuery(user.Id), cancellationToken); if (member is not null) { - var dmEmbed = UserResponses.YouHaveBeenBlacklisted(reason); + var dmEmbed = BlacklistBotMessages.YouHaveBeenBlacklisted(reason); await member.SendMessageAsync(dmEmbed); } }, cancellationToken); diff --git a/src/Modmail.NET/Features/Blacklist/Handlers/ProcessRemoveUserFromBlacklistHandler.cs b/src/Modmail.NET/Features/Blacklist/Handlers/ProcessRemoveUserFromBlacklistHandler.cs index 6b5dc07d..d3e42a8d 100644 --- a/src/Modmail.NET/Features/Blacklist/Handlers/ProcessRemoveUserFromBlacklistHandler.cs +++ b/src/Modmail.NET/Features/Blacklist/Handlers/ProcessRemoveUserFromBlacklistHandler.cs @@ -1,9 +1,13 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Blacklist.Commands; +using Modmail.NET.Features.Blacklist.Queries; +using Modmail.NET.Features.Blacklist.Static; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.User.Queries; namespace Modmail.NET.Features.Blacklist.Handlers; @@ -29,11 +33,11 @@ public async Task Handle(ProcessRemoveUserFromBlacklistCommand var member = await _sender.Send(new GetDiscordMemberQuery(request.UserId), cancellationToken); var memberInfo = DiscordUserInfo.FromDiscordMember(member); if (member is not null) { - var dmEmbed = UserResponses.YouHaveBeenRemovedFromBlacklist(modUser); + var dmEmbed = BlacklistBotMessages.YouHaveBeenRemovedFromBlacklist(modUser); await member.SendMessageAsync(dmEmbed); } - var embedLog = LogResponses.BlacklistRemoved(modUser, memberInfo); + var embedLog = LogBotMessages.BlacklistRemoved(modUser, memberInfo); var logChannel = await _sender.Send(new GetDiscordLogChannelQuery(), cancellationToken); await logChannel.SendMessageAsync(embedLog); }, cancellationToken); diff --git a/src/Modmail.NET/Features/Blacklist/Queries.cs b/src/Modmail.NET/Features/Blacklist/Queries.cs deleted file mode 100644 index dbe18871..00000000 --- a/src/Modmail.NET/Features/Blacklist/Queries.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Attributes; -using Modmail.NET.Entities; - -namespace Modmail.NET.Features.Blacklist; - -public sealed record CheckUserBlacklistStatusQuery(ulong DiscordUserId) : IRequest; - -[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] -public sealed record GetBlacklistQuery( - ulong AuthorizedUserId, - ulong DiscordUserId, - bool AllowNull = false) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] -public sealed record GetAllBlacklistQuery(ulong AuthorizedUserId) : IRequest>, - IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Queries/CheckUserBlacklistStatusQuery.cs b/src/Modmail.NET/Features/Blacklist/Queries/CheckUserBlacklistStatusQuery.cs new file mode 100644 index 00000000..7991f387 --- /dev/null +++ b/src/Modmail.NET/Features/Blacklist/Queries/CheckUserBlacklistStatusQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Blacklist.Queries; + +public sealed record CheckUserBlacklistStatusQuery(ulong DiscordUserId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Queries/GetAllBlacklistQuery.cs b/src/Modmail.NET/Features/Blacklist/Queries/GetAllBlacklistQuery.cs new file mode 100644 index 00000000..e4b553ec --- /dev/null +++ b/src/Modmail.NET/Features/Blacklist/Queries/GetAllBlacklistQuery.cs @@ -0,0 +1,11 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Blacklist.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] +public sealed record GetAllBlacklistQuery(ulong AuthorizedUserId) : IRequest>, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Queries/GetBlacklistQuery.cs b/src/Modmail.NET/Features/Blacklist/Queries/GetBlacklistQuery.cs new file mode 100644 index 00000000..52834795 --- /dev/null +++ b/src/Modmail.NET/Features/Blacklist/Queries/GetBlacklistQuery.cs @@ -0,0 +1,14 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Blacklist.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageBlacklist))] +public sealed record GetBlacklistQuery( + ulong AuthorizedUserId, + ulong DiscordUserId, + bool AllowNull = false) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Blacklist/Static/BlacklistBotMessages.cs b/src/Modmail.NET/Features/Blacklist/Static/BlacklistBotMessages.cs new file mode 100644 index 00000000..60c314fa --- /dev/null +++ b/src/Modmail.NET/Features/Blacklist/Static/BlacklistBotMessages.cs @@ -0,0 +1,34 @@ +using DSharpPlus.Entities; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Blacklist.Static; + +public static class BlacklistBotMessages +{ + public static DiscordEmbedBuilder YouHaveBeenBlacklisted(string reason = null) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.YouHaveBeenBlacklisted.GetTranslation()) + .WithDescription(LangKeys.YouHaveBeenBlacklistedDescription.GetTranslation()) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithColor(ModmailColors.ErrorColor); + + if (!string.IsNullOrEmpty(reason)) embed.AddField(LangKeys.Reason.GetTranslation(), reason); + + return embed; + } + + public static DiscordEmbedBuilder YouHaveBeenRemovedFromBlacklist(DiscordUserInfo user) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.YouHaveBeenRemovedFromBlacklist.GetTranslation()) + .WithDescription(LangKeys.YouHaveBeenRemovedFromBlacklistDescription.GetTranslation()) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithUserAsAuthor(user) + .WithColor(ModmailColors.SuccessColor); + return embed; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Bot/Queries.cs b/src/Modmail.NET/Features/Bot/Queries.cs deleted file mode 100644 index 184683f9..00000000 --- a/src/Modmail.NET/Features/Bot/Queries.cs +++ /dev/null @@ -1,13 +0,0 @@ -using DSharpPlus.Entities; -using MediatR; -using Modmail.NET.Attributes; - -namespace Modmail.NET.Features.Bot; - -[CachePolicy("GetMainGuildQuery", 300)] -public sealed record GetDiscordMainGuildQuery : IRequest; - -[CachePolicy("GetDiscordLogChannelQuery", 60)] -public sealed record GetDiscordLogChannelQuery : IRequest; - -public sealed record GetDiscordMemberQuery(ulong UserId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/ComponentInteractionCreatedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/ComponentInteractionCreatedEvent.cs new file mode 100644 index 00000000..23dee23c --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/ComponentInteractionCreatedEvent.cs @@ -0,0 +1,218 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.User.Commands; +using Serilog; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class ComponentInteractionCreatedEvent +{ + [PerformanceLoggerAspect] + public static async Task ComponentInteractionCreated( + DiscordClient client, + ComponentInteractionCreatedEventArgs args + ) { + Log.Debug( + "[{Source}] Component interaction created. CustomId: {CustomId}, UserId: {UserId}, ChannelId: {ChannelId}, InteractionId: {InteractionId}, MessageId: {MessageId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.User?.Id, + args.Channel?.Id, + args.Interaction?.Id, + args.Message?.Id + ); + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + + try { + await sender.Send(new UpdateDiscordUserCommand(args.User)); + + var key = args.Interaction?.Data?.CustomId; + var (interactionName, parameters) = UtilInteraction.ParseKey(key); + var messageId = args.Message.Id; + + switch (interactionName) { + case "star": + await ProcessStarInteraction(sender, args, messageId); + break; + case "ticket_type": + await ProcessTicketTypeInteraction(sender, args, messageId); + break; + case "close_ticket": // This must stay due to deprecation and support for existing tickets (v2.0 beta) + case "close_ticket_with_reason": + await ProcessCloseTicketInteraction(sender, args, messageId); + break; + default: + Log.Warning( + "[{Source}] Unknown interaction name: {InteractionName}, CustomId: {CustomId}", + nameof(ComponentInteractionCreatedEvent), + interactionName, + args.Interaction?.Data?.CustomId + ); + break; + } + } + catch (ModmailBotException ex) { + Log.Warning( + ex, + "[{Source}] ModmailBotException: Error processing component interaction. CustomId: {CustomId}, UserId: {UserId}, ChannelId: {ChannelId}, InteractionId: {InteractionId}, MessageId: {MessageId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.User?.Id, + args.Channel?.Id, + args.Interaction?.Id, + args.Message?.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Unhandled exception processing component interaction. CustomId: {CustomId}, UserId: {UserId}, ChannelId: {ChannelId}, InteractionId: {InteractionId}, MessageId: {MessageId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.User?.Id, + args.Channel?.Id, + args.Interaction?.Id, + args.Message?.Id + ); + } + } + + private static async Task ProcessStarInteraction( + ISender sender, + ComponentInteractionCreatedEventArgs args, + ulong messageId + ) { + var key = args.Interaction?.Data?.CustomId; + try { + var (interactionName, parameters) = UtilInteraction.ParseKey(key); + //feedback process show modal + var starParam = parameters[0]; + var ticketIdParam = parameters[1]; + + var starCount = int.Parse(starParam); + var ticketId = Guid.Parse(ticketIdParam); + + var feedbackModal = TicketModals.CreateFeedbackModal(starCount, ticketId, messageId); + + await args.Interaction.CreateResponseAsync( + DiscordInteractionResponseType.Modal, + feedbackModal + ); + + Log.Information( + "[{Source}] Star interaction processed. TicketId: {TicketId}, StarCount: {StarCount}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + ticketId, + starCount, + args.Interaction?.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Error processing star interaction submission. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.Interaction?.Id + ); + } + } + + private static async Task ProcessTicketTypeInteraction( + ISender sender, + ComponentInteractionCreatedEventArgs args, + ulong messageId + ) { + var key = args.Interaction?.Data?.CustomId; + try { + await args.Interaction.CreateResponseAsync( + DiscordInteractionResponseType.UpdateMessage + ); + var (interactionName, parameters) = UtilInteraction.ParseKey(key); + + var ticketIdParam = parameters[0]; + var ticketId = Guid.Parse(ticketIdParam); + var selectedTypeKey = args.Values.FirstOrDefault(); + if (string.IsNullOrEmpty(selectedTypeKey)) { + Log.Warning( + "[{Source}] No ticket type selected. InteractionId: {InteractionId}, MessageId: {MessageId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Id, + messageId + ); + return; + } + + await sender.Send(new ProcessChangeTicketTypeCommand( + ticketId, + selectedTypeKey, + null, + args.Channel, + args.Message, + args.User.Id + )); + + Log.Information( + "[{Source}] Ticket type changed. TicketId: {TicketId}, SelectedTypeKey: {SelectedTypeKey}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + ticketId, + selectedTypeKey, + args.Interaction?.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Error processing process ticket type interaction. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.Interaction?.Id + ); + } + } + + private static async Task ProcessCloseTicketInteraction( + ISender sender, + ComponentInteractionCreatedEventArgs args, + ulong messageId + ) { + var key = args.Interaction?.Data?.CustomId; + try { + var (interactionName, parameters) = UtilInteraction.ParseKey(key); + + var ticketIdParam = parameters[0]; + var ticketId = Guid.Parse(ticketIdParam); + var modal = TicketModals.CreateCloseTicketWithReasonModal(ticketId); + await args.Interaction.CreateResponseAsync( + DiscordInteractionResponseType.Modal, + modal + ); + + Log.Information( + "[{Source}] Close ticket interaction triggered. TicketId: {TicketId}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + ticketId, + args.Interaction?.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Error processing process close ticket interaction. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ComponentInteractionCreatedEvent), + args.Interaction?.Data?.CustomId, + args.Interaction?.Id + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/ModalSubmittedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/ModalSubmittedEvent.cs new file mode 100644 index 00000000..d99c8c1c --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/ModalSubmittedEvent.cs @@ -0,0 +1,141 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.User.Commands; +using Modmail.NET.Language; +using Serilog; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class ModalSubmittedEvent +{ + [PerformanceLoggerAspect] + public static async Task ModalSubmitted( + DiscordClient client, + ModalSubmittedEventArgs args + ) { + Log.Debug( + "[{Source}] Modal submitted. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ModalSubmittedEvent), + args.Interaction.Data.CustomId, + args.Interaction.Id + ); + + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + + try { + await sender.Send(new UpdateDiscordUserCommand(args.Interaction.User)); + + var (interactionName, parameters) = + UtilInteraction.ParseKey(args.Interaction.Data.CustomId); + + switch (interactionName) { + case "feedback": + await ProcessFeedback(sender, args, parameters); + await args.Interaction.CreateResponseAsync( + DiscordInteractionResponseType.ChannelMessageWithSource, + new DiscordInteractionResponseBuilder().AsEphemeral() + .WithContent(LangProvider.This.GetTranslation(LangKeys.ThankYouForFeedback)) + ); + break; + case "close_ticket_with_reason": + await args.Interaction.CreateResponseAsync( + DiscordInteractionResponseType.DeferredMessageUpdate + ); + + await ProcessCloseTicketWithReason(sender, args, parameters); + break; + default: + Log.Warning( + "[{Source}] Unknown interaction name: {InteractionName}, CustomId: {CustomId}", + nameof(ModalSubmittedEvent), + interactionName, + args.Interaction.Data.CustomId + ); + break; + } + } + catch (ModmailBotException ex) { + Log.Warning( + ex, + "[{Source}] ModmailBotException: Error processing modal submission. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ModalSubmittedEvent), + args.Interaction.Data.CustomId, + args.Interaction.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Unhandled exception processing modal submission. CustomId: {CustomId}, InteractionId: {InteractionId}", + nameof(ModalSubmittedEvent), + args.Interaction.Data.CustomId, + args.Interaction.Id + ); + } + } + + private static async Task ProcessFeedback( + ISender sender, + ModalSubmittedEventArgs args, + string[] parameters + ) { + var textInput = args.Values["feedback"]; + + var starParam = parameters[0]; + var ticketIdParam = parameters[1]; + var messageIdParam = parameters[2]; + + var starCount = int.Parse(starParam); + var ticketId = Guid.Parse(ticketIdParam); + var feedbackMessageId = ulong.Parse(messageIdParam); + + var feedbackMessage = await args.Interaction.Channel.GetMessageAsync(feedbackMessageId); + await sender.Send(new ProcessAddFeedbackCommand( + ticketId, + starCount, + textInput, + feedbackMessage + )); + + Log.Information( + "[{Source}] Feedback processed. TicketId: {TicketId}, StarCount: {StarCount}, InteractionId: {InteractionId}", + nameof(ModalSubmittedEvent), + ticketId, + starCount, + args.Interaction.Id + ); + } + + private static async Task ProcessCloseTicketWithReason( + ISender sender, + ModalSubmittedEventArgs args, + string[] parameters + ) { + var textInput = args.Values["reason"]; + var ticketIdParam = parameters[0]; + var ticketId = Guid.Parse(ticketIdParam); + + await sender.Send(new ProcessCloseTicketCommand( + ticketId, + args.Interaction.User.Id, + textInput, + args.Interaction.Channel + )); + + Log.Information( + "[{Source}] Ticket closed with reason. TicketId: {TicketId}, InteractionId: {InteractionId}", + nameof(ModalSubmittedEvent), + ticketId, + args.Interaction.Id + ); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnChannelDeletedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnChannelDeletedEvent.cs new file mode 100644 index 00000000..d7615bfa --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnChannelDeletedEvent.cs @@ -0,0 +1,109 @@ +using DSharpPlus; +using DSharpPlus.Entities.AuditLogs; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.User.Commands; +using Modmail.NET.Language; +using Serilog; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class OnChannelDeletedEvent +{ + [PerformanceLoggerAspect] + public static async Task OnChannelDeleted( + DiscordClient client, + ChannelDeletedEventArgs args + ) { + Log.Debug( + "[{Source}] Channel deletion event triggered. ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + args.Channel.Id + ); + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + var langData = scope.ServiceProvider.GetRequiredService(); + + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(args.Channel.Topic); + if (ticketId == Guid.Empty) { + Log.Debug( + "[{Source}] Channel is not a ticket channel, ignoring. ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + args.Channel.Id + ); + return; + } + + try { + var auditLogEntry = await args.Guild + .GetAuditLogsAsync(1, null, DiscordAuditLogActionType.ChannelDelete) + .FirstOrDefaultAsync(); + var user = auditLogEntry?.UserResponsible ?? client.CurrentUser; + Log.Debug( + "[{Source}] Channel deletion initiated by user: {UserId}, ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + user.Id, + args.Channel.Id + ); + await sender.Send(new UpdateDiscordUserCommand(user)); + + var ticket = await sender.Send(new GetTicketQuery(ticketId, true)); + if (ticket is null) { + Log.Warning( + "[{Source}] Could not retrieve ticket information, possibly already deleted. TicketId: {TicketId}, ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + ticketId, + args.Channel.Id + ); + return; + } + + if (ticket.ClosedDateUtc.HasValue) { + Log.Information( + "[{Source}] Ticket already closed, ignoring channel deletion event. TicketId: {TicketId}, ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + ticketId, + args.Channel.Id + ); + return; // Ticket is already closed + } + + await sender.Send(new ProcessCloseTicketCommand( + ticketId, + user.Id, + langData.GetTranslation(LangKeys.ChannelWasDeleted), + args.Channel + )); + + Log.Information( + "[{Source}] Ticket channel deleted. TicketId: {TicketId}, ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + ticketId, + args.Channel.Id + ); + } + catch (ModmailBotException ex) { + Log.Warning( + ex, + "[{Source}] ModmailBotException: Error processing channel deletion. ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + args.Channel.Id + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Unhandled exception processing channel deletion. ChannelId: {ChannelId}", + nameof(OnChannelDeletedEvent), + args.Channel.Id + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnMessageCreatedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageCreatedEvent.cs new file mode 100644 index 00000000..af406ee3 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageCreatedEvent.cs @@ -0,0 +1,19 @@ +using DSharpPlus; +using DSharpPlus.EventArgs; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Features.Ticket.Services; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class OnMessageCreatedEvent +{ + public static async Task OnMessageCreated(DiscordClient client, MessageCreatedEventArgs args) { + _ = UserUpdateEvents.UpdateUser(client, args.Author); + if (args.Message.Author?.IsBot == true) return; + if (args.Message.IsTTS) return; + + var scope = client.ServiceProvider.CreateScope(); + var ticketMessageQueue = scope.ServiceProvider.GetRequiredService(); + await ticketMessageQueue.Enqueue(args.Author.Id, args); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnMessageDeletedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageDeletedEvent.cs new file mode 100644 index 00000000..e9f7c9fe --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageDeletedEvent.cs @@ -0,0 +1,192 @@ +using DSharpPlus; +using DSharpPlus.EventArgs; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Static; +using Serilog; +using NotFoundException = DSharpPlus.Exceptions.NotFoundException; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class OnMessageDeletedEvent +{ + [PerformanceLoggerAspect] + public static async Task OnMessageDeleted( + DiscordClient client, + MessageDeletedEventArgs args + ) { + _ = UserUpdateEvents.UpdateUser(client, args.Message.Author); + + if (args.Message.Author?.IsBot == true) { + Log.Debug( + "[{Source}] Ignoring message deletion from bot. MessageId: {MessageId}, ChannelId: {ChannelId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + args.Channel.Id + ); + return; + } + + using var scope = client.ServiceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + if (args.Channel.IsPrivate) + await ProcessPrivateMessageDeletion(client, dbContext, args); + else + await ProcessTicketChannelMessageDeletion(client, dbContext, args); + } + + private static async Task ProcessPrivateMessageDeletion( + DiscordClient client, + ModmailDbContext dbContext, + MessageDeletedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing private message deletion. MessageId: {MessageId}, ChannelId: {ChannelId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + args.Channel.Id + ); + + var messageEntity = await dbContext.TicketMessages + .FirstOrDefaultAsync( + x => !x.SentByMod && x.MessageDiscordId == args.Message.Id + ); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No TicketMessage entity found for deleted private message. MessageId: {MessageId}", + nameof(OnMessageDeletedEvent), + args.Message.Id + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == messageEntity.TicketId && + x.PrivateMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for deleted private message. MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + messageEntity.TicketId + ); + return; + } + + await DeleteMirroredMessage( + client, + messageEntity, + dbContext, + ticket.ModMessageChannelId, + messageEntity.BotMessageId + ); + } + + private static async Task ProcessTicketChannelMessageDeletion( + DiscordClient client, + ModmailDbContext dbContext, + MessageDeletedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing ticket channel message deletion. MessageId: {MessageId}, ChannelId: {ChannelId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + args.Channel.Id + ); + + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(args.Channel.Topic); + if (ticketId == Guid.Empty) { + Log.Warning( + "[{Source}] Could not extract valid TicketId from channel topic. ChannelId: {ChannelId}, Topic: {Topic}", + nameof(OnMessageDeletedEvent), + args.Channel.Id, + args.Channel.Topic + ); + return; + } + + var messageEntity = await dbContext.TicketMessages.FirstOrDefaultAsync(x => + x.SentByMod && x.TicketId == ticketId && x.MessageDiscordId == args.Message.Id); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No TicketMessage entity found for deleted ticket channel message. MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + ticketId + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == ticketId && + x.ModMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for deleted ticket channel message. MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageDeletedEvent), + args.Message.Id, + ticketId + ); + return; + } + + + await DeleteMirroredMessage( + client, + messageEntity, + dbContext, + ticket.PrivateMessageChannelId, + messageEntity.BotMessageId + ); + } + + private static async Task DeleteMirroredMessage( + DiscordClient client, + TicketMessage messageEntity, + ModmailDbContext dbContext, + ulong channelId, + ulong botMessageId + ) { + try { + messageEntity.ChangeStatus = TicketMessageChangeStatus.Deleted; + dbContext.Update(messageEntity); + var affected = await dbContext.SaveChangesAsync(); + if (affected == 0) throw new DbInternalException(); + + var channel = await client.GetChannelAsync(channelId); + var message = await channel.GetMessageAsync(botMessageId); + await message.DeleteAsync(); + Log.Information( + "[{Source}] Processed message deleted {ChannelId} {MessageId} " + + "{MessageAuthor}", + nameof(OnMessageDeletedEvent), + channel.Id, + message.Id, + message.Author?.Id + ); + } + catch (NotFoundException) { + Log.Warning( + "[{Source}] Mirrored message not found, assuming it was already deleted. ChannelId: {ChannelId}, BotMessageId: {BotMessageId}", + nameof(OnMessageDeletedEvent), + channelId, + botMessageId + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] An error occurred while deleting the mirrored message. ChannelId: {ChannelId}, BotMessageId: {BotMessageId}", + nameof(OnMessageDeletedEvent), + channelId, + botMessageId + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionAddedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionAddedEvent.cs new file mode 100644 index 00000000..6b91d951 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionAddedEvent.cs @@ -0,0 +1,242 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using DSharpPlus.Exceptions; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Features.User.Commands; +using Serilog; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public class OnMessageReactionAddedEvent +{ + [PerformanceLoggerAspect] + public static async Task OnMessageReactionAdded( + DiscordClient client, + MessageReactionAddedEventArgs args + ) { + if (args is null) { + Log.Debug( + "[{Source}] MessageReactionAddedEventArgs is null, exiting", + nameof(OnMessageReactionAddedEvent) + ); + return; + } + + if (args.User.IsBot) { + Log.Debug( + "[{Source}] Ignoring reaction added by bot. UserId: {UserId}", + nameof(OnMessageReactionAddedEvent), + args.User.Id + ); + return; + } + + if (args.Emoji.Name == TicketConstants.ProcessedReactionDiscordEmojiUnicode) { + Log.Debug( + "[{Source}] Ignoring processed reaction emoji. EmojiName: {EmojiName}", + nameof(OnMessageReactionAddedEvent), + args.Emoji.Name + ); + return; + } + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + await sender.Send(new UpdateDiscordUserCommand(args.User)); + + if (args.Channel.IsPrivate) + await ProcessPrivateChannelReaction(client, scope, args); + else + await ProcessTicketChannelReaction(client, scope, args); + } + + private static async Task ProcessPrivateChannelReaction( + DiscordClient client, + IServiceScope scope, + MessageReactionAddedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing reaction added in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id, + args.Emoji.Name + ); + + if (args.Message.Author?.Id != client.CurrentUser.Id) { + Log.Debug( + "[{Source}] Ignoring reaction removal not from bot's own message in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + + if (args.User.Id == client.CurrentUser.Id) { + Log.Debug( + "[{Source}] Ignoring bots reaction in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + var dbContext = scope.ServiceProvider.GetRequiredService(); + var messageEntity = await dbContext.TicketMessages + .FirstOrDefaultAsync(x => x.SentByMod && x.BotMessageId == args.Message.Id); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for reaction in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.OpenerUserId == args.User.Id && + x.Id == messageEntity.TicketId && x.PrivateMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for reaction in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id, + messageEntity.TicketId + ); + return; + } + + await AddReactionToMirroredMessage( + client, + ticket.ModMessageChannelId, + messageEntity.MessageDiscordId, + args.Emoji + ); + } + + private static async Task ProcessTicketChannelReaction( + DiscordClient client, + IServiceScope scope, + MessageReactionAddedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing reaction added in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id, + args.Emoji.Name + ); + + var topic = args.Channel.Topic; + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(topic); + if (ticketId == Guid.Empty) { + Log.Warning( + "[{Source}] Could not extract valid TicketId from channel topic. ChannelId: {ChannelId}, Topic: {Topic}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + topic + ); + return; + } + + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var messageEntity = await dbContext.TicketMessages.FirstOrDefaultAsync(x => + !x.SentByMod && x.BotMessageId == args.Message.Id && x.TicketId == ticketId); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for reaction in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == ticketId && + x.ModMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for reaction in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + await AddReactionToMirroredMessage( + client, + ticket.PrivateMessageChannelId, + messageEntity.MessageDiscordId, + args.Emoji + ); + } + + private static async Task AddReactionToMirroredMessage( + DiscordClient client, + ulong? channelId, + ulong? messageId, + DiscordEmoji emoji + ) { + if (!channelId.HasValue || !messageId.HasValue) { + Log.Warning( + "[{Source}] Cannot add reaction to mirrored message. Invalid ChannelId or MessageId. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionAddedEvent), + channelId, + messageId, + emoji.Name + ); + return; + } + + try { + var channel = await client.GetChannelAsync(channelId.Value); + var message = await channel.GetMessageAsync(messageId.Value); + await message.CreateReactionAsync(emoji); + Log.Information( + "[{Source}] Processed reaction added {ChannelId} {MessageId} " + + "{MessageAuthor} {Emoji}", + nameof(OnMessageReactionAddedEvent), + channel.Id, + message.Id, + message.Author?.Id, + emoji.Name + ); + } + catch (NotFoundException) { + Log.Warning( + "[{Source}] Mirrored message not found, cannot add reaction. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionAddedEvent), + channelId, + messageId, + emoji.Name + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] An error occurred while adding the reaction to the mirrored message. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionAddedEvent), + channelId, + messageId, + emoji.Name + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionRemovedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionRemovedEvent.cs new file mode 100644 index 00000000..65e07e60 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageReactionRemovedEvent.cs @@ -0,0 +1,242 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using DSharpPlus.Exceptions; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Features.User.Commands; +using Serilog; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class OnMessageReactionRemovedEvent +{ + [PerformanceLoggerAspect] + public static async Task OnMessageReactionRemoved( + DiscordClient client, + MessageReactionRemovedEventArgs args + ) { + if (args is null) { + Log.Debug( + "[{Source}] MessageReactionRemovedEventArgs is null, exiting", + nameof(OnMessageReactionRemovedEvent) + ); + return; + } + + if (args.User.IsBot) { + Log.Debug( + "[{Source}] Ignoring reaction removed by bot. UserId: {UserId}", + nameof(OnMessageReactionRemovedEvent), + args.User.Id + ); + return; + } + + if (args.Emoji.Name == TicketConstants.ProcessedReactionDiscordEmojiUnicode) { + Log.Debug( + "[{Source}] Ignoring processed reaction emoji removed. EmojiName: {EmojiName}", + nameof(OnMessageReactionRemovedEvent), + args.Emoji.Name + ); + return; + } + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + await sender.Send(new UpdateDiscordUserCommand(args.User)); + + if (args.Channel.IsPrivate) + await ProcessPrivateChannelReactionRemoval(client, scope, args); + else + await ProcessTicketChannelReactionRemoval(client, scope, args); + } + + private static async Task ProcessPrivateChannelReactionRemoval( + DiscordClient client, + IServiceScope scope, + MessageReactionRemovedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing reaction removed in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id, + args.Emoji.Name + ); + + if (args.Message.Author?.Id != client.CurrentUser.Id) { + Log.Debug( + "[{Source}] Ignoring reaction removal not from bot's own message in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + if (args.User.Id == client.CurrentUser.Id) { + Log.Debug( + "[{Source}] Ignoring bots reaction in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionAddedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + + var dbContext = scope.ServiceProvider.GetRequiredService(); + var messageEntity = await dbContext.TicketMessages + .FirstOrDefaultAsync(x => x.SentByMod && x.BotMessageId == args.Message.Id); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for reaction removal in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.OpenerUserId == args.User.Id && + x.Id == messageEntity.TicketId && x.PrivateMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for reaction removal in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id, + messageEntity.TicketId + ); + return; + } + + await RemoveReactionFromMirroredMessage( + client, + ticket.ModMessageChannelId, + messageEntity.MessageDiscordId, + args.Emoji + ); + } + + private static async Task ProcessTicketChannelReactionRemoval( + DiscordClient client, + IServiceScope scope, + MessageReactionRemovedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing reaction removed in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id, + args.Emoji.Name + ); + + var topic = args.Channel.Topic; + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(topic); + if (ticketId == Guid.Empty) { + Log.Warning( + "[{Source}] Could not extract valid TicketId from channel topic. ChannelId: {ChannelId}, Topic: {Topic}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + topic + ); + return; + } + + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var messageEntity = await dbContext.TicketMessages.FirstOrDefaultAsync(x => + !x.SentByMod && x.BotMessageId == args.Message.Id && x.TicketId == ticketId); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for reaction removal in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == ticketId && + x.ModMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for reaction removal in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageReactionRemovedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + await RemoveReactionFromMirroredMessage( + client, + ticket.PrivateMessageChannelId, + messageEntity.MessageDiscordId, + args.Emoji + ); + } + + private static async Task RemoveReactionFromMirroredMessage( + DiscordClient client, + ulong? channelId, + ulong? messageId, + DiscordEmoji emoji + ) { + if (!channelId.HasValue || !messageId.HasValue) { + Log.Warning( + "[{Source}] Cannot remove reaction from mirrored message. Invalid ChannelId or MessageId. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionRemovedEvent), + channelId, + messageId, + emoji.Name + ); + return; + } + + try { + var channel = await client.GetChannelAsync(channelId.Value); + var message = await channel.GetMessageAsync(messageId.Value); + await message.DeleteOwnReactionAsync(emoji); + Log.Information( + "[{Source}] Processed reaction removed {ChannelId} " + + "{MessageId} {MessageAuthor} {Emoji}", + nameof(OnMessageReactionRemovedEvent), + channel.Id, + message.Id, + message.Author?.Id, + emoji + ); + } + catch (NotFoundException) { + Log.Warning( + "[{Source}] Mirrored message not found, cannot remove reaction. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionRemovedEvent), + channelId, + messageId, + emoji.Name + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] An error occurred while removing the reaction from the mirrored message. ChannelId: {ChannelId}, MessageId: {MessageId}, Emoji: {Emoji}", + nameof(OnMessageReactionRemovedEvent), + channelId, + messageId, + emoji.Name + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/OnMessageUpdatedEvent.cs b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageUpdatedEvent.cs new file mode 100644 index 00000000..8fa29303 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/OnMessageUpdatedEvent.cs @@ -0,0 +1,266 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Features.User.Commands; +using Serilog; +using NotFoundException = DSharpPlus.Exceptions.NotFoundException; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class OnMessageUpdatedEvent +{ + [PerformanceLoggerAspect] + public static async Task OnMessageUpdated( + DiscordClient client, + MessageUpdatedEventArgs args + ) { + if (args is null) { + Log.Debug( + "[{Source}] MessageUpdatedEventArgs is null, exiting", + nameof(OnMessageUpdatedEvent) + ); + return; + } + + if (args.Author.IsBot) { + Log.Debug( + "[{Source}] Ignoring message update from bot. UserId: {UserId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Author.Id, + args.Message.Id + ); + return; + } + + if (args.MessageBefore?.Content == args.Message.Content) { + Log.Debug( + "[{Source}] Message content unchanged, ignoring update. MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Message.Id + ); + return; + } + + using var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + await sender.Send(new UpdateDiscordUserCommand(args.Author)); + + if (args.Channel.IsPrivate) + await ProcessPrivateChannelMessageUpdate(client, scope, args); + else + await ProcessTicketChannelMessageUpdate(client, scope, args); + } + + private static async Task ProcessPrivateChannelMessageUpdate( + DiscordClient client, + IServiceScope scope, + MessageUpdatedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing message update in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id + ); + + if (args.Message.Author?.Id == client.CurrentUser.Id) { + Log.Debug( + "[{Source}] Ignoring message update on bot's own message in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + var dbContext = scope.ServiceProvider.GetRequiredService(); + var messageEntity = await dbContext.TicketMessages + .FirstOrDefaultAsync(x => + !x.SentByMod && x.MessageDiscordId == args.Message.Id && + x.SenderUserId == args.Message.Author.Id); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for update in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == messageEntity.TicketId && + x.PrivateMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for update in private channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id, + messageEntity.TicketId + ); + return; + } + + await UpdateMirroredMessage( + client, + messageEntity, + dbContext, + scope, + ticket.ModMessageChannelId, + messageEntity.BotMessageId, + args.MessageBefore, + args.Message, + ticket.Anonymous + ); + } + + private static async Task ProcessTicketChannelMessageUpdate( + DiscordClient client, + IServiceScope scope, + MessageUpdatedEventArgs args + ) { + Log.Debug( + "[{Source}] Processing message update in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id + ); + + var topic = args.Channel.Topic; + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(topic); + if (ticketId == Guid.Empty) { + Log.Warning( + "[{Source}] Could not extract valid TicketId from channel topic. ChannelId: {ChannelId}, Topic: {Topic}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + topic + ); + return; + } + + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var messageEntity = await dbContext.TicketMessages.FirstOrDefaultAsync(x => + x.SentByMod && x.MessageDiscordId == args.Message.Id && + x.SenderUserId == args.Message.Author.Id); + if (messageEntity is null) { + Log.Debug( + "[{Source}] No matching TicketMessage found for update in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + var ticket = await dbContext.Tickets.FirstOrDefaultAsync(x => + !x.ClosedDateUtc.HasValue && x.Id == ticketId && + x.ModMessageChannelId == args.Channel.Id); + if (ticket is null) { + Log.Warning( + "[{Source}] No active ticket found for update in ticket channel. ChannelId: {ChannelId}, MessageId: {MessageId}, TicketId: {TicketId}", + nameof(OnMessageUpdatedEvent), + args.Channel.Id, + args.Message.Id, + ticketId + ); + return; + } + + await UpdateMirroredMessage( + client, + messageEntity, + dbContext, + scope, + ticket.PrivateMessageChannelId, + messageEntity.BotMessageId, + args.MessageBefore, + args.Message, + ticket.Anonymous + ); + } + + private static async Task UpdateMirroredMessage( + DiscordClient client, + TicketMessage messageEntity, + ModmailDbContext dbContext, + IServiceScope scope, + ulong channelId, + ulong messageId, + DiscordMessage oldMessage, + DiscordMessage updatedMessage, + bool anonymous + ) { + try { + var newMessageHistory = new TicketMessageHistory { + TicketMessageId = messageEntity.Id, + MessageContentBefore = messageEntity.MessageContent, + MessageContentAfter = updatedMessage.Content + }; + + messageEntity.ChangeStatus = TicketMessageChangeStatus.Updated; + messageEntity.MessageContent = updatedMessage.Content; + dbContext.Update(messageEntity); + dbContext.Add(newMessageHistory); + + var affected = await dbContext.SaveChangesAsync(); + if (affected == 0) throw new DbInternalException(); + + var sender = scope.ServiceProvider.GetRequiredService(); + var option = await sender.Send(new GetGuildOptionQuery(false)); + + var channel = await client.GetChannelAsync(channelId); + var message = await channel.GetMessageAsync(messageId); + var embed = + updatedMessage.Channel!.IsPrivate + ? TicketBotMessages.Ticket.MessageEdited(updatedMessage) + : TicketBotMessages.User.MessageEdited(updatedMessage, option.AlwaysAnonymous || anonymous); + + //TODO: Add support for removing attachment files from message on message update event + //Currently discord API or library does not support attachment removal from sent message + await message.ModifyAsync(x => { + x.ClearEmbeds(); + x.AddEmbed(embed); + }); + + Log.Information( + "[{Source}] Processed message edited {ChannelId} " + + "{MessageId} {MessageAuthor}", + nameof(OnMessageUpdatedEvent), + channel.Id, + message.Id, + updatedMessage.Author?.Id + ); + } + catch (NotFoundException) { + Log.Warning( + "[{Source}] Mirrored message not found, cannot update. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + channelId, + messageId + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] An error occurred while updating the mirrored message. ChannelId: {ChannelId}, MessageId: {MessageId}", + nameof(OnMessageUpdatedEvent), + channelId, + messageId + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Events/UserUpdateEvents.cs b/src/Modmail.NET/Features/DiscordBot/Events/UserUpdateEvents.cs new file mode 100644 index 00000000..69ac1907 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Events/UserUpdateEvents.cs @@ -0,0 +1,45 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Features.User.Commands; + +namespace Modmail.NET.Features.DiscordBot.Events; + +public static class UserUpdateEvents +{ + public static async Task UpdateUser(DiscordClient client, DiscordUser user) { + var scope = client.ServiceProvider.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + await sender.Send(new UpdateDiscordUserCommand(user)); + } + + public static async Task InteractionCreated(DiscordClient client, InteractionCreatedEventArgs args) { + await UpdateUser(client, args.Interaction.User); + } + + public static async Task OnUserUpdated(DiscordClient client, UserUpdatedEventArgs args) { + await UpdateUser(client, args.UserAfter); + } + + public static async Task OnUserSettingsUpdated(DiscordClient client, UserSettingsUpdatedEventArgs args) { + await UpdateUser(client, args.User); + } + + public static async Task OnGuildMemberAdded(DiscordClient client, GuildMemberAddedEventArgs args) { + await UpdateUser(client, args.Member); + } + + public static async Task OnGuildMemberRemoved(DiscordClient client, GuildMemberRemovedEventArgs args) { + await UpdateUser(client, args.Member); + } + + public static async Task OnGuildBanAdded(DiscordClient client, GuildBanAddedEventArgs args) { + await UpdateUser(client, args.Member); + } + + public static async Task OnGuildBanRemoved(DiscordClient client, GuildBanRemovedEventArgs args) { + await UpdateUser(client, args.Member); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordLogChannelHandler.cs b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordLogChannelHandler.cs similarity index 86% rename from src/Modmail.NET/Features/Bot/Handlers/GetDiscordLogChannelHandler.cs rename to src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordLogChannelHandler.cs index 4a4650db..c631fe67 100644 --- a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordLogChannelHandler.cs +++ b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordLogChannelHandler.cs @@ -1,10 +1,12 @@ using DSharpPlus.Entities; using DSharpPlus.Exceptions; using MediatR; -using Modmail.NET.Features.Guild; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Commands; +using Modmail.NET.Features.Guild.Queries; using Serilog; -namespace Modmail.NET.Features.Bot.Handlers; +namespace Modmail.NET.Features.DiscordBot.Handlers; public class GetDiscordLogChannelHandler : IRequestHandler { diff --git a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordMainGuildHandler.cs b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMainGuildHandler.cs similarity index 85% rename from src/Modmail.NET/Features/Bot/Handlers/GetDiscordMainGuildHandler.cs rename to src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMainGuildHandler.cs index a70da8b7..e7d7a207 100644 --- a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordMainGuildHandler.cs +++ b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMainGuildHandler.cs @@ -1,14 +1,16 @@ using DSharpPlus.Entities; using MediatR; using Microsoft.Extensions.Options; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.User.Commands; +using Modmail.NET.Language; using Serilog; using NotFoundException = DSharpPlus.Exceptions.NotFoundException; -namespace Modmail.NET.Features.Bot.Handlers; +namespace Modmail.NET.Features.DiscordBot.Handlers; public class GetDiscordMainGuildHandler : IRequestHandler { @@ -35,7 +37,7 @@ public async Task Handle(GetDiscordMainGuildQuery request, Cancell } catch (NotFoundException) { Log.Error("Main guild not found: {GuildId}", guildId); - throw new Exceptions.NotFoundException(LangKeys.MainGuild); + throw new Common.Exceptions.NotFoundException(LangKeys.MainGuild); } var guildOption = await _sender.Send(new GetGuildOptionQuery(false), cancellationToken) ?? throw new NullReferenceException(); diff --git a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordMemberHandler.cs b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMemberHandler.cs similarity index 86% rename from src/Modmail.NET/Features/Bot/Handlers/GetDiscordMemberHandler.cs rename to src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMemberHandler.cs index 56ab2b24..d07bd893 100644 --- a/src/Modmail.NET/Features/Bot/Handlers/GetDiscordMemberHandler.cs +++ b/src/Modmail.NET/Features/DiscordBot/Handlers/GetDiscordMemberHandler.cs @@ -1,9 +1,10 @@ using DSharpPlus.Entities; using DSharpPlus.Exceptions; using MediatR; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.User.Commands; -namespace Modmail.NET.Features.Bot.Handlers; +namespace Modmail.NET.Features.DiscordBot.Handlers; public class GetDiscordMemberHandler : IRequestHandler { diff --git a/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordLogChannelQuery.cs b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordLogChannelQuery.cs new file mode 100644 index 00000000..0c93f3c3 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordLogChannelQuery.cs @@ -0,0 +1,8 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Attributes; + +namespace Modmail.NET.Features.DiscordBot.Queries; + +[CachePolicy("GetDiscordLogChannelQuery", 60)] +public sealed record GetDiscordLogChannelQuery : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMainGuildQuery.cs b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMainGuildQuery.cs new file mode 100644 index 00000000..af555385 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMainGuildQuery.cs @@ -0,0 +1,8 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Attributes; + +namespace Modmail.NET.Features.DiscordBot.Queries; + +[CachePolicy("GetMainGuildQuery", 300)] +public sealed record GetDiscordMainGuildQuery : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMemberQuery.cs b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMemberQuery.cs new file mode 100644 index 00000000..897f9092 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordBot/Queries/GetDiscordMemberQuery.cs @@ -0,0 +1,6 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.DiscordBot.Queries; + +public sealed record GetDiscordMemberQuery(ulong UserId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Checks/Attributes/RequireMainServerAttribute.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireMainServerAttribute.cs similarity index 62% rename from src/Modmail.NET/Checks/Attributes/RequireMainServerAttribute.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireMainServerAttribute.cs index c385d448..e97d981e 100644 --- a/src/Modmail.NET/Checks/Attributes/RequireMainServerAttribute.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireMainServerAttribute.cs @@ -1,5 +1,5 @@ using DSharpPlus.Commands.ContextChecks; -namespace Modmail.NET.Checks.Attributes; +namespace Modmail.NET.Features.DiscordCommands.Checks.Attributes; public class RequireMainServerAttribute : ContextCheckAttribute; \ No newline at end of file diff --git a/src/Modmail.NET/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs similarity index 74% rename from src/Modmail.NET/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs index 80355fc8..5dbe222d 100644 --- a/src/Modmail.NET/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequirePermissionLevelOrHigherAttribute.cs @@ -1,6 +1,7 @@ using DSharpPlus.Commands.ContextChecks; +using Modmail.NET.Features.Permission.Static; -namespace Modmail.NET.Checks.Attributes; +namespace Modmail.NET.Features.DiscordCommands.Checks.Attributes; public class RequirePermissionLevelOrHigherAttribute : ContextCheckAttribute { diff --git a/src/Modmail.NET/Checks/Attributes/RequireTicketChannelAttribute.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireTicketChannelAttribute.cs similarity index 63% rename from src/Modmail.NET/Checks/Attributes/RequireTicketChannelAttribute.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireTicketChannelAttribute.cs index 583386c0..39d806d5 100644 --- a/src/Modmail.NET/Checks/Attributes/RequireTicketChannelAttribute.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/RequireTicketChannelAttribute.cs @@ -1,5 +1,5 @@ using DSharpPlus.Commands.ContextChecks; -namespace Modmail.NET.Checks.Attributes; +namespace Modmail.NET.Features.DiscordCommands.Checks.Attributes; public class RequireTicketChannelAttribute : ContextCheckAttribute; \ No newline at end of file diff --git a/src/Modmail.NET/Checks/Attributes/UpdateUserInformationAttribute.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/UpdateUserInformationAttribute.cs similarity index 63% rename from src/Modmail.NET/Checks/Attributes/UpdateUserInformationAttribute.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/UpdateUserInformationAttribute.cs index 64d2c71e..7e45ed1c 100644 --- a/src/Modmail.NET/Checks/Attributes/UpdateUserInformationAttribute.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/Attributes/UpdateUserInformationAttribute.cs @@ -1,5 +1,5 @@ using DSharpPlus.Commands.ContextChecks; -namespace Modmail.NET.Checks.Attributes; +namespace Modmail.NET.Features.DiscordCommands.Checks.Attributes; public class UpdateUserInformationAttribute : ContextCheckAttribute; \ No newline at end of file diff --git a/src/Modmail.NET/Checks/RequireMainServerCheck.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/RequireMainServerCheck.cs similarity index 81% rename from src/Modmail.NET/Checks/RequireMainServerCheck.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/RequireMainServerCheck.cs index 4ca36646..45d5ddd7 100644 --- a/src/Modmail.NET/Checks/RequireMainServerCheck.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/RequireMainServerCheck.cs @@ -2,9 +2,10 @@ using DSharpPlus.Commands.ContextChecks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Modmail.NET.Checks.Attributes; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Language; -namespace Modmail.NET.Checks; +namespace Modmail.NET.Features.DiscordCommands.Checks; public class RequireMainServerCheck : IContextCheck { diff --git a/src/Modmail.NET/Checks/RequirePermissionLevelOrHigherCheck.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/RequirePermissionLevelOrHigherCheck.cs similarity index 89% rename from src/Modmail.NET/Checks/RequirePermissionLevelOrHigherCheck.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/RequirePermissionLevelOrHigherCheck.cs index 34413472..bc5aabec 100644 --- a/src/Modmail.NET/Checks/RequirePermissionLevelOrHigherCheck.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/RequirePermissionLevelOrHigherCheck.cs @@ -4,10 +4,11 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Features.Permission; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Language; -namespace Modmail.NET.Checks; +namespace Modmail.NET.Features.DiscordCommands.Checks; public class RequirePermissionLevelOrHigherCheck : IContextCheck { diff --git a/src/Modmail.NET/Checks/RequireTicketChannelCheck.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/RequireTicketChannelCheck.cs similarity index 74% rename from src/Modmail.NET/Checks/RequireTicketChannelCheck.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/RequireTicketChannelCheck.cs index 1757ac4c..785a48b7 100644 --- a/src/Modmail.NET/Checks/RequireTicketChannelCheck.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/RequireTicketChannelCheck.cs @@ -1,9 +1,10 @@ using DSharpPlus.Commands; using DSharpPlus.Commands.ContextChecks; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Language; -namespace Modmail.NET.Checks; +namespace Modmail.NET.Features.DiscordCommands.Checks; public class RequireTicketChannelCheck : IContextCheck { diff --git a/src/Modmail.NET/Checks/UpdateUserInformationCheck.cs b/src/Modmail.NET/Features/DiscordCommands/Checks/UpdateUserInformationCheck.cs similarity index 77% rename from src/Modmail.NET/Checks/UpdateUserInformationCheck.cs rename to src/Modmail.NET/Features/DiscordCommands/Checks/UpdateUserInformationCheck.cs index dce1abb0..b142fefa 100644 --- a/src/Modmail.NET/Checks/UpdateUserInformationCheck.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Checks/UpdateUserInformationCheck.cs @@ -2,10 +2,10 @@ using DSharpPlus.Commands.ContextChecks; using MediatR; using Microsoft.Extensions.DependencyInjection; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.User.Commands; -namespace Modmail.NET.Checks; +namespace Modmail.NET.Features.DiscordCommands.Checks; public class UpdateUserInformationCheck : IContextCheck { diff --git a/src/Modmail.NET/Commands/Slash/BlacklistSlashCommands.cs b/src/Modmail.NET/Features/DiscordCommands/Handlers/BlacklistSlashCommands.cs similarity index 78% rename from src/Modmail.NET/Commands/Slash/BlacklistSlashCommands.cs rename to src/Modmail.NET/Features/DiscordCommands/Handlers/BlacklistSlashCommands.cs index 22b5d51d..642c1ce1 100644 --- a/src/Modmail.NET/Commands/Slash/BlacklistSlashCommands.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Handlers/BlacklistSlashCommands.cs @@ -3,15 +3,19 @@ using DSharpPlus.Commands.Processors.SlashCommands; using DSharpPlus.Entities; using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Aspects; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Extensions; -using Modmail.NET.Features.Blacklist; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Blacklist.Commands; +using Modmail.NET.Features.Blacklist.Queries; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.User.Commands; +using Modmail.NET.Language; using Serilog; -namespace Modmail.NET.Commands.Slash; +namespace Modmail.NET.Features.DiscordCommands.Handlers; [PerformanceLoggerAspect] [Command("blacklist")] @@ -39,13 +43,13 @@ public async Task Add(SlashCommandContext ctx, try { await _sender.Send(new UpdateDiscordUserCommand(user)); await _sender.Send(new ProcessAddUserToBlacklistCommand(ctx.User.Id, user.Id, reason)); - await ctx.EditResponseAsync(Webhooks.Success(LangKeys.UserBlacklisted.GetTranslation())); + await ctx.EditResponseAsync(ModmailWebhooks.Success(LangKeys.UserBlacklisted.GetTranslation())); Log.Information(logMessage, ctx.User.Id, user.Id, reason); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { Log.Warning(ex, logMessage, ctx.User.Id, @@ -78,9 +82,9 @@ DiscordUser user Log.Information(logMessage, ctx.User.Id, user.Id); - await ctx.EditResponseAsync(Webhooks.Success(LangKeys.UserBlacklisted.GetTranslation())); + await ctx.EditResponseAsync(ModmailWebhooks.Success(LangKeys.UserBlacklisted.GetTranslation())); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.EditResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, @@ -106,13 +110,13 @@ DiscordUser user await ctx.Interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try { var isBlocked = await _sender.Send(new CheckUserBlacklistStatusQuery(user.Id)); - await ctx.EditResponseAsync(Webhooks.Info(LangKeys.UserBlacklistStatus.GetTranslation(), - isBlocked - ? LangKeys.UserIsBlacklisted.GetTranslation() - : LangKeys.UserIsNotBlacklisted.GetTranslation())); + await ctx.EditResponseAsync(ModmailWebhooks.Info(LangKeys.UserBlacklistStatus.GetTranslation(), + isBlocked + ? LangKeys.UserIsBlacklisted.GetTranslation() + : LangKeys.UserIsNotBlacklisted.GetTranslation())); Log.Information(logMessage, ctx.User.Id, user.Id); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.EditResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, ctx.User.Id, user.Id); } diff --git a/src/Modmail.NET/Commands/ModmailCommands.cs b/src/Modmail.NET/Features/DiscordCommands/Handlers/ModmailCommands.cs similarity index 71% rename from src/Modmail.NET/Commands/ModmailCommands.cs rename to src/Modmail.NET/Features/DiscordCommands/Handlers/ModmailCommands.cs index 161f496d..40e138b5 100644 --- a/src/Modmail.NET/Commands/ModmailCommands.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Handlers/ModmailCommands.cs @@ -3,14 +3,16 @@ using DSharpPlus.Commands.ContextChecks; using DSharpPlus.Entities; using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Aspects; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Extensions; -using Modmail.NET.Features.Guild; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.Guild.Commands; +using Modmail.NET.Language; using Serilog; -namespace Modmail.NET.Commands; +namespace Modmail.NET.Features.DiscordCommands.Handlers; [PerformanceLoggerAspect] [RequireGuild] @@ -33,11 +35,11 @@ public async Task Setup(CommandContext ctx) { try { await _sender.Send(new ProcessGuildSetupCommand(ctx.User.Id, ctx.Guild)); - await ctx.RespondAsync(Embeds.Success(LangKeys.ServerSetupComplete.GetTranslation())); + await ctx.RespondAsync(ModmailEmbeds.Success(LangKeys.ServerSetupComplete.GetTranslation())); Log.Information(logMessage, ctx.User.Id); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.RespondAsync(ex.ToEmbedResponse()); Log.Warning(ex, logMessage, diff --git a/src/Modmail.NET/Features/DiscordCommands/Handlers/TagSlashCommands.cs b/src/Modmail.NET/Features/DiscordCommands/Handlers/TagSlashCommands.cs new file mode 100644 index 00000000..5e835d41 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordCommands/Handlers/TagSlashCommands.cs @@ -0,0 +1,62 @@ +using System.ComponentModel; +using DSharpPlus.Commands; +using DSharpPlus.Commands.Processors.SlashCommands; +using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers; +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.DiscordCommands.Helpers; +using Modmail.NET.Features.Tag.Helpers; +using Modmail.NET.Features.Tag.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; +using Serilog; + +namespace Modmail.NET.Features.DiscordCommands.Handlers; + +public class TagSlashCommands +{ + private readonly ISender _sender; + + public TagSlashCommands(ISender sender) { + _sender = sender; + } + + [Command("tag")] + [Description("Get tag content")] + [PerformanceLoggerAspect] + [UpdateUserInformation] + public async Task Get(SlashCommandContext ctx, + [Parameter("name")] [Description("Tag name")] [SlashAutoCompleteProvider(typeof(TagProvider))] + string name + ) { + const string logMessage = $"[{nameof(TagSlashCommands)}]{nameof(Get)}({{ContextUserId}},{{TagName}})"; + await ctx.Interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + try { + var tag = await _sender.Send(new GetTagByNameQuery(name)); + + await ctx.EditResponseAsync(TagBotMessages.TagSent(tag)); + + var channelTopic = ctx.Channel.Topic; + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(channelTopic); + if (ticketId != Guid.Empty) { + var isActiveTicket = await _sender.Send(new CheckActiveTicketQuery(ticketId)); + if (isActiveTicket) await _sender.Send(new ProcessTagSendMessageCommand(ticketId, tag.Id, ctx.User, ctx.Channel, ctx.Guild)); + } + + Log.Information(logMessage, ctx.User.Id, name); + } + catch (ModmailBotException ex) { + await ctx.EditResponseAsync(ex.ToWebhookResponse()); + Log.Warning(ex, logMessage, ctx.User.Id, name); + } + catch (Exception ex) { + await ctx.EditResponseAsync(ex.ToWebhookResponse()); + Log.Fatal(ex, logMessage, ctx.User.Id, name); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Commands/Slash/TicketSlashCommands.cs b/src/Modmail.NET/Features/DiscordCommands/Handlers/TicketSlashCommands.cs similarity index 79% rename from src/Modmail.NET/Commands/Slash/TicketSlashCommands.cs rename to src/Modmail.NET/Features/DiscordCommands/Handlers/TicketSlashCommands.cs index 3d97ce42..50729123 100644 --- a/src/Modmail.NET/Commands/Slash/TicketSlashCommands.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Handlers/TicketSlashCommands.cs @@ -4,18 +4,22 @@ using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers; using DSharpPlus.Entities; using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Aspects; -using Modmail.NET.Checks.Attributes; -using Modmail.NET.Extensions; -using Modmail.NET.Features.Permission; -using Modmail.NET.Features.Ticket; -using Modmail.NET.Features.TicketType; -using Modmail.NET.Providers; -using Modmail.NET.Utils; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.DiscordCommands.Checks.Attributes; +using Modmail.NET.Features.DiscordCommands.Helpers; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Language; using Serilog; -namespace Modmail.NET.Commands.Slash; +namespace Modmail.NET.Features.DiscordCommands.Handlers; [PerformanceLoggerAspect] [Command("ticket")] @@ -46,16 +50,16 @@ public async Task CloseTicket(SlashCommandContext ctx, var isAnyTeamMember = await _sender.Send(new CheckUserInAnyTeamQuery(ctx.User.Id)); if (!isAnyTeamMember) { await ctx.Interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage, - Interactions.Error(LangKeys.YouDoNotHavePermissionToUseThisCommand.GetTranslation()).AsEphemeral()); + ModmailInteractions.Error(LangKeys.YouDoNotHavePermissionToUseThisCommand.GetTranslation()).AsEphemeral()); return; } } await _sender.Send(new ProcessCloseTicketCommand(ticketId, ctx.User.Id, reason, ctx.Channel)); - await ctx.Interaction.EditOriginalResponseAsync(Webhooks.Success(LangKeys.TicketClosed.GetTranslation())); + await ctx.Interaction.EditOriginalResponseAsync(ModmailWebhooks.Success(LangKeys.TicketClosed.GetTranslation())); Log.Information(logMessage, ctx.User.Id, reason); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.Interaction.EditOriginalResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, ctx.User.Id, reason); } @@ -75,10 +79,10 @@ public async Task SetPriority(SlashCommandContext ctx, try { var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(ctx.Channel.Topic); await _sender.Send(new ProcessChangePriorityCommand(ticketId, ctx.User.Id, priority, ctx.Channel)); - await ctx.Interaction.EditOriginalResponseAsync(Webhooks.Success(LangKeys.TicketPriorityChanged.GetTranslation())); + await ctx.Interaction.EditOriginalResponseAsync(ModmailWebhooks.Success(LangKeys.TicketPriorityChanged.GetTranslation())); Log.Information(logMessage, ctx.User.Id, priority); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.Interaction.EditOriginalResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, ctx.User.Id, priority); } @@ -100,10 +104,10 @@ string note try { var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(ctx.Channel.Topic); await _sender.Send(new ProcessAddNoteCommand(ticketId, ctx.User.Id, note)); - await ctx.Interaction.EditOriginalResponseAsync(Webhooks.Success(LangKeys.NoteAdded.GetTranslation())); + await ctx.Interaction.EditOriginalResponseAsync(ModmailWebhooks.Success(LangKeys.NoteAdded.GetTranslation())); Log.Information(logMessage, ctx.User.Id, note); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.Interaction.EditOriginalResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, ctx.User.Id, note); } @@ -121,10 +125,10 @@ public async Task ToggleAnonymous(SlashCommandContext ctx) { try { var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(ctx.Channel.Topic); await _sender.Send(new ProcessToggleAnonymousCommand(ticketId, ctx.Channel)); - await ctx.Interaction.EditOriginalResponseAsync(Webhooks.Success(LangKeys.TicketAnonymousToggled.GetTranslation())); + await ctx.Interaction.EditOriginalResponseAsync(ModmailWebhooks.Success(LangKeys.TicketAnonymousToggled.GetTranslation())); Log.Information(logMessage, ctx.User.Id); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.Interaction.EditOriginalResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage, ctx.User.Id); } @@ -145,10 +149,10 @@ public async Task SetType(SlashCommandContext ctx, try { var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(ctx.Channel.Topic); await _sender.Send(new ProcessChangeTicketTypeCommand(ticketId, type, ctx.Channel, UserId: ctx.User.Id)); - await ctx.Interaction.EditOriginalResponseAsync(Webhooks.Success(LangKeys.TicketTypeChanged.GetTranslation())); + await ctx.Interaction.EditOriginalResponseAsync(ModmailWebhooks.Success(LangKeys.TicketTypeChanged.GetTranslation())); Log.Information(logMessage, ctx.User.Id, type); } - catch (BotExceptionBase e) { + catch (ModmailBotException e) { await ctx.Interaction.EditOriginalResponseAsync(e.ToWebhookResponse()); Log.Warning(e, logMessage, ctx.User.Id, type); } @@ -167,12 +171,12 @@ public async Task GetTicketType(SlashCommandContext ctx) { try { var ticketType = await _sender.Send(new GetTicketTypeByChannelIdQuery(ctx.Channel.Id, true)); if (ticketType is null) - await ctx.EditResponseAsync(Webhooks.Info(LangKeys.TicketTypeNotSet.GetTranslation())); + await ctx.EditResponseAsync(ModmailWebhooks.Info(LangKeys.TicketTypeNotSet.GetTranslation())); else - await ctx.EditResponseAsync(Webhooks.Info(LangKeys.TicketType.GetTranslation(), $"`{ticketType.Name}` - {ticketType.Description}")); + await ctx.EditResponseAsync(ModmailWebhooks.Info(LangKeys.TicketType.GetTranslation(), $"`{ticketType.Name}` - {ticketType.Description}")); Log.Information(logMessage); } - catch (BotExceptionBase ex) { + catch (ModmailBotException ex) { await ctx.EditResponseAsync(ex.ToWebhookResponse()); Log.Warning(ex, logMessage); } diff --git a/src/Modmail.NET/Features/DiscordCommands/Helpers/TagProvider.cs b/src/Modmail.NET/Features/DiscordCommands/Helpers/TagProvider.cs new file mode 100644 index 00000000..abdace48 --- /dev/null +++ b/src/Modmail.NET/Features/DiscordCommands/Helpers/TagProvider.cs @@ -0,0 +1,26 @@ +using DSharpPlus.Commands.Processors.SlashCommands; +using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers; +using DSharpPlus.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; +using Modmail.NET.Database; + +namespace Modmail.NET.Features.DiscordCommands.Helpers; + +public class TagProvider : IAutoCompleteProvider +{ + public async ValueTask> AutoCompleteAsync(AutoCompleteContext context) { + const string cacheKey = "TagProvider.Provider.AutoComplete"; + var cache = context.ServiceProvider.GetRequiredService(); + return await cache.GetOrCreateAsync(cacheKey, Get, new MemoryCacheEntryOptions { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5) + }); + + async Task> Get(ICacheEntry entry) { + var scope = context.ServiceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + return await dbContext.Tags.Select(x => new DiscordAutoCompleteChoice(x.Name, x.Name)).ToArrayAsync(); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Providers/TicketTypeProvider.cs b/src/Modmail.NET/Features/DiscordCommands/Helpers/TicketTypeProvider.cs similarity index 92% rename from src/Modmail.NET/Providers/TicketTypeProvider.cs rename to src/Modmail.NET/Features/DiscordCommands/Helpers/TicketTypeProvider.cs index d8bc900a..d15ebf9d 100644 --- a/src/Modmail.NET/Providers/TicketTypeProvider.cs +++ b/src/Modmail.NET/Features/DiscordCommands/Helpers/TicketTypeProvider.cs @@ -4,9 +4,9 @@ using MediatR; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; -using Modmail.NET.Features.TicketType; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Providers; +namespace Modmail.NET.Features.DiscordCommands.Helpers; public class TicketTypeProvider : IAutoCompleteProvider { diff --git a/src/Modmail.NET/Features/Guild/Commands.cs b/src/Modmail.NET/Features/Guild/Commands.cs deleted file mode 100644 index 634dce75..00000000 --- a/src/Modmail.NET/Features/Guild/Commands.cs +++ /dev/null @@ -1,19 +0,0 @@ -using DSharpPlus.Entities; -using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Attributes; -using Modmail.NET.Entities; - -namespace Modmail.NET.Features.Guild; - -[PermissionCheck(nameof(AuthPolicy.Owner))] -public sealed record ClearGuildOptionCommand(ulong AuthorizedUserId) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.Owner))] -public sealed record ProcessGuildSetupCommand(ulong AuthorizedUserId, DiscordGuild Guild) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.Owner))] -public sealed record ProcessCreateLogChannelCommand(ulong AuthorizedUserId, DiscordGuild Guild) : IRequest, - IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Commands/ClearGuildOptionCommand.cs b/src/Modmail.NET/Features/Guild/Commands/ClearGuildOptionCommand.cs new file mode 100644 index 00000000..6308cb82 --- /dev/null +++ b/src/Modmail.NET/Features/Guild/Commands/ClearGuildOptionCommand.cs @@ -0,0 +1,10 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Guild.Commands; + +[PermissionCheck(nameof(AuthPolicy.Owner))] +public sealed record ClearGuildOptionCommand(ulong AuthorizedUserId) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Commands/ProcessCreateLogChannelCommand.cs b/src/Modmail.NET/Features/Guild/Commands/ProcessCreateLogChannelCommand.cs new file mode 100644 index 00000000..86729f49 --- /dev/null +++ b/src/Modmail.NET/Features/Guild/Commands/ProcessCreateLogChannelCommand.cs @@ -0,0 +1,11 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Guild.Commands; + +[PermissionCheck(nameof(AuthPolicy.Owner))] +public sealed record ProcessCreateLogChannelCommand(ulong AuthorizedUserId, DiscordGuild Guild) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Commands/ProcessGuildSetupCommand.cs b/src/Modmail.NET/Features/Guild/Commands/ProcessGuildSetupCommand.cs new file mode 100644 index 00000000..6341ba90 --- /dev/null +++ b/src/Modmail.NET/Features/Guild/Commands/ProcessGuildSetupCommand.cs @@ -0,0 +1,12 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Guild.Commands; + +[PermissionCheck(nameof(AuthPolicy.Owner))] +public sealed record ProcessGuildSetupCommand(ulong AuthorizedUserId, DiscordGuild Guild) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Handlers/CheckAnyGuildSetupHandler.cs b/src/Modmail.NET/Features/Guild/Handlers/CheckAnyGuildSetupHandler.cs index 49974d45..2e3b97ba 100644 --- a/src/Modmail.NET/Features/Guild/Handlers/CheckAnyGuildSetupHandler.cs +++ b/src/Modmail.NET/Features/Guild/Handlers/CheckAnyGuildSetupHandler.cs @@ -1,6 +1,7 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Guild.Queries; namespace Modmail.NET.Features.Guild.Handlers; diff --git a/src/Modmail.NET/Features/Guild/Handlers/ClearGuildOptionHandler.cs b/src/Modmail.NET/Features/Guild/Handlers/ClearGuildOptionHandler.cs index e9416ca9..b4fa6065 100644 --- a/src/Modmail.NET/Features/Guild/Handlers/ClearGuildOptionHandler.cs +++ b/src/Modmail.NET/Features/Guild/Handlers/ClearGuildOptionHandler.cs @@ -1,6 +1,7 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Guild.Commands; namespace Modmail.NET.Features.Guild.Handlers; diff --git a/src/Modmail.NET/Features/Guild/Handlers/GetGuildOptionHandler.cs b/src/Modmail.NET/Features/Guild/Handlers/GetGuildOptionHandler.cs index d9533c8d..b0b5ef75 100644 --- a/src/Modmail.NET/Features/Guild/Handlers/GetGuildOptionHandler.cs +++ b/src/Modmail.NET/Features/Guild/Handlers/GetGuildOptionHandler.cs @@ -1,9 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Guild.Queries; namespace Modmail.NET.Features.Guild.Handlers; diff --git a/src/Modmail.NET/Features/Guild/Handlers/ProcessCreateLogChannelHandlers.cs b/src/Modmail.NET/Features/Guild/Handlers/ProcessCreateLogChannelHandlers.cs index 382d6071..98f2af8c 100644 --- a/src/Modmail.NET/Features/Guild/Handlers/ProcessCreateLogChannelHandlers.cs +++ b/src/Modmail.NET/Features/Guild/Handlers/ProcessCreateLogChannelHandlers.cs @@ -1,10 +1,16 @@ using DSharpPlus.Entities; using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.Permission; -using Modmail.NET.Utils; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Commands; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Guild.Static; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Teams.Static; +using Modmail.NET.Language; namespace Modmail.NET.Features.Guild.Handlers; @@ -42,10 +48,10 @@ public async Task Handle(ProcessCreateLogChannelCommand request, category = await guild.GetChannelAsync(guildOption.CategoryId); } catch (NotFoundException) { - category = await guild.CreateChannelCategoryAsync(Const.CategoryName, permissionOverwrites); + category = await guild.CreateChannelCategoryAsync(GuildConstants.CategoryName, permissionOverwrites); } - var logChannel = await guild.CreateTextChannelAsync(Const.LogChannelName, category, LangProvider.This.GetTranslation(LangKeys.ModmailLogChannelTopic), permissionOverwrites); + var logChannel = await guild.CreateTextChannelAsync(GuildConstants.LogChannelName, category, LangProvider.This.GetTranslation(LangKeys.ModmailLogChannelTopic), permissionOverwrites); guildOption.LogChannelId = logChannel.Id; guildOption.CategoryId = category.Id; diff --git a/src/Modmail.NET/Features/Guild/Handlers/ProcessGuildSetupHandler.cs b/src/Modmail.NET/Features/Guild/Handlers/ProcessGuildSetupHandler.cs index 5427e420..20dd2b72 100644 --- a/src/Modmail.NET/Features/Guild/Handlers/ProcessGuildSetupHandler.cs +++ b/src/Modmail.NET/Features/Guild/Handlers/ProcessGuildSetupHandler.cs @@ -1,8 +1,10 @@ using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Guild.Commands; +using Modmail.NET.Features.Guild.Queries; namespace Modmail.NET.Features.Guild.Handlers; @@ -32,7 +34,7 @@ public async Task Handle(ProcessGuildSetupCommand request, Cancella TakeFeedbackAfterClosing = false, IconUrl = request.Guild.IconUrl, Name = request.Guild.Name, - BannerUrl = request.Guild.BannerUrl, + BannerUrl = request.Guild.BannerUrl }; await _sender.Send(new ClearGuildOptionCommand(request.AuthorizedUserId), cancellationToken); diff --git a/src/Modmail.NET/Features/Guild/Queries/CheckAnyGuildSetupQuery.cs b/src/Modmail.NET/Features/Guild/Queries/CheckAnyGuildSetupQuery.cs new file mode 100644 index 00000000..044948ba --- /dev/null +++ b/src/Modmail.NET/Features/Guild/Queries/CheckAnyGuildSetupQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Guild.Queries; + +public sealed record CheckAnyGuildSetupQuery : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Queries.cs b/src/Modmail.NET/Features/Guild/Queries/GetGuildOptionQuery.cs similarity index 50% rename from src/Modmail.NET/Features/Guild/Queries.cs rename to src/Modmail.NET/Features/Guild/Queries/GetGuildOptionQuery.cs index 900bda79..1f3a8bbc 100644 --- a/src/Modmail.NET/Features/Guild/Queries.cs +++ b/src/Modmail.NET/Features/Guild/Queries/GetGuildOptionQuery.cs @@ -1,10 +1,8 @@ using MediatR; using Modmail.NET.Attributes; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; -namespace Modmail.NET.Features.Guild; +namespace Modmail.NET.Features.Guild.Queries; [CachePolicy("GetGuildOptionQuery", 60)] -public sealed record GetGuildOptionQuery(bool AllowNull) : IRequest; - -public sealed record CheckAnyGuildSetupQuery : IRequest; \ No newline at end of file +public sealed record GetGuildOptionQuery(bool AllowNull) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Guild/Static/GuildConstants.cs b/src/Modmail.NET/Features/Guild/Static/GuildConstants.cs new file mode 100644 index 00000000..ddbabd54 --- /dev/null +++ b/src/Modmail.NET/Features/Guild/Static/GuildConstants.cs @@ -0,0 +1,7 @@ +namespace Modmail.NET.Features.Guild.Static; + +public static class GuildConstants +{ + public const string CategoryName = "Modmail"; + public const string LogChannelName = "📄modmail-logs"; +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Metric/Commands.cs b/src/Modmail.NET/Features/Metric/Commands.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Modmail.NET/Features/Metric/Handlers/GetLatestMetricHandler.cs b/src/Modmail.NET/Features/Metric/Handlers/GetLatestMetricHandler.cs index 57863a28..2cf84b34 100644 --- a/src/Modmail.NET/Features/Metric/Handlers/GetLatestMetricHandler.cs +++ b/src/Modmail.NET/Features/Metric/Handlers/GetLatestMetricHandler.cs @@ -1,8 +1,11 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Models.Dto; +using Modmail.NET.Features.Metric.Models; +using Modmail.NET.Features.Metric.Queries; +using Modmail.NET.Features.Teams.Static; +using Modmail.NET.Language; namespace Modmail.NET.Features.Metric.Handlers; diff --git a/src/Modmail.NET/Jobs/StatisticsCalculatorJob.cs b/src/Modmail.NET/Features/Metric/Jobs/StatisticsCalculatorJob.cs similarity index 89% rename from src/Modmail.NET/Jobs/StatisticsCalculatorJob.cs rename to src/Modmail.NET/Features/Metric/Jobs/StatisticsCalculatorJob.cs index 3c1e2a33..6bd0cdd1 100644 --- a/src/Modmail.NET/Jobs/StatisticsCalculatorJob.cs +++ b/src/Modmail.NET/Features/Metric/Jobs/StatisticsCalculatorJob.cs @@ -3,14 +3,15 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Modmail.NET.Abstract; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Metric.Static; using Serilog; -namespace Modmail.NET.Jobs; +namespace Modmail.NET.Features.Metric.Jobs; public class StatisticsCalculatorJob : HangfireRecurringJobBase { @@ -28,7 +29,7 @@ public override async Task Execute() { var sender = scope.ServiceProvider.GetRequiredService(); var guildOption = await sender.Send(new GetGuildOptionQuery(false)); var statDays = guildOption.StatisticsCalculateDays; - if (statDays is < Const.StatisticsCalculateDaysMin or > Const.StatisticsCalculateDaysMax) statDays = Const.DefaultStatisticsCalculateDays; + if (statDays is < MetricConstants.StatisticsCalculateDaysMin or > MetricConstants.StatisticsCalculateDaysMax) statDays = MetricConstants.DefaultStatisticsCalculateDays; var statDate = UtilDate.GetNow().AddDays(-statDays); @@ -52,7 +53,7 @@ public override async Task Execute() { private static async Task GetAvgResponseTimeSeconds(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.TicketMessages - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .Select(x => new { x.TicketId, x.RegisterDateUtc, @@ -89,7 +90,7 @@ public override async Task Execute() { private static async Task GetAvgTicketsClosedPerDay(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.Tickets - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .Where(x => x.ClosedDateUtc.HasValue) .GroupBy(x => x.ClosedDateUtc.Value.Date) .Select(group => group.Count()) @@ -105,7 +106,7 @@ public override async Task Execute() { private static async Task GetAvgTicketsOpenedPerDay(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.Tickets - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .GroupBy(x => x.RegisterDateUtc.Date) .Select(group => group.Count()) .DefaultIfEmpty() @@ -120,7 +121,7 @@ public override async Task Execute() { private static async Task GetAvgTicketClosedSeconds(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.Tickets - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .Where(x => x.ClosedDateUtc.HasValue) .Select(x => EF.Functions.DateDiffSecond(x.RegisterDateUtc, x.ClosedDateUtc.Value)) .DefaultIfEmpty() @@ -135,7 +136,7 @@ public override async Task Execute() { private static async Task GetFastestClosedTicketSeconds(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.Tickets - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .Where(x => x.ClosedDateUtc.HasValue) .Select(x => EF.Functions.DateDiffSecond(x.RegisterDateUtc, x.ClosedDateUtc.Value)) .DefaultIfEmpty() @@ -150,7 +151,7 @@ public override async Task Execute() { private static async Task GetSlowestClosedTicketSeconds(ModmailDbContext dbContext, DateTime statDate) { try { return await dbContext.Tickets - .Where(x => x.RegisterDateUtc < statDate) + .Where(x => x.RegisterDateUtc > statDate) .Where(x => x.ClosedDateUtc.HasValue) .Select(x => EF.Functions.DateDiffSecond(x.RegisterDateUtc, x.ClosedDateUtc.Value)) .DefaultIfEmpty() diff --git a/src/Modmail.NET/Models/Dto/ChartItemDto.cs b/src/Modmail.NET/Features/Metric/Models/ChartItemDto.cs similarity index 65% rename from src/Modmail.NET/Models/Dto/ChartItemDto.cs rename to src/Modmail.NET/Features/Metric/Models/ChartItemDto.cs index fe68a825..49103df3 100644 --- a/src/Modmail.NET/Models/Dto/ChartItemDto.cs +++ b/src/Modmail.NET/Features/Metric/Models/ChartItemDto.cs @@ -1,3 +1,3 @@ -namespace Modmail.NET.Models.Dto; +namespace Modmail.NET.Features.Metric.Models; public sealed record ChartItemDto(TCategory Category, TValue Value); \ No newline at end of file diff --git a/src/Modmail.NET/Models/Dto/MetricDto.cs b/src/Modmail.NET/Features/Metric/Models/MetricDto.cs similarity index 92% rename from src/Modmail.NET/Models/Dto/MetricDto.cs rename to src/Modmail.NET/Features/Metric/Models/MetricDto.cs index 430c31f0..8daadba1 100644 --- a/src/Modmail.NET/Models/Dto/MetricDto.cs +++ b/src/Modmail.NET/Features/Metric/Models/MetricDto.cs @@ -1,6 +1,6 @@ -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; -namespace Modmail.NET.Models.Dto; +namespace Modmail.NET.Features.Metric.Models; public sealed record MetricDto { diff --git a/src/Modmail.NET/Features/Metric/Queries.cs b/src/Modmail.NET/Features/Metric/Queries/GetLatestMetricQuery.cs similarity index 67% rename from src/Modmail.NET/Features/Metric/Queries.cs rename to src/Modmail.NET/Features/Metric/Queries/GetLatestMetricQuery.cs index 5a86064b..c6ba9811 100644 --- a/src/Modmail.NET/Features/Metric/Queries.cs +++ b/src/Modmail.NET/Features/Metric/Queries/GetLatestMetricQuery.cs @@ -1,8 +1,8 @@ using MediatR; using Modmail.NET.Attributes; -using Modmail.NET.Models.Dto; +using Modmail.NET.Features.Metric.Models; -namespace Modmail.NET.Features.Metric; +namespace Modmail.NET.Features.Metric.Queries; [CachePolicy(nameof(GetLatestMetricQuery), 60 * 60, false)] public sealed record GetLatestMetricQuery(bool AllowNull) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Metric/Static/MetricConstants.cs b/src/Modmail.NET/Features/Metric/Static/MetricConstants.cs new file mode 100644 index 00000000..ece49d0c --- /dev/null +++ b/src/Modmail.NET/Features/Metric/Static/MetricConstants.cs @@ -0,0 +1,8 @@ +namespace Modmail.NET.Features.Metric.Static; + +public static class MetricConstants +{ + public const int StatisticsCalculateDaysMin = 30; + public const int StatisticsCalculateDaysMax = 365; + public const int DefaultStatisticsCalculateDays = 90; +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Handler/CheckPermissionAccessHandler.cs b/src/Modmail.NET/Features/Permission/Handler/CheckPermissionAccessHandler.cs index 4bf35850..1e596903 100644 --- a/src/Modmail.NET/Features/Permission/Handler/CheckPermissionAccessHandler.cs +++ b/src/Modmail.NET/Features/Permission/Handler/CheckPermissionAccessHandler.cs @@ -1,7 +1,10 @@ using MediatR; using Microsoft.Extensions.Options; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; namespace Modmail.NET.Features.Permission.Handler; diff --git a/src/Modmail.NET/Features/Permission/Handler/CheckRoleInAnyTeamHandler.cs b/src/Modmail.NET/Features/Permission/Handler/CheckRoleInAnyTeamHandler.cs index 0cdaa37e..05db7700 100644 --- a/src/Modmail.NET/Features/Permission/Handler/CheckRoleInAnyTeamHandler.cs +++ b/src/Modmail.NET/Features/Permission/Handler/CheckRoleInAnyTeamHandler.cs @@ -1,6 +1,8 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Teams.Static; namespace Modmail.NET.Features.Permission.Handler; diff --git a/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoHandler.cs b/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoHandler.cs index 47799dbf..5369912e 100644 --- a/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoHandler.cs +++ b/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoHandler.cs @@ -1,7 +1,8 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Models; +using Modmail.NET.Features.Permission.Models; +using Modmail.NET.Features.Permission.Queries; namespace Modmail.NET.Features.Permission.Handler; diff --git a/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoOrHigherHandler.cs b/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoOrHigherHandler.cs index 06c55275..088e0119 100644 --- a/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoOrHigherHandler.cs +++ b/src/Modmail.NET/Features/Permission/Handler/GetPermissionInfoOrHigherHandler.cs @@ -1,7 +1,8 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Models; +using Modmail.NET.Features.Permission.Models; +using Modmail.NET.Features.Permission.Queries; namespace Modmail.NET.Features.Permission.Handler; diff --git a/src/Modmail.NET/Features/Permission/Handler/GetPermissionLevelHandler.cs b/src/Modmail.NET/Features/Permission/Handler/GetPermissionLevelHandler.cs index 0b2d9884..e55b2584 100644 --- a/src/Modmail.NET/Features/Permission/Handler/GetPermissionLevelHandler.cs +++ b/src/Modmail.NET/Features/Permission/Handler/GetPermissionLevelHandler.cs @@ -2,7 +2,10 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Modmail.NET.Database; -using Modmail.NET.Features.Bot; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Teams.Static; namespace Modmail.NET.Features.Permission.Handler; diff --git a/src/Modmail.NET/Models/PermissionInfo.cs b/src/Modmail.NET/Features/Permission/Models/PermissionInfo.cs similarity index 73% rename from src/Modmail.NET/Models/PermissionInfo.cs rename to src/Modmail.NET/Features/Permission/Models/PermissionInfo.cs index 8e5d30ac..11fafd17 100644 --- a/src/Modmail.NET/Models/PermissionInfo.cs +++ b/src/Modmail.NET/Features/Permission/Models/PermissionInfo.cs @@ -1,4 +1,7 @@ -namespace Modmail.NET.Models; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Teams.Static; + +namespace Modmail.NET.Features.Permission.Models; public sealed record PermissionInfo(TeamPermissionLevel PermissionLevel, ulong Key, TeamMemberDataType Type, bool PingOnNewTicket, bool PingOnNewMessage) { diff --git a/src/Modmail.NET/Features/Permission/Queries.cs b/src/Modmail.NET/Features/Permission/Queries.cs deleted file mode 100644 index 05145a6b..00000000 --- a/src/Modmail.NET/Features/Permission/Queries.cs +++ /dev/null @@ -1,16 +0,0 @@ -using MediatR; -using Modmail.NET.Models; - -namespace Modmail.NET.Features.Permission; - -public sealed record GetPermissionLevelQuery(ulong UserId, bool IncludeRole = false) : IRequest; - -public sealed record GetPermissionInfoQuery : IRequest; - -public sealed record GetPermissionInfoOrHigherQuery(TeamPermissionLevel LevelOrHigher) : IRequest; - -public sealed record CheckUserInAnyTeamQuery(ulong MemberId) : IRequest; - -public sealed record CheckRoleInAnyTeamQuery(ulong RoleId) : IRequest; - -public sealed record CheckPermissionAccessQuery(ulong UserId, AuthPolicy Policy) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/CheckPermissionAccessQuery.cs b/src/Modmail.NET/Features/Permission/Queries/CheckPermissionAccessQuery.cs new file mode 100644 index 00000000..0f2bc11e --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/CheckPermissionAccessQuery.cs @@ -0,0 +1,6 @@ +using MediatR; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record CheckPermissionAccessQuery(ulong UserId, AuthPolicy Policy) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/CheckRoleInAnyTeamQuery.cs b/src/Modmail.NET/Features/Permission/Queries/CheckRoleInAnyTeamQuery.cs new file mode 100644 index 00000000..210eaaa7 --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/CheckRoleInAnyTeamQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record CheckRoleInAnyTeamQuery(ulong RoleId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/CheckUserInAnyTeamQuery.cs b/src/Modmail.NET/Features/Permission/Queries/CheckUserInAnyTeamQuery.cs new file mode 100644 index 00000000..a934060d --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/CheckUserInAnyTeamQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record CheckUserInAnyTeamQuery(ulong MemberId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoOrHigherQuery.cs b/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoOrHigherQuery.cs new file mode 100644 index 00000000..c952cc3f --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoOrHigherQuery.cs @@ -0,0 +1,7 @@ +using MediatR; +using Modmail.NET.Features.Permission.Models; +using Modmail.NET.Features.Permission.Static; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record GetPermissionInfoOrHigherQuery(TeamPermissionLevel LevelOrHigher) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoQuery.cs b/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoQuery.cs new file mode 100644 index 00000000..1b62b9eb --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/GetPermissionInfoQuery.cs @@ -0,0 +1,6 @@ +using MediatR; +using Modmail.NET.Features.Permission.Models; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record GetPermissionInfoQuery : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Permission/Queries/GetPermissionLevelQuery.cs b/src/Modmail.NET/Features/Permission/Queries/GetPermissionLevelQuery.cs new file mode 100644 index 00000000..6f627374 --- /dev/null +++ b/src/Modmail.NET/Features/Permission/Queries/GetPermissionLevelQuery.cs @@ -0,0 +1,6 @@ +using MediatR; +using Modmail.NET.Features.Permission.Static; + +namespace Modmail.NET.Features.Permission.Queries; + +public sealed record GetPermissionLevelQuery(ulong UserId, bool IncludeRole = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Static/TeamPermissionLevel.cs b/src/Modmail.NET/Features/Permission/Static/TeamPermissionLevel.cs similarity index 64% rename from src/Modmail.NET/Static/TeamPermissionLevel.cs rename to src/Modmail.NET/Features/Permission/Static/TeamPermissionLevel.cs index 671cb423..429ece38 100644 --- a/src/Modmail.NET/Static/TeamPermissionLevel.cs +++ b/src/Modmail.NET/Features/Permission/Static/TeamPermissionLevel.cs @@ -1,4 +1,4 @@ -namespace Modmail.NET.Static; +namespace Modmail.NET.Features.Permission.Static; public enum TeamPermissionLevel { diff --git a/src/Modmail.NET/Features/Tag/Commands/ProcessCreateTagCommand.cs b/src/Modmail.NET/Features/Tag/Commands/ProcessCreateTagCommand.cs new file mode 100644 index 00000000..28bf61ad --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Commands/ProcessCreateTagCommand.cs @@ -0,0 +1,6 @@ +using MediatR; + +namespace Modmail.NET.Features.Tag.Commands; + +//TODO: Permission check +public sealed record ProcessCreateTagCommand(ulong AuthorizedUserId, string Name, string Title, string Content) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Commands/ProcessRemoveTagCommand.cs b/src/Modmail.NET/Features/Tag/Commands/ProcessRemoveTagCommand.cs new file mode 100644 index 00000000..c80abe1f --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Commands/ProcessRemoveTagCommand.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace Modmail.NET.Features.Tag.Commands; + +//TODO: Permission check +// [PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] +public sealed record ProcessRemoveTagCommand(ulong AuthorizedUserId, Guid Id) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Commands/ProcessUpdateTagCommand.cs b/src/Modmail.NET/Features/Tag/Commands/ProcessUpdateTagCommand.cs new file mode 100644 index 00000000..b6a886b1 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Commands/ProcessUpdateTagCommand.cs @@ -0,0 +1,6 @@ +using MediatR; + +namespace Modmail.NET.Features.Tag.Commands; + +//TODO: Permission check +public sealed record ProcessUpdateTagCommand(ulong AuthorizedUserId, Guid Id, string Name, string Title, string Content) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Handlers/GetTagByNameHandler.cs b/src/Modmail.NET/Features/Tag/Handlers/GetTagByNameHandler.cs new file mode 100644 index 00000000..ad67f326 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Handlers/GetTagByNameHandler.cs @@ -0,0 +1,24 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Database; +using Modmail.NET.Features.Tag.Queries; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Tag.Handlers; + +public class GetTagByNameHandler : IRequestHandler +{ + private readonly ModmailDbContext _dbContext; + + public GetTagByNameHandler(ModmailDbContext dbContext) { + _dbContext = dbContext; + } + + public async Task Handle(GetTagByNameQuery request, CancellationToken cancellationToken) { + var tag = await _dbContext.Tags.Where(x => x.Name == request.Name).FirstOrDefaultAsync(cancellationToken); + if (tag is null) throw new ModmailBotException(LangKeys.TagDoesntExists); + + return tag; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Handlers/ProcessCreateTagHandler.cs b/src/Modmail.NET/Features/Tag/Handlers/ProcessCreateTagHandler.cs new file mode 100644 index 00000000..237de251 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Handlers/ProcessCreateTagHandler.cs @@ -0,0 +1,37 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Database; +using Modmail.NET.Features.Tag.Commands; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Tag.Handlers; + +public class ProcessCreateTagHandler : IRequestHandler +{ + private readonly ModmailDbContext _dbContext; + + public ProcessCreateTagHandler(ModmailDbContext dbContext) { + _dbContext = dbContext; + } + + public async Task Handle(ProcessCreateTagCommand request, CancellationToken cancellationToken) { + var fixedName = request.Name.Trim().Replace(" ", "-").ToLower(); + + var exists = await _dbContext.Tags.Where(x => x.Name == fixedName).AnyAsync(cancellationToken: cancellationToken); + if (exists) { + throw new ModmailBotException(LangKeys.TagWithSameNameAlreadyExists); + } + + var entity = new Database.Entities.Tag { + Name = fixedName, + Title = request.Title.Trim(), + Content = request.Content.Trim(), + }; + _dbContext.Add(entity); + var affected = await _dbContext.SaveChangesAsync(cancellationToken); + if (affected == 0) throw new DbInternalException(); + + return entity; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Handlers/ProcessRemoveTagHandler.cs b/src/Modmail.NET/Features/Tag/Handlers/ProcessRemoveTagHandler.cs new file mode 100644 index 00000000..5e4d46a0 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Handlers/ProcessRemoveTagHandler.cs @@ -0,0 +1,29 @@ +using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Database; +using Modmail.NET.Features.Tag.Commands; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Tag.Handlers; + +public class ProcessRemoveTagHandler : IRequestHandler +{ + private readonly ModmailDbContext _dbContext; + + public ProcessRemoveTagHandler(ModmailDbContext dbContext) { + _dbContext = dbContext; + } + + public async Task Handle(ProcessRemoveTagCommand request, CancellationToken cancellationToken) { + var entity = await _dbContext.Tags.FindAsync([request.Id], cancellationToken); + + if (entity is null) throw new NotFoundException(LangKeys.Tag); + + _dbContext.Remove(entity); + + var affected = await _dbContext.SaveChangesAsync(cancellationToken); + if (affected == 0) throw new DbInternalException(); + + return entity; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Handlers/ProcessUpdateTagHandler.cs b/src/Modmail.NET/Features/Tag/Handlers/ProcessUpdateTagHandler.cs new file mode 100644 index 00000000..b8598ee3 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Handlers/ProcessUpdateTagHandler.cs @@ -0,0 +1,44 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Database; +using Modmail.NET.Features.Tag.Commands; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Tag.Handlers; + +public class ProcessUpdateTagHandler : IRequestHandler +{ + private readonly ModmailDbContext _dbContext; + + public ProcessUpdateTagHandler(ModmailDbContext dbContext) { + _dbContext = dbContext; + } + + public async Task Handle(ProcessUpdateTagCommand request, CancellationToken cancellationToken) { + var fixedName = request.Name.Trim().Replace(" ", "-").ToLower(); + + var entity = await _dbContext.Tags.Where(x => x.Name == fixedName).FirstOrDefaultAsync(cancellationToken: cancellationToken); + if (entity is null) { + throw new ModmailBotException(LangKeys.TagDoesntExists); + } + + var isNameSame = fixedName.Equals(entity.Name, StringComparison.InvariantCultureIgnoreCase); + if (!isNameSame) { + var exists = await _dbContext.Tags.Where(x => x.Name == fixedName).AnyAsync(cancellationToken: cancellationToken); + if (exists) { + throw new ModmailBotException(LangKeys.TagWithSameNameAlreadyExists); + } + } + + entity.Title = request.Title.Trim(); + entity.Content = request.Content.Trim(); + entity.Name = fixedName; + + _dbContext.Update(entity); + var affected = await _dbContext.SaveChangesAsync(cancellationToken); + if (affected == 0) throw new DbInternalException(); + + return entity; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Helpers/TagBotMessages.cs b/src/Modmail.NET/Features/Tag/Helpers/TagBotMessages.cs new file mode 100644 index 00000000..fd5c9f3b --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Helpers/TagBotMessages.cs @@ -0,0 +1,38 @@ +using DSharpPlus.Entities; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Tag.Helpers; + +public static class TagBotMessages +{ + public static DiscordMessageBuilder TagSent(Database.Entities.Tag message) { + var embed = new DiscordEmbedBuilder() + .WithTitle(message.Title) + .WithDescription(message.Content) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithColor(ModmailColors.TagReceivedColor); + + var msg = new DiscordMessageBuilder(); + msg.AddEmbed(embed); + return msg; + } + + public static DiscordMessageBuilder TagReceivedToTicket(Database.Entities.Tag message, DiscordUser author = null, bool anonymous = false) { + var embed = new DiscordEmbedBuilder() + .WithTitle(message.Title) + .WithDescription(message.Content) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithColor(ModmailColors.TagReceivedColor); + + if (author is not null) + if (!anonymous) + embed.WithUserAsAuthor(author); + + var msg = new DiscordMessageBuilder(); + msg.AddEmbed(embed); + return msg; + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Tag/Queries/GetTagByNameQuery.cs b/src/Modmail.NET/Features/Tag/Queries/GetTagByNameQuery.cs new file mode 100644 index 00000000..0a27ddf1 --- /dev/null +++ b/src/Modmail.NET/Features/Tag/Queries/GetTagByNameQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Tag.Queries; + +public sealed record GetTagByNameQuery(string Name) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands.cs b/src/Modmail.NET/Features/Teams/Commands.cs deleted file mode 100644 index 49686f04..00000000 --- a/src/Modmail.NET/Features/Teams/Commands.cs +++ /dev/null @@ -1,64 +0,0 @@ -using DSharpPlus.Entities; -using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Attributes; -using Modmail.NET.Entities; - -namespace Modmail.NET.Features.Teams; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessRenameTeamCommand( - ulong AuthorizedUserId, - Guid Id, - string NewName) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessRemoveTeamCommand( - ulong AuthorizedUserId, - Guid Id) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessAddTeamMemberCommand( - ulong AuthorizedUserId, - Guid Id, - ulong MemberId) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessRemoveTeamMemberCommand( - ulong AuthorizedUserId, - ulong TeamMemberKey, - TeamMemberDataType Type) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessUpdateTeamCommand( - ulong AuthorizedUserId, - string TeamName, - TeamPermissionLevel? PermissionLevel, - bool? PingOnNewTicket, - bool? PingOnTicketMessage, - bool? IsEnabled, - bool? AllowAccessToWebPanel -) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessCreateTeamCommand( - ulong AuthorizedUserId, - string TeamName, - TeamPermissionLevel PermissionLevel, - bool PingOnNewTicket = false, - bool PingOnTicketMessage = false, - bool AllowAccessToWebPanel = false -) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record ProcessAddRoleToTeamCommand( - ulong AuthorizedUserId, - Guid Id, - DiscordRole Role) : IRequest, - IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessAddRoleToTeamCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessAddRoleToTeamCommand.cs new file mode 100644 index 00000000..64826a0b --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessAddRoleToTeamCommand.cs @@ -0,0 +1,14 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessAddRoleToTeamCommand( + ulong AuthorizedUserId, + Guid Id, + DiscordRole Role) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessAddTeamMemberCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessAddTeamMemberCommand.cs new file mode 100644 index 00000000..13308fd4 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessAddTeamMemberCommand.cs @@ -0,0 +1,13 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessAddTeamMemberCommand( + ulong AuthorizedUserId, + Guid Id, + ulong MemberId) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessCreateTeamCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessCreateTeamCommand.cs new file mode 100644 index 00000000..cd275061 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessCreateTeamCommand.cs @@ -0,0 +1,19 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Permission.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessCreateTeamCommand( + ulong AuthorizedUserId, + string TeamName, + TeamPermissionLevel PermissionLevel, + bool PingOnNewTicket = false, + bool PingOnTicketMessage = false, + bool AllowAccessToWebPanel = false +) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamCommand.cs new file mode 100644 index 00000000..7826766a --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamCommand.cs @@ -0,0 +1,13 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessRemoveTeamCommand( + ulong AuthorizedUserId, + Guid Id) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamMemberCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamMemberCommand.cs new file mode 100644 index 00000000..9bc93eb7 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessRemoveTeamMemberCommand.cs @@ -0,0 +1,14 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Teams.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessRemoveTeamMemberCommand( + ulong AuthorizedUserId, + ulong TeamMemberKey, + TeamMemberDataType Type) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessRenameTeamCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessRenameTeamCommand.cs new file mode 100644 index 00000000..963cd892 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessRenameTeamCommand.cs @@ -0,0 +1,13 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessRenameTeamCommand( + ulong AuthorizedUserId, + Guid Id, + string NewName) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Commands/ProcessUpdateTeamCommand.cs b/src/Modmail.NET/Features/Teams/Commands/ProcessUpdateTeamCommand.cs new file mode 100644 index 00000000..f5f0fb39 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Commands/ProcessUpdateTeamCommand.cs @@ -0,0 +1,19 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Permission.Static; + +namespace Modmail.NET.Features.Teams.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record ProcessUpdateTeamCommand( + ulong AuthorizedUserId, + string TeamName, + TeamPermissionLevel? PermissionLevel, + bool? PingOnNewTicket, + bool? PingOnTicketMessage, + bool? IsEnabled, + bool? AllowAccessToWebPanel +) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Handlers/CheckTeamExistsHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/CheckTeamExistsHandler.cs index 8418ec32..175e0212 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/CheckTeamExistsHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/CheckTeamExistsHandler.cs @@ -1,6 +1,7 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/CheckUserInAnyTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/CheckUserInAnyTeamHandler.cs index ac789db6..f7968395 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/CheckUserInAnyTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/CheckUserInAnyTeamHandler.cs @@ -1,7 +1,8 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Features.Permission; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Teams.Static; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/GetTeamByNameHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/GetTeamByNameHandler.cs index 1a249436..add83425 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/GetTeamByNameHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/GetTeamByNameHandler.cs @@ -1,8 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Teams.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/GetTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/GetTeamHandler.cs index 225c8fb3..11299729 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/GetTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/GetTeamHandler.cs @@ -1,7 +1,8 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/GetTeamListHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/GetTeamListHandler.cs index 54a7fdf1..90ea3fef 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/GetTeamListHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/GetTeamListHandler.cs @@ -1,8 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Teams.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessAddRoleToTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessAddRoleToTeamHandler.cs index f75d78b1..82031b62 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessAddRoleToTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessAddRoleToTeamHandler.cs @@ -1,9 +1,11 @@ using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Permission; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Static; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessAddTeamMemberHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessAddTeamMemberHandler.cs index a0a63a7b..c9cd548b 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessAddTeamMemberHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessAddTeamMemberHandler.cs @@ -1,9 +1,12 @@ using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Permission; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Queries; +using Modmail.NET.Features.Teams.Static; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessCreateTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessCreateTeamHandler.cs index 74eb2550..12c4c1db 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessCreateTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessCreateTeamHandler.cs @@ -1,8 +1,11 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Permission; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Permission.Static; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamHandler.cs index 9f2e4d23..b4570083 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamHandler.cs @@ -1,7 +1,9 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamMemberHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamMemberHandler.cs index cd7737cb..148f6d33 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamMemberHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessRemoveTeamMemberHandler.cs @@ -1,7 +1,9 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Language; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessRenameTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessRenameTeamHandler.cs index 90f2731f..c84a7f6f 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessRenameTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessRenameTeamHandler.cs @@ -1,6 +1,8 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Handlers/ProcessUpdateTeamHandler.cs b/src/Modmail.NET/Features/Teams/Handlers/ProcessUpdateTeamHandler.cs index 09c0b913..dbc1ad6a 100644 --- a/src/Modmail.NET/Features/Teams/Handlers/ProcessUpdateTeamHandler.cs +++ b/src/Modmail.NET/Features/Teams/Handlers/ProcessUpdateTeamHandler.cs @@ -1,6 +1,8 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Teams.Commands; +using Modmail.NET.Features.Teams.Queries; namespace Modmail.NET.Features.Teams.Handlers; diff --git a/src/Modmail.NET/Features/Teams/Queries.cs b/src/Modmail.NET/Features/Teams/Queries.cs deleted file mode 100644 index 61b668e4..00000000 --- a/src/Modmail.NET/Features/Teams/Queries.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Attributes; -using Modmail.NET.Entities; - -namespace Modmail.NET.Features.Teams; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record CheckTeamExistsQuery(ulong AuthorizedUserId, string Name) : IRequest, - IPermissionCheck; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record GetTeamListQuery(ulong AuthorizedUserId, bool ThrowIfEmpty = false) : IRequest>; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record GetTeamByNameQuery(ulong AuthorizedUserId, string Name, bool AllowNull = false) : IRequest; - -[PermissionCheck(nameof(AuthPolicy.ManageTeams))] -public sealed record GetTeamQuery(ulong AuthorizedUserId, Guid Id, bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Queries/CheckTeamExistsQuery.cs b/src/Modmail.NET/Features/Teams/Queries/CheckTeamExistsQuery.cs new file mode 100644 index 00000000..d5de1b60 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Queries/CheckTeamExistsQuery.cs @@ -0,0 +1,10 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Teams.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record CheckTeamExistsQuery(ulong AuthorizedUserId, string Name) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Queries/GetTeamByNameQuery.cs b/src/Modmail.NET/Features/Teams/Queries/GetTeamByNameQuery.cs new file mode 100644 index 00000000..49aaff67 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Queries/GetTeamByNameQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Teams.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record GetTeamByNameQuery(ulong AuthorizedUserId, string Name, bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Queries/GetTeamListQuery.cs b/src/Modmail.NET/Features/Teams/Queries/GetTeamListQuery.cs new file mode 100644 index 00000000..46fea692 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Queries/GetTeamListQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Teams.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record GetTeamListQuery(ulong AuthorizedUserId, bool ThrowIfEmpty = false) : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Teams/Queries/GetTeamQuery.cs b/src/Modmail.NET/Features/Teams/Queries/GetTeamQuery.cs new file mode 100644 index 00000000..56722879 --- /dev/null +++ b/src/Modmail.NET/Features/Teams/Queries/GetTeamQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Teams.Queries; + +[PermissionCheck(nameof(AuthPolicy.ManageTeams))] +public sealed record GetTeamQuery(ulong AuthorizedUserId, Guid Id, bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Static/TeamMemberDataType.cs b/src/Modmail.NET/Features/Teams/Static/TeamMemberDataType.cs similarity index 80% rename from src/Modmail.NET/Static/TeamMemberDataType.cs rename to src/Modmail.NET/Features/Teams/Static/TeamMemberDataType.cs index 80b3853f..38697d2f 100644 --- a/src/Modmail.NET/Static/TeamMemberDataType.cs +++ b/src/Modmail.NET/Features/Teams/Static/TeamMemberDataType.cs @@ -1,6 +1,6 @@ using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers; -namespace Modmail.NET.Static; +namespace Modmail.NET.Features.Teams.Static; public enum TeamMemberDataType { diff --git a/src/Modmail.NET/Features/Ticket/Commands.cs b/src/Modmail.NET/Features/Ticket/Commands.cs deleted file mode 100644 index 4e6c7730..00000000 --- a/src/Modmail.NET/Features/Ticket/Commands.cs +++ /dev/null @@ -1,62 +0,0 @@ -using DSharpPlus.Entities; -using MediatR; - -namespace Modmail.NET.Features.Ticket; - -public sealed record ProcessCloseTicketCommand( - Guid TicketId, - ulong CloserUserId = 0, - string CloseReason = null, - DiscordChannel ModChatChannel = null, - bool DontSendFeedbackMessage = false) : IRequest; - -public sealed record ProcessChangePriorityCommand( - Guid TicketId, - ulong ModUserId, - TicketPriority NewPriority, - DiscordChannel TicketChannel = null) : IRequest; - -public sealed record ProcessUserSentMessageCommand( - Guid TicketId, - DiscordMessage Message, - DiscordChannel PrivateChannel = null) : IRequest; - -public sealed record ProcessCreateNewTicketCommand( - DiscordUser User, - DiscordChannel PrivateChannel, - DiscordMessage Message) : IRequest; - -public sealed record ProcessModSendMessageCommand( - Guid TicketId, - DiscordUser ModUser, - DiscordMessage Message, - DiscordChannel Channel, - DiscordGuild Guild -) : IRequest; - -public sealed record ProcessAddFeedbackCommand( - Guid TicketId, - int StarCount, - string TextInput, - DiscordMessage FeedbackMessage -) : IRequest; - -public sealed record ProcessAddNoteCommand( - Guid TicketId, - ulong UserId, - string Note -) : IRequest; - -public sealed record ProcessToggleAnonymousCommand( - Guid TicketId, - DiscordChannel TicketChannel = null -) : IRequest; - -public sealed record ProcessChangeTicketTypeCommand( - Guid TicketId, - string Type, - DiscordChannel TicketChannel = null, - DiscordChannel PrivateChannel = null, - DiscordMessage PrivateMessageWithComponent = null, - ulong UserId = 0 -) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessAddFeedbackCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessAddFeedbackCommand.cs new file mode 100644 index 00000000..88184072 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessAddFeedbackCommand.cs @@ -0,0 +1,11 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessAddFeedbackCommand( + Guid TicketId, + int StarCount, + string TextInput, + DiscordMessage FeedbackMessage +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessAddNoteCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessAddNoteCommand.cs new file mode 100644 index 00000000..54d485a8 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessAddNoteCommand.cs @@ -0,0 +1,9 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessAddNoteCommand( + Guid TicketId, + ulong UserId, + string Note +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessChangePriorityCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessChangePriorityCommand.cs new file mode 100644 index 00000000..d293d4f9 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessChangePriorityCommand.cs @@ -0,0 +1,11 @@ +using DSharpPlus.Entities; +using MediatR; +using Modmail.NET.Features.Ticket.Static; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessChangePriorityCommand( + Guid TicketId, + ulong ModUserId, + TicketPriority NewPriority, + DiscordChannel TicketChannel = null) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessChangeTicketTypeCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessChangeTicketTypeCommand.cs new file mode 100644 index 00000000..4cfdd088 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessChangeTicketTypeCommand.cs @@ -0,0 +1,13 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessChangeTicketTypeCommand( + Guid TicketId, + string Type, + DiscordChannel TicketChannel = null, + DiscordChannel PrivateChannel = null, + DiscordMessage PrivateMessageWithComponent = null, + ulong UserId = 0 +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessCloseTicketCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessCloseTicketCommand.cs new file mode 100644 index 00000000..6d9cf2ad --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessCloseTicketCommand.cs @@ -0,0 +1,11 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessCloseTicketCommand( + Guid TicketId, + ulong CloserUserId = 0, + string CloseReason = null, + DiscordChannel ModChatChannel = null, + bool DontSendFeedbackMessage = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateNewTicketCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateNewTicketCommand.cs new file mode 100644 index 00000000..db35006f --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateNewTicketCommand.cs @@ -0,0 +1,9 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessCreateNewTicketCommand( + DiscordUser User, + DiscordChannel PrivateChannel, + DiscordMessage Message) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateTicketTypeCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateTicketTypeCommand.cs new file mode 100644 index 00000000..51e466d7 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessCreateTicketTypeCommand.cs @@ -0,0 +1,15 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; + +namespace Modmail.NET.Features.Ticket.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] +public sealed record ProcessCreateTicketTypeCommand( + ulong AuthorizedUserId, + string Name, + string Emoji, + string Description, + long Order, + string EmbedMessageTitle, + string EmbedMessageContent) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessModSendMessageCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessModSendMessageCommand.cs new file mode 100644 index 00000000..c3f85be9 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessModSendMessageCommand.cs @@ -0,0 +1,12 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessModSendMessageCommand( + Guid TicketId, + DiscordUser ModUser, + DiscordMessage Message, + DiscordChannel Channel, + DiscordGuild Guild +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessRemoveTicketTypeCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessRemoveTicketTypeCommand.cs new file mode 100644 index 00000000..d37a3e8a --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessRemoveTicketTypeCommand.cs @@ -0,0 +1,11 @@ +using MediatR; +using Modmail.NET.Abstract; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] +public sealed record ProcessRemoveTicketTypeCommand(ulong AuthorizedUserId, Guid Id) : IRequest, + IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessTagSendMessageCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessTagSendMessageCommand.cs new file mode 100644 index 00000000..8f2f55bc --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessTagSendMessageCommand.cs @@ -0,0 +1,12 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessTagSendMessageCommand( + Guid TicketId, + Guid TagId, + DiscordUser ModUser, + DiscordChannel Channel, + DiscordGuild Guild +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessToggleAnonymousCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessToggleAnonymousCommand.cs new file mode 100644 index 00000000..c8440f91 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessToggleAnonymousCommand.cs @@ -0,0 +1,9 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessToggleAnonymousCommand( + Guid TicketId, + DiscordChannel TicketChannel = null +) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessUpdateTicketTypeCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessUpdateTicketTypeCommand.cs new file mode 100644 index 00000000..afa6bd1a --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessUpdateTicketTypeCommand.cs @@ -0,0 +1,9 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Commands; + +[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] +public sealed record ProcessUpdateTicketTypeCommand(ulong AuthorizedUserId, TicketType TicketType) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Commands/ProcessUserSentMessageCommand.cs b/src/Modmail.NET/Features/Ticket/Commands/ProcessUserSentMessageCommand.cs new file mode 100644 index 00000000..d089b15a --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Commands/ProcessUserSentMessageCommand.cs @@ -0,0 +1,9 @@ +using DSharpPlus.Entities; +using MediatR; + +namespace Modmail.NET.Features.Ticket.Commands; + +public sealed record ProcessUserSentMessageCommand( + Guid TicketId, + DiscordMessage Message, + DiscordChannel PrivateChannel = null) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Handlers/CheckActiveTicketHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/CheckActiveTicketHandler.cs new file mode 100644 index 00000000..ad61c61d --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Handlers/CheckActiveTicketHandler.cs @@ -0,0 +1,19 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Modmail.NET.Database; +using Modmail.NET.Features.Ticket.Queries; + +namespace Modmail.NET.Features.Ticket.Handlers; + +public class CheckActiveTicketHandler : IRequestHandler +{ + private readonly ModmailDbContext _dbContext; + + public CheckActiveTicketHandler(ModmailDbContext dbContext) { + _dbContext = dbContext; + } + + public async Task Handle(CheckActiveTicketQuery request, CancellationToken cancellationToken) { + return await _dbContext.Tickets.Where(x => x.Id == request.TicketId && !x.ClosedDateUtc.HasValue).AnyAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/TicketType/Handlers/CheckTicketTypeExistsHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/CheckTicketTypeExistsHandler.cs similarity index 86% rename from src/Modmail.NET/Features/TicketType/Handlers/CheckTicketTypeExistsHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/CheckTicketTypeExistsHandler.cs index 9ac3032f..44eeb37a 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/CheckTicketTypeExistsHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/CheckTicketTypeExistsHandler.cs @@ -1,8 +1,9 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; public class CheckTicketTypeExistsHandler : IRequestHandler { diff --git a/src/Modmail.NET/Features/Ticket/Handlers/GetActiveTicketByUserIdHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetActiveTicketByUserIdHandler.cs index 03c295e3..909b110c 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/GetActiveTicketByUserIdHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetActiveTicketByUserIdHandler.cs @@ -1,11 +1,13 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Ticket.Handlers; -public class GetActiveTicketByUserIdHandler : IRequestHandler +public class GetActiveTicketByUserIdHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; @@ -13,7 +15,7 @@ public GetActiveTicketByUserIdHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(GetTicketByUserIdQuery request, CancellationToken cancellationToken) { + public async Task Handle(GetTicketByUserIdQuery request, CancellationToken cancellationToken) { var ticket = await _dbContext.Tickets .FirstOrDefaultAsync(x => x.OpenerUserId == request.UserId && !x.ClosedDateUtc.HasValue, cancellationToken); if (!request.AllowNull && ticket is null) throw new NotFoundException(LangKeys.Ticket); diff --git a/src/Modmail.NET/Features/Ticket/Handlers/GetTicketHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketHandler.cs index 45477569..63f9dd1e 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/GetTicketHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketHandler.cs @@ -1,10 +1,12 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketHandler : IRequestHandler +public class GetTicketHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; @@ -12,7 +14,7 @@ public GetTicketHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(GetTicketQuery request, CancellationToken cancellationToken) { + public async Task Handle(GetTicketQuery request, CancellationToken cancellationToken) { var ticket = await _dbContext.Tickets.FindAsync([request.Id], cancellationToken); if (ticket is null) { if (!request.AllowNull) throw new NotFoundException(LangKeys.Ticket); diff --git a/src/Modmail.NET/Features/Ticket/Handlers/GetTicketListByTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketListByTypeHandler.cs index 0ddc6fe0..379b2fd3 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/GetTicketListByTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketListByTypeHandler.cs @@ -1,10 +1,11 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Features.Ticket.Queries; namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketListByTypeHandler : IRequestHandler> +public class GetTicketListByTypeHandler : IRequestHandler> { private readonly ModmailDbContext _dbContext; @@ -12,7 +13,7 @@ public GetTicketListByTypeHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task> Handle(GetTicketListByTypeQuery request, CancellationToken cancellationToken) { + public async Task> Handle(GetTicketListByTypeQuery request, CancellationToken cancellationToken) { var tickets = await _dbContext.Tickets .Where(x => x.TicketTypeId == request.TicketTypeId) .ToListAsync(cancellationToken); diff --git a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeByChannelIdHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeByChannelIdHandler.cs similarity index 64% rename from src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeByChannelIdHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeByChannelIdHandler.cs index 8c84325f..15020039 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeByChannelIdHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeByChannelIdHandler.cs @@ -1,11 +1,14 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketTypeByChannelIdHandler : IRequestHandler +public class GetTicketTypeByChannelIdHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; @@ -13,7 +16,7 @@ public GetTicketTypeByChannelIdHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(GetTicketTypeByChannelIdQuery request, CancellationToken cancellationToken) { + public async Task Handle(GetTicketTypeByChannelIdQuery request, CancellationToken cancellationToken) { var result = await _dbContext.Tickets.Where(x => x.ModMessageChannelId == request.ChannelId) .Select(x => x.TicketType) .FirstOrDefaultAsync(cancellationToken); diff --git a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeBySearchHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeBySearchHandler.cs similarity index 63% rename from src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeBySearchHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeBySearchHandler.cs index 066cbf10..1c68ce86 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeBySearchHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeBySearchHandler.cs @@ -1,11 +1,14 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketTypeBySearchHandler : IRequestHandler +public class GetTicketTypeBySearchHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; @@ -13,7 +16,7 @@ public GetTicketTypeBySearchHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(GetTicketTypeBySearchQuery request, CancellationToken cancellationToken) { + public async Task Handle(GetTicketTypeBySearchQuery request, CancellationToken cancellationToken) { var result = await _dbContext.TicketTypes.FirstOrDefaultAsync(x => x.Key == request.NameOrKey || x.Name == request.NameOrKey, cancellationToken); if (!request.AllowNull && result is null) throw new NotFoundWithException(LangKeys.TicketType, request.NameOrKey); return result; diff --git a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeHandler.cs similarity index 60% rename from src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeHandler.cs index b7159173..4bad5f5e 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeHandler.cs @@ -1,10 +1,12 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketTypeHandler : IRequestHandler +public class GetTicketTypeHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; @@ -12,7 +14,7 @@ public GetTicketTypeHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(GetTicketTypeQuery request, CancellationToken cancellationToken) { + public async Task Handle(GetTicketTypeQuery request, CancellationToken cancellationToken) { var data = await _dbContext.TicketTypes.FindAsync([request.Id], cancellationToken); if (!request.AllowNull && data is null) throw new TicketTypeNotExistsException(); diff --git a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeListHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeListHandler.cs similarity index 63% rename from src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeListHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeListHandler.cs index 29846dfd..5865dab8 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/GetTicketTypeListHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/GetTicketTypeListHandler.cs @@ -1,10 +1,12 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; -public class GetTicketTypeListHandler : IRequestHandler> +public class GetTicketTypeListHandler : IRequestHandler> { private readonly ModmailDbContext _dbContext; @@ -12,7 +14,7 @@ public GetTicketTypeListHandler(ModmailDbContext dbContext) { _dbContext = dbContext; } - public async Task> Handle(GetTicketTypeListQuery request, CancellationToken cancellationToken) { + public async Task> Handle(GetTicketTypeListQuery request, CancellationToken cancellationToken) { var query = _dbContext.TicketTypes.AsQueryable(); if (request.OnlyActive) query = query.Where(x => x.IsEnabled == true); diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddFeedbackHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddFeedbackHandler.cs index b5925260..4cd7ce90 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddFeedbackHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddFeedbackHandler.cs @@ -1,7 +1,10 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; namespace Modmail.NET.Features.Ticket.Handlers; @@ -26,7 +29,7 @@ public async Task Handle(ProcessAddFeedbackCommand request, CancellationToken ca if (!guildOption.TakeFeedbackAfterClosing) throw new InvalidOperationException("Feedback is not enabled for this guild: " + guildOption.GuildId); var ticket = await _sender.Send(new GetTicketQuery(request.TicketId, MustBeClosed: true), cancellationToken); - + if (ticket.FeedbackStar.HasValue) throw new FeedbackAlreadySubmittedException(); ticket.FeedbackStar = request.StarCount; ticket.FeedbackMessage = request.TextInput; @@ -34,6 +37,10 @@ public async Task Handle(ProcessAddFeedbackCommand request, CancellationToken ca var affected = await _dbContext.SaveChangesAsync(cancellationToken); if (affected == 0) throw new DbInternalException(); - _ = Task.Run(async () => { await request.FeedbackMessage.ModifyAsync(x => { x.AddEmbed(UserResponses.FeedbackReceivedUpdateMessage(ticket)); }); }, cancellationToken); + await request.FeedbackMessage.ModifyAsync(x => { + x.Clear(); + x.AddEmbed(TicketBotMessages.User.FeedbackReceivedUpdateMessage(ticket)); + }); + // _ = Task.Run(async () => { }); }, cancellationToken); } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddNoteHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddNoteHandler.cs index 637d10d2..101e7186 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddNoteHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessAddNoteHandler.cs @@ -1,8 +1,11 @@ using DSharpPlus.Exceptions; using MediatR; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.User.Queries; namespace Modmail.NET.Features.Ticket.Handlers; @@ -33,7 +36,7 @@ public async Task Handle(ProcessAddNoteCommand request, CancellationToken cancel try { var user = await _sender.Send(new GetDiscordUserInfoQuery(request.UserId), cancellationToken); var mailChannel = await _bot.Client.GetChannelAsync(ticket.ModMessageChannelId); - await mailChannel.SendMessageAsync(TicketResponses.NoteAdded(noteEntity, user)); + await mailChannel.SendMessageAsync(TicketBotMessages.Ticket.NoteAdded(noteEntity, user)); } catch (NotFoundException) { //ignored diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangePriorityHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangePriorityHandler.cs index 1fd5ed0c..3ac9adb6 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangePriorityHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangePriorityHandler.cs @@ -1,9 +1,13 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.Guild; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Features.User.Queries; using NotFoundException = DSharpPlus.Exceptions.NotFoundException; namespace Modmail.NET.Features.Ticket.Handlers; @@ -41,26 +45,26 @@ public async Task Handle(ProcessChangePriorityCommand request, CancellationToken try { var guildOption = await _sender.Send(new GetGuildOptionQuery(false), cancellationToken); var privateChannel = await _bot.Client.GetChannelAsync(ticket.PrivateMessageChannelId); - await privateChannel.SendMessageAsync(UserResponses.TicketPriorityChanged(guildOption, modUser, ticket, oldPriority, request.NewPriority)); + await privateChannel.SendMessageAsync(TicketBotMessages.User.TicketPriorityChanged(guildOption, modUser, ticket, oldPriority, request.NewPriority)); } catch (NotFoundException) { //ignored } var logChannel = await _sender.Send(new GetDiscordLogChannelQuery(), cancellationToken); - await logChannel.SendMessageAsync(LogResponses.TicketPriorityChanged(modUser, ticket, oldPriority, request.NewPriority)); + await logChannel.SendMessageAsync(LogBotMessages.TicketPriorityChanged(modUser, ticket, oldPriority, request.NewPriority)); try { var ticketChannel = request.TicketChannel ?? await _bot.Client.GetChannelAsync(ticket.ModMessageChannelId); var newChName = request.NewPriority switch { - TicketPriority.Normal => Const.NormalPriorityEmoji + string.Format(Const.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), - TicketPriority.High => Const.HighPriorityEmoji + string.Format(Const.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), - TicketPriority.Low => Const.LowPriorityEmoji + string.Format(Const.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), + TicketPriority.Normal => TicketConstants.NormalPriorityEmoji + string.Format(TicketConstants.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), + TicketPriority.High => TicketConstants.HighPriorityEmoji + string.Format(TicketConstants.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), + TicketPriority.Low => TicketConstants.LowPriorityEmoji + string.Format(TicketConstants.TicketNameTemplate, ticket.OpenerUser?.Username.Trim()), _ => "" }; await ticketChannel.ModifyAsync(x => { x.Name = newChName; }); - await ticketChannel.SendMessageAsync(TicketResponses.TicketPriorityChanged(modUser, ticket, oldPriority, request.NewPriority)); + await ticketChannel.SendMessageAsync(TicketBotMessages.Ticket.TicketPriorityChanged(modUser, ticket, oldPriority, request.NewPriority)); } catch (NotFoundException) { //ignored diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangeTicketTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangeTicketTypeHandler.cs index 8bc5585c..05bd4af1 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangeTicketTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessChangeTicketTypeHandler.cs @@ -1,10 +1,13 @@ using DSharpPlus.Entities; using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.TicketType; -using Modmail.NET.Features.UserInfo; -using Modmail.NET.Jobs; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Jobs; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.User.Queries; +using Modmail.NET.Language; using NotFoundException = DSharpPlus.Exceptions.NotFoundException; namespace Modmail.NET.Features.Ticket.Handlers; @@ -52,7 +55,7 @@ await Task.Run(async () => { var userInfo = await _sender.Send(new GetDiscordUserInfoQuery(userId), cancellationToken); var ticketChannel = request.TicketChannel ?? await _bot.Client.GetChannelAsync(ticket.ModMessageChannelId); - if (ticketChannel is not null) await ticketChannel.SendMessageAsync(TicketResponses.TicketTypeChanged(userInfo, ticketType)); + if (ticketChannel is not null) await ticketChannel.SendMessageAsync(TicketBotMessages.Ticket.TicketTypeChanged(userInfo, ticketType)); if (ticket.BotTicketCreatedMessageInDmId != 0) { try { diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessCloseTicketHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCloseTicketHandler.cs index 64a7654a..a6438a51 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessCloseTicketHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCloseTicketHandler.cs @@ -1,10 +1,14 @@ using DSharpPlus.Exceptions; using MediatR; using Microsoft.Extensions.Options; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.Guild; -using Modmail.NET.Utils; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; namespace Modmail.NET.Features.Ticket.Handlers; @@ -65,15 +69,15 @@ public async Task Handle(ProcessCloseTicketCommand request, CancellationToken ca await modChatChannel.DeleteAsync(LangProvider.This.GetTranslation(LangKeys.TicketClosed)); try { var pmChannel = await _bot.Client.GetChannelAsync(ticket.PrivateMessageChannelId); - await pmChannel.SendMessageAsync(UserResponses.YourTicketHasBeenClosed(ticket, guildOption, transcriptUri)); - if (guildOption.TakeFeedbackAfterClosing && !request.DontSendFeedbackMessage) await pmChannel.SendMessageAsync(UserResponses.GiveFeedbackMessage(ticket, guildOption)); + await pmChannel.SendMessageAsync(TicketBotMessages.User.YourTicketHasBeenClosed(ticket, guildOption, transcriptUri)); + if (guildOption.TakeFeedbackAfterClosing && !request.DontSendFeedbackMessage) await pmChannel.SendMessageAsync(TicketBotMessages.User.GiveFeedbackMessage(ticket, guildOption)); } catch (NotFoundException) { //ignored } var logChannel = await _sender.Send(new GetDiscordLogChannelQuery(), cancellationToken); - await logChannel.SendMessageAsync(LogResponses.TicketClosed(ticket)); + await logChannel.SendMessageAsync(LogBotMessages.TicketClosed(ticket)); }, cancellationToken); } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateNewTicketHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateNewTicketHandler.cs index e1398bb2..48ee152c 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateNewTicketHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateNewTicketHandler.cs @@ -1,15 +1,18 @@ using DSharpPlus.Entities; using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.Guild; -using Modmail.NET.Features.Permission; -using Modmail.NET.Features.TicketType; -using Modmail.NET.Jobs; -using Modmail.NET.Services; -using Modmail.NET.Utils; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Jobs; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.Ticket.Services; +using Modmail.NET.Features.Ticket.Static; +using TicketMessage = Modmail.NET.Database.Entities.TicketMessage; namespace Modmail.NET.Features.Ticket.Handlers; @@ -38,7 +41,7 @@ public async Task Handle(ProcessCreateNewTicketCommand request, CancellationToke var guild = await _sender.Send(new GetDiscordMainGuildQuery(), cancellationToken); //make new privateChannel - var channelName = string.Format(Const.TicketNameTemplate, request.User.Username.Trim()); + var channelName = string.Format(TicketConstants.TicketNameTemplate, request.User.Username.Trim()); var category = await _bot.Client.GetChannelAsync(guildOption.CategoryId); var ticketId = Guid.CreateVersion7(); @@ -55,12 +58,13 @@ public async Task Handle(ProcessCreateNewTicketCommand request, CancellationToke if (member is null) return; - var newTicketMessageBuilder = TicketResponses.NewTicket(member, ticketId, permissions); - + var pingOnNewTicket = permissions.Where(x => x.PingOnNewTicket).ToArray(); + var msg = TicketBotMessages.Ticket.NewTicket(member, ticketId); + msg.WithContent(UtilMention.GetMentionsMessageString(pingOnNewTicket)); var ticketMessage = TicketMessage.MapFrom(ticketId, request.Message, false); - var ticket = new Entities.Ticket { + var ticket = new Database.Entities.Ticket { OpenerUserId = request.User.Id, ModMessageChannelId = mailChannel.Id, RegisterDateUtc = UtilDate.GetNow(), @@ -84,10 +88,10 @@ public async Task Handle(ProcessCreateNewTicketCommand request, CancellationToke var ticketTypes = await _sender.Send(new GetTicketTypeListQuery(true), cancellationToken); - var ticketCreatedMessage = await request.PrivateChannel.SendMessageAsync(UserResponses.YouHaveCreatedNewTicket(guild, - guildOption, - ticketTypes, - ticketId)); + var ticketCreatedMessage = await request.PrivateChannel.SendMessageAsync(TicketBotMessages.User.YouHaveCreatedNewTicket(guild, + guildOption, + ticketTypes, + ticketId)); _ticketTypeSelectionTimeoutJob.AddMessage(ticketCreatedMessage); @@ -101,14 +105,14 @@ public async Task Handle(ProcessCreateNewTicketCommand request, CancellationToke foreach (var attachment in ticketMessage.Attachments) await _attachmentDownloadService.Handle(attachment.Id, attachment.Url, Path.GetExtension(attachment.FileName)); - await mailChannel.SendMessageAsync(newTicketMessageBuilder); - var botMessage = await mailChannel.SendMessageAsync(TicketResponses.MessageReceived(request.Message, ticketMessage.Attachments.ToArray())); + await mailChannel.SendMessageAsync(msg); + var botMessage = await mailChannel.SendMessageAsync(TicketBotMessages.Ticket.MessageReceived(request.Message, ticketMessage.Attachments.ToArray())); ticketMessage.BotMessageId = botMessage.Id; - var newTicketCreatedLog = LogResponses.NewTicketCreated(request.Message, mailChannel, ticket.Id); + var newTicketCreatedLog = LogBotMessages.NewTicketCreated(request.Message, mailChannel, ticket.Id); var logChannel = await _sender.Send(new GetDiscordLogChannelQuery(), cancellationToken); await logChannel.SendMessageAsync(newTicketCreatedLog); - await request.Message.CreateReactionAsync(DiscordEmoji.FromName(_bot.Client, Const.ProcessedReactionDiscordEmojiString, false)); + await request.Message.CreateReactionAsync(DiscordEmoji.FromUnicode(TicketConstants.ProcessedReactionDiscordEmojiUnicode)); } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/TicketType/Handlers/ProcessCreateTicketTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateTicketTypeHandler.cs similarity index 83% rename from src/Modmail.NET/Features/TicketType/Handlers/ProcessCreateTicketTypeHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateTicketTypeHandler.cs index 9760a619..9477c2ff 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/ProcessCreateTicketTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessCreateTicketTypeHandler.cs @@ -1,9 +1,12 @@ using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; public class ProcessCreateTicketTypeHandler : IRequestHandler { @@ -26,7 +29,7 @@ public async Task Handle(ProcessCreateTicketTypeCommand request, CancellationTok var id = Guid.CreateVersion7(); var idClean = id.ToString().Replace("-", ""); - var ticketType = new Entities.TicketType { + var ticketType = new TicketType { Id = id, Key = idClean, Name = request.Name, diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessModSendMessageHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessModSendMessageHandler.cs index 034c7443..4368866e 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessModSendMessageHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessModSendMessageHandler.cs @@ -1,11 +1,15 @@ using DSharpPlus.Entities; using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; -using Modmail.NET.Services; -using Modmail.NET.Utils; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.Ticket.Services; +using Modmail.NET.Features.Ticket.Static; +using TicketMessage = Modmail.NET.Database.Entities.TicketMessage; namespace Modmail.NET.Features.Ticket.Handlers; @@ -48,7 +52,7 @@ public async Task Handle(ProcessModSendMessageCommand request, CancellationToken var anonymous = guildOption.AlwaysAnonymous || ticket.Anonymous; var privateChannel = await _bot.Client.GetChannelAsync(ticket.PrivateMessageChannelId); - var embed = UserResponses.MessageReceived(request.Message, ticketMessage.Attachments.ToArray(), anonymous); + var embed = TicketBotMessages.User.MessageReceived(request.Message, ticketMessage.Attachments.ToArray(), anonymous); var botMessage = await privateChannel.SendMessageAsync(embed); ticketMessage.BotMessageId = botMessage.Id; @@ -57,6 +61,6 @@ public async Task Handle(ProcessModSendMessageCommand request, CancellationToken var affected = await _dbContext.SaveChangesAsync(cancellationToken); if (affected == 0) throw new DbInternalException(); - await request.Message.CreateReactionAsync(DiscordEmoji.FromName(_bot.Client, Const.ProcessedReactionDiscordEmojiString, false)); + await request.Message.CreateReactionAsync(DiscordEmoji.FromUnicode(TicketConstants.ProcessedReactionDiscordEmojiUnicode)); } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/TicketType/Handlers/ProcessRemoveTicketTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessRemoveTicketTypeHandler.cs similarity index 73% rename from src/Modmail.NET/Features/TicketType/Handlers/ProcessRemoveTicketTypeHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/ProcessRemoveTicketTypeHandler.cs index 37f90fb1..e1f36f55 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/ProcessRemoveTicketTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessRemoveTicketTypeHandler.cs @@ -1,11 +1,13 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Ticket; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; -public class ProcessRemoveTicketTypeHandler : IRequestHandler +public class ProcessRemoveTicketTypeHandler : IRequestHandler { private readonly ModmailDbContext _dbContext; private readonly ISender _sender; @@ -16,7 +18,7 @@ public ProcessRemoveTicketTypeHandler(ModmailDbContext dbContext, _sender = sender; } - public async Task Handle(ProcessRemoveTicketTypeCommand request, CancellationToken cancellationToken) { + public async Task Handle(ProcessRemoveTicketTypeCommand request, CancellationToken cancellationToken) { var ticketType = await _sender.Send(new GetTicketTypeQuery(request.Id), cancellationToken); var allTicketsByType = await _sender.Send(new GetTicketListByTypeQuery(ticketType.Id), cancellationToken); diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessTagSendMessageHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessTagSendMessageHandler.cs new file mode 100644 index 00000000..094d1ace --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessTagSendMessageHandler.cs @@ -0,0 +1,63 @@ +using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Tag.Helpers; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Language; +using TicketMessage = Modmail.NET.Database.Entities.TicketMessage; + +namespace Modmail.NET.Features.Ticket.Handlers; + +public class ProcessTagSendMessageHandler : IRequestHandler +{ + private readonly ModmailBot _bot; + private readonly ModmailDbContext _dbContext; + private readonly ISender _sender; + + public ProcessTagSendMessageHandler(ISender sender, + ModmailBot bot, + ModmailDbContext dbContext) { + _sender = sender; + _bot = bot; + _dbContext = dbContext; + } + + public async Task Handle(ProcessTagSendMessageCommand request, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(request.ModUser); + ArgumentNullException.ThrowIfNull(request.Channel); + ArgumentNullException.ThrowIfNull(request.Guild); + + var guildOption = await _sender.Send(new GetGuildOptionQuery(false), cancellationToken); + + var ticket = await _sender.Send(new GetTicketQuery(request.TicketId, MustBeOpen: true), cancellationToken); + ticket.LastMessageDateUtc = UtilDate.GetNow(); + + _dbContext.Update(ticket); + + + var privateChannel = await _bot.Client.GetChannelAsync(ticket.PrivateMessageChannelId); + + var tag = await _dbContext.Tags.FindAsync([request.TagId], cancellationToken); + if (tag is null) throw new ModmailBotException(LangKeys.TagDoesntExists); + + var ticketMessage = new TicketMessage { + SenderUserId = request.ModUser.Id, + MessageDiscordId = 0, + TicketId = request.TicketId, + SentByMod = true, + MessageContent = "TagCommand:" + tag.Name //TODO: Find a way to render tags or directly insert tag content to ticket message logs + }; + + var tagMessage = TagBotMessages.TagReceivedToTicket(tag, request.ModUser, ticket.Anonymous || guildOption.AlwaysAnonymous); + var botMessage = await privateChannel.SendMessageAsync(tagMessage); + + ticketMessage.BotMessageId = botMessage.Id; + await _dbContext.AddAsync(ticketMessage, cancellationToken); + + var affected = await _dbContext.SaveChangesAsync(cancellationToken); + if (affected == 0) throw new DbInternalException(); + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessToggleAnonymousHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessToggleAnonymousHandler.cs index 70756730..616ba997 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessToggleAnonymousHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessToggleAnonymousHandler.cs @@ -1,6 +1,9 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; namespace Modmail.NET.Features.Ticket.Handlers; @@ -30,7 +33,7 @@ public async Task Handle(ProcessToggleAnonymousCommand request, CancellationToke _ = Task.Run(async () => { var ticketChannel = request.TicketChannel ?? await _bot.Client.GetChannelAsync(ticket.ModMessageChannelId); - if (ticketChannel is not null) await ticketChannel.SendMessageAsync(TicketResponses.AnonymousToggled(ticket)); + if (ticketChannel is not null) await ticketChannel.SendMessageAsync(TicketBotMessages.Ticket.AnonymousToggled(ticket)); }, cancellationToken); } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/TicketType/Handlers/ProcessUpdateTicketTypeHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessUpdateTicketTypeHandler.cs similarity index 89% rename from src/Modmail.NET/Features/TicketType/Handlers/ProcessUpdateTicketTypeHandler.cs rename to src/Modmail.NET/Features/Ticket/Handlers/ProcessUpdateTicketTypeHandler.cs index 06767c5d..7cfe1458 100644 --- a/src/Modmail.NET/Features/TicketType/Handlers/ProcessUpdateTicketTypeHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessUpdateTicketTypeHandler.cs @@ -1,8 +1,9 @@ using MediatR; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Exceptions; +using Modmail.NET.Features.Ticket.Commands; -namespace Modmail.NET.Features.TicketType.Handlers; +namespace Modmail.NET.Features.Ticket.Handlers; public class ProcessUpdateTicketTypeHandler : IRequestHandler { diff --git a/src/Modmail.NET/Features/Ticket/Handlers/ProcessUserSentMessageHandler.cs b/src/Modmail.NET/Features/Ticket/Handlers/ProcessUserSentMessageHandler.cs index 5f65cc78..d878cfc5 100644 --- a/src/Modmail.NET/Features/Ticket/Handlers/ProcessUserSentMessageHandler.cs +++ b/src/Modmail.NET/Features/Ticket/Handlers/ProcessUserSentMessageHandler.cs @@ -1,11 +1,15 @@ using DSharpPlus.Entities; using MediatR; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Permission; -using Modmail.NET.Services; -using Modmail.NET.Utils; +using Modmail.NET.Features.Permission.Queries; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Helpers; +using Modmail.NET.Features.Ticket.Queries; +using Modmail.NET.Features.Ticket.Services; +using Modmail.NET.Features.Ticket.Static; +using TicketMessage = Modmail.NET.Database.Entities.TicketMessage; namespace Modmail.NET.Features.Ticket.Handlers; @@ -42,13 +46,17 @@ public async Task Handle(ProcessUserSentMessageCommand request, CancellationToke var mailChannel = await _bot.Client.GetChannelAsync(ticket.ModMessageChannelId); var permissions = await _sender.Send(new GetPermissionInfoQuery(), cancellationToken); - var botMessage = await mailChannel.SendMessageAsync(TicketResponses.MessageReceived(request.Message, ticketMessage.Attachments.ToArray(), permissions)); + var pingOnNewTicket = permissions.Where(x => x.PingOnNewMessage).ToArray(); + + var msg = TicketBotMessages.Ticket.MessageReceived(request.Message, ticketMessage.Attachments.ToArray()); + msg.WithContent(UtilMention.GetMentionsMessageString(pingOnNewTicket)); + var botMessage = await mailChannel.SendMessageAsync(msg); ticketMessage.BotMessageId = botMessage.Id; _dbContext.Add(ticketMessage); var affected = await _dbContext.SaveChangesAsync(cancellationToken); if (affected == 0) throw new DbInternalException(); - await request.Message.CreateReactionAsync(DiscordEmoji.FromName(_bot.Client, Const.ProcessedReactionDiscordEmojiString, false)); + await request.Message.CreateReactionAsync(DiscordEmoji.FromUnicode(TicketConstants.ProcessedReactionDiscordEmojiUnicode)); } } \ No newline at end of file diff --git a/src/Modmail.NET/Static/LogResponses.cs b/src/Modmail.NET/Features/Ticket/Helpers/LogBotMessages.cs similarity index 80% rename from src/Modmail.NET/Static/LogResponses.cs rename to src/Modmail.NET/Features/Ticket/Helpers/LogBotMessages.cs index 289f00c0..7647e98a 100644 --- a/src/Modmail.NET/Static/LogResponses.cs +++ b/src/Modmail.NET/Features/Ticket/Helpers/LogBotMessages.cs @@ -1,13 +1,16 @@ using DSharpPlus.Entities; -using Modmail.NET.Entities; -using Modmail.NET.Extensions; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Language; -namespace Modmail.NET.Static; +namespace Modmail.NET.Features.Ticket.Helpers; /// /// Contains the embed messages bot to send to log channel /// -public static class LogResponses +public static class LogBotMessages { public static DiscordEmbedBuilder NewTicketCreated(DiscordMessage initialMessage, DiscordChannel mailChannel, @@ -16,17 +19,17 @@ public static DiscordEmbedBuilder NewTicketCreated(DiscordMessage initialMessage .WithTitle(LangKeys.NewTicketCreated.GetTranslation()) .WithUserAsAuthor(initialMessage.Author) .WithCustomTimestamp() - .WithColor(Colors.TicketCreatedColor) + .WithColor(ModmailColors.TicketCreatedColor) .AddField(LangKeys.TicketId.GetTranslation(), ticketId.ToString().ToUpper()) .AddField(LangKeys.User.GetTranslation(), initialMessage.Author!.Mention); return embed; } - public static DiscordEmbedBuilder TicketClosed(Ticket ticket) { + public static DiscordEmbedBuilder TicketClosed(Database.Entities.Ticket ticket) { var embed = new DiscordEmbedBuilder() .WithCustomTimestamp() .WithTitle(LangKeys.TicketClosed.GetTranslation()) - .WithColor(Colors.TicketClosedColor) + .WithColor(ModmailColors.TicketClosedColor) .AddField(LangKeys.TicketId.GetTranslation(), ticket.Id.ToString().ToUpper()) .AddField(LangKeys.OpenedBy.GetTranslation(), ticket.OpenerUser!.GetMention(), true) .AddField(LangKeys.ClosedBy.GetTranslation(), ticket.CloserUser!.GetMention(), true) @@ -42,7 +45,7 @@ public static DiscordEmbedBuilder BlacklistAdded(DiscordUserInfo author, Discord var embed = new DiscordEmbedBuilder() .WithTitle(LangKeys.UserBlacklisted.GetTranslation()) .WithUserAsAuthor(author) - .WithColor(Colors.InfoColor) + .WithColor(ModmailColors.InfoColor) .AddField(LangKeys.User.GetTranslation(), user.GetMention(), true) .AddField(LangKeys.UserId.GetTranslation(), user.Id.ToString(), true); @@ -54,18 +57,18 @@ public static DiscordEmbedBuilder BlacklistRemoved(DiscordUserInfo author, Disco var embed = new DiscordEmbedBuilder() .WithTitle(LangKeys.UserBlacklistRemoved.GetTranslation()) .WithUserAsAuthor(author) - .WithColor(Colors.InfoColor) + .WithColor(ModmailColors.InfoColor) .AddField(LangKeys.User.GetTranslation(), user.GetMention(), true) .AddField(LangKeys.UserId.GetTranslation(), user.Id.ToString(), true); return embed; } - public static DiscordEmbedBuilder TicketPriorityChanged(DiscordUserInfo modUser, Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { + public static DiscordEmbedBuilder TicketPriorityChanged(DiscordUserInfo modUser, Database.Entities.Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { var embed = new DiscordEmbedBuilder() .WithTitle(LangKeys.TicketPriorityChanged.GetTranslation()) .WithCustomTimestamp() - .WithColor(Colors.TicketPriorityChangedColor) + .WithColor(ModmailColors.TicketPriorityChangedColor) .AddField(LangKeys.TicketId.GetTranslation(), ticket.Id.ToString().ToUpper()) .AddField(LangKeys.OldPriority.GetTranslation(), oldPriority.ToString(), true) .AddField(LangKeys.NewPriority.GetTranslation(), newPriority.ToString(), true); diff --git a/src/Modmail.NET/Features/Ticket/Helpers/TicketBotMessages.cs b/src/Modmail.NET/Features/Ticket/Helpers/TicketBotMessages.cs new file mode 100644 index 00000000..e5537b27 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Helpers/TicketBotMessages.cs @@ -0,0 +1,246 @@ +using DSharpPlus.Entities; +using Modmail.NET.Common.Extensions; +using Modmail.NET.Common.Static; +using Modmail.NET.Common.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.Ticket.Static; +using Modmail.NET.Language; + +namespace Modmail.NET.Features.Ticket.Helpers; + +public static class TicketBotMessages +{ + public static class User + { + public static DiscordEmbedBuilder FeedbackReceivedUpdateMessage(Database.Entities.Ticket ticket) { + var feedbackDone = new DiscordEmbedBuilder() + .WithTitle(LangKeys.FeedbackReceived.GetTranslation()) + .WithCustomTimestamp() + .WithGuildInfoFooter() + .AddField(LangKeys.Star.GetTranslation(), LangKeys.StarEmoji.GetTranslation() + ticket.FeedbackStar) + .AddField(LangKeys.Feedback.GetTranslation(), ticket.FeedbackMessage) + .WithColor(ModmailColors.FeedbackColor); + return feedbackDone; + } + + + public static DiscordMessageBuilder YourTicketHasBeenClosed(Database.Entities.Ticket ticket, GuildOption guildOption, Uri transcriptUri) { + var messageBuilder = new DiscordMessageBuilder(); + var embedBuilder = new DiscordEmbedBuilder() + .WithTitle(LangKeys.YourTicketHasBeenClosed.GetTranslation()) + .WithDescription(LangKeys.YourTicketHasBeenClosedDescription.GetTranslation()) + .WithGuildInfoFooter(guildOption) + .WithCustomTimestamp() + .WithColor(ModmailColors.TicketClosedColor); + + var closingMessage = LangKeys.ClosingMessageDescription.GetTranslation(); + + if (!string.IsNullOrEmpty(closingMessage)) embedBuilder.WithDescription(closingMessage); + + if (!string.IsNullOrEmpty(ticket.CloseReason)) embedBuilder.AddField(LangKeys.CloseReason.GetTranslation(), ticket.CloseReason); + + if (transcriptUri is not null) messageBuilder.AddComponents(new DiscordLinkButtonComponent(transcriptUri.AbsoluteUri, LangKeys.Transcript.GetTranslation())); + + messageBuilder.AddEmbed(embedBuilder); + return messageBuilder; + } + + public static DiscordMessageBuilder GiveFeedbackMessage(Database.Entities.Ticket ticket, GuildOption guildOption) { + var ticketFeedbackMsgToUser = new DiscordMessageBuilder(); + var starList = new List { + new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 1, ticket.Id), "1", false, new DiscordComponentEmoji("⭐")), + new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 2, ticket.Id), "2", false, new DiscordComponentEmoji("⭐")), + new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 3, ticket.Id), "3", false, new DiscordComponentEmoji("⭐")), + new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 4, ticket.Id), "4", false, new DiscordComponentEmoji("⭐")), + new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 5, ticket.Id), "5", false, new DiscordComponentEmoji("⭐")) + }; + + var ticketFeedbackEmbed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.Feedback.GetTranslation()) + .WithDescription(LangKeys.FeedbackDescription.GetTranslation()) + .WithCustomTimestamp() + .WithGuildInfoFooter(guildOption) + .WithColor(ModmailColors.FeedbackColor); + + var response = ticketFeedbackMsgToUser + .AddEmbed(ticketFeedbackEmbed) + .AddComponents(starList); + return response; + } + + public static DiscordEmbedBuilder TicketPriorityChanged(GuildOption guildOption, DiscordUserInfo info, Database.Entities.Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { + var embed = new DiscordEmbedBuilder() + .WithGuildInfoFooter(guildOption) + .WithTitle(LangKeys.TicketPriorityChanged.GetTranslation()) + .WithCustomTimestamp() + .WithColor(ModmailColors.TicketPriorityChangedColor) + .AddField(LangKeys.OldPriority.GetTranslation(), oldPriority.ToString(), true) + .AddField(LangKeys.NewPriority.GetTranslation(), newPriority.ToString(), true); + if (!ticket.Anonymous) embed.WithUserAsAuthor(info); + // else embed.WithUserAsAuthor(ModmailBot.This.Client.CurrentUser); + return embed; + } + + + public static DiscordMessageBuilder YouHaveCreatedNewTicket(DiscordGuild guild, + GuildOption option, + List ticketTypes, + Guid ticketId) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.YouHaveCreatedNewTicket.GetTranslation()) + .WithFooter(guild.Name, guild.IconUrl) + .WithCustomTimestamp() + .WithColor(ModmailColors.TicketCreatedColor); + var greetingMessage = LangKeys.GreetingMessageDescription.GetTranslation(); + if (!string.IsNullOrEmpty(greetingMessage)) + embed.WithDescription(greetingMessage); + + var builder = new DiscordMessageBuilder() + .AddEmbed(embed); + + if (ticketTypes.Count > 0) { + var selectBox = new DiscordSelectComponent(UtilInteraction.BuildKey("ticket_type", ticketId.ToString()), + LangKeys.PleaseSelectATicketType.GetTranslation(), + ticketTypes.Select(x => new DiscordSelectComponentOption(x.Name, + x.Key.ToString(), + x.Description, + false, + !string.IsNullOrWhiteSpace(x.Emoji) + ? new DiscordComponentEmoji(x.Emoji) + : null)) + .ToList()); + builder.AddComponents(selectBox); + } + + return builder; + } + + + public static DiscordMessageBuilder MessageReceived(DiscordMessage message, TicketMessageAttachment[] attachments, bool anonymous) { + var embed = new DiscordEmbedBuilder() + .WithDescription(message.Content) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithColor(ModmailColors.MessageReceivedColor); + + if (!anonymous) embed.WithUserAsAuthor(message.Author); + + var msg = new DiscordMessageBuilder(); + + msg.AddEmbed(embed); + msg.AddAttachments(attachments); + + return msg; + } + + public static DiscordEmbedBuilder MessageEdited(DiscordMessage message, bool anonymous) { + var embed = new DiscordEmbedBuilder() + .WithDescription(message.Content) + .WithGuildInfoFooter() + .WithCustomTimestamp() + .WithColor(ModmailColors.MessageReceivedColor); + + if (!anonymous) embed.WithUserAsAuthor(message.Author); + + return embed; + } + } + + public static class Ticket + { + public static DiscordMessageBuilder NewTicket(DiscordUser member, Guid ticketId) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.NewTicket.GetTranslation()) + .WithCustomTimestamp() + .WithDescription(LangKeys.NewTicketDescriptionMessage.GetTranslation()) + .WithAuthor(member.GetUsername(), iconUrl: member.AvatarUrl) + .AddField(LangKeys.User.GetTranslation(), member.Mention, true) + .AddField(LangKeys.TicketId.GetTranslation(), ticketId.ToString().ToUpper(), true) + .WithColor(ModmailColors.TicketCreatedColor); + + var messageBuilder = new DiscordMessageBuilder() + .AddEmbed(embed) + .AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Danger, + UtilInteraction.BuildKey("close_ticket_with_reason", ticketId.ToString()), + LangKeys.CloseTicket.GetTranslation(), + emoji: new DiscordComponentEmoji("🔒")) + ); + return messageBuilder; + } + + public static DiscordEmbedBuilder NoteAdded(TicketNote note, DiscordUserInfo user) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.NoteAdded.GetTranslation()) + .WithDescription(note.Content) + .WithColor(ModmailColors.NoteAddedColor) + .WithCustomTimestamp() + .WithUserAsAuthor(user); + return embed; + } + + public static DiscordEmbedBuilder AnonymousToggled(Database.Entities.Ticket ticket) { + var embed2 = new DiscordEmbedBuilder() + .WithTitle(ticket.Anonymous + ? LangKeys.AnonymousModOn.GetTranslation() + : LangKeys.AnonymousModOff.GetTranslation()) + .WithColor(ModmailColors.AnonymousToggledColor) + .WithCustomTimestamp() + .WithDescription(ticket.Anonymous + ? LangKeys.TicketSetAnonymousDescription.GetTranslation() + : LangKeys.TicketSetNotAnonymousDescription.GetTranslation()); + + if (ticket.OpenerUser is not null) embed2.WithUserAsAuthor(ticket.OpenerUser); + + + return embed2; + } + + public static DiscordEmbedBuilder TicketTypeChanged(DiscordUserInfo user, TicketType ticketType) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.TicketTypeChanged.GetTranslation()) + .WithUserAsAuthor(user) + .WithCustomTimestamp() + .WithColor(ModmailColors.TicketTypeChangedColor); + if (ticketType is not null) + embed.WithDescription(string.Format(LangKeys.TicketTypeSet.GetTranslation(), ticketType.Emoji, ticketType.Name)); + else + embed.WithDescription(LangKeys.TicketTypeRemoved.GetTranslation()); + + return embed; + } + + public static DiscordEmbedBuilder TicketPriorityChanged(DiscordUserInfo modUser, Database.Entities.Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { + var embed = new DiscordEmbedBuilder() + .WithTitle(LangKeys.TicketPriorityChanged.GetTranslation()) + .WithColor(ModmailColors.TicketPriorityChangedColor) + .WithCustomTimestamp() + .AddField(LangKeys.OldPriority.GetTranslation(), oldPriority.ToString(), true) + .AddField(LangKeys.NewPriority.GetTranslation(), newPriority.ToString(), true) + .WithUserAsAuthor(modUser); + return embed; + } + + public static DiscordMessageBuilder MessageReceived(DiscordMessage message, + TicketMessageAttachment[] attachments) { + var embed = new DiscordEmbedBuilder() + .WithDescription(message.Content) + .WithCustomTimestamp() + .WithColor(ModmailColors.MessageReceivedColor) + .WithUserAsAuthor(message.Author); + + var msgBuilder = new DiscordMessageBuilder() + .AddEmbed(embed) + .AddAttachments(attachments); + return msgBuilder; + } + + public static DiscordEmbedBuilder MessageEdited(DiscordMessage message) { + var embed = new DiscordEmbedBuilder() + .WithDescription(message.Content) + .WithCustomTimestamp() + .WithColor(ModmailColors.MessageReceivedColor) + .WithUserAsAuthor(message.Author); + return embed; + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Static/Modals.cs b/src/Modmail.NET/Features/Ticket/Helpers/TicketModals.cs similarity index 84% rename from src/Modmail.NET/Static/Modals.cs rename to src/Modmail.NET/Features/Ticket/Helpers/TicketModals.cs index b3e0d994..11e5980a 100644 --- a/src/Modmail.NET/Static/Modals.cs +++ b/src/Modmail.NET/Features/Ticket/Helpers/TicketModals.cs @@ -1,9 +1,11 @@ using DSharpPlus.Entities; -using Modmail.NET.Utils; +using Modmail.NET.Common.Static; +using Modmail.NET.Common.Utils; +using Modmail.NET.Language; -namespace Modmail.NET.Static; +namespace Modmail.NET.Features.Ticket.Helpers; -public static class Modals +public static class TicketModals { public static DiscordInteractionResponseBuilder CreateFeedbackModal(int starCount, Guid ticketId, ulong messageId) { var modal = new DiscordInteractionResponseBuilder() @@ -13,8 +15,8 @@ public static DiscordInteractionResponseBuilder CreateFeedbackModal(int starCoun "feedback", LangKeys.PleaseTellUsReasonsForYourRating.GetTranslation(), style: DiscordTextInputStyle.Paragraph, - min_length: 10, - max_length: 500)); + required: false, + max_length: DbLength.FeedbackMessage)); return modal; } @@ -27,7 +29,7 @@ public static DiscordInteractionResponseBuilder CreateCloseTicketWithReasonModal LangKeys.EnterReasonForClosingThisTicket.GetTranslation(), style: DiscordTextInputStyle.Paragraph, required: false, - max_length: 500)); + max_length: DbLength.Reason)); return modal; } } \ No newline at end of file diff --git a/src/Modmail.NET/Jobs/TicketDataDeleteJob.cs b/src/Modmail.NET/Features/Ticket/Jobs/TicketDataDeleteJob.cs similarity index 90% rename from src/Modmail.NET/Jobs/TicketDataDeleteJob.cs rename to src/Modmail.NET/Features/Ticket/Jobs/TicketDataDeleteJob.cs index 4cce73e4..508abff0 100644 --- a/src/Modmail.NET/Jobs/TicketDataDeleteJob.cs +++ b/src/Modmail.NET/Features/Ticket/Jobs/TicketDataDeleteJob.cs @@ -3,12 +3,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Modmail.NET.Abstract; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Features.Guild; -using Modmail.NET.Utils; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Static; using Serilog; -namespace Modmail.NET.Jobs; +namespace Modmail.NET.Features.Ticket.Jobs; public class TicketDataDeleteJob : HangfireRecurringJobBase { @@ -24,7 +25,7 @@ public override async Task Execute() { var sender = scope.ServiceProvider.GetRequiredService(); var guildOption = await sender.Send(new GetGuildOptionQuery(false)) ?? throw new NullReferenceException(); - if (guildOption.TicketDataDeleteWaitDays == -1) return; + if (guildOption.TicketDataDeleteWaitDays < TicketConstants.TicketDataDeleteWaitDaysMin) return; var dbContext = scope.ServiceProvider.GetRequiredService(); var timeoutDate = UtilDate.GetNow().AddDays(-guildOption.TicketDataDeleteWaitDays); diff --git a/src/Modmail.NET/Jobs/TicketTimeoutJob.cs b/src/Modmail.NET/Features/Ticket/Jobs/TicketTimeoutJob.cs similarity index 86% rename from src/Modmail.NET/Jobs/TicketTimeoutJob.cs rename to src/Modmail.NET/Features/Ticket/Jobs/TicketTimeoutJob.cs index fce02f3b..cbc0674a 100644 --- a/src/Modmail.NET/Jobs/TicketTimeoutJob.cs +++ b/src/Modmail.NET/Features/Ticket/Jobs/TicketTimeoutJob.cs @@ -3,14 +3,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Modmail.NET.Abstract; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Features.Guild; -using Modmail.NET.Features.Ticket; -using Modmail.NET.Utils; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.Ticket.Commands; using Serilog; -namespace Modmail.NET.Jobs; +namespace Modmail.NET.Features.Ticket.Jobs; public class TicketTimeoutJob : HangfireRecurringJobBase { @@ -30,16 +29,15 @@ public override async Task Execute() { if (guildOption.TicketTimeoutHours == -1) return; //Disabled var tickets = await GetTimeoutTickets(); - if (tickets.Length > 0) { + if (tickets.Length > 0) foreach (var ticket in tickets) { await sender.Send(new ProcessCloseTicketCommand(ticket.Id, bot.Client.CurrentUser.Id, "Ticket timed out")); Log.Information("Ticket {TicketId} has been closed due to timeout", ticket.Id); } - } return; - async Task GetTimeoutTickets() { + async Task GetTimeoutTickets() { if (guildOption.TicketTimeoutHours == -1) return []; var timeoutDate = UtilDate.GetNow().AddHours(-guildOption.TicketTimeoutHours); diff --git a/src/Modmail.NET/Jobs/TicketTypeSelectionTimeoutJob.cs b/src/Modmail.NET/Features/Ticket/Jobs/TicketTypeSelectionTimeoutJob.cs similarity index 94% rename from src/Modmail.NET/Jobs/TicketTypeSelectionTimeoutJob.cs rename to src/Modmail.NET/Features/Ticket/Jobs/TicketTypeSelectionTimeoutJob.cs index 77f55c94..46362d62 100644 --- a/src/Modmail.NET/Jobs/TicketTypeSelectionTimeoutJob.cs +++ b/src/Modmail.NET/Features/Ticket/Jobs/TicketTypeSelectionTimeoutJob.cs @@ -2,9 +2,9 @@ using DSharpPlus.Entities; using Hangfire; using Modmail.NET.Abstract; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; -namespace Modmail.NET.Jobs; +namespace Modmail.NET.Features.Ticket.Jobs; public class TicketTypeSelectionTimeoutJob : HangfireRecurringJobBase { diff --git a/src/Modmail.NET/Features/Ticket/Mappers/TicketDtoMapper.cs b/src/Modmail.NET/Features/Ticket/Mappers/TicketDtoMapper.cs new file mode 100644 index 00000000..a07ba912 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Mappers/TicketDtoMapper.cs @@ -0,0 +1,12 @@ +using Modmail.NET.Features.Ticket.Models; +using Riok.Mapperly.Abstractions; + +namespace Modmail.NET.Features.Ticket.Mappers; + +[Mapper] +public static partial class TicketDtoMapper +{ + public static partial IQueryable ProjectToDto(this IQueryable queryable); + + public static partial TicketDto ToDto(this Database.Entities.Ticket entity); +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Mappers/TicketFeedbackDtoMapper.cs b/src/Modmail.NET/Features/Ticket/Mappers/TicketFeedbackDtoMapper.cs new file mode 100644 index 00000000..4f1a78fb --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Mappers/TicketFeedbackDtoMapper.cs @@ -0,0 +1,12 @@ +using Modmail.NET.Features.Ticket.Models; +using Riok.Mapperly.Abstractions; + +namespace Modmail.NET.Features.Ticket.Mappers; + +[Mapper] +public static partial class TicketFeedbackDtoMapper +{ + public static partial IQueryable ProjectToFeedbackDto(this IQueryable queryable); + + public static partial TicketFeedbackDto ToFeedbackDto(this Database.Entities.Ticket entity); +} \ No newline at end of file diff --git a/src/Modmail.NET/Models/Dto/TicketDto.cs b/src/Modmail.NET/Features/Ticket/Models/TicketDto.cs similarity index 77% rename from src/Modmail.NET/Models/Dto/TicketDto.cs rename to src/Modmail.NET/Features/Ticket/Models/TicketDto.cs index 6768ee69..fabe0c0f 100644 --- a/src/Modmail.NET/Models/Dto/TicketDto.cs +++ b/src/Modmail.NET/Features/Ticket/Models/TicketDto.cs @@ -1,6 +1,6 @@ -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; -namespace Modmail.NET.Models.Dto; +namespace Modmail.NET.Features.Ticket.Models; public class TicketDto { @@ -15,4 +15,6 @@ public class TicketDto public DiscordUserInfo AssignedUser { get; set; } public TicketType TicketType { get; set; } public string CloseReason { get; set; } + public int? FeedbackStar { get; set; } + public string FeedbackMessage { get; set; } } \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Models/TicketFeedbackDto.cs b/src/Modmail.NET/Features/Ticket/Models/TicketFeedbackDto.cs new file mode 100644 index 00000000..813a8b65 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Models/TicketFeedbackDto.cs @@ -0,0 +1,12 @@ +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Models; + +public class TicketFeedbackDto +{ + public required Guid Id { get; set; } + public required DateTime ClosedDateUtc { get; set; } + public required DiscordUserInfo OpenerUser { get; set; } + public int? FeedbackStar { get; set; } + public string FeedbackMessage { get; set; } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries.cs b/src/Modmail.NET/Features/Ticket/Queries.cs deleted file mode 100644 index 8b4264fb..00000000 --- a/src/Modmail.NET/Features/Ticket/Queries.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MediatR; - -namespace Modmail.NET.Features.Ticket; - -public sealed record GetTicketQuery( - Guid Id, - bool AllowNull = false, - bool MustBeOpen = false, - bool MustBeClosed = false) : IRequest; - -public sealed record GetTicketByUserIdQuery( - ulong UserId, - bool AllowNull = false, - bool MustBeOpen = false, - bool MustBeClosed = false) : IRequest; - -public sealed record GetTicketListByTypeQuery(Guid TicketTypeId) : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/CheckActiveTicketQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/CheckActiveTicketQuery.cs new file mode 100644 index 00000000..c4260373 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/CheckActiveTicketQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record CheckActiveTicketQuery(Guid TicketId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/CheckTicketTypeExistsQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/CheckTicketTypeExistsQuery.cs new file mode 100644 index 00000000..6b704221 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/CheckTicketTypeExistsQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record CheckTicketTypeExistsQuery(string NameOrKey) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketByUserIdQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketByUserIdQuery.cs new file mode 100644 index 00000000..f82325c5 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketByUserIdQuery.cs @@ -0,0 +1,9 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketByUserIdQuery( + ulong UserId, + bool AllowNull = false, + bool MustBeOpen = false, + bool MustBeClosed = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketListByTypeQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketListByTypeQuery.cs new file mode 100644 index 00000000..7fb64e10 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketListByTypeQuery.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketListByTypeQuery(Guid TicketTypeId) : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketQuery.cs new file mode 100644 index 00000000..f08a295a --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketQuery.cs @@ -0,0 +1,9 @@ +using MediatR; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketQuery( + Guid Id, + bool AllowNull = false, + bool MustBeOpen = false, + bool MustBeClosed = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeByChannelIdQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeByChannelIdQuery.cs new file mode 100644 index 00000000..5a94c757 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeByChannelIdQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketTypeByChannelIdQuery( + ulong ChannelId, + bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeBySearchQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeBySearchQuery.cs new file mode 100644 index 00000000..488ee20c --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeBySearchQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketTypeBySearchQuery( + string NameOrKey, + bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeListQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeListQuery.cs new file mode 100644 index 00000000..bf284d0d --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeListQuery.cs @@ -0,0 +1,6 @@ +using MediatR; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketTypeListQuery(bool OnlyActive = false) : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeQuery.cs b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeQuery.cs new file mode 100644 index 00000000..a33dca61 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Queries/GetTicketTypeQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.Ticket.Queries; + +public sealed record GetTicketTypeQuery( + Guid Id, + bool AllowNull = false) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Services/TicketAttachmentDownloadService.cs b/src/Modmail.NET/Features/Ticket/Services/TicketAttachmentDownloadService.cs similarity index 73% rename from src/Modmail.NET/Services/TicketAttachmentDownloadService.cs rename to src/Modmail.NET/Features/Ticket/Services/TicketAttachmentDownloadService.cs index 60934d0f..e7a5e15c 100644 --- a/src/Modmail.NET/Services/TicketAttachmentDownloadService.cs +++ b/src/Modmail.NET/Features/Ticket/Services/TicketAttachmentDownloadService.cs @@ -1,9 +1,13 @@ +using Modmail.NET.Common.Static; using Serilog; -namespace Modmail.NET.Services; +namespace Modmail.NET.Features.Ticket.Services; public class TicketAttachmentDownloadService { + public const string AttachmentDownloadDirectory = "AttachmentDownloads"; + public const int HttpClientDownloadTimeoutSeconds = 90; + private readonly IHttpClientFactory _httpClientFactory; public TicketAttachmentDownloadService(IHttpClientFactory httpClientFactory) { @@ -13,13 +17,13 @@ public TicketAttachmentDownloadService(IHttpClientFactory httpClientFactory) { public async Task Handle(Guid attachmentId, string url, string fileExtension) { if (attachmentId == Guid.Empty) throw new ArgumentNullException(nameof(attachmentId)); - if (!Directory.Exists(Const.AttachmentDownloadDirectory)) Directory.CreateDirectory(Const.AttachmentDownloadDirectory); + if (!Directory.Exists(AttachmentDownloadDirectory)) Directory.CreateDirectory(AttachmentDownloadDirectory); - var filePath = Path.Combine(Const.AttachmentDownloadDirectory, $"{attachmentId}.{fileExtension.Trim().Trim('.')}"); + var filePath = Path.Combine(AttachmentDownloadDirectory, $"{attachmentId}.{fileExtension.Trim().Trim('.')}"); if (Path.Exists(filePath)) throw new InvalidOperationException("File in path already exists: " + filePath); var client = _httpClientFactory.CreateClient(); - client.Timeout = TimeSpan.FromSeconds(Const.HttpClientDownloadTimeoutSeconds); + client.Timeout = TimeSpan.FromSeconds(HttpClientDownloadTimeoutSeconds); try { using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)) { diff --git a/src/Modmail.NET/Features/Ticket/Services/TicketMessage.cs b/src/Modmail.NET/Features/Ticket/Services/TicketMessage.cs new file mode 100644 index 00000000..b7befe5c --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Services/TicketMessage.cs @@ -0,0 +1,180 @@ +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Modmail.NET.Abstract; +using Modmail.NET.Common.Aspects; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Utils; +using Modmail.NET.Features.Blacklist.Queries; +using Modmail.NET.Features.Blacklist.Static; +using Modmail.NET.Features.Ticket.Commands; +using Modmail.NET.Features.Ticket.Queries; +using Serilog; + +namespace Modmail.NET.Features.Ticket.Services; + +public class TicketMessage : MemoryQueueBase +{ + private readonly IOptions _options; + private readonly IServiceScopeFactory _scopeFactory; + + public TicketMessage( + IServiceScopeFactory scopeFactory, + IOptions options + ) : base(TimeSpan.FromMinutes(15)) { + _scopeFactory = scopeFactory; + _options = options; + } + + protected override async Task Handle(ulong userId, MessageCreatedEventArgs args) { + if (args.Message.Content.StartsWith(_options.Value.BotPrefix)) { + Log.Debug( + "[{Source}] Ignoring message due to bot prefix. UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + userId, + args.Message.Content + ); + return; + } + + if (args.Channel.IsPrivate) + await HandlePrivateTicketMessageAsync(args.Message, args.Channel, args.Author); + else + await HandleGuildTicketMessageAsync(args.Message, args.Channel, args.Author, args.Guild); + } + + [PerformanceLoggerAspect] + private async Task HandlePrivateTicketMessageAsync( + DiscordMessage message, + DiscordChannel channel, + DiscordUser user + ) { + Log.Debug( + "[{Source}] Handling private ticket message. UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + user.Id, + message.Content + ); + + using var scope = _scopeFactory.CreateScope(); + var sender = scope.ServiceProvider.GetRequiredService(); + + try { + if (await sender.Send(new CheckUserBlacklistStatusQuery(user.Id))) { + Log.Information( + "[{Source}] User is blacklisted, sending rejection message. UserId: {UserId}", + nameof(TicketMessage), + user.Id + ); + await channel.SendMessageAsync(BlacklistBotMessages.YouHaveBeenBlacklisted()); + return; + } + + var activeTicket = await sender.Send(new GetTicketByUserIdQuery(user.Id, true, true)); + if (activeTicket is not null) { + Log.Debug( + "[{Source}] Active ticket found, processing user message. TicketId: {TicketId}, UserId: {UserId}", + nameof(TicketMessage), + activeTicket.Id, + user.Id + ); + await sender.Send(new ProcessUserSentMessageCommand(activeTicket.Id, message, channel)); + } + else { + Log.Debug( + "[{Source}] No active ticket found, creating a new ticket. UserId: {UserId}", + nameof(TicketMessage), + user.Id + ); + await sender.Send(new ProcessCreateNewTicketCommand(user, channel, message)); + } + + Log.Information( + "[{Source}] Processed private message. UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + user.Id, + message.Content + ); + } + catch (ModmailBotException ex) { + Log.Warning( + ex, + "[{Source}] ModmailBotException: Error processing private message. UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + user.Id, + message.Content + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Unexpected error processing private message. UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + user.Id, + message.Content + ); + } + } + + [PerformanceLoggerAspect] + private async Task HandleGuildTicketMessageAsync( + DiscordMessage message, + DiscordChannel channel, + DiscordUser modUser, + DiscordGuild guild + ) { + Log.Debug( + "[{Source}] Handling guild ticket message. ChannelId: {ChannelId}, UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + channel.Id, + modUser.Id, + message.Content + ); + + using var scope = _scopeFactory.CreateScope(); + var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(channel.Topic); + if (ticketId == Guid.Empty) { + Log.Warning( + "[{Source}] Invalid ticket id, ignoring message. ChannelId: {ChannelId}, Topic: {Topic}", + nameof(TicketMessage), + channel.Id, + channel.Topic + ); + return; + } + + try { + var sender = scope.ServiceProvider.GetRequiredService(); + await sender.Send(new ProcessModSendMessageCommand(ticketId, modUser, message, channel, guild)); + Log.Information( + "[{Source}] Processed guild message. TicketId: {TicketId}, UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + ticketId, + modUser.Id, + message.Content + ); + } + catch (ModmailBotException ex) { + Log.Warning( + ex, + "[{Source}] ModmailBotException: Error processing guild message. TicketId: {TicketId}, UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + ticketId, + modUser.Id, + message.Content + ); + } + catch (Exception ex) { + Log.Error( + ex, + "[{Source}] Unexpected error processing guild message. TicketId: {TicketId}, UserId: {UserId}, Message: {Message}", + nameof(TicketMessage), + ticketId, + modUser.Id, + message.Content + ); + } + } +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Static/TicketConstants.cs b/src/Modmail.NET/Features/Ticket/Static/TicketConstants.cs new file mode 100644 index 00000000..96c04f3d --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Static/TicketConstants.cs @@ -0,0 +1,17 @@ +namespace Modmail.NET.Features.Ticket.Static; + +public static class TicketConstants +{ + public const string TicketNameTemplate = "ticket-{0}"; + public const string HighPriorityEmoji = "🔴"; + public const string NormalPriorityEmoji = ""; + public const string LowPriorityEmoji = "🟢"; + + public const int TicketTimeoutMinAllowedHours = 12; + public const int TicketTimeoutMaxAllowedHours = 24 * 7 * 4; + + public const int TicketDataDeleteWaitDaysMin = 1; + public const int TicketDataDeleteWaitDaysMax = 365; + + public const string ProcessedReactionDiscordEmojiUnicode = "✅"; +} \ No newline at end of file diff --git a/src/Modmail.NET/Features/Ticket/Static/TicketMessageChangeStatus.cs b/src/Modmail.NET/Features/Ticket/Static/TicketMessageChangeStatus.cs new file mode 100644 index 00000000..e0e917a1 --- /dev/null +++ b/src/Modmail.NET/Features/Ticket/Static/TicketMessageChangeStatus.cs @@ -0,0 +1,8 @@ +namespace Modmail.NET.Features.Ticket.Static; + +public enum TicketMessageChangeStatus +{ + None, + Updated, + Deleted +} \ No newline at end of file diff --git a/src/Modmail.NET/Static/TicketPriority.cs b/src/Modmail.NET/Features/Ticket/Static/TicketPriority.cs similarity index 82% rename from src/Modmail.NET/Static/TicketPriority.cs rename to src/Modmail.NET/Features/Ticket/Static/TicketPriority.cs index e83f0408..7739c3ec 100644 --- a/src/Modmail.NET/Static/TicketPriority.cs +++ b/src/Modmail.NET/Features/Ticket/Static/TicketPriority.cs @@ -1,6 +1,6 @@ using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers; -namespace Modmail.NET.Static; +namespace Modmail.NET.Features.Ticket.Static; public enum TicketPriority { diff --git a/src/Modmail.NET/Features/TicketType/Commands.cs b/src/Modmail.NET/Features/TicketType/Commands.cs deleted file mode 100644 index e8596db1..00000000 --- a/src/Modmail.NET/Features/TicketType/Commands.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MediatR; -using Modmail.NET.Abstract; -using Modmail.NET.Attributes; - -namespace Modmail.NET.Features.TicketType; - -[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] -public sealed record ProcessUpdateTicketTypeCommand(ulong AuthorizedUserId, Entities.TicketType TicketType) : IRequest; - -[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] -public sealed record ProcessCreateTicketTypeCommand( - ulong AuthorizedUserId, - string Name, - string Emoji, - string Description, - long Order, - string EmbedMessageTitle, - string EmbedMessageContent) : IRequest; - -[PermissionCheck(nameof(AuthPolicy.ManageTicketTypes))] -public sealed record ProcessRemoveTicketTypeCommand(ulong AuthorizedUserId, Guid Id) : IRequest, - IPermissionCheck; \ No newline at end of file diff --git a/src/Modmail.NET/Features/TicketType/Queries.cs b/src/Modmail.NET/Features/TicketType/Queries.cs deleted file mode 100644 index 3fdb8425..00000000 --- a/src/Modmail.NET/Features/TicketType/Queries.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MediatR; - -namespace Modmail.NET.Features.TicketType; - -public sealed record GetTicketTypeQuery( - Guid Id, - bool AllowNull = false) : IRequest; - -public sealed record GetTicketTypeBySearchQuery( - string NameOrKey, - bool AllowNull = false) : IRequest; - -public sealed record GetTicketTypeByChannelIdQuery( - ulong ChannelId, - bool AllowNull = false) : IRequest; - -public sealed record GetTicketTypeListQuery(bool OnlyActive = false) : IRequest>; - -public sealed record CheckTicketTypeExistsQuery(string NameOrKey) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/UserInfo/Commands.cs b/src/Modmail.NET/Features/User/Commands/UpdateDiscordUserCommand.cs similarity index 63% rename from src/Modmail.NET/Features/UserInfo/Commands.cs rename to src/Modmail.NET/Features/User/Commands/UpdateDiscordUserCommand.cs index afe79e9a..5fe3349a 100644 --- a/src/Modmail.NET/Features/UserInfo/Commands.cs +++ b/src/Modmail.NET/Features/User/Commands/UpdateDiscordUserCommand.cs @@ -1,7 +1,7 @@ using DSharpPlus.Entities; using MediatR; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; -namespace Modmail.NET.Features.UserInfo; +namespace Modmail.NET.Features.User.Commands; public sealed record UpdateDiscordUserCommand(DiscordUser DiscordUser) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoDictHandler.cs b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoDictHandler.cs similarity index 86% rename from src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoDictHandler.cs rename to src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoDictHandler.cs index 56d30417..e4ae5197 100644 --- a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoDictHandler.cs +++ b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoDictHandler.cs @@ -1,9 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.User.Queries; -namespace Modmail.NET.Features.UserInfo.Handlers; +namespace Modmail.NET.Features.User.Handlers; public class GetDiscordUserInfoDictHandler : IRequestHandler> { diff --git a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoHandler.cs b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoHandler.cs similarity index 85% rename from src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoHandler.cs rename to src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoHandler.cs index 4d84b1f9..ec79298e 100644 --- a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoHandler.cs +++ b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoHandler.cs @@ -1,11 +1,14 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Modmail.NET.Common.Exceptions; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Exceptions; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.User.Commands; +using Modmail.NET.Features.User.Queries; +using Modmail.NET.Language; using NotFoundException = DSharpPlus.Exceptions.NotFoundException; -namespace Modmail.NET.Features.UserInfo.Handlers; +namespace Modmail.NET.Features.User.Handlers; public class GetDiscordUserInfoHandler : IRequestHandler { diff --git a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoListHandler.cs b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoListHandler.cs similarity index 81% rename from src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoListHandler.cs rename to src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoListHandler.cs index 01cfa16e..59dfe64b 100644 --- a/src/Modmail.NET/Features/UserInfo/Handlers/GetDiscordUserInfoListHandler.cs +++ b/src/Modmail.NET/Features/User/Handlers/GetDiscordUserInfoListHandler.cs @@ -1,9 +1,10 @@ using MediatR; using Microsoft.EntityFrameworkCore; using Modmail.NET.Database; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.User.Queries; -namespace Modmail.NET.Features.UserInfo.Handlers; +namespace Modmail.NET.Features.User.Handlers; public class GetDiscordUserInfoListHandler : IRequestHandler> { diff --git a/src/Modmail.NET/Features/UserInfo/Handlers/UpdateDiscordUserHandler.cs b/src/Modmail.NET/Features/User/Handlers/UpdateDiscordUserHandler.cs similarity index 89% rename from src/Modmail.NET/Features/UserInfo/Handlers/UpdateDiscordUserHandler.cs rename to src/Modmail.NET/Features/User/Handlers/UpdateDiscordUserHandler.cs index 18babb87..cc9e3960 100644 --- a/src/Modmail.NET/Features/UserInfo/Handlers/UpdateDiscordUserHandler.cs +++ b/src/Modmail.NET/Features/User/Handlers/UpdateDiscordUserHandler.cs @@ -1,9 +1,10 @@ using MediatR; +using Modmail.NET.Common.Utils; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Utils; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.User.Commands; -namespace Modmail.NET.Features.UserInfo.Handlers; +namespace Modmail.NET.Features.User.Handlers; public class UpdateDiscordUserHandler : IRequestHandler { diff --git a/src/Modmail.NET/Jobs/DiscordUserInfoSyncJob.cs b/src/Modmail.NET/Features/User/Jobs/DiscordUserInfoSyncJob.cs similarity index 92% rename from src/Modmail.NET/Jobs/DiscordUserInfoSyncJob.cs rename to src/Modmail.NET/Features/User/Jobs/DiscordUserInfoSyncJob.cs index 36276219..bb3fead6 100644 --- a/src/Modmail.NET/Jobs/DiscordUserInfoSyncJob.cs +++ b/src/Modmail.NET/Features/User/Jobs/DiscordUserInfoSyncJob.cs @@ -3,12 +3,12 @@ using Microsoft.Extensions.DependencyInjection; using Modmail.NET.Abstract; using Modmail.NET.Database; -using Modmail.NET.Entities; -using Modmail.NET.Features.Bot; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Database.Entities; +using Modmail.NET.Features.DiscordBot.Queries; +using Modmail.NET.Features.User.Queries; using Serilog; -namespace Modmail.NET.Jobs; +namespace Modmail.NET.Features.User.Jobs; public class DiscordUserInfoSyncJob : HangfireRecurringJobBase { diff --git a/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoDictQuery.cs b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoDictQuery.cs new file mode 100644 index 00000000..b8f18d72 --- /dev/null +++ b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoDictQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.User.Queries; + +[CachePolicy("GetDiscordUserInfoQuery", 5)] +public sealed record GetDiscordUserInfoDictQuery : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoListQuery.cs b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoListQuery.cs new file mode 100644 index 00000000..5c3d8b18 --- /dev/null +++ b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoListQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.User.Queries; + +[CachePolicy("GetDiscordUserInfoQuery", 5)] +public sealed record GetDiscordUserInfoListQuery : IRequest>; \ No newline at end of file diff --git a/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoQuery.cs b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoQuery.cs new file mode 100644 index 00000000..ec6ed013 --- /dev/null +++ b/src/Modmail.NET/Features/User/Queries/GetDiscordUserInfoQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using Modmail.NET.Attributes; +using Modmail.NET.Database.Entities; + +namespace Modmail.NET.Features.User.Queries; + +[CachePolicy("GetDiscordUserInfoQuery", 60)] +public sealed record GetDiscordUserInfoQuery(ulong UserId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/Features/UserInfo/Queries.cs b/src/Modmail.NET/Features/UserInfo/Queries.cs deleted file mode 100644 index 84e0a986..00000000 --- a/src/Modmail.NET/Features/UserInfo/Queries.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MediatR; -using Modmail.NET.Attributes; -using Modmail.NET.Entities; - -namespace Modmail.NET.Features.UserInfo; - -[CachePolicy("GetDiscordUserInfoQuery", 5)] -public sealed record GetDiscordUserInfoListQuery : IRequest>; - -[CachePolicy("GetDiscordUserInfoQuery", 5)] -public sealed record GetDiscordUserInfoDictQuery : IRequest>; - -[CachePolicy("GetDiscordUserInfoQuery", 60)] -public sealed record GetDiscordUserInfoQuery(ulong UserId) : IRequest; \ No newline at end of file diff --git a/src/Modmail.NET/GlobalUsing.cs b/src/Modmail.NET/GlobalUsing.cs index a506b0fd..5f282702 100644 --- a/src/Modmail.NET/GlobalUsing.cs +++ b/src/Modmail.NET/GlobalUsing.cs @@ -1,2 +1 @@ -global using Modmail.NET.Language; -global using Modmail.NET.Static; \ No newline at end of file + \ No newline at end of file diff --git a/src/Modmail.NET/Language/LangKeys.cs b/src/Modmail.NET/Language/LangKeys.cs index 6a8ee87c..696bbee7 100644 --- a/src/Modmail.NET/Language/LangKeys.cs +++ b/src/Modmail.NET/Language/LangKeys.cs @@ -203,5 +203,15 @@ public enum LangKeys ErrorNotFound, NotJoinedMainServer, TicketPriority, - Transcript + Transcript, + MessageEdited, + Edited, + NoFeedbackProvided, + FeedbackAlreadySubmitted, + Tag, + TagWithSameNameAlreadyExists, + TagDoesntExists, + TagCreatedSuccessfully, + TagRemovedSuccessfully, + TagUpdatedSuccessfully } \ No newline at end of file diff --git a/src/Modmail.NET/Language/LangProvider.cs b/src/Modmail.NET/Language/LangProvider.cs index 8ec39aed..23690add 100644 --- a/src/Modmail.NET/Language/LangProvider.cs +++ b/src/Modmail.NET/Language/LangProvider.cs @@ -1,6 +1,6 @@ using System.Text; using Microsoft.Extensions.Options; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; using Newtonsoft.Json; using Serilog; diff --git a/src/Modmail.NET/Language/StringInteropProvider.cs b/src/Modmail.NET/Language/StringInteropProvider.cs index 3a507eee..23828673 100644 --- a/src/Modmail.NET/Language/StringInteropProvider.cs +++ b/src/Modmail.NET/Language/StringInteropProvider.cs @@ -1,6 +1,6 @@ using System.Text; using DSharpPlus.Entities; -using Modmail.NET.Entities; +using Modmail.NET.Database.Entities; namespace Modmail.NET.Language; diff --git a/src/Modmail.NET/Mappers/TicketDtoMapper.cs b/src/Modmail.NET/Mappers/TicketDtoMapper.cs deleted file mode 100644 index 6792beeb..00000000 --- a/src/Modmail.NET/Mappers/TicketDtoMapper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Modmail.NET.Entities; -using Modmail.NET.Models.Dto; -using Riok.Mapperly.Abstractions; - -namespace Modmail.NET.Mappers; - -[Mapper] -public static partial class TicketDtoMapper -{ - public static partial IQueryable ProjectToDto(this IQueryable queryable); - - public static partial TicketDto ToDto(this Ticket entity); -} \ No newline at end of file diff --git a/src/Modmail.NET/Models/Dto/DiscordTicketMessageDto.cs b/src/Modmail.NET/Models/Dto/DiscordTicketMessageDto.cs deleted file mode 100644 index 6f68a97b..00000000 --- a/src/Modmail.NET/Models/Dto/DiscordTicketMessageDto.cs +++ /dev/null @@ -1,6 +0,0 @@ -using DSharpPlus; -using DSharpPlus.EventArgs; - -namespace Modmail.NET.Models.Dto; - -public sealed record DiscordTicketMessageDto(DiscordClient Sender, MessageCreatedEventArgs Args); \ No newline at end of file diff --git a/src/Modmail.NET/Modmail.NET.csproj b/src/Modmail.NET/Modmail.NET.csproj index 63bdd31d..59ea81ac 100644 --- a/src/Modmail.NET/Modmail.NET.csproj +++ b/src/Modmail.NET/Modmail.NET.csproj @@ -6,7 +6,7 @@ disable false 13 - 2.2.0 + 2.3.0 @@ -17,9 +17,9 @@ - - - + + + diff --git a/src/Modmail.NET/ModmailBot.cs b/src/Modmail.NET/ModmailBot.cs index e664ded7..23992666 100644 --- a/src/Modmail.NET/ModmailBot.cs +++ b/src/Modmail.NET/ModmailBot.cs @@ -2,9 +2,11 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Modmail.NET.Exceptions; -using Modmail.NET.Features.Guild; -using Modmail.NET.Features.UserInfo; +using Modmail.NET.Common.Exceptions; +using Modmail.NET.Common.Static; +using Modmail.NET.Features.Guild.Commands; +using Modmail.NET.Features.Guild.Queries; +using Modmail.NET.Features.User.Commands; using Serilog; namespace Modmail.NET; diff --git a/src/Modmail.NET/ModmailEventHandlers.cs b/src/Modmail.NET/ModmailEventHandlers.cs deleted file mode 100644 index 18862de9..00000000 --- a/src/Modmail.NET/ModmailEventHandlers.cs +++ /dev/null @@ -1,256 +0,0 @@ -using DSharpPlus; -using DSharpPlus.Entities; -using DSharpPlus.Entities.AuditLogs; -using DSharpPlus.EventArgs; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Modmail.NET.Abstract; -using Modmail.NET.Aspects; -using Modmail.NET.Features.Ticket; -using Modmail.NET.Features.UserInfo; -using Modmail.NET.Models.Dto; -using Modmail.NET.Queues; -using Modmail.NET.Utils; -using Serilog; - -namespace Modmail.NET; - -public class ModmailEventHandlers -{ - public static async Task InteractionCreated(DiscordClient client, InteractionCreatedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.Interaction?.User)); - } - - public static async Task OnUserUpdated(DiscordClient client, UserUpdatedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args.UserAfter)); - } - - public static async Task OnUserSettingsUpdated(DiscordClient client, UserSettingsUpdatedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args.User)); - } - - public static async Task OnMessageDeleted(DiscordClient client, MessageDeletedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.Message.Author)); - } - - public static async Task OnMessageReactionAdded(DiscordClient client, MessageReactionAddedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.User)); - } - - public static async Task OnMessageUpdated(DiscordClient client, MessageUpdatedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.Author)); - } - - public static async Task OnGuildMemberAdded(DiscordClient client, GuildMemberAddedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.Member)); - } - - public static async Task OnGuildMemberRemoved(DiscordClient client, GuildMemberRemovedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args?.Member)); - } - - public static async Task OnMessageCreated(DiscordClient client, MessageCreatedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args.Message.Author)); - if (args.Message.Author?.IsBot == true) return; - if (args.Message.IsTTS) return; - - var ticketMessageQueue = scope.ServiceProvider.GetRequiredService(); - await ticketMessageQueue.Enqueue(args.Author.Id, new DiscordTicketMessageDto(client, args)); - } - - public static async Task OnGuildBanAdded(DiscordClient client, GuildBanAddedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args.Member)); - } - - public static async Task OnGuildBanRemoved(DiscordClient client, GuildBanRemovedEventArgs args) { - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new UpdateDiscordUserCommand(args.Member)); - } - - [PerformanceLoggerAspect] - public static async Task OnChannelDeleted(DiscordClient client, ChannelDeletedEventArgs args) { - const string logMessage = $"[{nameof(OnChannelDeleted)}]({{ChannelId}})"; - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - var langData = scope.ServiceProvider.GetRequiredService(); - var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(args.Channel.Topic); - if (ticketId != Guid.Empty) - try { - var auditLogEntry = await args.Guild.GetAuditLogsAsync(1, null, DiscordAuditLogActionType.ChannelDelete).FirstOrDefaultAsync(); - var user = auditLogEntry?.UserResponsible ?? client.CurrentUser; - await sender.Send(new UpdateDiscordUserCommand(user)); - var ticket = await sender.Send(new GetTicketQuery(ticketId, true)); - if (ticket is not null) { - if (ticket.ClosedDateUtc.HasValue) return; // Ticket is already closed - await sender.Send(new ProcessCloseTicketCommand(ticketId, user.Id, langData.GetTranslation(LangKeys.ChannelWasDeleted), args.Channel)); - Log.Information(logMessage, args.Channel.Id); - } - } - catch (BotExceptionBase ex) { - Log.Warning(ex, logMessage, args.Channel.Id); - } - catch (Exception ex) { - Log.Error(ex, logMessage, args.Channel.Id); - } - } - - - [PerformanceLoggerAspect] - public static async Task ComponentInteractionCreated(DiscordClient client, ComponentInteractionCreatedEventArgs args) { - const string logMessage = $"[{nameof(ComponentInteractionCreated)}]({{CustomId}},{{UserId}},{{ChannelId}},{{InteractionId}},{{MessageId}})"; - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - - try { - await sender.Send(new UpdateDiscordUserCommand(args.User)); - var interaction = args.Interaction; - var key = interaction?.Data?.CustomId; - var (interactionName, parameters) = UtilInteraction.ParseKey(key); - var messageId = args.Message.Id; - - switch (interactionName) { - case "star": { - //feedback process show modal - var starParam = parameters[0]; - var ticketIdParam = parameters[1]; - - var starCount = int.Parse(starParam); - var ticketId = Guid.Parse(ticketIdParam); - - var feedbackModal = Modals.CreateFeedbackModal(starCount, ticketId, messageId); - - await args.Interaction.CreateResponseAsync(DiscordInteractionResponseType.Modal, feedbackModal); - Log.Information(logMessage, - args.Interaction?.Data?.CustomId, - args.User?.Id, - args.Channel?.Id, - args.Interaction?.Id, - args.Message?.Id); - break; - } - case "ticket_type": { - //for user ticket change type allowed only once for user - await args.Interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage); - var ticketIdParam = parameters[0]; - var ticketId = Guid.Parse(ticketIdParam); - var selectedTypeKey = args.Values.FirstOrDefault(); - if (string.IsNullOrEmpty(selectedTypeKey)) break; - - await sender.Send(new ProcessChangeTicketTypeCommand(ticketId, selectedTypeKey, null, args.Channel, args.Message, args.User.Id)); - Log.Information(logMessage, - args.Interaction?.Data?.CustomId, - args.User?.Id, - args.Channel?.Id, - args.Interaction?.Id, - args.Message?.Id); - break; - } - case "close_ticket": // This must stay due to deprecation and support for existing tickets (v2.0 beta) - case "close_ticket_with_reason": { - var ticketIdParam = parameters[0]; - var ticketId = Guid.Parse(ticketIdParam); - var modal = Modals.CreateCloseTicketWithReasonModal(ticketId); - await args.Interaction.CreateResponseAsync(DiscordInteractionResponseType.Modal, modal); - - Log.Information(logMessage, - args.Interaction?.Data?.CustomId, - args.User?.Id, - args.Channel?.Id, - args.Interaction?.Id, - args.Message?.Id); - break; - } - } - } - - - catch (BotExceptionBase ex) { - Log.Warning(ex, - logMessage, - args.Interaction?.Data?.CustomId, - args.User?.Id, - args.Channel?.Id, - args.Interaction?.Id, - args.Message?.Id); - } - catch (Exception ex) { - Log.Fatal(ex, - logMessage, - args.Interaction?.Data?.CustomId, - args.User?.Id, - args.Channel?.Id, - args.Interaction?.Id, - args.Message?.Id); - } - } - - [PerformanceLoggerAspect] - public static async Task ModalSubmitted(DiscordClient client, ModalSubmittedEventArgs args) { - const string logMessage = $"[{nameof(ModalSubmitted)}]({{CustomId}},{{InteractionId}})"; - await args.Interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, - new DiscordInteractionResponseBuilder().AsEphemeral().WithContent(LangProvider.This.GetTranslation(LangKeys.ThankYouForFeedback))); - var scope = client.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - try { - await sender.Send(new UpdateDiscordUserCommand(args.Interaction.User)); - - // var interaction = args.Interaction; - var id = args.Interaction.Data.CustomId; - var (interactionName, parameters) = UtilInteraction.ParseKey(id); - switch (interactionName) { - case "feedback": { - var textInput = args.Values["feedback"]; - - var starParam = parameters[0]; - var ticketIdParam = parameters[1]; - var messageIdParam = parameters[2]; - - var starCount = int.Parse(starParam); - var ticketId = Guid.Parse(ticketIdParam); - var feedbackMessageId = ulong.Parse(messageIdParam); - - var feedbackMessage = await args.Interaction.Channel.GetMessageAsync(feedbackMessageId); - await sender.Send(new ProcessAddFeedbackCommand(ticketId, starCount, textInput, feedbackMessage)); - Log.Information(logMessage, args.Interaction.Data.CustomId, args.Interaction.Id); - break; - } - case "close_ticket_with_reason": { - var textInput = args.Values["reason"]; - var ticketIdParam = parameters[0]; - var ticketId = Guid.Parse(ticketIdParam); - await sender.Send(new ProcessCloseTicketCommand(ticketId, args.Interaction.User.Id, textInput, args.Interaction.Channel)); - Log.Information(logMessage, args.Interaction.Data.CustomId, args.Interaction.Id); - break; - } - } - } - - catch (BotExceptionBase ex) { - Log.Warning(ex, logMessage, args.Interaction.Data.CustomId, args.Interaction.Id); - } - catch (Exception ex) { - Log.Error(ex, logMessage, args.Interaction.Data.CustomId, args.Interaction.Id); - } - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Pipeline/CachingPipelineBehavior.cs b/src/Modmail.NET/Pipeline/CachingPipelineBehavior.cs index 4c3ceebf..e1e34f19 100644 --- a/src/Modmail.NET/Pipeline/CachingPipelineBehavior.cs +++ b/src/Modmail.NET/Pipeline/CachingPipelineBehavior.cs @@ -2,7 +2,7 @@ using MediatR; using Microsoft.Extensions.Caching.Memory; using Modmail.NET.Attributes; -using Modmail.NET.Utils; +using Modmail.NET.Common.Utils; using Serilog; namespace Modmail.NET.Pipeline; diff --git a/src/Modmail.NET/Pipeline/LoggerPipelineBehavior.cs b/src/Modmail.NET/Pipeline/LoggerPipelineBehavior.cs index fba66f36..f3e1ff7b 100644 --- a/src/Modmail.NET/Pipeline/LoggerPipelineBehavior.cs +++ b/src/Modmail.NET/Pipeline/LoggerPipelineBehavior.cs @@ -1,5 +1,5 @@ using MediatR; -using Modmail.NET.Abstract; +using Modmail.NET.Common.Exceptions; using Serilog; namespace Modmail.NET.Pipeline; @@ -13,7 +13,7 @@ public async Task Handle(TRequest request, RequestHandlerDelegate> AutoCompleteAsync(AutoCompleteContext context) { - const string cacheKey = "TeamProvider.Provider.AutoComplete"; - var cache = context.ServiceProvider.GetRequiredService(); - return await cache.GetOrCreateAsync(cacheKey, Get, new MemoryCacheEntryOptions { - AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60) - }); - - async Task> Get(ICacheEntry cacheEntry) { - var scope = context.ServiceProvider.CreateScope(); - var sender = scope.ServiceProvider.GetRequiredService(); - var bot = scope.ServiceProvider.GetRequiredService(); - var teamsDbList = await sender.Send(new GetTeamListQuery(bot.Client.CurrentUser.Id)); - var teams = teamsDbList.Select(x => new DiscordAutoCompleteChoice(x.Name, x.Name)); - return await Task.FromResult(teams.AsEnumerable()); - } - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Queues/TicketMessageQueue.cs b/src/Modmail.NET/Queues/TicketMessageQueue.cs deleted file mode 100644 index b804aa1b..00000000 --- a/src/Modmail.NET/Queues/TicketMessageQueue.cs +++ /dev/null @@ -1,80 +0,0 @@ -using DSharpPlus.Entities; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Modmail.NET.Abstract; -using Modmail.NET.Aspects; -using Modmail.NET.Features.Blacklist; -using Modmail.NET.Features.Ticket; -using Modmail.NET.Models.Dto; -using Modmail.NET.Utils; -using Serilog; - -namespace Modmail.NET.Queues; - -public class TicketMessageQueue : BaseQueue -{ - private readonly IOptions _options; - private readonly IServiceScopeFactory _scopeFactory; - - public TicketMessageQueue(IServiceScopeFactory scopeFactory, - IOptions options) : base(TimeSpan.FromMinutes(15)) { - _scopeFactory = scopeFactory; - _options = options; - } - - protected override async Task Handle(ulong userId, DiscordTicketMessageDto dto) { - if (dto.Args.Message.Content.StartsWith(_options.Value.BotPrefix)) - return; - - if (dto.Args.Channel.IsPrivate) - await HandlePrivateTicketMessageAsync(dto.Args.Message, dto.Args.Channel, dto.Args.Author); - else - await HandleGuildTicketMessageAsync(dto.Args.Message, dto.Args.Channel, dto.Args.Author, dto.Args.Guild); - } - - [PerformanceLoggerAspect] - private async Task HandlePrivateTicketMessageAsync(DiscordMessage message, DiscordChannel channel, DiscordUser user) { - using var scope = _scopeFactory.CreateScope(); - try { - var sender = scope.ServiceProvider.GetRequiredService(); - if (await sender.Send(new CheckUserBlacklistStatusQuery(user.Id))) { - await channel.SendMessageAsync(UserResponses.YouHaveBeenBlacklisted()); - return; - } - - var activeTicket = await sender.Send(new GetTicketByUserIdQuery(user.Id, true, true)); - if (activeTicket is not null) - await sender.Send(new ProcessUserSentMessageCommand(activeTicket.Id, message, channel)); - else - await sender.Send(new ProcessCreateNewTicketCommand(user, channel, message)); - - Log.Information("[TicketMessageQueue] Processed private message from {UserId}: {Message}", user.Id, message.Content); - } - catch (BotExceptionBase ex) { - Log.Warning(ex, "[TicketMessageQueue] Error processing private message from {UserId}: {Message}", user.Id, message.Content); - } - catch (Exception ex) { - Log.Error(ex, "[TicketMessageQueue] Unexpected error processing private message from {UserId}: {Message}", user.Id, message.Content); - } - } - - [PerformanceLoggerAspect] - private async Task HandleGuildTicketMessageAsync(DiscordMessage message, DiscordChannel channel, DiscordUser modUser, DiscordGuild guild) { - using var scope = _scopeFactory.CreateScope(); - try { - var ticketId = UtilChannelTopic.GetTicketIdFromChannelTopic(channel.Topic); - if (ticketId == Guid.Empty) return; - - var sender = scope.ServiceProvider.GetRequiredService(); - await sender.Send(new ProcessModSendMessageCommand(ticketId, modUser, message, channel, guild)); - Log.Information("[TicketMessageQueue] Processed guild message from {UserId}: {Message}", modUser.Id, message.Content); - } - catch (BotExceptionBase ex) { - Log.Warning(ex, "[TicketMessageQueue] Error processing guild message from {UserId}: {Message}", modUser.Id, message.Content); - } - catch (Exception ex) { - Log.Error(ex, "[TicketMessageQueue] Unexpected error processing guild message from {UserId}: {Message}", modUser.Id, message.Content); - } - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Dependency.cs b/src/Modmail.NET/ServiceLocator.cs similarity index 98% rename from src/Modmail.NET/Dependency.cs rename to src/Modmail.NET/ServiceLocator.cs index 40a03271..d739f932 100644 --- a/src/Modmail.NET/Dependency.cs +++ b/src/Modmail.NET/ServiceLocator.cs @@ -1,6 +1,7 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Modmail.NET.Language; namespace Modmail.NET; diff --git a/src/Modmail.NET/Static/AuthPolicy.cs b/src/Modmail.NET/Static/AuthPolicy.cs deleted file mode 100644 index 025e0878..00000000 --- a/src/Modmail.NET/Static/AuthPolicy.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Ardalis.SmartEnum; - -namespace Modmail.NET.Static; - -/// -/// AuthPolicy smart enum -/// -public sealed class AuthPolicy : SmartEnum -{ - public static readonly AuthPolicy Support = new(nameof(Support), 1); - public static readonly AuthPolicy Moderator = new(nameof(Moderator), 2); - public static readonly AuthPolicy Admin = new(nameof(Admin), 3); - public static readonly AuthPolicy Owner = new(nameof(Owner), 4); - public static readonly AuthPolicy ManageTickets = new(nameof(ManageTickets), 6); - public static readonly AuthPolicy ManageTicketTypes = new(nameof(ManageTicketTypes), 7); - public static readonly AuthPolicy ManageTeams = new(nameof(ManageTeams), 8); - public static readonly AuthPolicy ManageBlacklist = new(nameof(ManageBlacklist), 9); - public static readonly AuthPolicy ManageHangfire = new(nameof(ManageHangfire), 10); - private AuthPolicy(string name, int value) : base(name, value) { } -} \ No newline at end of file diff --git a/src/Modmail.NET/Static/Const.cs b/src/Modmail.NET/Static/Const.cs deleted file mode 100644 index dbd2b937..00000000 --- a/src/Modmail.NET/Static/Const.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DSharpPlus.Entities; - -namespace Modmail.NET.Static; - -public static class Const -{ - public const string CategoryName = "Modmail"; - public const string LogChannelName = "📄modmail-logs"; - public const string TicketNameTemplate = "ticket-{0}"; - public const string HighPriorityEmoji = "🔴"; - public const string NormalPriorityEmoji = ""; - public const string LowPriorityEmoji = "🟢"; - public const int TicketTimeoutMinAllowedHours = 12; - public const int TicketTimeoutMaxAllowedHours = 24 * 7 * 4; - public const int TicketDataDeleteWaitDaysMin = 1; - public const int TicketDataDeleteWaitDaysMax = 365; - public const int StatisticsCalculateDaysMin = 30; - public const int StatisticsCalculateDaysMax = 365; - public const int DefaultStatisticsCalculateDays = 90; - public const string ThemeCookieName = "Modmail.NET.Theme"; - public const string AttachmentDownloadDirectory = "AttachmentDownloads"; - public const int HttpClientDownloadTimeoutSeconds = 90; - public const string ProcessedReactionDiscordEmojiString = ":white_check_mark:"; - public static readonly DiscordActivity DiscordActivity = new(LangProvider.This.GetTranslation(LangKeys.ModerationConcerns), DiscordActivityType.ListeningTo); -} \ No newline at end of file diff --git a/src/Modmail.NET/Static/Interactions.cs b/src/Modmail.NET/Static/Interactions.cs deleted file mode 100644 index d067b481..00000000 --- a/src/Modmail.NET/Static/Interactions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DSharpPlus.Entities; - -namespace Modmail.NET.Static; - -public static class Interactions -{ - public static DiscordInteractionResponseBuilder Error(string title, string message = "") { - return new DiscordInteractionResponseBuilder().AddEmbed(Embeds.Error(title, message)); - } - - public static DiscordInteractionResponseBuilder Success(string title, string message = "") { - return new DiscordInteractionResponseBuilder().AddEmbed(Embeds.Success(title, message)); - } - - public static DiscordInteractionResponseBuilder Info(string title, string message = "") { - return new DiscordInteractionResponseBuilder().AddEmbed(Embeds.Info(title, message)); - } - - public static DiscordInteractionResponseBuilder Warning(string title, string message = "") { - return new DiscordInteractionResponseBuilder().AddEmbed(Embeds.Warning(title, message)); - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Static/TicketResponses.cs b/src/Modmail.NET/Static/TicketResponses.cs deleted file mode 100644 index bbea43ff..00000000 --- a/src/Modmail.NET/Static/TicketResponses.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Text; -using DSharpPlus.Entities; -using Modmail.NET.Entities; -using Modmail.NET.Extensions; -using Modmail.NET.Models; -using Modmail.NET.Utils; - -namespace Modmail.NET.Static; - -/// -/// Contains the embed messages bot to send to ticket channels -/// -public static class TicketResponses -{ - public static DiscordMessageBuilder NewTicket(DiscordUser member, Guid ticketId, PermissionInfo[] permissionInfos) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.NewTicket.GetTranslation()) - .WithCustomTimestamp() - .WithDescription(LangKeys.NewTicketDescriptionMessage.GetTranslation()) - .WithAuthor(member.GetUsername(), iconUrl: member.AvatarUrl) - .AddField(LangKeys.User.GetTranslation(), member.Mention, true) - .AddField(LangKeys.TicketId.GetTranslation(), ticketId.ToString().ToUpper(), true) - .WithColor(Colors.TicketCreatedColor); - - var messageBuilder = new DiscordMessageBuilder() - .AddEmbed(embed) - .AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Danger, - UtilInteraction.BuildKey("close_ticket_with_reason", ticketId.ToString()), - LangKeys.CloseTicket.GetTranslation(), - emoji: new DiscordComponentEmoji("🔒")) - ); - - var sb = new StringBuilder(); - foreach (var permissionInfo in permissionInfos.Where(permissionInfo => permissionInfo.PingOnNewTicket)) sb.AppendLine(permissionInfo.GetMention()); - - messageBuilder.WithContent(sb.ToString()); - return messageBuilder; - } - - public static DiscordEmbedBuilder NoteAdded(TicketNote note, DiscordUserInfo user) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.NoteAdded.GetTranslation()) - .WithDescription(note.Content) - .WithColor(Colors.NoteAddedColor) - .WithCustomTimestamp() - .WithUserAsAuthor(user); - return embed; - } - - public static DiscordEmbedBuilder AnonymousToggled(Ticket ticket) { - var embed2 = new DiscordEmbedBuilder() - .WithTitle(ticket.Anonymous - ? LangKeys.AnonymousModOn.GetTranslation() - : LangKeys.AnonymousModOff.GetTranslation()) - .WithColor(Colors.AnonymousToggledColor) - .WithCustomTimestamp() - .WithDescription(ticket.Anonymous - ? LangKeys.TicketSetAnonymousDescription.GetTranslation() - : LangKeys.TicketSetNotAnonymousDescription.GetTranslation()); - - if (ticket.OpenerUser is not null) embed2.WithUserAsAuthor(ticket.OpenerUser); - - - return embed2; - } - - public static DiscordEmbedBuilder TicketTypeChanged(DiscordUserInfo user, TicketType ticketType) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.TicketTypeChanged.GetTranslation()) - .WithUserAsAuthor(user) - .WithCustomTimestamp() - .WithColor(Colors.TicketTypeChangedColor); - if (ticketType is not null) - embed.WithDescription(string.Format(LangKeys.TicketTypeSet.GetTranslation(), ticketType.Emoji, ticketType.Name)); - else - embed.WithDescription(LangKeys.TicketTypeRemoved.GetTranslation()); - - return embed; - } - - public static DiscordEmbedBuilder TicketPriorityChanged(DiscordUserInfo modUser, Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.TicketPriorityChanged.GetTranslation()) - .WithColor(Colors.TicketPriorityChangedColor) - .WithCustomTimestamp() - .AddField(LangKeys.OldPriority.GetTranslation(), oldPriority.ToString(), true) - .AddField(LangKeys.NewPriority.GetTranslation(), newPriority.ToString(), true) - .WithUserAsAuthor(modUser); - return embed; - } - - public static DiscordMessageBuilder MessageReceived(DiscordMessage message, - TicketMessageAttachment[] attachments, - PermissionInfo[] permissions = null) { - var embed = new DiscordEmbedBuilder() - .WithDescription(message.Content) - .WithCustomTimestamp() - .WithColor(Colors.MessageReceivedColor) - .WithUserAsAuthor(message.Author); - - var msgBuilder = new DiscordMessageBuilder() - .AddEmbed(embed) - .AddAttachments(attachments); - if (permissions is not null && permissions.Length > 0) { - var sb = new StringBuilder(); - foreach (var permissionInfo in permissions.Where(x => x.PingOnNewMessage)) sb.AppendLine(permissionInfo.GetMention()); - - msgBuilder.WithContent(sb.ToString()); - } - - return msgBuilder; - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Static/UserResponses.cs b/src/Modmail.NET/Static/UserResponses.cs deleted file mode 100644 index 55f5c07e..00000000 --- a/src/Modmail.NET/Static/UserResponses.cs +++ /dev/null @@ -1,158 +0,0 @@ -using DSharpPlus.Entities; -using Modmail.NET.Entities; -using Modmail.NET.Extensions; -using Modmail.NET.Utils; -using DiscordMessageBuilder = DSharpPlus.Entities.DiscordMessageBuilder; - -namespace Modmail.NET.Static; - -/// -/// Contains the messages bot to send to user -/// -public static class UserResponses -{ - public static DiscordEmbedBuilder FeedbackReceivedUpdateMessage(Ticket ticket) { - var feedbackDone = new DiscordEmbedBuilder() - .WithTitle(LangKeys.FeedbackReceived.GetTranslation()) - .WithCustomTimestamp() - .WithGuildInfoFooter() - .AddField(LangKeys.Star.GetTranslation(), LangKeys.StarEmoji.GetTranslation() + ticket.FeedbackStar) - .AddField(LangKeys.Feedback.GetTranslation(), ticket.FeedbackMessage) - .WithColor(Colors.FeedbackColor); - return feedbackDone; - } - - - public static DiscordMessageBuilder YourTicketHasBeenClosed(Ticket ticket, GuildOption guildOption, Uri transcriptUri) { - var messageBuilder = new DiscordMessageBuilder(); - var embedBuilder = new DiscordEmbedBuilder() - .WithTitle(LangKeys.YourTicketHasBeenClosed.GetTranslation()) - .WithDescription(LangKeys.YourTicketHasBeenClosedDescription.GetTranslation()) - .WithGuildInfoFooter(guildOption) - .WithCustomTimestamp() - .WithColor(Colors.TicketClosedColor); - - var closingMessage = LangKeys.ClosingMessageDescription.GetTranslation(); - - if (!string.IsNullOrEmpty(closingMessage)) embedBuilder.WithDescription(closingMessage); - - if (!string.IsNullOrEmpty(ticket.CloseReason)) embedBuilder.AddField(LangKeys.CloseReason.GetTranslation(), ticket.CloseReason); - - if (transcriptUri is not null) messageBuilder.AddComponents(new DiscordLinkButtonComponent(transcriptUri.AbsoluteUri, LangKeys.Transcript.GetTranslation())); - - messageBuilder.AddEmbed(embedBuilder); - return messageBuilder; - } - - public static DiscordMessageBuilder GiveFeedbackMessage(Ticket ticket, GuildOption guildOption) { - var ticketFeedbackMsgToUser = new DiscordMessageBuilder(); - var starList = new List { - new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 1, ticket.Id), "1", false, new DiscordComponentEmoji("⭐")), - new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 2, ticket.Id), "2", false, new DiscordComponentEmoji("⭐")), - new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 3, ticket.Id), "3", false, new DiscordComponentEmoji("⭐")), - new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 4, ticket.Id), "4", false, new DiscordComponentEmoji("⭐")), - new DiscordButtonComponent(DiscordButtonStyle.Primary, UtilInteraction.BuildKey("star", 5, ticket.Id), "5", false, new DiscordComponentEmoji("⭐")) - }; - - var ticketFeedbackEmbed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.Feedback.GetTranslation()) - .WithDescription(LangKeys.FeedbackDescription.GetTranslation()) - .WithCustomTimestamp() - .WithGuildInfoFooter(guildOption) - .WithColor(Colors.FeedbackColor); - - var response = ticketFeedbackMsgToUser - .AddEmbed(ticketFeedbackEmbed) - .AddComponents(starList); - return response; - } - - public static DiscordEmbedBuilder TicketPriorityChanged(GuildOption guildOption, DiscordUserInfo info, Ticket ticket, TicketPriority oldPriority, TicketPriority newPriority) { - var embed = new DiscordEmbedBuilder() - .WithGuildInfoFooter(guildOption) - .WithTitle(LangKeys.TicketPriorityChanged.GetTranslation()) - .WithCustomTimestamp() - .WithColor(Colors.TicketPriorityChangedColor) - .AddField(LangKeys.OldPriority.GetTranslation(), oldPriority.ToString(), true) - .AddField(LangKeys.NewPriority.GetTranslation(), newPriority.ToString(), true); - if (!ticket.Anonymous) embed.WithUserAsAuthor(info); - // else embed.WithUserAsAuthor(ModmailBot.This.Client.CurrentUser); - return embed; - } - - - public static DiscordEmbedBuilder YouHaveBeenBlacklisted(string reason = null) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.YouHaveBeenBlacklisted.GetTranslation()) - .WithDescription(LangKeys.YouHaveBeenBlacklistedDescription.GetTranslation()) - .WithGuildInfoFooter() - .WithCustomTimestamp() - .WithColor(Colors.ErrorColor); - - if (!string.IsNullOrEmpty(reason)) embed.AddField(LangKeys.Reason.GetTranslation(), reason); - - return embed; - } - - public static DiscordMessageBuilder YouHaveCreatedNewTicket(DiscordGuild guild, - GuildOption option, - List ticketTypes, - Guid ticketId) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.YouHaveCreatedNewTicket.GetTranslation()) - .WithFooter(guild.Name, guild.IconUrl) - .WithCustomTimestamp() - .WithColor(Colors.TicketCreatedColor); - var greetingMessage = LangKeys.GreetingMessageDescription.GetTranslation(); - if (!string.IsNullOrEmpty(greetingMessage)) - embed.WithDescription(greetingMessage); - - var builder = new DiscordMessageBuilder() - .AddEmbed(embed); - - if (ticketTypes.Count > 0) { - var selectBox = new DiscordSelectComponent(UtilInteraction.BuildKey("ticket_type", ticketId.ToString()), - LangKeys.PleaseSelectATicketType.GetTranslation(), - ticketTypes.Select(x => new DiscordSelectComponentOption(x.Name, - x.Key.ToString(), - x.Description, - false, - !string.IsNullOrWhiteSpace(x.Emoji) - ? new DiscordComponentEmoji(x.Emoji) - : null)) - .ToList()); - builder.AddComponents(selectBox); - } - - return builder; - } - - - public static DiscordEmbedBuilder YouHaveBeenRemovedFromBlacklist(DiscordUserInfo user) { - var embed = new DiscordEmbedBuilder() - .WithTitle(LangKeys.YouHaveBeenRemovedFromBlacklist.GetTranslation()) - .WithDescription(LangKeys.YouHaveBeenRemovedFromBlacklistDescription.GetTranslation()) - .WithGuildInfoFooter() - .WithCustomTimestamp() - .WithUserAsAuthor(user) - .WithColor(Colors.SuccessColor); - return embed; - } - - public static DiscordMessageBuilder MessageReceived(DiscordMessage message, TicketMessageAttachment[] attachments, bool anonymous) { - var embed = new DiscordEmbedBuilder() - .WithDescription(message.Content) - .WithGuildInfoFooter() - .WithCustomTimestamp() - .WithColor(Colors.MessageReceivedColor); - - if (!anonymous) embed.WithUserAsAuthor(message.Author); - - var msg = new DiscordMessageBuilder(); - - msg.AddEmbed(embed); - msg.AddAttachments(attachments); - - return msg; - } -} \ No newline at end of file diff --git a/src/Modmail.NET/Static/Webhooks.cs b/src/Modmail.NET/Static/Webhooks.cs deleted file mode 100644 index 96572bcf..00000000 --- a/src/Modmail.NET/Static/Webhooks.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DSharpPlus.Entities; - -namespace Modmail.NET.Static; - -public static class Webhooks -{ - public static DiscordWebhookBuilder Error(string title, string message = "") { - return new DiscordWebhookBuilder().AddEmbed(Embeds.Error(title, message)); - } - - public static DiscordWebhookBuilder Success(string title, string message = "") { - return new DiscordWebhookBuilder().AddEmbed(Embeds.Success(title, message)); - } - - public static DiscordWebhookBuilder Info(string title, string message = "") { - return new DiscordWebhookBuilder().AddEmbed(Embeds.Info(title, message)); - } - - public static DiscordWebhookBuilder Warning(string title, string message = "") { - return new DiscordWebhookBuilder().AddEmbed(Embeds.Warning(title, message)); - } -} \ No newline at end of file