Added bot moderator role option

This commit is contained in:
Noikoio 2019-05-03 20:34:04 -07:00
parent 11ceadfb36
commit 2c8a283c5b
5 changed files with 130 additions and 65 deletions

View file

@ -110,12 +110,11 @@ Class BirthdayBot
' Ban and role warning check
Dim roleWarning As Boolean
Dim isManager = author.GuildPermissions.ManageGuild
SyncLock KnownGuilds
Dim gi = KnownGuilds(channel.Guild.Id)
' Skip ban check if user is a manager
If Not isManager Then
If Not gi.IsUserModerator(author) Then
If gi.IsUserBlockedAsync(author.Id).GetAwaiter().GetResult() Then
Return
End If

View file

@ -4,8 +4,8 @@
<OutputType>Exe</OutputType>
<RootNamespace>BirthdayBot</RootNamespace>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>0.5.3</Version>
<AssemblyVersion>0.5.3.0</AssemblyVersion>
<Version>0.6.0</Version>
<AssemblyVersion>0.6.0.0</AssemblyVersion>
<Authors>Noikoio</Authors>
<Company />
<Description>Discord bot for birthday reminders.</Description>

View file

@ -1,4 +1,5 @@
Imports System.Data.Common
Imports Discord.WebSocket
Imports Npgsql
Imports NpgsqlTypes
@ -12,6 +13,7 @@ Friend Class GuildSettings
Private ReadOnly _db As Database
Private _bdayRole As ULong?
Private _announceCh As ULong?
Private _modRole As ULong?
Private _tz As String
Private _moderated As Boolean
Private _userCache As Dictionary(Of ULong, GuildUserSettings)
@ -84,8 +86,7 @@ Friend Class GuildSettings
End Property
''' <summary>
''' Gets or sets if the server is in moderated mode.
''' Updating this value updates the database.
''' Gets whether the guild is in moderated mode.
''' </summary>
Public ReadOnly Property IsModerated As Boolean
Get
@ -93,6 +94,15 @@ Friend Class GuildSettings
End Get
End Property
''' <summary>
''' Gets the designated moderator role ID.
''' </summary>
Public ReadOnly Property ModeratorRole As ULong?
Get
Return _modRole
End Get
End Property
' Called by LoadSettingsAsync. Double-check ordinals when changes are made.
Private Sub New(reader As DbDataReader, dbconfig As Database)
_db = dbconfig
@ -107,6 +117,7 @@ Friend Class GuildSettings
If Not reader.IsDBNull(2) Then _announceCh = CULng(reader.GetInt64(2))
_tz = If(reader.IsDBNull(3), Nothing, reader.GetString(3))
_moderated = reader.GetBoolean(4)
If Not reader.IsDBNull(5) Then _modRole = CULng(reader.GetInt64(5))
' Get user information loaded up.
Dim userresult = GuildUserSettings.GetGuildUsersAsync(dbconfig, GuildId)
@ -169,6 +180,19 @@ Friend Class GuildSettings
End Using
End Function
''' <summary>
''' Checks if the given user is a moderator either by having the Manage Server permission or
''' being in the designated modeartor role.
''' </summary>
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
IsUserModerator = False
End Function
''' <summary>
''' Adds the specified user to the block list, preventing them from issuing commands.
''' </summary>
@ -224,6 +248,11 @@ Friend Class GuildSettings
Await UpdateDatabaseAsync()
End Function
Public Async Function UpdateModeratorRoleAsync(roleId As ULong?) As Task
_modRole = roleId
Await UpdateDatabaseAsync()
End Function
#Region "Database"
Public Const BackingTable = "settings"
Public Const BackingTableBans = "banned_users"
@ -235,7 +264,8 @@ Friend Class GuildSettings
"role_id bigint null, " +
"channel_announce_id bigint null, " +
"time_zone text null, " +
"moderated boolean not null default FALSE" +
"moderated boolean not null default FALSE, " +
"moderator_role bigint null" +
")"
c.ExecuteNonQuery()
End Using
@ -257,7 +287,7 @@ Friend Class GuildSettings
Using db = Await dbsettings.OpenConnectionAsync()
Using c = db.CreateCommand()
' Take note of ordinals for use in the constructor
c.CommandText = "select guild_id, role_id, channel_announce_id, time_zone, moderated " +
c.CommandText = "select guild_id, role_id, channel_announce_id, time_zone, moderated, moderator_role " +
$"from {BackingTable} where guild_id = @Gid"
c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guild
c.Prepare()
@ -291,7 +321,8 @@ Friend Class GuildSettings
"role_id = @RoleId, " +
"channel_announce_id = @ChannelId, " +
"time_zone = @TimeZone, " +
"moderated = @Moderated " +
"moderated = @Moderated, " +
"moderator_role = @ModRole " +
"where guild_id = @Gid"
c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = GuildId
With c.Parameters.Add("@RoleId", NpgsqlDbType.Bigint)
@ -316,6 +347,13 @@ Friend Class GuildSettings
End If
End With
c.Parameters.Add("@Moderated", NpgsqlDbType.Boolean).Value = _moderated
With c.Parameters.Add("@ModRole", NpgsqlDbType.Bigint)
If ModeratorRole.HasValue Then
.Value = ModeratorRole.Value
Else
.Value = DBNull.Value
End If
End With
c.Prepare()
Await c.ExecuteNonQueryAsync()
End Using

View file

@ -43,13 +43,15 @@ Friend Class HelpInfoCommands
' Manager section
Dim mpfx = cpfx + "config "
Dim managerField As New EmbedFieldBuilder With {
.Name = "Commands for server managers",
Dim moderatorField As New EmbedFieldBuilder With {
.Name = "Commands for server managers and bot moderators",
.Value =
$"{mpfx}role (role name or ID)`" + vbLf +
" » Configures the role to apply to users having birthdays." + vbLf +
$"{mpfx}channel (channel name or ID)`" + vbLf +
" » Configures the channel to use for announcements. Leave blank to disable." + vbLf +
$"{mpfx}modrole (role name or ID)`" + vbLf +
" » Sets the designated role for bot moderators. Moderators can access `bb.config` and `bb.override`." + vbLf +
$"{mpfx}zone (time zone name)`" + vbLf +
" » Sets the default time zone for all dates that don't have their own zone set." + vbLf +
$" »» See `{CommandPrefix}help-tzdata`. Leave blank to set to UTC." + vbLf +
@ -65,28 +67,21 @@ Friend Class HelpInfoCommands
Dim helpNoManager As New EmbedBuilder
helpNoManager.AddField(cmdField)
Dim helpManager As New EmbedBuilder
helpManager.AddField(cmdField)
helpManager.AddField(managerField)
Dim helpModerator As New EmbedBuilder
helpModerator.AddField(cmdField)
helpModerator.AddField(moderatorField)
Return (helpNoManager.Build(), helpManager.Build())
Return (helpNoManager.Build(), helpModerator.Build())
End Function
Private Async Function CmdHelp(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' Determine if an additional message about an invalid role should be added.
Dim useFunctionMessage = False
Dim gs As GuildSettings
' Determine if the user asking is a moderator
Dim showManagerCommands As Boolean
SyncLock Instance.KnownGuilds
gs = Instance.KnownGuilds(reqChannel.Guild.Id)
showManagerCommands = Instance.KnownGuilds(reqChannel.Guild.Id).IsUserModerator(reqUser)
End SyncLock
If Not gs.RoleId.HasValue Then
useFunctionMessage = True
End If
' Determine if the user asking is a manager
Dim showManagerCommands = reqUser.GuildPermissions.ManageGuild
Await reqChannel.SendMessageAsync("", embed:=If(showManagerCommands, _helpEmbedManager, _helpEmbed))
Await reqChannel.SendMessageAsync(embed:=If(showManagerCommands, _helpEmbedManager, _helpEmbed))
End Function
Private Async Function CmdHelpTzdata(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task

View file

@ -22,6 +22,7 @@ Friend Class ManagerCommands
_subcommands = New Dictionary(Of String, ConfigSubcommand)(StringComparer.InvariantCultureIgnoreCase) From {
{"role", AddressOf ScmdRole},
{"channel", AddressOf ScmdChannel},
{"modrole", AddressOf ScmdModRole},
{"zone", AddressOf ScmdZone},
{"block", AddressOf ScmdBlock},
{"unblock", AddressOf ScmdBlock},
@ -36,9 +37,14 @@ Friend Class ManagerCommands
End Sub
Private Async Function CmdConfigDispatch(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' Managers only past this point.
If Not reqUser.GuildPermissions.ManageGuild Then
Await reqChannel.SendMessageAsync(":x: This command may only be used by those with the `Manage Server` permission.")
' 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
Await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.")
Return
End If
@ -47,6 +53,12 @@ Friend Class ManagerCommands
Return
End If
' Special case: Restrict 'modrole' to only guild managers
If param(1).Equals("modrole", StringComparison.OrdinalIgnoreCase) And Not reqUser.GuildPermissions.ManageGuild Then
Await reqChannel.SendMessageAsync(":x: This command may only be used by those with the `Manage Server` permission.")
Return
End If
' Subcommands get a subset of the parameters, to make things a little easier.
Dim confparam(param.Length - 2) As String ' subtract one extra???
Array.Copy(param, 1, confparam, 0, param.Length - 1)
@ -59,47 +71,17 @@ Friend Class ManagerCommands
End Function
#Region "Configuration sub-commands"
Private Shared ReadOnly RoleMention As New Regex("<@?&(?<snowflake>\d+)>", RegexOptions.Compiled)
' Birthday role set
Private Async Function ScmdRole(param As String(), reqChannel As SocketTextChannel) As Task
If param.Length <> 2 Then
Await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
Return
End If
Dim guild = reqChannel.Guild
Dim input = param(1)
Dim role As SocketRole = Nothing
Dim role = FindUserInputRole(param(1), guild)
' Resembles a role mention? Strip it to the pure number
Dim rmatch = RoleMention.Match(input)
If rmatch.Success Then
input = rmatch.Groups("snowflake").Value
End If
' Attempt to get role by ID
Dim rid As ULong
If ULong.TryParse(input, rid) Then
role = guild.GetRole(rid)
Else
' Reset the search value on the off chance there's a role name actually starting with "<&" and ending with ">"
input = param(1)
End If
' If not already found, attempt to search role by string name
If role Is Nothing Then
For Each search In guild.Roles
If String.Equals(search.Name, input, StringComparison.InvariantCultureIgnoreCase) Then
role = search
Exit For
End If
Next
End If
' Final result
If role Is Nothing Then
Await reqChannel.SendMessageAsync(":x: Unable to determine the given role.")
Await reqChannel.SendMessageAsync(RoleInputError)
Else
SyncLock Instance.KnownGuilds
Instance.KnownGuilds(guild.Id).UpdateRoleAsync(role.Id).Wait()
@ -152,6 +134,25 @@ Friend Class ManagerCommands
End If
End Function
' Moderator role set
Private Async Function ScmdModRole(param As String(), reqChannel As SocketTextChannel) As Task
If param.Length <> 2 Then
Await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
Return
End If
Dim guild = reqChannel.Guild
Dim role = FindUserInputRole(param(1), guild)
If role Is Nothing Then
Await reqChannel.SendMessageAsync(RoleInputError)
Else
SyncLock Instance.KnownGuilds
Instance.KnownGuilds(guild.Id).UpdateModeratorRoleAsync(role.Id).Wait()
End SyncLock
Await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.")
End If
End Function
' Guild default time zone set/unset
Private Async Function ScmdZone(param As String(), reqChannel As SocketTextChannel) As Task
If param.Length = 1 Then
@ -263,10 +264,10 @@ Friend Class ManagerCommands
' Execute command as another user
Private Async Function CmdOverride(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
' Managers only. Silently drop if the check fails.
If Not reqUser.GuildPermissions.ManageGuild Then
Return
End If
' 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 param.Length <> 3 Then
Await reqChannel.SendMessageAsync(GenericError)
@ -302,4 +303,36 @@ Friend Class ManagerCommands
Await reqChannel.SendMessageAsync($"Executing `{cmdsearch.ToLower()}` on behalf of {If(overuser.Nickname, overuser.Username)}:")
Await action.Invoke(overparam, reqChannel, overuser)
End Function
#Region "Common/helper methods"
Private Const RoleInputError = ":x: Unable to determine the given role."
Private Shared ReadOnly RoleMention As New Regex("<@?&(?<snowflake>\d+)>", RegexOptions.Compiled)
Private Function FindUserInputRole(inputStr As String, guild As SocketGuild) As SocketRole
' Resembles a role mention? Strip it to the pure number
Dim input = inputStr
Dim rmatch = RoleMention.Match(input)
If rmatch.Success Then
input = rmatch.Groups("snowflake").Value
End If
' Attempt to get role by ID, or Nothing
Dim rid As ULong
If ULong.TryParse(input, rid) Then
Return guild.GetRole(rid)
Else
' Reset the search value on the off chance there's a role name that actually resembles a role ping.
input = inputStr
End If
' If not already found, attempt to search role by string name
For Each search In guild.Roles
If String.Equals(search.Name, input, StringComparison.InvariantCultureIgnoreCase) Then
Return search
End If
Next
Return Nothing
End Function
#End Region
End Class