Quote commands slightly changed and some of them renamed. Added a lot of new aliases. Notable rename is .liqu to .qli

Quotes now follow the same naming pattern as Expression commands
Code vastly improved
This commit is contained in:
Toastie (DCS Team) 2024-08-21 20:22:31 +12:00
parent 2e541eebac
commit 3a25433ec8
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
6 changed files with 222 additions and 146 deletions

View file

@ -1,6 +1,50 @@
namespace EllieBot.Modules.Utility;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Utility;
public interface IQuoteService
{
/// <summary>
/// Delete all quotes created by the author in a guild
/// </summary>
/// <param name="guildId">ID of the guild</param>
/// <param name="userId">ID of the user</param>
/// <returns>Number of deleted qutoes</returns>
Task<int> DeleteAllAuthorQuotesAsync(ulong guildId, ulong userId);
/// <summary>
/// Delete all quotes in a guild
/// </summary>
/// <param name="guildId">ID of the guild</param>
/// <returns>Number of deleted qutoes</returns>
Task<int> DeleteAllQuotesAsync(ulong guildId);
Task<IReadOnlyCollection<Quote>> GetAllQuotesAsync(ulong guildId, int page, OrderType order);
Task<Quote?> GetQuoteByKeywordAsync(ulong guildId, string keyword);
Task<IReadOnlyCollection<Quote>> SearchQuoteKeywordTextAsync(
ulong guildId,
string? keyword,
string text);
Task<IReadOnlyCollection<Quote>> GetGuildQuotesAsync(ulong guildId);
Task<int> RemoveAllByKeyword(ulong guildId, string keyword);
Task<Quote?> GetQuoteByIdAsync(ulong guildId, kwum quoteId);
Task<Quote> AddQuoteAsync(
ulong guildId,
ulong authorId,
string authorName,
string keyword,
string text);
Task<Quote?> EditQuoteAsync(ulong authorId, int quoteId, string text);
Task<bool> DeleteQuoteAsync(
ulong guildId,
ulong authorId,
bool isQuoteManager,
int quoteId);
Task<bool> ImportQuotesAsync(ulong guildId, string input);
}

View file

@ -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<QuoteService>
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<Quote>()
.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<Quote>()
.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<Quote>().GetById(quoteId);
if (q?.GuildId != ctx.Guild.Id || (!hasManageMessages && q.AuthorId != ctx.Message.Author.Id))
response = GetText(strs.quotes_remove_none);
else
{
uow.Set<Quote>().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<bool> ImportExprsAsync(ulong guildId, string input)
{
Dictionary<string, List<ExportedQuote>> data;
try
{
data = Yaml.Deserializer.Deserialize<Dictionary<string, List<ExportedQuote>>>(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<Quote>()
.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
};
}
}
}

View file

@ -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,7 +47,7 @@ public sealed class QuoteService : IQuoteService, IEService
return deleted;
}
public async Task<IReadOnlyList<Quote>> GetAllQuotesAsync(ulong guildId, int page, OrderType order)
public async Task<IReadOnlyCollection<Quote>> GetAllQuotesAsync(ulong guildId, int page, OrderType order)
{
await using var uow = _db.GetDbContext();
var q = uow.Set<Quote>()
@ -57,7 +59,7 @@ public sealed class QuoteService : IQuoteService, IEService
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<Quote?> GetQuoteByKeywordAsync(ulong guildId, string keyword)
@ -97,11 +99,12 @@ public sealed class QuoteService : IQuoteService, IEService
return toReturn;
}
public IEnumerable<Quote> GetForGuild(ulong guildId)
public async Task<IReadOnlyCollection<Quote>> GetGuildQuotesAsync(ulong guildId)
{
using var uow = _db.GetDbContext();
var quotes = uow.GetTable<Quote>()
.Where(x => x.GuildId == guildId);
await using var uow = _db.GetDbContext();
var quotes = await uow.GetTable<Quote>()
.Where(x => x.GuildId == guildId)
.ToListAsyncLinqToDB();
return quotes;
}
@ -124,7 +127,96 @@ public sealed class QuoteService : IQuoteService, IEService
var quote = await uow.GetTable<Quote>()
.Where(x => x.Id == quoteId && x.GuildId == guildId)
.FirstAsyncLinqToDB();
.FirstOrDefaultAsyncLinqToDB();
return quote;
}
public async Task<Quote> AddQuoteAsync(
ulong guildId,
ulong authorId,
string authorName,
string keyword,
string text)
{
keyword = keyword.ToUpperInvariant();
Quote q;
await using var uow = _db.GetDbContext();
uow.Set<Quote>()
.Add(q = new()
{
AuthorId = authorId,
AuthorName = authorName,
GuildId = guildId,
Keyword = keyword,
Text = text
});
await uow.SaveChangesAsync();
return q;
}
public async Task<Quote?> EditQuoteAsync(ulong authorId, int quoteId, string text)
{
await using var uow = _db.GetDbContext();
var result = await uow.GetTable<Quote>()
.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<bool> DeleteQuoteAsync(
ulong guildId,
ulong authorId,
bool isQuoteManager,
int quoteId)
{
await using var uow = _db.GetDbContext();
var q = uow.Set<Quote>().GetById(quoteId);
var count = await uow.GetTable<Quote>()
.Where(x => x.GuildId == guildId && x.Id == quoteId)
.Where(x => isQuoteManager || (x.AuthorId == authorId))
.DeleteAsync();
return count > 0;
}
public async Task<bool> ImportQuotesAsync(ulong guildId, string input)
{
Dictionary<string?, List<ExportedQuote?>> data;
try
{
data = Yaml.Deserializer.Deserialize<Dictionary<string?, List<ExportedQuote?>>>(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<Quote>()
.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;
}
}

View file

@ -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
};
}

View file

@ -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

View file

@ -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