add support for rabbit message Q for the bot

This commit is contained in:
Madpeter 2025-06-23 23:03:20 +01:00
parent c410412097
commit 5c69da371a
5 changed files with 398 additions and 1 deletions

View file

@ -152,7 +152,18 @@ ENV basic_Username='' \
smtp_User='me@email.addr.tld' \
smtp_Token='none' \
smtp_SendAlertsAndLoginsTo='me@somewhere.tld' \
smtp_HideStatusOutput='false'
smtp_HideStatusOutput='false' \
rabbit_Enabled='false' \
rabbit_HideStatusOutput='false' \
rabbit_HostIP='127.0.0.1' \
rabbit_HostUsername='guest' \
rabbit_HostPassword='guest' \
rabbit_HostPort='5672' \
rabbit_NotecardQueue='notecards' \
rabbit_CommandQueue='commands' \
rabbit_ImQueue='ims' \
rabbit_GroupImQueue='groupims' \
rabbit_LogDebug='true'
EXPOSE 80
ENV ASPNETCORE_URLS http://+:80

View file

@ -0,0 +1,72 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents.Config
{
public class RabbitConfig(bool fromENV, string fromFolder = "") : Config(fromENV, fromFolder)
{
protected override void MakeSettings()
{
filename = "rabbit";
settings.Add("Enabled");
settings.Add("HideStatusOutput");
settings.Add("HostIP");
settings.Add("HostPort");
settings.Add("HostUsername");
settings.Add("HostPassword");
settings.Add("NotecardQueue");
settings.Add("CommandQueue");
settings.Add("ImQueue");
settings.Add("GroupImQueue");
settings.Add("LogDebug");
}
public bool GetLogDebug()
{
return ReadSettingAsBool("LogDebug", true);
}
public bool GetEnabled()
{
return ReadSettingAsBool("Enabled", false);
}
public string GetHostIP()
{
return ReadSettingAsString("HostIP", "127.0.0.1");
}
public string GetHostUsername()
{
return ReadSettingAsString("HostUsername", "guest");
}
public string GetHostPassword()
{
return ReadSettingAsString("HostPassword", "guest");
}
public int GetHostPort()
{
return ReadSettingAsInt("HostPort", 5672);
}
public string GetNotecardQueue()
{
return ReadSettingAsString("NotecardQueue", "notecards");
}
public string GetCommandQueue()
{
return ReadSettingAsString("CommandQueue", "commands");
}
public string GetImQueue()
{
return ReadSettingAsString("ImQueue", "ims");
}
public string GetGroupImQueue()
{
return ReadSettingAsString("GroupImQueue", "groupims");
}
}
}

View file

@ -41,6 +41,7 @@ namespace SecondBotEvents
public HomeboundService HomeboundService { get { return (HomeboundService)GetService("HomeboundService"); } }
public EventsService EventsService { get { return (EventsService)GetService("EventsService"); } }
public DialogService DialogService { get { return (DialogService)GetService("DialogService"); } }
public RabbitService RabbitService { get { return (RabbitService)GetService("RabbitService"); } }
public SmtpService SmtpService { get { return (SmtpService)GetService("SmtpService"); } }
@ -177,6 +178,7 @@ namespace SecondBotEvents
RegisterService("ChatGptService");
//RegisterService("RecoveryService");
RegisterService("SmtpService");
RegisterService("RabbitService");
if (BotClient.IsLoaded() == false)
{
LogFormater.Info("Config is not loaded :(");

View file

@ -24,6 +24,7 @@
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NRedisStack" Version="0.13.1" />
<PackageReference Include="RabbitMQ.Client" Version="7.1.2" />
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,311 @@
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;
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;
}
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;
}
}