Skip to content

Commit c25fdf5

Browse files
committed
feat(character-task): implement character task framework
Add an abstract class for character tasks, including properties for task status, execution tracking, and availability checks. Introduce a result class to encapsulate task outcomes and an enum for result statuses.
1 parent 1953bb2 commit c25fdf5

3 files changed

Lines changed: 213 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
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+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace LlamaLibrary.Helpers.CharacterSwitching;
2+
3+
/// <summary>
4+
/// Represents the result of a character task run.
5+
/// </summary>
6+
public sealed class CharacterTaskResult
7+
{
8+
public CharacterTaskResult(bool wasSuccessful, CharacterTaskResultStatus status, string message)
9+
{
10+
WasSuccessful = wasSuccessful;
11+
Status = status;
12+
Message = message;
13+
}
14+
15+
public bool WasSuccessful { get; }
16+
17+
public CharacterTaskResultStatus Status { get; }
18+
19+
public string Message { get; }
20+
}
21+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace LlamaLibrary.Helpers.CharacterSwitching;
2+
3+
/// <summary>
4+
/// Represents the outcome category for a character task run.
5+
/// </summary>
6+
public enum CharacterTaskResultStatus
7+
{
8+
Success,
9+
FailedCannotContinue,
10+
FailedUnavailable,
11+
FailedNavigation,
12+
FailedOther,
13+
FailedException
14+
}
15+

0 commit comments

Comments
 (0)