List CSV output; show warning message on disconnect

This commit is contained in:
Noikoio 2019-07-14 00:29:35 -07:00
parent fe5bdb53f9
commit f3e70f8459
4 changed files with 97 additions and 25 deletions

View file

@ -15,6 +15,12 @@ Class Heartbeat
Log($"Tick {tick:00000} - Bot uptime: {BotUptime()}") Log($"Tick {tick:00000} - Bot uptime: {BotUptime()}")
End If End If
If (BotInstance.DiscordClient.ConnectionState = Discord.ConnectionState.Disconnected) Then
Log("Client is disconnected! Restart the app if this persists.")
' The library alone cannot be restarted as it is in an unknown state. It was not designed to be restarted.
' This is the part where we'd signal something to restart us if we were fancy.
End If
Return Task.CompletedTask Return Task.CompletedTask
End Function End Function
End Class End Class

View file

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>BirthdayBot</RootNamespace> <RootNamespace>BirthdayBot</RootNamespace>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.1.0</Version> <Version>1.1.1</Version>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<Authors>Noiiko</Authors> <Authors>Noiiko</Authors>
<Company /> <Company />
<Description>Discord bot for birthday reminders.</Description> <Description>Discord bot for birthday reminders.</Description>
<StartupObject>Sub Main</StartupObject> <StartupObject>Sub Main</StartupObject>
<OptionStrict>On</OptionStrict> <OptionStrict>On</OptionStrict>
<ApplicationManifest>My Project\app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

View file

@ -53,7 +53,7 @@ Friend Class HelpInfoCommands
$"{cpfx}config`" + vbLf + $"{cpfx}config`" + vbLf +
$" » Edit bot configuration. See `{CommandPrefix}help-config`." + vbLf + $" » Edit bot configuration. See `{CommandPrefix}help-config`." + vbLf +
$"{cpfx}list`" + vbLf + $"{cpfx}list`" + vbLf +
$" » Exports all known birthdays to file." + vbLf + $" » Exports all birthdays to file. Accepts `csv` as a parameter." + vbLf +
$"{cpfx}override (user ping or ID) (command w/ parameters)`" + vbLf + $"{cpfx}override (user ping or ID) (command w/ parameters)`" + vbLf +
" » Perform certain commands on behalf of another user." " » Perform certain commands on behalf of another user."
} }

View file

@ -28,31 +28,37 @@ Class ListingCommands
SyncLock Instance.KnownGuilds SyncLock Instance.KnownGuilds
reqMod = Instance.KnownGuilds(reqChannel.Guild.Id).IsUserModerator(reqUser) reqMod = Instance.KnownGuilds(reqChannel.Guild.Id).IsUserModerator(reqUser)
End SyncLock End SyncLock
If Not reqMod Then If Not reqMod Then
Await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.") Await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.")
Return Return
End If End If
Dim bdlist = Await BuildList(reqChannel.Guild, False) Dim useCsv = False
' Check for CSV option
If param.Length = 2 Then
If (param(1).ToLower() = "csv") Then
useCsv = True
Else
Await reqChannel.SendMessageAsync(":x: That is not available as an export format.")
Return
End If
ElseIf param.Length > 2 Then
Await reqChannel.SendMessageAsync(GenericError)
Return
End If
Dim filepath = Path.GetTempPath() + "birthdaybot-" + reqChannel.Guild.Id.ToString() + ".txt" Dim bdlist = Await LoadList(reqChannel.Guild, False)
Using f = File.CreateText(filepath)
f.WriteLine("Birthdays in " + reqChannel.Guild.Name) Dim filepath = Path.GetTempPath() + "birthdaybot-" + reqChannel.Guild.Id.ToString()
f.WriteLine() Dim fileoutput As String
For Each item In bdlist If useCsv Then
Dim user = reqChannel.Guild.GetUser(item.UserId) fileoutput = ListExportCsv(reqChannel, bdlist)
If user Is Nothing Then Continue For ' User disappeared in the instant between getting list and processing filepath += ".csv"
f.Write($"● {MonthNames(item.BirthMonth)}-{item.BirthDay.ToString("00")}: ") Else
f.Write(item.UserId) fileoutput = ListExportNormal(reqChannel, bdlist)
f.Write(" " + user.Username + "#" + user.Discriminator) filepath += ".txt"
If user.Nickname IsNot Nothing Then End If
f.Write(" - Nickname: " + user.Nickname) Await File.WriteAllTextAsync(filepath, fileoutput, Encoding.UTF8)
End If
f.WriteLine()
Next
Await f.FlushAsync()
End Using
Try Try
Await reqChannel.SendFileAsync(filepath, $"Exported {bdlist.Count} birthdays to file.") Await reqChannel.SendFileAsync(filepath, $"Exported {bdlist.Count} birthdays to file.")
@ -73,7 +79,7 @@ Class ListingCommands
Dim search = DateIndex(now.Month, now.Day) - 4 ' begin search 4 days prior to current date UTC Dim search = DateIndex(now.Month, now.Day) - 4 ' begin search 4 days prior to current date UTC
If search <= 0 Then search = 366 - Math.Abs(search) If search <= 0 Then search = 366 - Math.Abs(search)
Dim query = Await BuildList(reqChannel.Guild, True) Dim query = Await LoadList(reqChannel.Guild, True)
If query.Count = 0 Then If query.Count = 0 Then
Await reqChannel.SendMessageAsync("There are currently no recent or upcoming birthdays.") Await reqChannel.SendMessageAsync("There are currently no recent or upcoming birthdays.")
Return Return
@ -119,7 +125,7 @@ Class ListingCommands
''' Fetches all guild birthdays and places them into an easily usable structure. ''' Fetches all guild birthdays and places them into an easily usable structure.
''' Users currently not in the guild are not included in the result. ''' Users currently not in the guild are not included in the result.
''' </summary> ''' </summary>
Private Async Function BuildList(guild As SocketGuild, escapeFormat As Boolean) As Task(Of List(Of ListItem)) Private Async Function LoadList(guild As SocketGuild, escapeFormat As Boolean) As Task(Of List(Of ListItem))
Dim ping As Boolean Dim ping As Boolean
SyncLock Instance.KnownGuilds SyncLock Instance.KnownGuilds
ping = Instance.KnownGuilds(guild.Id).AnnouncePing ping = Instance.KnownGuilds(guild.Id).AnnouncePing
@ -155,6 +161,66 @@ Class ListingCommands
End Using End Using
End Function End Function
Private Function ListExportNormal(channel As SocketGuildChannel, list As IEnumerable(Of ListItem)) As String
'Output: "● Mon-dd: (user ID) Username [ - Nickname: (nickname)]"
Dim result As New StringBuilder()
With result
.AppendLine("Birthdays in " + channel.Guild.Name)
.AppendLine()
For Each item In list
Dim user = channel.Guild.GetUser(item.UserId)
If user Is Nothing Then Continue For ' User disappeared in the instant between getting list and processing
.Append($"● {MonthNames(item.BirthMonth)}-{item.BirthDay.ToString("00")}: ")
.Append(item.UserId)
.Append(" " + user.Username + "#" + user.Discriminator)
If user.Nickname IsNot Nothing Then
.Append(" - Nickname: " + user.Nickname)
End If
.AppendLine()
Next
End With
Return result.ToString()
End Function
Private Function ListExportCsv(channel As SocketGuildChannel, list As IEnumerable(Of ListItem)) As String
' Output: User ID, Username, Nickname, Month-Day, Month, Day
Dim result As New StringBuilder()
With result
' Conforming to RFC 4180
' With header.
result.Append("UserID,Username,Nickname,MonthDayDisp,Month,Day")
result.Append(vbCrLf) ' crlf is specified by the standard
For Each item In list
Dim user = channel.Guild.GetUser(item.UserId)
If user Is Nothing Then Continue For ' User disappeared in the instant between getting list and processing
.Append(item.UserId)
.Append(",")
.Append(CsvEscape(user.Username + "#" + user.Discriminator))
.Append(",")
If user.Nickname IsNot Nothing Then .Append(user.Nickname)
.Append(",")
.Append($"{MonthNames(item.BirthMonth)}-{item.BirthDay.ToString("00")}")
.Append(",")
.Append(item.BirthMonth)
.Append(",")
.Append(item.BirthDay)
.Append(vbCrLf)
Next
End With
Return result.ToString()
End Function
Private Function CsvEscape(input As String) As String
Dim result As New StringBuilder
result.Append("""")
For Each ch In input
If ch = """"c Then result.Append(""""c)
result.Append(ch)
Next
result.Append("""")
Return result.ToString()
End Function
Private Function DateIndex(month As Integer, day As Integer) As Integer Private Function DateIndex(month As Integer, day As Integer) As Integer
DateIndex = 0 DateIndex = 0
' Add month offset ' Add month offset