Additions to BackgroundWorker

-Added Discord Bots reporting
-Birthdays now properly processed asynchronously
-Added an announcement count
This commit is contained in:
Noikoio 2018-10-17 23:58:19 -07:00
parent 4fb2ac3dee
commit 9f6ab5867a
3 changed files with 99 additions and 23 deletions

View file

@ -1,4 +1,5 @@
Imports System.Text Imports System.Net
Imports System.Text
Imports System.Threading Imports System.Threading
Imports Discord.WebSocket Imports Discord.WebSocket
Imports NodaTime Imports NodaTime
@ -11,8 +12,7 @@ Class BackgroundWorker
Private ReadOnly _db As Database Private ReadOnly _db As Database
Private ReadOnly Property WorkerCancel As New CancellationTokenSource Private ReadOnly Property WorkerCancel As New CancellationTokenSource
Private _workerTask As Task Private _workerTask As Task
' NOTE: Interval greatly lowered. Raise to 45 seconds if server count goes up. Const Interval = 45 ' How often the worker wakes up, in seconds. Adjust as needed.
Const Interval = 15 ' How often the worker wakes up, in seconds
Private _clock As IClock Private _clock As IClock
Sub New(instance As BirthdayBot, dbsettings As Database) Sub New(instance As BirthdayBot, dbsettings As Database)
@ -31,19 +31,21 @@ Class BackgroundWorker
Await _workerTask Await _workerTask
End Function End Function
''' <summary>
''' Background task. Kicks off many other tasks.
''' </summary>
Private Async Function WorkerLoop() As Task Private Async Function WorkerLoop() As Task
Try Try
While Not WorkerCancel.IsCancellationRequested While Not WorkerCancel.IsCancellationRequested
' Start background tasks.
Dim bgTasks As New List(Of Task) From {
ReportServerCount(),
BirthdayAsync()
}
Await Task.WhenAll(bgTasks)
' All done. Wait until we have to work again.
Await Task.Delay(Interval * 1000, WorkerCancel.Token) Await Task.Delay(Interval * 1000, WorkerCancel.Token)
WorkerCancel.Token.ThrowIfCancellationRequested()
Try
For Each guild In _bot.DiscordClient.Guilds
Dim b = BirthdayWorkAsync(guild)
Await b
Next
Catch ex As Exception
Log("Error", ex.ToString())
End Try
End While End While
Catch ex As TaskCanceledException Catch ex As TaskCanceledException
Return Return
@ -52,16 +54,50 @@ Class BackgroundWorker
#Region "Birthday handling" #Region "Birthday handling"
''' <summary> ''' <summary>
''' All birthday checking happens here. ''' Birthday tasks processing. Sets up a task per guild and waits on them.
''' </summary> ''' </summary>
Private Async Function BirthdayWorkAsync(guild As SocketGuild) As Task Private Async Function BirthdayAsync() As Task
Dim tasks As New List(Of Task(Of Integer))
For Each guild In _bot.DiscordClient.Guilds
Dim t = BirthdayGuildProcessAsync(guild)
tasks.Add(t)
Next
Try
Await Task.WhenAll(tasks)
Catch ex As Exception
Dim exs = From task In tasks
Where task.Exception IsNot Nothing
Select task.Exception
Log("Error", "Encountered one or more unhandled exceptions during birthday processing.")
For Each iex In exs
Log("Error", iex.ToString())
Next
End Try
' Usage report: Show how many announcements were done
Dim announces = 0
Dim guilds = 0
For Each task In tasks
If task.Result > 0 Then
announces += task.Result
guilds += 1
End If
Next
If announces > 0 Then Log("Background", $"Announcing {announces} birthday(s) in {guilds} guild(s).")
End Function
''' <summary>
''' Birthday processing for an individual guild.
''' </summary>
''' <returns>Number of birthdays announced.</returns>
Private Async Function BirthdayGuildProcessAsync(guild As SocketGuild) As Task(Of Integer)
' Gather required information ' Gather required information
Dim tz As String Dim tz As String
Dim users As IEnumerable(Of GuildUserSettings) Dim users As IEnumerable(Of GuildUserSettings)
Dim role As SocketRole = Nothing Dim role As SocketRole = Nothing
Dim channel As SocketTextChannel = Nothing Dim channel As SocketTextChannel = Nothing
SyncLock _bot.KnownGuilds SyncLock _bot.KnownGuilds
If Not _bot.KnownGuilds.ContainsKey(guild.Id) Then Return If Not _bot.KnownGuilds.ContainsKey(guild.Id) Then Return 0
Dim gs = _bot.KnownGuilds(guild.Id) Dim gs = _bot.KnownGuilds(guild.Id)
tz = gs.TimeZone tz = gs.TimeZone
users = gs.Users users = gs.Users
@ -70,7 +106,7 @@ Class BackgroundWorker
If gs.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value) If gs.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
If role Is Nothing Then If role Is Nothing Then
gs.RoleWarning = True gs.RoleWarning = True
Return Return 0
End If End If
End SyncLock End SyncLock
@ -80,10 +116,12 @@ Class BackgroundWorker
' Set birthday role, get list of users now having birthdays ' Set birthday role, get list of users now having birthdays
Dim announceNames = Await BirthdayApplyAsync(guild, role, birthdays) Dim announceNames = Await BirthdayApplyAsync(guild, role, birthdays)
If announceNames.Count = 0 Then Return If announceNames.Count <> 0 Then
' Send out announcement message
Await BirthdayAnnounceAsync(guild, channel, announceNames)
End If
' Send out announcement message Return announceNames.Count
Await BirthdayAnnounceAsync(guild, channel, announceNames)
End Function End Function
''' <summary> ''' <summary>
@ -114,7 +152,7 @@ Class BackgroundWorker
Dim checkNow = _clock.GetCurrentInstant().InZone(tz) Dim checkNow = _clock.GetCurrentInstant().InZone(tz)
' Special case: If birthday is February 29 and it's not a leap year, recognize it on March 1st ' Special case: If birthday is February 29 and it's not a leap year, recognize it on March 1st
If targetMonth = 2 And targetDay = 29 And Not DateTime.IsLeapYear(checkNow.Year) Then If targetMonth = 2 And targetDay = 29 And Not Date.IsLeapYear(checkNow.Year) Then
targetMonth = 3 targetMonth = 3
targetDay = 1 targetDay = 1
End If End If
@ -181,9 +219,9 @@ Class BackgroundWorker
Else Else
name = item.Username name = item.Username
End If End If
namedisplay.Append(name) namedisplay.Append("**" + name + "**")
Next Next
result = $"Please wish our members a happy birthday!{vbLf}In no particular order: {namedisplay.ToString()}" result = $"Please wish our esteemed members a happy birthday!{vbLf}In no particular order: {namedisplay.ToString()}"
End If End If
Try Try
@ -193,4 +231,34 @@ Class BackgroundWorker
End Try End Try
End Function End Function
#End Region #End Region
#Region "Activity reporting"
Private _activityCount As Integer = 0
Private Async Function ReportServerCount() As Task
' Once roughly every 6 hours, with an initial ~2 minute delay. Be mindful of the interval value.
_activityCount += 1
If _activityCount Mod 400 <> 2 Then Return
Dim count = _bot.DiscordClient.Guilds.Count
Log("Activity Report", $"Currently in {count} guild(s).")
Dim dtok = _bot._cfg.DBotsToken
If dtok IsNot Nothing Then
Const dUrl As String = "https://bots.discord.pw/api/bots/{0}/stats"
Using client As New WebClient()
Dim uri = New Uri(String.Format(dUrl, CType(_bot.DiscordClient.CurrentUser.Id, String)))
Dim data = "{ ""server_count"": " + CType(count, String) + " }"
client.Headers(HttpRequestHeader.Authorization) = dtok
client.Headers(HttpRequestHeader.ContentType) = "application/json"
Try
Await client.UploadStringTaskAsync(uri, data)
Log("Activity Report", "Count sent to Discord Bots.")
Catch ex As WebException
Log("Activity Report", "Encountered error on sending to Discord Bots: " + ex.Message)
End Try
End Using
End If
End Function
#End Region
End Class End Class

View file

@ -14,7 +14,7 @@ Class BirthdayBot
Private ReadOnly _cmdsMods As ManagerCommands Private ReadOnly _cmdsMods As ManagerCommands
Private WithEvents _client As DiscordSocketClient Private WithEvents _client As DiscordSocketClient
Private _cfg As Configuration Friend _cfg As Configuration
Private ReadOnly _worker As BackgroundWorker Private ReadOnly _worker As BackgroundWorker
Friend ReadOnly Property DiscordClient As DiscordSocketClient Friend ReadOnly Property DiscordClient As DiscordSocketClient

View file

@ -7,6 +7,7 @@ Imports System.IO
''' </summary> ''' </summary>
Class Configuration Class Configuration
Public ReadOnly Property BotToken As String Public ReadOnly Property BotToken As String
Public ReadOnly Property DBotsToken As String
Public ReadOnly Property DatabaseSettings As Database Public ReadOnly Property DatabaseSettings As Database
Sub New() Sub New()
@ -25,6 +26,13 @@ Class Configuration
Throw New Exception("'BotToken' must be specified.") Throw New Exception("'BotToken' must be specified.")
End If End If
Dim dbj = jc("DBotsToken")
If dbj IsNot Nothing Then
DBotsToken = dbj.Value(Of String)()
Else
DBotsToken = Nothing
End If
Dim sqlcs = jc("SqlConnectionString").Value(Of String)() Dim sqlcs = jc("SqlConnectionString").Value(Of String)()
If String.IsNullOrWhiteSpace(sqlcs) Then If String.IsNullOrWhiteSpace(sqlcs) Then
Throw New Exception("'SqlConnectionString' must be specified.") Throw New Exception("'SqlConnectionString' must be specified.")