diff --git a/BirthdayBot/BackgroundServiceRunner.vb b/BirthdayBot/BackgroundServiceRunner.vb index 0d8c5d8..f132c07 100644 --- a/BirthdayBot/BackgroundServiceRunner.vb +++ b/BirthdayBot/BackgroundServiceRunner.vb @@ -40,7 +40,7 @@ Class BackgroundServiceRunner ' Delay a bit before we start (or continue) work. Await Task.Delay(Interval * 1000, WorkerCancel.Token) - ' Start background tasks. + ' Execute background tasks. Dim tasks As New List(Of Task) For Each service In Workers tasks.Add(service.OnTick(_tickCount)) diff --git a/BirthdayBot/BackgroundServices/BirthdayRoleUpdate.vb b/BirthdayBot/BackgroundServices/BirthdayRoleUpdate.vb index fac7736..3cfa0f7 100644 --- a/BirthdayBot/BackgroundServices/BirthdayRoleUpdate.vb +++ b/BirthdayBot/BackgroundServices/BirthdayRoleUpdate.vb @@ -58,23 +58,25 @@ Class BirthdayRoleUpdate Dim channel As SocketTextChannel = Nothing Dim announce As (String, String) 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 gs.RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value) + If Not BotInstance.GuildCache.ContainsKey(guild.Id) Then Return 0 ' guild not yet fully loaded; skip processing + + 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 - gs.RoleWarningNonexist = True + .RoleWarningNonexist = True Return 0 Else - gs.RoleWarningNonexist = False + .RoleWarningNonexist = False End If - End SyncLock + End With ' Determine who's currently having a birthday Dim birthdays = GetGuildCurrentBirthdays(users, tz) @@ -100,9 +102,7 @@ Class BirthdayRoleUpdate ' Update warning flag Dim updateError = Not correctRolePermissions Or gotForbidden - SyncLock BotInstance.KnownGuilds - BotInstance.KnownGuilds(guild.Id).RoleWarningPermission = updateError - End SyncLock + BotInstance.GuildCache(guild.Id).RoleWarningPermission = updateError ' Quit now if the warning flag was set. Announcement data is not available. If updateError Then Return 0 diff --git a/BirthdayBot/BackgroundServices/GuildStatistics.vb b/BirthdayBot/BackgroundServices/GuildStatistics.vb index bd55f3d..82233b8 100644 --- a/BirthdayBot/BackgroundServices/GuildStatistics.vb +++ b/BirthdayBot/BackgroundServices/GuildStatistics.vb @@ -11,8 +11,8 @@ Class GuildStatistics End Sub Public Overrides Async Function OnTick(tick As Integer) As Task - ' Activate roughly every 5 hours (interval: 45) - If tick Mod 400 <> 2 Then Return + ' Activate roughly every 2 hours (interval: 45) + If tick Mod 160 <> 2 Then Return Dim count = BotInstance.DiscordClient.Guilds.Count Log($"Currently in {count} guild(s).") diff --git a/BirthdayBot/BirthdayBot.vb b/BirthdayBot/BirthdayBot.vb index 47685bd..1787f3d 100644 --- a/BirthdayBot/BirthdayBot.vb +++ b/BirthdayBot/BirthdayBot.vb @@ -1,4 +1,5 @@ -Imports BirthdayBot.CommandsCommon +Imports System.Collections.Concurrent +Imports BirthdayBot.CommandsCommon Imports Discord Imports Discord.Net Imports Discord.WebSocket @@ -25,13 +26,12 @@ Class BirthdayBot End Get End Property - ''' SyncLock when using. The lock object is itself. - Friend ReadOnly Property KnownGuilds As Dictionary(Of ULong, GuildStateInformation) + Friend ReadOnly Property GuildCache As ConcurrentDictionary(Of ULong, GuildStateInformation) Public Sub New(conf As Configuration, dc As DiscordSocketClient) Config = conf _client = dc - KnownGuilds = New Dictionary(Of ULong, GuildStateInformation) + GuildCache = New ConcurrentDictionary(Of ULong, GuildStateInformation) _worker = New BackgroundServiceRunner(Me) @@ -72,20 +72,16 @@ Class BirthdayBot _client.Dispose() End Function - Private Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable - SyncLock KnownGuilds - If Not KnownGuilds.ContainsKey(g.Id) Then - Dim gi = GuildStateInformation.LoadSettingsAsync(Config.DatabaseSettings, g.Id).GetAwaiter().GetResult() - KnownGuilds.Add(g.Id, gi) - End If - End SyncLock - Return Task.CompletedTask + Private Async Function LoadGuild(g As SocketGuild) As Task Handles _client.JoinedGuild, _client.GuildAvailable + If Not GuildCache.ContainsKey(g.Id) Then + Dim gi = Await GuildStateInformation.LoadSettingsAsync(Config.DatabaseSettings, g.Id) + GuildCache.TryAdd(g.Id, gi) + End If End Function Private Function DiscardGuild(g As SocketGuild) As Task Handles _client.LeftGuild - SyncLock KnownGuilds - KnownGuilds.Remove(g.Id) - End SyncLock + Dim rm As GuildStateInformation = Nothing + GuildCache.TryRemove(g.Id, rm) Return Task.CompletedTask End Function @@ -115,18 +111,14 @@ Class BirthdayBot ' Ban and role warning check Dim roleWarningText As String - SyncLock KnownGuilds - Dim gi = KnownGuilds(channel.Guild.Id) - - ' Skip ban check if user is a manager - If Not gi.IsUserModerator(author) Then - If gi.IsUserBlockedAsync(author.Id).GetAwaiter().GetResult() Then - Return - End If + Dim gi = GuildCache(channel.Guild.Id) + ' Skip ban check if user is a manager + If Not gi.IsUserModerator(author) Then + If gi.IsUserBlockedAsync(author.Id).GetAwaiter().GetResult() Then + Return End If - - roleWarningText = gi.IssueRoleWarning - End SyncLock + End If + roleWarningText = gi.IssueRoleWarning Try If roleWarningText IsNot Nothing Then diff --git a/BirthdayBot/BirthdayBot.vbproj b/BirthdayBot/BirthdayBot.vbproj index 338ac7c..18a8aa5 100644 --- a/BirthdayBot/BirthdayBot.vbproj +++ b/BirthdayBot/BirthdayBot.vbproj @@ -4,7 +4,7 @@ Exe BirthdayBot netcoreapp2.0 - 1.1.2 + 1.2.0 Noiiko Discord bot for birthday reminders. diff --git a/BirthdayBot/Data/GuildStateInformation.vb b/BirthdayBot/Data/GuildStateInformation.vb index 48ae930..579eff8 100644 --- a/BirthdayBot/Data/GuildStateInformation.vb +++ b/BirthdayBot/Data/GuildStateInformation.vb @@ -31,22 +31,24 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property IssueRoleWarning As String Get - If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then - _roleLastWarning = DateTimeOffset.UtcNow - Else + SyncLock Me + If DateTimeOffset.UtcNow - _roleLastWarning > RoleWarningInterval Then + _roleLastWarning = DateTimeOffset.UtcNow + Else + Return Nothing + 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 + 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 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 - 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 SyncLock End Get End Property @@ -65,7 +67,9 @@ Friend Class GuildStateInformation ''' Friend ReadOnly Property RoleWarningUnset As Boolean Get - Return _bdayRole Is Nothing + SyncLock Me + Return _bdayRole Is Nothing + End SyncLock End Get End Property @@ -75,9 +79,11 @@ Friend Class GuildStateInformation Public ReadOnly Property Users As IEnumerable(Of GuildUserSettings) Get Dim items As New List(Of GuildUserSettings) - For Each item In _userCache.Values - items.Add(item) - Next + SyncLock Me + For Each item In _userCache.Values + items.Add(item) + Next + End SyncLock Return items End Get End Property @@ -87,7 +93,9 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property RoleId As ULong? Get - Return _bdayRole + SyncLock Me + Return _bdayRole + End SyncLock End Get End Property @@ -96,7 +104,9 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property AnnounceChannelId As ULong? Get - Return _announceCh + SyncLock Me + Return _announceCh + End SyncLock End Get End Property @@ -105,7 +115,9 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property TimeZone As String Get - Return _tz + SyncLock Me + Return _tz + End SyncLock End Get End Property @@ -114,7 +126,9 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property IsModerated As Boolean Get - Return _moderated + SyncLock Me + Return _moderated + End SyncLock End Get End Property @@ -123,7 +137,9 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property ModeratorRole As ULong? Get - Return _modRole + SyncLock Me + Return _modRole + End SyncLock End Get End Property @@ -132,17 +148,20 @@ Friend Class GuildStateInformation ''' Public ReadOnly Property AnnounceMessages As (String, String) Get - Return (_announceMsg, _announceMsgPl) + SyncLock Me + Return (_announceMsg, _announceMsgPl) + End SyncLock End Get End Property ''' ''' Gets whether to ping users in the announcement message instead of displaying their names. ''' - ''' Public ReadOnly Property AnnouncePing As Boolean Get - Return _announcePing + SyncLock Me + Return _announcePing + End SyncLock End Get 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, ''' a new instance is created which is capable of adding the user to the database. ''' - ''' + ''' + ''' For users with the Known property set to False, be sure to call + ''' if the resulting object is otherwise unused. + ''' Public Function GetUser(userId As ULong) As GuildUserSettings - If _userCache.ContainsKey(userId) Then - Return _userCache(userId) - End If + SyncLock Me + If _userCache.ContainsKey(userId) Then + Return _userCache(userId) + End If - ' 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. - Dim blank As New GuildUserSettings(_GuildId, userId) - _userCache.Add(userId, blank) - Return blank + ' No result. Create a blank entry and add it to the list, in case it + ' gets updated and then referenced later. + Dim blank As New GuildUserSettings(_GuildId, userId) + _userCache.Add(userId, blank) + Return blank + End SyncLock End Function ''' @@ -192,12 +216,13 @@ Friend Class GuildStateInformation ''' Public Async Function DeleteUserAsync(userId As ULong) As Task Dim user As GuildUserSettings = Nothing - If _userCache.TryGetValue(userId, user) Then - Await user.DeleteAsync(_db) - Else - Return - End If - _userCache.Remove(userId) + SyncLock Me + If Not _userCache.TryGetValue(userId, user) Then + Return + End If + _userCache.Remove(userId) + End SyncLock + Await user.DeleteAsync(_db) End Function ''' @@ -208,6 +233,8 @@ Friend Class GuildStateInformation Public Async Function IsUserBlockedAsync(userId As ULong) As Task(Of Boolean) 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 c = db.CreateCommand() c.CommandText = $"select * from {BackingTableBans} " + @@ -229,9 +256,11 @@ Friend Class GuildStateInformation ''' Public Function IsUserModerator(user As SocketGuildUser) As Boolean If user.GuildPermissions.ManageGuild Then Return True - If ModeratorRole.HasValue Then - If user.Roles.Where(Function(r) r.Id = ModeratorRole.Value).Count > 0 Then Return True - End If + SyncLock Me + If ModeratorRole.HasValue Then + If user.Roles.Where(Function(r) r.Id = ModeratorRole.Value).Count > 0 Then Return True + End If + End SyncLock IsUserModerator = False End Function @@ -240,6 +269,7 @@ Friend Class GuildStateInformation ''' Adds the specified user to the block list, preventing them from issuing commands. ''' Public Async Function BlockUserAsync(userId As ULong) As Task + ' TODO cache block list? Using db = Await _db.OpenConnectionAsync() Using c = db.CreateCommand() c.CommandText = $"insert into {BackingTableBans} (guild_id, user_id) " + @@ -257,6 +287,7 @@ Friend Class GuildStateInformation ''' Removes the specified user from the block list. ''' Public Async Function UnbanUserAsync(userId As ULong) As Task + ' TODO cache block list? Using db = Await _db.OpenConnectionAsync() Using c = db.CreateCommand() c.CommandText = $"delete from {BackingTableBans} where " + @@ -269,46 +300,60 @@ Friend Class GuildStateInformation End Using End Function - Public Async Function UpdateRoleAsync(roleId As ULong) As Task - _bdayRole = roleId - _roleLastWarning = New DateTimeOffset - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateRole(roleId As ULong) + SyncLock Me + _bdayRole = roleId + _roleLastWarning = New DateTimeOffset + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateAnnounceChannelAsync(channelId As ULong?) As Task - _announceCh = channelId - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateAnnounceChannel(channelId As ULong?) + SyncLock Me + _announceCh = channelId + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateTimeZoneAsync(tzString As String) As Task - _tz = tzString - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateTimeZone(tzString As String) + SyncLock Me + _tz = tzString + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateModeratedModeAsync(isModerated As Boolean) As Task - _moderated = isModerated - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateModeratedMode(isModerated As Boolean) + SyncLock Me + _moderated = isModerated + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateModeratorRoleAsync(roleId As ULong?) As Task - _modRole = roleId - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateModeratorRole(roleId As ULong?) + SyncLock Me + _modRole = roleId + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateAnnounceMessageAsync(message As String, plural As Boolean) As Task - If plural Then - _announceMsgPl = message - Else - _announceMsg = message - End If + Public Sub UpdateAnnounceMessage(message As String, plural As Boolean) + SyncLock Me + If plural Then + _announceMsgPl = message + Else + _announceMsg = message + End If - Await UpdateDatabaseAsync() - End Function + UpdateDatabase() + End SyncLock + End Sub - Public Async Function UpdateAnnouncePingAsync(value As Boolean) As Task - _announcePing = value - Await UpdateDatabaseAsync() - End Function + Public Sub UpdateAnnouncePing(value As Boolean) + SyncLock Me + _announcePing = value + UpdateDatabase() + End SyncLock + End Sub #Region "Database" Public Const BackingTable = "settings" @@ -374,8 +419,8 @@ Friend Class GuildStateInformation ''' Updates the backing database with values from this instance ''' This is a non-asynchronous operation. That may be bad. ''' - Private Async Function UpdateDatabaseAsync() As Task - Using db = Await _db.OpenConnectionAsync() + Private Sub UpdateDatabase() + Using db = _db.OpenConnectionAsync().GetAwaiter().GetResult() Using c = db.CreateCommand() c.CommandText = $"update {BackingTable} set " + "role_id = @RoleId, " + @@ -433,9 +478,9 @@ Friend Class GuildStateInformation End With c.Parameters.Add("@AnnouncePing", NpgsqlDbType.Boolean).Value = _announcePing c.Prepare() - Await c.ExecuteNonQueryAsync() + c.ExecuteNonQuery() End Using End Using - End Function + End Sub #End Region End Class diff --git a/BirthdayBot/UserInterface/ListingCommands.vb b/BirthdayBot/UserInterface/ListingCommands.vb index d0869f0..f185e09 100644 --- a/BirthdayBot/UserInterface/ListingCommands.vb +++ b/BirthdayBot/UserInterface/ListingCommands.vb @@ -24,11 +24,7 @@ Class ListingCommands ' Creates a file with all birthdays. 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. - Dim reqMod As Boolean - SyncLock Instance.KnownGuilds - reqMod = Instance.KnownGuilds(reqChannel.Guild.Id).IsUserModerator(reqUser) - End SyncLock - If Not reqMod Then + If Not Instance.GuildCache(reqChannel.Guild.Id).IsUserModerator(reqUser) Then Await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.") Return End If @@ -126,10 +122,7 @@ Class ListingCommands ''' Users currently not in the guild are not included in the result. ''' Private Async Function LoadList(guild As SocketGuild, escapeFormat As Boolean) As Task(Of List(Of ListItem)) - Dim ping As Boolean - SyncLock Instance.KnownGuilds - ping = Instance.KnownGuilds(guild.Id).AnnouncePing - End SyncLock + Dim ping = Instance.GuildCache(guild.Id).AnnouncePing Using db = Await BotConfig.DatabaseSettings.OpenConnectionAsync() Using c = db.CreateCommand() diff --git a/BirthdayBot/UserInterface/ManagerCommands.vb b/BirthdayBot/UserInterface/ManagerCommands.vb index a392cfe..a9a852f 100644 --- a/BirthdayBot/UserInterface/ManagerCommands.vb +++ b/BirthdayBot/UserInterface/ManagerCommands.vb @@ -42,11 +42,7 @@ Friend Class ManagerCommands Private Async Function CmdConfigDispatch(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task ' Ignore those without the proper permissions. ' Requires either the manage guild permission or to be in the moderators role - Dim allowed As Boolean - SyncLock Instance.KnownGuilds - allowed = Instance.KnownGuilds(reqUser.Guild.Id).IsUserModerator(reqUser) - End SyncLock - If Not allowed Then + If Not Instance.GuildCache(reqUser.Guild.Id).IsUserModerator(reqUser) Then Await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.") Return End If @@ -88,9 +84,7 @@ Friend Class ManagerCommands ElseIf role.Id = reqChannel.Guild.EveryoneRole.Id Then Await reqChannel.SendMessageAsync(":x: You cannot set that as the birthday role.") Else - SyncLock Instance.KnownGuilds - Instance.KnownGuilds(guild.Id).UpdateRoleAsync(role.Id).Wait() - End SyncLock + Instance.GuildCache(guild.Id).UpdateRole(role.Id) Await reqChannel.SendMessageAsync($":white_check_mark: The birthday role has been set as **{role.Name}**.") End If End Function @@ -116,9 +110,7 @@ Friend Class ManagerCommands Return End If - SyncLock Instance.KnownGuilds - Instance.KnownGuilds(reqChannel.Guild.Id).UpdateAnnouncePingAsync(setting).Wait() - End SyncLock + Instance.GuildCache(reqChannel.Guild.Id).UpdateAnnouncePing(setting) Await reqChannel.SendMessageAsync(result) End Function @@ -126,18 +118,15 @@ Friend Class ManagerCommands Private Async Function ScmdChannel(param As String(), reqChannel As SocketTextChannel) As Task If param.Length = 1 Then ' No extra parameter. Unset announcement channel. - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) + Dim gi = Instance.GuildCache(reqChannel.Guild.Id) - ' Extra detail: Show a unique message if a channel hadn't been set prior. - If Not gi.AnnounceChannelId.HasValue Then - reqChannel.SendMessageAsync(":x: There is no announcement channel set. Nothing to unset.").Wait() - Return - End If - - gi.UpdateAnnounceChannelAsync(Nothing).Wait() - End SyncLock + ' Extra detail: Show a unique message if a channel hadn't been set prior. + If Not gi.AnnounceChannelId.HasValue Then + reqChannel.SendMessageAsync(":x: There is no announcement channel set. Nothing to unset.").Wait() + Return + End If + gi.UpdateAnnounceChannel(Nothing) Await reqChannel.SendMessageAsync(":white_check_mark: The announcement channel has been unset.") Else ' Parameter check: This needs a channel mention to function. @@ -156,10 +145,8 @@ Friend Class ManagerCommands End If ' Update the value - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) - gi.UpdateAnnounceChannelAsync(chId).Wait() - End SyncLock + Dim gi = Instance.GuildCache(reqChannel.Guild.Id) + gi.UpdateAnnounceChannel(chId) ' Report the success 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 Await reqChannel.SendMessageAsync(RoleInputError) Else - SyncLock Instance.KnownGuilds - Instance.KnownGuilds(guild.Id).UpdateModeratorRoleAsync(role.Id).Wait() - End SyncLock + Instance.GuildCache(guild.Id).UpdateModeratorRole(role.Id) Await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.") End If End Function @@ -189,17 +174,15 @@ Friend Class ManagerCommands Private Async Function ScmdZone(param As String(), reqChannel As SocketTextChannel) As Task If param.Length = 1 Then ' No extra parameter. Unset guild default time zone. - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) + Dim gi = Instance.GuildCache(reqChannel.Guild.Id) - ' Extra detail: Show a unique message if there is no set zone. - If Not gi.AnnounceChannelId.HasValue Then - reqChannel.SendMessageAsync(":x: A default zone is not set. Nothing to unset.").Wait() - Return - End If + ' Extra detail: Show a unique message if there is no set zone. + If Not gi.AnnounceChannelId.HasValue Then + reqChannel.SendMessageAsync(":x: A default zone is not set. Nothing to unset.").Wait() + Return + End If - gi.UpdateTimeZoneAsync(Nothing).Wait() - End SyncLock + gi.UpdateTimeZone(Nothing) Await reqChannel.SendMessageAsync(":white_check_mark: The default time zone preference has been removed.") Else @@ -213,10 +196,7 @@ Friend Class ManagerCommands End Try ' Update value - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) - gi.UpdateTimeZoneAsync(zone).Wait() - End SyncLock + Instance.GuildCache(reqChannel.Guild.Id).UpdateTimeZone(zone) ' Report the success Await reqChannel.SendMessageAsync($":white_check_mark: The server's time zone has been set to **{zone}**.") @@ -238,26 +218,23 @@ Friend Class ManagerCommands Return End If - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) - Dim isBanned = gi.IsUserBlockedAsync(inputId).GetAwaiter().GetResult() - - If doBan Then - If Not isBanned Then - gi.BlockUserAsync(inputId).Wait() - reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").Wait() - Else - reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").Wait() - End If + Dim gi = Instance.GuildCache(reqChannel.Guild.Id) + Dim isBanned = Await gi.IsUserBlockedAsync(inputId) + If doBan Then + If Not isBanned Then + Await gi.BlockUserAsync(inputId) + reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").Wait() Else - If isBanned Then - gi.UnbanUserAsync(inputId).Wait() - reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").Wait() - Else - reqChannel.SendMessageAsync(":white_check_mark: The specified user has not been blocked.").Wait() - End If + reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").Wait() End If - End SyncLock + Else + If isBanned Then + Await gi.UnbanUserAsync(inputId) + reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").Wait() + Else + reqChannel.SendMessageAsync(":white_check_mark: The specified user has not been blocked.").Wait() + End If + End If End Function ' "moderated on/off" - Sets/unsets moderated mode. @@ -278,13 +255,9 @@ Friend Class ManagerCommands Return End If - Dim currentSet As Boolean - - SyncLock Instance.KnownGuilds - Dim gi = Instance.KnownGuilds(reqChannel.Guild.Id) - currentSet = gi.IsModerated - gi.UpdateModeratedModeAsync(modSet).Wait() - End SyncLock + Dim gi = Instance.GuildCache(reqChannel.Guild.Id) + Dim currentSet = gi.IsModerated + gi.UpdateModeratedMode(modSet) If currentSet = modSet Then Await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode is already {parameter}.") @@ -307,9 +280,7 @@ Friend Class ManagerCommands clear = True End If - SyncLock Instance.KnownGuilds - Instance.KnownGuilds(reqChannel.Guild.Id).UpdateAnnounceMessageAsync(newmsg, plural).Wait() - End SyncLock + Instance.GuildCache(reqChannel.Guild.Id).UpdateAnnounceMessage(newmsg, plural) 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"))) End Function @@ -318,9 +289,7 @@ Friend Class ManagerCommands ' Execute command as another user 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. - SyncLock Instance.KnownGuilds - If Not Instance.KnownGuilds(reqUser.Guild.Id).IsUserModerator(reqUser) Then Return - End SyncLock + If Not Instance.GuildCache(reqUser.Guild.Id).IsUserModerator(reqUser) Then Return If param.Length <> 3 Then Await reqChannel.SendMessageAsync(GenericError) diff --git a/BirthdayBot/UserInterface/UserCommands.vb b/BirthdayBot/UserInterface/UserCommands.vb index 25e7c2f..fb104b4 100644 --- a/BirthdayBot/UserInterface/UserCommands.vb +++ b/BirthdayBot/UserInterface/UserCommands.vb @@ -104,11 +104,9 @@ Class UserCommands ' Parsing successful. Update user information. Dim known As Boolean ' Extra detail: Bot's response changes if the user was previously unknown. Try - SyncLock Instance.KnownGuilds - Dim user = Instance.KnownGuilds(reqChannel.Guild.Id).GetUser(reqUser.Id) - known = user.IsKnown - user.UpdateAsync(bmonth, bday, btz, BotConfig.DatabaseSettings).Wait() - End SyncLock + Dim user = Instance.GuildCache(reqChannel.Guild.Id).GetUser(reqUser.Id) + known = user.IsKnown + Await user.UpdateAsync(bmonth, bday, btz, BotConfig.DatabaseSettings) Catch ex As Exception Log("Error", ex.ToString()) reqChannel.SendMessageAsync(":x: An unknown error occurred. The bot owner has been notified.").Wait() @@ -128,21 +126,20 @@ Class UserCommands End If Dim btz As String = Nothing - SyncLock Instance.KnownGuilds - Dim user = Instance.KnownGuilds(reqChannel.Guild.Id).GetUser(reqUser.Id) - If Not user.IsKnown Then - reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.").Wait() - Return - End If + Dim user = Instance.GuildCache(reqChannel.Guild.Id).GetUser(reqUser.Id) + If Not user.IsKnown Then + Await reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.") + Return + End If + + Try + btz = ParseTimeZone(param(1)) + Catch ex As Exception + reqChannel.SendMessageAsync(ex.Message).Wait() + Return + End Try + Await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings) - Try - btz = ParseTimeZone(param(1)) - Catch ex As Exception - reqChannel.SendMessageAsync(ex.Message).Wait() - Return - End Try - user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings).Wait() - End SyncLock Await reqChannel.SendMessageAsync($":white_check_mark: Your time zone has been updated to **{btz}**.") End Function @@ -155,13 +152,10 @@ Class UserCommands ' Extra detail: Send a notification if the user isn't actually known by the bot. Dim known As Boolean - SyncLock Instance.KnownGuilds - Dim g = Instance.KnownGuilds(reqChannel.Guild.Id) - known = g.GetUser(reqUser.Id).IsKnown - If known Then - g.DeleteUserAsync(reqUser.Id).Wait() - End If - End SyncLock + Dim g = Instance.GuildCache(reqChannel.Guild.Id) + known = g.GetUser(reqUser.Id).IsKnown + ' Delete database and cache entry + Await g.DeleteUserAsync(reqUser.Id) If Not known Then Await reqChannel.SendMessageAsync(":white_check_mark: I don't have your information. Nothing to remove.") Else