diff --git a/DiscordIntegration.Bot/Bot.cs b/DiscordIntegration.Bot/Bot.cs index 61b734f..d6b23d8 100644 --- a/DiscordIntegration.Bot/Bot.cs +++ b/DiscordIntegration.Bot/Bot.cs @@ -1,3 +1,5 @@ +using DiscordIntegration.Dependency.Database; + namespace DiscordIntegration.Bot; using System.Collections.Specialized; @@ -56,6 +58,8 @@ private async Task Init() return; } + DatabaseHandler.Init(); + Log.Debug(ServerNumber, nameof(Init), "Setting up commands..."); InteractionService = new(Client, new InteractionServiceConfig() { diff --git a/DiscordIntegration.Bot/Commands/SendCommand.cs b/DiscordIntegration.Bot/Commands/SendCommand.cs index 2ba1be0..fea83f8 100644 --- a/DiscordIntegration.Bot/Commands/SendCommand.cs +++ b/DiscordIntegration.Bot/Commands/SendCommand.cs @@ -22,7 +22,7 @@ public async Task Send([Summary("command", "The command to send.")] string comma ErrorCodes canRunCommand = SlashCommandHandler.CanRunCommand((IGuildUser) Context.User, bot.ServerNumber, command); if (canRunCommand != ErrorCodes.None) { - await RespondAsync(embed: await ErrorHandlingService.GetErrorEmbed(canRunCommand)); + await RespondAsync(embed: await ErrorHandlingService.GetErrorEmbed(canRunCommand), ephemeral: true); return; } diff --git a/DiscordIntegration.Bot/Commands/WatchlistCommands.cs b/DiscordIntegration.Bot/Commands/WatchlistCommands.cs new file mode 100644 index 0000000..14c7b33 --- /dev/null +++ b/DiscordIntegration.Bot/Commands/WatchlistCommands.cs @@ -0,0 +1,63 @@ +using DiscordIntegration.Bot.Services; +using DiscordIntegration.Dependency.Database; + +namespace DiscordIntegration.Bot.Commands; + +using Discord; +using Discord.Interactions; + +[Group("watchlist", "Commands for managing the watchlist.")] +public class WatchlistCommands: InteractionModuleBase +{ + private readonly Bot bot; + + public WatchlistCommands(Bot bot) => this.bot = bot; + + [SlashCommand("add", "Adds a UserID to the watchlist.")] + public async Task AddToWatchlist([Summary("UserId", "The user ID of the player to watch.")] string userId, [Summary("Reason", "The reason they should be watched.")] string reason) + { + ErrorCodes canRunCommand = SlashCommandHandler.CanRunCommand((IGuildUser) Context.User, bot.ServerNumber, "watchlist"); + if (canRunCommand != ErrorCodes.None) + { + await RespondAsync(embed: await ErrorHandlingService.GetErrorEmbed(canRunCommand), ephemeral: true); + return; + } + + if (DatabaseHandler.CheckWatchlist(userId, out string res)) + { + await RespondAsync( + embed: await EmbedBuilderService.CreateBasicEmbed("User already on Watchlist", + $"The userID {userId} is already on the watchlist for {reason}. If you wish to change the reason, please remove the user first then re-add them.", + Color.Orange), ephemeral: true); + return; + } + + DatabaseHandler.AddEntry(userId, reason); + await RespondAsync( + embed: await EmbedBuilderService.CreateBasicEmbed("User added to Watchlist", + $"The userID {userId} has been added to the watchlist for {reason}", Color.Green), ephemeral: true); + } + + [SlashCommand("remove", "Removes a UserID from the watchlist.")] + public async Task RemoveFromWatchlist([Summary("UserID", "The user ID of the player to remove from the watchlist.")] string userId) + { + ErrorCodes canRunCommand = + SlashCommandHandler.CanRunCommand((IGuildUser)Context.User, bot.ServerNumber, "watchlist"); + if (canRunCommand != ErrorCodes.None) + { + await RespondAsync(embed: await ErrorHandlingService.GetErrorEmbed(canRunCommand), ephemeral: true); + return; + } + + if (!DatabaseHandler.CheckWatchlist(userId, out string _)) + { + await RespondAsync(embed: await ErrorHandlingService.GetErrorEmbed(ErrorCodes.NoRecordForUserFound), ephemeral: true); + return; + } + + DatabaseHandler.RemoveEntry(userId); + await RespondAsync( + embed: await EmbedBuilderService.CreateBasicEmbed("User removed from watchlist.", + $"User {userId} has been removed from the watchlist.", Color.Green), ephemeral: true); + } +} \ No newline at end of file diff --git a/DiscordIntegration.Bot/Config.cs b/DiscordIntegration.Bot/Config.cs index 9a8e8fb..6ce5e6e 100644 --- a/DiscordIntegration.Bot/Config.cs +++ b/DiscordIntegration.Bot/Config.cs @@ -87,6 +87,14 @@ public class Config LogType = LogType.Embed }, }, + Watchlist = new List + { + new() + { + Id = 0, + LogType = LogType.Embed + }, + }, } } } diff --git a/DiscordIntegration.Bot/ConfigObjects/LogChannels.cs b/DiscordIntegration.Bot/ConfigObjects/LogChannels.cs index abd42ab..a50c813 100644 --- a/DiscordIntegration.Bot/ConfigObjects/LogChannels.cs +++ b/DiscordIntegration.Bot/ConfigObjects/LogChannels.cs @@ -10,6 +10,7 @@ public class LogChannels public List? Reports { get; set; } = new(); public List? StaffCopy { get; set; } = new(); public List? Errors { get; set; } = new(); + public List? Watchlist { get; set; } = new(); public IEnumerable this[ChannelType result] { @@ -22,6 +23,7 @@ public IEnumerable this[ChannelType result] ChannelType.Reports => Reports, ChannelType.StaffCopy => StaffCopy, ChannelType.Errors => Errors, + ChannelType.Watchlist => Watchlist, _ => throw new ArgumentOutOfRangeException(nameof(result), result, null) })!; } diff --git a/DiscordIntegration.Bot/DiscordIntegration.Bot.csproj b/DiscordIntegration.Bot/DiscordIntegration.Bot.csproj index bb835b1..3a8abd1 100644 --- a/DiscordIntegration.Bot/DiscordIntegration.Bot.csproj +++ b/DiscordIntegration.Bot/DiscordIntegration.Bot.csproj @@ -5,8 +5,8 @@ net6.0 enable enable - 1.5.1 - 1.5.1 + 1.6.0 + 1.6.0 diff --git a/DiscordIntegration.Dependency/ChannelType.cs b/DiscordIntegration.Dependency/ChannelType.cs index d4ad3ae..7b4b333 100644 --- a/DiscordIntegration.Dependency/ChannelType.cs +++ b/DiscordIntegration.Dependency/ChannelType.cs @@ -7,6 +7,7 @@ public enum ChannelType Bans, Reports, StaffCopy, - Errors + Errors, + Watchlist } } \ No newline at end of file diff --git a/DiscordIntegration.Dependency/Database/DatabaseHandler.cs b/DiscordIntegration.Dependency/Database/DatabaseHandler.cs new file mode 100644 index 0000000..f93dba8 --- /dev/null +++ b/DiscordIntegration.Dependency/Database/DatabaseHandler.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using Microsoft.Data.Sqlite; + +namespace DiscordIntegration.Dependency.Database; + +public class DatabaseHandler +{ + private static string _connectionString = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "DiscordIntegration.db"); + + public static void Init() + { + using SqliteConnection conn = new(_connectionString); + conn.Open(); + + using (SqliteCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = + "CREATE TABLE IF NOT EXISTS Watchlist(Id INTEGER PRIMARY KEY AUTOINCREMENT, UserId TEXT, Reason TEXT)"; + cmd.ExecuteNonQuery(); + } + + conn.Close(); + } + + public static void AddEntry(string userId, string reason) + { + using SqliteConnection conn = new(_connectionString); + conn.Open(); + + using (SqliteCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "INSERT INTO Watchlist(UserId, Reason) VALUES(@id, @reason)"; + cmd.Parameters.AddWithValue("@id", userId); + cmd.Parameters.AddWithValue("@reason", reason); + cmd.ExecuteNonQuery(); + } + conn.Close(); + } + + + public static void RemoveEntry(string userId) + { + using SqliteConnection conn = new(_connectionString); + conn.Open(); + + using (SqliteCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Watchlist WHERE UserId=@id"; + cmd.Parameters.AddWithValue("@id", userId); + + cmd.ExecuteNonQuery(); + } + + conn.Close(); + } + + public static bool CheckWatchlist(string userId, out string reason) + { + reason = "Not in watchlist"; + using SqliteConnection conn = new(_connectionString); + conn.Open(); + + using (SqliteCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT * FROM Watchlist WHERE UserId=@id"; + cmd.Parameters.AddWithValue("@id", userId); + + using (SqliteDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + reason = reader.GetString(2); + return true; + } + } + } + conn.Close(); + return false; + } +} \ No newline at end of file diff --git a/DiscordIntegration.Dependency/DiscordIntegration.Dependency.csproj b/DiscordIntegration.Dependency/DiscordIntegration.Dependency.csproj old mode 100755 new mode 100644 index 23307f9..d6577ef --- a/DiscordIntegration.Dependency/DiscordIntegration.Dependency.csproj +++ b/DiscordIntegration.Dependency/DiscordIntegration.Dependency.csproj @@ -11,29 +11,10 @@ 512 10 enable - net472 - 1.4.2 - 1.4.2 + 1.5.0 + 1.5.0 ../bin/Plugin/dependencies - - - AnyCPU - true - full - false - ../bin/Plugin/dependencies/ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ../bin/Plugin/dependencies/ - TRACE - prompt - 4 + net472 @@ -48,15 +29,8 @@ + - - diff --git a/DiscordIntegration.Dependency/packages.config b/DiscordIntegration.Dependency/packages.config index a9de8b5..2be1b2c 100644 --- a/DiscordIntegration.Dependency/packages.config +++ b/DiscordIntegration.Dependency/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/DiscordIntegration/API/Network.cs b/DiscordIntegration/API/Network.cs old mode 100755 new mode 100644 diff --git a/DiscordIntegration/Commands/Main.cs b/DiscordIntegration/Commands/Main.cs index 828ebcd..6dec087 100644 --- a/DiscordIntegration/Commands/Main.cs +++ b/DiscordIntegration/Commands/Main.cs @@ -28,11 +28,17 @@ public override void LoadGeneratedCommands() { RegisterCommand(PlayerList.Instance); RegisterCommand(StaffList.Instance); + + if (Instance.Config.UseWatchlist) + { + RegisterCommand(WatchlistAdd.Instance); + RegisterCommand(WatchlistRemove.Instance); + } } protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) { - response = $"{Language.InvalidSubcommand} {Language.Available}: playerlist, stafflist, reload, add, remove"; + response = $"{Language.InvalidSubcommand} {Language.Available}: playerlist, stafflis" + $"{(Instance.Config.UseWatchlist ? "watchadd, watchrem" : string.Empty)}"; return false; } } diff --git a/DiscordIntegration/Commands/WatchlistAdd.cs b/DiscordIntegration/Commands/WatchlistAdd.cs new file mode 100644 index 0000000..ac6ecdb --- /dev/null +++ b/DiscordIntegration/Commands/WatchlistAdd.cs @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +using DiscordIntegration.Dependency.Database; + +namespace DiscordIntegration.Commands +{ + using System; + using System.Text; + using CommandSystem; + using Exiled.API.Features; + using Exiled.Permissions.Extensions; + using NorthwoodLib.Pools; + using static DiscordIntegration; + + /// + /// Adds a user to the watchlist. + /// + internal sealed class WatchlistAdd : ICommand + { +#pragma warning disable SA1600 // Elements should be documented + private WatchlistAdd() + { + } + + public static WatchlistAdd Instance { get; } = new WatchlistAdd(); + + public string Command { get; } = "watchadd"; + + public string[] Aliases { get; } = new[] { "wla" }; + + public string Description { get; } = Language.WatchlistAddDescription; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if (!sender.CheckPermission("di.watchlistadd")) + { + response = string.Format(Language.NotEnoughPermissions, "di.watchlistadd"); + return false; + } + + Player player = Player.Get(arguments.ElementAt(2)); + string reason = string.Empty; + foreach (string s in arguments.Skip(2)) + reason += $"{s} "; + reason = reason.TrimEnd(' '); + DatabaseHandler.AddEntry(player.UserId, reason); + response = $"{player.Nickname} added to watchlist for {reason}"; + return true; + } + } +} \ No newline at end of file diff --git a/DiscordIntegration/Commands/WatchlistRemove.cs b/DiscordIntegration/Commands/WatchlistRemove.cs new file mode 100644 index 0000000..0b11248 --- /dev/null +++ b/DiscordIntegration/Commands/WatchlistRemove.cs @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +using DiscordIntegration.Dependency.Database; + +namespace DiscordIntegration.Commands +{ + using System; + using System.Text; + using CommandSystem; + using Exiled.API.Features; + using Exiled.Permissions.Extensions; + using NorthwoodLib.Pools; + using static DiscordIntegration; + + /// + /// Removes a user from the watchlist. + /// + internal sealed class WatchlistRemove : ICommand + { +#pragma warning disable SA1600 // Elements should be documented + private WatchlistRemove() + { + } + + public static WatchlistRemove Instance { get; } = new WatchlistRemove(); + + public string Command { get; } = "watchrem"; + + public string[] Aliases { get; } = new[] { "wlr" }; + + public string Description { get; } = Language.WatchlistRemoveDescription; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if (!sender.CheckPermission("di.watchlistremove")) + { + response = string.Format(Language.NotEnoughPermissions, "di.watchlistremove"); + return false; + } + + Player player = Player.Get(arguments.ElementAt(2)); + DatabaseHandler.RemoveEntry(player.UserId); + response = $"{player.Nickname} removed from watchlist."; + return true; + } + } +} \ No newline at end of file diff --git a/DiscordIntegration/Config.cs b/DiscordIntegration/Config.cs index f2d4c90..0e3631e 100644 --- a/DiscordIntegration/Config.cs +++ b/DiscordIntegration/Config.cs @@ -117,5 +117,11 @@ public sealed class Config : IConfig /// [Description("The plugin language")] public string Language { get; private set; } = "english"; + + /// + /// Gets a value indicating whether to use the player watchlist feature. + /// + [Description("Indicates whether the player watchlist feature can be used.")] + public bool UseWatchlist { get; set; } = true; } } \ No newline at end of file diff --git a/DiscordIntegration/DiscordIntegration.cs b/DiscordIntegration/DiscordIntegration.cs index 5af2f87..60bc3fc 100644 --- a/DiscordIntegration/DiscordIntegration.cs +++ b/DiscordIntegration/DiscordIntegration.cs @@ -5,6 +5,8 @@ // // ----------------------------------------------------------------------- +using DiscordIntegration.Dependency.Database; + namespace DiscordIntegration { using System; @@ -104,6 +106,8 @@ public override void OnEnabled() Language.Save(); Language.Load(); + DatabaseHandler.Init(); + RegisterEvents(); Bot.UpdateActivityCancellationTokenSource = new CancellationTokenSource(); diff --git a/DiscordIntegration/DiscordIntegration.csproj b/DiscordIntegration/DiscordIntegration.csproj old mode 100755 new mode 100644 index 99611f0..2248738 --- a/DiscordIntegration/DiscordIntegration.csproj +++ b/DiscordIntegration/DiscordIntegration.csproj @@ -3,8 +3,8 @@ enable annotations - 6.0.6 - 6.0.6 + 6.1.0 + 6.1.0 10 net472 @@ -24,7 +24,7 @@ - + @@ -45,6 +45,9 @@ ..\..\References\UnityEngine.CoreModule.dll + + ..\..\References\UnityEngine.PhysicsModule.dll + diff --git a/DiscordIntegration/Events/PlayerHandler.cs b/DiscordIntegration/Events/PlayerHandler.cs index 940c1f5..d587989 100644 --- a/DiscordIntegration/Events/PlayerHandler.cs +++ b/DiscordIntegration/Events/PlayerHandler.cs @@ -5,19 +5,14 @@ // // ----------------------------------------------------------------------- +using DiscordIntegration.Dependency.Database; + namespace DiscordIntegration.Events { using System; - using System.Linq; - using API.Commands; - using API.User; using Dependency; - using Exiled.API.Enums; - using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.Events.EventArgs; - using PlayerStatsSystem; - using Scp914; using static DiscordIntegration; /// @@ -256,6 +251,13 @@ public async void OnChangingRole(ChangingRoleEventArgs ev) public async void OnVerified(VerifiedEventArgs ev) { + if (Instance.Config.UseWatchlist) + { + if (DatabaseHandler.CheckWatchlist(ev.Player.UserId, out string reason)) + if (!string.IsNullOrEmpty(reason)) + await Network.SendAsync(new RemoteCommand(ActionType.Log, ChannelType.Watchlist, string.Format(Language.WatchlistedUserJoined, ev.Player.Nickname, ev.Player.UserId, ev.Player.IPAddress, reason))); + } + if (Instance.Config.EventsToLog.PlayerJoined && (!ev.Player.DoNotTrack || !Instance.Config.ShouldRespectDoNotTrack)) await Network.SendAsync(new RemoteCommand(ActionType.Log, ChannelType.GameEvents, string.Format(Language.HasJoinedTheGame, ev.Player.Nickname, Instance.Config.ShouldLogUserIds ? ev.Player.UserId : Language.Redacted, Instance.Config.ShouldLogIPAddresses ? ev.Player.IPAddress : Language.Redacted))).ConfigureAwait(false); if (Instance.Config.StaffOnlyEventsToLog.PlayerJoined) diff --git a/DiscordIntegration/Language.cs b/DiscordIntegration/Language.cs index a45032a..f104490 100644 --- a/DiscordIntegration/Language.cs +++ b/DiscordIntegration/Language.cs @@ -259,6 +259,12 @@ public Language() public string NotAuthenticated { get; set; } = "Not authenticated"; public string DedicatedServer { get; set; } = "Dedicated server"; + + public string WatchlistedUserJoined { get; set; } = "Watchlisted user {0} ({1}) [{2}] has joined the server.\nWatchlist Reason: {3}"; + + public string WatchlistAddDescription { get; set; } = "Adds a user to the watchlist."; + + public string WatchlistRemoveDescription { get; set; } = "Removes a user from the watchlist."; #pragma warning restore CS1591 #pragma warning restore SA1600 // Elements should be documented