Skip to content

Commit

Permalink
Stabilize and enhance some minor bits
Browse files Browse the repository at this point in the history
This adds some improvements and changes to make the bot more stable.
Some were mistakes I stumbled upon when doing some random testing.

- Widgets now can be in an "unloaded" state. WidgetUserModule holds
  references to all IWidget instances that are assigned to given user,
  while IWidget provides two new abstracts that derivatives have to
  implement: OnLoad() and OnUnload(). This allows to allocate a widget
  and attempt a load, and when it fails keep the widget unloaded.
  Failures can happen for many different reasons, ex. widget can be
  created when module that produces its events is not yet enabled.
- Widgets can be manually reloaded by using `widget reload` command. No
  ID added will reload all widgets assigned to current user.
- EventSystem now throws dedicated Event/Dispatcher not found
  exceptions. This is for more clarity, as it used to return "key not
  present in dictionary" generic error.
- Some parts of LukeBot now rely on https_domain prop instead of
  server_ip. This is to ensure IP is not visible in ex. OAuth callbacks
  (although it does not matter much, since anyone can DNS lookup the
  domain...)
  • Loading branch information
lookeypl committed Sep 27, 2024
1 parent b0186ed commit 53e99a1
Show file tree
Hide file tree
Showing 26 changed files with 350 additions and 108 deletions.
2 changes: 1 addition & 1 deletion LukeBot.API/SpotifyToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public SpotifyToken(AuthFlow flow, string lbUser)
"https://accounts.spotify.com/authorize",
"https://accounts.spotify.com/api/token",
"https://accounts.spotify.com/api/revoke",
"http://" + Conf.Get<string>(Common.Constants.PROP_STORE_SERVER_IP_PROP) + "/callback/spotify"
"http://" + Conf.Get<string>(Common.Constants.PROP_STORE_HTTPS_DOMAIN_PROP) + "/callback/spotify"
)
{
}
Expand Down
2 changes: 1 addition & 1 deletion LukeBot.API/TwitchToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public TwitchToken(AuthFlow flow, string lbUser)
"https://id.twitch.tv/oauth2/authorize",
"https://id.twitch.tv/oauth2/token",
"https://id.twitch.tv/oauth2/revoke",
"https://" + Conf.Get<string>(Common.Constants.PROP_STORE_SERVER_IP_PROP) + "/callback/twitch"
"https://" + Conf.Get<string>(Common.Constants.PROP_STORE_HTTPS_DOMAIN_PROP) + "/callback/twitch"
)
{
}
Expand Down
5 changes: 4 additions & 1 deletion LukeBot.Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ public class Constants
public static readonly Path PROP_STORE_USERS_PROP = Path.Form(LUKEBOT_USER_ID, PROP_STORE_USERS_PROP_NAME);
public static readonly Path PROP_STORE_RECONNECT_COUNT_PROP = Path.Form(LUKEBOT_USER_ID, PROP_STORE_RECONNECT_COUNT_PROP_NAME);

public const string DEFAULT_SERVER_IP = "localhost:5000";
public const string DEFAULT_SERVER_IP = "127.0.0.1";
public const string DEFAULT_SERVER_HTTPS_DOMAIN = "localhost";
public const string PROPERTY_STORE_FILE = "Data/props.lukebot";
public const string DEFAULT_LOGIN_NAME = "SET_BOT_LOGIN_HERE";
public const string DEFAULT_CLIENT_ID_NAME = "SET_YOUR_CLIENT_ID_HERE";
public const string DEFAULT_CLIENT_SECRET_NAME = "SET_YOUR_CLIENT_SECRET_HERE";

public const int SERVERCLI_DEFAULT_PORT = 55268; // in T9: LKBOT

public const string SPOTIFY_MODULE_NAME = "spotify";
public const string TWITCH_MODULE_NAME = "twitch";
public const string WIDGET_MODULE_NAME = "widget";
Expand Down
1 change: 1 addition & 0 deletions LukeBot.Common/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;


Expand Down
7 changes: 7 additions & 0 deletions LukeBot.Communication/EventSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ internal EventCollection(string lbUser)
*/
public Event Event(string name)
{
if (!mEvents.ContainsKey(name))
throw new EventNotFoundException(name);

return mEvents[name];
}

Expand Down Expand Up @@ -139,6 +142,8 @@ public List<EventCallback> RegisterPublisher(IEventPublisher p)
{
string pubName = p.GetName();

Logger.Log().Debug("Registering publisher {0}", pubName);

if (mPublishers.ContainsKey(pubName))
throw new PublisherAlreadyRegisteredException(pubName);

Expand Down Expand Up @@ -170,6 +175,8 @@ public void UnregisterPublisher(IEventPublisher p)
{
string pubName = p.GetName();

Logger.Log().Debug("Unregistering publisher {0}", pubName);

if (!mPublishers.ContainsKey(pubName))
return;

Expand Down
12 changes: 12 additions & 0 deletions LukeBot.Communication/Exception/DispatcherNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using LukeBot.Communication.Common;


namespace LukeBot.Communication
{
public class DispatcherNotFoundException: LukeBot.Common.Exception
{
public DispatcherNotFoundException(string dispatcherName)
: base(string.Format("Not found dispatcher: {0}", dispatcherName))
{}
}
}
12 changes: 12 additions & 0 deletions LukeBot.Communication/Exception/EventNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using LukeBot.Communication.Common;


namespace LukeBot.Communication
{
public class EventNotFoundException: LukeBot.Common.Exception
{
public EventNotFoundException(string eventName)
: base(string.Format("Not found event: {0}", eventName))
{}
}
}
17 changes: 11 additions & 6 deletions LukeBot.Endpoint/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,32 +61,37 @@ public IWebHostBuilder CreateHostBuilder()
logging.AddProvider(new LBLoggingProvider());
});

string IP = Conf.Get<string>(Common.Constants.PROP_STORE_SERVER_IP_PROP);
string domain;
string[] URLs;

if (IP.Contains("localhost"))
if (!Conf.TryGet<string>(Common.Constants.PROP_STORE_HTTPS_DOMAIN_PROP, out domain))
{
domain = "localhost";
}

if (domain.Contains("localhost"))
{
// manually set only localhost
// we do this path just in case someone prefers to use different-than-default port 5000
URLs = new string[]
{
"https://" + IP + "/",
"https://" + domain + "/",
};
}
else
{
// add defined address + localhost:5000
URLs = new string[]
{
"https://" + IP + "/",
"https://" + domain + "/",
"https://localhost:5000/"
};
}

Logger.Log().Debug("Endpoint using host addresses:");
Logger.Log().Info("Endpoint using host addresses:");
foreach (string addr in URLs)
{
Logger.Log().Debug(" - https://" + IP + "/");
Logger.Log().Info(" - https://" + addr + "/");
}

builder.UseUrls(URLs);
Expand Down
4 changes: 2 additions & 2 deletions LukeBot.Module/IUserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
public interface IUserModule
{
public void Run();
public void RequestShutdown();
public void WaitForShutdown();
public void RequestShutdown(); // TODO replace with single call Shutdown()
public void WaitForShutdown(); // TODO ^
public ModuleType GetModuleType();
}
}
7 changes: 6 additions & 1 deletion LukeBot.Twitch/IRCChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace LukeBot.Twitch
{
class IRCChannel: IEventPublisher
class IRCChannel: IEventPublisher, IDisposable
{
private string mLBUser;
private string mChannelName;
Expand Down Expand Up @@ -109,6 +109,11 @@ public List<EventDescriptor> GetEvents()
return events;
}

public void Dispose()
{
Comms.Event.User(mLBUser).UnregisterPublisher(this);
}

public IRCChannel(string lbUser, API.Twitch.GetUserData userData, Token userToken, BadgeCollection globalBadges)
{
mLBUser = lbUser;
Expand Down
7 changes: 1 addition & 6 deletions LukeBot.Twitch/TwitchIRC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,6 @@ public TwitchIRC(string username, Token token)
Logger.Log().Info("Twitch IRC module initialized");
}

~TwitchIRC()
{
Disconnect();
WaitForShutdown();
}

public void JoinChannel(string lbUser, API.Twitch.GetUserData user, Token token)
{
mChannelsMutex.WaitOne();
Expand Down Expand Up @@ -390,6 +384,7 @@ public void PartChannel(API.Twitch.GetUserData user)

mIRCClient.Send(IRCMessage.PART(user.login));

mChannels[user.login].Dispose();
mChannels.Remove(user.login);

mChannelsMutex.ReleaseMutex();
Expand Down
22 changes: 20 additions & 2 deletions LukeBot.Widget/Alerts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ protected override void OnConfigurationUpdate()
AwaitEventCompletion();
}

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

Expand All @@ -163,6 +162,25 @@ public Alerts(string lbUser, string id, string name)
collection.Event(Events.TWITCH_SUBSCRIPTION).InterruptEndpoint += OnEventInterrupt;
}

protected override void OnUnload()
{
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;

collection.Event(Events.TWITCH_CHEER).Endpoint -= OnSimpleEvent<TwitchCheerArgs>;
collection.Event(Events.TWITCH_CHEER).InterruptEndpoint -= OnEventInterrupt;

collection.Event(Events.TWITCH_SUBSCRIPTION).Endpoint -= OnSubscriptionEvent;
collection.Event(Events.TWITCH_SUBSCRIPTION).InterruptEndpoint -= OnEventInterrupt;
}

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

public override WidgetType GetWidgetType()
{
return WidgetType.alerts;
Expand Down
15 changes: 13 additions & 2 deletions LukeBot.Widget/Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,25 @@ protected override void OnConnected()
// noop
}

public Chat(string lbUser, string id, string name)
: base(lbUser, "LukeBot.Widget/Widgets/Chat.html", id, name)
protected override void OnLoad()
{
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;
}

protected override void OnUnload()
{
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 Chat(string lbUser, string id, string name)
: base(lbUser, "LukeBot.Widget/Widgets/Chat.html", id, name)
{
}

public override WidgetType GetWidgetType()
{
return WidgetType.chat;
Expand Down
8 changes: 8 additions & 0 deletions LukeBot.Widget/Echo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ protected override void OnConnected()
}
}

protected override void OnLoad()
{
}

protected override void OnUnload()
{
}

public Echo(string lbUser, string id, string name)
: base(lbUser, "LukeBot.Widget/Widgets/Echo.html", id, name)
{
Expand Down
50 changes: 33 additions & 17 deletions LukeBot.Widget/IWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public WebSocketRecv(WebSocketReceiveResult result, string data)

public string ID { get; private set; }
public string Name { get; private set; }
public bool Loaded { get; private set; }
public string mWidgetFilePath;
protected string mLBUser;
private List<string> mHead;
Expand All @@ -50,6 +51,8 @@ public WebSocketRecv(WebSocketReceiveResult result, string data)
private Config.Path mConfigurationPath;

protected bool Connected { get { return mWS != null && mWS.State == WebSocketState.Open; } }
protected abstract void OnLoad(); // called when widget is loaded. Can throw, which will leave widget in unloaded state.
protected abstract void OnUnload(); // called when widget is loaded. Can throw, which will leave widget in unloaded state.
protected abstract void OnConnected();
protected virtual void OnConfigurationUpdate() {}

Expand All @@ -67,13 +70,13 @@ private string GetWidgetCode()

internal string GetWidgetAddress()
{
string serverAddress = Conf.Get<string>(LukeBot.Common.Constants.PROP_STORE_SERVER_IP_PROP);
string serverAddress = Conf.Get<string>(LukeBot.Common.Constants.PROP_STORE_HTTPS_DOMAIN_PROP);
return "https://" + serverAddress + "/widget/" + ID;
}

private string GetWidgetWSAddress()
{
string serverAddress = Conf.Get<string>(LukeBot.Common.Constants.PROP_STORE_SERVER_IP_PROP);
string serverAddress = Conf.Get<string>(LukeBot.Common.Constants.PROP_STORE_HTTPS_DOMAIN_PROP);
return "wss://" + serverAddress + "/widgetws/" + ID;
}

Expand Down Expand Up @@ -237,6 +240,7 @@ public IWidget(string lbUser, string widgetFilePath, string id, string name, Wid

ID = id;
Name = name;
Loaded = false;
mLBUser = lbUser;
mHead = new List<string>();
mWS = null;
Expand All @@ -251,14 +255,40 @@ public IWidget(string lbUser, string widgetFilePath, string id, string name, Wid
.Push(ID)
.Push(Constants.PROP_CONFIG);
mConfiguration = config;
LoadConfiguration();
}

public IWidget(string lbUser, string widgetFilePath, string id, string name)
: this(lbUser, widgetFilePath, id, name, new EmptyWidgetConfiguration())
{
}

public void Load()
{
if (Loaded)
return;

LoadConfiguration();
OnLoad();
Loaded = true;
}

public void Unload()
{
if (!Loaded)
return;

mWSThreadDone = true;
CloseWS(WebSocketCloseStatus.NormalClosure);

if (mWSMessagingThread != null)
mWSMessagingThread.Join();

SaveConfiguration();

OnUnload();
Loaded = false;
}

public string GetPage()
{
string page = "<!DOCTYPE html><html><head>";
Expand Down Expand Up @@ -314,19 +344,5 @@ public WidgetDesc GetDesc()
}

public abstract WidgetType GetWidgetType();

public virtual void RequestShutdown()
{
mWSThreadDone = true;
CloseWS(WebSocketCloseStatus.NormalClosure);
}

public virtual void WaitForShutdown()
{
if (mWSMessagingThread != null)
mWSMessagingThread.Join();

SaveConfiguration();
}
}
}
Loading

0 comments on commit 53e99a1

Please sign in to comment.