diff --git a/Cocotte/Modules/Activities/ActivityModule.cs b/Cocotte/Modules/Activities/ActivityModule.cs index 789bcee..bf43ca1 100644 --- a/Cocotte/Modules/Activities/ActivityModule.cs +++ b/Cocotte/Modules/Activities/ActivityModule.cs @@ -24,6 +24,8 @@ public partial class ActivityModule : InteractionModuleBase (SocketGuildUser) Context.User; + public ActivityModule(ILogger logger, IOptions options, ActivityHelper activityHelper, ActivitiesRepository activitiesRepository, ActivityFormatter activityFormatter) { _logger = logger; @@ -155,7 +157,8 @@ public partial class ActivityModule : InteractionModuleBase + { + m.Content = ""; + m.Components = components.Build(); + m.Embed = _activityFormatter.ActivityEmbed(activity, Enumerable.Repeat(rolePlayer, 1).ToImmutableList()).Build(); + }); + } + + private ActivityPlayer CreateActivityPlayer(Activity activity, SocketGuildUser user, bool areRolesEnabled) + { + return areRolesEnabled ? new ActivityRolePlayer { Activity = activity, UserId = user.Id, @@ -218,29 +242,15 @@ public partial class ActivityModule : InteractionModuleBase - { - m.Content = ""; - m.Components = components.Build(); - m.Embed = _activityFormatter.ActivityEmbed(activity, Enumerable.Repeat(rolePlayer, 1).ToImmutableList()).Build(); - }); } [ComponentInteraction("activity join:*", ignoreGroupNames: true)] - public async Task JoinActivity(ulong activityId) + public async Task JoinActivity(ulong messageId) { var user = (SocketGuildUser)Context.User; // Check if activity exists - if (await _activitiesRepository.FindActivity(Context.Guild.Id, activityId) is not { } activity) + if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, messageId) is not { } activity) { await RespondAsync( ephemeral: true, @@ -251,7 +261,7 @@ public partial class ActivityModule : InteractionModuleBase= activity.MaxPlayers) + if (_activitiesRepository.ActivityPlayerCount(activity) >= activity.MaxPlayers) { await RespondAsync( ephemeral: true, @@ -272,20 +282,9 @@ public partial class ActivityModule : InteractionModuleBase + await channel.ModifyMessageAsync(activity.MessageId, properties => { properties.Embed = _activityFormatter.ActivityEmbed(activity, players).Build(); @@ -400,8 +397,8 @@ public partial class ActivityModule : InteractionModuleBase= activity.MaxPlayers; properties.Components = updateReason switch { - ActivityUpdateReason.PlayerJoin when isActivityFull => ActivityComponents(activity.ActivityId, disabled: true).Build(), - ActivityUpdateReason.PlayerLeave when !isActivityFull => ActivityComponents(activity.ActivityId, disabled: false).Build(), + ActivityUpdateReason.PlayerJoin when isActivityFull => ActivityComponents(activity.MessageId, disabled: true).Build(), + ActivityUpdateReason.PlayerLeave when !isActivityFull => ActivityComponents(activity.MessageId, disabled: false).Build(), _ => Optional.Unspecified }; }); diff --git a/Cocotte/Modules/Activities/ActivityModuleDebug.cs b/Cocotte/Modules/Activities/ActivityModuleDebug.cs index 6c5d821..c80648c 100644 --- a/Cocotte/Modules/Activities/ActivityModuleDebug.cs +++ b/Cocotte/Modules/Activities/ActivityModuleDebug.cs @@ -13,7 +13,7 @@ public partial class ActivityModule { if (message is IUserMessage userMessage && userMessage.Author.IsBot) { - if (await _activitiesRepository.FindActivity(Context.Guild.Id, message.Id) is { } activity) + if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, message.Id) is { } activity) { // Generate random player var player = new ActivityRolePlayer diff --git a/Cocotte/Modules/Activities/ActivityModuleThread.cs b/Cocotte/Modules/Activities/ActivityModuleThread.cs index ac13f9d..f4755f6 100644 --- a/Cocotte/Modules/Activities/ActivityModuleThread.cs +++ b/Cocotte/Modules/Activities/ActivityModuleThread.cs @@ -1,4 +1,8 @@ -using Discord; +using System.Diagnostics.CodeAnalysis; +using Cocotte.Modules.Activities.Models; +using Cocotte.Utils; +using Discord; +using Discord.Interactions; using Discord.WebSocket; namespace Cocotte.Modules.Activities; @@ -40,4 +44,92 @@ public partial class ActivityModule return thread.Id; } + + [SlashCommand("ajouter", "Ajouter un joueur à cette activité")] + public async Task ThreadAddPlayer(IUser user) + { + // Get activity linked to this thread + var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id); + + if (!await CheckCommandInThread(activity) || activity is null) + { + return; + } + + // Check that the user is not already in the activity + if (await _activitiesRepository.ActivityContainsUser(activity, user.Id)) + { + await RespondAsync( + ephemeral: true, + embed: EmbedUtils.ErrorEmbed("Cet utilisateur est **déjà inscrit** à cette activité").Build() + ); + + return; + } + + var activityPlayer = CreateActivityPlayer(activity, (SocketGuildUser) user, activity.AreRolesEnabled); + + activity.ActivityPlayers.Add(activityPlayer); + await _activitiesRepository.SaveChanges(); + + await UpdateActivityEmbed(activity, ActivityUpdateReason.PlayerJoin); + + await RespondAsync( + ephemeral: true, + embed: EmbedUtils.SuccessEmbed($"{((IGuildUser) user).DisplayName} a bien été **ajouté** à l'activité").Build() + ); + } + + [SlashCommand("ajouter", "Ajouter un joueur à cette activité")] + public async Task ThreadRemovePlayer(IUser user) + { + // TODO: Autocomplete + // Get activity linked to this thread + var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id); + + if (!await CheckCommandInThread(activity) || activity is null) + { + return; + } + + // Check that the user is not already in the activity + if (await _activitiesRepository.ActivityContainsUser(activity, user.Id)) + { + await RespondAsync( + ephemeral: true, + embed: EmbedUtils.ErrorEmbed("Cet utilisateur est **déjà inscrit** à cette activité").Build() + ); + + return; + } + + var activityPlayer = CreateActivityPlayer(activity, (SocketGuildUser) user, activity.AreRolesEnabled); + + activity.ActivityPlayers.Add(activityPlayer); + await _activitiesRepository.SaveChanges(); + + await UpdateActivityEmbed(activity, ActivityUpdateReason.PlayerJoin); + + await RespondAsync( + ephemeral: true, + embed: EmbedUtils.SuccessEmbed($"{((IGuildUser) user).DisplayName} a bien été **ajouté** à l'activité").Build() + ); + } + + + private async Task CheckCommandInThread(Activity? activity) + { + // Check if activity is not null (means we are in a valid thread) + if (activity is null) + { + await RespondAsync( + ephemeral: true, + embed: EmbedUtils.ErrorEmbed("Vous devez être dans un **thread lié à une activité** pour utiliser cette commande").Build() + ); + + return false; + } + + return true; + } } \ No newline at end of file diff --git a/Cocotte/Modules/Activities/Models/ActivitiesRepository.cs b/Cocotte/Modules/Activities/Models/ActivitiesRepository.cs index 4c8e7a6..2e290c1 100644 --- a/Cocotte/Modules/Activities/Models/ActivitiesRepository.cs +++ b/Cocotte/Modules/Activities/Models/ActivitiesRepository.cs @@ -12,18 +12,17 @@ public class ActivitiesRepository _cocotteDbContext = cocotteDbContext; } - public async Task FindActivity(ulong guildId, ulong activityId) + public async Task FindActivity(ulong guildId, ulong channelId, ulong messageId) { - return await _cocotteDbContext.Activities.FindAsync(guildId, activityId); + return await _cocotteDbContext.Activities.FindAsync(guildId, channelId, messageId); } - public async Task FindActivityPlayer(ulong guildId, ulong activityId, ulong playerId) + public async Task FindActivityPlayer(ulong guildId, ulong channelId, ulong messageId, ulong userId) { - return await _cocotteDbContext.ActivityPlayers.FindAsync(guildId, activityId, playerId); + return await _cocotteDbContext.ActivityPlayers.FindAsync(guildId, channelId, messageId, userId); } - public async Task ActivityPlayerCount(Activity activity) => - await _cocotteDbContext.ActivityPlayers.Where(player => player.ActivityId == activity.ActivityId).CountAsync(); + public int ActivityPlayerCount(Activity activity) => activity.ActivityPlayers.Count; public async Task> LoadActivityPlayers(Activity activity) { @@ -49,4 +48,17 @@ public class ActivitiesRepository { _cocotteDbContext.Activities.Remove(activity); } + + public Activity? FindActivityByThreadId(ulong threadId) + { + return _cocotteDbContext.Activities.FirstOrDefault(activity => activity.ThreadId == threadId); + } + + public async Task ActivityContainsUser(Activity activity, ulong userId) + { + return await _cocotteDbContext.Entry(activity) + .Collection(a => a.ActivityPlayers) + .Query() + .AnyAsync(p => p.UserId == userId); + } } \ No newline at end of file diff --git a/Cocotte/Modules/Activities/Models/Activity.cs b/Cocotte/Modules/Activities/Models/Activity.cs index cf12d98..e1ba1f7 100644 --- a/Cocotte/Modules/Activities/Models/Activity.cs +++ b/Cocotte/Modules/Activities/Models/Activity.cs @@ -4,13 +4,16 @@ using Microsoft.EntityFrameworkCore; namespace Cocotte.Modules.Activities.Models; -[PrimaryKey(nameof(GuildId), nameof(ActivityId))] +[PrimaryKey(nameof(GuildId), nameof(ChannelId), nameof(MessageId))] +[Index(nameof(ThreadId))] public class Activity { [DatabaseGenerated(DatabaseGeneratedOption.None)] public required ulong GuildId { get; init; } [DatabaseGenerated(DatabaseGeneratedOption.None)] - public required ulong ActivityId { get; init; } + public required ulong ChannelId { get; init; } + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public required ulong MessageId { get; init; } public required ulong ThreadId { get; init; } public required ulong CreatorUserId { get; init; } @@ -25,6 +28,6 @@ public class Activity public override string ToString() { - return $"{nameof(ActivityId)}: {ActivityId}, {nameof(CreatorUserId)}: {CreatorUserId}, {nameof(Description)}: {Description}, {nameof(Type)}: {Type}, {nameof(Name)}: {Name}, {nameof(MaxPlayers)}: {MaxPlayers}"; + return $"{nameof(MessageId)}: {MessageId}, {nameof(CreatorUserId)}: {CreatorUserId}, {nameof(Description)}: {Description}, {nameof(Type)}: {Type}, {nameof(Name)}: {Name}, {nameof(MaxPlayers)}: {MaxPlayers}"; } } \ No newline at end of file diff --git a/Cocotte/Modules/Activities/Models/ActivityPlayer.cs b/Cocotte/Modules/Activities/Models/ActivityPlayer.cs index 0c627d2..15afedf 100644 --- a/Cocotte/Modules/Activities/Models/ActivityPlayer.cs +++ b/Cocotte/Modules/Activities/Models/ActivityPlayer.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; namespace Cocotte.Modules.Activities.Models; -[PrimaryKey(nameof(GuildId), nameof(ActivityId), nameof(UserId))] +[PrimaryKey(nameof(GuildId), nameof(ChannelId), nameof(MessageId), nameof(UserId))] public class ActivityPlayer { [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -12,7 +12,8 @@ public class ActivityPlayer public required string Name { get; init; } public ulong GuildId { get; set; } - public ulong ActivityId { get; init; } + public ulong ChannelId { get; set; } + public ulong MessageId { get; init; } public required Activity Activity { get; init; } public override string ToString()