using ConVar; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using Oxide.Core.Plugins; using System; using System.Collections.Generic; using System.Linq; using WebSocketSharp; namespace Oxide.Plugins { [Info("Auto Demo Record Lite", "Pho3niX90", "1.0.82")] [Description("Automatic recording based on conditions.")] internal class AutoDemoRecordLite : RustPlugin { private List _reports = new List(); private Dictionary _timers = new Dictionary(); private ARConfig config; int lastSavedCount = 0; [PluginReference] Plugin DiscordApi, DiscordMessages; private void Loaded() { LoadData(); } private void Unload() { SaveData(); _reports.Clear(); _reports = null; foreach (var player in BasePlayer.activePlayerList) { if (player.Connection.IsRecording) player.Connection.StopRecording(); } foreach (Timer timer in _timers.Values) { timer.Destroy(); } _timers.Clear(); _timers = null; } protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["Recording Started"] = "Recording started for player {0}, eta is {1} mins", ["Recording Ended"] = "Recording finished for player {0}, player was recorded for {1} mins", }, this); } string GetMsg(string key) => lang.GetMessage(key, this); void OnPlayerReported(BasePlayer reporter, string targetName, string targetId, string subject, string message, string type) { ulong targetIdLong = 0; if (ulong.TryParse(targetId, out targetIdLong)) { var report = new ARReport(reporter.UserIDString, reporter.displayName, targetId, targetName, subject, message, type); _reports.Add(report); ProcessF7(targetIdLong, report); } } void OnPlayerCommand(BasePlayer player, string command, string[] args) { if (!command.ToLower().Equals("report") || args.Length < 2) return; var target = BasePlayer.Find(args[0]); if (target == null) return; List reason = args.Skip(1).ToList(); var report = new ARReport(player.UserIDString, player.displayName, target.UserIDString, target.displayName, "Report", string.Join(" ", reason), "Report"); _reports.Add(report); ProcessF7(target.userID, report); } void ReportCommand(IPlayer reporter, IPlayer target, string reason) { var report = new ARReport(reporter.Id, reporter.Name, target.Id, target.Name, "DM Report", reason, "DM Report"); _reports.Add(report); ProcessF7(ulong.Parse(target.Id), report); } private void OnDestroy() { foreach (Timer timer in _timers.Values) { timer.Destroy(); } _timers.Clear(); _reports.Clear(); } void ProcessF7(ulong targetId, ARReport report = null) { BasePlayer accused = BasePlayer.FindByID(targetId); if (accused == null) return; if (accused.IsConnected) { // record player only if he has reaced the amount in the config. And only when there is no recording active. if (CheckReports(accused) >= config.AR_Report) { if (!_timers.ContainsKey(accused.userID)) { StartRecording(accused, report); } } } } void StartRecording(BasePlayer player, ARReport report = null) { var msg = string.Format(GetMsg("Recording Started"), player.UserIDString, config.AR_Report_Length); Puts(msg); if (config.AR_Discord_Notify_RecordStart) NotifyDiscord(player, msg, report, true); player.StartDemoRecording(); if (config.AR_Report_Length > 0) { _timers[player.userID] = timer.Once(config.AR_Report_Length * 60, () => StopRecording(player, report)); } } void StopRecording(BasePlayer player, ARReport report) { var msg = string.Format(GetMsg("Recording Ended"), player.UserIDString, config.AR_Report_Length); Puts(msg); if (config.AR_Discord_Notify_RecordStop) NotifyDiscord(player, msg, report, false); if (_timers.ContainsKey(player.userID)) { player.StopDemoRecording(); if (config.AR_Clear_Counter) { _reports.RemoveAll(x => x.targetId == player.UserIDString); } _timers.Remove(player.userID); } } int CheckReports(BasePlayer player) => config.AR_Report_Seconds > 0 ? this._reports.Count(x => secondsAgo(x.created) <= config.AR_Report_Seconds && x.targetId == player.UserIDString) : this._reports.Count(x => x.targetId == player.UserIDString); int secondsAgo(DateTime timeFrom) => (int)Math.Round((DateTime.UtcNow - timeFrom).TotalSeconds); void NotifyDiscord(BasePlayer player, string msg, ARReport report, bool isStart) { if (!config.AR_Discord_Webhook.IsNullOrEmpty() && !config.Equals("https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks") && (config.AR_Discord_Notify_RecordStart || config.AR_Discord_Notify_RecordStop)) { List fields = new List(); fields.Add(new EmbedFieldList() { name = covalence.Server.Name, value = $"[steam://connect/{covalence.Server.Address}:{covalence.Server.Port}](steam://connect/{covalence.Server.Address}:{covalence.Server.Port})", inline = true }); if (report != null && ((isStart && config.AR_Discord_Notify_RecordStartMsg) || (!isStart && config.AR_Discord_Notify_RecordStopMsg))) { fields.Add(new EmbedFieldList() { name = "Reporter", value = $"{report.reporterName} ({report.reporterId})", inline = false }); fields.Add(new EmbedFieldList() { name = "Report Subject", value = $"{report.type}: {report.subject}", inline = false }); fields.Add(new EmbedFieldList() { name = "Report Message", value = report.message, inline = false }); } fields.Add(new EmbedFieldList() { name = player.displayName, value = msg, inline = false }); string json = JsonConvert.SerializeObject(fields.ToArray()); if (DiscordApi != null && DiscordApi.IsLoaded) { DiscordApi?.Call("API_SendEmbeddedMessage", config.AR_Discord_Webhook, "Auto Demo Recorder", config.AR_Discord_Color, json); } else if (DiscordMessages != null && DiscordMessages.IsLoaded) { DiscordMessages?.Call("API_SendFancyMessage", config.AR_Discord_Webhook, "Auto Demo Recorder", config.AR_Discord_Color, json); } else { Puts("No discord API plugin loaded, will not publish to hook!"); } } } #region Configuration private class ARConfig { // Config default vars public int AR_Report = 1; public int AR_Report_Length = 1; public bool AR_Clear_Counter = false; public string AR_Discord_Webhook = "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks"; public int AR_Discord_Color = 39423; public bool AR_Discord_Notify_RecordStart = false; public bool AR_Discord_Notify_RecordStop = false; public int AR_Report_Seconds = 0; public bool AR_Discord_Notify_RecordStartMsg = true; public bool AR_Discord_Notify_RecordStopMsg = false; public bool AR_Save_Reports = true; // Plugin reference private AutoDemoRecordLite plugin; public ARConfig(AutoDemoRecordLite plugin) { this.plugin = plugin; /** * Load all saved config values * */ GetConfig(ref AR_Report, "Auto record after X reports"); GetConfig(ref AR_Report_Length, "Auto record for X minutes"); GetConfig(ref AR_Clear_Counter, "Clear report counter after recording?"); GetConfig(ref AR_Discord_Webhook, "Discord Webhook"); GetConfig(ref AR_Discord_Color, "Discord MSG Color"); GetConfig(ref AR_Discord_Notify_RecordStart, "Discord: Notify if recording is started"); GetConfig(ref AR_Discord_Notify_RecordStartMsg, "Discord: Include report with start message?"); GetConfig(ref AR_Discord_Notify_RecordStop, "Discord: Notify if recording is stopped"); GetConfig(ref AR_Discord_Notify_RecordStopMsg, "Discord: Include report with end message?"); GetConfig(ref AR_Report_Seconds, "Only record when reports within X seconds"); GetConfig(ref AR_Save_Reports, "Save/Load reports to datafile on reload"); plugin.SaveConfig(); } private void GetConfig(ref T variable, params string[] path) { if (path.Length == 0) return; if (plugin.Config.Get(path) == null) { SetConfig(ref variable, path); plugin.PrintWarning($"Added new field to config: {string.Join("/", path)}"); } variable = (T)Convert.ChangeType(plugin.Config.Get(path), typeof(T)); } private void SetConfig(ref T variable, params string[] path) => plugin.Config.Set(path.Concat(new object[] { variable }).ToArray()); } protected override void LoadConfig() { base.LoadConfig(); config = new ARConfig(this); } void LoadData() { if (!config.AR_Save_Reports) return; try { _reports = Interface.Oxide.DataFileSystem.ReadObject>(this.Name); lastSavedCount = _reports.Count(); } catch (Exception e) { Puts(e.Message); } } void SaveData() { int recordsDiff = _reports.Count() - lastSavedCount; if (!config.AR_Save_Reports || recordsDiff == 0) return; try { Interface.Oxide.DataFileSystem.WriteObject(this.Name, _reports, true); } catch (Exception e) { Puts(e.Message); } } protected override void LoadDefaultConfig() => PrintWarning("Generating new configuration file."); #endregion #region Classes public class EmbedFieldList { public string name { get; set; } public string value { get; set; } public bool inline { get; set; } } public class ARReport { public string reporterName; public string reporterId; public string targetName; public string targetId; public string subject; public string message; public string type; public DateTime created; public ARReport() { } public ARReport(string reporterId, string reporterName, string targetId, string targetName, string subject, string message, string type) { this.reporterId = reporterId; this.reporterName = reporterName; this.targetId = targetId; this.targetName = targetName; this.subject = subject; this.message = message; this.type = type; this.created = DateTime.UtcNow; } public ARReport(string targetId, string targetName) { this.targetName = targetName; this.targetId = targetId; } } #endregion } }