using System; using System.Collections.Generic; using Newtonsoft.Json; using Oxide.Core; using UnityEngine; using Oxide.Core.Plugins; using Oxide.Ext.Discord.Clients; using Oxide.Ext.Discord.Entities; using Oxide.Ext.Discord.Libraries; using Oxide.Ext.Discord.Interfaces; using Oxide.Ext.Discord.Constants; using Oxide.Core.Libraries.Covalence; using Oxide.Ext.Discord.Attributes; using Oxide.Ext.Discord.Connections; using Oxide.Ext.Discord.Builders; using System.Linq; using System.Text; using ConVar; using System.Text.RegularExpressions; using Facepunch; using Facepunch.Models; namespace Oxide.Plugins { [Info("DiscordCore", "ThePitereq", "2.0.0")] public class DiscordCore : RustPlugin, IDiscordPlugin, IDiscordLink { [PluginReference] private readonly Plugin EnablePvP, Friends, Clans, PlaytimeAPI, PopUpAPI, ChatNations, BetterChatMute; public DiscordClient Client { get; set; } private static Snowflake GUILD_ID; private readonly DiscordLink _link = Interface.Oxide.GetLibrary(); private static DiscordGuild guild; private readonly DiscordAppCommand _appCommand = Interface.Oxide.GetLibrary(); private readonly BotConnection _discordSettings = new(); private static readonly Dictionary generatedCodes = new(); private static DiscordChannel chatChannel; private static readonly List cupboardAuth = new(); private static int authCount = 0; private static readonly Dictionary lastIdea = new(); private static bool initialized = false; private class AuthRequest { public string authId; public ulong ownerId; public ulong cupboardId; public ulong requestId; public string requestName; } private static readonly Dictionary voteEmojis = new() { {"A", "🇦"}, {"B", "🇧"}, {"C", "🇨"}, {"D", "🇩"}, {"E", "🇪"}, {"F", "🇫"}, {"G", "🇬"}, {"H", "🇭"}, {"I", "🇮"}, {"J", "🇯"}, {"K", "🇰"}, {"L", "🇱"}, {"M", "🇲"}, {"N", "🇳"}, {"O", "🇴"}, {"P", "🇵"}, {"Q", "🇶"}, {"R", "🇷"}, {"S", "🇸"}, {"T", "🇹"}, {"U", "🇺"}, {"V", "🇻"}, {"W", "🇼"}, {"X", "🇽"}, {"Y", "🇾"}, {"Z", "🇿"} }; private void Init() => LoadData(); private void OnServerInitialized() { LoadConfig(); _discordSettings.ApiToken = config.token; _discordSettings.Intents = GatewayIntents.Guilds | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages | GatewayIntents.GuildMessageReactions | GatewayIntents.DirectMessages | GatewayIntents.DirectMessageReactions | GatewayIntents.MessageContent; _discordSettings.LogLevel = Ext.Discord.Logging.DiscordLogLevel.Debug; _link.AddLinkPlugin(this); Client.Connect(_discordSettings); cmd.AddChatCommand(config.command, this, nameof(DiscordConnectCommand)); if (config.playerCount) timer.Every(60, () => UpdateStatus()); if (config.enableTickets) timer.Every(Core.Random.Range(600, 900), () => CheckChannelRemoval()); if (!config.dtrChatEnabled || (config.dtrChatEnabled && ChatNations != null)) Unsubscribe(nameof(OnPlayerChat)); if (config.enableVotes) timer.Every(60, () => CheckVotes()); if (config.cupboardAuthCheck) cmd.AddChatCommand(config.cupboardCommand, this, nameof(AllowCupboardCommand)); else Unsubscribe(nameof(OnCupboardAuthorize)); if (config.reportCheatChannel == 0) Unsubscribe(nameof(OnPlayerReported)); if (!config.enableReports) Unsubscribe(nameof(OnFeedbackReported)); } private void Unload() { Client.Disconnect(); SaveData(); } private void UpdateStatus() { if (!initialized) return; UpdatePresenceCommand upc = new() { Afk = false, Since = 0, Status = UserStatusType.Online, Activities = new() { new() { Type = ActivityType.Playing, Name = $"{BasePlayer.activePlayerList.Count}/{ConVar.Server.maxplayers}" } } }; Client.UpdateStatus(upc); } private void CheckVotes() { foreach (var voteChannel in data.voteEnds) { foreach (var voteMess in voteChannel.Value) { if (data.endedVotes.Contains(voteMess.Key)) continue; if ((voteMess.Value - DateTime.Now).TotalSeconds > 0) continue; FinishVote(guild.GetChannel(voteChannel.Key), voteMess.Key); } } } private void CheckChannelRemoval() { DateTime now = DateTime.Now; foreach (var channel in data.channelRemovalDate) { if ((now - channel.Value).TotalHours > 24) { DiscordChannel ch = guild.GetChannel(channel.Key); ch?.Delete(Client); } } } private void DiscordConnectCommand(BasePlayer player, string command, string[] args) { if (args.Length == 0) { Mess(player, "ConnectHelp", config.command); return; } if (_link.IsLinked(player.UserIDString)) { if (args[0].ToLower() == "unlink") { DiscordUser usr = _link.GetDiscordUser(player.UserIDString); OnUnlinked(player.IPlayer, usr); Mess(player, "SuccessfullyUnlinked"); SynchronizeRanks(player); SynchronizeRanks(usr); return; } Mess(player, "AlreadyLinked"); return; } string code = args[0]; if (!generatedCodes.ContainsKey(code)) { Mess(player, "NotValidCode", code); return; } OnLinked(player.IPlayer, generatedCodes[code]); Mess(player, "SuccessfullyLinked"); SynchronizeRanks(player); SynchronizeRanks(generatedCodes[code]); generatedCodes.Remove(code); } private void AllowCupboardCommand(BasePlayer player, string command, string[] args) { if (args.Length == 0) { StringBuilder sb = Facepunch.Pool.Get(); sb.Clear(); foreach (var auth in cupboardAuth) if (auth.ownerId == player.userID) sb.Append("\n- ").Append(auth.authId); if (sb.Length == 0) sb.Append(Lang("NoRequests", player.UserIDString)); Mess(player, "RequestHelp", config.cupboardCommand, sb.ToString()); Facepunch.Pool.FreeUnmanaged(ref sb); return; } AuthRequest request = cupboardAuth.FirstOrDefault(x => x.authId == args[0]); if (request != null && request.ownerId == player.userID) { BuildingPrivlidge tc = BaseNetworkable.serverEntities.Find(new NetworkableId(request.cupboardId)) as BuildingPrivlidge; if (tc == null) return; tc.authorizedPlayers.Add(new ProtoBuf.PlayerNameID() { userid = request.requestId, username = request.requestName }); tc.SendNetworkUpdate(); BasePlayer authPlayer = BasePlayer.FindByID(request.requestId); Mess(player, "AuthAcceptedOwner"); if (authPlayer != null) Mess(authPlayer, "AuthAccepted"); cupboardAuth.Remove(request); } else { StringBuilder sb = Facepunch.Pool.Get(); sb.Clear(); foreach (var auth in cupboardAuth) if (auth.ownerId == player.userID) sb.Append("\n- ").Append(auth.authId); if (sb.Length == 0) sb.Append(Lang("NoRequests", player.UserIDString)); Mess(player, "RequestHelp", config.cupboardCommand, sb.ToString()); Facepunch.Pool.FreeUnmanaged(ref sb); } } private void SynchronizeRanks(BasePlayer player) { if (!initialized) return; if (!_link.IsLinked(player.UserIDString)) { if (!string.IsNullOrEmpty(config.rustGroupId)) permission.RemoveUserGroup(player.UserIDString, config.rustGroupId); if (!string.IsNullOrEmpty(config.rustNitroBoostGroupId)) permission.RemoveUserGroup(player.UserIDString, config.rustNitroBoostGroupId); return; } else { if (!string.IsNullOrEmpty(config.rustGroupId)) permission.AddUserGroup(player.UserIDString, config.rustGroupId); DiscordUser user = _link.GetDiscordUser(player.UserIDString); if (!string.IsNullOrEmpty(config.rustNitroBoostGroupId)) { if (guild.GetMember(user.Username)?.Roles.Contains(guild.GetBoosterRole().Id) ?? false) permission.AddUserGroup(player.UserIDString, config.rustNitroBoostGroupId); else permission.RemoveUserGroup(player.UserIDString, config.rustNitroBoostGroupId); } foreach (var role in config.syncedGroups) { if (!guild.Roles.ContainsKey(role.Value)) continue; if (permission.UserHasGroup(player.UserIDString, role.Key)) guild.AddMemberRole(Client, user, guild.Roles[role.Value]); else guild.RemoveMemberRole(Client, user, guild.Roles[role.Value]); } if (config.syncNickname) guild.EditMemberNick(Client, user.Id, player.displayName); } } private void SynchronizeRanks(DiscordUser user) { if (!initialized) return; if (!_link.IsLinked(user)) { GuildMember member = guild.GetMember(user.Id); if (!string.IsNullOrEmpty(config.discordGroupId) && guild.Roles.ContainsKey(config.discordGroupId) && member.Roles.Contains(config.discordGroupId)) guild.RemoveMemberRole(Client, user, guild.Roles[config.discordGroupId]); foreach (var role in config.syncedGroups) if (guild.Roles.ContainsKey(role.Value) && member.Roles.Contains(role.Value)) guild.RemoveMemberRole(Client, user, guild.Roles[role.Value]); return; } else { GuildMember member = guild.GetMember(user.Id); if (!string.IsNullOrEmpty(config.discordGroupId) && guild.Roles.ContainsKey(config.discordGroupId) && !member.Roles.Contains(config.discordGroupId)) guild.AddMemberRole(Client, user, guild.Roles[config.discordGroupId]); IPlayer linkPlayer = _link.GetPlayer(user.Id); foreach (var role in config.syncedGroups) { if (!guild.Roles.ContainsKey(role.Value)) continue; if (linkPlayer.BelongsToGroup(role.Key)) guild.AddMemberRole(Client, user, guild.Roles[role.Value]); else if (member.Roles.Contains(role.Value)) guild.RemoveMemberRole(Client, user, guild.Roles[role.Value]); } } } private void OnNationsChat(BasePlayer player, string message, Chat.ChatChannel channel, string pluginChannel, bool isGlobal) { if (!initialized) return; if (channel != 0) return; if (message.Contains("@everyone") || message.Contains("@here")) return; if (!isGlobal && !config.validChatChannels.Contains(pluginChannel)) return; string ch = isGlobal ? "GLOBAL" : pluginChannel; chatChannel.CreateMessage(Client, string.Format(config.discordChatFormat, player.displayName, message, ch.ToUpper())); } private void OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel) { if (!initialized) return; if (channel != 0) return; if (message.Contains("@everyone") || message.Contains("@here")) return; chatChannel.CreateMessage(Client, string.Format(config.discordChatFormat, player.displayName, message, "GLOBAL")); } private void OnPlayerConnected(BasePlayer player) => SynchronizeRanks(player); private object OnCupboardAuthorize(BuildingPrivlidge privilege, BasePlayer player) { if (!initialized) return null; if (player.IsAdmin) return null; if (privilege.OwnerID == 0) return null; if (privilege.OwnerID == player.userID) return null; if (EnablePvP != null && EnablePvP.Call("IsPvP")) return null; ulong userId = player.userID.Get(); if (Friends != null && Friends.Call("IsFriend", userId, privilege.OwnerID)) return null; if (Clans != null && Clans.Call("IsClanMember", userId, privilege.OwnerID)) return null; string ownerIdString = privilege.OwnerID.ToString(); AuthRequest oldAuth = cupboardAuth.FirstOrDefault(x => x.cupboardId == privilege.net.ID.Value && x.requestId == player.userID); if (oldAuth != null) { Mess(player, "RequestAlreadySent"); return false; } string authId; if (_link.IsLinked(ownerIdString)) { DiscordUser user = _link.GetDiscordUser(ownerIdString); if (user != null) { Puts($"Request for authorization in {privilege.OwnerID}'s cupboard at {privilege.transform.position} by {player.displayName}!"); authId = authCount.ToString(); authCount++; cupboardAuth.Add(new AuthRequest() { authId = authId, cupboardId = privilege.net.ID.Value, ownerId = privilege.OwnerID, requestId = player.userID, requestName = player.displayName }); Mess(player, "AuthAsk"); user.CreateDirectMessageChannel(Client).Then((channel) => { DiscordEmbedBuilder deb = new(); deb.AddTitle(Lang("Discord_TcAuthTitle", ownerIdString)); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(Lang("Discord_AuthQuestion", ownerIdString, player.displayName, MapHelper.PositionToString(privilege.transform.position))); ButtonComponent button = new() { Label = Lang("Discord_TcAuthButton", ownerIdString), Style = ButtonStyle.Primary, Disabled = false, Emoji = DiscordEmoji.FromCharacter("🔑"), CustomId = $"authorize_cupboard_{authId}" }; DiscordMessageBuilder dmb = new(); dmb.AddEmbed(deb.Build()); ActionRowComponent arc = new(); arc.Components.Add(button); dmb.AddActionRow(arc); channel.CreateMessage(Client, dmb.Build()); }); } return false; } BasePlayer tcOwner = BasePlayer.FindByID(privilege.OwnerID); if (tcOwner == null) { Mess(player, "NotPossibleToAuth"); return false; } authId = authCount.ToString(); cupboardAuth.Add(new AuthRequest() { authId = authId, cupboardId = privilege.net.ID.Value, ownerId = privilege.OwnerID, requestId = player.userID, requestName = player.displayName }); Mess(player, "AuthAsk"); Mess(tcOwner, "AuthQuestion", player.displayName, MapHelper.PositionToString(privilege.transform.position), config.cupboardCommand, authCount); Puts($"Request for authorization in {tcOwner.userID}'s cupboard at {privilege.transform.position} by {player.displayName}!"); authCount++; return false; } private void OnPlayerReported(BasePlayer player, string targetName, string targetId, string subject, string message, string type) { string connection = "NOT CONNECTED"; if (_link.IsLinked(player.UserIDString)) connection = $"{_link.GetLinkedMember(player.UserIDString, guild).User.Username}"; Puts($"{player.displayName} ({player.userID}) sent Report on player {targetName} ({targetId}) of type {type} with subject {subject} and message {message}."); DiscordChannel channel = guild.Channels[config.reportCheatChannel]; DiscordEmbedBuilder deb = new(); deb.AddTitle("New Player Report!"); deb.AddColor(DiscordColor.Blurple); deb.AddDescription($"**Reported:** {targetName} ({targetId})\n**Report Type:** {type}\n**Title:** {subject}\n**Description:** {message}"); deb.AddFooter($"{player.displayName} ({player.userID}) - {connection}"); channel.CreateMessage(Client, deb.Build()); } private void OnFeedbackReported(BasePlayer player, string subject, string message, ReportType type) { DiscordChannel channel = null; string connection = "NOT CONNECTED"; if (_link.IsLinked(player.UserIDString)) connection = $"{_link.GetLinkedMember(player.UserIDString, guild).User.Username}"; if (type == ReportType.General && config.reportGeneralChannel != 0) { Puts($"{player.displayName} ({player.userID}) sent Encouragement Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportGeneralChannel]; } else if (type == ReportType.Bug && config.reportBugChannel != 0) { Puts($"{player.displayName} ({player.userID}) sent Bug Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportBugChannel]; } else if (type == ReportType.Cheat && config.reportCheatChannel != 0) { Puts($"{player.displayName} ({player.userID}) sent Cheat Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportCheatChannel]; } else if (type == ReportType.Abuse && config.reportAbuseChannel != 0) { Puts($"{player.displayName} ({player.userID}) sent Abuse Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportAbuseChannel]; } else if (type == ReportType.Idea && config.reportIdeaChannel != 0) { if (lastIdea.ContainsKey(player.userID) && (DateTime.Now - lastIdea[player.userID]).TotalMinutes < 15) { PopUpAPI?.Call("API_ShowPopup", player, Lang("TooManyIdeas", player.UserIDString), 10f, "Overall"); return; } lastIdea[player.userID] = DateTime.Now; Puts($"{player.displayName} ({player.userID}) sent Idea Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportIdeaChannel]; } else if (type == ReportType.OffensiveContent && config.reportOffensiveChannel != 0) { Puts($"{player.displayName} ({player.userID}) sent Offense Report with subject {subject} and message {message}."); channel = guild.Channels[config.reportOffensiveChannel]; } if (channel == null) return; DiscordEmbedBuilder deb = new(); deb.AddTitle(subject); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(message); deb.AddFooter($"{player.displayName} ({player.userID}) - {connection}"); channel.CreateMessage(Client, deb.Build()).Then((message) => { if (type == ReportType.Idea && config.reportIdeaChannel != 0) { message.CreateReaction(Client, config.ideaYesEmote); message.CreateReaction(Client, config.ideaNoEmote); PopUpAPI?.Call("API_ShowPopup", player, Lang("IdeaSent", player.UserIDString), 10f, "Overall"); } }); } [HookMethod(DiscordExtHooks.OnDiscordGuildMembersLoaded)] private void OnDiscordMembersLoaded(DiscordGuild guild) { foreach (var user in guild.Members.Values) SynchronizeRanks(user.User); if (config.dtrChatEnabled && config.chatChannelId != 0) chatChannel = guild.GetChannel(config.chatChannelId); } [HookMethod(DiscordExtHooks.OnDiscordGatewayReady)] private void OnDiscordGatewayReady(GatewayReadyEvent ready) { GUILD_ID = ready.Guilds.Keys.First(); guild = ready.Guilds.Values.First(); if (ready.Guilds.Count > 1) Puts("WARNING! Bot has been found on more than one server and it might not work! Bot can be present only on one server!"); ApplicationCommandBuilder acb = new("generateconnect", "Generates Connection Creation Embed", ApplicationCommandType.ChatInput); acb.AddDefaultPermissions(PermissionFlags.Administrator); acb.AllowInDirectMessages(false); ready.Application.CreateGuildCommand(Client, GUILD_ID, acb.Build()); _appCommand.AddApplicationCommand(this, Client.Bot.Application.Id, (i, data) => { CreateConnectionEmbedCommand(i, data); }, acb.CommandName); if (config.enableTickets) { acb = new("generateticket", "Generates Ticket Creation Embed", ApplicationCommandType.ChatInput); acb.AddDefaultPermissions(PermissionFlags.Administrator); acb.AllowInDirectMessages(false); ready.Application.CreateGuildCommand(Client, GUILD_ID, acb.Build()); _appCommand.AddApplicationCommand(this, Client.Bot.Application.Id, (i, data) => { CreateTicketEmbedCommand(i, data); }, acb.CommandName); acb = new("close", "Closes Ticket", ApplicationCommandType.ChatInput); acb.AddDefaultPermissions(PermissionFlags.SendMessages); acb.AllowInDirectMessages(false); ready.Application.CreateGuildCommand(Client, GUILD_ID, acb.Build()); _appCommand.AddApplicationCommand(this, Client.Bot.Application.Id, (i, data) => { CreateTicketCloseCommand(i, data); }, acb.CommandName); } if (config.enableVotes) { acb = new("vote", "Creates Vote Embed", ApplicationCommandType.ChatInput); acb.AllowInDirectMessages(false); ready.Application.CreateGuildCommand(Client, GUILD_ID, acb.Build()); _appCommand.AddApplicationCommand(this, Client.Bot.Application.Id, (i, data) => { CreateVoteCommand(i, data); }, acb.CommandName); acb = new("voteend", "Ends Vote Faster", ApplicationCommandType.ChatInput); acb.AllowInDirectMessages(false); acb.AddOption(CommandOptionType.String, "vote_id", "Vote Message ID"); ready.Application.CreateGuildCommand(Client, GUILD_ID, acb.Build()); _appCommand.AddApplicationCommand(this, Client.Bot.Application.Id, (i, data) => { EndVoteCommand(i, data); }, acb.CommandName); } Puts("Bot connected successfully!"); initialized = true; } [HookMethod(DiscordExtHooks.OnDiscordInteractionCreated)] private void OnDiscordInteractionCreated(DiscordInteraction interaction) { if (interaction.Data.CustomId == "verify_discord_button") { InteractionCallbackData icd; Snowflake dcId = interaction.User.Id; if (_link.IsLinked(dcId)) { string linked = _link.GetPlayer(dcId).Id; icd = new InteractionCallbackData() { Content = Lang("Discord_LinkedAlready", linked), Flags = MessageFlags.Ephemeral }; } else if (generatedCodes.ContainsValue(interaction.User)) { string code = generatedCodes.First(x => x.Value == interaction.User).Key; icd = new InteractionCallbackData() { Content = string.Format(config.lang.connectionSentCode, code, config.command), Flags = MessageFlags.Ephemeral }; } else { string code; do code = Core.Random.Range(100000, 1000000).ToString(); while (generatedCodes.ContainsKey(code)); generatedCodes.Add(code, interaction.User); icd = new InteractionCallbackData() { Content = string.Format(config.lang.connectionSentCode, code, config.command), Flags = MessageFlags.Ephemeral }; } interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } else if (config.enableTickets && interaction.Data.CustomId.StartsWith("ticket_discord_button_")) { string supportLang = interaction.Data.CustomId.Split('_')[3]; LanguageButtonConfig lbc = config.ticketLangButtons[supportLang]; List permissions = new() { new() { Id = guild.EveryoneRole.Id, Type = PermissionType.Role, Deny = PermissionFlags.ViewChannel }, new() { Id = interaction.User.Id, Type = PermissionType.Member, Allow = PermissionFlags.ViewChannel } }; foreach (var supportRank in lbc.supportRanks) { permissions.Add( new() { Id = supportRank, Type = PermissionType.Role, Allow = PermissionFlags.ViewChannel } ); } ChannelCreate newChannel = new() { Type = ChannelType.GuildText, Name = $"{supportLang}–{data.ticketId:0000}", ParentId = config.newTicketsParentId, PermissionOverwrites = permissions, DefaultAutoArchiveDuration = 10080 }; data.ticketId++; interaction.Guild.CreateChannel(Client, newChannel).Then((channel) => { DiscordEmbedBuilder deb = new(); deb.AddTitle(lbc.greetingMessageTitle); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(string.Format(lbc.greetingMessageDescription, interaction.User.Id)); StringBuilder sb = Facepunch.Pool.Get(); sb.Clear(); sb.Append($"<@{interaction.User.Id}> "); foreach (var ping in lbc.supportRanks) sb.Append($"<@&{ping.Id}> "); channel.CreateMessage(Client, sb.ToString()); Facepunch.Pool.FreeUnmanaged(ref sb); channel.CreateMessage(Client, deb.Build()); InteractionCallbackData icd = new() { Content = string.Format(lbc.responseMessage, $"<#{channel.Id}>"), Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); }); } else if (config.enableVotes && interaction.Data.CustomId == "vote_modal") { string voteName = interaction.Data.GetComponent("vote_title").Value; string voteDesc = interaction.Data.GetComponent("vote_description").Value; var voteFields = interaction.Data.GetComponent("vote_fields").Value.SplitToLines(); string voteTimeString = interaction.Data.GetComponent("vote_time").Value; DiscordEmbedBuilder deb = new(); deb.AddTitle(voteName); deb.AddColor(DiscordColor.Blurple); StringBuilder sb = Facepunch.Pool.Get(); sb.Clear(); sb.AppendLine(voteDesc).AppendLine(); int counter = 0; foreach (var field in voteFields) { sb.Append(voteEmojis.ElementAt(counter).Value).Append(" - ").AppendLine(field); counter++; } long voteTime = FormatToSeconds(voteTimeString); if (voteTime > 0) { long voteEnd = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + voteTime; sb.Append(string.Format(config.lang.voteEndTime, $"")); } deb.AddDescription(sb.ToString()); Facepunch.Pool.FreeUnmanaged(ref sb); DiscordMessageBuilder dmb = new(); dmb.AddEmbed(deb.Build()); int randomKey = Core.Random.Range(0, 1000000); ButtonComponent button = new() { Label = config.lang.voteButton, Style = ButtonStyle.Primary, Disabled = false, Emoji = DiscordEmoji.FromCharacter("🗳️"), CustomId = $"vote_discord_button_{randomKey}" }; ActionRowComponent arc = new(); arc.Components.Add(button); dmb.AddActionRow(arc); if (config.votePingRole != 0) interaction.Channel.CreateMessage(Client, $"<@&{config.votePingRole}>"); interaction.Channel.CreateMessage(Client, dmb.Build()).Then((mess) => { data.voteLinks[randomKey] = mess.Id; data.voteCount[mess.Id] = voteFields.Count(); if (voteTime > 0) { data.voteEnds.TryAdd(mess.ChannelId, new Dictionary()); data.voteEnds[mess.ChannelId].Add(mess.Id, DateTime.Now.AddSeconds(voteTime)); } }); InteractionCallbackData icd = new() { Content = "Vote successfully created!", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } else if (config.enableVotes && interaction.Data.CustomId.StartsWith("vote_discord_button_")) { int linkId = int.Parse(interaction.Data.CustomId.Split('_')[3]); if (!data.voteLinks.ContainsKey(linkId)) { InteractionCallbackData icd = new() { Content = "Error while trying to vote! Contact administrator.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } Snowflake voteId = data.voteLinks[linkId]; if (data.endedVotes.Contains(voteId)) { InteractionCallbackData icd = new() { Content = config.lang.voteEnded, Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } InteractionModalBuilder imb = new(interaction); int fixedVotePoints = GetVotePoints(interaction.User.Id); imb.AddModalTitle(string.Format(config.lang.voteSubmitTitle, fixedVotePoints)); imb.AddModalCustomId($"vote_for_modal_{voteId}"); int voteCount = data.voteCount[voteId]; string voteTitle = string.Format(config.lang.voteSubmitText, $"A-{voteEmojis.ElementAt(voteCount - 1).Key}"); imb.AddInputText("vote_field", voteTitle, InputTextStyles.Short, null, true, "B", 1, 1); interaction.CreateResponse(Client, imb.Build()); } else if (config.enableVotes && interaction.Data.CustomId.StartsWith("vote_for_modal_") && Snowflake.TryParse(interaction.Data.CustomId.Split('_')[3], out Snowflake linkId)) { string voteKey = interaction.Data.GetComponent("vote_field").Value.ToUpper(); InteractionCallbackData icd; if (data.endedVotes.Contains(linkId)) { icd = new() { Content = config.lang.voteEnded, Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } if (!voteEmojis.ContainsKey(voteKey)) { icd = new() { Content = config.lang.voteNotValidLetter, Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } data.voteWeights.TryAdd(linkId, new Dictionary>()); int fixedVotePoints = GetVotePoints(interaction.User.Id); var pair = new KeyValuePair(voteKey, fixedVotePoints); data.voteWeights[linkId][interaction.User.Id] = pair; icd = new() { Content = string.Format(config.lang.voteValid, voteKey, fixedVotePoints), Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } else if (config.cupboardAuthCheck && interaction.Data.CustomId.StartsWith("authorize_cupboard_")) { string authId = interaction.Data.CustomId.Split('_')[2]; AuthRequest request = cupboardAuth.FirstOrDefault(x => x.authId == authId); InteractionCallbackData icd; string playerId = _link.GetPlayerId(interaction.User.Id).Id; if (request == null) { icd = new() { Content = Lang("Discord_AuthError", playerId), Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } BuildingPrivlidge tc = BaseNetworkable.serverEntities.Find(new NetworkableId(request.cupboardId)) as BuildingPrivlidge; if (tc == null) { icd = new() { Content = Lang("Discord_AuthError", playerId), Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } tc.authorizedPlayers.Add(new ProtoBuf.PlayerNameID() { userid = request.requestId, username = request.requestName }); tc.SendNetworkUpdate(); BasePlayer authPlayer = BasePlayer.FindByID(request.requestId); if (authPlayer != null) Mess(authPlayer, "AuthAccepted"); cupboardAuth.Remove(request); icd = new() { Content = Lang("Discord_AuthAccepted", playerId), Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } } public int GetVotePoints(Snowflake id) { float points = 1; if (_link.IsLinked(id)) { IPlayer linkedUser = _link.GetPlayer(id); if (PlaytimeAPI != null) { int playtime = PlaytimeAPI.Call("API_GetPlaytime", ulong.Parse(linkedUser.Id)); foreach (var playLimit in config.voteTimePlayedPoints) { if (playLimit.Key * 60 < playtime) { points = playLimit.Value; break; } } foreach (var perm in config.votePointMultipliers) if (linkedUser.HasPermission(perm.Key)) points *= perm.Value; } } return Mathf.FloorToInt(points); } public static long FormatToSeconds(string input) { string pattern = "(?\\d+)(?[dhms])"; Match match = Regex.Match(input, pattern, RegexOptions.IgnoreCase); if (!match.Success) return 0; int value = int.Parse(match.Groups["value"].Value); string unit = match.Groups["unit"].Value.ToLower(); return unit switch { "d" => value * 86400, "h" => value * 3600, "m" => value * 60, "s" => value, _ => 0 }; } [HookMethod(DiscordExtHooks.OnDiscordGuildMessageCreated)] private void OnDiscordGuildMessageCreated(DiscordMessage message, DiscordChannel channel, DiscordGuild guild) { if (message.Author.Id == Client.Bot.BotUser.Id) return; if (config.dtrChatEnabled && channel.Id == config.chatChannelId) { if (!_link.IsLinked(message.Author.Id)) return; IPlayer player = _link.GetPlayer(message.Author.Id); if (ChatNations != null && ChatNations.Call("IsMuted", ulong.Parse(player.Id))) return; if (BetterChatMute != null && BetterChatMute.Call("API_IsMuted", player)) return; string messageOutput = string.Format(config.gameChatFormat, player.Name, message.Content); foreach (var perm in config.gameChatFormatPerms) { if (permission.UserHasPermission(player.Id, perm.Key)) { messageOutput = string.Format(perm.Value, player.Name, message.Content); break; } } string builtCommand = $"chat.add 0 {player.Id} \"{messageOutput}\""; foreach (var onlinePlayer in BasePlayer.activePlayerList) onlinePlayer.SendConsoleCommand(builtCommand); Puts($"[DTR Chat] [Discord] {player.Name} » {message.Content}"); } else if (config.logTickets && config.newTicketsParentId == channel.ParentId) { if (message.Attachments.Count > 0) LogToFile(message.ChannelId, $"[{DateTime.Now}] {message.Author.Username} > {message.Content} (Attachment: {message.Attachments.FirstOrDefault().Value.Url})", this); else LogToFile(message.ChannelId, $"[{DateTime.Now}] {message.Author.Username} > {message.Content}", this); } } [HookMethod(nameof(CreateConnectionEmbedCommand))] [DiscordApplicationCommand("generateconnect")] private void CreateConnectionEmbedCommand(DiscordInteraction interaction, InteractionDataParsed idp) { DiscordEmbedBuilder deb = new(); deb.AddTitle(config.lang.connectionEmbedTitle); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(config.lang.connectionEmbedDescription); ButtonComponent button = new() { Label = config.lang.connectionEmbedButtonText, Style = ButtonStyle.Primary, Disabled = false, Emoji = DiscordEmoji.FromCharacter("🔗"), CustomId = "verify_discord_button" }; DiscordMessageBuilder dmb = new(); dmb.AddEmbed(deb.Build()); ActionRowComponent arc = new(); arc.Components.Add(button); dmb.AddActionRow(arc); interaction.Channel.CreateMessage(Client, dmb.Build()); InteractionCallbackData icd = new() { Content = "Created connection embed.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } [HookMethod(nameof(CreateTicketEmbedCommand))] [DiscordApplicationCommand("generateticket")] private void CreateTicketEmbedCommand(DiscordInteraction interaction, InteractionDataParsed idp) { DiscordEmbedBuilder deb = new(); deb.AddTitle(config.lang.ticketEmbedTitle); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(config.lang.ticketEmbedDescription); DiscordMessageBuilder dmb = new(); dmb.AddEmbed(deb.Build()); ActionRowComponent arc = new(); foreach (var langButt in config.ticketLangButtons) { ButtonComponent button = new() { Label = langButt.Value.displayedName, Style = ButtonStyle.Primary, Disabled = false, Emoji = DiscordEmoji.FromCharacter(langButt.Value.displayedIcon), CustomId = $"ticket_discord_button_{langButt.Key}" }; arc.Components.Add(button); } dmb.AddActionRow(arc); interaction.Channel.CreateMessage(Client, dmb.Build()); InteractionCallbackData icd = new() { Content = "Created ticket embed.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } [HookMethod(nameof(CreateTicketCloseCommand))] [DiscordApplicationCommand("close")] private void CreateTicketCloseCommand(DiscordInteraction interaction, InteractionDataParsed idp) { InteractionCallbackData icd; if (interaction.Channel.ParentId != config.newTicketsParentId) { icd = new() { Content = config.lang.ticketCannotClose, Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); return; } DiscordChannel ch = interaction.Channel; GuildChannelUpdate gcu = new() { Name = ch.Name, ParentId = config.closedTicketsParentId, Topic = ch.Topic, Type = ch.Type, RateLimitPerUser = ch.RateLimitPerUser, Bitrate = ch.Bitrate, UserLimit = ch.UserLimit }; ch.EditGuildChannel(Client, gcu); data.channelRemovalDate[interaction.Channel.Id] = DateTime.Now; DiscordEmbedBuilder deb = new(); deb.AddTitle(config.lang.ticketClosedTitle); deb.AddColor(DiscordColor.Blurple); deb.AddDescription(config.lang.ticketClosedDescription); icd = new() { Embeds = new() { deb.Build() } }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } [HookMethod(nameof(CreateVoteCommand))] [DiscordApplicationCommand("vote")] private void CreateVoteCommand(DiscordInteraction interaction, InteractionDataParsed idp) { InteractionModalBuilder imb = new(interaction); imb.AddModalTitle("Vote Creator"); imb.AddModalCustomId("vote_modal"); imb.AddInputText("vote_title", "Vote Title", InputTextStyles.Short, config.defaultVoteTitle, true, "Map Vote"); imb.AddInputText("vote_description", "Vote Description", InputTextStyles.Paragraph, config.defaultVoteDescription, true, "Let's vote for the map on the next wipe!"); imb.AddInputText("vote_fields", "Vote Fields (each in new line)", InputTextStyles.Paragraph, config.defaultVoteFields, true, "Procedural Map\nSome Custom Map\nSome Other Custom Map"); imb.AddInputText("vote_time", "Vote Duration (format: d, h, m)", InputTextStyles.Short, config.defaultVoteTime, false, "20h"); interaction.CreateResponse(Client, imb.Build()); } [HookMethod(nameof(EndVoteCommand))] [DiscordApplicationCommand("voteend")] private void EndVoteCommand(DiscordInteraction interaction, InteractionDataParsed idp) { if (Snowflake.TryParse(idp.Args.GetString("vote_id"), out Snowflake id)) { if (data.endedVotes.Contains(id)) { InteractionCallbackData icd = new() { Content = "Vote already ended.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } else if (!data.voteEnds.ContainsKey((Snowflake)interaction.ChannelId) || !data.voteEnds[(Snowflake)interaction.ChannelId].ContainsKey(id)) { InteractionCallbackData icd = new() { Content = "Linked message is not an valid vote.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } else { InteractionCallbackData icd; if (FinishVote(interaction.Channel, id)) icd = new() { Content = "Vote ended forcefully.", Flags = MessageFlags.Ephemeral }; else icd = new() { Content = "Vote had no votes. Vote closed. Summary sending cancelled.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } } else { InteractionCallbackData icd = new() { Content = "Input field is not valid message ID.", Flags = MessageFlags.Ephemeral }; interaction.CreateResponse(Client, InteractionResponseType.ChannelMessageWithSource, icd); } } private bool FinishVote(DiscordChannel channel, Snowflake voteId) { if (!data.voteWeights.ContainsKey(voteId)) { data.endedVotes.Add(voteId); return false; } Dictionary summedVotes = Facepunch.Pool.Get>(); summedVotes.Clear(); foreach (var user in data.voteWeights[voteId].Values) { summedVotes.TryAdd(user.Key, 0); summedVotes[user.Key] += user.Value; } DiscordEmbedBuilder deb = new(); deb.AddTitle(config.lang.voteEndTitle); deb.AddColor(DiscordColor.Blurple); StringBuilder sb = Facepunch.Pool.Get(); sb.Clear(); sb.Append(config.lang.voteEndDescription); for (int x = 0; x < data.voteCount[voteId]; x++) { var voteKey = voteEmojis.ElementAt(x); int votes = summedVotes.ContainsKey(voteKey.Key) ? summedVotes[voteKey.Key] : 0; sb.Append(voteKey.Value).Append(" - ").Append(votes).Append('\n'); } deb.AddDescription(sb.ToString()); Facepunch.Pool.FreeUnmanaged(ref sb); Facepunch.Pool.FreeUnmanaged(ref summedVotes); channel.CreateMessage(Client, deb.Build()); data.endedVotes.Add(voteId); return true; } private void OnInstaSell(string userId, int amount, string price, string itemName) { if (!initialized) return; if (_link.IsLinked(userId)) { DiscordUser user = _link.GetDiscordUser(userId); if (user != null) { Puts($"Sending insta-sell message of x{amount} {itemName} for {price} to {userId}."); user.CreateDirectMessageChannel(Client).Then((channel) => channel.CreateMessage(Client, Lang("Discord_InstaSell", userId, amount, itemName, price))); } } } private void OnAlertPrice(string userId, string price, string itemName) { if (!initialized) return; if (_link.IsLinked(userId)) { DiscordUser user = _link.GetDiscordUser(userId); if (user != null) { Puts($"Sending alert message about {price} price of {itemName} to {userId}."); user.CreateDirectMessageChannel(Client).Then((channel) => channel.CreateMessage(Client, Lang("Discord_AlertPrice", userId, itemName, price))); } } } private void API_SynchronizeDiscord(ulong userId) => API_SynchronizeDiscord(userId.ToString()); private void API_SynchronizeDiscord(string userId) { if (!initialized) return; BasePlayer player = BasePlayer.Find(userId); if (player != null) SynchronizeRanks(player); if (_link.IsLinked(userId)) { DiscordUser user = _link.GetDiscordUser(userId); SynchronizeRanks(user); } } private void API_SendEmbedMessage(string channelId, string title, string description, string color, string thumbnailUrl = "", string footerUrl = "", string footerText = "") { if (!initialized) return; DiscordChannel channel = guild.Channels[(Snowflake)channelId]; DiscordEmbedBuilder deb = new(); deb.AddTitle(title); deb.AddColor(new DiscordColor(color)); deb.AddDescription(description); if (thumbnailUrl != "") deb.AddThumbnail(thumbnailUrl); if (footerUrl != "" && footerText != "") deb.AddFooter(footerText, footerUrl); channel.CreateMessage(Client, deb.Build()); } private void API_SendMessage(string channelId, string description) { if (!initialized) return; DiscordChannel channel = guild.Channels[(Snowflake)channelId]; channel.CreateMessage(Client, description); } private void API_SendPrivateMessage(string userId, string message) { if (!initialized) return; if (_link.IsLinked(userId)) { DiscordUser user = _link.GetDiscordUser(userId); user?.CreateDirectMessageChannel(Client).Then((channel) => channel.CreateMessage(Client, message)); } } public IDictionary GetPlayerIdToDiscordIds() { Dictionary linked = new(); foreach (var data in data.linkedPlayers) linked.Add(new PlayerId(data.Key), data.Value); return linked; } private void OnLinked(IPlayer player, DiscordUser user) { _link.OnLinked(this, player, user); data.linkedPlayers[player.Id] = user.Id; } private void OnUnlinked(IPlayer player, DiscordUser user) { _link.OnUnlinked(this, player, user); data.linkedPlayers.Remove(player.Id); } protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["NoRequests"] = "You have no authorization requests!", ["TooManyIdeas"] = "You are sending too many ideas! You can send one idea every 15 minutes!", ["IdeaSent"] = "Idea has been successfully sent!", ["ConnectHelp"] = "Command usage:\n/{0} - Connect your Discord with game using code obtained from Discord server\n/{0} unlink - Unlink your previously connected Discord account", ["SuccessfullyUnlinked"] = "You've successfully unlinked your account!", ["AlreadyLinked"] = "Your account is already linked!", ["NotValidCode"] = "The code {0} is not valid code!\nGet your code from official server's Discord and try again!", ["SuccessfullyLinked"] = "You've successfully linked your account!", ["RequestHelp"] = "Command usage:\n/{0} - Answer to authorization request\n\nYour current auth request IDs:\n{1}", ["AuthAcceptedOwner"] = "You've approved authorization request!", ["AuthAccepted"] = "The cupboard owner accepted your authorization request!", ["RequestAlreadySent"] = "Authorization request has been already sent.\nWait for a while for an cupboard owner to respond.", ["AuthAsk"] = "You've sent authorization request to the cupboard owner.\nWait for a while for him to respond!", ["AuthQuestion"] = "Player {0} is requesting for access to your cupboard at {1}.\nRun /{2} {3} to grant him access or ignore to deny.", ["Discord_AuthQuestion"] = "Player **{0}** is requesting for access to your cupboard at **{1}**.\nClick the button, to give him access, or ignore to deny.", ["Discord_TcAuthTitle"] = "Cupboard Authorization Request", ["Discord_TcAuthButton"] = "Authorize", ["Discord_LinkedAlready"] = "Your account is already linked!\nRun **/discord unlink** in-game to unlink your account.", ["Discord_AuthError"] = "An error occured while trying to authorize player.\nYou may already authorized player or cupboard cannot be found.", ["Discord_AuthAccepted"] = "You've successfully authorized player in your cupboard!", ["Discord_InstaSell"] = "Hey, it's me! Your broker!\nI've sold your **x{0} {1}** for a total of {2}!\nCurrency should be on your account now!", ["Discord_AlertPrice"] = "Hey, it's me! Your broker!\nI just found that price for **{0}** has exceed your alert value and it's **{1}** now!\nIf you want to sell your items in bank join the server while the price is good!", }, this); } private void Mess(BasePlayer player, string key, params object[] args) => SendReply(player, Lang(key, player.UserIDString, args)); private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); private static PluginConfig config = new(); protected override void LoadConfig() { base.LoadConfig(); config = Config.ReadObject(); Config.WriteObject(config); } protected override void LoadDefaultConfig() { Config.WriteObject(config = new() { syncedGroups = new Dictionary() { { "vip", (Snowflake)123456 }, { "svip", (Snowflake)332265 }, }, gameChatFormatPerms = new Dictionary() { { "discordcore.chat.admin", "[Discord] [Admin] {0} » {1}" }, { "discordcore.chat.vip", "[Discord] [VIP] {0} » {1}" } }, discordChatFormatPerms = new Dictionary() { { "discordcore.chat.admin", "*[A]* **{0}** » *{1}*" }, { "discordcore.chat.vip", "*[VIP]* **{0}** » *{1}*" } }, ticketLangButtons = new Dictionary() { { "en", new LanguageButtonConfig() { displayedName = "English", displayedIcon = "🇺🇸", responseMessage = "Ticket has been created! {0}", greetingMessageTitle = "New Ticket", greetingMessageDescription = "Hey, <@{0}>! Welcome in your ticket! \nSupport group has been informed about your problem.\n**Describe your problem here and wait for the answer!**\nIf you want to close your ticket, feel free to write **/close** command.", supportRanks = new List() { (Snowflake)5643345, (Snowflake)223235, } } }, { "pl", new LanguageButtonConfig() { displayedName = "Polski", displayedIcon = "🇵🇱", responseMessage = "Strefa pomocy została stworzona! {0}", greetingMessageTitle = "Nowe Zgłoszenie", greetingMessageDescription = "Hej, <@{0}>! Witaj w swoim zgłoszeniu \nGrupa wsparcia została poinformowana o Twoim problemie.\n**Opisz tutaj swój problem i poczekaj na odpowiedź!**\nJeśli chcesz zamknąć zgłoszenie, napisz polecenie **/close**.", supportRanks = new List() { (Snowflake)5643345, (Snowflake)223235, } } } }, validChatChannels = new List() { "en", "pl", "de", "ru" }, voteTimePlayedPoints = new Dictionary() { { 9000, 5 }, { 4500, 4 }, { 1800, 3 }, { 300, 2 } }, votePointMultipliers = new Dictionary() { { "discordcore.svip", 2 }, { "discordcore.vip", 1.5f } } }, true); } private class PluginConfig { [JsonProperty("BOT API Token")] public string token = "Paste Token Here!"; [JsonProperty("Show Player Count In Status")] public bool playerCount = true; [JsonProperty("Connection - In-Game Command")] public string command = "connect"; [JsonProperty("Connection - Granted Discord Group ID")] public Snowflake discordGroupId = (Snowflake)0; [JsonProperty("Connection - Synced Groups")] public Dictionary syncedGroups = new(); [JsonProperty("Connection - Synchronize In-Game Nickname")] public bool syncNickname = true; [JsonProperty("Connection - Granted RUST Group Name")] public string rustGroupId = ""; [JsonProperty("Connection - Nitro Boost Granted RUST Group Name")] public string rustNitroBoostGroupId = ""; [JsonProperty("Discord Rust Chat - Enabled")] public bool dtrChatEnabled = true; [JsonProperty("Discord Rust Chat - Channel ID")] public Snowflake chatChannelId = (Snowflake)0; [JsonProperty("Discord Rust Chat - Discord To Rust Format")] public string gameChatFormat = "[Discord] {0} » {1}"; [JsonProperty("Discord Rust Chat - Discord To Rust Format With Required Permission")] public Dictionary gameChatFormatPerms = new(); [JsonProperty("Discord Rust Chat - Rust To Discord Format")] public string discordChatFormat = "**{0}** » *{1}*"; [JsonProperty("Discord Rust Chat - Rust To Discord Format With Required Permission")] public Dictionary discordChatFormatPerms = new(); [JsonProperty("Discord Rust Chat - Valid ChatNations Channels")] public List validChatChannels = new(); [JsonProperty("Tickets - Enabled")] public bool enableTickets = true; [JsonProperty("Tickets - Language Buttons")] public Dictionary ticketLangButtons = new(); [JsonProperty("Tickets - Log Tickets To Files")] public bool logTickets = true; [JsonProperty("Tickets - New Tickets Parent ID")] public Snowflake newTicketsParentId = (Snowflake)0; [JsonProperty("Tickets - Closed Tickets Parent ID")] public Snowflake closedTicketsParentId = (Snowflake)0; [JsonProperty("Votes - Enabled")] public bool enableVotes = true; [JsonProperty("Votes - Ping Role ID")] public Snowflake votePingRole = (Snowflake)0; [JsonProperty("Votes - Default Vote Title")] public string defaultVoteTitle = "Map Vote"; [JsonProperty("Votes - Default Vote Description")] public string defaultVoteDescription = "Feel free to vote for one of the maps that will appear on the next wipe.\nYour vote will have more points if you played enough on current wipe.\nYour ranks also may vary on the point amount.\nIf your account is not connected to discord you will vote with 1 point."; [JsonProperty("Votes - Default Vote Fields")] public string defaultVoteFields = "Procedural Map\nCustom Map"; [JsonProperty("Votes - Default Vote Time")] public string defaultVoteTime = "24h"; [JsonProperty("Votes - Playtime Vote Points (minutes : points)")] public Dictionary voteTimePlayedPoints = new(); [JsonProperty("Votes - Permission Vote Points Multipliers (perm : multiplier)")] public Dictionary votePointMultipliers = new(); [JsonProperty("F7 Reports - Enabled")] public bool enableReports = true; [JsonProperty("F7 Reports - Abuse Report Channel (0, to disable)")] public Snowflake reportAbuseChannel = (Snowflake)0; [JsonProperty("F7 Reports - Bug Report Channel (0, to disable)")] public Snowflake reportBugChannel = (Snowflake)0; [JsonProperty("F7 Reports - Cheat Report Channel (0, to disable)")] public Snowflake reportCheatChannel = (Snowflake)0; [JsonProperty("F7 Reports - General Report Channel (0, to disable)")] public Snowflake reportGeneralChannel = (Snowflake)0; [JsonProperty("F7 Reports - Offensive Report Channel (0, to disable)")] public Snowflake reportOffensiveChannel = (Snowflake)0; [JsonProperty("F7 Reports - Idea Report Channel (0, to disable)")] public Snowflake reportIdeaChannel = (Snowflake)0; [JsonProperty("F7 Reports - Idea Yes Emote")] public string ideaYesEmote = ":yes:1038108556542627891"; [JsonProperty("F7 Reports - Idea No Emote")] public string ideaNoEmote = ":no:1038108555007492187"; [JsonProperty("Cupboard Auth Check - Enabled")] public bool cupboardAuthCheck = true; [JsonProperty("Cupboard Auth Check - Command")] public string cupboardCommand = "tcallow"; [JsonProperty("Text Messages")] public DiscordLangConfig lang = new(); } private class DiscordLangConfig { [JsonProperty("Connection - Embed Title")] public string connectionEmbedTitle = "[Discord - RUST] Connection Verification"; [JsonProperty("Connection - Embed Description")] public string connectionEmbedDescription = "Click button below to generate code that will allow you to connect your Discord account with your RUST account."; [JsonProperty("Connection - Embed Button Text")] public string connectionEmbedButtonText = "Verify Connection"; [JsonProperty("Connection - Sent Code")] public string connectionSentCode = "Your verification code is **{0}**.\nGo to the server and run `/{1} {0}` to connect your Discord to Rust account."; [JsonProperty("Vote - End Title")] public string voteEndTitle = "Vote has ended."; [JsonProperty("Vote - End Description")] public string voteEndDescription = "These are the vote results:\n\n"; [JsonProperty("Vote - End Time")] public string voteEndTime = "\nVote results will be available {0}."; [JsonProperty("Vote - Ended")] public string voteEnded = "This vote has been ended. You can no longer vote for that!"; [JsonProperty("Vote - Button")] public string voteButton = "Vote"; [JsonProperty("Vote - Voting Title")] public string voteSubmitTitle = "Vote Submit - Your vote will have {0} points"; [JsonProperty("Vote - Voting Text")] public string voteSubmitText = "Vote by letter. You can use {0} letters."; [JsonProperty("Vote - Not Valid Input")] public string voteNotValidLetter = "Your vote input is not valid. Try again!"; [JsonProperty("Vote - Valid Input")] public string voteValid = "You've successfully voted for **{0}** with **{1}** points!"; [JsonProperty("Tickets - Embed Title")] public string ticketEmbedTitle = "Create Report"; [JsonProperty("Tickets - Embed Description")] public string ticketEmbedDescription = "If you need help, feel free to create a ticket to get in touch with **Admins**.\nSelect in what language you need to get in touch."; [JsonProperty("Tickets - Cannot Close")] public string ticketCannotClose = "This channel isn't an ticket that can be closed or it has been closed already."; [JsonProperty("Tickets - Closed Title")] public string ticketClosedTitle = "Ticket Closed"; [JsonProperty("Tickets - Closed Description")] public string ticketClosedDescription = "Ticket has been closed.\nIt will be automatically removed after 24 hours."; [JsonProperty("Cupboard Auth Check - Title")] public string tcAuthTitle = "Cupboard Authorization Request"; [JsonProperty("Cupboard Auth Check - Description")] public string tcAuthDesc = "Player **{0}** is requesting for cupboard access at **{1}**.\nClick button to grant him access or just ignore this message."; [JsonProperty("Cupboard Auth Check - Button")] public string tcAuthButton = "Authorize"; [JsonProperty("Cupboard Auth Check - Error")] public string authError = "An error occured while trying to respond to this request. Probably cupboard is missing, or you've already responded to this request."; [JsonProperty("Cupboard Auth Check - Granted Access")] public string authAccepted = "You've successfully granted access to the cupboard!"; } private class LanguageButtonConfig { [JsonProperty("Displayed Name")] public string displayedName = ""; [JsonProperty("Displayed Icon")] public string displayedIcon = ""; [JsonProperty("Assigned Support Discord Group IDs")] public List supportRanks = new(); [JsonProperty("Response Message")] public string responseMessage = "Ticket has been created! {0}"; [JsonProperty("Greeting Message - Title")] public string greetingMessageTitle = "New Ticket"; [JsonProperty("Greeting Message - Description")] public string greetingMessageDescription = "Hey, <@{0}>! Welcome in your ticket! \nSupport group has been informed about your problem.\n**Describe your problem here and wait for the answer!**\nIf you want to close your ticket, feel free to write **/close** command."; } private static PluginData data = new(); private class PluginData { public int ticketId = 0; [JsonProperty("User Data")] public Dictionary linkedPlayers = new(); public Dictionary channelRemovalDate = new(); public Dictionary voteLinks = new(); public Dictionary>> voteWeights = new(); public Dictionary voteCount = new(); public Dictionary> voteEnds = new(); public HashSet endedVotes = new(); } private void LoadData() { data = Interface.Oxide.DataFileSystem.ReadObject(Name); timer.Every(Core.Random.Range(500, 700), SaveData); } private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, data); } }