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.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
If role Is Nothing Then
gs.RoleWarning = True
gs.RoleWarningNonexist = True
Return 0
Else
gs.RoleWarningNonexist = False
End If
gs.RoleWarning = False
End SyncLock
' 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
' 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)
If HasCorrectRolePermissions(guild, role) Then
Dim correctRolePermissions = HasCorrectRolePermissions(guild, role)
Dim gotForbidden = False
Dim announceNames As IEnumerable(Of SocketGuildUser) = Nothing
If correctRolePermissions Then
Try
announceNames = Await UpdateGuildBirthdayRoles(guild, role, birthdays)
Catch ex As Discord.Net.HttpException
If ex.HttpCode = HttpStatusCode.Forbidden Then
announceNames = Nothing
gotForbidden = True
Else
Throw
End If
End Try
Else
announceNames = Nothing
End If
If announceNames Is Nothing Then
SyncLock BotInstance.KnownGuilds
' Nothing on announceNAmes signals failure to apply roles. Set the warning message.
BotInstance.KnownGuilds(guild.Id).RoleWarning = True
End SyncLock
Return 0
End If
' Update warning flag
Dim updateError = Not correctRolePermissions Or gotForbidden
SyncLock BotInstance.KnownGuilds
BotInstance.KnownGuilds(guild.Id).RoleWarningPermission = updateError
End SyncLock
' Quit now if the warning flag was set. Announcement data is not available.
If updateError Then Return 0
If announceNames.Count <> 0 Then
' Send out announcement message

View file

@ -26,12 +26,12 @@ Class BirthdayBot
End Property
''' <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)
Config = conf
_client = dc
KnownGuilds = New Dictionary(Of ULong, GuildSettings)
KnownGuilds = New Dictionary(Of ULong, GuildStateInformation)
_worker = New BackgroundServiceRunner(Me)
@ -75,7 +75,7 @@ Class BirthdayBot
Private Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable
SyncLock KnownGuilds
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)
End If
End SyncLock
@ -114,7 +114,7 @@ Class BirthdayBot
End If
' Ban and role warning check
Dim roleWarning As Boolean
Dim roleWarningText As String
SyncLock KnownGuilds
Dim gi = KnownGuilds(channel.Guild.Id)
@ -125,13 +125,13 @@ Class BirthdayBot
End If
End If
roleWarning = gi.RoleWarning
roleWarningText = gi.IssueRoleWarning
End SyncLock
Try
If roleWarning Then
If roleWarningText IsNot Nothing Then
Try
Await channel.SendMessageAsync(RoleWarningMsg)
Await channel.SendMessageAsync(roleWarningText)
Catch ex As HttpException
' Don't let this prevent the bot from continuing command execution.
End Try

View file

@ -26,7 +26,7 @@ Class Database
Private Sub SetupTables()
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)
End Using
End Sub

View file

@ -4,11 +4,10 @@ Imports Npgsql
Imports NpgsqlTypes
''' <summary>
''' Collection of GuildUserSettings instances. Holds cached information on guild users and overall
''' guild options, and provides some database abstractions regarding them all.
''' Object instances are loaded when entering a guild and discarded when the bot leaves the guild.
''' Holds various pieces of state information for a guild the bot is operating in.
''' Includes, among other things, a copy of the guild's settings and a list of all known users with birthdays.
''' </summary>
Friend Class GuildSettings
Friend Class GuildStateInformation
Public ReadOnly Property GuildId As ULong
Private ReadOnly _db As Database
Private _bdayRole As ULong?
@ -19,33 +18,55 @@ Friend Class GuildSettings
Private _announceMsg As String
Private _announceMsgPl As String
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 Shared ReadOnly RoleWarningInterval As New TimeSpan(1, 0, 0)
''' <summary>
''' Flag for notifying servers that the bot is unable to manipulate its role.
''' Can be set at any time. Reading this will only return True if it's been set as such,
''' and it is only returned after a set time has passed in order to not constantly show the message.
''' Message for notifying servers that the bot is unable to manipulate its designated role for various reasons.
''' The returned value is dependent on certain warning flags accessible in this class. To avoid bombarding users
''' 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>
Public Property RoleWarning As Boolean
Public ReadOnly Property IssueRoleWarning As String
Get
If _roleWarning = True Then
' Only report a warning every so often.
If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then
_roleLastWarning = DateTimeOffset.UtcNow
Return True
Else
Return False
End If
If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then
_roleLastWarning = DateTimeOffset.UtcNow
Else
Return Nothing
End If
Return False
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
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
Set(value As Boolean)
_roleWarning = value
End Set
End Property
''' <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.
If Not reader.IsDBNull(1) Then
_bdayRole = CULng(reader.GetInt64(1))
RoleWarning = False
Else
RoleWarning = True
End If
If Not reader.IsDBNull(2) Then _announceCh = CULng(reader.GetInt64(2))
_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
_bdayRole = roleId
_roleWarning = False
_roleLastWarning = New DateTimeOffset
Await UpdateDatabaseAsync()
End Function
@ -326,7 +343,7 @@ Friend Class GuildSettings
''' 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.
''' </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 c = db.CreateCommand()
' Take note of ordinals for use in the constructor
@ -336,7 +353,7 @@ Friend Class GuildSettings
c.Prepare()
Using r = Await c.ExecuteReaderAsync()
If Await r.ReadAsync() Then
Return New GuildSettings(r, dbsettings)
Return New GuildStateInformation(r, dbsettings)
End If
End Using
End Using

View file

@ -4,7 +4,7 @@ Imports NpgsqlTypes
''' <summary>
''' 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>
Class GuildUserSettings
Private _month As Integer
@ -125,7 +125,7 @@ Class GuildUserSettings
Friend Shared Sub SetUpDatabaseTable(db As NpgsqlConnection)
Using c = db.CreateCommand()
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, " +
"birth_month integer not null, " +
"birth_day integer not null, " +