mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 08:34:27 -05:00 
			
		
		
		
	Restructured folders and project names, ci should be fixed
This commit is contained in:
		
							
								
								
									
										95
									
								
								src/NadekoBot/Common/PubSub/EventPubSub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/NadekoBot/Common/PubSub/EventPubSub.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public class EventPubSub : IPubSub
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Dictionary<string, Dictionary<Delegate, List<Func<object, ValueTask>>>> _actions
 | 
			
		||||
            = new Dictionary<string, Dictionary<Delegate, List<Func<object, ValueTask>>>>();
 | 
			
		||||
        private readonly object locker = new object();
 | 
			
		||||
        
 | 
			
		||||
        public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
 | 
			
		||||
        {
 | 
			
		||||
            Func<object, ValueTask> localAction = obj => action((TData) obj);
 | 
			
		||||
            lock(locker)
 | 
			
		||||
            {
 | 
			
		||||
                Dictionary<Delegate, List<Func<object, ValueTask>>> keyActions;
 | 
			
		||||
                if (!_actions.TryGetValue(key.Key, out keyActions))
 | 
			
		||||
                {
 | 
			
		||||
                    keyActions = new Dictionary<Delegate, List<Func<object, ValueTask>>>();
 | 
			
		||||
                    _actions[key.Key] = keyActions;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                List<Func<object, ValueTask>> sameActions;
 | 
			
		||||
                if (!keyActions.TryGetValue(action, out sameActions))
 | 
			
		||||
                {
 | 
			
		||||
                    sameActions = new List<Func<object, ValueTask>>();
 | 
			
		||||
                    keyActions[action] = sameActions;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sameActions.Add(localAction);
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public Task Pub<TData>(in TypedKey<TData> key, TData data)
 | 
			
		||||
        {
 | 
			
		||||
            lock (locker)
 | 
			
		||||
            {
 | 
			
		||||
                if(_actions.TryGetValue(key.Key, out var actions))
 | 
			
		||||
                {
 | 
			
		||||
                    // if this class ever gets used, this needs to be properly implemented
 | 
			
		||||
                    // 1. ignore all valuetasks which are completed
 | 
			
		||||
                    // 2. return task.whenall all other tasks
 | 
			
		||||
                    return Task.WhenAll(actions
 | 
			
		||||
                        .SelectMany(kvp => kvp.Value)
 | 
			
		||||
                        .Select(action => action(data).AsTask()));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task Unsub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
 | 
			
		||||
        {
 | 
			
		||||
            lock (locker)
 | 
			
		||||
            {
 | 
			
		||||
                // get subscriptions for this action
 | 
			
		||||
                if (_actions.TryGetValue(key.Key, out var actions))
 | 
			
		||||
                {
 | 
			
		||||
                    var hashCode = action.GetHashCode();
 | 
			
		||||
                    // get subscriptions which have the same action hash code
 | 
			
		||||
                    // note: having this as a list allows for multiple subscriptions of
 | 
			
		||||
                    //       the same insance's/static method
 | 
			
		||||
                    if (actions.TryGetValue(action, out var sameActions))
 | 
			
		||||
                    {
 | 
			
		||||
                        // remove last subscription
 | 
			
		||||
                        sameActions.RemoveAt(sameActions.Count - 1);
 | 
			
		||||
                        
 | 
			
		||||
                        // if the last subscription was the only subscription
 | 
			
		||||
                        // we can safely remove this action's dictionary entry
 | 
			
		||||
                        if (sameActions.Count == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            actions.Remove(action);
 | 
			
		||||
                            
 | 
			
		||||
                            // if our dictionary has no more elements after 
 | 
			
		||||
                            // removing the entry
 | 
			
		||||
                            // it's safe to remove it from the key's subscriptions
 | 
			
		||||
                            if (actions.Count == 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                _actions.Remove(key.Key);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Common/PubSub/IPubSub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Common/PubSub/IPubSub.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public interface IPubSub
 | 
			
		||||
    {
 | 
			
		||||
        public Task Pub<TData>(in TypedKey<TData> key, TData data);
 | 
			
		||||
        public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Common/PubSub/ISeria.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Common/PubSub/ISeria.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public interface ISeria
 | 
			
		||||
    {
 | 
			
		||||
        byte[] Serialize<T>(T data);
 | 
			
		||||
        T Deserialize<T>(byte[] data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/NadekoBot/Common/PubSub/JsonSeria.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/NadekoBot/Common/PubSub/JsonSeria.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using NadekoBot.Core.Common.JsonConverters;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public class JsonSeria : ISeria
 | 
			
		||||
    {
 | 
			
		||||
        private JsonSerializerOptions serializerOptions = new JsonSerializerOptions()
 | 
			
		||||
        {
 | 
			
		||||
            Converters =
 | 
			
		||||
            {
 | 
			
		||||
                new Rgba32Converter(),
 | 
			
		||||
                new CultureInfoConverter(),
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        public byte[] Serialize<T>(T data) 
 | 
			
		||||
            => JsonSerializer.SerializeToUtf8Bytes(data, serializerOptions);
 | 
			
		||||
 | 
			
		||||
        public T Deserialize<T>(byte[] data)
 | 
			
		||||
        {
 | 
			
		||||
            if (data is null)
 | 
			
		||||
                return default;
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            return JsonSerializer.Deserialize<T>(data, serializerOptions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/NadekoBot/Common/PubSub/RedisPubSub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/NadekoBot/Common/PubSub/RedisPubSub.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using NadekoBot.Core.Services;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using Serilog;
 | 
			
		||||
using StackExchange.Redis;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public sealed class RedisPubSub : IPubSub
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ConnectionMultiplexer _multi;
 | 
			
		||||
        private readonly ISeria _serializer;
 | 
			
		||||
        private readonly IBotCredentials _creds;
 | 
			
		||||
 | 
			
		||||
        public RedisPubSub(ConnectionMultiplexer multi, ISeria serializer, IBotCredentials creds)
 | 
			
		||||
        {
 | 
			
		||||
            _multi = multi;
 | 
			
		||||
            _serializer = serializer;
 | 
			
		||||
            _creds = creds;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task Pub<TData>(in TypedKey<TData> key, TData data)
 | 
			
		||||
        {
 | 
			
		||||
            var serialized = _serializer.Serialize(data);
 | 
			
		||||
            return _multi.GetSubscriber().PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
 | 
			
		||||
        {
 | 
			
		||||
            var eventName = key.Key;
 | 
			
		||||
            return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", async (ch, data) =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var dataObj = _serializer.Deserialize<TData>(data);
 | 
			
		||||
                    await action(dataObj);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Error($"Error handling the event {eventName}: {ex.Message}");
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								src/NadekoBot/Common/PubSub/TypedKey.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/NadekoBot/Common/PubSub/TypedKey.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public readonly struct TypedKey<TData>
 | 
			
		||||
    {
 | 
			
		||||
        public readonly string Key;
 | 
			
		||||
 | 
			
		||||
        public TypedKey(in string key)
 | 
			
		||||
        {
 | 
			
		||||
            Key = key;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static implicit operator TypedKey<TData>(in string input)
 | 
			
		||||
            => new TypedKey<TData>(input);
 | 
			
		||||
        public static implicit operator string(in TypedKey<TData> input)
 | 
			
		||||
            => input.Key;
 | 
			
		||||
 | 
			
		||||
        public static bool operator ==(in TypedKey<TData> left, in TypedKey<TData> right)
 | 
			
		||||
            => left.Key == right.Key;
 | 
			
		||||
        public static bool operator !=(in TypedKey<TData> left, in TypedKey<TData> right)
 | 
			
		||||
            => !(left == right);
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
            => obj is TypedKey<TData> o && o == this;
 | 
			
		||||
 | 
			
		||||
        public override int GetHashCode() => Key?.GetHashCode() ?? 0;
 | 
			
		||||
 | 
			
		||||
        public override string ToString() => Key;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/NadekoBot/Common/PubSub/YamlSeria.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/NadekoBot/Common/PubSub/YamlSeria.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
using NadekoBot.Core.Common.Configs;
 | 
			
		||||
using YamlDotNet.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Core.Common
 | 
			
		||||
{
 | 
			
		||||
    public class YamlSeria : IConfigSeria
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ISerializer _serializer;
 | 
			
		||||
        private readonly IDeserializer _deserializer;
 | 
			
		||||
 | 
			
		||||
        private static readonly Regex CodePointRegex
 | 
			
		||||
            = new Regex(@"(\\U(?<code>[a-zA-Z0-9]{8})|\\u(?<code>[a-zA-Z0-9]{4})|\\x(?<code>[a-zA-Z0-9]{2}))",
 | 
			
		||||
                RegexOptions.Compiled);
 | 
			
		||||
 | 
			
		||||
        public YamlSeria()
 | 
			
		||||
        {
 | 
			
		||||
            _serializer = Yaml.Serializer;
 | 
			
		||||
            _deserializer = Yaml.Deserializer;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public string Serialize<T>(T obj)
 | 
			
		||||
        {
 | 
			
		||||
            var escapedOutput = _serializer.Serialize(obj);
 | 
			
		||||
            var output = CodePointRegex.Replace(escapedOutput, me =>
 | 
			
		||||
            {
 | 
			
		||||
                var str = me.Groups["code"].Value;
 | 
			
		||||
                var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
 | 
			
		||||
                return newString;
 | 
			
		||||
            });
 | 
			
		||||
            return output;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T Deserialize<T>(string data) 
 | 
			
		||||
            => _deserializer.Deserialize<T>(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user