diff --git a/Attribute/CommandAttribute.cs b/Attribute/CommandAttribute.cs new file mode 100644 index 00000000..e8daea49 --- /dev/null +++ b/Attribute/CommandAttribute.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using PriconneBotConsoleApp.DataType; +using PriconneBotConsoleApp.Extension; + +namespace PriconneBotConsoleApp.Attribute +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public class CommandAttribute : System.Attribute + { + public CommandAttribute( + string[] names = null, + int minArgumentLength = 0, + int maxArgumentLength = int.MaxValue, + params ChannelFeatureType[] compatibleChannels) + { + Names = names ?? new[] { string.Empty }; + MinArgumentLength = minArgumentLength; + MaxArgumentLength = maxArgumentLength; + CompatibleChannels = compatibleChannels.Length == 0 ? new[] { ChannelFeatureType.All } : compatibleChannels; + } + + public CommandAttribute( + string name, + int minArgumentLength = 0, + int maxArgumentLength = int.MaxValue, + params ChannelFeatureType[] compatibleChannels) + : this( + name == null ? null : new[] { name }, + minArgumentLength, + maxArgumentLength, + compatibleChannels) + { + } + + public CommandAttribute( + AttackType attackType, + int minArgumentLength = 0, + int maxArgumentLength = int.MaxValue, + params ChannelFeatureType[] compatibleChannels) + : this( + attackType.GetMultiDescription().Names, + minArgumentLength, + maxArgumentLength, + compatibleChannels) + { + } + + /// + /// 受け取ったコマンドを格納する + /// + public IReadOnlyList Names { get; } + + /// + /// 引数の長さの最小値 + /// + public int MinArgumentLength { get; } + + /// + /// 引数の長さの最大値 + /// + public int MaxArgumentLength { get; } + + /// + /// コマンドが対応するチャンネル + /// + public IReadOnlyList CompatibleChannels { get; } + + public bool IsCompatibleArgumentLength(int argLength) + => MinArgumentLength <= argLength && argLength <= MaxArgumentLength; + } +} diff --git a/Attribute/Description.cs b/Attribute/MultiDescriptionAttribute.cs similarity index 55% rename from Attribute/Description.cs rename to Attribute/MultiDescriptionAttribute.cs index c3772e56..737441f6 100644 --- a/Attribute/Description.cs +++ b/Attribute/MultiDescriptionAttribute.cs @@ -1,20 +1,27 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace PriconneBotConsoleApp.Attribute { public struct MultiDescriptionData { - public string LongDescription; - public string ShortDescription; - public string[] Aliases; + public string LongDescription { get; init; } + public string ShortDescription { get; init; } + public IReadOnlyList Aliases { get; init; } + + private string[] m_Names; + + public string[] Names + => m_Names ??= Aliases.Append(LongDescription).Append(ShortDescription).ToArray(); } [AttributeUsage(AttributeTargets.Field)] public class MultiDescriptionAttribute : System.Attribute { public MultiDescriptionAttribute( - string longDescription, - string shortDescription = null, + string longDescription, + string shortDescription = null, params string[] aliases) { Data = new MultiDescriptionData @@ -26,5 +33,5 @@ public MultiDescriptionAttribute( } public MultiDescriptionData Data { get; } - } + } } diff --git a/DataModel/CommandEventArgs.cs b/DataModel/CommandEventArgs.cs new file mode 100644 index 00000000..8d046ced --- /dev/null +++ b/DataModel/CommandEventArgs.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Discord.WebSocket; +using PriconneBotConsoleApp.Database; +using PriconneBotConsoleApp.DataType; +using PriconneBotConsoleApp.Extension; + +namespace PriconneBotConsoleApp.DataModel +{ + public class CommandEventArgs : EventArgs + { + public CommandEventArgs(SocketUserMessage socketUserMessage) + { + SocketUserMessage = socketUserMessage; + + if (SocketUserMessage.Content.Length == 0) + { + throw new ArgumentException("Content.Length が 0"); + } + + var splitContents = SocketUserMessage.Content.ZenToHan().Split(' ', StringSplitOptions.RemoveEmptyEntries); + Name = splitContents[0]; + Arguments = splitContents.Length > 1 ? splitContents.Skip(1).ToList() : Array.Empty(); + User = SocketUserMessage.Author as SocketGuildUser; + Channel = SocketUserMessage.Channel as SocketTextChannel; + PlayerData = DatabasePlayerDataController.LoadPlayerData(Channel.Guild.Id, SocketUserMessage.Author.Id); + Role = Channel.Guild.GetRole(PlayerData?.ClanData.ClanRoleID ?? 0); + + if (Role == null) + { + return; + } + + ClanData = DatabaseClanDataController.LoadClanData(Role); + + ChannelFeatureType = + (ChannelFeatureType?)ClanData.ChannelData.FirstOrDefault(x => x.ChannelID == Channel.Id)?.FeatureID + ?? ChannelFeatureType.All; + } + + public SocketUserMessage SocketUserMessage { get; } + public string Name { get; } + public IReadOnlyList Arguments { get; } + public SocketGuildUser User { get; } + public SocketTextChannel Channel { get; } + public SocketRole Role { get; } + public ClanData ClanData { get; } + public PlayerData PlayerData { get; } + public ChannelFeatureType ChannelFeatureType { get; } + } +} diff --git a/DataModel/Shiori/ClanData.cs b/DataModel/Shiori/ClanData.cs index aad51050..e2465059 100644 --- a/DataModel/Shiori/ClanData.cs +++ b/DataModel/Shiori/ClanData.cs @@ -1,4 +1,5 @@ using PriconneBotConsoleApp.DataType; +using PriconneBotConsoleApp.Extension; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -82,68 +83,33 @@ public void SetBossLap(int bossNum, int bossLap) }; } + public int GetBossLap(BossNumberType bossNumberType) + => GetBossLap((int)bossNumberType); + + public void SetBossLap(BossNumberType bossNumberType, int bossLap) + => SetBossLap((int)bossNumberType, bossLap); + /// - /// 最も周回数を返す + /// クラン内のチャンネルIDを返す。 /// + /// /// - public int GetMinBossLap() - => Enumerable.Min(new int[] { Boss1Lap, Boss2Lap, Boss3Lap, Boss4Lap, Boss5Lap }); + public ulong GetChannelID(ChannelFeatureType channelFeatureType) + => ChannelData?.GetChannelID(ClanID, channelFeatureType) ?? 0; /// - /// 5つのボスデータから今のボスに変換。来月削除。 + /// クラン内のメッセージIDを返す。 /// - /// + /// /// - [Obsolete] - public byte GetNowBoss() - { - if (Boss1Lap == Boss2Lap - && Boss2Lap == Boss3Lap - && Boss3Lap == Boss4Lap - && Boss4Lap == Boss5Lap) - { - return (byte)BossNumberType.Boss5Number; - } - if (Boss1Lap == Boss2Lap + 1 ) - { - return (byte)BossNumberType.Boss1Number; - } - else if (Boss2Lap == Boss3Lap + 1) - { - return (byte)BossNumberType.Boss2Number; - } - else if (Boss3Lap == Boss4Lap + 1) - { - return (byte)BossNumberType.Boss3Number; - } - else if (Boss4Lap == Boss5Lap + 1) - { - return (byte)BossNumberType.Boss4Number; - } - - return 0; - } + public ulong GetMessageID(MessageFeatureType messageFeatureType) + => MessageData?.GetMessageID(ClanID, messageFeatureType) ?? 0; /// - /// 5つのボスデータから今のLapに変換。来月削除。 + /// 最も小さい周回数を返す /// - /// /// - [Obsolete] - public ushort GetNowLap() - { - if (Boss1Lap == Boss2Lap - && Boss2Lap == Boss3Lap - && Boss3Lap == Boss4Lap - && Boss4Lap == Boss5Lap) - { - return Boss5Lap; - } - else - { - return Boss1Lap; - } - } + public int GetMinBossLap() + => Enumerable.Min(new int[] { Boss1Lap, Boss2Lap, Boss3Lap, Boss4Lap, Boss5Lap }); } - } diff --git a/DataType/AttackType.cs b/DataType/AttackType.cs index 791b3bca..3e465638 100644 --- a/DataType/AttackType.cs +++ b/DataType/AttackType.cs @@ -13,7 +13,7 @@ public enum AttackType Magic, [MultiDescription("ニャル", "ニ", "n", "N")] - NewYearKyaru, + NewYearKaryl, [MultiDescription("持ち越し", "持", "-", "持越し", "持越")] CarryOver, diff --git a/DataType/BossNumberType.cs b/DataType/BossNumberType.cs index 6ca9c3a0..b792c2b6 100644 --- a/DataType/BossNumberType.cs +++ b/DataType/BossNumberType.cs @@ -2,6 +2,7 @@ { public enum BossNumberType { + Unknown = 0, Boss1Number = 1, Boss2Number = 2, Boss3Number = 3, diff --git a/DataType/ErrorType.cs b/DataType/ErrorType.cs index 2027cb0c..2c80f6dc 100644 --- a/DataType/ErrorType.cs +++ b/DataType/ErrorType.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.ComponentModel; +using System.ComponentModel; namespace PriconneBotConsoleApp.DataType { @@ -12,10 +9,13 @@ public enum ErrorType //予約関連 [Description("予約に失敗しました。")] FailedReservation, - [Description("予約できません。予約可能時間は{0}~{1}です。")] + + [Description("予約できません。予約可能時間は{0}:00~{1}:00です。")] OutOfReservationTime, + [Description("コメントが長いので切り取られました。\n 問題がある場合は予約削除をして再度予約してください。")] TooLongComment, + [Description("予約できません。予約は{0}周目まで可能です。")] OutOfReservationBossLaps, diff --git a/DataType/InfomationType.cs b/DataType/InformationType.cs similarity index 60% rename from DataType/InfomationType.cs rename to DataType/InformationType.cs index 7c7a52b2..6ffecbb7 100644 --- a/DataType/InfomationType.cs +++ b/DataType/InformationType.cs @@ -7,12 +7,16 @@ namespace PriconneBotConsoleApp.DataType { - public enum InfomationType + public enum InformationType { Unknown, // 凸報告関連 [Description("<@{0}>の凸報告を代理削除しました。\nこのメッセージは{1}秒後削除されます。")] - DeleteInsted, + DeleteInstead, + + //持ち越し関連 + [Description("持ち越しをすべて削除しました。\nこのメッセージは{0}秒後削除されます。")] + DeleteAllCarryOverData, } } diff --git a/DataType/ProgressStatus.cs b/DataType/ProgressStatus.cs index a1febc90..6f0a8e79 100644 --- a/DataType/ProgressStatus.cs +++ b/DataType/ProgressStatus.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.ComponentModel; namespace PriconneBotConsoleApp.DataType { @@ -25,6 +20,6 @@ public enum ProgressStatus : byte SOS, [Description("🏃")] - CarryOver, + SubdueBoss, } } diff --git a/DataType/ShioriFeature/ChannelFeatureType.cs b/DataType/ShioriFeature/ChannelFeatureType.cs index 5009709b..c0c9e151 100644 --- a/DataType/ShioriFeature/ChannelFeatureType.cs +++ b/DataType/ShioriFeature/ChannelFeatureType.cs @@ -5,6 +5,9 @@ public enum ChannelFeatureType : uint { Unknown = 0, + /// すべてのチャンネルで利用できるコマンドにつけられる. + All = 9999, + DeclareID = 1001, ReserveID = 1002, ReserveResultID = 1003, diff --git a/DataType/WarningType.cs b/DataType/WarningType.cs new file mode 100644 index 00000000..8cc0c060 --- /dev/null +++ b/DataType/WarningType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PriconneBotConsoleApp.DataType +{ + public enum WarningType + { + [Description("コメントが長いので切り取られました。\n 問題がある場合は予約削除をして再度予約してください。") ] + TooLongComment, + } +} diff --git a/Database/Shiori/DatabaseProgressController.cs b/Database/Shiori/DatabaseProgressController.cs index 5c13a8c7..89bb012d 100644 --- a/Database/Shiori/DatabaseProgressController.cs +++ b/Database/Shiori/DatabaseProgressController.cs @@ -23,10 +23,11 @@ public static IEnumerable GetProgressData(ClanData clanData, BossN public static IEnumerable GetProgressData(PlayerData playerData, BossNumberType bossNumber) { + var playerID = playerData?.PlayerID ?? 0; using var databaseConnector = new DatabaseConnector(); return databaseConnector.ProgressData.AsQueryable() - .Where(x => x.PlayerID == playerData.PlayerID && x.BossNumber == (byte)bossNumber && !x.DeleteFlag) + .Where(x => x.PlayerID == playerID && x.BossNumber == (byte)bossNumber && !x.DeleteFlag) .ToArray(); } diff --git a/Database/Shiori/DatabaseReservationController.cs b/Database/Shiori/DatabaseReservationController.cs index f644d435..f6424f1d 100644 --- a/Database/Shiori/DatabaseReservationController.cs +++ b/Database/Shiori/DatabaseReservationController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using PriconneBotConsoleApp.DataModel; @@ -35,24 +36,22 @@ public static List LoadReservationData(ClanData clanData) public static List LoadReservationData(PlayerData playerData) { - if (playerData == null) + if (playerData == null|| playerData.PlayerID == 0) { return null; } using var databaseConnector = new DatabaseConnector(); - var playerID = LoadPlayerID(databaseConnector.PlayerData, playerData); - return databaseConnector.ReservationData.AsQueryable() - .Where(b => b.PlayerID == playerID && !b.DeleteFlag) + .Where(b => b.PlayerID == playerData.PlayerID && !b.DeleteFlag) .OrderBy(o => o.BattleLap).ThenBy(d => d.BossNumber) .ToList(); } public static IEnumerable LoadReservationData(ClanData clanData, int bossNumber) { - if (bossNumber < CommonDefine.MinBossNumber || bossNumber > CommonDefine.MaxBossNumber) + if (!CommonDefine.IsValidBossNumber(bossNumber)) { return null; } @@ -67,7 +66,7 @@ public static IEnumerable LoadReservationData(ClanData clanData public static List LoadBossLapReservationData(ClanData clanData, int bossNumber) { - if (bossNumber < CommonDefine.MinBossNumber || bossNumber > CommonDefine.MaxBossNumber) + if (!CommonDefine.IsValidBossNumber(bossNumber)) { return null; } @@ -96,28 +95,25 @@ public static List LoadBossLapReservationData(ClanData clanData /// public static void CreateReservationData(ReservationData reservationData) { - var playerData = reservationData.PlayerData; - using var databaseConnector = new DatabaseConnector(); - var transaction = databaseConnector.Database.BeginTransaction(); - var playerID = LoadPlayerID(databaseConnector.PlayerData, playerData); - - if (playerID == 0) + if (reservationData.PlayerID == 0) { - transaction.Rollback(); return; } - databaseConnector.ReservationData.Add( - new ReservationData() - { - PlayerID = playerID, - BattleLap = reservationData.BattleLap, - BossNumber = reservationData.BossNumber, - CommentData = reservationData.CommentData - }); + using var databaseConnector = new DatabaseConnector(); + var transaction = databaseConnector.Database.BeginTransaction(); - databaseConnector.SaveChanges(); - transaction.Commit(); + databaseConnector.ReservationData.Add(reservationData); + + try + { + databaseConnector.SaveChanges(); + transaction.Commit(); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } } /// @@ -126,30 +122,20 @@ public static void CreateReservationData(ReservationData reservationData) /// public static void UpdateReservationData(ReservationData reservationData) { - var playerData = reservationData.PlayerData; using var databaseConnector = new DatabaseConnector(); var transaction = databaseConnector.Database.BeginTransaction(); - var playerID = LoadPlayerID(databaseConnector.PlayerData, playerData); + var updateData = databaseConnector.ReservationData.FirstOrDefault(x => x.ReserveID == reservationData.ReserveID); - if (playerID == 0) + try { - transaction.Rollback(); - return; + updateData.CommentData = reservationData.CommentData; + databaseConnector.SaveChanges(); + transaction.Commit(); } - - var updateData = databaseConnector.ReservationData - .FirstOrDefault(d => d.PlayerID == playerID && d.BattleLap == reservationData.BattleLap - && d.BossNumber == reservationData.BossNumber && !d.DeleteFlag); - - if (updateData == null) + catch { transaction.Rollback(); - return; } - - updateData.CommentData = reservationData.CommentData; - databaseConnector.SaveChanges(); - transaction.Commit(); } public static void DeleteReservationData(ReservationData reservationData) @@ -162,7 +148,6 @@ public static void DeleteReservationData(IEnumerable reservatio foreach (var reservationData in reservationDataSet) { - var updateData = databaseConnector.ReservationData.AsQueryable() .FirstOrDefault(d => d.PlayerID == reservationData.PlayerID && d.BossNumber == reservationData.BossNumber && d.BattleLap == reservationData.BattleLap && !d.DeleteFlag); @@ -176,15 +161,5 @@ public static void DeleteReservationData(IEnumerable reservatio databaseConnector.SaveChanges(); transaction.Commit(); } - - private static ulong LoadPlayerID(IQueryable queryable, PlayerData playerData) - { - return queryable - .Include(b => b.ClanData) - .FirstOrDefault(b => b.ClanData.ServerID == playerData.ClanData.ServerID - && b.ClanData.ClanRoleID == playerData.ClanData.ClanRoleID - && b.UserID == playerData.UserID) - ?.PlayerID ?? 0; - } } } diff --git a/Define/ClanBattleDefine.cs b/Define/ClanBattleDefine.cs index 84e6af72..701edba8 100644 --- a/Define/ClanBattleDefine.cs +++ b/Define/ClanBattleDefine.cs @@ -2,6 +2,9 @@ { public static class ClanBattleDefine { - public const short MaxLapNumber = 250; + public const ushort MaxLapNumber = 250; + + public static bool IsValidLapNumber(int value) + => 0 <= value && value <= MaxLapNumber; } } diff --git a/Define/CommonDefine.cs b/Define/CommonDefine.cs index 5c9dac39..43cb2641 100644 --- a/Define/CommonDefine.cs +++ b/Define/CommonDefine.cs @@ -19,5 +19,14 @@ public static class CommonDefine public const int MaxDamageValue = 999999; public const int DisplayDamageUnit = 10000; + + public static bool IsValidBossNumber(int value) + => MinBossNumber <= value && value <= MaxBossNumber; + + public static bool IsValidBattleTime(int value) + => MinBattleTime <= value && value <= MaxBattleTime; + + public static bool IsValidDamageValue(int value) + => 0 <= value && value <= MaxDamageValue; } } diff --git a/Define/TimeDefine.cs b/Define/TimeDefine.cs index 0fcb6dc1..8bb0a977 100644 --- a/Define/TimeDefine.cs +++ b/Define/TimeDefine.cs @@ -8,6 +8,7 @@ public static class TimeDefine public readonly static TimeSpan DailyRefreshTime = new(5, 0, 30); public readonly static TimeSpan ErrorMessageDisplayTime = new(0, 0, 5); + public readonly static TimeSpan WarningMessageDisplayTime = new(0, 0, 20); public readonly static TimeSpan SuccessMessageDisplayTime = new(0, 0, 30); } } diff --git a/Extension/DictionaryExtension.cs b/Extension/DictionaryExtension.cs index 97a43ff0..59815ad6 100644 --- a/Extension/DictionaryExtension.cs +++ b/Extension/DictionaryExtension.cs @@ -1,22 +1,51 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace PriconneBotConsoleApp.Extension { public static class DictionaryExtension { public static TValue GetValueOrInitialize( - this IDictionary dictionary, - TKey key, - Func initializer) + [NotNull] this IDictionary dictionary, + [NotNull] TKey key, + [NotNull] Func initializer) => dictionary.TryGetValue(key, out var result) ? result : dictionary[key] = initializer(); - public static void Deconstruct( - this KeyValuePair pair, - out Tkey key, - out TValue value) + /// + /// を複数のキーで検索できるようにしたバージョン + /// + /// paramsに対応するため、out引数を先に書く必要がある点に注意 + /// + /// 検索対象の辞書 + /// 検索結果 (無い場合はdefaultになる) + /// キーの候補 (前方から順に検索される) + /// 辞書のキーの型 + /// 辞書の値の型 + /// + public static bool TryGetValueMany( + [NotNull] this IDictionary dictionary, + [NotNull] out TValue value, + [NotNull] params TKey[] keys) + { + foreach (var key in keys) + { + if (dictionary.TryGetValue(key, out value)) + { + return true; + } + } + + value = default; + return false; + } + + public static void Deconstruct( + [NotNull] this KeyValuePair pair, + [NotNull] out TKey key, + [NotNull] out TValue value) => (key, value) = (pair.Key, pair.Value); } } diff --git a/Extension/EnumExtension.cs b/Extension/EnumExtension.cs index fb8abb49..4a74f00b 100644 --- a/Extension/EnumExtension.cs +++ b/Extension/EnumExtension.cs @@ -1,30 +1,43 @@ -using PriconneBotConsoleApp.Attribute; -using System; +using System; using System.ComponentModel; using System.Reflection; +using PriconneBotConsoleApp.Attribute; +using PriconneBotConsoleApp.DataType; namespace PriconneBotConsoleApp.Extension { public static class EnumExtension { - public static string GetDescription(this Enum type) + public static string GetDescription(this T enumValue) where T : Enum { - var descriptionAttribute = type.GetType() - .GetField(type.ToString()).GetCustomAttribute(false); + var enumString = enumValue.ToString(); - return descriptionAttribute?.Description ?? type.ToString(); + return typeof(T).GetField(enumString)?.GetCustomAttribute(false)?.Description + ?? enumString; } - public static MultiDescriptionData GetMultiDescripion(this Enum enumValue) - => enumValue.GetType() - .GetField(enumValue.ToString()) - .GetCustomAttribute(false) - ?.Data - ?? new MultiDescriptionData - { - LongDescription = enumValue.ToString(), - ShortDescription = string.Empty, - Aliases = Array.Empty(), - }; + public static MultiDescriptionData GetMultiDescription(this T enumValue) where T : Enum + { + var enumString = enumValue.ToString(); + + return typeof(T).GetField(enumString)?.GetCustomAttribute(false)?.Data + ?? new MultiDescriptionData + { + LongDescription = enumString, + ShortDescription = string.Empty, + Aliases = Array.Empty() + }; + } + + public static BossNumberType GetBossNumberType(this ChannelFeatureType channelFeatureType) + => channelFeatureType switch + { + ChannelFeatureType.ProgressBoss1ID => BossNumberType.Boss1Number, + ChannelFeatureType.ProgressBoss2ID => BossNumberType.Boss2Number, + ChannelFeatureType.ProgressBoss3ID => BossNumberType.Boss3Number, + ChannelFeatureType.ProgressBoss4ID => BossNumberType.Boss4Number, + ChannelFeatureType.ProgressBoss5ID => BossNumberType.Boss5Number, + _ => throw new ArgumentException($"Cannot cast to BossNumberType. {channelFeatureType}") + }; } } diff --git a/Extension/EnumerableExtension.cs b/Extension/EnumerableExtension.cs new file mode 100644 index 00000000..bfa4920f --- /dev/null +++ b/Extension/EnumerableExtension.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace PriconneBotConsoleApp.Extension +{ + public static class EnumerableExtension + { + public static void ForEach([NotNull] this IEnumerable source, [NotNull] Action action) + { + foreach (var element in source) + { + action(element); + } + } + + public static TSource MaxBy([NotNull] this IEnumerable source, [NotNull] Func func) + where TValue : IComparable + { + var firstFlag = true; + TValue maxValue = default; + TSource maxSource = default; + + foreach (var element in source) + { + var value = func(element); + + if (firstFlag || value.CompareTo(maxValue) > 0) + { + maxValue = value; + maxSource = element; + firstFlag = false; + } + } + + return maxSource; + } + } +} diff --git a/Extension/IEnumerableExtension.cs b/Extension/IEnumerableExtension.cs deleted file mode 100644 index d557000c..00000000 --- a/Extension/IEnumerableExtension.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PriconneBotConsoleApp.Extension -{ - public static class IEnumerableExtension - { - public static void ForEach(this IEnumerable source, Action action) - { - foreach (T element in source) - { - action(element); - } - } - } -} \ No newline at end of file diff --git a/Script/ClanBattle/BattleCarryOver.cs b/Script/ClanBattle/BattleCarryOver.cs index a6e2e8b1..38797a00 100644 --- a/Script/ClanBattle/BattleCarryOver.cs +++ b/Script/ClanBattle/BattleCarryOver.cs @@ -14,9 +14,7 @@ namespace PriconneBotConsoleApp.Script { public class BattleCarryOver { - private readonly ClanData m_ClanData; - private readonly SocketUserMessage m_UserMessage; - private readonly SocketRole m_ClanRole; + private readonly CommandEventArgs m_CommandEventArgs; private class PlayerInfo { @@ -47,165 +45,145 @@ public string GetCarryOverString() } } - public BattleCarryOver(ClanData clanData, SocketUserMessage userMessage) + public BattleCarryOver(CommandEventArgs commandEventArgs) { - m_ClanData = clanData; - m_UserMessage = userMessage; - var guild = (userMessage.Channel as SocketTextChannel)?.Guild; - m_ClanRole = guild?.GetRole(clanData.ClanRoleID); - } - - public async Task RunByMessage() - { - var messageContent = m_UserMessage.Content; - - if (messageContent.StartsWith("!")) - { - if (messageContent.StartsWith("!init")) - { - InitAllData(); - } - else if (messageContent.StartsWith("!rm")) - { - UpdateOtherPlayerData(); - } - else if (messageContent.StartsWith("!list")) - { - await SendClanCarryOverList(); - } - - } - else - { - UpdateCarryOverData(); - } + m_CommandEventArgs = commandEventArgs; } /// - /// 個人の持ち越し所持・消化報告 + /// 持ち越しを登録するか更新する. 引数は2以上 /// - private void UpdateCarryOverData() + public void UpdateCarryOverData() { - const int minCommandLength = 1; - // TODO : " "を定数化する。 - var splitMessage = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); - var result = false; + var playerData = DatabasePlayerDataController + .LoadPlayerData(m_CommandEventArgs.Role, m_CommandEventArgs.SocketUserMessage.Author.Id); - if (splitMessage.Length < minCommandLength) + if (!byte.TryParse(m_CommandEventArgs.Arguments[0], out var bossNumber) + || !byte.TryParse(m_CommandEventArgs.Arguments[1], out var remainTime) + || !CommonDefine.IsValidBossNumber(bossNumber) + || !CommonDefine.IsValidBattleTime(remainTime) + || playerData == null) { return; } - // TODO : 持ち越し番号をEnum化 - - if (EnumMapper.TryParse(splitMessage.First(), out var attackType) - && attackType == AttackType.CarryOver) + CarryOverData carryOverData = new() { - var userCarryOverData = CommandToCarryOverData(splitMessage); + BossNumber = bossNumber, + RemainTime = remainTime, + // TODO : " "を定数化する。 + CommentData = string.Join(" ", m_CommandEventArgs.Arguments.Skip(2)), + PlayerID = playerData.PlayerID, + }; - if (userCarryOverData == null) - { - return; - } + var databaseCarryOverList = DatabaseCarryOverController.GetCarryOverData(playerData).ToArray(); + var databaseCarryOverData = databaseCarryOverList.FirstOrDefault(x => x.BossNumber == carryOverData.BossNumber && x.RemainTime == carryOverData.RemainTime); + var result = false; - var userID = m_UserMessage.Author.Id; - var playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, userID); + if (databaseCarryOverData != null) + { + carryOverData.CarryOverID = databaseCarryOverData.CarryOverID; + result = DatabaseCarryOverController.UpdateCarryOverData(carryOverData); + } + else if (databaseCarryOverList.Length < CommonDefine.MaxCarryOverNumber) + { + result = DatabaseCarryOverController.CreateCarryOverData(carryOverData); + } - if (playerData == null) - { - return; - } + if (result) + { + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); + } + } - var databaseCarryOverList = DatabaseCarryOverController.GetCarryOverData(playerData).ToArray(); - var databaseCarryOverData = databaseCarryOverList.FirstOrDefault(x => x.BossNumber == userCarryOverData.BossNumber && x.RemainTime == userCarryOverData.RemainTime); + /// + /// 持ち越しデータを削除する時に用いる。引数0か1。 + /// + public void DeleteCarryOverData() + { + const int defaultDeleteNumber = 1; + var playerData = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.Role, m_CommandEventArgs.User.Id); - if (databaseCarryOverData == null && databaseCarryOverList.Length < CommonDefine.MaxCarryOverNumber) - { - result = DatabaseCarryOverController.CreateCarryOverData(userCarryOverData); - } - else if (databaseCarryOverData != null) - { - userCarryOverData.CarryOverID = databaseCarryOverData.CarryOverID; - result = DatabaseCarryOverController.UpdateCarryOverData(userCarryOverData); - } - } - // TODO:ここの消化記述を別にまとめたい - else if (splitMessage.First() == "消化") + try { - var playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, m_UserMessage.Author.Id); - - if (splitMessage.Length > 1 && byte.TryParse(splitMessage[1], out byte deleteNumber)) + if (byte.TryParse(m_CommandEventArgs.Arguments.ElementAtOrDefault(0), out var deleteNumber)) { - result = DeleteCarryOverData(playerData, deleteNumber); + DeletePlayerCarryOverData(playerData, deleteNumber); } else { - result = DeleteCarryOverData(playerData); + DeletePlayerCarryOverData(playerData, defaultDeleteNumber); } } - - if (result) + catch { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + return; } + + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); } - private void UpdateOtherPlayerData() + /// + /// 他人の持越しを削除する。引数は2つ必要とする。 + /// + public void DeleteOtherPlayerData() { - const int minCommandLength = 1; - - // TODO : " "を定数化する。 - var splitMessage = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); + var targetUser = MentionUtils.TryParseUser(m_CommandEventArgs.Arguments[0], out var userID) + || ulong.TryParse(m_CommandEventArgs.Arguments[0], out userID) + ? m_CommandEventArgs.Role.Guild.GetUser(userID) + : throw new ArgumentNullException("ユーザー情報がありません。"); - if (splitMessage.Length <= minCommandLength || m_UserMessage.MentionedUsers.FirstOrDefault()?.Id is not ulong userID) + // コマンドは `!rm @削除対象のユーザー 古い方から何番目か` としている。 + if (!byte.TryParse(m_CommandEventArgs.Arguments[1], out var number) + || number <= 0 + || CommonDefine.MaxReportNumber < number) { - return; + throw new ArgumentOutOfRangeException("持ち越しがありません。"); } - // コマンドは `!rm @削除対象のユーザー 古い方から何番目か` としている。 - var deleteNumber = (splitMessage.Length > minCommandLength + 1 && byte.TryParse(splitMessage[minCommandLength + 1], out var number)) ? number : (byte)0; + var playerData = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.Role, targetUser.Id); - var playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, userID); - - if (DeleteCarryOverData(playerData, deleteNumber)) + try + { + DeletePlayerCarryOverData(playerData, number); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); + } + catch (Exception e) { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + Console.WriteLine(e.Message); } } - private async Task SendClanCarryOverList() + /// + /// 持ち越しリストを表示するコマンド。引数0か1。 + /// + /// + public async Task SendClanCarryOverList() { var clanCarryOverEmbed = CreateEmbedData(); - await m_UserMessage.Channel.SendMessageAsync(embed: clanCarryOverEmbed); + await m_CommandEventArgs.Channel.SendMessageAsync(embed: clanCarryOverEmbed); } - private bool DeleteCarryOverData(PlayerData playerData, byte deleteNumber = 0) + private void DeletePlayerCarryOverData(PlayerData playerData, byte deleteNumber) { var carryOverList = DatabaseCarryOverController.GetCarryOverData(playerData) - .OrderBy(x => x.DateTime).ToArray(); + .OrderBy(x => x.DateTime) + .ToArray(); if (carryOverList.Length == 0) { - return false; + throw new ArgumentOutOfRangeException("持ち越しがありません。"); } - var result = false; - - if (deleteNumber > 0 && deleteNumber <= carryOverList.Length) - { - result = DatabaseCarryOverController.DeleteCarryOverData(carryOverList[deleteNumber - 1]); - } - else - { - result = DatabaseCarryOverController.DeleteCarryOverData(carryOverList.First()); - } - - return result; + DatabaseCarryOverController.DeleteCarryOverData(carryOverList[(deleteNumber > 0 && deleteNumber <= carryOverList.Length) ? deleteNumber - 1 : 0]); } - private void InitAllData() + /// + /// 全ての持ち越しを削除する。引数は0。 + /// + public void InitAllData() { - var carryOverList = DatabaseCarryOverController.GetCarryOverData(m_ClanData); + var carryOverList = DatabaseCarryOverController.GetCarryOverData(m_CommandEventArgs.ClanData); if (!carryOverList.Any()) { @@ -213,41 +191,21 @@ private void InitAllData() } DatabaseCarryOverController.DeleteCarryOverData(carryOverList); - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); - } + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); - private CarryOverData CommandToCarryOverData(string[] messageData) - { - const int bossNumberColumn = 1; - const int remainTimeColumn = 2; - const int messageMinLength = 3; - - if (messageData.Length < messageMinLength || - !byte.TryParse(messageData[bossNumberColumn], out var bossNumber) || !byte.TryParse(messageData[remainTimeColumn], out var remainTime) - || bossNumber < CommonDefine.MinBossNumber || bossNumber > CommonDefine.MaxBossNumber - || remainTime < CommonDefine.MinBattleTime || remainTime > CommonDefine.MaxBattleTime) - { - return null; - } - - var playerID = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, m_UserMessage.Author.Id).PlayerID; - - return new CarryOverData() - { - BossNumber = bossNumber, - RemainTime = remainTime, - // TODO : " "を定数化する。 - CommentData = string.Join(" ", messageData.Skip(3)), - PlayerID = playerID, - }; + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync( + TimeDefine.SuccessMessageDisplayTime, + string.Format(EnumMapper.ToLabel(InformationType.DeleteAllCarryOverData), TimeDefine.SuccessMessageDisplayTime) + ); } // 持ち越しを表示するUIを考える private Embed CreateEmbedData() { - var carryOverArray = DatabaseCarryOverController.GetCarryOverData(m_ClanData) + var carryOverArray = DatabaseCarryOverController.GetCarryOverData(m_CommandEventArgs.ClanData) .OrderBy(x => x.DateTime); - var playerArray = DatabasePlayerDataController.LoadPlayerData(m_ClanData); + + var playerArray = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.ClanData); List carryOverStringList = new(); foreach (var playerData in playerArray) diff --git a/Script/ClanBattle/BattleDeclaration.cs b/Script/ClanBattle/BattleDeclaration.cs index 7d72d56e..582bc32f 100644 --- a/Script/ClanBattle/BattleDeclaration.cs +++ b/Script/ClanBattle/BattleDeclaration.cs @@ -80,9 +80,9 @@ public async Task RunDeclarationCommandByMessage() if (m_UserMessage.Content.StartsWith("!call")) { await DeclarationCallCommand(); - var battleReservation = new BattleReservation(m_UserRole); - battleReservation.DeleteUnusedData(m_BossNumber); - await battleReservation.UpdateSystemMessage(); + var battleReservationSummary = new BattleReservationSummary(m_UserRole); + battleReservationSummary.DeleteUnusedData(m_BossNumber); + await battleReservationSummary.UpdateMessage(); } } @@ -119,7 +119,7 @@ public async Task RunByInteraction() case ButtonType.SubdueBoss: await NextBossCommand(); - await new BattleReservation(m_UserRole).UpdateSystemMessage(); + await new BattleReservationSummary(m_UserRole).UpdateMessage(); return; case ButtonType.CancelBattle: @@ -128,7 +128,7 @@ public async Task RunByInteraction() } await UpdateDeclarationBotMessage(); - await new BattleReservation(m_UserRole).UpdateSystemMessage(); + await new BattleReservationSummary(m_UserRole).UpdateMessage(); } /// @@ -307,9 +307,9 @@ private async Task NextBossCommand() m_UserClanData.SetBossLap(m_BossNumber, nextBattleLap); DatabaseClanDataController.UpdateClanData(m_UserClanData); - var battleReservation = new BattleReservation(m_UserRole); - battleReservation.DeleteUnusedData(m_BossNumber); - await Task.WhenAll(SendDeclarationBotMessage(), battleReservation.UpdateSystemMessage()); + var battleReservationSummary = new BattleReservationSummary(m_UserRole); + battleReservationSummary.DeleteUnusedData(m_BossNumber); + await Task.WhenAll(SendDeclarationBotMessage(), battleReservationSummary.UpdateMessage()); } /// diff --git a/Script/ClanBattle/BattleProgress.cs b/Script/ClanBattle/BattleProgress.cs index a7dd4d04..ff184f50 100644 --- a/Script/ClanBattle/BattleProgress.cs +++ b/Script/ClanBattle/BattleProgress.cs @@ -9,20 +9,13 @@ using PriconneBotConsoleApp.DataModel; using PriconneBotConsoleApp.DataType; using PriconneBotConsoleApp.Define; -using PriconneBotConsoleApp.Extension; namespace PriconneBotConsoleApp.Script { public class BattleProgress { - private readonly SocketUserMessage m_UserMessage; - private readonly SocketRole m_UserRole; - private readonly ClanData m_UserClanData; - private readonly SocketGuild m_Guild; - private readonly byte m_BossNumber; - private readonly bool m_AllBattleFlag = true; - - private ProgressData m_UserProgressData; + private readonly CommandEventArgs m_CommandEventArgs; + private readonly BossNumberType m_BossNumberType; private class PlayerInfo { @@ -44,7 +37,7 @@ public string GetNameWithData() return string.Join( halfSizeWhitespace, - ((ProgressStatus) ProgressData.Status).ToLabel(), + ((ProgressStatus)ProgressData.Status).ToLabel(), ProgressData.CarryOverFlag ? "持" : fullSizeWhitespace, $"{ProgressData.Damage,6}@{ProgressData.RemainTime:D2}", ((AttackType)ProgressData.AttackType).ToShortLabel(), @@ -55,177 +48,162 @@ public string GetNameWithData() } } - public BattleProgress(ClanData clanData, SocketUserMessage userMessage, byte bossNumber) + public BattleProgress(CommandEventArgs commandEventArgs) + { + m_CommandEventArgs = commandEventArgs; + m_BossNumberType = ChannelTypeToBossNumber(m_CommandEventArgs.ChannelFeatureType); + } + + /// + /// 進行を開始するコマンド。引数は「ボス番号」。 + /// + /// + public async Task Start() { - if (clanData.RoleData == null || clanData.ChannelData == null || clanData.MessageData == null) + if (!ushort.TryParse(m_CommandEventArgs.Arguments[0], out var lap) + || !ClanBattleDefine.IsValidLapNumber(lap)) { - clanData = DatabaseClanDataController.LoadClanData(m_UserRole); + return; } - m_BossNumber = bossNumber; - m_UserClanData = clanData; - m_UserMessage = userMessage; - m_Guild = (userMessage.Channel as SocketTextChannel)?.Guild; - m_UserRole = m_Guild?.GetRole(clanData.ClanRoleID); + await ChangeLap(lap); } - public async Task RunByMessage() + public async Task SendList() + => await SendClanProgressList(); + + public async Task NextBoss() + => await ChangeLap(); + + /// + /// 編成データをアップデートする。 + /// + /// + /// + public async Task UpdateAttackData(AttackType attackType) { - if (m_UserMessage.Content.StartsWith("!")) + if (!TryGetProgressData(out var userProgressData)) { - if (m_UserMessage.Content.StartsWith("!init")) - { - InitializeProgressData(); - return; - } - else if (m_UserMessage.Content.StartsWith("!list")) - { - await SendClanProgressList(); - return; - } - else if (m_UserMessage.Content.StartsWith("!next")) - { - await ChangeLap(); - return; - } - else if (m_UserMessage.Content.StartsWith("!call")) - { - await CallProgress(); - return; - } - else if (m_UserMessage.Content.StartsWith("!rm")) + userProgressData = new ProgressData { - await RevertOrRevertUserData(true); - return; - } - else if (m_UserMessage.Content.StartsWith("!rv")) - { - await RevertOrRevertUserData(); - return; - } + PlayerID = m_CommandEventArgs.PlayerData.PlayerID, + BossNumber = (byte)m_BossNumberType, + BattleLap = (ushort)m_CommandEventArgs.ClanData.GetBossLap(m_BossNumberType), + }; + } + + if (attackType == AttackType.CarryOver) + { + userProgressData.CarryOverFlag = true; } else { - await UpdateProgressData(); + userProgressData.AttackType = (byte)attackType; } - return; + await UpdateProgressData(userProgressData, m_CommandEventArgs.PlayerData); } - private async Task UpdateProgressData() + /// + /// ダメージ・残り時間を抽出。 + /// + /// + /// + public async Task UpdateDamageData() { - var messageData = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); - var progressUser = m_UserMessage.Author; + // TODO : progressDataFlagでやりくりしているのが非常に見にくいので修正したい + var progressDataFlag = false; + int damageNumber = 0; + byte remainTimeNumber = 0; - if (m_UserMessage.MentionedUsers.Count() == 1) + // TODO : 冗長なRegexの高速化 #131 + if (Regex.IsMatch(m_CommandEventArgs.Name, @"\d+万$")) { - progressUser = m_UserMessage.MentionedUsers.FirstOrDefault(); - } - - var progressPlayerData = DatabasePlayerDataController.LoadPlayerData(m_UserRole, progressUser.Id); - m_UserProgressData = DatabaseProgressController.GetProgressData(progressPlayerData, (BossNumberType)m_BossNumber) - .Where(x => x.Status != (byte)ProgressStatus.AttackDone || x.Status != (byte)ProgressStatus.CarryOver) - .FirstOrDefault(); - - var successFlag = false; + if (!int.TryParse(Regex.Match(m_CommandEventArgs.Name, @"\d+").ToString(), out damageNumber) + || CommonDefine.IsValidDamageValue(damageNumber)) + { + return; + } - if (UpdateAttackData(messageData[0])) - { - successFlag = true; - } - else if (UpdateDamageData(messageData)) - { - successFlag = true; - } - else if (m_UserProgressData == null) - { - return false; - } - else if (m_UserProgressData.ProgressID != 0 ) - { - successFlag = UpdateStatusData(messageData); + progressDataFlag = true; } - if (!successFlag) + // TODO : 冗長なRegexの高速化 #131 + if (Regex.IsMatch(m_CommandEventArgs.Name, @"\d+@\d+")) { - return false; - } + var damageText = Regex.Match(m_CommandEventArgs.Name, @"\d+@").ToString(); + var remainTimeText = Regex.Match(m_CommandEventArgs.Name, @"@\d+").ToString(); - if (m_UserProgressData.Status == (byte)ProgressStatus.Unknown) - { - m_UserProgressData.Status = (byte)ProgressStatus.AttackReported; - } + if (!int.TryParse(Regex.Match(damageText, @"\d+").ToString(), out damageNumber) + || !byte.TryParse(Regex.Match(remainTimeText, @"\d+").ToString(), out remainTimeNumber) + || CommonDefine.IsValidDamageValue(damageNumber) + || remainTimeNumber > CommonDefine.MaxBattleTime) + { + return; + } - m_UserProgressData.BossNumber = m_BossNumber; - m_UserProgressData.BattleLap = (ushort)m_UserClanData.GetBossLap(m_BossNumber); + progressDataFlag = true; + } - if (m_UserProgressData.ProgressID == 0) + if (!progressDataFlag) { - m_UserProgressData.PlayerID = progressPlayerData.PlayerID; - - if (DatabaseProgressController.CreateProgressData(m_UserProgressData)) - { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); - await SendClanProgressList(); - return true; - } + return; } - else + + if (!TryGetProgressData(out var userProgressData)) { - if (DatabaseProgressController.ModifyProgressData(m_UserProgressData)) + userProgressData = new ProgressData() { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); - await SendClanProgressList(); - return true; - } + PlayerID = m_CommandEventArgs.PlayerData.PlayerID, + BossNumber = (byte)m_BossNumberType, + BattleLap = (ushort)m_CommandEventArgs.ClanData.GetBossLap(m_BossNumberType), + }; } - return false; - } - - private async Task CallProgress() - { - var messageData = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); - - if (messageData.Length != 2 || !ushort.TryParse(messageData[1], out var lap)) + if (damageNumber == 0) { - return; + userProgressData.Status = (byte)ProgressStatus.SOS; } - await ChangeLap(lap); + userProgressData.Damage = (uint)damageNumber; + userProgressData.RemainTime = remainTimeNumber; + userProgressData.CommentData = string.Join(" ", m_CommandEventArgs.Arguments); + await UpdateProgressData(userProgressData, m_CommandEventArgs.PlayerData); } - private async Task ChangeLap(ushort lap = 0) + /// + /// 進行報告のステータス変更。引数 1か2。 + /// + /// + /// + public async Task UpdateStatusData(ProgressStatus progressStatus) { - InitializeProgressData(); - var bossLap = m_UserClanData.GetBossLap(m_BossNumber); - - if (lap == 0) - { - m_UserClanData.SetBossLap(m_BossNumber, bossLap + 1); - } - else + if (!TryGetProgressData(out var userProgressData)) { - m_UserClanData.SetBossLap(m_BossNumber, lap); + return; } - DatabaseClanDataController.UpdateClanData(m_UserClanData); - await SendClanProgressList(); - - return true; + userProgressData.Status = (byte)progressStatus; + await UpdateProgressData(userProgressData, m_CommandEventArgs.PlayerData); } - private async Task RevertOrRevertUserData(bool deleteFlag = false) + /// + /// 進行データの削除や戻しを行います。 + /// + /// + /// + public async Task RemoveOrRevertUserData(bool deleteFlag = false) { - var userData = m_UserMessage.MentionedUsers.FirstOrDefault(); + var userData = m_CommandEventArgs.SocketUserMessage.MentionedUsers.FirstOrDefault(); if (userData == null) { return; } - var playerData = DatabasePlayerDataController.LoadPlayerData(m_UserRole, userData.Id); - var playerProgressData = DatabaseProgressController.GetProgressData(playerData, (BossNumberType)m_BossNumber) + var playerData = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.Role, userData.Id); + + var playerProgressData = DatabaseProgressController.GetProgressData(playerData, m_BossNumberType) .OrderByDescending(x => x.UpdateDateTime).FirstOrDefault(); if (playerProgressData == null) @@ -247,229 +225,126 @@ private async Task RevertOrRevertUserData(bool deleteFlag = false) } /// - /// 凸報告の初期化 + /// Initコマンドが入力されたとき /// /// - private void InitializeProgressData() + public async Task InitCommand() { - var deleteData = DatabaseProgressController.GetProgressData(m_UserClanData, (BossNumberType)m_BossNumber); - - if (deleteData == null) - { - return; - } - - if (DatabaseProgressController.DeleteProgressData(deleteData)) - { - return; - } - - return; + InitializeProgressData(); + await SendClanProgressList(); } - private async Task SendClanProgressList(bool removeLastMessage = true) + private async Task UpdateProgressData(ProgressData progressData, PlayerData playerData) { - var clanProgressEmbed = CreateProgressData(); - var sendMessage = await m_UserMessage.Channel.SendMessageAsync(embed: clanProgressEmbed); - var bossNumber = 0; - if (sendMessage == null) + if (progressData.Status == (byte)ProgressStatus.Unknown) { - return; + progressData.Status = (byte)ProgressStatus.AttackReported; } - if (m_AllBattleFlag) + if (progressData.ProgressID == 0) { - bossNumber = m_BossNumber; - } - - var progressChannel = m_Guild.GetChannel(m_UserClanData.ChannelData.GetChannelID(m_UserClanData.ClanID, BossNumberToChannelType(bossNumber))) as SocketTextChannel; - var lastMessageID = m_UserClanData.MessageData.GetMessageID(m_UserClanData.ClanID, BossNumberToMessageType(bossNumber)); - DatabaseMessageDataController.UpdateMessageID(m_UserClanData, sendMessage.Id, BossNumberToMessageType(bossNumber)); - var lastMessage = progressChannel.GetCachedMessage(lastMessageID); + progressData.PlayerID = playerData.PlayerID; - if (lastMessage == null) - { - IMessage lastIMessage; - - try + if (DatabaseProgressController.CreateProgressData(progressData)) { - lastIMessage = await progressChannel.GetMessageAsync(lastMessageID); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - return; + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + await SendClanProgressList(); } - - await lastIMessage.DeleteAsync(); - return; } - - await lastMessage.DeleteAsync(); - return; - } - - /// - /// 編成データをアップデートする。 - /// - /// - /// - private bool UpdateAttackData(string inputCommand) - { - try + else { - var attackType = EnumMapper.Parse(inputCommand); - - if (m_UserProgressData == null) - { - m_UserProgressData = new ProgressData(); - } - - if (attackType == AttackType.CarryOver) - { - m_UserProgressData.CarryOverFlag = true; - } - else + if (DatabaseProgressController.ModifyProgressData(progressData)) { - m_UserProgressData.AttackType = (byte)attackType; + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + await SendClanProgressList(); } - - return true; - } - catch - { - return false; } } - /// - /// ダメージ・残り時間を抽出。 - /// - /// - /// - private bool UpdateDamageData(string[] inputCommand) + private async Task ChangeLap(ushort lap = 0) { - var damageData = inputCommand.Select(x => Regex.Match(x, @"\d+万")).Where(x => x != null).FirstOrDefault().ToString(); + InitializeProgressData(); + var bossLap = m_CommandEventArgs.ClanData.GetBossLap(m_BossNumberType); - if (damageData != "") + if (lap == 0) { - if (!uint.TryParse(Regex.Match(damageData, @"\d+").ToString(), out uint damageNumber) - || damageNumber > CommonDefine.MaxDamageValue) - { - return false; - } - - if (m_UserProgressData == null) - { - m_UserProgressData = new ProgressData(); - } - - if (damageNumber == 0) - { - m_UserProgressData.Status = (byte)ProgressStatus.SOS; - } - - m_UserProgressData.Damage = damageNumber; - var dataIndex = Array.IndexOf(inputCommand, damageData); - m_UserProgressData.CommentData = string.Join(" ", inputCommand.Where((value, index) => index != dataIndex)); - - return true; + m_CommandEventArgs.ClanData.SetBossLap(m_BossNumberType, bossLap + 1); } - - var timeAndDamageData = inputCommand.Select(x => Regex.Match(x, @"\d+@\d+")).Where(x => x != null).FirstOrDefault().ToString(); - - if (timeAndDamageData != "") + else { - var damageText = Regex.Match(timeAndDamageData, @"\d+@").ToString(); - var remainTimeText = Regex.Match(timeAndDamageData, @"@\d+").ToString(); - - if (!uint.TryParse(Regex.Match(damageText, @"\d+").ToString(), out uint damageNumber) - || !byte.TryParse(Regex.Match(remainTimeText, @"\d+").ToString(), out byte remainTimeNumber) - || damageNumber > CommonDefine.MaxDamageValue || remainTimeNumber > CommonDefine.MaxBattleTime) - { - return false; - } - - if (m_UserProgressData == null) - { - m_UserProgressData = new ProgressData(); - } - - if (damageNumber == 0) - { - m_UserProgressData.Status = (byte)ProgressStatus.SOS; - } - - m_UserProgressData.Damage = damageNumber; - m_UserProgressData.RemainTime = remainTimeNumber; - var dataIndex = Array.IndexOf(inputCommand, timeAndDamageData); - m_UserProgressData.CommentData = string.Join(" ", inputCommand.Where((value, index) => index != dataIndex)); - - return true; + m_CommandEventArgs.ClanData.SetBossLap(m_BossNumberType, lap); } - return false; + DatabaseClanDataController.UpdateClanData(m_CommandEventArgs.ClanData); + await SendClanProgressList(); } /// - /// 進行報告のステータス変更。 + /// 凸進行報告状況の初期化 /// - /// /// - private bool UpdateStatusData(string[] inputCommand) + private void InitializeProgressData() { - if (m_UserProgressData == null) + if (DatabaseProgressController.GetProgressData(m_CommandEventArgs.ClanData, m_BossNumberType) is { } deleteData) { - return false; + DatabaseProgressController.DeleteProgressData(deleteData); } + } - int maxSplitNumber = 2; - var SplitData = inputCommand[0].Split("@", maxSplitNumber, StringSplitOptions.RemoveEmptyEntries); - - var dataUpdateFlag = SplitData[0] switch - { - "atk" or "凸確定" => m_UserProgressData.Status = (byte)ProgressStatus.AttackDone, - "kari" or "仮確定" => m_UserProgressData.Status = (byte)ProgressStatus.AttackReady, - "sos" or "ziko" or "jiko" or "事故" => m_UserProgressData.Status = (byte)ProgressStatus.SOS, - "〆確定" or "fin" => m_UserProgressData.Status = (byte)ProgressStatus.CarryOver, - _ => 0, - }; + private async Task SendClanProgressList(bool removeLastMessage = true) + { + var clanProgressEmbed = CreateProgressList(); + var sendMessage = await m_CommandEventArgs.Channel.SendMessageAsync(embed: clanProgressEmbed); - if (dataUpdateFlag == 0) + if (sendMessage == null) { - return false; + return; } - if (SplitData.Length == 1 || !uint.TryParse(SplitData[1], out uint damegeData)) + var channelIDTemp = m_CommandEventArgs.ClanData.GetChannelID(BossNumberToChannelType(m_BossNumberType)); + var progressChannel = m_CommandEventArgs.Role.Guild.GetChannel(channelIDTemp) as SocketTextChannel; + var lastMessageID = m_CommandEventArgs.ClanData.GetMessageID(BossNumberToMessageType(m_BossNumberType)); + DatabaseMessageDataController.UpdateMessageID(m_CommandEventArgs.ClanData, sendMessage.Id, BossNumberToMessageType(m_BossNumberType)); + var cachedMessage = progressChannel.GetCachedMessage(lastMessageID); + + if (cachedMessage == null) { - return true; - } + try + { + var message = await progressChannel.GetMessageAsync(lastMessageID); - if (m_UserProgressData.Status == (byte)ProgressStatus.CarryOver) + if (removeLastMessage) + { + await message.DeleteAsync(); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + else if (removeLastMessage) { - m_UserProgressData.RemainTime = (byte)damegeData; - return true; + await cachedMessage.DeleteAsync(); } - - m_UserProgressData.Damage = damegeData; - - return true; } - private Embed CreateProgressData() + private Embed CreateProgressList() { - var clanProgressData = DatabaseProgressController.GetProgressData(m_UserClanData, (BossNumberType)m_BossNumber) - .OrderBy(x => x.Status).ThenByDescending(x => x.Damage).ThenBy(x => x.CreateDateTime) + var clanPlayerDataList = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.ClanData); + + var progressPlayer = DatabaseProgressController.GetProgressData(m_CommandEventArgs.ClanData, m_BossNumberType) + .OrderBy(x => x.Status) + .ThenByDescending(x => x.Damage) + .ThenBy(x => x.CreateDateTime) + .Select(x => new PlayerInfo(clanPlayerDataList.FirstOrDefault(y => y.PlayerID == x.PlayerID), x)) .ToArray(); - var clanPlayerDataList = DatabasePlayerDataController.LoadPlayerData(m_UserClanData); - var progressPlayer = clanProgressData.Select(x => new PlayerInfo( - clanPlayerDataList.FirstOrDefault(y => y.PlayerID == x.PlayerID), - x - )).ToArray(); - var bossLap = m_UserClanData.GetBossLap(m_BossNumber); + + var bossLap = m_CommandEventArgs.ClanData.GetBossLap(m_BossNumberType); + var bossData = RediveClanBattleData.BossDataList - .FirstOrDefault(x => x.BossNumber == m_BossNumber && x.LapNumberFrom <= bossLap && (x.LapNumberTo == -1 || x.LapNumberTo >= bossLap)); + .FirstOrDefault(x => x.BossNumber == (byte)m_BossNumberType && x.LapNumberFrom <= bossLap && (x.LapNumberTo == -1 || x.LapNumberTo >= bossLap)); var summaryStringBuilder = new StringBuilder(); // 持ち越しデータ出力 @@ -478,19 +353,19 @@ private Embed CreateProgressData() var remainAttackString = new StringBuilder(); remainAttackString.Append("残凸 "); - var reportCount = DatabaseReportDataController.GetRemainPlayerCount(m_UserClanData); + var reportCount = DatabaseReportDataController.GetRemainPlayerCount(m_CommandEventArgs.ClanData); for (int i = CommonDefine.MaxReportNumber; i >= 0; i--) { remainAttackString.Append((i == 0 ? "完凸:" : i + "凸:") + reportCount[i] + "人 "); } + summaryStringBuilder.AppendLine(remainAttackString.ToString()); // ボスのHPをここに入力(万表示) var bossHP = bossData?.HP / CommonDefine.DisplayDamageUnit ?? 0; var sumAttackDoneHP = progressPlayer.Where(x => x.ProgressData.Status == (byte)ProgressStatus.AttackDone).Select(x => (int)x.ProgressData.Damage).Sum(); var sumAttackReadyHP = progressPlayer.Where(x => x.ProgressData.Status == (byte)ProgressStatus.AttackReady).Select(x => (int)x.ProgressData.Damage).Sum(); - summaryStringBuilder.AppendLine("現在HP " + (bossHP - sumAttackDoneHP) + "万 / " + bossHP + "万"); summaryStringBuilder.AppendLine("仮確HP " + (bossHP - sumAttackReadyHP - sumAttackDoneHP) + "万 / " + bossHP + "万"); @@ -501,6 +376,7 @@ private Embed CreateProgressData() }; var reportMessage = string.Join("\n", progressPlayer.Select(x => x.GetNameWithData()).ToArray()); + var embedFieldBuilder = new EmbedFieldBuilder() { Name = "参加者", @@ -510,7 +386,7 @@ private Embed CreateProgressData() var embedBuilder = new EmbedBuilder(); embedBuilder.AddField(headerFieldBuilder); embedBuilder.AddField(embedFieldBuilder); - embedBuilder.Title = $"{m_UserClanData.GetBossLap(m_BossNumber)}周目 {m_BossNumber}ボス {bossData?.Name}"; + embedBuilder.Title = $"{m_CommandEventArgs.ClanData.GetBossLap(m_BossNumberType)}周目 {(uint)m_BossNumberType}ボス {bossData?.Name}"; embedBuilder.Footer = new EmbedFooterBuilder() { @@ -520,30 +396,58 @@ private Embed CreateProgressData() return embedBuilder.Build(); } - private MessageFeatureType BossNumberToMessageType(int bossNumber) + private ProgressData GetProgressData(PlayerData playerData = null) { - return bossNumber switch + if (playerData == null) { - (int)BossNumberType.Boss1Number => MessageFeatureType.ProgressBoss1ID, - (int)BossNumberType.Boss2Number => MessageFeatureType.ProgressBoss2ID, - (int)BossNumberType.Boss3Number => MessageFeatureType.ProgressBoss3ID, - (int)BossNumberType.Boss4Number => MessageFeatureType.ProgressBoss4ID, - (int)BossNumberType.Boss5Number => MessageFeatureType.ProgressBoss5ID, + playerData = m_CommandEventArgs.PlayerData; + } + + return DatabaseProgressController.GetProgressData(playerData, m_BossNumberType) + .Where(x => x.Status != (byte)ProgressStatus.AttackDone || x.Status != (byte)ProgressStatus.SubdueBoss) + .FirstOrDefault(); + } + + private bool TryGetProgressData(out ProgressData progressData) + => (progressData = GetProgressData()) != null; + + private MessageFeatureType BossNumberToMessageType(BossNumberType bossNumberType) + { + return bossNumberType switch + { + BossNumberType.Boss1Number => MessageFeatureType.ProgressBoss1ID, + BossNumberType.Boss2Number => MessageFeatureType.ProgressBoss2ID, + BossNumberType.Boss3Number => MessageFeatureType.ProgressBoss3ID, + BossNumberType.Boss4Number => MessageFeatureType.ProgressBoss4ID, + BossNumberType.Boss5Number => MessageFeatureType.ProgressBoss5ID, _ => MessageFeatureType.ProgressID, }; } - private ChannelFeatureType BossNumberToChannelType(int bossNumber) + private ChannelFeatureType BossNumberToChannelType(BossNumberType bossNumberType) { - return bossNumber switch + return bossNumberType switch { - (int)BossNumberType.Boss1Number => ChannelFeatureType.ProgressBoss1ID, - (int)BossNumberType.Boss2Number => ChannelFeatureType.ProgressBoss2ID, - (int)BossNumberType.Boss3Number => ChannelFeatureType.ProgressBoss3ID, - (int)BossNumberType.Boss4Number => ChannelFeatureType.ProgressBoss4ID, - (int)BossNumberType.Boss5Number => ChannelFeatureType.ProgressBoss5ID, + BossNumberType.Boss1Number => ChannelFeatureType.ProgressBoss1ID, + BossNumberType.Boss2Number => ChannelFeatureType.ProgressBoss2ID, + BossNumberType.Boss3Number => ChannelFeatureType.ProgressBoss3ID, + BossNumberType.Boss4Number => ChannelFeatureType.ProgressBoss4ID, + BossNumberType.Boss5Number => ChannelFeatureType.ProgressBoss5ID, _ => ChannelFeatureType.ProgressID, }; } + + private BossNumberType ChannelTypeToBossNumber(ChannelFeatureType channelFeatureType) + { + return channelFeatureType switch + { + ChannelFeatureType.ProgressBoss1ID => BossNumberType.Boss1Number, + ChannelFeatureType.ProgressBoss2ID => BossNumberType.Boss2Number, + ChannelFeatureType.ProgressBoss3ID => BossNumberType.Boss3Number, + ChannelFeatureType.ProgressBoss4ID => BossNumberType.Boss4Number, + ChannelFeatureType.ProgressBoss5ID => BossNumberType.Boss5Number, + _ => BossNumberType.Unknown, + }; + } } } diff --git a/Script/ClanBattle/BattleReport.cs b/Script/ClanBattle/BattleReport.cs index 9f48755f..082d6862 100644 --- a/Script/ClanBattle/BattleReport.cs +++ b/Script/ClanBattle/BattleReport.cs @@ -4,7 +4,6 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Discord; -using Discord.WebSocket; using PriconneBotConsoleApp.Database; using PriconneBotConsoleApp.DataModel; using PriconneBotConsoleApp.DataType; @@ -15,10 +14,7 @@ namespace PriconneBotConsoleApp.Script { public class BattleReport { - private readonly ClanData m_ClanData; - private readonly SocketUserMessage m_UserMessage; - private readonly SocketRole m_ClanRole; - private readonly SocketGuild m_Guild; + private readonly CommandEventArgs m_CommandEventArgs; private class PlayerInfo { @@ -48,71 +44,55 @@ public string GetNameWithReport() } } - public BattleReport(ClanData clanData, SocketUserMessage userMessage) - { - m_ClanData = clanData; - m_UserMessage = userMessage; - m_Guild = (userMessage.Channel as SocketTextChannel)?.Guild; - m_ClanRole = m_Guild?.GetRole(clanData.ClanRoleID); - } + public BattleReport(CommandEventArgs commandEventArgs) + => m_CommandEventArgs = commandEventArgs; - public async Task RunByMessage() + /// + /// 個人の凸報告 + /// + public void RegisterReportData() { - if (m_UserMessage.Content.StartsWith("!")) + var reportData = new ReportData(); + + // TODO : 冗長なRegexの高速化 #131 + if (Regex.IsMatch(m_CommandEventArgs.Name, @"\d\D{1,3}")) { - if (m_UserMessage.Content.StartsWith("!add")) - { - RegisterOtherUserReportData(); - } - else if (m_UserMessage.Content.StartsWith("!list")) - { - await SendClanAttackList(); - } - else if (m_UserMessage.Content.StartsWith("!rm")) - { - DeleteReportData(); - } - else if (m_UserMessage.Content.StartsWith("!init")) + var bossNumber = int.Parse(Regex.Match(m_CommandEventArgs.Name, @"\d").Value); + + if (!EnumMapper.TryParse(Regex.Match(m_CommandEventArgs.Name, @"\D{1,3}").Value, out var attackType) + || attackType == AttackType.Unknown + || attackType == AttackType.CarryOver) { - DeleteAllClanReport(); + return; } - } - else - { - RegisterReportData(); - } - } - /// - /// 個人の凸報告 - /// - private void RegisterReportData() - { - var playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, m_UserMessage.Author.Id); + reportData.AttackType = (byte)attackType; + reportData.BossNumber = (byte)bossNumber; + } - if (playerData == null) + if (!CommonDefine.IsValidBossNumber(reportData.BossNumber)) { return; } - var reportData = StringToReportData(m_UserMessage.Content.ZenToHan(), playerData.PlayerID); + reportData.PlayerID = m_CommandEventArgs.PlayerData.PlayerID; if (reportData == null) { return; } - var userReportedData = DatabaseReportDataController.GetReportData(playerData); + var userReportedData = DatabaseReportDataController.GetReportData(m_CommandEventArgs.PlayerData); if (userReportedData.Count() >= CommonDefine.MaxReportNumber) { - _ = m_UserMessage.Channel.SendTimedMessageAsync(TimeDefine.ErrorMessageDisplayTime, ErrorType.UpperLimitReport.ToLabel()); + _ = m_CommandEventArgs.SocketUserMessage.Channel.SendTimedMessageAsync(TimeDefine.ErrorMessageDisplayTime, ErrorType.UpperLimitReport.ToLabel()); return; } if (DatabaseReportDataController.CreateReportData(reportData)) { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); } return; @@ -121,18 +101,18 @@ private void RegisterReportData() /// /// 個人の最新の凸報告を削除する。 /// - private void DeleteReportData() + public void DeleteReportData() { - var splitContent = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); var playerData = new PlayerData(); - if (splitContent.Length == 2 - && ulong.TryParse(Regex.Match(splitContent[1], @"\d+").Value, out ulong registerUserID)) + + if (m_CommandEventArgs.Arguments.Count == 1 + && ulong.TryParse(Regex.Match(m_CommandEventArgs.Arguments[0], @"\d+").Value, out ulong registerUserID)) { - playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, registerUserID); + playerData = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.Role, registerUserID); } else { - playerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, m_UserMessage.Author.Id); + playerData = m_CommandEventArgs.PlayerData; } if (playerData == null) @@ -140,18 +120,17 @@ private void DeleteReportData() return; } - var recentReportData = DatabaseReportDataController.GetReportData(playerData) - .OrderBy(x => x.DateTime).ToList(); - var removeData = recentReportData.Last(); + var removeData = DatabaseReportDataController.GetReportData(playerData) + .MaxBy(x => x.DateTime); if (DatabaseReportDataController.DeleteReportData(removeData)) { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); - if (playerData.UserID != m_UserMessage.Author.Id) + if (playerData.UserID != m_CommandEventArgs.User.Id) { - _ = m_UserMessage.Channel.SendTimedMessageAsync(TimeDefine.SuccessMessageDisplayTime, - string.Format(InfomationType.DeleteInsted.ToLabel(), playerData.UserID, TimeDefine.SuccessMessageDisplayTime)); + _ = m_CommandEventArgs.SocketUserMessage.Channel.SendTimedMessageAsync(TimeDefine.SuccessMessageDisplayTime, + string.Format(InformationType.DeleteInstead.ToLabel(), playerData.UserID, TimeDefine.SuccessMessageDisplayTime)); } } } @@ -159,60 +138,57 @@ private void DeleteReportData() /// /// 代理報告用 /// - private void RegisterOtherUserReportData() + public void RegisterOtherUserReportData() { - var splitContent = m_UserMessage.Content.ZenToHan().Split(" ", StringSplitOptions.RemoveEmptyEntries); - - if (splitContent.Length != CommonDefine.MaxReportNumber) + if ((!ulong.TryParse(m_CommandEventArgs.Arguments[0], out var registerUserID) + && !MentionUtils.TryParseUser(m_CommandEventArgs.Arguments[0], out registerUserID)) + || !byte.TryParse(m_CommandEventArgs.Arguments[1], out var bossNumber) + || !CommonDefine.IsValidBossNumber(bossNumber) + || !EnumMapper.TryParse(m_CommandEventArgs.Arguments[2], out var attackType)) { return; } - if (!ulong.TryParse(Regex.Match(splitContent[1], @"\d+").Value, out ulong registerUserID)) - { - return; - } - - var registerPlayerData = DatabasePlayerDataController.LoadPlayerData(m_ClanRole, registerUserID); + var registerPlayerData = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.Role, registerUserID); if (registerPlayerData == null) { return; } - var reportData = StringToReportData(splitContent[2], registerPlayerData.PlayerID); - - if (reportData == null) + var reportData = new ReportData { - return; - } + AttackType = (byte)attackType, + BossNumber = bossNumber, + PlayerID = registerUserID, + }; var userReportedData = DatabaseReportDataController.GetReportData(registerPlayerData); if (userReportedData.Count() >= CommonDefine.MaxReportNumber) { - _ = m_UserMessage.Channel.SendTimedMessageAsync(TimeDefine.ErrorMessageDisplayTime, ErrorType.UpperLimitReport.ToLabel()); + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync(TimeDefine.ErrorMessageDisplayTime, ErrorType.UpperLimitReport.ToLabel()); return; } if (DatabaseReportDataController.CreateReportData(reportData)) { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(EnumMapper.ToEmoji(ReactionType.Success)); } return; - } /// /// クランの凸報告を削除 /// - private void DeleteAllClanReport() + public void DeleteAllClanReport() { - var clanReportData = DatabaseReportDataController.GetReportData(m_ClanData); + var clanReportData = DatabaseReportDataController.GetReportData(m_CommandEventArgs.ClanData); + if (DatabaseReportDataController.DeleteReportData(clanReportData)) { - _ = m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(EnumMapper.ToEmoji(ReactionType.Success)); } } @@ -220,43 +196,10 @@ private void DeleteAllClanReport() /// 凸報告データ送信用 /// /// - private async Task SendClanAttackList() + public async Task SendClanAttackList() { var clanAttackEmbed = CreateClanReportData(); - await m_UserMessage.Channel.SendMessageAsync(embed: clanAttackEmbed); - } - - /// - /// メッセージ情報から報告データに変換する。 - /// - /// メッセージ内容 - /// - private ReportData StringToReportData(string messageContent, ulong playerID) - { - var userReportData = new ReportData(); - - if (Regex.IsMatch(messageContent, @"\d\D{1,3}")) - { - var bossNumber = int.Parse(Regex.Match(messageContent, @"\d").Value); - - if (!EnumMapper.TryParse(Regex.Match(messageContent, @"\D{1,3}").Value, out var attackType) - || attackType == AttackType.Unknown || attackType == AttackType.CarryOver) - { - return null; - } - - userReportData.AttackType = (byte)attackType; - userReportData.BossNumber = (byte)bossNumber; - } - - if (userReportData.BossNumber < CommonDefine.MinBossNumber || userReportData.BossNumber > CommonDefine.MaxBossNumber) - { - return null; - } - - userReportData.PlayerID = playerID; - - return userReportData; + await m_CommandEventArgs.SocketUserMessage.Channel.SendMessageAsync(embed: clanAttackEmbed); } /// @@ -267,8 +210,8 @@ private Embed CreateClanReportData() { var embedBuilder = new EmbedBuilder(); - var clanPlayerDataList = DatabasePlayerDataController.LoadPlayerData(m_ClanData); - var reportDataList = DatabaseReportDataController.GetReportData(m_ClanData); + var clanPlayerDataList = DatabasePlayerDataController.LoadPlayerData(m_CommandEventArgs.ClanData); + var reportDataList = DatabaseReportDataController.GetReportData(m_CommandEventArgs.ClanData); var playerInfoList = clanPlayerDataList.Select(x => new PlayerInfo( x.PlayerID, diff --git a/Script/ClanBattle/BattleReservation.cs b/Script/ClanBattle/BattleReservation.cs index 32bbaf87..38fc4a27 100644 --- a/Script/ClanBattle/BattleReservation.cs +++ b/Script/ClanBattle/BattleReservation.cs @@ -1,17 +1,11 @@ using System; using System.Linq; using System.Text; -using System.Threading.Tasks; -using System.Reflection; -using System.ComponentModel; -using Discord; -using Discord.WebSocket; +using PriconneBotConsoleApp.Database; using PriconneBotConsoleApp.DataModel; using PriconneBotConsoleApp.DataType; -using PriconneBotConsoleApp.Database; -using PriconneBotConsoleApp.Extension; -using System.Collections.Generic; using PriconneBotConsoleApp.Define; +using PriconneBotConsoleApp.Extension; namespace PriconneBotConsoleApp.Script { @@ -19,232 +13,87 @@ public class BattleReservation { private const int MaxCommentLength = 30; - private readonly ClanData m_UserClanData; - private readonly SocketRole m_UserRole; - private readonly SocketUserMessage m_UserMessage; - private readonly SocketInteraction m_UserInteraction; + private readonly CommandEventArgs m_CommandEventArgs; - private BattleReservation( - ClanData userClanData, - ISocketMessageChannel channel, - SocketUserMessage userMessage = null, - SocketInteraction userInterction = null) - { - m_UserClanData = userClanData; - m_UserRole = (channel as SocketGuildChannel)?.Guild.GetRole(m_UserClanData.ClanRoleID); - m_UserMessage = userMessage; - m_UserInteraction = userInterction; - } - - public BattleReservation(ClanData userClanData, SocketUserMessage message) - : this(userClanData, message.Channel, userMessage: message) - { - } + public BattleReservation(CommandEventArgs commandEventArgs) + => m_CommandEventArgs = commandEventArgs; - public BattleReservation(ClanData userClanData, SocketInteraction interaction) - : this(userClanData, interaction.Channel, userInterction: interaction) - { - } - - public BattleReservation(SocketRole userRole) - { - m_UserRole = userRole; - m_UserClanData = DatabaseClanDataController.LoadClanData(userRole); - } + /// + /// 個人の予約一覧を表示する。引数は無し。 + /// + public void PlayerReserveList() + => m_CommandEventArgs.Channel.SendMessageAsync(CreateUserReservationDataMessage(m_CommandEventArgs.PlayerData)); - public async Task RunReservationCommand() + public void RegisterReserveData() { - var userMessage = m_UserMessage; - - if (userMessage == null) + if (!IsReservationAllowTime()) { - return; - } - - var messageContents = userMessage.Content; - - if (messageContents.StartsWith("予約")) - { - switch (messageContents) - { - case "予約": - case "予約確認": - case "予約状況": - Console.WriteLine("予約確認"); - await userMessage.Channel.SendMessageAsync(CreateUserReservationDataMessage()); - return; - } - - if (!IsReservationAllowTime()) - { - await SendErrorMessage(ErrorType.OutOfReservationTime, - $"{m_UserClanData.ReservationStartTime.Hours}:00", $"{m_UserClanData.ReservationEndTime.Hours}:00"); - return; - } - - var reservationData = MessageToReservationData(); - - if (reservationData is null) - { - await SendErrorMessage(ErrorType.FailedReservation); - return; - } - - var allowReservationLap = m_UserClanData.ReservationLap == 0 - ? ClanBattleDefine.MaxLapNumber : (m_UserClanData.ReservationLap + m_UserClanData.GetMinBossLap()); - - if (reservationData.BattleLap > allowReservationLap) - { - await SendErrorMessage(ErrorType.OutOfReservationBossLaps, allowReservationLap.ToString()); - return; - } - - RegisterReservationData(reservationData); - await SuccessAddEmoji(); - await UpdateSystemMessage(); + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync( + TimeDefine.ErrorMessageDisplayTime, + string.Format(ErrorType.OutOfReservationTime.ToLabel(), + $"{m_CommandEventArgs.ClanData.ReservationStartTime.Hours}", + $"{m_CommandEventArgs.ClanData.ReservationEndTime.Hours}" + ) + ); - if (m_UserClanData.GetBossLap(reservationData.BossNumber) == reservationData.BattleLap) - { - await new BattleDeclaration(m_UserRole, (BossNumberType)reservationData.BossNumber).UpdateDeclarationBotMessage(); - } + return; } - else if (messageContents.StartsWith("削除")) - { - var deleteReservationData = MessageToReservationData(); - if (deleteReservationData is null) - { - // await FailedToRegisterMessage(); - return; - } + var reservationData = MessageToReservationData(); - if (DeleteUserReservationData(deleteReservationData)) - { - await SuccessAddEmoji(); - await UpdateSystemMessage(); - } - } - else if (messageContents.StartsWith("!rm")) + if (reservationData is null) { - var userReservationData = MessageToUserReservationData(); - - if (userReservationData == null || !DeleteUserReservationData(userReservationData)) - { - return; - } + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync( + TimeDefine.ErrorMessageDisplayTime, + ErrorType.FailedReservation.ToLabel() + ); - await SuccessAddEmoji(); - await UpdateSystemMessage(); - - if (m_UserClanData.GetBossLap(userReservationData.BossNumber) == userReservationData.BattleLap) - { - await new BattleDeclaration(m_UserRole, (BossNumberType)userReservationData.BossNumber).UpdateDeclarationBotMessage(); - } + return; } - } + var allowReservationLap = m_CommandEventArgs.ClanData.ReservationLap == 0 + ? ClanBattleDefine.MaxLapNumber + : (m_CommandEventArgs.ClanData.ReservationLap + m_CommandEventArgs.ClanData.GetMinBossLap()); - public async Task RunReservationResultCommand() - { - if (m_UserMessage.Content.StartsWith("!start")) + if (reservationData.BattleLap > allowReservationLap) { - await SendSystemMessage(); - } - } + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync( + TimeDefine.ErrorMessageDisplayTime, + string.Format(ErrorType.OutOfReservationBossLaps.ToLabel(), allowReservationLap.ToString()) + ); - public async Task RunResultInteraction() - { - if (m_UserInteraction == null) - { return; } - var messageComponent = (SocketMessageComponent)m_UserInteraction; + RegisterReservationData(reservationData); + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); + _ = new BattleReservationSummary(m_CommandEventArgs.Role, m_CommandEventArgs.ClanData).UpdateMessage(); - if (!Enum.TryParse(messageComponent.Data.CustomId, out var buttonType)) + if (m_CommandEventArgs.ClanData.GetBossLap(reservationData.BossNumber) == reservationData.BattleLap) { - return; - } - - switch (buttonType) - { - case ButtonType.Reload: - await UpdateSystemMessage(); - break; + _ = new BattleDeclaration(m_CommandEventArgs.Role, (BossNumberType)reservationData.BossNumber).UpdateDeclarationBotMessage(); } } - /// - /// 凸予約一覧チャンネルにメッセージを送信する。 - /// - /// - public async Task SendSystemMessage() + public void DeleteReserveData() { - var embedData = CreateAllReservationDataMessage(); - var componentData = CreateSystemMessageComponent(); - var reservationResultChannelID = m_UserClanData.ChannelData - .GetChannelID(m_UserClanData.ClanID, ChannelFeatureType.ReserveResultID); + var deleteReservationData = MessageToReservationData(); - if (reservationResultChannelID == 0) + if (deleteReservationData == null) { return; } - var resultChannel = m_UserRole.Guild - .GetTextChannel(reservationResultChannelID); - - var sendedMessageData = await resultChannel.SendMessageAsync(embed: embedData, component: componentData); - DatabaseMessageDataController.UpdateMessageID(m_UserClanData, sendedMessageData.Id, MessageFeatureType.ReserveResultID); - } - - public async Task UpdateSystemMessage() - { - var reservationMessageID = m_UserClanData.MessageData - .GetMessageID(m_UserClanData.ClanID, MessageFeatureType.ReserveResultID); - var reservationResultChannelID = m_UserClanData.ChannelData - .GetChannelID(m_UserClanData.ClanID, ChannelFeatureType.ReserveResultID); - - if (reservationResultChannelID == 0 || reservationMessageID == 0) + if (DeleteUserReservationData(deleteReservationData)) { - return; - } + _ = m_CommandEventArgs.SocketUserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); + _ = new BattleReservationSummary(m_CommandEventArgs.Role, m_CommandEventArgs.ClanData).UpdateMessage(); - var guildChannel = m_UserRole.Guild - .GetChannel(reservationResultChannelID) as SocketTextChannel; - var socketMessage = guildChannel.GetCachedMessage(reservationMessageID); - - if (socketMessage == null || !(socketMessage is SocketUserMessage)) - { - var message = await guildChannel.GetMessageAsync(reservationMessageID); - - if (message != null) + if (m_CommandEventArgs.ClanData.GetBossLap(deleteReservationData.BossNumber) == deleteReservationData.BattleLap) { - await guildChannel.DeleteMessageAsync(message); - await SendSystemMessage(); + _ = new BattleDeclaration(m_CommandEventArgs.Role, (BossNumberType)deleteReservationData.BossNumber).UpdateDeclarationBotMessage(); } - - return; } - - var serverMessage = socketMessage as SocketUserMessage; - var embedData = CreateAllReservationDataMessage(); - await serverMessage.ModifyAsync(x => x.Embed = embedData); - } - - public void DeleteUnusedData(byte bossNumber) - { - var clanReservationData = DatabaseReservationController.LoadReservationData(m_UserClanData, bossNumber); - var bossLap = m_UserClanData.GetBossLap(bossNumber); - var deleteData = clanReservationData.Where(x => x.BattleLap < bossLap); - DatabaseReservationController.DeleteReservationData(deleteData); - } - - private MessageComponent CreateSystemMessageComponent() - { - ComponentBuilder componentBuilder = new(); - componentBuilder.WithButton( - ButtonType.Reload.ToLongLabel(), ButtonType.Reload.ToString(), ButtonStyle.Secondary, ButtonType.Reload.ToEmoji()); - return componentBuilder.Build(); } /// @@ -255,30 +104,25 @@ private MessageComponent CreateSystemMessageComponent() /// private ReservationData MessageToReservationData() { - var splitMessageContent = m_UserMessage.Content.ZenToHan().Split(' ', StringSplitOptions.RemoveEmptyEntries); - - if (splitMessageContent.Length < 3 - || !(byte.TryParse(splitMessageContent[1], out byte battleLap) && battleLap > 0) - || !(byte.TryParse(splitMessageContent[2], out byte bossNumber) && bossNumber <= CommonDefine.MaxBossNumber && bossNumber >= CommonDefine.MinBossNumber) - || battleLap < m_UserClanData.GetBossLap(bossNumber)) + if (!byte.TryParse(m_CommandEventArgs.Arguments[1], out var bossNumber) + || !byte.TryParse(m_CommandEventArgs.Arguments[0], out var battleLap) + || !CommonDefine.IsValidBossNumber(bossNumber) + || !CommonDefine.IsValidBattleTime(battleLap)) { return null; } - var commentData = string.Join(' ', splitMessageContent.Skip(3)); + + var commentData = string.Join(' ', m_CommandEventArgs.Arguments.Skip(2)); if (commentData.Length > MaxCommentLength) { commentData = commentData.Substring(0, MaxCommentLength); - m_UserMessage.Channel.SendMessageAsync($"コメントが長いので切り取られました。\n 問題がある場合は予約削除をして再度予約してください。"); + _ = m_CommandEventArgs.Channel.SendTimedMessageAsync(TimeDefine.WarningMessageDisplayTime, WarningType.TooLongComment.ToLabel()); } return new ReservationData() { - PlayerData = new PlayerData - { - ClanData = m_UserClanData, - UserID = m_UserMessage.Author.Id, - }, + PlayerID = m_CommandEventArgs.PlayerData.PlayerID, BattleLap = battleLap, BossNumber = bossNumber, CommentData = commentData, @@ -287,51 +131,25 @@ private ReservationData MessageToReservationData() private void RegisterReservationData(ReservationData reservationData) { - var allSqlReservationData = DatabaseReservationController.LoadReservationData(reservationData.PlayerData); + var allSqlReservationData = DatabaseReservationController.LoadReservationData(m_CommandEventArgs.PlayerData); - var doesExistReservationData = allSqlReservationData - .Any(x => x.BossNumber == reservationData.BossNumber && x.BattleLap == reservationData.BattleLap); + var DatabaseReservationData = allSqlReservationData + .FirstOrDefault(x => x.BossNumber == reservationData.BossNumber && x.BattleLap == reservationData.BattleLap); - if (!doesExistReservationData) + if (DatabaseReservationData == null) { DatabaseReservationController.CreateReservationData(reservationData); } else { + reservationData.ReserveID = DatabaseReservationData.ReserveID; DatabaseReservationController.UpdateReservationData(reservationData); } } - /// - /// 個人が予約しているデータの取得 - /// - /// - private ReservationData MessageToUserReservationData() - { - var splitMessageContent = m_UserMessage.Content.ZenToHan().Split(' ', StringSplitOptions.RemoveEmptyEntries); - - if (splitMessageContent.Length != 4 - || !ulong.TryParse(splitMessageContent[1], out ulong userID) - || !(byte.TryParse(splitMessageContent[2], out byte battleLap) && battleLap > 0) - || !(byte.TryParse(splitMessageContent[3], out byte bossNumber) && bossNumber <= CommonDefine.MaxBossNumber && bossNumber >= CommonDefine.MinBossNumber)) - { - return null; - } - - var playerData = DatabasePlayerDataController.LoadPlayerData(m_UserRole, userID); - - if (playerData == null) - { - return null; - } - - return DatabaseReservationController.LoadReservationData(playerData) - .FirstOrDefault(d => d.BattleLap == battleLap && d.BossNumber == bossNumber); - } - private bool DeleteUserReservationData(ReservationData reservationData) { - var userReservationDataList = DatabaseReservationController.LoadReservationData(reservationData.PlayerData); + var userReservationDataList = DatabaseReservationController.LoadReservationData(m_CommandEventArgs.PlayerData); var sqlReservationData = userReservationDataList .Where(x => x.BossNumber == reservationData.BossNumber && x.BattleLap == reservationData.BattleLap) @@ -347,11 +165,6 @@ private bool DeleteUserReservationData(ReservationData reservationData) return true; } - private string CreateUserReservationDataMessage() - => CreateUserReservationDataMessage( - DatabasePlayerDataController.LoadPlayerData(m_UserRole, m_UserMessage.Author.Id) - ); - private string CreateUserReservationDataMessage(PlayerData playerData) { var reservationDataSet = DatabaseReservationController.LoadReservationData(playerData); @@ -377,82 +190,19 @@ private string CreateUserReservationDataMessage(PlayerData playerData) return messageData.ToString(); } - /// - /// 予約メッセージを作成する - /// - /// - /// - private Embed CreateAllReservationDataMessage() - { - var reservationDataSet = DatabaseReservationController.LoadReservationData(m_UserClanData); - List> reservationDataList = new(); - - for (var i = 0; i < CommonDefine.MaxBossNumber; i++) - { - reservationDataList.Add(new List()); - } - - reservationDataSet.ForEach(x => reservationDataList[x.BossNumber - 1].Add(x)); - EmbedBuilder embedBuilder = new(); - - for (var i = 0; i < CommonDefine.MaxBossNumber; i++) - { - EmbedFieldBuilder fieldBuilder = new(); - - if (!reservationDataList[i].Any()) - { - // 何かの空白代入して空行を生成している。 - fieldBuilder.Value = "\n\u200b"; - } - else - { - StringBuilder messageData = new(); - messageData.AppendLine("```python"); - reservationDataList[i].ForEach(x => messageData.AppendLine($"{x.BattleLap,2}周目 {x.PlayerData.GuildUserName} {x.CommentData}")); - messageData.AppendLine("```"); - fieldBuilder.Value = messageData.ToString(); - } - - fieldBuilder.Name = $"{i + 1}ボス({reservationDataList[i].Count}件)"; - embedBuilder.AddField(fieldBuilder); - } - - embedBuilder.Title = $"現在の予約状況:計{reservationDataSet.Count}件"; - - return embedBuilder.Build(); - } - - private async Task SendErrorMessage(ErrorType type, params string[] parameters) - { - var descriptionString = type.GetDescription(); - var sendMessage = string.Empty; - if (descriptionString == null) - { - sendMessage = type.ToString(); - } - else - { - sendMessage = string.Format(descriptionString, parameters); - } - await m_UserMessage.Channel.SendMessageAsync(sendMessage); - } - - private async Task SuccessAddEmoji() - => await m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.GetDescription())); - /// /// 予約できる時間かどうか判断する。 /// /// private bool IsReservationAllowTime() { - if (m_UserClanData == null) + if (m_CommandEventArgs.ClanData == null) { return false; } - var startTime = m_UserClanData.ReservationStartTime; - var endTime = m_UserClanData.ReservationEndTime; + var startTime = m_CommandEventArgs.ClanData.ReservationStartTime; + var endTime = m_CommandEventArgs.ClanData.ReservationEndTime; var nowTime = DateTime.Now.TimeOfDay; if (startTime.Hours == 0 && endTime.Hours == 0) diff --git a/Script/ClanBattle/BattleReservationSummary.cs b/Script/ClanBattle/BattleReservationSummary.cs new file mode 100644 index 00000000..bba2321d --- /dev/null +++ b/Script/ClanBattle/BattleReservationSummary.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; +using PriconneBotConsoleApp.Database; +using PriconneBotConsoleApp.DataModel; +using PriconneBotConsoleApp.DataType; +using PriconneBotConsoleApp.Define; + +namespace PriconneBotConsoleApp.Script +{ + public class BattleReservationSummary + { + private readonly ClanData m_ClanData; + private readonly SocketRole m_Role; + private readonly SocketTextChannel m_SocketTextChannel; + + public BattleReservationSummary(SocketRole role, ClanData clanData = null) + { + m_Role = role; + + if (clanData == null) + { + m_ClanData = DatabaseClanDataController.LoadClanData(m_Role); + } + else + { + m_ClanData = clanData; + } + + m_SocketTextChannel = m_Role.Guild.GetChannel(m_ClanData.GetChannelID(ChannelFeatureType.ReserveResultID)) as SocketTextChannel; + } + + public async Task RunInteraction(SocketMessageComponent messageComponent) + { + if (!Enum.TryParse(messageComponent.Data.CustomId, out var buttonType)) + { + return; + } + + if (buttonType == ButtonType.Reload) + { + await UpdateMessage(); + } + } + + /// + /// 凸予約一覧チャンネルにメッセージを送信する。 + /// + /// + public async Task SendMessage() + { + var embedData = CreateEmbed(); + var componentData = CreateComponent(); + var sendedMessageData = await m_SocketTextChannel.SendMessageAsync(embed: embedData, component: componentData); + DatabaseMessageDataController.UpdateMessageID(m_ClanData, sendedMessageData.Id, MessageFeatureType.ReserveResultID); + } + + public async Task UpdateMessage() + { + if (m_SocketTextChannel == null) + { + return; + } + + var reservationMessageID = m_ClanData.GetMessageID(MessageFeatureType.ReserveResultID); + var cachedMessage = m_SocketTextChannel.GetCachedMessage(reservationMessageID); + var embedData = CreateEmbed(); + var componentData = CreateComponent(); + + if (cachedMessage is SocketUserMessage serverMessage) + { + await serverMessage.ModifyAsync(x => x.Embed = embedData); + } + else + { + var message = await m_SocketTextChannel.GetMessageAsync(reservationMessageID); + await SendMessage(); + + if (message != null) + { + await m_SocketTextChannel.DeleteMessageAsync(message); + } + } + } + + public void DeleteUnusedData(byte bossNumber) + { + var clanReservationData = DatabaseReservationController.LoadReservationData(m_ClanData, bossNumber); + var bossLap = m_ClanData.GetBossLap(bossNumber); + var deleteData = clanReservationData.Where(x => x.BattleLap < bossLap); + DatabaseReservationController.DeleteReservationData(deleteData); + } + + /// + /// 予約メッセージを作成する + /// + /// + /// + private Embed CreateEmbed() + { + var reservationDataSet = DatabaseReservationController.LoadReservationData(m_ClanData); + List> reservationDataList = new(); + + // TODO : Linqで行けそうな気がする + for (var i = 0; i < CommonDefine.MaxBossNumber; i++) + { + reservationDataList.Add(new List()); + } + + reservationDataSet.ForEach(x => reservationDataList[x.BossNumber - 1].Add(x)); + EmbedBuilder embedBuilder = new(); + + for (var i = 0; i < CommonDefine.MaxBossNumber; i++) + { + EmbedFieldBuilder fieldBuilder = new(); + + if (!reservationDataList[i].Any()) + { + // 空行を保つためのゼロ幅空白(\u200b)を挿入している。 + fieldBuilder.Value = "\n\u200b"; + } + else + { + StringBuilder messageData = new(); + messageData.AppendLine("```python"); + reservationDataList[i].ForEach(x => messageData.AppendLine($"{x.BattleLap,2}周目 {x.PlayerData.GuildUserName} {x.CommentData}")); + messageData.AppendLine("```"); + fieldBuilder.Value = messageData.ToString(); + } + + fieldBuilder.Name = $"{i + 1}ボス({reservationDataList[i].Count}件)"; + embedBuilder.AddField(fieldBuilder); + } + + embedBuilder.Title = $"現在の予約状況:計{reservationDataSet.Count}件"; + + return embedBuilder.Build(); + } + + private MessageComponent CreateComponent() + { + ComponentBuilder componentBuilder = new(); + componentBuilder.WithButton( + ButtonType.Reload.ToLongLabel(), + ButtonType.Reload.ToString(), + ButtonStyle.Secondary, + ButtonType.Reload.ToEmoji() + ); + + return componentBuilder.Build(); + } + } +} diff --git a/Script/ClanBattle/BattleTaskKill.cs b/Script/ClanBattle/BattleTaskKill.cs index 1eba12a9..361a6144 100644 --- a/Script/ClanBattle/BattleTaskKill.cs +++ b/Script/ClanBattle/BattleTaskKill.cs @@ -153,6 +153,6 @@ private async Task SyncTaskKillRole() } private async Task SuccessAddEmoji() - => await m_UserMessage.AddReactionAsync(new Emoji(ReactionType.Success.ToLabel())); + => await m_UserMessage.AddReactionAsync(ReactionType.Success.ToEmoji()); } } diff --git a/Script/ClanBattle/UpdateDate.cs b/Script/ClanBattle/UpdateDate.cs index da5b09ef..190f3dbb 100644 --- a/Script/ClanBattle/UpdateDate.cs +++ b/Script/ClanBattle/UpdateDate.cs @@ -55,7 +55,7 @@ public async Task DeleteYesterdayData() } taskList.Add(new BattleTaskKill(clanRole).SyncTaskKillData()); - taskList.Add(new BattleReservation(clanRole).UpdateSystemMessage()); + taskList.Add(new BattleReservationSummary(clanRole).UpdateMessage()); for (int i = 0; i < CommonDefine.MaxBossNumber; i++) { diff --git a/Script/Commands.cs b/Script/Commands.cs new file mode 100644 index 00000000..3f6ac1a4 --- /dev/null +++ b/Script/Commands.cs @@ -0,0 +1,348 @@ +using System.Threading.Tasks; +using PriconneBotConsoleApp.Attribute; +using PriconneBotConsoleApp.DataModel; +using PriconneBotConsoleApp.DataType; + +namespace PriconneBotConsoleApp.Script +{ + public static class Commands + { + // 持ち越し関連 + [Command(AttackType.CarryOver, 2, compatibleChannels: ChannelFeatureType.CarryOverID)] + public static Task UpdateCarryOverData(CommandEventArgs commandEventArgs) + { + new BattleCarryOver(commandEventArgs).UpdateCarryOverData(); + return Task.CompletedTask; + } + + [Command("消化", 0, 1, compatibleChannels: ChannelFeatureType.CarryOverID)] + public static Task DeleteCarryOverData(CommandEventArgs commandEventArgs) + { + new BattleCarryOver(commandEventArgs).DeleteCarryOverData(); + return Task.CompletedTask; + } + + [Command("!rm", 0, 2, compatibleChannels: ChannelFeatureType.CarryOverID)] + public static Task DeleteOtherPlayerCarryOverData(CommandEventArgs commandEventArgs) + { + if (commandEventArgs.Arguments.Count == 2) + { + new BattleCarryOver(commandEventArgs).DeleteOtherPlayerData(); + } + else + { + new BattleCarryOver(commandEventArgs).DeleteCarryOverData(); + } + + return Task.CompletedTask; + } + + [Command("!list", 0, 0, compatibleChannels: ChannelFeatureType.CarryOverID)] + public static async Task DisplayCarryOverList(CommandEventArgs commandEventArgs) + { + await new BattleCarryOver(commandEventArgs).SendClanCarryOverList(); + } + + [Command("!init", 0, 0, compatibleChannels: ChannelFeatureType.CarryOverID)] + public static Task InitAllCarryOverData(CommandEventArgs commandEventArgs) + { + new BattleCarryOver(commandEventArgs).InitAllData(); + return Task.CompletedTask; + } + + // 進行関連 + [Command( + AttackType.Physics, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task AttackPhysics(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateAttackData(AttackType.Physics); + + [Command( + AttackType.Magic, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task AttackMagic(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateAttackData(AttackType.Magic); + + [Command( + AttackType.NewYearKaryl, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task AttackNewYearKaryl(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateAttackData(AttackType.NewYearKaryl); + + [Command( + AttackType.CarryOver, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task AttackCarryOver(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateAttackData(AttackType.CarryOver); + + [Command( + new[] { "kari", "仮確定" }, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task UpdateProgressStatusReady(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateStatusData(ProgressStatus.AttackReady); + + [Command( + new[] { "atk", "確定" }, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task UpdateProgressStatusDone(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateStatusData(ProgressStatus.AttackDone); + + [Command( + new[] { "sos", "jiko", "ziko", "事故" }, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task UpdateProgressSOS(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateStatusData(ProgressStatus.SOS); + + [Command( + new[] { "〆確定", "fin" }, + 0, + 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task UpdateProgressFin(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateStatusData(ProgressStatus.SubdueBoss); + + [Command( + "!init", 0, 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task InitProgressData(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).InitCommand(); + + [Command( + "!call", 1, 1, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task CallProgress(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).Start(); + + [Command( + "!list", 0, 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task DisplayList(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).SendList(); + + [Command( + "!rm", 1, 1, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task RemoveProgressData(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).RemoveOrRevertUserData(true); + + [Command( + "!rv", 1, 1, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task RevertProgressData(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).RemoveOrRevertUserData(); + + [Command( + "!next", 0, 0, + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID + )] + public static async Task NextProgressBoss(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).NextBoss(); + + [Command( + compatibleChannels: new[] + { + ChannelFeatureType.ProgressBoss1ID, + ChannelFeatureType.ProgressBoss2ID, + ChannelFeatureType.ProgressBoss3ID, + ChannelFeatureType.ProgressBoss4ID, + ChannelFeatureType.ProgressBoss5ID, + } + )] + public static async Task UpdateProgressDamage(CommandEventArgs commandEventArgs) + => await new BattleProgress(commandEventArgs).UpdateDamageData(); + + [Command("!list", 0, 0, ChannelFeatureType.ReportID)] + public static async Task ListReport(CommandEventArgs commandEventArgs) + => await new BattleReport(commandEventArgs).SendClanAttackList(); + + [Command("!rm", 0, 1, ChannelFeatureType.ReportID)] + public static Task RemovePlayerReportData(CommandEventArgs commandEventArgs) + { + new BattleReport(commandEventArgs).DeleteReportData(); + return Task.CompletedTask; + } + + [Command("!add", 3, 3, ChannelFeatureType.ReportID)] + public static Task RegisterOtherPlayerReportData(CommandEventArgs commandEventArgs) + { + new BattleReport(commandEventArgs).RegisterOtherUserReportData(); + return Task.CompletedTask; + } + + [Command("!init", 0, 0, ChannelFeatureType.ReportID)] + public static Task InitReportData(CommandEventArgs commandEventArgs) + { + new BattleReport(commandEventArgs).DeleteAllClanReport(); + return Task.CompletedTask; + } + + [Command(minArgumentLength: 0, maxArgumentLength: 0, compatibleChannels: ChannelFeatureType.ReportID)] + public static Task RegisterReportData(CommandEventArgs commandEventArgs) + { + new BattleReport(commandEventArgs).RegisterReportData(); + return Task.CompletedTask; + } + + [Command("予約", 0, compatibleChannels: ChannelFeatureType.ReserveID)] + public static Task ReservationCommand(CommandEventArgs commandEventArgs) + { + BattleReservation battleReservation = new(commandEventArgs); + + if (commandEventArgs.Arguments.Count == 0) + { + battleReservation.PlayerReserveList(); + } + else + { + battleReservation.RegisterReserveData(); + } + + return Task.CompletedTask; + } + + [Command(new[] { "予約一覧, 予約確認, 予約状況, !list" }, 0, 0, ChannelFeatureType.ReserveID)] + public static Task ListReservation(CommandEventArgs commandEventArgs) + { + new BattleReservation(commandEventArgs).PlayerReserveList(); + return Task.CompletedTask; + } + + [Command(new[] { "削除", "!rm" }, 2, 2, ChannelFeatureType.ReserveID)] + public static Task DeleteReserveData(CommandEventArgs commandEventArgs) + { + new BattleReservation(commandEventArgs).DeleteReserveData(); + return Task.CompletedTask; + } + + [Command("!start", 0, 0, ChannelFeatureType.ReserveResultID)] + public static async Task SendReserveSummary(CommandEventArgs commandEventArgs) + => await new BattleReservationSummary(commandEventArgs.Role, commandEventArgs.ClanData).SendMessage(); + + [Command(compatibleChannels: new[] + { + ChannelFeatureType.DeclareBoss1ID, + ChannelFeatureType.DeclareBoss2ID, + ChannelFeatureType.DeclareBoss3ID, + ChannelFeatureType.DeclareBoss4ID, + ChannelFeatureType.DeclareBoss5ID, + })] + public static async Task StartDeclare(CommandEventArgs commandEventArgs) + { + var BossNumber = commandEventArgs.ChannelFeatureType switch + { + ChannelFeatureType.DeclareBoss1ID => BossNumberType.Boss1Number, + ChannelFeatureType.DeclareBoss2ID => BossNumberType.Boss2Number, + ChannelFeatureType.DeclareBoss3ID => BossNumberType.Boss3Number, + ChannelFeatureType.DeclareBoss4ID => BossNumberType.Boss4Number, + ChannelFeatureType.DeclareBoss5ID => BossNumberType.Boss5Number, + _ => BossNumberType.Unknown, + }; + + if (BossNumber == BossNumberType.Unknown) + { + return; + } + + await new BattleDeclaration(commandEventArgs.ClanData, commandEventArgs.SocketUserMessage, BossNumber).RunDeclarationCommandByMessage(); + } + + [Command(compatibleChannels: ChannelFeatureType.TaskKillID)] + public static async Task StartTaskkill(CommandEventArgs commandEventArgs) + => await new BattleTaskKill(commandEventArgs.ClanData, commandEventArgs.SocketUserMessage).RunByMessageCommands(); + + [Command("!dice", 0, 1)] + public static async Task Dice(CommandEventArgs commandEventArgs) + => await new Dice(commandEventArgs).Run(); + + [Command("!tl", 2, 2)] + public static async Task TimeLineConversion(CommandEventArgs commandEventArgs) + => await new TimeLineConversion(commandEventArgs).RunByMessage(); + + [Command("!today", 0, 0)] + public static async Task EventInformation(CommandEventArgs commandEventArgs) + => await new PriconneEventViewer(commandEventArgs).SendEventInfomationByMessage(); + } +} diff --git a/Script/Program.cs b/Script/Program.cs index 2a48a8ab..cb6afe84 100644 --- a/Script/Program.cs +++ b/Script/Program.cs @@ -27,6 +27,7 @@ public class Program public async Task MainAsync() { BotConfigManager.SetJsonConfig(ConfigPath); + CommandMapper.InitCommandCache(); m_config = new DiscordSocketConfig { @@ -59,24 +60,29 @@ public async Task MainAsync() /// /// メッセージの受信処理 /// - /// + /// /// - private async Task CommandRecieved(SocketMessage messageParam) + private async Task CommandRecieved(SocketMessage socketMessage) { - if (messageParam is not SocketUserMessage message) + if (socketMessage is not SocketUserMessage socketUserMessage) { return; } - Console.WriteLine($"{message.Channel.Name} {message.Author.Username}:{message}"); + Console.WriteLine($"{socketUserMessage.Channel.Name} {socketUserMessage.Author.Username}:{socketUserMessage}"); - if (message.Author.IsBot) + if (socketUserMessage.Author.IsBot) { return; } - var receiveMessages = new ReceiveMessageController(message); - await receiveMessages.RunMessageReceive(); + try + { + await CommandMapper.Invoke(new CommandEventArgs(socketUserMessage)); + } + catch + { + } } /// diff --git a/Script/ReceiveInteractionController.cs b/Script/ReceiveInteractionController.cs index f29da2a2..24f59a64 100644 --- a/Script/ReceiveInteractionController.cs +++ b/Script/ReceiveInteractionController.cs @@ -13,6 +13,7 @@ public class ReceiveInteractionController private readonly SocketTextChannel m_TextChannel; private readonly PlayerData m_PlayerData; private readonly ClanData m_ClanData; + private readonly SocketRole m_Role; public ReceiveInteractionController(SocketInteraction interaction) { @@ -25,8 +26,8 @@ public ReceiveInteractionController(SocketInteraction interaction) return; } - var playerRole = m_TextChannel.Guild.GetRole(m_PlayerData.ClanData.ClanRoleID); - m_ClanData = DatabaseClanDataController.LoadClanData(playerRole); + m_Role = m_TextChannel.Guild.GetRole(m_PlayerData.ClanData.ClanRoleID); + m_ClanData = DatabaseClanDataController.LoadClanData(m_Role); } public async Task Run() @@ -40,9 +41,10 @@ public async Task Run() .FirstOrDefault(x => x.ChannelID == m_TextChannel.Id) ?.FeatureID ?? 0; - if (channelFeatureID == (int)ChannelFeatureType.ReserveResultID) + if (channelFeatureID == (int)ChannelFeatureType.ReserveResultID + && m_Interaction is SocketMessageComponent socketMessageComponent) { - await new BattleReservation(m_ClanData, m_Interaction).RunResultInteraction(); + await new BattleReservationSummary(m_Role, m_ClanData).RunInteraction(socketMessageComponent); } BattleDeclaration battleDeclaration = channelFeatureID switch diff --git a/Script/ReceiveMessageController.cs b/Script/ReceiveMessageController.cs deleted file mode 100644 index 016bffaa..00000000 --- a/Script/ReceiveMessageController.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Threading.Tasks; -using Discord.WebSocket; -using PriconneBotConsoleApp.DataModel; -using PriconneBotConsoleApp.Database; -using System.Linq; -using PriconneBotConsoleApp.DataType; -using PriconneBotConsoleApp.Extension; - -namespace PriconneBotConsoleApp.Script -{ - public class ReceiveMessageController - { - private readonly ClanData m_PlayerClanData; - private readonly PlayerData m_PlayerData; - private readonly SocketUserMessage m_ReceiveMessage; - - public ReceiveMessageController(SocketUserMessage message) - { - m_ReceiveMessage = message; - var messageChannel = message.Channel as SocketGuildChannel; - var guildID = messageChannel.Guild.Id; - var userID = message.Author.Id; - m_PlayerData = DatabasePlayerDataController.LoadPlayerData(guildID, userID); - - if (m_PlayerData == null) - { - return; - } - - var userRole = messageChannel.Guild.GetRole(m_PlayerData.ClanData.ClanRoleID); - - if (userRole == null) - { - return; - } - - m_PlayerClanData = DatabaseClanDataController.LoadClanData(userRole); - } - - public async Task RunMessageReceive() - { - if (m_ReceiveMessage != null) - { - await RunMessageReceive(m_ReceiveMessage); - } - } - - public async Task RunMessageReceive(SocketUserMessage message) - { - await new TimeLineConversion(message).RunByMessage(); - await new PriconneEventViewer(message).SendEventInfomationByMessage(); - await new Dice(message).Run(); - - if (m_PlayerData == null || m_PlayerClanData == null) - { - return; - } - - var messageChannelID = message.Channel.Id; - var featureID = m_PlayerClanData.ChannelData.FirstOrDefault(x => x.ChannelID == messageChannelID)?.FeatureID ?? 0; - - if (messageChannelID == m_PlayerClanData.ChannelData.GetChannelID(m_PlayerClanData.ClanID, ChannelFeatureType.ReserveID)) - { - await new BattleReservation(m_PlayerClanData, message).RunReservationCommand(); - } - - if (messageChannelID == m_PlayerClanData.ChannelData.GetChannelID(m_PlayerClanData.ClanID, ChannelFeatureType.ReserveResultID)) - { - await new BattleReservation(m_PlayerClanData, message).RunReservationResultCommand(); - } - - if (messageChannelID == m_PlayerClanData.ChannelData.GetChannelID(m_PlayerClanData.ClanID, ChannelFeatureType.TaskKillID)) - { - await new BattleTaskKill(m_PlayerClanData, message).RunByMessageCommands(); - } - - if (messageChannelID == m_PlayerClanData.ChannelData.GetChannelID(m_PlayerClanData.ClanID, ChannelFeatureType.ReportID)) - { - await new BattleReport(m_PlayerClanData, message).RunByMessage(); - } - - if (messageChannelID == m_PlayerClanData.ChannelData.GetChannelID(m_PlayerClanData.ClanID, ChannelFeatureType.CarryOverID)) - { - await new BattleCarryOver(m_PlayerClanData, message).RunByMessage(); - } - - BattleDeclaration battleDeclaration = featureID switch - { - (int)ChannelFeatureType.DeclareBoss1ID => new(m_PlayerClanData, message, BossNumberType.Boss1Number), - (int)ChannelFeatureType.DeclareBoss2ID => new(m_PlayerClanData, message, BossNumberType.Boss2Number), - (int)ChannelFeatureType.DeclareBoss3ID => new(m_PlayerClanData, message, BossNumberType.Boss3Number), - (int)ChannelFeatureType.DeclareBoss4ID => new(m_PlayerClanData, message, BossNumberType.Boss4Number), - (int)ChannelFeatureType.DeclareBoss5ID => new(m_PlayerClanData, message, BossNumberType.Boss5Number), - _ => null, - }; - - if (battleDeclaration != null) - { - await battleDeclaration.RunDeclarationCommandByMessage(); - } - - BattleProgress battleProgress = featureID switch - { - (uint)ChannelFeatureType.ProgressBoss1ID => new(m_PlayerClanData, message, (byte)BossNumberType.Boss1Number), - (uint)ChannelFeatureType.ProgressBoss2ID => new(m_PlayerClanData, message, (byte)BossNumberType.Boss2Number), - (uint)ChannelFeatureType.ProgressBoss3ID => new(m_PlayerClanData, message, (byte)BossNumberType.Boss3Number), - (uint)ChannelFeatureType.ProgressBoss4ID => new(m_PlayerClanData, message, (byte)BossNumberType.Boss4Number), - (uint)ChannelFeatureType.ProgressBoss5ID => new(m_PlayerClanData, message, (byte)BossNumberType.Boss5Number), - _ => null, - }; - - if (battleProgress != null) - { - await battleProgress.RunByMessage(); - } - } - } -} diff --git a/Script/Utility/CommandMapper.cs b/Script/Utility/CommandMapper.cs new file mode 100644 index 00000000..a1500ec4 --- /dev/null +++ b/Script/Utility/CommandMapper.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using PriconneBotConsoleApp.Attribute; +using PriconneBotConsoleApp.DataModel; +using PriconneBotConsoleApp.DataType; +using PriconneBotConsoleApp.Extension; + +namespace PriconneBotConsoleApp.Script +{ + public static class CommandMapper + { + /// コマンドキャッシュのキーの型 + /// 対応するチャンネル + /// 対応するコマンド名 + private record CacheKey(ChannelFeatureType ChannelFeature, string Name); + + /// コマンドキャッシュの要素 + private class CacheValue + { + internal CacheValue(MethodInfo methodInfo) + { + Info = methodInfo.GetCustomAttribute(false); + + if (Info != null) + { + IsValid = true; + Handler = (Func)Delegate.CreateDelegate(typeof(Func), methodInfo); + } + } + + /// 対象のがつけられているかどうか + internal bool IsValid { get; } + + /// 対象のにつけられた + internal CommandAttribute Info { get; } + + /// 対象のの実体 + internal Func Handler { get; } + } + + private static ConcurrentDictionary m_CommandCache; + + public static void InitCommandCache() + => m_CommandCache = new( + typeof(Commands) + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .Select(methodInfo => new CacheValue(methodInfo)) + .Where(cacheValue => cacheValue.IsValid) + .SelectMany( + valueTuple => valueTuple.Info.CompatibleChannels.SelectMany( + _ => valueTuple.Info.Names, + (channelFeature, name) => new CacheKey(channelFeature, name) + ), + (valueTuple, keyTuple) => (keyTuple, valueTuple)) + .ToDictionary(keyValueTuple => keyValueTuple.keyTuple, keyValueTuple => keyValueTuple.valueTuple) + ); + + public static async Task Invoke(CommandEventArgs commandEventArgs) + { + if (!m_CommandCache.TryGetValueMany( + out var functionData, + new(commandEventArgs.ChannelFeatureType, commandEventArgs.Name), + new(ChannelFeatureType.All, commandEventArgs.Name), + new(commandEventArgs.ChannelFeatureType, string.Empty))) + { + throw new KeyNotFoundException(commandEventArgs.SocketUserMessage.Content); + } + + if (!functionData.Info.IsCompatibleArgumentLength(commandEventArgs.Arguments.Count)) + { + throw new ArgumentException( + $"Argument length {commandEventArgs.Arguments.Count} is incompatible; Expect : between {functionData.Info.MinArgumentLength} and {functionData.Info.MaxArgumentLength}\n{commandEventArgs.SocketUserMessage.Content}" + ); + } + + await functionData.Handler(commandEventArgs); + } + } +} diff --git a/Script/Utility/Dice.cs b/Script/Utility/Dice.cs index c92640d2..6e28c5e5 100644 --- a/Script/Utility/Dice.cs +++ b/Script/Utility/Dice.cs @@ -1,38 +1,29 @@ -using Discord.WebSocket; -using PriconneBotConsoleApp.Define; -using PriconneBotConsoleApp.Extension; -using System; +using System; using System.Threading.Tasks; +using PriconneBotConsoleApp.DataModel; +using PriconneBotConsoleApp.Define; namespace PriconneBotConsoleApp.Script { public class Dice { - private SocketMessage m_UserMessage; + private CommandEventArgs m_CommandEventArgs; - public Dice(SocketMessage message) - { - m_UserMessage = message; - } + public Dice(CommandEventArgs commandEventArgs) + => m_CommandEventArgs = commandEventArgs; public async Task Run() { - var splitMessage = m_UserMessage.Content.ZenToHan().Split(' ', StringSplitOptions.RemoveEmptyEntries); var diceMax = UtilityDefine.DefaultMaxDiceNumber; - if (splitMessage.Length == 0 || splitMessage[0] != "!dice") - { - return; - } - - if (splitMessage.Length == 2 && int.TryParse(splitMessage[1], out var number)) + if (m_CommandEventArgs.Arguments.Count == 1 && int.TryParse(m_CommandEventArgs.Arguments[0], out var number)) { diceMax = number; } var diceResult = new Random().Next(UtilityDefine.DefaultMinDiceNumber, diceMax); var sendMessage = $"{diceResult}"; - await m_UserMessage.Channel.SendMessageAsync(sendMessage); + await m_CommandEventArgs.Channel.SendMessageAsync(sendMessage); } } } diff --git a/Script/Utility/EnumMapper.cs b/Script/Utility/EnumMapper.cs index 42826580..588b2a58 100644 --- a/Script/Utility/EnumMapper.cs +++ b/Script/Utility/EnumMapper.cs @@ -35,10 +35,10 @@ public static string ToLabel(this T data) where T : Enum }; private static MultiDescriptionData GetMultiDescriptionData(T data) where T : Enum - => m_MultiDescriptionDictionary.GetValueOrInitialize((typeof(T), data), data.GetMultiDescripion); + => m_MultiDescriptionDictionary.GetValueOrInitialize((typeof(T), data), () => data.GetMultiDescription()); private static string ToSingleLabel(this T data) where T : Enum - => m_SingleDescriptionDictionary.GetValueOrInitialize((typeof(T), data), data.GetDescription); + => m_SingleDescriptionDictionary.GetValueOrInitialize((typeof(T), data), () => data.GetDescription()); public static string ToLongLabel(this T data) where T : Enum => GetMultiDescriptionData(data).LongDescription; @@ -46,13 +46,13 @@ public static string ToLongLabel(this T data) where T : Enum public static string ToShortLabel(this T data) where T : Enum => GetMultiDescriptionData(data).ShortDescription; - private static string[] GetAliases(this T data) where T : Enum + private static IReadOnlyList GetAliases(this T data) where T : Enum => GetMultiDescriptionData(data).Aliases; public static Emoji ToEmoji(this ButtonType buttonType) => new(buttonType.ToShortLabel()); - public static Emoji emoji(this ReactionType reactionType) + public static Emoji ToEmoji(this ReactionType reactionType) => new(reactionType.ToLabel()); private static DescriptionType GetDescriptionType() where T : Enum diff --git a/Script/Utility/PriconneEventViewer.cs b/Script/Utility/PriconneEventViewer.cs index 1ec68142..18185241 100644 --- a/Script/Utility/PriconneEventViewer.cs +++ b/Script/Utility/PriconneEventViewer.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Discord; using PriconneBotConsoleApp.Database; +using PriconneBotConsoleApp.DataModel; using PriconneBotConsoleApp.DataType; using PriconneBotConsoleApp.Extension; @@ -11,21 +12,14 @@ namespace PriconneBotConsoleApp.Script { public class PriconneEventViewer { - private IMessage m_UserMessage; - public PriconneEventViewer(IMessage message) - { - m_UserMessage = message; - } + private CommandEventArgs m_CommandEventArgs; + public PriconneEventViewer(CommandEventArgs commandEventArgs) + => m_CommandEventArgs = commandEventArgs; public async Task SendEventInfomationByMessage() { - if (m_UserMessage.Content != "!today") - { - return; - } - var eventString = EventString(); - await m_UserMessage.Channel.SendMessageAsync(text: eventString) ; + await m_CommandEventArgs.Channel.SendMessageAsync(text: eventString) ; } public string EventString() diff --git a/Script/Utility/TimeLineConversion.cs b/Script/Utility/TimeLineConversion.cs index 4e0ee822..f70b73c3 100644 --- a/Script/Utility/TimeLineConversion.cs +++ b/Script/Utility/TimeLineConversion.cs @@ -1,17 +1,17 @@ using System; -using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Discord; using Discord.WebSocket; +using PriconneBotConsoleApp.DataModel; using PriconneBotConsoleApp.Define; namespace PriconneBotConsoleApp.Script { public class TimeLineConversion { - private IMessage m_userMessage; + private CommandEventArgs m_CommandEventArgs; private class ConvertData { @@ -22,19 +22,12 @@ private class ConvertData public IMessage Message = null; } - public TimeLineConversion(IMessage message) - { - m_userMessage = message; - } + public TimeLineConversion(CommandEventArgs commandEventArgs) + => m_CommandEventArgs = commandEventArgs; public async Task RunByMessage() { - if (m_userMessage == null || !m_userMessage.Content.StartsWith("!tl")) - { - return; - } - - var messageData = await LoadTimeLineMessage(m_userMessage); + var messageData = await LoadTimeLineMessage(); if (messageData == null) { @@ -42,9 +35,7 @@ public async Task RunByMessage() } var convertMessage = ConversionMessage(messageData.Message.Content, messageData.Time); - var userChannelData = m_userMessage.Channel as ISocketMessageChannel; - await userChannelData.SendMessageAsync(convertMessage); - return; + await m_CommandEventArgs.Channel.SendMessageAsync(convertMessage); } /// @@ -53,11 +44,10 @@ public async Task RunByMessage() /// /// /// - private async Task LoadTimeLineMessage(IMessage message) + private async Task LoadTimeLineMessage() { - var splitMessageContent = message.Content.Split( new[] { " ", " " }, StringSplitOptions.RemoveEmptyEntries); - - if (splitMessageContent.Length != 3 || !int.TryParse(splitMessageContent[2], out int timeData) || timeData < CommonDefine.MinBattleTime || timeData > CommonDefine.MaxBattleTime ) + if (!int.TryParse(m_CommandEventArgs.Arguments[1], out int timeData) + || !CommonDefine.IsValidBattleTime(timeData)) { return null; } @@ -67,10 +57,10 @@ private async Task LoadTimeLineMessage(IMessage message) Time = timeData, }; - var uriData = new Uri(splitMessageContent[1]); + var uriData = new Uri(m_CommandEventArgs.Arguments[0]); var discordID = uriData.Segments; - if (discordID.Count() != 5) + if (discordID.Length != 5) { return null; } @@ -79,8 +69,7 @@ private async Task LoadTimeLineMessage(IMessage message) convertData.MessageChannelID = discordID[3].Replace("/", ""); convertData.MessageID = discordID[4]; - var userChannelData = message.Channel as SocketGuildChannel; - var timeLineChannelData = userChannelData.Guild.GetChannel(ulong.Parse(convertData.MessageChannelID)) as SocketTextChannel; + var timeLineChannelData = m_CommandEventArgs.Role.Guild.GetChannel(ulong.Parse(convertData.MessageChannelID)) as SocketTextChannel; convertData.Message = await timeLineChannelData.GetMessageAsync(ulong.Parse(convertData.MessageID)); if (convertData.Message == null) @@ -157,8 +146,6 @@ private string ConversionMessage(string messageData, int timeData) { afterLineMessageContent = afterLineMessageContent.Replace(matchTimeData.Value, $"{afterSeconds:D2}{matchTimeData.Groups[2]}"); } - - } sendMessageContent.AppendLine(afterLineMessageContent);