2024-10-07 21:44:38 +13:00
using Humanizer ;
2022-11-23 23:53:21 +13:00
using Dapper ;
using SqlKata ;
2024-10-07 21:44:38 +13:00
using Myriad.Builders ;
using Myriad.Extensions ;
using Myriad.Cache ;
2022-11-23 23:54:21 +13:00
using Myriad.Rest ;
using Myriad.Types ;
2025-09-09 10:43:00 -04:00
using Myriad.Rest.Types.Requests ;
2025-09-09 11:32:13 -04:00
using Myriad.Rest.Exceptions ;
2022-11-23 23:54:21 +13:00
2021-06-08 19:37:44 +02:00
using PluralKit.Core ;
2021-11-26 21:10:56 -05:00
namespace PluralKit.Bot ;
public class Admin
2021-06-08 19:37:44 +02:00
{
2021-11-26 21:10:56 -05:00
private readonly BotConfig _botConfig ;
2022-11-23 23:54:21 +13:00
private readonly DiscordApiClient _rest ;
2024-10-07 21:44:38 +13:00
private readonly IDiscordCache _cache ;
2025-09-09 11:32:13 -04:00
private readonly PrivateChannelService _dmCache ;
2021-11-26 21:10:56 -05:00
2025-09-09 11:32:13 -04:00
public Admin ( BotConfig botConfig , DiscordApiClient rest , IDiscordCache cache , PrivateChannelService dmCache )
2021-06-08 19:37:44 +02:00
{
2021-11-26 21:10:56 -05:00
_botConfig = botConfig ;
2022-11-23 23:54:21 +13:00
_rest = rest ;
2024-10-07 21:44:38 +13:00
_cache = cache ;
2025-09-09 11:32:13 -04:00
_dmCache = dmCache ;
2024-10-07 21:44:38 +13:00
}
2024-10-23 10:08:25 +13:00
private Task < ( ulong Id , User ? User ) [ ] > GetUsers ( IEnumerable < ulong > ids )
{
async Task < ( ulong Id , User ? User ) > Inner ( ulong id )
{
var user = await _cache . GetOrFetchUser ( _rest , id ) ;
return ( id , user ) ;
}
return Task . WhenAll ( ids . Select ( Inner ) ) ;
}
2024-10-07 21:44:38 +13:00
public async Task < Embed > CreateEmbed ( Context ctx , PKSystem system )
{
string UntilLimit ( int count , int limit )
{
var brackets = new List < int > { 10 , 25 , 50 , 100 } ;
if ( count = = limit )
return "(at limit)" ;
foreach ( var x in brackets )
{
if ( limit - x < = count )
return $"(approx. {x} to limit)" ;
}
return "" ;
}
var config = await ctx . Repository . GetSystemConfig ( system . Id ) ;
// Fetch/render info for all accounts simultaneously
var accounts = await ctx . Repository . GetSystemAccounts ( system . Id ) ;
var users = ( await GetUsers ( accounts ) ) . Select ( x = > x . User ? . NameAndMention ( ) ? ? $"(deleted: `{x.Id}`)" ) ;
var eb = new EmbedBuilder ( )
. Title ( "System info" )
. Color ( DiscordUtils . Green )
. Field ( new Embed . Field ( "System ID" , $"`{system.Hid}`" ) )
. Field ( new Embed . Field ( "Linked accounts" , string . Join ( "\n" , users ) . Truncate ( 1000 ) ) ) ;
var memberLimit = config . MemberLimitOverride ? ? Limits . MaxMemberCount ;
var memberCount = await ctx . Repository . GetSystemMemberCount ( system . Id ) ;
eb . Field ( new Embed . Field ( "Member limit" , $"{memberLimit} {UntilLimit(memberCount, memberLimit)}" , true ) ) ;
var groupLimit = config . GroupLimitOverride ? ? Limits . MaxGroupCount ;
var groupCount = await ctx . Repository . GetSystemGroupCount ( system . Id ) ;
eb . Field ( new Embed . Field ( "Group limit" , $"{groupLimit} {UntilLimit(groupCount, groupLimit)}" , true ) ) ;
return eb . Build ( ) ;
2021-11-26 21:10:56 -05:00
}
2021-06-08 19:37:44 +02:00
2024-10-23 10:08:25 +13:00
public async Task < Embed > CreateAbuseLogEmbed ( Context ctx , AbuseLog abuseLog )
{
// Fetch/render info for all accounts simultaneously
var accounts = await ctx . Repository . GetAbuseLogAccounts ( abuseLog . Id ) ;
var systems = await Task . WhenAll ( accounts . Select ( x = > ctx . Repository . GetSystemByAccount ( x ) ) ) ;
var users = ( await GetUsers ( accounts ) ) . Select ( x = > x . User ? . NameAndMention ( ) ? ? $"(deleted: `{x.Id}`)" ) ;
List < string > flagstr = new ( ) ;
if ( abuseLog . DenyBotUsage )
flagstr . Add ( "- bot usage denied" ) ;
var eb = new EmbedBuilder ( )
. Title ( $"Abuse log: {abuseLog.Uuid.ToString()}" )
. Color ( DiscordUtils . Red )
. Footer ( new Embed . EmbedFooter ( $"Created on {abuseLog.Created.FormatZoned(ctx.Zone)}" ) ) ;
if ( systems . Any ( x = > x ! = null ) )
{
var sysList = string . Join ( ", " , systems . Select ( x = > $"`{x.DisplayHid()}`" ) ) ;
eb . Field ( new Embed . Field ( $"{Emojis.Warn} Accounts have registered system(s)" , sysList ) ) ;
}
eb . Field ( new Embed . Field ( "Accounts" , string . Join ( "\n" , users ) . Truncate ( 1000 ) , true ) ) ;
eb . Field ( new Embed . Field ( "Flags" , flagstr . Any ( ) ? string . Join ( "\n" , flagstr ) : "(none)" , true ) ) ;
if ( abuseLog . Description ! = null )
eb . Field ( new Embed . Field ( "Description" , abuseLog . Description . Truncate ( 1000 ) ) ) ;
return eb . Build ( ) ;
}
2025-10-08 03:26:40 +00:00
public async Task UpdateSystemId ( Context ctx , PKSystem target , string newHid , bool confirmYes )
2021-11-26 21:10:56 -05:00
{
ctx . AssertBotAdmin ( ) ;
2021-06-08 19:37:44 +02:00
2022-01-22 03:05:01 -05:00
var existingSystem = await ctx . Repository . GetSystemByHid ( newHid ) ;
2021-11-26 21:10:56 -05:00
if ( existingSystem ! = null )
throw new PKError ( $"Another system already exists with ID `{newHid}`." ) ;
2021-06-08 19:37:44 +02:00
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2025-10-08 03:26:40 +00:00
if ( ! await ctx . PromptYesNo ( $"Change system ID of `{target.Hid}` to `{newHid}`?" , "Change" , flagValue : confirmYes ) )
2021-11-26 21:10:56 -05:00
throw new PKError ( "ID change cancelled." ) ;
2021-06-08 19:37:44 +02:00
2022-01-22 03:05:01 -05:00
await ctx . Repository . UpdateSystem ( target . Id , new SystemPatch { Hid = newHid } ) ;
2021-11-26 21:10:56 -05:00
await ctx . Reply ( $"{Emojis.Success} System ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2021-06-08 19:37:44 +02:00
2025-10-08 03:26:40 +00:00
public async Task UpdateMemberId ( Context ctx , PKMember target , string newHid , bool confirmYes )
2021-11-26 21:10:56 -05:00
{
ctx . AssertBotAdmin ( ) ;
2021-08-27 11:03:47 -04:00
2022-01-22 03:05:01 -05:00
var existingMember = await ctx . Repository . GetMemberByHid ( newHid ) ;
2021-11-26 21:10:56 -05:00
if ( existingMember ! = null )
throw new PKError ( $"Another member already exists with ID `{newHid}`." ) ;
2021-06-08 19:37:44 +02:00
2024-10-07 21:44:38 +13:00
var system = await ctx . Repository . GetSystem ( target . System ) ;
await ctx . Reply ( null , await CreateEmbed ( ctx , system ) ) ;
2021-11-26 21:10:56 -05:00
if ( ! await ctx . PromptYesNo (
$"Change member ID of **{target.NameFor(LookupContext.ByNonOwner)}** (`{target.Hid}`) to `{newHid}`?" ,
2025-10-08 03:26:40 +00:00
"Change" , flagValue : confirmYes
2021-11-26 21:10:56 -05:00
) )
throw new PKError ( "ID change cancelled." ) ;
2021-06-08 19:37:44 +02:00
2022-01-22 03:05:01 -05:00
await ctx . Repository . UpdateMember ( target . Id , new MemberPatch { Hid = newHid } ) ;
2021-11-26 21:10:56 -05:00
await ctx . Reply ( $"{Emojis.Success} Member ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2021-08-27 11:03:47 -04:00
2025-10-08 03:26:40 +00:00
public async Task UpdateGroupId ( Context ctx , PKGroup target , string newHid , bool confirmYes )
2021-11-26 21:10:56 -05:00
{
ctx . AssertBotAdmin ( ) ;
2021-06-08 19:37:44 +02:00
2022-01-22 03:05:01 -05:00
var existingGroup = await ctx . Repository . GetGroupByHid ( newHid ) ;
2021-11-26 21:10:56 -05:00
if ( existingGroup ! = null )
throw new PKError ( $"Another group already exists with ID `{newHid}`." ) ;
2021-07-08 10:04:05 -04:00
2024-10-07 21:44:38 +13:00
var system = await ctx . Repository . GetSystem ( target . System ) ;
await ctx . Reply ( null , await CreateEmbed ( ctx , system ) ) ;
2021-11-26 21:10:56 -05:00
if ( ! await ctx . PromptYesNo ( $"Change group ID of **{target.Name}** (`{target.Hid}`) to `{newHid}`?" ,
2025-10-08 03:26:40 +00:00
"Change" , flagValue : confirmYes
2021-11-26 21:10:56 -05:00
) )
throw new PKError ( "ID change cancelled." ) ;
2021-07-08 10:04:05 -04:00
2022-01-22 03:05:01 -05:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { Hid = newHid } ) ;
2021-11-26 21:10:56 -05:00
await ctx . Reply ( $"{Emojis.Success} Group ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2021-07-08 10:04:05 -04:00
2025-10-08 03:26:40 +00:00
public async Task RerollSystemId ( Context ctx , PKSystem target , bool confirmYes )
2022-11-23 23:53:21 +13:00
{
ctx . AssertBotAdmin ( ) ;
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2025-10-08 03:26:40 +00:00
if ( ! await ctx . PromptYesNo ( $"Reroll system ID `{target.Hid}`?" , "Reroll" , flagValue : confirmYes ) )
2022-11-23 23:53:21 +13:00
throw new PKError ( "ID change cancelled." ) ;
var query = new Query ( "systems" ) . AsUpdate ( new
{
hid = new UnsafeLiteral ( "find_free_system_hid()" ) ,
} )
. Where ( "id" , target . Id ) ;
var newHid = await ctx . Database . QueryFirst < string > ( query , "returning hid" ) ;
await ctx . Reply ( $"{Emojis.Success} System ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2025-10-08 03:26:40 +00:00
public async Task RerollMemberId ( Context ctx , PKMember target , bool confirmYes )
2022-11-23 23:53:21 +13:00
{
ctx . AssertBotAdmin ( ) ;
2024-10-07 21:44:38 +13:00
var system = await ctx . Repository . GetSystem ( target . System ) ;
await ctx . Reply ( null , await CreateEmbed ( ctx , system ) ) ;
2022-11-23 23:53:21 +13:00
if ( ! await ctx . PromptYesNo (
$"Reroll member ID for **{target.NameFor(LookupContext.ByNonOwner)}** (`{target.Hid}`)?" ,
2025-10-08 03:26:40 +00:00
"Reroll" , flagValue : confirmYes
2022-11-23 23:53:21 +13:00
) )
throw new PKError ( "ID change cancelled." ) ;
var query = new Query ( "members" ) . AsUpdate ( new
{
hid = new UnsafeLiteral ( "find_free_member_hid()" ) ,
} )
. Where ( "id" , target . Id ) ;
var newHid = await ctx . Database . QueryFirst < string > ( query , "returning hid" ) ;
await ctx . Reply ( $"{Emojis.Success} Member ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2025-10-08 03:26:40 +00:00
public async Task RerollGroupId ( Context ctx , PKGroup target , bool confirmYes )
2022-11-23 23:53:21 +13:00
{
ctx . AssertBotAdmin ( ) ;
2024-10-07 21:44:38 +13:00
var system = await ctx . Repository . GetSystem ( target . System ) ;
await ctx . Reply ( null , await CreateEmbed ( ctx , system ) ) ;
2022-11-23 23:53:21 +13:00
if ( ! await ctx . PromptYesNo ( $"Reroll group ID for **{target.Name}** (`{target.Hid}`)?" ,
2025-10-08 03:26:40 +00:00
"Change" , flagValue : confirmYes
2022-11-23 23:53:21 +13:00
) )
throw new PKError ( "ID change cancelled." ) ;
var query = new Query ( "groups" ) . AsUpdate ( new
{
hid = new UnsafeLiteral ( "find_free_group_hid()" ) ,
} )
. Where ( "id" , target . Id ) ;
var newHid = await ctx . Database . QueryFirst < string > ( query , "returning hid" ) ;
await ctx . Reply ( $"{Emojis.Success} Group ID updated (`{target.Hid}` -> `{newHid}`)." ) ;
}
2025-10-08 03:26:40 +00:00
public async Task SystemMemberLimit ( Context ctx , PKSystem target , int? newLimit , bool confirmYes )
2021-11-26 21:10:56 -05:00
{
ctx . AssertBotAdmin ( ) ;
2021-07-08 10:04:05 -04:00
2022-01-22 03:05:01 -05:00
var config = await ctx . Repository . GetSystemConfig ( target . Id ) ;
2021-11-29 21:35:21 -05:00
var currentLimit = config . MemberLimitOverride ? ? Limits . MaxMemberCount ;
2025-10-04 01:57:48 +00:00
if ( newLimit = = null )
2021-06-08 19:37:44 +02:00
{
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2021-11-26 21:10:56 -05:00
return ;
}
2021-06-08 19:37:44 +02:00
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2025-10-08 03:26:40 +00:00
if ( ! await ctx . PromptYesNo ( $"Update member limit from **{currentLimit}** to **{newLimit}**?" , "Update" , flagValue : confirmYes ) )
2021-11-26 21:10:56 -05:00
throw new PKError ( "Member limit change cancelled." ) ;
2021-06-08 19:37:44 +02:00
2022-01-22 03:05:01 -05:00
await ctx . Repository . UpdateSystemConfig ( target . Id , new SystemConfigPatch { MemberLimitOverride = newLimit } ) ;
2021-11-26 21:10:56 -05:00
await ctx . Reply ( $"{Emojis.Success} Member limit updated." ) ;
}
2021-06-08 19:37:44 +02:00
2025-10-08 03:26:40 +00:00
public async Task SystemGroupLimit ( Context ctx , PKSystem target , int? newLimit , bool confirmYes )
2021-11-26 21:10:56 -05:00
{
ctx . AssertBotAdmin ( ) ;
2021-08-27 11:03:47 -04:00
2022-01-22 03:05:01 -05:00
var config = await ctx . Repository . GetSystemConfig ( target . Id ) ;
2021-11-29 21:35:21 -05:00
var currentLimit = config . GroupLimitOverride ? ? Limits . MaxGroupCount ;
2025-10-04 01:57:48 +00:00
if ( newLimit = = null )
2021-07-08 10:04:05 -04:00
{
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2021-11-26 21:10:56 -05:00
return ;
}
2021-07-08 10:04:05 -04:00
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , target ) ) ;
2025-10-08 03:26:40 +00:00
if ( ! await ctx . PromptYesNo ( $"Update group limit from **{currentLimit}** to **{newLimit}**?" , "Update" , flagValue : confirmYes ) )
2021-11-26 21:10:56 -05:00
throw new PKError ( "Group limit change cancelled." ) ;
2021-07-08 10:04:05 -04:00
2022-01-22 03:05:01 -05:00
await ctx . Repository . UpdateSystemConfig ( target . Id , new SystemConfigPatch { GroupLimitOverride = newLimit } ) ;
2021-11-26 21:10:56 -05:00
await ctx . Reply ( $"{Emojis.Success} Group limit updated." ) ;
2021-06-08 19:37:44 +02:00
}
2022-11-23 23:54:21 +13:00
2025-10-08 03:26:40 +00:00
public async Task SystemRecover ( Context ctx , string systemToken , User account , bool rerollToken , bool confirmYes )
2022-11-23 23:54:21 +13:00
{
ctx . AssertBotAdmin ( ) ;
var systemId = await ctx . Database . Execute ( conn = > conn . QuerySingleOrDefaultAsync < SystemId ? > (
"select id from systems where token = @token" ,
new { token = systemToken }
) ) ;
if ( systemId = = null )
throw new PKError ( "Could not retrieve a system with that token." ) ;
var existingAccount = await ctx . Repository . GetSystemByAccount ( account . Id ) ;
if ( existingAccount ! = null )
2024-12-31 08:09:18 -07:00
throw Errors . AccountInOtherSystem ( existingAccount , ctx . Config , ctx . DefaultPrefix ) ;
2022-11-23 23:54:21 +13:00
var system = await ctx . Repository . GetSystem ( systemId . Value ! ) ;
2024-10-07 21:44:38 +13:00
await ctx . Reply ( null , await CreateEmbed ( ctx , system ) ) ;
2022-11-23 23:54:21 +13:00
2025-10-08 03:26:40 +00:00
if ( ! await ctx . PromptYesNo ( $"Associate account {account.NameAndMention()} with system `{system.Hid}`?" , "Recover account" , flagValue : confirmYes ) )
2022-11-23 23:54:21 +13:00
throw new PKError ( "System recovery cancelled." ) ;
await ctx . Repository . AddAccount ( system . Id , account . Id ) ;
if ( rerollToken )
await ctx . Repository . UpdateSystem ( system . Id , new SystemPatch { Token = StringUtils . GenerateToken ( ) } ) ;
if ( ( await ctx . BotPermissions ) . HasFlag ( PermissionSet . ManageMessages ) )
await _rest . DeleteMessage ( ctx . Message ) ;
await ctx . Reply ( null , new Embed
{
Title = "System recovered" ,
Description = $"{account.NameAndMention()} has been linked to system `{system.Hid}`." ,
Fields = new Embed . Field [ ]
{
new Embed . Field ( "Token rerolled?" , rerollToken ? "yes" : "no" , true ) ,
new Embed . Field ( "Actioned by" , ctx . Author . NameAndMention ( ) , true ) ,
} ,
Color = DiscordUtils . Green ,
} ) ;
}
2024-10-23 10:08:25 +13:00
2025-10-04 01:57:48 +00:00
public async Task SystemDelete ( Context ctx , PKSystem target )
2024-10-23 10:08:25 +13:00
{
ctx . AssertBotAdmin ( ) ;
await ctx . Reply ( $"To delete the following system, reply with the system's UUID: `{target.Uuid.ToString()}`" ,
await CreateEmbed ( ctx , target ) ) ;
if ( ! await ctx . ConfirmWithReply ( target . Uuid . ToString ( ) ) )
throw new PKError ( "System deletion cancelled." ) ;
await ctx . BusyIndicator ( async ( ) = >
await ctx . Repository . DeleteSystem ( target . Id ) ) ;
await ctx . Reply ( $"{Emojis.Success} System deletion succesful." ) ;
}
2025-10-04 01:57:48 +00:00
public async Task AbuseLogCreate ( Context ctx , User account , bool denyBotUsage , string? description )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
2024-10-23 10:08:25 +13:00
2025-10-04 01:57:48 +00:00
var abuseLog = await ctx . Repository . CreateAbuseLog ( description , denyBotUsage ) ;
2024-10-23 10:08:25 +13:00
await ctx . Repository . AddAbuseLogAccount ( abuseLog . Id , account . Id ) ;
await ctx . Reply (
$"Created new abuse log with UUID `{abuseLog.Uuid.ToString()}`." ,
await CreateAbuseLogEmbed ( ctx , abuseLog ) ) ;
}
2025-10-04 01:57:48 +00:00
public async Task < AbuseLog ? > GetAbuseLog ( Context ctx , User ? account , string? id )
{
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = null ! ;
if ( account ! = null )
{
abuseLog = await ctx . Repository . GetAbuseLogByAccount ( account . Id ) ;
}
else
{
abuseLog = await ctx . Repository . GetAbuseLogByGuid ( new Guid ( id ) ) ;
}
if ( abuseLog = = null )
{
await ctx . Reply ( $"{Emojis.Error} Could not find an existing abuse log entry for that query." ) ;
return null ;
}
return abuseLog ;
}
public async Task AbuseLogShow ( Context ctx , User ? account , string? id )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , account , id ) ;
if ( abuseLog = = null )
return ;
2024-10-23 10:08:25 +13:00
await ctx . Reply ( null , await CreateAbuseLogEmbed ( ctx , abuseLog ) ) ;
}
2025-10-04 01:57:48 +00:00
public async Task AbuseLogFlagDeny ( Context ctx , User ? account , string? id , bool? value )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , account , id ) ;
if ( abuseLog = = null )
return ;
if ( value = = null )
2024-10-23 10:08:25 +13:00
{
await ctx . Reply (
$"Bot usage is currently {(abuseLog.DenyBotUsage ? " denied " : " allowed ")} "
+ $"for accounts associated with abuse log `{abuseLog.Uuid}`." ) ;
}
else
{
if ( abuseLog . DenyBotUsage ! = value )
2025-10-04 01:57:48 +00:00
await ctx . Repository . UpdateAbuseLog ( abuseLog . Id , new AbuseLogPatch { DenyBotUsage = value . Value } ) ;
2024-10-23 10:08:25 +13:00
await ctx . Reply (
2025-10-04 01:57:48 +00:00
$"Bot usage is now **{(value.Value ? " denied " : " allowed ")}** "
2024-10-23 10:08:25 +13:00
+ $"for accounts associated with abuse log `{abuseLog.Uuid}`." ) ;
}
}
2025-10-08 03:26:40 +00:00
public async Task AbuseLogDescription ( Context ctx , User ? account , string? id , string? description , bool clear , bool confirmClear )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , account , id ) ;
if ( abuseLog = = null )
return ;
2025-10-08 03:26:40 +00:00
if ( clear & & await ctx . ConfirmClear ( "this abuse log description" , confirmClear ) )
2024-10-23 10:08:25 +13:00
{
await ctx . Repository . UpdateAbuseLog ( abuseLog . Id , new AbuseLogPatch { Description = null } ) ;
await ctx . Reply ( $"{Emojis.Success} Abuse log description cleared." ) ;
}
2025-10-04 01:57:48 +00:00
else if ( description ! = null )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
await ctx . Repository . UpdateAbuseLog ( abuseLog . Id , new AbuseLogPatch { Description = description } ) ;
2024-10-23 10:08:25 +13:00
await ctx . Reply ( $"{Emojis.Success} Abuse log description updated." ) ;
}
else
{
var eb = new EmbedBuilder ( )
. Description ( $"Showing description for abuse log `{abuseLog.Uuid}`" ) ;
await ctx . Reply ( abuseLog . Description , eb . Build ( ) ) ;
}
}
2025-10-04 01:57:48 +00:00
public async Task AbuseLogAddUser ( Context ctx , User ? accountToFind , string? id , User account )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , accountToFind , id ) ;
if ( abuseLog = = null )
return ;
2024-10-23 10:08:25 +13:00
await ctx . Repository . AddAbuseLogAccount ( abuseLog . Id , account . Id ) ;
await ctx . Reply (
$"Added user {account.NameAndMention()} to the abuse log with UUID `{abuseLog.Uuid.ToString()}`." ,
await CreateAbuseLogEmbed ( ctx , abuseLog ) ) ;
}
2025-10-04 01:57:48 +00:00
public async Task AbuseLogRemoveUser ( Context ctx , User ? accountToFind , string? id , User account )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , accountToFind , id ) ;
if ( abuseLog = = null )
return ;
2024-10-23 10:08:25 +13:00
await ctx . Repository . UpdateAccount ( account . Id , new ( )
{
AbuseLog = null ,
} ) ;
await ctx . Reply (
$"Removed user {account.NameAndMention()} from the abuse log with UUID `{abuseLog.Uuid.ToString()}`." ,
await CreateAbuseLogEmbed ( ctx , abuseLog ) ) ;
}
2025-10-04 01:57:48 +00:00
public async Task AbuseLogDelete ( Context ctx , User ? account , string? id )
2024-10-23 10:08:25 +13:00
{
2025-10-04 01:57:48 +00:00
ctx . AssertBotAdmin ( ) ;
AbuseLog ? abuseLog = await GetAbuseLog ( ctx , account , id ) ;
if ( abuseLog = = null )
return ;
2024-10-23 10:08:25 +13:00
if ( ! await ctx . PromptYesNo ( $"Really delete abuse log entry `{abuseLog.Uuid}`?" , "Delete" , matchFlag : false ) )
{
await ctx . Reply ( $"{Emojis.Error} Deletion cancelled." ) ;
return ;
}
await ctx . Repository . DeleteAbuseLog ( abuseLog . Id ) ;
await ctx . Reply ( $"{Emojis.Success} Successfully deleted abuse log entry." ) ;
}
2025-09-09 10:43:00 -04:00
2025-10-04 01:57:48 +00:00
public async Task SendAdminMessage ( Context ctx , User account , string content )
2025-09-09 10:43:00 -04:00
{
ctx . AssertBotAdmin ( ) ;
var messageContent = $"## [Admin Message]\n\n{content}\n\nWe cannot read replies sent to this DM. If you wish to contact the staff team, please join the support server (<https://discord.gg/PczBt78>) or send us an email at <legal@pluralkit.me>." ;
try
{
2025-09-09 11:32:13 -04:00
var dm = await _dmCache . GetOrCreateDmChannel ( account . Id ) ;
var msg = await ctx . Rest . CreateMessage ( dm ,
2025-09-09 10:43:00 -04:00
new MessageRequest { Content = messageContent }
) ;
}
2025-09-09 11:32:13 -04:00
catch ( ForbiddenException )
2025-09-09 10:43:00 -04:00
{
await ctx . Reply (
$"{Emojis.Error} Error while sending DM." ) ;
2025-09-09 11:32:13 -04:00
return ;
2025-09-09 10:43:00 -04:00
}
await ctx . Reply ( $"{Emojis.Success} Successfully sent message." ) ;
}
2021-06-08 19:37:44 +02:00
}