Skip to content

Commit

Permalink
Widget: Add Configuration code for Widgets
Browse files Browse the repository at this point in the history
This adds necessary bits for Widgets to be configurable. Configuration
can be changed via CLI "widget update" command.

- Widgets can have their own configuration class which must inherit and
  implement abstract WidgetConfiguration class. WidgetConfiguration
  inherits from LukeBot.Communication.Common.EventArgsBase in order to
  make sending configuration to HTML/JS side of Widgets hassle-free.
- Each Widget has its configuration available as protected
  mConfiguration field, kept in base IWidget class.
- For Widgets which don't have a configuration, EmptyWidgetConfig object
  is created automatically. This config is treated specially by code
  saving the configuration to Property Store and skipped (it's empty
  anyway so no reason to clutter the Property Store).
- Widget's Configuration is automatically loaded and created. To modify
  it, "widget update <widget_name> <changes>" is available. Changes are
  parsed with the same method as "event test" call.
  • Loading branch information
lookeypl committed May 28, 2024
1 parent 8b92519 commit f0eb1b7
Show file tree
Hide file tree
Showing 19 changed files with 427 additions and 106 deletions.
63 changes: 63 additions & 0 deletions LukeBot.Common/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -65,5 +66,67 @@ public static Process StartBrowser(string url)

return result;
}

// Parse a list of strings into a list of key-value tuples. Useful for providing arguments
// to inner systems of LukeBot (ex. EventSystem's test command, or Widget's config update)
// Notable parsing details:
// - Key always has to be a string without spaces
// - There must be no spaces surrounding the = sign, so always <key>=<value>
// - Longer strings with spaces are allowed if put in quotation marks
// - No escape characters are supported (yet) (TODO?)
// Following args list is valid:
// Tier=2 Message="This is a message!" User=username
// Produces three tuples (all strings):
// ("Tier", "2")
// ("Message", "This is a message!")
// ("User", "username")
// TestEvent() will further parse the data for correctness against Event's
// TestArgs list, if available.
public static IEnumerable<(string attrib, string value)> ConvertArgStringsToTuples(IEnumerable<string> argsList)
{
List<(string attrib, string value)> ret = new();

string a = "", v = "";
bool readingString = false;
foreach (string s in argsList)
{
if (readingString)
{
if (s.EndsWith('"'))
{
readingString = false;
v += ' ' + s.Substring(0, s.Length - 1);
ret.Add((a, v));
}
else
{
v += ' ' + s;
}

continue;
}

string[] tokens = s.Split('=');
if (tokens.Length != 2)
{
throw new ArgumentException("Failed to parse test event attributes");
}

a = tokens[0];

if (tokens[1].StartsWith('"'))
{
v = tokens[1].Substring(1);
readingString = true;
}
else
{
v = tokens[1];
ret.Add((a, v));
}
}

return ret;
}
}
}
2 changes: 2 additions & 0 deletions LukeBot.Config/Conf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public static T Get<T>(Path path)
return mStore.Get(path).Get<T>();
}

// TODO modify should probably be smarter than that
// (as in - if Config field does not exist, create it automatically)
public static void Modify<T>(Path path, T value)
{
mStore.Modify<T>(path, value);
Expand Down
1 change: 1 addition & 0 deletions LukeBot.Twitch/TwitchUserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using LukeBot.Logging;
using LukeBot.Module;
using LukeBot.Twitch.EventSub;
using LukeBot.Twitch.Common;
using Widget = LukeBot.Widget;


Expand Down
24 changes: 24 additions & 0 deletions LukeBot.Widget.Common/WidgetConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using LukeBot.Communication.Common;
using Newtonsoft.Json;


namespace LukeBot.Widget.Common
{
public abstract class WidgetConfiguration: EventArgsBase
{
public WidgetConfiguration(string name)
: base(name)
{
}

public string SerializeConfiguration()
{
return JsonConvert.SerializeObject(this);
}

public abstract void DeserializeConfiguration(string configString);
public abstract void ValidateUpdate(string field, string value);
public abstract void Update(string field, string value);
public abstract string ToFormattedString();
}
}
60 changes: 50 additions & 10 deletions LukeBot.Widget/Alerts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using LukeBot.Logging;
using LukeBot.Twitch.Common;
using LukeBot.Widget.Common;
using Newtonsoft.Json;


namespace LukeBot.Widget
Expand All @@ -19,15 +20,15 @@ namespace LukeBot.Widget
*/
public class Alerts: IWidget
{
private class AlertInterrupt : EventArgsBase
private class AlertInterrupt: EventArgsBase
{
public AlertInterrupt()
: base("AlertInterrupt")
{
}
}

private class AlertWidgetConfig : EventArgsBase
private class AlertWidgetConfig: WidgetConfiguration
{
public string Alignment { get; set; }

Expand All @@ -36,10 +37,49 @@ public AlertWidgetConfig()
{
Alignment = "right";
}

public override void DeserializeConfiguration(string configString)
{
AlertWidgetConfig config = JsonConvert.DeserializeObject<AlertWidgetConfig>(configString);

Alignment = config.Alignment;
}

public override void ValidateUpdate(string field, string value)
{
switch (field)
{
case "Alignment":
{
if (value != "left" && value != "right")
throw new WidgetConfigurationUpdateException("Invalid Alignment value: {0}. Allowed values: \"left\" or \"right\"", value);
break;
}
default:
Logger.Log().Warning("Unrecognized Alert Widget config field: {0}", field);
break;
}
}

public override void Update(string field, string value)
{
switch (field)
{
case "Alignment": Alignment = value; break;
}
}

public override string ToFormattedString()
{
return " Alignment: " + Alignment;
}
}

private void AwaitEventCompletion()
{
if (!Connected)
return;

WidgetEventCompletionResponse resp = RecvFromWS<WidgetEventCompletionResponse>();
if (resp == null)
{
Expand Down Expand Up @@ -98,20 +138,20 @@ private void OnEventInterrupt(object o, EventArgsBase args)

protected override void OnConnected()
{
AlertWidgetConfig config = new AlertWidgetConfig();
// TODO hacky, make it work properly and implement widget config system
WidgetDesc desc = GetDesc();
if (desc.Name != null && desc.Name.EndsWith("_Left"))
config.Alignment = "left";
SendToWS(config);
SendToWS(mConfiguration);
AwaitEventCompletion();
}

protected override void OnConfigurationUpdate()
{
SendToWS(mConfiguration);
AwaitEventCompletion();
}

public Alerts(string lbUser, string id, string name)
: base("LukeBot.Widget/Widgets/Alerts.html", id, name)
: base(lbUser, "LukeBot.Widget/Widgets/Alerts.html", id, name, new AlertWidgetConfig())
{
EventCollection collection = Comms.Event.User(lbUser);
EventCollection collection = Comms.Event.User(mLBUser);

collection.Event(Events.TWITCH_CHANNEL_POINTS_REDEMPTION).Endpoint += OnSimpleEvent<TwitchChannelPointsRedemptionArgs>;
collection.Event(Events.TWITCH_CHANNEL_POINTS_REDEMPTION).InterruptEndpoint += OnEventInterrupt;
Expand Down
8 changes: 4 additions & 4 deletions LukeBot.Widget/Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ protected override void OnConnected()
}

public Chat(string lbUser, string id, string name)
: base("LukeBot.Widget/Widgets/Chat.html", id, name)
: base(lbUser, "LukeBot.Widget/Widgets/Chat.html", id, name)
{
Comms.Event.User(lbUser).Event(Events.TWITCH_CHAT_MESSAGE).Endpoint += OnMessage;
Comms.Event.User(lbUser).Event(Events.TWITCH_CHAT_CLEAR_USER).Endpoint += OnClearChat;
Comms.Event.User(lbUser).Event(Events.TWITCH_CHAT_CLEAR_MESSAGE).Endpoint += OnClearMsg;
Comms.Event.User(mLBUser).Event(Events.TWITCH_CHAT_MESSAGE).Endpoint += OnMessage;
Comms.Event.User(mLBUser).Event(Events.TWITCH_CHAT_CLEAR_USER).Endpoint += OnClearChat;
Comms.Event.User(mLBUser).Event(Events.TWITCH_CHAT_CLEAR_MESSAGE).Endpoint += OnClearMsg;
}

public override WidgetType GetWidgetType()
Expand Down
4 changes: 4 additions & 0 deletions LukeBot.Widget/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ namespace LukeBot.Widget
internal class Constants
{
public const string PROP_WIDGETS = "widgets";
public const string PROP_STORE_WIDGET_DOMAIN = "widget";
public const string PROP_CONFIG = "config";

public const string EMPTY_WIDGET_CONFIGURATION_NAME = "EmptyWidgetConfiguration";
}
}
4 changes: 2 additions & 2 deletions LukeBot.Widget/Echo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ protected override void OnConnected()
}
}

public Echo(string id, string name)
: base("LukeBot.Widget/Widgets/Echo.html", id, name)
public Echo(string lbUser, string id, string name)
: base(lbUser, "LukeBot.Widget/Widgets/Echo.html", id, name)
{
}

Expand Down
31 changes: 31 additions & 0 deletions LukeBot.Widget/EmptyWidgetConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using LukeBot.Logging;
using LukeBot.Widget.Common;


namespace LukeBot.Widget
{
internal class EmptyWidgetConfiguration: WidgetConfiguration
{
public EmptyWidgetConfiguration()
: base(Constants.EMPTY_WIDGET_CONFIGURATION_NAME)
{
}

public override void DeserializeConfiguration(string configString)
{
}

public override void ValidateUpdate(string field, string value)
{
}

public override void Update(string field, string value)
{
}

public override string ToFormattedString()
{
return "";
}
}
}
10 changes: 10 additions & 0 deletions LukeBot.Widget/Exception/WidgetConfigurationUpdateException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using LukeBot.Common;

namespace LukeBot.Widget
{
public class WidgetConfigurationUpdateException: Exception
{
public WidgetConfigurationUpdateException(string fmt, params object[] args)
: base(string.Format(fmt, args)) {}
}
}
Loading

0 comments on commit f0eb1b7

Please sign in to comment.