Remove huge lock on cache

This commit is contained in:
Noikoio 2019-09-06 12:11:10 -07:00
parent 92f4c42b5b
commit 0c4d39e24d
9 changed files with 227 additions and 234 deletions

View file

@ -40,7 +40,7 @@ Class BackgroundServiceRunner
' Delay a bit before we start (or continue) work. ' Delay a bit before we start (or continue) work.
Await Task.Delay(Interval * 1000, WorkerCancel.Token) Await Task.Delay(Interval * 1000, WorkerCancel.Token)
' Start background tasks. ' Execute background tasks.
Dim tasks As New List(Of Task) Dim tasks As New List(Of Task)
For Each service In Workers For Each service In Workers
tasks.Add(service.OnTick(_tickCount)) tasks.Add(service.OnTick(_tickCount))

View file

@ -58,23 +58,25 @@ Class BirthdayRoleUpdate
Dim channel As SocketTextChannel = Nothing Dim channel As SocketTextChannel = Nothing
Dim announce As (String, String) Dim announce As (String, String)
Dim announceping As Boolean Dim announceping As Boolean
SyncLock BotInstance.KnownGuilds
If Not BotInstance.KnownGuilds.ContainsKey(guild.Id) Then Return 0
Dim gs = BotInstance.KnownGuilds(guild.Id)
tz = gs.TimeZone
users = gs.Users
announce = gs.AnnounceMessages
announceping = gs.AnnouncePing
If gs.AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value) If Not BotInstance.GuildCache.ContainsKey(guild.Id) Then Return 0 ' guild not yet fully loaded; skip processing
If gs.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
Dim gs = BotInstance.GuildCache(guild.Id)
With gs
tz = .TimeZone
users = .Users
announce = .AnnounceMessages
announceping = .AnnouncePing
If .AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value)
If .RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
If role Is Nothing Then If role Is Nothing Then
gs.RoleWarningNonexist = True .RoleWarningNonexist = True
Return 0 Return 0
Else Else
gs.RoleWarningNonexist = False .RoleWarningNonexist = False
End If End If
End SyncLock End With
' Determine who's currently having a birthday ' Determine who's currently having a birthday
Dim birthdays = GetGuildCurrentBirthdays(users, tz) Dim birthdays = GetGuildCurrentBirthdays(users, tz)
@ -100,9 +102,7 @@ Class BirthdayRoleUpdate
' Update warning flag ' Update warning flag
Dim updateError = Not correctRolePermissions Or gotForbidden Dim updateError = Not correctRolePermissions Or gotForbidden
SyncLock BotInstance.KnownGuilds BotInstance.GuildCache(guild.Id).RoleWarningPermission = updateError
BotInstance.KnownGuilds(guild.Id).RoleWarningPermission = updateError
End SyncLock
' Quit now if the warning flag was set. Announcement data is not available. ' Quit now if the warning flag was set. Announcement data is not available.
If updateError Then Return 0 If updateError Then Return 0

View file

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

View file

@ -1,4 +1,5 @@
Imports BirthdayBot.CommandsCommon Imports System.Collections.Concurrent
Imports BirthdayBot.CommandsCommon
Imports Discord Imports Discord
Imports Discord.Net Imports Discord.Net
Imports Discord.WebSocket Imports Discord.WebSocket
@ -25,13 +26,12 @@ Class BirthdayBot
End Get End Get
End Property End Property
''' <summary>SyncLock when using. The lock object is itself.</summary> Friend ReadOnly Property GuildCache As ConcurrentDictionary(Of ULong, GuildStateInformation)
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, GuildStateInformation) GuildCache = New ConcurrentDictionary(Of ULong, GuildStateInformation)
_worker = New BackgroundServiceRunner(Me) _worker = New BackgroundServiceRunner(Me)
@ -72,20 +72,16 @@ Class BirthdayBot
_client.Dispose() _client.Dispose()
End Function End Function
Private Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable Private Async Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable
SyncLock KnownGuilds If Not GuildCache.ContainsKey(g.Id) Then
If Not KnownGuilds.ContainsKey(g.Id) Then Dim gi = Await GuildStateInformation.LoadSettingsAsync(Config.DatabaseSettings, g.Id)
Dim gi = GuildStateInformation.LoadSettingsAsync(Config.DatabaseSettings, g.Id).GetAwaiter().GetResult() GuildCache.TryAdd(g.Id, gi)
KnownGuilds.Add(g.Id, gi)
End If End If
End SyncLock
Return Task.CompletedTask
End Function End Function
Private Function DiscardGuild(g As SocketGuild) As Task Handles _client.LeftGuild Private Function DiscardGuild(g As SocketGuild) As Task Handles _client.LeftGuild
SyncLock KnownGuilds Dim rm As GuildStateInformation = Nothing
KnownGuilds.Remove(g.Id) GuildCache.TryRemove(g.Id, rm)
End SyncLock
Return Task.CompletedTask Return Task.CompletedTask
End Function End Function
@ -115,18 +111,14 @@ Class BirthdayBot
' Ban and role warning check ' Ban and role warning check
Dim roleWarningText As String Dim roleWarningText As String
SyncLock KnownGuilds Dim gi = GuildCache(channel.Guild.Id)
Dim gi = KnownGuilds(channel.Guild.Id)
' Skip ban check if user is a manager ' Skip ban check if user is a manager
If Not gi.IsUserModerator(author) Then If Not gi.IsUserModerator(author) Then
If gi.IsUserBlockedAsync(author.Id).GetAwaiter().GetResult() Then If gi.IsUserBlockedAsync(author.Id).GetAwaiter().GetResult() Then
Return Return
End If End If
End If End If
roleWarningText = gi.IssueRoleWarning roleWarningText = gi.IssueRoleWarning
End SyncLock
Try Try
If roleWarningText IsNot Nothing Then If roleWarningText IsNot Nothing Then

View file

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

View file

@ -31,6 +31,7 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property IssueRoleWarning As String Public ReadOnly Property IssueRoleWarning As String
Get Get
SyncLock Me
If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then
_roleLastWarning = DateTimeOffset.UtcNow _roleLastWarning = DateTimeOffset.UtcNow
Else Else
@ -47,6 +48,7 @@ Friend Class GuildStateInformation
End If End If
Return Nothing Return Nothing
End SyncLock
End Get End Get
End Property End Property
@ -65,7 +67,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Friend ReadOnly Property RoleWarningUnset As Boolean Friend ReadOnly Property RoleWarningUnset As Boolean
Get Get
SyncLock Me
Return _bdayRole Is Nothing Return _bdayRole Is Nothing
End SyncLock
End Get End Get
End Property End Property
@ -75,9 +79,11 @@ Friend Class GuildStateInformation
Public ReadOnly Property Users As IEnumerable(Of GuildUserSettings) Public ReadOnly Property Users As IEnumerable(Of GuildUserSettings)
Get Get
Dim items As New List(Of GuildUserSettings) Dim items As New List(Of GuildUserSettings)
SyncLock Me
For Each item In _userCache.Values For Each item In _userCache.Values
items.Add(item) items.Add(item)
Next Next
End SyncLock
Return items Return items
End Get End Get
End Property End Property
@ -87,7 +93,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property RoleId As ULong? Public ReadOnly Property RoleId As ULong?
Get Get
SyncLock Me
Return _bdayRole Return _bdayRole
End SyncLock
End Get End Get
End Property End Property
@ -96,7 +104,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property AnnounceChannelId As ULong? Public ReadOnly Property AnnounceChannelId As ULong?
Get Get
SyncLock Me
Return _announceCh Return _announceCh
End SyncLock
End Get End Get
End Property End Property
@ -105,7 +115,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property TimeZone As String Public ReadOnly Property TimeZone As String
Get Get
SyncLock Me
Return _tz Return _tz
End SyncLock
End Get End Get
End Property End Property
@ -114,7 +126,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property IsModerated As Boolean Public ReadOnly Property IsModerated As Boolean
Get Get
SyncLock Me
Return _moderated Return _moderated
End SyncLock
End Get End Get
End Property End Property
@ -123,7 +137,9 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property ModeratorRole As ULong? Public ReadOnly Property ModeratorRole As ULong?
Get Get
SyncLock Me
Return _modRole Return _modRole
End SyncLock
End Get End Get
End Property End Property
@ -132,17 +148,20 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public ReadOnly Property AnnounceMessages As (String, String) Public ReadOnly Property AnnounceMessages As (String, String)
Get Get
SyncLock Me
Return (_announceMsg, _announceMsgPl) Return (_announceMsg, _announceMsgPl)
End SyncLock
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets whether to ping users in the announcement message instead of displaying their names. ''' Gets whether to ping users in the announcement message instead of displaying their names.
''' </summary> ''' </summary>
''' <returns></returns>
Public ReadOnly Property AnnouncePing As Boolean Public ReadOnly Property AnnouncePing As Boolean
Get Get
SyncLock Me
Return _announcePing Return _announcePing
End SyncLock
End Get End Get
End Property End Property
@ -174,17 +193,22 @@ Friend Class GuildStateInformation
''' Gets user information from this guild. If the user doesn't exist in the backing database, ''' Gets user information from this guild. If the user doesn't exist in the backing database,
''' a new instance is created which is capable of adding the user to the database. ''' a new instance is created which is capable of adding the user to the database.
''' </summary> ''' </summary>
''' <param name="userId"></param> ''' <remarks>
''' For users with the Known property set to False, be sure to call
''' <see cref="GuildUserSettings.DeleteAsync(Database)"/> if the resulting object is otherwise unused.
''' </remarks>
Public Function GetUser(userId As ULong) As GuildUserSettings Public Function GetUser(userId As ULong) As GuildUserSettings
SyncLock Me
If _userCache.ContainsKey(userId) Then If _userCache.ContainsKey(userId) Then
Return _userCache(userId) Return _userCache(userId)
End If End If
' No result. Create a blank entry and add it to the list, in case it ' No result. Create a blank entry and add it to the list, in case it
' gets referenced later regardless of if having been updated or not. ' gets updated and then referenced later.
Dim blank As New GuildUserSettings(_GuildId, userId) Dim blank As New GuildUserSettings(_GuildId, userId)
_userCache.Add(userId, blank) _userCache.Add(userId, blank)
Return blank Return blank
End SyncLock
End Function End Function
''' <summary> ''' <summary>
@ -192,12 +216,13 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public Async Function DeleteUserAsync(userId As ULong) As Task Public Async Function DeleteUserAsync(userId As ULong) As Task
Dim user As GuildUserSettings = Nothing Dim user As GuildUserSettings = Nothing
If _userCache.TryGetValue(userId, user) Then SyncLock Me
Await user.DeleteAsync(_db) If Not _userCache.TryGetValue(userId, user) Then
Else
Return Return
End If End If
_userCache.Remove(userId) _userCache.Remove(userId)
End SyncLock
Await user.DeleteAsync(_db)
End Function End Function
''' <summary> ''' <summary>
@ -208,6 +233,8 @@ Friend Class GuildStateInformation
Public Async Function IsUserBlockedAsync(userId As ULong) As Task(Of Boolean) Public Async Function IsUserBlockedAsync(userId As ULong) As Task(Of Boolean)
If IsModerated Then Return True If IsModerated Then Return True
' Block list is not cached, thus doing a database lookup
' TODO cache block list?
Using db = Await _db.OpenConnectionAsync() Using db = Await _db.OpenConnectionAsync()
Using c = db.CreateCommand() Using c = db.CreateCommand()
c.CommandText = $"select * from {BackingTableBans} " + c.CommandText = $"select * from {BackingTableBans} " +
@ -229,9 +256,11 @@ Friend Class GuildStateInformation
''' </summary> ''' </summary>
Public Function IsUserModerator(user As SocketGuildUser) As Boolean Public Function IsUserModerator(user As SocketGuildUser) As Boolean
If user.GuildPermissions.ManageGuild Then Return True If user.GuildPermissions.ManageGuild Then Return True
SyncLock Me
If ModeratorRole.HasValue Then If ModeratorRole.HasValue Then
If user.Roles.Where(Function(r) r.Id = ModeratorRole.Value).Count > 0 Then Return True If user.Roles.Where(Function(r) r.Id = ModeratorRole.Value).Count > 0 Then Return True
End If End If
End SyncLock
IsUserModerator = False IsUserModerator = False
End Function End Function
@ -240,6 +269,7 @@ Friend Class GuildStateInformation
''' Adds the specified user to the block list, preventing them from issuing commands. ''' Adds the specified user to the block list, preventing them from issuing commands.
''' </summary> ''' </summary>
Public Async Function BlockUserAsync(userId As ULong) As Task Public Async Function BlockUserAsync(userId As ULong) As Task
' TODO cache block list?
Using db = Await _db.OpenConnectionAsync() Using db = Await _db.OpenConnectionAsync()
Using c = db.CreateCommand() Using c = db.CreateCommand()
c.CommandText = $"insert into {BackingTableBans} (guild_id, user_id) " + c.CommandText = $"insert into {BackingTableBans} (guild_id, user_id) " +
@ -257,6 +287,7 @@ Friend Class GuildStateInformation
''' Removes the specified user from the block list. ''' Removes the specified user from the block list.
''' </summary> ''' </summary>
Public Async Function UnbanUserAsync(userId As ULong) As Task Public Async Function UnbanUserAsync(userId As ULong) As Task
' TODO cache block list?
Using db = Await _db.OpenConnectionAsync() Using db = Await _db.OpenConnectionAsync()
Using c = db.CreateCommand() Using c = db.CreateCommand()
c.CommandText = $"delete from {BackingTableBans} where " + c.CommandText = $"delete from {BackingTableBans} where " +
@ -269,46 +300,60 @@ Friend Class GuildStateInformation
End Using End Using
End Function End Function
Public Async Function UpdateRoleAsync(roleId As ULong) As Task Public Sub UpdateRole(roleId As ULong)
SyncLock Me
_bdayRole = roleId _bdayRole = roleId
_roleLastWarning = New DateTimeOffset _roleLastWarning = New DateTimeOffset
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateAnnounceChannelAsync(channelId As ULong?) As Task Public Sub UpdateAnnounceChannel(channelId As ULong?)
SyncLock Me
_announceCh = channelId _announceCh = channelId
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateTimeZoneAsync(tzString As String) As Task Public Sub UpdateTimeZone(tzString As String)
SyncLock Me
_tz = tzString _tz = tzString
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateModeratedModeAsync(isModerated As Boolean) As Task Public Sub UpdateModeratedMode(isModerated As Boolean)
SyncLock Me
_moderated = isModerated _moderated = isModerated
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateModeratorRoleAsync(roleId As ULong?) As Task Public Sub UpdateModeratorRole(roleId As ULong?)
SyncLock Me
_modRole = roleId _modRole = roleId
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateAnnounceMessageAsync(message As String, plural As Boolean) As Task Public Sub UpdateAnnounceMessage(message As String, plural As Boolean)
SyncLock Me
If plural Then If plural Then
_announceMsgPl = message _announceMsgPl = message
Else Else
_announceMsg = message _announceMsg = message
End If End If
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
Public Async Function UpdateAnnouncePingAsync(value As Boolean) As Task Public Sub UpdateAnnouncePing(value As Boolean)
SyncLock Me
_announcePing = value _announcePing = value
Await UpdateDatabaseAsync() UpdateDatabase()
End Function End SyncLock
End Sub
#Region "Database" #Region "Database"
Public Const BackingTable = "settings" Public Const BackingTable = "settings"
@ -374,8 +419,8 @@ Friend Class GuildStateInformation
''' Updates the backing database with values from this instance ''' Updates the backing database with values from this instance
''' This is a non-asynchronous operation. That may be bad. ''' This is a non-asynchronous operation. That may be bad.
''' </summary> ''' </summary>
Private Async Function UpdateDatabaseAsync() As Task Private Sub UpdateDatabase()
Using db = Await _db.OpenConnectionAsync() Using db = _db.OpenConnectionAsync().GetAwaiter().GetResult()
Using c = db.CreateCommand() Using c = db.CreateCommand()
c.CommandText = $"update {BackingTable} set " + c.CommandText = $"update {BackingTable} set " +
"role_id = @RoleId, " + "role_id = @RoleId, " +
@ -433,9 +478,9 @@ Friend Class GuildStateInformation
End With End With
c.Parameters.Add("@AnnouncePing", NpgsqlDbType.Boolean).Value = _announcePing c.Parameters.Add("@AnnouncePing", NpgsqlDbType.Boolean).Value = _announcePing
c.Prepare() c.Prepare()
Await c.ExecuteNonQueryAsync() c.ExecuteNonQuery()
End Using End Using
End Using End Using
End Function End Sub
#End Region #End Region
End Class End Class

View file

@ -24,11 +24,7 @@ Class ListingCommands
' Creates a file with all birthdays. ' Creates a file with all birthdays.
Private Async Function CmdList(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task Private Async Function CmdList(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' For now, we're restricting this command to moderators only. This may turn into an option later. ' For now, we're restricting this command to moderators only. This may turn into an option later.
Dim reqMod As Boolean If Not Instance.GuildCache(reqChannel.Guild.Id).IsUserModerator(reqUser) Then
SyncLock Instance.KnownGuilds
reqMod = Instance.KnownGuilds(reqChannel.Guild.Id).IsUserModerator(reqUser)
End SyncLock
If Not reqMod Then
Await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.") Await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.")
Return Return
End If End If
@ -126,10 +122,7 @@ Class ListingCommands
''' Users currently not in the guild are not included in the result. ''' Users currently not in the guild are not included in the result.
''' </summary> ''' </summary>
Private Async Function LoadList(guild As SocketGuild, escapeFormat As Boolean) As Task(Of List(Of ListItem)) Private Async Function LoadList(guild As SocketGuild, escapeFormat As Boolean) As Task(Of List(Of ListItem))
Dim ping As Boolean Dim ping = Instance.GuildCache(guild.Id).AnnouncePing
SyncLock Instance.KnownGuilds
ping = Instance.KnownGuilds(guild.Id).AnnouncePing
End SyncLock
Using db = Await BotConfig.DatabaseSettings.OpenConnectionAsync() Using db = Await BotConfig.DatabaseSettings.OpenConnectionAsync()
Using c = db.CreateCommand() Using c = db.CreateCommand()

View file

@ -42,11 +42,7 @@ Friend Class ManagerCommands
Private Async Function CmdConfigDispatch(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task Private Async Function CmdConfigDispatch(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' Ignore those without the proper permissions. ' Ignore those without the proper permissions.
' Requires either the manage guild permission or to be in the moderators role ' Requires either the manage guild permission or to be in the moderators role
Dim allowed As Boolean If Not Instance.GuildCache(reqUser.Guild.Id).IsUserModerator(reqUser) Then
SyncLock Instance.KnownGuilds
allowed = Instance.KnownGuilds(reqUser.Guild.Id).IsUserModerator(reqUser)
End SyncLock
If Not allowed Then
Await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.") Await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.")
Return Return
End If End If
@ -88,9 +84,7 @@ Friend Class ManagerCommands
ElseIf role.Id = reqChannel.Guild.EveryoneRole.Id Then ElseIf role.Id = reqChannel.Guild.EveryoneRole.Id Then
Await reqChannel.SendMessageAsync(":x: You cannot set that as the birthday role.") Await reqChannel.SendMessageAsync(":x: You cannot set that as the birthday role.")
Else Else
SyncLock Instance.KnownGuilds Instance.GuildCache(guild.Id).UpdateRole(role.Id)
Instance.KnownGuilds(guild.Id).UpdateRoleAsync(role.Id).Wait()
End SyncLock
Await reqChannel.SendMessageAsync($":white_check_mark: The birthday role has been set as **{role.Name}**.") Await reqChannel.SendMessageAsync($":white_check_mark: The birthday role has been set as **{role.Name}**.")
End If End If
End Function End Function
@ -116,9 +110,7 @@ Friend Class ManagerCommands
Return Return
End If End If
SyncLock Instance.KnownGuilds Instance.GuildCache(reqChannel.Guild.Id).UpdateAnnouncePing(setting)
Instance.KnownGuilds(reqChannel.Guild.Id).UpdateAnnouncePingAsync(setting).Wait()
End SyncLock
Await reqChannel.SendMessageAsync(result) Await reqChannel.SendMessageAsync(result)
End Function End Function
@ -126,8 +118,7 @@ Friend Class ManagerCommands
Private Async Function ScmdChannel(param As String(), reqChannel As SocketTextChannel) As Task Private Async Function ScmdChannel(param As String(), reqChannel As SocketTextChannel) As Task
If param.Length = 1 Then If param.Length = 1 Then
' No extra parameter. Unset announcement channel. ' No extra parameter. Unset announcement channel.
SyncLock Instance.KnownGuilds Dim gi = Instance.GuildCache(reqChannel.Guild.Id)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id)
' Extra detail: Show a unique message if a channel hadn't been set prior. ' Extra detail: Show a unique message if a channel hadn't been set prior.
If Not gi.AnnounceChannelId.HasValue Then If Not gi.AnnounceChannelId.HasValue Then
@ -135,9 +126,7 @@ Friend Class ManagerCommands
Return Return
End If End If
gi.UpdateAnnounceChannelAsync(Nothing).Wait() gi.UpdateAnnounceChannel(Nothing)
End SyncLock
Await reqChannel.SendMessageAsync(":white_check_mark: The announcement channel has been unset.") Await reqChannel.SendMessageAsync(":white_check_mark: The announcement channel has been unset.")
Else Else
' Parameter check: This needs a channel mention to function. ' Parameter check: This needs a channel mention to function.
@ -156,10 +145,8 @@ Friend Class ManagerCommands
End If End If
' Update the value ' Update the value
SyncLock Instance.KnownGuilds Dim gi = Instance.GuildCache(reqChannel.Guild.Id)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) gi.UpdateAnnounceChannel(chId)
gi.UpdateAnnounceChannelAsync(chId).Wait()
End SyncLock
' Report the success ' Report the success
Await reqChannel.SendMessageAsync($":white_check_mark: The announcement channel is now set to <#{chId}>.") Await reqChannel.SendMessageAsync($":white_check_mark: The announcement channel is now set to <#{chId}>.")
@ -178,9 +165,7 @@ Friend Class ManagerCommands
If role Is Nothing Then If role Is Nothing Then
Await reqChannel.SendMessageAsync(RoleInputError) Await reqChannel.SendMessageAsync(RoleInputError)
Else Else
SyncLock Instance.KnownGuilds Instance.GuildCache(guild.Id).UpdateModeratorRole(role.Id)
Instance.KnownGuilds(guild.Id).UpdateModeratorRoleAsync(role.Id).Wait()
End SyncLock
Await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.") Await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.")
End If End If
End Function End Function
@ -189,8 +174,7 @@ Friend Class ManagerCommands
Private Async Function ScmdZone(param As String(), reqChannel As SocketTextChannel) As Task Private Async Function ScmdZone(param As String(), reqChannel As SocketTextChannel) As Task
If param.Length = 1 Then If param.Length = 1 Then
' No extra parameter. Unset guild default time zone. ' No extra parameter. Unset guild default time zone.
SyncLock Instance.KnownGuilds Dim gi = Instance.GuildCache(reqChannel.Guild.Id)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id)
' Extra detail: Show a unique message if there is no set zone. ' Extra detail: Show a unique message if there is no set zone.
If Not gi.AnnounceChannelId.HasValue Then If Not gi.AnnounceChannelId.HasValue Then
@ -198,8 +182,7 @@ Friend Class ManagerCommands
Return Return
End If End If
gi.UpdateTimeZoneAsync(Nothing).Wait() gi.UpdateTimeZone(Nothing)
End SyncLock
Await reqChannel.SendMessageAsync(":white_check_mark: The default time zone preference has been removed.") Await reqChannel.SendMessageAsync(":white_check_mark: The default time zone preference has been removed.")
Else Else
@ -213,10 +196,7 @@ Friend Class ManagerCommands
End Try End Try
' Update value ' Update value
SyncLock Instance.KnownGuilds Instance.GuildCache(reqChannel.Guild.Id).UpdateTimeZone(zone)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id)
gi.UpdateTimeZoneAsync(zone).Wait()
End SyncLock
' Report the success ' Report the success
Await reqChannel.SendMessageAsync($":white_check_mark: The server's time zone has been set to **{zone}**.") Await reqChannel.SendMessageAsync($":white_check_mark: The server's time zone has been set to **{zone}**.")
@ -238,26 +218,23 @@ Friend Class ManagerCommands
Return Return
End If End If
SyncLock Instance.KnownGuilds Dim gi = Instance.GuildCache(reqChannel.Guild.Id)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) Dim isBanned = Await gi.IsUserBlockedAsync(inputId)
Dim isBanned = gi.IsUserBlockedAsync(inputId).GetAwaiter().GetResult()
If doBan Then If doBan Then
If Not isBanned Then If Not isBanned Then
gi.BlockUserAsync(inputId).Wait() Await gi.BlockUserAsync(inputId)
reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").Wait() reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").Wait()
Else Else
reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").Wait() reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").Wait()
End If End If
Else Else
If isBanned Then If isBanned Then
gi.UnbanUserAsync(inputId).Wait() Await gi.UnbanUserAsync(inputId)
reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").Wait() reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").Wait()
Else Else
reqChannel.SendMessageAsync(":white_check_mark: The specified user has not been blocked.").Wait() reqChannel.SendMessageAsync(":white_check_mark: The specified user has not been blocked.").Wait()
End If End If
End If End If
End SyncLock
End Function End Function
' "moderated on/off" - Sets/unsets moderated mode. ' "moderated on/off" - Sets/unsets moderated mode.
@ -278,13 +255,9 @@ Friend Class ManagerCommands
Return Return
End If End If
Dim currentSet As Boolean Dim gi = Instance.GuildCache(reqChannel.Guild.Id)
Dim currentSet = gi.IsModerated
SyncLock Instance.KnownGuilds gi.UpdateModeratedMode(modSet)
Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id)
currentSet = gi.IsModerated
gi.UpdateModeratedModeAsync(modSet).Wait()
End SyncLock
If currentSet = modSet Then If currentSet = modSet Then
Await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode is already {parameter}.") Await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode is already {parameter}.")
@ -307,9 +280,7 @@ Friend Class ManagerCommands
clear = True clear = True
End If End If
SyncLock Instance.KnownGuilds Instance.GuildCache(reqChannel.Guild.Id).UpdateAnnounceMessage(newmsg, plural)
Instance.KnownGuilds(reqChannel.Guild.Id).UpdateAnnounceMessageAsync(newmsg, plural).Wait()
End SyncLock
Const report = ":white_check_mark: The {0} birthday announcement message has been {1}." Const report = ":white_check_mark: The {0} birthday announcement message has been {1}."
Await reqChannel.SendMessageAsync(String.Format(report, If(plural, "plural", "singular"), If(clear, "reset", "updated"))) Await reqChannel.SendMessageAsync(String.Format(report, If(plural, "plural", "singular"), If(clear, "reset", "updated")))
End Function End Function
@ -318,9 +289,7 @@ Friend Class ManagerCommands
' Execute command as another user ' Execute command as another user
Private Async Function CmdOverride(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task Private Async Function CmdOverride(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' Moderators only. As with config, silently drop if this check fails. ' Moderators only. As with config, silently drop if this check fails.
SyncLock Instance.KnownGuilds If Not Instance.GuildCache(reqUser.Guild.Id).IsUserModerator(reqUser) Then Return
If Not Instance.KnownGuilds(reqUser.Guild.Id).IsUserModerator(reqUser) Then Return
End SyncLock
If param.Length <> 3 Then If param.Length <> 3 Then
Await reqChannel.SendMessageAsync(GenericError) Await reqChannel.SendMessageAsync(GenericError)

View file

@ -104,11 +104,9 @@ Class UserCommands
' Parsing successful. Update user information. ' Parsing successful. Update user information.
Dim known As Boolean ' Extra detail: Bot's response changes if the user was previously unknown. Dim known As Boolean ' Extra detail: Bot's response changes if the user was previously unknown.
Try Try
SyncLock Instance.KnownGuilds Dim user = Instance.GuildCache(reqChannel.Guild.Id).GetUser(reqUser.Id)
Dim user = Instance.KnownGuilds(reqChannel.Guild.Id).GetUser(reqUser.Id)
known = user.IsKnown known = user.IsKnown
user.UpdateAsync(bmonth, bday, btz, BotConfig.DatabaseSettings).Wait() Await user.UpdateAsync(bmonth, bday, btz, BotConfig.DatabaseSettings)
End SyncLock
Catch ex As Exception Catch ex As Exception
Log("Error", ex.ToString()) Log("Error", ex.ToString())
reqChannel.SendMessageAsync(":x: An unknown error occurred. The bot owner has been notified.").Wait() reqChannel.SendMessageAsync(":x: An unknown error occurred. The bot owner has been notified.").Wait()
@ -128,10 +126,9 @@ Class UserCommands
End If End If
Dim btz As String = Nothing Dim btz As String = Nothing
SyncLock Instance.KnownGuilds Dim user = Instance.GuildCache(reqChannel.Guild.Id).GetUser(reqUser.Id)
Dim user = Instance.KnownGuilds(reqChannel.Guild.Id).GetUser(reqUser.Id)
If Not user.IsKnown Then If Not user.IsKnown Then
reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.").Wait() Await reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.")
Return Return
End If End If
@ -141,8 +138,8 @@ Class UserCommands
reqChannel.SendMessageAsync(ex.Message).Wait() reqChannel.SendMessageAsync(ex.Message).Wait()
Return Return
End Try End Try
user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings).Wait() Await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings)
End SyncLock
Await reqChannel.SendMessageAsync($":white_check_mark: Your time zone has been updated to **{btz}**.") Await reqChannel.SendMessageAsync($":white_check_mark: Your time zone has been updated to **{btz}**.")
End Function End Function
@ -155,13 +152,10 @@ Class UserCommands
' Extra detail: Send a notification if the user isn't actually known by the bot. ' Extra detail: Send a notification if the user isn't actually known by the bot.
Dim known As Boolean Dim known As Boolean
SyncLock Instance.KnownGuilds Dim g = Instance.GuildCache(reqChannel.Guild.Id)
Dim g = Instance.KnownGuilds(reqChannel.Guild.Id)
known = g.GetUser(reqUser.Id).IsKnown known = g.GetUser(reqUser.Id).IsKnown
If known Then ' Delete database and cache entry
g.DeleteUserAsync(reqUser.Id).Wait() Await g.DeleteUserAsync(reqUser.Id)
End If
End SyncLock
If Not known Then If Not known Then
Await reqChannel.SendMessageAsync(":white_check_mark: I don't have your information. Nothing to remove.") Await reqChannel.SendMessageAsync(":white_check_mark: I don't have your information. Nothing to remove.")
Else Else