Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@

- UwuMarket
- GamerSupps
- Orchid Eight
- KatDragonz

</details>
<details> <summary><strong>KoFi</strong></summary>

Expand Down
140 changes: 140 additions & 0 deletions SubathonManager.Core/Enums/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System.Reflection;
using System.Collections.Concurrent;
// ReSharper disable NullableWarningSuppressionIsUsed

namespace SubathonManager.Core.Enums;

public static class EnumExtensions
{
public static string GetDescription(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
if (field == null) return value.ToString();

var attr = field.GetCustomAttribute<EnumMetaAttribute>();
return attr?.Description ?? value.ToString();
}

public static string GetLabel(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
if (field == null) return value.ToString();

var attr = field.GetCustomAttribute<EnumMetaAttribute>();
return attr?.Label ?? value.ToString();
}

public static bool IsDisabled(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
if (field == null) return true;
var attr = field.GetCustomAttribute<EnumMetaAttribute>();
return !attr?.Enabled ?? true;
}

public static bool IsEnabled(this Enum value)
{
return !IsDisabled(value);
}

public static int GetOrderNumber(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
if (field == null) return 99999;
var attr = field.GetCustomAttribute<EnumMetaAttribute>();
return attr?.Order ?? 99999;
}

}

[AttributeUsage(AttributeTargets.Field)]
public class EnumMetaAttribute : Attribute
{
public virtual string? Description { get; set; } = "";
public virtual string? Label { get; init; }
public int Order { get; init; }

public bool Enabled { get; init; } = true;
}

public class EventSourceMetaAttribute : EnumMetaAttribute
{
public override string? Label => SourceGroup is (SubathonSourceGroup.UseSource or SubathonSourceGroup.Unknown) ? ToString() : SourceGroup.GetLabel();
public SubathonSourceGroup SourceGroup { get; init; } = SubathonSourceGroup.UseSource;

public int SourceOrder { get; init; } = 99999;
}

public class EventTypeMetaAttribute : EnumMetaAttribute
{
public override string? Description => Source is not (SubathonEventSource.Command or SubathonEventSource.Unknown) ? $"{Source.ToString()} {Label}".Trim( ): Label;
public bool IsCurrencyDonation { get; init; }
public bool IsGift { get; init; }
public bool IsMembership { get; init; }
public bool IsSubscription { get; init; }
public bool IsSubscriptionLike => IsSubscription || IsGift || IsMembership;
public bool IsExternal { get; init; }

public bool IsExtension { get; init; }
public bool IsToken { get; init; }
public bool IsRaid { get; init; }
public bool IsTrain { get; init; }
public bool IsFollow { get; init; }
public bool IsOrder { get; init; }
public bool IsCommand { get; init; }
public bool IsOther { get; init; }
public bool HasValueConfig { get; init; } = true;

public SubathonEventSource Source { get; set; } = SubathonEventSource.Unknown;
}

public class GoAffProTypeMetaAttribute : EventTypeMetaAttribute
{
public override string? Description => Label;
public GoAffProSource StoreSource { get; init; } = GoAffProSource.Unknown;
}


public class GoAffProSourceMetaAttribute : EnumMetaAttribute
{
public SubathonEventType OrderEvent { get; init; } = SubathonEventType.Unknown;

public int SiteId { get; init; } = -1;
}

public class CommandMetaAttribute : EnumMetaAttribute
{
public bool RequiresParameter { get; init; }
public bool IsControlType { get; init; }
}


public static class EnumMetaCache
{
private static readonly ConcurrentDictionary<Type, Dictionary<object, EnumMetaAttribute?>> Cache = new();

public static T? Get<T>(Enum value) where T : EnumMetaAttribute
{
var type = value.GetType();

var map = Cache.GetOrAdd(type, t =>
{
var dict = new Dictionary<object, EnumMetaAttribute?>();

foreach (var field in t.GetFields(BindingFlags.Public | BindingFlags.Static))
{
var enumValue = field.GetValue(null)!;
var attr2 = field.GetCustomAttribute<EnumMetaAttribute>();

dict[enumValue] = attr2;
}

return dict;
});

if (!map.TryGetValue(value, out var attr))
return null;

return attr as T;
}
}
45 changes: 37 additions & 8 deletions SubathonManager.Core/Enums/GoAffProSource.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
using System.Diagnostics.CodeAnalysis;
// ReSharper disable NullableWarningSuppressionIsUsed

namespace SubathonManager.Core.Enums;

public enum GoAffProSource
{
[GoAffProSourceMeta(Description="Unknown", Enabled=false)]
Unknown,
[GoAffProSourceMeta(Description="GamerSupps", SiteId=165328, OrderEvent = SubathonEventType.GamerSuppsOrder)]
GamerSupps,
UwUMarket
[GoAffProSourceMeta(Description="UwUMarket", SiteId=132230, OrderEvent = SubathonEventType.UwUMarketOrder)]
UwUMarket,
[GoAffProSourceMeta(SiteId=7142837, OrderEvent = SubathonEventType.OrchidEightOrder, Description = "Orchid Eight", Label = "Orchid Eight")]
OrchidEight,
[GoAffProSourceMeta(Description="KatDragonz", SiteId=7160049, OrderEvent = SubathonEventType.KatDragonzOrder, Enabled=true)]
KatDragonz
}

public enum GoAffProModes
Expand All @@ -19,13 +27,34 @@ public enum GoAffProModes
[ExcludeFromCodeCoverage]
public static class GoAffProSourceeHelper
{
public static SubathonEventType GetOrderEvent(this GoAffProSource source)
private static GoAffProSourceMetaAttribute? Meta(this GoAffProSource? value)
{
return source switch
{
GoAffProSource.GamerSupps => SubathonEventType.GamerSuppsOrder,
GoAffProSource.UwUMarket => SubathonEventType.UwUMarketOrder,
_ => SubathonEventType.Unknown
};
if (!value.HasValue) return null;
var meta = EnumMetaCache.Get<GoAffProSourceMetaAttribute>(value);
return meta;
}

private static readonly Lazy<Dictionary<int, GoAffProSource>> SiteIdToSource =
new(() =>
Enum.GetValues<GoAffProSource>()
.Select(e => (Source: e, Meta: ((GoAffProSource?)e).Meta()))
.Where(x => x.Meta?.SiteId > 0 && x.Meta != null)
.ToDictionary(x => x.Meta!.SiteId, x => x.Source)
);

public static bool TryGetSource(int siteId, out GoAffProSource source) =>
SiteIdToSource.Value.TryGetValue(siteId, out source);

public static int GetSiteId(this GoAffProSource source) =>
((GoAffProSource?)source).Meta()?.SiteId ?? -1;

public static bool TryGetSiteId(this GoAffProSource source, out int siteId)
{
siteId = ((GoAffProSource?)source).Meta()?.SiteId ?? -1;
return siteId != -1;
}

public static SubathonEventType GetOrderEvent(this GoAffProSource source) =>
((GoAffProSource?)source).Meta()?.OrderEvent ?? SubathonEventType.Unknown;

}
55 changes: 26 additions & 29 deletions SubathonManager.Core/Enums/SubathonCommandType.cs
Original file line number Diff line number Diff line change
@@ -1,61 +1,58 @@
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace SubathonManager.Core.Enums;

public enum SubathonCommandType
{
[CommandMeta(Description="Add Points", RequiresParameter = true)]
AddPoints,
[CommandMeta(Description="Remove Points", RequiresParameter = true)]
SubtractPoints,
[CommandMeta(Description="Set Points", RequiresParameter = true, IsControlType = true)]
SetPoints,
[CommandMeta(Description="Add Time", RequiresParameter = true)]
AddTime,
[CommandMeta(Description="Remove Time", RequiresParameter = true)]
SubtractTime,
[CommandMeta(Description="Set Time", RequiresParameter = true, IsControlType = true)]
SetTime,
[CommandMeta(Description="Lock", IsControlType = true)]
Lock,
[CommandMeta(Description="Unlock", IsControlType = true)]
Unlock,
[CommandMeta(Description="Pause Timer", IsControlType = true)]
Pause,
[CommandMeta(Description="Resume Timer", IsControlType = true)]
Resume,
[CommandMeta(Description="Set Multiplier", RequiresParameter = true, IsControlType = true)]
SetMultiplier,
[CommandMeta(Description="Stop Multiplier", IsControlType = true)]
StopMultiplier,
[CommandMeta(Description="None")]
None,
[CommandMeta(Description="Unknown")]
Unknown,
[CommandMeta(Description="Refresh Overlays", IsControlType = true)]
RefreshOverlays,
[CommandMeta(Description="Add Money", RequiresParameter = true)]
AddMoney,
[CommandMeta(Description="Remove Money", RequiresParameter = true)]
SubtractMoney
}

[ExcludeFromCodeCoverage]
public static class SubathonCommandTypeHelper
{
private static readonly SubathonCommandType[] ParamRequiredCommands = new[]
private static CommandMetaAttribute? Meta(this SubathonCommandType value)
{
SubathonCommandType.AddPoints,
SubathonCommandType.SubtractPoints,
SubathonCommandType.SetPoints,
SubathonCommandType.AddTime,
SubathonCommandType.SubtractTime,
SubathonCommandType.SetTime,
SubathonCommandType.SetMultiplier,
SubathonCommandType.AddMoney,
SubathonCommandType.SubtractMoney
};
var meta = EnumMetaCache.Get<CommandMetaAttribute>(value);
return meta;
}

private static readonly SubathonCommandType[] ControlTypeCommands = new[] // can't "undo"
{
SubathonCommandType.Resume,
SubathonCommandType.Pause,
SubathonCommandType.StopMultiplier,
SubathonCommandType.Lock,
SubathonCommandType.Unlock,
SubathonCommandType.SetMultiplier,
SubathonCommandType.RefreshOverlays,
SubathonCommandType.SetPoints,
SubathonCommandType.SetTime
};


public static bool IsParametersRequired(this SubathonCommandType command) =>
ParamRequiredCommands.Contains(command);
public static bool IsParametersRequired(this SubathonCommandType command) =>
command.Meta()?.RequiresParameter ?? false;

public static bool IsControlTypeCommand(this SubathonCommandType command) =>
ControlTypeCommands.Contains(command);
command.Meta()?.IsControlType ?? false;

}
53 changes: 27 additions & 26 deletions SubathonManager.Core/Enums/SubathonEventSource.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;

namespace SubathonManager.Core.Enums;

public enum SubathonEventSource
{
// some may not actually be event sources in the future, but also integration sources
[EventSourceMeta(Description = "Twitch", SourceGroup = SubathonSourceGroup.Stream, SourceOrder=1, Order=10)]
Twitch,
[EventSourceMeta(Description = "StreamElements", SourceGroup = SubathonSourceGroup.StreamExtension, SourceOrder=21, Order=20)]
StreamElements,
[EventSourceMeta(Description = "KoFi", SourceGroup = SubathonSourceGroup.ExternalService, SourceOrder=41, Order=40)]
KoFi,
[EventSourceMeta(Description = "YouTube", SourceGroup = SubathonSourceGroup.Stream, SourceOrder=2, Order=11)]
YouTube,
[EventSourceMeta(Description = "Commands", SourceGroup = SubathonSourceGroup.Misc, SourceOrder=2000, Order=100)]
Command, // can be from any chat
Simulated, // buttons to test in UI?
[EventSourceMeta(Description = "Simulated", SourceOrder=8000, Order=199)]
Simulated, // buttons to test in UI?
[EventSourceMeta(Description = "Unknown", SourceGroup=SubathonSourceGroup.Misc, SourceOrder=9000, Order=101)]
Unknown, // default
[EventSourceMeta(Description = "StreamLabs", SourceGroup = SubathonSourceGroup.StreamExtension, SourceOrder=22, Order=21)]
StreamLabs,
[EventSourceMeta(Description = "Generic External Services", SourceGroup = SubathonSourceGroup.ExternalService, SourceOrder=1000, Order=99)]
External,
[EventSourceMeta(Description = "Blerp", SourceGroup = SubathonSourceGroup.StreamExtension, SourceOrder=81, Order=30)]
Blerp,
[EventSourceMeta(Description = "Picarto", SourceGroup = SubathonSourceGroup.Stream, SourceOrder=3, Order=12)]
Picarto,
[EventSourceMeta(Description = "GoAffPro Affiliate Stores", SourceGroup = SubathonSourceGroup.ExternalService, SourceOrder=61, Order=50)]
GoAffPro
}

[ExcludeFromCodeCoverage]
public static class SubathonEventSourceHelper
{
private static readonly List<SubathonEventSource> SourceOrder =
[
SubathonEventSource.Twitch,
SubathonEventSource.YouTube,
SubathonEventSource.Picarto,
SubathonEventSource.StreamElements,
SubathonEventSource.StreamLabs,
SubathonEventSource.KoFi,
SubathonEventSource.GoAffPro,
SubathonEventSource.Blerp
];

private static readonly List<SubathonEventSource> SourceOrderEnd =
[
SubathonEventSource.External, SubathonEventSource.Command
];

public static int GetSourceOrder(SubathonEventSource source)
public static int GetSourceOrder(SubathonEventSource source) => ((SubathonEventSource?)source).Meta()?.SourceOrder ?? 99999;

private static EventSourceMetaAttribute? Meta(this SubathonEventSource? value)
{
var idx = SourceOrder.IndexOf(source);
if (idx >= 0) return idx;
if (!value.HasValue) return null;
var meta = EnumMetaCache.Get<EventSourceMetaAttribute>(value);
return meta;
}

var endIdx = SourceOrderEnd.IndexOf(source);
if (endIdx >= 0) return SourceOrder.Count + 1000 + endIdx;
public static SubathonSourceGroup GetGroup(this SubathonEventSource source) =>
((SubathonEventSource?)source).Meta()?.SourceGroup ?? SubathonSourceGroup.Unknown;

return SourceOrder.Count + endIdx;
}
public static string GetGroupLabel(this SubathonEventSource source) => ((SubathonEventSource?)source).Meta()?.Label ?? source.ToString();

public static int GetGroupLabelOrder(this SubathonEventSource source) => ((SubathonEventSource?)source).Meta()?.Order ?? 99999;
}
Loading
Loading