2020-07-06 17:50:39 +00:00
using System.Text ;
2021-03-28 10:02:41 +00:00
using System.Text.RegularExpressions ;
2020-07-18 11:30:54 +00:00
2020-08-16 10:10:54 +00:00
using Humanizer ;
2021-11-27 02:10:56 +00:00
using Myriad.Builders ;
using Myriad.Types ;
2021-11-25 20:33:02 +00:00
using Newtonsoft.Json.Linq ;
2021-02-09 22:36:43 +00:00
using NodaTime ;
2020-06-29 21:51:12 +00:00
using PluralKit.Core ;
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class Groups
2020-06-29 21:51:12 +00:00
{
2021-11-27 02:10:56 +00:00
public enum AddRemoveOperation
2020-06-29 21:51:12 +00:00
{
2021-11-27 02:10:56 +00:00
Add ,
Remove
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
private readonly HttpClient _client ;
private readonly DispatchService _dispatch ;
private readonly EmbedService _embeds ;
2021-08-27 15:03:47 +00:00
2022-01-22 08:05:01 +00:00
public Groups ( EmbedService embeds , HttpClient client ,
2021-11-27 02:10:56 +00:00
DispatchService dispatch )
{
_embeds = embeds ;
_client = client ;
_dispatch = dispatch ;
}
2020-08-16 10:10:54 +00:00
2021-11-27 02:10:56 +00:00
public async Task CreateGroup ( Context ctx )
{
ctx . CheckSystem ( ) ;
// Check group name length
var groupName = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "You must pass a group name." ) ;
if ( groupName . Length > Limits . MaxGroupNameLength )
throw new PKError ( $"Group name too long ({groupName.Length}/{Limits.MaxGroupNameLength} characters)." ) ;
// Check group cap
2022-01-22 08:05:01 +00:00
var existingGroupCount = await ctx . Repository . GetSystemGroupCount ( ctx . System . Id ) ;
2021-11-30 02:35:21 +00:00
var groupLimit = ctx . Config . GroupLimitOverride ? ? Limits . MaxGroupCount ;
2021-11-27 02:10:56 +00:00
if ( existingGroupCount > = groupLimit )
throw new PKError (
$"System has reached the maximum number of groups ({groupLimit}). Please delete unused groups first in order to create new ones." ) ;
// Warn if there's already a group by this name
2022-01-22 08:05:01 +00:00
var existingGroup = await ctx . Repository . GetGroupByName ( ctx . System . Id , groupName ) ;
2021-11-27 02:10:56 +00:00
if ( existingGroup ! = null )
{
var msg =
$"{Emojis.Warn} You already have a group in your system with the name \" { existingGroup . Name } \ " (with ID `{existingGroup.Hid}`). Do you want to create another group with the same name?" ;
if ( ! await ctx . PromptYesNo ( msg , "Create" ) )
throw new PKError ( "Group creation cancelled." ) ;
}
2021-08-27 15:03:47 +00:00
2022-01-22 08:05:01 +00:00
// todo: this is supposed to be a transaction, but it's not used in any useful way
// consider removing it?
using var conn = await ctx . Database . Obtain ( ) ;
var newGroup = await ctx . Repository . CreateGroup ( ctx . System . Id , groupName ) ;
2021-08-27 15:03:47 +00:00
2021-12-01 16:48:49 +00:00
var dispatchData = new JObject ( ) ;
dispatchData . Add ( "name" , groupName ) ;
if ( ctx . Config . GroupDefaultPrivate )
{
var patch = new GroupPatch ( ) . WithAllPrivacy ( PrivacyLevel . Private ) ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( newGroup . Id , patch , conn ) ;
2021-12-01 16:48:49 +00:00
dispatchData . Merge ( patch . ToJson ( ) ) ;
}
_ = _dispatch . Dispatch ( newGroup . Id , new UpdateDispatchData
{
Event = DispatchEvent . CREATE_GROUP ,
EventData = dispatchData
} ) ;
2021-11-25 20:33:02 +00:00
2022-02-05 14:26:14 +00:00
var reference = newGroup . Reference ( ctx ) ;
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Description (
$"Your new group, **{groupName}**, has been created, with the group ID **`{newGroup.Hid}`**.\nBelow are a couple of useful commands:" )
2022-02-05 14:26:14 +00:00
. Field ( new Embed . Field ( "View the group card" , $"> pk;group **{reference}**" ) )
2021-11-27 02:10:56 +00:00
. Field ( new Embed . Field ( "Add members to the group" ,
2022-02-05 14:26:14 +00:00
$"> pk;group **{reference}** add **MemberName**\n> pk;group **{reference}** add **Member1** **Member2** **Member3** (and so on...)" ) )
2021-11-27 02:10:56 +00:00
. Field ( new Embed . Field ( "Set the description" ,
2022-02-05 14:26:14 +00:00
$"> pk;group **{reference}** description **This is my new group, and here is the description!**" ) )
2021-11-27 02:10:56 +00:00
. Field ( new Embed . Field ( "Set the group icon" ,
2022-02-05 14:26:14 +00:00
$"> pk;group **{reference}** icon\n*(with an image attached)*" ) ) ;
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group created!" , eb . Build ( ) ) ;
if ( existingGroupCount > = Limits . WarnThreshold ( groupLimit ) )
await ctx . Reply (
$"{Emojis.Warn} You are approaching the per-system group limit ({existingGroupCount} / {groupLimit} groups). Please review your group list for unused or duplicate groups." ) ;
}
2021-09-13 06:46:40 +00:00
2021-11-27 02:10:56 +00:00
public async Task RenameGroup ( Context ctx , PKGroup target )
{
ctx . CheckOwnGroup ( target ) ;
// Check group name length
var newName = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "You must pass a new group name." ) ;
if ( newName . Length > Limits . MaxGroupNameLength )
throw new PKError (
$"New group name too long ({newName.Length}/{Limits.MaxMemberNameLength} characters)." ) ;
2020-06-29 21:51:12 +00:00
2021-11-27 02:10:56 +00:00
// Warn if there's already a group by this name
2022-01-22 08:05:01 +00:00
var existingGroup = await ctx . Repository . GetGroupByName ( ctx . System . Id , newName ) ;
2021-11-27 02:10:56 +00:00
if ( existingGroup ! = null & & existingGroup . Id ! = target . Id )
2020-07-06 17:50:39 +00:00
{
2021-11-27 02:10:56 +00:00
var msg =
$"{Emojis.Warn} You already have a group in your system with the name \" { existingGroup . Name } \ " (with ID `{existingGroup.Hid}`). Do you want to rename this group to that name too?" ;
if ( ! await ctx . PromptYesNo ( msg , "Rename" ) )
throw new PKError ( "Group rename cancelled." ) ;
}
2021-08-27 15:03:47 +00:00
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { Name = newName } ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group name changed from **{target.Name}** to **{newName}**." ) ;
}
2020-08-16 10:10:54 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupDisplayName ( Context ctx , PKGroup target )
{
var noDisplayNameSetMessage = "This group does not have a display name set." ;
if ( ctx . System ? . Id = = target . System )
noDisplayNameSetMessage + =
2022-02-05 14:26:14 +00:00
$" To set one, type `pk;group {target.Reference(ctx)} displayname <display name>`." ;
2020-07-06 17:50:39 +00:00
2021-11-27 02:10:56 +00:00
// No perms check, display name isn't covered by member privacy
2020-08-20 19:43:17 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
2020-08-20 19:43:17 +00:00
{
2021-11-27 02:10:56 +00:00
if ( target . DisplayName = = null )
await ctx . Reply ( noDisplayNameSetMessage ) ;
else
await ctx . Reply ( $"```\n{target.DisplayName}\n```" ) ;
return ;
}
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
{
if ( target . DisplayName = = null )
2021-08-27 23:20:14 +00:00
{
2021-11-27 02:10:56 +00:00
await ctx . Reply ( noDisplayNameSetMessage ) ;
2021-08-27 23:20:14 +00:00
}
else
{
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Field ( new Embed . Field ( "Name" , target . Name ) )
. Field ( new Embed . Field ( "Display Name" , target . DisplayName ) ) ;
2021-08-27 15:03:47 +00:00
2022-02-05 14:26:14 +00:00
var reference = target . Reference ( ctx ) ;
2021-11-27 02:10:56 +00:00
if ( ctx . System ? . Id = = target . System )
eb . Description (
2022-02-05 14:26:14 +00:00
$"To change display name, type `pk;group {reference} displayname <display name>`."
+ $"To clear it, type `pk;group {reference} displayname -clear`."
+ $"To print the raw display name, type `pk;group {reference} displayname -raw`." ) ;
2020-08-20 19:43:17 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : eb . Build ( ) ) ;
2020-08-20 19:43:17 +00:00
}
2021-11-27 02:10:56 +00:00
return ;
2020-07-06 17:50:39 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
ctx . CheckOwnGroup ( target ) ;
if ( await ctx . MatchClear ( "this group's display name" ) )
2020-07-06 17:50:39 +00:00
{
2021-11-27 02:10:56 +00:00
var patch = new GroupPatch { DisplayName = Partial < string > . Null ( ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2020-07-06 17:50:39 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group display name cleared." ) ;
2022-01-15 03:30:02 +00:00
if ( target . NamePrivacy = = PrivacyLevel . Private )
await ctx . Reply ( $"{Emojis.Warn} Since this group no longer has a display name set, their name privacy **can no longer take effect**." ) ;
2021-11-27 02:10:56 +00:00
}
else
{
var newDisplayName = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
2021-05-24 19:18:57 +00:00
2021-11-27 02:10:56 +00:00
var patch = new GroupPatch { DisplayName = Partial < string > . Present ( newDisplayName ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group display name changed." ) ;
}
}
2020-07-06 17:50:39 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupDescription ( Context ctx , PKGroup target )
{
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . DescriptionPrivacy ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var noDescriptionSetMessage = "This group does not have a description set." ;
if ( ctx . System ? . Id = = target . System )
noDescriptionSetMessage + =
2022-02-05 14:26:14 +00:00
$" To set one, type `pk;group {target.Reference(ctx)} description <description>`." ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
{
if ( target . Description = = null )
await ctx . Reply ( noDescriptionSetMessage ) ;
else
await ctx . Reply ( $"```\n{target.Description}\n```" ) ;
return ;
2020-07-06 17:50:39 +00:00
}
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
2020-08-08 13:09:42 +00:00
{
2021-11-27 02:10:56 +00:00
if ( target . Description = = null )
await ctx . Reply ( noDescriptionSetMessage ) ;
else
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( "Group description" )
. Description ( target . Description )
. Field ( new Embed . Field ( "\u200B" ,
2022-02-05 14:26:14 +00:00
$"To print the description with formatting, type `pk;group {target.Reference(ctx)} description -raw`."
2021-11-27 02:10:56 +00:00
+ ( ctx . System ? . Id = = target . System
2022-02-05 14:26:14 +00:00
? $" To clear it, type `pk;group {target.Reference(ctx)} description -clear`."
2021-11-27 02:10:56 +00:00
: "" ) ) )
. Build ( ) ) ;
return ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
ctx . CheckOwnGroup ( target ) ;
2020-08-08 13:09:42 +00:00
2021-11-27 02:10:56 +00:00
if ( await ctx . MatchClear ( "this group's description" ) )
{
var patch = new GroupPatch { Description = Partial < string > . Null ( ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group description cleared." ) ;
}
else
{
var description = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
if ( description . IsLongerThan ( Limits . MaxDescriptionLength ) )
throw Errors . StringTooLongError ( "Description" , description . Length , Limits . MaxDescriptionLength ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var patch = new GroupPatch { Description = Partial < string > . Present ( description ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2020-08-08 13:09:42 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group description changed." ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupIcon ( Context ctx , PKGroup target )
{
async Task ClearIcon ( )
{
ctx . CheckOwnGroup ( target ) ;
2021-08-27 15:03:47 +00:00
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { Icon = null } ) ;
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group icon cleared." ) ;
}
2020-08-08 13:09:42 +00:00
2021-11-27 02:10:56 +00:00
async Task SetIcon ( ParsedImage img )
{
ctx . CheckOwnGroup ( target ) ;
2020-08-08 13:09:42 +00:00
2021-11-27 02:10:56 +00:00
await AvatarUtils . VerifyAvatarOrThrow ( _client , img . Url ) ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { Icon = img . Url } ) ;
2021-11-27 02:10:56 +00:00
var msg = img . Source switch
{
AvatarSource . User = >
$"{Emojis.Success} Group icon changed to {img.SourceUser?.Username}'s avatar!\n{Emojis.Warn} If {img.SourceUser?.Username} changes their avatar, the group icon will need to be re-set." ,
AvatarSource . Url = > $"{Emojis.Success} Group icon changed to the image at the given URL." ,
AvatarSource . Attachment = >
$"{Emojis.Success} Group icon changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the group icon will stop working." ,
_ = > throw new ArgumentOutOfRangeException ( )
} ;
// The attachment's already right there, no need to preview it.
var hasEmbed = img . Source ! = AvatarSource . Attachment ;
await ( hasEmbed
? ctx . Reply ( msg , new EmbedBuilder ( ) . Image ( new Embed . EmbedImage ( img . Url ) ) . Build ( ) )
: ctx . Reply ( msg ) ) ;
2020-08-08 13:09:42 +00:00
}
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
async Task ShowIcon ( )
2021-08-02 17:46:12 +00:00
{
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . IconPrivacy ) ;
2021-12-06 09:01:42 +00:00
2021-11-27 02:10:56 +00:00
if ( ( target . Icon ? . Trim ( ) ? ? "" ) . Length > 0 )
2021-08-02 17:46:12 +00:00
{
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Title ( "Group icon" )
. Image ( new Embed . EmbedImage ( target . Icon . TryGetCleanCdnUrl ( ) ) ) ;
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
if ( target . System = = ctx . System ? . Id )
2022-02-05 14:26:14 +00:00
eb . Description ( $"To clear, use `pk;group {target.Reference(ctx)} icon -clear`." ) ;
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : eb . Build ( ) ) ;
}
else
2021-08-02 17:46:12 +00:00
{
2021-11-27 02:10:56 +00:00
throw new PKSyntaxError (
"This group does not have an icon set. Set one by attaching an image to this command, or by passing an image URL or @mention." ) ;
}
}
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
if ( await ctx . MatchClear ( "this group's icon" ) )
await ClearIcon ( ) ;
else if ( await ctx . MatchImage ( ) is { } img )
await SetIcon ( img ) ;
else
await ShowIcon ( ) ;
}
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupBannerImage ( Context ctx , PKGroup target )
{
async Task ClearBannerImage ( )
{
ctx . CheckOwnGroup ( target ) ;
2021-08-02 17:46:12 +00:00
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { BannerImage = null } ) ;
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group banner image cleared." ) ;
}
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
async Task SetBannerImage ( ParsedImage img )
{
ctx . CheckOwnGroup ( target ) ;
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
await AvatarUtils . VerifyAvatarOrThrow ( _client , img . Url , true ) ;
2021-08-02 17:46:12 +00:00
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch { BannerImage = img . Url } ) ;
2021-11-27 02:10:56 +00:00
var msg = img . Source switch
{
AvatarSource . Url = > $"{Emojis.Success} Group banner image changed to the image at the given URL." ,
AvatarSource . Attachment = >
$"{Emojis.Success} Group banner image changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the banner image will stop working." ,
AvatarSource . User = > throw new PKError ( "Cannot set a banner image to an user's avatar." ) ,
_ = > throw new ArgumentOutOfRangeException ( )
} ;
// The attachment's already right there, no need to preview it.
var hasEmbed = img . Source ! = AvatarSource . Attachment ;
await ( hasEmbed
? ctx . Reply ( msg , new EmbedBuilder ( ) . Image ( new Embed . EmbedImage ( img . Url ) ) . Build ( ) )
: ctx . Reply ( msg ) ) ;
2021-08-02 17:46:12 +00:00
}
2021-11-27 02:10:56 +00:00
async Task ShowBannerImage ( )
2021-03-28 10:02:41 +00:00
{
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . DescriptionPrivacy ) ;
2021-12-06 09:01:42 +00:00
2021-11-27 02:10:56 +00:00
if ( ( target . BannerImage ? . Trim ( ) ? ? "" ) . Length > 0 )
2021-03-28 10:02:41 +00:00
{
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Title ( "Group banner image" )
. Image ( new Embed . EmbedImage ( target . BannerImage ) ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( target . System = = ctx . System ? . Id )
2022-02-05 14:26:14 +00:00
eb . Description ( $"To clear, use `pk;group {target.Reference(ctx)} banner clear`." ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : eb . Build ( ) ) ;
2021-03-28 10:02:41 +00:00
}
else
{
2021-11-27 02:10:56 +00:00
throw new PKSyntaxError (
"This group does not have a banner image set. Set one by attaching an image to this command, or by passing an image URL or @mention." ) ;
}
}
2021-03-28 10:02:41 +00:00
2021-11-27 02:10:56 +00:00
if ( await ctx . MatchClear ( "this group's banner image" ) )
await ClearBannerImage ( ) ;
else if ( await ctx . MatchImage ( ) is { } img )
await SetBannerImage ( img ) ;
else
await ShowBannerImage ( ) ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupColor ( Context ctx , PKGroup target )
{
var color = ctx . RemainderOrNull ( ) ;
if ( await ctx . MatchClear ( ) )
{
ctx . CheckOwnGroup ( target ) ;
var patch = new GroupPatch { Color = Partial < string > . Null ( ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2021-03-28 10:02:41 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group color cleared." ) ;
}
else if ( ! ctx . HasNext ( ) )
{
if ( target . Color = = null )
if ( ctx . System ? . Id = = target . System )
await ctx . Reply (
2022-02-05 14:26:14 +00:00
$"This group does not have a color set. To set one, type `pk;group {target.Reference(ctx)} color <color>`." ) ;
2021-11-27 02:10:56 +00:00
else
await ctx . Reply ( "This group does not have a color set." ) ;
else
2021-03-28 10:02:41 +00:00
await ctx . Reply ( embed : new EmbedBuilder ( )
2021-11-27 02:10:56 +00:00
. Title ( "Group color" )
. Color ( target . Color . ToDiscordColor ( ) )
. Thumbnail ( new Embed . EmbedThumbnail ( $"https://fakeimg.pl/256x256/{target.Color}/?text=%20" ) )
. Description ( $"This group's color is **#{target.Color}**."
+ ( ctx . System ? . Id = = target . System
2022-02-05 14:26:14 +00:00
? $" To clear it, type `pk;group {target.Reference(ctx)} color -clear`."
2021-11-27 02:10:56 +00:00
: "" ) )
2021-03-28 10:02:41 +00:00
. Build ( ) ) ;
}
2021-11-27 02:10:56 +00:00
else
2020-07-06 17:50:39 +00:00
{
2021-11-27 02:10:56 +00:00
ctx . CheckOwnGroup ( target ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( color . StartsWith ( "#" ) ) color = color . Substring ( 1 ) ;
if ( ! Regex . IsMatch ( color , "^[0-9a-fA-F]{6}$" ) ) throw Errors . InvalidColorError ( color ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var patch = new GroupPatch { Color = Partial < string > . Present ( color . ToLowerInvariant ( ) ) } ;
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , patch ) ;
2020-07-06 17:50:39 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( $"{Emojis.Success} Group color changed." )
. Color ( color . ToDiscordColor ( ) )
. Thumbnail ( new Embed . EmbedThumbnail ( $"https://fakeimg.pl/256x256/{color}/?text=%20" ) )
. Build ( ) ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task ListSystemGroups ( Context ctx , PKSystem system )
{
if ( system = = null )
{
ctx . CheckSystem ( ) ;
system = ctx . System ;
}
2021-08-27 15:03:47 +00:00
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( system . Id , system . GroupListPrivacy ) ;
2020-07-06 17:50:39 +00:00
2022-01-15 03:30:02 +00:00
// explanation of privacy lookup here:
// - ParseListOptions checks list access privacy and sets the privacy filter (which members show up in list)
// - RenderGroupList checks the indivual privacy for each member (NameFor, etc)
// the own system is always allowed to look up their list
var opts = ctx . ParseListOptions ( ctx . DirectLookupContextFor ( system . Id ) ) ;
await ctx . RenderGroupList (
ctx . LookupContextFor ( system . Id ) ,
system . Id ,
GetEmbedTitle ( system , opts ) ,
system . Color ,
opts
) ;
}
2021-11-27 02:10:56 +00:00
2022-01-15 03:30:02 +00:00
private string GetEmbedTitle ( PKSystem target , ListOptions opts )
{
var title = new StringBuilder ( "Groups of " ) ;
2021-11-27 02:10:56 +00:00
2022-01-15 03:30:02 +00:00
if ( target . Name ! = null )
title . Append ( $"{target.Name} (`{target.Hid}`)" ) ;
else
title . Append ( $"`{target.Hid}`" ) ;
2020-06-29 21:51:12 +00:00
2022-01-15 03:30:02 +00:00
if ( opts . Search ! = null )
title . Append ( $" matching **{opts.Search}**" ) ;
2021-11-27 02:10:56 +00:00
2022-01-15 03:30:02 +00:00
return title . ToString ( ) ;
2021-11-27 02:10:56 +00:00
}
2020-07-07 13:28:53 +00:00
2021-11-27 02:10:56 +00:00
public async Task ShowGroupCard ( Context ctx , PKGroup target )
{
var system = await GetGroupSystem ( ctx , target ) ;
await ctx . Reply ( embed : await _embeds . CreateGroupEmbed ( ctx , system , target ) ) ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task GroupPrivacy ( Context ctx , PKGroup target , PrivacyLevel ? newValueFromCommand )
{
ctx . CheckSystem ( ) . CheckOwnGroup ( target ) ;
// Display privacy settings
if ( ! ctx . HasNext ( ) & & newValueFromCommand = = null )
{
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( $"Current privacy settings for {target.Name}" )
2022-01-15 03:30:02 +00:00
. Field ( new Embed . Field ( "Name" , target . NamePrivacy . Explanation ( ) ) )
2021-11-27 02:10:56 +00:00
. Field ( new Embed . Field ( "Description" , target . DescriptionPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Icon" , target . IconPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Member list" , target . ListPrivacy . Explanation ( ) ) )
2022-01-15 03:30:02 +00:00
. Field ( new Embed . Field ( "Metadata (creation date)" , target . MetadataPrivacy . Explanation ( ) ) )
2021-11-27 02:10:56 +00:00
. Field ( new Embed . Field ( "Visibility" , target . Visibility . Explanation ( ) ) )
. Description (
2022-02-05 14:26:14 +00:00
$"To edit privacy settings, use the command:\n> pk;group **{target.Reference(ctx)}** privacy **<subject>** **<level>**\n\n- `subject` is one of `name`, `description`, `icon`, `members`, `metadata`, `visibility`, or `all`\n- `level` is either `public` or `private`." )
2021-11-27 02:10:56 +00:00
. Build ( ) ) ;
return ;
2020-07-07 17:34:23 +00:00
}
2021-11-27 02:10:56 +00:00
async Task SetAll ( PrivacyLevel level )
2020-07-07 13:28:53 +00:00
{
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch ( ) . WithAllPrivacy ( level ) ) ;
2021-11-27 02:10:56 +00:00
if ( level = = PrivacyLevel . Private )
await ctx . Reply (
$"{Emojis.Success} All {target.Name}'s privacy settings have been set to **{level.LevelName()}**. Other accounts will now see nothing on the group card." ) ;
else
await ctx . Reply (
$"{Emojis.Success} All {target.Name}'s privacy settings have been set to **{level.LevelName()}**. Other accounts will now see everything on the group card." ) ;
2020-07-07 13:28:53 +00:00
}
2020-11-14 17:05:30 +00:00
2021-11-27 02:10:56 +00:00
async Task SetLevel ( GroupPrivacySubject subject , PrivacyLevel level )
2020-07-18 11:53:02 +00:00
{
2022-01-22 08:05:01 +00:00
await ctx . Repository . UpdateGroup ( target . Id , new GroupPatch ( ) . WithPrivacy ( subject , level ) ) ;
2020-07-18 11:53:02 +00:00
2021-11-27 02:10:56 +00:00
var subjectName = subject switch
2020-07-18 11:53:02 +00:00
{
2022-01-15 03:30:02 +00:00
GroupPrivacySubject . Name = > "name privacy" ,
2021-11-27 02:10:56 +00:00
GroupPrivacySubject . Description = > "description privacy" ,
GroupPrivacySubject . Icon = > "icon privacy" ,
GroupPrivacySubject . List = > "member list" ,
2022-01-15 03:30:02 +00:00
GroupPrivacySubject . Metadata = > "metadata" ,
2021-11-27 02:10:56 +00:00
GroupPrivacySubject . Visibility = > "visibility" ,
_ = > throw new ArgumentOutOfRangeException ( $"Unknown privacy subject {subject}" )
} ;
var explanation = ( subject , level ) switch
2020-07-18 11:53:02 +00:00
{
2022-01-15 03:30:02 +00:00
( GroupPrivacySubject . Name , PrivacyLevel . Private ) = >
"This group's name is now hidden from other systems, and will be replaced by the group's display name." ,
2021-11-27 02:10:56 +00:00
( GroupPrivacySubject . Description , PrivacyLevel . Private ) = >
"This group's description is now hidden from other systems." ,
( GroupPrivacySubject . Icon , PrivacyLevel . Private ) = >
"This group's icon is now hidden from other systems." ,
( GroupPrivacySubject . Visibility , PrivacyLevel . Private ) = >
"This group is now hidden from group lists and member cards." ,
2022-01-15 03:30:02 +00:00
( GroupPrivacySubject . Metadata , PrivacyLevel . Private ) = >
"This group's metadata (eg. creation date) is now hidden from other systems." ,
2021-11-27 02:10:56 +00:00
( GroupPrivacySubject . List , PrivacyLevel . Private ) = >
"This group's member list is now hidden from other systems." ,
2022-01-15 03:30:02 +00:00
( GroupPrivacySubject . Name , PrivacyLevel . Public ) = >
"This group's name is no longer hidden from other systems." ,
2021-11-27 02:10:56 +00:00
( GroupPrivacySubject . Description , PrivacyLevel . Public ) = >
"This group's description is no longer hidden from other systems." ,
( GroupPrivacySubject . Icon , PrivacyLevel . Public ) = >
"This group's icon is no longer hidden from other systems." ,
( GroupPrivacySubject . Visibility , PrivacyLevel . Public ) = >
"This group is no longer hidden from group lists and member cards." ,
2022-01-15 03:30:02 +00:00
( GroupPrivacySubject . Metadata , PrivacyLevel . Public ) = >
"This group's metadata (eg. creation date) is no longer hidden from other systems." ,
2021-11-27 02:10:56 +00:00
( GroupPrivacySubject . List , PrivacyLevel . Public ) = >
"This group's member list is no longer hidden from other systems." ,
_ = > throw new InvalidOperationException ( $"Invalid subject/level tuple ({subject}, {level})" )
} ;
await ctx . Reply (
$"{Emojis.Success} {target.Name}'s **{subjectName}** has been set to **{level.LevelName()}**. {explanation}" ) ;
2022-01-15 03:30:02 +00:00
if ( subject = = GroupPrivacySubject . Name & & level = = PrivacyLevel . Private & & target . DisplayName = = null )
await ctx . Reply (
$"{Emojis.Warn} This group does not have a display name set, and name privacy **will not take effect**." ) ;
2020-07-18 11:53:02 +00:00
}
2020-07-07 17:34:44 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . Match ( "all" ) | | newValueFromCommand ! = null )
await SetAll ( newValueFromCommand ? ? ctx . PopPrivacyLevel ( ) ) ;
else
await SetLevel ( ctx . PopGroupPrivacySubject ( ) , ctx . PopPrivacyLevel ( ) ) ;
}
2020-08-08 12:56:34 +00:00
2021-11-27 02:10:56 +00:00
public async Task DeleteGroup ( Context ctx , PKGroup target )
{
ctx . CheckOwnGroup ( target ) ;
2020-08-08 12:56:34 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Warn} Are you sure you want to delete this group? If so, reply to this message with the group's ID (`{target.Hid}`).\n**Note: this action is permanent.**" ) ;
if ( ! await ctx . ConfirmWithReply ( target . Hid ) )
throw new PKError (
$"Group deletion cancelled. Note that you must reply with your group ID (`{target.Hid}`) *verbatim*." ) ;
2021-08-27 15:03:47 +00:00
2022-01-22 08:05:01 +00:00
await ctx . Repository . DeleteGroup ( target . Id ) ;
2020-08-08 12:56:34 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Group deleted." ) ;
}
2021-02-09 22:36:43 +00:00
2021-11-27 02:10:56 +00:00
private async Task < PKSystem > GetGroupSystem ( Context ctx , PKGroup target )
{
var system = ctx . System ;
if ( system ? . Id = = target . System )
return system ;
2022-01-22 08:05:01 +00:00
return await ctx . Repository . GetSystem ( target . System ) ! ;
2020-06-29 21:51:12 +00:00
}
}