mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-21 21:54:36 +00:00
Added bot moderator role option
This commit is contained in:
parent
11ceadfb36
commit
2c8a283c5b
5 changed files with 130 additions and 65 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue