2024-09-21 00:13:18 +12:00
using System.Net.Http.Json ;
using System.Text ;
using EllieBot.Common.ModuleBehaviors ;
using System.Text.Json.Serialization ;
namespace EllieBot.Modules.Administration.Self ;
public sealed class ToastielabReleaseModel
{
[JsonPropertyName("tag_name")]
public required string TagName { get ; init ; }
}
public sealed class CheckForUpdatesService : IEService , IReadyExecutor
{
private readonly BotConfigService _bcs ;
private readonly IBotCredsProvider _bcp ;
private readonly IHttpClientFactory _httpFactory ;
private readonly DiscordSocketClient _client ;
private readonly IMessageSenderService _sender ;
2024-10-05 11:44:44 +13:00
private const string RELEASES_URL = "https://toastielab.dev/api/v1/repos/Emotions-stuff/elliebot/releases" ;
2024-09-21 00:13:18 +12:00
public CheckForUpdatesService (
BotConfigService bcs ,
IBotCredsProvider bcp ,
IHttpClientFactory httpFactory ,
DiscordSocketClient client ,
IMessageSenderService sender )
{
_bcs = bcs ;
_bcp = bcp ;
_httpFactory = httpFactory ;
_client = client ;
_sender = sender ;
}
public async Task OnReadyAsync ( )
{
if ( _client . ShardId ! = 0 )
return ;
using var timer = new PeriodicTimer ( TimeSpan . FromHours ( 1 ) ) ;
while ( await timer . WaitForNextTickAsync ( ) )
{
var conf = _bcs . Data ;
if ( ! conf . CheckForUpdates )
continue ;
try
{
using var http = _httpFactory . CreateClient ( ) ;
var toastielabRelease = ( await http . GetFromJsonAsync < ToastielabReleaseModel [ ] > ( RELEASES_URL ) )
? . FirstOrDefault ( ) ;
if ( toastielabRelease ? . TagName is null )
continue ;
var latest = toastielabRelease . TagName ;
var latestVersion = Version . Parse ( latest ) ;
var lastKnownVersion = GetLastKnownVersion ( ) ;
if ( lastKnownVersion is null )
{
UpdateLastKnownVersion ( latestVersion ) ;
continue ;
}
if ( latestVersion > lastKnownVersion )
{
UpdateLastKnownVersion ( latestVersion ) ;
// pull changelog
var changelog = await http . GetStringAsync ( "https://toastielab.dev/Emotions-stuff/elliebot/raw/branch/v5/CHANGELOG.md" ) ;
var thisVersionChangelog = GetVersionChangelog ( latestVersion , changelog ) ;
if ( string . IsNullOrWhiteSpace ( thisVersionChangelog ) )
{
Log . Warning ( "New version {BotVersion} was found but changelog is unavailable" ,
thisVersionChangelog ) ;
continue ;
}
var creds = _bcp . GetCreds ( ) ;
await creds . OwnerIds
. Select ( async x = >
{
var user = await _client . GetUserAsync ( x ) ;
if ( user is null )
return ;
var eb = _sender . CreateEmbed ( )
. WithOkColor ( )
. WithAuthor ( $"EllieBot v{latest} Released!" )
. WithTitle ( "Changelog" )
. WithUrl ( "https://toastielab.dev/Emotions-stuff/elliebot/src/branch/v5/CHANGELOG.md" )
. WithDescription ( thisVersionChangelog . TrimTo ( 4096 ) )
. WithFooter (
"You may disable these messages by typing '.conf bot checkforupdates false'" ) ;
await _sender . Response ( user ) . Embed ( eb ) . SendAsync ( ) ;
} )
. WhenAll ( ) ;
}
}
catch ( Exception ex )
{
Log . Error ( ex , "Error while checking for new bot release: {ErrorMessage}" , ex . Message ) ;
}
}
}
private string? GetVersionChangelog ( Version latestVersion , string changelog )
{
var clSpan = changelog . AsSpan ( ) ;
var sb = new StringBuilder ( ) ;
var started = false ;
foreach ( var line in clSpan . EnumerateLines ( ) )
{
// if we're at the current version, keep reading lines and adding to the output
if ( started )
{
// if we got to previous version, end
if ( line . StartsWith ( "## [" ) )
break ;
// if we're reading a new segment, reformat it to print it better to discord
if ( line . StartsWith ( "### " ) )
{
sb . AppendLine ( Format . Bold ( line . ToString ( ) ) ) ;
}
else
{
sb . AppendLine ( line . ToString ( ) ) ;
}
continue ;
}
if ( line . StartsWith ( $"## [{latestVersion.ToString()}]" ) )
{
started = true ;
continue ;
}
}
return sb . ToString ( ) ;
}
private const string LAST_KNOWN_VERSION_PATH = "data/last_known_version.txt" ;
private Version ? GetLastKnownVersion ( )
{
if ( ! File . Exists ( LAST_KNOWN_VERSION_PATH ) )
return null ;
return Version . TryParse ( File . ReadAllText ( LAST_KNOWN_VERSION_PATH ) , out var ver )
? ver
: null ;
}
private void UpdateLastKnownVersion ( Version version )
{
File . WriteAllText ( "data/last_known_version.txt" , version . ToString ( ) ) ;
}
}