split warn punishments into a separate table

Added warn endpoints
Reminders should now be able to ping everyone if the user who created the reminder has that permission
This commit is contained in:
Toastie (DCS Team) 2024-10-23 19:20:55 +13:00
parent aca7ace527
commit 17d4d2a925
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
27 changed files with 7434 additions and 214 deletions

View file

@ -35,7 +35,7 @@ enum GrpcGreetType {
} }
message UpdateGreetReply { message UpdateGreetReply {
GrpcGreetSettings settings = 1; bool Success = 1;
} }
message TestGreetRequest { message TestGreetRequest {

View file

@ -11,6 +11,7 @@ service GrpcOther {
rpc BotOnGuild(BotOnGuildRequest) returns (BotOnGuildReply); 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,14 @@ 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 { message BotOnGuildRequest {
uint64 guildId = 1; uint64 guildId = 1;
} }

View file

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

View file

@ -194,11 +194,6 @@ public abstract class EllieContext : 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()
@ -276,6 +271,18 @@ public abstract class EllieContext : 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
@ -338,6 +345,7 @@ public abstract class EllieContext : 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

View file

@ -96,7 +96,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 +103,6 @@ public static class GuildConfigExtensions
if (!config.WarningsInitialized) if (!config.WarningsInitialized)
{ {
config.WarningsInitialized = true; config.WarningsInitialized = true;
config.WarnPunishments = DefaultWarnPunishments;
} }
return config; return config;

View file

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

View file

@ -3,6 +3,7 @@ namespace EllieBot.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; }

View file

@ -66,4 +66,19 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
WHERE SendBoostMessage = TRUE; WHERE SendBoostMessage = TRUE;
"""); """);
} }
public static void AddGuildIdsToWarningPunishment(MigrationBuilder builder)
{
builder.Sql("""
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
);
""");
}
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.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);
}
}
}

View file

@ -2938,9 +2938,9 @@ namespace EllieBot.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 EllieBot.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 EllieBot.Migrations.PostgreSql
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("EllieBot.Db.Models.WarningPunishment", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_warningpunishment_guildconfigs_guildconfigid");
});
modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b => modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b =>
{ {
b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings") b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings")
@ -3740,8 +3731,6 @@ namespace EllieBot.Migrations.PostgreSql
b.Navigation("VcRoleInfos"); b.Navigation("VcRoleInfos");
b.Navigation("WarnPunishments");
b.Navigation("XpSettings"); b.Navigation("XpSettings");
}); });

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.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);
}
}
}

View file

@ -2183,7 +2183,7 @@ namespace EllieBot.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 EllieBot.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 EllieBot.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("EllieBot.Db.Models.WarningPunishment", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b => modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b =>
{ {
b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings") b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings")
@ -2880,8 +2872,6 @@ namespace EllieBot.Migrations
b.Navigation("VcRoleInfos"); b.Navigation("VcRoleInfos");
b.Navigation("WarnPunishments");
b.Navigation("XpSettings"); b.Navigation("XpSettings");
}); });

View file

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

View file

@ -66,10 +66,10 @@ public partial class Administration
{ {
await _sender.Response(user) await _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString()))) .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString()) .AddField(GetText(strs.moderator), ctx.User.ToString())
.AddField(GetText(strs.reason), reason ?? "-")) .AddField(GetText(strs.reason), reason ?? "-"))
.SendAsync(); .SendAsync();
} }
catch catch
@ -85,8 +85,9 @@ 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()
.WithDescription(GetText(strs.cant_apply_punishment)); .WithErrorColor()
.WithDescription(GetText(strs.cant_apply_punishment));
if (dmFailed) if (dmFailed)
errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -266,9 +267,9 @@ public partial class Administration
}); });
return _sender.CreateEmbed() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.warnings_list)) .WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws)); .WithDescription(string.Join("\n", ws));
}) })
.SendAsync(); .SendAsync();
} }
@ -278,7 +279,7 @@ public partial class Administration
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public Task WarnDelete(IGuildUser user, int index) public Task WarnDelete(IGuildUser user, int index)
=> WarnDelete(user.Id, index); => WarnDelete(user.Id, index);
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
@ -286,15 +287,15 @@ 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)
{ {
await Response().Error(strs.warning_not_found).SendAsync(); await Response().Error(strs.warning_not_found).SendAsync();
return; return;
} }
await Response().Confirm(strs.warning_deleted(Format.Bold(index.ToString()))).SendAsync(); await Response().Confirm(strs.warning_deleted(Format.Bold(index.ToString()))).SendAsync();
} }
@ -311,7 +312,7 @@ public partial class Administration
{ {
if (index < 0) if (index < 0)
return; return;
var success = await _service.WarnClearAsync(ctx.Guild.Id, userId, index, ctx.User.ToString()); var success = await _service.WarnClearAsync(ctx.Guild.Id, userId, index, ctx.User.ToString());
var userStr = Format.Bold((ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString()); var userStr = Format.Bold((ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString());
if (index == 0) if (index == 0)
@ -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())
@ -478,13 +479,13 @@ public partial class Administration
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune); await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true) .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
.AddField("ID", userId.ToString(), true) .AddField("ID", userId.ToString(), true)
.AddField(GetText(strs.duration), .AddField(GetText(strs.duration),
time.Time.ToPrettyStringHm(), time.Time.ToPrettyStringHm(),
true); true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -505,11 +506,12 @@ 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()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithOkColor()
.AddField("ID", userId.ToString(), true)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.SendAsync(); .AddField("ID", userId.ToString(), true))
.SendAsync();
} }
else else
await Ban(user, msg); await Ban(user, msg);
@ -543,10 +545,10 @@ public partial class Administration
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -734,14 +736,15 @@ 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(user, banPrune, ("Softban | " + ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, ("Softban | " + ctx.User + " | " + msg).TrimTo(512));
try { await ctx.Guild.RemoveBanAsync(user); } try
{ await ctx.Guild.RemoveBanAsync(user); }
catch { await ctx.Guild.RemoveBanAsync(user); } catch { await ctx.Guild.RemoveBanAsync(user); }
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user)) .WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -793,10 +796,10 @@ public partial class Administration
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512)); await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.kicked_user)) .WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -826,8 +829,8 @@ public partial class Administration
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg)); var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
await _sender.Response(user) await _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(dmMessage)) .WithDescription(dmMessage))
.SendAsync(); .SendAsync();
} }
catch catch
@ -838,10 +841,10 @@ public partial class Administration
await user.SetTimeOutAsync(time.Time); await user.SetTimeOutAsync(time.Time);
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⏳ " + GetText(strs.timedout_user)) .WithTitle("⏳ " + GetText(strs.timedout_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -899,9 +902,9 @@ public partial class Administration
missStr = "-"; missStr = "-";
var toSend = _sender.CreateEmbed() var toSend = _sender.CreateEmbed()
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count))) .WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr) .AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor(); .WithPendingColor();
var banningMessage = await Response().Embed(toSend).SendAsync(); var banningMessage = await Response().Embed(toSend).SendAsync();
@ -919,11 +922,13 @@ 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())))
.WithOkColor() .AddField(GetText(strs.invalid(missing.Count)),
.Build()); missStr)
.WithOkColor()
.Build());
} }
[Cmd] [Cmd]
@ -945,10 +950,10 @@ public partial class Administration
//send a message but don't wait for it //send a message but don't wait for it
var banningMessageTask = Response() var banningMessageTask = Response()
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_in_progress(bans.Count()))) GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)
.WithPendingColor()) .WithPendingColor())
.SendAsync(); .SendAsync();
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
@ -966,11 +971,11 @@ public partial class Administration
var banningMessage = await banningMessageTask; var banningMessage = await banningMessageTask;
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed() await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_completed(bans.Count()))) GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)
.WithOkColor() .WithOkColor()
.Build()); .Build());
} }
public class WarnExpireOptions : IEllieCommandOptions public class WarnExpireOptions : IEllieCommandOptions

View file

@ -1,7 +1,6 @@
#nullable disable #nullable disable
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Common.TypeReaders.Models; using EllieBot.Common.TypeReaders.Models;
using EllieBot.Modules.Permissions.Services; using EllieBot.Modules.Permissions.Services;
@ -83,17 +82,24 @@ public class UserPunishService : IEService, 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();
} }
@ -260,12 +266,12 @@ public class UserPunishService : IEService, IReadyExecutor
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
var cleared = await uow.GetTable<Warning>() var cleared = await uow.GetTable<Warning>()
.Where(x => toClear.Contains(x.Id)) .Where(x => toClear.Contains(x.Id))
.UpdateAsync(_ => new() .UpdateAsync(_ => new()
{ {
Forgiven = true, Forgiven = true,
ForgivenBy = "expiry" ForgivenBy = "expiry"
}); });
var toDelete = await uow.GetTable<Warning>() var toDelete = await uow.GetTable<Warning>()
.Where(x => uow.GetTable<GuildConfig>() .Where(x => uow.GetTable<GuildConfig>()
@ -282,8 +288,8 @@ public class UserPunishService : IEService, IReadyExecutor
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
var deleted = await uow.GetTable<Warning>() var deleted = await uow.GetTable<Warning>()
.Where(x => toDelete.Contains(x.Id)) .Where(x => toDelete.Contains(x.Id))
.DeleteAsync(); .DeleteAsync();
if (cleared > 0 || deleted > 0) if (cleared > 0 || deleted > 0)
{ {
@ -377,18 +383,19 @@ public class UserPunishService : IEService, 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 : IEService, 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 = timeMinutes,
Time = (int?)time?.Time.TotalMinutes ?? 0, RoleId = roleId
RoleId = punish == PunishmentAction.AddRole ? role!.Id : default(ulong?) },
}); _ => new()
uow.SaveChanges(); {
Punishment = punish,
Time = timeMinutes,
RoleId = roleId
},
() => new()
{
GuildId = guildId,
Count = number
});
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>()
.ToArray(); .Where(x => x.GuildId == guildId)
.OrderBy(x => x.Count)
.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(
@ -513,12 +524,12 @@ public class UserPunishService : IEService, IReadyExecutor
await ctx.Set<BanTemplate>() await ctx.Set<BanTemplate>()
.ToLinqToDBTable() .ToLinqToDBTable()
.InsertOrUpdateAsync(() => new() .InsertOrUpdateAsync(() => new()
{ {
GuildId = guildId, GuildId = guildId,
Text = null, Text = null,
DateAdded = DateTime.UtcNow, DateAdded = DateTime.UtcNow,
PruneDays = pruneDays PruneDays = pruneDays
}, },
old => new() old => new()
{ {
PruneDays = pruneDays PruneDays = pruneDays
@ -607,12 +618,12 @@ public class UserPunishService : IEService, 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 : IEService, 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);
}
} }

View file

@ -197,26 +197,29 @@ public class RemindService : IEService, 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")
.AddField("Created At", .AddField("Created At",
r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?") r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
.AddField("By", .AddField("By",
(await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString())) (await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()))
.Text(r.Message) .Text(r.Message)
.SendAsync(); .SendAsync();
} }
} }
catch (Exception ex) catch (Exception ex)

View file

@ -6,7 +6,7 @@ using EllieBot.Modules.Utility;
namespace EllieBot.GrpcApi; namespace EllieBot.GrpcApi;
public class ExprsSvc : GrpcExprs.GrpcExprsBase, IEService public class ExprsSvc : GrpcExprs.GrpcExprsBase, IGrpcSvc, IEService
{ {
private readonly EllieExpressionsService _svc; private readonly EllieExpressionsService _svc;
private readonly IQuoteService _qs; private readonly IQuoteService _qs;
@ -19,6 +19,9 @@ public class ExprsSvc : GrpcExprs.GrpcExprsBase, IEService
_client = client; _client = client;
} }
public ServerServiceDefinition Bind()
=> GrpcExprs.BindService(this);
private ulong GetUserId(Metadata meta) private ulong GetUserId(Metadata meta)
=> ulong.Parse(meta.FirstOrDefault(x => x.Key == "userid")!.Value); => ulong.Parse(meta.FirstOrDefault(x => x.Key == "userid")!.Value);

View file

@ -3,7 +3,7 @@ using GreetType = EllieBot.Services.GreetType;
namespace EllieBot.GrpcApi; namespace EllieBot.GrpcApi;
public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IEService public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IGrpcSvc, IEService
{ {
private readonly GreetService _gs; private readonly GreetService _gs;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
@ -14,6 +14,9 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IEService
_client = client; _client = client;
} }
public ServerServiceDefinition Bind()
=> GrpcGreet.BindService(this);
private static GrpcGreetSettings ToConf(GreetSettings? conf) private static GrpcGreetSettings ToConf(GreetSettings? conf)
{ {
if (conf is null) if (conf is null)
@ -50,11 +53,14 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IEService
var settings = await _gs.GetGreetSettingsAsync(gid, type); var settings = await _gs.GetGreetSettingsAsync(gid, type);
if (settings is null) if (settings is null)
return new(); return new()
{
Success = false
};
return new() return new()
{ {
Settings = ToConf(settings) Success = true
}; };
} }

View file

@ -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, IEService public sealed class OtherSvc : GrpcOther.GrpcOtherBase, IGrpcSvc, IEService
{ {
private readonly IDiscordClient _client; private readonly IDiscordClient _client;
private readonly XpService _xp; private readonly XpService _xp;
@ -39,6 +39,9 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, IEService
_cache = cache; _cache = cache;
} }
public ServerServiceDefinition Bind()
=> GrpcOther.BindService(this);
[GrpcNoAuthRequired] [GrpcNoAuthRequired]
public override async Task<BotOnGuildReply> BotOnGuild(BotOnGuildRequest request, ServerCallContext context) public override async Task<BotOnGuildReply> BotOnGuild(BotOnGuildRequest request, ServerCallContext context)
{ {
@ -52,6 +55,22 @@ public sealed class OtherSvc : GrpcOther.GrpcOtherBase, IEService
return reply; 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;
}
public override async Task<GetTextChannelsReply> GetTextChannels( public override async Task<GetTextChannelsReply> GetTextChannels(
GetTextChannelsRequest request, GetTextChannelsRequest request,
ServerCallContext context) ServerCallContext context)

View file

@ -0,0 +1,222 @@
using Grpc.Core;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services;
using Enum = System.Enum;
namespace EllieBot.GrpcApi;
public sealed class WarnSvc : GrpcWarn.GrpcWarnBase, IGrpcSvc, IEService
{
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.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 ? Ellie.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
};
}
}

View file

@ -45,7 +45,7 @@ public sealed partial class GrpcApiPermsInterceptor : Interceptor
return await continuation(request, context); return await continuation(request, context);
// otherwise the method requires auth, and if it requires auth then the guildid has to be specified // otherwise the method requires auth, and if it requires auth then the guildid has to be specified
if (!metadata.ContainsKey("guildid")) if (string.IsNullOrWhiteSpace(gidString))
throw new RpcException(new(StatusCode.Unauthenticated, "guildid has to be specified.")); throw new RpcException(new(StatusCode.Unauthenticated, "guildid has to be specified."));
var userId = ulong.Parse(metadata["userid"]); var userId = ulong.Parse(metadata["userid"]);

View file

@ -9,22 +9,16 @@ public class GrpcApiService : IEService, 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;
} }
@ -53,21 +47,19 @@ public class GrpcApiService : IEService, IReadyExecutor
new[] { new KeyCertificatePair(cert.CertChain, cert.CertPrivateKey) }); new[] { new KeyCertificatePair(cert.CertChain, cert.CertPrivateKey) });
} }
_app = new()
_app = new Server()
{ {
Services =
{
GrpcOther.BindService(_other).Intercept(interceptor),
GrpcExprs.BindService(_exprs).Intercept(interceptor),
GrpcGreet.BindService(_greet).Intercept(interceptor),
},
Ports = Ports =
{ {
new(host, port, serverCreds), 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);

View file

@ -0,0 +1,8 @@
using Grpc.Core;
namespace EllieBot.GrpcApi;
public interface IGrpcSvc
{
ServerServiceDefinition Bind();
}

View file

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

View file

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