-
-
Notifications
You must be signed in to change notification settings - Fork 4
Add expression toggle action and expression caching for Stream Deck plugin #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…lugin Implemented the ExpressionToggleAction to handle toggling VTube Studio expressions via Stream Deck, supported by a new ExpressionCache class. Added related API requests, events, and models to VTube Studio API integration, and updated the plugin to target .NET 9.0.
WalkthroughThis update introduces new features for managing VTube Studio expressions via a Stream Deck plugin. It adds classes for handling expression state and activation requests/responses, implements an expression cache, and creates a new Stream Deck action for toggling expressions. The update also includes UI components for expression selection, logging adjustments, and extends event handling in the websocket client. Minor configuration and project file updates are included. Changes
Sequence Diagram(s)sequenceDiagram
participant StreamDeck as Stream Deck
participant ExpressionToggleAction as ExpressionToggleAction
participant ExpressionCache as ExpressionCache
participant VTSClient as VTubeStudioWebsocketClient
participant VTubeStudio as VTube Studio
StreamDeck->>ExpressionToggleAction: Button Pressed
ExpressionToggleAction->>VTSClient: Send ExpressionActivationRequest
VTSClient->>VTubeStudio: WebSocket Request (activate/deactivate expression)
VTubeStudio-->>VTSClient: ExpressionActivationResponse
VTSClient-->>ExpressionCache: OnExpressionActivation event
ExpressionCache-->>ExpressionToggleAction: Update expression state
ExpressionToggleAction->>StreamDeck: Update key state
sequenceDiagram
participant PropertyInspector as PropertyInspector UI
participant StreamDeck as Stream Deck Plugin
participant ExpressionCache as ExpressionCache
PropertyInspector->>StreamDeck: Request expressions list
StreamDeck->>ExpressionCache: Get available expressions
ExpressionCache-->>StreamDeck: Return expressions list
StreamDeck-->>PropertyInspector: Provide expressions for dropdown
PropertyInspector->>StreamDeck: Save settings (expression, fade time)
Poem
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (13)
streamdeck-vtubestudio/nlog.config (1)
11-17: Properly closed the commented-out file logger target
The added closing comment delimiter (-->) on line 16 correctly matches the opening<!--on line 11, ensuring that the file logger<target>remains fully commented out.
Consider removing the trailing whitespace on line 17 to keep the XML tidy and prevent unnecessary diffs in the future.StreamdeckLib/StreamDeckConnection.cs (1)
99-103: Remove or clean up commented debug logs
The debug‐level log statements were commented out to reduce noise, but leaving them as commented code clutters the codebase. Consider either removing these lines entirely or guarding them behind a conditional compilation symbol (e.g.,#if DEBUG).Also applies to: 193-193
VTubeStudioAPI/Responses/ExpressionStateResponse.cs (1)
1-19: New ExpressionStateResponse class looks good.The class is well-structured with appropriate JSON property mappings and required markers for non-nullable properties. It correctly models the expression state response from VTube Studio.
However, I noticed the namespace is
VTubeStudioAPI.Responseswhile importing fromVTubeStudioAPI.Models. Consider standardizing namespace conventions across the project.streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/main.js (2)
6-23: Global StreamDeck connection function implemented.The function properly initializes the StreamDeck connection and sets up the Vue application with a Vuex store.
Consider adding error handling for the WebSocket connection and Vue initialization to improve robustness. Also, since mutations, actions, and modules are empty, you could simplify by omitting these empty objects.
window.connectElgatoStreamDeckSocket = function (inPort, inPluginUUID, inRegisterEvent, inInfo, inActionInfo) { let streamDeck = new StreamDeck(inPort, inPluginUUID, inRegisterEvent, inInfo, inActionInfo); console.log("Connected to stream deck") let store = createStore({ state: { streamDeck, }, - mutations: { - }, - actions: { - }, - modules: { - } }) - createApp(App).use(store).mount('#app') + try { + createApp(App).use(store).mount('#app') + } catch (error) { + console.error("Failed to initialize Vue application:", error) + } }
25-27: Remove unnecessary blank lines.For code cleanliness, consider removing the extra blank lines at the end of the file.
} - - -VTubeStudioAPI/VTubeStudioWebsocketClient.cs (1)
91-91: Remove duplicate assignment of _tryingToConnect variableThe same variable is being set to false twice consecutively.
Connect(); _tryingToConnect = false; - _tryingToConnect = false;streamdeck-vtubestudio/ExpressionCache.cs (1)
22-22: Unused WeakReference listThis static list of WeakReference is populated in the constructor and cleared in the finalizer, but it's never actually used elsewhere in the code.
Consider removing this if it's not serving a purpose, or add a comment explaining its intended future use.
streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/App.vue (3)
56-61: Consider adding error handling for event listenersThe event listeners don't include any error handling. If an error occurs while processing events, it could disrupt the component's functionality.
this.$store.state.streamDeck.on('didReceiveSettings', settings => console.log(settings)) - this.$store.state.streamDeck.on('connected', payload => this.settings = payload?.payload?.settings ?? this.settings) - this.$store.state.streamDeck.on('sendToPropertyInspector', e => { - this.expressions = e.expressions ?? []; - this.websocketConnected = e.connected ?? false - }) + this.$store.state.streamDeck.on('connected', payload => { + try { + this.settings = payload?.payload?.settings ?? this.settings; + } catch (error) { + console.error('Error processing connected event:', error); + } + }) + this.$store.state.streamDeck.on('sendToPropertyInspector', e => { + try { + this.expressions = e.expressions ?? []; + this.websocketConnected = e.connected ?? false; + } catch (error) { + console.error('Error processing sendToPropertyInspector event:', error); + } + })
66-68: Empty style sectionThe style section is empty and can be removed to improve code clarity.
-<style> - -</style>
10-10: Consider adding a label for the fade time sliderThe fade time slider doesn't have a clear label indicating what it controls, which might confuse users.
- <input type="range" min="0" max="5" step="0.1" v-model="settings.fadeTime"> + <div class="sdpi-item-label">Fade Time: {{ settings.fadeTime }}s</div> + <input type="range" min="0" max="5" step="0.1" v-model="settings.fadeTime" title="Fade Time">streamdeck-vtubestudio/Actions/ExpressionToggleAction.cs (3)
18-25: The Expression property has a hardcoded default value.While having defaults is good, the specific expression "Cat Tail.exp3.json" might not exist in all VTube Studio installations. Consider setting this to null by default and handling the null case, or documenting this as a required setup step.
[JsonProperty("expression")] -public string? Expression { get; set; } = "Cat Tail.exp3.json"; +public string? Expression { get; set; } = null;
48-58: Extract duplicate expression state logic to a helper method.The same logic to determine if an expression is active appears in both the Pressed and Tick methods. This should be extracted to a helper method to avoid duplication and improve maintainability.
+ private bool IsExpressionActive() => _expressionCache.Expressions.FirstOrDefault(e => e.File == Settings.Expression)?.Active ?? false; public override void Tick() { base.Tick(); - var activate = !_expressionCache.Expressions.FirstOrDefault(e => e.File == Settings.Expression)?.Active ?? false; + var activate = !IsExpressionActive(); Connection.SendMessage(new SetStateRequest { Payload = new ((uint) (activate ? 1 : 0)), Context = ContextId, }); } protected override void Pressed() { if (!_expressionCache.ModelLoaded) return; - var activate = !_expressionCache.Expressions.FirstOrDefault(e => e.File == Settings.Expression)?.Active ?? false; + var activate = !IsExpressionActive(); // rest of the method... }
65-71: Implement validation in SettingsUpdated.The SettingsUpdated method is empty, but it could be used to validate the new settings and handle any required UI updates when settings change.
protected override void SettingsUpdated(ExpressionTogglePayload oldSettings, ExpressionTogglePayload newSettings) { + // Validate the new expression exists + if (string.IsNullOrEmpty(newSettings.Expression) || + !_expressionCache.Expressions.Any(e => e.File == newSettings.Expression)) + { + Logger.LogWarning("Selected expression '{Expression}' does not exist", newSettings.Expression); + } + + // Update UI state if needed + Tick(); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
streamdeck-vtubestudio/vts_logo_transparent_darker.pngis excluded by!**/*.png
📒 Files selected for processing (19)
StreamdeckLib/Models/SetStateRequest.cs(1 hunks)StreamdeckLib/Models/StatePayload.cs(1 hunks)StreamdeckLib/StreamDeckConnection.cs(2 hunks)VTubeStudioAPI/Events/HotkeyTriggeredEvent.cs(1 hunks)VTubeStudioAPI/Models/Expression.cs(1 hunks)VTubeStudioAPI/Requests/ApiRequest.cs(1 hunks)VTubeStudioAPI/Requests/ExpressionActivationRequest.cs(1 hunks)VTubeStudioAPI/Requests/ExpressionStateRequest.cs(1 hunks)VTubeStudioAPI/Responses/ExpressionStateResponse.cs(1 hunks)VTubeStudioAPI/Responses/ResponseType.cs(2 hunks)VTubeStudioAPI/VTubeStudioWebsocketClient.cs(7 hunks)streamdeck-vtubestudio/Actions/ExpressionToggleAction.cs(1 hunks)streamdeck-vtubestudio/ExpressionCache.cs(1 hunks)streamdeck-vtubestudio/Program.cs(3 hunks)streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/App.vue(1 hunks)streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/main.js(1 hunks)streamdeck-vtubestudio/manifest.json(1 hunks)streamdeck-vtubestudio/nlog.config(1 hunks)streamdeck-vtubestudio/streamdeck-vtubestudio.csproj(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
VTubeStudioAPI/Responses/ResponseType.cs (3)
VTubeStudioAPI/Requests/ExpressionStateRequest.cs (1)
ExpressionStateRequest(5-10)VTubeStudioAPI/Requests/ExpressionActivationRequest.cs (1)
ExpressionActivationRequest(6-16)VTubeStudioAPI/Responses/ExpressionStateResponse.cs (1)
ExpressionStateResponse(6-19)
streamdeck-vtubestudio/Program.cs (1)
streamdeck-vtubestudio/ExpressionCache.cs (3)
ExpressionCache(15-94)ExpressionCache(30-41)ExpressionCache(83-91)
streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/main.js (1)
streamdeck-vtubestudio/PropertyInspector/src/utils/streamdeck.js (1)
StreamDeck(1-164)
VTubeStudioAPI/Responses/ExpressionStateResponse.cs (1)
VTubeStudioAPI/Models/Expression.cs (1)
Expression(5-24)
VTubeStudioAPI/VTubeStudioWebsocketClient.cs (5)
VTubeStudioAPI/Requests/HotkeyTriggerRequest.cs (1)
EventSubscriptionRequest(23-34)VTubeStudioAPI/Events/HotkeyTriggeredEvent.cs (1)
HotkeyTriggeredEvent(5-30)VTubeStudioAPI/Responses/ExpressionStateResponse.cs (1)
ExpressionStateResponse(6-19)streamdeck-vtubestudio/ExpressionCache.cs (1)
OnExpressionState(62-81)VTubeStudioAPI/ApiEventArgs.cs (1)
ApiEventArgs(5-10)
🔇 Additional comments (20)
streamdeck-vtubestudio/nlog.config (1)
25-27: Properly closed the commented-out combined logger rule
The closing-->on line 26 now aligns with the opening comment on line 24, ensuring the<logger name="*"...>rule remains commented without affecting active debug logging.streamdeck-vtubestudio/streamdeck-vtubestudio.csproj (1)
5-5: Target framework upgraded to .NET 9.0
Updating tonet9.0aligns with the new expression-toggle features. Please verify that all referenced packages (hosting, NLog, WebSocketSharp) and your CI/CD pipelines are compatible with .NET 9.0.StreamdeckLib/Models/SetStateRequest.cs (1)
3-3: Expose SetStateRequest for external consumption
Changing the record from internal to public is necessary for external components (e.g., Stream Deck plugin) to send state updates. Confirm that no unintended consumers now rely on this type and that your public API surface remains intentional.StreamdeckLib/Models/StatePayload.cs (1)
3-3: Rename payload property toStatefor clarity
Switching the positional property fromPayloadtoStateimproves semantic clarity. Ensure that JSON serialization still matches the Stream Deck protocol’s expectations (i.e.,{ "state": <value> }underpayload).VTubeStudioAPI/Requests/ApiRequest.cs (1)
2-9: IgnoreMessageTypein JSON serialization
Adding[JsonIgnore]on the abstractMessageTypeproperty prevents it from appearing in outgoing payloads. Verify that derived request classes still include the correctrequestTypefield as required by the VTube Studio API (via your custom converter or another mechanism).streamdeck-vtubestudio/Program.cs (3)
23-24: Activated debug argument loading from file.The code now reads debug arguments from a file in the roaming application data folder during DEBUG builds, which is helpful for development but ensure this path is accessible.
54-54: New ExpressionCache service registered.The ExpressionCache singleton service has been added to the dependency injection container, aligning with the new expression toggling functionality.
64-67: Added service provider validation.Enabling
ValidateOnBuild = truewill catch dependency injection issues at build time rather than runtime, improving reliability.VTubeStudioAPI/Responses/ResponseType.cs (2)
26-27: Added new expression-related request types.The new request types for expression state and activation align with the added functionality.
57-58: Added corresponding response types for expressions.These response types properly complement the request types and follow the established naming convention.
streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/main.js (1)
1-5: Appropriate Vue.js imports for the new component.The imports correctly set up Vue 3 with Vuex for state management.
VTubeStudioAPI/Requests/ExpressionStateRequest.cs (1)
5-10: LGTM! Clean and focused request implementation.The
ExpressionStateRequestclass looks well-implemented with a properly defined nullable parameter for optional expression file specification and the correct message type override.VTubeStudioAPI/Events/HotkeyTriggeredEvent.cs (1)
5-30: LGTM! Well-structured event model.The
HotkeyTriggeredEventclass is properly designed with appropriate JSON property mappings and required annotations for required fields. The model captures all necessary attributes of a hotkey trigger event.VTubeStudioAPI/Requests/ExpressionActivationRequest.cs (1)
6-16: Well-structured expression activation request classThe ExpressionActivationRequest class is properly implemented with:
- Appropriate constructor parameters with sensible defaults
- Properly annotated JSON properties for serialization
- Correct nullable type for ExpressionFile
- Proper override of MessageType from base class
This class aligns well with the VTube Studio API design for toggling expressions.
VTubeStudioAPI/VTubeStudioWebsocketClient.cs (3)
46-46: Good to see HotkeyTriggeredEvent subscription being enabledEnabling this subscription will allow the application to respond to hotkey events from VTube Studio, which is necessary for the expression toggle functionality.
246-248: Event declaration matches the new message typesThe static events for handling the new message types are properly declared, matching the implementation in the MessageReceived method.
198-206:Details
❌ Incorrect review comment
Consider passing response data for ExpressionActivationResponse
For the ExpressionActivationResponse case, you're invoking the event with empty EventArgs, while for other responses you're passing the deserialized response data. If the response contains useful information, consider passing it to subscribers.
case ResponseType.ExpressionActivationResponse: - OnExpressionActivation?.Invoke(this, EventArgs.Empty); + OnExpressionActivation?.Invoke(this, new (response.Data!.ToObject<ExpressionActivationResponse>()!)); break;
🏁 Script executed:
#!/bin/bash # Search for the ExpressionActivationResponse class to see if it contains any useful properties fd --exec grep -A 10 "class ExpressionActivationResponse" {} \;Length of output: 2328
I wasn’t able to locate the
ExpressionActivationResponsedefinition with the previous command. Let’s search the entire repo and inspect the class to see if it carries any data.
🏁 Script executed:
#!/bin/bash # Locate and inspect the ExpressionActivationResponse class definition rg -n "class ExpressionActivationResponse" -A10Length of output: 49
No payload for ExpressionActivationResponse
I couldn’t find anyExpressionActivationResponseclass or payload in the repo—there’s no data to pass to subscribers. KeepingEventArgs.Emptyhere is appropriate.Likely an incorrect or invalid review comment.
streamdeck-vtubestudio/ExpressionCache.cs (1)
62-81: Well-implemented thread-safe cache update with error handlingThe OnExpressionState method correctly:
- Uses async/await pattern with semaphore for thread safety
- Has proper error handling with logging
- Updates all relevant cache properties
- Ensures semaphore is released in a finally block
This implementation will provide reliable expression caching even in concurrent scenarios.
streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/App.vue (1)
43-46: Well-implemented settings persistenceThe deep watch on settings with automatic saving ensures that user configuration changes are immediately persisted to StreamDeck storage.
streamdeck-vtubestudio/Actions/ExpressionToggleAction.cs (1)
12-15: LGTM - Good use of attributes and inheritance pattern.The StreamDeck action is properly registered with a unique identifier and inherits from the appropriate base class with a strongly-typed payload.
| namespace VTubeStudioAPI.Models; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent namespace naming.
The namespace VTubeStudioAPI.Models differs from the convention used in other files (Cazzar.StreamDeck.VTubeStudio.VTubeStudioApi...). This inconsistency could cause confusion and potential import issues.
Change the namespace to match the project convention:
-namespace VTubeStudioAPI.Models;
+namespace Cazzar.StreamDeck.VTubeStudio.VTubeStudioApi.Models;🤖 Prompt for AI Agents
In VTubeStudioAPI/Models/Expression.cs at lines 3 to 4, the namespace is
currently set to VTubeStudioAPI.Models, which is inconsistent with the project's
naming convention. Update the namespace to follow the pattern used in other
files, such as Cazzar.StreamDeck.VTubeStudio.VTubeStudioApi.Models, to maintain
consistency and avoid import issues.
| VTubeStudioWebsocketClient.OnModelLoad -= Refresh; | ||
| VTubeStudioWebsocketClient.OnHotkeyTriggeredEvent -= RefreshHotkey; | ||
| VTubeStudioWebsocketClient.OnCurrentModelInformation -= Refresh; | ||
| VTubeStudioWebsocketClient.OnAuthenticationResponse -= HandleAuthenticated; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing unsubscription for OnExpressionActivation
The class subscribes to the OnExpressionActivation event in the constructor, but doesn't unsubscribe in the finalizer, which could lead to memory leaks.
~ExpressionCache()
{
instances.Remove(new(this));
VTubeStudioWebsocketClient.OnExpressionState -= OnExpressionState;
VTubeStudioWebsocketClient.OnModelLoad -= Refresh;
VTubeStudioWebsocketClient.OnHotkeyTriggeredEvent -= RefreshHotkey;
VTubeStudioWebsocketClient.OnCurrentModelInformation -= Refresh;
VTubeStudioWebsocketClient.OnAuthenticationResponse -= HandleAuthenticated;
+ VTubeStudioWebsocketClient.OnExpressionActivation -= Refresh;
}🤖 Prompt for AI Agents
In streamdeck-vtubestudio/ExpressionCache.cs at line 90, the code unsubscribes
from OnAuthenticationResponse but misses unsubscribing from
OnExpressionActivation, which was subscribed in the constructor. Add a line to
unsubscribe from VTubeStudioWebsocketClient.OnExpressionActivation by removing
the HandleExpressionActivated handler in the finalizer or dispose method to
prevent memory leaks.
| <NotConnected v-if="!websocketConnected" /> | ||
| <div v-else> | ||
| <div v-if="websocketConnected" class="sdpi-item"> | ||
| <div class="sdpi-item-label">Size ({{ settings.size ?? '0' }})</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unexpected reference to settings.size
The template references settings.size but this property is not defined in the data object's settings. This could cause undefined behavior or display issues.
Either:
- Remove the reference if it's not needed:
- <div class="sdpi-item-label">Size ({{ settings.size ?? '0' }})</div>
+ <div class="sdpi-item-label">Expression</div>- Or add the size property to the settings object:
settings: {
expression: '',
fadeTime: 0.5,
+ size: 0,
}🤖 Prompt for AI Agents
In streamdeck-vtubestudio/PropertyInspector/src/pages/ExpressionToggle/App.vue
at line 5, the template references settings.size which is not defined in the
data object's settings, potentially causing undefined behavior. To fix this,
either remove the reference to settings.size from the template if it is
unnecessary, or add a size property with a default value to the settings object
in the component's data to ensure it is defined and avoid display issues.
| #nullable enable | ||
| using System.Linq; | ||
| using Cazzar.StreamDeck.VTubeStudio.VTubeStudioApi; | ||
| using Cazzar.StreamDeck.VTubeStudio.VTubeStudioApi.Requests; | ||
| using Microsoft.Extensions.Logging; | ||
| using Newtonsoft.Json; | ||
| using StreamDeckLib; | ||
| using StreamDeckLib.Models; | ||
|
|
||
| namespace Cazzar.StreamDeck.VTubeStudio.Actions; | ||
|
|
||
|
|
||
| [StreamDeckAction("dev.cazzar.vtubestudio.expressiontoggle")] | ||
| public class ExpressionToggleAction : BaseAction<ExpressionToggleAction.ExpressionTogglePayload> | ||
| { | ||
| private readonly ExpressionCache _expressionCache; | ||
|
|
||
| public class ExpressionTogglePayload | ||
| { | ||
| [JsonProperty("expression")] | ||
| public string? Expression { get; set; } = "Cat Tail.exp3.json"; | ||
|
|
||
| [JsonProperty("fadeTime")] | ||
| public float FadeTime { get; set; } = 0.5f; | ||
| } | ||
|
|
||
| public ExpressionToggleAction(GlobalSettingsManager gsm, VTubeStudioWebsocketClient vts, IStreamDeckConnection isd, ILogger<ExpressionToggleAction> logger, ExpressionCache expressionCache) : base(gsm, vts, isd, logger) | ||
| { | ||
| _expressionCache = expressionCache; | ||
| } | ||
|
|
||
| protected override void Pressed() | ||
| { | ||
| // if (!_expressionCache.ModelLoaded) | ||
| // return; | ||
|
|
||
| var activate = !_expressionCache.Expressions.FirstOrDefault(e => e.File == Settings.Expression)?.Active ?? false; | ||
|
|
||
| Vts.Send(new ExpressionActivationRequest(Settings.Expression, activate)); | ||
|
|
||
| Connection.SendMessage(new SetStateRequest | ||
| { | ||
| Payload = new ((uint) (activate ? 1 : 0)), | ||
| Context = ContextId, | ||
| }); | ||
| } | ||
|
|
||
| public override void Tick() | ||
| { | ||
| base.Tick(); | ||
| var activate = !_expressionCache.Expressions.FirstOrDefault(e => e.File == Settings.Expression)?.Active ?? false; | ||
|
|
||
| Connection.SendMessage(new SetStateRequest | ||
| { | ||
| Payload = new ((uint) (activate ? 1 : 0)), | ||
| Context = ContextId, | ||
| }); | ||
| } | ||
|
|
||
| protected override object GetClientData() => new | ||
| { | ||
| sxpressions = _expressionCache.Expressions.DistinctBy(e => e.File).Select(e => new { e.Name, e.File }), | ||
| }; | ||
|
|
||
| protected override void Released() | ||
| { | ||
| } | ||
|
|
||
| protected override void SettingsUpdated(ExpressionTogglePayload oldSettings, ExpressionTogglePayload newSettings) | ||
| { | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add null-checking and error handling for robust operation.
The code should include additional null checks and error handling to make it more robust, especially since the Stream Deck plugin operates in an environment where network issues or timing problems could occur.
Consider adding:
- Null checks for Settings.Expression before using it
- Try-catch blocks around network operations
- Logging for error conditions
For example:
protected override void Pressed()
{
if (!_expressionCache.ModelLoaded)
{
Logger.LogWarning("Cannot toggle expression: No model is loaded");
return;
}
if (string.IsNullOrEmpty(Settings.Expression))
{
Logger.LogWarning("Cannot toggle expression: No expression selected");
return;
}
try
{
var activate = !IsExpressionActive();
Logger.LogInformation("Toggling expression {Expression} to {State}",
Settings.Expression, activate ? "active" : "inactive");
Vts.Send(new ExpressionActivationRequest(Settings.Expression, activate, Settings.FadeTime));
Connection.SendMessage(new SetStateRequest
{
Payload = new ((uint) (activate ? 1 : 0)),
Context = ContextId,
});
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to toggle expression {Expression}", Settings.Expression);
}
}🤖 Prompt for AI Agents
In streamdeck-vtubestudio/Actions/ExpressionToggleAction.cs lines 1 to 72, add
null checks and error handling in the Pressed method to improve robustness.
Specifically, check if Settings.Expression is null or empty before proceeding,
and verify that the model is loaded via _expressionCache.ModelLoaded. Wrap the
network calls to Vts.Send and Connection.SendMessage in a try-catch block to
catch exceptions, and log warnings or errors appropriately using
Logger.LogWarning and Logger.LogError. Also, add informative logging for
toggling actions with Logger.LogInformation to track state changes.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Summary by CodeRabbit
New Features
Enhancements
Other Changes