added quote api

This commit is contained in:
Toastie (DCS Team) 2024-10-17 16:45:22 +13:00
parent a59168da0b
commit 43f20cbbc2
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
6 changed files with 161 additions and 10 deletions

View file

@ -2,7 +2,7 @@
option csharp_namespace = "EllieBot.GrpcApi"; option csharp_namespace = "EllieBot.GrpcApi";
import "google/protobuf/empty.proto"; import "google/protobuf/empty.proto";
package exprs; package exprs;
@ -10,6 +10,10 @@ service GrpcExprs {
rpc GetExprs(GetExprsRequest) returns (GetExprsReply); rpc GetExprs(GetExprsRequest) returns (GetExprsReply);
rpc AddExpr(AddExprRequest) returns (AddExprReply); rpc AddExpr(AddExprRequest) returns (AddExprReply);
rpc DeleteExpr(DeleteExprRequest) returns (google.protobuf.Empty); rpc DeleteExpr(DeleteExprRequest) returns (google.protobuf.Empty);
rpc GetQuotes(GetQuotesRequest) returns (GetQuotesReply);
rpc AddQuote(AddQuoteRequest) returns (AddQuoteReply);
rpc DeleteQuote(DeleteQuoteRequest) returns (google.protobuf.Empty);
} }
message DeleteExprRequest { message DeleteExprRequest {
@ -48,3 +52,38 @@ message AddExprReply {
string id = 1; string id = 1;
bool success = 2; bool success = 2;
} }
message GetQuotesRequest {
uint64 guildId = 1;
string query = 2;
int32 page = 3;
}
message GetQuotesReply {
repeated QuoteDto quotes = 1;
int32 totalCount = 2;
}
message QuoteDto {
string id = 1;
string trigger = 2;
string response = 3;
uint64 authorId = 4;
string authorName = 5;
}
message AddQuoteRequest {
uint64 guildId = 1;
QuoteDto quote = 2;
}
message AddQuoteReply {
string id = 1;
bool success = 2;
}
message DeleteQuoteRequest {
string id = 1;
uint64 guildId = 2;
}

View file

@ -1,4 +1,3 @@
#nullable disable
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
@ -11,7 +10,7 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, IEService
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredsProvider _creds; private readonly IBotCredsProvider _creds;
private ConcurrentDictionary<ulong, ulong> _enabled; private ConcurrentDictionary<ulong, ulong> _enabled = new();
public AutoPublishService(DbService db, DiscordSocketClient client, IBotCredsProvider creds) public AutoPublishService(DbService db, DiscordSocketClient client, IBotCredsProvider creds)
{ {
@ -20,7 +19,7 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, IEService
_creds = creds; _creds = creds;
} }
public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg) public async Task ExecOnNoCommandAsync(IGuild? guild, IUserMessage msg)
{ {
if (guild is null) if (guild is null)
return; return;

View file

@ -67,7 +67,6 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
// private readonly GlobalPermissionService _gperm; // private readonly GlobalPermissionService _gperm;
// private readonly CmdCdService _cmdCds; // private readonly CmdCdService _cmdCds;
private readonly IPermissionChecker _permChecker; private readonly IPermissionChecker _permChecker;
private readonly ICommandHandler _cmd;
private readonly IBotStrings _strings; private readonly IBotStrings _strings;
private readonly IBot _bot; private readonly IBot _bot;
private readonly IPubSub _pubSub; private readonly IPubSub _pubSub;
@ -84,7 +83,6 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
IBotStrings strings, IBotStrings strings,
IBot bot, IBot bot,
DiscordSocketClient client, DiscordSocketClient client,
ICommandHandler cmd,
IPubSub pubSub, IPubSub pubSub,
IMessageSenderService sender, IMessageSenderService sender,
IReplacementService repSvc, IReplacementService repSvc,
@ -93,7 +91,6 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
{ {
_db = db; _db = db;
_client = client; _client = client;
_cmd = cmd;
_strings = strings; _strings = strings;
_bot = bot; _bot = bot;
_pubSub = pubSub; _pubSub = pubSub;

View file

@ -27,6 +27,8 @@ public interface IQuoteService
string? keyword, string? keyword,
string text); string text);
Task<(IReadOnlyCollection<Quote> quotes, int totalCount)> FindQuotesAsync(ulong guildId, string query, int page);
Task<IReadOnlyCollection<Quote>> GetGuildQuotesAsync(ulong guildId); Task<IReadOnlyCollection<Quote>> GetGuildQuotesAsync(ulong guildId);
Task<int> RemoveAllByKeyword(ulong guildId, string keyword); Task<int> RemoveAllByKeyword(ulong guildId, string keyword);
Task<Quote?> GetQuoteByIdAsync(ulong guildId, int quoteId); Task<Quote?> GetQuoteByIdAsync(ulong guildId, int quoteId);
@ -39,6 +41,7 @@ public interface IQuoteService
string text); string text);
Task<Quote?> EditQuoteAsync(ulong authorId, int quoteId, string text); Task<Quote?> EditQuoteAsync(ulong authorId, int quoteId, string text);
Task<Quote?> EditQuoteAsync(ulong guildId, int quoteId, string keyword, string text);
Task<bool> DeleteQuoteAsync( Task<bool> DeleteQuoteAsync(
ulong guildId, ulong guildId,

View file

@ -169,6 +169,23 @@ public sealed class QuoteService : IQuoteService, IEService
return q; return q;
} }
public async Task<Quote?> EditQuoteAsync(
ulong guildId,
int quoteId,
string keyword,
string text)
{
await using var uow = _db.GetDbContext();
var result = await uow.GetTable<Quote>()
.Where(x => x.Id == quoteId && x.GuildId == guildId)
.Set(x => x.Keyword, keyword)
.Set(x => x.Text, text)
.UpdateWithOutputAsync((del, ins) => ins);
var q = result.FirstOrDefault();
return q;
}
public async Task<bool> DeleteQuoteAsync( public async Task<bool> DeleteQuoteAsync(
ulong guildId, ulong guildId,
ulong authorId, ulong authorId,
@ -219,4 +236,24 @@ public sealed class QuoteService : IQuoteService, IEService
return true; return true;
} }
public async Task<(IReadOnlyCollection<Quote> quotes, int totalCount)> FindQuotesAsync(
ulong guildId,
string query,
int page)
{
await using var uow = _db.GetDbContext();
var baseQuery = uow.GetTable<Quote>()
.Where(x => x.GuildId == guildId)
.Where(x => x.Keyword.Contains(query) || x.Text.Contains(query));
var quotes = await baseQuery
.OrderBy(x => x.Id)
.Skip((page - 1) * 10)
.Take(10)
.ToListAsyncLinqToDB();
return (quotes, await baseQuery.CountAsyncLinqToDB());
}
} }

View file

@ -2,20 +2,31 @@
using Grpc.Core; using Grpc.Core;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.EllieExpressions; using EllieBot.Modules.EllieExpressions;
using EllieBot.Modules.Utility;
namespace EllieBot.GrpcApi; namespace EllieBot.GrpcApi;
public class ExprsSvc : GrpcExprs.GrpcExprsBase, IEService public class ExprsSvc : GrpcExprs.GrpcExprsBase, IEService
{ {
private readonly EllieExpressionsService _svc; private readonly EllieExpressionsService _svc;
private readonly IQuoteService _qs;
private readonly DiscordSocketClient _client;
public ExprsSvc(EllieExpressionsService svc) public ExprsSvc(EllieExpressionsService svc, IQuoteService qs, DiscordSocketClient client)
{ {
_svc = svc; _svc = svc;
_qs = qs;
_client = client;
} }
private ulong GetUserId(Metadata meta)
=> ulong.Parse(meta.FirstOrDefault(x => x.Key == "userid")!.Value);
public override async Task<AddExprReply> AddExpr(AddExprRequest request, ServerCallContext context) public override async Task<AddExprReply> AddExpr(AddExprRequest request, ServerCallContext context)
{ {
if (string.IsNullOrWhiteSpace(request.Expr.Trigger) || string.IsNullOrWhiteSpace(request.Expr.Response))
throw new RpcException(new Status(StatusCode.InvalidArgument, "Trigger and response are required"));
EllieExpression expr; EllieExpression expr;
if (!string.IsNullOrWhiteSpace(request.Expr.Id)) if (!string.IsNullOrWhiteSpace(request.Expr.Id))
{ {
@ -66,8 +77,73 @@ public class ExprsSvc : GrpcExprs.GrpcExprsBase, IEService
public override async Task<Empty> DeleteExpr(DeleteExprRequest request, ServerCallContext context) public override async Task<Empty> DeleteExpr(DeleteExprRequest request, ServerCallContext context)
{ {
await _svc.DeleteAsync(request.GuildId, new kwum(request.Id)); if (kwum.TryParse(request.Id, out var id))
await _svc.DeleteAsync(request.GuildId, id);
return new Empty(); return new Empty();
} }
}
public override async Task<GetQuotesReply> GetQuotes(GetQuotesRequest request, ServerCallContext context)
{
if (request.Page < 0)
throw new RpcException(new Status(StatusCode.InvalidArgument, "Page must be >= 0"));
var (quotes, totalCount) = await _qs.FindQuotesAsync(request.GuildId, request.Query, request.Page);
var reply = new GetQuotesReply();
reply.TotalCount = totalCount;
reply.Quotes.AddRange(quotes.Select(x => new QuoteDto()
{
Id = new kwum(x.Id).ToString(),
Trigger = x.Keyword,
Response = x.Text,
AuthorId = x.AuthorId,
AuthorName = x.AuthorName
}));
return reply;
}
public override async Task<AddQuoteReply> AddQuote(AddQuoteRequest request, ServerCallContext context)
{
var userId = GetUserId(context.RequestHeaders);
if (string.IsNullOrWhiteSpace(request.Quote.Trigger) || string.IsNullOrWhiteSpace(request.Quote.Response))
throw new RpcException(new Status(StatusCode.InvalidArgument, "Trigger and response are required"));
if (string.IsNullOrWhiteSpace(request.Quote.Id))
{
var q = await _qs.AddQuoteAsync(request.GuildId,
userId,
(await _client.GetUserAsync(userId))?.Username ?? userId.ToString(),
request.Quote.Trigger,
request.Quote.Response);
return new()
{
Id = new kwum(q.Id).ToString()
};
}
if (!kwum.TryParse(request.Quote.Id, out var qid))
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid quote id"));
await _qs.EditQuoteAsync(
request.GuildId,
new kwum(request.Quote.Id),
request.Quote.Trigger,
request.Quote.Response);
return new()
{
Id = new kwum(qid).ToString()
};
}
public override async Task<Empty> DeleteQuote(DeleteQuoteRequest request, ServerCallContext context)
{
await _qs.DeleteQuoteAsync(request.GuildId, GetUserId(context.RequestHeaders), true, new kwum(request.Id));
return new Empty();
}
}