Skip to content

Commit 75daa4d

Browse files
committed
Implement EmoteAlert handling. Add new tests. Rework EmotesPage.
1 parent 03653ab commit 75daa4d

File tree

10 files changed

+178
-27
lines changed

10 files changed

+178
-27
lines changed

ArduinoTwitchBot.Code/TwitchBot.cs

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Linq;
45
using System.Threading.Tasks;
56
using TwitchLib.Api;
67
using TwitchLib.Client;
8+
using TwitchLib.Client.Models;
79
using TwitchLib.PubSub;
810

911
namespace ArduinoTwitchBot.Code
@@ -25,9 +27,15 @@ public static TwitchBot Instance
2527
}
2628
#endregion
2729

30+
public TwitchBot()
31+
{
32+
Alerts = new Alert[6];
33+
}
34+
2835
public string ClientId { get; set; }
2936
public string AccessToken { get; set; }
3037
public string PortName { get; set; }
38+
// Follows, subs, bits, raids, hosts and emotes, in this order.
3139
public Alert[] Alerts { get; set; }
3240

3341
// API to get Channel Id from username.
@@ -37,8 +45,10 @@ public static TwitchBot Instance
3745
public TwitchPubSub _client { get; private set; }
3846

3947
// Experimental client for listening to chat messages
40-
private TwitchClient _chatClient;
48+
public TwitchClient _chatClient { get; private set; }
49+
public List<string> EmotesList { get; set; }
4150

51+
// Connect the PubSub client.
4252
public async void Connect(string clientId, string accessToken, string channelName, string portName, Alert[] alerts)
4353
{
4454
// Check if the needed parameters are provided.
@@ -91,19 +101,81 @@ public async void Connect(string clientId, string accessToken, string channelNam
91101
_client.OnHost += this.Client_OnHost;
92102
}
93103

94-
if (alerts[5])
95-
{
96-
// TODO
97-
}
104+
// ChatClient is connected from a different place.
98105

99106
_client.Connect();
100107
}
101108

109+
// Disconnect the PubSub client.
102110
public void Disconnect()
103111
{
104112
_client?.Disconnect();
105113
}
106114

115+
// Connect the ClientChat (Emote alerts).
116+
public void ConnectChatClient(string accessToken, string channelName, string portName, Alert emoteAlert, List<string> emotesList, string botName = "ArduinoBot")
117+
{
118+
// Make sure EmotesList has been filled up by the user.
119+
if (EmotesList?.Count == 0)
120+
{
121+
throw new Exception("Emote list is empty - unable to listen to emotes sent in chat.");
122+
}
123+
124+
AccessToken = accessToken;
125+
EmotesList = emotesList;
126+
PortName = portName;
127+
Alerts[5] = emoteAlert;
128+
129+
_chatClient = new TwitchClient();
130+
var credentials = new ConnectionCredentials(botName, accessToken);
131+
_chatClient.Initialize(credentials, channelName);
132+
133+
_chatClient.OnJoinedChannel += ChatClient_OnJoinedChannel;
134+
_chatClient.OnMessageReceived += ChatClient_OnMessageReceived;
135+
_chatClient.OnConnected += ChatClient_OnConnected; ;
136+
137+
_chatClient.Connect();
138+
}
139+
140+
private void ChatClient_OnConnected(object sender, TwitchLib.Client.Events.OnConnectedArgs e)
141+
{
142+
#if DEBUG
143+
Trace.WriteLine($"Connected bot of username: {e.BotUsername} to channel: {e.AutoJoinChannel}");
144+
#endif
145+
}
146+
147+
private void ChatClient_OnMessageReceived(object sender, TwitchLib.Client.Events.OnMessageReceivedArgs e)
148+
{
149+
// Check if message contains an emote that is on the list.
150+
if (EmotesList.Any(x => e.ChatMessage.Message.Contains(x)))
151+
{
152+
// Send a signal.
153+
try
154+
{
155+
SerialPortHelper.SendMessage(PortName, Alerts[5].Signal, Alerts[5].SignalType);
156+
}
157+
catch (Exception ex)
158+
{
159+
#if DEBUG
160+
Trace.WriteLine(ex.Message);
161+
#endif
162+
}
163+
}
164+
}
165+
166+
private void ChatClient_OnJoinedChannel(object sender, TwitchLib.Client.Events.OnJoinedChannelArgs e)
167+
{
168+
#if DEBUG
169+
Trace.WriteLine($"Joined channel: {e.Channel}");
170+
#endif
171+
}
172+
173+
// Disconnect the ClientChat (Emote alerts).
174+
public void DisconnectChatClient()
175+
{
176+
_chatClient?.Disconnect();
177+
}
178+
107179
public async Task<string> GetChannelId(string channelName)
108180
{
109181
string channelId = "";

ArduinoTwitchBot.Tests/TwitchBotTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ArduinoTwitchBot.Code;
22
using NUnit.Framework;
33
using System;
4+
using System.Collections.Generic;
45

56
namespace ArduinoTwitchBot.Tests
67
{
@@ -88,5 +89,26 @@ public void Disconnect_NoExceptionThrown()
8889
{
8990
Assert.DoesNotThrow(() => _twitchBot.Disconnect());
9091
}
92+
93+
[Test]
94+
public void ConnectChatClient_NoExceptionThrown_ChatClientNotNull()
95+
{
96+
var channelName = "Stukeleyak";
97+
var portName = "COM3";
98+
var alert = new Alert(true, "Test");
99+
var emotesList = new List<string>() { "Kappa" };
100+
101+
Assert.DoesNotThrow(() => _twitchBot.ConnectChatClient(Properties.Resources.AccessToken, channelName, portName, alert, emotesList));
102+
Assert.IsNotNull(_twitchBot._chatClient);
103+
Assert.IsNotNull(_twitchBot.AccessToken);
104+
Assert.IsNotNull(_twitchBot.PortName);
105+
Assert.IsNotNull(_twitchBot.EmotesList);
106+
}
107+
108+
[Test]
109+
public void DisconnectChatClient_NoExceptionThrown()
110+
{
111+
Assert.DoesNotThrow(() => _twitchBot.DisconnectChatClient());
112+
}
91113
}
92114
}

ArduinoTwitchBot.UI/MainWindow.xaml.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ protected override void OnClosing(CancelEventArgs e)
3636
{
3737
settingsPage.SaveSettings();
3838
}
39+
else if (this.PageFrame.Content is EmotesPage emotesPage)
40+
{
41+
emotesPage.SaveEmotes();
42+
}
3943

4044
// Save user settings before quitting.
4145
try
@@ -47,8 +51,9 @@ protected override void OnClosing(CancelEventArgs e)
4751
MessageBox.Show($"{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
4852
}
4953

50-
// Disconnect the bot (if it's running).
54+
// Disconnect both bots (if they are running).
5155
TwitchBot.Instance.Disconnect();
56+
TwitchBot.Instance.DisconnectChatClient();
5257

5358
base.OnClosing(e);
5459
}

ArduinoTwitchBot.UI/Pages/EmotesPage.xaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
66
xmlns:local="clr-namespace:ArduinoTwitchBot.UI.Pages"
77
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
8-
mc:Ignorable="d" Background="White"
8+
mc:Ignorable="d" Background="{DynamicResource MaterialDesignPaper}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" FontFamily="{materialDesign:MaterialDesignFont}"
99
d:DesignHeight="400" d:DesignWidth="500"
1010
Title="EmotesPage">
1111

1212
<StackPanel>
1313

14-
<Button x:Name="MainPageButton" Width="100" Height="40" Margin="20" Padding="0" Click="MainPageButton_Click" ToolTip="Click here to go back." FontSize="18">
14+
<Button x:Name="MainPageButton" Width="100" Height="40" Margin="10" Padding="0" Click="MainPageButton_Click" ToolTip="Click here to go back. Emotes will be saved automatically." FontSize="18">
1515
<StackPanel Orientation="Horizontal">
1616
<materialDesign:PackIcon Kind="Home" Width="20" Height="20" VerticalAlignment="Center"/>
1717
<TextBlock Text="Home" VerticalAlignment="Center" Margin="2,0,0,0"/>
1818
</StackPanel>
1919
</Button>
2020

21-
<Border BorderThickness="1" BorderBrush="Gray" Width="450" Height="300" ToolTip="Enter emote names, one in each line. A signal will be sent if any of these emotes is used in chat.">
22-
<RichTextBox x:Name="EmotesTextBox" AcceptsReturn="True" FontSize="16">
21+
<Border BorderThickness="1" BorderBrush="Gray" Width="450" Height="300" ToolTip="Enter emote names, separated with commas, semicolons or spaces. A signal will be sent if any of these emotes is used in chat." Margin="0,10,0,0">
22+
<TextBox x:Name="EmotesTextBox" AcceptsReturn="True" FontSize="16" Padding="8" MaxLines="14">
2323

24-
</RichTextBox>
24+
</TextBox>
2525
</Border>
2626
</StackPanel>
2727
</Page>

ArduinoTwitchBot.UI/Pages/EmotesPage.xaml.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.Windows;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Windows;
25
using System.Windows.Controls;
36

47
namespace ArduinoTwitchBot.UI.Pages
@@ -8,12 +11,29 @@ public partial class EmotesPage : Page
811
public EmotesPage()
912
{
1013
InitializeComponent();
14+
15+
// Works even if EmotesList is null.
16+
EmotesTextBox.Text = string.Join(", ", UserSettings.EmotesList?.ToArray());
17+
EmotesTextBox.Focus();
1118
}
1219

13-
private void MainPageButton_Click(object sender, RoutedEventArgs e)
20+
public void SaveEmotes()
1421
{
15-
// Save emotes.
22+
// Get Text from the RichTextBox.
23+
string text = EmotesTextBox.Text;
24+
// Save emotes to UserSettings.
25+
List<string> emotesList;
1626

27+
// Valid separators: comma, semicolon, space.
28+
char[] separators = new[] { ',', ';', ' ' };
29+
emotesList = text.Split(separators, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
30+
31+
UserSettings.EmotesList = emotesList;
32+
}
33+
34+
private void MainPageButton_Click(object sender, RoutedEventArgs e)
35+
{
36+
SaveEmotes();
1737

1838
(Application.Current.MainWindow as MainWindow).PageFrame.Navigate(new MainPage());
1939
}

ArduinoTwitchBot.UI/Pages/MainPage.xaml

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</StackPanel>
1919
</Button>
2020

21-
<StackPanel x:Name="FollowAlertPanel" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
21+
<StackPanel x:Name="FollowAlertPanel" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5,24,0,0">
2222
<CheckBox x:Name="FollowAlertCheckbox" IsThreeState="False" ToolTip="Should follower events be announced?" FontSize="18">
2323
<TextBlock Text="Follow alert" Margin="0,-2,10,0"/>
2424
</CheckBox>
@@ -83,25 +83,22 @@
8383

8484
</StackPanel>
8585

86-
<StackPanel x:Name="EmoteAlertPanel" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5" Visibility="Hidden">
87-
<CheckBox x:Name="EmoteAlertCheckbox" IsThreeState="False" ToolTip="Should certain emotes in chat be announced?" Margin="20,0,16,0" IsEnabled="False">
88-
<TextBlock Margin="0,-2,0,0">
89-
<Run Foreground="Black" Text="Emote alert" FontSize="14"/>
90-
<Run Foreground="OrangeRed" Text="(beta)" FontSize="14"/>
91-
</TextBlock>
86+
<StackPanel x:Name="EmoteAlertPanel" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
87+
<CheckBox x:Name="EmoteAlertCheckbox" IsThreeState="False" ToolTip="Should certain emotes in chat be announced? Make sure to edit the list of emotes, using the button to the right, before Connecting." Margin="38,0,0,0">
88+
<TextBlock Margin="0,-2,8,0" Text="Emote alert" FontSize="18"/>
9289
</CheckBox>
9390

94-
<TextBox x:Name="EmoteAlertSignalBox" Width="80" Margin="20,0,0,0" materialDesign:HintAssist.Hint="Value" ToolTip="Enter a value that will be sent through the Serial Port." FontSize="18"/>
91+
<TextBox x:Name="EmoteAlertSignalBox" Width="80" Margin="25,0,0,0" materialDesign:HintAssist.Hint="Value" ToolTip="Enter a value that will be sent through the Serial Port." FontSize="18"/>
9592

9693
<ComboBox x:Name="EmoteSignalTypeBox" Width="80" Margin="10,0,0,0" ToolTip="Should the given value be interpreted as an int, or a string?" FontSize="18">
9794
<!-- Dynamically generated values -->
9895
</ComboBox>
9996

100-
<materialDesign:PackIcon x:Name="SetEmoteListButton" Kind="PencilCircleOutline" Width="20" Height="20" Margin="20,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Right" Foreground="Purple" ToolTip="Click here to create or edit the list of announced emotes." PreviewMouseUp="SetEmoteListButton_PreviewMouseUp" Cursor="Hand"/>
97+
<materialDesign:PackIcon x:Name="SetEmoteListButton" Kind="PencilCircle" Width="20" Height="20" Margin="20,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Right" Foreground="Purple" ToolTip="Click here to create or edit the list of announced emotes." PreviewMouseUp="SetEmoteListButton_PreviewMouseUp" Cursor="Hand"/>
10198

10299
</StackPanel>
103100

104-
<Button x:Name="ConnectButton" Width="100" Height="40" Margin="0,10,0,20" Padding="0" ToolTip="Connect the bot! (make sure that you filled in everything in the Settings page before clicking this)." FontSize="18" Click="ConnectButton_Click">
101+
<Button x:Name="ConnectButton" Width="100" Height="40" Margin="0,41,0,0" Padding="0" ToolTip="Connect the bot! (make sure that you filled in everything in the Settings page before clicking this)." FontSize="18" Click="ConnectButton_Click">
105102
<StackPanel Orientation="Horizontal">
106103
<materialDesign:PackIcon Kind="Connection" Width="20" Height="20" VerticalAlignment="Center"/>
107104

ArduinoTwitchBot.UI/Pages/MainPage.xaml.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,18 @@ private void ConnectButton_Click(object sender, RoutedEventArgs e)
161161

162162
var alerts = UserSettings.Alerts;
163163

164-
// Connect the bot.
164+
// Connect the PubSub client if any (or all) of the 1-5 alerts have been selected.
165+
// Else connect the Chat client.
165166
try
166167
{
167-
TwitchBot.Instance.Connect(UserSettings.ClientId, UserSettings.AccessToken, UserSettings.ChannelName, UserSettings.PortName, alerts);
168+
if (alerts.Take(5).Any(x => x.IsActive))
169+
{
170+
TwitchBot.Instance.Connect(UserSettings.ClientId, UserSettings.AccessToken, UserSettings.ChannelName, UserSettings.PortName, alerts);
171+
}
172+
if (alerts[5].IsActive)
173+
{
174+
TwitchBot.Instance.ConnectChatClient(UserSettings.AccessToken, UserSettings.ChannelName, UserSettings.PortName, alerts[5], UserSettings.EmotesList);
175+
}
168176
}
169177
catch (Exception ex)
170178
{

ArduinoTwitchBot.UI/Properties/Settings.Designer.cs

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ArduinoTwitchBot.UI/Properties/Settings.settings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,8 @@
7171
<Setting Name="EmoteAlertType" Type="System.String" Scope="User">
7272
<Value Profile="(Default)" />
7373
</Setting>
74+
<Setting Name="EmotesList" Type="System.Collections.Specialized.StringCollection" Scope="User">
75+
<Value Profile="(Default)" />
76+
</Setting>
7477
</Settings>
7578
</SettingsFile>

ArduinoTwitchBot.UI/UserSettings.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using ArduinoTwitchBot.Code;
22
using System;
3+
using System.Collections.Generic;
4+
using System.Collections.Specialized;
5+
using System.Linq;
36

47
namespace ArduinoTwitchBot.UI
58
{
@@ -13,6 +16,7 @@ public static class UserSettings
1316
public static string ChannelName { get; set; }
1417
public static Alert[] Alerts { get; set; }
1518
public static bool IsDarkTheme { get; set; }
19+
public static List<string> EmotesList { get; set; }
1620

1721
#endregion
1822

@@ -80,8 +84,13 @@ public static void SaveUserSettings()
8084
Properties.Settings.Default["EmoteAlert"] = Alerts[5].IsActive;
8185
Properties.Settings.Default["EmoteAlertValue"] = Alerts[5].Signal;
8286
Properties.Settings.Default["EmoteAlertType"] = Alerts[5].SignalType.ToString();
87+
88+
// Save emotes list.
89+
var stringCollection = new StringCollection();
90+
stringCollection.AddRange(EmotesList.ToArray());
91+
Properties.Settings.Default["EmotesList"] = stringCollection;
8392
}
84-
catch (Exception ex)
93+
catch (Exception)
8594
{
8695
throw;
8796
}
@@ -118,6 +127,9 @@ public static void LoadUserSettings()
118127

119128
new Alert(bool.Parse(Properties.Settings.Default["EmoteAlert"].ToString()), Properties.Settings.Default["EmoteAlertValue"].ToString(), (SignalType)Enum.Parse(typeof(SignalType),Properties.Settings.Default["EmoteAlertType"].ToString()))
120129
};
130+
131+
// Load emotes list.
132+
EmotesList = (Properties.Settings.Default["EmotesList"] as StringCollection).Cast<string>().ToList();
121133
}
122134
catch (Exception)
123135
{
@@ -127,6 +139,7 @@ public static void LoadUserSettings()
127139
AccessToken = AccessToken is null ? "" : AccessToken;
128140
ChannelName = ChannelName is null ? "" : ChannelName;
129141
Alerts = Alerts is null ? Array.Empty<Alert>() : Alerts;
142+
EmotesList = EmotesList is null ? new List<string>() : EmotesList;
130143
}
131144
}
132145
}

0 commit comments

Comments
 (0)