Improved role processing

Made the process more efficient so it iterates over less names as it
determines which roles to alter.
Additionally, it now attempts to be proactive in finding potential
issues when setting roles and skipping them if there are issues. This
had been causing issues with rate limiting due to having an enormous
number of failed requests.
This commit is contained in:
Noikoio 2019-06-07 16:46:12 -07:00
parent 78beb1897f
commit 60fa8f40ec
2 changed files with 71 additions and 28 deletions

View file

@ -120,29 +120,56 @@ Class BackgroundWorker
Dim birthdays = BirthdayCalculate(users, tz) Dim birthdays = BirthdayCalculate(users, tz)
' Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply. ' Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply.
' Set birthday role, get list of users now having birthdays ' 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) Dim announceNames As IEnumerable(Of SocketGuildUser)
If BirthdayHasGoodRolePermissions(guild, role) Then
Try Try
announceNames = Await BirthdayApplyAsync(guild, role, birthdays) announceNames = Await BirthdayApplyAsync(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
Else
Throw
End If
End Try
Else
announceNames = Nothing
End If
If announceNames Is Nothing Then
SyncLock _bot.KnownGuilds SyncLock _bot.KnownGuilds
' Failed to apply role. Set the warning. ' Nothing on announceNAmes signals failure to apply roles. Set the warning message.
_bot.KnownGuilds(guild.Id).RoleWarning = True _bot.KnownGuilds(guild.Id).RoleWarning = True
End SyncLock End SyncLock
Return 0 Return 0
End If End If
Throw
End Try
If announceNames.Count <> 0 Then If announceNames.Count <> 0 Then
' Send out announcement message ' Send out announcement message
Await BirthdayAnnounceAsync(announce, announceping, channel, announceNames) Await BirthdayAnnounceAsync(announce, announceping, channel, announceNames)
End If End If
Return announceNames.Count Return announceNames.Count
End Function End Function
''' <summary>
''' Checks if the bot may be allowed to alter roles.
''' </summary>
Private Function BirthdayHasGoodRolePermissions(guild As SocketGuild, role As SocketRole) As Boolean
If Not guild.CurrentUser.GuildPermissions.ManageRoles Then
' Bot user cannot manage roles
Return False
End If
' Check potential role order conflict
If role.Position >= guild.CurrentUser.Hierarchy Then
' Target role is at or above bot's highest role.
Return False
End If
Return True
End Function
''' <summary> ''' <summary>
''' Gets all known users from the given guild and returns a list including only those who are ''' Gets all known users from the given guild and returns a list including only those who are
''' currently experiencing a birthday in the respective time zone. ''' currently experiencing a birthday in the respective time zone.
@ -187,21 +214,37 @@ Class BackgroundWorker
''' Sets the birthday role to all applicable users. Unsets it from all others who may have it. ''' Sets the birthday role to all applicable users. Unsets it from all others who may have it.
''' </summary> ''' </summary>
''' <returns>A list of users who had the birthday role applied. Use for the announcement message.</returns> ''' <returns>A list of users who had the birthday role applied. Use for the announcement message.</returns>
Private Async Function BirthdayApplyAsync(g As SocketGuild, r As SocketRole, names As HashSet(Of ULong)) As Task(Of IEnumerable(Of SocketGuildUser)) Private Async Function BirthdayApplyAsync(g As SocketGuild,
If Not g.HasAllMembers Then Await g.DownloadUsersAsync() r As SocketRole,
Dim newBirthdays As New List(Of SocketGuildUser) names As HashSet(Of ULong)) As Task(Of IEnumerable(Of SocketGuildUser))
For Each user In g.Users ' Check members currently with the role. Figure out which users to remove it from.
If names.Contains(user.Id) Then Dim roleRemoves As New List(Of SocketGuildUser)
' User's in the list. Should have the role. Add and make note of if user does not. Dim roleKeeps As New HashSet(Of ULong)
If Not user.Roles.Contains(r) Then Dim q = 0
Await user.AddRoleAsync(r) For Each member In r.Members
newBirthdays.Add(user) If Not names.Contains(member.Id) Then
End If roleRemoves.Add(member)
Else Else
' User's not in the list. Should remove the role. roleKeeps.Add(member.Id)
If user.Roles.Contains(r) Then Await user.RemoveRoleAsync(r)
End If End If
q += 1
Next Next
' TODO Can we remove during the iteration instead of after? investigate later...
For Each user In roleRemoves
Await user.RemoveRoleAsync(r)
Next
' Apply role to members not already having it. Prepare announcement list.
Dim newBirthdays As New List(Of SocketGuildUser)
For Each target In names
Dim member = g.GetUser(target)
If member Is Nothing Then Continue For
If roleKeeps.Contains(member.Id) Then Continue For ' already has role - do nothing
Await member.AddRoleAsync(r)
newBirthdays.Add(member)
Next
Return newBirthdays Return newBirthdays
End Function End Function

View file

@ -4,8 +4,8 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>BirthdayBot</RootNamespace> <RootNamespace>BirthdayBot</RootNamespace>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.0.0</Version> <Version>1.0.4</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion> <AssemblyVersion>1.0.4.0</AssemblyVersion>
<Authors>Noiiko</Authors> <Authors>Noiiko</Authors>
<Company /> <Company />
<Description>Discord bot for birthday reminders.</Description> <Description>Discord bot for birthday reminders.</Description>