diff --git a/.github/workflows/check-buildstatus.yml b/.github/workflows/check-buildstatus.yml
index a5c4d329..26693bd5 100644
--- a/.github/workflows/check-buildstatus.yml
+++ b/.github/workflows/check-buildstatus.yml
@@ -1,4 +1,4 @@
-name: Check TwitchLib.Api PR Build Status
+name: Check TwitchLib.Client PR Build Status
on:
pull_request:
@@ -10,12 +10,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v5
- name: Setup .NET
- uses: actions/setup-dotnet@v2
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 6.0.x
+ dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- - name: Build TwitchLib.Api
+ - name: Build TwitchLib.Client
run: dotnet build --no-restore
+ - name: Test TwitchLib.Client
+ run: dotnet test --no-build --verbosity normal
diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml
index 3a7e2db0..3c2a6af8 100644
--- a/.github/workflows/preview-release.yml
+++ b/.github/workflows/preview-release.yml
@@ -8,21 +8,31 @@ jobs:
release-preview:
if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')"
runs-on: ubuntu-latest
+ permissions:
+ id-token: write # enable GitHub OIDC token issuance for this job
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v5
+ - uses: benjlevesque/short-sha@v3.0
+ id: short-sha
- name: Setup .NET
- uses: actions/setup-dotnet@v2
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 6.0.x
+ dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build TwitchLib.Client
run: dotnet build -c Release --no-restore
- name: Pack TwitchLib.Client
- run: dotnet pack TwitchLib.Client.sln -v normal -c Release -o nugets --no-build --version-suffix "preview-${{ github.sha }}"
+ run: dotnet pack -v normal -c Release --no-build --version-suffix "preview.${{ github.run_number }}.${{ steps.short-sha.outputs.sha }}"
+ # Get a short-lived NuGet API key
+ - name: NuGet login (OIDC → temp API key)
+ uses: NuGet/login@v1
+ id: login
+ with:
+ user: ${{ secrets.NUGET_USER }}
- name: Push to Nuget
- run: dotnet nuget push "./nugets/*.nupkg" -k ${{ secrets.API_NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json
+ run: dotnet nuget push "./artifacts/package/release/*.nupkg" --api-key ${{steps.login.outputs.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
- name: Send Discord Notification
uses: sarisia/actions-status-discord@v1
if: always()
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 026ef91a..3e1bfbb7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -8,21 +8,29 @@ jobs:
release:
if: "contains(toJSON(github.event.commits.*.message), '[Release]')"
runs-on: ubuntu-latest
-
+ permissions:
+ id-token: write # enable GitHub OIDC token issuance for this job
+
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v5
- name: Setup .NET
- uses: actions/setup-dotnet@v2
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 6.0.x
+ dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build TwitchLib.Client
run: dotnet build -c Release --no-restore
- name: Pack TwitchLib.Client
- run: dotnet pack TwitchLib.Client.sln -v normal -c Release -o nugets --no-build
+ run: dotnet pack -v normal -c Release --no-build
+ # Get a short-lived NuGet API key
+ - name: NuGet login (OIDC → temp API key)
+ uses: NuGet/login@v1
+ id: login
+ with:
+ user: ${{ secrets.NUGET_USER }}
- name: Push to Nuget
- run: dotnet nuget push "./nugets/*.nupkg" -k ${{ secrets.API_NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json
+ run: dotnet nuget push "./artifacts/package/release/*.nupkg" --api-key ${{steps.login.outputs.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
- name: Send Discord Notification
uses: sarisia/actions-status-discord@v1
if: always()
@@ -30,4 +38,4 @@ jobs:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
title: "Release Nuget Package"
username: "TwitchLib Deploy"
-
\ No newline at end of file
+
diff --git a/.gitignore b/.gitignore
index 89ad0e0e..c7b1e2c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -188,6 +188,9 @@ UpgradeLog*.htm
# Microsoft Fakes
FakesAssemblies/
+# BenchmarkDotNet artifacts
+BenchmarkDotNet.Artifacts/
+
# =========================
# Operating System Files
# =========================
@@ -233,4 +236,6 @@ $RECYCLE.BIN/
*.msp
# Windows shortcuts
-*.lnk
\ No newline at end of file
+*.lnk
+
+.idea/*
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..44941166
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,36 @@
+
+
+
+
+ $(MSBuildThisFileDirectory)artifacts
+ enable
+ enable
+ latest
+ strict
+
+ en-US
+ True
+
+
+
+
+ swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin
+ Copyright 2025
+ twitchlib.png
+ https://github.com/TwitchLib/TwitchLib.Client
+ https://github.com/TwitchLib/TwitchLib.Client
+ Git
+ twitch library irc chat c# csharp api events net
+ a lot of changes
+ 4.0.1
+ $(VersionSuffix)
+ $(VersionPrefix)
+ $(VersionPrefix)
+ MIT
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index ba9c95a2..8d924345 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,83 @@
-# TwitchLib.Client
+# TwitchLib.Client
Client component of TwitchLib.
-For a general overview and example, refer to https://github.com/TwitchLib/TwitchLib/blob/master/README.md
+> [!TIP]
+> With the introduction of Chat on EventSub, it is recommended to upgrade your chatbots that are using Twitch IRC to use EventSub (for reading chat messages and roomstates) and Twitch API (for sending chat messages).
+
+## Installation
+
+| NuGet | | [![TwitchLib.Client][1]][2] |
+| :--------------- | ----: | :---------------------------------------------------------------- |
+| Package Manager | `PM>` | `Install-Package TwitchLib.Client -Version 4.0.1` |
+| .NET CLI | `>` | `dotnet add package TwitchLib.Client --version 4.0.1` |
+| PackageReference | | `` |
+| Paket CLI | `>` | `paket add TwitchLib.Client --version 4.0.1` |
+
+[1]: https://img.shields.io/nuget/v/TwitchLib.Client.svg?label=TwitchLib.Client
+[2]: https://www.nuget.org/packages/TwitchLib.Client
+
+## ⚠ Breaking Changes in Version 4.0.1 ⚠
+
+Version 4.0.1 contains breaking changes.
+- Removed obsolete methods.
+- Methods are now asynchronous. (The return value changed from `void` to `Task` and gains `Async` suffix)
+- Events are now asynchronous (return value changed from `void` to `Task`)
+- `Add/RemoveChatCommandIdentifier` methods were removed, use `ChatCommandIdentifiers` property instead (same applies to whisper) and uses `string` instead of `char`
+- `OnLog` event was removed (you can still use `ILoggerFactory` to get logs)
+- removed builders classes (removed `TwitchLib.Client.Models.Builders namespace`)
+- changed public fields to properties
+- rewritten all models in `TwitchLib.Client.Models`
+ - some props/classes can be slightly renamed
+ - some properties (`IsModerator`, `IsSubscriber`, `HasTurbo`, `IsVip`, `IsPartner`, `IsStaff`) moved to the `UserDetails` property
+
+## Minimal Setup
+Step 1: Create a new Console project
+
+Step 2: Install the `TwitchLib.Client` NuGet package. (See above on how to do that)
+
+Step 2.5: (Optional) Install the [`Microsoft.Extensions.Logging.Console`](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Console) NuGet package (or some other compatible logging provider) to see logs.
+
+Step 3: Add the following code to your `Program.cs` file:
+```cs
+using Microsoft.Extensions.Logging;
+using TwitchLib.Client;
+using TwitchLib.Client.Events;
+using TwitchLib.Client.Models;
+
+// Optional logger
+var loggerFactory = LoggerFactory.Create(c => c
+ .AddConsole()
+// .SetMinimumLevel(LogLevel.Trace) // uncomment to view raw messages received from twitch
+);
+
+var credentials = new ConnectionCredentials(); // anonymous user, add Username and OAuth token to get the ability to send messages
+var client = new TwitchClient(loggerFactory: loggerFactory);
+
+client.Initialize(credentials);
+client.OnConnected += Client_OnConnected;
+client.OnJoinedChannel += Client_OnJoinedChannel;
+client.OnMessageReceived += Client_OnMessageReceived;
+
+await client.ConnectAsync();
+await Task.Delay(Timeout.Infinite);
+
+
+async Task Client_OnConnected(object? sender, OnConnectedEventArgs e)
+{
+ await client.JoinChannelAsync("channel_name"); // replace with the channel you want to join
+}
+
+async Task Client_OnJoinedChannel(object? sender, OnJoinedChannelArgs e)
+{
+ Console.WriteLine($"Connected to {e.Channel}");
+ await client.SendMessageAsync(e.Channel, "Hey guys! I am a bot connected via TwitchLib!");
+}
+
+async Task Client_OnMessageReceived(object? sender, OnMessageReceivedArgs e)
+{
+ Console.WriteLine($"{e.ChatMessage.Username}#{e.ChatMessage.Channel}: {e.ChatMessage.Message}");
+}
+```
+Step 4: Change the `channel_name` in the `Client_OnConnected` method to the name of the channel you want to join and run the application.
+
+More complete examples can be found in the [TwitchLib.Client.Example](/TwitchLib.Client.Example/)
diff --git a/TwitchLib.Client.Benchmark/ChatMessage.cs b/TwitchLib.Client.Benchmark/ChatMessage.cs
new file mode 100644
index 00000000..75c562fd
--- /dev/null
+++ b/TwitchLib.Client.Benchmark/ChatMessage.cs
@@ -0,0 +1,22 @@
+using BenchmarkDotNet.Attributes;
+using TwitchLib.Client.Parsing;
+using TwitchLib.Client.Models;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Benchmark
+{
+ [ShortRunJob, MemoryDiagnoser]
+ public class ChatMessageBenchmark
+ {
+ private static readonly IrcMessage Message = IrcParser.ParseMessage(
+ "@badge-info=subscriber/22;badges=subscriber/3012;color=#FFFF00;display-name=FELYP8;emote-only=1;emotes=521050:0-6,8-14,16-22,24-30,32-38,40-46,48-54,56-62,64-70,72-78,80-86,88-94,96-102,104-110,148-154,156-162,164-170,172-178,180-186,188-194,196-202,204-210,212-218,220-226,228-234,236-242,244-250,252-258,260-266/302827730:112-119/302827734:121-128/302827735:130-137/302827737:139-146;first-msg=0;flags=;id=1844235a-c24e-4e18-937b-805d6601aebe;mod=0;returning-chatter=0;room-id=22484632;subscriber=1;tmi-sent-ts=1685664001040;turbo=0;user-id=162760707;user-type= :felyp8!felyp8@felyp8.tmi.twitch.tv PRIVMSG #forsen :forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE1 forsenE2 forsenE3 forsenE4 forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE");
+
+ private readonly MessageEmoteCollection ChannelEmotes = new();
+
+ [Benchmark]
+ public ChatMessage ClientScenarioCreate()
+ {
+ return new("test_name", Message, ChannelEmotes);
+ }
+ }
+}
diff --git a/TwitchLib.Client.Benchmark/IrcMessageHandler.cs b/TwitchLib.Client.Benchmark/IrcMessageHandler.cs
new file mode 100644
index 00000000..645963cb
--- /dev/null
+++ b/TwitchLib.Client.Benchmark/IrcMessageHandler.cs
@@ -0,0 +1,18 @@
+using BenchmarkDotNet.Attributes;
+using TwitchLib.Client.Parsing;
+
+namespace TwitchLib.Client.Benchmark
+{
+ [MemoryDiagnoser]
+ public class IrcMessageHandlerBenchmark
+ {
+ [Benchmark]
+ public bool ParseAndCheckFailedAuth()
+ {
+ return IrcParser
+ .ParseMessage(@"@badges=subscriber/0;color=#0000FF;display-name=KittyJinxu;emotes=30259:0-6;id=1154b7c0-8923-464e-a66b-3ef55b1d4e50;login=kittyjinxu;mod=0;msg-id=ritual;msg-param-ritual-name=new_chatter;room-id=35740817;subscriber=1;system-msg=@KittyJinxu\sis\snew\shere.\sSay\shello!;tmi-sent-ts=1514387871555;turbo=0;user-id=187446639;user-type= USERNOTICE #thorlar kittyjinxu > #thorlar: HeyGuys")
+ .ToString()
+ .StartsWith(":tmi.twitch.tv NOTICE * :Login authentication failed");
+ }
+ }
+}
diff --git a/TwitchLib.Client.Benchmark/IrcParser.cs b/TwitchLib.Client.Benchmark/IrcParser.cs
new file mode 100644
index 00000000..c093de85
--- /dev/null
+++ b/TwitchLib.Client.Benchmark/IrcParser.cs
@@ -0,0 +1,23 @@
+using BenchmarkDotNet.Attributes;
+using TwitchLib.Client.Parsing;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Benchmark
+{
+ [MemoryDiagnoser]
+ public class IrcParserBenchmark
+ {
+ [Params(
+ "@msg-id=host_on :tmi.twitch.tv NOTICE #burkeblack :Now hosting DjTechlive.",
+ ":blubott.tmi.twitch.tv 366 blubott #monstercat :End of /NAMES list",
+ ":jtv!jtv@jtv.tmi.twitch.tv PRIVMSG (HOSTED):(HOSTER) is now hosting you for (VIEWERS_TOTAL) viewers.",
+ "@msg-id=raid_notice_mature :tmi.twitch.tv NOTICE #swiftyspiffy :This channel is intended for mature audiences.",
+ @"@badges=subscriber/0;color=#0000FF;display-name=KittyJinxu;emotes=30259:0-6;id=1154b7c0-8923-464e-a66b-3ef55b1d4e50;login=kittyjinxu;mod=0;msg-id=ritual;msg-param-ritual-name=new_chatter;room-id=35740817;subscriber=1;system-msg=@KittyJinxu\sis\snew\shere.\sSay\shello!;tmi-sent-ts=1514387871555;turbo=0;user-id=187446639;user-type= USERNOTICE #thorlar kittyjinxu > #thorlar: HeyGuys",
+ "@badge-info=subscriber/22;badges=subscriber/3012;color=#FFFF00;display-name=FELYP8;emote-only=1;emotes=521050:0-6,8-14,16-22,24-30,32-38,40-46,48-54,56-62,64-70,72-78,80-86,88-94,96-102,104-110,148-154,156-162,164-170,172-178,180-186,188-194,196-202,204-210,212-218,220-226,228-234,236-242,244-250,252-258,260-266/302827730:112-119/302827734:121-128/302827735:130-137/302827737:139-146;first-msg=0;flags=;id=1844235a-c24e-4e18-937b-805d6601aebe;mod=0;returning-chatter=0;room-id=22484632;subscriber=1;tmi-sent-ts=1685664001040;turbo=0;user-id=162760707;user-type= :felyp8!felyp8@felyp8.tmi.twitch.tv PRIVMSG #forsen :forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE1 forsenE2 forsenE3 forsenE4 forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE forsenE"
+ )]
+ public string? Message;
+
+ [Benchmark]
+ public IrcMessage Parse() => IrcParser.ParseMessage(Message!);
+ }
+}
diff --git a/TwitchLib.Client.Benchmark/Program.cs b/TwitchLib.Client.Benchmark/Program.cs
new file mode 100644
index 00000000..e4ee9667
--- /dev/null
+++ b/TwitchLib.Client.Benchmark/Program.cs
@@ -0,0 +1,5 @@
+using BenchmarkDotNet.Running;
+
+BenchmarkSwitcher
+ .FromAssembly(typeof(Program).Assembly)
+ .Run(args);
\ No newline at end of file
diff --git a/TwitchLib.Client.Benchmark/TwitchLib.Client.Benchmark.csproj b/TwitchLib.Client.Benchmark/TwitchLib.Client.Benchmark.csproj
new file mode 100644
index 00000000..8987b778
--- /dev/null
+++ b/TwitchLib.Client.Benchmark/TwitchLib.Client.Benchmark.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ false
+ nullable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TwitchLib.Client.Enums/ChatColorPresets.cs b/TwitchLib.Client.Enums/ChatColorPresets.cs
deleted file mode 100644
index 47748db4..00000000
--- a/TwitchLib.Client.Enums/ChatColorPresets.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace TwitchLib.Client.Enums
-{
- /// Enum representing the available chat name color presets.
- public enum ChatColorPresets
- {
- /// Chat color name.
- Blue,
- /// Chat color name.
- Coral,
- /// Chat color name.
- DodgerBlue,
- /// Chat color name.
- SpringGreen,
- /// Chat color name.
- YellowGreen,
- /// Chat color name.
- Green,
- /// Chat color name.
- OrangeRed,
- /// Chat color name.
- Red,
- /// Chat color name.
- GoldenRod,
- /// Chat color name.
- HotPink,
- /// Chat color name.
- CadetBlue,
- /// Chat color name.
- SeaGreen,
- /// Chat color name.
- Chocolate,
- /// Chat color name.
- BlueViolet,
- /// Chat color name.
- Firebrick
- }
-}
diff --git a/TwitchLib.Client.Enums/CommercialLength.cs b/TwitchLib.Client.Enums/CommercialLength.cs
deleted file mode 100644
index 41a4d378..00000000
--- a/TwitchLib.Client.Enums/CommercialLength.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace TwitchLib.Client.Enums
-{
- /// A list of valid commercial lengths.
- public enum CommercialLength
- {
- /// 30 second commercial
- Seconds30 = 30,
- /// 60 second commercial
- Seconds60 = 60,
- /// 90 second commercial
- Seconds90 = 90,
- /// 120 second commercial
- Seconds120 = 120,
- /// 150 second commercial
- Seconds150 = 150,
- /// 180 second commercial
- Seconds180 = 180
- }
-}
diff --git a/TwitchLib.Client.Enums/PaidLevel.cs b/TwitchLib.Client.Enums/PaidLevel.cs
new file mode 100644
index 00000000..75ceae4b
--- /dev/null
+++ b/TwitchLib.Client.Enums/PaidLevel.cs
@@ -0,0 +1,18 @@
+namespace TwitchLib.Client.Enums;
+
+///
+/// The level of the Hype Chat.
+///
+public enum PaidLevel
+{
+ One = 1,
+ Two,
+ Three,
+ Four,
+ Five,
+ Six,
+ Seven,
+ Eight,
+ Nine,
+ Ten,
+}
diff --git a/TwitchLib.Client.Enums/StringEnum.cs b/TwitchLib.Client.Enums/StringEnum.cs
deleted file mode 100644
index b7ed9ba3..00000000
--- a/TwitchLib.Client.Enums/StringEnum.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace TwitchLib.Client.Enums
-{
- ///Really cool way of doing string enums, credits to RogueException (https://github.com/RogueException)
- public abstract class StringEnum
- {
- /// Value of enum
- public string Value { get; }
-
- /// StringEnum constructor.
- protected StringEnum(string value)
- {
- Value = value;
- }
-
- /// Returns string value for overriden ToString()
- /// Enum value
- public override string ToString() => Value;
- }
-}
diff --git a/TwitchLib.Client.Enums/ThrottleType.cs b/TwitchLib.Client.Enums/ThrottleType.cs
deleted file mode 100644
index 7fded858..00000000
--- a/TwitchLib.Client.Enums/ThrottleType.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace TwitchLib.Client.Enums
-{
- /// Enum representing the available throttle types.
- public enum ThrottleType
- {
- /// Throttle based on message being too short.
- MessageTooShort = 0,
- /// Throttle based on message being too long.
- MessageTooLong = 1
- }
-}
diff --git a/TwitchLib.Client.Enums/TwitchLib.Client.Enums.csproj b/TwitchLib.Client.Enums/TwitchLib.Client.Enums.csproj
index bcac43e6..b6eb516d 100644
--- a/TwitchLib.Client.Enums/TwitchLib.Client.Enums.csproj
+++ b/TwitchLib.Client.Enums/TwitchLib.Client.Enums.csproj
@@ -1,24 +1,8 @@
- netstandard2.0
- TwitchLib.Client.Enums
- 3.4.0
- $(VersionSuffix)
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
Project containing all of the enums used in TwitchLib.Client.
- true
- swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin
- https://cdn.syzuna-programs.de/images/twitchlib.png
- https://github.com/TwitchLib/TwitchLib.Client
- https://opensource.org/licenses/MIT
- Copyright 2023
- Incremental changes. See commit history.
- https://github.com/TwitchLib/TwitchLib.Client
- Git
- twitch library irc chat c# csharp api events pubsub net standard 2.0
- en-US
- 3.4.0
- 3.4.0
- True
+
diff --git a/TwitchLib.Client.Enums/UserDetails.cs b/TwitchLib.Client.Enums/UserDetails.cs
new file mode 100644
index 00000000..f35aaeaa
--- /dev/null
+++ b/TwitchLib.Client.Enums/UserDetails.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace TwitchLib.Client.Enums;
+
+[Flags]
+public enum UserDetails
+{
+ None = 0,
+ Moderator = 1,
+ Turbo = 2,
+ Subscriber = 4,
+ Vip = 8,
+
+ Partner = 16,
+ Staff = 32,
+}
diff --git a/TwitchLib.Client.Example/Program.cs b/TwitchLib.Client.Example/Program.cs
new file mode 100644
index 00000000..3ad01537
--- /dev/null
+++ b/TwitchLib.Client.Example/Program.cs
@@ -0,0 +1,101 @@
+using Microsoft.Extensions.Logging;
+using TwitchLib.Client;
+using TwitchLib.Client.Events;
+using TwitchLib.Client.Models;
+
+var loggerFactory = LoggerFactory.Create(c => c
+ .AddConsole()
+ //.SetMinimumLevel(LogLevel.Trace) // uncomment to view raw messages received from twitch
+);
+
+var credentials = new ConnectionCredentials(); // anonymous user, add Username and OAuth token to get the ability to send messages
+var client = new TwitchClient(loggerFactory: loggerFactory)
+{
+ ChatCommandIdentifiers = { "!", "?" }, // you can customize the command identifiers, if not set, defaults to '!'
+};
+
+client.Initialize(credentials);
+client.OnConnected += Client_OnConnected;
+client.OnJoinedChannel += Client_OnJoinedChannel;
+client.OnMessageReceived += Client_OnMessageReceived;
+
+client.OnChatCommandReceived += Client_OnChatCommandReceived;
+
+await client.ConnectAsync();
+await Task.Delay(Timeout.Infinite);
+
+
+async Task Client_OnConnected(object? sender, OnConnectedEventArgs e)
+{
+ await client.JoinChannelAsync("channel_name"); // replace with the channel you want to join
+}
+
+async Task Client_OnJoinedChannel(object? sender, OnJoinedChannelArgs e)
+{
+ Console.WriteLine($"Connected to {e.Channel}");
+ await client.SendMessageAsync(e.Channel, "Hey guys! I am a bot connected via TwitchLib!");
+}
+
+async Task Client_OnMessageReceived(object? sender, OnMessageReceivedArgs e)
+{
+ Console.WriteLine($"{e.ChatMessage.Username}#{e.ChatMessage.Channel}: {e.ChatMessage.Message}");
+}
+
+async Task Client_OnChatCommandReceived(object? sender, OnChatCommandReceivedArgs e)
+{
+ var channel = e.ChatMessage.Channel;
+ var command = e.Command;
+
+ switch (command.Name.ToLower())
+ {
+ case "info":
+ await client.SendMessageAsync(channel, "Hi, I am a bot created using TwitchLib.Client.");
+ break;
+ case "repeat":
+ if (string.IsNullOrWhiteSpace(command.ArgumentsAsString))
+ return;
+ await client.SendMessageAsync(channel, command.ArgumentsAsString);
+ break;
+ case "leave":
+ if (!e.ChatMessage.IsBroadcaster)
+ return;
+ await client.SendMessageAsync(channel, "Goodbye! I am leaving the channel.");
+ await client.LeaveChannelAsync(channel);
+ break;
+ case "help":
+ if (command.ArgumentsAsList.Count is 0)
+ {
+ var helpText = "Available commands: !info, !repeat , !help [command]";
+ if (e.ChatMessage.IsBroadcaster)
+ helpText += ", !leave";
+
+ await client.SendMessageAsync(channel, helpText);
+ return;
+ }
+ switch (command.ArgumentsAsList[0])
+ {
+ case "info":
+ await client.SendMessageAsync(channel, "The !info command provides information about the bot.");
+ break;
+ case "repeat":
+ await client.SendMessageAsync(channel, "The !repeat command repeats the message you provide.");
+ break;
+ case "help":
+ await client.SendMessageAsync(channel, "The !help command provides information about available commands.");
+ break;
+ case "leave":
+ if (!e.ChatMessage.IsBroadcaster)
+ return;
+
+ await client.SendMessageAsync(channel, "The !leave command makes the bot leave the channel.");
+ break;
+ default:
+ await client.SendMessageAsync(channel, $"No help available for command: {command.ArgumentsAsList[0]}");
+ break;
+ }
+ break;
+ default:
+ Console.WriteLine($"Unknown command: {command.Name}");
+ break;
+ }
+}
diff --git a/TwitchLib.Client.Example/TwitchLib.Client.Example.csproj b/TwitchLib.Client.Example/TwitchLib.Client.Example.csproj
new file mode 100644
index 00000000..4fe86fd6
--- /dev/null
+++ b/TwitchLib.Client.Example/TwitchLib.Client.Example.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TwitchLib.Client.Models/Announcement.cs b/TwitchLib.Client.Models/Announcement.cs
index 5fd56c37..856c1b7b 100644
--- a/TwitchLib.Client.Models/Announcement.cs
+++ b/TwitchLib.Client.Models/Announcement.cs
@@ -1,185 +1,78 @@
-using System.Collections.Generic;
-using System.Drawing;
-using TwitchLib.Client.Enums;
-using TwitchLib.Client.Models.Extensions.NetCore;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
-{
- /// Class representing Announcement in a Twitch channel.
- public class Announcement
- {
- /// Property representing announcement message id
- public string Id { get; }
-
- /// Property representing list of badges assigned.
- public List> Badges { get; }
-
- /// Metadata associated with each badge
- public List> BadgeInfo { get; }
-
- /// Property representing internal system message value.
- public string SystemMessage { get; }
-
- /// Property representing internal system message value, parsed.
- public string SystemMessageParsed { get; }
-
- /// Property representing whether or not the announcer is the broadcaster.
- public bool IsBroadcaster { get; }
-
- /// Property representing whether or not the announcer is a moderator.
- public bool IsModerator { get; }
-
- /// Property representing whether or not announcer is a partner.
- public bool IsPartner { get; }
-
- /// Property representing whether or not the announcer is a subscriber.
- public bool IsSubscriber { get; }
-
- /// Property representing whether or not the announcer is a twitch staff member.
- public bool IsStaff { get; }
-
- /// Property representing whether or not the announcer is a turbo member.
- public bool IsTurbo { get; }
-
- /// Property representing the user's login.
- public string Login { get; }
-
- /// Property representing the user's id.
- public string UserId { get; }
+namespace TwitchLib.Client.Models;
- /// Property representing the room id.
- public string RoomId { get; }
-
- /// Property representing the user type of the announcer.
- public UserType UserType { get; }
-
- /// Property representing the tmi-sent-ts value.
- public string TmiSentTs { get; }
-
- /// Property representing emote set of announcement.
- public string EmoteSet { get; }
-
- /// Property representing the raw IRC message (for debugging/customized parsing)
- public string RawIrc { get; }
-
- /// Property representing the msg-id value.
- public string MsgId { get; }
-
- /// Property representing the color value of the announcement.
- public string MsgParamColor { get; }
+/// Class representing Announcement in a Twitch channel.
+public class Announcement : UserNoticeBase
+{
+ /// Property representing the color value of the announcement.
+ public string MsgParamColor { get; protected set; } = default!;
- /// Property representing the colorhex of the announcer.
- public string ColorHex { get; }
+ /// Property representing the message of the announcement.
+ public string Message { get; protected set; }
- /// Property representing HEX color as a System.Drawing.Color object.
- public Color Color { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The IRC message from Twitch to be processed.
+ public Announcement(IrcMessage ircMessage) : base(ircMessage)
+ {
+ Message = ircMessage.Message;
+ }
- /// Property representing the message of the announcement.
- public string Message { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Announcement(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ string msgParamColor,
+ string message)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamColor = msgParamColor;
+ Message = message;
+ }
- /// SAMPLE: @badge-info=;badges=broadcaster/1,ambassador/1;color=#033700;display-name=BarryCarlyon;emotes=;flags=;id=55d90904-e515-47d0-ac1d-879f7f1d7b01;login=barrycarlyon;mod=0;msg-id=announcement;msg-param-color=PRIMARY;room-id=15185913;subscriber=0;system-msg=;tmi-sent-ts=1648758023469;user-id=15185913;user-type= :tmi.twitch.tv USERNOTICE #barrycarlyon :test announcment main
- /// Announcement object constructor.
- /// The IRC message from Twitch to be processed.
- public Announcement(IrcMessage ircMessage)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
- RawIrc = ircMessage.ToString();
- Message = ircMessage.Message;
-
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- foreach (var badge in Badges)
- {
- switch (badge.Key)
- {
- case "broadcaster":
- IsBroadcaster = true;
- break;
- case "turbo":
- IsTurbo = true;
- break;
- case "moderator":
- IsModerator = true;
- break;
- case "subscriber":
- IsSubscriber = true;
- break;
- case "admin":
- IsStaff = true;
- break;
- case "staff":
- IsStaff = true;
- break;
- case "partner":
- IsPartner = true;
- break;
- }
- }
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- ColorHex = tagValue;
- if (!string.IsNullOrEmpty(ColorHex))
- Color = ColorTranslator.FromHtml(ColorHex);
- break;
- case Tags.MsgParamColor:
- MsgParamColor = tagValue;
- break;
- case Tags.Emotes:
- EmoteSet = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.SystemMsg:
- SystemMessage = tagValue;
- SystemMessageParsed = tagValue.Replace("\\s", " ");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.UserId:
- UserId = tagValue;
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- }
- }
+ case Tags.MsgParamColor:
+ MsgParamColor = tag.Value;
+ break;
+ default:
+ return false;
}
+ return true;
}
-}
\ No newline at end of file
+}
diff --git a/TwitchLib.Client.Models/AnonGiftPaidUpgrade.cs b/TwitchLib.Client.Models/AnonGiftPaidUpgrade.cs
new file mode 100644
index 00000000..9031ff31
--- /dev/null
+++ b/TwitchLib.Client.Models/AnonGiftPaidUpgrade.cs
@@ -0,0 +1,82 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class AnonGiftPaidUpgrade : UserNoticeBase
+{
+ ///
+ /// The number of gifts the gifter has given during the promo indicated by .
+ ///
+ public int MsgParamPromoGiftTotal { get; protected set; }
+
+ ///
+ /// The subscriptions promo, if any, that is ongoing (for example, Subtember 2018).
+ ///
+ public string MsgParamPromoName { get; protected set; } = default!;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AnonGiftPaidUpgrade(IrcMessage ircMessage) : base(ircMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AnonGiftPaidUpgrade(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ int msgParamPromoGiftTotal,
+ string msgParamPromoName)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamPromoGiftTotal = msgParamPromoGiftTotal;
+ MsgParamPromoName = msgParamPromoName;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamPromoGiftTotal:
+ MsgParamPromoGiftTotal = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamPromoName:
+ MsgParamPromoName = tag.Value;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/BitsBadgeTier.cs b/TwitchLib.Client.Models/BitsBadgeTier.cs
new file mode 100644
index 00000000..d92d32b1
--- /dev/null
+++ b/TwitchLib.Client.Models/BitsBadgeTier.cs
@@ -0,0 +1,70 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class BitsBadgeTier : UserNoticeBase
+{
+ ///
+ /// The tier of the Bits badge the user just earned. For example, 100, 1000, or 10000.
+ ///
+ public int MsgParamThreshold { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BitsBadgeTier(IrcMessage ircMessage) : base(ircMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BitsBadgeTier(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ int msgParamThreshold)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamThreshold = msgParamThreshold;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamThreshold:
+ MsgParamThreshold = int.Parse(tag.Value);
+ break;
+ }
+ return true;
+ }
+}
diff --git a/TwitchLib.Client.Models/Builders/ChannelStateBuilder.cs b/TwitchLib.Client.Models/Builders/ChannelStateBuilder.cs
deleted file mode 100644
index bd9bb1ce..00000000
--- a/TwitchLib.Client.Models/Builders/ChannelStateBuilder.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ChannelStateBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private string _broadcasterLanguage;
- private string _channel;
- private bool _emoteOnly;
- private TimeSpan _followersOnly;
- private bool _mercury;
- private bool _r9K;
- private bool _rituals;
- private string _roomId;
- private int _slowMode;
- private bool _subOnly;
-
- private ChannelStateBuilder()
- {
- }
-
- public ChannelStateBuilder WithBroadcasterLanguage(string broadcasterLanguage)
- {
- _broadcasterLanguage = broadcasterLanguage;
- return this;
- }
-
- public ChannelStateBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public ChannelStateBuilder WithEmoteOnly(bool emoteOnly)
- {
- _emoteOnly = emoteOnly;
- return this;
- }
-
- public ChannelStateBuilder WIthFollowersOnly(TimeSpan followersOnly)
- {
- _followersOnly = followersOnly;
- return this;
- }
-
- public ChannelStateBuilder WithMercury(bool mercury)
- {
- _mercury = mercury;
- return this;
- }
-
- public ChannelStateBuilder WithR9K(bool r9k)
- {
- _r9K = r9k;
- return this;
- }
-
- public ChannelStateBuilder WithRituals(bool rituals)
- {
- _rituals = rituals;
- return this;
- }
-
- public ChannelStateBuilder WithRoomId(string roomId)
- {
- _roomId = roomId;
- return this;
- }
-
- public ChannelStateBuilder WithSlowMode(int slowMode)
- {
- _slowMode = slowMode;
- return this;
- }
-
- public ChannelStateBuilder WithSubOnly(bool subOnly)
- {
- _subOnly = subOnly;
- return this;
- }
-
- public static ChannelStateBuilder Create()
- {
- return new ChannelStateBuilder();
- }
-
- public ChannelState Build()
- {
- return new ChannelState(
- _r9K,
- _rituals,
- _subOnly,
- _slowMode,
- _emoteOnly,
- _broadcasterLanguage,
- _channel,
- _followersOnly,
- _mercury,
- _roomId);
- }
-
- public ChannelState BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new ChannelState(fromIrcMessageBuilderDataObject.Message);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/ChatCommandBuilder.cs b/TwitchLib.Client.Models/Builders/ChatCommandBuilder.cs
deleted file mode 100644
index 74976f0c..00000000
--- a/TwitchLib.Client.Models/Builders/ChatCommandBuilder.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.Collections.Generic;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ChatCommandBuilder : IBuilder
- {
- private readonly List _argumentsAsList = new List();
- private string _argumentsAsString;
- private ChatMessage _chatMessage;
- private char _commandIdentifier;
- private string _commandText;
-
- private ChatCommandBuilder()
- {
- }
-
- public ChatCommandBuilder WithArgumentsAsList(params string[] argumentsList)
- {
- _argumentsAsList.AddRange(argumentsList);
- return this;
- }
-
- public ChatCommandBuilder WithArgumentsAsString(string argumentsAsString)
- {
- _argumentsAsString = argumentsAsString;
- return this;
- }
-
- public ChatCommandBuilder WithChatMessage(ChatMessage chatMessage)
- {
- _chatMessage = chatMessage;
- return this;
- }
-
- public ChatCommandBuilder WithCommandIdentifier(char commandIdentifier)
- {
- _commandIdentifier = commandIdentifier;
- return this;
- }
-
- public ChatCommandBuilder WithCommandText(string commandText)
- {
- _commandText = commandText;
- return this;
- }
-
- public static ChatCommandBuilder Create()
- {
- return new ChatCommandBuilder();
- }
-
- public ChatCommand Build()
- {
- return new ChatCommand(
- _chatMessage,
- _commandText,
- _argumentsAsString,
- _argumentsAsList,
- _commandIdentifier);
- }
-
- public ChatCommand BuildFromChatMessage(ChatMessage chatMessage)
- {
- return new ChatCommand(chatMessage);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/ChatMessageBuilder.cs b/TwitchLib.Client.Models/Builders/ChatMessageBuilder.cs
deleted file mode 100644
index 3e3dcd2e..00000000
--- a/TwitchLib.Client.Models/Builders/ChatMessageBuilder.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ChatMessageBuilder : IBuilder
- {
- private TwitchLibMessage _twitchLibMessage;
- private readonly List> BadgeInfo = new List>();
- private int _bits;
- private double _bitsInDollars;
- private string _channel;
- private CheerBadge _cheerBadge;
- private string _emoteReplacedMessage;
- private string _id;
- private bool _isBroadcaster;
- private bool _isMe;
- private bool _isModerator;
- private bool _isSubscriber;
- private bool _isVip;
- private bool _isStaff;
- private bool _isPartner;
- private string _message;
- private Noisy _noisy;
- private string _rawIrcMessage;
- private string _roomId;
- private int _subscribedMonthCount;
-
- private ChatMessageBuilder()
- {
- _twitchLibMessage = TwitchLibMessageBuilder.Create().Build();
- }
-
- public ChatMessageBuilder WithTwitchLibMessage(TwitchLibMessage twitchLibMessage)
- {
- _twitchLibMessage = twitchLibMessage;
- return this;
- }
-
- public ChatMessageBuilder WithBadgeInfos(params KeyValuePair[] badgeInfos)
- {
- BadgeInfo.AddRange(badgeInfos);
- return this;
- }
-
- public ChatMessageBuilder WithBits(int bits)
- {
- _bits = bits;
- return this;
- }
-
- public ChatMessageBuilder WithBitsInDollars(double bitsInDollars)
- {
- _bitsInDollars = bitsInDollars;
- return this;
- }
-
- public ChatMessageBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public ChatMessageBuilder WithCheerBadge(CheerBadge cheerBadge)
- {
- _cheerBadge = cheerBadge;
- return this;
- }
-
- public ChatMessageBuilder WithEmoteReplaceMessage(string emoteReplaceMessage)
- {
- _emoteReplacedMessage = emoteReplaceMessage;
- return this;
- }
-
- public ChatMessageBuilder WithId(string id)
- {
- _id = id;
- return this;
- }
-
- public ChatMessageBuilder WithIsBroadcaster(bool isBroadcaster)
- {
- _isBroadcaster = isBroadcaster;
- return this;
- }
-
- public ChatMessageBuilder WithIsMe(bool isMe)
- {
- _isMe = isMe;
- return this;
- }
-
- public ChatMessageBuilder WithIsModerator(bool isModerator)
- {
- _isModerator = isModerator;
- return this;
- }
-
- public ChatMessageBuilder WithIsSubscriber(bool isSubscriber)
- {
- _isSubscriber = isSubscriber;
- return this;
- }
- public ChatMessageBuilder WithIsVip(bool isVip)
- {
- _isVip = isVip;
- return this;
- }
- public ChatMessageBuilder WithIsStaff(bool isStaff)
- {
- _isStaff = isStaff;
- return this;
- }
-
- public ChatMessageBuilder WithIsPartner(bool isPartner)
- {
- _isPartner = isPartner;
- return this;
- }
- public ChatMessageBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public ChatMessageBuilder WithNoisy(Noisy noisy)
- {
- _noisy = noisy;
- return this;
- }
-
- public ChatMessageBuilder WithRawIrcMessage(string rawIrcMessage)
- {
- _rawIrcMessage = rawIrcMessage;
- return this;
- }
-
- public ChatMessageBuilder WithRoomId(string roomId)
- {
- _roomId = roomId;
- return this;
- }
-
- public ChatMessageBuilder WithSubscribedMonthCount(int subscribedMonthCount)
- {
- _subscribedMonthCount = subscribedMonthCount;
- return this;
- }
-
- public static ChatMessageBuilder Create()
- {
- return new ChatMessageBuilder();
- }
-
- public ChatMessage Build()
- {
- return new ChatMessage(
- _twitchLibMessage.BotUsername,
- _twitchLibMessage.UserId,
- _twitchLibMessage.Username,
- _twitchLibMessage.DisplayName,
- _twitchLibMessage.ColorHex,
- _twitchLibMessage.Color,
- _twitchLibMessage.EmoteSet,
- _message,
- _twitchLibMessage.UserType,
- _channel,
- _id,
- _isSubscriber,
- _subscribedMonthCount,
- _roomId,
- _twitchLibMessage.IsTurbo,
- _isModerator,
- _isMe,
- _isBroadcaster,
- _isVip,
- _isPartner,
- _isStaff,
- _noisy,
- _rawIrcMessage,
- _emoteReplacedMessage,
- _twitchLibMessage.Badges,
- _cheerBadge,
- _bits,
- _bitsInDollars);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/CheerBadgeBuilder.cs b/TwitchLib.Client.Models/Builders/CheerBadgeBuilder.cs
deleted file mode 100644
index c7b092c8..00000000
--- a/TwitchLib.Client.Models/Builders/CheerBadgeBuilder.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class CheerBadgeBuilder : IBuilder
- {
- private int _cheerAmount;
-
- public CheerBadgeBuilder WithCheerAmount(int cheerAmount)
- {
- _cheerAmount = cheerAmount;
- return this;
- }
-
- public CheerBadge Build()
- {
- return new CheerBadge(_cheerAmount);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/CommunitySubscriptionBuilder.cs b/TwitchLib.Client.Models/Builders/CommunitySubscriptionBuilder.cs
deleted file mode 100644
index dff4fbaf..00000000
--- a/TwitchLib.Client.Models/Builders/CommunitySubscriptionBuilder.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class CommunitySubscriptionBuilder : IFromIrcMessageBuilder
- {
- private CommunitySubscriptionBuilder()
- {
- }
-
- public static CommunitySubscriptionBuilder Create()
- {
- return new CommunitySubscriptionBuilder();
- }
-
- public CommunitySubscription BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new CommunitySubscription(fromIrcMessageBuilderDataObject.Message);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/ConnectionCredentialsBuilder.cs b/TwitchLib.Client.Models/Builders/ConnectionCredentialsBuilder.cs
deleted file mode 100644
index 386acb7a..00000000
--- a/TwitchLib.Client.Models/Builders/ConnectionCredentialsBuilder.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ConnectionCredentialsBuilder : IBuilder
- {
- private string _twitchUsername;
- private string _twitchOAuth;
- private string _twitchWebsocketURI = ConnectionCredentials.DefaultWebSocketUri;
- private bool _disableUsernameCheck;
-
- private ConnectionCredentialsBuilder()
- {
- }
-
- public ConnectionCredentialsBuilder WithTwitchUsername(string twitchUsername)
- {
- _twitchUsername = twitchUsername;
- return this;
- }
-
- public ConnectionCredentialsBuilder WithTwitchOAuth(string twitchOAuth)
- {
- _twitchOAuth = twitchOAuth;
- return this;
- }
-
- public ConnectionCredentialsBuilder WithTwitchWebSocketUri(string twitchWebSocketUri)
- {
- _twitchWebsocketURI = twitchWebSocketUri;
- return this;
- }
-
- public ConnectionCredentialsBuilder WithDisableUsernameCheck(bool disableUsernameCheck)
- {
- _disableUsernameCheck = disableUsernameCheck;
- return this;
- }
-
- public static ConnectionCredentialsBuilder Create()
- {
- return new ConnectionCredentialsBuilder();
- }
-
- public ConnectionCredentials Build()
- {
- return new ConnectionCredentials(
- _twitchUsername,
- _twitchOAuth,
- twitchWebsocketURI: _twitchWebsocketURI,
- disableUsernameCheck: _disableUsernameCheck);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/EmoteBuilder.cs b/TwitchLib.Client.Models/Builders/EmoteBuilder.cs
deleted file mode 100644
index 1830953f..00000000
--- a/TwitchLib.Client.Models/Builders/EmoteBuilder.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using static TwitchLib.Client.Models.EmoteSet;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class EmoteBuilder : IBuilder
- {
- private string _id;
- private string _name;
- private int _startIndex;
- private int _endIndex;
-
- private EmoteBuilder()
- {
- }
-
- public static EmoteBuilder Create()
- {
- return new EmoteBuilder();
- }
-
- public EmoteBuilder WithId(string id)
- {
- _id = id;
- return this;
- }
-
- public EmoteBuilder WithName(string name)
- {
- _name = name;
- return this;
- }
-
- public EmoteBuilder WithStartIndex(int startIndex)
- {
- _startIndex = startIndex;
- return this;
- }
-
- public EmoteBuilder WithEndIndex(int endIndex)
- {
- _endIndex = endIndex;
- return this;
- }
-
- public Emote Build()
- {
- return new Emote(
- _id,
- _name,
- _startIndex,
- _endIndex);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/ErrorEventBuilder.cs b/TwitchLib.Client.Models/Builders/ErrorEventBuilder.cs
deleted file mode 100644
index 0749d614..00000000
--- a/TwitchLib.Client.Models/Builders/ErrorEventBuilder.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ErrorEventBuilder : IBuilder
- {
- private string _message;
-
- private ErrorEventBuilder()
- {
- }
-
- public ErrorEventBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public static ErrorEventBuilder Create()
- {
- return new ErrorEventBuilder();
- }
-
- public ErrorEvent Build()
- {
- return new ErrorEvent
- {
- Message = _message
- };
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/FromIrcMessageBuilderDataObject.cs b/TwitchLib.Client.Models/Builders/FromIrcMessageBuilderDataObject.cs
deleted file mode 100644
index bfd279f0..00000000
--- a/TwitchLib.Client.Models/Builders/FromIrcMessageBuilderDataObject.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using TwitchLib.Client.Models.Internal;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public class FromIrcMessageBuilderDataObject
- {
- public IrcMessage Message { get; set; }
-
- public object AditionalData { get; set; }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/GiftedSubscriptionBuilder.cs b/TwitchLib.Client.Models/Builders/GiftedSubscriptionBuilder.cs
deleted file mode 100644
index 5d1783dc..00000000
--- a/TwitchLib.Client.Models/Builders/GiftedSubscriptionBuilder.cs
+++ /dev/null
@@ -1,221 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class GiftedSubscriptionBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private readonly List> _badges = new List>();
- private readonly List> _badgeInfo = new List>();
- private string _color;
- private string _displayName;
- private string _emotes;
- private string _id;
- private bool _isModerator;
- private bool _isSubscriber;
- private bool _isTurbo;
- private string _login;
- private string _msgId;
- private string _msgParamMonths;
- private string _msgParamRecipientDisplayName;
- private string _msgParamRecipientId;
- private string _msgParamRecipientUserName;
- private string _msgParamSubPlanName;
- private string _msgParamMultiMonthGiftDuration;
- private SubscriptionPlan _msgParamSubPlan;
- private string _roomId;
- private string _systemMsg;
- private string _systemMsgParsed;
- private string _tmiSentTs;
- private string _userId;
- private UserType _userType;
-
- private GiftedSubscriptionBuilder()
- {
- }
-
- public GiftedSubscriptionBuilder WithBadges(params KeyValuePair[] badges)
- {
- _badges.AddRange(badges);
- return this;
- }
-
- public GiftedSubscriptionBuilder WithBadgeInfos(params KeyValuePair[] badgeInfos)
- {
- _badgeInfo.AddRange(badgeInfos);
- return this;
- }
-
- public GiftedSubscriptionBuilder WithColor(string color)
- {
- _color = color;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithDisplayName(string displayName)
- {
- _displayName = displayName;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithEmotes(string emotes)
- {
- _emotes = emotes;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithId(string id)
- {
- _id = id;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithIsModerator(bool isModerator)
- {
- _isModerator = isModerator;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithIsSubscriber(bool isSubscriber)
- {
- _isSubscriber = isSubscriber;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithIsTurbo(bool isTurbo)
- {
- _isTurbo = isTurbo;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithLogin(string login)
- {
- _login = login;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMessageId(string msgId)
- {
- _msgId = msgId;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamCumulativeMonths(string msgParamMonths)
- {
- _msgParamMonths = msgParamMonths;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamRecipientDisplayName(string msgParamRecipientDisplayName)
- {
- _msgParamRecipientDisplayName = msgParamRecipientDisplayName;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamRecipientId(string msgParamRecipientId)
- {
- _msgParamRecipientId = msgParamRecipientId;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamRecipientUserName(string msgParamRecipientUserName)
- {
- _msgParamRecipientUserName = msgParamRecipientUserName;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamSubPlanName(string msgParamSubPlanName)
- {
- _msgParamSubPlanName = msgParamSubPlanName;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamMultiMonthGiftDuration(string msgParamMultiMonthGiftDuration)
- {
- _msgParamMultiMonthGiftDuration = msgParamMultiMonthGiftDuration;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithMsgParamSubPlan(SubscriptionPlan msgParamSubPlan)
- {
- _msgParamSubPlan = msgParamSubPlan;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithRoomId(string roomId)
- {
- _roomId = roomId;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithSystemMsg(string systemMsg)
- {
- _systemMsg = systemMsg;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithSystemMsgParsed(string systemMsgParsed)
- {
- _systemMsgParsed = systemMsgParsed;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithTmiSentTs(string tmiSentTs)
- {
- _tmiSentTs = tmiSentTs;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithUserId(string userId)
- {
- _userId = userId;
- return this;
- }
-
- public GiftedSubscriptionBuilder WithUserType(UserType userType)
- {
- _userType = userType;
- return this;
- }
-
- public static GiftedSubscriptionBuilder Create()
- {
- return new GiftedSubscriptionBuilder();
- }
-
- public GiftedSubscription Build()
- {
- return new GiftedSubscription(
- _badges,
- _badgeInfo,
- _color,
- _displayName,
- _emotes,
- _id,
- _login,
- _isModerator,
- _msgId,
- _msgParamMonths,
- _msgParamRecipientDisplayName,
- _msgParamRecipientId,
- _msgParamRecipientUserName,
- _msgParamSubPlanName,
- _msgParamMultiMonthGiftDuration,
- _msgParamSubPlan,
- _roomId,
- _isSubscriber,
- _systemMsg,
- _systemMsgParsed,
- _tmiSentTs,
- _isTurbo,
- _userType,
- _userId);
- }
-
- public GiftedSubscription BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new GiftedSubscription(fromIrcMessageBuilderDataObject.Message);
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/IBuilder.cs b/TwitchLib.Client.Models/Builders/IBuilder.cs
deleted file mode 100644
index a607fbe3..00000000
--- a/TwitchLib.Client.Models/Builders/IBuilder.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public interface IBuilder
- {
- T Build();
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/IFromIrcMessageBuilder.cs b/TwitchLib.Client.Models/Builders/IFromIrcMessageBuilder.cs
deleted file mode 100644
index 8269981b..00000000
--- a/TwitchLib.Client.Models/Builders/IFromIrcMessageBuilder.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public interface IFromIrcMessageBuilder
- {
- T BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject);
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/IrcMessageBuilder.cs b/TwitchLib.Client.Models/Builders/IrcMessageBuilder.cs
deleted file mode 100644
index 33a76d56..00000000
--- a/TwitchLib.Client.Models/Builders/IrcMessageBuilder.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums.Internal;
-using TwitchLib.Client.Models.Internal;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class IrcMessageBuilder : IBuilder
- {
- private IrcCommand _ircCommand;
- private readonly List _parameters = new List();
- private string _hostmask;
- private Dictionary _tags;
-
- public static IrcMessageBuilder Create()
- {
- return new IrcMessageBuilder();
- }
-
- private IrcMessageBuilder()
- {
- }
-
- public IrcMessageBuilder WithCommand(IrcCommand ircCommand)
- {
- _ircCommand = ircCommand;
- return Builder();
- }
-
- public IrcMessageBuilder WithParameter(params string[] parameters)
- {
- _parameters.AddRange(parameters);
- return Builder();
- }
-
- private IrcMessageBuilder Builder()
- {
- return this;
- }
-
- public IrcMessage BuildWithUserOnly(string user)
- {
- return new IrcMessage(user);
- }
-
- public IrcMessageBuilder WithHostMask(string hostMask)
- {
- _hostmask = hostMask;
- return Builder();
- }
-
- public IrcMessageBuilder WithTags(Dictionary tags)
- {
- _tags = tags;
- return Builder();
- }
-
- public IrcMessage Build()
- {
- return new IrcMessage(_ircCommand, _parameters.ToArray(), _hostmask, _tags);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/OutboundChatMessageBuilder.cs b/TwitchLib.Client.Models/Builders/OutboundChatMessageBuilder.cs
deleted file mode 100644
index 89f7584b..00000000
--- a/TwitchLib.Client.Models/Builders/OutboundChatMessageBuilder.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class OutboundChatMessageBuilder : IBuilder
- {
- private string _channel;
- private string _message;
- private string _userName;
-
- private OutboundChatMessageBuilder()
- {
- }
-
- public static OutboundChatMessageBuilder Create()
- {
- return new OutboundChatMessageBuilder();
- }
-
- public OutboundChatMessageBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public OutboundChatMessageBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public OutboundChatMessageBuilder WithUsername(string userName)
- {
- _userName = userName;
- return this;
- }
-
- public OutboundChatMessage Build()
- {
- return new OutboundChatMessage
- {
- Channel = _channel,
- Message = _message,
- Username = _userName,
- };
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/OutboundWhisperMessageBuilder.cs b/TwitchLib.Client.Models/Builders/OutboundWhisperMessageBuilder.cs
deleted file mode 100644
index 5f6827b2..00000000
--- a/TwitchLib.Client.Models/Builders/OutboundWhisperMessageBuilder.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class OutboundWhisperMessageBuilder : IBuilder
- {
- private string _username;
- private string _receiver;
- private string _message;
-
- private OutboundWhisperMessageBuilder()
- {
- }
-
- public OutboundWhisperMessageBuilder WithUsername(string username)
- {
- _username = username;
- return this;
- }
-
- public OutboundWhisperMessageBuilder WithReceiver(string receiver)
- {
- _receiver = receiver;
- return this;
- }
-
- public OutboundWhisperMessageBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public static OutboundWhisperMessageBuilder Create()
- {
- return new OutboundWhisperMessageBuilder();
- }
-
- public OutboundWhisperMessage Build()
- {
- return new OutboundWhisperMessage
- {
- Message = _message,
- Receiver = _receiver,
- Username = _username
- };
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/OutgoingMessageBuilder.cs b/TwitchLib.Client.Models/Builders/OutgoingMessageBuilder.cs
deleted file mode 100644
index 7e49b549..00000000
--- a/TwitchLib.Client.Models/Builders/OutgoingMessageBuilder.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class OutgoingMessageBuilder : IBuilder
- {
- private string _channel;
- private string _message;
- private int _nonce;
- private string _sender;
- private MessageState _messageState;
-
- private OutgoingMessageBuilder()
- {
- }
-
- public static OutgoingMessageBuilder Create()
- {
- return new OutgoingMessageBuilder();
- }
-
- public OutgoingMessageBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public OutgoingMessageBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public OutgoingMessageBuilder WithNonce(int nonce)
- {
- _nonce = nonce;
- return this;
- }
-
- public OutgoingMessageBuilder WithSender(string sender)
- {
- _sender = sender;
- return this;
- }
-
- public OutgoingMessageBuilder WithMessageState(MessageState messageState)
- {
- _messageState = messageState;
- return this;
- }
-
- public OutgoingMessage Build()
- {
- return new OutgoingMessage
- {
- Channel = _channel,
- Message = _message,
- Nonce = _nonce,
- Sender = _sender,
- State = _messageState
- };
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/RaidNotificationBuilder.cs b/TwitchLib.Client.Models/Builders/RaidNotificationBuilder.cs
deleted file mode 100644
index 71abc7b9..00000000
--- a/TwitchLib.Client.Models/Builders/RaidNotificationBuilder.cs
+++ /dev/null
@@ -1,184 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class RaidNotificationBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private readonly List> _badges = new List>();
- private readonly List> _badgeInfo = new List>();
- private string _color;
- private string _displayName;
- private string _emotes;
- private string _id;
- private bool _isModerator;
- private bool _isSubscriber;
- private bool _isTurbo;
- private string _login;
- private string _msgId;
- private string _msgParamDisplayName;
- private string _msgParamLogin;
- private string _msgParamViewerCount;
- private string _roomId;
- private string _systemMsg;
- private string _systemMsgParsed;
- private string _tmiSentTs;
- private string _userId;
- private UserType _userType;
-
- public RaidNotificationBuilder WithBadges(params KeyValuePair[] badges)
- {
- _badges.AddRange(badges);
- return this;
- }
-
- public RaidNotificationBuilder WithBadgeInfos(params KeyValuePair[] badgeInfos)
- {
- _badgeInfo.AddRange(badgeInfos);
- return this;
- }
-
- public RaidNotificationBuilder WithColor(string color)
- {
- _color = color;
- return this;
- }
-
- public RaidNotificationBuilder WithDisplayName(string displayName)
- {
- _displayName = displayName;
- return this;
- }
-
- public RaidNotificationBuilder WithEmotes(string emotes)
- {
- _emotes = emotes;
- return this;
- }
-
- public RaidNotificationBuilder WithId(string id)
- {
- _id = id;
- return this;
- }
-
- public RaidNotificationBuilder WithIsModerator(bool isModerator)
- {
- _isModerator = isModerator;
- return this;
- }
-
- public RaidNotificationBuilder WithIsSubscriber(bool isSubscriber)
- {
- _isSubscriber = isSubscriber;
- return this;
- }
-
- public RaidNotificationBuilder WithIsTurbo(bool isTurbo)
- {
- _isTurbo = isTurbo;
- return this;
- }
-
- public RaidNotificationBuilder WithLogin(string login)
- {
- _login = login;
- return this;
- }
-
- public RaidNotificationBuilder WithMessageId(string msgId)
- {
- _msgId = msgId;
- return this;
- }
-
- public RaidNotificationBuilder WithMsgParamDisplayName(string msgParamDisplayName)
- {
- _msgParamDisplayName = msgParamDisplayName;
- return this;
- }
-
- public RaidNotificationBuilder WithMsgParamLogin(string msgParamLogin)
- {
- _msgParamLogin = msgParamLogin;
- return this;
- }
-
- public RaidNotificationBuilder WithMsgParamViewerCount(string msgParamViewerCount)
- {
- _msgParamViewerCount = msgParamViewerCount;
- return this;
- }
-
- public RaidNotificationBuilder WithRoomId(string roomId)
- {
- _roomId = roomId;
- return this;
- }
-
- public RaidNotificationBuilder WithSystemMsg(string systemMsg)
- {
- _systemMsg = systemMsg;
- return this;
- }
-
- public RaidNotificationBuilder WithSystemMsgParsed(string systemMsgParsed)
- {
- _systemMsgParsed = systemMsgParsed;
- return this;
- }
-
- public RaidNotificationBuilder WithTmiSentTs(string tmiSentTs)
- {
- _tmiSentTs = tmiSentTs;
- return this;
- }
-
- public RaidNotificationBuilder WithUserId(string userId)
- {
- _userId = userId;
- return this;
- }
-
- public RaidNotificationBuilder WithUserType(UserType userType)
- {
- _userType = userType;
- return this;
- }
-
- private RaidNotificationBuilder()
- {
- }
-
- public RaidNotification Build()
- {
- return new RaidNotification(
- _badges,
- _badgeInfo,
- _color,
- _displayName,
- _emotes,
- _id,
- _login,
- _isModerator,
- _msgId,
- _msgParamDisplayName,
- _msgParamLogin,
- _msgParamViewerCount,
- _roomId,
- _isSubscriber,
- _systemMsg,
- _systemMsgParsed,
- _tmiSentTs,
- _isTurbo,
- _userType,
- _userId);
- }
-
- public RaidNotification BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new RaidNotification(fromIrcMessageBuilderDataObject.Message);
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/ReSubscriberBuilder.cs b/TwitchLib.Client.Models/Builders/ReSubscriberBuilder.cs
deleted file mode 100644
index 76ac1d63..00000000
--- a/TwitchLib.Client.Models/Builders/ReSubscriberBuilder.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class ReSubscriberBuilder : SubscriberBaseBuilder, IBuilder, IFromIrcMessageBuilder
- {
- private ReSubscriberBuilder()
- {
- }
-
- public static new ReSubscriberBuilder Create()
- {
- return new ReSubscriberBuilder();
- }
-
- public ReSubscriber BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new ReSubscriber(fromIrcMessageBuilderDataObject.Message);
- }
-
- ReSubscriber IBuilder.Build()
- {
- return (ReSubscriber)Build();
- }
-
- public override SubscriberBase Build()
- {
- return new ReSubscriber(
- Badges,
- BadgeInfo,
- ColorHex,
- Color,
- DisplayName,
- EmoteSet,
- Id,
- Login,
- SystemMessage,
- MessageId,
- MsgParamCumulativeMonths,
- MsgParamStreakMonths,
- MsgParamShouldShareStreak,
- ParsedSystemMessage,
- ResubMessage,
- SubscriptionPlan,
- SubscriptionPlanName,
- RoomId,
- UserId,
- IsModerator,
- IsTurbo,
- IsSubscriber,
- IsPartner,
- TmiSentTs,
- UserType,
- RawIrc,
- Channel,
- Months);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/RitualNewChatterBuilder.cs b/TwitchLib.Client.Models/Builders/RitualNewChatterBuilder.cs
deleted file mode 100644
index af84a44b..00000000
--- a/TwitchLib.Client.Models/Builders/RitualNewChatterBuilder.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class RitualNewChatterBuilder : IFromIrcMessageBuilder
- {
- public RitualNewChatter BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new RitualNewChatter(fromIrcMessageBuilderDataObject.Message);
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Builders/SentMessageBuilder.cs b/TwitchLib.Client.Models/Builders/SentMessageBuilder.cs
deleted file mode 100644
index f4672521..00000000
--- a/TwitchLib.Client.Models/Builders/SentMessageBuilder.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class SentMessageBuilder : IBuilder
- {
- private readonly List> _badges = new List>();
- private string _channel;
- private string _colorHex;
- private string _displayName;
- private string _emoteSet;
- private bool _isModerator;
- private bool _isSubscriber;
- private string _message;
- private UserType _userType;
-
- private SentMessageBuilder()
- {
- }
-
- public SentMessageBuilder WithBadges(params KeyValuePair[] badges)
- {
- _badges.AddRange(badges);
- return this;
- }
-
- public SentMessageBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public SentMessageBuilder WithColorHex(string colorHex)
- {
- _colorHex = colorHex;
- return this;
- }
-
- public SentMessageBuilder WithDisplayName(string displayName)
- {
- _displayName = displayName;
- return this;
- }
-
- public SentMessageBuilder WithEmoteSet(string emoteSet)
- {
- _emoteSet = emoteSet;
- return this;
- }
-
- public SentMessageBuilder WithIsModerator(bool isModerator)
- {
- _isModerator = isModerator;
- return this;
- }
-
- public SentMessageBuilder WithIsSubscriber(bool isSubscriber)
- {
- _isSubscriber = isSubscriber;
- return this;
- }
-
- public SentMessageBuilder WithMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public SentMessageBuilder WithUserType(UserType userType)
- {
- _userType = userType;
- return this;
- }
-
- public static SentMessageBuilder Create()
- {
- return new SentMessageBuilder();
- }
-
- public SentMessage BuildFromUserState(UserState userState, string message)
- {
- return new SentMessage(userState, message);
- }
-
- public SentMessage Build()
- {
- return new SentMessage(
- _badges,
- _channel,
- _colorHex,
- _displayName,
- _emoteSet,
- _isModerator,
- _isSubscriber,
- _userType,
- _message);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/SubscriberBaseBuilder.cs b/TwitchLib.Client.Models/Builders/SubscriberBaseBuilder.cs
deleted file mode 100644
index 474efdcc..00000000
--- a/TwitchLib.Client.Models/Builders/SubscriberBaseBuilder.cs
+++ /dev/null
@@ -1,276 +0,0 @@
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public class SubscriberBaseBuilder : IBuilder
- {
- protected List> Badges { get; } = new List>();
-
- public List> BadgeInfo { get; } = new List>();
-
- protected string ColorHex { get; set; }
-
- protected Color Color { get; set; }
-
- protected string DisplayName { get; set; }
-
- protected string EmoteSet { get; set; }
-
- protected string Id { get; set; }
-
- protected bool IsModerator { get; set; }
-
- protected bool IsPartner { get; set; }
-
- protected bool IsSubscriber { get; set; }
-
- protected bool IsTurbo { get; set; }
-
- protected string Login { get; set; }
-
- protected string RawIrc { get; set; }
-
- protected string ResubMessage { get; set; }
-
- protected string RoomId { get; set; }
-
- protected SubscriptionPlan SubscriptionPlan { get; set; }
-
- protected string SubscriptionPlanName { get; set; }
-
- protected string SystemMessage { get; set; }
-
- protected string ParsedSystemMessage { get; set; }
-
- protected string TmiSentTs { get; set; }
-
- protected string UserId { get; set; }
-
- protected UserType UserType { get; set; }
-
- protected string Channel { get; set; }
-
- protected string MessageId { get; set; }
-
- protected string MsgParamCumulativeMonths { get; set; }
-
- protected string MsgParamStreakMonths { get; set; }
-
- protected bool MsgParamShouldShareStreak { get; set; }
-
- protected int Months { get; set; }
-
- protected SubscriberBaseBuilder()
- {
- }
-
- public static SubscriberBaseBuilder Create()
- {
- return new SubscriberBaseBuilder();
- }
-
- public SubscriberBaseBuilder WithBadges(params KeyValuePair[] badges)
- {
- Badges.AddRange(badges);
- return this;
- }
-
- public SubscriberBaseBuilder WithBadgeInfos(params KeyValuePair[] badgeInfos)
- {
- BadgeInfo.AddRange(badgeInfos);
- return this;
- }
-
- public SubscriberBaseBuilder WithColorHex(string colorHex)
- {
- ColorHex = colorHex;
- return this;
- }
-
- public SubscriberBaseBuilder WithColor(Color color)
- {
- Color = color;
- return this;
- }
-
- public SubscriberBaseBuilder WithDisplayName(string displayName)
- {
- DisplayName = displayName;
- return this;
- }
-
- public SubscriberBaseBuilder WithEmoteSet(string emoteSet)
- {
- EmoteSet = emoteSet;
- return this;
- }
-
- public SubscriberBaseBuilder WithId(string id)
- {
- Id = id;
- return this;
- }
-
- public SubscriberBaseBuilder WithIsModerator(bool isModerator)
- {
- IsModerator = isModerator;
- return this;
- }
-
- public SubscriberBaseBuilder WithIsPartner(bool isPartner)
- {
- IsPartner = isPartner;
- return this;
- }
-
- public SubscriberBaseBuilder WithIsSubscriber(bool isSubscriber)
- {
- IsSubscriber = isSubscriber;
- return this;
- }
-
- public SubscriberBaseBuilder WithIsTurbo(bool isTurbo)
- {
- IsTurbo = isTurbo;
- return this;
- }
-
- public SubscriberBaseBuilder WithLogin(string login)
- {
- Login = login;
- return this;
- }
-
- public SubscriberBaseBuilder WithRawIrc(string rawIrc)
- {
- RawIrc = rawIrc;
- return this;
- }
-
- public SubscriberBaseBuilder WithResubMessage(string resubMessage)
- {
- ResubMessage = resubMessage;
- return this;
- }
-
- public SubscriberBaseBuilder WithRoomId(string roomId)
- {
- RoomId = roomId;
- return this;
- }
-
- public SubscriberBaseBuilder WithSubscribtionPlan(SubscriptionPlan subscriptionPlan)
- {
- SubscriptionPlan = subscriptionPlan;
- return this;
- }
-
- public SubscriberBaseBuilder WithSubscriptionPlanName(string subscriptionPlanName)
- {
- SubscriptionPlanName = subscriptionPlanName;
- return this;
- }
-
- public SubscriberBaseBuilder WithSystemMessage(string systemMessage)
- {
- SystemMessage = systemMessage;
- return this;
- }
-
- public SubscriberBaseBuilder WithParsedSystemMessage(string parsedSystemMessage)
- {
- ParsedSystemMessage = parsedSystemMessage;
- return this;
- }
-
- public SubscriberBaseBuilder WithTmiSentTs(string tmiSentTs)
- {
- TmiSentTs = tmiSentTs;
- return this;
- }
-
- public SubscriberBaseBuilder WithUserType(UserType userType)
- {
- UserType = userType;
- return this;
- }
-
- public SubscriberBaseBuilder WithUserId(string userId)
- {
- UserId = userId;
- return this;
- }
-
- public SubscriberBaseBuilder WithMonths(int months)
- {
- Months = months;
- return this;
- }
-
- public SubscriberBaseBuilder WithMessageId(string messageId)
- {
- MessageId = messageId;
- return this;
- }
-
- public SubscriberBaseBuilder WithMsgParamCumulativeMonths(string msgParamCumulativeMonths)
- {
- MsgParamCumulativeMonths = msgParamCumulativeMonths;
- return this;
- }
-
- public SubscriberBaseBuilder WithMsgParamStreakMonths(string msgParamStreakMonths)
- {
- MsgParamStreakMonths = msgParamStreakMonths;
- return this;
- }
-
- public SubscriberBaseBuilder WithMsgParamShouldShareStreak(bool msgParamShouldShareStreak)
- {
- MsgParamShouldShareStreak = msgParamShouldShareStreak;
- return this;
- }
-
- public SubscriberBaseBuilder WithChannel(string channel)
- {
- Channel = channel;
- return this;
- }
-
- public virtual SubscriberBase Build()
- {
- return new SubscriberBase(
- Badges,
- BadgeInfo,
- ColorHex,
- Color,
- DisplayName,
- EmoteSet,
- Id,
- Login,
- SystemMessage,
- MessageId,
- MsgParamCumulativeMonths,
- MsgParamStreakMonths,
- MsgParamShouldShareStreak,
- ParsedSystemMessage,
- ResubMessage,
- SubscriptionPlan,
- SubscriptionPlanName,
- RoomId,
- UserId,
- IsModerator,
- IsTurbo,
- IsSubscriber,
- IsPartner,
- TmiSentTs,
- UserType,
- RawIrc,
- Channel,
- Months);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/SubscriberBuilder.cs b/TwitchLib.Client.Models/Builders/SubscriberBuilder.cs
deleted file mode 100644
index 018daf55..00000000
--- a/TwitchLib.Client.Models/Builders/SubscriberBuilder.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class SubscriberBuilder : SubscriberBaseBuilder, IBuilder, IFromIrcMessageBuilder
- {
- private SubscriberBuilder()
- {
- }
-
- public static new SubscriberBuilder Create()
- {
- return new SubscriberBuilder();
- }
-
- public Subscriber BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new Subscriber(fromIrcMessageBuilderDataObject.Message);
- }
-
- Subscriber IBuilder.Build()
- {
- return (Subscriber)Build();
- }
-
- public override SubscriberBase Build()
- {
- return new Subscriber(
- Badges,
- BadgeInfo,
- ColorHex,
- Color,
- DisplayName,
- EmoteSet,
- Id,
- Login,
- SystemMessage,
- MessageId,
- MsgParamCumulativeMonths,
- MsgParamStreakMonths,
- MsgParamShouldShareStreak,
- ParsedSystemMessage,
- ResubMessage,
- SubscriptionPlan,
- SubscriptionPlanName,
- RoomId,
- UserId,
- IsModerator,
- IsTurbo,
- IsSubscriber,
- IsPartner,
- TmiSentTs,
- UserType,
- RawIrc,
- Channel);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/TwitchLibMessageBuilder.cs b/TwitchLib.Client.Models/Builders/TwitchLibMessageBuilder.cs
deleted file mode 100644
index b0828213..00000000
--- a/TwitchLib.Client.Models/Builders/TwitchLibMessageBuilder.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class TwitchLibMessageBuilder : TwitchLibMessage, IBuilder
- {
- private TwitchLibMessageBuilder()
- {
- }
-
- public TwitchLibMessageBuilder WithBadges(List> badges)
- {
- Badges = badges;
- return this;
- }
-
- public TwitchLibMessageBuilder WithColorHex(string colorHex)
- {
- ColorHex = colorHex;
- return this;
- }
-
- public TwitchLibMessageBuilder WithColorHex(Color color)
- {
- Color = color;
- return this;
- }
-
- public TwitchLibMessageBuilder WithUsername(string username)
- {
- Username = username;
- return this;
- }
-
- public TwitchLibMessageBuilder WithDisplayName(string displayName)
- {
- DisplayName = displayName;
- return this;
- }
-
- public TwitchLibMessageBuilder WithEmoteSet(EmoteSet emoteSet)
- {
- EmoteSet = emoteSet;
- return this;
- }
-
- public TwitchLibMessageBuilder WithUserId(string userId)
- {
- UserId = userId;
- return this;
- }
-
- public TwitchLibMessageBuilder WithIsTurbo(bool isTurbo)
- {
- IsTurbo = isTurbo;
- return this;
- }
-
- public TwitchLibMessageBuilder WithBotUserName(string botUserName)
- {
- BotUsername = botUserName;
- return this;
- }
-
- public TwitchLibMessageBuilder WithUserType(UserType userType)
- {
- UserType = userType;
- return this;
- }
-
- public static TwitchLibMessageBuilder Create()
- {
- return new TwitchLibMessageBuilder();
- }
-
- public TwitchLibMessage Build()
- {
- return this;
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/UserBanBuilder.cs b/TwitchLib.Client.Models/Builders/UserBanBuilder.cs
deleted file mode 100644
index c694433f..00000000
--- a/TwitchLib.Client.Models/Builders/UserBanBuilder.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class UserBanBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private string _channelName;
- private string _userName;
- private string _banReason;
- private string _roomId;
- private string _targetUserId;
-
- public static UserBanBuilder Create()
- {
- return new UserBanBuilder();
- }
-
- private UserBanBuilder()
- {
- }
-
- public UserBanBuilder WithChannel(string channel)
- {
- _channelName = channel;
- return this;
- }
-
- public UserBanBuilder WithUserName(string userName)
- {
- _userName = userName;
- return this;
- }
-
- public UserBanBuilder WithBanReason(string banReason)
- {
- _banReason = banReason;
- return this;
- }
-
- public UserBanBuilder WithRoomId(string roomId)
- {
- _roomId = roomId;
- return this;
- }
-
- public UserBanBuilder WithTargetUserId(string targetUserId)
- {
- _targetUserId = targetUserId;
- return this;
- }
-
- public UserBan BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new UserBan(fromIrcMessageBuilderDataObject.Message);
- }
-
- public UserBan Build()
- {
- return new UserBan(_channelName, _userName, _banReason, _roomId, _targetUserId);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/UserStateBuilder.cs b/TwitchLib.Client.Models/Builders/UserStateBuilder.cs
deleted file mode 100644
index 6c25a746..00000000
--- a/TwitchLib.Client.Models/Builders/UserStateBuilder.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class UserStateBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private readonly List> _badges = new List>();
- private readonly List> _badgeInfo = new List>();
- private string _channel;
- private string _colorHex;
- private string _displayName;
- private string _emoteSet;
- private string _id;
- private bool _isModerator;
- private bool _isSubscriber;
- private UserType _userType;
-
- private UserStateBuilder()
- {
- }
-
- public UserStateBuilder WithBadges(params KeyValuePair[] badges)
- {
- _badges.AddRange(badges);
- return this;
- }
-
- public UserStateBuilder WithBadgeInfos(params KeyValuePair[] badgeInfos)
- {
- _badgeInfo.AddRange(badgeInfos);
- return this;
- }
-
- public UserStateBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public UserStateBuilder WithColorHex(string olorHex)
- {
- _colorHex = olorHex;
- return this;
- }
-
- public UserStateBuilder WithDisplayName(string displayName)
- {
- _displayName = displayName;
- return this;
- }
-
- public UserStateBuilder WithEmoteSet(string emoteSet)
- {
- _emoteSet = emoteSet;
- return this;
- }
-
- public UserStateBuilder Id(string id)
- {
- _id = id;
- return this;
- }
-
- public UserStateBuilder WithIsModerator(bool isModerator)
- {
- _isModerator = isModerator;
- return this;
- }
-
- public UserStateBuilder WithIsSubscriber(bool isSubscriber)
- {
- _isSubscriber = isSubscriber;
- return this;
- }
-
- public UserStateBuilder WithUserType(UserType userType)
- {
- _userType = userType;
- return this;
- }
-
- public static UserStateBuilder Create()
- {
- return new UserStateBuilder();
- }
-
- public UserState BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new UserState(fromIrcMessageBuilderDataObject.Message);
- }
-
- public UserState Build()
- {
- return new UserState(
- _badges,
- _badgeInfo,
- _colorHex,
- _displayName,
- _emoteSet,
- _channel,
- _id,
- _isSubscriber,
- _isModerator,
- _userType);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/UserTimeoutBuilder.cs b/TwitchLib.Client.Models/Builders/UserTimeoutBuilder.cs
deleted file mode 100644
index a7998fb5..00000000
--- a/TwitchLib.Client.Models/Builders/UserTimeoutBuilder.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class UserTimeoutBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private string _channel;
- private int _timeoutDuration;
- private string _timeoutReason;
- private string _username;
- private string _targetUserId;
-
- private UserTimeoutBuilder()
- {
- }
-
- public UserTimeoutBuilder WithChannel(string channel)
- {
- _channel = channel;
- return this;
- }
-
- public UserTimeoutBuilder WithTimeoutDuration(int timeoutDuration)
- {
- _timeoutDuration = timeoutDuration;
- return this;
- }
-
- public UserTimeoutBuilder WithTimeoutReason(string timeoutReason)
- {
- _timeoutReason = timeoutReason;
- return this;
- }
-
- public UserTimeoutBuilder WithUsername(string username)
- {
- _username = username;
- return this;
- }
- public UserTimeoutBuilder WithTargetUserId(string targetUserId)
- {
- _targetUserId = targetUserId;
- return this;
- }
-
- public static UserTimeoutBuilder Create()
- {
- return new UserTimeoutBuilder();
- }
-
- public UserTimeout BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- return new UserTimeout(fromIrcMessageBuilderDataObject.Message);
- }
-
- public UserTimeout Build()
- {
- return new UserTimeout(_channel, _username, _targetUserId, _timeoutDuration, _timeoutReason);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/WhisperCommandBuilder.cs b/TwitchLib.Client.Models/Builders/WhisperCommandBuilder.cs
deleted file mode 100644
index 1ec4b44b..00000000
--- a/TwitchLib.Client.Models/Builders/WhisperCommandBuilder.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Collections.Generic;
-
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class WhisperCommandBuilder : IBuilder
- {
- private readonly List _argumentsAsList = new List();
- private string _argumentsAsString;
- private char _commandIdentifier;
- private string _commandText;
- private WhisperMessage _whisperMessage;
-
- private WhisperCommandBuilder()
- {
- }
-
- public WhisperCommandBuilder WithWhisperMessage(WhisperMessage whisperMessage)
- {
- _whisperMessage = whisperMessage;
- return this;
- }
-
- public WhisperCommandBuilder WithCommandText(string commandText)
- {
- _commandText = commandText;
- return this;
- }
-
- public WhisperCommandBuilder WithCommandIdentifier(char commandIdentifier)
- {
- _commandIdentifier = commandIdentifier;
- return this;
- }
-
- public WhisperCommandBuilder WithArgumentAsString(string argumentAsString)
- {
- _argumentsAsString = argumentAsString;
- return this;
- }
-
- public WhisperCommandBuilder WithArguments(params string[] arguments)
- {
- _argumentsAsList.AddRange(arguments);
- return this;
- }
-
- public static WhisperCommandBuilder Create()
- {
- return new WhisperCommandBuilder();
- }
-
- public WhisperCommand BuildFromWhisperMessage(WhisperMessage whisperMessage)
- {
- return new WhisperCommand(whisperMessage);
- }
-
- public WhisperCommand Build()
- {
- return new WhisperCommand(_whisperMessage, _commandText, _argumentsAsString, _argumentsAsList, _commandIdentifier);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/Builders/WhisperMessageBuilder.cs b/TwitchLib.Client.Models/Builders/WhisperMessageBuilder.cs
deleted file mode 100644
index f7de11c8..00000000
--- a/TwitchLib.Client.Models/Builders/WhisperMessageBuilder.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-namespace TwitchLib.Client.Models.Builders
-{
- public sealed class WhisperMessageBuilder : IBuilder, IFromIrcMessageBuilder
- {
- private TwitchLibMessage _twitchLibMessage;
- private string _messageId;
- private string _threadId;
- private string _message;
-
- private WhisperMessageBuilder()
- {
- _twitchLibMessage = TwitchLibMessageBuilder.Create().Build();
- }
-
- public static WhisperMessageBuilder Create()
- {
- return new WhisperMessageBuilder();
- }
-
- public WhisperMessageBuilder WithTwitchLibMessage(TwitchLibMessage twitchLibMessage)
- {
- _twitchLibMessage = twitchLibMessage;
- return this;
- }
-
- public WhisperMessageBuilder WithMessageId(string messageId)
- {
- _messageId = messageId;
- return this;
- }
-
- public WhisperMessageBuilder WithThreadId(string threadId)
- {
- _threadId = threadId;
- return this;
- }
-
- public WhisperMessageBuilder WhihMessage(string message)
- {
- _message = message;
- return this;
- }
-
- public WhisperMessage BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
- {
- string botName = fromIrcMessageBuilderDataObject.AditionalData.ToString();
- return new WhisperMessage(fromIrcMessageBuilderDataObject.Message, botName);
- }
-
- public WhisperMessage Build()
- {
- return new WhisperMessage(
- _twitchLibMessage.Badges,
- _twitchLibMessage.ColorHex,
- _twitchLibMessage.Color,
- _twitchLibMessage.Username,
- _twitchLibMessage.DisplayName,
- _twitchLibMessage.EmoteSet,
- _threadId,
- _messageId,
- _twitchLibMessage.UserId,
- _twitchLibMessage.IsTurbo,
- _twitchLibMessage.BotUsername,
- _message,
- _twitchLibMessage.UserType);
- }
- }
-}
diff --git a/TwitchLib.Client.Models/ChannelState.cs b/TwitchLib.Client.Models/ChannelState.cs
index 128b95d7..7c8cf616 100644
--- a/TwitchLib.Client.Models/ChannelState.cs
+++ b/TwitchLib.Client.Models/ChannelState.cs
@@ -1,6 +1,4 @@
-using System;
-
-using TwitchLib.Client.Models.Internal;
+using TwitchLib.Client.Models.Internal;
namespace TwitchLib.Client.Models
{
@@ -8,7 +6,7 @@ namespace TwitchLib.Client.Models
public class ChannelState
{
/// Property representing the current broadcaster language.
- public string BroadcasterLanguage { get; }
+ public string BroadcasterLanguage { get; } = default!;
/// Property representing the current channel.
public string Channel { get; }
@@ -16,7 +14,7 @@ public class ChannelState
/// Property representing whether EmoteOnly mode is being applied to chat or not. WILL BE NULL IF VALUE NOT PRESENT.
public bool? EmoteOnly { get; }
- /// Property representing how long needed to be following to talk. If null, FollowersOnly is not enabled.
+ /// Property representing how long needed to be following to talk. Timeout.InfiniteTimespan indicates that FollowersOnly mode is switched off. If null, FollowersOnly status is not changed.
public TimeSpan? FollowersOnly { get; } = null;
/// Property representing mercury value. Not sure what it's for.
@@ -29,7 +27,7 @@ public class ChannelState
public bool? Rituals { get; }
/// Twitch assigned room id
- public string RoomId { get; }
+ public string RoomId { get; } = default!;
/// Property representing whether Slow mode is being applied to chat or not. WILL BE NULL IF VALUE NOT PRESENT.
public int? SlowMode { get; }
@@ -37,55 +35,62 @@ public class ChannelState
/// Property representing whether Sub Mode is being applied to chat or not. WILL BE NULL IF VALUE NOT PRESENT.
public bool? SubOnly { get; }
+ ///
+ /// Contains undocumented tags.
+ ///
+ public Dictionary? UndocumentedTags { get; }
+
/// ChannelState object constructor.
public ChannelState(IrcMessage ircMessage)
{
//@broadcaster-lang=;emote-only=0;r9k=0;slow=0;subs-only=1 :tmi.twitch.tv ROOMSTATE #burkeblack
- foreach (var tag in ircMessage.Tags.Keys)
+ foreach (var tag in ircMessage.Tags)
{
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
+ var tagValue = tag.Value;
+ switch (tag.Key)
{
case Tags.BroadcasterLang:
BroadcasterLanguage = tagValue;
break;
case Tags.EmoteOnly:
- EmoteOnly = Common.Helpers.ConvertToBool(tagValue);
+ EmoteOnly = TagHelper.ToBool(tagValue);
break;
case Tags.R9K:
- R9K = Common.Helpers.ConvertToBool(tagValue);
+ R9K = TagHelper.ToBool(tagValue);
break;
case Tags.Rituals:
- Rituals = Common.Helpers.ConvertToBool(tagValue);
+ Rituals = TagHelper.ToBool(tagValue);
break;
case Tags.Slow:
- var success = int.TryParse(tagValue, out var slowDuration);
+ var success = int.TryParse(tag.Value, out var slowDuration);
SlowMode = success ? slowDuration : (int?)null;
break;
case Tags.SubsOnly:
- SubOnly = Common.Helpers.ConvertToBool(tagValue);
+ SubOnly = TagHelper.ToBool(tagValue);
break;
case Tags.FollowersOnly:
- if(int.TryParse(tagValue, out int minutes) && minutes > -1)
+ if (int.TryParse(tagValue, out int minutes))
{
- FollowersOnly = TimeSpan.FromMinutes(minutes);
+ FollowersOnly = minutes > -1 ? TimeSpan.FromMinutes(minutes) : Timeout.InfiniteTimeSpan;
}
break;
case Tags.RoomId:
RoomId = tagValue;
break;
case Tags.Mercury:
- Mercury = Common.Helpers.ConvertToBool(tagValue);
+ Mercury = TagHelper.ToBool(tagValue);
break;
default:
- Console.WriteLine("[TwitchLib][ChannelState] Unaccounted for: " + tag);
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
break;
}
}
Channel = ircMessage.Channel;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public ChannelState(
bool r9k,
bool rituals,
diff --git a/TwitchLib.Client.Models/ChatCommand.cs b/TwitchLib.Client.Models/ChatCommand.cs
deleted file mode 100644
index b53b07c1..00000000
--- a/TwitchLib.Client.Models/ChatCommand.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace TwitchLib.Client.Models
-{
- /// Object representing a command received via Twitch chat.
- public class ChatCommand
- {
- /// Property representing all arguments received in a List form.
- public List ArgumentsAsList { get; }
-
- /// Property representing all arguments received in a string form.
- public string ArgumentsAsString { get; }
-
- /// Property representing the chat message that the command came in.
- public ChatMessage ChatMessage { get; }
-
- /// Property representing the command identifier (ie command prefix).
- public char CommandIdentifier { get; }
-
- /// Property representing the actual command (without the command prefix).
- public string CommandText { get; }
-
- /// ChatCommand constructor.
- ///
- public ChatCommand(ChatMessage chatMessage)
- {
- ChatMessage = chatMessage;
- CommandText = chatMessage.Message.Split(' ')?[0].Substring(1, chatMessage.Message.Split(' ')[0].Length - 1) ?? chatMessage.Message.Substring(1, chatMessage.Message.Length - 1); ;
- ArgumentsAsString = chatMessage.Message.Contains(" ") ? chatMessage.Message.Replace(chatMessage.Message.Split(' ')?[0] + " ", "") : "";
- if (!chatMessage.Message.Contains("\"") || chatMessage.Message.Count(x => x == '"') % 2 == 1)
- ArgumentsAsList = chatMessage.Message.Split(' ')?.Where(arg => arg != chatMessage.Message[0] + CommandText).ToList() ?? new List();
- else
- ArgumentsAsList = Common.Helpers.ParseQuotesAndNonQuotes(ArgumentsAsString);
- CommandIdentifier = chatMessage.Message[0];
- }
-
- public ChatCommand(
- ChatMessage chatMessage,
- string commandText,
- string argumentsAsString,
- List argumentsAsList,
- char commandIdentifier)
- {
- ChatMessage = chatMessage;
- CommandText = commandText;
- ArgumentsAsString = argumentsAsString;
- ArgumentsAsList = argumentsAsList;
- CommandIdentifier = commandIdentifier;
- }
- }
-}
diff --git a/TwitchLib.Client.Models/ChatMessage.cs b/TwitchLib.Client.Models/ChatMessage.cs
index 4f30f848..c6deca40 100644
--- a/TwitchLib.Client.Models/ChatMessage.cs
+++ b/TwitchLib.Client.Models/ChatMessage.cs
@@ -1,9 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
-using TwitchLib.Client.Models.Extensions.NetCore;
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Extensions;
using TwitchLib.Client.Models.Internal;
namespace TwitchLib.Client.Models
@@ -11,10 +7,13 @@ namespace TwitchLib.Client.Models
/// Class represents ChatMessage in a Twitch channel.
public class ChatMessage : TwitchLibMessage
{
- protected readonly MessageEmoteCollection _emoteCollection;
+ readonly ChatReply? _chatReply;
+ readonly HypeChat? _hypeChat;
+
+ protected readonly MessageEmoteCollection? _emoteCollection;
/// Information associated with badges. Not all badges will be in this list. Use carefully.
- public List> BadgeInfo { get; }
+ public List> BadgeInfo { get; } = default!;
/// If viewer sent bits in their message, total amount will be here.
public int Bits { get; }
@@ -26,16 +25,16 @@ public class ChatMessage : TwitchLibMessage
public string Channel { get; }
/// If a cheer badge exists, this property represents the raw value and color (more later). Can be null.
- public CheerBadge CheerBadge { get; }
+ public CheerBadge? CheerBadge { get; }
/// If a custom reward is present with the message, the ID will be set (null by default)
- public string CustomRewardId { get; }
+ public string? CustomRewardId { get; }
/// Text after emotes have been handled (if desired). Will be null if replaceEmotes is false.
- public string EmoteReplacedMessage { get; }
+ public string? EmoteReplacedMessage { get; }
/// Unique message identifier assigned by Twitch
- public string Id { get; }
+ public string Id { get; } = default!;
/// Chat message from broadcaster identifier flag
public bool IsBroadcaster { get; }
@@ -49,24 +48,9 @@ public class ChatMessage : TwitchLibMessage
/// Chat message /me identifier flag.
public bool IsMe { get; }
- /// Channel specific moderator status.
- public bool IsModerator { get; }
-
/// Message used channel points to skip sub mode
public bool IsSkippingSubMode { get; internal set; }
- /// Channel specific subscriber status.
- public bool IsSubscriber { get; }
-
- /// Message is from channel VIP
- public bool IsVip { get; }
-
- /// Message is from a Twitch Staff member
- public bool IsStaff { get; }
-
- /// Message is from a Twitch Partner
- public bool IsPartner { get; }
-
/// Twitch chat message contents.
public string Message { get; }
@@ -74,16 +58,19 @@ public class ChatMessage : TwitchLibMessage
public Noisy Noisy { get; }
/// Unique identifier of chat room.
- public string RoomId { get; }
+ public string RoomId { get; } = default!;
/// Number of months a person has been subbed.
public int SubscribedMonthCount { get; }
/// Sent timestamp generated by TMI
- public string TmiSentTs { get; }
+ public DateTimeOffset TmiSent { get; }
- // Chat reply information. Will be null if it is not a reply.
- public ChatReply ChatReply { get; }
+ /// Chat reply information. Will be null if it is not a reply.
+ public ChatReply? ChatReply => _chatReply;
+
+ /// Hype Chat information.
+ public HypeChat? HypeChat => _hypeChat;
//Example IRC message: @badges=moderator/1,warcraft/alliance;color=;display-name=Swiftyspiffyv4;emotes=;mod=1;room-id=40876073;subscriber=0;turbo=0;user-id=103325214;user-type=mod :swiftyspiffyv4!swiftyspiffyv4@swiftyspiffyv4.tmi.twitch.tv PRIVMSG #swiftyspiffy :asd
/// Constructor for ChatMessage object.
@@ -91,11 +78,15 @@ public class ChatMessage : TwitchLibMessage
/// The IRC message from Twitch to be processed.
/// The to register new emotes on and, if desired, use for emote replacement.
/// Whether to replace emotes for this chat message. Defaults to false.
+ /// Adds prefix to replaced emotes. Defaults to empty string.
+ /// Adds prefix to replaced emotes. Defaults to empty string.
public ChatMessage(
string botUsername,
IrcMessage ircMessage,
- ref MessageEmoteCollection emoteCollection,
- bool replaceEmotes = false)
+ MessageEmoteCollection? emoteCollection = null,
+ bool replaceEmotes = false,
+ string prefix = "",
+ string suffix = "")
{
BotUsername = botUsername;
RawIrcMessage = ircMessage.ToString();
@@ -119,14 +110,14 @@ public ChatMessage(
Username = ircMessage.User;
Channel = ircMessage.Channel;
- foreach (var tag in ircMessage.Tags.Keys)
+ var userDetails = UserDetails.None;
+ foreach (var tag in ircMessage.Tags)
{
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
+ var (tagKey, tagValue) = (tag.Key, tag.Value);
+ switch (tagKey)
{
case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
+ Badges = TagHelper.ToBadges(tagValue);
// Iterate through saved badges for special circumstances
foreach (var badge in Badges)
{
@@ -137,36 +128,25 @@ public ChatMessage(
break;
case "subscriber":
// Prioritize BadgeInfo subscribe count, as its more accurate
- if(SubscribedMonthCount == 0)
+ if (SubscribedMonthCount == 0)
{
SubscribedMonthCount = int.Parse(badge.Value);
}
break;
- case "vip":
- IsVip = true;
+ default:
break;
- case "admin":
- IsStaff = true;
- break;
- case "staff":
- IsStaff = true;
- break;
- case "partner":
- IsPartner = true;
- break;
-
}
}
break;
case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
+ BadgeInfo = TagHelper.ToBadges(tagValue);
// check if founder is one of them, and get months from that
var founderBadge = BadgeInfo.Find(b => b.Key == "founder");
- if(!founderBadge.Equals(default(KeyValuePair)))
+ if (!founderBadge.Equals(default(KeyValuePair)))
{
- IsSubscriber = true;
SubscribedMonthCount = int.Parse(founderBadge.Value);
- } else
+ }
+ else
{
var subBadge = BadgeInfo.Find(b => b.Key == "subscriber");
// BadgeInfo has better accuracy than Badges subscriber value
@@ -181,9 +161,7 @@ public ChatMessage(
BitsInDollars = ConvertBitsToUsd(Bits);
break;
case Tags.Color:
- ColorHex = tagValue;
- if (!string.IsNullOrWhiteSpace(ColorHex))
- Color = ColorTranslator.FromHtml(ColorHex);
+ HexColor = tagValue;
break;
case Tags.CustomRewardId:
CustomRewardId = tagValue;
@@ -201,80 +179,47 @@ public ChatMessage(
Id = tagValue;
break;
case Tags.MsgId:
- handleMsgId(tagValue);
+ HandleMsgId(tagValue);
break;
case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Moderator;
break;
case Tags.Noisy:
- Noisy = Common.Helpers.ConvertToBool(tagValue) ? Noisy.True : Noisy.False;
- break;
- case Tags.ReplyParentDisplayName:
- if (ChatReply == null) { ChatReply = new ChatReply(); } // ChatReply is null if not reply
- ChatReply.ParentDisplayName = tagValue;
- break;
- case Tags.ReplyParentMsgBody:
- if (ChatReply == null) { ChatReply = new ChatReply(); } // ChatReply is null if not reply
- ChatReply.ParentMsgBody = tagValue;
- break;
- case Tags.ReplyParentMsgId:
- if (ChatReply == null) { ChatReply = new ChatReply(); } // ChatReply is null if not reply
- ChatReply.ParentMsgId = tagValue;
- break;
- case Tags.ReplyParentUserId:
- if (ChatReply == null) { ChatReply = new ChatReply(); } // ChatReply is null if not reply
- ChatReply.ParentUserId = tagValue;
- break;
- case Tags.ReplyParentUserLogin:
- if (ChatReply == null) { ChatReply = new ChatReply(); } // ChatReply is null if not reply
- ChatReply.ParentUserLogin = tagValue;
+ Noisy = TagHelper.ToBool(tagValue) ? Noisy.True : Noisy.False;
break;
case Tags.RoomId:
RoomId = tagValue;
break;
case Tags.Subscriber:
- // this check because when founder is set, the subscriber value is actually 0, which is problematic
- IsSubscriber = IsSubscriber == false ? Common.Helpers.ConvertToBool(tagValue) : true;
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Subscriber;
break;
case Tags.TmiSentTs:
- TmiSentTs = tagValue;
+ TmiSent = TagHelper.ToDateTimeOffsetFromUnixMs(tagValue);
break;
case Tags.Turbo:
- IsTurbo = Common.Helpers.ConvertToBool(tagValue);
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Turbo;
break;
case Tags.UserId:
UserId = tagValue;
break;
case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- IsStaff = true;
- break;
- case "staff":
- UserType = UserType.Staff;
- IsStaff = true;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
+ UserType = TagHelper.ToUserType(tagValue);
+ break;
+ default:
+ if (!(ChatReply.TrySetTag(ref _chatReply, tag) || HypeChat.TrySetTag(ref _hypeChat, tag)))
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
break;
}
}
+ UserDetail = new(userDetails, Badges);
//Parse the emoteSet
- if (EmoteSet != null && Message != null && EmoteSet.Emotes.Count > 0)
+ if (_emoteCollection != null && EmoteSet?.Emotes.Count > 0)
{
- var uniqueEmotes = EmoteSet.RawEmoteSetString.Split('/');
- foreach (var emote in uniqueEmotes)
+ foreach (var emote in new SpanSliceEnumerator(EmoteSet.RawEmoteSetString!, '/'))
{
var firstColon = emote.IndexOf(':');
var firstComma = emote.IndexOf(',');
@@ -282,13 +227,20 @@ public ChatMessage(
var firstDash = emote.IndexOf('-');
if (firstColon > 0 && firstDash > firstColon && firstComma > firstDash)
{
- if (int.TryParse(emote.Substring(firstColon + 1, firstDash - firstColon - 1), out var low) &&
- int.TryParse(emote.Substring(firstDash + 1, firstComma - firstDash - 1), out var high))
+#if NETSTANDARD2_0
+ var lowStr = emote.Slice(firstColon + 1, firstDash - firstColon - 1).ToString();
+ var highStr = emote.Slice(firstDash + 1, firstComma - firstDash - 1).ToString();
+#else
+ var lowStr = emote.Slice(firstColon + 1, firstDash - firstColon - 1);
+ var highStr = emote.Slice(firstDash + 1, firstComma - firstDash - 1);
+#endif
+ if (int.TryParse(lowStr, out var low) &&
+ int.TryParse(highStr, out var high))
{
if (low >= 0 && low < high && high < Message.Length)
{
//Valid emote, let's parse
- var id = emote.Substring(0, firstColon);
+ var id = emote.Slice(0, firstColon).ToString();
//Pull the emote text from the message
var text = Message.Substring(low, high - low + 1);
_emoteCollection.Add(new MessageEmote(id, text));
@@ -298,12 +250,11 @@ public ChatMessage(
}
if (replaceEmotes)
{
- EmoteReplacedMessage = _emoteCollection.ReplaceEmotes(Message);
+ EmoteReplacedMessage = _emoteCollection?.ReplaceEmotes(Message, prefix: prefix, suffix: suffix);
}
}
- if (EmoteSet == null)
- EmoteSet = new EmoteSet(default(string), Message);
+ EmoteSet ??= new EmoteSet(default(string), Message);
// Check if display name was set, and if it wasn't, set it to username
if (string.IsNullOrEmpty(DisplayName))
@@ -316,66 +267,55 @@ public ChatMessage(
IsBroadcaster = true;
}
- if (Channel.Split(':').Length == 3)
+ var splitData = Channel.Split(':');
+ if (splitData.Length == 3 &&
+ string.Equals(splitData[1], UserId, StringComparison.InvariantCultureIgnoreCase))
{
- if (string.Equals(Channel.Split(':')[1], UserId, StringComparison.InvariantCultureIgnoreCase))
- {
- UserType = UserType.Broadcaster;
- IsBroadcaster = true;
- }
+ UserType = UserType.Broadcaster;
+ IsBroadcaster = true;
}
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public ChatMessage(
string botUsername,
string userId,
string userName,
string displayName,
- string colorHex,
- Color color,
+ string hexColor,
EmoteSet emoteSet,
string message,
UserType userType,
string channel,
string id,
- bool isSubscriber,
int subscribedMonthCount,
string roomId,
- bool isTurbo,
- bool isModerator,
bool isMe,
bool isBroadcaster,
- bool isVip,
- bool isPartner,
- bool isStaff,
Noisy noisy,
string rawIrcMessage,
string emoteReplacedMessage,
List> badges,
CheerBadge cheerBadge,
int bits,
- double bitsInDollars)
+ double bitsInDollars,
+ UserDetail userDetail)
{
BotUsername = botUsername;
UserId = userId;
DisplayName = displayName;
- ColorHex = colorHex;
- Color = color;
+ HexColor = hexColor;
EmoteSet = emoteSet;
Message = message;
UserType = userType;
Channel = channel;
Id = id;
- IsSubscriber = isSubscriber;
SubscribedMonthCount = subscribedMonthCount;
RoomId = roomId;
- IsTurbo = isTurbo;
- IsModerator = isModerator;
IsMe = isMe;
IsBroadcaster = isBroadcaster;
- IsVip = isVip;
- IsPartner = isPartner;
- IsStaff = isStaff;
Noisy = noisy;
RawIrcMessage = rawIrcMessage;
EmoteReplacedMessage = emoteReplacedMessage;
@@ -384,17 +324,18 @@ public ChatMessage(
Bits = bits;
BitsInDollars = bitsInDollars;
Username = userName;
+ UserDetail = userDetail;
}
- private void handleMsgId(string val)
+ private void HandleMsgId(string val)
{
- switch(val) {
- case MsgIds.HighlightedMessage:
- IsHighlighted = true;
- break;
- case MsgIds.SkipSubsModeMessage:
- IsSkippingSubMode = true;
- break;
+ if (val == MsgIds.HighlightedMessage)
+ {
+ IsHighlighted = true;
+ }
+ else if (val == MsgIds.SkipSubsModeMessage)
+ {
+ IsSkippingSubMode = true;
}
}
@@ -409,23 +350,14 @@ Conversion Rates
10000 bits = $126.00 (10%)
25000 bits = $308.00 (12%)
*/
- if (bits < 1500)
+ return bits switch
{
- return (double)bits / 100 * 1.4;
- }
- if (bits < 5000)
- {
- return (double)bits / 1500 * 19.95;
- }
- if (bits < 10000)
- {
- return (double)bits / 5000 * 64.40;
- }
- if (bits < 25000)
- {
- return (double)bits / 10000 * 126;
- }
- return (double)bits / 25000 * 308;
+ < 1500 => (double)bits / 100 * 1.4,
+ < 5000 => (double)bits / 1500 * 19.95,
+ < 10000 => (double)bits / 5000 * 64.40,
+ < 25000 => (double)bits / 10000 * 126,
+ _ => (double)bits / 25000 * 308
+ };
}
}
}
diff --git a/TwitchLib.Client.Models/ChatReply.cs b/TwitchLib.Client.Models/ChatReply.cs
index c25948ad..7dd76a0a 100644
--- a/TwitchLib.Client.Models/ChatReply.cs
+++ b/TwitchLib.Client.Models/ChatReply.cs
@@ -1,25 +1,74 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using TwitchLib.Client.Models.Internal;
namespace TwitchLib.Client.Models
{
/// Class representing a chat reply/thread
public class ChatReply
{
- /// Property representing the display name of the responded to message
- public string ParentDisplayName { get; internal set; }
+ ///
+ /// The display name of the sender of the direct parent message.
+ ///
+ public string ParentDisplayName { get; internal set; } = default!;
- /// Property representing the message contents of the responded to message
- public string ParentMsgBody { get; internal set; }
+ ///
+ /// The text of the direct parent message.
+ ///
+ public string ParentMsgBody { get; internal set; } = default!;
- /// Property representing the id of the responded to message
- public string ParentMsgId { get; internal set; }
+ ///
+ /// An ID that uniquely identifies the direct parent message that this message is replying to.
+ ///
+ public string ParentMsgId { get; internal set; } = default!;
- /// Property representing the user id of the sender of the responded to message
- public string ParentUserId { get; internal set; }
+ ///
+ /// An ID that identifies the sender of the direct parent message.
+ ///
+ public string ParentUserId { get; internal set; } = default!;
- /// Property representing the user login of the sender of the responded to message
- public string ParentUserLogin { get; internal set; }
+ ///
+ /// The login name of the sender of the direct parent message.
+ ///
+ public string ParentUserLogin { get; internal set; } = default!;
+
+ ///
+ /// An ID that uniquely identifies the top-level parent message of the reply thread that this message is replying to.
+ ///
+ public string ThreadParentMsgId { get; internal set; } = default!;
+
+ ///
+ /// The login name of the sender of the top-level parent message.
+ ///
+ public string ThreadParentUserLogin { get; internal set; } = default!;
+
+ internal static bool TrySetTag(ref ChatReply? reply, KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.ReplyParentDisplayName:
+ (reply ??= new()).ParentDisplayName = tag.Value;
+ break;
+ case Tags.ReplyParentMsgBody:
+ (reply ??= new()).ParentMsgBody = tag.Value;
+ break;
+ case Tags.ReplyParentMsgId:
+ (reply ??= new()).ParentMsgId = tag.Value;
+ break;
+ case Tags.ReplyParentUserId:
+ (reply ??= new()).ParentUserId = tag.Value;
+ break;
+ case Tags.ReplyParentUserLogin:
+ (reply ??= new()).ParentUserLogin = tag.Value;
+ break;
+ case Tags.ReplyThreadParentMsgId:
+ (reply ??= new()).ThreadParentMsgId = tag.Value;
+ break;
+ case Tags.ReplyThreadParentUserLogin:
+ (reply ??= new()).ThreadParentUserLogin = tag.Value;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
}
}
diff --git a/TwitchLib.Client.Models/CommandInfo.cs b/TwitchLib.Client.Models/CommandInfo.cs
new file mode 100644
index 00000000..889664f6
--- /dev/null
+++ b/TwitchLib.Client.Models/CommandInfo.cs
@@ -0,0 +1,110 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace TwitchLib.Client.Models;
+
+/// Object representing a command received via Twitch chat.
+public class CommandInfo
+{
+ /// Property representing the command identifier (ie command prefix).
+ public string Identifier { get; }
+
+ /// Property representing the actual command (without the command prefix).
+ public string Name { get; }
+
+ /// Property representing all arguments received in a string form.
+ public string ArgumentsAsString { get; }
+
+ /// Property representing all arguments received in a List form.
+ public List ArgumentsAsList { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommandInfo(string identifier, string name) : this(identifier, name, string.Empty, new())
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommandInfo(string identifier, string name, string argumentsAsString, List argumentsAsList)
+ {
+ Identifier = identifier;
+ Name = name;
+ ArgumentsAsString = argumentsAsString;
+ ArgumentsAsList = argumentsAsList;
+ }
+
+ ///
+ /// Tries to parse a message with specified command identifier into a value.
+ ///
+ /// true if s was successfully parsed; otherwise, false.
+#if NETSTANDARD2_0
+ internal static bool TryParse(string commandIdentifier, ReadOnlySpan message, out CommandInfo result)
+#else
+ internal static bool TryParse(string commandIdentifier, ReadOnlySpan message, [MaybeNullWhen(false)] out CommandInfo result)
+#endif
+ {
+ result = default!;
+ if(!message.StartsWith(commandIdentifier.AsSpan()))
+ return false;
+
+ message = message.Slice(commandIdentifier.Length);
+ if (message.IsEmpty || message[0] == ' ') // if string contains only the identifier or the first char after identifier is space, then it is invalid input
+ return false;
+ var indexOfSpace = message.IndexOf(' ');
+ if (indexOfSpace == -1)
+ {
+ var name = message.ToString();
+ result = new(commandIdentifier, name);
+ }
+ else
+ {
+ var name = message.Slice(0, indexOfSpace).ToString();
+ message = message.Slice(indexOfSpace + 1).TrimStart();
+ var argumentsAsString = message.ToString();
+ result = new(commandIdentifier, name, argumentsAsString, ParseArgumentsToList(message));
+ }
+ return true;
+
+ static List ParseArgumentsToList(ReadOnlySpan s)
+ {
+ int index;
+ var arguments = new List();
+ while (!s.IsEmpty)
+ {
+ bool isQuote = s[0] == '"';
+ if (s[0] == '"')
+ {
+ s = s.Slice(1);
+ index = s.IndexOf('"');
+ }
+ else
+ {
+ index = s.IndexOfAny('"', ' ');
+ }
+ if (index == -1)
+ {
+ arguments.Add(s.ToString());
+ s = default;
+ }
+ else
+ {
+ arguments.Add(s.Slice(0, index).ToString());
+ if (!isQuote && s[index] == '"') // s"txt" we dont want remove quote after s
+ index--;
+ s = s.Slice(index + 1);
+ }
+ s = s.TrimStart();
+ }
+ return arguments;
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ return ArgumentsAsString.Length == 0
+ ? $"{Identifier}{Name}"
+ : $"{Identifier}{Name} {ArgumentsAsString}";
+ }
+}
diff --git a/TwitchLib.Client.Models/Common/Helpers.cs b/TwitchLib.Client.Models/Common/Helpers.cs
deleted file mode 100644
index 4e9094ed..00000000
--- a/TwitchLib.Client.Models/Common/Helpers.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace TwitchLib.Client.Models.Common
-{
- /// Static class of helper functions used around the project.
- public static class Helpers
- {
- ///
- /// Parses out strings that have quotes, ideal for commands that use quotes for parameters
- ///
- /// Input string to attempt to parse.
- /// List of contents of quotes from the input string
- public static List ParseQuotesAndNonQuotes(string message)
- {
- var args = new List();
-
- // Return if empty string
- if (message == "")
- return new List();
-
- var previousQuoted = message[0] != '"';
- // Parse quoted text as a single argument
- foreach (var arg in message.Split('"'))
- {
- if (string.IsNullOrEmpty(arg))
- continue;
-
- // This arg is a quoted arg, add it right away
- if (!previousQuoted)
- {
- args.Add(arg);
- previousQuoted = true;
- continue;
- }
-
- if (!arg.Contains(" "))
- continue;
-
- // This arg is non-quoted, iterate through each split and add it if it's not empty/whitespace
- foreach (var dynArg in arg.Split(' '))
- {
- if (string.IsNullOrWhiteSpace(dynArg))
- continue;
-
- args.Add(dynArg);
- previousQuoted = false;
- }
- }
- return args;
- }
-
- ///
- /// Parses the badges field in GLOBALUSERSTATE, PRIVMSG, USERNOTICE, USERSTATE, etc
- ///
- /// The data.
- /// List of keyvalue pairs representing each badge and value associated
- public static List> ParseBadges(string badgesStr)
- {
- var badges = new List>();
-
- if (badgesStr.Contains('/'))
- {
- if (!badgesStr.Contains(","))
- badges.Add(new KeyValuePair(badgesStr.Split('/')[0], badgesStr.Split('/')[1]));
- else
- foreach (var badge in badgesStr.Split(','))
- badges.Add(new KeyValuePair(badge.Split('/')[0], badge.Split('/')[1]));
- }
-
- return badges;
- }
-
- public static string ParseToken(string token, string message)
- {
- var tokenValue = string.Empty;
-
- for (var i = message.IndexOf(token, StringComparison.InvariantCultureIgnoreCase);
- i > -1;
- i = message.IndexOf(token, i + token.Length, StringComparison.InvariantCultureIgnoreCase))
- {
- tokenValue = new string(message
- .Substring(i)
- .TakeWhile(x => x != ';' && x != ' ')
- .ToArray())
- .Split('=')
- .LastOrDefault();
- }
-
- return tokenValue;
- }
-
- public static bool ConvertToBool(string data)
- {
- return data == "1";
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/CommunityPayForward.cs b/TwitchLib.Client.Models/CommunityPayForward.cs
new file mode 100644
index 00000000..b1fa1d4d
--- /dev/null
+++ b/TwitchLib.Client.Models/CommunityPayForward.cs
@@ -0,0 +1,90 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class CommunityPayForward : UserNoticeBase
+{
+ public bool MsgParamPriorGifterAnonymous { get; protected set; }
+
+ public string MsgParamPriorGifterDisplayName { get; protected set; } = default!;
+
+ public string MsgParamPriorGifterId { get; protected set; } = default!;
+
+ public string MsgParamPriorGifterUserName { get; protected set; } = default!;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommunityPayForward(IrcMessage ircMessage) : base(ircMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommunityPayForward(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ bool msgParamPriorGifterAnonymous,
+ string msgParamPriorGifterDisplayName,
+ string msgParamPriorGifterId,
+ string msgParamPriorGifterUserName)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamPriorGifterAnonymous = msgParamPriorGifterAnonymous;
+ MsgParamPriorGifterDisplayName = msgParamPriorGifterDisplayName;
+ MsgParamPriorGifterId = msgParamPriorGifterId;
+ MsgParamPriorGifterUserName = msgParamPriorGifterUserName;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamPriorGifterAnonymous:
+ MsgParamPriorGifterAnonymous = bool.Parse(tag.Value);
+ break;
+ case Tags.MsgParamPriorGifterDisplayName:
+ MsgParamPriorGifterDisplayName = tag.Value;
+ break;
+ case Tags.MsgParamPriorGifterId:
+ MsgParamPriorGifterId = tag.Value;
+ break;
+ case Tags.MsgParamPriorGifterUserName:
+ MsgParamPriorGifterUserName = tag.Value;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/TwitchLib.Client.Models/CommunitySubscription.cs b/TwitchLib.Client.Models/CommunitySubscription.cs
index ce7b4ffc..3787f0a5 100644
--- a/TwitchLib.Client.Models/CommunitySubscription.cs
+++ b/TwitchLib.Client.Models/CommunitySubscription.cs
@@ -1,146 +1,108 @@
-using System;
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+//submysterygift
+public class CommunitySubscription : UserNoticeBase
{
- public class CommunitySubscription
+ private Goal? _goal;
+
+ public bool IsAnonymous { get; }
+
+ public Goal? MsgParamGoal { get => _goal; protected set => _goal = value; }
+
+ public string MsgParamGiftTheme { get; protected set; } = default!;
+
+ public int MsgParamMassGiftCount { get; protected set; }
+
+ public string MsgParamOriginId { get; protected set; } = default!;
+
+ public int MsgParamSenderCount { get; protected set; }
+
+ ///
+ /// The type of subscription plan being used.
+ ///
+ public SubscriptionPlan MsgParamSubPlan { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommunitySubscription(IrcMessage ircMessage) : base(ircMessage)
{
- private const string AnonymousGifterUserId = "274598607";
+ IsAnonymous = UserId == AnonymousGifterUserId;
+ }
- public List> Badges;
- public List> BadgeInfo;
- public string Color;
- public string DisplayName;
- public string Emotes;
- public string Id;
- public string Login;
- public bool IsModerator;
- public bool IsAnonymous;
- public string MsgId;
- public int MsgParamMassGiftCount;
- public int MsgParamSenderCount;
- public SubscriptionPlan MsgParamSubPlan;
- public string RoomId;
- public bool IsSubscriber;
- public string SystemMsg;
- public string SystemMsgParsed;
- public string TmiSentTs;
- public bool IsTurbo;
- public string UserId;
- public UserType UserType;
- public string MsgParamMultiMonthGiftDuration;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommunitySubscription(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ Goal? msgParamGoal,
+ string msgParamGiftTheme,
+ int msgParamMassGiftCount,
+ string msgParamOriginId,
+ int msgParamSenderCount,
+ SubscriptionPlan msgParamSubPlan)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ IsAnonymous = userId == AnonymousGifterUserId;
+ MsgParamGoal = msgParamGoal;
+ MsgParamGiftTheme = msgParamGiftTheme;
+ MsgParamMassGiftCount = msgParamMassGiftCount;
+ MsgParamOriginId = msgParamOriginId;
+ MsgParamSenderCount = msgParamSenderCount;
+ MsgParamSubPlan = msgParamSubPlan;
+ }
- public CommunitySubscription(IrcMessage ircMessage)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- Color = tagValue;
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- Emotes = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.MsgParamSubPlan:
- switch (tagValue)
- {
- case "prime":
- MsgParamSubPlan = SubscriptionPlan.Prime;
- break;
- case "1000":
- MsgParamSubPlan = SubscriptionPlan.Tier1;
- break;
- case "2000":
- MsgParamSubPlan = SubscriptionPlan.Tier2;
- break;
- case "3000":
- MsgParamSubPlan = SubscriptionPlan.Tier3;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(tagValue.ToLower));
- }
- break;
- case Tags.MsgParamMassGiftCount:
- MsgParamMassGiftCount = int.Parse(tagValue);
- break;
- case Tags.MsgParamSenderCount:
- MsgParamSenderCount = int.Parse(tagValue);
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- IsSubscriber = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.SystemMsg:
- SystemMsg = tagValue;
- SystemMsgParsed = tagValue.Replace("\\s", " ").Replace("\\n", "");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.Turbo:
- IsTurbo = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.UserId:
- UserId = tagValue;
- if(UserId == AnonymousGifterUserId)
- {
- IsAnonymous = true;
- }
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- case Tags.MsgParamMultiMonthGiftDuration:
- MsgParamMultiMonthGiftDuration = tagValue;
- break;
- }
- }
+ case Tags.MsgParamGiftTheme:
+ MsgParamGiftTheme = tag.Value;
+ break;
+ case Tags.MsgParamMassGiftCount:
+ MsgParamMassGiftCount = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamSenderCount:
+ MsgParamSenderCount = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamSubPlan:
+ MsgParamSubPlan = TagHelper.ToSubscriptionPlan(tag.Value);
+ break;
+ default:
+ return Goal.TrySetTag(ref _goal, tag);
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/ConnectionCredentials.cs b/TwitchLib.Client.Models/ConnectionCredentials.cs
index cefa5e2c..b8adaa45 100644
--- a/TwitchLib.Client.Models/ConnectionCredentials.cs
+++ b/TwitchLib.Client.Models/ConnectionCredentials.cs
@@ -1,16 +1,17 @@
-using System;
-using System.Text.RegularExpressions;
+using System.Text.RegularExpressions;
namespace TwitchLib.Client.Models
{
/// Class used to store credentials used to connect to Twitch chat/whisper.
- public class ConnectionCredentials
+ public partial class ConnectionCredentials
{
- public const string DefaultWebSocketUri = "wss://irc-ws.chat.twitch.tv:443";
-
- /// Property representing URI used to connect to Twitch websocket service.
- public string TwitchWebsocketURI { get; }
-
+#if NET7_0_OR_GREATER
+ [GeneratedRegex("^([a-zA-Z0-9][a-zA-Z0-9_]{4,25})$")]
+ private static partial Regex GetUsernameCheckRegex();
+#else
+ private static Regex GetUsernameCheckRegex() => UsernameCheckRegex;
+ private static readonly Regex UsernameCheckRegex = new("^([a-zA-Z0-9][a-zA-Z0-9_]{4,25})$");
+#endif
/// Property representing bot's oauth.
public string TwitchOAuth { get; }
@@ -24,27 +25,32 @@ public class ConnectionCredentials
public ConnectionCredentials(
string twitchUsername,
string twitchOAuth,
- string twitchWebsocketURI = DefaultWebSocketUri,
bool disableUsernameCheck = false,
- Capabilities capabilities = null)
+ Capabilities? capabilities = null)
{
- if (!disableUsernameCheck && !new Regex("^([a-zA-Z0-9][a-zA-Z0-9_]{3,25})$").Match(twitchUsername).Success)
+ if (!disableUsernameCheck && !GetUsernameCheckRegex().Match(twitchUsername).Success)
+ {
throw new Exception($"Twitch username does not appear to be valid. {twitchUsername}");
+ }
TwitchUsername = twitchUsername.ToLower();
TwitchOAuth = twitchOAuth;
// Make sure proper formatting is applied to oauth
- if (!twitchOAuth.Contains(":"))
+ if (!TwitchOAuth.Contains(":"))
{
- TwitchOAuth = $"oauth:{twitchOAuth.Replace("oauth", "")}";
+ TwitchOAuth = $"oauth:{TwitchOAuth.Replace("oauth", "")}";
}
- TwitchWebsocketURI = twitchWebsocketURI;
+ Capabilities = capabilities ?? new Capabilities();
+ }
- if (capabilities == null)
- capabilities = new Capabilities();
- Capabilities = capabilities;
+ /// Constructor for ConnectionCredentials object as anonymous user.
+ public ConnectionCredentials(Capabilities? capabilities = null)
+ {
+ TwitchUsername = $"justinfan{new Random().Next(1000, 89999)}";
+ TwitchOAuth = "oauth:";
+ Capabilities = capabilities ?? new Capabilities();
}
}
@@ -60,6 +66,9 @@ public class Capabilities
/// Enables several Twitch-specific commands.
public bool Commands { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
public Capabilities(bool membership = true, bool tags = true, bool commands = true)
{
Membership = membership;
diff --git a/TwitchLib.Client.Models/ContinuedGiftedSubscription.cs b/TwitchLib.Client.Models/ContinuedGiftedSubscription.cs
index a2ce8e22..d6d76eb3 100644
--- a/TwitchLib.Client.Models/ContinuedGiftedSubscription.cs
+++ b/TwitchLib.Client.Models/ContinuedGiftedSubscription.cs
@@ -1,132 +1,104 @@
-using System;
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+// giftpaidupgrade
+public class ContinuedGiftedSubscription : UserNoticeBase
{
- public class ContinuedGiftedSubscription
+ ///
+ /// The number of gifts the gifter has given during the promo indicated by .
+ ///
+ public int MsgParamPromoGiftTotal { get; protected set; }
+
+ ///
+ /// The subscriptions promo, if any, that is ongoing (for example, Subtember 2018).
+ ///
+ public string MsgParamPromoName { get; protected set; } = default!;
+
+ ///
+ /// The login name of the user who gifted the subscription.
+ ///
+ public string MsgParamSenderLogin { get; protected set; } = default!;
+
+ ///
+ /// The display name of the user who gifted the subscription.
+ ///
+ public string MsgParamSenderName { get; protected set; } = default!;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ContinuedGiftedSubscription(IrcMessage ircMessage) : base(ircMessage)
{
- //@badge-info=subscriber/11;badges=subscriber/9;color=#DAA520;display-name=Varanid;emotes=;flags=;id=a2d384c1-c30a-409e-8001-9e7d8f9c784d;login=varanid;mod=0;msg-id=giftpaidupgrade;msg-param-sender-login=cletusbueford;msg-param-sender-name=CletusBueford;room-id=44338537;subscriber=1;system-msg=Varanid\sis\scontinuing\sthe\sGift\sSub\sthey\sgot\sfrom\sCletusBueford!;tmi-sent-ts=1612497386372;user-id=67505836;user-type= :tmi.twitch.tv USERNOTICE #burkeblack
-
- public List> Badges { get; }
-
- public List> BadgeInfo { get; }
-
- public string Color { get; }
-
- public string DisplayName { get; }
-
- public string Emotes { get; }
-
- public string Flags { get; }
-
- public string Id { get; }
-
- public string Login { get; }
-
- public bool IsModerator { get; }
-
- public string MsgId { get; }
-
- public string MsgParamSenderLogin { get; }
-
- public string MsgParamSenderName { get; }
-
- public string RoomId { get; }
-
- public bool IsSubscriber { get; }
-
- public string SystemMsg { get; }
-
- public string TmiSentTs { get; }
-
- public string UserId { get; }
+ }
- public UserType UserType { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ContinuedGiftedSubscription(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ int msgParamPromoGiftTotal,
+ string msgParamPromoName,
+ string msgParamSenderLogin,
+ string msgParamSenderName)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamPromoGiftTotal = msgParamPromoGiftTotal;
+ MsgParamPromoName = msgParamPromoName;
+ MsgParamSenderLogin = msgParamSenderLogin;
+ MsgParamSenderName = msgParamSenderName;
+ }
- public ContinuedGiftedSubscription(IrcMessage ircMessage)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
- {
- case Tags.SystemMsg:
- SystemMsg = tagValue;
- break;
- case Tags.Flags:
- Flags = tagValue;
- break;
- case Tags.MsgParamSenderLogin:
- MsgParamSenderLogin = tagValue;
- break;
- case Tags.MsgParamSenderName:
- MsgParamSenderName = tagValue;
- break;
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- Color = tagValue;
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- Emotes = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- IsSubscriber = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.UserId:
- UserId = tagValue;
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- }
- }
+ case Tags.MsgParamPromoGiftTotal:
+ MsgParamPromoGiftTotal = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamPromoName:
+ MsgParamPromoName = tag.Value;
+ break;
+ case Tags.MsgParamSenderLogin:
+ MsgParamSenderLogin = tag.Value;
+ break;
+ case Tags.MsgParamSenderName:
+ MsgParamSenderName = tag.Value;
+ break;
+ default:
+ return false;
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/EmoteSet.cs b/TwitchLib.Client.Models/EmoteSet.cs
index dd8f1efe..35d64d6d 100644
--- a/TwitchLib.Client.Models/EmoteSet.cs
+++ b/TwitchLib.Client.Models/EmoteSet.cs
@@ -1,7 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-
-using TwitchLib.Client.Models.Extractors;
+using TwitchLib.Client.Models.Extractors;
// TODO: Builder is missing
namespace TwitchLib.Client.Models
@@ -13,25 +10,24 @@ public class EmoteSet
public List Emotes { get; }
/// The raw emote set string obtained from Twitch, for legacy purposes.
- public string RawEmoteSetString { get; }
+ public string? RawEmoteSetString { get; }
/// Constructor for ChatEmoteSet object.
///
///
- public EmoteSet(string rawEmoteSetString, string message)
+ public EmoteSet(string? rawEmoteSetString, string message)
{
// this should be removed and used outside of object
RawEmoteSetString = rawEmoteSetString;
- EmoteExtractor emoteExtractor = new EmoteExtractor();
- Emotes = emoteExtractor.Extract(rawEmoteSetString, message).ToList();
+ Emotes = EmoteExtractor.Extract(rawEmoteSetString, message);
}
/// Constructor for ChatEmoteSet object.
/// Collection of Emote instances
/// Original string from which emotes were created
- public EmoteSet(IEnumerable emotes, string emoteSetData)
+ public EmoteSet(IEnumerable emotes, string rawEmoteSetString)
{
- RawEmoteSetString = emoteSetData;
+ RawEmoteSetString = rawEmoteSetString;
Emotes = emotes.ToList();
}
}
diff --git a/TwitchLib.Client.Models/ErrorEvent.cs b/TwitchLib.Client.Models/ErrorEvent.cs
index 085614f7..e1bcf34c 100644
--- a/TwitchLib.Client.Models/ErrorEvent.cs
+++ b/TwitchLib.Client.Models/ErrorEvent.cs
@@ -4,6 +4,14 @@
public class ErrorEvent
{
/// Message pertaining to the error.
- public string Message { get; set; }
+ public string Message { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ErrorEvent(string message)
+ {
+ Message = message;
+ }
}
}
diff --git a/TwitchLib.Client.Models/Extensions/NetCore/ColorTranslator.cs b/TwitchLib.Client.Models/Extensions/NetCore/ColorTranslator.cs
deleted file mode 100644
index 0041e1da..00000000
--- a/TwitchLib.Client.Models/Extensions/NetCore/ColorTranslator.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Drawing;
-using System.Globalization;
-
-namespace TwitchLib.Client.Models.Extensions.NetCore
-{
- public static class ColorTranslator
- {
- public static Color FromHtml(string hexColor)
- {
- hexColor = hexColor + 00;
- var argb = int.Parse(hexColor.Replace("#", ""), NumberStyles.HexNumber);
- return Color.FromArgb(argb);
- }
- }
-}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Extensions/SpanSliceEnumerator.cs b/TwitchLib.Client.Models/Extensions/SpanSliceEnumerator.cs
new file mode 100644
index 00000000..e95c1709
--- /dev/null
+++ b/TwitchLib.Client.Models/Extensions/SpanSliceEnumerator.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace TwitchLib.Client.Models.Extensions;
+
+internal ref struct SpanSliceEnumerator
+{
+ ReadOnlySpan _span;
+ readonly char _char;
+
+ public SpanSliceEnumerator(ReadOnlySpan span, char @char)
+ {
+ _span = span;
+ _char = @char;
+ }
+
+ public SpanSliceEnumerator(string str, char @char) : this(str.AsSpan(), @char)
+ { }
+
+ public ReadOnlySpan Current { get; private set; }
+
+ public SpanSliceEnumerator GetEnumerator() => this;
+
+ public bool MoveNext()
+ {
+ if (_span.IsEmpty)
+ return false;
+ var index = _span.IndexOf(_char);
+
+ if (index == -1)
+ {
+ Current = _span;
+ _span = default;
+ }
+ else
+ {
+ Current = _span.Slice(0, index);
+ _span = _span.Slice(index + 1);
+ }
+ return true;
+ }
+}
diff --git a/TwitchLib.Client.Models/Extractors/EmoteExtractor.cs b/TwitchLib.Client.Models/Extractors/EmoteExtractor.cs
index 8cdff6bb..7e89eddf 100644
--- a/TwitchLib.Client.Models/Extractors/EmoteExtractor.cs
+++ b/TwitchLib.Client.Models/Extractors/EmoteExtractor.cs
@@ -1,81 +1,44 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Models.Builders;
+using TwitchLib.Client.Models.Extensions;
namespace TwitchLib.Client.Models.Extractors
{
- public class EmoteExtractor
+ public static class EmoteExtractor
{
- public IEnumerable Extract(string rawEmoteSetString, string message)
+ public static List Extract(string? rawEmoteSetString, string message)
{
- if (string.IsNullOrEmpty(rawEmoteSetString)
- || string.IsNullOrEmpty(message))
- {
- yield break;
- }
-
- if (rawEmoteSetString.Contains("/"))
+ var emotes = new List();
+ if (string.IsNullOrEmpty(rawEmoteSetString) || string.IsNullOrEmpty(message))
+ return emotes;
+
+ System.Globalization.StringInfo messageInfo = new(message);
+ // 25:5-9,28-32/28087:15-21 => 25:5-9,28-32 28087:15-21
+#pragma warning disable CS8604 // Possible null reference argument. false positiv in NS 2.0
+ foreach (var emoteData in new SpanSliceEnumerator(rawEmoteSetString, '/'))
+#pragma warning restore CS8604 // Possible null reference argument.
{
- // Message contains multiple different emotes, first parse by unique emotes: 28087:15-21/25:5-9,28-32
- foreach (var emoteData in rawEmoteSetString.Split('/'))
+ var index = emoteData.IndexOf(':');
+ var emoteId = emoteData.Slice(0, index).ToString();
+ // 25:5-9,28-32 => 5-9 28-32
+ foreach (var emote in new SpanSliceEnumerator(emoteData.Slice(index + 1), ','))
{
- var emoteId = emoteData.Split(':')[0];
- if (emoteData.Contains(","))
- {
- // Multiple copies of a single emote: 25:5-9,28-32
- foreach (var emote in emoteData.Replace($"{emoteId}:", "").Split(','))
- yield return GetEmote(emote, emoteId, message);
- }
- else
- {
- // Single copy of single emote: 25:5-9/28087:16-22
- yield return GetEmote(emoteData, emoteId, message, true);
- }
+ index = emote.IndexOf('-');
+ var startSlice = emote.Slice(0, index);
+ var endSlice = emote.Slice(index + 1);
+#if NETSTANDARD2_0
+ var start = int.Parse(startSlice.ToString());
+ var end = int.Parse(endSlice.ToString());
+#else
+ var start = int.Parse(startSlice);
+ var end = int.Parse(endSlice);
+#endif
+ int trueStart = messageInfo.SubstringByTextElements(0, start + 1).Length - 1;
+ string name = messageInfo.SubstringByTextElements(start, end - start + 1);
+ int trueEnd = trueStart + name.Length - 1;
+
+ emotes.Add(new(emoteId, name, trueStart, trueEnd));
}
}
- else
- {
- var emoteId = rawEmoteSetString.Split(':')[0];
- // Message contains a single, or multiple of the same emote
- if (rawEmoteSetString.Contains(","))
- {
- // Multiple copies of a single emote: 25:5-9,28-32
- foreach (var emote in rawEmoteSetString.Replace($"{emoteId}:", "").Split(','))
- yield return GetEmote(emote, emoteId, message);
- }
- else
- {
- // Single copy of single emote: 25:5-9
- yield return GetEmote(rawEmoteSetString, emoteId, message, true);
- }
- }
- }
-
- private Emote GetEmote(string emoteData, string emoteId, string message, bool single = false)
- {
- int startIndex = -1;
- int endIndex = -1;
-
- if (single)
- {
- startIndex = int.Parse(emoteData.Split(':')[1].Split('-')[0]);
- endIndex = int.Parse(emoteData.Split(':')[1].Split('-')[1]);
- }
- else
- {
- startIndex = int.Parse(emoteData.Split('-')[0]);
- endIndex = int.Parse(emoteData.Split('-')[1]);
- }
-
- string name = message.Substring(startIndex, (endIndex - startIndex) + 1);
-
- EmoteBuilder emoteBuilder = EmoteBuilder.Create()
- .WithId(emoteId)
- .WithName(name)
- .WithStartIndex(startIndex)
- .WithEndIndex(endIndex);
-
- return emoteBuilder.Build();
+ return emotes;
}
}
}
diff --git a/TwitchLib.Client.Models/Extractors/IExtractor.cs b/TwitchLib.Client.Models/Extractors/IExtractor.cs
deleted file mode 100644
index a3786ab7..00000000
--- a/TwitchLib.Client.Models/Extractors/IExtractor.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using TwitchLib.Client.Models.Internal;
-
-namespace TwitchLib.Client.Models.Extractors
-{
- public interface IExtractor
- {
- TResult Extract(IrcMessage ircMessage);
- }
-}
diff --git a/TwitchLib.Client.Models/Extractors/UserDetailsExtractor.cs b/TwitchLib.Client.Models/Extractors/UserDetailsExtractor.cs
new file mode 100644
index 00000000..aef9000b
--- /dev/null
+++ b/TwitchLib.Client.Models/Extractors/UserDetailsExtractor.cs
@@ -0,0 +1,23 @@
+using TwitchLib.Client.Enums;
+
+namespace TwitchLib.Client.Models.Extractors;
+
+internal static class UserDetailsExtractor
+{
+ public static UserDetails Extract(List> badges)
+ {
+ var userDetails = UserDetails.None;
+ foreach (var item in badges)
+ {
+ userDetails |= item.Key switch
+ {
+ "founder" or "subscriber" => UserDetails.Subscriber,
+ "admin" or "staff" => UserDetails.Staff,
+ "partner" => UserDetails.Partner,
+ "vip" => UserDetails.Vip,
+ _ => default
+ };
+ }
+ return userDetails;
+ }
+}
diff --git a/TwitchLib.Client.Models/GiftedSubscription.cs b/TwitchLib.Client.Models/GiftedSubscription.cs
index b6f84faf..a55162e7 100644
--- a/TwitchLib.Client.Models/GiftedSubscription.cs
+++ b/TwitchLib.Client.Models/GiftedSubscription.cs
@@ -1,234 +1,157 @@
-using System;
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+//SubGift
+public class GiftedSubscription : UserNoticeBase
{
- public class GiftedSubscription
+ private Goal? _goal;
+
+ public Goal? MsgParamGoal { get => _goal; protected set => _goal = value; }
+
+ public bool IsAnonymous { get; }
+
+ ///
+ /// The total number of months the user has subscribed.
+ ///
+ public string MsgParamMonths { get; protected set; } = default!;
+
+ ///
+ /// If this message sourced from an event, this is the ID of that event
+ ///
+ public string MsgParamOriginId { get; protected set; }
+
+ ///
+ /// The display name of the subscription gift recipient.
+ ///
+ public string MsgParamRecipientDisplayName { get; protected set; } = default!;
+
+ ///
+ /// The user ID of the subscription gift recipient.
+ ///
+ public string MsgParamRecipientId { get; protected set; } = default!;
+
+ ///
+ /// The user name of the subscription gift recipient.
+ ///
+ public string MsgParamRecipientUserName { get; protected set; } = default!;
+
+ public int MsgParamSenderCount { get; protected set; }
+
+ ///
+ /// The type of subscription plan being used.
+ ///
+ public SubscriptionPlan MsgParamSubPlan { get; protected set; }
+
+ ///
+ /// The display name of the subscription plan. This may be a default name or one created by the channel owner.
+ ///
+ public string MsgParamSubPlanName { get; protected set; } = default!;
+
+ ///
+ /// The number of months gifted as part of a single, multi-month gift.
+ ///
+ public int MsgParamMultiMonthGiftDuration { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GiftedSubscription(IrcMessage ircMessage) : base(ircMessage)
{
- private const string AnonymousGifterUserId = "274598607";
-
- public List> Badges { get; }
-
- public List> BadgeInfo { get; }
-
- public string Color { get; }
-
- public string DisplayName { get; }
-
- public string Emotes { get; }
-
- public string Id { get; }
-
- public bool IsModerator { get; }
-
- public bool IsSubscriber { get; }
-
- public bool IsTurbo { get; }
-
- public bool IsAnonymous { get; }
-
- public string Login { get; }
-
- public string MsgId { get; }
-
- public string MsgParamMonths { get; }
-
- public string MsgParamRecipientDisplayName { get; }
-
- public string MsgParamRecipientId { get; }
-
- public string MsgParamRecipientUserName { get; }
-
- public string MsgParamSubPlanName { get; }
-
- public SubscriptionPlan MsgParamSubPlan { get; }
-
- public string RoomId { get; }
-
- public string SystemMsg { get; }
-
- public string SystemMsgParsed { get; }
-
- public string TmiSentTs { get; }
-
- public string UserId { get; }
-
- public UserType UserType { get; }
-
- public string MsgParamMultiMonthGiftDuration { get; }
+ IsAnonymous = UserId == AnonymousGifterUserId;
+ }
- public GiftedSubscription(IrcMessage ircMessage)
- {
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- Color = tagValue;
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- Emotes = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.MsgParamMonths:
- MsgParamMonths = tagValue;
- break;
- case Tags.MsgParamRecipientDisplayname:
- MsgParamRecipientDisplayName = tagValue;
- break;
- case Tags.MsgParamRecipientId:
- MsgParamRecipientId = tagValue;
- break;
- case Tags.MsgParamRecipientUsername:
- MsgParamRecipientUserName = tagValue;
- break;
- case Tags.MsgParamSubPlanName:
- MsgParamSubPlanName = tagValue;
- break;
- case Tags.MsgParamSubPlan:
- switch (tagValue)
- {
- case "prime":
- MsgParamSubPlan = SubscriptionPlan.Prime;
- break;
- case "1000":
- MsgParamSubPlan = SubscriptionPlan.Tier1;
- break;
- case "2000":
- MsgParamSubPlan = SubscriptionPlan.Tier2;
- break;
- case "3000":
- MsgParamSubPlan = SubscriptionPlan.Tier3;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(tagValue.ToLower));
- }
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- IsSubscriber = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.SystemMsg:
- SystemMsg = tagValue;
- SystemMsgParsed = tagValue.Replace("\\s", " ").Replace("\\n", "");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.Turbo:
- IsTurbo = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.UserId:
- UserId = tagValue;
- if (UserId == AnonymousGifterUserId)
- {
- IsAnonymous = true;
- }
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- case Tags.MsgParamMultiMonthGiftDuration:
- MsgParamMultiMonthGiftDuration = tagValue;
- break;
- }
- }
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GiftedSubscription(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ Goal? msgParamGoal,
+ string msgParamMonths,
+ string msgParamOriginId,
+ string msgParamRecipientDisplayName,
+ string msgParamRecipientId,
+ string msgParamRecipientUserName,
+ int msgParamSenderCount,
+ SubscriptionPlan msgParamSubPlan,
+ string msgParamSubPlanName,
+ int msgParamMultiMonthGiftDuration)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamGoal = msgParamGoal;
+ IsAnonymous = userId == AnonymousGifterUserId;
+ MsgParamMonths = msgParamMonths;
+ MsgParamOriginId = msgParamOriginId;
+ MsgParamRecipientDisplayName = msgParamRecipientDisplayName;
+ MsgParamRecipientId = msgParamRecipientId;
+ MsgParamRecipientUserName = msgParamRecipientUserName;
+ MsgParamSenderCount = msgParamSenderCount;
+ MsgParamSubPlan = msgParamSubPlan;
+ MsgParamSubPlanName = msgParamSubPlanName;
+ MsgParamMultiMonthGiftDuration = msgParamMultiMonthGiftDuration;
+ }
- public GiftedSubscription(
- List> badges,
- List> badgeInfo,
- string color,
- string displayName,
- string emotes,
- string id,
- string login,
- bool isModerator,
- string msgId,
- string msgParamMonths,
- string msgParamRecipientDisplayName,
- string msgParamRecipientId,
- string msgParamRecipientUserName,
- string msgParamSubPlanName,
- string msgMultiMonthDuration,
- SubscriptionPlan msgParamSubPlan,
- string roomId,
- bool isSubscriber,
- string systemMsg,
- string systemMsgParsed,
- string tmiSentTs,
- bool isTurbo,
- UserType userType,
- string userId)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
- Badges = badges;
- BadgeInfo = badgeInfo;
- Color = color;
- DisplayName = displayName;
- Emotes = emotes;
- Id = id;
- Login = login;
- IsModerator = isModerator;
- MsgId = msgId;
- MsgParamMonths = msgParamMonths;
- MsgParamRecipientDisplayName = msgParamRecipientDisplayName;
- MsgParamRecipientId = msgParamRecipientId;
- MsgParamRecipientUserName = msgParamRecipientUserName;
- MsgParamSubPlanName = msgParamSubPlanName;
- MsgParamSubPlan = msgParamSubPlan;
- MsgParamMultiMonthGiftDuration = msgMultiMonthDuration;
- RoomId = roomId;
- IsSubscriber = isSubscriber;
- SystemMsg = systemMsg;
- SystemMsgParsed = systemMsgParsed;
- TmiSentTs = tmiSentTs;
- IsTurbo = isTurbo;
- UserType = userType;
- UserId = userId;
+ case Tags.MsgParamMonths:
+ MsgParamMonths = tag.Value;
+ break;
+ case Tags.MsgParamOriginId:
+ MsgParamOriginId = tag.Value;
+ break;
+ case Tags.MsgParamRecipientDisplayName:
+ MsgParamRecipientDisplayName = tag.Value;
+ break;
+ case Tags.MsgParamRecipientId:
+ MsgParamRecipientId = tag.Value;
+ break;
+ case Tags.MsgParamRecipientUsername:
+ MsgParamRecipientUserName = tag.Value;
+ break;
+ case Tags.MsgParamSubPlanName:
+ MsgParamSubPlanName = tag.Value;
+ break;
+ case Tags.MsgParamSubPlan:
+ MsgParamSubPlan = TagHelper.ToSubscriptionPlan(tag.Value);
+ break;
+ case Tags.MsgParamMultiMonthGiftDuration:
+ MsgParamMultiMonthGiftDuration = int.Parse(tag.Value);
+ break;
+ default:
+ return Goal.TrySetTag(ref _goal, tag);
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/Goal.cs b/TwitchLib.Client.Models/Goal.cs
new file mode 100644
index 00000000..a86f26f6
--- /dev/null
+++ b/TwitchLib.Client.Models/Goal.cs
@@ -0,0 +1,41 @@
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class Goal
+{
+ public string ContributionType { get; protected set; } = default!; //SUB_POINTS, SUB
+
+ public int CurrentContributions { get; protected set; }
+
+ public string? Description { get; protected set; }
+
+ public int TargetContributions { get; protected set; }
+
+ public int UserContributions { get; protected set; }
+
+ internal static bool TrySetTag(ref Goal? goal, KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamGoalContributionType:
+ (goal ??= new()).ContributionType = tag.Value;
+ break;
+ case Tags.MsgParamGoalCurrentContributions:
+ (goal ??= new()).CurrentContributions = int.Parse(tag.Value); ;
+ break;
+ case Tags.MsgParamGoalDescription:
+ (goal ??= new()).Description = tag.Value;
+ break;
+ case Tags.MsgParamGoalTargetContributions:
+ (goal ??= new()).TargetContributions = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamGoalUserContributions:
+ (goal ??= new()).UserContributions = int.Parse(tag.Value);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/TwitchLib.Client.Models/HypeChat.cs b/TwitchLib.Client.Models/HypeChat.cs
new file mode 100644
index 00000000..984cbad5
--- /dev/null
+++ b/TwitchLib.Client.Models/HypeChat.cs
@@ -0,0 +1,65 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+public class HypeChat
+{
+ ///
+ /// The value of the Hype Chat sent by the user.
+ ///
+ public int Amount { get; internal set; }
+
+ public double CalculatedAmount => Exponent == 0
+ ? Amount
+ : Amount / (10 * Exponent);
+
+ ///
+ /// The ISO 4217 alphabetic currency code the user has sent the Hype Chat in.
+ ///
+ public string Currency { get; internal set; } = default!;
+
+ ///
+ /// Indicates how many decimal points this currency represents partial amounts in. Decimal points start from the right side of the value defined in pinned-chat-paid-amount.
+ ///
+ public int Exponent { get; internal set; }
+
+ ///
+ /// The level of the Hype Chat, in English.Possible values are:
+ ///
+ public PaidLevel Level { get; internal set; }
+
+ ///
+ /// A Boolean value that determines if the message sent with the Hype Chat was filled in by the system.
+ ///
+ ///
+ /// If true (1), the user entered no message and the body message was automatically filled in by the system.
+ /// If false (0), the user provided their own message to send with the Hype Chat.
+ ///
+ public bool IsSystemMessage { get; internal set; }
+
+ internal static bool TrySetTag(ref HypeChat? hypeChat, KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.PinnedChatPaidAmount:
+ (hypeChat ??= new()).Amount = int.Parse(tag.Value);
+ break;
+ case Tags.PinnedChatPaidCurrency:
+ (hypeChat ??= new()).Currency = tag.Value;
+ break;
+ case Tags.PinnedChatPaidExponent:
+ (hypeChat ??= new()).Exponent = int.Parse(tag.Value);
+ break;
+ case Tags.PinnedChatPaidLevel:
+ (hypeChat ??= new()).Level = Enum.TryParse(tag.Value, true, out var val)
+ ? val
+ : throw new ArgumentException($"Requested value '{tag.Value}' was not found.");
+ break;
+ case Tags.PinnedChatPaidIsSystemMessage:
+ (hypeChat ??= new()).IsSystemMessage = TagHelper.ToBool(tag.Value);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/TwitchLib.Client.Models/Interfaces/IHexColorProperty.cs b/TwitchLib.Client.Models/Interfaces/IHexColorProperty.cs
new file mode 100644
index 00000000..91896749
--- /dev/null
+++ b/TwitchLib.Client.Models/Interfaces/IHexColorProperty.cs
@@ -0,0 +1,9 @@
+namespace TwitchLib.Client.Models.Interfaces;
+
+public interface IHexColorProperty
+{
+ ///
+ /// Property representing HEX color
+ ///
+ string HexColor { get; }
+}
diff --git a/TwitchLib.Client.Models/Interfaces/ISendOptions.cs b/TwitchLib.Client.Models/Interfaces/ISendOptions.cs
new file mode 100644
index 00000000..b3a4bff1
--- /dev/null
+++ b/TwitchLib.Client.Models/Interfaces/ISendOptions.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace TwitchLib.Client.Models.Interfaces
+{
+ public interface ISendOptions
+ {
+ ///
+ /// Number of Messages Allowed Per Instance of the .
+ ///
+ uint SendsAllowedInPeriod { get; }
+
+ ///
+ /// Minimum time between sending items from the queue [in ms] (default 50ms).
+ ///
+ ushort SendDelay { get; }
+
+ ///
+ /// Period Between each reset of the throttling instance window.
+ ///
+ TimeSpan ThrottlingPeriod { get; }
+
+ ///
+ /// The amount of time an object can wait to be sent before it is considered dead, and should be skipped (default 30 minutes).
+ /// A dead item will be ignored and removed from the send queue when it is hit.
+ ///
+ TimeSpan CacheItemTimeout { get; }
+
+ ///
+ /// Maximum number of Queued outgoing messages (default 10_000).
+ ///
+ uint QueueCapacity { get; }
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Internal/IrcMessage.cs b/TwitchLib.Client.Models/Internal/IrcMessage.cs
index dd2fcca5..a2e2a3ee 100644
--- a/TwitchLib.Client.Models/Internal/IrcMessage.cs
+++ b/TwitchLib.Client.Models/Internal/IrcMessage.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
using System.Text;
using TwitchLib.Client.Enums.Internal;
@@ -10,41 +9,44 @@ public class IrcMessage
///
/// The channel the message was sent in
///
- public string Channel => Params.StartsWith("#") ? Params.Remove(0, 1) : Params;
+ public string Channel => _channel ??= Params.StartsWith("#") ? Params.Remove(0, 1) : Params;
+ private string? _channel;
- public string Params => _parameters != null && _parameters.Length > 0 ? _parameters[0] : "";
+ public string Params => _parameters?.Length > 0 ? _parameters[0] : "";
///
/// Message itself
///
public string Message => Trailing;
- public string Trailing => _parameters != null && _parameters.Length > 1 ? _parameters[_parameters.Length - 1] : "";
+ public string Trailing => _parameters?.Length > 1 ? _parameters[_parameters.Length - 1] : "";
///
/// Command parameters
///
- private readonly string[] _parameters;
+ private readonly string[]? _parameters;
///
/// The user whose message it is
///
- public readonly string User;
+ public string User { get; }
///
/// Hostmask of the user
///
- public readonly string Hostmask;
+ public string? Hostmask { get; }
///
/// Raw Command
///
- public readonly IrcCommand Command;
+ public IrcCommand Command { get; }
///
/// IRCv3 tags
///
- public readonly Dictionary Tags;
+ public Dictionary Tags { get; }
+
+ private string? _rawString;
///
/// Create an INCOMPLETE IrcMessage only carrying username
@@ -56,7 +58,7 @@ public IrcMessage(string user)
User = user;
Hostmask = null;
Command = IrcCommand.Unknown;
- Tags = null;
+ Tags = new Dictionary();
}
///
@@ -70,58 +72,91 @@ public IrcMessage(
IrcCommand command,
string[] parameters,
string hostmask,
- Dictionary tags = null)
+ Dictionary? tags = null)
{
var idx = hostmask.IndexOf('!');
- User = idx != -1 ? hostmask.Substring(0, idx) : hostmask;
+ User = idx >= 0 ? hostmask.Substring(0, idx) : hostmask;
Hostmask = hostmask;
+ Command = command;
+ Tags = tags ?? new Dictionary();
+
_parameters = parameters;
+ if (command == IrcCommand.RPL_353
+ && Params.Length > 0
+ && Params.Contains("#"))
+ {
+ _parameters[0] = $"#{_parameters[0].Split('#')[1]}";
+ }
+ }
+
+ ///
+ /// Create an IrcMessage, settings its raw string.
+ /// IrcParser *must* use this constructor, otherwise the raw string
+ /// will be re-generated each time it is parsed and then passed to handlers.
+ ///
+ /// Raw IRC message
+ /// IRC Command
+ /// Command params
+ /// User
+ /// Hostmask
+ /// IRCv3 tags
+ internal IrcMessage(
+ string raw,
+ IrcCommand command,
+ string[] parameters,
+ string user,
+ string hostmask,
+ Dictionary? tags = null)
+ {
+ User = user;
+ Hostmask = hostmask;
Command = command;
- Tags = tags;
+ Tags = tags ?? new Dictionary();
- if (command == IrcCommand.RPL_353)
+ _rawString = raw;
+ _parameters = parameters;
+ if (command == IrcCommand.RPL_353
+ && Params.Length > 0
+ && Params.Contains("#"))
{
- if(Params.Length > 0 && Params.Contains("#"))
- {
- _parameters[0] = $"#{_parameters[0].Split('#')[1]}";
- }
+ _parameters[0] = $"#{_parameters[0].Split('#')[1]}";
}
}
- public new string ToString()
+ ///
+ public override string ToString() => _rawString ??= GenerateToString();
+
+ private string GenerateToString()
{
- var raw = new StringBuilder(32);
- if (Tags != null)
+ var raw = new StringBuilder(128);
+ if (Tags?.Count > 0)
{
- var tags = new string[Tags.Count];
- var i = 0;
+ raw.Append('@');
foreach (var tag in Tags)
{
- tags[i] = tag.Key + "=" + tag.Value;
- ++i;
- }
-
- if (tags.Length > 0)
- {
- raw.Append("@").Append(string.Join(";", tags)).Append(" ");
+ raw.Append(tag.Key).Append('=').Append(tag.Value).Append(';');
}
+ raw[raw.Length - 1] = ' ';
}
if (!string.IsNullOrEmpty(Hostmask))
{
- raw.Append(":").Append(Hostmask).Append(" ");
+ raw.Append(':').Append(Hostmask).Append(' ');
}
- raw.Append(Command.ToString().ToUpper().Replace("RPL_", ""));
- if (_parameters.Length <= 0)
+ // The "RPL_" replace is required because TwitchLib.Client.Enums.Internal.IrcCommand
+ // has the RPL_ prefix on all RPL commands, but the Twitch IRCv3 spec does not.
+ // Thus, if the message has not been constructed with _rawString from incoming data, remove the prefix.
+ raw.Append(Command.ToString().ToUpperInvariant().Replace("RPL_", ""));
+ if (_parameters == null || _parameters.Length == 0)
return raw.ToString();
- if (_parameters[0] != null && _parameters[0].Length > 0)
+ if (!string.IsNullOrEmpty(_parameters[0]))
{
raw.Append(" ").Append(_parameters[0]);
}
- if (_parameters.Length > 1 && _parameters[1] != null && _parameters[1].Length > 0)
+ if (_parameters.Length > 1 && _parameters[1]?.Length > 0)
{
raw.Append(" :").Append(_parameters[1]);
}
@@ -129,4 +164,4 @@ public IrcMessage(
return raw.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/TwitchLib.Client.Models/Internal/MsgIds.cs b/TwitchLib.Client.Models/Internal/MsgIds.cs
index 531f4f42..ff15d550 100644
--- a/TwitchLib.Client.Models/Internal/MsgIds.cs
+++ b/TwitchLib.Client.Models/Internal/MsgIds.cs
@@ -1,4 +1,6 @@
-namespace TwitchLib.Client.Models.Internal
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace TwitchLib.Client.Models.Internal
{
public static class MsgIds
{
@@ -9,10 +11,13 @@ public static class MsgIds
public const string AlreadyR9KOn = "already_r9k_on";
public const string AlreadySubsOff = "already_subs_off";
public const string AlreadySubsOn = "already_subs_on";
+ public const string AnonGiftPaidUpgrade = "anongiftpaidupgrade";
public const string Announcement = "announcement";
public const string BadUnbanNoBan = "bad_unban_no_ban";
public const string BanSuccess = "ban_success";
+ public const string BitsBadgeTier = "bitsbadgetier";
public const string ColorChanged = "color_changed";
+ public const string CommunityPayForward = "communitypayforward";
public const string EmoteOnlyOff = "emote_only_off";
public const string EmoteOnlyOn = "emote_only_on";
public const string HighlightedMessage = "highlighted-message";
@@ -38,16 +43,21 @@ public static class MsgIds
public const string RaidErrorSelf = "raid_error_self";
public const string RaidNoticeMature = "raid_notice_mature";
public const string ReSubscription = "resub";
+ public const string Ritual = "ritual";
public const string R9KOff = "r9k_off";
public const string R9KOn = "r9k_on";
+ public const string StandardPayForward = "standardpayforward";
public const string SubGift = "subgift";
public const string CommunitySubscription = "submysterygift";
public const string ContinuedGiftedSubscription = "giftpaidupgrade";
public const string Subscription = "sub";
public const string SubsOff = "subs_off";
public const string SubsOn = "subs_on";
+ public const string SlowOn = "slow_on";
+ public const string SlowOff = "slow_off";
public const string TimeoutSuccess = "timeout_success";
public const string UnbanSuccess = "unban_success";
+ public const string Unraid = "unraid";
public const string UnrecognizedCmd = "unrecognized_cmd";
public const string UserIntro = "user-intro";
public const string VIPsSuccess = "vips_success";
diff --git a/TwitchLib.Client.Models/Internal/TagHelper.cs b/TwitchLib.Client.Models/Internal/TagHelper.cs
new file mode 100644
index 00000000..8a03a187
--- /dev/null
+++ b/TwitchLib.Client.Models/Internal/TagHelper.cs
@@ -0,0 +1,64 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Extensions;
+
+namespace TwitchLib.Client.Models.Internal;
+
+internal static class TagHelper
+{
+ ///
+ /// Parses the badges field in GLOBALUSERSTATE, PRIVMSG, USERNOTICE, USERSTATE, etc
+ ///
+ /// The data.
+ /// List of keyvalue pairs representing each badge and value associated
+ public static List> ToBadges(string badgesStr)
+ {
+ var badges = new List>();
+
+ if (badgesStr.Contains('/'))
+ {
+ foreach (var badge in new SpanSliceEnumerator(badgesStr, ','))
+ {
+ var index = badge.IndexOf('/');
+ var key = badge.Slice(0, index).ToString();
+ var value = badge.Slice(index + 1).ToString();
+ badges.Add(new KeyValuePair(key, value));
+ }
+ }
+ return badges;
+ }
+
+ public static UserType ToUserType(string s)
+ {
+ return s switch
+ {
+ "mod" => UserType.Moderator,
+ "global_mod" => UserType.GlobalModerator,
+ "admin" => UserType.Admin,
+ "staff" => UserType.Staff,
+ _ => UserType.Viewer,
+ };
+ }
+
+ public static SubscriptionPlan ToSubscriptionPlan(string s)
+ {
+ return s switch
+ {
+ "Prime" or "prime" => SubscriptionPlan.Prime,
+ "1000" => SubscriptionPlan.Tier1,
+ "2000" => SubscriptionPlan.Tier2,
+ "3000" => SubscriptionPlan.Tier3,
+ _ => throw new ArgumentException($"Unhandled value {s} for {nameof(UserType)}", nameof(s)),
+ };
+ }
+
+ public static bool ToBool(string s)
+ {
+ return s == "1";
+ }
+
+ public static DateTimeOffset ToDateTimeOffsetFromUnixMs(string s)
+ {
+ var timestamp = long.Parse(s);
+ return DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
+ }
+}
diff --git a/TwitchLib.Client.Models/Internal/Tags.cs b/TwitchLib.Client.Models/Internal/Tags.cs
index 09978936..4bb8ba86 100644
--- a/TwitchLib.Client.Models/Internal/Tags.cs
+++ b/TwitchLib.Client.Models/Internal/Tags.cs
@@ -1,4 +1,5 @@
-namespace TwitchLib.Client.Models.Internal
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+namespace TwitchLib.Client.Models.Internal
{
public static class Tags
{
@@ -28,7 +29,17 @@ public static class Tags
public const string MsgParamDisplayname = "msg-param-displayName"; // Sent only on raid
public const string MsgParamLogin = "msg-param-login"; // Sent only on raid
public const string MsgParamCumulativeMonths = "msg-param-cumulative-months"; // Sent only on sub, resub
+ public const string MsgParamGiftTheme = "msg-param-gift-theme";
+ public const string MsgParamGoalContributionType = "msg-param-goal-contribution-type";
+ public const string MsgParamGoalCurrentContributions = "msg-param-goal-current-contributions";
+ public const string MsgParamGoalDescription = "msg-param-goal-description";
+ public const string MsgParamGoalTargetContributions = "msg-param-goal-target-contributions";
+ public const string MsgParamGoalUserContributions = "msg-param-goal-user-contributions";
public const string MsgParamMonths = "msg-param-months"; // Sent only on subgift, anonsubgift
+ public const string MsgParamPriorGifterAnonymous = "msg-param-prior-gifter-anonymous";
+ public const string MsgParamPriorGifterDisplayName = "msg-param-prior-gifter-display-name";
+ public const string MsgParamPriorGifterId = "msg-param-prior-gifter-id";
+ public const string MsgParamPriorGifterUserName = "msg-param-prior-gifter-user-name";
public const string MsgParamPromoGiftTotal = "msg-param-promo-gift-total"; // Sent only on anongiftpaidupgrade, giftpaidupgrade
public const string MsgParamPromoName = "msg-param-promo-name"; // Sent only on anongiftpaidupgrade, giftpaidupgrade
public const string MsgParamShouldShareStreak = "msg-param-should-share-streak"; // Sent only on sub, resub
@@ -36,7 +47,8 @@ public static class Tags
public const string MsgParamSubPlan = "msg-param-sub-plan"; // Sent only on sub, resub, subgift, anonsubgift
public const string MsgParamSubPlanName = "msg-param-sub-plan-name"; // Sent only on sub, resub, subgift, anonsubgift
public const string MsgParamViewerCount = "msg-param-viewerCount"; // Sent only on raid
- public const string MsgParamRecipientDisplayname = "msg-param-recipient-display-name"; // Sent only on subgift, anonsubgift
+ public const string MsgParamOriginId = "msg-param-origin-id";
+ public const string MsgParamRecipientDisplayName = "msg-param-recipient-display-name"; // Sent only on subgift, anonsubgift
public const string MsgParamRecipientId = "msg-param-recipient-id"; // Sent only on subgift, anonsubgift
public const string MsgParamRecipientUsername = "msg-param-recipient-user-name"; // Sent only on subgift, anonsubgift
public const string MsgParamRitualName = "msg-param-ritual-name"; // Sent only on ritual
@@ -46,11 +58,22 @@ public static class Tags
public const string MsgParamSenderName = "msg-param-sender-name"; // Sent only on giftpaidupgrade
public const string MsgParamThreshold = "msg-param-threshold"; // Sent only on bitsbadgetier
public const string Noisy = "noisy";
+ public const string PinnedChatPaidAmount = "pinned-chat-paid-amount";
+ public const string PinnedChatPaidCurrency = "pinned-chat-paid-currency";
+ public const string PinnedChatPaidExponent = "pinned-chat-paid-exponent";
+ public const string PinnedChatPaidLevel = "pinned-chat-paid-level";
+ public const string PinnedChatPaidIsSystemMessage = "pinned-chat-paid-is-system-message";
+
+ #region Sent only on replies
public const string ReplyParentDisplayName = "reply-parent-display-name"; // Sent only on replies
public const string ReplyParentMsgBody = "reply-parent-msg-body"; // Sent only on replies
public const string ReplyParentMsgId = "reply-parent-msg-id"; // Sent only on replies
public const string ReplyParentUserId = "reply-parent-user-id"; // Sent only on replies
public const string ReplyParentUserLogin = "reply-parent-user-login"; // Sent only on replies
+ public const string ReplyThreadParentMsgId = "reply-thread-parent-msg-id";
+ public const string ReplyThreadParentUserLogin = "reply-thread-parent-user-login";
+ #endregion
+
public const string Rituals = "rituals";
public const string RoomId = "room-id";
public const string R9K = "r9k";
diff --git a/TwitchLib.Client.Models/JoinedChannel.cs b/TwitchLib.Client.Models/JoinedChannel.cs
index 75df2a63..ecefd752 100644
--- a/TwitchLib.Client.Models/JoinedChannel.cs
+++ b/TwitchLib.Client.Models/JoinedChannel.cs
@@ -9,11 +9,11 @@ public class JoinedChannel
/// The current channel the TwitcChatClient is connected to.
public string Channel { get; }
- /// Object representing current state of channel (r9k, slow, etc).
- public ChannelState ChannelState { get; protected set; }
+ ///// Object representing current state of channel (r9k, slow, etc).
+ //public ChannelState ChannelState { get; protected set; }
/// The most recent message received.
- public ChatMessage PreviousMessage { get; protected set; }
+ public ChatMessage? PreviousMessage { get; protected set; }
/// JoinedChannel object constructor.
public JoinedChannel(string channel)
diff --git a/TwitchLib.Client.Models/MessageEmote.cs b/TwitchLib.Client.Models/MessageEmote.cs
index 72e70d14..73e458b2 100644
--- a/TwitchLib.Client.Models/MessageEmote.cs
+++ b/TwitchLib.Client.Models/MessageEmote.cs
@@ -1,5 +1,5 @@
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
+using System.Text;
using System.Text.RegularExpressions;
// TODO: Missing builder
@@ -73,6 +73,23 @@ public class MessageEmote
"//cdn.betterttv.net/emote/{0}/3x"
}
);
+
+ ///
+ /// Collection of Composite Format Strings which will substitute
+ /// an emote ID to get a URL for an image from the 7tv CDN
+ ///
+ ///
+ /// These are sorted such that the enum can be used as an index,
+ /// eg SevenTvEmoteUrls[]
+ ///
+ public static readonly ReadOnlyCollection SevenTvEmoteUrls = new ReadOnlyCollection(
+ new[]
+ {
+ "//cdn.7tv.app/emote/{0}/1x.avif",
+ "//cdn.7tv.app/emote/{0}/2x.avif",
+ "//cdn.7tv.app/emote/{0}/5x.avif"
+ }
+ );
#endregion Third-Party Emote URLs
///
@@ -85,16 +102,14 @@ public class MessageEmote
public static string SourceMatchingReplacementText(MessageEmote caller)
{
var sizeIndex = (int)caller.Size;
- switch (caller.Source)
+ return caller.Source switch
{
- case EmoteSource.BetterTwitchTv:
- return string.Format(BetterTwitchTvEmoteUrls[sizeIndex], caller.Id);
- case EmoteSource.FrankerFaceZ:
- return string.Format(FrankerFaceZEmoteUrls[sizeIndex], caller.Id);
- case EmoteSource.Twitch:
- return string.Format(TwitchEmoteUrls[sizeIndex], caller.Id);
- }
- return caller.Text;
+ EmoteSource.SevenTv => string.Format(SevenTvEmoteUrls[sizeIndex], caller.Id),
+ EmoteSource.BetterTwitchTv => string.Format(BetterTwitchTvEmoteUrls[sizeIndex], caller.Id),
+ EmoteSource.FrankerFaceZ => string.Format(FrankerFaceZEmoteUrls[sizeIndex], caller.Id),
+ EmoteSource.Twitch => string.Format(TwitchEmoteUrls[sizeIndex], caller.Id),
+ _ => caller.Text,
+ };
}
/// Enum supplying the supported sites which provide Emote images.
@@ -107,7 +122,10 @@ public enum EmoteSource
FrankerFaceZ,
/// Emotes hosted by BetterTTV.net
- BetterTwitchTv
+ BetterTwitchTv,
+
+ /// Emotes hosted by 7tv
+ SevenTv
}
/// Enum denoting the emote sizes
@@ -132,30 +150,26 @@ public enum EmoteSize
Large = 2
}
- private readonly string _id, _text, _escapedText;
- private readonly EmoteSource _source;
- private readonly EmoteSize _size;
-
///
/// Emote ID as used by the emote source. Will be provided as {0}
/// to be substituted into the indicated URL if needed.
///
- public string Id => _id;
+ public string Id { get; }
///
/// Emote text which appears in a message and is meant to be replaced by the emote image.
///
- public string Text => _text;
+ public string Text { get; }
///
/// The specified for this emote.
///
- public EmoteSource Source => _source;
+ public EmoteSource Source { get; }
///
/// The specified for this emote.
///
- public EmoteSize Size => _size;
+ public EmoteSize Size { get; }
///
/// The string to substitute emote text for.
@@ -172,7 +186,7 @@ public enum EmoteSize
/// The emote text regex-escaped
/// so that it can be embedded into a regex pattern.
///
- public string EscapedText => _escapedText;
+ public string EscapedText { get; }
///
/// Constructor for a new MessageEmote instance.
@@ -201,13 +215,13 @@ public MessageEmote(
string text,
EmoteSource source = EmoteSource.Twitch,
EmoteSize size = EmoteSize.Small,
- ReplaceEmoteDelegate replacementDelegate = null)
+ ReplaceEmoteDelegate? replacementDelegate = null)
{
- _id = id;
- _text = text;
- _escapedText = Regex.Escape(text);
- _source = source;
- _size = size;
+ Id = id;
+ Text = text;
+ EscapedText = Regex.Escape(text);
+ Source = source;
+ Size = size;
if (replacementDelegate != null)
{
ReplacementDelegate = replacementDelegate;
@@ -220,30 +234,36 @@ public MessageEmote(
///
public class MessageEmoteCollection
{
- private readonly SortedList _emoteList;
- private const string BasePattern = @"(\b{0}\b)";
+ private readonly Dictionary _emotes;
+#if NET8_0_OR_GREATER
+ private static readonly CompositeFormat BasePattern = CompositeFormat.Parse(@"(\b {0}\b)|(\b{0} \b)|(?<=\W){0}(?=$)|(?<=\s){0}(?=\s)|(^{0}$)");
+#else
+ private const string BasePattern = @"(\b {0}\b)|(\b{0} \b)|(?<=\W){0}(?=$)|(?<=\s){0}(?=\s)|(^{0}$)";
+#endif
/// Do not access directly! Backing field for
- private string _currentPattern;
- private Regex _regex;
+ private string? _currentPattern;
+ private Regex? _regex;
private readonly EmoteFilterDelegate _preferredFilter;
///
/// Property so that we can be confident
/// always reflects changes to .
///
- private string CurrentPattern
+ private string? CurrentPattern
{
get => _currentPattern;
set
{
- if (_currentPattern != null && _currentPattern.Equals(value)) return;
+ if (_currentPattern?.Equals(value) is true)
+ return;
+
_currentPattern = value;
PatternChanged = true;
}
}
- private Regex CurrentRegex
+ private Regex? CurrentRegex
{
get
{
@@ -273,7 +293,7 @@ private Regex CurrentRegex
///
public MessageEmoteCollection()
{
- _emoteList = new SortedList();
+ _emotes = new();
_preferredFilter = AllInclusiveEmoteFilter;
}
@@ -294,19 +314,23 @@ public MessageEmoteCollection(EmoteFilterDelegate preferredFilter) : this()
/// The to add to the collection.
public void Add(MessageEmote emote)
{
- if (!_emoteList.TryGetValue(emote.Text, out var _))
- {
- _emoteList.Add(emote.Text, emote);
- }
+#if NETSTANDARD2_0
+ if (_emotes.ContainsKey(emote.Text))
+ return;
+ _emotes.Add(emote.Text, emote);
+#else
+ if(!_emotes.TryAdd(emote.Text, emote))
+ return;
+#endif
if (CurrentPattern == null)
{
//string i = String.Format(_basePattern, "(" + emote.EscapedText + "){0}");
- CurrentPattern = string.Format(BasePattern, emote.EscapedText);
+ CurrentPattern = string.Format(null, BasePattern, emote.EscapedText);
}
else
{
- CurrentPattern = CurrentPattern + "|" + string.Format(BasePattern, emote.EscapedText);
+ CurrentPattern = CurrentPattern + "|" + string.Format(null, BasePattern, emote.EscapedText);
}
}
@@ -333,9 +357,8 @@ public void Merge(IEnumerable emotes)
/// The to remove.
public void Remove(MessageEmote emote)
{
- if (!_emoteList.ContainsKey(emote.Text)) return;
-
- _emoteList.Remove(emote.Text);
+ if (!_emotes.Remove(emote.Text))
+ return;
// These patterns look a lot scarier than they are because we have to look for
// a lot of regex characters, which means we do a lot of escaping!
@@ -346,7 +369,7 @@ public void Remove(MessageEmote emote)
// Matches |(\bEMOTE\b) including the preceding | so that the following | and emote (if any)
// merge seamlessly when this section is removed. Again, wrapped in a group.
var otherEmotePattern = @"(\|\(\\b" + emote.EscapedText + @"\\b\))";
- var newPattern = Regex.Replace(CurrentPattern, firstEmotePattern + "|" + otherEmotePattern, "");
+ var newPattern = Regex.Replace(CurrentPattern, firstEmotePattern + "|" + otherEmotePattern, ""); // todo: possible ArgumentNullException
CurrentPattern = newPattern.Equals("") ? null : newPattern;
}
@@ -355,7 +378,7 @@ public void Remove(MessageEmote emote)
///
public void RemoveAll()
{
- _emoteList.Clear();
+ _emotes.Clear();
CurrentPattern = null;
}
@@ -372,17 +395,48 @@ public void RemoveAll()
/// received is to be replaced.
/// Defaults to .
///
+ ///
+ /// String providing additional detection capabilities for further processing
+ ///
+ ///
+ /// String providing additional detection capabilities for further processing
+ ///
///
/// A string where all of the original emote text has been replaced with
/// its designated s
///
- public string ReplaceEmotes(string originalMessage, EmoteFilterDelegate del = null)
+ public string ReplaceEmotes(string originalMessage, EmoteFilterDelegate? del = null, string prefix = "", string suffix = "")
{
- if (CurrentRegex == null) return originalMessage;
- if (del != null && del != CurrentEmoteFilter) CurrentEmoteFilter = del;
- var newMessage = CurrentRegex.Replace(originalMessage, GetReplacementString);
+ if (CurrentRegex == null)
+ {
+ return originalMessage;
+ }
+
+ if (del != null && del != CurrentEmoteFilter)
+ {
+ CurrentEmoteFilter = del;
+ }
+
+ var newMessage = CurrentRegex.Replace(originalMessage, match =>
+ {
+ // the match includes possible white space on either side, so we need to preserve that
+ var emoteCode = match.Value.Trim();
+ if (match.Value[0] == ' ')
+ prefix += " ";
+ if (match.Value[match.Value.Length - 1] == ' ')
+ suffix = " " + suffix;
+ if (!_emotes.TryGetValue(emoteCode, out var emote))
+ {
+ return match.Value;
+ }
+
+ return CurrentEmoteFilter(emote) ? prefix + emote.ReplacementString + suffix : match.Value;
+ });
CurrentEmoteFilter = _preferredFilter;
- return newMessage;
+
+ // the hacky replacement logic above will leave 2 spaces between emote and non-emote
+ // twitch doesn't allow this anyways, so this fix should be fine
+ return newMessage.Replace(" ", " ");
}
///
@@ -415,14 +469,5 @@ public static bool TwitchOnlyEmoteFilter(MessageEmote emote)
{
return emote.Source == MessageEmote.EmoteSource.Twitch;
}
-
- private string GetReplacementString(Match m)
- {
- if (!_emoteList.ContainsKey(m.Value)) return m.Value;
-
- var emote = _emoteList[m.Value];
- return CurrentEmoteFilter(emote) ? emote.ReplacementString : m.Value;
- //If the match doesn't exist in the list ("shouldn't happen") or the filter excludes it, don't replace.
- }
}
}
diff --git a/TwitchLib.Client.Models/OutboundChatMessage.cs b/TwitchLib.Client.Models/OutboundChatMessage.cs
index 6efc8a6c..3e40ec77 100644
--- a/TwitchLib.Client.Models/OutboundChatMessage.cs
+++ b/TwitchLib.Client.Models/OutboundChatMessage.cs
@@ -6,22 +6,24 @@ public class OutboundChatMessage
public string Message { get; set; }
- public string Username { get; set; }
-
- public string ReplyToId { get; set; }
+ public string? ReplyToId { get; set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OutboundChatMessage(string channel, string message)
+ {
+ Channel = channel;
+ Message = message;
+ }
+
+ ///
public override string ToString()
{
- var user = Username.ToLower();
var channel = Channel.ToLower();
- if(ReplyToId == null)
- {
- return $":{user}!{user}@{user}.tmi.twitch.tv PRIVMSG #{channel} :{Message}";
- } else
- {
- return $"@reply-parent-msg-id={ReplyToId} PRIVMSG #{channel} :{Message}";
- }
-
+ return ReplyToId is null
+ ? $"PRIVMSG #{channel} :{Message}"
+ : $"@reply-parent-msg-id={ReplyToId} PRIVMSG #{channel} :{Message}";
}
}
}
diff --git a/TwitchLib.Client.Models/OutboundWhisperMessage.cs b/TwitchLib.Client.Models/OutboundWhisperMessage.cs
deleted file mode 100644
index 35d9d00c..00000000
--- a/TwitchLib.Client.Models/OutboundWhisperMessage.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace TwitchLib.Client.Models
-{
- public class OutboundWhisperMessage
- {
- public string Username { get; set; }
-
- public string Receiver { get; set; }
-
- public string Message { get; set; }
-
- public override string ToString()
- {
- return $":{Username}~{Username}@{Username}.tmi.twitch.tv PRIVMSG #jtv :/w {Receiver} {Message}";
- }
- }
-}
diff --git a/TwitchLib.Client.Models/OutgoingMessage.cs b/TwitchLib.Client.Models/OutgoingMessage.cs
deleted file mode 100644
index e5aa69ba..00000000
--- a/TwitchLib.Client.Models/OutgoingMessage.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace TwitchLib.Client.Models
-{
- public class OutgoingMessage
- {
- public string Channel { get; set; }
-
- public string Message { get; set; }
-
- public int Nonce { get; set; }
-
- public string Sender { get; set; }
-
- public MessageState State { get; set; }
- }
-
- public enum MessageState : byte
- {
- /// Message did not originate from this session, or was successfully sent.
- Normal = 0,
-
- /// Message is current queued.
- Queued,
-
- /// Message failed to be sent.
- Failed
- }
-}
-
diff --git a/TwitchLib.Client.Models/PrimePaidSubscriber.cs b/TwitchLib.Client.Models/PrimePaidSubscriber.cs
index 81ed1e9d..53c56fa5 100644
--- a/TwitchLib.Client.Models/PrimePaidSubscriber.cs
+++ b/TwitchLib.Client.Models/PrimePaidSubscriber.cs
@@ -1,76 +1,81 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Text;
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+public class PrimePaidSubscriber : UserNoticeBase
{
- public class PrimePaidSubscriber : SubscriberBase
+ ///
+ /// The type of subscription plan being used.
+ ///
+ public SubscriptionPlan MsgParamSubPlan { get; protected set; }
+
+ ///
+ /// Property representing system message.
+ ///
+ public string ResubMessage { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The IRC message from Twitch to be processed.
+ public PrimePaidSubscriber(IrcMessage ircMessage) : base(ircMessage)
{
- public PrimePaidSubscriber(IrcMessage ircMessage) : base(ircMessage)
- {
- }
+ ResubMessage = ircMessage.Message;
+ }
- public PrimePaidSubscriber(
- List> badges,
- List> badgeInfo,
- string colorHex,
- Color color,
- string displayName,
- string emoteSet,
- string id,
- string login,
- string systemMessage,
- string msgId,
- string msgParamCumulativeMonths,
- string msgParamStreakMonths,
- bool msgParamShouldShareStreak,
- string systemMessageParsed,
- string resubMessage,
- SubscriptionPlan subscriptionPlan,
- string subscriptionPlanName,
- string roomId,
- string userId,
- bool isModerator,
- bool isTurbo,
- bool isSubscriber,
- bool isPartner,
- string tmiSentTs,
- UserType userType,
- string rawIrc,
- string channel,
- int months = 0)
- : base(badges,
- badgeInfo,
- colorHex,
- color,
- displayName,
- emoteSet,
- id,
- login,
- systemMessage,
- msgId,
- msgParamCumulativeMonths,
- msgParamStreakMonths,
- msgParamShouldShareStreak,
- systemMessageParsed,
- resubMessage,
- subscriptionPlan,
- subscriptionPlanName,
- roomId,
- userId,
- isModerator,
- isTurbo,
- isSubscriber,
- isPartner,
- tmiSentTs,
- userType,
- rawIrc,
- channel,
- months)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PrimePaidSubscriber(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ SubscriptionPlan msgParamSubPlan,
+ string resubMessage)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamSubPlan = msgParamSubPlan;
+ ResubMessage = resubMessage;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
+ case Tags.MsgParamSubPlan:
+ MsgParamSubPlan = TagHelper.ToSubscriptionPlan(tag.Value);
+ break;
+ default:
+ return false;
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/RaidNotification.cs b/TwitchLib.Client.Models/RaidNotification.cs
index 3db1f1bf..d1a24921 100644
--- a/TwitchLib.Client.Models/RaidNotification.cs
+++ b/TwitchLib.Client.Models/RaidNotification.cs
@@ -1,180 +1,92 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+public class RaidNotification : UserNoticeBase
{
- public class RaidNotification
+ ///
+ /// The display name of the broadcaster raiding this channel.
+ ///
+ public string MsgParamDisplayName { get; protected set; } = default!;
+
+ ///
+ /// The login name of the broadcaster raiding this channel.
+ ///
+ public string MsgParamLogin { get; protected set; } = default!;
+
+ ///
+ /// The number of viewers raiding this channel from the broadcaster’s channel.
+ ///
+ public string MsgParamViewerCount { get; protected set; } = default!;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RaidNotification(IrcMessage ircMessage) : base(ircMessage)
{
- public List> Badges { get; }
-
- public List> BadgeInfo { get; }
-
- public string Color { get; }
-
- public string DisplayName { get; }
-
- public string Emotes { get; }
-
- public string Id { get; }
-
- public string Login { get; }
-
- public bool Moderator { get; }
-
- public string MsgId { get; }
-
- public string MsgParamDisplayName { get; }
-
- public string MsgParamLogin { get; }
-
- public string MsgParamViewerCount { get; }
-
- public string RoomId { get; }
-
- public bool Subscriber { get; }
-
- public string SystemMsg { get; }
-
- public string SystemMsgParsed { get; }
-
- public string TmiSentTs { get; }
-
- public bool Turbo { get; }
-
- public string UserId { get; }
-
- public UserType UserType { get; }
-
- // @badges=;color=#FF0000;display-name=Heinki;emotes=;id=4fb7ab2d-aa2c-4886-a286-46e20443f3d6;login=heinki;mod=0;msg-id=raid;msg-param-displayName=Heinki;msg-param-login=heinki;msg-param-viewerCount=4;room-id=27229958;subscriber=0;system-msg=4\sraiders\sfrom\sHeinki\shave\sjoined\n!;tmi-sent-ts=1510249711023;turbo=0;user-id=44110799;user-type= :tmi.twitch.tv USERNOTICE #pandablack
- public RaidNotification(IrcMessage ircMessage)
- {
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
+ }
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- Color = tagValue;
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- Emotes = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- Moderator = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.MsgParamDisplayname:
- MsgParamDisplayName = tagValue;
- break;
- case Tags.MsgParamLogin:
- MsgParamLogin = tagValue;
- break;
- case Tags.MsgParamViewerCount:
- MsgParamViewerCount = tagValue;
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- Subscriber = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.SystemMsg:
- SystemMsg = tagValue;
- SystemMsgParsed = tagValue.Replace("\\s", " ").Replace("\\n", "");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.Turbo:
- Turbo = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.UserId:
- UserId = tagValue;
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- }
- }
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RaidNotification(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ string msgParamDisplayName,
+ string msgParamLogin,
+ string msgParamViewerCount)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamDisplayName = msgParamDisplayName;
+ MsgParamLogin = msgParamLogin;
+ MsgParamViewerCount = msgParamViewerCount;
+ }
- public RaidNotification(
- List> badges,
- List> badgeInfo,
- string color,
- string displayName,
- string emotes,
- string id,
- string login,
- bool moderator,
- string msgId,
- string msgParamDisplayName,
- string msgParamLogin,
- string msgParamViewerCount,
- string roomId,
- bool subscriber,
- string systemMsg,
- string systemMsgParsed,
- string tmiSentTs,
- bool turbo,
- UserType userType,
- string userId)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
- Badges = badges;
- BadgeInfo = badgeInfo;
- Color = color;
- DisplayName = displayName;
- Emotes = emotes;
- Id = id;
- Login = login;
- Moderator = moderator;
- MsgId = msgId;
- MsgParamDisplayName = msgParamDisplayName;
- MsgParamLogin = msgParamLogin;
- MsgParamViewerCount = msgParamViewerCount;
- RoomId = roomId;
- Subscriber = subscriber;
- SystemMsg = systemMsg;
- SystemMsgParsed = systemMsgParsed;
- TmiSentTs = tmiSentTs;
- Turbo = turbo;
- UserType = userType;
- UserId = userId;
+ case Tags.MsgParamDisplayname:
+ MsgParamDisplayName = tag.Value;
+ break;
+ case Tags.MsgParamLogin:
+ MsgParamLogin = tag.Value;
+ break;
+ case Tags.MsgParamViewerCount:
+ MsgParamViewerCount = tag.Value;
+ break;
+ default:
+ return false;
}
+ return true;
}
}
-
diff --git a/TwitchLib.Client.Models/ReSubscriber.cs b/TwitchLib.Client.Models/ReSubscriber.cs
index e7b56053..b93ab76f 100644
--- a/TwitchLib.Client.Models/ReSubscriber.cs
+++ b/TwitchLib.Client.Models/ReSubscriber.cs
@@ -1,77 +1,117 @@
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+public class ReSubscriber : UserNoticeBase
{
- public class ReSubscriber : SubscriberBase
+ ///
+ /// The total number of months the user has subscribed.
+ ///
+ public int MsgParamCumulativeMonths { get; protected set; }
+
+ ///
+ /// A Boolean value that indicates whether the user wants their streaks shared.
+ ///
+ public bool MsgParamShouldShareStreak { get; protected set; }
+
+ ///
+ /// The number of consecutive months the user has subscribed.
+ ///
+ public int MsgParamStreakMonths { get; protected set; }
+
+ ///
+ /// The type of subscription plan being used.
+ ///
+ public SubscriptionPlan MsgParamSubPlan { get; protected set; }
+
+ ///
+ /// The display name of the subscription plan. This may be a default name or one created by the channel owner.
+ ///
+ public string MsgParamSubPlanName { get; protected set; } = default!;
+
+ public string ResubMessage { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ReSubscriber(IrcMessage ircMessage) : base(ircMessage)
{
- public int Months => monthsInternal;
+ ResubMessage = ircMessage.Message;
+ }
- public ReSubscriber(IrcMessage ircMessage) : base(ircMessage)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ReSubscriber(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ int msgParamCumulativeMonths,
+ bool msgParamShouldShareStreak,
+ int msgParamStreakMonths,
+ SubscriptionPlan msgParamSubPlan,
+ string msgParamSubPlanName,
+ string resubMessage)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamCumulativeMonths = msgParamCumulativeMonths;
+ MsgParamShouldShareStreak = msgParamShouldShareStreak;
+ MsgParamStreakMonths = msgParamStreakMonths;
+ MsgParamSubPlan = msgParamSubPlan;
+ MsgParamSubPlanName = msgParamSubPlanName;
+ ResubMessage = resubMessage;
+ }
- public ReSubscriber(
- List> badges,
- List> badgeInfo,
- string colorHex,
- Color color,
- string displayName,
- string emoteSet,
- string id,
- string login,
- string systemMessage,
- string msgId,
- string msgParamCumulativeMonths,
- string msgParamStreakMonths,
- bool msgParamShouldShareStreak,
- string systemMessageParsed,
- string resubMessage,
- SubscriptionPlan subscriptionPlan,
- string subscriptionPlanName,
- string roomId,
- string userId,
- bool isModerator,
- bool isTurbo,
- bool isSubscriber,
- bool isPartner,
- string tmiSentTs,
- UserType userType,
- string rawIrc,
- string channel,
- int months = 0)
- : base(badges,
- badgeInfo,
- colorHex,
- color,
- displayName,
- emoteSet,
- id,
- login,
- systemMessage,
- msgId,
- msgParamCumulativeMonths,
- msgParamStreakMonths,
- msgParamShouldShareStreak,
- systemMessageParsed,
- resubMessage,
- subscriptionPlan,
- subscriptionPlanName,
- roomId,
- userId,
- isModerator,
- isTurbo,
- isSubscriber,
- isPartner,
- tmiSentTs,
- userType,
- rawIrc,
- channel,
- months)
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
+ case Tags.MsgParamCumulativeMonths:
+ MsgParamCumulativeMonths = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamShouldShareStreak:
+ MsgParamShouldShareStreak = TagHelper.ToBool(tag.Value);
+ break;
+ case Tags.MsgParamStreakMonths:
+ MsgParamStreakMonths = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamSubPlan:
+ MsgParamSubPlan = TagHelper.ToSubscriptionPlan(tag.Value);
+ break;
+ case Tags.MsgParamSubPlanName:
+ MsgParamSubPlanName = tag.Value;
+ break;
+ default:
+ return false;
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/Ritual.cs b/TwitchLib.Client.Models/Ritual.cs
new file mode 100644
index 00000000..59810f01
--- /dev/null
+++ b/TwitchLib.Client.Models/Ritual.cs
@@ -0,0 +1,77 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class Ritual : UserNoticeBase
+{
+ ///
+ /// The name of the ritual being celebrated.
+ ///
+ public string MsgParamRitualName { get; protected set; } = default!;
+
+ public string Message { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Ritual(IrcMessage ircMessage) : base(ircMessage)
+ {
+ Message = ircMessage.Message;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Ritual(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ string msgParamRitualName,
+ string message)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamRitualName = msgParamRitualName;
+ Message = message;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamRitualName:
+ MsgParamRitualName = tag.Value;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/RitualNewChatter.cs b/TwitchLib.Client.Models/RitualNewChatter.cs
deleted file mode 100644
index 4fc688a6..00000000
--- a/TwitchLib.Client.Models/RitualNewChatter.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System.Collections.Generic;
-
-using TwitchLib.Client.Enums;
-using TwitchLib.Client.Models.Internal;
-
-namespace TwitchLib.Client.Models
-{
- public class RitualNewChatter
- {
- public List> Badges { get; }
-
- public List> BadgeInfo { get; }
-
- public string Color { get; }
-
- public string DisplayName { get; }
-
- public string Emotes { get; }
-
- public string Id { get; }
-
- public bool IsModerator { get; }
-
- public bool IsSubscriber { get; }
-
- public bool IsTurbo { get; }
-
- public string Login { get; }
-
- public string Message { get; }
-
- public string MsgId { get; }
-
- public string MsgParamRitualName { get; }
-
- public string RoomId { get; }
-
- public string SystemMsgParsed { get; }
-
- public string SystemMsg { get; }
-
- public string TmiSentTs { get; }
-
- public string UserId { get; }
-
- public UserType UserType { get; }
-
- // badges=subscriber/0;color=#0000FF;display-name=KittyJinxu;emotes=30259:0-6;id=1154b7c0-8923-464e-a66b-3ef55b1d4e50;
- // login=kittyjinxu;mod=0;msg-id=ritual;msg-param-ritual-name=new_chatter;room-id=35740817;subscriber=1;
- // system-msg=@KittyJinxu\sis\snew\shere.\sSay\shello!;tmi-sent-ts=1514387871555;turbo=0;user-id=187446639;
- // user-type= USERNOTICE #thorlar kittyjinxu > #thorlar: HeyGuys
- public RitualNewChatter(IrcMessage ircMessage)
- {
- Message = ircMessage.Message;
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
-
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- Color = tagValue;
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- Emotes = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.MsgParamRitualName:
- MsgParamRitualName = tagValue;
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- IsSubscriber = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.SystemMsg:
- SystemMsg = tagValue;
- SystemMsgParsed = tagValue.Replace("\\s", " ").Replace("\\n", "");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.Turbo:
- IsTurbo = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.UserId:
- UserId = tagValue;
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- }
- }
- }
- }
-}
diff --git a/TwitchLib.Client.Models/SendOptions.cs b/TwitchLib.Client.Models/SendOptions.cs
new file mode 100644
index 00000000..d31e4e1c
--- /dev/null
+++ b/TwitchLib.Client.Models/SendOptions.cs
@@ -0,0 +1,55 @@
+using System;
+using TwitchLib.Client.Models.Interfaces;
+
+namespace TwitchLib.Client.Models
+{
+ public class SendOptions : ISendOptions
+ {
+ ///
+ public uint SendsAllowedInPeriod { get; }
+
+ ///
+ public ushort SendDelay { get; }
+
+ ///
+ public TimeSpan ThrottlingPeriod { get; } = TimeSpan.FromSeconds(30);
+
+ ///
+ public uint QueueCapacity { get; }
+
+ ///
+ public TimeSpan CacheItemTimeout { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A of zero means:
+ ///
+ /// all messages that are enqueued to send
+ /// are going to be throttled!
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SendOptions(
+ uint sendsAllowedInPeriod = 20,
+ uint queueCapacity = 10_000,
+ uint cacheItemTimeoutInMinutes = 30,
+ ushort sendDelay = 50)
+ {
+ SendsAllowedInPeriod = sendsAllowedInPeriod;
+ QueueCapacity = queueCapacity;
+ CacheItemTimeout = TimeSpan.FromMinutes(cacheItemTimeoutInMinutes);
+ SendDelay = sendDelay;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/SentMessage.cs b/TwitchLib.Client.Models/SentMessage.cs
index 764246f1..7b083a82 100644
--- a/TwitchLib.Client.Models/SentMessage.cs
+++ b/TwitchLib.Client.Models/SentMessage.cs
@@ -1,9 +1,10 @@
-using System.Collections.Generic;
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Interfaces;
namespace TwitchLib.Client.Models
{
/// Model representing a sent message.
- public class SentMessage
+ public class SentMessage : IHexColorProperty
{
/// Badges the sender has
public List> Badges { get; }
@@ -12,7 +13,7 @@ public class SentMessage
public string Channel { get; }
/// Sender's name color.
- public string ColorHex { get; }
+ public string HexColor { get; }
/// Display name of the sender.
public string DisplayName { get; }
@@ -30,7 +31,7 @@ public class SentMessage
public string Message { get; }
/// The type of user (admin, broadcaster, viewer, moderator)
- public Enums.UserType UserType { get; }
+ public UserType UserType { get; }
/// Model constructor.
public SentMessage(
@@ -39,7 +40,7 @@ public SentMessage(
{
Badges = state.Badges;
Channel = state.Channel;
- ColorHex = state.ColorHex;
+ HexColor = state.HexColor;
DisplayName = state.DisplayName;
EmoteSet = state.EmoteSet;
IsModerator = state.IsModerator;
@@ -48,20 +49,23 @@ public SentMessage(
Message = message;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public SentMessage(
List> badges,
string channel,
- string colorHex,
+ string hexColor,
string displayName,
string emoteSet,
bool isModerator,
bool isSubscriber,
- Enums.UserType userType,
+ UserType userType,
string message)
{
Badges = badges;
Channel = channel;
- ColorHex = colorHex;
+ HexColor = hexColor;
DisplayName = displayName;
EmoteSet = emoteSet;
IsModerator = isModerator;
diff --git a/TwitchLib.Client.Models/StandardPayForward.cs b/TwitchLib.Client.Models/StandardPayForward.cs
new file mode 100644
index 00000000..23bbc696
--- /dev/null
+++ b/TwitchLib.Client.Models/StandardPayForward.cs
@@ -0,0 +1,111 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class StandardPayForward : UserNoticeBase
+{
+ public bool MsgParamPriorGifterAnonymous { get; protected set; }
+
+ public string MsgParamPriorGifterDisplayName { get; protected set; } = default!;
+
+ public long MsgParamPriorGifterId { get; protected set; }
+
+ public string MsgParamPriorGifterUserName { get; protected set; } = default!;
+
+ public string? MsgParamRecipientDisplayName { get; protected set; }
+
+ public long? MsgParamRecipientId { get; protected set; }
+
+ public string? MsgParamRecipientUserName { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StandardPayForward(IrcMessage ircMessage) : base(ircMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StandardPayForward(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ bool msgParamPriorGifterAnonymous,
+ string msgParamPriorGifterDisplayName,
+ long msgParamPriorGifterId,
+ string msgParamPriorGifterUserName,
+ string? msgParamRecipientDisplayName,
+ long? msgParamRecipientId,
+ string? msgParamRecipientUserName)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamPriorGifterAnonymous = msgParamPriorGifterAnonymous;
+ MsgParamPriorGifterDisplayName = msgParamPriorGifterDisplayName;
+ MsgParamPriorGifterId = msgParamPriorGifterId;
+ MsgParamPriorGifterUserName = msgParamPriorGifterUserName;
+ MsgParamRecipientDisplayName = msgParamRecipientDisplayName;
+ MsgParamRecipientId = msgParamRecipientId;
+ MsgParamRecipientUserName = msgParamRecipientUserName;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
+ {
+ case Tags.MsgParamPriorGifterAnonymous:
+ MsgParamPriorGifterAnonymous = bool.Parse(tag.Value);
+ break;
+ case Tags.MsgParamPriorGifterDisplayName:
+ MsgParamPriorGifterDisplayName = tag.Value;
+ break;
+ case Tags.MsgParamPriorGifterId:
+ MsgParamPriorGifterId = long.Parse(tag.Value);
+ break;
+ case Tags.MsgParamPriorGifterUserName:
+ MsgParamPriorGifterUserName = tag.Value;
+ break;
+ case Tags.MsgParamRecipientDisplayName:
+ MsgParamRecipientDisplayName = tag.Value;
+ break;
+ case Tags.MsgParamRecipientId:
+ MsgParamRecipientId = long.Parse(tag.Value);
+ break;
+ case Tags.MsgParamRecipientUsername:
+ MsgParamRecipientUserName = tag.Value;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/Subscriber.cs b/TwitchLib.Client.Models/Subscriber.cs
index 116a6e87..d5fd3964 100644
--- a/TwitchLib.Client.Models/Subscriber.cs
+++ b/TwitchLib.Client.Models/Subscriber.cs
@@ -1,75 +1,117 @@
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
using TwitchLib.Client.Models.Internal;
-namespace TwitchLib.Client.Models
+namespace TwitchLib.Client.Models;
+
+public class Subscriber : UserNoticeBase
{
- public class Subscriber : SubscriberBase
+ ///
+ /// The total number of months the user has subscribed.
+ ///
+ public int MsgParamCumulativeMonths { get; protected set; }
+
+ ///
+ /// A Boolean value that indicates whether the user wants their streaks shared.
+ ///
+ public bool MsgParamShouldShareStreak { get; protected set; }
+
+ ///
+ /// The number of consecutive months the user has subscribed.
+ ///
+ public int MsgParamStreakMonths { get; protected set; }
+
+ ///
+ /// The type of subscription plan being used.
+ ///
+ public SubscriptionPlan MsgParamSubPlan { get; protected set; }
+
+ ///
+ /// The display name of the subscription plan. This may be a default name or one created by the channel owner.
+ ///
+ public string MsgParamSubPlanName { get; protected set; } = default!;
+
+ public string ResubMessage { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Subscriber(IrcMessage ircMessage) : base(ircMessage)
{
- public Subscriber(IrcMessage ircMessage)
- : base(ircMessage)
- {
- }
+ ResubMessage = ircMessage.Message;
+ }
- public Subscriber(
- List> badges,
- List> badgeInfo,
- string colorHex,
- Color color,
- string displayName,
- string emoteSet,
- string id,
- string login,
- string systemMessage,
- string msgId,
- string msgParamCumulativeMonths,
- string msgParamStreakMonths,
- bool msgParamShouldShareStreak,
- string systemMessageParsed,
- string resubMessage,
- SubscriptionPlan subscriptionPlan,
- string subscriptionPlanName,
- string roomId,
- string userId,
- bool isModerator,
- bool isTurbo,
- bool isSubscriber,
- bool isPartner,
- string tmiSentTs,
- UserType userType,
- string rawIrc,
- string channel)
- : base(badges,
- badgeInfo,
- colorHex,
- color,
- displayName,
- emoteSet,
- id,
- login,
- systemMessage,
- msgId,
- msgParamCumulativeMonths,
- msgParamStreakMonths,
- msgParamShouldShareStreak,
- systemMessageParsed,
- resubMessage,
- subscriptionPlan,
- subscriptionPlanName,
- roomId,
- userId,
- isModerator,
- isTurbo,
- isSubscriber,
- isPartner,
- tmiSentTs,
- userType,
- rawIrc,
- channel,
- months: 0)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Subscriber(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags,
+ int msgParamCumulativeMonths,
+ bool msgParamShouldShareStreak,
+ int msgParamStreakMonths,
+ SubscriptionPlan msgParamSubPlan,
+ string msgParamSubPlanName,
+ string resubMessage)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ MsgParamCumulativeMonths = msgParamCumulativeMonths;
+ MsgParamShouldShareStreak = msgParamShouldShareStreak;
+ MsgParamStreakMonths = msgParamStreakMonths;
+ MsgParamSubPlan = msgParamSubPlan;
+ MsgParamSubPlanName = msgParamSubPlanName;
+ ResubMessage = resubMessage;
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ switch (tag.Key)
{
+ case Tags.MsgParamCumulativeMonths:
+ MsgParamCumulativeMonths = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamShouldShareStreak:
+ MsgParamShouldShareStreak = TagHelper.ToBool(tag.Value);
+ break;
+ case Tags.MsgParamStreakMonths:
+ MsgParamStreakMonths = int.Parse(tag.Value);
+ break;
+ case Tags.MsgParamSubPlan:
+ MsgParamSubPlan = TagHelper.ToSubscriptionPlan(tag.Value);
+ break;
+ case Tags.MsgParamSubPlanName:
+ MsgParamSubPlanName = tag.Value;
+ break;
+ default:
+ return false;
}
+ return true;
}
}
diff --git a/TwitchLib.Client.Models/SubscriberBase.cs b/TwitchLib.Client.Models/SubscriberBase.cs
deleted file mode 100644
index 03c3cdc0..00000000
--- a/TwitchLib.Client.Models/SubscriberBase.cs
+++ /dev/null
@@ -1,288 +0,0 @@
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
-using TwitchLib.Client.Models.Extensions.NetCore;
-using TwitchLib.Client.Models.Internal;
-
-namespace TwitchLib.Client.Models
-{
- /// Class representing a resubscriber.
- public class SubscriberBase
- {
- /// Property representing list of badges assigned.
- public List> Badges { get; }
-
- /// Metadata associated with each badge
- public List> BadgeInfo { get; }
-
- /// Property representing the colorhex of the resubscriber.
- public string ColorHex { get; }
-
- /// Property representing HEX color as a System.Drawing.Color object.
- public Color Color { get; }
-
- /// Property representing resubscriber's customized display name.
- public string DisplayName { get; }
-
- /// Property representing emote set of resubscriber.
- public string EmoteSet { get; }
-
- /// Property representing resub message id
- public string Id { get; }
-
- /// Property representing whether or not the resubscriber is a moderator.
- public bool IsModerator { get; }
-
- /// Property representing whether or not person is a partner.
- public bool IsPartner { get; }
-
- /// Property representing whether or not the resubscriber is a subscriber (YES).
- public bool IsSubscriber { get; }
-
- /// Property representing whether or not the resubscriber is a turbo member.
- public bool IsTurbo { get; }
-
- /// Property representing login of resubscription event.
- public string Login { get; }
-
- public string MsgId { get; }
-
- public string MsgParamCumulativeMonths { get; }
-
- public bool MsgParamShouldShareStreak { get; }
-
- public string MsgParamStreakMonths { get; }
-
- /// Property representing the raw IRC message (for debugging/customized parsing)
- public string RawIrc { get; }
-
- /// Property representing system message.
- public string ResubMessage { get; }
-
- /// Property representing the room id.
- public string RoomId { get; }
-
- /// Property representing the plan a user is on.
- public SubscriptionPlan SubscriptionPlan { get; } = SubscriptionPlan.NotSet;
-
- /// Property representing the subscription plan name.
- public string SubscriptionPlanName { get; }
-
- /// Property representing internval system message value.
- public string SystemMessage { get; }
-
- /// Property representing internal system message value, parsed.
- public string SystemMessageParsed { get; }
-
- /// Property representing the tmi-sent-ts value.
- public string TmiSentTs { get; }
-
- /// Property representing the user's id.
- public string UserId { get; }
-
- /// Property representing the user type of the resubscriber.
- public UserType UserType { get; }
-
- public string Channel { get; }
-
- // @badges=subscriber/1,turbo/1;color=#2B119C;display-name=JustFunkIt;emotes=;id=9dasn-asdibas-asdba-as8as;login=justfunkit;mod=0;msg-id=resub;msg-param-months=2;room-id=44338537;subscriber=1;system-msg=JustFunkIt\ssubscribed\sfor\s2\smonths\sin\sa\srow!;turbo=1;user-id=26526370;user-type= :tmi.twitch.tv USERNOTICE #burkeblack :AVAST YEE SCURVY DOG
-
- protected readonly int monthsInternal;
-
- /// Subscriber object constructor.
- protected SubscriberBase(IrcMessage ircMessage)
- {
- RawIrc = ircMessage.ToString();
- ResubMessage = ircMessage.Message;
-
- foreach (var tag in ircMessage.Tags.Keys)
- {
- var tagValue = ircMessage.Tags[tag];
- switch (tag)
- {
- case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
- // iterate through badges for special circumstances
- foreach (var badge in Badges)
- {
- if (badge.Key == "partner")
- IsPartner = true;
- }
- break;
- case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
- break;
- case Tags.Color:
- ColorHex = tagValue;
- if (!string.IsNullOrEmpty(ColorHex))
- Color = ColorTranslator.FromHtml(ColorHex);
- break;
- case Tags.DisplayName:
- DisplayName = tagValue;
- break;
- case Tags.Emotes:
- EmoteSet = tagValue;
- break;
- case Tags.Id:
- Id = tagValue;
- break;
- case Tags.Login:
- Login = tagValue;
- break;
- case Tags.Mod:
- IsModerator = ConvertToBool(tagValue);
- break;
- case Tags.MsgId:
- MsgId = tagValue;
- break;
- case Tags.MsgParamCumulativeMonths:
- MsgParamCumulativeMonths = tagValue;
- break;
- case Tags.MsgParamStreakMonths:
- MsgParamStreakMonths = tagValue;
- break;
- case Tags.MsgParamShouldShareStreak:
- MsgParamShouldShareStreak = Common.Helpers.ConvertToBool(tagValue);
- break;
- case Tags.MsgParamSubPlan:
- switch (tagValue.ToLower())
- {
- case "prime":
- SubscriptionPlan = SubscriptionPlan.Prime;
- break;
- case "1000":
- SubscriptionPlan = SubscriptionPlan.Tier1;
- break;
- case "2000":
- SubscriptionPlan = SubscriptionPlan.Tier2;
- break;
- case "3000":
- SubscriptionPlan = SubscriptionPlan.Tier3;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(tagValue.ToLower));
- }
- break;
- case Tags.MsgParamSubPlanName:
- SubscriptionPlanName = tagValue.Replace("\\s", " ");
- break;
- case Tags.RoomId:
- RoomId = tagValue;
- break;
- case Tags.Subscriber:
- IsSubscriber = ConvertToBool(tagValue);
- break;
- case Tags.SystemMsg:
- SystemMessage = tagValue;
- SystemMessageParsed = tagValue.Replace("\\s", " ");
- break;
- case Tags.TmiSentTs:
- TmiSentTs = tagValue;
- break;
- case Tags.Turbo:
- IsTurbo = ConvertToBool(tagValue);
- break;
- case Tags.UserId:
- UserId = tagValue;
- break;
- case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
- break;
- }
- }
- }
-
- internal SubscriberBase(
- List> badges,
- List> badgeInfo,
- string colorHex,
- Color color,
- string displayName,
- string emoteSet,
- string id,
- string login,
- string systemMessage,
- string msgId,
- string msgParamCumulativeMonths,
- string msgParamStreakMonths,
- bool msgParamShouldShareStreak,
- string systemMessageParsed,
- string resubMessage,
- SubscriptionPlan subscriptionPlan,
- string subscriptionPlanName,
- string roomId,
- string userId,
- bool isModerator,
- bool isTurbo,
- bool isSubscriber,
- bool isPartner,
- string tmiSentTs,
- UserType userType,
- string rawIrc,
- string channel,
- int months)
- {
- Badges = badges;
- BadgeInfo = badgeInfo;
- ColorHex = colorHex;
- Color = color;
- DisplayName = displayName;
- EmoteSet = emoteSet;
- Id = id;
- Login = login;
- MsgId = msgId;
- MsgParamCumulativeMonths = msgParamCumulativeMonths;
- MsgParamStreakMonths = msgParamStreakMonths;
- MsgParamShouldShareStreak = msgParamShouldShareStreak;
- SystemMessage = systemMessage;
- SystemMessageParsed = systemMessageParsed;
- ResubMessage = resubMessage;
- SubscriptionPlan = subscriptionPlan;
- SubscriptionPlanName = subscriptionPlanName;
- RoomId = roomId;
- UserId = UserId;
- IsModerator = isModerator;
- IsTurbo = isTurbo;
- IsSubscriber = isSubscriber;
- IsPartner = isPartner;
- TmiSentTs = tmiSentTs;
- UserType = userType;
- RawIrc = rawIrc;
- monthsInternal = months;
- UserId = userId;
- Channel = channel;
- }
-
- private static bool ConvertToBool(string data)
- {
- return data == "1";
- }
-
- /// Overriden ToString method, prints out all properties related to resub.
- public override string ToString()
- {
- return $"Badges: {Badges.Count}, color hex: {ColorHex}, display name: {DisplayName}, emote set: {EmoteSet}, login: {Login}, system message: {SystemMessage}, msgId: {MsgId}, msgParamCumulativeMonths: {MsgParamCumulativeMonths}" +
- $"msgParamStreakMonths: {MsgParamStreakMonths}, msgParamShouldShareStreak: {MsgParamShouldShareStreak}, resub message: {ResubMessage}, months: {monthsInternal}, room id: {RoomId}, user id: {UserId}, mod: {IsModerator}, turbo: {IsTurbo}, sub: {IsSubscriber}, user type: {UserType}, raw irc: {RawIrc}";
- }
- }
-}
diff --git a/TwitchLib.Client.Models/TwitchLib.Client.Models.csproj b/TwitchLib.Client.Models/TwitchLib.Client.Models.csproj
index 36936e20..ea2f7b87 100644
--- a/TwitchLib.Client.Models/TwitchLib.Client.Models.csproj
+++ b/TwitchLib.Client.Models/TwitchLib.Client.Models.csproj
@@ -1,30 +1,26 @@
- netstandard2.0
- TwitchLib.Client.Models
- 3.4.0
- $(VersionSuffix)
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
Project contains all of the models used in TwitchLib.Client.
- true
- swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin
- https://cdn.syzuna-programs.de/images/twitchlib.png
- https://github.com/TwitchLib/TwitchLib.Client
- https://opensource.org/licenses/MIT
- Copyright 2023
- Incremental changes. See commit history.
- https://github.com/TwitchLib/TwitchLib.Client
- Git
- twitch library irc chat c# csharp api events pubsub net standard 2.0
- en-US
- 3.4.0
- 3.4.0
- True
+
+
+
+
+
+ <_Parameter1>TwitchLib.Client
+
+
+ <_Parameter1>TwitchLib.Client.Test
+
+
+
-
+
+
diff --git a/TwitchLib.Client.Models/TwitchLibMessage.cs b/TwitchLib.Client.Models/TwitchLibMessage.cs
index e8ac392a..40ab5437 100644
--- a/TwitchLib.Client.Models/TwitchLibMessage.cs
+++ b/TwitchLib.Client.Models/TwitchLibMessage.cs
@@ -1,44 +1,43 @@
-using System.Collections.Generic;
-using System.Drawing;
-
-using TwitchLib.Client.Enums;
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Interfaces;
namespace TwitchLib.Client.Models
{
/// Class represents Message.
- public abstract class TwitchLibMessage
+ public abstract class TwitchLibMessage : IHexColorProperty
{
/// List of key-value pair badges.
- public List> Badges { get; protected set; }
+ public List> Badges { get; protected set; } = default!;
/// Twitch username of the bot that received the message.
- public string BotUsername { get; protected set; }
+ public string BotUsername { get; protected set; } = default!;
/// Property representing HEX color as a System.Drawing.Color object.
- public Color Color { get; protected set; }
-
- /// Hex representation of username color in chat (THIS CAN BE NULL IF VIEWER HASN'T SET COLOR).
- public string ColorHex { get; protected set; }
+ public string HexColor { get; protected set; } = default!;
/// Case-sensitive username of sender of chat message.
- public string DisplayName { get; protected set; }
+ public string DisplayName { get; protected set; } = default!;
/// Emote Ids that exist in message.
- public EmoteSet EmoteSet { get; protected set; }
-
- /// Twitch site-wide turbo status.
- public bool IsTurbo { get; protected set; }
+ public EmoteSet EmoteSet { get; protected set; } = default!;
/// Twitch-unique integer assigned on per account basis.
- public string UserId { get; protected set; }
+ public string UserId { get; protected set; } = default!;
/// Username of sender of chat message.
- public string Username { get; protected set; }
+ public string Username { get; protected set; } = default!;
+
+ public UserDetail UserDetail { get; protected set; }
/// User type can be viewer, moderator, global mod, admin, or staff
public UserType UserType { get; protected set; }
/// Raw IRC-style text received from Twitch.
- public string RawIrcMessage { get; protected set; }
+ public string RawIrcMessage { get; protected set; } = default!;
+
+ ///
+ /// Contains undocumented tags.
+ ///
+ public Dictionary? UndocumentedTags { get; protected set; }
}
}
diff --git a/TwitchLib.Client.Models/UnraidNotification.cs b/TwitchLib.Client.Models/UnraidNotification.cs
new file mode 100644
index 00000000..dc8864b2
--- /dev/null
+++ b/TwitchLib.Client.Models/UnraidNotification.cs
@@ -0,0 +1,57 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public class UnraidNotification : UserNoticeBase
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UnraidNotification(IrcMessage ircMessage) : base(ircMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UnraidNotification(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags)
+ : base(badgeInfo,
+ badges,
+ hexColor,
+ displayName,
+ emotes,
+ id,
+ login,
+ msgId,
+ roomId,
+ systemMsg,
+ tmiSent,
+ userDetail,
+ userId,
+ userType,
+ undocumentedTags)
+ {
+ }
+
+ ///
+ protected override bool TrySet(KeyValuePair tag)
+ {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/TwitchLib.Client.Models/UserBan.cs b/TwitchLib.Client.Models/UserBan.cs
index 81fc9ba4..1278d32e 100644
--- a/TwitchLib.Client.Models/UserBan.cs
+++ b/TwitchLib.Client.Models/UserBan.cs
@@ -4,32 +4,26 @@ namespace TwitchLib.Client.Models
{
public class UserBan
{
- /// Reason for ban, if it was provided.
- public string BanReason;
-
/// Channel that had ban event.
- public string Channel;
+ public string Channel { get; }
/// User that was banned.
- public string Username;
+ public string Username { get; }
/// Channel that had ban event. Id.
- public string RoomId;
+ public string RoomId { get; } = default!;
/// User that was banned. Id.
- public string TargetUserId;
+ public string TargetUserId { get; } = default!;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UserBan(IrcMessage ircMessage)
{
Channel = ircMessage.Channel;
Username = ircMessage.Message;
- var successBanReason = ircMessage.Tags.TryGetValue(Tags.BanReason, out var banReason);
- if (successBanReason)
- {
- BanReason = banReason;
- }
-
if (ircMessage.Tags.TryGetValue(Tags.RoomId, out var roomId))
{
RoomId = roomId;
@@ -41,16 +35,17 @@ public UserBan(IrcMessage ircMessage)
}
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UserBan(
string channel,
string username,
- string banReason,
string roomId,
string targetUserId)
{
Channel = channel;
Username = username;
- BanReason = banReason;
RoomId = roomId;
TargetUserId = targetUserId;
}
diff --git a/TwitchLib.Client.Models/UserDetails.cs b/TwitchLib.Client.Models/UserDetails.cs
new file mode 100644
index 00000000..ed8dc2dd
--- /dev/null
+++ b/TwitchLib.Client.Models/UserDetails.cs
@@ -0,0 +1,62 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Extractors;
+
+namespace TwitchLib.Client.Models;
+
+public readonly struct UserDetail
+{
+ readonly UserDetails _flags;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UserDetail(UserDetails userDetails)
+ {
+ _flags = userDetails;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal UserDetail(UserDetails userDetails, List> badges)
+ {
+ var badgesUserDetails = UserDetailsExtractor.Extract(badges);
+ _flags = userDetails | badgesUserDetails;
+ }
+
+ ///
+ /// A Boolean value that determines whether the user is a moderator.
+ ///
+ public bool IsModerator => _flags.HasFlag(UserDetails.Moderator);
+
+ ///
+ /// A Boolean value that determines whether the user is a subscriber.
+ ///
+ public bool IsSubscriber => _flags.HasFlag(UserDetails.Subscriber);
+
+ ///
+ /// A Boolean value that indicates whether the user has site-wide commercial free mode enabled.
+ ///
+ public bool HasTurbo => _flags.HasFlag(UserDetails.Turbo);
+
+ ///
+ /// Message is from channel VIP.
+ ///
+ public bool IsVip => _flags.HasFlag(UserDetails.Vip);
+
+ ///
+ /// Message is from a Twitch Partner.
+ ///
+ public bool IsPartner => _flags.HasFlag(UserDetails.Partner);
+
+ ///
+ /// Message is from a Twitch Staff member.
+ ///
+ public bool IsStaff => _flags.HasFlag(UserDetails.Staff);
+
+ ///
+ public override string ToString()
+ {
+ return _flags.ToString();
+ }
+}
diff --git a/TwitchLib.Client.Models/UserNoticeBase.cs b/TwitchLib.Client.Models/UserNoticeBase.cs
new file mode 100644
index 00000000..1a5eaf30
--- /dev/null
+++ b/TwitchLib.Client.Models/UserNoticeBase.cs
@@ -0,0 +1,186 @@
+using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Interfaces;
+using TwitchLib.Client.Models.Internal;
+
+namespace TwitchLib.Client.Models;
+
+public abstract class UserNoticeBase : IHexColorProperty
+{
+ internal const string AnonymousGifterUserId = "274598607";
+
+ ///
+ /// Contains metadata related to the chat badges in the tag.
+ ///
+ public List> BadgeInfo { get; protected set; } = default!;
+
+ ///
+ /// List of chat badges.
+ ///
+ public List> Badges { get; protected set; } = default!;
+
+ ///
+ public string HexColor { get; protected set; } = default!;
+
+ ///
+ /// The user’s display name, escaped as described in the IRCv3 spec.
+ ///
+ public string DisplayName { get; protected set; } = default!;
+
+ ///
+ /// List of emotes and their positions in the message.
+ ///
+ public string Emotes { get; protected set; } = default!;
+
+ ///
+ /// An ID that uniquely identifies this message.
+ ///
+ public string Id { get; protected set; } = default!;
+
+ ///
+ /// The login name of the user whose action generated the message.
+ ///
+ public string Login { get; protected set; } = default!;
+
+ ///
+ /// The type of notice (not the ID).
+ ///
+ public string MsgId { get; protected set; } = default!;
+
+ ///
+ /// An ID that identifies the chat room (channel).
+ ///
+ public string RoomId { get; protected set; } = default!;
+
+ ///
+ /// The message Twitch shows in the chat room for this notice.
+ ///
+ public string SystemMsg { get; protected set; } = default!;
+
+ ///
+ /// The time for when the Twitch IRC server received the message.
+ ///
+ public DateTimeOffset TmiSent { get; protected set; }
+
+ public UserDetail UserDetail { get; protected set; }
+
+ ///
+ /// The user’s ID.
+ ///
+ public string UserId { get; protected set; } = default!;
+
+ ///
+ /// he type of user sending the whisper message.
+ ///
+ public UserType UserType { get; protected set; }
+
+ public Dictionary? UndocumentedTags { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected UserNoticeBase(IrcMessage ircMessage)
+ {
+ var userDetails = UserDetails.None;
+ foreach (var tag in ircMessage.Tags)
+ {
+ switch (tag.Key)
+ {
+ case Tags.BadgeInfo:
+ BadgeInfo = TagHelper.ToBadges(tag.Value);
+ break;
+ case Tags.Badges:
+ Badges = TagHelper.ToBadges(tag.Value);
+ break;
+ case Tags.Color:
+ HexColor = tag.Value;
+ break;
+ case Tags.DisplayName:
+ DisplayName = tag.Value;
+ break;
+ case Tags.Emotes:
+ Emotes = tag.Value;
+ break;
+ case Tags.Id:
+ Id = tag.Value;
+ break;
+ case Tags.Login:
+ Login = tag.Value;
+ break;
+ case Tags.Mod:
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Moderator;
+ break;
+ case Tags.MsgId:
+ MsgId = tag.Value;
+ break;
+ case Tags.RoomId:
+ RoomId = tag.Value;
+ break;
+ case Tags.Subscriber:
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Subscriber;
+ break;
+ case Tags.SystemMsg:
+ SystemMsg = tag.Value.Replace("\\s", " ");
+ break;
+ case Tags.TmiSentTs:
+ TmiSent = TagHelper.ToDateTimeOffsetFromUnixMs(tag.Value);
+ break;
+ case Tags.Turbo:
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Turbo;
+ break;
+ case Tags.UserId:
+ UserId = tag.Value;
+ break;
+ case Tags.UserType:
+ UserType = TagHelper.ToUserType(tag.Value);
+ break;
+ default:
+ if (!TrySet(tag))
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
+ break;
+ }
+ }
+ UserDetail = new UserDetail(userDetails, Badges);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected UserNoticeBase(
+ List> badgeInfo,
+ List> badges,
+ string hexColor,
+ string displayName,
+ string emotes,
+ string id,
+ string login,
+ string msgId,
+ string roomId,
+ string systemMsg,
+ DateTimeOffset tmiSent,
+ UserDetail userDetail,
+ string userId,
+ UserType userType,
+ Dictionary? undocumentedTags)
+ {
+ BadgeInfo = badgeInfo;
+ Badges = badges;
+ HexColor = hexColor;
+ DisplayName = displayName;
+ Emotes = emotes;
+ Id = id;
+ Login = login;
+ MsgId = msgId;
+ RoomId = roomId;
+ SystemMsg = systemMsg;
+ TmiSent = tmiSent;
+ UserDetail = userDetail;
+ UserId = userId;
+ UserType = userType;
+ UndocumentedTags = undocumentedTags;
+ }
+
+ protected abstract bool TrySet(KeyValuePair tag);
+}
diff --git a/TwitchLib.Client.Models/UserState.cs b/TwitchLib.Client.Models/UserState.cs
index 7d404ca7..de3b5bca 100644
--- a/TwitchLib.Client.Models/UserState.cs
+++ b/TwitchLib.Client.Models/UserState.cs
@@ -1,13 +1,11 @@
-using System;
-using System.Collections.Generic;
-
using TwitchLib.Client.Enums;
+using TwitchLib.Client.Models.Interfaces;
using TwitchLib.Client.Models.Internal;
namespace TwitchLib.Client.Models
{
/// Class representing state of a specific user.
- public class UserState
+ public class UserState : IHexColorProperty
{
/// Properrty representing the chat badges a specific user has.
public List> Badges { get; } = new List>();
@@ -19,16 +17,16 @@ public class UserState
public string Channel { get; }
/// Properrty representing HEX user's name.
- public string ColorHex { get; }
+ public string HexColor { get; } = default!;
/// Property representing user's display name.
- public string DisplayName { get; }
+ public string DisplayName { get; } = default!;
/// Property representing emote sets available to user.
- public string EmoteSet { get; }
-
+ public string EmoteSet { get; } = default!;
+
/// Property representing the user's Id.
- public string Id { get; }
+ public string Id { get; } = default!;
/// Property representing Turbo status.
public bool IsModerator { get; }
@@ -36,9 +34,19 @@ public class UserState
/// Property representing subscriber status.
public bool IsSubscriber { get; }
+ ///
+ /// A Boolean value that indicates whether the user has site-wide commercial free mode enabled.
+ ///
+ public bool Turbo { get; }
+
/// Property representing returned user type of user.
public UserType UserType { get; }
+ ///
+ /// Contains undocumented tags.
+ ///
+ public Dictionary? UndocumentedTags { get; }
+
///
/// Constructor for UserState.
///
@@ -47,19 +55,19 @@ public UserState(IrcMessage ircMessage)
{
Channel = ircMessage.Channel;
- foreach (var tag in ircMessage.Tags.Keys)
+ foreach (var tag in ircMessage.Tags)
{
- var tagValue = ircMessage.Tags[tag];
- switch (tag)
+ var tagValue = tag.Value;
+ switch (tag.Key)
{
case Tags.Badges:
- Badges = Common.Helpers.ParseBadges(tagValue);
+ Badges = TagHelper.ToBadges(tagValue);
break;
case Tags.BadgeInfo:
- BadgeInfo = Common.Helpers.ParseBadges(tagValue);
+ BadgeInfo = TagHelper.ToBadges(tagValue);
break;
case Tags.Color:
- ColorHex = tagValue;
+ HexColor = tagValue;
break;
case Tags.DisplayName:
DisplayName = tagValue;
@@ -71,34 +79,19 @@ public UserState(IrcMessage ircMessage)
Id = tagValue;
break;
case Tags.Mod:
- IsModerator = Common.Helpers.ConvertToBool(tagValue);
+ IsModerator = TagHelper.ToBool(tagValue);
break;
case Tags.Subscriber:
- IsSubscriber = Common.Helpers.ConvertToBool(tagValue);
+ IsSubscriber = TagHelper.ToBool(tagValue);
+ break;
+ case Tags.Turbo:
+ Turbo = TagHelper.ToBool(tagValue);
break;
case Tags.UserType:
- switch (tagValue)
- {
- case "mod":
- UserType = UserType.Moderator;
- break;
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
+ UserType = TagHelper.ToUserType(tag.Value);
break;
default:
- // This should never happen, unless Twitch changes their shit
- Console.WriteLine($"Unaccounted for [UserState]: {tag}");
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
break;
}
}
@@ -107,10 +100,13 @@ public UserState(IrcMessage ircMessage)
UserType = UserType.Broadcaster;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UserState(
List> badges,
List> badgeInfo,
- string colorHex,
+ string hexColor,
string displayName,
string emoteSet,
string channel,
@@ -121,7 +117,7 @@ public UserState(
{
Badges = badges;
BadgeInfo = badgeInfo;
- ColorHex = colorHex;
+ HexColor = hexColor;
DisplayName = displayName;
EmoteSet = emoteSet;
Channel = channel;
diff --git a/TwitchLib.Client.Models/UserTimeout.cs b/TwitchLib.Client.Models/UserTimeout.cs
index f58de55c..1ceded1f 100644
--- a/TwitchLib.Client.Models/UserTimeout.cs
+++ b/TwitchLib.Client.Models/UserTimeout.cs
@@ -5,56 +5,62 @@ namespace TwitchLib.Client.Models
public class UserTimeout
{
/// Channel that had timeout event.
- public string Channel;
+ public string Channel { get; }
- /// Duration of timeout IN SECONDS.
- public int TimeoutDuration;
-
- /// Reason for timeout, if it was provided.
- public string TimeoutReason;
+ /// Duration of timeout
+ public TimeSpan TimeoutDuration { get; }
/// Viewer that was timed out.
- public string Username;
+ public string Username { get; }
/// Id of Viewer that was timed out.
- public string TargetUserId;
+ public string TargetUserId { get; } = default!;
+
+ ///
+ /// Contains undocumented tags.
+ ///
+ public Dictionary? UndocumentedTags { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UserTimeout(IrcMessage ircMessage)
{
Channel = ircMessage.Channel;
Username = ircMessage.Message;
- foreach (var tag in ircMessage.Tags.Keys)
+ foreach (var tag in ircMessage.Tags)
{
- var tagValue = ircMessage.Tags[tag];
+ var tagValue = tag.Value;
- switch (tag)
+ switch (tag.Key)
{
case Tags.BanDuration:
- TimeoutDuration = int.Parse(tagValue);
- break;
- case Tags.BanReason:
- TimeoutReason = tagValue;
+ TimeoutDuration = TimeSpan.FromSeconds(int.Parse(tagValue));
break;
case Tags.TargetUserId:
TargetUserId = tagValue;
break;
+ default:
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
+ break;
}
}
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UserTimeout(
string channel,
string username,
- string targetuserId,
- int timeoutDuration,
- string timeoutReason)
+ string targetUserId,
+ TimeSpan timeoutDuration)
{
Channel = channel;
Username = username;
- TargetUserId = targetuserId;
+ TargetUserId = targetUserId;
TimeoutDuration = timeoutDuration;
- TimeoutReason = timeoutReason;
}
}
}
diff --git a/TwitchLib.Client.Models/WhisperCommand.cs b/TwitchLib.Client.Models/WhisperCommand.cs
deleted file mode 100644
index be5e434d..00000000
--- a/TwitchLib.Client.Models/WhisperCommand.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace TwitchLib.Client.Models
-{
- /// Object representing a command received via Twitch chat.
- public class WhisperCommand
- {
- /// Property representing all arguments received in a List form.
- public List ArgumentsAsList { get; }
-
- /// Property representing all arguments received in a string form.
- public string ArgumentsAsString { get; }
-
- /// Property representing the command identifier (ie command prefix).
- public char CommandIdentifier { get; }
-
- /// Property representing the actual command (without the command prefix).
- public string CommandText { get; }
-
- /// Property representing the chat message that the command came in.
- public WhisperMessage WhisperMessage { get; }
-
- /// ChatCommand constructor.
- ///
- public WhisperCommand(WhisperMessage whisperMessage)
- {
- WhisperMessage = whisperMessage;
- CommandText = whisperMessage.Message.Split(' ')?[0].Substring(1, whisperMessage.Message.Split(' ')[0].Length - 1) ?? whisperMessage.Message.Substring(1, whisperMessage.Message.Length - 1);
- ArgumentsAsString = whisperMessage.Message.Replace(whisperMessage.Message.Split(' ')?[0] + " ", "");
- ArgumentsAsList = whisperMessage.Message.Split(' ')?.Where(arg => arg != whisperMessage.Message[0] + CommandText).ToList() ?? new List();
- CommandIdentifier = whisperMessage.Message[0];
- }
-
- public WhisperCommand(
- WhisperMessage whisperMessage,
- string commandText,
- string argumentsAsString,
- List argumentsAsList,
- char commandIdentifier)
- {
- WhisperMessage = whisperMessage;
- CommandText = commandText;
- ArgumentsAsString = argumentsAsString;
- ArgumentsAsList = argumentsAsList;
- CommandIdentifier = commandIdentifier;
- }
- }
-}
diff --git a/TwitchLib.Client.Models/WhisperMessage.cs b/TwitchLib.Client.Models/WhisperMessage.cs
index 9c56963a..59b222ea 100644
--- a/TwitchLib.Client.Models/WhisperMessage.cs
+++ b/TwitchLib.Client.Models/WhisperMessage.cs
@@ -1,9 +1,4 @@
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-
using TwitchLib.Client.Enums;
-using TwitchLib.Client.Models.Extensions.NetCore;
using TwitchLib.Client.Models.Internal;
namespace TwitchLib.Client.Models
@@ -12,39 +7,40 @@ namespace TwitchLib.Client.Models
public class WhisperMessage : TwitchLibMessage
{
/// Property representing message identifier.
- public string MessageId { get; }
+ public string MessageId { get; } = default!;
/// Property representing identifier of the message thread.
- public string ThreadId { get; }
+ public string ThreadId { get; } = default!;
/// Property representing identifier of the message thread.
public string Message { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
public WhisperMessage(
List> badges,
- string colorHex,
- Color color,
+ string hexColor,
string username,
string displayName,
EmoteSet emoteSet,
string threadId,
string messageId,
string userId,
- bool isTurbo,
string botUsername,
string message,
+ UserDetail userDetail,
UserType userType)
{
Badges = badges;
- ColorHex = colorHex;
- Color = color;
+ HexColor = hexColor;
Username = username;
DisplayName = displayName;
EmoteSet = emoteSet;
ThreadId = threadId;
MessageId = messageId;
UserId = userId;
- IsTurbo = isTurbo;
+ UserDetail = userDetail;
BotUsername = botUsername;
Message = message;
UserType = userType;
@@ -62,26 +58,17 @@ public WhisperMessage(IrcMessage ircMessage, string botUsername)
RawIrcMessage = ircMessage.ToString();
Message = ircMessage.Message;
- foreach (var tag in ircMessage.Tags.Keys)
+ var userDetails = UserDetails.None;
+ foreach (var tag in ircMessage.Tags)
{
- var tagValue = ircMessage.Tags[tag];
- switch (tag)
+ var tagValue = tag.Value;
+ switch (tag.Key)
{
case Tags.Badges:
- Badges = new List>();
- if (tagValue.Contains('/'))
- {
- if (!tagValue.Contains(","))
- Badges.Add(new KeyValuePair(tagValue.Split('/')[0], tagValue.Split('/')[1]));
- else
- foreach (var badge in tagValue.Split(','))
- Badges.Add(new KeyValuePair(badge.Split('/')[0], badge.Split('/')[1]));
- }
+ Badges = TagHelper.ToBadges(tagValue);
break;
case Tags.Color:
- ColorHex = tagValue;
- if (!string.IsNullOrEmpty(ColorHex))
- Color = ColorTranslator.FromHtml(ColorHex);
+ HexColor = tagValue;
break;
case Tags.DisplayName:
DisplayName = tagValue;
@@ -96,33 +83,23 @@ public WhisperMessage(IrcMessage ircMessage, string botUsername)
ThreadId = tagValue;
break;
case Tags.Turbo:
- IsTurbo = Common.Helpers.ConvertToBool(tagValue);
+ if (TagHelper.ToBool(tag.Value))
+ userDetails |= UserDetails.Turbo;
break;
case Tags.UserId:
UserId = tagValue;
break;
case Tags.UserType:
- switch (tagValue)
- {
- case "global_mod":
- UserType = UserType.GlobalModerator;
- break;
- case "admin":
- UserType = UserType.Admin;
- break;
- case "staff":
- UserType = UserType.Staff;
- break;
- default:
- UserType = UserType.Viewer;
- break;
- }
+ UserType = TagHelper.ToUserType(tag.Value);
+ break;
+ default:
+ (UndocumentedTags ??= new()).Add(tag.Key, tag.Value);
break;
}
}
+ UserDetail = new(userDetails, Badges);
- if (EmoteSet == null)
- EmoteSet = new EmoteSet(default(string), Message);
+ EmoteSet ??= new EmoteSet(default(string), Message);
}
}
}
diff --git a/TwitchLib.Client.Test/CommandInfoTest.cs b/TwitchLib.Client.Test/CommandInfoTest.cs
new file mode 100644
index 00000000..d7571f62
--- /dev/null
+++ b/TwitchLib.Client.Test/CommandInfoTest.cs
@@ -0,0 +1,35 @@
+using TwitchLib.Client.Models;
+using Xunit;
+
+namespace TwitchLib.Client.Test;
+
+public class CommandInfoTest
+{
+ [Theory]
+ [InlineData("", "")]
+ [InlineData("!", "!")]
+ [InlineData("!", "! command")]
+ [InlineData("?", "!command")]
+ public void ParsingFailAndReturnNull(string commandIdentifier, string message)
+ {
+ Assert.False(CommandInfo.TryParse(commandIdentifier, message, out var commandInfo));
+ Assert.Null(commandInfo);
+ }
+
+ [Theory]
+ [InlineData("!", "!command", 0)]
+ [InlineData("!", "!command arg1", 1)]
+ [InlineData("!", "!command arg1 arg2", 2)]
+ [InlineData("!", "!command arg1 arg2 arg3 arg4", 4)]
+ [InlineData("!", "!command \"arg1 with space\"", 1)]
+ [InlineData("!", "!command \"arg1 with space\" \"arg2 with space\"", 2)]
+ [InlineData("cmd!", "cmd!command", 0)]
+ [InlineData("cmd!", "cmd!command arg1", 1)]
+ [InlineData("cmd!", "cmd!command \"arg1 with space\" \"arg2 with space\"", 2)]
+ public void Parsing(string commandIdentifier, string message, int argCount)
+ {
+ Assert.True(CommandInfo.TryParse(commandIdentifier, message, out var commandInfo));
+ Assert.NotNull(commandInfo);
+ Assert.Equal(argCount, commandInfo.ArgumentsAsList.Count);
+ }
+}
diff --git a/TwitchLib.Client.Test/MockIClient.cs b/TwitchLib.Client.Test/MockIClient.cs
index 1af8efd5..79338ce8 100644
--- a/TwitchLib.Client.Test/MockIClient.cs
+++ b/TwitchLib.Client.Test/MockIClient.cs
@@ -1,65 +1,54 @@
using System;
+using System.Threading.Tasks;
using TwitchLib.Communication.Events;
using TwitchLib.Communication.Interfaces;
+using TwitchLib.Communication.Models;
namespace TwitchLib.Client.Test
{
public class MockIClient : IClient
{
- public void WhisperThrottled(OnWhisperThrottledEventArgs eventArgs)
- {
- throw new NotImplementedException();
- }
-
public TimeSpan DefaultKeepAliveInterval { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int SendQueueLength => throw new NotImplementedException();
public bool IsConnected { get; private set; }
- public IClientOptions Options { get; set; }
+ public IClientOptions Options { get; set; } = new ClientOptions();
public int WhisperQueueLength => throw new NotImplementedException();
- public event EventHandler OnConnected;
- public event EventHandler OnData;
- public event EventHandler OnDisconnected;
- public event EventHandler OnError;
- public event EventHandler OnFatality;
- public event EventHandler OnMessage;
- public event EventHandler OnSendFailed;
- public event EventHandler OnStateChanged;
- public event EventHandler OnReconnected;
- public event EventHandler OnMessageThrottled;
- public event EventHandler OnWhisperThrottled;
+ public event AsyncEventHandler? OnConnected;
+ public event AsyncEventHandler? OnDisconnected;
+ public event AsyncEventHandler? OnError;
+ public event AsyncEventHandler? OnFatality;
+ public event AsyncEventHandler? OnMessage;
+ public event AsyncEventHandler? OnSendFailed;
+ public event AsyncEventHandler? OnReconnected;
- public void Close(bool callDisconnect = true)
+ public Task CloseAsync()
{
IsConnected = false;
OnDisconnected?.Invoke(this, new OnDisconnectedEventArgs());
+ return Task.CompletedTask;
}
public void Dispose()
{ }
- public void Dispose(bool waitForSendsToComplete)
- { }
-
- public bool Open()
+ public async Task OpenAsync()
{
IsConnected = true;
- OnConnected?.Invoke(this, new OnConnectedEventArgs());
+ if (OnConnected is not null)
+ await OnConnected.Invoke(this, new OnConnectedEventArgs());
return true;
}
- public void Reconnect()
+ public async Task ReconnectAsync()
{
IsConnected = true;
- OnReconnected?.Invoke(this, new OnReconnectedEventArgs());
- }
-
- public void MessageThrottled(OnMessageThrottledEventArgs eventArgs)
- {
- throw new NotImplementedException();
+ if (OnReconnected is not null)
+ await OnReconnected.Invoke(this, new OnConnectedEventArgs());
+ return true;
}
public void SendFailed(OnSendFailedEventArgs eventArgs)
@@ -72,14 +61,15 @@ public void Error(OnErrorEventArgs eventArgs)
throw new NotImplementedException();
}
- public bool Send(string data)
+ public Task SendAsync(string data)
{
- return true;
+ return Task.FromResult(true);
}
- public void ReceiveMessage(string message)
+ public async Task ReceiveMessage(string message)
{
- OnMessage?.Invoke(this, new OnMessageEventArgs { Message = message });
+ if (OnMessage is not null)
+ await OnMessage.Invoke(this, new OnMessageEventArgs(message));
}
public bool SendWhisper(string data)
diff --git a/TwitchLib.Client.Test/MyAssert.cs b/TwitchLib.Client.Test/MyAssert.cs
new file mode 100644
index 00000000..2c48b6b9
--- /dev/null
+++ b/TwitchLib.Client.Test/MyAssert.cs
@@ -0,0 +1,99 @@
+using System;
+using Xunit;
+using TwitchLib.Communication.Events;
+using System.Threading.Tasks;
+using Xunit.Sdk;
+
+namespace TwitchLib.Client.Test;
+
+//TL;DR: XUNIT with modification to accept async event Handler
+public partial class MyAssert
+{
+ ///
+ /// Verifies that a event with the exact event args (and not a derived type) is raised.
+ ///
+ /// The type of the event arguments to expect
+ /// Code to attach the event handler
+ /// Code to detach the event handler
+ /// A delegate to the code to be tested
+ /// The event sender and arguments wrapped in an object
+ /// Thrown when the expected event was not raised.
+ public static async Task> RaisesAsync(Action> attach, Action> detach, Func