mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
477581f616 | ||
|
ff30105816 | ||
|
49f04a594b | ||
|
716090a132 | ||
|
c835514c7b | ||
|
b136e7ff0e | ||
|
9dd2997b0f | ||
|
fde5309ea4 | ||
|
a8e4173e9b | ||
|
74b4c4b64d | ||
|
6cc5a160a2 | ||
|
ca8e022db6 | ||
|
cd8c14c607 | ||
|
1340533c21 | ||
|
14d86b9042 | ||
|
3a504a954f | ||
|
822ce0b8de | ||
|
40490a4656 | ||
|
0cf7909fef |
32
CHANGELOG.md
32
CHANGELOG.md
@@ -2,6 +2,38 @@
|
|||||||
|
|
||||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [5.1.15] - 21.10.2024
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added -c option for `.xpglb`
|
||||||
|
-
|
||||||
|
|
||||||
|
## Change
|
||||||
|
|
||||||
|
- Leaderboards will now show 10 users per page
|
||||||
|
- A lot of internal changes and improvements
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fixed a big issue which caused several features to not get loaded on bot restart
|
||||||
|
- Alias collision fix `.qse` is now quotesearch, `.qs` will stay `.queuesearch`
|
||||||
|
- Fixed some migrations which would prevent users from updating from ancient versions
|
||||||
|
- Waifulb will no longer show #0000 discrims
|
||||||
|
- More `.greet` command fixes
|
||||||
|
- Author name will now be counted as content in embeds. Embeds can now only have author fields and still be valid
|
||||||
|
- Grpc api fixes, and additions
|
||||||
|
|
||||||
|
## [5.1.14] - 03.10.2024
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Improved `.xplb -c`, it will now correctly only show users who are still in the server with no count limit
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fixed medusa load error on startup
|
||||||
|
|
||||||
## [5.1.13] - 03.10.2024
|
## [5.1.13] - 03.10.2024
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@@ -12,13 +12,14 @@ namespace NadekoBot.Generators
|
|||||||
{
|
{
|
||||||
public readonly record struct MethodPermData
|
public readonly record struct MethodPermData
|
||||||
{
|
{
|
||||||
public readonly string Name;
|
public readonly ImmutableArray<(string Name, string Value)> MethodPerms;
|
||||||
public readonly string Value;
|
public readonly ImmutableArray<string> NoAuthRequired;
|
||||||
|
|
||||||
public MethodPermData(string name, string value)
|
public MethodPermData(ImmutableArray<(string Name, string Value)> methodPerms,
|
||||||
|
ImmutableArray<string> noAuthRequired)
|
||||||
{
|
{
|
||||||
Name = name;
|
MethodPerms = methodPerms;
|
||||||
Value = value;
|
NoAuthRequired = noAuthRequired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ namespace NadekoBot.Generators
|
|||||||
[Generator]
|
[Generator]
|
||||||
public class GrpcApiPermGenerator : IIncrementalGenerator
|
public class GrpcApiPermGenerator : IIncrementalGenerator
|
||||||
{
|
{
|
||||||
public const string Attribute =
|
public const string GRPC_API_PERM_ATTRIBUTE =
|
||||||
"""
|
"""
|
||||||
namespace NadekoBot.GrpcApi;
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
@@ -38,12 +39,25 @@ namespace NadekoBot.Generators
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
public const string GRPC_NO_AUTH_REQUIRED_ATTRIBUTE =
|
||||||
|
"""
|
||||||
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
|
[System.AttributeUsage(System.AttributeTargets.Method)]
|
||||||
|
public class GrpcNoAuthRequiredAttribute : System.Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
{
|
{
|
||||||
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("GrpcApiPermAttribute.cs",
|
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("GrpcApiPermAttribute.cs",
|
||||||
SourceText.From(Attribute, Encoding.UTF8)));
|
SourceText.From(GRPC_API_PERM_ATTRIBUTE, Encoding.UTF8)));
|
||||||
|
|
||||||
var enumsToGenerate = context.SyntaxProvider
|
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("GrpcNoAuthRequiredAttribute.cs",
|
||||||
|
SourceText.From(GRPC_NO_AUTH_REQUIRED_ATTRIBUTE, Encoding.UTF8)));
|
||||||
|
|
||||||
|
var perms = context.SyntaxProvider
|
||||||
.ForAttributeWithMetadataName(
|
.ForAttributeWithMetadataName(
|
||||||
"NadekoBot.GrpcApi.GrpcApiPermAttribute",
|
"NadekoBot.GrpcApi.GrpcApiPermAttribute",
|
||||||
predicate: static (s, _) => s is MethodDeclarationSyntax,
|
predicate: static (s, _) => s is MethodDeclarationSyntax,
|
||||||
@@ -52,11 +66,24 @@ namespace NadekoBot.Generators
|
|||||||
.Select(static (x, _) => x!.Value)
|
.Select(static (x, _) => x!.Value)
|
||||||
.Collect();
|
.Collect();
|
||||||
|
|
||||||
context.RegisterSourceOutput(enumsToGenerate,
|
|
||||||
|
var all = context.SyntaxProvider
|
||||||
|
.ForAttributeWithMetadataName(
|
||||||
|
"NadekoBot.GrpcApi.GrpcNoAuthRequiredAttribute",
|
||||||
|
predicate: static (s, _) => s is MethodDeclarationSyntax,
|
||||||
|
transform: static (ctx, _) => GetNoAuthMethodName(ctx.SemanticModel, ctx.TargetNode))
|
||||||
|
.Collect()
|
||||||
|
.Combine(perms)
|
||||||
|
.Select((x, _) => new MethodPermData(x.Right, x.Left));
|
||||||
|
|
||||||
|
context.RegisterSourceOutput(all,
|
||||||
static (spc, source) => Execute(source, spc));
|
static (spc, source) => Execute(source, spc));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodPermData? GetMethodSemanticTargets(SemanticModel model, SyntaxNode node)
|
private static string GetNoAuthMethodName(SemanticModel model, SyntaxNode node)
|
||||||
|
=> ((MethodDeclarationSyntax)node).Identifier.Text;
|
||||||
|
|
||||||
|
private static (string Name, string Value)? GetMethodSemanticTargets(SemanticModel model, SyntaxNode node)
|
||||||
{
|
{
|
||||||
var method = (MethodDeclarationSyntax)node;
|
var method = (MethodDeclarationSyntax)node;
|
||||||
|
|
||||||
@@ -64,20 +91,14 @@ namespace NadekoBot.Generators
|
|||||||
var attr = method.AttributeLists
|
var attr = method.AttributeLists
|
||||||
.SelectMany(x => x.Attributes)
|
.SelectMany(x => x.Attributes)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
// .FirstOrDefault(x => x.Name.ToString() == "GrpcApiPermAttribute");
|
|
||||||
|
|
||||||
|
|
||||||
if (attr is null)
|
if (attr is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// if (model.GetSymbolInfo(attr).Symbol is not IMethodSymbol attrSymbol)
|
return (name, attr.ArgumentList?.Arguments[0].ToString() ?? "__missing_perm__");
|
||||||
// return null;
|
|
||||||
|
|
||||||
return new MethodPermData(name, attr.ArgumentList?.Arguments[0].ToString() ?? "__missing_perm__");
|
|
||||||
// return new MethodPermData(name, attrSymbol.Parameters[0].ContainingType.ToDisplayString() + "." + attrSymbol.Parameters[0].Name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Execute(ImmutableArray<MethodPermData> fields, SourceProductionContext ctx)
|
private static void Execute(MethodPermData data, SourceProductionContext ctx)
|
||||||
{
|
{
|
||||||
using (var stringWriter = new StringWriter())
|
using (var stringWriter = new StringWriter())
|
||||||
using (var sw = new IndentedTextWriter(stringWriter))
|
using (var sw = new IndentedTextWriter(stringWriter))
|
||||||
@@ -87,16 +108,17 @@ namespace NadekoBot.Generators
|
|||||||
sw.WriteLine("namespace NadekoBot.GrpcApi;");
|
sw.WriteLine("namespace NadekoBot.GrpcApi;");
|
||||||
sw.WriteLine();
|
sw.WriteLine();
|
||||||
|
|
||||||
sw.WriteLine("public partial class PermsInterceptor");
|
sw.WriteLine("public partial class GrpcApiPermsInterceptor");
|
||||||
sw.WriteLine("{");
|
sw.WriteLine("{");
|
||||||
|
|
||||||
sw.Indent++;
|
sw.Indent++;
|
||||||
|
|
||||||
sw.WriteLine("public static FrozenDictionary<string, GuildPerm> perms = new Dictionary<string, GuildPerm>()");
|
sw.WriteLine(
|
||||||
|
"private static FrozenDictionary<string, GuildPerm> _perms = new Dictionary<string, GuildPerm>()");
|
||||||
sw.WriteLine("{");
|
sw.WriteLine("{");
|
||||||
|
|
||||||
sw.Indent++;
|
sw.Indent++;
|
||||||
foreach (var field in fields)
|
foreach (var field in data.MethodPerms)
|
||||||
{
|
{
|
||||||
sw.WriteLine("{{ \"{0}\", {1} }},", field.Name, field.Value);
|
sw.WriteLine("{{ \"{0}\", {1} }},", field.Name, field.Value);
|
||||||
}
|
}
|
||||||
@@ -104,6 +126,21 @@ namespace NadekoBot.Generators
|
|||||||
sw.Indent--;
|
sw.Indent--;
|
||||||
sw.WriteLine("}.ToFrozenDictionary();");
|
sw.WriteLine("}.ToFrozenDictionary();");
|
||||||
|
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("private static FrozenSet<string> _noAuthRequired = new HashSet<string>()");
|
||||||
|
sw.WriteLine("{");
|
||||||
|
|
||||||
|
sw.Indent++;
|
||||||
|
foreach (var noauth in data.NoAuthRequired)
|
||||||
|
{
|
||||||
|
sw.WriteLine("{{ \"{0}\" }},", noauth);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.WriteLine("");
|
||||||
|
|
||||||
|
sw.Indent--;
|
||||||
|
sw.WriteLine("}.ToFrozenSet();");
|
||||||
|
|
||||||
sw.Indent--;
|
sw.Indent--;
|
||||||
sw.WriteLine("}");
|
sw.WriteLine("}");
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
}
|
@@ -5,20 +5,13 @@ option csharp_namespace = "NadekoBot.GrpcApi";
|
|||||||
package greet;
|
package greet;
|
||||||
|
|
||||||
service GrpcGreet {
|
service GrpcGreet {
|
||||||
rpc GetGreetSettings (GetGreetRequest) returns (GetGreetReply);
|
rpc GetGreetSettings (GetGreetRequest) returns (GrpcGreetSettings);
|
||||||
rpc UpdateGreet (UpdateGreetRequest) returns (UpdateGreetReply);
|
rpc UpdateGreet (UpdateGreetRequest) returns (UpdateGreetReply);
|
||||||
rpc TestGreet (TestGreetRequest) returns (TestGreetReply);
|
rpc TestGreet (TestGreetRequest) returns (TestGreetReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetGreetReply {
|
|
||||||
GrpcGreetSettings greet = 1;
|
|
||||||
GrpcGreetSettings greetDm = 2;
|
|
||||||
GrpcGreetSettings bye = 3;
|
|
||||||
GrpcGreetSettings boost = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GrpcGreetSettings {
|
message GrpcGreetSettings {
|
||||||
optional uint64 channelId = 1;
|
string channelId = 1;
|
||||||
string message = 2;
|
string message = 2;
|
||||||
bool isEnabled = 3;
|
bool isEnabled = 3;
|
||||||
GrpcGreetType type = 4;
|
GrpcGreetType type = 4;
|
||||||
@@ -26,6 +19,7 @@ message GrpcGreetSettings {
|
|||||||
|
|
||||||
message GetGreetRequest {
|
message GetGreetRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
|
GrpcGreetType type = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateGreetRequest {
|
message UpdateGreetRequest {
|
||||||
@@ -41,7 +35,7 @@ enum GrpcGreetType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message UpdateGreetReply {
|
message UpdateGreetReply {
|
||||||
bool success = 1;
|
bool Success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestGreetRequest {
|
message TestGreetRequest {
|
||||||
|
@@ -8,9 +8,10 @@ import "google/protobuf/timestamp.proto";
|
|||||||
package other;
|
package other;
|
||||||
|
|
||||||
service GrpcOther {
|
service GrpcOther {
|
||||||
|
rpc BotOnGuild(BotOnGuildRequest) returns (BotOnGuildReply);
|
||||||
rpc GetGuilds(google.protobuf.Empty) returns (GetGuildsReply);
|
rpc GetGuilds(google.protobuf.Empty) returns (GetGuildsReply);
|
||||||
rpc GetTextChannels(GetTextChannelsRequest) returns (GetTextChannelsReply);
|
rpc GetTextChannels(GetTextChannelsRequest) returns (GetTextChannelsReply);
|
||||||
|
rpc GetRoles(GetRolesRequest) returns (GetRolesReply);
|
||||||
|
|
||||||
rpc GetCurrencyLb(GetLbRequest) returns (CurrencyLbReply);
|
rpc GetCurrencyLb(GetLbRequest) returns (CurrencyLbReply);
|
||||||
rpc GetXpLb(GetLbRequest) returns (XpLbReply);
|
rpc GetXpLb(GetLbRequest) returns (XpLbReply);
|
||||||
@@ -20,6 +21,22 @@ service GrpcOther {
|
|||||||
rpc GetServerInfo(ServerInfoRequest) returns (GetServerInfoReply);
|
rpc GetServerInfo(ServerInfoRequest) returns (GetServerInfoReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetRolesRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRolesReply {
|
||||||
|
repeated RoleReply roles = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BotOnGuildRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BotOnGuildReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message GetGuildsReply {
|
message GetGuildsReply {
|
||||||
repeated GuildReply guilds = 1;
|
repeated GuildReply guilds = 1;
|
||||||
}
|
}
|
||||||
|
@@ -6,11 +6,17 @@ package warn;
|
|||||||
|
|
||||||
service GrpcWarn {
|
service GrpcWarn {
|
||||||
rpc GetWarnSettings (WarnSettingsRequest) returns (WarnSettingsReply);
|
rpc GetWarnSettings (WarnSettingsRequest) returns (WarnSettingsReply);
|
||||||
|
|
||||||
|
rpc SetWarnExpiry(SetWarnExpiryRequest) returns (SetWarnExpiryReply);
|
||||||
rpc AddWarnp (AddWarnpRequest) returns (AddWarnpReply);
|
rpc AddWarnp (AddWarnpRequest) returns (AddWarnpReply);
|
||||||
rpc DeleteWarnp (DeleteWarnpRequest) returns (DeleteWarnpReply);
|
rpc DeleteWarnp (DeleteWarnpRequest) returns (DeleteWarnpReply);
|
||||||
|
|
||||||
|
rpc GetLatestWarnings(GetLatestWarningsRequest) returns (GetLatestWarningsReply);
|
||||||
rpc GetUserWarnings(GetUserWarningsRequest) returns (GetUserWarningsReply);
|
rpc GetUserWarnings(GetUserWarningsRequest) returns (GetUserWarningsReply);
|
||||||
rpc ClearWarning(ClearWarningRequest) returns (ClearWarningReply);
|
|
||||||
rpc SetWarnExpiry(SetWarnExpiryRequest) returns (SetWarnExpiryReply);
|
rpc ForgiveWarning(ForgiveWarningRequest) returns (ForgiveWarningReply);
|
||||||
|
rpc DeleteWarning(ForgiveWarningRequest) returns (ForgiveWarningReply);
|
||||||
|
|
||||||
}
|
}
|
||||||
message WarnSettingsRequest {
|
message WarnSettingsRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
@@ -19,12 +25,14 @@ message WarnSettingsRequest {
|
|||||||
message WarnPunishment {
|
message WarnPunishment {
|
||||||
int32 threshold = 1;
|
int32 threshold = 1;
|
||||||
string action = 2;
|
string action = 2;
|
||||||
int64 duration = 3;
|
int32 duration = 3;
|
||||||
|
string role = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message WarnSettingsReply {
|
message WarnSettingsReply {
|
||||||
repeated WarnPunishment punishments = 1;
|
repeated WarnPunishment punishments = 1;
|
||||||
int32 expiryDays = 2;
|
int32 expiryDays = 2;
|
||||||
|
bool deleteOnExpire = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddWarnpRequest {
|
message AddWarnpRequest {
|
||||||
@@ -38,7 +46,7 @@ message AddWarnpReply {
|
|||||||
|
|
||||||
message DeleteWarnpRequest {
|
message DeleteWarnpRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
int32 warnpIndex = 2;
|
int32 threshold = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DeleteWarnpReply {
|
message DeleteWarnpReply {
|
||||||
@@ -47,37 +55,53 @@ message DeleteWarnpReply {
|
|||||||
|
|
||||||
message GetUserWarningsRequest {
|
message GetUserWarningsRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
uint64 user_id = 2;
|
string user = 2;
|
||||||
|
int32 page = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetUserWarningsReply {
|
message GetUserWarningsReply {
|
||||||
repeated Warning warnings = 1;
|
repeated Warning warnings = 1;
|
||||||
|
int32 totalCount = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Warning {
|
message Warning {
|
||||||
int32 id = 1;
|
string id = 1;
|
||||||
string reason = 2;
|
string reason = 2;
|
||||||
int64 timestamp = 3;
|
int64 timestamp = 3;
|
||||||
int64 expiry_timestamp = 4;
|
int64 weight = 4;
|
||||||
bool cleared = 5;
|
bool forgiven = 5;
|
||||||
string clearedBy = 6;
|
string forgivenBy = 6;
|
||||||
|
string user = 7;
|
||||||
|
uint64 userId = 8;
|
||||||
|
string moderator = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ClearWarningRequest {
|
message ForgiveWarningRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
uint64 userId = 2;
|
string warnId = 2;
|
||||||
optional int32 warnId = 3;
|
string modName = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ClearWarningReply {
|
message ForgiveWarningReply {
|
||||||
bool success = 1;
|
bool success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SetWarnExpiryRequest {
|
message SetWarnExpiryRequest {
|
||||||
uint64 guildId = 1;
|
uint64 guildId = 1;
|
||||||
int32 expiryDays = 2;
|
int32 expiryDays = 2;
|
||||||
|
bool deleteOnExpire = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SetWarnExpiryReply {
|
message SetWarnExpiryReply {
|
||||||
bool success = 1;
|
bool success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetLatestWarningsRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
int32 page = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestWarningsReply {
|
||||||
|
repeated Warning warnings = 1;
|
||||||
|
int32 totalCount = 2;
|
||||||
|
}
|
@@ -88,13 +88,6 @@ public static class DiscordUserExtensions
|
|||||||
.Count()
|
.Count()
|
||||||
+ 1;
|
+ 1;
|
||||||
|
|
||||||
public static async Task<IReadOnlyCollection<DiscordUser>> GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page, int perPage)
|
|
||||||
=> await users.ToLinqToDBTable()
|
|
||||||
.OrderByDescending(x => x.TotalXp)
|
|
||||||
.Skip(page * perPage)
|
|
||||||
.Take(perPage)
|
|
||||||
.ToArrayAsyncLinqToDB();
|
|
||||||
|
|
||||||
public static Task<List<DiscordUser>> GetTopRichest(
|
public static Task<List<DiscordUser>> GetTopRichest(
|
||||||
this DbSet<DiscordUser> users,
|
this DbSet<DiscordUser> users,
|
||||||
ulong botId,
|
ulong botId,
|
||||||
|
@@ -57,8 +57,7 @@ public static class GuildConfigExtensions
|
|||||||
List<ulong> availableGuilds)
|
List<ulong> availableGuilds)
|
||||||
{
|
{
|
||||||
var result = await configs
|
var result = await configs
|
||||||
.AsQueryable()
|
.IncludeEverything()
|
||||||
.Include(x => x.CommandCooldowns)
|
|
||||||
.Where(x => availableGuilds.Contains(x.GuildId))
|
.Where(x => availableGuilds.Contains(x.GuildId))
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
@@ -96,7 +95,6 @@ public static class GuildConfigExtensions
|
|||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Permissions = Permissionv2.GetDefaultPermlist,
|
Permissions = Permissionv2.GetDefaultPermlist,
|
||||||
WarningsInitialized = true,
|
WarningsInitialized = true,
|
||||||
WarnPunishments = DefaultWarnPunishments
|
|
||||||
});
|
});
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
@@ -104,7 +102,6 @@ public static class GuildConfigExtensions
|
|||||||
if (!config.WarningsInitialized)
|
if (!config.WarningsInitialized)
|
||||||
{
|
{
|
||||||
config.WarningsInitialized = true;
|
config.WarningsInitialized = true;
|
||||||
config.WarnPunishments = DefaultWarnPunishments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
@@ -26,17 +26,6 @@ public static class UserXpExtensions
|
|||||||
return usr;
|
return usr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IReadOnlyCollection<UserXpStats>> GetUsersFor(
|
|
||||||
this DbSet<UserXpStats> xps,
|
|
||||||
ulong guildId,
|
|
||||||
int page)
|
|
||||||
=> await xps.ToLinqToDBTable()
|
|
||||||
.Where(x => x.GuildId == guildId)
|
|
||||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
|
||||||
.Skip(page * 9)
|
|
||||||
.Take(9)
|
|
||||||
.ToArrayAsyncLinqToDB();
|
|
||||||
|
|
||||||
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
||||||
=> await xps.ToLinqToDBTable()
|
=> await xps.ToLinqToDBTable()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
|
@@ -77,7 +77,6 @@ public class GuildConfig : DbEntity
|
|||||||
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
|
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
|
||||||
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
|
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
|
||||||
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
|
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
|
||||||
public List<WarningPunishment> WarnPunishments { get; set; } = new();
|
|
||||||
public bool WarningsInitialized { get; set; }
|
public bool WarningsInitialized { get; set; }
|
||||||
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
|
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
|
||||||
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
|
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
|
||||||
|
@@ -3,6 +3,7 @@ namespace NadekoBot.Db.Models;
|
|||||||
|
|
||||||
public class WarningPunishment : DbEntity
|
public class WarningPunishment : DbEntity
|
||||||
{
|
{
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
public PunishmentAction Punishment { get; set; }
|
public PunishmentAction Punishment { get; set; }
|
||||||
public int Time { get; set; }
|
public int Time { get; set; }
|
||||||
|
@@ -62,7 +62,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
|
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
|
||||||
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
|
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
|
||||||
|
|
||||||
// todo add guild colors
|
|
||||||
// public DbSet<GuildColors> GuildColors { get; set; }
|
// public DbSet<GuildColors> GuildColors { get; set; }
|
||||||
|
|
||||||
|
|
||||||
@@ -195,11 +194,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
.WithOne()
|
.WithOne()
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<GuildConfig>()
|
|
||||||
.HasMany(x => x.WarnPunishments)
|
|
||||||
.WithOne()
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<GuildConfig>()
|
modelBuilder.Entity<GuildConfig>()
|
||||||
.HasMany(x => x.SlowmodeIgnoredRoles)
|
.HasMany(x => x.SlowmodeIgnoredRoles)
|
||||||
.WithOne()
|
.WithOne()
|
||||||
@@ -277,6 +271,18 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region WarningPunishments
|
||||||
|
|
||||||
|
var warnpunishmentEntity = modelBuilder.Entity<WarningPunishment>(b =>
|
||||||
|
{
|
||||||
|
b.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.Count
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Self Assignable Roles
|
#region Self Assignable Roles
|
||||||
|
|
||||||
@@ -339,6 +345,7 @@ public abstract class NadekoContext : DbContext
|
|||||||
du.HasIndex(x => x.TotalXp);
|
du.HasIndex(x => x.TotalXp);
|
||||||
du.HasIndex(x => x.CurrencyAmount);
|
du.HasIndex(x => x.CurrencyAmount);
|
||||||
du.HasIndex(x => x.UserId);
|
du.HasIndex(x => x.UserId);
|
||||||
|
du.HasIndex(x => x.Username);
|
||||||
});
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@@ -38,6 +38,7 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
|
|||||||
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
|
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
|
||||||
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
|
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
|
||||||
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
|
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
|
||||||
|
DELETE FROM "Permissions" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,4 +66,20 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
|
|||||||
WHERE SendBoostMessage = TRUE;
|
WHERE SendBoostMessage = TRUE;
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddGuildIdsToWarningPunishment(MigrationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Sql("""
|
||||||
|
DELETE FROM WarningPunishment WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
|
||||||
|
UPDATE WarningPunishment
|
||||||
|
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE Id = GuildConfigId);
|
||||||
|
|
||||||
|
DELETE FROM WarningPunishment as wp
|
||||||
|
WHERE (wp.Count, wp.GuildConfigId) in (
|
||||||
|
SELECT wp2.Count, wp2.GuildConfigId FROM WarningPunishment as wp2
|
||||||
|
GROUP BY wp2.Count, wp2.GuildConfigId
|
||||||
|
HAVING COUNT(id) > 1
|
||||||
|
);
|
||||||
|
""");
|
||||||
|
}
|
||||||
}
|
}
|
3778
src/NadekoBot/Migrations/PostgreSql/20241018004623_warn-split.Designer.cs
generated
Normal file
3778
src/NadekoBot/Migrations/PostgreSql/20241018004623_warn-split.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class warnsplit : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "guildid",
|
||||||
|
table: "warningpunishment",
|
||||||
|
type: "numeric(20,0)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0m);
|
||||||
|
|
||||||
|
MigrationQueries.AddGuildIdsToWarningPunishment(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||||
|
table: "warningpunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_warningpunishment_guildconfigid",
|
||||||
|
table: "warningpunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "guildconfigid",
|
||||||
|
table: "warningpunishment");
|
||||||
|
|
||||||
|
migrationBuilder.AddUniqueConstraint(
|
||||||
|
name: "ak_warningpunishment_guildid_count",
|
||||||
|
table: "warningpunishment",
|
||||||
|
columns: new[] { "guildid", "count" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropUniqueConstraint(
|
||||||
|
name: "ak_warningpunishment_guildid_count",
|
||||||
|
table: "warningpunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "guildid",
|
||||||
|
table: "warningpunishment");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "guildconfigid",
|
||||||
|
table: "warningpunishment",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_warningpunishment_guildconfigid",
|
||||||
|
table: "warningpunishment",
|
||||||
|
column: "guildconfigid");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||||
|
table: "warningpunishment",
|
||||||
|
column: "guildconfigid",
|
||||||
|
principalTable: "guildconfigs",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2938,9 +2938,9 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnType("timestamp without time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildconfigid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<int>("Punishment")
|
b.Property<int>("Punishment")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
@@ -2957,8 +2957,8 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_warningpunishment");
|
.HasName("pk_warningpunishment");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId")
|
b.HasAlternateKey("GuildId", "Count")
|
||||||
.HasDatabaseName("ix_warningpunishment_guildconfigid");
|
.HasName("ak_warningpunishment_guildid_count");
|
||||||
|
|
||||||
b.ToTable("warningpunishment", (string)null);
|
b.ToTable("warningpunishment", (string)null);
|
||||||
});
|
});
|
||||||
@@ -3616,15 +3616,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.WarningPunishment", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
|
||||||
.WithMany("WarnPunishments")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.HasConstraintName("fk_warningpunishment_guildconfigs_guildconfigid");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.XpCurrencyReward", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.XpCurrencyReward", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
|
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
|
||||||
@@ -3740,8 +3731,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
b.Navigation("VcRoleInfos");
|
b.Navigation("VcRoleInfos");
|
||||||
|
|
||||||
b.Navigation("WarnPunishments");
|
|
||||||
|
|
||||||
b.Navigation("XpSettings");
|
b.Navigation("XpSettings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
2919
src/NadekoBot/Migrations/Sqlite/20241018004612_warn-split.Designer.cs
generated
Normal file
2919
src/NadekoBot/Migrations/Sqlite/20241018004612_warn-split.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
72
src/NadekoBot/Migrations/Sqlite/20241018004612_warn-split.cs
Normal file
72
src/NadekoBot/Migrations/Sqlite/20241018004612_warn-split.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class warnsplit : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "GuildId",
|
||||||
|
table: "WarningPunishment",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0ul);
|
||||||
|
|
||||||
|
MigrationQueries.AddGuildIdsToWarningPunishment(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
|
||||||
|
table: "WarningPunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_WarningPunishment_GuildConfigId",
|
||||||
|
table: "WarningPunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GuildConfigId",
|
||||||
|
table: "WarningPunishment");
|
||||||
|
|
||||||
|
migrationBuilder.AddUniqueConstraint(
|
||||||
|
name: "AK_WarningPunishment_GuildId_Count",
|
||||||
|
table: "WarningPunishment",
|
||||||
|
columns: new[] { "GuildId", "Count" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropUniqueConstraint(
|
||||||
|
name: "AK_WarningPunishment_GuildId_Count",
|
||||||
|
table: "WarningPunishment");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GuildId",
|
||||||
|
table: "WarningPunishment");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "GuildConfigId",
|
||||||
|
table: "WarningPunishment",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_WarningPunishment_GuildConfigId",
|
||||||
|
table: "WarningPunishment",
|
||||||
|
column: "GuildConfigId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
|
||||||
|
table: "WarningPunishment",
|
||||||
|
column: "GuildConfigId",
|
||||||
|
principalTable: "GuildConfigs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2183,7 +2183,7 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("Punishment")
|
b.Property<int>("Punishment")
|
||||||
@@ -2197,7 +2197,7 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId");
|
b.HasAlternateKey("GuildId", "Count");
|
||||||
|
|
||||||
b.ToTable("WarningPunishment");
|
b.ToTable("WarningPunishment");
|
||||||
});
|
});
|
||||||
@@ -2760,14 +2760,6 @@ namespace NadekoBot.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.WarningPunishment", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
|
||||||
.WithMany("WarnPunishments")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.XpCurrencyReward", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.XpCurrencyReward", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
|
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
|
||||||
@@ -2880,8 +2872,6 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Navigation("VcRoleInfos");
|
b.Navigation("VcRoleInfos");
|
||||||
|
|
||||||
b.Navigation("WarnPunishments");
|
|
||||||
|
|
||||||
b.Navigation("XpSettings");
|
b.Navigation("XpSettings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
using LinqToDB;
|
using LinqToDB;
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
@@ -11,7 +10,7 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
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, INService
|
|||||||
_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;
|
||||||
@@ -37,8 +36,6 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo GUILDS
|
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
var creds = _creds.GetCreds();
|
var creds = _creds.GetCreds();
|
||||||
@@ -51,6 +48,18 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
_enabled = items
|
_enabled = items
|
||||||
.ToDictionary(x => x.GuildId, x => x.ChannelId)
|
.ToDictionary(x => x.GuildId, x => x.ChannelId)
|
||||||
.ToConcurrent();
|
.ToConcurrent();
|
||||||
|
|
||||||
|
_client.LeftGuild += ClientOnLeftGuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClientOnLeftGuild(SocketGuild guild)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
_enabled.TryRemove(guild.Id, out _);
|
||||||
|
|
||||||
|
await ctx.GetTable<AutoPublishChannel>()
|
||||||
|
.Where(x => x.GuildId == guild.Id)
|
||||||
|
.DeleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ToggleAutoPublish(ulong guildId, ulong channelId)
|
public async Task<bool> ToggleAutoPublish(ulong guildId, ulong channelId)
|
||||||
|
@@ -207,6 +207,18 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
.Contains(x.GuildId))
|
.Contains(x.GuildId))
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete autopublish channels
|
||||||
|
await ctx.GetTable<AutoPublishChannel>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete greet settings
|
||||||
|
await ctx.GetTable<GreetSettings>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
GuildCount = guildIds.Keys.Count,
|
GuildCount = guildIds.Keys.Count,
|
||||||
|
@@ -139,7 +139,6 @@ public partial class Administration
|
|||||||
public Task DeleteXp()
|
public Task DeleteXp()
|
||||||
=> ConfirmActionInternalAsync("Delete Xp", () => _xcs.DeleteXp());
|
=> ConfirmActionInternalAsync("Delete Xp", () => _xcs.DeleteXp());
|
||||||
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public Task DeleteWaifus()
|
public Task DeleteWaifus()
|
||||||
|
@@ -200,9 +200,7 @@ public partial class Administration
|
|||||||
|
|
||||||
if (!isEnabled)
|
if (!isEnabled)
|
||||||
{
|
{
|
||||||
var cmdName = GetCmdName(type);
|
await SendGreetEnableHint(type);
|
||||||
|
|
||||||
await Response().Pending(strs.boostmsg_enable($"`{prefix}{cmdName}`")).SendAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,18 +224,23 @@ public partial class Administration
|
|||||||
await _service.Test(ctx.Guild.Id, type, (ITextChannel)ctx.Channel, user);
|
await _service.Test(ctx.Guild.Id, type, (ITextChannel)ctx.Channel, user);
|
||||||
var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
|
var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
|
||||||
|
|
||||||
|
if (conf?.IsEnabled is not true)
|
||||||
|
await SendGreetEnableHint(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendGreetEnableHint(GreetType type)
|
||||||
|
{
|
||||||
var cmd = $"`{prefix}{GetCmdName(type)}`";
|
var cmd = $"`{prefix}{GetCmdName(type)}`";
|
||||||
|
|
||||||
var str = type switch
|
var str = type switch
|
||||||
{
|
{
|
||||||
GreetType.Greet => strs.boostmsg_enable(cmd),
|
GreetType.Greet => strs.greetmsg_enable(cmd),
|
||||||
GreetType.Bye => strs.greetmsg_enable(cmd),
|
GreetType.Bye => strs.byemsg_enable(cmd),
|
||||||
GreetType.Boost => strs.byemsg_enable(cmd),
|
GreetType.Boost => strs.boostmsg_enable(cmd),
|
||||||
GreetType.GreetDm => strs.greetdmmsg_enable(cmd),
|
GreetType.GreetDm => strs.greetdmmsg_enable(cmd),
|
||||||
_ => strs.error
|
_ => strs.error
|
||||||
};
|
};
|
||||||
|
|
||||||
if (conf?.IsEnabled is not true)
|
|
||||||
await Response().Pending(str).SendAsync();
|
await Response().Pending(str).SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -85,7 +85,8 @@ public partial class Administration
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Warning(ex, "Exception occured while warning a user");
|
Log.Warning(ex, "Exception occured while warning a user");
|
||||||
var errorEmbed = _sender.CreateEmbed().WithErrorColor()
|
var errorEmbed = _sender.CreateEmbed()
|
||||||
|
.WithErrorColor()
|
||||||
.WithDescription(GetText(strs.cant_apply_punishment));
|
.WithDescription(GetText(strs.cant_apply_punishment));
|
||||||
|
|
||||||
if (dmFailed)
|
if (dmFailed)
|
||||||
@@ -117,7 +118,7 @@ public partial class Administration
|
|||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task WarnExpire()
|
public async Task WarnExpire()
|
||||||
{
|
{
|
||||||
var expireDays = await _service.GetWarnExpire(ctx.Guild.Id);
|
var (expireDays, _) = await _service.GetWarnExpire(ctx.Guild.Id);
|
||||||
|
|
||||||
if (expireDays == 0)
|
if (expireDays == 0)
|
||||||
await Response().Confirm(strs.warns_dont_expire).SendAsync();
|
await Response().Confirm(strs.warns_dont_expire).SendAsync();
|
||||||
@@ -287,7 +288,7 @@ public partial class Administration
|
|||||||
if (--index < 0)
|
if (--index < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var warn = await _service.WarnDelete(userId, index);
|
var warn = await _service.WarnDelete(ctx.Guild.Id, userId, index);
|
||||||
|
|
||||||
if (warn is null)
|
if (warn is null)
|
||||||
{
|
{
|
||||||
@@ -344,7 +345,7 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
|
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return;
|
return;
|
||||||
@@ -380,7 +381,7 @@ public partial class Administration
|
|||||||
if (punish is PunishmentAction.TimeOut && time is null)
|
if (punish is PunishmentAction.TimeOut && time is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return;
|
return;
|
||||||
@@ -407,7 +408,7 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
public async Task WarnPunish(int number)
|
public async Task WarnPunish(int number)
|
||||||
{
|
{
|
||||||
if (!_service.WarnPunishRemove(ctx.Guild.Id, number))
|
if (!await _service.WarnPunishRemove(ctx.Guild.Id, number))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Response().Confirm(strs.warn_punish_rem(Format.Bold(number.ToString()))).SendAsync();
|
await Response().Confirm(strs.warn_punish_rem(Format.Bold(number.ToString()))).SendAsync();
|
||||||
@@ -417,7 +418,7 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task WarnPunishList()
|
public async Task WarnPunishList()
|
||||||
{
|
{
|
||||||
var ps = _service.WarnPunishList(ctx.Guild.Id);
|
var ps = await _service.WarnPunishList(ctx.Guild.Id);
|
||||||
|
|
||||||
string list;
|
string list;
|
||||||
if (ps.Any())
|
if (ps.Any())
|
||||||
@@ -505,7 +506,8 @@ public partial class Administration
|
|||||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response()
|
||||||
|
.Embed(_sender.CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField("ID", userId.ToString(), true))
|
.AddField("ID", userId.ToString(), true))
|
||||||
@@ -920,8 +922,10 @@ public partial class Administration
|
|||||||
|
|
||||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_ban_completed(banning.Count())))
|
GetText(strs.mass_ban_completed(
|
||||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
banning.Count())))
|
||||||
|
.AddField(GetText(strs.invalid(missing.Count)),
|
||||||
|
missStr)
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using LinqToDB;
|
using LinqToDB;
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Common.TypeReaders.Models;
|
using NadekoBot.Common.TypeReaders.Models;
|
||||||
using NadekoBot.Modules.Permissions.Services;
|
using NadekoBot.Modules.Permissions.Services;
|
||||||
@@ -83,17 +82,24 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
};
|
};
|
||||||
|
|
||||||
long previousCount;
|
long previousCount;
|
||||||
List<WarningPunishment> ps;
|
var ps = await WarnPunishList(guildId);
|
||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
previousCount = uow.GetTable<Warning>()
|
||||||
|
.Where(w => w.GuildId == guildId && w.UserId == userId && !w.Forgiven)
|
||||||
previousCount = uow.Set<Warning>()
|
|
||||||
.ForId(guildId, userId)
|
|
||||||
.Where(w => !w.Forgiven && w.UserId == userId)
|
|
||||||
.Sum(x => x.Weight);
|
.Sum(x => x.Weight);
|
||||||
|
|
||||||
uow.Set<Warning>().Add(warn);
|
await uow.GetTable<Warning>()
|
||||||
|
.InsertAsync(() => new()
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
GuildId = guildId,
|
||||||
|
Forgiven = false,
|
||||||
|
Reason = reason,
|
||||||
|
Moderator = modName,
|
||||||
|
Weight = weight,
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
});
|
||||||
|
|
||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
@@ -324,11 +330,11 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> GetWarnExpire(ulong guildId)
|
public Task<(int, bool)> GetWarnExpire(ulong guildId)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
using var uow = _db.GetDbContext();
|
||||||
var config = uow.GuildConfigsForId(guildId, set => set);
|
var config = uow.GuildConfigsForId(guildId, set => set);
|
||||||
return Task.FromResult(config.WarnExpireHours / 24);
|
return Task.FromResult((config.WarnExpireHours / 24, config.WarnExpireAction == WarnExpireAction.Delete));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WarnExpireAsync(ulong guildId, int days, bool delete)
|
public async Task WarnExpireAsync(ulong guildId, int days, bool delete)
|
||||||
@@ -377,18 +383,19 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool WarnPunish(
|
public async Task<bool> WarnPunish(
|
||||||
ulong guildId,
|
ulong guildId,
|
||||||
int number,
|
int number,
|
||||||
PunishmentAction punish,
|
PunishmentAction punish,
|
||||||
StoopidTime time,
|
TimeSpan? time,
|
||||||
IRole role = null)
|
IRole role = null)
|
||||||
{
|
{
|
||||||
// these 3 don't make sense with time
|
// these 3 don't make sense with time
|
||||||
if (punish is PunishmentAction.Softban or PunishmentAction.Kick or PunishmentAction.RemoveRoles
|
if (punish is PunishmentAction.Softban or PunishmentAction.Kick or PunishmentAction.RemoveRoles
|
||||||
&& time is not null)
|
&& time is not null)
|
||||||
return false;
|
return false;
|
||||||
if (number <= 0 || (time is not null && time.Time > TimeSpan.FromDays(49)))
|
|
||||||
|
if (number <= 0 || (time is not null && time > TimeSpan.FromDays(59)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (punish is PunishmentAction.AddRole && role is null)
|
if (punish is PunishmentAction.AddRole && role is null)
|
||||||
@@ -397,47 +404,51 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
if (punish is PunishmentAction.TimeOut && time is null)
|
if (punish is PunishmentAction.TimeOut && time is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
using var uow = _db.GetDbContext();
|
var timeMinutes = (int?)time?.TotalMinutes ?? 0;
|
||||||
var ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
var roleId = punish == PunishmentAction.AddRole ? role!.Id : default(ulong?);
|
||||||
var toDelete = ps.Where(x => x.Count == number);
|
await using var uow = _db.GetDbContext();
|
||||||
|
await uow.GetTable<WarningPunishment>()
|
||||||
uow.RemoveRange(toDelete);
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
|
||||||
ps.Add(new()
|
|
||||||
{
|
{
|
||||||
|
GuildId = guildId,
|
||||||
Count = number,
|
Count = number,
|
||||||
Punishment = punish,
|
Punishment = punish,
|
||||||
Time = (int?)time?.Time.TotalMinutes ?? 0,
|
Time = timeMinutes,
|
||||||
RoleId = punish == PunishmentAction.AddRole ? role!.Id : default(ulong?)
|
RoleId = roleId
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
Punishment = punish,
|
||||||
|
Time = timeMinutes,
|
||||||
|
RoleId = roleId
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
Count = number
|
||||||
});
|
});
|
||||||
uow.SaveChanges();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool WarnPunishRemove(ulong guildId, int number)
|
public async Task<bool> WarnPunishRemove(ulong guildId, int count)
|
||||||
{
|
{
|
||||||
if (number <= 0)
|
await using var uow = _db.GetDbContext();
|
||||||
return false;
|
var numDeleted = await uow.GetTable<WarningPunishment>()
|
||||||
|
.DeleteAsync(x => x.GuildId == guildId && x.Count == count);
|
||||||
|
|
||||||
using var uow = _db.GetDbContext();
|
return numDeleted > 0;
|
||||||
var ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
|
||||||
var p = ps.FirstOrDefault(x => x.Count == number);
|
|
||||||
|
|
||||||
if (p is not null)
|
|
||||||
{
|
|
||||||
uow.Remove(p);
|
|
||||||
uow.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WarningPunishment[] WarnPunishList(ulong guildId)
|
public async Task<WarningPunishment[]> WarnPunishList(ulong guildId)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
return uow.GuildConfigsForId(guildId, gc => gc.Include(x => x.WarnPunishments))
|
|
||||||
.WarnPunishments.OrderBy(x => x.Count)
|
var wps = uow.GetTable<WarningPunishment>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderBy(x => x.Count)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
return wps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IReadOnlyCollection<(string Original, ulong? Id, string Reason)> Bans, int Missing) MassKill(
|
public (IReadOnlyCollection<(string Original, ulong? Id, string Reason)> Bans, int Missing) MassKill(
|
||||||
@@ -607,12 +618,12 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
return await _repSvc.ReplaceAsync(output, repCtx);
|
return await _repSvc.ReplaceAsync(output, repCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Warning> WarnDelete(ulong userId, int index)
|
public async Task<Warning> WarnDelete(ulong guildId, ulong userId, int index)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
var warn = await uow.GetTable<Warning>()
|
var warn = await uow.GetTable<Warning>()
|
||||||
.Where(x => x.UserId == userId)
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
.OrderByDescending(x => x.DateAdded)
|
.OrderByDescending(x => x.DateAdded)
|
||||||
.Skip(index)
|
.Skip(index)
|
||||||
.FirstOrDefaultAsyncLinqToDB();
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
@@ -626,4 +637,73 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
|
|
||||||
return warn;
|
return warn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> WarnDelete(ulong guildId, int id)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
var numDeleted = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Id == id)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return numDeleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(IReadOnlyCollection<Warning> latest, int totalCount)> GetLatestWarnings(
|
||||||
|
ulong guildId,
|
||||||
|
int page = 1)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(page);
|
||||||
|
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var latest = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderByDescending(x => x.DateAdded)
|
||||||
|
.Skip(10 * (page - 1))
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
var totalCount = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.CountAsyncLinqToDB();
|
||||||
|
|
||||||
|
return (latest, totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ForgiveWarning(ulong requestGuildId, int warnId, string modName)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var success = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == requestGuildId && x.Id == warnId)
|
||||||
|
.UpdateAsync(_ => new()
|
||||||
|
{
|
||||||
|
Forgiven = true,
|
||||||
|
ForgivenBy = modName,
|
||||||
|
})
|
||||||
|
== 1;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(IReadOnlyCollection<Warning> latest, int totalCount)> GetUserWarnings(
|
||||||
|
ulong guildId,
|
||||||
|
ulong userId,
|
||||||
|
int page)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(page);
|
||||||
|
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var latest = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
|
.OrderByDescending(x => x.DateAdded)
|
||||||
|
.Skip(10 * (page - 1))
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
var totalCount = await uow.GetTable<Warning>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
|
.CountAsyncLinqToDB();
|
||||||
|
|
||||||
|
return (latest, totalCount);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -67,7 +67,6 @@ public sealed class NadekoExpressionsService : 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 NadekoExpressionsService : 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 NadekoExpressionsService : IExecOnMessage, IReadyExecutor
|
|||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_client = client;
|
_client = client;
|
||||||
_cmd = cmd;
|
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
_bot = bot;
|
_bot = bot;
|
||||||
_pubSub = pubSub;
|
_pubSub = pubSub;
|
||||||
|
@@ -252,18 +252,18 @@ public partial class Gambling
|
|||||||
var claimer = "no one";
|
var claimer = "no one";
|
||||||
string status;
|
string status;
|
||||||
|
|
||||||
var waifuUsername = w.Username.TrimTo(20);
|
var waifuUsername = w.WaifuName.TrimTo(20);
|
||||||
var claimerUsername = w.Claimer?.TrimTo(20);
|
var claimerUsername = w.ClaimerName?.TrimTo(20);
|
||||||
|
|
||||||
if (w.Claimer is not null)
|
if (w.ClaimerName is not null)
|
||||||
claimer = $"{claimerUsername}#{w.ClaimerDiscrim}";
|
claimer = $"{claimerUsername}";
|
||||||
if (w.Affinity is null)
|
if (w.Affinity is null)
|
||||||
status = $"... but {waifuUsername}'s heart is empty";
|
status = $"... but {waifuUsername}'s heart is empty";
|
||||||
else if (w.Affinity + w.AffinityDiscrim == w.Claimer + w.ClaimerDiscrim)
|
else if (w.Affinity == w.ClaimerName)
|
||||||
status = $"... and {waifuUsername} likes {claimerUsername} too <3";
|
status = $"... and {waifuUsername} likes {claimerUsername} too <3";
|
||||||
else
|
else
|
||||||
status = $"... but {waifuUsername}'s heart belongs to {w.Affinity.TrimTo(20)}#{w.AffinityDiscrim}";
|
status = $"... but {waifuUsername}'s heart belongs to {w.Affinity.TrimTo(20)}";
|
||||||
return $"**{waifuUsername}#{w.Discrim}** - claimed by **{claimer}**\n\t{status}";
|
return $"**{waifuUsername}** - claimed by **{claimer}**\n\t{status}";
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@@ -40,12 +40,17 @@ public static class WaifuExtensions
|
|||||||
.Take(count)
|
.Take(count)
|
||||||
.Select(x => new WaifuLbResult
|
.Select(x => new WaifuLbResult
|
||||||
{
|
{
|
||||||
Affinity = x.Affinity == null ? null : x.Affinity.Username,
|
Affinity = x.Affinity == null
|
||||||
AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
|
? null
|
||||||
Claimer = x.Claimer == null ? null : x.Claimer.Username,
|
: x.Affinity.Username
|
||||||
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
+ (x.Affinity.Discriminator != "0000" ? "#" + x.Affinity.Discriminator : ""),
|
||||||
Username = x.Waifu.Username,
|
ClaimerName =
|
||||||
Discrim = x.Waifu.Discriminator,
|
x.Claimer == null
|
||||||
|
? null
|
||||||
|
: x.Claimer.Username
|
||||||
|
+ (x.Claimer.Discriminator != "0000" ? "#" + x.Claimer.Discriminator : ""),
|
||||||
|
WaifuName = x.Waifu.Username
|
||||||
|
+ (x.Waifu.Discriminator != "0000" ? "#" + x.Waifu.Discriminator : ""),
|
||||||
Price = x.Price
|
Price = x.Price
|
||||||
})
|
})
|
||||||
.ToListAsyncEF();
|
.ToListAsyncEF();
|
||||||
|
@@ -3,14 +3,11 @@ namespace NadekoBot.Db.Models;
|
|||||||
|
|
||||||
public class WaifuLbResult
|
public class WaifuLbResult
|
||||||
{
|
{
|
||||||
public string Username { get; set; }
|
public string WaifuName { get; set; }
|
||||||
public string Discrim { get; set; }
|
|
||||||
|
|
||||||
public string Claimer { get; set; }
|
public string ClaimerName { get; set; }
|
||||||
public string ClaimerDiscrim { get; set; }
|
|
||||||
|
|
||||||
public string Affinity { get; set; }
|
public string Affinity { get; set; }
|
||||||
public string AffinityDiscrim { get; set; }
|
|
||||||
|
|
||||||
public long Price { get; set; }
|
public long Price { get; set; }
|
||||||
}
|
}
|
@@ -100,10 +100,6 @@ public sealed class AiAssistantService
|
|||||||
|
|
||||||
using var client = _httpFactory.CreateClient();
|
using var client = _httpFactory.CreateClient();
|
||||||
|
|
||||||
// todo customize according to the bot's config
|
|
||||||
// - CurrencyName
|
|
||||||
// -
|
|
||||||
|
|
||||||
using var response = await client.SendAsync(request);
|
using var response = await client.SendAsync(request);
|
||||||
|
|
||||||
if (response.StatusCode == HttpStatusCode.TooManyRequests)
|
if (response.StatusCode == HttpStatusCode.TooManyRequests)
|
||||||
|
@@ -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,
|
||||||
|
@@ -169,6 +169,23 @@ public sealed class QuoteService : IQuoteService, INService
|
|||||||
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, INService
|
|||||||
|
|
||||||
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());
|
||||||
|
}
|
||||||
}
|
}
|
@@ -197,17 +197,20 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
|||||||
|
|
||||||
var st = SmartText.CreateFrom(r.Message);
|
var st = SmartText.CreateFrom(r.Message);
|
||||||
|
|
||||||
|
var res = _sender.Response(ch)
|
||||||
|
.UserBasedMentions(_client.GetGuild(r.ServerId)?.GetUser(r.UserId));
|
||||||
|
|
||||||
if (st is SmartEmbedText set)
|
if (st is SmartEmbedText set)
|
||||||
{
|
{
|
||||||
await _sender.Response(ch).Embed(set.GetEmbed()).SendAsync();
|
await res.Embed(set.GetEmbed()).SendAsync();
|
||||||
}
|
}
|
||||||
else if (st is SmartEmbedTextArray seta)
|
else if (st is SmartEmbedTextArray seta)
|
||||||
{
|
{
|
||||||
await _sender.Response(ch).Embeds(seta.GetEmbedBuilders()).SendAsync();
|
await res.Embeds(seta.GetEmbedBuilders()).SendAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _sender.Response(ch)
|
await res
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(_sender.CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("Reminder")
|
.WithTitle("Reminder")
|
||||||
|
@@ -107,7 +107,7 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
[Cmd]
|
[Cmd]
|
||||||
[UserPerm(GuildPerm.ManageChannels)]
|
[UserPerm(GuildPerm.ManageChannels)]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task XpExclude(Channel _, [Leftover] IChannel channel = null)
|
public async Task XpExclude(Channel _, [Leftover] IChannel? channel = null)
|
||||||
{
|
{
|
||||||
if (channel is null)
|
if (channel is null)
|
||||||
channel = ctx.Channel;
|
channel = ctx.Channel;
|
||||||
@@ -182,29 +182,28 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args);
|
var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args);
|
||||||
|
|
||||||
await ctx.Channel.TriggerTypingAsync();
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
var socketGuild = (SocketGuild)ctx.Guild;
|
|
||||||
var allCleanUsers = new List<UserXpStats>();
|
|
||||||
if (opts.Clean)
|
if (opts.Clean)
|
||||||
{
|
{
|
||||||
await ctx.Channel.TriggerTypingAsync();
|
|
||||||
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
|
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
|
||||||
|
|
||||||
allCleanUsers = (await _service.GetTopUserXps(ctx.Guild.Id, 1000))
|
|
||||||
.Where(user => socketGuild.GetUser(user.UserId) is not null)
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = opts.Clean
|
async Task<IReadOnlyCollection<UserXpStats>> GetPageItems(int curPage)
|
||||||
? Response()
|
{
|
||||||
.Paginated()
|
var socketGuild = (SocketGuild)ctx.Guild;
|
||||||
.Items(allCleanUsers)
|
if (opts.Clean)
|
||||||
: Response()
|
{
|
||||||
.Paginated()
|
return await _service.GetGuildUserXps(ctx.Guild.Id,
|
||||||
.PageItems((curPage) => _service.GetUserXps(ctx.Guild.Id, curPage));
|
socketGuild.Users.Select(x => x.Id).ToList(),
|
||||||
|
curPage);
|
||||||
|
}
|
||||||
|
|
||||||
await res
|
return await _service.GetGuildUserXps(ctx.Guild.Id, curPage);
|
||||||
.PageSize(9)
|
}
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.PageItems(GetPageItems)
|
||||||
|
.PageSize(10)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((users, curPage) =>
|
.Page((users, curPage) =>
|
||||||
{
|
{
|
||||||
@@ -237,15 +236,33 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task XpGlobalLeaderboard(int page = 1)
|
public async Task XpGlobalLeaderboard(int page = 1, params string[] args)
|
||||||
{
|
{
|
||||||
if (--page < 0 || page > 99)
|
if (--page < 0 || page > 99)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args);
|
||||||
|
|
||||||
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
if (opts.Clean)
|
||||||
|
{
|
||||||
|
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<IReadOnlyCollection<DiscordUser>> GetPageItems(int curPage)
|
||||||
|
{
|
||||||
|
if (opts.Clean)
|
||||||
|
{
|
||||||
|
return await _service.GetGlobalUserXps(page, ((SocketGuild)ctx.Guild).Users.Select(x => x.Id).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _service.GetGlobalUserXps(curPage);
|
||||||
|
}
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Paginated()
|
.Paginated()
|
||||||
.PageItems(async curPage => await _service.GetUserXps(curPage))
|
.PageItems(GetPageItems)
|
||||||
.PageSize(9)
|
.PageSize(10)
|
||||||
.Page((users, curPage) =>
|
.Page((users, curPage) =>
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = _sender.CreateEmbed()
|
||||||
@@ -282,7 +299,9 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
if (role.IsManaged)
|
if (role.IsManaged)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var count = await _service.AddXpToUsersAsync(ctx.Guild.Id, amount, role.Members.Select(x => x.Id).ToArray());
|
var count = await _service.AddXpToUsersAsync(ctx.Guild.Id,
|
||||||
|
amount,
|
||||||
|
role.Members.Select(x => x.Id).ToArray());
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(
|
.Confirm(
|
||||||
strs.xpadd_users(Format.Bold(amount.ToString()), Format.Bold(count.ToString())))
|
strs.xpadd_users(Format.Bold(amount.ToString()), Format.Bold(count.ToString())))
|
||||||
|
@@ -12,6 +12,7 @@ using SixLabors.ImageSharp.PixelFormats;
|
|||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using LinqToDB.Tools;
|
||||||
using NadekoBot.Modules.Patronage;
|
using NadekoBot.Modules.Patronage;
|
||||||
using Color = SixLabors.ImageSharp.Color;
|
using Color = SixLabors.ImageSharp.Color;
|
||||||
using Exception = System.Exception;
|
using Exception = System.Exception;
|
||||||
@@ -563,23 +564,50 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
uow.SaveChanges();
|
uow.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<UserXpStats>> GetUserXps(ulong guildId, int page)
|
public async Task<IReadOnlyCollection<UserXpStats>> GetGuildUserXps(ulong guildId, int page)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
return await uow.Set<UserXpStats>().GetUsersFor(guildId, page);
|
return await uow
|
||||||
|
.UserXpStats
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<UserXpStats>> GetTopUserXps(ulong guildId, int count)
|
public async Task<IReadOnlyCollection<UserXpStats>> GetGuildUserXps(ulong guildId, List<ulong> users, int page)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
return await uow.Set<UserXpStats>().GetTopUserXps(guildId, count);
|
return await uow.Set<UserXpStats>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId.In(users))
|
||||||
|
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IReadOnlyCollection<DiscordUser>> GetUserXps(int page, int perPage = 9)
|
public async Task<IReadOnlyCollection<DiscordUser>> GetGlobalUserXps(int page)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
return uow.Set<DiscordUser>()
|
|
||||||
.GetUsersXpLeaderboardFor(page, perPage);
|
return await uow.GetTable<DiscordUser>()
|
||||||
|
.OrderByDescending(x => x.TotalXp)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<DiscordUser>> GetGlobalUserXps(int page, List<ulong> users)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
return await uow.GetTable<DiscordUser>()
|
||||||
|
.Where(x => x.UserId.In(users))
|
||||||
|
.OrderByDescending(x => x.TotalXp)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ChangeNotificationType(ulong userId, ulong guildId, XpNotificationLocation type)
|
public async Task ChangeNotificationType(ulong userId, ulong guildId, XpNotificationLocation type)
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>true</ImplicitUsings>
|
<ImplicitUsings>true</ImplicitUsings>
|
||||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
<Version>5.1.13</Version>
|
<Version>5.1.15</Version>
|
||||||
|
|
||||||
<!-- Output/build -->
|
<!-- Output/build -->
|
||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
|
@@ -2,21 +2,34 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using NadekoBot.Modules.NadekoExpressions;
|
using NadekoBot.Modules.NadekoExpressions;
|
||||||
|
using NadekoBot.Modules.Utility;
|
||||||
|
|
||||||
namespace NadekoBot.GrpcApi;
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
public class ExprsSvc : GrpcExprs.GrpcExprsBase, INService
|
public class ExprsSvc : GrpcExprs.GrpcExprsBase, IGrpcSvc, INService
|
||||||
{
|
{
|
||||||
private readonly NadekoExpressionsService _svc;
|
private readonly NadekoExpressionsService _svc;
|
||||||
|
private readonly IQuoteService _qs;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
public ExprsSvc(NadekoExpressionsService svc)
|
public ExprsSvc(NadekoExpressionsService svc, IQuoteService qs, DiscordSocketClient client)
|
||||||
{
|
{
|
||||||
_svc = svc;
|
_svc = svc;
|
||||||
|
_qs = qs;
|
||||||
|
_client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
public ServerServiceDefinition Bind()
|
||||||
|
=> GrpcExprs.BindService(this);
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
NadekoExpression expr;
|
NadekoExpression expr;
|
||||||
if (!string.IsNullOrWhiteSpace(request.Expr.Id))
|
if (!string.IsNullOrWhiteSpace(request.Expr.Id))
|
||||||
{
|
{
|
||||||
@@ -45,7 +58,6 @@ public class ExprsSvc : GrpcExprs.GrpcExprsBase, INService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
public override async Task<GetExprsReply> GetExprs(GetExprsRequest request, ServerCallContext context)
|
public override async Task<GetExprsReply> GetExprs(GetExprsRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var (exprs, totalCount) = await _svc.FindExpressionsAsync(request.GuildId, request.Query, request.Page);
|
var (exprs, totalCount) = await _svc.FindExpressionsAsync(request.GuildId, request.Query, request.Page);
|
||||||
@@ -66,11 +78,75 @@ public class ExprsSvc : GrpcExprs.GrpcExprsBase, INService
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -3,7 +3,7 @@ using GreetType = NadekoBot.Services.GreetType;
|
|||||||
|
|
||||||
namespace NadekoBot.GrpcApi;
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, INService
|
public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IGrpcSvc, INService
|
||||||
{
|
{
|
||||||
private readonly GreetService _gs;
|
private readonly GreetService _gs;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
@@ -14,11 +14,8 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, INService
|
|||||||
_client = client;
|
_client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GreetSettings GetDefaultGreet(GreetType type)
|
public ServerServiceDefinition Bind()
|
||||||
=> new GreetSettings()
|
=> GrpcGreet.BindService(this);
|
||||||
{
|
|
||||||
GreetType = type
|
|
||||||
};
|
|
||||||
|
|
||||||
private static GrpcGreetSettings ToConf(GreetSettings? conf)
|
private static GrpcGreetSettings ToConf(GreetSettings? conf)
|
||||||
{
|
{
|
||||||
@@ -29,40 +26,37 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, INService
|
|||||||
{
|
{
|
||||||
Message = conf.MessageText,
|
Message = conf.MessageText,
|
||||||
Type = (GrpcGreetType)conf.GreetType,
|
Type = (GrpcGreetType)conf.GreetType,
|
||||||
ChannelId = conf.ChannelId ?? 0,
|
ChannelId = conf.ChannelId?.ToString() ?? string.Empty,
|
||||||
IsEnabled = conf.IsEnabled,
|
IsEnabled = conf.IsEnabled,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
public override async Task<GrpcGreetSettings> GetGreetSettings(GetGreetRequest request, ServerCallContext context)
|
||||||
public override async Task<GetGreetReply> GetGreetSettings(GetGreetRequest request, ServerCallContext context)
|
|
||||||
{
|
{
|
||||||
var guildId = request.GuildId;
|
var guildId = request.GuildId;
|
||||||
|
|
||||||
var greetConf = await _gs.GetGreetSettingsAsync(guildId, GreetType.Greet);
|
var conf = await _gs.GetGreetSettingsAsync(guildId, (GreetType)request.Type);
|
||||||
var byeConf = await _gs.GetGreetSettingsAsync(guildId, GreetType.Bye);
|
|
||||||
var boostConf = await _gs.GetGreetSettingsAsync(guildId, GreetType.Boost);
|
|
||||||
var greetDmConf = await _gs.GetGreetSettingsAsync(guildId, GreetType.GreetDm);
|
|
||||||
// todo timer
|
|
||||||
|
|
||||||
return new GetGreetReply()
|
return ToConf(conf);
|
||||||
{
|
|
||||||
Greet = ToConf(greetConf),
|
|
||||||
Bye = ToConf(byeConf),
|
|
||||||
Boost = ToConf(boostConf),
|
|
||||||
GreetDm = ToConf(greetDmConf)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
public override async Task<UpdateGreetReply> UpdateGreet(UpdateGreetRequest request, ServerCallContext context)
|
public override async Task<UpdateGreetReply> UpdateGreet(UpdateGreetRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var gid = request.GuildId;
|
var gid = request.GuildId;
|
||||||
var s = request.Settings;
|
var s = request.Settings;
|
||||||
var msg = s.Message;
|
var msg = s.Message;
|
||||||
|
|
||||||
|
var type = GetGreetType(s.Type);
|
||||||
|
|
||||||
await _gs.SetMessage(gid, GetGreetType(s.Type), msg);
|
await _gs.SetMessage(gid, GetGreetType(s.Type), msg);
|
||||||
await _gs.SetGreet(gid, s.ChannelId, GetGreetType(s.Type), s.IsEnabled);
|
await _gs.SetGreet(gid, ulong.Parse(s.ChannelId), type, s.IsEnabled);
|
||||||
|
var settings = await _gs.GetGreetSettingsAsync(gid, type);
|
||||||
|
|
||||||
|
if (settings is null)
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
@@ -70,7 +64,6 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, INService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
public override Task<TestGreetReply> TestGreet(TestGreetRequest request, ServerCallContext context)
|
public override Task<TestGreetReply> TestGreet(TestGreetRequest request, ServerCallContext context)
|
||||||
=> TestGreet(request.GuildId, request.ChannelId, request.UserId, request.Type);
|
=> TestGreet(request.GuildId, request.ChannelId, request.UserId, request.Type);
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ public static class GrpcApiExtensions
|
|||||||
=> ulong.Parse(context.RequestHeaders.FirstOrDefault(x => x.Key == "userid")!.Value);
|
=> ulong.Parse(context.RequestHeaders.FirstOrDefault(x => x.Key == "userid")!.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
public sealed class OtherSvc : GrpcOther.GrpcOtherBase, IGrpcSvc, INService
|
||||||
{
|
{
|
||||||
private readonly IDiscordClient _client;
|
private readonly IDiscordClient _client;
|
||||||
private readonly XpService _xp;
|
private readonly XpService _xp;
|
||||||
@@ -19,6 +19,7 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
private readonly WaifuService _waifus;
|
private readonly WaifuService _waifus;
|
||||||
private readonly ICoordinator _coord;
|
private readonly ICoordinator _coord;
|
||||||
private readonly IStatsService _stats;
|
private readonly IStatsService _stats;
|
||||||
|
private readonly IBotCache _cache;
|
||||||
|
|
||||||
public OtherSvc(
|
public OtherSvc(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
@@ -26,7 +27,8 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
ICurrencyService cur,
|
ICurrencyService cur,
|
||||||
WaifuService waifus,
|
WaifuService waifus,
|
||||||
ICoordinator coord,
|
ICoordinator coord,
|
||||||
IStatsService stats)
|
IStatsService stats,
|
||||||
|
IBotCache cache)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_xp = xp;
|
_xp = xp;
|
||||||
@@ -34,35 +36,41 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
_waifus = waifus;
|
_waifus = waifus;
|
||||||
_coord = coord;
|
_coord = coord;
|
||||||
_stats = stats;
|
_stats = stats;
|
||||||
|
_cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<GetGuildsReply> GetGuilds(Empty request, ServerCallContext context)
|
public ServerServiceDefinition Bind()
|
||||||
{
|
=> GrpcOther.BindService(this);
|
||||||
var guilds = await _client.GetGuildsAsync(CacheMode.CacheOnly);
|
|
||||||
|
|
||||||
var reply = new GetGuildsReply();
|
[GrpcNoAuthRequired]
|
||||||
var userId = context.GetUserId();
|
public override async Task<BotOnGuildReply> BotOnGuild(BotOnGuildRequest request, ServerCallContext context)
|
||||||
|
|
||||||
var toReturn = new List<IGuild>();
|
|
||||||
foreach (var g in guilds)
|
|
||||||
{
|
{
|
||||||
var user = await g.GetUserAsync(userId, CacheMode.AllowDownload);
|
var guild = await _client.GetGuildAsync(request.GuildId);
|
||||||
if (user.GuildPermissions.Has(GuildPermission.Administrator))
|
|
||||||
toReturn.Add(g);
|
|
||||||
}
|
|
||||||
|
|
||||||
reply.Guilds.AddRange(toReturn
|
var reply = new BotOnGuildReply
|
||||||
.Select(x => new GuildReply()
|
|
||||||
{
|
{
|
||||||
Id = x.Id,
|
Success = guild is not null
|
||||||
Name = x.Name,
|
};
|
||||||
IconUrl = x.IconUrl
|
|
||||||
}));
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetRolesReply> GetRoles(GetRolesRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var g = await _client.GetGuildAsync(request.GuildId);
|
||||||
|
var roles = g?.Roles;
|
||||||
|
var reply = new GetRolesReply();
|
||||||
|
reply.Roles.AddRange(roles?.Select(x => new RoleReply()
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Name = x.Name,
|
||||||
|
Color = x.Color.ToString(),
|
||||||
|
IconUrl = x.GetIconUrl() ?? string.Empty,
|
||||||
|
}) ?? new List<RoleReply>());
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
public override async Task<GetTextChannelsReply> GetTextChannels(
|
public override async Task<GetTextChannelsReply> GetTextChannels(
|
||||||
GetTextChannelsRequest request,
|
GetTextChannelsRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
@@ -81,6 +89,35 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GrpcNoAuthRequired]
|
||||||
|
public override async Task<GetGuildsReply> GetGuilds(Empty request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var guilds = await _client.GetGuildsAsync(CacheMode.CacheOnly);
|
||||||
|
|
||||||
|
var reply = new GetGuildsReply();
|
||||||
|
var userId = context.GetUserId();
|
||||||
|
|
||||||
|
var toReturn = new List<IGuild>();
|
||||||
|
foreach (var g in guilds)
|
||||||
|
{
|
||||||
|
var user = await g.GetUserAsync(userId);
|
||||||
|
if (user is not null && user.GuildPermissions.Has(GuildPermission.Administrator))
|
||||||
|
toReturn.Add(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.Guilds.AddRange(toReturn
|
||||||
|
.Select(x => new GuildReply()
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Name = x.Name,
|
||||||
|
IconUrl = x.IconUrl
|
||||||
|
}));
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[GrpcNoAuthRequired]
|
||||||
public override async Task<CurrencyLbReply> GetCurrencyLb(GetLbRequest request, ServerCallContext context)
|
public override async Task<CurrencyLbReply> GetCurrencyLb(GetLbRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var users = await _cur.GetTopRichest(_client.CurrentUser.Id, request.Page, request.PerPage);
|
var users = await _cur.GetTopRichest(_client.CurrentUser.Id, request.Page, request.PerPage);
|
||||||
@@ -103,9 +140,10 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GrpcNoAuthRequired]
|
||||||
public override async Task<XpLbReply> GetXpLb(GetLbRequest request, ServerCallContext context)
|
public override async Task<XpLbReply> GetXpLb(GetLbRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var users = await _xp.GetUserXps(request.Page, request.PerPage);
|
var users = await _xp.GetGlobalUserXps(request.Page);
|
||||||
|
|
||||||
var reply = new XpLbReply();
|
var reply = new XpLbReply();
|
||||||
|
|
||||||
@@ -127,6 +165,7 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GrpcNoAuthRequired]
|
||||||
public override async Task<WaifuLbReply> GetWaifuLb(GetLbRequest request, ServerCallContext context)
|
public override async Task<WaifuLbReply> GetWaifuLb(GetLbRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var waifus = await _waifus.GetTopWaifusAtPage(request.Page, request.PerPage);
|
var waifus = await _waifus.GetTopWaifusAtPage(request.Page, request.PerPage);
|
||||||
@@ -134,19 +173,23 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
var reply = new WaifuLbReply();
|
var reply = new WaifuLbReply();
|
||||||
reply.Entries.AddRange(waifus.Select(x => new WaifuLbEntry()
|
reply.Entries.AddRange(waifus.Select(x => new WaifuLbEntry()
|
||||||
{
|
{
|
||||||
ClaimedBy = x.Claimer ?? string.Empty,
|
ClaimedBy = x.ClaimerName ?? string.Empty,
|
||||||
IsMutual = x.Claimer == x.Affinity,
|
IsMutual = x.ClaimerName == x.Affinity,
|
||||||
Value = x.Price,
|
Value = x.Price,
|
||||||
User = x.Username,
|
User = x.WaifuName,
|
||||||
}));
|
}));
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<GetShardStatusesReply> GetShardStatuses(Empty request, ServerCallContext context)
|
[GrpcNoAuthRequired]
|
||||||
|
public override async Task<GetShardStatusesReply> GetShardStatuses(Empty request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var reply = new GetShardStatusesReply();
|
var reply = new GetShardStatusesReply();
|
||||||
|
|
||||||
// todo cache
|
await _cache.GetOrAddAsync<List<ShardStatus>>("coord:statuses",
|
||||||
|
() => Task.FromResult(_coord.GetAllShardStatuses().ToList())!,
|
||||||
|
TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
var shards = _coord.GetAllShardStatuses();
|
var shards = _coord.GetAllShardStatuses();
|
||||||
|
|
||||||
reply.Shards.AddRange(shards.Select(x => new ShardStatusReply()
|
reply.Shards.AddRange(shards.Select(x => new ShardStatusReply()
|
||||||
@@ -157,10 +200,10 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, INService
|
|||||||
LastUpdate = Timestamp.FromDateTime(x.LastUpdate),
|
LastUpdate = Timestamp.FromDateTime(x.LastUpdate),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return Task.FromResult(reply);
|
|
||||||
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
[GrpcApiPerm(GuildPerm.Administrator)]
|
|
||||||
public override async Task<GetServerInfoReply> GetServerInfo(ServerInfoRequest request, ServerCallContext context)
|
public override async Task<GetServerInfoReply> GetServerInfo(ServerInfoRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var info = await _stats.GetGuildInfoAsync(request.GuildId);
|
var info = await _stats.GetGuildInfoAsync(request.GuildId);
|
||||||
|
224
src/NadekoBot/Services/GrpcApi/WarnSvc.cs
Normal file
224
src/NadekoBot/Services/GrpcApi/WarnSvc.cs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
using Grpc.Core;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
using Enum = System.Enum;
|
||||||
|
|
||||||
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
|
public sealed class WarnSvc : GrpcWarn.GrpcWarnBase, IGrpcSvc, INService
|
||||||
|
{
|
||||||
|
private readonly UserPunishService _ups;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public WarnSvc(UserPunishService ups, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_ups = ups;
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerServiceDefinition Bind()
|
||||||
|
=> GrpcWarn.BindService(this);
|
||||||
|
|
||||||
|
public override async Task<WarnSettingsReply> GetWarnSettings(
|
||||||
|
WarnSettingsRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var list = await _ups.WarnPunishList(request.GuildId);
|
||||||
|
|
||||||
|
var wsr = new WarnSettingsReply();
|
||||||
|
|
||||||
|
(wsr.ExpiryDays, wsr.DeleteOnExpire) = await _ups.GetWarnExpire(request.GuildId);
|
||||||
|
|
||||||
|
wsr.Punishments.AddRange(list.Select(x => new WarnPunishment()
|
||||||
|
{
|
||||||
|
Action = x.Punishment.ToString(),
|
||||||
|
Duration = x.Time,
|
||||||
|
Threshold = x.Count,
|
||||||
|
Role = x.RoleId is ulong rid
|
||||||
|
? _client.GetGuild(request.GuildId)?.GetRole(rid)?.Name ?? x.RoleId?.ToString() ?? string.Empty
|
||||||
|
: string.Empty
|
||||||
|
}));
|
||||||
|
|
||||||
|
return wsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<SetWarnExpiryReply> SetWarnExpiry(
|
||||||
|
SetWarnExpiryRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (request.ExpiryDays > 366)
|
||||||
|
{
|
||||||
|
return new SetWarnExpiryReply()
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await _ups.WarnExpireAsync(request.GuildId, request.ExpiryDays, request.DeleteOnExpire);
|
||||||
|
|
||||||
|
return new SetWarnExpiryReply()
|
||||||
|
{
|
||||||
|
Success = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<DeleteWarnpReply> DeleteWarnp(DeleteWarnpRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var succ = await _ups.WarnPunishRemove(request.GuildId, request.Threshold);
|
||||||
|
|
||||||
|
return new DeleteWarnpReply
|
||||||
|
{
|
||||||
|
Success = succ
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AddWarnpReply> AddWarnp(AddWarnpRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (request.Punishment.Threshold <= 0)
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "Threshold must be greater than 0"));
|
||||||
|
|
||||||
|
var g = _client.GetGuild(request.GuildId);
|
||||||
|
|
||||||
|
if (g is null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "Guild not found"));
|
||||||
|
|
||||||
|
if (!Enum.TryParse<PunishmentAction>(request.Punishment.Action, out var action))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid action"));
|
||||||
|
|
||||||
|
IRole? role = null;
|
||||||
|
if (action == PunishmentAction.AddRole && ulong.TryParse(request.Punishment.Role, out var roleId))
|
||||||
|
{
|
||||||
|
role = g.GetRole(roleId);
|
||||||
|
|
||||||
|
if (role is null)
|
||||||
|
return new AddWarnpReply()
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!ulong.TryParse(context.RequestHeaders.GetValue("userid"), out var userId))
|
||||||
|
return new AddWarnpReply()
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await ((IGuild)g).GetUserAsync(userId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "User not found"));
|
||||||
|
|
||||||
|
var userMaxRole = user.GetRoles().MaxBy(x => x.Position)?.Position ?? 0;
|
||||||
|
if (g.OwnerId != user.Id && userMaxRole <= role.Position)
|
||||||
|
{
|
||||||
|
return new AddWarnpReply()
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var duration = TimeSpan.FromMinutes(request.Punishment.Duration);
|
||||||
|
|
||||||
|
var succ = await _ups.WarnPunish(request.GuildId,
|
||||||
|
request.Punishment.Threshold,
|
||||||
|
action,
|
||||||
|
duration,
|
||||||
|
role
|
||||||
|
);
|
||||||
|
|
||||||
|
return new AddWarnpReply()
|
||||||
|
{
|
||||||
|
Success = succ
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetLatestWarningsReply> GetLatestWarnings(
|
||||||
|
GetLatestWarningsRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var (latest, count) = await _ups.GetLatestWarnings(request.GuildId, request.Page);
|
||||||
|
|
||||||
|
var reply = new GetLatestWarningsReply()
|
||||||
|
{
|
||||||
|
TotalCount = count
|
||||||
|
};
|
||||||
|
|
||||||
|
reply.Warnings.AddRange(latest.Select(MapWarningToGrpcWarning));
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetUserWarningsReply> GetUserWarnings(
|
||||||
|
GetUserWarningsRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<Db.Models.Warning> latest = [];
|
||||||
|
var count = 0;
|
||||||
|
if (ulong.TryParse(request.User, out var userId))
|
||||||
|
{
|
||||||
|
(latest, count) = await _ups.GetUserWarnings(request.GuildId, userId, request.Page);
|
||||||
|
}
|
||||||
|
else if (_client.GetGuild(request.GuildId)?.Users.FirstOrDefault(x => x.Username == request.User) is { } user)
|
||||||
|
{
|
||||||
|
(latest, count) = await _ups.GetUserWarnings(request.GuildId, user.Id, request.Page);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply = new GetUserWarningsReply
|
||||||
|
{
|
||||||
|
TotalCount = count
|
||||||
|
};
|
||||||
|
|
||||||
|
reply.Warnings.AddRange(latest.Select(MapWarningToGrpcWarning));
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Warning MapWarningToGrpcWarning(Db.Models.Warning x)
|
||||||
|
{
|
||||||
|
return new Warning
|
||||||
|
{
|
||||||
|
Id = new kwum(x.Id).ToString(),
|
||||||
|
Forgiven = x.Forgiven,
|
||||||
|
ForgivenBy = x.ForgivenBy ?? string.Empty,
|
||||||
|
Reason = x.Reason ?? string.Empty,
|
||||||
|
Timestamp = x.DateAdded is { } da ? Nadeko.Common.Extensions.ToTimestamp(da) : 0,
|
||||||
|
Weight = x.Weight,
|
||||||
|
Moderator = x.Moderator ?? string.Empty,
|
||||||
|
User = _client.GetUser(x.UserId)?.Username ?? x.UserId.ToString(),
|
||||||
|
UserId = x.UserId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ForgiveWarningReply> ForgiveWarning(
|
||||||
|
ForgiveWarningRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!kwum.TryParse(request.WarnId, out var wid))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid warning ID"));
|
||||||
|
|
||||||
|
var succ = await _ups.ForgiveWarning(request.GuildId, wid, request.ModName);
|
||||||
|
|
||||||
|
return new ForgiveWarningReply
|
||||||
|
{
|
||||||
|
Success = succ
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ForgiveWarningReply> DeleteWarning(
|
||||||
|
ForgiveWarningRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!kwum.TryParse(request.WarnId, out var wid))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid warning ID"));
|
||||||
|
|
||||||
|
var succ = await _ups.WarnDelete(request.GuildId, wid);
|
||||||
|
|
||||||
|
return new ForgiveWarningReply
|
||||||
|
{
|
||||||
|
Success = succ
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
86
src/NadekoBot/Services/GrpcApiPermsInterceptor.cs
Normal file
86
src/NadekoBot/Services/GrpcApiPermsInterceptor.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using Grpc.Core;
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
|
|
||||||
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
|
public sealed partial class GrpcApiPermsInterceptor : Interceptor
|
||||||
|
{
|
||||||
|
private const GuildPerm DEFAULT_PERMISSION = GuildPermission.Administrator;
|
||||||
|
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public GrpcApiPermsInterceptor(DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
||||||
|
TRequest request,
|
||||||
|
ServerCallContext context,
|
||||||
|
UnaryServerMethod<TRequest, TResponse> continuation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = context.Method[(context.Method.LastIndexOf('/') + 1)..];
|
||||||
|
|
||||||
|
// get metadata
|
||||||
|
var metadata = context
|
||||||
|
.RequestHeaders
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
|
Log.Information("grpc | g: {GuildId} | u: {UserID} | cmd: {Method}",
|
||||||
|
metadata.TryGetValue("guildid", out var gidString) ? gidString : "none",
|
||||||
|
metadata.TryGetValue("userid", out var uidString) ? uidString : "none",
|
||||||
|
method);
|
||||||
|
|
||||||
|
|
||||||
|
// there always has to be a user who makes the call
|
||||||
|
if (!metadata.ContainsKey("userid"))
|
||||||
|
throw new RpcException(new(StatusCode.Unauthenticated, "userid has to be specified."));
|
||||||
|
|
||||||
|
// get the method name without the service name
|
||||||
|
|
||||||
|
// if the method is explicitly marked as not requiring auth
|
||||||
|
if (_noAuthRequired.Contains(method))
|
||||||
|
return await continuation(request, context);
|
||||||
|
|
||||||
|
// otherwise the method requires auth, and if it requires auth then the guildid has to be specified
|
||||||
|
if (string.IsNullOrWhiteSpace(gidString))
|
||||||
|
throw new RpcException(new(StatusCode.Unauthenticated, "guildid has to be specified."));
|
||||||
|
|
||||||
|
var userId = ulong.Parse(metadata["userid"]);
|
||||||
|
var guildId = ulong.Parse(gidString);
|
||||||
|
|
||||||
|
// check if the user has the required permission
|
||||||
|
if (_perms.TryGetValue(method, out var perm))
|
||||||
|
{
|
||||||
|
await EnsureUserHasPermission(guildId, userId, perm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if not then use the default, which is Administrator permission
|
||||||
|
await EnsureUserHasPermission(guildId, userId, DEFAULT_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await continuation(request, context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Error thrown by {ContextMethod}", context.Method);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureUserHasPermission(ulong guildId, ulong userId, GuildPerm perm)
|
||||||
|
{
|
||||||
|
IGuild guild = _client.GetGuild(guildId);
|
||||||
|
var user = guild is null ? null : await guild.GetUserAsync(userId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "User not found"));
|
||||||
|
|
||||||
|
if (!user.GuildPermissions.Has(perm))
|
||||||
|
throw new RpcException(new Status(StatusCode.PermissionDenied,
|
||||||
|
$"You need {perm} permission to use this method"));
|
||||||
|
}
|
||||||
|
}
|
@@ -9,22 +9,16 @@ public class GrpcApiService : INService, IReadyExecutor
|
|||||||
private Server? _app;
|
private Server? _app;
|
||||||
|
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
private readonly OtherSvc _other;
|
private readonly IEnumerable<IGrpcSvc> _svcs;
|
||||||
private readonly ExprsSvc _exprs;
|
|
||||||
private readonly GreetByeSvc _greet;
|
|
||||||
private readonly IBotCredsProvider _creds;
|
private readonly IBotCredsProvider _creds;
|
||||||
|
|
||||||
public GrpcApiService(
|
public GrpcApiService(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
OtherSvc other,
|
IEnumerable<IGrpcSvc> svcs,
|
||||||
ExprsSvc exprs,
|
|
||||||
GreetByeSvc greet,
|
|
||||||
IBotCredsProvider creds)
|
IBotCredsProvider creds)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_other = other;
|
_svcs = svcs;
|
||||||
_exprs = exprs;
|
|
||||||
_greet = greet;
|
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,28 +33,40 @@ public class GrpcApiService : INService, IReadyExecutor
|
|||||||
var host = creds.GrpcApi.Host;
|
var host = creds.GrpcApi.Host;
|
||||||
var port = creds.GrpcApi.Port + _client.ShardId;
|
var port = creds.GrpcApi.Port + _client.ShardId;
|
||||||
|
|
||||||
var interceptor = new PermsInterceptor(_client);
|
var interceptor = new GrpcApiPermsInterceptor(_client);
|
||||||
|
|
||||||
_app = new Server()
|
var serverCreds = ServerCredentials.Insecure;
|
||||||
|
|
||||||
|
if (creds.GrpcApi is
|
||||||
{
|
{
|
||||||
Services =
|
CertPrivateKey: not null and not "",
|
||||||
|
CertChain: not null and not ""
|
||||||
|
} cert)
|
||||||
|
{
|
||||||
|
serverCreds = new SslServerCredentials(
|
||||||
|
new[] { new KeyCertificatePair(cert.CertChain, cert.CertPrivateKey) });
|
||||||
|
}
|
||||||
|
|
||||||
|
_app = new()
|
||||||
{
|
{
|
||||||
GrpcOther.BindService(_other).Intercept(interceptor),
|
|
||||||
GrpcExprs.BindService(_exprs).Intercept(interceptor),
|
|
||||||
GrpcGreet.BindService(_greet).Intercept(interceptor),
|
|
||||||
},
|
|
||||||
Ports =
|
Ports =
|
||||||
{
|
{
|
||||||
new(host, port, ServerCredentials.Insecure),
|
new(host, port, serverCreds),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
foreach (var svc in _svcs)
|
||||||
|
{
|
||||||
|
_app.Services.Add(svc.Bind().Intercept(interceptor));
|
||||||
|
}
|
||||||
|
|
||||||
_app.Start();
|
_app.Start();
|
||||||
|
|
||||||
Log.Information("Grpc Api Server started on port {Host}:{Port}", host, port);
|
Log.Information("Grpc Api Server started on port {Host}:{Port}", host, port);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Log.Error(ex, "Error starting Grpc Api Server");
|
||||||
_app?.ShutdownAsync().GetAwaiter().GetResult();
|
_app?.ShutdownAsync().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
src/NadekoBot/Services/IGrpcSvc.cs
Normal file
8
src/NadekoBot/Services/IGrpcSvc.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Grpc.Core;
|
||||||
|
|
||||||
|
namespace NadekoBot.GrpcApi;
|
||||||
|
|
||||||
|
public interface IGrpcSvc
|
||||||
|
{
|
||||||
|
ServerServiceDefinition Bind();
|
||||||
|
}
|
@@ -1,67 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
namespace NadekoBot.GrpcApi;
|
|
||||||
|
|
||||||
public sealed partial class PermsInterceptor : Interceptor
|
|
||||||
{
|
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
|
|
||||||
public PermsInterceptor(DiscordSocketClient client)
|
|
||||||
{
|
|
||||||
_client = client;
|
|
||||||
Log.Information("interceptor created");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
|
||||||
TRequest request,
|
|
||||||
ServerCallContext context,
|
|
||||||
UnaryServerMethod<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Information("Starting receiving call. Type/Method: {Type} / {Method}",
|
|
||||||
MethodType.Unary,
|
|
||||||
context.Method);
|
|
||||||
|
|
||||||
// get metadata
|
|
||||||
var metadata = context
|
|
||||||
.RequestHeaders
|
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
|
|
||||||
|
|
||||||
var method = context.Method[(context.Method.LastIndexOf('/') + 1)..];
|
|
||||||
|
|
||||||
if (perms.TryGetValue(method, out var perm))
|
|
||||||
{
|
|
||||||
Log.Information("Required permission for {Method} is {Perm}",
|
|
||||||
method,
|
|
||||||
perm);
|
|
||||||
|
|
||||||
var userId = ulong.Parse(metadata["userid"]);
|
|
||||||
var guildId = ulong.Parse(metadata["guildid"]);
|
|
||||||
|
|
||||||
IGuild guild = _client.GetGuild(guildId);
|
|
||||||
var user = guild is null ? null : await guild.GetUserAsync(userId);
|
|
||||||
|
|
||||||
if (user is null)
|
|
||||||
throw new RpcException(new Status(StatusCode.NotFound, "User not found"));
|
|
||||||
|
|
||||||
if (!user.GuildPermissions.Has(perm))
|
|
||||||
throw new RpcException(new Status(StatusCode.PermissionDenied,
|
|
||||||
$"You need {perm} permission to use this method"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Information("No permission required for {Method}", method);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await continuation(request, context);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Error thrown by {ContextMethod}", context.Method);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,7 +6,7 @@ namespace NadekoBot.Common;
|
|||||||
public sealed class Creds : IBotCreds
|
public sealed class Creds : IBotCreds
|
||||||
{
|
{
|
||||||
[Comment("""DO NOT CHANGE""")]
|
[Comment("""DO NOT CHANGE""")]
|
||||||
public int Version { get; set; } = 12;
|
public int Version { get; set; } = 13;
|
||||||
|
|
||||||
[Comment("""Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/""")]
|
[Comment("""Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/""")]
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
@@ -292,8 +292,8 @@ public sealed class Creds : IBotCreds
|
|||||||
public sealed record GrpcApiConfig
|
public sealed record GrpcApiConfig
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; } = false;
|
public bool Enabled { get; set; } = false;
|
||||||
public string CertPath { get; set; } = string.Empty;
|
public string CertChain { get; set; } = string.Empty;
|
||||||
public string CertPassword { get; set; } = string.Empty;
|
public string CertPrivateKey { get; set; } = string.Empty;
|
||||||
public string Host { get; set; } = "localhost";
|
public string Host { get; set; } = "localhost";
|
||||||
public int Port { get; set; } = 43120;
|
public int Port { get; set; } = 43120;
|
||||||
}
|
}
|
||||||
|
@@ -140,9 +140,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
creds.BotCache = BotCacheImplemenation.Memory;
|
creds.BotCache = BotCacheImplemenation.Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creds.Version < 12)
|
if (creds.Version < 13)
|
||||||
{
|
{
|
||||||
creds.Version = 12;
|
creds.Version = 13;
|
||||||
File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
|
File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -287,9 +287,9 @@ public sealed partial class ResponseBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseBuilder UserBasedMentions()
|
public ResponseBuilder UserBasedMentions(IGuildUser? permUser = null)
|
||||||
{
|
{
|
||||||
sanitizeMentions = !((InternalResolveUser() as IGuildUser)?.GuildPermissions.MentionEveryone ?? false);
|
sanitizeMentions = !((InternalResolveUser() as IGuildUser ?? permUser)?.GuildPermissions.MentionEveryone ?? false);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -69,6 +69,7 @@ public abstract record SmartEmbedTextBase : SmartText
|
|||||||
=> !string.IsNullOrWhiteSpace(Title)
|
=> !string.IsNullOrWhiteSpace(Title)
|
||||||
|| !string.IsNullOrWhiteSpace(Description)
|
|| !string.IsNullOrWhiteSpace(Description)
|
||||||
|| !string.IsNullOrWhiteSpace(Url)
|
|| !string.IsNullOrWhiteSpace(Url)
|
||||||
|
|| !string.IsNullOrWhiteSpace(Author?.Name)
|
||||||
|| !string.IsNullOrWhiteSpace(Thumbnail)
|
|| !string.IsNullOrWhiteSpace(Thumbnail)
|
||||||
|| !string.IsNullOrWhiteSpace(Image)
|
|| !string.IsNullOrWhiteSpace(Image)
|
||||||
|| (Footer is not null
|
|| (Footer is not null
|
||||||
|
@@ -52,4 +52,14 @@ public class StoopidTime
|
|||||||
Time = ts
|
Time = ts
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator TimeSpan(StoopidTime st)
|
||||||
|
=> st.Time;
|
||||||
|
|
||||||
|
public static implicit operator StoopidTime(TimeSpan ts)
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Input = ts.ToString(),
|
||||||
|
Time = ts
|
||||||
|
};
|
||||||
}
|
}
|
@@ -365,7 +365,6 @@ quoteshow:
|
|||||||
- qushow
|
- qushow
|
||||||
quotesearch:
|
quotesearch:
|
||||||
- quotesearch
|
- quotesearch
|
||||||
- qs
|
|
||||||
- qse
|
- qse
|
||||||
- qsearch
|
- qsearch
|
||||||
quoteid:
|
quoteid:
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
# DO NOT CHANGE
|
# DO NOT CHANGE
|
||||||
version: 1
|
version: 1
|
||||||
# List of medusae automatically loaded at startup
|
# List of medusae automatically loaded at startup
|
||||||
loaded:
|
loaded: []
|
||||||
- ngrpc
|
|
Reference in New Issue
Block a user