Exemple #1
0
        /// <inheritdoc />
        protected override Task <bool> SendNotification(NotificationData notification)
        {
            var ircClient = this.ircClients[notification.Server];

            if (ircClient != null)
            {
                ircClient.Command("PRIVMSG", notification.Channel, notification.Text);
                return(Task.FromResult(true));
            }

            return(Task.FromResult(false));
        }
Exemple #2
0
        /// <summary>
        /// Handles service bus messages.
        /// </summary>
        private async Task ServiceBusMessageHandler(ProcessMessageEventArgs args)
        {
            string           body             = args.Message.Body.ToString();
            NotificationData notificationData = null;

            try
            {
                notificationData = JsonConvert.DeserializeObject <NotificationData>(body);
            }
            catch (JsonSerializationException ex)
            {
                Log.Warning(ex, "Failed to deserialize notification");
            }

            if (notificationData != null)
            {
                // If it's a system notification, handle it directly, otherwise pass it along
                if (notificationData.Type == NotificationType.System)
                {
                    switch (notificationData.SubType)
                    {
                    case SubType.SettingsUpdate:
                        await this.UpdateSettingsAsync();

                        break;

                    case SubType.Shutdown:
                        Log.Information("Shutdown notification received");
                        this.exitCode = (int)ExitCode.ExpectedShutdown;
                        break;

                    default:
                        Log.Error($"Error processing notification, unrecognized subtype: {notificationData.SubType}");
                        break;
                    }
                }
                else
                {
                    try
                    {
                        await this.SendNotification(notificationData);
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Error processing notification");
                    }
                }
            }

            // For now, no retries. Regardless of notification send success, complete it.
            await args.CompleteMessageAsync(args.Message);
        }
Exemple #3
0
        /// <inheritdoc />
        protected override Task <bool> SendNotification(NotificationData notification)
        {
            if (this.ircClients.TryGetValue(notification.Server, out var ircClient))
            {
                ircClient.Command("PRIVMSG", notification.Channel, notification.Text);
                var props = new Dictionary <string, string> {
                    { "server", ircClient.Host.ToLowerInvariant() },
                    { "channel", notification.Channel.ToLowerInvariant() },
                };

                this.TrackEvent("notificationSent", props);
                return(Task.FromResult(true));
            }

            return(Task.FromResult(false));
        }
Exemple #4
0
        /// <summary>
        /// Creates a QueueClient for azure service bus to listen for notifications.
        /// </summary>
        private void StartServiceBusListener()
        {
            this.queueClient = new QueueClient(this.Config.ServiceBusConnectionString, this.queueName, ReceiveMode.PeekLock);

            // Register an OnMessage callback
            this.queueClient.RegisterMessageHandler(
                async(message, token) =>
            {
                var body = Encoding.UTF8.GetString(message.Body);

                NotificationData notificationData = null;

                try
                {
                    notificationData = JsonConvert.DeserializeObject <NotificationData>(body);
                }
                catch (JsonSerializationException ex)
                {
                    this.Logger.Log(LogType.Warn, $"Failed to deserialize notification: {ex}");
                }

                if (notificationData != null)
                {
                    try
                    {
                        await this.SendNotification(notificationData);
                    }
                    catch (Exception ex)
                    {
                        this.Logger.Log(LogType.Error, "Error processing notification: {0}", ex);
                    }
                }

                // For now, no retries. Regardless of notification send success, complete it.
                await queueClient.CompleteAsync(message.SystemProperties.LockToken);
            });
        }
Exemple #5
0
        protected override async Task <bool> SendNotification(NotificationData notification)
        {
            if (notification.Text.Contains("%%username%%"))
            {
                var member = await this.client.GetMemberAsync(new HashId(notification.Server), new HashId(notification.UserId));

                if (member != null)
                {
                    notification.Text = notification.Text.Replace("%%username%%", member.Nickname ?? member.Name);
                }
            }

            Message originalMessage = null;

            if (!string.IsNullOrEmpty(notification.MessageId))
            {
                try
                {
                    originalMessage = await this.client.GetMessageAsync(Guid.Parse(notification.Channel), Guid.Parse(notification.MessageId));
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Failed to fetch notification base message");
                }
            }

            if (originalMessage != null)
            {
                await originalMessage.ReplyAsync(notification.Text);
            }
            else
            {
                await this.client.CreateMessageAsync(Guid.Parse(notification.Channel), notification.Text);
            }

            return(true);
        }
Exemple #6
0
 /// <summary>
 /// Attempts to process and send a notification.
 /// </summary>
 /// <param name="notification">The notification data.</param>
 /// <returns>True, if notification was successfully processed.</returns>
 protected abstract Task <bool> SendNotification(NotificationData notification);
        /// <inheritdoc />
        protected override async Task <bool> SendNotification(NotificationData notification)
        {
            var guild = this.Client.GetGuild(Convert.ToUInt64(notification.Server)) as IGuild;

            // if the guild wasn't found, it belongs to another shard.
            if (guild == null)
            {
                return(false);
            }

            var channelToUse = this.Client.GetChannel(Convert.ToUInt64(notification.Channel)) as ITextChannel;

            // if the channel doesn't exist or we don't have send permissions, try to get the default channel instead.
            if (!channelToUse?.GetCurrentUserPermissions().SendMessages ?? true)
            {
                channelToUse = await guild.GetDefaultChannelAsync() as ITextChannel;

                // if the default channel couldn't be found or we don't have permissions, then we're SOL.
                // flag as processed.
                if (channelToUse == null || !channelToUse.GetCurrentUserPermissions().SendMessages)
                {
                    return(true);
                }
            }

            var settings = SettingsConfig.GetSettings(notification.Server);

            // adjust the notification text to disable discord link parsing, if configured to do so
            if (settings.DisableLinkParsing)
            {
                notification.Text = Consts.UrlRegex.Replace(notification.Text, new MatchEvaluator((Match urlMatch) =>
                {
                    return($"<{urlMatch.Captures[0]}>");
                }));
            }

            try
            {
                if (notification.Embed != null && settings.HasFlag(notification.Type))
                {
                    // TODO: discord handles twitter embeds nicely; should adjust the notification data accordingly so we don't need this explicit check here
                    if (notification.Type == NotificationType.Twitter)
                    {
                        await channelToUse.SendMessageAsync(notification.Embed.Url);
                    }
                    else
                    {
                        var url = string.IsNullOrEmpty(notification.Embed.Url) ? string.Empty : $"<{notification.Embed.Url}>";
                        await channelToUse.SendMessageAsync(url, false, notification.Embed.CreateEmbedBuilder());
                    }
                }
                else
                {
                    await channelToUse.SendMessageAsync(notification.Text);
                }
            }
            catch (Exception ex)
            {
                // TODO:
                // Add retry support.
                string extraData = null;
                if (notification.Embed != null)
                {
                    extraData = JsonConvert.SerializeObject(notification.Embed);
                }
                this.Logger.Log(LogType.Error, $"Failed to send notification {extraData}: {ex}");
            }

            return(true);
        }
Exemple #8
0
        /// <inheritdoc />
        protected override async Task <bool> SendNotification(NotificationData notification)
        {
            // if the guild wasn't found, it belongs to another shard.
            if (!(this.Client.GetGuild(Convert.ToUInt64(notification.Server)) is IGuild guild))
            {
                return(false);
            }

            Log.Information($"Sending {notification.Type} notification to {notification.Channel} on guild {notification.Server}");

            string extraText    = string.Empty;
            var    channelToUse = this.Client.GetChannel(Convert.ToUInt64(notification.Channel)) as ITextChannel;

            // if the channel doesn't exist or we don't have send permissions, try to get the default channel instead.
            if (!channelToUse?.GetCurrentUserPermissions().SendMessages ?? true)
            {
                // add some hint text about the misconfigured channel
                string hint = "fix it in the admin panel";
                if (notification.Type == NotificationType.Reminder)
                {
                    hint = "server owner: `.remove timer ###` and recreate it";
                }

                if (channelToUse == null)
                {
                    extraText = $" [Note: the channel configured is missing; {hint}]";
                }
                else
                {
                    extraText = $" [Note: missing permissions for the channel configured; adjust permissions or {hint}]";
                }

                channelToUse = await guild.GetDefaultChannelAsync();

                // if the default channel couldn't be found or we don't have permissions, then we're SOL.
                // flag as processed.
                if (channelToUse == null || !channelToUse.GetCurrentUserPermissions().SendMessages)
                {
                    return(true);
                }
            }

            var settings = SettingsConfig.GetSettings(notification.Server);

            // adjust the notification text to disable discord link parsing, if configured to do so
            if (settings.DisableLinkParsing && notification.Type != NotificationType.Reminder)
            {
                notification.Text = Consts.UrlRegex.Replace(notification.Text, new MatchEvaluator((Match urlMatch) =>
                {
                    return($"<{urlMatch.Captures[0]}>");
                }));
            }

            try
            {
                var customText      = settings.NotificationText.FirstOrDefault(n => n.Type == notification.Type)?.Text;
                var allowedMentions = notification.AllowMentions ?
                                      new AllowedMentions {
                    MentionRepliedUser = false, AllowedTypes = AllowedMentionTypes.Roles | AllowedMentionTypes.Users | AllowedMentionTypes.Everyone
                } :
                AllowedMentions.None;

                MessageReference messageReference = null;
                if (!string.IsNullOrEmpty(notification.MessageId) && ulong.TryParse(notification.MessageId, out var messageId))
                {
                    messageReference = new MessageReference(messageId, failIfNotExists: false);
                }

                if (notification.Embed != null && settings.HasFlag(notification.Type))
                {
                    // TODO: discord handles twitter embeds nicely; should adjust the notification data accordingly so we don't need this explicit check here
                    if (notification.Type == NotificationType.Twitter)
                    {
                        var messageText = $"{notification.Embed.Title} {notification.Embed.Url} {customText}{extraText}".Trim();
                        await channelToUse.SendMessageAsync(messageText, allowedMentions : allowedMentions, messageReference : messageReference);
                    }
                    else
                    {
                        var messageText = string.IsNullOrEmpty(notification.Embed.Url) ? string.Empty : $"<{notification.Embed.Url}>";
                        messageText += $" {customText}{extraText}".TrimEnd();
                        await channelToUse.SendMessageAsync(messageText, false, notification.Embed.CreateEmbedBuilder().Build(), allowedMentions : allowedMentions, messageReference : messageReference);
                    }
                }
                else
                {
                    var messageText = $"{notification.Text} {customText}{extraText}".TrimEnd();
                    var sentMesage  = await channelToUse.SendMessageAsync(messageText.SubstringUpTo(Discord.DiscordConfig.MaxMessageSize), allowedMentions : allowedMentions, messageReference : messageReference);

                    // update feedback messages to include the message ID
                    if (notification.Type == NotificationType.Feedback && notification.SubType != SubType.Reply)
                    {
                        await sentMesage.ModifyAsync(m => m.Content = $"{sentMesage.Content} (mid: {sentMesage.Id})");
                    }
                }

                var props = new Dictionary <string, string> {
                    { "server", notification.Server },
                    { "channel", notification.Channel },
                    { "notificationType", notification.Type.ToString() },
                };
                this.TrackEvent("notificationSent", props);
            }
            catch (Exception ex)
            {
                // TODO:
                // Add retry support.
                string extraData = null;
                if (notification.Embed != null)
                {
                    extraData = JsonConvert.SerializeObject(notification.Embed);
                }
                Log.Error(ex, $"Failed to send notification {extraData}");
            }

            return(true);
        }
Exemple #9
0
        /// <inheritdoc />
        protected override async Task <bool> SendNotification(NotificationData notification)
        {
            // if the guild wasn't found, it belongs to another shard.
            if (!(this.Client.GetGuild(Convert.ToUInt64(notification.Server)) is IGuild guild))
            {
                return(false);
            }

            var channelToUse = this.Client.GetChannel(Convert.ToUInt64(notification.Channel)) as ITextChannel;

            // if the channel doesn't exist or we don't have send permissions, try to get the default channel instead.
            if (!channelToUse?.GetCurrentUserPermissions().SendMessages ?? true)
            {
                channelToUse = await guild.GetDefaultChannelAsync() as ITextChannel;

                // if the default channel couldn't be found or we don't have permissions, then we're SOL.
                // flag as processed.
                if (channelToUse == null || !channelToUse.GetCurrentUserPermissions().SendMessages)
                {
                    return(true);
                }
            }

            var settings = SettingsConfig.GetSettings(notification.Server);

            // adjust the notification text to disable discord link parsing, if configured to do so
            if (settings.DisableLinkParsing && notification.Type != NotificationType.Reminder)
            {
                notification.Text = Consts.UrlRegex.Replace(notification.Text, new MatchEvaluator((Match urlMatch) =>
                {
                    return($"<{urlMatch.Captures[0]}>");
                }));
            }

            try
            {
                if (notification.Embed != null && settings.HasFlag(notification.Type))
                {
                    // TODO: discord handles twitter embeds nicely; should adjust the notification data accordingly so we don't need this explicit check here
                    if (notification.Type == NotificationType.Twitter)
                    {
                        await channelToUse.SendMessageAsync(notification.Embed.Url);
                    }
                    else
                    {
                        var customText  = settings.NotificationText.FirstOrDefault(n => n.Type == notification.Type)?.Text;
                        var messageText = string.IsNullOrEmpty(notification.Embed.Url) ? string.Empty : $"<{notification.Embed.Url}>";
                        if (!string.IsNullOrEmpty(customText))
                        {
                            messageText += $" {customText}";
                        }

                        await channelToUse.SendMessageAsync(messageText, false, notification.Embed.CreateEmbedBuilder().Build());
                    }
                }
                else
                {
                    await channelToUse.SendMessageAsync(notification.Text.SubstringUpTo(Discord.DiscordConfig.MaxMessageSize));
                }

                var props = new Dictionary <string, string> {
                    { "server", notification.Server },
                    { "channel", notification.Channel },
                    { "notificationType", notification.Type.ToString() },
                };
                this.TrackEvent("notificationSent", props);
            }
            catch (Exception ex)
            {
                // TODO:
                // Add retry support.
                string extraData = null;
                if (notification.Embed != null)
                {
                    extraData = JsonConvert.SerializeObject(notification.Embed);
                }
                Log.Error(ex, $"Failed to send notification {extraData}");
            }

            return(true);
        }