using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; 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.Constants; using Oxide.Ext.Discord.Entities; using Oxide.Ext.Discord.Entities.Applications; using Oxide.Ext.Discord.Entities.Gatway; using Oxide.Ext.Discord.Entities.Gatway.Events; using Oxide.Ext.Discord.Entities.Guilds; using Oxide.Ext.Discord.Entities.Permissions; using Oxide.Ext.Discord.Entities.Users; using Oxide.Ext.Discord.Extensions; using Oxide.Ext.Discord.Libraries.Linking; using Oxide.Ext.Discord.Logging; namespace Oxide.Plugins { [Info("Discord Sync", "Tricky & OuTSMoKE", "1.2.0")] [Description("Integrates players with the discord server")] public class DiscordSync : CovalencePlugin { #region Declared [DiscordClient] private DiscordClient Client; private readonly DiscordSettings _settings = new DiscordSettings { Intents = GatewayIntents.Guilds | GatewayIntents.GuildMembers }; private DiscordGuild _guild; private readonly DiscordLink _link = GetLibrary(); #endregion #region Config Configuration config; class Configuration { [JsonProperty(PropertyName = "Discord Bot Token")] public string BotToken = string.Empty; [JsonProperty(PropertyName = "Discord Server ID (Optional if bot only in 1 guild)")] public Snowflake GuildId { get; set; } = new Snowflake(); [JsonProperty(PropertyName = "Enable Nick Syncing")] public bool NickSync = false; [JsonProperty(PropertyName = "Enable Ban Syncing")] public bool BanSync = false; [JsonProperty(PropertyName = "Enable Role Syncing")] public bool RoleSync = true; // [JsonProperty(PropertyName = "Auto Reload Plugin")] // public bool AutoReloadPlugin { get; set; } // [JsonProperty(PropertyName = "Auto Reload Time (Seconds, Minimum 60)")] // public int AutoReloadTime { get; set; } [JsonConverter(typeof(StringEnumConverter))] [DefaultValue(DiscordLogLevel.Info)] [JsonProperty(PropertyName = "Discord Extension Log Level (Verbose, Debug, Info, Warning, Error, Exception, Off)")] public DiscordLogLevel ExtensionDebugging { get; set; } = DiscordLogLevel.Info; [JsonProperty(PropertyName = "Role Setup", ObjectCreationHandling = ObjectCreationHandling.Replace)] public List RoleSetup = new List { new RoleInfo { OxideGroup = "default", DiscordRole = "Member" }, new RoleInfo { OxideGroup = "vip", DiscordRole = "Donator" } }; public class RoleInfo { [JsonProperty(PropertyName = "Oxide Group")] public string OxideGroup; [JsonProperty(PropertyName = "Discord Role")] public string DiscordRole; } } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) throw new Exception(); } catch { Config.WriteObject(config, false, $"{Interface.Oxide.ConfigDirectory}/{Name}.jsonError"); PrintError("The configuration file contains an error and has been replaced with a default config.\n" + "The error configuration file was saved in the .jsonError extension"); LoadDefaultConfig(); } SaveConfig(); } protected override void LoadDefaultConfig() => config = new Configuration(); protected override void SaveConfig() => Config.WriteObject(config); #endregion #region Oxide / Discord Hooks [HookMethod(DiscordHooks.OnDiscordClientCreated)] private void OnDiscordClientCreated() { if (config.BotToken != string.Empty) { _settings.ApiToken = config.BotToken; _settings.LogLevel = config.ExtensionDebugging; Client.Connect(_settings); } else { PrintWarning("Please set the Discord Bot Token and reload the plugin"); } } void OnUserConnected(IPlayer player) { if (config.NickSync) HandleNick(player); if (config.BanSync) HandleBan(player); if (config.RoleSync) HandleRole(player); } private void OnServerInitialized() { if (!config.RoleSync) Unsubscribe(nameof(OnUserGroupAdded)); if (!config.RoleSync) Unsubscribe(nameof(OnUserGroupRemoved)); if (!config.BanSync) Unsubscribe(nameof(OnUserBanned)); if (!config.BanSync) Unsubscribe(nameof(OnUserUnbanned)); // var reloadtime = config.AutoReloadTime; // if (config.AutoReloadPlugin && config.AutoReloadTime > 59) // { // timer.Every(reloadtime, () => Reload()); // } } // Called when the client is created, and the plugin can use it [HookMethod(DiscordHooks.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; } DiscordGuild 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."); return; } if (Client.Bot.Application.Flags.HasValue && !Client.Bot.Application.Flags.Value.HasFlag(ApplicationFlags.GatewayGuildMembersLimited)) { 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; } _guild = guild; Puts($"Connected to bot: {Client.Bot.BotUser.Username}"); } [HookMethod(DiscordHooks.OnDiscordPlayerLinked)] private void OnAuthenticate(IPlayer player, DiscordUser user) { if (config.NickSync) HandleNick(player); if (config.BanSync) HandleBan(player); if (config.RoleSync) HandleRole(player); } private void OnUserNameUpdated(string id) { var player = players.FindPlayerById(id); if (player == null) return; HandleNick(player); } private void OnUserBanned(string name, string id) => OnUserUnbanned(name, id); private void OnUserUnbanned(string name, string id) { var player = players.FindPlayerById(id); if (player == null) return; HandleBan(player); } private void OnUserGroupAdded(string id, string groupName) => OnUserGroupRemoved(id, groupName); private void OnUserGroupRemoved(string id, string groupName) { config.RoleSetup.ForEach(roleSetup => { if (roleSetup.OxideGroup == groupName) HandleRole(id, roleSetup.DiscordRole, groupName); }); } #endregion #region Handle private void HandleNick(IPlayer player) { var discordId = player.GetDiscordUserId() ?? default(Snowflake); if (!discordId.IsValid()) return; var guildmember = GetGuildMember(discordId); if (guildmember == null) return; if (guildmember.Nickname == player.Name) return; _guild.ModifyUsersNick(Client, discordId, player.Name); } private void HandleBan(IPlayer player) { Snowflake discordId = player.GetDiscordUserId() ?? default(Snowflake); if (!discordId.IsValid()) return; if (GetGuildMember(discordId) == null) return; _guild.GetGuildBans(Client, bans => { if ((bans.Any(ban => ban.User.Id != discordId) || bans.Count() == 0) && player.IsBanned) { _guild.CreateGuildBan(Client, discordId, new GuildBanCreate { DeleteMessageDays = 0 }); } else if (bans.Any(ban => ban.User.Id == discordId) && !player.IsBanned) { _guild.RemoveGuildBan(Client, discordId); } }); } private void HandleRole(string id, string roleName, string oxideGroup) { Snowflake discordId = _link.GetDiscordId(id) ?? default(Snowflake); if (!discordId.IsValid()) return; var guildmember = GetGuildMember(discordId); if (guildmember == null) return; var role = GetRoleByName(roleName); if (role == null) { Puts($"Unable to find '{roleName}' discord role!"); return; } if (HasGroup(id, oxideGroup) && !UserHasRole(discordId, role.Id)) { _guild.AddGuildMemberRole(Client, guildmember.User, role); } else if (!HasGroup(id, oxideGroup) && UserHasRole(discordId, role.Id)) { _guild.RemoveGuildMemberRole(Client, guildmember.User, role); } } private void HandleRole(IPlayer player) { config.RoleSetup.ForEach(roleSetup => { GetGroups(player.Id).ToList().ForEach(playerGroup => { if (roleSetup.OxideGroup == playerGroup) HandleRole(player.Id, roleSetup.DiscordRole, playerGroup); }); }); } #endregion #region Helpers private bool HasGroup(string id, string groupName) => permission.UserHasGroup(id, groupName); private string[] GetGroups(string id) => permission.GetUserGroups(id); private DiscordRole GetRoleByName(string roleName) => _guild.GetRole(roleName); private GuildMember GetGuildMember(Snowflake discordId) => _guild.Members[discordId]; private bool UserHasRole(Snowflake discordId, Snowflake roleId) { return GetGuildMember(discordId).HasRole(roleId); } #endregion } }