351 lines
14 KiB
C#
351 lines
14 KiB
C#
using SecondBotEvents.Config;
|
|
using RabbitMQ.Client;
|
|
using System;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using RabbitMQ.Client.Events;
|
|
using Newtonsoft.Json;
|
|
using Swan;
|
|
using OpenMetaverse;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
|
|
namespace SecondBotEvents.Services
|
|
{
|
|
public class RabbitService : BotServices
|
|
{
|
|
protected async Task<Task> CommandMessageEvent(object o, BasicDeliverEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
JsonCommandFormat result = JsonConvert.DeserializeObject<JsonCommandFormat>(Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
if (result == null || string.IsNullOrEmpty(result.CommandName))
|
|
{
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Warn("Command Message format is invalid or missing fields. " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Info($"Command Message UUID: {result.CommandName}, args: {String.Join(",", result.commandArgs)}");
|
|
}
|
|
SignedCommand C = new(master.CommandsService, "rabbit", result.CommandName, "", result.commandArgs, 0, "", false, 0, "", false);
|
|
master.CommandsService.RunCommand("rabbit", C, false);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogFormater.Warn("Command Message decode failed " + E.Message + " raw=" + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
protected async Task<Task> NotecardMessageEvent(object o, BasicDeliverEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
JsonNotecardFormat result = JsonConvert.DeserializeObject<JsonNotecardFormat>(Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
if (
|
|
result == null ||
|
|
string.IsNullOrEmpty(result.Name) ||
|
|
string.IsNullOrEmpty(result.UUID) ||
|
|
string.IsNullOrEmpty(result.Content) ||
|
|
string.IsNullOrEmpty(result.Subject)
|
|
)
|
|
{
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Warn("Notecard Message format is invalid or missing fields. " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Info($"Notecard Message Name: {result.Name}, Subject: {result.Subject}");
|
|
}
|
|
if (UUID.TryParse(result.UUID, out UUID uuid) == false)
|
|
{
|
|
LogFormater.Warn("Notecard Message UUID is not valid: " + result.UUID);
|
|
return Task.CompletedTask;
|
|
}
|
|
master.BotClient.SendNotecard(result.Name, result.Content, uuid, false, result.Subject);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogFormater.Warn("Notecard Message decode failed " + E.Message + " raw=" + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
protected async Task<Task> ImMessageEvent(object o, BasicDeliverEventArgs e)
|
|
{
|
|
Console.WriteLine("Message received: " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
try
|
|
{
|
|
JsonMessageFormat result = JsonConvert.DeserializeObject<JsonMessageFormat>(Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
if (result == null || string.IsNullOrEmpty(result.UUID) || string.IsNullOrEmpty(result.Message))
|
|
{
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Warn("IM Message format is invalid or missing fields. " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Info($"IM Message UUID: {result.UUID}, Message: {result.Message}");
|
|
}
|
|
if (UUID.TryParse(result.UUID, out UUID uuid) == false)
|
|
{
|
|
LogFormater.Warn("IM Message UUID is not valid: " + result.UUID);
|
|
return Task.CompletedTask;
|
|
}
|
|
GetClient().Self.InstantMessage(uuid, result.Message);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogFormater.Warn("IM Message decode failed " + E.Message + " raw=" + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
protected async Task<Task> GroupMessageEvent(object o, BasicDeliverEventArgs e)
|
|
{
|
|
Console.WriteLine("Message received: " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
try
|
|
{
|
|
JsonMessageFormat result = JsonConvert.DeserializeObject<JsonMessageFormat>(Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
if (result == null || string.IsNullOrEmpty(result.UUID) || string.IsNullOrEmpty(result.Message))
|
|
{
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Warn("Group IM Message format is invalid or missing fields. " + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Info($"Group Message UUID: {result.UUID}, Message: {result.Message}");
|
|
}
|
|
if (UUID.TryParse(result.UUID, out UUID uuid) == false)
|
|
{
|
|
LogFormater.Warn("Group Message UUID is not valid: " + result.UUID);
|
|
return Task.CompletedTask;
|
|
}
|
|
GetClient().Self.InstantMessageGroup(uuid, result.Message);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogFormater.Warn("Group IM Message decode failed " + E.Message + " raw=" + Encoding.UTF8.GetString(e.Body.ToArray()));
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
public new RabbitConfig myConfig = null;
|
|
protected bool hasSecondbot = false;
|
|
public RabbitService(EventsSecondBot setMaster) : base(setMaster)
|
|
{
|
|
myConfig = new RabbitConfig(master.fromEnv, master.fromFolder);
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
ConnectionFactory factory;
|
|
IConnection connection;
|
|
Dictionary<string, IChannel> channels = new Dictionary<string, IChannel>();
|
|
public override void Start(bool updateEnabled = false, bool setEnabledTo = false)
|
|
{
|
|
if (updateEnabled == true)
|
|
{
|
|
myConfig.setEnabled(setEnabledTo);
|
|
}
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
Stop();
|
|
return;
|
|
}
|
|
LogFormater.Info("Rabbit service [Starting]");
|
|
running = true;
|
|
master.BotClientNoticeEvent += BotClientRestart;
|
|
}
|
|
|
|
public KeyValuePair<bool,string> SendMessage(string Qname,string message)
|
|
{
|
|
if (connection == null || connection.IsOpen == false)
|
|
{
|
|
LogFormater.Warn("Rabbit service [Connection is not open] cannot send message to queue " + Qname);
|
|
return new KeyValuePair<bool, string>(false, "Connection is not open");
|
|
}
|
|
if (string.IsNullOrEmpty(Qname) || string.IsNullOrEmpty(message))
|
|
{
|
|
LogFormater.Warn("Rabbit service [Queue name or message is empty] cannot send message to queue " + Qname);
|
|
return new KeyValuePair<bool, string>(false, "Queue name or message is empty");
|
|
}
|
|
IChannel channel = null;
|
|
if (channels.ContainsKey(Qname) == false)
|
|
{
|
|
channel = connection.CreateChannelAsync().Await();
|
|
channel.QueueDeclareAsync(queue: Qname, durable: true, exclusive: false, autoDelete: false,
|
|
arguments: null);
|
|
channels.Add(Qname, channel);
|
|
}
|
|
channel = channels[Qname];
|
|
if (channel == null || channel.IsOpen == false)
|
|
{
|
|
LogFormater.Warn("Rabbit service [Channel is not open] " + Qname + " reconnecting");
|
|
channel = connection.CreateChannelAsync().Await();
|
|
channel.QueueDeclareAsync(queue: Qname, durable: true, exclusive: false, autoDelete: false,
|
|
arguments: null);
|
|
channels[Qname] = channel;
|
|
}
|
|
if (channel == null || channel.IsOpen == false)
|
|
{
|
|
LogFormater.Warn("Rabbit service [Channel is still not open] " + Qname + " cannot send message");
|
|
return new KeyValuePair<bool, string>(false, "Channel is still not open");
|
|
}
|
|
var body = Encoding.UTF8.GetBytes(message);
|
|
channel.BasicPublishAsync(exchange: "", routingKey: Qname, body: body);
|
|
return new KeyValuePair<bool, string>(true, "Message sent to queue " + Qname);
|
|
}
|
|
|
|
protected void connectService()
|
|
{
|
|
if (connection != null && connection.IsOpen == true)
|
|
{
|
|
connection.CloseAsync().Await();
|
|
connection = null;
|
|
}
|
|
var endpoints = new System.Collections.Generic.List<AmqpTcpEndpoint> {
|
|
new AmqpTcpEndpoint(myConfig.GetHostIP(),myConfig.GetHostPort()),
|
|
};
|
|
ConnectionFactory factory = new ConnectionFactory();
|
|
factory.UserName = myConfig.GetHostUsername();
|
|
factory.Password = myConfig.GetHostPassword();
|
|
try
|
|
{
|
|
connection = factory.CreateConnectionAsync(endpoints).Await();
|
|
if (connection == null || connection.IsOpen == false)
|
|
{
|
|
LogFormater.Crit("Rabbit service [Failed to connect to RabbitMQ] stopping", true);
|
|
Stop();
|
|
return;
|
|
}
|
|
|
|
createChannelWorker(myConfig.GetNotecardQueue(), "NotecardMessageEvent");
|
|
createChannelWorker(myConfig.GetCommandQueue(), "CommandMessageEvent");
|
|
createChannelWorker(myConfig.GetImQueue(), "ImMessageEvent");
|
|
createChannelWorker(myConfig.GetGroupImQueue(), "GroupMessageEvent");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogFormater.Crit("Rabbit service [Failed to connect to RabbitMQ] " +
|
|
"" + myConfig.GetHostIP() + ":" + myConfig.GetHostPort() + " " +
|
|
"with user: " + myConfig.GetHostUsername() + "\n" +
|
|
"error:" + ex.Message, true);
|
|
Stop();
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
protected async void createChannelWorker(string queueName, string functionpointer)
|
|
{
|
|
IChannel channel = connection.CreateChannelAsync().Await();
|
|
channels.Add(queueName, channel);
|
|
var consumer = new AsyncEventingBasicConsumer(channel);
|
|
|
|
// Use reflection to get the method by name and create a delegate
|
|
var method = typeof(RabbitService).GetMethod(functionpointer, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
|
if (method == null)
|
|
throw new InvalidOperationException($"Handler method '{functionpointer}' not found.");
|
|
|
|
// Create delegate matching AsyncEventHandler<BasicDeliverEventArgs>
|
|
AsyncEventHandler<BasicDeliverEventArgs> handler =
|
|
(AsyncEventHandler<BasicDeliverEventArgs>)Delegate.CreateDelegate(
|
|
typeof(AsyncEventHandler<BasicDeliverEventArgs>), this, method);
|
|
|
|
consumer.ReceivedAsync += handler;
|
|
|
|
string consumerTag = Guid.NewGuid().ToString(); // Generate a unique consumer tag.
|
|
channel.QueueDeclareAsync(
|
|
queue: queueName,
|
|
durable: true,
|
|
exclusive: false,
|
|
autoDelete: false,
|
|
arguments: null
|
|
).Await();
|
|
await channel.BasicConsumeAsync(
|
|
queue: queueName,
|
|
autoAck: true,
|
|
consumerTag: consumerTag,
|
|
noLocal: false,
|
|
exclusive: false,
|
|
arguments: null,
|
|
consumer: consumer
|
|
);
|
|
if (myConfig.GetLogDebug() == true)
|
|
{
|
|
LogFormater.Info($"Rabbit service [Consumer started for queue: {queueName}]");
|
|
}
|
|
}
|
|
|
|
protected void BotClientRestart(object o, BotClientNotice e)
|
|
{
|
|
if (e.isStart == false)
|
|
{
|
|
return;
|
|
}
|
|
LogFormater.Info("Rabbit service [Attached to new client]");
|
|
hasSecondbot = true;
|
|
connectService();
|
|
}
|
|
|
|
public override void Stop()
|
|
{
|
|
if (running == true)
|
|
{
|
|
running = false;
|
|
LogFormater.Info("Rabbit service [Stopping]");
|
|
if (master != null)
|
|
{
|
|
master.BotClientNoticeEvent -= BotClientRestart;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string Status()
|
|
{
|
|
if (myConfig == null)
|
|
{
|
|
return "No Config";
|
|
}
|
|
else if (myConfig.GetEnabled() == false)
|
|
{
|
|
return "Disabled";
|
|
}
|
|
return "Enabled";
|
|
}
|
|
}
|
|
|
|
public class JsonMessageFormat
|
|
{
|
|
public string UUID;
|
|
public string Message;
|
|
}
|
|
|
|
public class JsonCommandFormat
|
|
{
|
|
public string CommandName;
|
|
public string[] commandArgs;
|
|
}
|
|
|
|
public class JsonNotecardFormat
|
|
{
|
|
public string Name;
|
|
public string Subject;
|
|
public string Content;
|
|
public string UUID;
|
|
}
|
|
}
|