C#:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Libraries;
namespace Oxide.Plugins {
[Info("Death Feed Free", "Goo_", "1.0.0")]
[Description("Free AI death commentary using Hugging Face Inference Providers")]
public class DeathFeedFree : RustPlugin {
private const string Version = "1.0.0";
private const string ApiUrl = "https://router.huggingface.co/v1/chat/completions";
private static readonly Regex RichTextRegex = new Regex(@"<[^>]+>", RegexOptions.Compiled);
private static readonly Regex WhitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled);
private Configuration _config;
private Dictionary<string, string> _headers;
private float _lastRequestTime;
#region Configuration
private class Configuration {
[JsonProperty("Config Version")]
public string ConfigVersion {
get;
set;
}
= "";
[JsonProperty("Enabled")]
public bool Enabled {
get;
set;
}
= true;
[JsonProperty("API Token (Hugging Face)")]
public string ApiToken {
get;
set;
}
= "";
[JsonProperty("Model")]
public string Model {
get;
set;
}
= "meta-llama/Llama-3.3-70B-Instruct";
[JsonProperty("Provider (auto/fastest/cheapest/groq/together/cerebras)")]
public string Provider {
get;
set;
}
= "auto";
[JsonProperty("System Prompt")]
public string SystemPrompt {
get;
set;
}
= "You are a witty sports commentator providing brief, entertaining commentary on deaths in a Rust game server. Keep responses to 1-2 sentences, be funny but not offensive. No slurs or excessive profanity.";
[JsonProperty("Max Tokens")]
public int MaxTokens {
get;
set;
}
= 64;
[JsonProperty("Temperature")]
public float Temperature {
get;
set;
}
= 0.8f;
[JsonProperty("Cooldown Seconds")]
public float CooldownSeconds {
get;
set;
}
= 5f;
[JsonProperty("Response Prefix")]
public string ResponsePrefix {
get;
set;
}
= "[Commentary]";
[JsonProperty("Response Color")]
public string ResponseColor {
get;
set;
}
= "#FFD700";
[JsonProperty("Use BetterChat Integration")]
public bool UseBetterChat {
get;
set;
}
= true;
[JsonProperty("Bot Name")]
public string BotName {
get;
set;
}
= "Death Feed";
[JsonProperty("Bot Steam ID (0 = server)")]
public ulong BotSteamId {
get;
set;
}
= 0;
[JsonProperty("Chat Channel (0 = Global, 1 = Team)")]
public int ChatChannel {
get;
set;
}
= 0;
[JsonProperty("Play Sound")]
public bool PlaySound {
get;
set;
}
= false;
[JsonProperty("Sound Prefab")]
public string SoundPrefab {
get;
set;
}
= "assets/prefabs/misc/easter/painted eggs/effects/egg_upgrade.prefab";
[JsonProperty("Debug Mode")]
public bool DebugMode {
get;
set;
}
= false;
}
protected override void LoadDefaultConfig() {
_config = new Configuration {
ConfigVersion = Version
}
;
}
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<Configuration>() ?? new Configuration();
ValidateAndMigrate();
SaveConfig();
}
catch {
LoadDefaultConfig();
}
RebuildHeaders();
}
protected override void SaveConfig() => Config.WriteObject(_config);
private void ValidateAndMigrate() {
_config.CooldownSeconds = Math.Max(0f, _config.CooldownSeconds);
_config.MaxTokens = Math.Max(1, Math.Min(256, _config.MaxTokens));
_config.Temperature = Math.Max(0f, Math.Min(2f, _config.Temperature));
_config.ChatChannel = Math.Max(0, Math.Min(1, _config.ChatChannel));
if (string.IsNullOrEmpty(_config.Model)) _config.Model = "meta-llama/Llama-3.3-70B-Instruct";
if (string.IsNullOrEmpty(_config.Provider)) _config.Provider = "auto";
if (_config.ConfigVersion != Version) {
var old = string.IsNullOrEmpty(_config.ConfigVersion) ? "0.0.0" : _config.ConfigVersion;
_config.ConfigVersion = Version;
Puts($"Config migrated: {old} -> {Version}");
}
}
private void RebuildHeaders() {
_headers = new Dictionary<string, string> {
["Authorization"]
= $"Bearer {_config.ApiToken}", ["Content-Type"] = "application/json"
}
;
}
#endregion
#region Hooks
private void Init() {
if (!_config.Enabled) {
Puts("Plugin disabled in config.");
return;
}
if (string.IsNullOrEmpty(_config.ApiToken)) PrintWarning("No API token configured. Get one from https://hf.co/settings/tokens");
}
private void OnServerInitialized() {
if (!_config.Enabled) return;
if (plugins.Find("DeathNotes") == null) {
PrintError("══════ MISSING DEPENDENCY ══════");
PrintError("DeathNotes plugin is REQUIRED but not installed.");
PrintError("Download: https://umod.org/plugins/death-notes");
PrintError("This plugin will NOT function without DeathNotes.");
}
else {
Puts("Ready. DeathNotes detected.");
}
}
private object OnDeathNotice(Dictionary<string, object> data, string message) {
if (!_config.Enabled || string.IsNullOrEmpty(_config.ApiToken) || string.IsNullOrEmpty(message)) return null;
var now = UnityEngine.Time.realtimeSinceStartup;
if (_config.CooldownSeconds > 0 && now - _lastRequestTime < _config.CooldownSeconds) return null;
var text = StripTags(message);
if (string.IsNullOrEmpty(text)) return null;
if (_config.DebugMode) Puts($"[Debug] Death: {text}");
SendRequest(text);
return null;
}
#endregion
#region API
private void SendRequest(string deathMessage) {
var model = BuildModelString();
var payload = JsonConvert.SerializeObject(new {
model, messages = new[] {
new {
role = "system", content = _config.SystemPrompt
}
, new {
role = "user", content = deathMessage
}
}
, max_tokens = _config.MaxTokens, temperature = _config.Temperature
}
);
if (_config.DebugMode) Puts($"[Debug] Request: {model}");
webrequest.Enqueue(ApiUrl, payload, OnResponse, this, RequestMethod.POST, _headers);
}
private string BuildModelString() {
var provider = _config.Provider?.ToLowerInvariant()?.Trim();
if (string.IsNullOrEmpty(provider) || provider == "auto") return _config.Model;
return $"{_config.Model}:{provider}";
}
private void OnResponse(int code, string response) {
if (_config.DebugMode) Puts($"[Debug] Response: {code}");
if (code != 200) {
if (code == 401) PrintError("Auth failed. Check API token.");
else if (_config.DebugMode) Puts($"[Debug] Error {code}: {response}");
return;
}
try {
var json = JObject.Parse(response);
var content = json["choices"]?[0]?["message"]?["content"]?.ToString()?.Trim();
if (string.IsNullOrEmpty(content)) return;
_lastRequestTime = UnityEngine.Time.realtimeSinceStartup;
if (_config.DebugMode) Puts($"[Debug] Commentary: {content}");
Broadcast(content);
if (_config.PlaySound && !string.IsNullOrEmpty(_config.SoundPrefab)) PlaySound();
}
catch (Exception ex) {
PrintError($"Parse error: {ex.Message}");
}
}
#endregion
#region Output
private void Broadcast(string commentary) {
var color = string.IsNullOrEmpty(_config.ResponseColor) ? "#FFD700" : _config.ResponseColor;
var prefix = string.IsNullOrEmpty(_config.ResponsePrefix) ? "" : $"{_config.ResponsePrefix} ";
var formatted = $"<color={color}>{prefix}{commentary}</color>";
if (_config.UseBetterChat) {
var msg = string.IsNullOrEmpty(_config.BotName) ? formatted : $"<color={color}>{_config.BotName}</color>: {formatted}";
var connections = Facepunch.Pool.GetList<Network.Connection>();
try {
foreach (var p in BasePlayer.activePlayerList) if (p?.net?.connection != null) connections.Add(p.net.connection);
if (connections.Count > 0) ConsoleNetwork.SendClientCommand(connections, "chat.add", _config.ChatChannel, _config.BotSteamId.ToString(), msg);
}
finally {
Facepunch.Pool.FreeList(ref connections);
}
Puts($"[Chat] {_config.BotName ?? "DeathFeed"}: {StripTags(commentary)}");
}
else {
foreach (var p in BasePlayer.activePlayerList) p?.ChatMessage(formatted);
}
}
private void PlaySound() {
foreach (var p in BasePlayer.activePlayerList) if (p != null) Effect.server.Run(_config.SoundPrefab, p.transform.position, UnityEngine.Vector3.zero, null, false);
}
#endregion
#region Helpers
private string StripTags(string text) {
if (string.IsNullOrEmpty(text)) return text;
return WhitespaceRegex.Replace(RichTextRegex.Replace(text, ""), " ").Trim();
}
#endregion
#region Commands
[ConsoleCommand("deathfeed.test")]
private void CmdTest(ConsoleSystem.Arg arg) {
if (!arg.IsAdmin) return;
if (string.IsNullOrEmpty(_config.ApiToken)) {
arg.ReplyWith("No API token configured.");
return;
}
arg.ReplyWith("Testing API...");
SendRequest("TestPlayer was killed by AnotherPlayer with AK47 from 150m");
}
[ConsoleCommand("deathfeed.status")]
private void CmdStatus(ConsoleSystem.Arg arg) {
if (!arg.IsAdmin) return;
arg.ReplyWith($@"=== Death Feed Free v{Version} ===
Config: {_config.ConfigVersion}
Enabled: {_config.Enabled}
Token: {(string.IsNullOrEmpty(_config.ApiToken) ? "NOT SET" : "Set")}
Model: {_config.Model}
Provider: {_config.Provider}
Cooldown: {_config.CooldownSeconds}s
BetterChat: {_config.UseBetterChat}
DeathNotes: {(plugins.Find("DeathNotes") != null ? "Loaded" : "NOT FOUND")}");
}
#endregion
}
}