2020-06-29 11:57:48 +00:00
using Dapper ;
2020-02-12 14:16:19 +00:00
using PluralKit.Core ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class MemberProxy
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
private readonly IDatabase _db ;
private readonly ModelRepository _repo ;
public MemberProxy ( IDatabase db , ModelRepository repo )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
_db = db ;
_repo = repo ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task Proxy ( Context ctx , PKMember target )
{
ctx . CheckSystem ( ) . CheckOwnMember ( target ) ;
ProxyTag ParseProxyTags ( string exampleProxy )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
// // Make sure there's one and only one instance of "text" in the example proxy given
var prefixAndSuffix = exampleProxy . Split ( "text" ) ;
if ( prefixAndSuffix . Length = = 1 ) prefixAndSuffix = prefixAndSuffix [ 0 ] . Split ( "TEXT" ) ;
if ( prefixAndSuffix . Length < 2 ) throw Errors . ProxyMustHaveText ;
if ( prefixAndSuffix . Length > 2 ) throw Errors . ProxyMultipleText ;
return new ProxyTag ( prefixAndSuffix [ 0 ] , prefixAndSuffix [ 1 ] ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
async Task < bool > WarnOnConflict ( ProxyTag newTag )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
var query = "select * from (select *, (unnest(proxy_tags)).prefix as prefix, (unnest(proxy_tags)).suffix as suffix from members where system = @System) as _ where prefix is not distinct from @Prefix and suffix is not distinct from @Suffix and id != @Existing" ;
var conflicts = ( await _db . Execute ( conn = > conn . QueryAsync < PKMember > ( query ,
new { newTag . Prefix , newTag . Suffix , Existing = target . Id , system = target . System } ) ) ) . ToList ( ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( conflicts . Count < = 0 ) return true ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
var conflictList = conflicts . Select ( m = > $"- **{m.NameFor(ctx)}**" ) ;
var msg = $"{Emojis.Warn} The following members have conflicting proxy tags:\n{string.Join('\n', conflictList)}\nDo you want to proceed anyway?" ;
return await ctx . PromptYesNo ( msg , "Proceed" ) ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// "Sub"command: clear flag
if ( await ctx . MatchClear ( ) )
{
// If we already have multiple tags, this would clear everything, so prompt that
if ( target . ProxyTags . Count > 1 )
2020-03-04 17:13:36 +00:00
{
2021-11-27 02:10:56 +00:00
var msg = $"{Emojis.Warn} You already have multiple proxy tags set: {target.ProxyTagsString()}\nDo you want to clear them all?" ;
if ( ! await ctx . PromptYesNo ( msg , "Clear" ) )
throw Errors . GenericCancelled ( ) ;
2020-03-04 17:13:36 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { ProxyTags = Partial < ProxyTag [ ] > . Present ( new ProxyTag [ 0 ] ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Proxy tags cleared." ) ;
}
// "Sub"command: no arguments; will print proxy tags
else if ( ! ctx . HasNext ( false ) )
{
if ( target . ProxyTags . Count = = 0 )
await ctx . Reply ( "This member does not have any proxy tags." ) ;
else
await ctx . Reply ( $"This member's proxy tags are:\n{target.ProxyTagsString(" \ n ")}" ) ;
}
// Subcommand: "add"
else if ( ctx . Match ( "add" , "append" ) )
{
if ( ! ctx . HasNext ( false ) )
throw new PKSyntaxError ( "You must pass an example proxy to add (eg. `[text]` or `J:text`)." ) ;
var tagToAdd = ParseProxyTags ( ctx . RemainderOrNull ( false ) ) ;
if ( tagToAdd . IsEmpty ) throw Errors . EmptyProxyTags ( target ) ;
if ( target . ProxyTags . Contains ( tagToAdd ) )
throw Errors . ProxyTagAlreadyExists ( tagToAdd , target ) ;
if ( tagToAdd . ProxyString . Length > Limits . MaxProxyTagLength )
throw new PKError (
$"Proxy tag too long ({tagToAdd.ProxyString.Length} > {Limits.MaxProxyTagLength} characters)." ) ;
if ( ! await WarnOnConflict ( tagToAdd ) )
throw Errors . GenericCancelled ( ) ;
var newTags = target . ProxyTags . ToList ( ) ;
newTags . Add ( tagToAdd ) ;
var patch = new MemberPatch { ProxyTags = Partial < ProxyTag [ ] > . Present ( newTags . ToArray ( ) ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Added proxy tags {tagToAdd.ProxyString.AsCode()}." ) ;
}
// Subcommand: "remove"
else if ( ctx . Match ( "remove" , "delete" ) )
{
if ( ! ctx . HasNext ( false ) )
throw new PKSyntaxError ( "You must pass a proxy tag to remove (eg. `[text]` or `J:text`)." ) ;
2020-06-29 11:57:48 +00:00
2021-11-27 02:10:56 +00:00
var tagToRemove = ParseProxyTags ( ctx . RemainderOrNull ( false ) ) ;
if ( tagToRemove . IsEmpty ) throw Errors . EmptyProxyTags ( target ) ;
if ( ! target . ProxyTags . Contains ( tagToRemove ) )
throw Errors . ProxyTagDoesNotExist ( tagToRemove , target ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
var newTags = target . ProxyTags . ToList ( ) ;
newTags . Remove ( tagToRemove ) ;
var patch = new MemberPatch { ProxyTags = Partial < ProxyTag [ ] > . Present ( newTags . ToArray ( ) ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Removed proxy tags {tagToRemove.ProxyString.AsCode()}." ) ;
}
// Subcommand: bare proxy tag given
else
{
var requestedTag = ParseProxyTags ( ctx . RemainderOrNull ( false ) ) ;
if ( requestedTag . IsEmpty ) throw Errors . EmptyProxyTags ( target ) ;
2020-06-29 11:57:48 +00:00
2021-11-27 02:10:56 +00:00
// This is mostly a legacy command, so it's gonna warn if there's
// already more than one proxy tag.
if ( target . ProxyTags . Count > 1 )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
var msg = $"This member already has more than one proxy tag set: {target.ProxyTagsString()}\nDo you want to replace them?" ;
if ( ! await ctx . PromptYesNo ( msg , "Replace" ) )
2020-02-01 12:03:02 +00:00
throw Errors . GenericCancelled ( ) ;
2021-11-27 02:10:56 +00:00
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ! await WarnOnConflict ( requestedTag ) )
throw Errors . GenericCancelled ( ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var newTags = new [ ] { requestedTag } ;
var patch = new MemberPatch { ProxyTags = Partial < ProxyTag [ ] > . Present ( newTags ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Member proxy tags set to {requestedTag.ProxyString.AsCode()}." ) ;
2020-02-01 12:03:02 +00:00
}
}
}