C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Ext.Discord.Attributes;
using Oxide.Ext.Discord.Builders;
using Oxide.Ext.Discord.Cache;
using Oxide.Ext.Discord.Callbacks;
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.Interfaces;
using Oxide.Ext.Discord.Libraries;
using Oxide.Ext.Discord.Logging;
using Oxide.Ext.Discord.Types;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Random = Oxide.Core.Random;
//DiscordRoles created with PluginMerge v(1.0.10.0) by MJSU @ https://github.com/dassjosh/Plugin.Merge
namespace Oxide.Plugins {
[Info("Discord Roles", "MJSU", "3.0.0")]
[Description("Syncs players oxide group with discord roles")]
public partial class DiscordRoles : CovalencePlugin, IDiscordPlugin, IDiscordPool {
#region Plugins\DiscordRoles.Commands.cs
[Command("dcr.forcecheck")]
private void HandleCommand(IPlayer player, string cmd, string[] args) {
Logger.Info("Begin checking all players");
CheckLinkedPlayers();
}
#endregion
#region Plugins\DiscordRoles.Config.cs
protected override void LoadDefaultConfig() {
PrintWarning("Loading Default Config");
}
protected override void LoadConfig() {
base.LoadConfig();
Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
_config = AdditionalConfig(Config.ReadObject<PluginConfig>());
Config.WriteObject(_config);
}
private PluginConfig AdditionalConfig(PluginConfig config) {
config.EventSettings = new EventSettings(config.EventSettings);
config.LogSettings = new LogSettings(config.LogSettings);
config.ConflictSettings = new ConflictSettings(config.ConflictSettings);
config.SyncSettings ??= new List<SyncSettings> {
new("Default", default, SyncMode.Server), new("VIP", default, SyncMode.Discord)
}
;
config.PriorityGroupSettings ??= new List<PriorityGroupSettings> {
new(null)
}
;
config.Nickname = new NicknameSettings(config.Nickname);
for (int index = 0; index < config.SyncSettings.Count; index++) {
config.SyncSettings[index] = new SyncSettings(config.SyncSettings[index]);
}
for (int index = 0; index < config.PriorityGroupSettings.Count; index++) {
config.PriorityGroupSettings[index] = new PriorityGroupSettings(config.PriorityGroupSettings[index]);
}
return config;
}
#endregion
#region Plugins\DiscordRoles.DiscordCommands.cs
public void RegisterCommands() {
ApplicationCommandBuilder builder = new ApplicationCommandBuilder("roles", "Discord Roles Command", ApplicationCommandType.ChatInput).AddDefaultPermissions(PermissionFlags.Administrator);
AddPlayerSyncCommand(builder);
AddUserSyncCommand(builder);
CommandCreate cmd = builder.Build();
DiscordCommandLocalization loc = builder.BuildCommandLocalization();
TemplateKey command = new("Roles");
_localizations.RegisterCommandLocalizationAsync(this, command, loc, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0)).Then(_ => {
_localizations.ApplyCommandLocalizationsAsync(this, cmd, command).Then(() => {
Client.Bot.Application.CreateGlobalCommand(Client, builder.Build());
}
);
}
);
}
public void AddPlayerSyncCommand(ApplicationCommandBuilder builder) {
builder.AddSubCommand("player", "Sync Oxide Player", sub => {
sub.AddOption(CommandOptionType.String, "player", "Player to Sync", options => options.Required().AutoComplete());
}
);
}
public void AddUserSyncCommand(ApplicationCommandBuilder builder) {
builder.AddSubCommand("user", "Sync Discord User", sub => {
sub.AddOption(CommandOptionType.User, "user", "User to Sync", options => options.Required());
}
);
}
[DiscordApplicationCommand("roles", "player")]
private void HandlePlayerCommand(DiscordInteraction interaction, InteractionDataParsed parsed) {
string playerId = parsed.Args.GetString("player");
IPlayer player = players.FindPlayerById(playerId);
DiscordUser user = player?.GetDiscordUser();
if (user == null) {
interaction.CreateTemplateResponse(Client, InteractionResponseType.ChannelMessageWithSource, TemplateKeys.Commands.Player.Errors.NotLinked, null, GetDefault(player));
return;
}
QueueSync(new PlayerSyncRequest(player, user.Id, SyncEvent.None, false));
interaction.CreateTemplateResponse(Client, InteractionResponseType.ChannelMessageWithSource, TemplateKeys.Commands.Player.Queued, null, GetDefault(player, user));
}
[DiscordApplicationCommand("roles", "user")]
private void HandleUserCommand(DiscordInteraction interaction, InteractionDataParsed parsed) {
DiscordUser user = parsed.Args.GetUser("user");
IPlayer player = user?.Player;
if (player == null) {
interaction.CreateTemplateResponse(Client, InteractionResponseType.ChannelMessageWithSource, TemplateKeys.Commands.User.Errors.NotLinked, null, GetDefault(user));
return;
}
QueueSync(new PlayerSyncRequest(player, user.Id, SyncEvent.None, false));
interaction.CreateTemplateResponse(Client, InteractionResponseType.ChannelMessageWithSource, TemplateKeys.Commands.User.Queued, null, GetDefault(player, user));
}
[DiscordAutoCompleteCommand("roles", "player", "player")]
private void HandleNameAutoComplete(DiscordInteraction interaction, InteractionDataOption focused) {
string search = focused.GetString();
InteractionAutoCompleteBuilder response = interaction.GetAutoCompleteBuilder();
response.AddAllOnlineFirstPlayers(search, PlayerNameFormatter.ClanName);
interaction.CreateResponse(Client, response);
}
#endregion
#region Plugins\DiscordRoles.DiscordHooks.cs
[HookMethod(DiscordExtHooks.OnDiscordGatewayReady)]
private void OnDiscordGatewayReady(GatewayReadyEvent ready) {
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;
}
Guild = null;
if (ready.Guilds.Count == 1 && !_config.GuildId.IsValid()) {
Guild = ready.Guilds.Values.FirstOrDefault();
}
if (Guild == null) {
Guild = ready.Guilds[_config.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.");
}
}
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;
}
SubscribeAll();
RegisterTemplates();
RegisterCommands();
}
[HookMethod(DiscordExtHooks.OnDiscordGuildCreated)]
private void OnDiscordGuildCreated(DiscordGuild guild) {
if (guild.Id != Guild?.Id) {
return;
}
ValidateSyncs();
ValidateNickname();
RegisterSyncs();
}
public bool ProcessSyncSetting(BaseSyncSettings data, DiscordRole botMaxRole) {
if (!permission.GroupExists(data.GroupName)) {
Logger.Warning("Server group does not exist: '{0}'.Please create the group or correct the name in the config and reload the plugin.", data.GroupName);
return false;
}
DiscordRole role = Guild.Roles[data.RoleId];
if (role == null) {
Logger.Warning("Discord Role ID does not exist: '{0}'.Please fix the role ID in the config and reload the plugin", data.RoleId);
return false;
}
data.Role = role;
if (botMaxRole != null && role.Position > botMaxRole.Position) {
Logger.Warning("Discord Role '{0}' has a role position of {1} which is higher than the highest bot role {2} with position {3}.The bot will not be able to grant this role until this is fixed.", role.Name, role.Position, botMaxRole.Name, botMaxRole.Position);
}
if (!ProcessRoles.Contains(data.RoleId)) {
ProcessRoles.Add(data.RoleId);
}
return true;
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMembersLoaded)]
private void OnDiscordGuildMembersLoaded(DiscordGuild guild) {
if (guild.Id != Guild?.Id) {
return;
}
if (_config.EventSettings.IsAnyEnabled(SyncEvent.PluginLoaded)) {
timer.In(5f, CheckLinkedPlayers);
Logger.Debug("{0} Members have been loaded.Starting processing in 5 seconds.", Guild.Name);
}
Puts($"{Title} Ready");
}
[HookMethod(DiscordExtHooks.OnDiscordPlayerLinked)]
private void OnDiscordPlayerLinked(IPlayer player, DiscordUser user) {
Logger.Debug($"{nameof(OnDiscordPlayerLinked)} Added Player: {{0}}({{1}}) Discord: {{2}}({{3}}) to be processed", player.Name, player.Id, user.FullUserName, user.Id);
ProcessChange(player, SyncEvent.PlayerLinkedChanged);
}
[HookMethod(DiscordExtHooks.OnDiscordPlayerUnlinked)]
private void OnDiscordPlayerUnlinked(IPlayer player, DiscordUser user) {
Logger.Debug($"{nameof(OnDiscordPlayerUnlinked)} Added Player: {{0}}({{1}}) Discord: {{2}}({{3}}) to be processed", player.Name, player.Id, user.FullUserName, user.Id);
ProcessLeaving(player.Id, user.Id, SyncEvent.PlayerLinkedChanged);
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberAdded)]
private void OnDiscordGuildMemberAdded(GuildMemberAddedEvent member) {
if (member.GuildId != Guild.Id) {
return;
}
Logger.Debug($"{nameof(OnDiscordGuildMemberAdded)} Added {{0}}({{1}}) to be processed", member.User.FullUserName, member.Id);
HandleDiscordChange(member, SyncEvent.DiscordServerJoinLeave);
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberRemoved)]
private void OnDiscordGuildMemberRemoved(GuildMember member, DiscordGuild guild) {
if (guild.Id != Guild.Id) {
return;
}
Logger.Debug($"{nameof(OnDiscordGuildMemberRemoved)} Added {{0}}({{1}}) to be processed", member.User.FullUserName, member.Id);
HandleDiscordChange(Guild.GetMember(member.User.Id, true), SyncEvent.DiscordServerJoinLeave);
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberNicknameUpdated)]
private void OnDiscordGuildMemberNicknameUpdated(GuildMember member, string oldNickname, string newNickname, DateTime? lastNicknameUpdate) {
if(lastNicknameUpdate.HasValue && DateTime.UtcNow - lastNicknameUpdate.Value < TimeSpan.FromMinutes(_config.Nickname.TimeBetweenNicknameSync)) {
Logger.Debug($"{nameof(OnDiscordGuildMemberNicknameUpdated)} Skipped processing {{0}}({{1}}) to be processed because nickname was changed {{2:0.00}} minutes ago.", member.User.FullUserName, member.Id, (DateTime.UtcNow - lastNicknameUpdate.Value).TotalMinutes);
return;
}
Logger.Debug($"{nameof(OnDiscordGuildMemberRemoved)} Added {{0}}({{1}}) to be processed because nickname changed: {{2}} -> {{3}}", member.User.FullUserName, member.Id, oldNickname, newNickname);
HandleDiscordChange(member, SyncEvent.DiscordNicknameChanged);
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberRoleAdded)]
private void OnDiscordGuildMemberRoleAdded(GuildMember member, Snowflake roleId, DiscordGuild guild) {
if (ProcessRoles.Contains(roleId)) {
Logger.Debug($"{nameof(OnDiscordGuildMemberRemoved)} Added {{0}}({{1}}) to be processed because {{2}} role added.", member.User.FullUserName, member.Id, guild.Roles[roleId]?.Name ?? "Unknown Role");
HandleDiscordChange(member, SyncEvent.DiscordRoleChanged);
}
}
[HookMethod(DiscordExtHooks.OnDiscordGuildMemberRoleRemoved)]
private void OnDiscordGuildMemberRoleRemoved(GuildMember member, Snowflake roleId, DiscordGuild guild) {
if (ProcessRoles.Contains(roleId)) {
Logger.Debug($"{nameof(OnDiscordGuildMemberRemoved)} Added {{0}}({{1}}) to be processed because {{2}} role removed.", member.User.FullUserName, member.Id, guild.Roles[roleId]?.Name ?? "Unknown Role");
HandleDiscordChange(member, SyncEvent.DiscordRoleChanged);
}
}
public void HandleDiscordChange(GuildMember member, SyncEvent syncEvent) {
DiscordUser user = member.User;
Snowflake userId = user.Id;
PlayerId playerId = _link.GetPlayerId(member.Id);
if (!playerId.IsValid) {
Logger.Debug("Skipping {0} Sync for {1}({2}).Player is not linked", syncEvent, user.FullUserName, userId);
return;
}
IPlayer player = playerId.Player;
if (player == null) {
Logger.Debug("Skipping {0} Sync for {1}({2}).No IPlayer found", syncEvent, user.FullUserName, userId);
return;
}
ProcessQueue.RemoveAll(p => p.MemberId == userId && !p.IsLeaving);
QueueSync(new PlayerSyncRequest(player, member, syncEvent, false));
}
#endregion
#region Plugins\DiscordRoles.Fields.cs
// ReSharper disable once UnassignedField.Global
public DiscordClient Client {
get;
set;
}
public DiscordPluginPool Pool {
get;
set;
}
[PluginReference]
#pragma warning disable CS0649
// ReSharper disable InconsistentNaming
private Plugin AntiSpam, Clans;
// ReSharper restore InconsistentNaming
#pragma warning restore CS0649
public PluginConfig _config;
public PluginData Data;
public DiscordGuild Guild;
private const string AccentColor = "#de8732";
private readonly DiscordLink _link = GetLibrary<DiscordLink>();
private readonly DiscordPlaceholders _placeholders = GetLibrary<DiscordPlaceholders>();
public readonly DiscordMessageTemplates Templates = GetLibrary<DiscordMessageTemplates>();
private readonly DiscordCommandLocalizations _localizations = GetLibrary<DiscordCommandLocalizations>();
public ILogger Logger;
public readonly List<BaseHandler> SyncHandlers = new();
public readonly List<Snowflake> ProcessRoles = new();
public readonly List<PlayerSyncRequest> ProcessQueue = new();
public readonly Hash<string, RecentSyncData> RecentSync = new();
public static DiscordRoles Instance;
#endregion
#region Plugins\DiscordRoles.Helpers.cs
public string GetPlayerName(IPlayer player) {
string playerName = player.Name;
if (_config.Nickname.UseAntiSpam && AntiSpam is {
IsLoaded : true
}
) {
playerName = AntiSpam.Call<string>("GetClearName", player);
if (string.IsNullOrEmpty(playerName)) {
Logger.Warning("AntiSpam returned an empty string for '{0}'", player.Name);
playerName = player.Name;
}
else if (!playerName.Equals(player.Name)) {
Logger.Debug("Nickname '{0}' was filtered by AntiSpam to '{1}'", player.Name, playerName);
}
}
if (_config.Nickname.SyncClanTag && Clans is {
IsLoaded : true
}
) {
string tag = Clans.Call<string>("GetClanOf", player.Id);
if (!string.IsNullOrEmpty(tag)) {
playerName = Lang(LangKeys.ClanTag, player, tag, playerName);
}
}
if (playerName.Length > 32) {
playerName = playerName.Substring(0, 32);
}
return playerName;
}
public RecentSyncData GetRecentSync(string playerId) {
RecentSyncData data = RecentSync[playerId];
if (data == null) {
data = new RecentSyncData(_config.ConflictSettings, playerId);
RecentSync[playerId] = data;
}
return data;
}
public void SaveData(bool force = false) {
if (Data == null) {
return;
}
if(!Data.HasChanged || !force) {
return;
}
Interface.Oxide.DataFileSystem.WriteObject(Name, Data);
Data.OnSaved();
}
#endregion
#region Plugins\DiscordRoles.Hooks.cs
private void OnUserConnected(IPlayer player) {
Logger.Debug($"{nameof(OnUserConnected)} Added Player: {{0}}({{1}}) to be processed", player.Name, player.Id);
ProcessChange(player, SyncEvent.PlayerConnected);
}
private void OnUserGroupAdded(string id, string group) {
IPlayer player = ServerPlayerCache.Instance.GetPlayerById(id);
Logger.Debug($"{nameof(OnUserGroupAdded)} Added Player: {{0}}({{1}}) to be processed because added to group {{2}}", player.Name, player.Id, group);
ProcessChange(player, SyncEvent.ServerGroupChanged);
}
private void OnUserGroupRemoved(string id, string group) {
IPlayer player = ServerPlayerCache.Instance.GetPlayerById(id);
Logger.Debug($"{nameof(OnUserGroupRemoved)} Added Player: {{0}}({{1}}) to be processed because removed from group {{2}}", player.Name, player.Id, group);
ProcessChange(player, SyncEvent.ServerGroupChanged);
}
public void ProcessChange(IPlayer player, SyncEvent syncEvent) {
if (player == null) {
return;
}
Snowflake userId = _link.GetDiscordId(player);
ProcessQueue.RemoveAll(p => p.Player.Id == player.Id && !p.IsLeaving);
QueueSync(new PlayerSyncRequest(player, userId, syncEvent, false));
}
public void ProcessLeaving(string playerId, Snowflake discordId, SyncEvent syncEvent) {
ProcessQueue.RemoveAll(p => p.Player.Id == playerId);
IPlayer player = players.FindPlayerById(playerId);
if (player != null && discordId.IsValid()) {
QueueSync(new PlayerSyncRequest(player, discordId, syncEvent, true));
}
}
#endregion
#region Plugins\DiscordRoles.HookSubscriptions.cs
public void SubscribeAll() {
SubscribeOxideAll();
SubscribeDiscordAll();
}
public void UnsubscribeAll() {
UnsubscribeOxideAll();
UnsubscribeDiscordAll();
}
public void SubscribeOxideAll() {
try {
Subscribe(nameof(OnUserConnected));
Subscribe(nameof(OnUserGroupAdded));
Subscribe(nameof(OnUserGroupRemoved));
Subscribe(nameof(OnDiscordPlayerLinked));
Subscribe(nameof(OnDiscordPlayerUnlinked));
}
catch {
}
}
public void SubscribeDiscordAll() {
Subscribe(nameof(OnDiscordGuildMemberNicknameUpdated));
Subscribe(nameof(OnDiscordGuildMemberRoleAdded));
Subscribe(nameof(OnDiscordGuildMemberRoleRemoved));
Subscribe(nameof(OnDiscordGuildMemberAdded));
Subscribe(nameof(OnDiscordGuildMemberRemoved));
}
public void UnsubscribeOxideAll() {
try {
Unsubscribe(nameof(OnUserConnected));
Unsubscribe(nameof(OnUserGroupAdded));
Unsubscribe(nameof(OnUserGroupRemoved));
Unsubscribe(nameof(OnDiscordPlayerLinked));
Unsubscribe(nameof(OnDiscordPlayerUnlinked));
}
catch {
// ignored
}
}
public void UnsubscribeDiscordAll() {
Unsubscribe(nameof(OnDiscordGuildMemberNicknameUpdated));
Unsubscribe(nameof(OnDiscordGuildMemberRoleAdded));
Unsubscribe(nameof(OnDiscordGuildMemberRoleRemoved));
Unsubscribe(nameof(OnDiscordGuildMemberAdded));
Unsubscribe(nameof(OnDiscordGuildMemberRemoved));
}
#endregion
#region Plugins\DiscordRoles.Lang.cs
public void RegisterLang() {
HashSet<string> server = new();
HashSet<string> player = new();
Dictionary<string, string> loc = new() {
[LangKeys.Chat]
= $"[#BEBEBE][[{AccentColor}]{Title}[/#]] {{0}}[/#]", [LangKeys.ClanTag] = "[{0}] {1}"
}
;
for (int index = 0; index < _config.SyncSettings.Count; index++) {
SyncSettings settings = _config.SyncSettings[index];
HandleServerRegistration(settings.ServerNotifications, loc, server);
HandlePlayerRegistration(settings.PlayerNotifications, loc, player);
}
for (int index = 0; index < _config.PriorityGroupSettings.Count; index++) {
PriorityGroupSettings group = _config.PriorityGroupSettings[index];
HandleServerRegistration(group.ServerNotifications, loc, server);
HandlePlayerRegistration(group.PlayerNotifications, loc, player);
}
lang.RegisterMessages(loc, this);
}
public void HandleServerRegistration(ServerNotificationSettings server, Dictionary<string, string> loc, HashSet<string> registered) {
server.Initialize();
if (registered.Add(server.LocalizationKey)) {
server.AddLocalizations(loc);
}
}
public void HandlePlayerRegistration(PlayerNotificationSettings player, Dictionary<string, string> loc, HashSet<string> registered) {
player.Initialize();
if (registered.Add(player.LocalizationKey)) {
player.AddLocalizations(loc);
}
}
public void Chat(string message) {
server.Broadcast(Lang(LangKeys.Chat, null, message));
}
public void Chat(IPlayer player, string message) {
server.Broadcast(Lang(LangKeys.Chat, player, message));
}
public string Lang(string key, IPlayer player = null) => lang.GetMessage(key, this, player?.Id);
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 string Lang(string key, PlaceholderData data, IPlayer player = null) {
string message = Lang(key, player);
if (data != null) {
message = _placeholders.ProcessPlaceholders(message, data);
}
return message;
}
#endregion
#region Plugins\DiscordRoles.Notifications.cs
public void SendSyncNotification(PlayerSyncRequest request, BaseSyncSettings sync, INotificationSettings settings, NotificationType type) {
using PlaceholderData data = GetDefault(request.Player, request.Member?.User ?? EntityCache<DiscordUser>.Instance.Get(request.MemberId), sync);
data.ManualPool();
if (settings.ServerNotifications.CanSendNotification(type)) {
SendServerMessage(settings.ServerNotifications, data, type);
SendDiscordMessage(settings.ServerNotifications, data, type);
}
if (settings.PlayerNotifications.CanSendNotification(type)) {
SendPlayerMessage(request.Player, settings.PlayerNotifications, data, type);
if (!request.IsLeaving) {
SendDiscordPmMessage(request.Player, settings.PlayerNotifications, data, type);
}
}
}
public void SendServerMessage(ServerNotificationSettings settings, PlaceholderData data, NotificationType type) {
if (settings.SendMessageToServer) {
Chat(Lang(settings.GetLocalizationKey(type), data));
}
}
public void SendDiscordMessage(ServerNotificationSettings settings, PlaceholderData data, NotificationType type) {
DiscordChannel channel = Guild.Channels[settings.DiscordMessageChannelId];
channel?.CreateGlobalTemplateMessage(Client, settings.GetLocalizationTemplate(type), null, data);
}
public void SendPlayerMessage(IPlayer player, PlayerNotificationSettings settings, PlaceholderData data, NotificationType type) {
if (settings.SendMessageToPlayer && player.IsConnected) {
Chat(player, Lang(settings.GetLocalizationKey(type), data, player));
}
}
public void SendDiscordPmMessage(IPlayer player, PlayerNotificationSettings settings, PlaceholderData data, NotificationType type) {
if (settings.SendDiscordPm) {
player.SendDiscordTemplateMessage(Client, settings.GetLocalizationTemplate(type), null, data);
}
}
#endregion
#region Plugins\DiscordRoles.Placeholders.cs
public void RegisterPlaceholders() {
_placeholders.RegisterPlaceholder<BaseSyncSettings, string>(this, PlaceholderKeys.Group, PlaceholderDataKeys.Sync, settings => settings.GroupName);
}
public PlaceholderData GetDefault(IPlayer player, DiscordUser user, BaseSyncSettings settings) {
return _placeholders.CreateData(this).AddPlayer(player).AddUser(user).Add(PlaceholderDataKeys.Sync, settings).AddRole(settings.Role);
}
public PlaceholderData GetDefault(IPlayer player) {
return _placeholders.CreateData(this).AddPlayer(player);
}
public PlaceholderData GetDefault(DiscordUser user) {
return _placeholders.CreateData(this).AddUser(user);
}
public PlaceholderData GetDefault(IPlayer player, DiscordUser user) {
return _placeholders.CreateData(this).AddPlayer(player).AddUser(user);
}
#endregion
#region Plugins\DiscordRoles.Processing.cs
private bool _isReadyForProcessing;
public void CheckLinkedPlayers() {
if (!IsDiscordLinkEnabled()) {
return;
}
IReadOnlyDictionary<PlayerId, Snowflake> links = _link.PlayerToDiscordIds;
foreach (KeyValuePair<PlayerId, Snowflake> link in links) {
IPlayer player = link.Key.Player;
if (player != null) {
QueueSync(new PlayerSyncRequest(player, link.Value, SyncEvent.PluginLoaded, false));
}
}
Logger.Info("Starting sync for {0} linked players", ProcessQueue.Count);
ProcessNextSync();
_isReadyForProcessing = true;
}
private bool IsDiscordLinkEnabled() {
if (!_link.IsEnabled) {
PrintWarning("No Discord Link plugin registered! This plugin will not work until one is added.Please add a Discord Link plugin and reload this plugin.");
return false;
}
return true;
}
public void QueueSync(PlayerSyncRequest sync) {
if (!IsDiscordLinkEnabled()) {
return;
}
if (!sync.MemberId.IsValid()) {
Logger.Debug("Skipping Sync: MemberId is invalid.Player ID: {0}({1})", sync.Player.Name, sync.Player.Id);
return;
}
if (ProcessQueue.Count <= 3) {
ProcessQueue.Add(sync);
if (_isReadyForProcessing && ProcessQueue.Count == 1) {
ProcessNextSync();
}
}
else {
ProcessQueue.Insert(3, sync);
}
}
public void ProcessNextSync() {
if (ProcessQueue.Count == 0) {
return;
}
PlayerSyncRequest request = ProcessQueue[0];
ProcessQueue.RemoveAt(0);
Promise promise = Promise.Create();
promise.Finally(ProcessNextSync);
ProcessUser(request, promise);
}
public void ProcessUser(PlayerSyncRequest request, Promise promise) {
ProcessUserCallback callback = ProcessUserCallback.Create(request, this, promise);
request.GetGuildMember().Finally(callback.RunAction);
}
public void HandleUserNick(PlayerSyncRequest request) {
if (!_config.Nickname.SyncNicknames) {
Logger.Debug("Skipping Nickname Sync: Nickname sync is disabled.");
return;
}
if (!_config.EventSettings.IsNicknameEnabled(request.Event)) {
Logger.Debug("Skipping Nickname Sync: Nickname sync is disabled for event: {0}.", request.Event);
return;
}
if (request.IsLeaving) {
Logger.Debug("Skipping Nickname Sync: Member is leaving.");
return;
}
if (request.Member.User.Id == Guild.OwnerId) {
Logger.Debug("Skipping Nickname Sync: Member is Discord Server Owner.");
return;
}
string playerName = GetPlayerName(request.Player);
if (playerName.Equals(request.Member.Nickname, StringComparison.Ordinal)) {
Logger.Debug("Skipping Nickname Sync: Member Nickname matches '{0}' expected value", playerName);
return;
}
Logger.Debug("Updating {0}'s Discord Nickname {1} -> {2}", request.PlayerName, request.Member.DisplayName, playerName);
string oldNickname = request.Member.Nickname;
Guild.EditMemberNick(Client, request.Member.User.Id, playerName).Then(member => {
Logger.Info("Successfully updated {0}'s Discord Nickname {1} -> {2}.Discord nickname now has the value: {3}", request.PlayerName, oldNickname, playerName, member.Nickname);
}
).Catch<ResponseError>(error => {
Logger.Error("An error has occured updating {0}'s discord server nickname to {1}.\n{2}", request.Member.DisplayName, playerName);
}
);
}
#endregion
#region Plugins\DiscordRoles.Setup.cs
private void Init() {
Instance = this;
Logger = DiscordLoggerFactory.Instance.CreateLogger(this, _config.LogSettings.PluginLogLevel, _config.LogSettings);
Data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
UnsubscribeAll();
RegisterLang();
}
private void OnServerInitialized() {
if (string.IsNullOrEmpty(_config.DiscordApiKey)) {
PrintWarning("Please enter your bot token in the config and reload the plugin.");
return;
}
if (_config.Nickname.UseAntiSpam) {
if (AntiSpam is not {
IsLoaded : true
}
) {
PrintWarning("AntiSpam is enabled in the config but is not loaded." + "Please disable the setting in the config or load AntiSpam: https://umod.org/plugins/anti-spam");
}
else if (AntiSpam.Version < new VersionNumber(2, 1, 3)) {
PrintWarning("AntiSpam requires version >= 2.1.3." + "Please update the plugin @ https://umod.org/plugins/anti-spam");
}
}
RegisterPlaceholders();
Client.Connect(new BotConnection {
ApiToken = _config.DiscordApiKey, LogLevel = _config.ExtensionDebugging, Intents = GatewayIntents.Guilds | GatewayIntents.GuildMembers
}
);
}
private void OnServerSave() => SaveData();
private void Unload() {
SaveData(true);
Instance = null;
}
#endregion
#region Plugins\DiscordRoles.SyncSetup.cs
public void ValidateSyncs() {
GuildMember botMember = Guild.Members[Client.Bot.BotUser.Id];
DiscordRole botMaxRole = botMember.Roles.Select(r => Guild.Roles[r]).OrderByDescending(r => r.Position).FirstOrDefault();
for (int index = _config.SyncSettings.Count - 1; index >= 0; index--) {
if (!ProcessSyncSetting(_config.SyncSettings[index], botMaxRole)) {
_config.SyncSettings.RemoveAt(index);
}
}
for (int index = _config.PriorityGroupSettings.Count - 1; index >= 0; index--) {
PriorityGroupSettings group = _config.PriorityGroupSettings[index];
for (int i = group.SyncSettings.Count - 1; i >= 0; i--) {
if (!ProcessSyncSetting(group.SyncSettings[i], botMaxRole)) {
group.SyncSettings.RemoveAt(index);
}
}
if (group.SyncSettings.Count == 0) {
_config.PriorityGroupSettings.RemoveAt(index);
}
}
}
public void ValidateNickname() {
if (_config.Nickname.SyncNicknames) {
PermissionFlags botPerms = Guild.GetUserPermissions(Client.Bot.BotUser.Id);
if ((botPerms & PermissionFlags.ManageNicknames) == 0) {
Logger.Warning("Sync nicknames is enabled but Discord Bot {0} does not have permission to change {1} Discord Server nicknames.Please grant the {0} bot permission to Manage Nicknames or disable sync nicknames.", Client.Bot.BotUser.FullUserName, Guild.Name);
}
}
}
public void RegisterSyncs() {
SyncHandlers.AddRange(_config.SyncSettings.Where(s => s.SyncMode == SyncMode.Bidirectional).Select(s => new BidirectionalSyncHandler(s)));
SyncHandlers.AddRange(_config.SyncSettings.Where(s => s.SyncMode == SyncMode.Server).GroupBy(s => s.RoleId).Select(s => new ServerSyncHandler(s.ToList())));
SyncHandlers.AddRange(_config.SyncSettings.Where(s => s.SyncMode == SyncMode.Discord).GroupBy(s => s.GroupName).Select(s => new DiscordSyncHandler(s.Key, s.ToList())));
SyncHandlers.AddRange(_config.PriorityGroupSettings.Select(p => new PrioritySyncHandler(p)));
}
#endregion
#region Plugins\DiscordRoles.Templates.cs
public void RegisterTemplates() {
DiscordMessageTemplate queued = CreatePrefixedTemplateEmbed($"Player: {DefaultKeys.Player.Name}({DefaultKeys.Player.Id}) User: {DefaultKeys.User.Mention} has been queued to be synced.", DiscordColor.Success);
Templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Player.Queued, queued, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
Templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.User.Queued, queued, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
DiscordMessageTemplate playerNotLinked = CreatePrefixedTemplateEmbed($"Player: {DefaultKeys.Player.Name}({DefaultKeys.Player.Id}) is not linked and cannot be queued to be synced", DiscordColor.Danger);
Templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.Player.Errors.NotLinked, playerNotLinked, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
DiscordMessageTemplate userNotLinked = CreatePrefixedTemplateEmbed($"User: {DefaultKeys.User.Mention} is not linked and cannot be queued to be synced", DiscordColor.Danger);
Templates.RegisterLocalizedTemplateAsync(this, TemplateKeys.Commands.User.Errors.NotLinked, userNotLinked, new TemplateVersion(1, 0, 0), new TemplateVersion(1, 0, 0));
}
public DiscordMessageTemplate CreatePrefixedTemplateEmbed(string description, DiscordColor color) {
return new DiscordMessageTemplate {
Embeds = new List<DiscordEmbedTemplate> {
new() {
Description = $"[{DefaultKeys.Plugin.Title}] {description}", Color = color.ToHex()
}
}
}
;
}
#endregion
#region Configuration\ConflictSettings.cs
public class ConflictSettings {
public int GroupConflictLimit {
get;
set;
}
public int RoleConflictLimit {
get;
set;
}
[JsonConstructor]
public ConflictSettings() {
}
public ConflictSettings(ConflictSettings settings) {
GroupConflictLimit = settings?.GroupConflictLimit ?? 3;
RoleConflictLimit = settings?.RoleConflictLimit ?? 3;
}
}
#endregion
#region Configuration\EnabledSyncEvents.cs
public class EnabledSyncEvents {
[JsonProperty("Sync On Plugin Load")]
public bool SyncOnPluginLoad {
get;
set;
}
[JsonProperty("Sync On Player Connected")]
public bool SyncOnPlayerConnected {
get;
set;
}
[JsonProperty("Sync On Server Group Changed")]
public bool SyncOnServerGroupChanged {
get;
set;
}
[JsonProperty("Sync On Discord Role Changed")]
public bool SyncOnDiscordRoleChanged {
get;
set;
}
[JsonProperty("Sync On Discord Nickname Changed")]
public bool SyncOnDiscordNicknameChanged {
get;
set;
}
[JsonProperty("Sync On Player Linked / Unlinked")]
public bool SyncOnLinkedChanged {
get;
set;
}
[JsonProperty("Sync On User Join / Leave Discord Server")]
public bool SyncOnDiscordServerJoinLeave {
get;
set;
}
public bool IsEnabled(SyncEvent syncEvent) {
switch (syncEvent) {
case SyncEvent.None : return false;
case SyncEvent.PluginLoaded : return SyncOnPluginLoad;
case SyncEvent.PlayerConnected : return SyncOnPlayerConnected;
case SyncEvent.ServerGroupChanged : return SyncOnServerGroupChanged;
case SyncEvent.DiscordRoleChanged : return SyncOnDiscordRoleChanged;
case SyncEvent.DiscordNicknameChanged : return SyncOnDiscordNicknameChanged;
case SyncEvent.PlayerLinkedChanged : return SyncOnLinkedChanged;
case SyncEvent.DiscordServerJoinLeave : return SyncOnDiscordServerJoinLeave;
default : throw new ArgumentOutOfRangeException(nameof(syncEvent), syncEvent, null);
}
}
}
#endregion
#region Configuration\EventSettings.cs
public class EventSettings {
[JsonProperty("Events To Sync Server Groups -> Discord Roles")]
public EnabledSyncEvents ServerSync {
get;
set;
}
[JsonProperty("Events To Sync Discord Roles -> Server Groups")]
public EnabledSyncEvents DiscordSync {
get;
set;
}
[JsonProperty("Events To Sync Discord Nickname")]
public EnabledSyncEvents NicknameSync {
get;
set;
}
[JsonConstructor]
public EventSettings() {
}
public EventSettings(EventSettings settings) {
ServerSync = new EnabledSyncEvents {
SyncOnPluginLoad = settings?.ServerSync?.SyncOnPluginLoad ?? true, SyncOnPlayerConnected = settings?.ServerSync?.SyncOnPlayerConnected ?? true, SyncOnServerGroupChanged = settings?.ServerSync?.SyncOnServerGroupChanged ?? true, SyncOnDiscordRoleChanged = settings?.ServerSync?.SyncOnDiscordRoleChanged ?? true, SyncOnDiscordNicknameChanged = settings?.ServerSync?.SyncOnDiscordNicknameChanged ?? false, SyncOnLinkedChanged = settings?.ServerSync?.SyncOnLinkedChanged ?? true, SyncOnDiscordServerJoinLeave = settings?.ServerSync?.SyncOnDiscordServerJoinLeave ?? true
}
;
DiscordSync = new EnabledSyncEvents {
SyncOnPluginLoad = settings?.DiscordSync?.SyncOnPluginLoad ?? true, SyncOnPlayerConnected = settings?.DiscordSync?.SyncOnPlayerConnected ?? true, SyncOnServerGroupChanged = settings?.DiscordSync?.SyncOnServerGroupChanged ?? true, SyncOnDiscordRoleChanged = settings?.DiscordSync?.SyncOnDiscordRoleChanged ?? true, SyncOnDiscordNicknameChanged = settings?.DiscordSync?.SyncOnDiscordNicknameChanged ?? false, SyncOnLinkedChanged = settings?.DiscordSync?.SyncOnLinkedChanged ?? true, SyncOnDiscordServerJoinLeave = settings?.DiscordSync?.SyncOnDiscordServerJoinLeave ?? true
}
;
NicknameSync = new EnabledSyncEvents {
SyncOnPluginLoad = settings?.NicknameSync?.SyncOnPluginLoad ?? true, SyncOnPlayerConnected = settings?.NicknameSync?.SyncOnPlayerConnected ?? true, SyncOnServerGroupChanged = settings?.NicknameSync?.SyncOnServerGroupChanged ?? false, SyncOnDiscordRoleChanged = settings?.NicknameSync?.SyncOnDiscordRoleChanged ?? false, SyncOnDiscordNicknameChanged = settings?.NicknameSync?.SyncOnDiscordNicknameChanged ?? true, SyncOnLinkedChanged = settings?.NicknameSync?.SyncOnLinkedChanged ?? true, SyncOnDiscordServerJoinLeave = settings?.NicknameSync?.SyncOnDiscordServerJoinLeave ?? false
}
;
}
public bool IsServerEnabled(SyncEvent syncEvent) {
return ServerSync.IsEnabled(syncEvent);
}
public bool IsDiscordEnabled(SyncEvent syncEvent) {
return DiscordSync.IsEnabled(syncEvent);
}
public bool IsNicknameEnabled(SyncEvent syncEvent) {
return NicknameSync.IsEnabled(syncEvent);
}
public bool IsAnyEnabled(SyncEvent syncEvent) {
return IsServerEnabled(syncEvent) || IsDiscordEnabled(syncEvent) || IsNicknameEnabled(syncEvent);
}
}
#endregion
#region Configuration\LogSettings.cs
public class LogSettings : IDiscordLoggingConfig {
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty(PropertyName = "Plugin Log Level (Verbose, Debug, Info, Warning, Error, Exception, Off)")]
public DiscordLogLevel PluginLogLevel {
get;
set;
}
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty(PropertyName = "Console Log Level (Verbose, Debug, Info, Warning, Error, Exception, Off)")]
public DiscordLogLevel ConsoleLogLevel {
get;
}
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty(PropertyName = "File Log Level (Verbose, Debug, Info, Warning, Error, Exception, Off)")]
public DiscordLogLevel FileLogLevel {
get;
}
[JsonProperty(PropertyName = "File DateTime Format")]
public string FileDateTimeFormat {
get;
}
[JsonProperty(PropertyName = "Console Log Format")]
public string ConsoleLogFormat {
get;
}
[JsonConstructor]
public LogSettings() {
}
public LogSettings(LogSettings settings) {
PluginLogLevel = settings?.PluginLogLevel ?? DiscordLogLevel.Info;
ConsoleLogLevel = settings?.ConsoleLogLevel ?? DiscordLogLevel.Info;
FileLogLevel = settings?.FileLogLevel ?? DiscordLogLevel.Off;
ConsoleLogFormat = settings?.ConsoleLogFormat ?? "[DiscordRoles] [{0}]: {1}";
FileDateTimeFormat = settings?.FileDateTimeFormat ?? "HH:mm:ss";
}
}
#endregion
#region Configuration\NicknameSettings.cs
public class NicknameSettings {
[DefaultValue(false)]
[JsonProperty(PropertyName = "Sync Nicknames")]
public bool SyncNicknames {
get;
set;
}
[DefaultValue(5f)]
[JsonProperty(PropertyName = "Minimum time between syncing player nicknames (Minutes)")]
public float TimeBetweenNicknameSync {
get;
set;
}
[DefaultValue(false)]
[JsonProperty(PropertyName = "Sync Clan Tag")]
public bool SyncClanTag {
get;
set;
}
[DefaultValue(false)]
[JsonProperty(PropertyName = "Use AntiSpam On Discord Nickname")]
public bool UseAntiSpam {
get;
set;
}
public NicknameSettings(NicknameSettings settings) {
SyncNicknames = settings?.SyncNicknames ?? false;
TimeBetweenNicknameSync = settings?.TimeBetweenNicknameSync ?? 5f;
SyncClanTag = settings?.SyncClanTag ?? false;
UseAntiSpam = settings?.UseAntiSpam ?? fals
// ... [Code preview truncated due to length. Full source in attached file] ...