public async Task AddMessage(ChatMessageViewModel message) { message.ProcessingStartTime = DateTimeOffset.Now; Logger.Log(LogLevel.Debug, string.Format("Message Received - {0} - {1} - {2}", message.ID.ToString(), message.ProcessingStartTime, message)); // Pre message processing if (message is UserChatMessageViewModel) { if (message.User != null) { if (message.Platform == StreamingPlatformTypeEnum.Twitch) { UserViewModel activeUser = ChannelSession.Services.User.GetUserByTwitchID(message.User.TwitchID); if (activeUser != null) { message.User = activeUser; } } message.User.UpdateLastActivity(); if (message.IsWhisper && ChannelSession.Settings.TrackWhispererNumber && !message.IsStreamerOrBot && message.User.WhispererNumber == 0) { await this.whisperNumberLock.WaitAndRelease(() => { if (!whisperMap.ContainsKey(message.User.ID)) { whisperMap[message.User.ID] = whisperMap.Count + 1; } message.User.WhispererNumber = whisperMap[message.User.ID]; return(Task.FromResult(0)); }); } } } // Add message to chat list bool showMessage = true; if (ChannelSession.Settings.HideBotMessages && message.User != null && ChannelSession.TwitchBotNewAPI != null && message.User.TwitchID.Equals(ChannelSession.TwitchBotNewAPI.id)) { showMessage = false; } if (!(message is AlertChatMessageViewModel) || !ChannelSession.Settings.OnlyShowAlertsInDashboard) { await DispatcherHelper.InvokeDispatcher(() => { this.messagesLookup[message.ID] = message; if (showMessage) { if (ChannelSession.Settings.LatestChatAtTop) { this.Messages.Insert(0, message); } else { this.Messages.Add(message); } } if (this.Messages.Count > ChannelSession.Settings.MaxMessagesInChat) { ChatMessageViewModel removedMessage = (ChannelSession.Settings.LatestChatAtTop) ? this.Messages.Last() : this.Messages.First(); this.messagesLookup.Remove(removedMessage.ID); this.Messages.Remove(removedMessage); } return(Task.FromResult(0)); }); } // Post message processing if (message is UserChatMessageViewModel && message.User != null) { if (message.IsWhisper && !message.IsStreamerOrBot) { if (!string.IsNullOrEmpty(ChannelSession.Settings.NotificationChatWhisperSoundFilePath)) { await ChannelSession.Services.AudioService.Play(ChannelSession.Settings.NotificationChatWhisperSoundFilePath, ChannelSession.Settings.NotificationChatWhisperSoundVolume, ChannelSession.Settings.NotificationsAudioOutput); } if (!string.IsNullOrEmpty(message.PlainTextMessage)) { EventTrigger trigger = new EventTrigger(EventTypeEnum.ChatWhisperReceived, message.User); trigger.SpecialIdentifiers["message"] = message.PlainTextMessage; await ChannelSession.Services.Events.PerformEvent(trigger); } // Don't send this if it's in response to another "You are whisperer #" message if (ChannelSession.Settings.TrackWhispererNumber && message.User.WhispererNumber > 0 && !message.PlainTextMessage.StartsWith("You are whisperer #", StringComparison.InvariantCultureIgnoreCase)) { await ChannelSession.Services.Chat.Whisper(message.User, $"You are whisperer #{message.User.WhispererNumber}.", sendAsStreamer : false); } } else { if (this.DisableChat) { Logger.Log(LogLevel.Debug, string.Format("Deleting Message As Chat Disabled - {0} - {1}", message.ID, message)); await this.DeleteMessage(message); return; } if (!string.IsNullOrEmpty(ChannelSession.Settings.NotificationChatTaggedSoundFilePath) && message.IsStreamerTagged) { await ChannelSession.Services.AudioService.Play(ChannelSession.Settings.NotificationChatTaggedSoundFilePath, ChannelSession.Settings.NotificationChatTaggedSoundVolume, ChannelSession.Settings.NotificationsAudioOutput); } else if (!string.IsNullOrEmpty(ChannelSession.Settings.NotificationChatMessageSoundFilePath)) { await ChannelSession.Services.AudioService.Play(ChannelSession.Settings.NotificationChatMessageSoundFilePath, ChannelSession.Settings.NotificationChatMessageSoundVolume, ChannelSession.Settings.NotificationsAudioOutput); } if (message.User != null && !this.userEntranceCommands.Contains(message.User.ID)) { this.userEntranceCommands.Add(message.User.ID); if (message.User.Data.EntranceCommand != null) { await message.User.Data.EntranceCommand.Perform(message.User, message.Platform); } } if (!string.IsNullOrEmpty(message.PlainTextMessage)) { EventTrigger trigger = new EventTrigger(EventTypeEnum.ChatMessageReceived, message.User); trigger.SpecialIdentifiers["message"] = message.PlainTextMessage; await ChannelSession.Services.Events.PerformEvent(trigger); } message.User.Data.TotalChatMessageSent++; string primaryTaggedUsername = message.PrimaryTaggedUsername; if (!string.IsNullOrEmpty(primaryTaggedUsername)) { UserViewModel primaryTaggedUser = ChannelSession.Services.User.GetUserByUsername(primaryTaggedUsername, message.Platform); if (primaryTaggedUser != null) { primaryTaggedUser.Data.TotalTimesTagged++; } } } await message.User.RefreshDetails(); if (!message.IsWhisper && await message.CheckForModeration()) { await this.DeleteMessage(message); return; } IEnumerable <string> arguments = null; if (ChannelSession.IsStreamer && !string.IsNullOrEmpty(message.PlainTextMessage) && message.User != null && !message.User.UserRoles.Contains(UserRoleEnum.Banned)) { if (!ChannelSession.Settings.AllowCommandWhispering && message.IsWhisper) { return; } if (ChannelSession.Settings.IgnoreBotAccountCommands) { if (ChannelSession.TwitchBotNewAPI != null && message.User.TwitchID.Equals(ChannelSession.TwitchBotNewAPI.id)) { return; } } Logger.Log(LogLevel.Debug, string.Format("Checking Message For Command - {0} - {1}", message.ID, message)); List <PermissionsCommandBase> commands = this.chatCommands.ToList(); foreach (PermissionsCommandBase command in message.User.Data.CustomCommands.Where(c => c.IsEnabled)) { commands.Add(command); } foreach (PermissionsCommandBase command in commands) { if (command.DoesTextMatchCommand(message.PlainTextMessage, out arguments)) { if (command.IsEnabled) { Logger.Log(LogLevel.Debug, string.Format("Command Found For Message - {0} - {1} - {2}", message.ID, message, command)); await command.Perform(message.User, message.Platform, arguments : arguments); if (command.Requirements.Settings.DeleteChatCommandWhenRun || (ChannelSession.Settings.DeleteChatCommandsWhenRun && !command.Requirements.Settings.DontDeleteChatCommandWhenRun)) { await this.DeleteMessage(message); } } break; } } } foreach (InventoryModel inventory in ChannelSession.Settings.Inventory.Values.ToList()) { if (inventory.ShopEnabled && CommandBase.DoesTextMatchCommand(message.PlainTextMessage, CommandBase.CommandMatchingRegexFormat, new List <string>() { inventory.ShopCommand }, out arguments)) { await inventory.PerformShopCommand(message.User, arguments, message.Platform); } else if (inventory.TradeEnabled && CommandBase.DoesTextMatchCommand(message.PlainTextMessage, CommandBase.CommandMatchingRegexFormat, new List <string>() { inventory.TradeCommand }, out arguments)) { string args = message.PlainTextMessage.Replace(inventory.TradeCommand, ""); await inventory.PerformTradeCommand(message.User, arguments, message.Platform); } } if (ChannelSession.Settings.RedemptionStoreEnabled) { if (CommandBase.DoesTextMatchCommand(message.PlainTextMessage, CommandBase.CommandMatchingRegexFormat, new List <string>() { ChannelSession.Settings.RedemptionStoreChatPurchaseCommand }, out arguments)) { await RedemptionStorePurchaseModel.Purchase(message.User, arguments); } else if (CommandBase.DoesTextMatchCommand(message.PlainTextMessage, CommandBase.CommandMatchingRegexFormat, new List <string>() { ChannelSession.Settings.RedemptionStoreModRedeemCommand }, out arguments)) { await RedemptionStorePurchaseModel.Redeem(message.User, arguments); } } GlobalEvents.ChatMessageReceived(message); await this.WriteToChatEventLog(message); } Logger.Log(LogLevel.Debug, string.Format("Message Processing Complete: {0} - {1} ms", message.ID, message.ProcessingTime)); if (message.ProcessingTime > 500) { Logger.Log(LogLevel.Error, string.Format("Long processing time detected for the following message: {0} - {1} ms - {2}", message.ID.ToString(), message.ProcessingTime, message)); } }