예제 #1
0
        private void *PrintMessageDetour(RaptureLogModule *raptureLogModule, XivChatType xivChatType, IntPtr senderName, IntPtr message, uint senderId, byte param)
        {
            try {
                if (chatTypes.Contains(xivChatType))
                {
                    // Need to hook it manually to handle changing the name until API4
                    var stdSender    = StdString.ReadFromPointer(senderName);
                    var parsedSender = SeString.Parse(stdSender.RawData);

                    if (Parse(ref parsedSender))
                    {
                        stdSender.RawData = parsedSender.Encode();
                        var allocatedString = Service.LibcFunction.NewString(stdSender.RawData);
                        var retVal          = printChatHook.Original(raptureLogModule, xivChatType, allocatedString.Address, message, senderId, param);
                        allocatedString.Dispose();
                        return(retVal);
                    }
                }
            } catch (Exception ex) {
                SimpleLog.Error(ex);
            }


            return(printChatHook.Original(raptureLogModule, xivChatType, senderName, message, senderId, param));
        }
예제 #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="PartyFinderListing"/> class.
        /// </summary>
        /// <param name="listing">The interop listing data.</param>
        internal PartyFinderListing(PartyFinderPacketListing listing)
        {
            var dataManager = Service <DataManager> .Get();

            this.objective          = listing.Objective;
            this.conditions         = listing.Conditions;
            this.dutyFinderSettings = listing.DutyFinderSettings;
            this.lootRules          = listing.LootRules;
            this.searchArea         = listing.SearchArea;
            this.slots       = listing.Slots.Select(accepting => new PartyFinderSlot(accepting)).ToArray();
            this.jobsPresent = listing.JobsPresent;

            this.Id               = listing.Id;
            this.ContentIdLower   = listing.ContentIdLower;
            this.Name             = SeString.Parse(listing.Name.TakeWhile(b => b != 0).ToArray());
            this.Description      = SeString.Parse(listing.Description.TakeWhile(b => b != 0).ToArray());
            this.World            = new Lazy <World>(() => dataManager.GetExcelSheet <World>().GetRow(listing.World));
            this.HomeWorld        = new Lazy <World>(() => dataManager.GetExcelSheet <World>().GetRow(listing.HomeWorld));
            this.CurrentWorld     = new Lazy <World>(() => dataManager.GetExcelSheet <World>().GetRow(listing.CurrentWorld));
            this.Category         = (DutyCategory)listing.Category;
            this.RawDuty          = listing.Duty;
            this.Duty             = new Lazy <ContentFinderCondition>(() => dataManager.GetExcelSheet <ContentFinderCondition>().GetRow(listing.Duty));
            this.DutyType         = (DutyType)listing.DutyType;
            this.BeginnersWelcome = listing.BeginnersWelcome == 1;
            this.SecondsRemaining = listing.SecondsRemaining;
            this.MinimumItemLevel = listing.MinimumItemLevel;
            this.Parties          = listing.NumParties;
            this.SlotsAvailable   = listing.NumSlots;
            this.JobsPresent      = listing.JobsPresent
                                    .Select(id => new Lazy <ClassJob>(
                                                () => id == 0
                        ? null
                        : dataManager.GetExcelSheet <ClassJob>().GetRow(id)))
                                    .ToArray();
        }
예제 #3
0
    public SeString ReadSeString(Utf8String xivString)
    {
        var len   = (int)(xivString.BufUsed > int.MaxValue ? int.MaxValue : xivString.BufUsed);
        var bytes = new byte[len];

        Marshal.Copy(new IntPtr(xivString.StringPtr), bytes, 0, len);
        return(SeString.Parse(bytes));
    }
예제 #4
0
        public void TestNewLinePayload()
        {
            var newLinePayloadBytes = new byte[] { 0x02, 0x10, 0x01, 0x03 };

            var seString = SeString.Parse(newLinePayloadBytes);

            Assert.True(newLinePayloadBytes.SequenceEqual(seString.Encode()));
        }
예제 #5
0
    /// <summary>
    /// Read an SeString from a specified memory address.
    /// </summary>
    /// <param name="memoryAddress">The memory address to read from.</param>
    /// <param name="maxLength">The maximum length of the string.</param>
    /// <returns>The read in string.</returns>
    public static SeString ReadSeString(IntPtr memoryAddress, int maxLength)
    {
        ReadRaw(memoryAddress, maxLength, out var buffer);

        var eos = Array.IndexOf(buffer, (byte)0);

        if (eos < 0)
        {
            return(SeString.Parse(buffer));
        }
        else
        {
            var newBuffer = new byte[eos];
            Buffer.BlockCopy(buffer, 0, newBuffer, 0, eos);
            return(SeString.Parse(newBuffer));
        }
    }
예제 #6
0
    public SeString ReadSeString(byte *ptr)
    {
        var offset = 0;

        while (true)
        {
            var b = *(ptr + offset);
            if (b == 0)
            {
                break;
            }
            offset += 1;
        }
        var bytes = new byte[offset];

        Marshal.Copy(new IntPtr(ptr), bytes, 0, offset);
        return(SeString.Parse(bytes));
    }
예제 #7
0
        private void OnChatMessage(XivChatType type, uint senderId, ref StdString sender,
                                   ref StdString message, ref bool isHandled)
        {
            if (type == XivChatType.Notice && !this.hasSeenLoadingMsg)
            {
                var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();

                this.dalamud.Framework.Gui.Chat.Print($"XIVLauncher in-game addon v{assemblyVersion} loaded.");

                foreach (var plugin in this.dalamud.PluginManager.Plugins)
                {
                    this.dalamud.Framework.Gui.Chat.Print($"    -> {plugin.Plugin.Name} v{plugin.Plugin.GetType().Assembly.GetName().Version} loaded.");
                }

                this.hasSeenLoadingMsg = true;

                if (string.IsNullOrEmpty(this.dalamud.Configuration.LastVersion) || !assemblyVersion.StartsWith(this.dalamud.Configuration.LastVersion))
                {
                    this.dalamud.Framework.Gui.Chat.PrintChat(new XivChatEntry {
                        MessageBytes = Encoding.UTF8.GetBytes("The In-Game addon has been updated or was reinstalled successfully! Please check the discord for a full changelog."),
                        Type         = XivChatType.Urgent
                    });

                    this.dalamud.Configuration.LastVersion = assemblyVersion;
                    this.dalamud.Configuration.Save(this.dalamud.StartInfo.ConfigurationPath);
                }
            }

#if !DEBUG && false
            if (!this.hasSeenLoadingMsg)
            {
                return;
            }
#endif

            var matched = this.rmtRegex.IsMatch(message.Value);
            if (matched)
            {
                // This seems to be a RMT ad - let's not show it
                Log.Debug("Handled RMT ad");
                isHandled = true;
                return;
            }

            var messageVal = message.Value;
            var senderVal  = sender.Value;

            if (this.dalamud.Configuration.BadWords != null &&
                this.dalamud.Configuration.BadWords.Any(x => messageVal.Contains(x)))
            {
                // This seems to be in the user block list - let's not show it
                Log.Debug("Blocklist triggered");
                isHandled = true;
                return;
            }

            if (type == XivChatType.RetainerSale)
            {
                foreach (var regex in retainerSaleRegexes[dalamud.StartInfo.Language])
                {
                    var matchInfo = regex.Match(message.Value);

                    // we no longer really need to do/validate the item matching since we read the id from the byte array
                    // but we'd be checking the main match anyway
                    var itemInfo = matchInfo.Groups["item"];
                    if (!itemInfo.Success)
                    {
                        continue;
                    }

                    var itemLink =
                        SeString.Parse(message.RawData).Payloads.First(x => x.Type == PayloadType.Item) as ItemPayload;

                    if (itemLink == null)
                    {
                        Log.Error("itemLink was null. Msg: {0}", BitConverter.ToString(message.RawData));
                        break;
                    }

                    Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.ItemId}, HQ {itemLink.IsHQ}");

                    var valueInfo = matchInfo.Groups["value"];
                    // not sure if using a culture here would work correctly, so just strip symbols instead
                    if (!valueInfo.Success || !int.TryParse(valueInfo.Value.Replace(",", "").Replace(".", ""), out var itemValue))
                    {
                        continue;
                    }

                    Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale(itemLink.ItemId, itemValue, itemLink.IsHQ));
                    break;
                }
            }

            var messageCopy = message;
            var senderCopy  = sender;
            Task.Run(() => this.dalamud.BotManager.ProcessChatMessage(type, messageCopy, senderCopy));

            // Handle all of this with SeString some day
            if ((this.HandledChatTypeColors.ContainsKey(type) || type == XivChatType.Say || type == XivChatType.Shout ||
                 type == XivChatType.Alliance || type == XivChatType.TellOutgoing || type == XivChatType.Yell) && !message.Value.Contains((char)0x02))
            {
                var italicsStart = message.Value.IndexOf("*");
                var italicsEnd   = message.Value.IndexOf("*", italicsStart + 1);

                var messageString = message.Value;

                while (italicsEnd != -1)
                {
                    var it = MakeItalics(
                        messageString.Substring(italicsStart, italicsEnd - italicsStart + 1).Replace("*", ""));
                    messageString = messageString.Remove(italicsStart, italicsEnd - italicsStart + 1);
                    messageString = messageString.Insert(italicsStart, it);
                    italicsStart  = messageString.IndexOf("*");
                    italicsEnd    = messageString.IndexOf("*", italicsStart + 1);
                }

                message.RawData = Encoding.UTF8.GetBytes(messageString);
            }


            var linkMatch = this.urlRegex.Match(message.Value);
            if (linkMatch.Value.Length > 0)
            {
                LastLink = linkMatch.Value;
            }
        }
예제 #8
0
 public SeString Parse(byte[] bytes) => SeString.Parse(new ReadOnlySpan <byte>(bytes));
예제 #9
0
 public unsafe SeString Parse(ReadOnlySpan <byte> data) => SeString.Parse(data);
예제 #10
0
 public unsafe SeString Parse(byte *ptr, int len) => SeString.Parse(ptr, len);
예제 #11
0
        public async Task ProcessChatMessage(XivChatType type, StdString message, StdString sender)
        {
            // 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 parsedSender = SeString.Parse(sender.RawData);
            var playerLink   = parsedSender.Payloads.FirstOrDefault(x => x.Type == PayloadType.Player) as PlayerPayload;

            string senderName;
            string senderWorld;

            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 (parsedSender.TextValue.EndsWith(this.dalamud.ClientState.LocalPlayer.Name))
                {
                    senderName = this.dalamud.ClientState.LocalPlayer.Name;
                }
                else
                {
                    Log.Error("playerLink was null. Sender: {0}", BitConverter.ToString(sender.RawData));

                    senderName = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : parsedSender.TextValue;
                }

                senderWorld = this.dalamud.ClientState.LocalPlayer.HomeWorld.GameData.Name;
            }
            else
            {
                senderName  = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : playerLink.PlayerName;
                senderWorld = playerLink.World.Name;
            }

            var rawMessage = SeString.Parse(message.RawData).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}");
                }
            }
        }
예제 #12
0
 /// <summary>
 /// Convert a Lumina SeString into a Dalamud SeString.
 /// This conversion re-parses the string.
 /// </summary>
 /// <param name="originalString">The original Lumina SeString.</param>
 /// <returns>The re-parsed Dalamud SeString.</returns>
 public static SeString ToDalamudString(this Lumina.Text.SeString originalString) => SeString.Parse(originalString.RawData);
예제 #13
0
        private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
                                                uint senderid, IntPtr parameter)
        {
            var retVal = IntPtr.Zero;

            try {
                var sender  = StdString.ReadFromPointer(pSenderName);
                var message = StdString.ReadFromPointer(pMessage);

                var parsedSender  = SeString.Parse(sender.RawData);
                var parsedMessage = SeString.Parse(message.RawData);

                //Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(message.RawData).Replace("-", " ")}] {message.Value} from {senderName.Value}");

                var originalMessageData = (byte[])message.RawData.Clone();
                var oldEdited           = parsedMessage.Encode();

                // Call events
                var isHandled = false;
                OnChatMessage?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
                OnChatMessageRaw?.Invoke(chattype, senderid, ref sender, ref message, ref isHandled);

                var newEdited = parsedMessage.Encode();

                if (!FastByteArrayCompare(oldEdited, newEdited))
                {
                    Log.Verbose("SeString was edited, taking precedence over StdString edit.");
                    message.RawData = newEdited;
                }
                Log.Debug($"\nOLD: {BitConverter.ToString(originalMessageData)}\nNEW: {BitConverter.ToString(newEdited)}");

                var            messagePtr      = pMessage;
                OwnedStdString allocatedString = null;

                if (!FastByteArrayCompare(originalMessageData, message.RawData))
                {
                    allocatedString = this.dalamud.Framework.Libc.NewString(message.RawData);
                    Log.Debug(
                        $"HandlePrintMessageDetour String modified: {originalMessageData}({messagePtr}) -> {message}({allocatedString.Address})");
                    messagePtr = allocatedString.Address;
                }

                // Print the original chat if it's handled.
                if (!isHandled)
                {
                    retVal = this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, parameter);
                }

                if (this.baseAddress == IntPtr.Zero)
                {
                    this.baseAddress = manager;
                }

                allocatedString?.Dispose();
            } catch (Exception ex) {
                Log.Error(ex, "Exception on OnChatMessage hook.");
                retVal = this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter);
            }

            return(retVal);
        }
예제 #14
0
        private void OnChatMessage(XivChatType type, uint senderId, ref StdString sender,
                                   ref StdString message, ref bool isHandled)
        {
            if (type == XivChatType.Notice && !this.hasSeenLoadingMsg)
            {
                PrintWelcomeMessage();
            }

            // For injections while logged in
            if (this.dalamud.ClientState.LocalPlayer != null && this.dalamud.ClientState.TerritoryType == 0 && !this.hasSeenLoadingMsg)
            {
                PrintWelcomeMessage();
            }

#if !DEBUG && false
            if (!this.hasSeenLoadingMsg)
            {
                return;
            }
#endif

            var messageVal = message.Value;
            var senderVal  = sender.Value;

            if (type == XivChatType.RetainerSale)
            {
                foreach (var regex in retainerSaleRegexes[dalamud.StartInfo.Language])
                {
                    var matchInfo = regex.Match(message.Value);

                    // we no longer really need to do/validate the item matching since we read the id from the byte array
                    // but we'd be checking the main match anyway
                    var itemInfo = matchInfo.Groups["item"];
                    if (!itemInfo.Success)
                    {
                        continue;
                    }

                    var itemLink =
                        SeString.Parse(message.RawData).Payloads.First(x => x.Type == PayloadType.Item) as ItemPayload;

                    if (itemLink == null)
                    {
                        Log.Error("itemLink was null. Msg: {0}", BitConverter.ToString(message.RawData));
                        break;
                    }

                    Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.Item.RowId}, HQ {itemLink.IsHQ}");

                    var valueInfo = matchInfo.Groups["value"];
                    // not sure if using a culture here would work correctly, so just strip symbols instead
                    if (!valueInfo.Success || !int.TryParse(valueInfo.Value.Replace(",", "").Replace(".", ""), out var itemValue))
                    {
                        continue;
                    }

                    Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale((int)itemLink.Item.RowId, itemValue, itemLink.IsHQ));
                    break;
                }
            }

            var messageCopy = message;
            var senderCopy  = sender;
            Task.Run(() => this.dalamud.BotManager.ProcessChatMessage(type, messageCopy, senderCopy));

            // Handle all of this with SeString some day
            if ((this.HandledChatTypeColors.ContainsKey(type) || type == XivChatType.Say || type == XivChatType.Shout ||
                 type == XivChatType.Alliance || type == XivChatType.TellOutgoing || type == XivChatType.Yell) && !message.Value.Contains((char)0x02))
            {
                var italicsStart = message.Value.IndexOf("*");
                var italicsEnd   = message.Value.IndexOf("*", italicsStart + 1);

                var messageString = message.Value;

                while (italicsEnd != -1)
                {
                    var it = MakeItalics(
                        messageString.Substring(italicsStart, italicsEnd - italicsStart + 1).Replace("*", ""));
                    messageString = messageString.Remove(italicsStart, italicsEnd - italicsStart + 1);
                    messageString = messageString.Insert(italicsStart, it);
                    italicsStart  = messageString.IndexOf("*");
                    italicsEnd    = messageString.IndexOf("*", italicsStart + 1);
                }

                message.RawData = Encoding.UTF8.GetBytes(messageString);
            }


            var linkMatch = this.urlRegex.Match(message.Value);
            if (linkMatch.Value.Length > 0)
            {
                LastLink = linkMatch.Value;
            }
        }
예제 #15
0
        public async Task ProcessChatMessage(XivChatType type, string message, string sender)
        {
            // Special case for outgoing tells, these should be sent under Incoming tells
            var wasOutgoingTell = false;

            if (type == XivChatType.TellOutgoing)
            {
                type            = XivChatType.TellIncoming;
                sender          = this.dalamud.ClientState.LocalPlayer.Name;
                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 senderSplit = sender.Split(new[] { this.worldIcon }, StringSplitOptions.None);


            var world = string.Empty;

            if (this.dalamud.ClientState.Actors.Length > 0)
            {
                world = this.dalamud.ClientState.LocalPlayer.CurrentWorld.Name;
            }

            if (senderSplit.Length == 2)
            {
                world  = senderSplit[1];
                sender = senderSplit[0];
            }

            sender  = SeString.Parse(sender).Output;
            message = SeString.Parse(message).Output;

            sender = RemoveAllNonLanguageCharacters(sender);

            var avatarUrl   = "";
            var lodestoneId = "";

            if (!this.config.DisableEmbeds)
            {
                var searchResult = await GetCharacterInfo(sender, world);

                lodestoneId = searchResult.LodestoneId;
                avatarUrl   = searchResult.AvatarUrl;
            }

            Thread.Sleep(this.config.ChatDelayMs);

            var name = wasOutgoingTell
                           ? "You"
                           : sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
                                           ? ""
                                           : $" on {world}");

            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 = message,
                        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}: {message}";

                    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}**: {message}");
                }
            }
        }
예제 #16
0
    /// <summary>
    /// Read a null-terminated SeString from a specified memory address.
    /// </summary>
    /// <param name="memoryAddress">The memory address to read from.</param>
    /// <returns>The read in string.</returns>
    public static SeString ReadSeStringNullTerminated(IntPtr memoryAddress)
    {
        var buffer = ReadRawNullTerminated(memoryAddress);

        return(SeString.Parse(buffer));
    }