2020-12-20 10:52:35 +00:00
using System.Net ;
using System.Web ;
2020-02-01 12:03:02 +00:00
2020-10-19 08:50:51 +00:00
using Dapper ;
2020-12-25 11:56:46 +00:00
using Myriad.Builders ;
2020-12-20 10:52:35 +00:00
using Newtonsoft.Json.Linq ;
2020-02-01 12:03:02 +00:00
using PluralKit.Core ;
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class Member
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
private readonly HttpClient _client ;
private readonly IDatabase _db ;
private readonly DispatchService _dispatch ;
private readonly EmbedService _embeds ;
private readonly ModelRepository _repo ;
public Member ( EmbedService embeds , IDatabase db , ModelRepository repo , HttpClient client ,
DispatchService dispatch )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
_embeds = embeds ;
_db = db ;
_repo = repo ;
_client = client ;
_dispatch = dispatch ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
public async Task NewMember ( Context ctx )
{
if ( ctx . System = = null ) throw Errors . NoSystemError ;
var memberName = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "You must pass a member name." ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// Hard name length cap
if ( memberName . Length > Limits . MaxMemberNameLength )
throw Errors . StringTooLongError ( "Member name" , memberName . Length , Limits . MaxMemberNameLength ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
// Warn if there's already a member by this name
var existingMember = await _repo . GetMemberByName ( ctx . System . Id , memberName ) ;
if ( existingMember ! = null )
{
var msg = $"{Emojis.Warn} You already have a member in your system with the name \" { existingMember . NameFor ( ctx ) } \ " (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?" ;
if ( ! await ctx . PromptYesNo ( msg , "Create" ) ) throw new PKError ( "Member creation cancelled." ) ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
await using var conn = await _db . Obtain ( ) ;
2020-06-29 12:54:11 +00:00
2021-11-27 02:10:56 +00:00
// Enforce per-system member limit
var memberCount = await _repo . GetSystemMemberCount ( ctx . System . Id ) ;
2021-11-30 02:35:21 +00:00
var memberLimit = ctx . Config . MemberLimitOverride ? ? Limits . MaxMemberCount ;
2021-11-27 02:10:56 +00:00
if ( memberCount > = memberLimit )
throw Errors . MemberLimitReachedError ( memberLimit ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
// Create the member
2021-12-01 16:48:49 +00:00
var member = await _repo . CreateMember ( ctx . System . Id , memberName , conn ) ;
2021-11-27 02:10:56 +00:00
memberCount + + ;
2021-04-14 12:56:41 +00:00
2021-12-01 16:48:49 +00:00
JObject dispatchData = new JObject ( ) ;
dispatchData . Add ( "name" , memberName ) ;
if ( ctx . Config . MemberDefaultPrivate )
{
var patch = new MemberPatch ( ) . WithAllPrivacy ( PrivacyLevel . Private ) ;
await _repo . UpdateMember ( member . Id , patch , conn ) ;
dispatchData . Merge ( patch . ToJson ( ) ) ;
}
2021-11-27 02:10:56 +00:00
// Try to match an image attached to the message
var avatarArg = ctx . Message . Attachments . FirstOrDefault ( ) ;
Exception imageMatchError = null ;
if ( avatarArg ! = null )
try
2021-04-14 12:56:41 +00:00
{
2021-11-27 02:10:56 +00:00
await AvatarUtils . VerifyAvatarOrThrow ( _client , avatarArg . Url ) ;
await _db . Execute ( conn = >
_repo . UpdateMember ( member . Id , new MemberPatch { AvatarUrl = avatarArg . Url } , conn ) ) ;
2021-04-14 12:56:41 +00:00
2021-12-01 16:48:49 +00:00
dispatchData . Add ( "avatar_url" , avatarArg . Url ) ;
2021-11-27 02:10:56 +00:00
}
catch ( Exception e )
{
imageMatchError = e ;
}
2021-11-25 20:33:02 +00:00
2021-12-01 16:48:49 +00:00
_ = _dispatch . Dispatch ( member . Id , new UpdateDispatchData
{
Event = DispatchEvent . CREATE_MEMBER ,
EventData = dispatchData ,
} ) ;
2021-11-27 02:10:56 +00:00
// Send confirmation and space hint
await ctx . Reply (
$"{Emojis.Success} Member \" { memberName } \ " (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member" ) ;
// todo: move this to ModelRepository
if ( await _db . Execute ( conn = > conn . QuerySingleAsync < bool > ( "select has_private_members(@System)" ,
2021-12-01 16:48:49 +00:00
new { System = ctx . System . Id } ) ) & & ! ctx . Config . MemberDefaultPrivate ) //if has private members
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.Hid} private`." ) ;
if ( avatarArg ! = null )
if ( imageMatchError = = null )
await ctx . Reply (
$"{Emojis.Success} Member avatar set to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the avatar will stop working." ) ;
else
await ctx . Reply ( $"{Emojis.Error} Couldn't set avatar: {imageMatchError.Message}" ) ;
if ( memberName . Contains ( " " ) )
await ctx . Reply (
$"{Emojis.Note} Note that this member's name contains spaces. You will need to surround it with \" double quotes \ " when using commands referring to it, or just use the member's 5-character ID (which is `{member.Hid}`)." ) ;
if ( memberCount > = memberLimit )
await ctx . Reply (
$"{Emojis.Warn} You have reached the per-system member limit ({memberLimit}). You will be unable to create additional members until existing members are deleted." ) ;
else if ( memberCount > = Limits . WarnThreshold ( memberLimit ) )
await ctx . Reply (
$"{Emojis.Warn} You are approaching the per-system member limit ({memberCount} / {memberLimit} members). Please review your member list for unused or duplicate members." ) ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task ViewMember ( Context ctx , PKMember target )
{
var system = await _repo . GetSystem ( target . System ) ;
await ctx . Reply (
2021-11-30 02:35:21 +00:00
embed : await _embeds . CreateMemberEmbed ( system , target , ctx . Guild , ctx . LookupContextFor ( system ) , ctx . Zone ) ) ;
2021-11-27 02:10:56 +00:00
}
2020-12-20 10:52:35 +00:00
2021-11-27 02:10:56 +00:00
public async Task Soulscream ( Context ctx , PKMember target )
{
// this is for a meme, please don't take this code seriously. :)
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var name = target . NameFor ( ctx . LookupContextFor ( target ) ) ;
var encoded = HttpUtility . UrlEncode ( name ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var resp = await _client . GetAsync ( $"https://onomancer.sibr.dev/api/generateStats2?name={encoded}" ) ;
if ( resp . StatusCode ! = HttpStatusCode . OK )
// lol
return ;
2020-12-20 10:52:35 +00:00
2021-11-27 02:10:56 +00:00
var data = JObject . Parse ( await resp . Content . ReadAsStringAsync ( ) ) ;
var scream = data [ "soulscream" ] ! . Value < string > ( ) ;
2020-12-20 10:52:35 +00:00
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Color ( DiscordUtils . Red )
. Title ( name )
. Url ( $"https://onomancer.sibr.dev/reflect?name={encoded}" )
. Description ( $"*{scream}*" ) ;
await ctx . Reply ( embed : eb . Build ( ) ) ;
2020-02-01 12:03:02 +00:00
}
2021-08-27 15:03:47 +00:00
}