Renamed class; added user-friendly feedback on errors

-Renamed GuildSettings to GuildStateInformation as it is more accurate
-Completely changed the behavior of the role error warning to provide a
more useful help message to the user based on the error that was
detected
This commit is contained in:
Noikoio 2019-08-29 23:31:40 -07:00
parent f3e70f8459
commit 5ad193c059
5 changed files with 71 additions and 52 deletions

View file

@ -69,10 +69,11 @@ Class BirthdayRoleUpdate
If gs.AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value) If gs.AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value)
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.RoleWarningNonexist = True
Return 0 Return 0
Else
gs.RoleWarningNonexist = False
End If End If
gs.RoleWarning = False
End SyncLock End SyncLock
' Determine who's currently having a birthday ' Determine who's currently having a birthday
@ -81,28 +82,29 @@ Class BirthdayRoleUpdate
' Set birthday roles, get list of users that had the role added ' Set birthday roles, get list of users that had the role added
' But first check if we are able to do so. Letting all requests fail instead will lead to rate limiting. ' But first check if we are able to do so. Letting all requests fail instead will lead to rate limiting.
Dim announceNames As IEnumerable(Of SocketGuildUser) Dim correctRolePermissions = HasCorrectRolePermissions(guild, role)
If HasCorrectRolePermissions(guild, role) Then Dim gotForbidden = False
Dim announceNames As IEnumerable(Of SocketGuildUser) = Nothing
If correctRolePermissions Then
Try Try
announceNames = Await UpdateGuildBirthdayRoles(guild, role, birthdays) announceNames = Await UpdateGuildBirthdayRoles(guild, role, birthdays)
Catch ex As Discord.Net.HttpException Catch ex As Discord.Net.HttpException
If ex.HttpCode = HttpStatusCode.Forbidden Then If ex.HttpCode = HttpStatusCode.Forbidden Then
announceNames = Nothing gotForbidden = True
Else Else
Throw Throw
End If End If
End Try End Try
Else
announceNames = Nothing
End If End If
If announceNames Is Nothing Then ' Update warning flag
Dim updateError = Not correctRolePermissions Or gotForbidden
SyncLock BotInstance.KnownGuilds SyncLock BotInstance.KnownGuilds
' Nothing on announceNAmes signals failure to apply roles. Set the warning message. BotInstance.KnownGuilds(guild.Id).RoleWarningPermission = updateError
BotInstance.KnownGuilds(guild.Id).RoleWarning = True
End SyncLock End SyncLock
Return 0 ' Quit now if the warning flag was set. Announcement data is not available.
End If If updateError Then Return 0
If announceNames.Count <> 0 Then If announceNames.Count <> 0 Then
' Send out announcement message ' Send out announcement message

View file

@ -26,12 +26,12 @@ Class BirthdayBot
End Property End Property
''' <summary>SyncLock when using. The lock object is itself.</summary> ''' <summary>SyncLock when using. The lock object is itself.</summary>
Friend ReadOnly Property KnownGuilds As Dictionary(Of ULong, GuildSettings) Friend ReadOnly Property KnownGuilds As Dictionary(Of ULong, GuildStateInformation)
Public Sub New(conf As Configuration, dc As DiscordSocketClient) Public Sub New(conf As Configuration, dc As DiscordSocketClient)
Config = conf Config = conf
_client = dc _client = dc
KnownGuilds = New Dictionary(Of ULong, GuildSettings) KnownGuilds = New Dictionary(Of ULong, GuildStateInformation)
_worker = New BackgroundServiceRunner(Me) _worker = New BackgroundServiceRunner(Me)
@ -75,7 +75,7 @@ Class BirthdayBot
Private Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable Private Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable
SyncLock KnownGuilds SyncLock KnownGuilds
If Not KnownGuilds.ContainsKey(g.Id) Then If Not KnownGuilds.ContainsKey(g.Id) Then
Dim gi = GuildSettings.LoadSettingsAsync(Config.DatabaseSettings, g.Id).GetAwaiter().GetResult() Dim gi = GuildStateInformation.LoadSettingsAsync(Config.DatabaseSettings, g.Id).GetAwaiter().GetResult()
KnownGuilds.Add(g.Id, gi) KnownGuilds.Add(g.Id, gi)
End If End If
End SyncLock End SyncLock
@ -114,7 +114,7 @@ Class BirthdayBot
End If End If
' Ban and role warning check ' Ban and role warning check
Dim roleWarning As Boolean Dim roleWarningText As String
SyncLock KnownGuilds SyncLock KnownGuilds
Dim gi = KnownGuilds(channel.Guild.Id) Dim gi = KnownGuilds(channel.Guild.Id)
@ -125,13 +125,13 @@ Class BirthdayBot
End If End If
End If End If
roleWarning = gi.RoleWarning roleWarningText = gi.IssueRoleWarning
End SyncLock End SyncLock
Try Try
If roleWarning Then If roleWarningText IsNot Nothing Then
Try Try
Await channel.SendMessageAsync(RoleWarningMsg) Await channel.SendMessageAsync(roleWarningText)
Catch ex As HttpException Catch ex As HttpException
' Don't let this prevent the bot from continuing command execution. ' Don't let this prevent the bot from continuing command execution.
End Try End Try

View file

@ -26,7 +26,7 @@ Class Database
Private Sub SetupTables() Private Sub SetupTables()
Using db = OpenConnectionAsync().GetAwaiter().GetResult() Using db = OpenConnectionAsync().GetAwaiter().GetResult()
GuildSettings.SetUpDatabaseTable(db) ' Note: Call this first. (Foreign reference constraints.) GuildStateInformation.SetUpDatabaseTable(db) ' Note: Call this first. (Foreign reference constraints.)
GuildUserSettings.SetUpDatabaseTable(db) GuildUserSettings.SetUpDatabaseTable(db)
End Using End Using
End Sub End Sub

View file

@ -4,11 +4,10 @@ Imports Npgsql
Imports NpgsqlTypes Imports NpgsqlTypes
''' <summary> ''' <summary>
''' Collection of GuildUserSettings instances. Holds cached information on guild users and overall ''' Holds various pieces of state information for a guild the bot is operating in.
''' guild options, and provides some database abstractions regarding them all. ''' Includes, among other things, a copy of the guild's settings and a list of all known users with birthdays.
''' Object instances are loaded when entering a guild and discarded when the bot leaves the guild.
''' </summary> ''' </summary>
Friend Class GuildSettings Friend Class GuildStateInformation
Public ReadOnly Property GuildId As ULong Public ReadOnly Property GuildId As ULong
Private ReadOnly _db As Database Private ReadOnly _db As Database
Private _bdayRole As ULong? Private _bdayRole As ULong?
@ -19,33 +18,55 @@ Friend Class GuildSettings
Private _announceMsg As String Private _announceMsg As String
Private _announceMsgPl As String Private _announceMsgPl As String
Private _announcePing As Boolean Private _announcePing As Boolean
Private _userCache As Dictionary(Of ULong, GuildUserSettings) Private ReadOnly _userCache As Dictionary(Of ULong, GuildUserSettings)
Private _roleWarning As Boolean
Private _roleLastWarning As New DateTimeOffset(DateTime.MinValue, TimeSpan.Zero) Private _roleLastWarning As New DateTimeOffset(DateTime.MinValue, TimeSpan.Zero)
Private Shared ReadOnly RoleWarningInterval As New TimeSpan(1, 0, 0) Private Shared ReadOnly RoleWarningInterval As New TimeSpan(1, 0, 0)
''' <summary> ''' <summary>
''' Flag for notifying servers that the bot is unable to manipulate its role. ''' Message for notifying servers that the bot is unable to manipulate its designated role for various reasons.
''' Can be set at any time. Reading this will only return True if it's been set as such, ''' The returned value is dependent on certain warning flags accessible in this class. To avoid bombarding users
''' and it is only returned after a set time has passed in order to not constantly show the message. ''' with the same message, this property only returns a non-Nothing value at most once per hour.
''' Otherwise, it shall always return Nothing if there is no warning to be issued.
''' </summary> ''' </summary>
Public Property RoleWarning As Boolean Public ReadOnly Property IssueRoleWarning As String
Get Get
If _roleWarning = True Then
' Only report a warning every so often.
If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then
_roleLastWarning = DateTimeOffset.UtcNow _roleLastWarning = DateTimeOffset.UtcNow
Return True
Else Else
Return False Return Nothing
End If End If
If RoleWarningUnset Or RoleWarningNonexist Then
Return "Warning: A birthday role must be configured before this bot can function properly. " +
"Update the designated role with `bb.config role (role name/ID)`."
End If End If
Return False If RoleWarningPermission Then
Return "Warning: This bot is unable to set the birthday role onto users. " +
"Make sure that this bot has the Manage Roles permission and is not placed below the birthday role."
End If
Return Nothing
End Get
End Property
''' <summary>
''' Role warning message: The birthday role cannot be accessed.
''' </summary>
Friend Property RoleWarningPermission As Boolean = False
''' <summary>
''' Role warning message: The birthday role no longer exists.
''' </summary>
Friend Property RoleWarningNonexist As Boolean = False
''' <summary>
''' Role warning message: The birthday role is not set.
''' </summary>
Friend ReadOnly Property RoleWarningUnset As Boolean
Get
Return _bdayRole Is Nothing
End Get End Get
Set(value As Boolean)
_roleWarning = value
End Set
End Property End Property
''' <summary> ''' <summary>
@ -132,9 +153,6 @@ Friend Class GuildSettings
' Weird: if using a ternary operator with a ULong?, Nothing resolves to 0 despite Option Strict On. ' Weird: if using a ternary operator with a ULong?, Nothing resolves to 0 despite Option Strict On.
If Not reader.IsDBNull(1) Then If Not reader.IsDBNull(1) Then
_bdayRole = CULng(reader.GetInt64(1)) _bdayRole = CULng(reader.GetInt64(1))
RoleWarning = False
Else
RoleWarning = True
End If End If
If Not reader.IsDBNull(2) Then _announceCh = CULng(reader.GetInt64(2)) If Not reader.IsDBNull(2) Then _announceCh = CULng(reader.GetInt64(2))
_tz = If(reader.IsDBNull(3), Nothing, reader.GetString(3)) _tz = If(reader.IsDBNull(3), Nothing, reader.GetString(3))
@ -253,7 +271,6 @@ Friend Class GuildSettings
Public Async Function UpdateRoleAsync(roleId As ULong) As Task Public Async Function UpdateRoleAsync(roleId As ULong) As Task
_bdayRole = roleId _bdayRole = roleId
_roleWarning = False
_roleLastWarning = New DateTimeOffset _roleLastWarning = New DateTimeOffset
Await UpdateDatabaseAsync() Await UpdateDatabaseAsync()
End Function End Function
@ -326,7 +343,7 @@ Friend Class GuildSettings
''' Retrieves an object instance representative of guild settings for the specified guild. ''' Retrieves an object instance representative of guild settings for the specified guild.
''' If settings for the given guild do not yet exist, a new value is created. ''' If settings for the given guild do not yet exist, a new value is created.
''' </summary> ''' </summary>
Friend Shared Async Function LoadSettingsAsync(dbsettings As Database, guild As ULong) As Task(Of GuildSettings) Friend Shared Async Function LoadSettingsAsync(dbsettings As Database, guild As ULong) As Task(Of GuildStateInformation)
Using db = Await dbsettings.OpenConnectionAsync() Using db = Await dbsettings.OpenConnectionAsync()
Using c = db.CreateCommand() Using c = db.CreateCommand()
' Take note of ordinals for use in the constructor ' Take note of ordinals for use in the constructor
@ -336,7 +353,7 @@ Friend Class GuildSettings
c.Prepare() c.Prepare()
Using r = Await c.ExecuteReaderAsync() Using r = Await c.ExecuteReaderAsync()
If Await r.ReadAsync() Then If Await r.ReadAsync() Then
Return New GuildSettings(r, dbsettings) Return New GuildStateInformation(r, dbsettings)
End If End If
End Using End Using
End Using End Using

View file

@ -4,7 +4,7 @@ Imports NpgsqlTypes
''' <summary> ''' <summary>
''' Representation of a user's birthday settings within a guild. ''' Representation of a user's birthday settings within a guild.
''' Instances are held and managed by <see cref="GuildSettings"/>. ''' Instances are held and managed by <see cref="GuildStateInformation"/>.
''' </summary> ''' </summary>
Class GuildUserSettings Class GuildUserSettings
Private _month As Integer Private _month As Integer
@ -125,7 +125,7 @@ Class GuildUserSettings
Friend Shared Sub SetUpDatabaseTable(db As NpgsqlConnection) Friend Shared Sub SetUpDatabaseTable(db As NpgsqlConnection)
Using c = db.CreateCommand() Using c = db.CreateCommand()
c.CommandText = $"create table if not exists {BackingTable} (" + c.CommandText = $"create table if not exists {BackingTable} (" +
$"guild_id bigint not null references {GuildSettings.BackingTable}, " + $"guild_id bigint not null references {GuildStateInformation.BackingTable}, " +
"user_id bigint not null, " + "user_id bigint not null, " +
"birth_month integer not null, " + "birth_month integer not null, " +
"birth_day integer not null, " + "birth_day integer not null, " +