Merge branch 'master' into lida_Dev
This commit is contained in:
commit
effb9d8839
|
|
@ -7,4 +7,5 @@ public enum BroswerActionEnum
|
|||
Typing = 3,
|
||||
Hover = 4,
|
||||
Scroll = 5,
|
||||
DragAndDrop = 6
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,9 @@ public class ElementPosition
|
|||
public float X { get; set; } = default!;
|
||||
|
||||
public float Y { get; set; } = default!;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{X}, {Y}]";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
namespace BotSharp.Abstraction.Infrastructures.Enums;
|
||||
|
||||
public enum EventPriority
|
||||
{
|
||||
Low = 1,
|
||||
Medium = 2,
|
||||
High = 3
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ public interface IEventPublisher
|
|||
/// <returns></returns>
|
||||
Task BroadcastAsync(string channel, string message);
|
||||
|
||||
Task PublishAsync(string channel, string message);
|
||||
Task PublishAsync(string channel, string message, EventPriority? priority = null);
|
||||
|
||||
Task ReDispatchAsync(string channel, int count = 10, string order = "asc");
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ public interface IEventSubscriber
|
|||
{
|
||||
Task SubscribeAsync(string channel, Func<string, string, Task> received);
|
||||
|
||||
Task SubscribeAsync(string channel, string group, Func<string, string, Task> received);
|
||||
Task SubscribeAsync(string channel, string group, bool priorityEnabled, Func<string, string, Task> received);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace BotSharp.Core.Infrastructures.Events;
|
||||
|
|
@ -20,10 +21,16 @@ public class RedisPublisher : IEventPublisher
|
|||
await _subscriber.PublishAsync(channel, message);
|
||||
}
|
||||
|
||||
public async Task PublishAsync(string channel, string message)
|
||||
public async Task PublishAsync(string channel, string message, EventPriority? priority = null)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
|
||||
// convert to apporiate channel by priority
|
||||
if (priority != null)
|
||||
{
|
||||
channel = $"{channel}-{priority}";
|
||||
}
|
||||
|
||||
if (CheckMessageExists(db, channel, "message", message))
|
||||
{
|
||||
_logger.LogError($"The message already exists {channel} {message}");
|
||||
|
|
@ -41,7 +48,7 @@ public class RedisPublisher : IEventPublisher
|
|||
_logger.LogInformation($"Published message {channel} {message} ({messageId})");
|
||||
}
|
||||
|
||||
private bool CheckMessageExists(IDatabase db, string streamName, string fieldName, string desiredValue)
|
||||
private bool CheckMessageExists(IDatabase db, string channel, string fieldName, string desiredValue)
|
||||
{
|
||||
// Define the range to fetch all messages
|
||||
RedisValue start = "-"; // Start from the smallest ID
|
||||
|
|
@ -49,7 +56,7 @@ public class RedisPublisher : IEventPublisher
|
|||
int count = 10; // Number of messages to retrieve
|
||||
|
||||
// Fetch the latest 10 messages
|
||||
var streamEntries = db.StreamRange(streamName, start, end, count, Order.Descending);
|
||||
var streamEntries = db.StreamRange(channel, start, end, count, Order.Descending);
|
||||
|
||||
if (streamEntries.Length == 0)
|
||||
{
|
||||
|
|
@ -84,7 +91,10 @@ public class RedisPublisher : IEventPublisher
|
|||
|
||||
try
|
||||
{
|
||||
var messageId = await db.StreamAddAsync(channel, "message", entry.Values[0].Value);
|
||||
var messageId = await db.StreamAddAsync(channel, [
|
||||
new NameValueEntry("message", entry.Values[0].Value),
|
||||
new NameValueEntry("timestamp", DateTime.UtcNow.ToString("o"))
|
||||
]);
|
||||
|
||||
_logger.LogWarning($"ReDispatched message: {channel} {entry.Values[0].Value} ({messageId})");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace BotSharp.Core.Infrastructures.Events;
|
||||
|
|
@ -24,10 +25,72 @@ public class RedisSubscriber : IEventSubscriber
|
|||
});
|
||||
}
|
||||
|
||||
public async Task SubscribeAsync(string channel, string group, Func<string, string, Task> received)
|
||||
public async Task SubscribeAsync(string channel, string group, bool priorityEnabled, Func<string, string, Task> received)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
|
||||
if (priorityEnabled)
|
||||
{
|
||||
await CreateConsumerGroup(db, $"{channel}-{EventPriority.Low}", group);
|
||||
await CreateConsumerGroup(db, $"{channel}-{EventPriority.Medium}", group);
|
||||
await CreateConsumerGroup(db, $"{channel}-{EventPriority.High}", group);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CreateConsumerGroup(db, channel, group);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
|
||||
if (priorityEnabled)
|
||||
{
|
||||
if (await HandleGroupMessage(db, $"{channel}-{EventPriority.High}", group, received) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (await HandleGroupMessage(db, $"{channel}-{EventPriority.Medium}", group, received) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await HandleGroupMessage(db, $"{channel}-{EventPriority.Low}", group, received);
|
||||
}
|
||||
else
|
||||
{
|
||||
await HandleGroupMessage(db, channel, group, received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> HandleGroupMessage(IDatabase db, string channel, string group, Func<string, string, Task> received)
|
||||
{
|
||||
var entries = await db.StreamReadGroupAsync(channel, group, Environment.MachineName, count: 1);
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
_logger.LogInformation($"Consumer {Environment.MachineName} received: {channel} {entry.Values[0].Value}");
|
||||
await db.StreamAcknowledgeAsync(channel, group, entry.Id);
|
||||
|
||||
try
|
||||
{
|
||||
await received(channel, entry.Values[0].Value);
|
||||
|
||||
// Optionally delete the message to save space
|
||||
await db.StreamDeleteAsync(channel, [entry.Id]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error processing message: {ex.Message}, event id: {channel} {entry.Id}\r\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
return entries.Length;
|
||||
}
|
||||
|
||||
private async Task CreateConsumerGroup(IDatabase db, string channel, string group)
|
||||
{
|
||||
// Create the consumer group if it doesn't exist
|
||||
try
|
||||
{
|
||||
|
|
@ -43,30 +106,5 @@ public class RedisSubscriber : IEventSubscriber
|
|||
_logger.LogError($"Error creating consumer group: '{group}' {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
var entries = await db.StreamReadGroupAsync(channel, group, Environment.MachineName, count: 1);
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
_logger.LogInformation($"Consumer {Environment.MachineName} received: {channel} {entry.Values[0].Value}");
|
||||
await db.StreamAcknowledgeAsync(channel, group, entry.Id);
|
||||
|
||||
try
|
||||
{
|
||||
await received(channel, entry.Values[0].Value);
|
||||
|
||||
// Optionally delete the message to save space
|
||||
await db.StreamDeleteAsync(channel, [entry.Id]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error processing message: {ex.Message}, event id: {channel} {entry.Id}\r\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(Random.Shared.Next(1, 11) * 100);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,10 +79,102 @@ public partial class PlaywrightWebDriver
|
|||
{
|
||||
await locator.HoverAsync();
|
||||
}
|
||||
else if (action.Action == BroswerActionEnum.DragAndDrop)
|
||||
{
|
||||
// Locate the element to drag
|
||||
var box = await locator.BoundingBoxAsync();
|
||||
|
||||
if (box != null)
|
||||
{
|
||||
// Calculate start position
|
||||
float startX = box.X + box.Width / 2; // Start at the center of the element
|
||||
float startY = box.Y + box.Height / 2;
|
||||
|
||||
// Drag offsets
|
||||
float offsetX = action.Position.X;
|
||||
// Move horizontally
|
||||
if (action.Position.Y == 0)
|
||||
{
|
||||
// Perform drag-and-move
|
||||
// Move mouse to the start position
|
||||
var mouse = page.Mouse;
|
||||
await mouse.MoveAsync(startX, startY);
|
||||
await mouse.DownAsync();
|
||||
|
||||
// Move mouse smoothly in increments
|
||||
var tracks = GetVelocityTrack(offsetX);
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
startX += track;
|
||||
await page.Mouse.MoveAsync(startX, 0, new MouseMoveOptions
|
||||
{
|
||||
Steps = 3
|
||||
});
|
||||
}
|
||||
|
||||
// Release mouse button
|
||||
await mouse.UpAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action.WaitTime > 0)
|
||||
{
|
||||
await Task.Delay(1000 * action.WaitTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<int> GetVelocityTrack(float distance)
|
||||
{
|
||||
// Initialize the track list to store the movement distances
|
||||
List<int> track = new List<int>();
|
||||
|
||||
// Initialize variables
|
||||
float current = 0; // Current position
|
||||
float mid = distance * 4 / 5; // Deceleration threshold
|
||||
float t = 0.2f; // Time interval
|
||||
float v = 1; // Initial velocity
|
||||
|
||||
// Generate the track
|
||||
while (current < distance)
|
||||
{
|
||||
float a; // Acceleration
|
||||
|
||||
// Determine acceleration based on position
|
||||
if (current < mid)
|
||||
{
|
||||
a = 4; // Accelerate
|
||||
}
|
||||
else
|
||||
{
|
||||
a = -3; // Decelerate
|
||||
}
|
||||
|
||||
// Calculate new velocity
|
||||
float v0 = v;
|
||||
v = v0 + a * t;
|
||||
|
||||
// Calculate the movement during this interval
|
||||
float move = v0 * t + 0.5f * a * t * t;
|
||||
|
||||
// Update current position
|
||||
if (current + move > distance)
|
||||
{
|
||||
move = distance - current;
|
||||
track.Add((int)Math.Round(move));
|
||||
break;
|
||||
}
|
||||
|
||||
current += move;
|
||||
|
||||
// Add rounded movement to the track
|
||||
track.Add((int)Math.Round(move));
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue