refactor: remove Discord bot

This commit is contained in:
goat 2020-12-05 23:47:41 +01:00
parent 5ae756577c
commit 969e935f27
3 changed files with 0 additions and 407 deletions

View file

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Dalamud.Configuration;
using Dalamud.DiscordBot;
using Dalamud.Game.Chat;
using Newtonsoft.Json;
using Serilog;
@ -12,8 +10,6 @@ namespace Dalamud
[Serializable]
internal class DalamudConfiguration
{
public DiscordFeatureConfiguration DiscordFeatureConfig { get; set; }
public bool OptOutMbCollection { get; set; } = false;
public List<string> BadWords { get; set; }

View file

@ -1,354 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.Chat;
using Dalamud.Game.Chat.SeStringHandling;
using Dalamud.Game.Chat.SeStringHandling.Payloads;
using Dalamud.Game.Internal.Libc;
using Discord;
using Discord.WebSocket;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json.Linq;
using Serilog;
namespace Dalamud.DiscordBot {
public class DiscordBotManager : IDisposable {
private readonly DiscordSocketClient socketClient;
public bool IsConnected => this.socketClient.ConnectionState == ConnectionState.Connected && this.isReady;
public ulong UserId => this.socketClient.CurrentUser.Id;
private readonly Dalamud dalamud;
private readonly DiscordFeatureConfiguration config;
private bool isReady;
private readonly List<SocketMessage> recentMessages = new List<SocketMessage>();
/// <summary>
/// The FFXIV payload sequence to represent the name/world separator
/// </summary>
private readonly string worldIcon = Encoding.UTF8.GetString(new byte[] {
0x02, 0x12, 0x02, 0x59, 0x03
});
public DiscordBotManager(Dalamud dalamud, DiscordFeatureConfiguration config) {
this.dalamud = dalamud;
this.config = config;
config.OwnerUserId = 123830058426040321;
this.socketClient = new DiscordSocketClient();
this.socketClient.Ready += SocketClientOnReady;
this.dalamud.NetworkHandlers.ProcessCfPop += ProcessCfPop;
}
private XivChatType GetChatTypeBySlug(string slug) {
var selectedType = XivChatType.None;
foreach (var chatType in Enum.GetValues(typeof(XivChatType)).Cast<XivChatType>()) {
var details = chatType.GetDetails();
if (details == null)
continue;
if (slug == details.Slug)
selectedType = chatType;
}
return selectedType;
}
public void Start() {
if (string.IsNullOrEmpty(this.config.Token)) {
Log.Error("Discord token is null or empty.");
return;
}
try {
this.socketClient.LoginAsync(TokenType.Bot, this.config.Token).GetAwaiter().GetResult();
this.socketClient.StartAsync().GetAwaiter().GetResult();
} catch (Exception ex) {
Log.Error(ex, "Discord bot login failed.");
this.dalamud.Framework.Gui.Chat.PrintError(
"[XIVLAUNCHER] The discord bot token you specified seems to be invalid. Please check the guide linked on the settings page for more details.");
}
}
private Task SocketClientOnReady() {
Log.Information("Discord bot connected as " + this.socketClient.CurrentUser);
this.isReady = true;
this.socketClient.SetGameAsync("FINAL FANTASY XIV").GetAwaiter().GetResult();
return Task.CompletedTask;
}
public async Task ProcessCfPop(ContentFinderCondition cfc) {
if (!this.IsConnected)
return;
try {
var contentName = cfc.Name;
if (this.config.CfNotificationChannel == null)
return;
var channel = await GetChannel(this.config.CfNotificationChannel);
var iconFolder = (cfc.Image / 1000) * 1000;
var embedBuilder = new EmbedBuilder {
Title = "Duty is ready: " + contentName,
Timestamp = DateTimeOffset.Now,
Color = new Color(0x297c00),
ImageUrl = "https://xivapi.com" + $"/i/{iconFolder}/{cfc.Image}.png"
};
await channel.SendMessageAsync(embed: embedBuilder.Build());
} catch (Exception ex) {
Log.Error(ex, "Could not process CF pop.");
}
}
public async Task ProcessCfPreferredRoleChange(string rouletteName, string prevRoleName, string currentRoleName)
{
if (!this.IsConnected)
return;
try {
if (this.config.CfPreferredRoleChannel == null)
return;
var channel = await GetChannel(this.config.CfPreferredRoleChannel);
var world = string.Empty;
if (this.dalamud.ClientState.Actors.Length > 0)
world = this.dalamud.ClientState.LocalPlayer.CurrentWorld.GameData.Name;
var embedBuilder = new EmbedBuilder
{
Title = "Roulette bonus changed: " + rouletteName,
Description = $"From {prevRoleName} to {currentRoleName}",
Footer = new EmbedFooterBuilder
{
Text = $"On {world} | XIVLauncher"
},
Timestamp = DateTimeOffset.Now,
Color = new Color(0xf5aa42),
};
await channel.SendMessageAsync(embed: embedBuilder.Build());
} catch (Exception ex) {
Log.Error(ex, "Could not process preferred role.");
}
}
public async Task ProcessRetainerSale(uint itemId, int amount, bool isHq) {
if (!IsConnected)
return;
try {
if (this.config.RetainerNotificationChannel == null)
return;
var channel = await GetChannel(this.config.RetainerNotificationChannel);
dynamic item = XivApi.GetItem(itemId).GetAwaiter().GetResult();
var character = this.dalamud.ClientState.LocalPlayer;
var characterInfo = await GetCharacterInfo(character.Name, character.HomeWorld.GameData.Name);
var embedBuilder = new EmbedBuilder
{
Title = (isHq ? "<:hq:593406013651156994> " : "") + item.Name,
Url = "https://www.garlandtools.org/db/#item/" + itemId,
Description = "Sold " + amount,
Timestamp = DateTimeOffset.Now,
Color = new Color(0xd89b0d),
ThumbnailUrl = "https://xivapi.com" + item.Icon,
Footer = new EmbedFooterBuilder
{
Text = $"XIVLauncher | {character.Name}",
IconUrl = characterInfo.AvatarUrl
}
};
await channel.SendMessageAsync(embed: embedBuilder.Build());
} catch (Exception ex) {
Log.Error(ex, "Could not process retainer msg.");
}
}
public async Task ProcessChatMessage(XivChatType type, SeString message, SeString sender) {
if (this.dalamud.SeStringManager == null)
return;
// Special case for outgoing tells, these should be sent under Incoming tells
var wasOutgoingTell = false;
if (type == XivChatType.TellOutgoing) {
type = XivChatType.TellIncoming;
wasOutgoingTell = true;
}
var chatTypeConfigs =
this.config.ChatTypeConfigurations.Where(typeConfig => typeConfig.ChatType == type);
if (!chatTypeConfigs.Any())
return;
var chatTypeDetail = type.GetDetails();
var channels = chatTypeConfigs.Select(c => GetChannel(c.Channel).GetAwaiter().GetResult());
var playerLink = sender.Payloads.FirstOrDefault(x => x.Type == PayloadType.Player) as PlayerPayload;
string senderName;
string senderWorld;
if (this.dalamud.ClientState.LocalPlayer != null) {
if (playerLink == null)
{
// chat messages from the local player do not include a player link, and are just the raw name
// but we should still track other instances to know if this is ever an issue otherwise
// Special case 2 - When the local player talks in party/alliance, the name comes through as raw text,
// but prefixed by their position number in the party (which for local player may always be 1)
if (sender.TextValue.EndsWith(this.dalamud.ClientState.LocalPlayer.Name))
{
senderName = this.dalamud.ClientState.LocalPlayer.Name;
}
else
{
Log.Error("playerLink was null. Sender: {0}", BitConverter.ToString(sender.Encode()));
senderName = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : sender.TextValue;
}
senderWorld = this.dalamud.ClientState.LocalPlayer.HomeWorld.GameData.Name;
}
else
{
senderName = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : playerLink.PlayerName;
senderWorld = playerLink.World.Name;
}
} else {
senderName = string.Empty;
senderWorld = string.Empty;
}
var rawMessage = message.TextValue;
var avatarUrl = string.Empty;
var lodestoneId = string.Empty;
if (!this.config.DisableEmbeds && !string.IsNullOrEmpty(senderName)) {
var searchResult = await GetCharacterInfo(senderName, senderWorld);
lodestoneId = searchResult.LodestoneId;
avatarUrl = searchResult.AvatarUrl;
}
Thread.Sleep(this.config.ChatDelayMs);
var name = wasOutgoingTell
? "You"
: senderName + (string.IsNullOrEmpty(senderWorld) || string.IsNullOrEmpty(senderName)
? ""
: $" on {senderWorld}");
for (var chatTypeIndex = 0; chatTypeIndex < chatTypeConfigs.Count(); chatTypeIndex++) {
if (!this.config.DisableEmbeds) {
var embedBuilder = new EmbedBuilder
{
Author = new EmbedAuthorBuilder
{
IconUrl = avatarUrl,
Name = name,
Url = !string.IsNullOrEmpty(lodestoneId) ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
},
Description = rawMessage,
Timestamp = DateTimeOffset.Now,
Footer = new EmbedFooterBuilder { Text = type.GetDetails().FancyName },
Color = new Color((uint)(chatTypeConfigs.ElementAt(chatTypeIndex).Color & 0xFFFFFF))
};
if (this.config.CheckForDuplicateMessages)
{
var recentMsg = this.recentMessages.FirstOrDefault(
msg => msg.Embeds.FirstOrDefault(
embed => embed.Description == embedBuilder.Description &&
embed.Author.HasValue &&
embed.Author.Value.Name == embedBuilder.Author.Name &&
embed.Timestamp.HasValue &&
Math.Abs(
(embed.Timestamp.Value.ToUniversalTime().Date -
embedBuilder
.Timestamp.Value.ToUniversalTime().Date)
.Milliseconds) < 15000)
!= null);
if (recentMsg != null)
{
Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
this.recentMessages.Remove(recentMsg);
return;
}
}
await channels.ElementAt(chatTypeIndex).SendMessageAsync(embed: embedBuilder.Build());
} else {
var simpleMessage = $"{name}: {rawMessage}";
if (this.config.CheckForDuplicateMessages) {
var recentMsg = this.recentMessages.FirstOrDefault(
msg => msg.Content == simpleMessage);
if (recentMsg != null)
{
Log.Verbose("Duplicate message: {0}", simpleMessage);
this.recentMessages.Remove(recentMsg);
return;
}
}
await channels.ElementAt(chatTypeIndex).SendMessageAsync($"**[{chatTypeDetail.Slug}]{name}**: {rawMessage}");
}
}
}
private async Task<(string LodestoneId, string AvatarUrl)> GetCharacterInfo(string name, string worldName) {
try
{
dynamic charCandidates = await XivApi.GetCharacterSearch(name, worldName);
if (charCandidates.Results.Count > 0)
{
var avatarUrl = charCandidates.Results[0].Avatar;
var lodestoneId = charCandidates.Results[0].ID;
return (lodestoneId, avatarUrl);
}
}
catch (Exception ex)
{
Log.Error(ex, "Could not get XIVAPI character search result.");
}
return (null, null);
}
private async Task<IMessageChannel> GetChannel(ChannelConfiguration channelConfig) {
if (channelConfig.Type == ChannelType.Guild)
return this.socketClient.GetGuild(channelConfig.GuildId).GetTextChannel(channelConfig.ChannelId);
return await this.socketClient.GetUser(channelConfig.ChannelId).GetOrCreateDMChannelAsync();
}
public void Dispose() {
this.socketClient.LogoutAsync().GetAwaiter().GetResult();
}
}
}

View file

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dalamud.Game.Chat;
namespace Dalamud.DiscordBot
{
public enum ChannelType {
Guild,
User
}
[Serializable]
public class ChannelConfiguration {
public ChannelType Type { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}
[Serializable]
public class ChatTypeConfiguration {
public XivChatType ChatType { get; set; }
public ChannelConfiguration Channel { get; set; }
public int Color { get; set; }
}
[Serializable]
public class DiscordFeatureConfiguration
{
public string Token { get; set; }
public bool CheckForDuplicateMessages { get; set; }
public int ChatDelayMs { get; set; }
public bool DisableEmbeds { get; set; }
public ulong OwnerUserId { get; set; }
public List<ChatTypeConfiguration> ChatTypeConfigurations { get; set; }
public ChannelConfiguration CfNotificationChannel { get; set; }
public ChannelConfiguration CfPreferredRoleChannel { get; set; }
public ChannelConfiguration RetainerNotificationChannel { get; set; }
}
}