diff --git a/BirthdayBot/BackgroundWorker.vb b/BirthdayBot/BackgroundWorker.vb
index 542f360..d648e93 100644
--- a/BirthdayBot/BackgroundWorker.vb
+++ b/BirthdayBot/BackgroundWorker.vb
@@ -1,4 +1,5 @@
-Imports System.Text
+Imports System.Net
+Imports System.Text
Imports System.Threading
Imports Discord.WebSocket
Imports NodaTime
@@ -11,8 +12,7 @@ Class BackgroundWorker
Private ReadOnly _db As Database
Private ReadOnly Property WorkerCancel As New CancellationTokenSource
Private _workerTask As Task
- ' NOTE: Interval greatly lowered. Raise to 45 seconds if server count goes up.
- Const Interval = 15 ' How often the worker wakes up, in seconds
+ Const Interval = 45 ' How often the worker wakes up, in seconds. Adjust as needed.
Private _clock As IClock
Sub New(instance As BirthdayBot, dbsettings As Database)
@@ -31,19 +31,21 @@ Class BackgroundWorker
Await _workerTask
End Function
+ '''
+ ''' Background task. Kicks off many other tasks.
+ '''
Private Async Function WorkerLoop() As Task
Try
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)
- 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
Catch ex As TaskCanceledException
Return
@@ -52,16 +54,50 @@ Class BackgroundWorker
#Region "Birthday handling"
'''
- ''' All birthday checking happens here.
+ ''' Birthday tasks processing. Sets up a task per guild and waits on them.
'''
- 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
+
+ '''
+ ''' Birthday processing for an individual guild.
+ '''
+ ''' Number of birthdays announced.
+ Private Async Function BirthdayGuildProcessAsync(guild As SocketGuild) As Task(Of Integer)
' Gather required information
Dim tz As String
Dim users As IEnumerable(Of GuildUserSettings)
Dim role As SocketRole = Nothing
Dim channel As SocketTextChannel = Nothing
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)
tz = gs.TimeZone
users = gs.Users
@@ -70,7 +106,7 @@ Class BackgroundWorker
If gs.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
If role Is Nothing Then
gs.RoleWarning = True
- Return
+ Return 0
End If
End SyncLock
@@ -80,10 +116,12 @@ Class BackgroundWorker
' Set birthday role, get list of users now having 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
- Await BirthdayAnnounceAsync(guild, channel, announceNames)
+ Return announceNames.Count
End Function
'''
@@ -114,7 +152,7 @@ Class BackgroundWorker
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
- 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
targetDay = 1
End If
@@ -181,9 +219,9 @@ Class BackgroundWorker
Else
name = item.Username
End If
- namedisplay.Append(name)
+ namedisplay.Append("**" + name + "**")
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
Try
@@ -193,4 +231,34 @@ Class BackgroundWorker
End Try
End Function
#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
diff --git a/BirthdayBot/BirthdayBot.vb b/BirthdayBot/BirthdayBot.vb
index 34f6ad2..4d1bc3d 100644
--- a/BirthdayBot/BirthdayBot.vb
+++ b/BirthdayBot/BirthdayBot.vb
@@ -14,7 +14,7 @@ Class BirthdayBot
Private ReadOnly _cmdsMods As ManagerCommands
Private WithEvents _client As DiscordSocketClient
- Private _cfg As Configuration
+ Friend _cfg As Configuration
Private ReadOnly _worker As BackgroundWorker
Friend ReadOnly Property DiscordClient As DiscordSocketClient
diff --git a/BirthdayBot/Configuration.vb b/BirthdayBot/Configuration.vb
index 7e4fb98..d41a01c 100644
--- a/BirthdayBot/Configuration.vb
+++ b/BirthdayBot/Configuration.vb
@@ -7,6 +7,7 @@ Imports System.IO
'''
Class Configuration
Public ReadOnly Property BotToken As String
+ Public ReadOnly Property DBotsToken As String
Public ReadOnly Property DatabaseSettings As Database
Sub New()
@@ -25,6 +26,13 @@ Class Configuration
Throw New Exception("'BotToken' must be specified.")
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)()
If String.IsNullOrWhiteSpace(sqlcs) Then
Throw New Exception("'SqlConnectionString' must be specified.")