marriage.
This commit is contained in:
346
SkyBot/Commands/RP/Marriage.cs
Normal file
346
SkyBot/Commands/RP/Marriage.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using SkyBot.Services;
|
||||
using Valour.Sdk.Models;
|
||||
|
||||
namespace SkyBot.Commands
|
||||
{
|
||||
public class Marriage : ICommand
|
||||
{
|
||||
public string Name => "marriage";
|
||||
public string[] Aliases => ["marry"];
|
||||
public string Description => "Marriage system — propose, accept, decline, check status, or divorce.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "marriage <propose|accept|decline|status|divorce>";
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
string sub = ctx.Args.Length > 0 ? ctx.Args[0].ToLower() : "status";
|
||||
|
||||
switch (sub)
|
||||
{
|
||||
case "propose":
|
||||
await HandlePropose(ctx, channel);
|
||||
break;
|
||||
case "accept":
|
||||
await HandleAccept(ctx, channel);
|
||||
break;
|
||||
case "decline":
|
||||
await HandleDecline(ctx, channel);
|
||||
break;
|
||||
case "divorce":
|
||||
await HandleDivorce(ctx, channel);
|
||||
break;
|
||||
case "force":
|
||||
await HandleForce(ctx, channel);
|
||||
break;
|
||||
case "status":
|
||||
default:
|
||||
await HandleStatus(ctx, channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandlePropose(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long proposerId = ctx.Member.UserId;
|
||||
string sender = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
long? targetUserId = null;
|
||||
string? targetMention = null;
|
||||
|
||||
// Prefer reply-to for target resolution
|
||||
if (ctx.Message.ReplyToId is not null)
|
||||
{
|
||||
var replied = await ctx.Message.FetchReplyMessageAsync();
|
||||
if (replied is not null)
|
||||
{
|
||||
var author = await replied.FetchAuthorAsync();
|
||||
if (author is not null)
|
||||
{
|
||||
targetUserId = author.Id;
|
||||
targetMention = $"«@u-{author.Id}»";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to message mention
|
||||
if (targetUserId is null && ctx.Message.Mentions is not null && ctx.Message.Mentions.Any())
|
||||
{
|
||||
long memberId = ctx.Message.Mentions.First().TargetId;
|
||||
var mentioned = await ctx.Planet.FetchMemberAsync(memberId);
|
||||
if (mentioned is not null)
|
||||
{
|
||||
targetUserId = mentioned.UserId;
|
||||
targetMention = $"«@u-{mentioned.UserId}»";
|
||||
}
|
||||
}
|
||||
|
||||
if (targetUserId is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please reply to someone's message or mention them. Usage: `{Config.Prefix}marriage propose @user`");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MarriageService.Propose(proposerId, targetUserId.Value);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.ProposeResult.SelfPropose:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You can't propose to yourself!");
|
||||
break;
|
||||
case MarriageService.ProposeResult.AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"You're already married, {sender}! You'd need to `{Config.Prefix}marriage divorce` first.");
|
||||
break;
|
||||
case MarriageService.ProposeResult.TargetAlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"{targetMention} is already married!");
|
||||
break;
|
||||
case MarriageService.ProposeResult.AlreadyProposed:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"You've already proposed to {targetMention}! Waiting for their response...");
|
||||
break;
|
||||
case MarriageService.ProposeResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💍 {sender} gets down on one knee and proposes to {targetMention}!\n" +
|
||||
$"{targetMention}, type `{Config.Prefix}marriage accept` to accept or `{Config.Prefix}marriage decline` to decline.\n" +
|
||||
$"This proposal expires in 5 minutes.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleAccept(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long acceptorId = ctx.Member.UserId;
|
||||
string acceptorName = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
var (result, proposerId) = await MarriageService.AcceptAsync(acceptorId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.AcceptResult.NoPendingProposal:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"You don't have any pending proposals to accept.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.Expired:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"That proposal from «@u-{proposerId}» has expired.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"One of you is already married! The proposal has been cancelled.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💒 {acceptorName} has accepted «@u-{proposerId}»'s proposal! They are now married! 🎉");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleDecline(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long acceptorId = ctx.Member.UserId;
|
||||
string acceptorName = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
var (result, proposerId) = MarriageService.Decline(acceptorId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DeclineResult.NoPendingProposal:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"You don't have any pending proposals to decline.");
|
||||
break;
|
||||
case MarriageService.DeclineResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {acceptorName} has declined «@u-{proposerId}»'s proposal.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleStatus(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long userId = ctx.Member.UserId;
|
||||
string name = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
if (ctx.Message.Mentions is not null && ctx.Message.Mentions.Any())
|
||||
{
|
||||
long memberId = ctx.Message.Mentions.First().TargetId;
|
||||
var mentioned = await ctx.Planet.FetchMemberAsync(memberId);
|
||||
if (mentioned is not null)
|
||||
{
|
||||
userId = mentioned.UserId;
|
||||
name = mentioned.Nickname ?? mentioned.User?.Name ?? "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
long? partnerId = MarriageService.GetPartner(userId);
|
||||
|
||||
if (partnerId is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {name} is not currently married.");
|
||||
}
|
||||
else
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💑 {name} is married to «@u-{partnerId}»!");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleForce(CommandContext ctx, Channel channel)
|
||||
{
|
||||
if (ctx.Member.UserId != Config.OwnerId)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You don't have permission to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
string action = ctx.Args.Length > 1 ? ctx.Args[1].ToLower() : "";
|
||||
|
||||
if (action is not ("marry" or "divorce"))
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Usage: `{Config.Prefix}marriage force marry @user1 @user2` or `{Config.Prefix}marriage force divorce @user1`");
|
||||
return;
|
||||
}
|
||||
|
||||
var mentions = ctx.Message.Mentions ?? [];
|
||||
|
||||
if (action == "marry")
|
||||
{
|
||||
if (mentions.Count < 2)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please mention two users. Usage: `{Config.Prefix}marriage force marry @user1 @user2`");
|
||||
return;
|
||||
}
|
||||
|
||||
var member1 = await ctx.Planet.FetchMemberAsync(mentions[0].TargetId);
|
||||
var member2 = await ctx.Planet.FetchMemberAsync(mentions[1].TargetId);
|
||||
|
||||
if (member1 is null || member2 is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not find one or both of those members.");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await MarriageService.ForceMarryAsync(member1.UserId, member2.UserId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.ForceMarryResult.SameUser:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You can't marry someone to themselves!");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.User1AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member1.UserId}» is already married.");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.User2AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member2.UserId}» is already married.");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💒 «@u-{member1.UserId}» and «@u-{member2.UserId}» are now married! 🎉");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // divorce
|
||||
{
|
||||
if (mentions.Count < 1)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please mention a user. Usage: `{Config.Prefix}marriage force divorce @user`");
|
||||
return;
|
||||
}
|
||||
|
||||
var member = await ctx.Planet.FetchMemberAsync(mentions[0].TargetId);
|
||||
|
||||
if (member is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not find that member.");
|
||||
return;
|
||||
}
|
||||
|
||||
var (result, partnerId) = await MarriageService.DivorceAsync(member.UserId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DivorceResult.NotMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member.UserId}» is not married.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 «@u-{member.UserId}» and «@u-{partnerId}» have been forcefully divorced.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleDivorce(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long userId = ctx.Member.UserId;
|
||||
string name = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
string action = ctx.Args.Length > 1 ? ctx.Args[1].ToLower() : "";
|
||||
|
||||
if (action == "cancel")
|
||||
{
|
||||
if (MarriageService.CancelDivorce(userId))
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"Divorce cancelled, {name}.");
|
||||
else
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You don't have a pending divorce to cancel.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == "confirm")
|
||||
{
|
||||
var (result, partnerId) = await MarriageService.DivorceAsync(userId, confirmed: true);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DivorceResult.NotMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"You're not married, {name}.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.NoConfirmation:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"No pending divorce found. Run `{Config.Prefix}marriage divorce` first.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.ConfirmationExpired:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Your divorce confirmation expired. Run `{Config.Prefix}marriage divorce` again to start over.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {name} and «@u-{partnerId}» have divorced.");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial divorce request — ask for confirmation
|
||||
long? partnerId2 = MarriageService.GetPartner(userId);
|
||||
if (partnerId2 is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"You're not married, {name}.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool requested = MarriageService.RequestDivorceConfirmation(userId);
|
||||
if (requested)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Are you sure you want to divorce «@u-{partnerId2}»?\n" +
|
||||
$"Type `{Config.Prefix}marriage divorce confirm` within 60 seconds to confirm, or `{Config.Prefix}marriage divorce cancel` to cancel.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user