mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-24 17:34:13 +00:00
Added operation diagnostic data
To assist with troubleshooting
This commit is contained in:
parent
b31813c210
commit
3fc13efc57
9 changed files with 253 additions and 46 deletions
|
@ -18,7 +18,7 @@ Class BirthdayRoleUpdate
|
||||||
''' Does processing on all available guilds at once.
|
''' Does processing on all available guilds at once.
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Public Overrides Async Function OnTick() As Task
|
Public Overrides Async Function OnTick() As Task
|
||||||
Dim tasks As New List(Of Task(Of Integer))
|
Dim tasks As New List(Of Task)
|
||||||
For Each guild In BotInstance.DiscordClient.Guilds
|
For Each guild In BotInstance.DiscordClient.Guilds
|
||||||
Dim t = ProcessGuildAsync(guild)
|
Dim t = ProcessGuildAsync(guild)
|
||||||
tasks.Add(t)
|
tasks.Add(t)
|
||||||
|
@ -32,6 +32,7 @@ Class BirthdayRoleUpdate
|
||||||
Select task.Exception
|
Select task.Exception
|
||||||
Log($"Encountered {exs.Count} errors during bulk guild processing.")
|
Log($"Encountered {exs.Count} errors during bulk guild processing.")
|
||||||
For Each iex In exs
|
For Each iex In exs
|
||||||
|
' TODO probably not a good idea
|
||||||
Log(iex.ToString())
|
Log(iex.ToString())
|
||||||
Next
|
Next
|
||||||
End Try
|
End Try
|
||||||
|
@ -53,7 +54,10 @@ Class BirthdayRoleUpdate
|
||||||
' TODO metrics for role sets, unsets, announcements - and I mentioned this above too
|
' TODO metrics for role sets, unsets, announcements - and I mentioned this above too
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Async Function ProcessGuildAsync(guild As SocketGuild) As Task(Of Integer)
|
''' <summary>
|
||||||
|
''' Main function where actual guild processing occurs.
|
||||||
|
''' </summary>
|
||||||
|
Private Async Function ProcessGuildAsync(guild As SocketGuild) As Task
|
||||||
' Gather required information
|
' Gather required information
|
||||||
Dim tz As String
|
Dim tz As String
|
||||||
Dim users As IEnumerable(Of GuildUserSettings)
|
Dim users As IEnumerable(Of GuildUserSettings)
|
||||||
|
@ -61,15 +65,19 @@ 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
|
||||||
|
Dim op As OperationStatus
|
||||||
|
|
||||||
If Not BotInstance.GuildCache.ContainsKey(guild.Id) Then Return 0 ' guild not yet fully loaded; skip processing
|
' Skip processing of guild if local info has not yet been loaded
|
||||||
|
If Not BotInstance.GuildCache.ContainsKey(guild.Id) Then Return
|
||||||
|
|
||||||
|
' Lock once to grab all info
|
||||||
Dim gs = BotInstance.GuildCache(guild.Id)
|
Dim gs = BotInstance.GuildCache(guild.Id)
|
||||||
With gs
|
With gs
|
||||||
tz = .TimeZone
|
tz = .TimeZone
|
||||||
users = .Users
|
users = .Users
|
||||||
announce = .AnnounceMessages
|
announce = .AnnounceMessages
|
||||||
announceping = .AnnouncePing
|
announceping = .AnnouncePing
|
||||||
|
op = .OperationLog
|
||||||
|
|
||||||
If .AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value)
|
If .AnnounceChannelId.HasValue Then channel = guild.GetTextChannel(gs.AnnounceChannelId.Value)
|
||||||
If .RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
|
If .RoleId.HasValue Then role = guild.GetRole(gs.RoleId.Value)
|
||||||
|
@ -81,55 +89,57 @@ Class BirthdayRoleUpdate
|
||||||
|
|
||||||
' Set birthday roles, get list of users that had the role added
|
' 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.
|
' But first check if we are able to do so. Letting all requests fail instead will lead to rate limiting.
|
||||||
Dim correctRoleSettings = HasCorrectRoleSettings(guild, role)
|
Dim roleCheck = CheckCorrectRoleSettings(guild, role)
|
||||||
Dim gotForbidden = False
|
If Not roleCheck.Item1 Then
|
||||||
|
SyncLock op
|
||||||
Dim announceNames As IEnumerable(Of SocketGuildUser) = Nothing
|
op(OperationType.BirthdayRole) = New OperationInfo(New Exception(roleCheck.Item2))
|
||||||
If correctRoleSettings Then
|
op(OperationType.BirthdayAnnounce) = Nothing
|
||||||
Try
|
End SyncLock
|
||||||
announceNames = Await UpdateGuildBirthdayRoles(guild, role, birthdays)
|
Return
|
||||||
Catch ex As Discord.Net.HttpException
|
|
||||||
If ex.HttpCode = HttpStatusCode.Forbidden Then
|
|
||||||
gotForbidden = True
|
|
||||||
Else
|
|
||||||
Throw
|
|
||||||
End If
|
|
||||||
End Try
|
|
||||||
End If
|
End If
|
||||||
|
|
||||||
' Update warning flag
|
Dim announcementList As IEnumerable(Of SocketGuildUser)
|
||||||
Dim updateError = Not correctRoleSettings Or gotForbidden
|
' Do actual role updating
|
||||||
' Quit now if the warning flag was set. Announcement data is not available.
|
Try
|
||||||
If updateError Then Return 0
|
announcementList = Await UpdateGuildBirthdayRoles(guild, role, birthdays)
|
||||||
|
SyncLock op
|
||||||
|
op(OperationType.BirthdayRole) = New OperationInfo()
|
||||||
|
End SyncLock
|
||||||
|
Catch ex As Discord.Net.HttpException
|
||||||
|
SyncLock op
|
||||||
|
op(OperationType.BirthdayRole) = New OperationInfo(ex)
|
||||||
|
op(OperationType.BirthdayAnnounce) = Nothing
|
||||||
|
End SyncLock
|
||||||
|
If ex.HttpCode <> HttpStatusCode.Forbidden Then
|
||||||
|
' Send unusual exceptions to calling method
|
||||||
|
Throw
|
||||||
|
End If
|
||||||
|
Return
|
||||||
|
End Try
|
||||||
|
|
||||||
If announceNames.Count <> 0 Then
|
If announcementList.Count <> 0 Then
|
||||||
' Send out announcement message
|
Await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList, op)
|
||||||
Await AnnounceBirthdaysAsync(announce, announceping, channel, announceNames)
|
|
||||||
End If
|
End If
|
||||||
Return announceNames.Count
|
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
''' <summary>
|
''' <summary>
|
||||||
''' Checks if the bot may be allowed to alter roles.
|
''' Checks if the bot may be allowed to alter roles.
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Private Function HasCorrectRoleSettings(guild As SocketGuild, role As SocketRole) As Boolean
|
Private Function CheckCorrectRoleSettings(guild As SocketGuild, role As SocketRole) As (Boolean, String)
|
||||||
If role Is Nothing Then
|
If role Is Nothing Then
|
||||||
' Designated role not found or defined in guild
|
Return (False, "Designated role not found or defined in guild")
|
||||||
Return False
|
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If Not guild.CurrentUser.GuildPermissions.ManageRoles Then
|
If Not guild.CurrentUser.GuildPermissions.ManageRoles Then
|
||||||
' Bot user cannot manage roles
|
Return (False, "Bot does not contain Manage Roles permission")
|
||||||
Return False
|
|
||||||
End If
|
End If
|
||||||
|
|
||||||
' Check potential role order conflict
|
' Check potential role order conflict
|
||||||
If role.Position >= guild.CurrentUser.Hierarchy Then
|
If role.Position >= guild.CurrentUser.Hierarchy Then
|
||||||
' Target role is at or above bot's highest role.
|
Return (False, "Targeted role is at or above bot's highest rank")
|
||||||
Return False
|
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Return True
|
Return (True, "Success")
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
''' <summary>
|
''' <summary>
|
||||||
|
@ -219,10 +229,16 @@ Class BirthdayRoleUpdate
|
||||||
''' who have just had their birthday role added.
|
''' who have just had their birthday role added.
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Private Async Function AnnounceBirthdaysAsync(announce As (String, String),
|
Private Async Function AnnounceBirthdaysAsync(announce As (String, String),
|
||||||
announcePing As Boolean,
|
announcePing As Boolean,
|
||||||
c As SocketTextChannel,
|
c As SocketTextChannel,
|
||||||
names As IEnumerable(Of SocketGuildUser)) As Task
|
names As IEnumerable(Of SocketGuildUser),
|
||||||
If c Is Nothing Then Return
|
op As OperationStatus) As Task
|
||||||
|
If c Is Nothing Then
|
||||||
|
SyncLock op
|
||||||
|
op(OperationType.BirthdayAnnounce) = New OperationInfo("Announcement channel missing or undefined")
|
||||||
|
End SyncLock
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
Dim announceMsg As String
|
Dim announceMsg As String
|
||||||
If names.Count = 1 Then
|
If names.Count = 1 Then
|
||||||
|
@ -252,9 +268,13 @@ Class BirthdayRoleUpdate
|
||||||
|
|
||||||
Try
|
Try
|
||||||
Await c.SendMessageAsync(announceMsg.Replace("%n", namedisplay.ToString()))
|
Await c.SendMessageAsync(announceMsg.Replace("%n", namedisplay.ToString()))
|
||||||
|
SyncLock op
|
||||||
|
op(OperationType.BirthdayAnnounce) = New OperationInfo()
|
||||||
|
End SyncLock
|
||||||
Catch ex As Discord.Net.HttpException
|
Catch ex As Discord.Net.HttpException
|
||||||
' Ignore
|
SyncLock op
|
||||||
' TODO keep tabs on this somehow for troubleshooting purposes
|
op(OperationType.BirthdayAnnounce) = New OperationInfo(ex)
|
||||||
|
End SyncLock
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
End Class
|
End Class
|
||||||
|
|
|
@ -10,6 +10,7 @@ Class BirthdayBot
|
||||||
Private ReadOnly _cmdsListing As ListingCommands
|
Private ReadOnly _cmdsListing As ListingCommands
|
||||||
Private ReadOnly _cmdsHelp As HelpInfoCommands
|
Private ReadOnly _cmdsHelp As HelpInfoCommands
|
||||||
Private ReadOnly _cmdsMods As ManagerCommands
|
Private ReadOnly _cmdsMods As ManagerCommands
|
||||||
|
Private ReadOnly _cmdsDiag As DiagnosticCommands
|
||||||
|
|
||||||
Private WithEvents Client As DiscordShardedClient
|
Private WithEvents Client As DiscordShardedClient
|
||||||
Private ReadOnly _worker As BackgroundServiceRunner
|
Private ReadOnly _worker As BackgroundServiceRunner
|
||||||
|
@ -49,15 +50,21 @@ Class BirthdayBot
|
||||||
For Each item In _cmdsMods.Commands
|
For Each item In _cmdsMods.Commands
|
||||||
_dispatchCommands.Add(item.Item1, item.Item2)
|
_dispatchCommands.Add(item.Item1, item.Item2)
|
||||||
Next
|
Next
|
||||||
|
_cmdsDiag = New DiagnosticCommands(Me, conf)
|
||||||
|
For Each item In _cmdsDiag.Commands
|
||||||
|
_dispatchCommands.Add(item.Item1, item.Item2)
|
||||||
|
Next
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Public Async Function Start() As Task
|
Public Async Function Start() As Task
|
||||||
Await Client.LoginAsync(TokenType.Bot, Config.BotToken)
|
Await Client.LoginAsync(TokenType.Bot, Config.BotToken)
|
||||||
Await Client.StartAsync()
|
Await Client.StartAsync()
|
||||||
|
|
||||||
|
#If Not DEBUG Then
|
||||||
Log("Background processing", "Delaying start")
|
Log("Background processing", "Delaying start")
|
||||||
Await Task.Delay(90000) ' TODO don't keep doing this
|
Await Task.Delay(90000) ' TODO don't keep doing this
|
||||||
Log("Background processing", "Delay complete")
|
Log("Background processing", "Delay complete")
|
||||||
|
#End If
|
||||||
_worker.Start()
|
_worker.Start()
|
||||||
|
|
||||||
Await Task.Delay(-1)
|
Await Task.Delay(-1)
|
||||||
|
@ -85,8 +92,8 @@ Class BirthdayBot
|
||||||
Return Task.CompletedTask
|
Return Task.CompletedTask
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Async Function SetStatus() As Task Handles Client.ShardConnected
|
Private Async Function SetStatus(shard As DiscordSocketClient) As Task Handles Client.ShardConnected
|
||||||
Await Client.SetGameAsync(CommandPrefix + "help")
|
Await shard.SetGameAsync(CommandPrefix + "help")
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Async Function Dispatch(msg As SocketMessage) As Task Handles Client.MessageReceived
|
Private Async Function Dispatch(msg As SocketMessage) As Task Handles Client.MessageReceived
|
||||||
|
|
|
@ -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.3.4</Version>
|
<Version>1.3.6</Version>
|
||||||
<Authors>Noi</Authors>
|
<Authors>Noi</Authors>
|
||||||
<Company />
|
<Company />
|
||||||
<Description>Discord bot for birthday reminders.</Description>
|
<Description>Discord bot for birthday reminders.</Description>
|
||||||
|
|
|
@ -7,6 +7,8 @@ Imports System.IO
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Class Configuration
|
Class Configuration
|
||||||
Public ReadOnly Property BotToken As String
|
Public ReadOnly Property BotToken As String
|
||||||
|
Public ReadOnly Property LogWebhook As String
|
||||||
|
Public ReadOnly Property DiagnosticChannel As ULong
|
||||||
Public ReadOnly Property DBotsToken As String
|
Public ReadOnly Property DBotsToken As String
|
||||||
Public ReadOnly Property DatabaseSettings As Database
|
Public ReadOnly Property DatabaseSettings As Database
|
||||||
|
|
||||||
|
@ -23,11 +25,19 @@ Class Configuration
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Dim jc = JObject.Parse(File.ReadAllText(confPath))
|
Dim jc = JObject.Parse(File.ReadAllText(confPath))
|
||||||
BotToken = jc("BotToken").Value(Of String)()
|
|
||||||
|
BotToken = jc("BotToken")?.Value(Of String)()
|
||||||
If String.IsNullOrWhiteSpace(BotToken) Then
|
If String.IsNullOrWhiteSpace(BotToken) Then
|
||||||
Throw New Exception("'BotToken' must be specified.")
|
Throw New Exception("'BotToken' must be specified.")
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
LogWebhook = jc("LogWebhook")?.Value(Of String)()
|
||||||
|
If String.IsNullOrWhiteSpace(LogWebhook) Then
|
||||||
|
Throw New Exception("'LogWebhook' must be specified.")
|
||||||
|
End If
|
||||||
|
|
||||||
|
DiagnosticChannel = jc("DiagnosticChannel").Value(Of ULong)()
|
||||||
|
|
||||||
Dim dbj = jc("DBotsToken")
|
Dim dbj = jc("DBotsToken")
|
||||||
If dbj IsNot Nothing Then
|
If dbj IsNot Nothing Then
|
||||||
DBotsToken = dbj.Value(Of String)()
|
DBotsToken = dbj.Value(Of String)()
|
||||||
|
@ -35,7 +45,7 @@ Class Configuration
|
||||||
DBotsToken = Nothing
|
DBotsToken = Nothing
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Dim sqlcs = jc("SqlConnectionString").Value(Of String)()
|
Dim sqlcs = jc("SqlConnectionString")?.Value(Of String)()
|
||||||
If String.IsNullOrWhiteSpace(sqlcs) Then
|
If String.IsNullOrWhiteSpace(sqlcs) Then
|
||||||
Throw New Exception("'SqlConnectionString' must be specified.")
|
Throw New Exception("'SqlConnectionString' must be specified.")
|
||||||
End If
|
End If
|
||||||
|
|
|
@ -7,7 +7,7 @@ Imports NpgsqlTypes
|
||||||
''' Holds various pieces of state information for a guild the bot is operating in.
|
''' Holds various pieces of state information for a guild the bot is operating in.
|
||||||
''' Includes, among other things, a copy of the guild's settings and a list of all known users with birthdays.
|
''' Includes, among other things, a copy of the guild's settings and a list of all known users with birthdays.
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Friend Class GuildStateInformation
|
Class GuildStateInformation
|
||||||
Public ReadOnly Property GuildId As ULong
|
Public ReadOnly Property GuildId As ULong
|
||||||
Private ReadOnly _db As Database
|
Private ReadOnly _db As Database
|
||||||
Private _bdayRole As ULong?
|
Private _bdayRole As ULong?
|
||||||
|
@ -19,9 +19,10 @@ Friend Class GuildStateInformation
|
||||||
Private _announceMsgPl As String
|
Private _announceMsgPl As String
|
||||||
Private _announcePing As Boolean
|
Private _announcePing As Boolean
|
||||||
Private ReadOnly _userCache As Dictionary(Of ULong, GuildUserSettings)
|
Private ReadOnly _userCache As Dictionary(Of ULong, GuildUserSettings)
|
||||||
|
Public ReadOnly Property OperationLog As OperationStatus
|
||||||
|
|
||||||
''' <summary>
|
''' <summary>
|
||||||
''' Gets a list of cached users. Use sparingly.
|
''' Gets a list of cached registered user information.
|
||||||
''' </summary>
|
''' </summary>
|
||||||
Public ReadOnly Property Users As IEnumerable(Of GuildUserSettings)
|
Public ReadOnly Property Users As IEnumerable(Of GuildUserSettings)
|
||||||
Get
|
Get
|
||||||
|
@ -115,6 +116,9 @@ Friend Class GuildStateInformation
|
||||||
' Called by LoadSettingsAsync. Double-check ordinals when changes are made.
|
' Called by LoadSettingsAsync. Double-check ordinals when changes are made.
|
||||||
Private Sub New(reader As DbDataReader, dbconfig As Database)
|
Private Sub New(reader As DbDataReader, dbconfig As Database)
|
||||||
_db = dbconfig
|
_db = dbconfig
|
||||||
|
|
||||||
|
OperationLog = New OperationStatus()
|
||||||
|
|
||||||
GuildId = CULng(reader.GetInt64(0))
|
GuildId = CULng(reader.GetInt64(0))
|
||||||
' Weird: if using a ternary operator with a ULong?, Nothing resolves to 0 despite Option Strict On.
|
' Weird: if using a ternary operator with a ULong?, Nothing resolves to 0 despite Option Strict On.
|
||||||
If Not reader.IsDBNull(1) Then
|
If Not reader.IsDBNull(1) Then
|
||||||
|
|
75
BirthdayBot/Diagnostics/DiagnosticCommands.vb
Normal file
75
BirthdayBot/Diagnostics/DiagnosticCommands.vb
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
Imports Discord.WebSocket
|
||||||
|
Imports Discord.Webhook
|
||||||
|
Imports System.Text
|
||||||
|
''' <summary>
|
||||||
|
''' Implements the command used by global bot moderators to get operation info for each guild.
|
||||||
|
''' </summary>
|
||||||
|
Class DiagnosticCommands
|
||||||
|
Inherits CommandsCommon
|
||||||
|
|
||||||
|
Private ReadOnly _webhook As DiscordWebhookClient
|
||||||
|
Private ReadOnly _diagChannel As ULong
|
||||||
|
|
||||||
|
Sub New(inst As BirthdayBot, db As Configuration)
|
||||||
|
MyBase.New(inst, db)
|
||||||
|
|
||||||
|
_webhook = New DiscordWebhookClient(db.LogWebhook)
|
||||||
|
_diagChannel = db.DiagnosticChannel
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Overrides ReadOnly Property Commands As IEnumerable(Of (String, CommandHandler))
|
||||||
|
Get
|
||||||
|
Return New List(Of (String, CommandHandler)) From {
|
||||||
|
("diag", AddressOf CmdDiag),
|
||||||
|
("checkme", AddressOf CmdCheckme)
|
||||||
|
}
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' Dumps all known guild information to the given webhook
|
||||||
|
Private Async Function CmdDiag(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
|
||||||
|
' Ignore if not in the correct channel
|
||||||
|
If reqChannel.Id <> _diagChannel Then Return
|
||||||
|
|
||||||
|
' Requires two parameters: (cmd) (guild id)
|
||||||
|
If param.Length <> 2 Then
|
||||||
|
Await reqChannel.SendMessageAsync(":x: Usage: (command) (guild ID)")
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim rgid As ULong
|
||||||
|
If Not ULong.TryParse(param(1), rgid) Then
|
||||||
|
Await reqChannel.SendMessageAsync(":x: Cannot parse numeric guild ID")
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim guild = Instance.DiscordClient.GetGuild(rgid)
|
||||||
|
If guild Is Nothing Then
|
||||||
|
Await reqChannel.SendMessageAsync(":x: Guild is not known to the bot")
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim gi = Instance.GuildCache(rgid)
|
||||||
|
If gi Is Nothing Then
|
||||||
|
Await reqChannel.SendMessageAsync(":x: Guild is known, but information is not available.")
|
||||||
|
End If
|
||||||
|
|
||||||
|
Await reqChannel.SendMessageAsync(":white_check_mark: Compiling info and sending to webhook.")
|
||||||
|
|
||||||
|
Dim report As New StringBuilder
|
||||||
|
report.AppendLine("=-=-=-=-GUILD INFORMATION-=-=-=-=")
|
||||||
|
' TODO dump config info
|
||||||
|
report.AppendLine($"{guild.Id}: {guild.Name}")
|
||||||
|
report.AppendLine($"User count: {guild.Users.Count}")
|
||||||
|
report.AppendLine("---")
|
||||||
|
SyncLock gi.OperationLog
|
||||||
|
report.Append(gi.OperationLog.GetDiagStrings())
|
||||||
|
End SyncLock
|
||||||
|
report.AppendLine("**Note**: Full stack traces for captured exceptions are printed to console.")
|
||||||
|
|
||||||
|
Await _webhook.SendMessageAsync(report.ToString())
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Async Function CmdCheckme(param As String(), reqChannel As SocketTextChannel, reqUser As SocketGuildUser) As Task
|
||||||
|
Await _webhook.SendMessageAsync($"{reqUser.Username}#{reqUser.Discriminator}: {reqChannel.Guild.Id} checkme")
|
||||||
|
End Function
|
||||||
|
End Class
|
37
BirthdayBot/Diagnostics/OperationInfo.vb
Normal file
37
BirthdayBot/Diagnostics/OperationInfo.vb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
''' <summary>
|
||||||
|
''' Information regarding a single type of operation.
|
||||||
|
''' </summary>
|
||||||
|
Class OperationInfo
|
||||||
|
''' <summary>
|
||||||
|
''' The time in which the respective operation was attempted.
|
||||||
|
''' </summary>
|
||||||
|
ReadOnly Property Timestamp As DateTimeOffset
|
||||||
|
''' <summary>
|
||||||
|
''' Any exception encountered during the respective operation.
|
||||||
|
''' </summary>
|
||||||
|
''' <returns>Nothing/null if the previous given operation was a success.</returns>
|
||||||
|
ReadOnly Property Exception As Exception
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Creates an instance containing a success status.
|
||||||
|
''' </summary>
|
||||||
|
Sub New()
|
||||||
|
Timestamp = DateTimeOffset.UtcNow
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Creates an instance containing a captured exception
|
||||||
|
''' </summary>
|
||||||
|
Sub New(ex As Exception)
|
||||||
|
Me.New()
|
||||||
|
Exception = ex
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Creates an instance containing a custom error message
|
||||||
|
''' </summary>
|
||||||
|
Sub New(message As String)
|
||||||
|
Me.New()
|
||||||
|
Exception = New Exception(message)
|
||||||
|
End Sub
|
||||||
|
End Class
|
49
BirthdayBot/Diagnostics/OperationStatus.vb
Normal file
49
BirthdayBot/Diagnostics/OperationStatus.vb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
Imports System.Text
|
||||||
|
''' <summary>
|
||||||
|
''' Holds information regarding previous operations done on a guild and their most recent success/error status.
|
||||||
|
''' </summary>
|
||||||
|
Class OperationStatus
|
||||||
|
Private ReadOnly _log As New Dictionary(Of OperationType, OperationInfo)
|
||||||
|
|
||||||
|
Default Public Property Item(otype As OperationType) As OperationInfo
|
||||||
|
Get
|
||||||
|
Dim o As OperationInfo = Nothing
|
||||||
|
If Not _log.TryGetValue(otype, o) Then
|
||||||
|
Return Nothing
|
||||||
|
End If
|
||||||
|
Return o
|
||||||
|
End Get
|
||||||
|
Set(value As OperationInfo)
|
||||||
|
If value Is Nothing Then
|
||||||
|
_log.Remove(otype)
|
||||||
|
Else
|
||||||
|
_log(otype) = value
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
|
||||||
|
''' <summary>
|
||||||
|
''' Prepares known information in a displayable format.
|
||||||
|
''' </summary>
|
||||||
|
Public Function GetDiagStrings() As String
|
||||||
|
Dim report As New StringBuilder
|
||||||
|
For Each otype As OperationType In [Enum].GetValues(GetType(OperationType))
|
||||||
|
Dim prefix = $"`{[Enum].GetName(GetType(OperationType), otype)}`: "
|
||||||
|
|
||||||
|
Dim info = Item(otype)
|
||||||
|
If info Is Nothing Then
|
||||||
|
report.AppendLine(prefix + "No data")
|
||||||
|
Continue For
|
||||||
|
End If
|
||||||
|
prefix += info.Timestamp.ToString("u") + " "
|
||||||
|
|
||||||
|
If info.Exception Is Nothing Then
|
||||||
|
report.AppendLine(prefix + "Success")
|
||||||
|
Else
|
||||||
|
Log("OperationStatus", prefix + info.Exception.ToString())
|
||||||
|
report.AppendLine(prefix + info.Exception.Message)
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
Return report.ToString()
|
||||||
|
End Function
|
||||||
|
End Class
|
5
BirthdayBot/Diagnostics/OperationType.vb
Normal file
5
BirthdayBot/Diagnostics/OperationType.vb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Enum OperationType
|
||||||
|
BirthdayRole
|
||||||
|
BirthdayAnnounce
|
||||||
|
CommandDispatch
|
||||||
|
End Enum
|
Loading…
Reference in a new issue