Change frequency of automatic birthday role checks

Changed up how the background services behave. Removed tick count,
greatly increased the interval, and added a manual call to update
individual guilds as their information changes.
Further changes to this may be made in the near future, going as far as
to possibly remvoe this structure altogether.
This commit is contained in:
Noikoio 2019-12-09 19:43:43 -08:00
parent 8ca06279e9
commit b31813c210
7 changed files with 38 additions and 38 deletions

View file

@ -4,24 +4,24 @@
''' Handles the execution of periodic background tasks.
''' </summary>
Class BackgroundServiceRunner
Const Interval = 45 ' Tick interval in seconds. Adjust as needed.
Const Interval = 8 * 60 ' Tick interval in seconds. Adjust as needed.
Private ReadOnly Property Workers As List(Of BackgroundService)
Private ReadOnly Property WorkerCancel As New CancellationTokenSource
Friend ReadOnly Property BirthdayUpdater As BirthdayRoleUpdate
Private _workerTask As Task
Private _tickCount As Integer
Sub New(instance As BirthdayBot)
BirthdayUpdater = New BirthdayRoleUpdate(instance)
Workers = New List(Of BackgroundService) From {
{New GuildStatistics(instance)},
{New Heartbeat(instance)},
{New BirthdayRoleUpdate(instance)}
{BirthdayUpdater}
}
End Sub
Public Sub Start()
_tickCount = 0
_workerTask = Task.Factory.StartNew(AddressOf WorkerLoop, WorkerCancel.Token,
TaskCreationOptions.LongRunning, TaskScheduler.Default)
End Sub
@ -43,7 +43,7 @@ Class BackgroundServiceRunner
' Execute background tasks.
Dim tasks As New List(Of Task)
For Each service In Workers
tasks.Add(service.OnTick(_tickCount))
tasks.Add(service.OnTick())
Next
Await Task.WhenAll(tasks)
Catch ex As TaskCanceledException
@ -52,7 +52,6 @@ Class BackgroundServiceRunner
Log("Background task", "Unhandled exception in background task thread:")
Log("Background task", ex.ToString())
End Try
_tickCount += 1
End While
End Function
End Class

View file

@ -13,5 +13,5 @@ MustInherit Class BackgroundService
Program.Log(Me.GetType().Name, message)
End Sub
MustOverride Function OnTick(tick As Integer) As Task
MustOverride Function OnTick() As Task
End Class

View file

@ -4,22 +4,20 @@ Imports Discord.WebSocket
Imports NodaTime
''' <summary>
''' Periodically scans all known guilds and adjusts birthday role membership as necessary.
''' Also handles birthday announcements.
''' Core automatic functionality of the bot. Manages role memberships based on birthday information,
''' and optionally sends the announcement message to appropriate guilds.
''' </summary>
Class BirthdayRoleUpdate
Inherits BackgroundService
Private ReadOnly Property Clock As IClock
Public Sub New(instance As BirthdayBot)
MyBase.New(instance)
Clock = SystemClock.Instance ' can be replaced with FakeClock during testing
End Sub
''' <summary>
''' Initial processing: Sets up a task per guild and waits on all.
''' Does processing on all available guilds at once.
''' </summary>
Public Overrides Async Function OnTick(tick As Integer) As Task
Public Overrides Async Function OnTick() As Task
Dim tasks As New List(Of Task(Of Integer))
For Each guild In BotInstance.DiscordClient.Guilds
Dim t = ProcessGuildAsync(guild)
@ -38,19 +36,24 @@ Class BirthdayRoleUpdate
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($"Announcing {announces} birthday(s) in {guilds} guild(s).")
' TODO metrics for role sets, unsets, announcements - and how to do that for singles too?
End Function
Async Function ProcessGuildAsync(guild As SocketGuild) As Task(Of Integer)
''' <summary>
''' Does role and announcement processing for a single specified guild.
''' </summary>
Public Async Function SingleUpdateFor(guild As SocketGuild) As Task
Try
Await ProcessGuildAsync(guild)
Catch ex As Exception
Log("Encountered an error during guild processing:")
Log(ex.ToString())
End Try
' TODO metrics for role sets, unsets, announcements - and I mentioned this above too
End Function
Private Async Function ProcessGuildAsync(guild As SocketGuild) As Task(Of Integer)
' Gather required information
Dim tz As String
Dim users As IEnumerable(Of GuildUserSettings)
@ -156,7 +159,7 @@ Class BirthdayRoleUpdate
Dim targetMonth = item.BirthMonth
Dim targetDay = item.BirthDay
Dim checkNow = Clock.GetCurrentInstant().InZone(tz)
Dim checkNow = SystemClock.Instance.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 Date.IsLeapYear(checkNow.Year) Then
targetMonth = 3

View file

@ -10,10 +10,7 @@ Class GuildStatistics
DBotsToken = instance.Config.DBotsToken
End Sub
Public Overrides Async Function OnTick(tick As Integer) As Task
' Activate roughly every 2 hours (interval: 45)
If tick Mod 160 <> 2 Then Return
Public Overrides Async Function OnTick() As Task
Dim count = BotInstance.DiscordClient.Guilds.Count
Log($"Currently in {count} guild(s).")

View file

@ -1,5 +1,5 @@
''' <summary>
''' Basic heartbeat function - indicates that the background task is still functioning.
''' Basic heartbeat function - hints that the background task is still alive.
''' </summary>
Class Heartbeat
Inherits BackgroundService
@ -8,19 +8,16 @@ Class Heartbeat
MyBase.New(instance)
End Sub
Public Overrides Function OnTick(tick As Integer) As Task
' Print a message roughly every 15 minutes (assuming 45s per tick).
If tick Mod 20 = 0 Then
Dim uptime = DateTimeOffset.UtcNow - Program.BotStartTime
Log($"Tick {tick:00000} - Bot uptime: {BotUptime()}")
End If
Public Overrides Function OnTick() As Task
Dim uptime = DateTimeOffset.UtcNow - Program.BotStartTime
Log($"Bot uptime: {BotUptime()}")
' Disconnection warn
For Each shard In BotInstance.DiscordClient.Shards
If shard.ConnectionState = Discord.ConnectionState.Disconnected Then
Log($"Shard {shard.ShardId} is disconnected! Restart the app if this persists.")
' The library alone cannot be restarted as it is in an unknown state. It was not designed to be restarted.
' This is the part where we'd signal something to restart us if we were fancy.
' TODO This is the part where we'd signal something to restart us if we were fancy.
End If
Next

View file

@ -118,6 +118,7 @@ Class BirthdayBot
End If
End If
' Execute the command
Try
Log("Command", $"{channel.Guild.Name}/{author.Username}#{author.Discriminator}: {msg.Content}")
Await command(csplit, channel, author)
@ -130,6 +131,9 @@ Class BirthdayBot
' Fail silently.
End Try
End Try
' Immediately check for role updates in the invoking guild
Await _worker.BirthdayUpdater.SingleUpdateFor(channel.Guild)
End If
End If
End Function

View file

@ -4,7 +4,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>BirthdayBot</RootNamespace>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.3.3</Version>
<Version>1.3.4</Version>
<Authors>Noi</Authors>
<Company />
<Description>Discord bot for birthday reminders.</Description>