├── .gitignore ├── README.md ├── ReportSystem.cs └── ReportSystem.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/.idea.-Discord-cs2-ReportSystem.dir/.idea/misc.xml 3 | bin/release/net7.0/CounterStrikeSharp.API.dll 4 | .idea/.idea.-Discord-cs2-ReportSystem.dir/.idea/workspace.xml 5 | bin/release/net7.0/publish/CounterStrikeSharp.API.dll 6 | bin/release/net7.0/publish/ReportSystem.deps.json 7 | bin/release/net7.0/publish/ReportSystem.dll 8 | bin/release/net7.0/publish/ReportSystem.pdb 9 | bin/release/net7.0/ReportSystem.deps.json 10 | bin/release/net7.0/ReportSystem.dll 11 | bin/release/net7.0/ReportSystem.pdb 12 | CounterStrikeSharp.API.dll 13 | obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 14 | obj/Debug/net7.0/ReportSystem.AssemblyInfo.cs 15 | obj/Debug/net7.0/ReportSystem.AssemblyInfoInputs.cache 16 | obj/Debug/net7.0/ReportSystem.assets.cache 17 | obj/Debug/net7.0/ReportSystem.csproj.AssemblyReference.cache 18 | obj/Debug/net7.0/ReportSystem.GeneratedMSBuildEditorConfig.editorconfig 19 | obj/Debug/net7.0/ReportSystem.GlobalUsings.g.cs 20 | obj/project.assets.json 21 | obj/project.nuget.cache 22 | obj/project.packagespec.json 23 | obj/release/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 24 | obj/release/net7.0/PublishOutputs.74cac132ae.txt 25 | obj/release/net7.0/ref/ReportSystem.dll 26 | obj/release/net7.0/refint/ReportSystem.dll 27 | obj/release/net7.0/ReportSystem.AssemblyInfo.cs 28 | obj/release/net7.0/ReportSystem.AssemblyInfoInputs.cache 29 | obj/release/net7.0/ReportSystem.assets.cache 30 | *.cache 31 | obj/release/net7.0/ReportSystem.csproj.CopyComplete 32 | obj/release/net7.0/ReportSystem.dll 33 | *.txt 34 | obj/release/net7.0/ReportSystem.GeneratedMSBuildEditorConfig.editorconfig 35 | obj/release/net7.0/ReportSystem.GlobalUsings.g.cs 36 | obj/release/net7.0/ReportSystem.pdb 37 | obj/ReportSystem.csproj.nuget.dgspec.json 38 | obj/ReportSystem.csproj.nuget.g.props 39 | obj/ReportSystem.csproj.nuget.g.targets 40 | obj/rider.project.restore.info 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # -Discord-cs2-ReportSystem 2 | Allows players to send reports (complaints) about other players. 3 | # Installation 4 | 1. Install [CounterStrike Sharp](https://github.com/roflmuffin/CounterStrikeSharp) and [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) 5 | 6 | 2. Download ReportSystem 7 | 8 | 3. Unzip the archive and upload it to the game server 9 | 10 | # Config 11 | ``` 12 | { 13 | "WebhookUrl": "https://discord.com/api/webhooks/" 14 | } 15 | ``` 16 | 17 | # Commands 18 | `css_report` `!report` - Send report 19 | -------------------------------------------------------------------------------- /ReportSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.Json; 3 | using CounterStrikeSharp.API; 4 | using CounterStrikeSharp.API.Core; 5 | using CounterStrikeSharp.API.Modules.Commands; 6 | using CounterStrikeSharp.API.Modules.Cvars; 7 | using CounterStrikeSharp.API.Modules.Entities; 8 | using CounterStrikeSharp.API.Modules.Menu; 9 | 10 | namespace ReportSystem; 11 | 12 | public class ReportSystem : BasePlugin 13 | { 14 | public override string ModuleAuthor => "phantom"; 15 | public override string ModuleName => "[Discord] ReportSystem"; 16 | public override string ModuleVersion => "v1.0.1"; 17 | 18 | private Config _config = null!; 19 | private PersonTargetData[] _selectedReason = new PersonTargetData[65]; 20 | 21 | public override void Load(bool hotReload) 22 | { 23 | _config = LoadConfig(); 24 | 25 | var mapsFilePath = Path.Combine(ModuleDirectory, "reasons.txt"); 26 | if (!File.Exists(mapsFilePath)) 27 | File.WriteAllText(mapsFilePath, ""); 28 | 29 | RegisterListener(slot => _selectedReason[slot + 1] = new PersonTargetData{Target = -1, IsSelectedReason = false}); 30 | RegisterListener(slot => _selectedReason[slot + 1] = null); 31 | 32 | AddCommand("css_report", "", (controller, info) => 33 | { 34 | if(controller == null) return; 35 | 36 | var reportMenu = new ChatMenu("Report"); 37 | reportMenu.MenuOptions.Clear(); 38 | foreach (var player in Utilities.GetPlayers()) 39 | { 40 | if(player.IsBot || player == controller) continue; 41 | 42 | reportMenu.AddMenuOption($"{player.PlayerName} [{player.Index}]", HandleMenu); 43 | } 44 | 45 | ChatMenus.OpenMenu(controller, reportMenu); 46 | }); 47 | 48 | AddCommandListener("say", Listener_Say); 49 | AddCommandListener("say_team", Listener_Say); 50 | } 51 | 52 | private HookResult Listener_Say(CCSPlayerController? player, CommandInfo commandinfo) 53 | { 54 | if (player == null) return HookResult.Continue; 55 | 56 | if (_selectedReason[player.Index] != null && _selectedReason[player.Index]!.IsSelectedReason) 57 | { 58 | var msg = GetTextInsideQuotes(commandinfo.ArgString); 59 | var target = Utilities.GetPlayerFromIndex(_selectedReason[player.Index]!.Target); 60 | switch (msg) 61 | { 62 | case "cancel": 63 | _selectedReason[player.Index]!.IsSelectedReason = false; 64 | return HookResult.Handled; 65 | default: 66 | Task.Run(() => SendMessageToDiscord(player.PlayerName, new SteamID(player.SteamID).SteamId2, target.PlayerName, 67 | new SteamID(target.SteamID).SteamId2, commandinfo.ArgString, $"{ConVar.Find("hostname")!.StringValue}\n{ConVar.Find("ip")!.StringValue}:{ConVar.Find("hostport")!.GetPrimitiveValue()}")); 68 | _selectedReason[player.Index]!.IsSelectedReason = false; 69 | player.PrintToChat($"[\x0C ReportSystem\x01 ] You have successfully filed a report on player `{target.PlayerName}`"); 70 | return HookResult.Handled; 71 | } 72 | } 73 | return HookResult.Continue; 74 | } 75 | 76 | private void HandleMenu(CCSPlayerController controller, ChatMenuOption option) 77 | { 78 | var parts = option.Text.Split('[', ']'); 79 | var lastPart = parts[^2]; 80 | var numbersOnly = string.Join("", lastPart.Where(char.IsDigit)); 81 | 82 | var index = int.Parse(numbersOnly.Trim()); 83 | var reason = File.ReadAllLines(Path.Combine(ModuleDirectory, "reasons.txt")); 84 | var reasonMenu = new ChatMenu("Reasons"); 85 | reasonMenu.MenuOptions.Clear(); 86 | 87 | reasonMenu.AddMenuOption($"My reason [{index}]", (playerController, menuOption) => 88 | { 89 | if (_selectedReason[playerController.Index] == null) return; 90 | 91 | _selectedReason[playerController.Index]!.IsSelectedReason = true; 92 | _selectedReason[playerController.Index]!.Target = index; 93 | playerController.PrintToChat($"[\x0C ReportSystem\x01 ] Write a reason, or write\x02 cancel\x01 to abort the submission."); 94 | }); 95 | foreach (var a in reason) 96 | { 97 | reasonMenu.AddMenuOption($"{a} [{index}]", HandleMenu2); 98 | } 99 | 100 | ChatMenus.OpenMenu(controller, reasonMenu); 101 | } 102 | 103 | private void HandleMenu2(CCSPlayerController controller, ChatMenuOption option) 104 | { 105 | var parts = option.Text.Split('[', ']'); 106 | var lastPart = parts[^2]; 107 | var numbersOnly = string.Join("", lastPart.Where(char.IsDigit)); 108 | 109 | var target = Utilities.GetPlayerFromIndex(int.Parse(numbersOnly.Trim())); 110 | var playerName = controller.PlayerName; 111 | var playerSid = controller.SteamID.ToString(); 112 | var targetName = target.PlayerName; 113 | var targetSid = target.SteamID.ToString(); 114 | var ip = target.SteamID.ToString(); 115 | Task.Run(() => SendMessageToDiscord(playerName, playerSid, targetName, 116 | targetSid, parts[0], ip)); 117 | 118 | controller.PrintToChat($"[\x0C ReportSystem\x01 ] You have successfully filed a report on player `{target.PlayerName}`"); 119 | } 120 | 121 | private async void SendMessageToDiscord(string clientName, string clientSteamId, string targetName, 122 | string targetSteamId, string msg, string ip) 123 | { 124 | try 125 | { 126 | var webhookUrl = GetWebhook(); 127 | 128 | if (string.IsNullOrEmpty(webhookUrl)) return; 129 | 130 | var httpClient = new HttpClient(); 131 | 132 | if (msg == "") return; 133 | 134 | var payload = new 135 | { 136 | embeds = new[] 137 | { 138 | new 139 | { 140 | title = "Report", 141 | description = ip, 142 | color = 16711680, //255204255 143 | fields = new[] 144 | { 145 | new 146 | { 147 | name = "Dispatcher", 148 | value = 149 | $"**Name:** {clientName}\n**SteamID:** {clientSteamId}\n**Link:** [steam account link](https://steamcommunity.com/profiles/{clientSteamId}/)", 150 | inline = false 151 | }, 152 | new 153 | { 154 | name = "Victim", 155 | value = 156 | $"**Name:** {targetName}\n**SteamID:** {targetSteamId}\n**Link:** [steam account link](https://steamcommunity.com/profiles/{targetSteamId}/)", 157 | inline = false 158 | }, 159 | new 160 | { 161 | name = "Reason", 162 | value = msg, 163 | inline = false 164 | } 165 | } 166 | } 167 | } 168 | }; 169 | 170 | var json = JsonSerializer.Serialize(payload); 171 | var content = new StringContent(json, Encoding.UTF8, "application/json"); 172 | var response = await httpClient.PostAsync(webhookUrl, content); 173 | 174 | Console.ForegroundColor = response.IsSuccessStatusCode ? ConsoleColor.Green : ConsoleColor.Red; 175 | Console.WriteLine(response.IsSuccessStatusCode 176 | ? "Success" 177 | : $"Error: {response.StatusCode}"); 178 | } 179 | catch (Exception e) 180 | { 181 | Console.WriteLine(e); 182 | throw; 183 | } 184 | 185 | } 186 | private Config LoadConfig() 187 | { 188 | var configPath = Path.Combine(ModuleDirectory, "settings.json"); 189 | 190 | if (!File.Exists(configPath)) return CreateConfig(configPath); 191 | 192 | var config = JsonSerializer.Deserialize(File.ReadAllText(configPath))!; 193 | 194 | return config; 195 | } 196 | 197 | private Config CreateConfig(string configPath) 198 | { 199 | var config = new Config 200 | { 201 | WebhookUrl = "" 202 | }; 203 | 204 | File.WriteAllText(configPath, 205 | JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true })); 206 | 207 | Console.ForegroundColor = ConsoleColor.DarkGreen; 208 | Console.WriteLine("[ReportSystem] The configuration was successfully saved to a file: " + configPath); 209 | Console.ResetColor(); 210 | 211 | return config; 212 | } 213 | private string GetWebhook() 214 | { 215 | return _config.WebhookUrl; 216 | } 217 | 218 | private string GetTextInsideQuotes(string input) 219 | { 220 | var startIndex = input.IndexOf('"'); 221 | var endIndex = input.LastIndexOf('"'); 222 | 223 | if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) 224 | { 225 | return input.Substring(startIndex + 1, endIndex - startIndex - 1); 226 | } 227 | 228 | return string.Empty; 229 | } 230 | } 231 | public class Config 232 | { 233 | public required string WebhookUrl { get; set; } 234 | } 235 | 236 | public class PersonTargetData 237 | { 238 | public int Target { get; set; } 239 | public bool IsSelectedReason { get; set; } 240 | } -------------------------------------------------------------------------------- /ReportSystem.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | ..\..\..\CounterStrikeSharp.API.dll 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------