C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Oxide.Core.Libraries.Covalence;
using System.Collections.Generic;
using System;
using ConVar;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Linq;
namespace Oxide.Plugins
{
[Info("Chat to Discord Relay", "Psystec", "1.1.0")]
[Description("Relay chat to Discord")]
public class ChatToDiscord : CovalencePlugin
{
Dictionary<string, SteamPlayerInfo> _steamPlayerInfoCache = new Dictionary<string, SteamPlayerInfo>();
public const string AdminPermission = "chattodiscord.admin";
#region Configuration
private Configuration _configuration;
private class Configuration
{
public string SteamApiKey { get; set; } = "https://steamcommunity.com/dev/apikey";
public string GlobalChatWebhook { get; set; } = "";
public string TeamChatWebhook { get; set; } = "";
public string ConnectionWebhook { get; set; } = "";
public bool AllowMentions { get; set; } = false;
public bool AllowSpecialCharacters { get; set; } = true;
public string GlobalChatFormat { get; set; } = "[{time}] [**GLOBAL**] **{username}**: `{message}`";
public string TeamChatFormat { get; set; } = "[{time}] [**TEAM**] **{username}**: `{message}`";
public string ConnectionFormat { get; set; } = "[{time}] **{username}**: {connectionstatus}";
public string DateFormat { get; set; } = "yyyy-MM-dd HH:mm:ss";
public string[] FilterBadWords { get; set; } = new string[] { "badword1" };
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Connected"] = "Connected.",
["Disconnected"] = "Disconnected.",
["NoPermission"] = "You do not have permission to use this command.",
["FileLoaded"] = "File loaded.",
["cmdCommand"] = "COMMAND",
["cmdDescription"] = "DESCRIPTION",
["cmdReload"] = "Reads the config file."
}, this);
}
protected override void SaveConfig() => Config.WriteObject(_configuration);
private void LoadNewConfig() => _configuration = Config.ReadObject<Configuration>();
protected override void LoadDefaultConfig() => _configuration = new Configuration();
protected override void LoadConfig()
{
base.LoadConfig();
_configuration = Config.ReadObject<Configuration>();
}
#endregion Configuration
#region Hooks
private void Init()
{
permission.RegisterPermission(AdminPermission, this);
}
private void Loaded()
{
CheckSubscribsions();
}
#endregion Hooks
#region User Connection Hooks
private void OnUserConnected(IPlayer player)
{
string message = _configuration.ConnectionFormat.Replace("{time}", DateTime.Now.ToString(_configuration.DateFormat)).Replace("{username}", player.Name).Replace("{userid}", player.Id).Replace("{connectionstatus}", Lang("Connected"));
SendToDiscord(_configuration.ConnectionWebhook, player.Name, player.Id, message);
}
private void OnUserDisconnected(IPlayer player)
{
string message = _configuration.ConnectionFormat.Replace("{time}", DateTime.Now.ToString(_configuration.DateFormat)).Replace("{username}", player.Name).Replace("{userid}", player.Id).Replace("{connectionstatus}", Lang("Disconnected"));
SendToDiscord(_configuration.ConnectionWebhook, player.Name, player.Id, message);
}
#endregion User Connection Hooks
#region User Chat Hooks
private void OnPlayerChat(BasePlayer player, string message, Chat.ChatChannel channel)
{
//Allow Metions
if (!_configuration.AllowMentions)
{
message = message.Replace("@here", "@.here").Replace("@everyone", "@.everyone");
}
//Allow Special Characters
if (!_configuration.AllowSpecialCharacters)
{
message = RemoveSpecialCharacters(message);
}
switch (channel)
{
case Chat.ChatChannel.Global:
if (!string.IsNullOrEmpty(_configuration.GlobalChatWebhook))
{
message = _configuration.GlobalChatFormat.Replace("{time}", DateTime.Now.ToString(_configuration.DateFormat)).Replace("{username}", player.displayName).Replace("{userid}", player.UserIDString).Replace("{message}", message);
SendToDiscord(_configuration.GlobalChatWebhook, player.displayName, player.UserIDString, message);
}
break;
case Chat.ChatChannel.Team:
if (!string.IsNullOrEmpty(_configuration.TeamChatWebhook))
{
message = _configuration.TeamChatFormat.Replace("{time}", DateTime.Now.ToString(_configuration.DateFormat)).Replace("{username}", player.displayName).Replace("{userid}", player.UserIDString).Replace("{message}", message);
SendToDiscord(_configuration.TeamChatWebhook, player.displayName, player.UserIDString, message);
}
break;
}
}
#endregion User Chat Hooks
#region Commands
[Command("chattodiscord")]
private void ChatToDiscordCommands(IPlayer player, string command, string[] args)
{
if (player == null)
return;
if (!HasPermission(player, AdminPermission))
return;
if (args.IsNullOrEmpty())
{
player.Reply(Lang("cmdCommand").PadRight(30) + Lang("cmdDescription"));
player.Reply(("chattodiscord loadconfig").PadRight(30) + Lang("cmdReload"));
return;
}
if (args[0] == "loadconfig")
{
player.Reply(Lang("FileLoaded"));
LoadNewConfig();
Loaded();
}
}
#endregion Commands
#region Helpers
private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private bool HasPermission(IPlayer player, string permission)
{
if (!player.HasPermission(permission))
{
player.Reply(Lang("NoPermission"));
PrintWarning("UserID: " + player.Id + " | UserName: " + player.Name + " | " + Lang("NoPermission"));
return false;
}
return true;
}
public static string RemoveSpecialCharacters(string message)
{
string pattern = "[^a-zA-Z0-9._@\\[\\] ]";
// Replace matched characters (those NOT allowed) with an empty string
string cleanedMessage = Regex.Replace(message, pattern, "");
return cleanedMessage;
}
private void SendToDiscord(string Webhook, string PlayerName, string PlayerID, string message)
{
if (_configuration.FilterBadWords.Any(word => message.IndexOf(word, StringComparison.OrdinalIgnoreCase) >= 0))
{
PrintWarning($"Blocked message from {PlayerName} ({PlayerID}): {message}");
return;
}
string steamUrl = $"http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={_configuration.SteamApiKey}&steamids={PlayerID}";
string defaultImage = "https://images.sftcdn.net/images/t_app-logo-l,f_auto,dpr_auto/p/e8326516-9b2c-11e6-9634-00163ec9f5fa/3905415571/rust-logo.png";
//string defaultImage = "https://files.facepunch.com/lewis/1b2911b1/rust-marque.svg";
Dictionary<string, string> headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" }
};
if (_steamPlayerInfoCache.ContainsKey(PlayerID))
{
//User cached Data from SteamPlayerInfo
foreach (Player p in _steamPlayerInfoCache[PlayerID].response.players)
{
defaultImage = p.avatarfull;
DiscordMessage dm = new DiscordMessage();
dm.username = PlayerName;
dm.avatar_url = defaultImage;
dm.content = message;
string payload = JsonConvert.SerializeObject(dm);
webrequest.Enqueue(Webhook, payload, (dcode, dresponse) =>
{
if (dcode != 200 && dcode != 204)
{
if (dresponse == null)
{
PrintWarning($"Discord didn't respond (down?) Code: {dcode}");
}
}
}, this, Core.Libraries.RequestMethod.POST, headers);
}
}
else
{
webrequest.Enqueue(steamUrl, null, (code, response) =>
{
if (code != 200)
{
PrintWarning($"ERROR: {response}");
PrintWarning($"steamUrl: {steamUrl}");
PrintWarning($"code: {code}");
return;
}
SteamPlayerInfo steamPlayers = JsonConvert.DeserializeObject<SteamPlayerInfo>(response);
_steamPlayerInfoCache.Add(PlayerID, steamPlayers); //Cache SteamPlayerInfo Data from Player
foreach (Player p in steamPlayers.response.players)
{
defaultImage = p.avatarfull;
DiscordMessage dm = new DiscordMessage();
dm.username = PlayerName;
dm.avatar_url = defaultImage;
dm.content = message;
string payload = JsonConvert.SerializeObject(dm);
webrequest.Enqueue(Webhook, payload, (dcode, dresponse) =>
{
if (dcode != 200 && dcode != 204)
{
if (dresponse == null)
{
PrintWarning($"Discord didn't respond (down?) Code: {dcode}");
}
}
}, this, Core.Libraries.RequestMethod.POST, headers);
}
}, this, Core.Libraries.RequestMethod.GET);
}
}
private void CheckSubscribsions()
{
if (string.IsNullOrEmpty(_configuration.GlobalChatWebhook) && string.IsNullOrEmpty(_configuration.TeamChatWebhook))
Unsubscribe(nameof(OnPlayerChat));
else
Subscribe(nameof(OnPlayerChat));
if (string.IsNullOrEmpty(_configuration.ConnectionWebhook))
{
Unsubscribe(nameof(OnUserConnected));
Unsubscribe(nameof(OnUserDisconnected));
}
else
{
Subscribe(nameof(OnUserConnected));
Subscribe(nameof(OnUserDisconnected));
}
}
#endregion Helpers
#region Classes
#region Discord Messages
public class DiscordMessage
{
/// <summary>
/// if used, it overrides the default username of the webhook
/// </summary>
public string username { get; set; }
/// <summary>
/// if used, it overrides the default avatar of the webhook
/// </summary>
public string avatar_url { get; set; }
/// <summary>
/// simple message, the message contains (up to 2000 characters)
/// </summary>
public string content { get; set; }
/// <summary>
/// array of embed objects.That means, you can use more than one in the same body
/// </summary>
public Embed[] embeds { get; set; }
}
public class Embed
{
/// <summary>
/// embed author object
/// </summary>
public Author author { get; set; }
/// <summary>
/// title of embed
/// </summary>
public string title { get; set; }
/// <summary>
/// url of embed.If title was used, it becomes hyperlink
/// </summary>
public string url { get; set; }
/// <summary>
/// description text
/// </summary>
public string description { get; set; }
/// <summary>
/// color code of the embed.You have to use Decimal numeral system, not Hexadecimal.Use color picker and converter: https://htmlcolorcodes.com/color-picker/ and https://www.binaryhexconverter.com/hex-to-decimal-converter
/// </summary>
public int color { get; set; }
/// <summary>
/// rray of embed field objects
/// </summary>
public Field[] fields { get; set; }
/// <summary>
/// embed thumbnail object
/// </summary>
public Thumbnail thumbnail { get; set; }
/// <summary>
/// embed image object
/// </summary>
public Image image { get; set; }
/// <summary>
/// embed footer object
/// </summary>
public Footer footer { get; set; }
}
public class Author
{
/// <summary>
/// name of author
/// </summary>
public string name { get; set; }
/// <summary>
/// url of author.If name was used, it becomes a hyperlink
/// </summary>
public string url { get; set; }
/// <summary>
/// url of author icon
/// </summary>
public string icon_url { get; set; }
}
public class Thumbnail
{
/// <summary>
/// url of thumbnail
/// </summary>
public string url { get; set; }
}
public class Image
{
/// <summary>
/// url of image
/// </summary>
public string url { get; set; }
}
public class Footer
{
/// <summary>
/// footer text, doesn't support Markdown
/// </summary>
public string text { get; set; }
/// <summary>
/// url of footer icon
/// </summary>
public string icon_url { get; set; }
}
public class Field
{
/// <summary>
/// name of the field
/// </summary>
public string name { get; set; }
/// <summary>
/// alue of the field
/// </summary>
public string value { get; set; }
/// <summary>
/// if true, fields will be displayed in same line, but there can only be 3 max in same line or 2 max if you used thumbnail
/// </summary>
public bool inline { get; set; }
}
#endregion
#region Steam Player Info
public class SteamPlayerInfo
{
public Response response { get; set; }
}
public class Response
{
public Player[] players { get; set; }
}
public class Player
{
public string steamid { get; set; }
public int communityvisibilitystate { get; set; }
public int profilestate { get; set; }
public string personaname { get; set; }
public string profileurl { get; set; }
public string avatar { get; set; }
public string avatarmedium { get; set; }
public string avatarfull { get; set; }
public string avatarhash { get; set; }
public int lastlogoff { get; set; }
public int personastate { get; set; }
public string primaryclanid { get; set; }
public int timecreated { get; set; }
public int personastateflags { get; set; }
public string gameserverip { get; set; }
public string gameserversteamid { get; set; }
public string gameextrainfo { get; set; }
public string gameid { get; set; }
public string loccountrycode { get; set; }
}
#endregion
#endregion Classes
}
}