grpc xpshop api
This commit is contained in:
parent
2f740e96b8
commit
b3d2785cec
13 changed files with 281 additions and 80 deletions
src
EllieBot.GrpcApiBase/protos
EllieBot
Modules
Administration
Xp
Services/GrpcApi
strings/responses
|
@ -12,6 +12,8 @@ service GrpcXp {
|
||||||
|
|
||||||
rpc AddReward(AddRewardRequest) returns (AddRewardReply);
|
rpc AddReward(AddRewardRequest) returns (AddRewardReply);
|
||||||
rpc DeleteReward(DeleteRewardRequest) returns (DeleteRewardReply);
|
rpc DeleteReward(DeleteRewardRequest) returns (DeleteRewardReply);
|
||||||
|
|
||||||
|
rpc GetUserXp(GetUserXpRequest) returns (GetUserXpReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetXpLbRequest {
|
message GetXpLbRequest {
|
||||||
|
@ -75,4 +77,18 @@ message DeleteRewardRequest {
|
||||||
|
|
||||||
message DeleteRewardReply {
|
message DeleteRewardReply {
|
||||||
bool success = 1;
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetUserXpRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
uint64 userId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetUserXpReply {
|
||||||
|
int64 xp = 1;
|
||||||
|
int64 requiredXp = 2;
|
||||||
|
int64 level = 3;
|
||||||
|
string club = 4;
|
||||||
|
string clubIcon = 5;
|
||||||
|
int32 rank = 6;
|
||||||
}
|
}
|
71
src/EllieBot.GrpcApiBase/protos/xpshop.proto
Normal file
71
src/EllieBot.GrpcApiBase/protos/xpshop.proto
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option csharp_namespace = "EllieBot.GrpcApi";
|
||||||
|
|
||||||
|
package greet;
|
||||||
|
|
||||||
|
service GrpcXpShop {
|
||||||
|
rpc AddXpShopItem (AddXpShopItemRequest) returns (AddXpShopItemReply);
|
||||||
|
rpc GetShopItems (GetShopItemsRequest) returns (GetShopItemsReply);
|
||||||
|
rpc UseShopItem (UseShopItemRequest) returns (UseShopItemReply);
|
||||||
|
rpc BuyShopItem (BuyShopItemRequest) returns (BuyShopItemReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
message UseShopItemRequest {
|
||||||
|
uint64 userId = 1;
|
||||||
|
string uniqueName = 2;
|
||||||
|
GrpcXpShopItemType itemType = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UseShopItemReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuyShopItemRequest {
|
||||||
|
uint64 userId = 1;
|
||||||
|
string uniqueName = 2;
|
||||||
|
GrpcXpShopItemType itemType = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuyShopItemReply {
|
||||||
|
bool success = 1;
|
||||||
|
optional BuyShopItemError Error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BuyShopItemError {
|
||||||
|
NotEnough = 0;
|
||||||
|
AlreadyOwned = 1;
|
||||||
|
Unknown = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddXpShopItemRequest {
|
||||||
|
XpShopItem item = 1;
|
||||||
|
string uniqueName = 2;
|
||||||
|
GrpcXpShopItemType itemType = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddXpShopItemReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetShopItemsRequest {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetShopItemsReply {
|
||||||
|
repeated XpShopItem bgs = 1;
|
||||||
|
repeated XpShopItem frames = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message XpShopItem {
|
||||||
|
string Name = 1;
|
||||||
|
string Description = 2;
|
||||||
|
int64 Price = 3;
|
||||||
|
string FullUrl = 4;
|
||||||
|
string PreviewUrl = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GrpcXpShopItemType {
|
||||||
|
Bg = 0;
|
||||||
|
Frame = 1;
|
||||||
|
}
|
|
@ -40,7 +40,9 @@ public partial class Administration
|
||||||
var progressMsg = await Response().Pending(strs.prune_progress(0, 100)).SendAsync();
|
var progressMsg = await Response().Pending(strs.prune_progress(0, 100)).SendAsync();
|
||||||
var progress = GetProgressTracker(progressMsg);
|
var progress = GetProgressTracker(progressMsg);
|
||||||
|
|
||||||
var result = await _service.PruneWhere(ctx.Channel,
|
var result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
ctx.Channel,
|
||||||
100,
|
100,
|
||||||
x => x.Author.Id == ctx.Client.CurrentUser.Id,
|
x => x.Author.Id == ctx.Client.CurrentUser.Id,
|
||||||
progress);
|
progress);
|
||||||
|
@ -66,13 +68,17 @@ public partial class Administration
|
||||||
|
|
||||||
PruneResult result;
|
PruneResult result;
|
||||||
if (opts.Safe)
|
if (opts.Safe)
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
(ITextChannel)ctx.Channel,
|
||||||
100,
|
100,
|
||||||
x => x.Author.Id == user.Id && !x.IsPinned,
|
x => x.Author.Id == user.Id && !x.IsPinned,
|
||||||
progress,
|
progress,
|
||||||
opts.After);
|
opts.After);
|
||||||
else
|
else
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
(ITextChannel)ctx.Channel,
|
||||||
100,
|
100,
|
||||||
x => x.Author.Id == user.Id,
|
x => x.Author.Id == user.Id,
|
||||||
progress,
|
progress,
|
||||||
|
@ -107,13 +113,17 @@ public partial class Administration
|
||||||
|
|
||||||
PruneResult result;
|
PruneResult result;
|
||||||
if (opts.Safe)
|
if (opts.Safe)
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
ctx.Channel,
|
||||||
count,
|
count,
|
||||||
x => !x.IsPinned && x.Id != progressMsg.Id,
|
x => !x.IsPinned && x.Id != progressMsg.Id,
|
||||||
progress,
|
progress,
|
||||||
opts.After);
|
opts.After);
|
||||||
else
|
else
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
ctx.Channel,
|
||||||
count,
|
count,
|
||||||
x => x.Id != progressMsg.Id,
|
x => x.Id != progressMsg.Id,
|
||||||
progress,
|
progress,
|
||||||
|
@ -133,13 +143,14 @@ public partial class Administration
|
||||||
await progressMsg.ModifyAsync(props =>
|
await progressMsg.ModifyAsync(props =>
|
||||||
{
|
{
|
||||||
props.Embed = CreateEmbed()
|
props.Embed = CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
||||||
.Build();
|
.Build();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -182,7 +193,9 @@ public partial class Administration
|
||||||
PruneResult result;
|
PruneResult result;
|
||||||
if (opts.Safe)
|
if (opts.Safe)
|
||||||
{
|
{
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
ctx.Channel,
|
||||||
count,
|
count,
|
||||||
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks && !m.IsPinned,
|
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks && !m.IsPinned,
|
||||||
progress,
|
progress,
|
||||||
|
@ -191,7 +204,9 @@ public partial class Administration
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
|
result = await _service.PruneWhere(
|
||||||
|
ctx.User.Id,
|
||||||
|
ctx.Channel,
|
||||||
count,
|
count,
|
||||||
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks,
|
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks,
|
||||||
progress,
|
progress,
|
||||||
|
@ -233,7 +248,7 @@ public partial class Administration
|
||||||
msg.DeleteAfter(5);
|
msg.DeleteAfter(5);
|
||||||
break;
|
break;
|
||||||
case PruneResult.FeatureLimit:
|
case PruneResult.FeatureLimit:
|
||||||
var msg2 = await Response().Pending(strs.feature_limit_reached_owner).SendAsync();
|
var msg2 = await Response().Pending(strs.prune_patron).SendAsync();
|
||||||
msg2.DeleteAfter(10);
|
msg2.DeleteAfter(10);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
#nullable disable
|
#nullable disable
|
||||||
using EllieBot.Modules.Patronage;
|
|
||||||
|
|
||||||
namespace EllieBot.Modules.Administration.Services;
|
namespace EllieBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public class PruneService : IEService
|
public class PruneService(ILogCommandService logService) : IEService
|
||||||
{
|
{
|
||||||
//channelids where prunes are currently occuring
|
|
||||||
private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _pruningGuilds = new();
|
private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _pruningGuilds = new();
|
||||||
private readonly TimeSpan _twoWeeks = TimeSpan.FromDays(14);
|
private readonly TimeSpan _twoWeeks = TimeSpan.FromDays(14);
|
||||||
private readonly ILogCommandService _logService;
|
|
||||||
private readonly IPatronageService _ps;
|
|
||||||
|
|
||||||
public PruneService(ILogCommandService logService, IPatronageService ps)
|
|
||||||
{
|
|
||||||
_logService = logService;
|
|
||||||
_ps = ps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PruneResult> PruneWhere(
|
public async Task<PruneResult> PruneWhere(
|
||||||
|
ulong runningUserId,
|
||||||
IMessageChannel channel,
|
IMessageChannel channel,
|
||||||
int amount,
|
int amount,
|
||||||
Func<IMessage, bool> predicate,
|
Func<IMessage, bool> predicate,
|
||||||
|
@ -37,11 +27,6 @@ public class PruneService : IEService
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (channel is ITextChannel tc && !await _ps.LimitHitAsync(LimitedFeatureName.Prune, tc.Guild.OwnerId))
|
|
||||||
{
|
|
||||||
return PruneResult.FeatureLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
IMessage[] msgs;
|
IMessage[] msgs;
|
||||||
IMessage lastMessage = null;
|
IMessage lastMessage = null;
|
||||||
|
@ -67,7 +52,7 @@ public class PruneService : IEService
|
||||||
var singleDeletable = new List<IMessage>();
|
var singleDeletable = new List<IMessage>();
|
||||||
foreach (var x in msgs)
|
foreach (var x in msgs)
|
||||||
{
|
{
|
||||||
_logService.AddDeleteIgnore(x.Id);
|
logService.AddDeleteIgnore(x.Id);
|
||||||
|
|
||||||
if (now - x.CreatedAt < _twoWeeks)
|
if (now - x.CreatedAt < _twoWeeks)
|
||||||
bulkDeletable.Add(x);
|
bulkDeletable.Add(x);
|
||||||
|
|
|
@ -57,8 +57,7 @@ public partial class Administration
|
||||||
_ => ctx.OkAsync(),
|
_ => ctx.OkAsync(),
|
||||||
async fl =>
|
async fl =>
|
||||||
{
|
{
|
||||||
_ = msg.RemoveReactionAsync(emote, ctx.Client.CurrentUser);
|
await msg.RemoveReactionAsync(emote, ctx.Client.CurrentUser);
|
||||||
await Response().Pending(strs.feature_limit_reached_owner).SendAsync();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,4 @@ public enum BuyResult
|
||||||
AlreadyOwned,
|
AlreadyOwned,
|
||||||
InsufficientFunds,
|
InsufficientFunds,
|
||||||
UnknownItem,
|
UnknownItem,
|
||||||
InsufficientPatronTier,
|
|
||||||
}
|
}
|
|
@ -327,12 +327,6 @@ public partial class Xp : EllieModule<XpService>
|
||||||
if (!string.IsNullOrWhiteSpace(item.Desc))
|
if (!string.IsNullOrWhiteSpace(item.Desc))
|
||||||
eb.AddField(GetText(strs.desc), item.Desc);
|
eb.AddField(GetText(strs.desc), item.Desc);
|
||||||
|
|
||||||
var tier = _service.GetXpShopTierRequirement(type);
|
|
||||||
if (tier != PatronTier.None)
|
|
||||||
{
|
|
||||||
eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return eb;
|
return eb;
|
||||||
})
|
})
|
||||||
.Interaction(async current =>
|
.Interaction(async current =>
|
||||||
|
@ -407,7 +401,6 @@ public partial class Xp : EllieModule<XpService>
|
||||||
BuyResult.AlreadyOwned =>
|
BuyResult.AlreadyOwned =>
|
||||||
await Response().Error(strs.xpshop_already_owned).Interaction(GetUseInteraction()).SendAsync(),
|
await Response().Error(strs.xpshop_already_owned).Interaction(GetUseInteraction()).SendAsync(),
|
||||||
BuyResult.UnknownItem => await Response().Error(strs.xpshop_item_not_found).SendAsync(),
|
BuyResult.UnknownItem => await Response().Error(strs.xpshop_item_not_found).SendAsync(),
|
||||||
BuyResult.InsufficientPatronTier => await Response().Error(strs.patron_insuff_tier).SendAsync(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace EllieBot.Modules.Xp;
|
||||||
public sealed partial class XpConfig : ICloneable<XpConfig>
|
public sealed partial class XpConfig : ICloneable<XpConfig>
|
||||||
{
|
{
|
||||||
[Comment("""DO NOT CHANGE""")]
|
[Comment("""DO NOT CHANGE""")]
|
||||||
public int Version { get; set; } = 10;
|
public int Version { get; set; } = 11;
|
||||||
|
|
||||||
[Comment("""How much XP will the users receive per message""")]
|
[Comment("""How much XP will the users receive per message""")]
|
||||||
public int TextXpPerMessage { get; set; } = 3;
|
public int TextXpPerMessage { get; set; } = 3;
|
||||||
|
@ -36,18 +36,6 @@ public sealed partial class XpConfig : ICloneable<XpConfig>
|
||||||
""")]
|
""")]
|
||||||
public bool IsEnabled { get; set; } = false;
|
public bool IsEnabled { get; set; } = false;
|
||||||
|
|
||||||
[Comment("""
|
|
||||||
Which patron tier do users need in order to use the .xpshop bgs command
|
|
||||||
Leave at 'None' if patron system is disabled or you don't want any restrictions
|
|
||||||
""")]
|
|
||||||
public PatronTier BgsTierRequirement { get; set; } = PatronTier.None;
|
|
||||||
|
|
||||||
[Comment("""
|
|
||||||
Which patron tier do users need in order to use the .xpshop frames command
|
|
||||||
Leave at 'None' if patron system is disabled or you don't want any restrictions
|
|
||||||
""")]
|
|
||||||
public PatronTier FramesTierRequirement { get; set; } = PatronTier.None;
|
|
||||||
|
|
||||||
[Comment("""
|
[Comment("""
|
||||||
Frames available for sale. Keys are unique IDs.
|
Frames available for sale. Keys are unique IDs.
|
||||||
Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#nullable disable
|
#nullable disable
|
||||||
using EllieBot.Common.Configs;
|
using EllieBot.Common.Configs;
|
||||||
|
using EllieBot.Db.Models;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Xp.Services;
|
namespace EllieBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
@ -48,12 +49,27 @@ public sealed class XpConfigService : ConfigServiceBase<XpConfig>
|
||||||
|
|
||||||
private void Migrate()
|
private void Migrate()
|
||||||
{
|
{
|
||||||
if (data.Version < 10)
|
if (data.Version < 11)
|
||||||
{
|
{
|
||||||
ModifyConfig(c =>
|
ModifyConfig(c => { c.Version = 11; });
|
||||||
{
|
|
||||||
c.Version = 10;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AddItemAsync(string uniqueName, XpShopItemType itemType, XpConfig.ShopItemInfo shopItemInfo)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
|
||||||
|
var success = false;
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
var items = itemType == XpShopItemType.Background
|
||||||
|
? c.Shop.Bgs
|
||||||
|
: c.Shop.Frames;
|
||||||
|
|
||||||
|
if (items is not null)
|
||||||
|
success = items.TryAdd(uniqueName, shopItemInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -989,18 +989,6 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
if (!conf.Shop.IsEnabled)
|
if (!conf.Shop.IsEnabled)
|
||||||
return BuyResult.XpShopDisabled;
|
return BuyResult.XpShopDisabled;
|
||||||
|
|
||||||
var req = type == XpShopItemType.Background
|
|
||||||
? conf.Shop.BgsTierRequirement
|
|
||||||
: conf.Shop.FramesTierRequirement;
|
|
||||||
|
|
||||||
if (req != PatronTier.None && !_creds.IsOwner(userId))
|
|
||||||
{
|
|
||||||
var patron = await _ps.GetPatronAsync(userId);
|
|
||||||
|
|
||||||
if (patron is null || (int)patron.Value.Tier < (int)req)
|
|
||||||
return BuyResult.InsufficientPatronTier;
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1127,13 +1115,6 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PatronTier GetXpShopTierRequirement(Xp.XpShopInputType type)
|
|
||||||
=> type switch
|
|
||||||
{
|
|
||||||
Xp.XpShopInputType.F => _xpConfig.Data.Shop.FramesTierRequirement,
|
|
||||||
_ => PatronTier.None,
|
|
||||||
};
|
|
||||||
|
|
||||||
public bool IsShopEnabled()
|
public bool IsShopEnabled()
|
||||||
=> _xpConfig.Data.Shop.IsEnabled;
|
=> _xpConfig.Data.Shop.IsEnabled;
|
||||||
|
|
||||||
|
|
93
src/EllieBot/Services/GrpcApi/XpShopSvc.cs
Normal file
93
src/EllieBot/Services/GrpcApi/XpShopSvc.cs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
using Grpc.Core;
|
||||||
|
using EllieBot.Db.Models;
|
||||||
|
using EllieBot.Modules.Xp;
|
||||||
|
using EllieBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
namespace EllieBot.GrpcApi;
|
||||||
|
|
||||||
|
public class XpShopSvc(XpService xp, XpConfigService xpConfig) : GrpcXpShop.GrpcXpShopBase, IGrpcSvc, IEService
|
||||||
|
{
|
||||||
|
public ServerServiceDefinition Bind()
|
||||||
|
=> GrpcXpShop.BindService(this);
|
||||||
|
|
||||||
|
public override async Task<BuyShopItemReply> BuyShopItem(BuyShopItemRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var result = await xp.BuyShopItemAsync(request.UserId, (XpShopItemType)request.ItemType, request.UniqueName);
|
||||||
|
|
||||||
|
var res = new BuyShopItemReply();
|
||||||
|
|
||||||
|
if (result == BuyResult.Success)
|
||||||
|
{
|
||||||
|
res.Success = true;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Error = result switch
|
||||||
|
{
|
||||||
|
BuyResult.AlreadyOwned => BuyShopItemError.AlreadyOwned,
|
||||||
|
BuyResult.InsufficientFunds => BuyShopItemError.NotEnough,
|
||||||
|
_ => BuyShopItemError.Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<UseShopItemReply> UseShopItem(UseShopItemRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var result = await xp.UseShopItemAsync(request.UserId, (XpShopItemType)request.ItemType, request.UniqueName);
|
||||||
|
|
||||||
|
var res = new UseShopItemReply
|
||||||
|
{
|
||||||
|
Success = result
|
||||||
|
};
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetShopItemsReply> GetShopItems(GetShopItemsRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var bgsTask = Task.Run(async () => await xp.GetShopBgs());
|
||||||
|
var frsTask = Task.Run(async () => await xp.GetShopFrames());
|
||||||
|
|
||||||
|
var bgs = await bgsTask.Fmap(x => x?.Map(y => MapItemToGrpcItem(y.Value, y.Key)) ?? []);
|
||||||
|
var frs = await frsTask.Fmap(z => z?.Map(y => MapItemToGrpcItem(y.Value, y.Key)) ?? []);
|
||||||
|
|
||||||
|
var res = new GetShopItemsReply();
|
||||||
|
|
||||||
|
res.Bgs.AddRange(bgs);
|
||||||
|
res.Frames.AddRange(frs);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
static XpShopItem MapItemToGrpcItem(XpConfig.ShopItemInfo item, string uniqueName)
|
||||||
|
{
|
||||||
|
return new XpShopItem()
|
||||||
|
{
|
||||||
|
Name = item.Name,
|
||||||
|
Price = item.Price,
|
||||||
|
Description = item.Desc,
|
||||||
|
FullUrl = item.Url,
|
||||||
|
PreviewUrl = item.Preview,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AddXpShopItemReply> AddXpShopItem(AddXpShopItemRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var result = await xpConfig.AddItemAsync(request.UniqueName, (XpShopItemType)request.ItemType,
|
||||||
|
new XpConfig.ShopItemInfo()
|
||||||
|
{
|
||||||
|
Name = request.Item.Name,
|
||||||
|
Price = 3000,
|
||||||
|
Desc = request.Item.Description,
|
||||||
|
Url = request.Item.FullUrl,
|
||||||
|
Preview = request.Item.PreviewUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new AddXpShopItemReply()
|
||||||
|
{
|
||||||
|
Success = result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -193,4 +193,51 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets XP information for a specific user in a guild
|
||||||
|
/// </summary>
|
||||||
|
public override async Task<GetUserXpReply> GetUserXp(
|
||||||
|
GetUserXpRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var guild = _client.GetGuild(request.GuildId);
|
||||||
|
|
||||||
|
if (guild is null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "Guild not found"));
|
||||||
|
|
||||||
|
var user = guild.GetUser(request.UserId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "User not found"));
|
||||||
|
|
||||||
|
var reply = new GetUserXpReply();
|
||||||
|
|
||||||
|
// Get user stats from the XP service
|
||||||
|
var stats = await _xp.GetUserStatsAsync(user);
|
||||||
|
var levelStats = stats.Guild;
|
||||||
|
|
||||||
|
// Get user's rank in guild
|
||||||
|
var guildRank = stats.GuildRanking;
|
||||||
|
|
||||||
|
// Fill the response with user XP data
|
||||||
|
reply.Xp = levelStats.LevelXp;
|
||||||
|
reply.RequiredXp = levelStats.RequiredXp;
|
||||||
|
reply.Level = levelStats.Level;
|
||||||
|
reply.Rank = guildRank;
|
||||||
|
|
||||||
|
// Add club information if available
|
||||||
|
if (stats.User.Club is not null)
|
||||||
|
{
|
||||||
|
reply.Club = stats.User.Club.ToString();
|
||||||
|
reply.ClubIcon = stats.User.Club.ImageUrl ?? string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply.Club = string.Empty;
|
||||||
|
reply.ClubIcon = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1045,10 +1045,8 @@
|
||||||
"bank_withdraw_insuff": "You don't have sufficient {0} in your bank account.",
|
"bank_withdraw_insuff": "You don't have sufficient {0} in your bank account.",
|
||||||
"cmd_group_commands": "'{0}' command group",
|
"cmd_group_commands": "'{0}' command group",
|
||||||
"limit_reached": "Feature limit of {0} reached.",
|
"limit_reached": "Feature limit of {0} reached.",
|
||||||
"feature_limit_reached_you": "You've reached the limit of {0} for the {1} feature. You may be able to increase this limit by upgrading your patron tier.",
|
"feature_limit": "The limit of {0} for the {1} feature has been reached. Server owner may be able to increase the limit by upgrading the Patron Tier.",
|
||||||
"feature_limit_reached_owner": "Feature limit reached. Server owner may upgrade patron level to increase the limit.",
|
"prune_patron": "Deleting messages 2 weeks old or older requires [Patron Tier X](https://patreon.com/join/elliebot) or higher.",
|
||||||
"feature_limit_reached_either": "The limit of {0} for the {1} feature has been reached. Either you or the server owner may able to upgrade this limit by upgrading the patron tier.",
|
|
||||||
"xp_shop_buy_required_tier": "Buying items from this shop requires Patron Tier {0} or higher.",
|
|
||||||
"available_commands": "Available Commands",
|
"available_commands": "Available Commands",
|
||||||
"tier": "Tier",
|
"tier": "Tier",
|
||||||
"pledge": "Pledge",
|
"pledge": "Pledge",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue