Иконка ресурса

FREE AutoSignModeration : 1.1.1 1.1.1

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Reference: System.Drawing
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;
using Graphics = System.Drawing.Graphics;
using System.Text;
using Facepunch;
using Oxide.Core.Libraries.Covalence;
using Encoder = System.Drawing.Imaging.Encoder;
namespace Oxide.Plugins {
    [Info("Auto Sign Moderation", "Whispers88", "1.1.1")]
    [Description("Uses the Omni AI/Open AI to auto moderate image content")]
    public class AutoSignModeration : CovalencePlugin {
        private Dictionary<ulong, float> _signCooldown = new Dictionary<ulong, float>();
        private Queue<SignData> _queuedImages = new Queue<SignData>();
        private Dictionary<ImageKey, SignData> _signsQueuedPool = new Dictionary<ImageKey, SignData>();
        private string permWhitelist = "autosignmoderation.whitelist";
#region Configuration
        private Configuration config;
        public class Configuration {
            [JsonProperty("Image Size 25 - 100%")]
            public float imageSizeReduction = 50;
            [JsonProperty("Image Quality 25 - 100%")]
            public float imageQualityReduction = 75;
            [JsonProperty("Sign Update Cooldown (seconds)")]
            public float signCooldown = 5;
            [JsonProperty("Player Moderated Cooldown (seconds)")]
            public float signModerationCooldown = 300;
            [JsonProperty("Hide signs while being checked")]
            public bool hideSign = true;
            [JsonProperty("Use Temp Loading Image")]
            public bool useTempImage = false;
            [JsonProperty("Temp Loading Image URL:")]
            public string tempModerationImageURL = "https://i.postimg.cc/4NNrqT2x/pngegg-2.png";
            [JsonProperty("Logging Mode Only")]
            public bool loggingMode = false;
            [JsonProperty("Send Player Chat Warnings")]
            public bool chatWarnings = false;
            [JsonProperty("Batch Mode - Disables hiding of signs")]
            public BatchSettings batchSettings = new BatchSettings();
            [JsonProperty("Discord Settings")]
            public DiscordSettings discordSettings = new DiscordSettings();
            [JsonProperty("Moderation API (Free) - Limited Options")]
            public ModerationAPI moderationAPI = new ModerationAPI();
            [JsonProperty("Advance Moderation API (Paid)")]
            public GPTModel gptModel = new GPTModel();
            public string ToJson() => JsonConvert.SerializeObject(this);
            public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
        }
        public class BatchSettings {
            [JsonProperty("Check images in batches (Advance Mode Only)")]
            public bool imagePooling = true;
            [JsonProperty("Batch Image Check Rate (Minutes)")]
            public float imagePoolingRate = 15;
            [JsonProperty("Minimum images to batch check")]
            public float minImagesPooled = 3;
            [JsonProperty("Max checks to bypass minimum images 0 = no bypass")]
            public float maxChecksImagesPooled = 4;
        }
        public class DiscordSettings {
            [JsonProperty("Log to Discord")]
            public bool discordLogging = false;
            [JsonProperty("Log moderated Images to Discord (WARNING THIS MAY SEND NSFW CONTENT TO YOUR DISCORD)")]
            public bool discordImageLogging = false;
            [JsonProperty("Discord Webhook")]
            public string DiscordWebhook = "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
            [JsonProperty("Discord Username")]
            public string DiscordUsername = "Sign Moderator";
            [JsonProperty("Server Name")]
            public string ServerName = "";
            [JsonProperty("Avatar URL")]
            public string AvatarUrl = "https://i.ibb.co/sQ10728/Loading-Pls-Wait2.png";
        }
        public class ModerationAPI {
            [JsonProperty("Enable")]
            public bool enabled = true;
            [JsonProperty("Open AI Token")]
            public string apiToken = "https://openai.com/index/openai-api/";
            [JsonProperty("Cooldown between API Checks (seconds)")]
            public float apiCooldown = 1;
            [JsonProperty("Block images of harassment")]
            public bool harassment = true;
            [JsonProperty("Block images of harassment/threatening")]
            public bool harassmentThreatening = true;
            [JsonProperty("Block images of sexual")]
            public bool sexual = true;
            [JsonProperty("Block images of hate")]
            public bool hate = true;
            [JsonProperty("Block images of hate/threatening")]
            public bool hateThreatening = true;
            [JsonProperty("Block images of illicit")]
            public bool illicit = true;
            [JsonProperty("Block images of illicit/violent")]
            public bool illicitViolent = true;
            [JsonProperty("Block images of self-harm/intent")]
            public bool selfHarmIntent = true;
            [JsonProperty("Block images of self-harm/instructions")]
            public bool selfHarmInstructions = true;
            [JsonProperty("Block images of self-harm")]
            public bool selfHarm = true;
            [JsonProperty("Block images of sexual/minors")]
            public bool sexualMinors = true;
            [JsonProperty("Block images of violence")]
            public bool violence = true;
            [JsonProperty("Block images of violence/graphic")]
            public bool violenceGraphic = true;
        }
        public class GPTModel {
            [JsonProperty("Enable GPT Model (WARNING THIS IS PAID PLEASE READ DOCS)")]
            public bool enabled = false;
            [JsonProperty("Open AI Token")]
            public string apiToken = "https://openai.com/index/openai-api/";
            [JsonProperty("Cooldown between API Checks (seconds)")]
            public float apiCooldown = 1;
            [JsonProperty("Model (Don't change this if you dont know what it is)")]
            public string model = "gpt-4o-mini";
            [JsonProperty("Content to moderate")]
            public string prompt = "Pornography, Hate Speech, Child Exploitation, Racist images signs text or symbols, Words like nigger, symbols which resemble swastikas";
        }
        protected override void LoadDefaultConfig() => config = new Configuration();
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                config = Config.ReadObject<Configuration>();
                if (config == null) {
                    throw new JsonException();
                }
                if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) {
                    LogWarning("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
            }
            catch {
                LogWarning($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
        }
        protected override void SaveConfig() {
            LogWarning($"Configuration changes saved to {Name}.json");
            Config.WriteObject(config, true);
        }
#endregion Configuration
#region Localization
        protected override void LoadDefaultMessages() {
            lang.RegisterMessages(new Dictionary<string, string> {
                ["WarningMessage"]
                = "You cannot post explicit content on signs", ["CooldownMessage"] = "You need to wait {0} before updating the sign."
            }
            , this);
        }
#endregion Localization
#region Classes
        public class SignData {
            public ulong playerId;
            public Signage sign;
            public int textureIndex;
            public uint crc;
            public FileStorage.Type type;
        }
#region API
        public class Categories {
            public bool harassment;
            public bool harassmentthreatening;
            public bool sexual;
            public bool hate;
            public bool hatethreatening;
            public bool illicit;
            public bool illicitviolent;
            public bool selfharmintent;
            public bool selfharminstructions;
            public bool selfharm;
            public bool sexualminors;
            public bool violence;
            public bool violencegraphic;
        }
        public class CategoryScores {
            public double harassment;
            public double harassmentthreatening;
            public double sexual;
            public double hate;
            public double hatethreatening;
            public double illicit;
            public double illicitviolent;
            public double selfharmintent;
            public double selfharminstructions;
            public double selfharm;
            public double sexualminors;
            public double violence;
            public double violencegraphic;
        }
        public class Result {
            public bool flagged;
            public Categories categories;
            public CategoryScores category_scores;
        }
        public class OmniDataRoot {
            public string id;
            public string model;
            public List<Result> results;
        }
        public class Choice {
            public int index;
            public Message message;
            public object logprobs;
            public string finish_reason;
        }
        public class CompletionTokensDetails {
            public int reasoning_tokens;
            public int audio_tokens;
            public int accepted_prediction_tokens;
            public int rejected_prediction_tokens;
        }
        public class Message {
            public string role;
            public string content;
            public object refusal;
        }
        public class PromptTokensDetails {
            public int cached_tokens;
            public int audio_tokens;
        }
        public class GPTRoot {
            public string id;
            public string @object;
            public int created;
            public string model;
            public List<Choice> choices;
            //public Usage usage;
            //public string service_tier;
            //public string system_fingerprint;
        }
        public class Usage {
            public int prompt_tokens;
            public int completion_tokens;
            public int total_tokens;
            public PromptTokensDetails prompt_tokens_details;
            public CompletionTokensDetails completion_tokens_details;
        }
#endregion API
#endregion Classes
#region Hooks
        public class ImageSize {
            public int Width {
                get;
            }
            public int Height {
                get;
            }
            public ImageSize(int width, int height) {
                Width = width;
                Height = height;
            }
        }
        public struct ImageKey : IEquatable<ImageKey> {
            public ulong NetID;
            public int TextureIndex;
            public bool Equals(ImageKey other) {
                return NetID == other.NetID && TextureIndex == other.TextureIndex;
            }
            public override bool Equals(object obj) {
                if (obj is ImageKey other) {
                    return Equals(other);
                }
                return false;
            }
            public override int GetHashCode() {
                return HashCode.Combine(NetID, TextureIndex);
            }
        }
        private ImageCodecInfo _imageCodecInfo = null;
        private EncoderParameters _encoderParams = null;
        private Dictionary<uint, ImageSize> _ImageSizeperAsset = new Dictionary<uint, ImageSize>();
        private void OnServerInitialized() {
            if (CheckPooledImagesRun != null)  // This is only really needed during testing if static instances persist
            {
                ServerMgr.Instance.StopCoroutine(CheckPooledImagesRun);
                CheckPooledImagesRun = null;
            }
            if (CheckImagesRun != null)  // This is only really needed during testing if static instances persist
            {
                ServerMgr.Instance.StopCoroutine(CheckImagesRun);
                CheckImagesRun = null;
            }
            permission.RegisterPermission(permWhitelist, this);
            _stringBuilder = new StringBuilder();
            gptModel = config.gptModel.model;
            prompt = config.gptModel.prompt;
            _ModerationAPIWait = CoroutineEx.waitForSeconds(config.moderationAPI.apiCooldown);
            _GPTModelAPIWait = CoroutineEx.waitForSeconds(config.gptModel.apiCooldown);
            _imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == ImageFormat.Jpeg.Guid);
            _encoderParams = new EncoderParameters(1);
            _encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, (long)config.imageQualityReduction);
            if (config.imageSizeReduction < 25) {
                config.imageSizeReduction = 25;
            }
            if (config.imageQualityReduction < 25) {
                config.imageSizeReduction = 25;
            }
            if (config.useTempImage) {
                _tempModerationImageURL = config.tempModerationImageURL;
            }
            if (GetLoadingImage != null) {
                ServerMgr.Instance.StopCoroutine(GetLoadingImage);
            }
            GetLoadingImage = LoadingImageSetup();
            ServerMgr.Instance.StartCoroutine(GetLoadingImage);
            foreach (var prefab in GameManifest.Current.entities) {
                var gamePrefab = GameManager.server.FindPrefab(prefab.ToLower());
                if (gamePrefab == null) continue;
                Signage sign = gamePrefab.GetComponent<Signage>();
                if (sign != null) {
                    if (sign.paintableSources?.Length < 1) continue;
                    _ImageSizeperAsset[sign.prefabID] = new ImageSize(sign.paintableSources[0].texWidth, sign.paintableSources[0].texHeight);
                    continue;
                }
                PhotoFrame photoFrame = gamePrefab.GetComponent<PhotoFrame>();
                if (photoFrame != null) {
                    _ImageSizeperAsset[photoFrame.prefabID] = new ImageSize(photoFrame.PaintableSource.texWidth, photoFrame.PaintableSource.texHeight);
                    continue;
                }
                NeonSign neonSign = gamePrefab.GetComponent<NeonSign>();
                if (neonSign != null) {
                    _ImageSizeperAsset[neonSign.prefabID] = new ImageSize(neonSign.paintableSources[0].texWidth, neonSign.paintableSources[0].texHeight);
                    continue;
                }
                WantedPoster wantedPoster = gamePrefab.GetComponent<WantedPoster>();
                if (wantedPoster != null) {
                    _ImageSizeperAsset[wantedPoster.prefabID] = new ImageSize(wantedPoster.TextureSize.x, wantedPoster.TextureSize.y);
                    continue;
                }
                CarvablePumpkin carvablePumpkin = gamePrefab.GetComponent<CarvablePumpkin>();
                if (carvablePumpkin != null) {
                    _ImageSizeperAsset[carvablePumpkin.prefabID] = new ImageSize(carvablePumpkin.paintableSources[0].texWidth, carvablePumpkin.paintableSources[0].texHeight);
                    continue;
                }
            }
            if (config.batchSettings.imagePooling) {
                if (!config.gptModel.enabled) {
                    Puts("You require gpt model to use image pooling.Disabling image pooling");
                    config.batchSettings.imagePooling = false;
                }
                else {
                    ServerMgr.Instance.InvokeRepeating(StartPooledImageCheck, 0, config.batchSettings.imagePoolingRate * 60);
                }
            }
        }
        private void Unload() {
            if (ServerMgr.Instance.IsInvoking(StartPooledImageCheck)) {
                ServerMgr.Instance.CancelInvoke(StartPooledImageCheck);
            }
            if (CheckPooledImagesRun != null) {
                ServerMgr.Instance.StopCoroutine(CheckPooledImagesRun);
            }
            if (GetLoadingImage != null) {
                ServerMgr.Instance.StopCoroutine(GetLoadingImage);
            }
            if (CheckImagesRun != null) {
                ServerMgr.Instance.StopCoroutine(CheckImagesRun);
            }
            CheckPooledImagesRun = null;
            GetLoadingImage = null;
            CheckImagesRun = null;
            _signCooldown = null;
            _queuedImages = null;
            _signsQueuedPool = null;
        }
        private void StartPooledImageCheck() {
            if (CheckPooledImagesRun != null) {
                return;
            }
            CheckPooledImagesRun = CheckPooledImages();
            ServerMgr.Instance.StartCoroutine(CheckPooledImagesRun);
        }
        private StringBuilder _formattedTime = new StringBuilder();
        private object? CanUpdateSign(BasePlayer player, Signage sign) {
            if (_signCooldown.TryGetValue(player.userID, out float cooldown) && cooldown > Time.time) {
                TimeSpan timeRemaining = TimeSpan.FromSeconds(cooldown - Time.time);
                _formattedTime.Clear();
                if (timeRemaining.Days > 0) _formattedTime.Append(timeRemaining.Days).Append("d ");
                if (timeRemaining.Hours > 0) _formattedTime.Append(timeRemaining.Hours).Append("h");
                if (timeRemaining.Minutes > 0) _formattedTime.Append(timeRemaining.Minutes).Append("m ");
                _formattedTime.Append(timeRemaining.Seconds).Append("s");
                ChatMessage(player.IPlayer, "CooldownMessage", _formattedTime.ToString());
                return false;
            }
            return null;
        }
        void OnSignUpdated(Signage sign, BasePlayer player, int textureIndex) {
            if (player == null || sign == null) return;
            if (HasPerm(player.UserIDString, permWhitelist)) return;
            if (config.batchSettings.imagePooling) {
                _signsQueuedPool[new ImageKey() {
                    NetID = sign.NetworkID.Value, TextureIndex = textureIndex
                }
                ] = new SignData {
                    playerId = player.userID, sign = sign, textureIndex = textureIndex, crc = sign.GetContentCRCs[textureIndex], type = sign.FileType
                }
                ;
                return;
            }
            _queuedImages.Enqueue(new SignData {
                playerId = player.userID, sign = sign, textureIndex = textureIndex, crc = sign.GetContentCRCs[textureIndex], type = sign.FileType
            }
            );
            if (config.hideSign) {
                SetImageToSign(sign, textureIndex, _tempModerationImageCRC);
            }
            _signCooldown[player.userID] = Time.time + config.signCooldown;
            if (CheckImagesRun == null) {
                CheckImagesRun = CheckImages();
                ServerMgr.Instance.StartCoroutine(CheckImagesRun);
            }
        }
#endregion Hooks
#region Methods
        private void SetImageToSign(Signage sign, int textureIndex, uint crc) {
            sign.textureIDs[textureIndex] = crc;
            sign.SendNetworkUpdateImmediate();
        }
        readonly string jsonline1 = "{\"model\": \"omni-moderation-latest\",\"input\": [ { \"type\": \"image_url\",\"image_url\": {\"url\": \"data:image/jpeg;base64,";
        readonly string jsonline2 = "\"}}]}";
        private static StringBuilder _stringBuilder = new StringBuilder();
        private string CreateJson(string base64Input) {
            _stringBuilder.Clear();
            _stringBuilder.Append(jsonline1);
            _stringBuilder.Append(base64Input);
            _stringBuilder.Append(jsonline2);
            return _stringBuilder.ToString();
        }
        readonly string jsongptline1 = "{\"model\": \"";
        readonly string jsongptline2 = "\",\"store\": false,\"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"Answer with only 'yes' or 'no' if the image contains any of the specified categories:";
        readonly string jsongptline3 = "\"},{\"type\": \"image_url\", \"image_url\": {\"url\": \"data:image/jpeg;base64,";
        readonly string jsongptline4 = "\"}}]}]}";
        private string gptModel = "gpt-4o-mini";
        private string prompt = "Racism, Pornographic material, Hate Speech";
        private string CreateGPTJson(string base64Input) {
            _stringBuilder.Clear();
            _stringBuilder.Append(jsongptline1);
            _stringBuilder.Append(gptModel);
            _stringBuilder.Append(jsongptline2);
            _stringBuilder.Append(prompt);
            _stringBuilder.Append(jsongptline3);
            _stringBuilder.Append(base64Input);
            _stringBuilder.Append(jsongptline4);
            return _stringBuilder.ToString();
        }
        private readonly string _moderationAPI = "https://api.openai.com/v1/moderations";
        private readonly string _gptModelAPI = "https://api.openai.com/v1/chat/completions";
        private static WaitForSeconds _ModerationAPIWait = CoroutineEx.waitForSeconds(5);
        private static WaitForSeconds _GPTModelAPIWait = CoroutineEx.waitForSeconds(5);
        private static IEnumerator CheckImagesRun;
        private IEnumerator CheckImages() {
            for (int i = 0; i < _queuedImages.Count; i++) {
                SignData signData = _queuedImages.Dequeue();
                if (signData.sign == null || signData.sign.GetContentCRCs.Length < 1 || signData.sign.GetContentCRCs[signData.textureIndex] != signData.crc) {
                    continue;
                }
                byte[] array = GetImageBytes(signData);
                if (array == null || array.Length == 0) {
                    continue;
                }
                string Base64 = Convert.ToBase64String(array);
                if (config.moderationAPI.enabled) {
                    yield return CheckModerationAPI(signData, array);
                }
                if (config.gptModel.enabled) {
                    yield return CheckGPTModelAPI(signData, array);
                }
                SetImageToSign(signData.sign, signData.textureIndex, signData.crc);
            }
            CheckImagesRun = null;
        }
        private IEnumerator CheckModerationAPI(SignData signData, byte[] array) {
            UnityWebRequest www = UnityWebRequest.Post(_moderationAPI, CreateJson(Convert.ToBase64String(array)), "application/json");
            www.SetRequestHeader("Authorization", $"Bearer {config.moderationAPI.apiToken}");
            www.timeout = 20;
            yield return www.SendWebRequest();
            if (IsBadResponse(www)) {
                yield return _ModerationAPIWait;
                yield break;
            }
            string jsonResponse = www.downloadHandler.text;
            OmniDataRoot omniData = JsonConvert.DeserializeObject<OmniDataRoot>(jsonResponse);
            if (omniData == null || omniData.results == null || omniData.results.Count < 1) {
                www.Dispose();
                yield break;
            }
            Result result = omniData.results[0];
            if (result.flagged) {
                ReportContent(signData, array);
            }
            www.Dispose();
            yield return false;
        }
        private IEnumerator CheckGPTModelAPI(SignData signData, byte[] array) {
            UnityWebRequest www = UnityWebRequest.Post(_gptModelAPI, CreateGPTJson(Convert.ToBase64String(array)), "application/json");
            www.SetRequestHeader("Authorization", $"Bearer {config.gptModel.apiToken}");
            www.timeout = 20;
            yield return www.SendWebRequest();
            if (IsBadResponse(www)) {
                yield return _GPTModelAPIWait;
                yield break;
            }
            string jsonResponse = www.downloadHandler.text;
            GPTRoot gptData = JsonConvert.DeserializeObject<GPTRoot>(jsonResponse);
            string response = gptData.choices[0]?.message?.content ?? string.Empty;
            if (response.Length > 2) {
                response = response.Substring(0, 2);
            }
            if (gptData != null && !string.Equals(response, "no", StringComparison.OrdinalIgnoreCase)) {
                ReportContent(signData, array);
            }
            www.Dispose();
            yield return false;
        }
        private void ReportContent(SignData signData, byte[] array) {
            if (!config.loggingMode) {
                signData.sign.ClearContent();
                FileStorage.server.Remove(signData.crc, signData.type, signData.sign.NetworkID);
            }
            _signCooldown[signData.playerId] = Time.time + config.signModerationCooldown;
            SendPlayerWarning(signData.playerId);
            if (config.discordSettings.discordLogging) {
                ServerMgr.Instance.StartCoroutine(LogToDiscord(signData, BasePlayer.FindByID(signData.playerId), gptModel, array));
            }
        }
        string badResponse = "Bad Response: ";
        private bool IsBadResponse(UnityWebRequest www) {
            if (www.result != UnityWebRequest.Result.Success) {
                PrintError(string.Join(badResponse, www.error));
                www.Dispose();
                return true;
            }
            return false;
        }
#region Image Pooling
        const string jsongptPoolline1 = "{\"model\": \"";
        const string jsongptPoolline2 = "\",\"store\": false,\"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \"Answer with only 'yes' or 'no' and comma separation per image per image if the images contain any of the specified categories:";
        const string jsongptPoolline3 = "\"},";
        const string jsongptPoolline4 = "{\"type\": \"image_url\", \"image_url\": {\"url\": \"data:image/jpeg;base64,";
        const string jsongptPoolline5 = "\"}},";
        const string jsongptPoolline6 = "]}]}";
        private int _pooledImageChecks = 0;
        private static IEnumerator CheckPooledImagesRun;
        private class PooledSignData {
            public byte[] bytes;
            public SignData sign;
        }
        private IEnumerator CheckPooledImages() {
            if (_signsQueuedPool.Count < config.batchSettings.minImagesPooled) {
                if (config.batchSettings.maxChecksImagesPooled == 0 || _pooledImageChecks < config.batchSettings.maxChecksImagesPooled) {
                    if (_signsQueuedPool.Count > 0) {
                        _pooledImageChecks += 1;
                    }
                    CheckPooledImagesRun = null;
                    yield break;
                }
            }
            _pooledImageChecks = 0;
            List<PooledSignData> checkedSigns = Pool.Get<List<PooledSignData>>();
            _stringBuilder.Clear();
            _stringBuilder.Append(jsongptPoolline1);
            _stringBuilder.Append(gptModel);
            _stringBuilder.Append(jsongptPoolline2);
            _stringBuilder.Append(prompt);
            _stringBuilder.Append(jsongptPoolline3);
            foreach (var sign in _signsQueuedPool) {
                SignData signData = sign.Value;
                byte[] array = GetImageBytes(signData);
                if (array == null || array.Length == 0) {
                    continue;
                }
                string Base64 = Convert.ToBase64String(array);
                _stringBuilder.Append(jsongptPoolline4);
                _stringBuilder.Append(Base64);
                _stringBuilder.Append(jsongptPoolline5);
                checkedSigns.Add(new PooledSignData() {
                    bytes = array, sign = signData
                }
                );
            }
            _stringBuilder.Remove(_stringBuilder.Length - 1, 1);
            //remove last comma
            _stringBuilder.Append(jsongptPoolline6);
            _signsQueuedPool.Clear();
            if (checkedSigns.Count < 1) {
                Pool.FreeUnmanaged(ref checkedSigns);
                CheckPooledImagesRun = null;
                yield break;
            }
            UnityWebRequest www = UnityWebRequest.Post(_gptModelAPI, _stringBuilder.ToString(), "application/json");
            www.SetRequestHeader("Authorization", $"Bearer {config.gptModel.apiToken}");
            www.timeout = 20;
            yield return www.SendWebRequest();
            if (IsBadResponse(www)) {
                Pool.FreeUnmanaged(ref checkedSigns);
                CheckPooledImagesRun = null;
                yield break;
            }
            string jsonResponse = www.downloadHandler.text;
            GPTRoot gptData = JsonConvert.DeserializeObject<GPTRoot>(jsonResponse);
            string response = gptData.choices[0]?.message?.content?.Replace(" ", string.Empty) ?? string.Empty;
            if (gptData == null || string.IsNullOrEmpty(response)) {
                www.Dispose();
                Pool.FreeUnmanaged(ref checkedSigns);
                CheckPooledImagesRun = null;
                yield break;
            }
            string[] responses = response.Split(',');
            for (int i = 0; i < responses.Length; i++) {
                if (gptData == null || !string.Equals(responses[i], "yes", StringComparison.OrdinalIgnoreCase)) continue;
                if (i >= checkedSigns.Count) {
                    Puts($"GPT has more responses than images {checkedSigns.Count} response:{string.Join(", ", responses)}");
                    break;
                }
                SignData signData = checkedSigns[i].sign;
                ReportContent(signData, checkedSigns[i].bytes);
            }
            www.Dispose();
            Pool.FreeUnmanaged(ref checkedSigns);
            CheckPooledImagesRun = null;
        }
#endregion Image Pooling
        private void SendPlayerWarning(ulong playerID) {
            if (!config.chatWarnings) return;
            BasePlayer player = BasePlayer.FindByID(playerID);
            if (player == null) return;
            ChatMessage(player.IPlayer, "WarningMessage");
        }
        public byte[] ResizeImage(byte[] bytes, int width, int height) {
            if (bytes == null || bytes.Length == 0) {
                PrintError("Invalid image byte array.");
                return null;
            }
            if (width <= 0 || height <= 0) {
                PrintError("Width and height must be greater than zero.");
                return null;
            }
            MemoryStream originalStream = Pool.Get<MemoryStream>();
            originalStream.Write(bytes, 0, bytes.Length);
            MemoryStream resizedStream = Pool.Get<MemoryStream>();
            using (var originalImage = Image.FromStream(originalStream)) using (var resizedImage = new Bitmap(width, height)) try {
                using (var graphics = Graphics.FromImage(resizedImage)) {
                    graphics.Clear(System.Drawing.Color.LightGray);
                    graphics.CompositingQuality = CompositingQuality.HighSpeed;
                    graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                    graphics.SmoothingMode = SmoothingMode.None;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
                    graphics.DrawImage(originalImage, 0, 0, width, height);
                    if (_imageCodecInfo == null || _encoderParams == null) {
                        resizedImage.Save(resizedStream, ImageFormat.Jpeg);
                    }
                    else {
                        resizedImage.Save(resizedStream, _imageCodecInfo, _encoderParams);
                    }
                    return resizedStream.ToArray();
                }
            }
            catch (Exception ex) {
                PrintError("Error resizing image: " + ex.Message);
                Pool.FreeUnmanaged(ref originalStream);
                Pool.FreeUnmanaged(ref resizedStream);
                return null;
            }
            finally {
                Pool.FreeUnmanaged(ref originalStream);
                Pool.FreeUnmanaged(ref resizedStream);
            }
        }
        private bool CheckImage(Result omniResult) {
            if (omniResult.categories.harassment && config.moderationAPI.harassment) {
                return true;
            }
            if (omniResult.categories.harassmentthreatening && config.moderationAPI.harassmentThreatening) {
                return true;
            }
            if (omniResult.categories.sexual && config.moderationAPI.sexual) {
                return true;
            }
            if (omniResult.categories.hate && config.moderationAPI.hate) {
                return true;
            }
            if (omniResult.categories.hatethreatening && config.moderationAPI.hateThreatening) {
                return true;
            }
            if (omniResult.categories.illicit && config.moderationAPI.illicit) {
                return true;
            }
            if (omniResult.categories.illicitviolent && config.moderationAPI.illicitViolent) {
                return true;
            }
            if (omniResult.categories.selfharmintent && config.moderationAPI.selfHarmIntent) {
                return true;
            }
            if (omniResult.categories.selfharminstructions && config.moderationAPI.selfHarmInstructions) {
                return true;
            }
            if (omniResult.categories.selfharm && config.moderationAPI.selfHarm) {
                return true;
            }
            if (omniResult.categories.sexualminors && config.moderationAPI.sexualMinors) {
                return true;
            }
            if (omniResult.categories.violence && config.moderationAPI.violence) {
                return true;
            }
            if (omniResult.categories.violencegraphic && config.moderationAPI.violenceGraphic) {
                return true;
            }
            return false;
        }
#endregion Methods
#region Set Up Loading Image
        private uint _tempModerationImageCRC;
        private string _tempModerationImageURL = "https://i.postimg.cc/Zq5qfgtk/10x10-00000000.png";
        private static IEnumerator GetLoadingImage = null;
        private IEnumerator LoadingImageSetup() {
            UnityWebRequest www = UnityWebRequest.Get(_tempModerationImageURL);
            www.timeout = 30;
            yield return www.SendWebRequest();
            if (www.result != UnityWebRequest.Result.Success) {
                PrintError(www.error + " Cannot get image from:" + _tempModerationImageURL);
                www.Dispose();
                yield break;
            }
            Texture2D texture = new Texture2D(2, 2);
            texture.LoadImage(www.downloadHandler.data);
            if (texture != null) {
                byte[] bytes = texture.EncodeToPNG();
                if (bytes.Length > ConVar.Server.maxpacketsize_command) {
                    float percentage = Mathf.Sqrt(((float)ConVar.Server.maxpacketsize_command / (float)texture.GetSizeInBytes()));
                    bytes = ResizeImage(bytes, (int)(texture.width * percentage), (int)(texture.height * percentage));
                }
                UnityEngine.Object.DestroyImmediate(texture);
                if (bytes != null) {
                    _tempModerationImageCRC = FileStorage.server.Store(bytes, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
                }
            }
            www.Dispose();
            GetLoadingImage = null;
        }
#endregion Set Up Loading Image
#region Discord Logging
        private IEnumerator LogToDiscord(SignData signData, BasePlayer player, string model, byte[] imageBytes) {
            var msg = CreateDiscordMessage(player.displayName, player.UserIDString, signData.sign.ShortPrefabName, signData.sign.transform.position, model, signData.crc);
            List<IMultipartFormSection> formData = new List<IMultipartFormSection> {
                new MultipartFormDataSection("payload_json", JsonConvert.SerializeObject(msg))
            }
            ;
            if (config.discordSettings.discordImageLogging) formData.Add(new MultipartFormFileSection("file1", imageBytes, $"{signData.crc}.png", "image/png"));
            UnityWebRequest wwwpost = UnityWebRequest.Post(config.discordSettings.DiscordWebhook, formData);
            yield return wwwpost.SendWebRequest();
            if (wwwpost.result != UnityWebRequest.Result.Success) {
                PrintError("Cannot post log to discord:" + wwwpost.error);
                wwwpost.Dispose();
                yield break;
            }
            wwwpost.Dispose();
        }
        private DiscordMessage CreateDiscordMessage(string playername, string userid, string itemname, Vector3 location, string model, uint crc) {
            string steamprofile = "https://steamcommunity.com/profiles/" + userid;
            var fields = new List<DiscordMessage.Fields>() {
                new DiscordMessage.Fields("Player: " + playername, $"[{userid}]({steamprofile})", true), new DiscordMessage.Fields("Entity", itemname, true), new DiscordMessage.Fields("AI Model", model, false), new DiscordMessage.Fields("Teleport position", $"```teleportpos {location}```", false)
            }
            ;
            var footer = new DiscordMessage.Footer($"Logged @{DateTime.UtcNow:dd/MM/yy HH:mm:ss}");
            DiscordMessage.Image image = new DiscordMessage.Image($"attachment://{crc}.png");
            var embeds = new List<DiscordMessage.Embeds>() {
                new DiscordMessage.Embeds("Server - " + (string.IsNullOrEmpty(config.discordSettings.ServerName) ? server.Name : config.discordSettings.ServerName), "A sign has been moderated" , fields, footer, image)
            }
            ;
            DiscordMessage msg = new DiscordMessage(config.discordSettings.DiscordUsername, config.discordSettings.AvatarUrl, embeds);
            return msg;
        }
#region Discord Class
        public class DiscordMessage {
            public string username {
                get;
                set;
            }
            public string avatar_url {
                get;
                set;
            }
            public List<Embeds> embeds {
                get;
                set;
            }
            public class Fields {
                public string name {
                    get;
                    set;
                }
                public string value {
                    get;
                    set;
                }
                public bool inline {
                    get;
                    set;
                }
                public Fields(string name, string value, bool inline) {
                    this.name = name;
                    this.value = value;
                    this.inline = inline;
                }
            }
            public class Footer {
                public string text {
                    get;
                    set;
                }
                public Footer(string text) {
                    this.text = text;
                }
            }
            public class Image {
                public string url {
                    get;
                    set;
                }
                public Image(string url) {
                    this.url = url;
                }
            }
            public class Embeds {
                public string title {
                    get;
                    set;
                }
                public string description {
                    get;
                    set;
                }
                public Image image {
                    get;
                    set;
                }
                public List<Fields> fields {
                    get;
                    set;
                }
                public Footer footer {
                    get;
                    set;
                }
                public Embeds(string title, string description, List<Fields> fields, Footer footer, Image image) {
                    this.title = title;
                    this.description = description;
                    this.image = image;
                    this.fields = fields;
                    this.footer = footer;
                }
            }
            public DiscordMessage(string username, string avatar_url, List<Embeds> embeds) {
                this.username = username;
                this.avatar_url = avatar_url;
                this.embeds = embeds;
            }
        }
#endregion
#endregion Discord Logging
#region Helpers
        private bool HasPerm(string id, string perm) => permission.UserHasPermission(id, perm);
        private string GetLang(string langKey, string playerId = null, params object[] args) => string.Format(lang.GetMessage(langKey, this, playerId), args);
        private void ChatMessage(IPlayer player, string langKey, params object[] args) {
            if (player.IsConnected) player.Message(GetLang(langKey, player.Id, args));
        }
        private byte[] GetImageBytes(SignData signData) {
            byte[] array = FileStorage.server.Get(signData.crc, signData.type, signData.sign.NetworkID);
            if (array == null) {
                //Puts($"Cannot get image from sign crc:{signData.sign.GetContentCRCs[signData.textureIndex]} netID:{signData.sign.NetworkID} entity:{signData.sign.ShortPrefabName}");
                return System.Array.Empty<byte>();
            }
            if (_ImageSizeperAsset.TryGetValue(signData.sign.prefabID, out ImageSize imageSize)) {
                float sizeReduction = (config.imageSizeReduction) / 100;
                array = ResizeImage(array, (int)(imageSize.Width * sizeReduction), (int)(imageSize.Height * sizeReduction));
                if (array == null) {
                    //Puts($"Cannot get image from sign crc:{signData.sign.GetContentCRCs[signData.textureIndex]} netID:{signData.sign.NetworkID} entity:{signData.sign.ShortPrefabName}");
                    return System.Array.Empty<byte>();
                }
            }
            return array;
        }
#endregion Helpers
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху