C#:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Ext.Discord;
using Oxide.Ext.Discord.Attributes;
using Oxide.Ext.Discord.DiscordEvents;
using Oxide.Ext.Discord.DiscordObjects;
using DiscordUser = Oxide.Ext.Discord.DiscordObjects.User;
using Random = Oxide.Core.Random;
namespace Oxide.Plugins
{
[Info("Discord Core", "MJSU", "0.17.0")]
[Description("Creates a link between a player and discord")]
internal class DiscordCore : CovalencePlugin
{
#region Class Fields
[PluginReference] private Plugin DiscordAuth, DiscordConnect;
[DiscordClient] private DiscordClient _client;
private StoredData _storedData; //Plugin Data
private PluginConfig _pluginConfig; //Plugin Config
private const string AccentColor = "de8732";
private const string UsePermission = "discordcore.use";
private const string PluginsPermission = "discordcore.plugins";
private readonly Hash<string, DiscordCommand> _discordCommands = new Hash<string, DiscordCommand>();
private readonly Hash<string, List<ChannelSubscription>> _channelSubscriptions = new Hash<string, List<ChannelSubscription>>();
private readonly List<DiscordActivation> _pendingDiscordActivations = new List<DiscordActivation>();
private readonly Hash<string, InGameActivation> _pendingInGameActivation = new Hash<string, InGameActivation>();
private readonly List<Plugin> _hookPlugins = new List<Plugin>();
private readonly Hash<string, string> _playerDmChannel = new Hash<string, string>();
private readonly Hash<string, Channel> _channelCache = new Hash<string, Channel>();
private DiscordUser _bot;
private Guild _discordServer;
private readonly List<GuildMember> _chunkUsers = new List<GuildMember>();
private Timer _chunkLoading;
private enum ConnectionStateEnum : byte { Disconnected, Connecting, Connected }
private ConnectionStateEnum _connectionState = ConnectionStateEnum.Disconnected;
private DateTime _lastUpdate;
private bool _initialized;
private Timer _connectionTimeout;
private int _guildCount;
private bool _useExternal;
private DiscordSettings _discordSettings;
#endregion
#region Setup & Loading
private void Init()
{
_storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
permission.RegisterPermission(UsePermission, this);
permission.RegisterPermission(PluginsPermission, this);
}
protected override void LoadDefaultConfig()
{
PrintWarning("Loading Default Config");
}
protected override void LoadConfig()
{
base.LoadConfig();
Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
_pluginConfig = Config.ReadObject<PluginConfig>();
Config.WriteObject(_pluginConfig);
}
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.UnknownCommand] = "Unknown Command",
[LangKeys.DiscordLeave] = "You have removed your discord bot connection",
[LangKeys.DiscordLeaveFailed] = "You are not subscribed to discord and were not removed",
[LangKeys.JoinAlreadySignedUp] = $"You're already signed up for discord. If you want to remove yourself from discord type [#{AccentColor}]/{{0}} leave[/#]",
[LangKeys.JoinInvalidSyntax] = $"Invalid Syntax. Ex. [#{AccentColor}]/{{0}} join name#1234[/#] to sign up for discord",
[LangKeys.JoinUnableToFindUser] = $"Unable to find user '{{0}}' in the {{1}} discord. Have you joined the {{1}} discord server @ [#{AccentColor}]discord.gg/{{2}}[/#]?",
[LangKeys.JoinReceivedPm] = $"You have received a PM from the {{0}}. Please respond to the bot with your code to complete the discord activation.\n[#{AccentColor}]{{1}}[/#]",
[LangKeys.JoinPleaseEnterCode] = "Please enter your code here to complete activation.",
[LangKeys.DiscordJoinInvalidSyntax] = $"Invalid syntax. Type [#{AccentColor}]/{{0}} code 123456[/#] where 123456 is the code you got from discord",
[LangKeys.DiscordJoinNoPendingActivations] = "You either don't have a pending activation or your code is invalid",
[LangKeys.DiscordJoinSuccessfullyRegistered] = "You have successfully registered your discord with the server",
[LangKeys.DiscordCommandsPreText] = "Available Commands:",
[LangKeys.DiscordCommandsBotChannelAllowed] = "(Bot Channel Allowed)",
[LangKeys.DiscordPlugins] = "List of Discord enabled plugins:\n",
[LangKeys.DiscordJoinWrongChannel] = "Please use /{0} in this private message and not to any discord server channels",
[LangKeys.DiscordCoreLinkingNotEnabled] = "DiscordCore linking is not enabled. Please use the external link system.",
[LangKeys.DiscordNotAllowedBotChannel] = "This command is not allowed in the bot channel and can only be used here.",
[LangKeys.DiscordMustSignUpBeforeCommands] = "You need to sign up for discord before you can start using commands. To begin type /{0}",
[LangKeys.DiscordPlayerIsServerConsole] = "You have linked yourself as server console. This is not allowed. Please unlink and link with your steam account.",
[LangKeys.DiscordMustSignUpBeforeBotCommands] = "You need to sign up for discord before you can use this command in the bot channel. To begin type /{0}",
[LangKeys.DiscordClientNotConnected] = "The discord client is not connected. Please contact an admin.",
[LangKeys.GameJoinCodeNotMatch] = "Your activation code does not match! Please reply with only the code.",
[LangKeys.DiscordCompleteActivation] = "To complete your discord activation in the server chat please type /{0} code {1}",
[LangKeys.ConsolePlayerNotSupported] = "You cannot use this command from the server console.",
[LangKeys.DiscordAuthNotLinked] = "You're not linked with DiscordAuth please authenticate and try again",
[LangKeys.DiscordConnectNotLinked] = "You're not linked with DiscordConnect please authenticate and try again",
[LangKeys.DiscordHooksNotLinked] = "You're not linked with this servers discord authentication please authenticate and try again",
[LangKeys.LinkNotification] = "Player {0}({1}) has linked with discord {2}({3})",
[LangKeys.RejoinNotification] = "Player {0}({1}) has rejoined and was linked with discord {2}({3})",
[LangKeys.UnlinkNotification] = "Player {0}({1}) has unlinked their discord {2}({3})",
[LangKeys.DiscordUserJoin] = "If you would like to link your discord account with the {0} server respond to this message with /{1}. " +
"This provides you with a bot which gives you access to commands while not on the server. Once you sign up type /{2} to learn more",
[LangKeys.NotLinked] = "You're not linked with the external link system. Please link before using this command.\n",
[LangKeys.HelpNotLinked] = "Discord: Allows communication between a server and discord.\n" +
"Type /{0} - (In a private message to the bot) - to start linking your server player and discord together\n" +
"Type /{1} - to see this message again",
[LangKeys.HelpLinked] = "Discord: Allows communication between a server and discord.\n" +
"Type /{0} - (In a private message to the bot) - to see the list of available commands\n" +
"Type /{1} - (In a private message to the bot) - to unlink your discord from the game server\n" +
"Type /{2} - to see this message again",
[LangKeys.HelpText] = $"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}} discordusername#discorduserid[/#] to start the activation of discord\n" +
$"Type [#{AccentColor}]/{{2}} {{4}}[/#] to remove yourself from discord\n" +
$"Type [#{AccentColor}]/{{2}}[/#] to see this message again"
}, this);
}
private void OnServerInitialized()
{
_discordSettings = new DiscordSettings
{
ApiToken = _pluginConfig.DiscordApiKey,
Debugging = _pluginConfig.ExtensionDebugging
};
if (_pluginConfig.UseDiscordAuth && DiscordAuth == null)
{
PrintWarning("Use DiscordAuth enabled but DiscordAuth not found.");
_pluginConfig.UseDiscordAuth = false;
}
if (_pluginConfig.UseDiscordConnect && DiscordConnect == null)
{
PrintWarning("Use DiscordConnect enabled but DiscordConnect not found.");
_pluginConfig.UseDiscordConnect = false;
}
if (_pluginConfig.UseDiscordAuth || _pluginConfig.UseDiscordConnect || _pluginConfig.UseHooks)
{
_useExternal = true;
_pluginConfig.EnableLinking = false;
}
if (_pluginConfig.EnableLinking && !_useExternal)
{
if (!string.IsNullOrEmpty(_pluginConfig.GameChatCommand))
{
AddCovalenceCommand(_pluginConfig.GameChatCommand, nameof(DiscordChatCommand));
}
RegisterCommand(_pluginConfig.LeaveCommand, this, HandleLeave, "To unlink your player account from discord");
}
if (!string.IsNullOrEmpty(_pluginConfig.HelpCommand))
{
RegisterCommand(_pluginConfig.HelpCommand, this, HandleHelp, "To view the discord bot help text", null, true);
}
if (!string.IsNullOrEmpty(_pluginConfig.CommandsCommand))
{
RegisterCommand(_pluginConfig.CommandsCommand, this, HandleCommands, "To view the list of available bot commands");
}
if (!string.IsNullOrEmpty(_pluginConfig.PluginsCommand))
{
RegisterCommand(_pluginConfig.PluginsCommand, this, HandlePlugins, "To see a list of discord enabled plugins", PluginsPermission);
}
ConnectClient();
_lastUpdate = DateTime.Now;
timer.Every(60, () =>
{
double duration = (DateTime.Now - _lastUpdate).TotalSeconds;
if (_connectionState != ConnectionStateEnum.Connecting && duration > _pluginConfig.HeartbeatTimeoutDuration)
{
PrintWarning($"Heartbeat timed out ({duration}s). Reconnecting");
CloseClient();
ConnectClient();
}
});
}
private void Unload()
{
CloseClient();
}
public void ConnectClient()
{
if (string.IsNullOrEmpty(_pluginConfig.DiscordApiKey))
{
PrintWarning("Please enter your discord bot API key and reload the plugin");
return;
}
_lastUpdate = DateTime.Now;
_connectionState = ConnectionStateEnum.Connecting;
timer.In(1f, () =>
{
Discord.CreateClient(this, _discordSettings); // Create a new DiscordClient
timer.In(60f, () =>
{
if (_connectionState == ConnectionStateEnum.Connecting)
{
_connectionState = ConnectionStateEnum.Disconnected;
}
});
});
}
public void CloseClient()
{
_connectionState = ConnectionStateEnum.Disconnected;
Interface.Oxide.CallHook("OnDiscordCoreClose");
Discord.CloseClient(_client);
}
private void OnPluginUnloaded(Plugin plugin)
{
foreach (KeyValuePair<string, DiscordCommand> commands in _discordCommands.Where(dc => dc.Value.PluginName == plugin.Name).ToList())
{
UnregisterCommand(commands.Key, plugin);
}
foreach (KeyValuePair<string, List<ChannelSubscription>> channelSubscription in _channelSubscriptions)
{
if (channelSubscription.Value.Any(cs => cs.PluginName == plugin.Name))
{
UnsubscribeChannel(channelSubscription.Key, plugin);
}
}
if (_hookPlugins.Contains(plugin))
{
_hookPlugins.Remove(plugin);
_client.Plugins.Remove(plugin);
}
}
#endregion
#region Chat Commands
private void DiscordChatCommand(IPlayer player, string cmd, string[] args)
{
if (player.Id == "server_console")
{
Chat(player, Lang(LangKeys.NoPermission, player));
return;
}
if (!player.HasPermission(UsePermission) && !player.IsAdmin)
{
Chat(player, Lang(LangKeys.NoPermission, player));
return;
}
if (args.Length == 0)
{
DisplayHelp(player);
return;
}
string option = args[0].ToLower();
if (option == _pluginConfig.InGameJoinCommand.ToLower())
{
HandleJoin(player, args);
}
else if (option == _pluginConfig.InGameLeaveCommand.ToLower())
{
if (_storedData.PlayerDiscordInfo.ContainsKey(player.Id))
{
HandleLeave(player, false);
Chat(player, Lang(LangKeys.DiscordLeave, player));
}
else
{
Chat(player, Lang(LangKeys.DiscordLeaveFailed, player));
}
}
else if (option == "code")
{
HandleDiscordJoinCode(player, args);
}
else
{
DisplayHelp(player);
}
}
public void DisplayHelp(IPlayer player)
{
Chat(player, Lang(LangKeys.HelpText, player, GetDiscordName(), _pluginConfig.JoinCode, _pluginConfig.GameChatCommand, _pluginConfig.InGameJoinCommand, _pluginConfig.InGameLeaveCommand));
}
public void HandleJoin(IPlayer player, string[] args)
{
if (_storedData.PlayerDiscordInfo.ContainsKey(player.Id))
{
Chat(player, Lang(LangKeys.JoinAlreadySignedUp, player, _pluginConfig.GameChatCommand));
return;
}
if (args.Length < 2)
{
Chat(player, Lang(LangKeys.JoinInvalidSyntax, player, _pluginConfig.GameChatCommand));
return;
}
if (_client == null)
{
Chat(player, Lang(LangKeys.DiscordClientNotConnected, player));
return;
}
DiscordUser user = GetUserByUsername(args[1]);
if (user == null)
{
Chat(player, Lang(LangKeys.JoinUnableToFindUser, player, args[1], GetDiscordName(), _pluginConfig.JoinCode));
return;
}
if (_storedData.PlayerDiscordInfo.Values.Any(di => di.DiscordId == user.id))
{
Chat(player, Lang(LangKeys.JoinAlreadySignedUp, player, _pluginConfig.GameChatCommand));
return;
}
string code = GenerateCode();
_pendingInGameActivation[user.id] = new InGameActivation
{
Code = code,
PlayerId = player.Id
};
Chat(player, Lang(LangKeys.JoinReceivedPm, player, _bot.username, code));
CreateChannelActivation(player, user, code, Lang(LangKeys.JoinPleaseEnterCode, player));
}
public void HandleDiscordJoinCode(IPlayer player, string[] args)
{
if (_storedData.PlayerDiscordInfo.ContainsKey(player.Id))
{
Chat(player, Lang(LangKeys.JoinAlreadySignedUp, player, _pluginConfig.GameChatCommand));
return;
}
if (args.Length < 2)
{
Chat(player, Lang(LangKeys.DiscordJoinInvalidSyntax, player, _pluginConfig.GameChatCommand));
return;
}
DiscordActivation act = _pendingDiscordActivations.FirstOrDefault(a => a.Code == args[1]);
if (act == null)
{
Chat(player, Lang(LangKeys.DiscordJoinNoPendingActivations, player));
return;
}
_storedData.PlayerDiscordInfo[player.Id] = new DiscordInfo
{
PlayerId = player.Id,
DiscordId = act.DiscordId,
};
string name = _discordServer.members.FirstOrDefault(m => m.user.id == act.DiscordId)?.user?.username;
SaveData();
SendMessageToUser(player.Id, Lang(LangKeys.DiscordJoinSuccessfullyRegistered, player));
Chat(player, Lang(LangKeys.DiscordJoinSuccessfullyRegistered, player));
SendMessageToChannel(_pluginConfig.LinkUnlinkAnnouncementChannel, Lang(LangKeys.LinkNotification, null, player.Name, player.Id, name, act.DiscordId));
Interface.Call("OnDiscordCoreJoin", player, act.DiscordId);
}
#endregion
#region Internal Discord Commands
public object HandleHelp(IPlayer player, string channelId, string cmd, string[] args)
{
string message;
if (player == null)
{
if (_pluginConfig.UseDiscordAuth)
{
message = Lang(LangKeys.DiscordAuthNotLinked);
}
else if (_pluginConfig.UseDiscordConnect)
{
message = Lang(LangKeys.DiscordConnectNotLinked);
}
else if (_pluginConfig.UseHooks)
{
message = Lang(LangKeys.DiscordHooksNotLinked);
}
else
{
message = Lang(LangKeys.HelpNotLinked, null, _pluginConfig.DiscordJoinCommand, _pluginConfig.HelpCommand);
}
}
else
{
message = Lang(LangKeys.HelpLinked, player, _pluginConfig.CommandsCommand, _pluginConfig.LeaveCommand, _pluginConfig.HelpCommand);
}
SendMessageToChannel(channelId, message);
return null;
}
public object HandleCommands(IPlayer player, string channelId, string cmd, string[] args)
{
string commands = $"Discord: {Lang(LangKeys.DiscordCommandsPreText, player)}\n";
foreach (KeyValuePair<string, DiscordCommand> command in _discordCommands)
{
if (!string.IsNullOrEmpty(command.Value.Permission) && !player.HasPermission(command.Value.Permission))
{
continue;
}
string commandText = $"/{command.Key}";
string botChannel = $" {(_pluginConfig.EnableBotChannel && command.Value.AllowInBotChannel ? Lang(LangKeys.DiscordCommandsBotChannelAllowed, player) : "")}";
string helpText = lang.GetMessage(command.Value.HelpText, command.Value.Plugin, player.Id);
commands += $"{commandText}{botChannel} - {helpText}\n";
}
SendMessageToChannel(channelId, commands);
return null;
}
public object HandleLeave(IPlayer player, string channelId, string cmd, string[] args)
{
SendMessageToChannel(channelId, "Discord: " + Lang(LangKeys.DiscordLeave, player));
HandleLeave(player, false);
return null;
}
public object HandlePlugins(IPlayer player, string channelId, string cmd, string[] args)
{
string pluginList = _discordCommands.Select(dc => dc.Value.PluginName).Distinct().Aggregate("Discord: " + Lang(LangKeys.DiscordPlugins, player), (current, plugin) => current + $"{plugin}\n");
SendMessageToChannel(channelId, pluginList);
return null;
}
#endregion
#region Discord Hooks
private void Discord_Ready(Ready ready)
{
try
{
_guildCount = ready.Guilds.Count;
if (_guildCount > 1)
{
if (string.IsNullOrEmpty(_pluginConfig.BotGuildId))
{
PrintError("Bot Guild Id is blank and the bot is in multiple guilds. " +
"Please set the bot guild id to the discord server you wish it to work for.");
return;
}
if (ready.Guilds.All(g => g.id != _pluginConfig.BotGuildId))
{
PrintError("Failed to find a matching guild for the Bot Guild Id. " +
"Please make sure your guild Id is correct and the bot is in that discord server.");
return;
}
}
_bot = ready.User;
Puts($"Connected to bot: {_bot.username}");
_connectionTimeout = timer.In(60f, () =>
{
CloseClient();
ConnectClient();
});
}
catch (Exception ex)
{
_connectionState = ConnectionStateEnum.Disconnected;
PrintError($"Failed to load DiscordCore: {ex}");
}
}
private void Discord_GuildCreate(Guild guild)
{
if (_connectionState != ConnectionStateEnum.Connecting)
{
return;
}
if (_guildCount != 1 && guild.id != _pluginConfig.BotGuildId)
{
return;
}
GuildConnected(guild);
}
private void GuildConnected(Guild guild)
{
try
{
_connectionTimeout?.Destroy();
_lastUpdate = DateTime.Now;
_discordServer = guild;
Puts($"Discord connected to server: {GetDiscordName()}");
_connectionState = ConnectionStateEnum.Connected;
if (!_initialized)
{
_initialized = true;
}
if (_pluginConfig.EnableBotChannel)
{
if (_discordServer.channels.All(c => c.name != _pluginConfig.BotChannel && c.id != _pluginConfig.BotChannel))
{
PrintWarning($"Bot channel is enabled but there is no channel found with name or id '{_pluginConfig.BotChannel}' on the discord server");
}
}
Puts($"{_discordServer.members.Count} Guild Discord Members Loaded");
if (_discordServer.members.All(m => m.user.bot ?? false))
{
PrintError($"You need to enable \"Presence Intent\" & \"Server Members Intent\" for {_bot.username} @ https://discord.com/developers/applications\n" +
"DiscordCore will not function correctly until that is fixed. Once updated please reload DiscordCore.");
return;
}
_client.RequestGuildMembers(guild.id);
}
catch (Exception ex)
{
_connectionState = ConnectionStateEnum.Disconnected;
PrintError($"Failed to connect to guild: {ex}");
}
}
private void Discord_GuildMembersChunk(GuildMembersChunk chunk)
{
_chunkLoading?.Destroy();
_chunkLoading = timer.In(1.5f, FinishChunk);
_chunkUsers.AddRange(chunk.members);
_lastUpdate = DateTime.Now;
}
private void FinishChunk()
{
_lastUpdate = DateTime.Now;
int added = 0;
foreach (GuildMember member in _chunkUsers)
{
if (_discordServer.members.All(m => m.user.id != member.user.id))
{
added++;
_discordServer.members.Add(member);
}
}
Puts($"{added} Chunk Discord Members Loaded");
Puts($"{_discordServer.members.Count} Total Discord Members Loaded");
timer.In(5f, () =>
{
foreach (KeyValuePair<string, DiscordInfo> info in _storedData.PlayerDiscordInfo.ToList())
{
if (_discordServer.members.All(m => m.user.id != info.Value.DiscordId))
{
IPlayer player = players.FindPlayerById(info.Key);
if (player != null)
{
HandleLeave(player, true);
Puts($"Player ({info.Value.PlayerId}) no longer in discord. Removing them from the data file.");
}
}
}
foreach (GuildMember member in _discordServer.members)
{
DiscordInfo existing = _storedData.LeftPlayerInfo[member.user.id];
if (existing != null)
{
HandleRejoin(member.user.id);
}
}
});
//Fixes issue in discord extension
NextTick(() =>
{
Interface.Call("OnDiscordCoreReady");
Puts($"Discord Core Ready");
});
}
private void DiscordSocket_HeartbeatSent()
{
_lastUpdate = DateTime.Now;
}
private void Discord_MessageCreate(Message message)
{
if (!_initialized)
{
return;
}
if (message.author.username == _bot.username)
{
Interface.Oxide.CallHook("OnDiscordBotMessage", message);
return;
}
bool botChannel = false;
string content = message.content;
Channel channel = _discordServer.channels.FirstOrDefault(c => c.id == message.channel_id);
if (channel != null)
{
bool isBotChannel = channel.name.ToLower().Equals(_pluginConfig.BotChannel.ToLower()) || channel.id == _pluginConfig.BotChannel;
bool shouldReturn = true;
if (!string.IsNullOrEmpty(_pluginConfig.DiscordJoinCommand) && content.ToLower().Equals("/" + _pluginConfig.DiscordJoinCommand))
{
if (_useExternal)
{
message.author.CreateDM(_client, pmChannel =>
{
pmChannel.CreateMessage(_client, Lang(LangKeys.DiscordCoreLinkingNotEnabled, null, _pluginConfig.DiscordJoinCommand));
});
}
else if (_pluginConfig.EnableJoinWrongChannelNonBotChannel && !isBotChannel || _pluginConfig.EnableJoinWrongChannelBotChannel && isBotChannel)
{
message.author.CreateDM(_client, pmChannel =>
{
pmChannel.CreateMessage(_client, Lang(LangKeys.DiscordJoinWrongChannel, null, _pluginConfig.DiscordJoinCommand));
});
}
NextTick(() =>
{
message.DeleteMessage(_client);
});
}
else if (_pluginConfig.EnableBotChannel
&& content.StartsWith("/")
&& isBotChannel)
{
string cmd = content.TrimStart('/').Split(' ')[0];
DiscordCommand command = _discordCommands[cmd];
if (command != null)
{
if (command.AllowInBotChannel)
{
botChannel = true;
}
else
{
NextTick(() =>
{
message.DeleteMessage(_client);
});
message.author.CreateDM(_client, pmChannel =>
{
pmChannel.CreateMessage(_client, Lang(LangKeys.DiscordNotAllowedBotChannel));
});
}
}
else
{
message.Reply(_client, Lang(LangKeys.UnknownCommand));
}
}
else if (_channelSubscriptions.ContainsKey(channel.id))
{
object output = null;
foreach (ChannelSubscription subscription in _channelSubscriptions[channel.id])
{
try
{
output = subscription.Method.Invoke(message);
}
catch (Exception ex)
{
PrintError($"Error invoking method on plugin {subscription.PluginName} on subscribed channel method {subscription.Method.Method.Name}:\n{ex}");
}
}
if (output != null)
{
shouldReturn = false;
}
}
if (shouldReturn && !botChannel)
{
return;
}
}
InGameActivation inGameActivation = _pendingInGameActivation[message.author.id];
if (inGameActivation != null)
{
HandleGameServerJoin(message, inGameActivation);
return;
}
if (content.Equals($"/{_pluginConfig.DiscordJoinCommand}", StringComparison.InvariantCultureIgnoreCase))
{
if (_useExternal)
{
SendMessageToUser(message.author.id, Lang(LangKeys.DiscordCoreLinkingNotEnabled, null, _pluginConfig.DiscordJoinCommand));
return;
}
HandleDiscordJoin(message);
return;
}
//Puts($"MessageCreate: {content}");
string id = GetDiscordId(message.author.id);
if (string.IsNullOrEmpty(id) && !botChannel)
{
if (!content.StartsWith("/"))
{
return;
}
if (!_pluginConfig.EnableLinking && !_useExternal)
{
return;
}
message.author.CreateDM(_client, dmChannel =>
{
if (_useExternal)
{
dmChannel.CreateMessage(_client, Lang(LangKeys.NotLinked, null, _pluginConfig.DiscordJoinCommand));
return;
}
dmChannel.CreateMessage(_client, Lang(LangKeys.DiscordMustSignUpBeforeCommands, null, _pluginConfig.DiscordJoinCommand));
});
return;
}
try
{
string playerId = GetPlayerId(id);
if (playerId == "server_console")
{
SendMessageToUser(message.author.id, Lang(LangKeys.DiscordPlayerIsServerConsole));
return;
}
IPlayer player = players.FindPlayerById(playerId);
if (content.StartsWith("/"))
{
string[] args;
string command;
ParseCommand(message.content.TrimStart('/'), out command, out args);
DiscordCommand discord = _discordCommands[command];
if (discord != null)
{
if (!string.IsNullOrEmpty(discord.Permission))
{
if (player == null)
{
SendMessageToUser(message.author.id, Lang(LangKeys.DiscordMustSignUpBeforeBotCommands, null, _pluginConfig.DiscordJoinCommand));
return;
}
if (!player.HasPermission(discord.Permission))
{
SendMessageToUser(player, Lang(LangKeys.NoPermission, player));
return;
}
}
discord.Method(player, message.channel_id, command, args);
}
else
{
SendMessageToUser(message.author.id, Lang(LangKeys.UnknownCommand, player));
}
}
else
{
Interface.Call("OnDiscordChat", player, message.content);
}
}
catch (Exception ex)
{
PrintError($"Error in message create:\n{ex}");
}
}
private void Discord_MemberAdded(GuildMember member)
{
HandleRejoin(member.user.id);
if (!_pluginConfig.EnabledJoinNotifications)
{
return;
}
member.user.CreateDM(_client, channel =>
{
channel.CreateMessage(_client, Lang(LangKeys.DiscordUserJoin, null, GetDiscordName(), _pluginConfig.DiscordJoinCommand, _pluginConfig.HelpCommand));
});
}
private void Discord_MemberRemoved(GuildMember member)
{
if (member?.user == null)
{
return;
}
DiscordInfo info = _storedData.GetInfoByDiscordId(member.user.id);
if (info == null)
{
return;
}
IPlayer player = players.FindPlayerById(info.PlayerId);
if (player == null)
{
return;
}
HandleLeave(player, true);
}
private void Discord_ChannelDelete(Channel channel)
{
// Delete all subscriptions to this channel for all plugins
_channelSubscriptions.Remove(channel.id);
foreach (KeyValuePair<string, Channel> cacheChannel in _channelCache.ToList())
{
if (channel.id == cacheChannel.Value.id)
{
_channelCache.Remove(cacheChannel.Key);
}
}
//TODO: Remove once fix is implemented in Discord Extension
_discordServer.channels.RemoveAll(c => c.id == channel.id);
}
private void Discord_UnhandledEvent(JObject messageObject)
{
if (_connectionState == ConnectionStateEnum.Connected)
{
PrintError("Discord Exception had an unhandled event. Discord Core will now try to reconnect");
timer.In(5f, () =>
{
CloseClient();
ConnectClient();
});
}
}
private void DiscordSocket_WebSocketErrored(Exception exception, string message)
{
if (_connectionState == ConnectionStateEnum.Connected)
{
PrintError("Discord Exception had a websocket error. Discord Core will now try to reconnect");
timer.In(5f, () =>
{
CloseClient();
ConnectClient();
});
}
}
private void DiscordSocket_WebSocketClosed(string reason, int code, bool clean)
{
if (_connectionState == ConnectionStateEnum.Connected)
{
PrintError("Discord Exception closed the websocket. Discord Core will now try to reconnect");
timer.In(5f, () =>
{
CloseClient();
ConnectClient();
});
}
}
#endregion
#region Joining
public void HandleGameServerJoin(Message message, InGameActivation activation)
{
if (activation.Code != message.content)
{
message.Reply(_client, Lang(LangKeys.GameJoinCodeNotMatch), false);
return;
}
_pendingInGameActivation.Remove(message.author.id);
_storedData.PlayerDiscordInfo[activation.PlayerId] = new DiscordInfo
{
DiscordId = message.author.id,
PlayerId = activation.PlayerId,
};
IPlayer player = covalence.Players.FindPlayer(activation.PlayerId);
SaveData();
Chat(player, Lang(LangKeys.DiscordJoinSuccessfullyRegistered, player));
message.Reply(_client, $"Discord: {Lang(LangKeys.DiscordJoinSuccessfullyRegistered, player)}", false);
string name = _discordServer.members.FirstOrDefault(m => m.user.id == message.author.id)?.nick;
SendMessageToChannel(_pluginConfig.LinkUnlinkAnnouncementChannel, Lang(LangKeys.LinkNotification, null, player.Name, player.Id, name, message.author.id));
Interface.Call("OnDiscordCoreJoin", player);
}
public void HandleDiscordJoin(Message message)
{
if (_storedData.PlayerDiscordInfo.Any(di => di.Value.DiscordId == message.author.id))
{
message.Reply(_client, Formatter.ToPlaintext(Lang(LangKeys.JoinAlreadySignedUp, null, _pluginConfig.DiscordJoinCommand)), false);
return;
}
string code = GenerateCode();
_pendingDiscordActivations.Add(new DiscordActivation
{
Code = code,
DiscordId = message.author.id,
ChannelId = message.channel_id,
});
message.Reply(_client, Lang(LangKeys.DiscordCompleteActivation, null, _pluginConfig.GameChatCommand, code), false);
}
public DiscordUser GetUserByUsername(string userName)
{
if (!IsDiscordCoreOnline())
{
return null;
}
return _discordServer.members.FirstOrDefault(m => $"{m?.user?.username?.ToLower()}#{m?.user?.discriminator}" == userName.ToLower())?.user;
}
public void CreateChannelActivation(IPlayer player, DiscordUser user, string code, string message)
{
user.CreateDM(_client, channel =>
{
channel.CreateMessage(_client, message);
_pendingInGameActivation[user.id] = new InGameActivation
{
PlayerId = player.Id,
Code = code,
ChannelId = channel.id,
DisplayName = player.Name
};
});
}
#endregion
#region Leaving
public void HandleLeave(IPlayer player, bool backup)
{
string discordId = GetDiscordId(player.Id);
Interface.Call("OnDiscordCoreLeave", player, discordId);
string name = _discordServer.members.FirstOrDefault(m => m.user.id == discordId)?.nick;
SendMessageToChannel(_pluginConfig.LinkUnlinkAnnouncementChannel, Lang(LangKeys.UnlinkNotification, null, player.Name, player.Id, name, discordId));
if (backup)
{
_storedData.LeftPlayerInfo[discordId] = _storedData.PlayerDiscordInfo[player.Id];
}
_storedData.PlayerDiscordInfo.Remove(player.Id);
SaveData();
}
public void HandleRejoin(string discordId)
{
if (!_pluginConfig.EnableLinking)
{
return;
}
DiscordInfo existing = _storedData.LeftPlayerInfo[discordId];
if (existing == null)
{
return;
}
_storedData.PlayerDiscordInfo[existing.PlayerId] = existing;
_storedData.LeftPlayerInfo.Remove(discordId);
IPlayer player = players.FindPlayerById(existing.PlayerId);
string name = _discordServer.members.FirstOrDefault(m => m.user.id == discordId)?.nick;
SendMessageToChannel(_pluginConfig.LinkUnlinkAnnouncementChannel, Lang(LangKeys.RejoinNotification, null, player.Name, player.Id, name, discordId));
Interface.Call("OnDiscordCoreJoin", existing.PlayerId, existing.DiscordId);
SaveData();
}
#endregion
#region API
#region Discord Server
[HookMethod("IsReady")]
public bool IsReady()
{
return _client?.DiscordServer != null;
}
[HookMethod("GetClient")]
public DiscordClient GetClient()
{
return _client;
}
[HookMethod("GetBot")]
public DiscordUser GetBot()
{
return _bot;
}
[HookMethod("GetServer")]
public Guild GetServer()
{
return _discordServer;
}
[HookMethod("GetDiscordName")]
public string GetDiscordName()
{
if (!string.IsNullOrEmpty(_pluginConfig.ServerNameOverride))
{
return _pluginConfig.ServerNameOverride;
}
return _discordServer?.name ?? "Not Connected";
}
[HookMethod("GetDiscordJoinCode")]
public string GetDiscordJoinCode()
{
return _pluginConfig.JoinCode;
}
[HookMethod("UpdatePresence")]
public void UpdatePresence(Presence presence)
{
try
{
_client?.UpdateStatus(presence);
}
catch
{
// ignored
}
}
[HookMethod("RegisterPluginForExtensionHooks")]
public void RegisterPluginForExtensionHooks(Plugin plugin)
{
if (!_client.Plugins.Contains(plugin))
{
_client.Plugins.Add(plugin);
}
if (!_hookPlugins.Contains(plugin))
{
_hookPlugins.Add(plugin);
}
}
#endregion
#region Send Message
#region Channel
[HookMethod("SendMessageToChannel")]
public void SendMessageToChannel(string channelNameOrId, string message)
{
Channel channel = GetChannel(channelNameOrId);
channel?.CreateMessage(_client, StripRustTags(Formatter.ToPlaintext(message)));
}
[HookMethod("SendMessageToChannel")]
public void SendMessageToChannel(string channelNameOrId, Embed message)
{
Channel channel = GetChannel(channelNameOrId);
channel?.CreateMessage(_client, message);
}
[HookMethod("SendMessageToChannel")]
public void SendMessageToChannel(string channelNameOrId, Message message)
{
Channel channel = GetChannel(channelNameOrId);
channel?.CreateMessage(_client, message);
}
#endregion
#region User
[HookMethod("SendMessageToUser")]
public void SendMessageToUser(string id, string message)
{
GetPlayerDmChannel(id, channel => { channel.CreateMessage(_client, StripRustTags(Formatter.ToPlaintext(message))); });
}
[HookMethod("SendMessageToUser")]
public void SendMessageToUser(IPlayer player, string message)
{
SendMessageToUser(player.Id, message);
}
[HookMethod("SendMessageToUser")]
public void SendMessageUser(string id, Embed message)
{
GetPlayerDmChannel(id, channel => { channel.CreateMessage(_client, message); });
}
[HookMethod("SendMessageToUser")]
public void SendMessageUser(string id, Message message)
{
GetPlayerDmChannel(id, channel => { channel.CreateMessage(_client, message); });
}
[HookMethod("SendMessageToUser")]
public void SendMessageUser(IPlayer player, Message message)
{
SendMessageUser(player.Id, message);
}
[HookMethod("UpdateUserNick")]
public void UpdateUserNick(strin
// ... [Code preview truncated due to length. Full source in attached file] ...