1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . ComponentModel ;
4+ using System . Runtime . CompilerServices ;
5+ using System . Threading . Tasks ;
6+ using LlamaLibrary . Helpers ;
7+
8+ namespace LlamaLibrary . Helpers . CharacterSwitching ;
9+
10+ public abstract class CharacterTask
11+ {
12+ private bool _isRunning ;
13+ private DateTime ? _lastRun ;
14+ private TimeSpan _lastRunDuration ;
15+ private CharacterTaskResultStatus ? _lastRunStatus ;
16+ private string _lastRunStatusMessage = string . Empty ;
17+ private bool _lastRunWasSuccessful ;
18+
19+ /// <summary>
20+ /// Gets the display name of the task.
21+ /// </summary>
22+ public abstract string Name { get ; }
23+
24+ /// <summary>
25+ /// Gets the user-facing description of the task.
26+ /// </summary>
27+ public abstract string Description { get ; }
28+
29+ /// <summary>
30+ /// Gets the name of the botbase that provides this task.
31+ /// </summary>
32+ public abstract string ProvidingBotbaseName { get ; }
33+
34+ /// <summary>
35+ /// Gets a value indicating whether this task is currently running.
36+ /// </summary>
37+ public bool IsRunning
38+ {
39+ get => _isRunning ;
40+ private set => SetField ( ref _isRunning , value ) ;
41+ }
42+
43+ /// <summary>
44+ /// Gets a value indicating whether the last run completed successfully.
45+ /// </summary>
46+ public bool LastRunWasSuccessful
47+ {
48+ get => _lastRunWasSuccessful ;
49+ private set => SetField ( ref _lastRunWasSuccessful , value ) ;
50+ }
51+
52+ /// <summary>
53+ /// Gets the local time when the task last finished running.
54+ /// </summary>
55+ public DateTime ? LastRun
56+ {
57+ get => _lastRun ;
58+ private set => SetField ( ref _lastRun , value ) ;
59+ }
60+
61+ /// <summary>
62+ /// Gets the status for the last run.
63+ /// </summary>
64+ public CharacterTaskResultStatus ? LastRunStatus
65+ {
66+ get => _lastRunStatus ;
67+ private set => SetField ( ref _lastRunStatus , value ) ;
68+ }
69+
70+ /// <summary>
71+ /// Gets the user-facing message from the last run.
72+ /// </summary>
73+ public string LastRunStatusMessage
74+ {
75+ get => _lastRunStatusMessage ;
76+ private set => SetField ( ref _lastRunStatusMessage , value ) ;
77+ }
78+
79+ /// <summary>
80+ /// Gets the duration of the last completed run.
81+ /// </summary>
82+ public TimeSpan LastRunDuration
83+ {
84+ get => _lastRunDuration ;
85+ private set => SetField ( ref _lastRunDuration , value ) ;
86+ }
87+
88+ /// <summary>
89+ /// Checks whether the task can run right now.
90+ /// </summary>
91+ /// <returns>
92+ /// A tuple where <c>canRun</c> indicates availability and <c>reason</c> explains unavailability when false.
93+ /// </returns>
94+ public abstract Task < ( bool canRun , string reason ) > CheckAvailabilityAsync ( ) ;
95+
96+ /// <summary>
97+ /// Runs the task-specific implementation.
98+ /// </summary>
99+ protected abstract Task < CharacterTaskResult > ExecuteAsync ( ) ;
100+
101+ /// <summary>
102+ /// Runs the full task flow: availability check, execution, and status tracking.
103+ /// </summary>
104+ /// <returns>The final run result.</returns>
105+ public virtual async Task < CharacterTaskResult > RunAsync ( )
106+ {
107+ IsRunning = true ;
108+ var startTime = DateTime . Now ;
109+
110+ try
111+ {
112+ var ( canRun , reason ) = await CheckAvailabilityAsync ( ) ;
113+ if ( ! canRun )
114+ {
115+ var statusMessage = string . IsNullOrWhiteSpace ( reason )
116+ ? Translator . GetText ( TranslationKey . CharacterTaskUnavailable )
117+ : reason ;
118+
119+ var unavailableResult = new CharacterTaskResult ( false , CharacterTaskResultStatus . FailedUnavailable , statusMessage ) ;
120+ LastRunWasSuccessful = unavailableResult . WasSuccessful ;
121+ LastRunStatus = unavailableResult . Status ;
122+ LastRunStatusMessage = unavailableResult . Message ;
123+ return unavailableResult ;
124+ }
125+
126+ var result = await ExecuteAsync ( ) ;
127+ var endTime = DateTime . Now ;
128+
129+ LastRun = endTime ;
130+ LastRunDuration = endTime - startTime ;
131+ LastRunWasSuccessful = result . WasSuccessful ;
132+ LastRunStatus = result . Status ;
133+ LastRunStatusMessage = result . Message ;
134+
135+ return result ;
136+ }
137+ catch ( Exception ex )
138+ {
139+ ff14bot . Helpers . Logging . WriteException ( ex ) ;
140+
141+ var endTime = DateTime . Now ;
142+ var statusMessage = Translator . GetText ( TranslationKey . CharacterTaskGenericError ) ;
143+ var exceptionResult = new CharacterTaskResult ( false , CharacterTaskResultStatus . FailedException , statusMessage ) ;
144+
145+ LastRun = endTime ;
146+ LastRunDuration = endTime - startTime ;
147+ LastRunWasSuccessful = false ;
148+ LastRunStatus = exceptionResult . Status ;
149+ LastRunStatusMessage = exceptionResult . Message ;
150+
151+ return exceptionResult ;
152+ }
153+ finally
154+ {
155+ IsRunning = false ;
156+ }
157+ }
158+
159+ public event PropertyChangedEventHandler ? PropertyChanged ;
160+
161+ protected virtual void OnPropertyChanged ( [ CallerMemberName ] string ? propertyName = null )
162+ {
163+ PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( propertyName ) ) ;
164+ }
165+
166+ protected bool SetField < T > ( ref T field , T value , [ CallerMemberName ] string ? propertyName = null )
167+ {
168+ if ( EqualityComparer < T > . Default . Equals ( field , value ) )
169+ {
170+ return false ;
171+ }
172+
173+ field = value ;
174+ OnPropertyChanged ( propertyName ) ;
175+ return true ;
176+ }
177+ }
0 commit comments