C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
namespace Oxide.Plugins
{
[Info("Slack", "Wulf/lukespragg", "0.1.5", ResourceId = 1952)]
[Description("Plugin API for sending messages and notifications to Slack")]
class Slack : CovalencePlugin
{
// Do NOT edit this file, instead edit Slack.json in oxide/config
#region Initialization
static readonly DateTime Epoch = new DateTime(1970, 1, 1);
static readonly Regex Regex = new Regex(@"<avatarIcon><!\[CDATA\[(.*)\]\]></avatarIcon>");
void Init() => LoadDefaultConfig();
#endregion
#region Configuration
string botName;
string hookUrl;
string iconUrl;
bool linkNames;
bool serverInfo;
protected override void LoadDefaultConfig()
{
Config["BotName"] = botName = GetConfig("BotName", "Oxide");
Config["HookUrl"] = hookUrl = GetConfig("HookUrl", "");
Config["IconUrl"] = iconUrl = GetConfig("IconUrl", "");
Config["LinkNames"] = linkNames = GetConfig("LinkNames", true);
Config["ServerInfo"] = serverInfo = GetConfig("ServerInfo", true);
SaveConfig();
}
#endregion
#region Basic Message API
/// <summary>
/// Sends a basic message to Slack using configured settings
/// </summary>
/// <param name="message"></param>
/// <param name="channel"></param>
/// <param name="callback"></param>
void Message(string message, string channel = null, Action<bool> callback = null)
{
if (!ErrorChecks(message, false)) return;
var payload = new SlackMessage
{
channel = channel,
icon_url = iconUrl,
link_names = linkNames ? "1" : "0",
mrkdwn = "true",
username = botName,
text = message
};
PostToSlack(payload, callback);
}
#endregion
#region Player Message API
/// <summary>
/// Sends a fancy message to Slack with linked profile, in-game avatar, and server information
/// </summary>
/// <param name="message"></param>
/// <param name="player"></param>
/// <param name="channel"></param>
/// <param name="callback"></param>
void FancyMessage(string message, IPlayer player, string channel = null, Action<bool> callback = null)
{
if (!ErrorChecks(message, true, player)) return;
webrequest.EnqueueGet($"http://steamcommunity.com/profiles/{player.Id}?xml=1", (code, response) =>
{
string avatar = null;
if (response != null && code == 200) avatar = Regex.Match(response).Groups[1].ToString();
var payload = new SlackMessage
{
attachments = new List<SlackAttachment>
{
new SlackAttachment
{
color = "#E68C17", // TODO: Config option
author_icon = avatar,
author_name = $"{player.Name} ({player.Id})",
author_link = $"http://steamcommunity.com/profiles/{player.Id}",
fallback = $"{player.Name}: {message}",
footer = serverInfo ? $"{server.Name}" : null, // ({server.Address})
text = message
}
},
channel = channel,
icon_url = iconUrl,
username = botName
};
PostToSlack(payload, callback);
}, this);
}
/// <summary>
/// Sends a simple message to Slack as a fake user with in-game avatar
/// </summary>
/// <param name="message"></param>
/// <param name="player"></param>
/// <param name="channel"></param>
/// <param name="callback"></param>
void SimpleMessage(string message, IPlayer player, string channel = null, Action<bool> callback = null)
{
if (!ErrorChecks(message, true, player)) return;
webrequest.EnqueueGet($"http://steamcommunity.com/profiles/{player.Id}?xml=1", (code, response) =>
{
string avatar = null;
if (response != null && code == 200) avatar = Regex.Match(response).Groups[1].ToString();
var payload = new SlackMessage
{
channel = channel,
icon_url = avatar,
link_names = linkNames ? "1" : "0",
mrkdwn = "true",
username = serverInfo ? $"{player.Name} @ {(server.Name).Truncate(20)}" : player.Name,
text = message
};
PostToSlack(payload, callback);
}, this);
}
#endregion
#region Ticket Message API
/// <summary>
/// Sends a ticket-styled message to Slack with reporter information and position
/// </summary>
/// <param name="message"></param>
/// <param name="player"></param>
/// <param name="channel"></param>
/// <param name="callback"></param>
void TicketMessage(string message, IPlayer player, string channel = null, Action<bool> callback = null)
{
if (!ErrorChecks(message, true, player)) return;
webrequest.EnqueueGet($"http://steamcommunity.com/profiles/{player.Id}?xml=1", (code, response) =>
{
string avatar = null;
if (response != null && code == 200) avatar = Regex.Match(response).Groups[1].ToString();
var position = player.Character.Position();
var payload = new SlackMessage
{
attachments = new List<SlackAttachment>
{
new SlackAttachment
{
color = "#E68C17", // TODO: Config option
author_name = $"{player.Name} ({player.Id})",
author_link = $"http://steamcommunity.com/profiles/{player.Id}",
fallback = $"{player.Name}: {message}",
fields = new List<SlackAttachmentField>
{
new SlackAttachmentField
{
value = $"X: {position.X}, Y: {position.Y}, Y: {position.Z}",
@short = "true"
}
},
footer = serverInfo ? $"{server.Name}" : null, // ({server.Address})
footer_icon = "http://i.imgur.com/AIF29RT.png",
text = message,
thumb_url = avatar,
ts = DateTime.UtcNow.Subtract(Epoch).TotalSeconds.ToString()
}
},
channel = channel,
icon_url = iconUrl,
username = botName
};
PostToSlack(payload, callback);
}, this);
}
#endregion
#region Slack API
/// <summary>
/// Posts SlackMessage payload to configured webhook service URL
/// </summary>
/// <param name="payload"></param>
/// <param name="callback"></param>
void PostToSlack(SlackMessage payload, Action<bool> callback)
{
if (payload == null || string.IsNullOrEmpty(hookUrl)) return;
webrequest.EnqueuePost(hookUrl, JsonConvert.SerializeObject(payload), (code, response) =>
{
if (code != 200 || response == null) PrintWarning($"Slack API responded with: {response} ({code})");
callback?.Invoke(code == 200 || response != null);
}, this);
}
/// <summary>
/// Required message base class for sending messages to Slack
/// Reference: https://api.slack.com/methods/chat.postMessage
/// </summary>
class SlackMessage
{
public string channel { get; set; } // Channel, private group, or IM channel to send the message to
public string icon_url { get; set; } // URL to an image to use as the icon for the message
public string link_names { get; set; } // Pass true to find and link channel names and usernames
public string mrkdwn { get; set; } // Pass true to allow Markdown formatting in the message
public string text { get; set; } // Optional text of the message to send
public string username { get; set; } // Name of the bot sending the message
public List<SlackAttachment> attachments; // Messages can have zero or more attachments
}
/// <summary>
/// Optional attachment class for use with SlackMessage to send attachment messages
/// Reference: https://api.slack.com/docs/attachments
/// </summary>
class SlackAttachment
{
public string author_icon { get; set; } // URL for a small 16px by 16px image beside the author_name text
public string author_link { get; set; } // URL that will hyperlink the author_name text
public string author_name { get; set; } // Small text used to display the author's name
public string color { get; set; } // Optional good, warning, danger, or any hex color code (eg.#439FE0)
public string fallback { get; set; } // A plain-text summary of the attachment
public string footer { get; set; } // Brief text to help contextualize and identify an attachment
public string footer_icon { get; set; } // URL for a small 16px by 16px image beside the footer text
public string image_url { get; set; } // URL to an image to display inside a message attachment
public string pretext { get; set; } // Optional text that appears above the attachment block
public string text { get; set; } // Optional text that appears within the attachment
public string thumb_url { get; set; } // Optional URL to an image to display as a thumbnail in a message attachment
public string title { get; set; } // Optional, larger, bold text near the top of a message attachment
public string title_link { get; set; } // Optional URL used to hyperlink the title text
public string ts { get; set; } // Optional timestamp to be formatted and shown next to the footer text
public List<SlackAttachmentField> fields; // A table inside the message attachment
}
/// <summary>
/// Optional field class for use with SlackAttachment to send attachment fields
/// </summary>
class SlackAttachmentField
{
public string title { get; set; } // Shown as a bold heading above the value text
public string value { get; set; } // Text value of the field (supports standard markup, can be multi-line)
public string @short { get; set; } // Pass true to allow value to be side-by-side with another value
}
#endregion
#region Error Checking
bool ErrorChecks(string message, bool callback, IPlayer player = null)
{
if (string.IsNullOrEmpty(hookUrl))
{
PrintWarning("Invalid HookUrl, please check your config!");
return false;
}
if (string.IsNullOrEmpty(message))
{
PrintWarning("No message was specified in API call!");
return false;
}
if (callback && player == null)
{
PrintWarning("No valid player was specified in API call!");
return false;
}
return true;
}
#endregion
#region Helpers
T GetConfig<T>(string name, T value) => Config[name] == null ? value : (T)Convert.ChangeType(Config[name], typeof(T));
#endregion
}
}