diff --git a/src/EllieBot/Modules/Utility/Quote/IQuoteService.cs b/src/EllieBot/Modules/Utility/Quote/IQuoteService.cs
index ccf360e..20bec68 100644
--- a/src/EllieBot/Modules/Utility/Quote/IQuoteService.cs
+++ b/src/EllieBot/Modules/Utility/Quote/IQuoteService.cs
@@ -1,6 +1,50 @@
-namespace EllieBot.Modules.Utility;
+using EllieBot.Db.Models;
+
+namespace EllieBot.Modules.Utility;
public interface IQuoteService
{
+ ///
+ /// Delete all quotes created by the author in a guild
+ ///
+ /// ID of the guild
+ /// ID of the user
+ /// Number of deleted qutoes
Task DeleteAllAuthorQuotesAsync(ulong guildId, ulong userId);
+
+ ///
+ /// Delete all quotes in a guild
+ ///
+ /// ID of the guild
+ /// Number of deleted qutoes
+ Task DeleteAllQuotesAsync(ulong guildId);
+
+ Task> GetAllQuotesAsync(ulong guildId, int page, OrderType order);
+ Task GetQuoteByKeywordAsync(ulong guildId, string keyword);
+
+ Task> SearchQuoteKeywordTextAsync(
+ ulong guildId,
+ string? keyword,
+ string text);
+
+ Task> GetGuildQuotesAsync(ulong guildId);
+ Task RemoveAllByKeyword(ulong guildId, string keyword);
+ Task GetQuoteByIdAsync(ulong guildId, kwum quoteId);
+
+ Task AddQuoteAsync(
+ ulong guildId,
+ ulong authorId,
+ string authorName,
+ string keyword,
+ string text);
+
+ Task EditQuoteAsync(ulong authorId, int quoteId, string text);
+
+ Task DeleteQuoteAsync(
+ ulong guildId,
+ ulong authorId,
+ bool isQuoteManager,
+ int quoteId);
+
+ Task ImportQuotesAsync(ulong guildId, string input);
}
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Utility/Quote/QuoteCommands.cs b/src/EllieBot/Modules/Utility/Quote/QuoteCommands.cs
index 27c7b23..bf50b9c 100644
--- a/src/EllieBot/Modules/Utility/Quote/QuoteCommands.cs
+++ b/src/EllieBot/Modules/Utility/Quote/QuoteCommands.cs
@@ -1,6 +1,4 @@
#nullable disable warnings
-using LinqToDB;
-using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.Yml;
using EllieBot.Db.Models;
using YamlDotNet.Serialization;
@@ -11,7 +9,7 @@ namespace EllieBot.Modules.Utility;
public partial class Utility
{
[Group]
- public partial class QuoteCommands : EllieModule
+ public partial class QuoteCommands : EllieModule
{
private const string PREPEND_EXPORT =
"""
@@ -48,19 +46,19 @@ public partial class Utility
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(1)]
- public Task ListQuotes(OrderType order = OrderType.Keyword)
- => ListQuotes(1, order);
+ public Task QuoteList(OrderType order = OrderType.Keyword)
+ => QuoteList(1, order);
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(0)]
- public async Task ListQuotes(int page = 1, OrderType order = OrderType.Keyword)
+ public async Task QuoteList(int page = 1, OrderType order = OrderType.Keyword)
{
page -= 1;
if (page < 0)
return;
- var quotes = await _service.GetAllQuotesAsync(ctx.Guild.Id, page, order);
+ var quotes = await _qs.GetAllQuotesAsync(ctx.Guild.Id, page, order);
if (quotes.Count == 0)
{
@@ -85,7 +83,7 @@ public partial class Utility
keyword = keyword.ToUpperInvariant();
- var quote = await _service.GetQuoteByKeywordAsync(ctx.Guild.Id, keyword);
+ var quote = await _qs.GetQuoteByKeywordAsync(ctx.Guild.Id, keyword);
if (quote is null)
return;
@@ -97,7 +95,6 @@ public partial class Utility
await Response()
.Text($"`{new kwum(quote.Id)}` 📣 " + text)
- .Sanitize()
.SendAsync();
}
@@ -106,7 +103,7 @@ public partial class Utility
[RequireContext(ContextType.Guild)]
public async Task QuoteShow(kwum quoteId)
{
- var quote = await _service.GetQuoteByIdAsync(ctx.Guild.Id, quoteId);
+ var quote = await _qs.GetQuoteByIdAsync(ctx.Guild.Id, quoteId);
if (quote is null)
{
@@ -179,7 +176,7 @@ public partial class Utility
keyword = keyword?.ToUpperInvariant();
- var quotes = await _service.SearchQuoteKeywordTextAsync(ctx.Guild.Id, keyword, textOrAuthor);
+ var quotes = await _qs.SearchQuoteKeywordTextAsync(ctx.Guild.Id, keyword, textOrAuthor);
await Response()
.Paginated()
@@ -218,7 +215,7 @@ public partial class Utility
if (quoteId < 0)
return;
- var quote = await _service.GetQuoteByIdAsync(ctx.Guild.Id, quoteId);
+ var quote = await _qs.GetQuoteByIdAsync(ctx.Guild.Id, quoteId);
if (quote is null)
{
@@ -226,8 +223,8 @@ public partial class Utility
return;
}
- var infoText = $"*`{new kwum(quote.Id)}` added by {quote.AuthorName.SanitizeAllMentions()}* 🗯️ "
- + quote.Keyword.ToLowerInvariant().SanitizeAllMentions()
+ var infoText = $"*`{new kwum(quote.Id)}` added by {quote.AuthorName}* 🗯️ "
+ + quote.Keyword.ToLowerInvariant()
+ ":\n";
@@ -236,7 +233,6 @@ public partial class Utility
text = await repSvc.ReplaceAsync(text, repCtx);
await Response()
.Text(infoText + text)
- .Sanitize()
.SendAsync();
}
@@ -248,26 +244,14 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
return;
- keyword = keyword.ToUpperInvariant();
+ var quote = await _qs.AddQuoteAsync(ctx.Guild.Id, ctx.User.Id, ctx.User.Username, keyword, text);
- Quote q;
- await using (var uow = _db.GetDbContext())
- {
- uow.Set()
- .Add(q = new()
- {
- AuthorId = ctx.Message.Author.Id,
- AuthorName = ctx.Message.Author.Username,
- GuildId = ctx.Guild.Id,
- Keyword = keyword,
- Text = text
- });
- await uow.SaveChangesAsync();
- }
-
- await Response().Confirm(strs.quote_added_new(Format.Code(new kwum(q.Id).ToString()))).SendAsync();
+ await Response()
+ .Confirm(strs.quote_added_new(Format.Code(new kwum(quote.Id).ToString())))
+ .SendAsync();
}
+
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task QuoteEdit(kwum quoteId, [Leftover] string text)
@@ -277,19 +261,7 @@ public partial class Utility
return;
}
- Quote q;
- await using (var uow = _db.GetDbContext())
- {
- var intId = (int)quoteId;
- var result = await uow.GetTable()
- .Where(x => x.Id == intId && x.AuthorId == ctx.User.Id)
- .Set(x => x.Text, text)
- .UpdateWithOutputAsync((del, ins) => ins);
-
- q = result.FirstOrDefault();
-
- await uow.SaveChangesAsync();
- }
+ var q = await _qs.EditQuoteAsync(ctx.User.Id, quoteId, text);
if (q is not null)
{
@@ -309,33 +281,19 @@ public partial class Utility
}
}
+
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task QuoteDelete(kwum quoteId)
{
var hasManageMessages = ((IGuildUser)ctx.Message.Author).GuildPermissions.ManageMessages;
- var success = false;
- string response;
- await using (var uow = _db.GetDbContext())
- {
- var q = uow.Set().GetById(quoteId);
-
- if (q?.GuildId != ctx.Guild.Id || (!hasManageMessages && q.AuthorId != ctx.Message.Author.Id))
- response = GetText(strs.quotes_remove_none);
- else
- {
- uow.Set().Remove(q);
- await uow.SaveChangesAsync();
- success = true;
- response = GetText(strs.quote_deleted(new kwum(quoteId)));
- }
- }
+ var success = await _qs.DeleteQuoteAsync(ctx.Guild.Id, ctx.User.Id, hasManageMessages, quoteId);
if (success)
- await Response().Confirm(response).SendAsync();
+ await Response().Confirm(strs.quote_deleted(quoteId)).SendAsync();
else
- await Response().Error(response).SendAsync();
+ await Response().Error(strs.quotes_remove_none).SendAsync();
}
[Cmd]
@@ -368,16 +326,9 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(keyword))
return;
- keyword = keyword.ToUpperInvariant();
+ await _qs.RemoveAllByKeyword(ctx.Guild.Id, keyword.ToUpperInvariant());
- await using (var uow = _db.GetDbContext())
- {
- await _service.RemoveAllByKeyword(ctx.Guild.Id, keyword.ToUpperInvariant());
-
- await uow.SaveChangesAsync();
- }
-
- await Response().Confirm(strs.quotes_deleted(Format.Bold(keyword.SanitizeAllMentions()))).SendAsync();
+ await Response().Confirm(strs.quotes_deleted(Format.Bold(keyword))).SendAsync();
}
[Cmd]
@@ -385,7 +336,7 @@ public partial class Utility
[UserPerm(GuildPerm.Administrator)]
public async Task QuotesExport()
{
- var quotes = _service.GetForGuild(ctx.Guild.Id).ToList();
+ var quotes = await _qs.GetGuildQuotesAsync(ctx.Guild.Id);
var exprsDict = quotes.GroupBy(x => x.Keyword)
.ToDictionary(x => x.Key, x => x.Select(ExportedQuote.FromModel));
@@ -400,7 +351,7 @@ public partial class Utility
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Ratelimit(300)]
-#if GLOBAL_ELLIE
+#if GLOBAL_NADEKO
[OwnerOnly]
#endif
public async Task QuotesImport([Leftover] string? input = null)
@@ -428,7 +379,7 @@ public partial class Utility
}
}
- var succ = await ImportExprsAsync(ctx.Guild.Id, input);
+ var succ = await _qs.ImportQuotesAsync(ctx.Guild.Id, input);
if (!succ)
{
await Response().Error(strs.expr_import_invalid_data).SendAsync();
@@ -437,56 +388,5 @@ public partial class Utility
await ctx.OkAsync();
}
-
- private async Task ImportExprsAsync(ulong guildId, string input)
- {
- Dictionary> data;
- try
- {
- data = Yaml.Deserializer.Deserialize>>(input);
- if (data.Sum(x => x.Value.Count) == 0)
- return false;
- }
- catch
- {
- return false;
- }
-
- await using var uow = _db.GetDbContext();
- foreach (var entry in data)
- {
- var keyword = entry.Key;
- await uow.Set()
- .AddRangeAsync(entry.Value.Where(quote => !string.IsNullOrWhiteSpace(quote.Txt))
- .Select(quote => new Quote
- {
- GuildId = guildId,
- Keyword = keyword,
- Text = quote.Txt,
- AuthorId = quote.Aid,
- AuthorName = quote.An
- }));
- }
-
- await uow.SaveChangesAsync();
- return true;
- }
-
- public class ExportedQuote
- {
- public string Id { get; set; }
- public string An { get; set; }
- public ulong Aid { get; set; }
- public string Txt { get; set; }
-
- public static ExportedQuote FromModel(Quote quote)
- => new()
- {
- Id = ((kwum)quote.Id).ToString(),
- An = quote.AuthorName,
- Aid = quote.AuthorId,
- Txt = quote.Text
- };
- }
}
}
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Utility/Quote/QuoteService.cs b/src/EllieBot/Modules/Utility/Quote/QuoteService.cs
index 2f75ce2..8cf8d62 100644
--- a/src/EllieBot/Modules/Utility/Quote/QuoteService.cs
+++ b/src/EllieBot/Modules/Utility/Quote/QuoteService.cs
@@ -1,6 +1,8 @@
#nullable disable warnings
using LinqToDB;
+using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
+using EllieBot.Common.Yml;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Utility;
@@ -45,19 +47,19 @@ public sealed class QuoteService : IQuoteService, IEService
return deleted;
}
- public async Task> GetAllQuotesAsync(ulong guildId, int page, OrderType order)
+ public async Task> GetAllQuotesAsync(ulong guildId, int page, OrderType order)
{
await using var uow = _db.GetDbContext();
var q = uow.Set()
.ToLinqToDBTable()
.Where(x => x.GuildId == guildId);
-
+
if (order == OrderType.Keyword)
q = q.OrderBy(x => x.Keyword);
else
q = q.OrderBy(x => x.Id);
-
- return await q.Skip(15 * page).Take(15).ToArrayAsync();
+
+ return await q.Skip(15 * page).Take(15).ToArrayAsyncLinqToDB();
}
public async Task GetQuoteByKeywordAsync(ulong guildId, string keyword)
@@ -97,11 +99,12 @@ public sealed class QuoteService : IQuoteService, IEService
return toReturn;
}
- public IEnumerable GetForGuild(ulong guildId)
+ public async Task> GetGuildQuotesAsync(ulong guildId)
{
- using var uow = _db.GetDbContext();
- var quotes = uow.GetTable()
- .Where(x => x.GuildId == guildId);
+ await using var uow = _db.GetDbContext();
+ var quotes = await uow.GetTable()
+ .Where(x => x.GuildId == guildId)
+ .ToListAsyncLinqToDB();
return quotes;
}
@@ -124,7 +127,96 @@ public sealed class QuoteService : IQuoteService, IEService
var quote = await uow.GetTable()
.Where(x => x.Id == quoteId && x.GuildId == guildId)
- .FirstAsyncLinqToDB();
+ .FirstOrDefaultAsyncLinqToDB();
+
return quote;
}
+
+ public async Task AddQuoteAsync(
+ ulong guildId,
+ ulong authorId,
+ string authorName,
+ string keyword,
+ string text)
+ {
+ keyword = keyword.ToUpperInvariant();
+
+ Quote q;
+ await using var uow = _db.GetDbContext();
+ uow.Set()
+ .Add(q = new()
+ {
+ AuthorId = authorId,
+ AuthorName = authorName,
+ GuildId = guildId,
+ Keyword = keyword,
+ Text = text
+ });
+ await uow.SaveChangesAsync();
+
+ return q;
+ }
+
+ public async Task EditQuoteAsync(ulong authorId, int quoteId, string text)
+ {
+ await using var uow = _db.GetDbContext();
+ var result = await uow.GetTable()
+ .Where(x => x.Id == quoteId && x.AuthorId == authorId)
+ .Set(x => x.Text, text)
+ .UpdateWithOutputAsync((del, ins) => ins);
+
+ var q = result.FirstOrDefault();
+ return q;
+ }
+
+ public async Task DeleteQuoteAsync(
+ ulong guildId,
+ ulong authorId,
+ bool isQuoteManager,
+ int quoteId)
+ {
+ await using var uow = _db.GetDbContext();
+ var q = uow.Set().GetById(quoteId);
+
+
+ var count = await uow.GetTable()
+ .Where(x => x.GuildId == guildId && x.Id == quoteId)
+ .Where(x => isQuoteManager || (x.AuthorId == authorId))
+ .DeleteAsync();
+
+
+ return count > 0;
+ }
+
+ public async Task ImportQuotesAsync(ulong guildId, string input)
+ {
+ Dictionary> data;
+ try
+ {
+ data = Yaml.Deserializer.Deserialize>>(input);
+ }
+ catch (Exception ex)
+ {
+ Log.Warning(ex, "Quote import failed: {Message}", ex.Message);
+ return false;
+ }
+
+
+ var toImport = data.SelectMany(x => x.Value.Select(v => (Key: x.Key, Value: v)))
+ .Where(x => !string.IsNullOrWhiteSpace(x.Key) && !string.IsNullOrWhiteSpace(x.Value?.Txt));
+
+ await using var uow = _db.GetDbContext();
+ await uow.GetTable()
+ .BulkCopyAsync(toImport
+ .Select(q => new Quote
+ {
+ GuildId = guildId,
+ Keyword = q.Key,
+ Text = q.Value.Txt,
+ AuthorId = q.Value.Aid,
+ AuthorName = q.Value.An
+ }));
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Utility/Quote/_common/ExportedQuote.cs b/src/EllieBot/Modules/Utility/Quote/_common/ExportedQuote.cs
new file mode 100644
index 0000000..c0ee68d
--- /dev/null
+++ b/src/EllieBot/Modules/Utility/Quote/_common/ExportedQuote.cs
@@ -0,0 +1,20 @@
+using EllieBot.Db.Models;
+
+namespace EllieBot.Modules.Utility;
+
+public class ExportedQuote
+{
+ public required string Id { get; init; }
+ public required string An { get; init; }
+ public required ulong Aid { get; init; }
+ public required string Txt { get; init; }
+
+ public static ExportedQuote FromModel(Quote quote)
+ => new()
+ {
+ Id = ((kwum)quote.Id).ToString(),
+ An = quote.AuthorName,
+ Aid = quote.AuthorId,
+ Txt = quote.Text
+ };
+}
\ No newline at end of file
diff --git a/src/EllieBot/data/aliases.yml b/src/EllieBot/data/aliases.yml
index b6f5924..af897a8 100644
--- a/src/EllieBot/data/aliases.yml
+++ b/src/EllieBot/data/aliases.yml
@@ -343,28 +343,57 @@ allcmdcooldowns:
- cmdcds
quoteadd:
- quoteadd
+ - qa
+ - qadd
+ - quadd
- .
quoteedit:
- quoteedit
+ - qe
+ - que
- qedit
quoteprint:
- quoteprint
+ - qp
+ - qup
- ..
+ - qprint
quoteshow:
- quoteshow
+ - qsh
- qshow
+ - qushow
quotesearch:
- quotesearch
+ - qs
+ - qse
- qsearch
quoteid:
- quoteid
- qid
quotedelete:
- quotedelete
+ - qd
- qdel
+ - qdelete
quotedeleteauthor:
- quotedeleteauthor
+ - qda
- qdelauth
+quotesexport:
+ - quotesexport
+ - qex
+ - qexport
+quotesimport:
+ - quotesimport
+ - qim
+ - qimp
+ - qimport
+quotelist:
+ - quotelist
+ - qli
+ - quli
+ - qulist
draw:
- draw
drawnew:
@@ -761,9 +790,6 @@ autotranslate:
- autotranslate
- at
- autotrans
-listquotes:
- - listquotes
- - liqu
typedel:
- typedel
typelist:
@@ -1210,12 +1236,6 @@ linkonlychannel:
- linkssonly
coordreload:
- coordreload
-quotesexport:
- - quotesexport
- - qexport
-quotesimport:
- - quotesimport
- - qimport
showembed:
- showembed
# EllieExpressions
diff --git a/src/EllieBot/data/strings/commands/commands.en-US.yml b/src/EllieBot/data/strings/commands/commands.en-US.yml
index 3f96439..f82206d 100644
--- a/src/EllieBot/data/strings/commands/commands.en-US.yml
+++ b/src/EllieBot/data/strings/commands/commands.en-US.yml
@@ -2502,7 +2502,7 @@ autotranslate:
params:
- autoDelete:
desc: "The option to automatically remove translated messages from the chat."
-listquotes:
+quotelist:
desc: Lists all quotes on the server ordered alphabetically or by ID. 15 Per page.
ex:
- 3