C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Ext.Discord.Builders;
using Oxide.Ext.Discord.Clients;
using Oxide.Ext.Discord.Connections;
using Oxide.Ext.Discord.Constants;
using Oxide.Ext.Discord.Entities;
using Oxide.Ext.Discord.Extensions;
using Oxide.Ext.Discord.Helpers;
using Oxide.Ext.Discord.Interfaces;
using Oxide.Ext.Discord.Libraries;
using Oxide.Ext.Discord.Logging;
using Random = Oxide.Core.Random;
namespace Oxide.Plugins {
[Info("Discord Core", "MJSU", "2.2.0")]
[Description("Creates a link between a player and discord")]
internal class DiscordCore : CovalencePlugin, IDiscordPlugin, IDiscordLink {
#region Class Fields
public DiscordClient Client {
get;
set;
}
private StoredData _storedData;
//Plugin Data
private PluginConfig _pluginConfig;
//Plugin Config
private const string AccentColor = "de8732";
private const string UsePermission = "discordcore.use";
private char[] _linkChars;
private DiscordUser _bot;
private DiscordGuild _guild;
private bool _initialized;
private readonly BotConnection _discordSettings = new BotConnection {
Intents = GatewayIntents.Guilds | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages | GatewayIntents.DirectMessages
}
;
private readonly Hash<string, BanInfo> _banList = new Hash<string, BanInfo>();
private readonly List<LinkActivation> _activations = new List<LinkActivation>();
private readonly List<Snowflake> _allowedCommandChannels = new List<Snowflake>();
private string _allowedChannelNames;
private char _cmdPrefix;
private readonly DiscordLink _link = Interface.Oxide.GetLibrary<DiscordLink>();
private readonly DiscordCommand _dcCommands = Interface.Oxide.GetLibrary<DiscordCommand>();
private const string AcceptEmoji = "✅";
private const string DeclineEmoji = "❌";
private const string LinkAccountsButtonId = nameof(DiscordCore) + "_LinkAccounts";
private const string MessageLinkAccountsButtonId = nameof(DiscordCore) + "_MLinkAccounts";
private const string AcceptLinkButtonId = nameof(DiscordCore) + "_AcceptLink";
private const string DeclineLinkButtonId = nameof(DiscordCore) + "_DeclineLink";
#endregion
#region Setup & Loading
private void Init() {
_storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
permission.RegisterPermission(UsePermission, this);
_linkChars = _pluginConfig.LinkSettings.LinkCodeCharacters.ToCharArray();
_cmdPrefix = _dcCommands.CommandPrefixes[0];
_discordSettings.ApiToken = _pluginConfig.ApiKey;
_discordSettings.LogLevel = _pluginConfig.ExtensionDebugging;
}
protected override void LoadDefaultConfig() {
PrintWarning("Loading Default Config");
}
protected override void LoadConfig() {
base.LoadConfig();
Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
_pluginConfig = AdditionalConfig(Config.ReadObject<PluginConfig>());
Config.WriteObject(_pluginConfig);
}
public PluginConfig AdditionalConfig(PluginConfig config) {
config.LinkSettings = new DiscordLinkingSettings(config.LinkSettings);
config.WelcomeMessageSettings = new WelcomeMessageSettings(config.WelcomeMessageSettings);
return config;
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
[LangKeys.NoPermission]
= "You do not have permission to use this command", [LangKeys.ChatFormat] = $"[#BEBEBE][[#{AccentColor}]{Title}[/#]] {{0}}[/#]", [LangKeys.DiscordFormat] = $"[{Title}] {{0}}", [LangKeys.DiscordCoreOffline] = "Discord Core is not online.Please try again later", [LangKeys.Commands.Unknown] = "Unknown Command", [LangKeys.GenericError] = "We have encountered an error trying.PLease try again later.", [LangKeys.ConsolePlayerNotSupported] = "You cannot use this command from the server console.", [LangKeys.Commands.Leave.Errors.NotLinked] = "We were unable to unlink your account as you do not appear to have been linked.", [LangKeys.Commands.Join.Modes] = "Please select which which mode you would like to use to link with discord.\n" + $"If you wish to join using a code please type [#{AccentColor}]/{{0}} {{1}} {{2}}[/#]\n" + "If you wish to join by your discord username or id please type: \n" + $"[#{AccentColor}]/{{0}} {{1}} {{{{Username}}}}#{{{{Discriminator}}}}[/#]\n" + $"Or [#{AccentColor}]/{{0}} {{1}} {{{{Discord User ID}}}}[/#]", [LangKeys.Commands.Join.Errors.AlreadySignedUp] = $"You have already linked your discord and game accounts.If you wish to remove this link type [#{AccentColor}]{{0}}{{1}} {{2}}[/#]", [LangKeys.Commands.Join.Errors.UnableToFindUser] = $"Unable to find user '{{0}}' in the {{1}} discord server.Have you joined the {{1}} discord server @ [#{AccentColor}]discord.gg/{{2}}[/#]?", [LangKeys.Commands.Join.Errors.FoundMultipleUsers] = "Found multiple users with username '{0}' in the {1} discord server.Please include more of the username and discriminator if possible.", [LangKeys.Commands.Join.Errors.UsernameSearchError] = "An error occured while trying to search by username.Please try a different username or try again later.", [LangKeys.Commands.Join.Errors.InvalidSyntax] = $"Invalid syntax.Type [#{AccentColor}]{{0}}{{1}} code 123456[/#] where 123456 is the code you got from discord", [LangKeys.Commands.Join.Errors.NoPendingActivations] = "There is no link currently in progress with the code '{0}'.Please confirm your code and try again.", [LangKeys.Commands.Join.Errors.MustBeUsedDiscord] = "You must complete the link on discord and not through the game server.", [LangKeys.Commands.Join.Errors.MustBeUsedServer] = "You must complete the link on the game server and not through discord.", //[LangKeys.Commands.Join.Errors.LinkInProgress] = "You already have an existing link in process.Please continue from that link.",
[LangKeys.Commands.Join.Errors.Banned]
= "You have been banned from any more link attempts.You have {0}h {1}m {2}s left on your ban.", [LangKeys.Commands.Join.Complete.Info] = $"To complete your activation please use the following command: <color=#{AccentColor}>{{0}}{{1}} {{2}} {{3}}</color>.\n", [LangKeys.Commands.Join.Complete.InfoServer] = $"In order to use this command you must be in the <color=#{AccentColor}>{{0}}</color> discord server.You can join @ <color=#{AccentColor}>discord.gg/{{1}}</color>.\n", [LangKeys.Commands.Join.Complete.InfoGuildAny] = "This command can be used in any guild channel.\n", [LangKeys.Commands.Join.Complete.InfoGuildChannel] = "This command can only used in the following guild channels / categories {0}.\n", [LangKeys.Commands.Join.Complete.InfoAlsoDm] = "This command can also be used in a direct message to guild bot {0}", [LangKeys.Commands.Join.Complete.InfoDmOnly] = "This command can only be used in a direct message to the guild bot {0}", [LangKeys.Commands.Join.Messages.Discord.Accept] = "Accept", [LangKeys.Commands.Join.Messages.Discord.Decline] = "Decline", [LangKeys.Commands.Join.Messages.Discord.LinkAccounts] = "Link Accounts", [LangKeys.Commands.Join.Messages.Discord.Username] = "The player '{0}' is trying to link their game account to this discord.\n" + "If you could like to accept please click on the {1} button.\n" + "If you did not initiate this link please click on the {2} button", [LangKeys.Commands.Join.Messages.Chat.UsernameDmSent] = "Our bot {0} has sent you a discord direct message.Please finish your setup there.", [LangKeys.Commands.Join.Messages.Discord.CompletedInGame] = $"To complete your activation please use the following command: {DiscordFormatting.Bold(" {
0
}
{
1
}
{
2
}
{
3
}
")} in game.", [LangKeys.Commands.Join.Messages.Discord.CompleteInGameResponse] = "Please check your DM's for steps on how to complete the link process.", [LangKeys.Commands.Join.Messages.Discord.Declined] = "We have successfully declined the link request.We're sorry for the inconvenience.", [LangKeys.Commands.Join.Messages.Chat.Declined] = "Your join request was declined by the discord user.Repeated declined attempts will result in a link ban.", [LangKeys.Linking.Chat.Linked] = "You have successfully linked with discord user {0}#{1}.", [LangKeys.Linking.Discord.Linked] = "You have successfully linked your discord {0}#{1} with in game player {2}", [LangKeys.Linking.Chat.Unlinked] = "You have successfully unlinked player {0} with your discord account.", [LangKeys.Linking.Discord.Unlinked] = "You have successfully unlinked your discord {0}#{1} with in game player {2}", [LangKeys.Notifications.Link] = "Player {0}({1}) has linked with discord {2}({3})", [LangKeys.Notifications.Rejoin] = "Player {0}({1}) has rejoined and was linked with discord {2}({3})", [LangKeys.Notifications.Unlink] = "Player {0}({1}) has unlinked their discord {2}({3})", [LangKeys.Guild.WelcomeMessage] = "Welcome to the {0} discord server.", [LangKeys.Guild.WelcomeLinkMessage] = " If you would link to link your account please click on the Link Accounts button below.", [LangKeys.Guild.LinkMessage] = "Welcome to the {0} discord server." + "This server supports linking your discord and in game accounts." + "If you would like to begin this process please click on the {1} button below this message.\n" + $"{DiscordFormatting.Underline("Note : You must be in game to complete the link.")}", [LangKeys.Emoji.Accept] = AcceptEmoji, [LangKeys.Emoji.Decline] = DeclineEmoji, [LangKeys.Commands.ChatHelpText] = $"Allows players to link their player and discord accounts together.Players must first join the {{0}} Discord @ [#{AccentColor}]discord.gg/{{1}}[/#]\n" + $"Type [#{AccentColor}]/{{2}} {{3}} [/#] to start the link process\n" + $"Type [#{AccentColor}]/{{2}} {{4}}[/#] to to unlink yourself from discord\n" + $"Type [#{AccentColor}]/{{2}}[/#] to see this message again", [LangKeys.Commands.DiscordHelpText] = "Allows players to link their in game player and discord accounts together.\n" + "Type {0}{1} {2} to start the link process\n" + "Type {0}{1} {3} to to unlink yourself from discord\n" + "Type {0}{1} to see this message again", //Commands
[CommandKeys.ChatCommand]
= "dc", [CommandKeys.ChatJoinCommand] = "join", [CommandKeys.ChatLeaveCommand] = "leave", [CommandKeys.ChatJoinCodeCommand] = "code", [CommandKeys.DiscordCommand] = "dc", [CommandKeys.DiscordJoinCommand] = "join", [CommandKeys.DiscordLeaveCommand] = "leave"
}
, this);
}
private void OnServerInitialized() {
RegisterChatLangCommand(nameof(DiscordCoreChatCommand), CommandKeys.ChatCommand);
_link.AddLinkPlugin(this);
if (string.IsNullOrEmpty(_pluginConfig.ApiKey)) {
PrintWarning("Please set the Discord Bot Token and reload the plugin");
return;
}
Client.Connect(_discordSettings);
}
private void OnPluginLoaded(Plugin plugin) {
if (ReferenceEquals(this, plugin)) {
return;
}
DiscordCoreReady(plugin);
}
private void Unload() {
Interface.Oxide.CallHook("OnDiscordCoreClose");
SaveData();
}
#endregion
#region Client Connection
public void DiscordCoreReady(Plugin plugin) {
NextTick(() => {
if (plugin == null) {
Interface.CallHook("OnDiscordCoreReady", Client, _bot, _guild);
}
else {
plugin.CallHook("OnDiscordCoreReady", Client, _bot, _guild);
}
}
);
}
#endregion
#region Chat Commands
private void DiscordCoreChatCommand(IPlayer player, string cmd, string[] args) {
if (!player.HasPermission(UsePermission)) {
Chat(player, LangKeys.NoPermission);
return;
}
if (!IsDiscordCoreOnline()) {
Chat(player, LangKeys.DiscordCoreOffline);
return;
}
if (args.Length == 0) {
DisplayHelp(player);
return;
}
string option = args[0];
if (player.IsAdmin && option.Equals("welcome")) {
HandleDebugWelcome(player, args);
return;
}
if (player.Id == "server_console") {
Chat(player, LangKeys.ConsolePlayerNotSupported);
return;
}
if (option.Equals(Lang(CommandKeys.ChatJoinCommand, player), StringComparison.OrdinalIgnoreCase)) {
HandleChatJoin(player, args);
return;
}
if (option.Equals(Lang(CommandKeys.ChatLeaveCommand, player), StringComparison.OrdinalIgnoreCase)) {
GuildMember discord = player.GetGuildMember(_guild);
if (discord != null) {
HandleLeave(player, discord.User, false, true);
}
else {
Chat(player, LangKeys.Commands.Leave.Errors.NotLinked);
}
return;
}
if (option.Equals("code", StringComparison.OrdinalIgnoreCase)) {
HandleChatCompleteLink(player, args);
return;
}
DisplayHelp(player);
}
public void DisplayHelp(IPlayer player) {
Chat(player, LangKeys.Commands.ChatHelpText, GetDiscordServerName(), _pluginConfig.JoinCode, Lang(CommandKeys.ChatCommand, player), Lang(CommandKeys.ChatJoinCommand, player), Lang(CommandKeys.ChatLeaveCommand, player));
}
public void HandleChatJoin(IPlayer player, string[] args) {
if (_link.IsLinked(player.Id)) {
Chat(player, LangKeys.Commands.Join.Errors.AlreadySignedUp, "/", Lang(CommandKeys.ChatCommand, player), Lang(CommandKeys.ChatLeaveCommand, player));
return;
}
// LinkActivation existing = _activations.FirstOrDefault(a => a.Player?.Id == player.Id);
// if (existing != null)
// {
// Chat(player,LangKeys.Commands.Join.Errors.LinkInProgress);
// return;
// }
if (args.Length == 1) {
Chat(player, LangKeys.Commands.Join.Modes, Lang(CommandKeys.ChatCommand, player), Lang(CommandKeys.ChatJoinCommand, player), Lang(CommandKeys.ChatJoinCodeCommand, player));
return;
}
if (args[1].Equals(Lang(CommandKeys.ChatJoinCodeCommand, player), StringComparison.OrdinalIgnoreCase)) {
HandleChatJoinWithCode(player);
}
else {
HandleChatJoinWithUserName(player, args[1]);
}
}
public void HandleChatJoinWithCode(IPlayer player) {
string code = GenerateCode();
_activations.RemoveAll(a => a.Player?.Id == player.Id);
_activations.Add(new LinkActivation {
Player = player, Code = code
}
);
StringBuilder message = new StringBuilder();
message.Append(Lang(LangKeys.Commands.Join.Complete.Info, player, _cmdPrefix, Lang(CommandKeys.DiscordCommand, player), "code", code ));
message.Append(Lang(LangKeys.Commands.Join.Complete.InfoServer, player, _guild.Name, _pluginConfig.JoinCode));
if (_pluginConfig.LinkSettings.AllowCommandsInGuild) {
if (_allowedCommandChannels.Count == 0) {
message.Append(Lang(LangKeys.Commands.Join.Complete.InfoGuildAny, player));
}
else {
message.Append(Lang(LangKeys.Commands.Join.Complete.InfoGuildChannel, player, _allowedChannelNames));
}
message.Append(Lang(LangKeys.Commands.Join.Complete.InfoAlsoDm, player, _bot.Username));
}
else {
message.Append(Lang(LangKeys.Commands.Join.Complete.InfoDmOnly, player, _bot.Username));
}
Chat(player, message.ToString());
}
private void HandleChatJoinWithUserName(IPlayer player, string search) {
if (_banList.ContainsKey(player.Id)) {
BanInfo ban = GetPlayerBanInfo(player.Id);
if (ban.IsBanned()) {
TimeSpan remaining = ban.GetRemainingBan();
Chat(player, LangKeys.Commands.Join.Errors.Banned, remaining.Hours, remaining.Minutes, remaining.Seconds);
return;
}
}
Snowflake id;
if (Snowflake.TryParse(search, out id)) {
GuildMember member = _guild.Members[id];
if (member == null) {
Chat(player, LangKeys.Commands.Join.Errors.UnableToFindUser, search, GetDiscordServerName(), _pluginConfig.JoinCode);
return;
}
HandleChatJoinUser(player, member.User);
return;
}
string[] userInfo = search.Split('#');
string userName = userInfo[0];
string discriminator = userInfo.Length > 1 ? userInfo[1] : null;
_guild.SearchMembers(Client, new GuildSearchMembers {
Query = userInfo[0], Limit = 1000
}
).Then(members => {
HandleChatJoinUserResults(player, members, userName, discriminator);
}
, error => {
player.Message(Lang(LangKeys.Commands.Join.Errors.UsernameSearchError, player));
}
);
}
private void HandleChatJoinUserResults(IPlayer player, List<GuildMember> members, string userName, string discriminator) {
string fullSearch = userName;
if (!string.IsNullOrEmpty(discriminator)) {
fullSearch += $"#{discriminator}";
}
if (members.Count == 0) {
Chat(player, LangKeys.Commands.Join.Errors.UnableToFindUser, fullSearch, GetDiscordServerName(), _pluginConfig.JoinCode);
return;
}
DiscordUser user = null;
int count = 0;
foreach (GuildMember member in members) {
DiscordUser searchUser = member.User;
if (discriminator == null) {
if (searchUser.Username.StartsWith(userName, StringComparison.OrdinalIgnoreCase)) {
user = searchUser;
count++;
if (count > 1) {
break;
}
}
}
else if (searchUser.Username.Equals(userName, StringComparison.OrdinalIgnoreCase) && searchUser.Discriminator.Equals(discriminator)) {
user = searchUser;
break;
}
}
if (user == null || count > 1) {
Chat(player, LangKeys.Commands.Join.Errors.FoundMultipleUsers, userName, GetDiscordServerName());
return;
}
HandleChatJoinUser(player, user);
}
private void HandleChatJoinUser(IPlayer player, DiscordUser user) {
MessageComponentBuilder builder = new MessageComponentBuilder();
builder.AddActionButton(ButtonStyle.Success, Lang(LangKeys.Commands.Join.Messages.Discord.Accept, player), AcceptLinkButtonId, false, false, DiscordEmoji.FromCharacter(Lang(LangKeys.Emoji.Accept, player)));
builder.AddActionButton(ButtonStyle.Danger, Lang(LangKeys.Commands.Join.Messages.Discord.Decline, player), DeclineLinkButtonId, false, false, DiscordEmoji.FromCharacter(Lang(LangKeys.Emoji.Decline, player)));
MessageCreate create = new MessageCreate {
Content = GetDiscordFormattedMessage(LangKeys.Commands.Join.Messages.Discord.Username, player, player.Name, Lang(LangKeys.Commands.Join.Messages.Discord.Accept, player), Lang(LangKeys.Commands.Join.Messages.Discord.Decline, player)), Components = builder.Build()
}
;
user.SendDirectMessage(Client, create).Then(message => {
_activations.RemoveAll(a => a?.Discord.Id == user.Id);
_activations.Add(new LinkActivation {
Player = player, Discord = user, Channel = message.ChannelId
}
);
Chat(player, LangKeys.Commands.Join.Messages.Chat.UsernameDmSent, _bot.Username);
}
, error => {
Chat(player, LangKeys.GenericError);
}
);
}
public void HandleChatCompleteLink(IPlayer player, string[] args) {
if (_link.IsLinked(player.Id)) {
Chat(player, Lang(LangKeys.Commands.Join.Errors.AlreadySignedUp, player, "/", Lang(CommandKeys.ChatCommand, player), Lang(CommandKeys.ChatLeaveCommand, player)));
return;
}
if (args.Length < 2) {
Chat(player, Lang(LangKeys.Commands.Join.Errors.InvalidSyntax, player, "/", Lang(CommandKeys.ChatCommand, player)));
return;
}
LinkActivation act = _activations.FirstOrDefault(a => a.Code == args[1]);
if (act == null) {
Chat(player, Lang(LangKeys.Commands.Join.Errors.NoPendingActivations, player, args[1]));
return;
}
if (act.Discord == null) {
Chat(player, Lang(LangKeys.Commands.Join.Errors.MustBeUsedDiscord));
return;
}
act.Player = player;
CompletedLink(act);
}
private void HandleDebugWelcome(IPlayer player, string[] args) {
if (args.Length < 2) {
Chat(player, "Invalid Syntax.Ex: /dc welcome {{discordId}}");
return;
}
Snowflake userId;
if (!Snowflake.TryParse(args[1], out userId)) {
Chat(player, "Discord ID is not a valid snowflake");
return;
}
SendWelcomeMessage(userId);
Chat(player, $"Welcome message send to {args[1]}");
}
#endregion
#region Discord Commands
private void DiscordCoreMessageCommand(DiscordMessage message, string cmd, string[] args) {
IPlayer player = message.Author.Player;
if (args.Length == 0) {
DisplayDiscordHelp(message, player);
return;
}
string option = args[0];
if (option.Equals(Lang(CommandKeys.DiscordJoinCommand, player), StringComparison.OrdinalIgnoreCase)) {
HandleDiscordJoin(message, player);
return;
}
if (option.Equals(Lang(CommandKeys.DiscordLeaveCommand, player), StringComparison.OrdinalIgnoreCase)) {
if (player != null) {
HandleLeave(player, message.Author, false, true);
}
else {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Leave.Errors.NotLinked));
}
return;
}
if (option.Equals("code", StringComparison.OrdinalIgnoreCase)) {
HandleDiscordCompleteLink(message, args);
return;
}
DisplayDiscordHelp(message, player);
}
public void DisplayDiscordHelp(DiscordMessage message, IPlayer player) {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.DiscordHelpText, player, _cmdPrefix, Lang(CommandKeys.DiscordCommand, player), Lang(CommandKeys.DiscordJoinCommand, player), Lang(CommandKeys.DiscordLeaveCommand, player)));
}
public void HandleDiscordJoin(DiscordMessage message, IPlayer player) {
if (player != null) {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.AlreadySignedUp, player, _cmdPrefix, Lang(CommandKeys.DiscordCommand, player), Lang(CommandKeys.DiscordLeaveCommand, player)));
return;
}
// LinkActivation existing = _activations.FirstOrDefault(a => a.Discord?.Id == message.Author.Id);
// if (existing != null)
// {
// message.Reply(_client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.LinkInProgress));
// return;
// }
string code = GenerateCode();
_activations.RemoveAll(a => a.Discord?.Id == message.Author.Id);
_activations.Add(new LinkActivation {
Discord = message.Author, Code = code
}
);
string linkMessage = GetDiscordFormattedMessage(LangKeys.Commands.Join.Messages.Discord.CompletedInGame, null, "/", Lang(CommandKeys.ChatCommand), "code", code);
if (message.GuildId.HasValue) {
message.Author.SendDirectMessage(Client, linkMessage);
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Messages.Discord.CompleteInGameResponse));
}
else {
message.Reply(Client, linkMessage);
}
}
public void HandleDiscordCompleteLink(DiscordMessage message, string[] args) {
if (args.Length < 2) {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.InvalidSyntax, null, _cmdPrefix, Lang(CommandKeys.DiscordCommand), "code"));
return;
}
LinkActivation act = _activations.FirstOrDefault(a => a.Code.Equals(args[1], StringComparison.OrdinalIgnoreCase));
if (act == null) {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.NoPendingActivations, null, args[1]));
return;
}
if (act.Player == null) {
message.Reply(Client, GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.MustBeUsedServer));
return;
}
act.Discord = message.Author;
CompletedLink(act);
}
#endregion
#region Discord Hooks
[HookMethod(DiscordExtHooks.OnDiscordGatewayReady)]
private void OnDiscordGatewayReady(GatewayReadyEvent ready) {
try {
if (ready.Guilds.Count == 0) {
PrintError("Your bot was not found in any discord servers.Please invite it to a server and reload the plugin.");
return;
}
DiscordGuild guild = null;
if (ready.Guilds.Count == 1 && !_pluginConfig.GuildId.IsValid()) {
guild = ready.Guilds.Values.FirstOrDefault();
}
if (guild == null) {
guild = ready.Guilds[_pluginConfig.GuildId];
if (guild == null) {
PrintError("Failed to find a matching guild for the Discord Server Id." + "Please make sure your guild Id is correct and the bot is in the discord server.");
return;
}
}
DiscordApplication app = Client.Bot.Application;
if (!app.HasApplicationFlag(ApplicationFlags.GatewayGuildMembersLimited) && !app.HasApplicationFlag(ApplicationFlags.GatewayGuildMembers)) {
PrintError($"You need to enable \"Server Members Intent\" for {Client.Bot.BotUser.Username} @ https://discord.com/developers/applications\n" + $"{Name} will not function correctly until that is fixed.Once updated please reload {Name}.");
return;
}
if (!app.HasApplicationFlag(ApplicationFlags.GatewayMessageContentLimited)) {
PrintWarning($"You will need to enable \"Message Content Intent\" for {Client.Bot.BotUser.Username} @ https://discord.com/developers/applications\n by April 2022" + $"{Name} will stop function correctly after that date until that is fixed.");
}
_bot = ready.User;
_guild = guild;
Puts($"Connected to bot: {_bot.Username}");
}
catch (Exception ex) {
PrintError($"Failed to load DiscordCore: {ex}");
}
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMembersLoaded)]
private void OnDiscordGuildMembersLoaded(DiscordGuild guild) {
if (guild.Id != _guild.Id) {
return;
}
try {
_guild = guild;
Puts($"Discord connected to server: {GetDiscordServerName()}");
if (!_initialized) {
_initialized = true;
}
Puts($"Loaded {_guild.Members.Count} Discord Members");
HandleLeaveRejoin();
SetupGuildLinkMessage();
List<string> allowedCommandChannelNames = new List<string>();
foreach (Snowflake id in _pluginConfig.LinkSettings.AllowCommandInChannels) {
DiscordChannel channel = _guild.Channels[id];
if (channel != null) {
_allowedCommandChannels.Add(channel.Id);
allowedCommandChannelNames.Add(channel.Name);
}
}
_allowedChannelNames = string.Join(", ", allowedCommandChannelNames.ToArray());
RegisterDiscordLangCommand(nameof(DiscordCoreMessageCommand), CommandKeys.DiscordCommand, true, _pluginConfig.LinkSettings.AllowCommandsInGuild, _allowedCommandChannels);
DiscordCoreReady(null);
Puts($"{Title} Ready");
}
catch (Exception ex) {
PrintError($"Failed to connect to guild: {ex}");
}
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberAdded)]
private void OnDiscordGuildMemberAdded(GuildMember member) {
bool rejoined = false;
if (_pluginConfig.LinkSettings.AutoRelinkPlayer) {
rejoined = HandleRejoin(member.User);
}
if (_pluginConfig.WelcomeMessageSettings.EnableJoinMessage && !rejoined) {
SendWelcomeMessage(member.Id);
}
}
private void SendWelcomeMessage(Snowflake discordId) {
MessageCreate create = new MessageCreate {
Content = Lang(LangKeys.Guild.WelcomeMessage, null, GetDiscordServerName())
}
;
if (_pluginConfig.WelcomeMessageSettings.EnableLinkButton) {
create.Content += Lang(LangKeys.Guild.WelcomeLinkMessage);
MessageComponentBuilder builder = new MessageComponentBuilder();
builder.AddActionButton(ButtonStyle.Success, Lang(LangKeys.Commands.Join.Messages.Discord.LinkAccounts), MessageLinkAccountsButtonId, false, false, DiscordEmoji.FromCharacter(Lang(LangKeys.Emoji.Accept)));
create.Components = builder.Build();
}
DiscordUser.CreateDirectMessageChannel(Client, discordId).Then(channel => {
channel.CreateMessage(Client, create);
}
);
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberRemoved)]
private void OnDiscordGuildMemberRemoved(GuildMember member) {
if (member?.User == null) {
return;
}
IPlayer player = _link.GetPlayer(member.User.Id);
if (player == null) {
return;
}
HandleLeave(player, member.User, true, false);
}
#endregion
#region Link Message Handling
[HookMethod(DiscordExtHooks.OnDiscordInteractionCreated)]
private void OnDiscordInteractionCreated(DiscordInteraction interaction) {
if (interaction.Type != InteractionType.MessageComponent) {
return;
}
if (!interaction.Data.ComponentType.HasValue || interaction.Data.ComponentType.Value != MessageComponentType.Button) {
return;
}
DiscordUser user = interaction.User ?? interaction.Member?.User;
switch (interaction.Data.CustomId) {
case LinkAccountsButtonId : HandleLinkAccountsButton(interaction, true);
break;
case MessageLinkAccountsButtonId : HandleLinkAccountsButton(interaction, false);
break;
case AcceptLinkButtonId : HandleAcceptLinkButton(interaction, user);
break;
case DeclineLinkButtonId : HandleDeclineLinkButton(interaction, user);
break;
}
}
public void HandleLinkAccountsButton(DiscordInteraction interaction, bool ephemeral) {
MessageFlags flags = ephemeral ? MessageFlags.Ephemeral : MessageFlags.None;
DiscordUser member = interaction.Member?.User ?? interaction.User;
IPlayer player = member.Player;
if (player != null) {
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.ChannelMessageWithSource, Data = new InteractionCallbackData {
Content = GetDiscordFormattedMessage(LangKeys.Commands.Join.Errors.AlreadySignedUp, player, _cmdPrefix, Lang(CommandKeys.DiscordCommand, player), Lang(CommandKeys.DiscordLeaveCommand, player)), Flags = flags
}
}
);
return;
}
string code = GenerateCode();
_activations.RemoveAll(a => a.Discord?.Id == member.Id);
_activations.Add(new LinkActivation {
Discord = member, Code = code
}
);
string linkMessage = GetDiscordFormattedMessage(LangKeys.Commands.Join.Messages.Discord.CompletedInGame, null, "/", Lang(CommandKeys.ChatCommand), "code", code);
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.ChannelMessageWithSource, Data = new InteractionCallbackData {
Content = linkMessage, Flags = flags
}
}
);
}
private void HandleAcceptLinkButton(DiscordInteraction interaction, DiscordUser user) {
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.DeferredUpdateMessage
}
);
LinkActivation act = _activations.FirstOrDefault(a => a.Discord?.Id == user.Id && a.Player != null);
if (act == null) {
return;
}
CompletedLink(act);
}
private void HandleDeclineLinkButton(DiscordInteraction interaction, DiscordUser user) {
interaction.CreateResponse(Client, new InteractionResponse {
Type = InteractionResponseType.DeferredUpdateMessage
}
);
LinkActivation act = _activations.FirstOrDefault(a => a.Discord?.Id == user.Id);
if (act != null) {
_activations.Remove(act);
interaction.CreateFollowUpMessage(Client, new CommandFollowupCreate {
Content = GetDiscordFormattedMessage(LangKeys.Commands.Join.Messages.Discord.Declined)
}
);
Chat(act.Player, LangKeys.Commands.Join.Messages.Chat.Declined);
LinkBanSettings banSettings = _pluginConfig.LinkSettings.LinkBanSettings;
if (banSettings.EnableLinkBanning) {
BanInfo ban = GetPlayerBanInfo(act.Player.Id);
ban.AddDeclined(banSettings.BanDeclineAmount, banSettings.BanDuration);
}
}
}
public void SetupGuildLinkMessage() {
DiscordLinkingSettings link = _pluginConfig.LinkSettings;
LinkMessageSettings settings = link.LinkMessageSettings;
if (!settings.Enabled) {
return;
}
if (!settings.ChannelId.IsValid()) {
PrintWarning("Link message is enabled but link message channel ID is not valid");
return;
}
DiscordChannel channel = _guild.Channels[settings.ChannelId];
if (channel == null) {
PrintWarning($"Link message failed to find channel with ID {settings.ChannelId}");
return;
}
string content = Lang(LangKeys.Guild.LinkMessage, null, GetDiscordServerName(), "Link Accounts");
if (_storedData.MessageData == null) {
MessageCreate message = CreateGuildLinkMessage(content);
channel.CreateMessage(Client, message).Then(SaveGuildLinkMessageInfo);
}
else {
channel.GetMessage(Client, _storedData.MessageData.MessageId).Then(message => {
message.Content = content;
message.Components.Clear();
message.Components = CreateGuildLinkActions();
message.Edit(Client, new MessageUpdate {
Content = content, Components = CreateGuildLinkActions()
}
);
}
).Catch<ResponseError>(error => {
if (error.HttpStatusCode == DiscordHttpStatusCode.NotFound) {
PrintWarning("The previous link message has been removed.Recreating the message.");
MessageCreate message = CreateGuildLinkMessage(content);
channel.CreateMessage(Client, message).Then(SaveGuildLinkMessageInfo);
}
}
);
}
}
public MessageCreate CreateGuildLinkMessage(string content) {
MessageCreate message = new MessageCreate {
Content = content, Components = CreateGuildLinkActions()
}
;
return message;
}
public List<ActionRowComponent> CreateGuildLinkActions() {
MessageComponentBuilder builder = new MessageComponentBuilder();
builder.AddActionButton(ButtonStyle.Success, Lang(LangKeys.Commands.Join.Messages.Discord.LinkAccounts), LinkAccountsButtonId, false, false, DiscordEmoji.FromCharacter(Lang(LangKeys.Emoji.Accept)));
return builder.Build();
}
public void SaveGuildLinkMessageInfo(DiscordMessage message) {
_storedData.MessageData = new LinkMessageData {
ChannelId = message.ChannelId, MessageId = message.Id
}
;
SaveData();
}
#endregion
#region Linking
public void CompletedLink(LinkActivation activation) {
IPlayer player = activation.Player;
DiscordUser user = activation.Discord;
_storedData.PlayerDiscordInfo[player.Id] = new DiscordInfo {
PlayerId = player.Id, DiscordId = user.Id
}
;
_activations.Remove(activation);
Chat(player, LangKeys.Linking.Chat.Linked, user.Username, user.Discriminator);
activation.Discord.SendDirectMessage(Client, GetDiscordFormattedMessage(LangKeys.Linking.Discord.Linked, player, user.Username, user.Discriminator, player.Name));
Snowflake channelId = _pluginConfig.LinkSettings.AnnouncementChannel;
if (channelId.IsValid()) {
DiscordChannel channel = _guild.Channels[channelId];
channel.CreateMessage(Client, Lang(LangKeys.Notifications.Link, null, player.Name, player.Id, user.Username, user.Id));
}
_link.OnLinked(this, activation.Player, activation.Discord);
SaveData();
}
public void HandleLeave(IPlayer player, DiscordUser user, bool backup, bool message) {
if (backup) {
_storedData.LeftPlayerInfo[user.Id] = _storedData.PlayerDiscordInfo[player.Id];
}
_storedData.PlayerDiscordInfo.Remove(player.Id);
if (!backup) {
Chat(player, LangKeys.Linking.Chat.Unlinked, player.Name);
if (message) {
user.SendDirectMessage(Client, GetDiscordFormattedMessage(LangKeys.Linking.Discord.Unlinked, player, user.Username, user.Discriminator, player.Name));
}
}
_link.OnUnlinked(this, player, user);
Snowflake channelId = _pluginConfig.LinkSettings.AnnouncementChannel;
if (channelId.IsValid()) {
DiscordChannel channel = _guild.Channels[channelId];
if (message) {
channel.CreateMessage(Client, Lang(LangKeys.Notifications.Unlink, null, player.Name, player.Id, user.Username, user.Id));
}
}
SaveData();
}
public bool HandleRejoin(DiscordUser user) {
DiscordInfo existing = _storedData.LeftPlayerInfo[user.Id];
if (existing == null) {
return false;
}
_storedData.PlayerDiscordInfo[existing.PlayerId] = existing;
_storedData.LeftPlayerInfo.Remove(user.Id);
IPlayer player = players.FindPlayerById(existing.PlayerId);
_link.OnLinked(this, player, user);
Snowflake channelId = _pluginConfig.LinkSettings.AnnouncementChannel;
if (channelId.IsValid()) {
DiscordChannel channel = _guild.Channels[channelId];
channel.CreateMessage(Client, Lang(LangKeys.Notifications.Rejoin, null, player.Name, player.Id, user.Username, user.Id));
}
SaveData();
return true;
}
public void HandleLeaveRejoin() {
foreach (DiscordInfo info in _storedData.PlayerDiscordInfo.Values.ToList()) {
if (!_guild.Members.ContainsKey(info.DiscordId)) {
IPlayer player = players.FindPlayerById(info.PlayerId);
if (player != null) {
DiscordUser user = player.GetDiscordUser();
HandleLeave(player, user, _pluginConfig.LinkSettings.AutoRelinkPlayer, false);
Puts($"Player {player.Name}({player.Id}) Discord {user.Id} is no longer in the guild and has been unlinked.");
}
}
}
if (_pluginConfig.LinkSettings.AutoRelinkPlayer) {
foreach (DiscordInfo info in _storedData.LeftPlayerInfo.Values.ToList()) {
GuildMember member = _guild.Members[info.DiscordId];
if (member != null) {
HandleRejoin(member.User);
}
}
}
}
#endregion
#region Discord Link
public IDictionary<PlayerId, Snowflake> GetPlayerIdToDiscordIds() {
Hash<PlayerId, Snowflake> data = new Hash<PlayerId, Snowflake>();
foreach (DiscordInfo info in _storedData.PlayerDiscordInfo.Values) {
data[new PlayerId(info.PlayerId)] = info.DiscordId;
}
return data;
}
#endregion
#region API
[HookMethod(nameof(GetDiscordServerName))]
public string GetDiscordServerName() {
if (!string.IsNullOrEmpty(_pluginConfig.ServerNameOverride)) {
return _pluginConfig.ServerNameOverride;
}
return _guild?.Name ?? "Not Connected";
}
#endregion
#region Discord Helpers
public string GetDiscordFormattedMessage(string key, IPlayer player = null, params object[] args) {
return Formatter.ToPlaintext(Lang(LangKeys.DiscordFormat, player, Lang(key, player, args)));
}
private bool IsDiscordCoreOnline() => _initialized && _guild != null;
#endregion
#region Helper Methods
public string GenerateCode() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < _pluginConfig.LinkSettings.LinkCodeLength; i++) {
sb.Append(_linkChars[Random.Range(0, _pluginConfig.LinkSettings.LinkCodeLength)]);
}
return sb.ToString();
}
public BanInfo GetPlayerBanInfo(string id) {
BanInfo info = _banList[id];
if (info == null) {
info = new BanInfo();
_banList[id] = info;
}
return info;
}
public void SaveData() {
if (_storedData != null) {
Interface.Oxide.DataFileSystem.WriteObject(Name, _storedData);
}
}
public void Chat(IPlayer player, string key, params object[] args) {
if (player.IsConnected) {
player.Reply(Lang(LangKeys.ChatFormat, player, Lang(key, player, args)));
}
}
public string Lang(string key, IPlayer player = null, params object[] args) {
try {
return string.Format(lang.GetMessage(key, this, player?.Id), args);
}
catch(Exception ex) {
PrintError($"Lang Key '{key}' threw exception\n:{ex}");
throw;
}
}
public void RegisterChatLangCommand(string command, string langKey) {
foreach (string langType in lang.GetLanguages(this)) {
Dictionary<string, string> langKeys = lang.GetMessages(langType, this);
string commandValue;
if (langKeys.TryGetValue(langKey, out commandValue) && !string.IsNullOrEmpty(commandValue)) {
AddCovalenceCommand(commandValue, command);
}
}
}
/// <summary>
/// Registers commands with discord using lang keys
/// </summary>
/// <param name="command">Name of the method to use in callback</param>
/// <param name="langKey">The name of the lang key dictionary</param>
/// <param name="direct">Should we register this command for direct messages</pa
// ... [Code preview truncated due to length. Full source in attached file] ...