using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using Lagrange.Core; using Lagrange.Core.Event.EventArg; using Lagrange.Core.Message; using Lagrange.Core.Utility.Extension; using Lagrange.OneBot.Core.Entity.Message; using Lagrange.OneBot.Core.Network; using Lagrange.OneBot.Database; using Lagrange.OneBot.Message.Entity; using LiteDB; using Microsoft.Extensions.Configuration; using JsonSerializer = System.Text.Json.JsonSerializer; namespace Lagrange.OneBot.Message; /// /// The class that converts the OneBot message to/from MessageEntity of Lagrange.Core /// public sealed class MessageService { private readonly LagrangeWebSvcCollection _service; private readonly LiteDatabase _context; private readonly IConfiguration _config; private readonly Dictionary> _entityToFactory; private readonly bool _stringPost; private static readonly JsonSerializerOptions Options; static MessageService() { Options = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ModifyTypeInfo } } }; } public MessageService(BotContext bot, LagrangeWebSvcCollection service, LiteDatabase context, IConfiguration config) { _service = service; _context = context; _config = config; _stringPost = config.GetValue("Message:StringPost"); var invoker = bot.Invoker; invoker.OnFriendMessageReceived += OnFriendMessageReceived; invoker.OnGroupMessageReceived += OnGroupMessageReceived; invoker.OnTempMessageReceived += OnTempMessageReceived; _entityToFactory = new Dictionary>(); foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { var attribute = type.GetCustomAttribute(); if (attribute != null) { var instance = (SegmentBase)type.CreateInstance(false); instance.Database = _context; if (_entityToFactory.TryGetValue(attribute.Entity, out var factories)) factories.Add((attribute.Type, instance)); else _entityToFactory[attribute.Entity] = [(attribute.Type, instance)]; } } } private void OnFriendMessageReceived(BotContext bot, FriendMessageEvent e) { var record = (MessageRecord)e.Chain; _context.GetCollection().Insert(new BsonValue(record.MessageHash), record); if (_config.GetValue("Message:IgnoreSelf") && e.Chain.FriendUin == bot.BotUin) return; // ignore self message var request = ConvertToPrivateMsg(bot.BotUin, e.Chain); _ = _service.SendJsonAsync(request); } public object ConvertToPrivateMsg(uint uin, MessageChain chain) { var segments = Convert(chain); int hash = MessageRecord.CalcMessageHash(chain.MessageId, chain.Sequence); string raw = ToRawMessage(segments); object request = _stringPost ? new OneBotPrivateStringMsg(uin, new OneBotSender(chain.FriendUin, chain.FriendInfo?.Nickname ?? string.Empty), "friend", ((DateTimeOffset)chain.Time).ToUnixTimeSeconds()) { MessageId = hash, UserId = chain.FriendUin, Message = raw, RawMessage = raw, TargetId = chain.TargetUin, } : new OneBotPrivateMsg(uin, new OneBotSender(chain.FriendUin, chain.FriendInfo?.Nickname ?? string.Empty), "friend", ((DateTimeOffset)chain.Time).ToUnixTimeSeconds()) { MessageId = hash, UserId = chain.FriendUin, Message = segments, RawMessage = raw, TargetId = chain.TargetUin }; return request; } private void OnGroupMessageReceived(BotContext bot, GroupMessageEvent e) { var record = (MessageRecord)e.Chain; _context.GetCollection().Insert(new BsonValue(record.MessageHash), record); if (_config.GetValue("Message:IgnoreSelf") && e.Chain.FriendUin == bot.BotUin) return; // ignore self message var request = ConvertToGroupMsg(bot.BotUin, e.Chain); _ = _service.SendJsonAsync(request); } public object ConvertToGroupMsg(uint uin, MessageChain chain) { var segments = Convert(chain); int hash = MessageRecord.CalcMessageHash(chain.MessageId, chain.Sequence); object request = _stringPost ? new OneBotGroupStringMsg(uin, chain.GroupUin ?? 0, ToRawMessage(segments), chain.GroupMemberInfo ?? throw new Exception("Group member not found"), hash, ((DateTimeOffset)chain.Time).ToUnixTimeSeconds()) : new OneBotGroupMsg(uin, chain.GroupUin ?? 0, segments, ToRawMessage(segments), chain.GroupMemberInfo ?? throw new Exception("Group member not found"), hash, ((DateTimeOffset)chain.Time).ToUnixTimeSeconds()); return request; } private void OnTempMessageReceived(BotContext bot, TempMessageEvent e) { var record = (MessageRecord)e.Chain; _context.GetCollection().Insert(new BsonValue(record.MessageHash), record); var segments = Convert(e.Chain); var request = new OneBotPrivateMsg(bot.BotUin, new OneBotSender(e.Chain.FriendUin, e.Chain.FriendInfo?.Nickname ?? string.Empty), "group", ((DateTimeOffset)e.Chain.Time).ToUnixTimeSeconds()) { MessageId = record.MessageHash, UserId = e.Chain.FriendUin, Message = segments, RawMessage = ToRawMessage(segments) }; _ = _service.SendJsonAsync(request); } public List Convert(MessageChain chain) { var result = new List(); foreach (var entity in chain) { if (_entityToFactory.TryGetValue(entity.GetType(), out var instances)) { foreach (var instance in instances) { if (instance.Factory.FromEntity(chain, entity) is { } segment) result.Add(new OneBotSegment(instance.Type, segment)); } } } return result; } private static string EscapeText(string str) => str .Replace("&", "&") .Replace("[", "[") .Replace("]", "]"); private static string EscapeCQ(string str) => EscapeText(str).Replace(",", ","); private static string ToRawMessage(List segments) { var rawMessageBuilder = new StringBuilder(); foreach (var segment in segments) { if (segment.Data is TextSegment textSeg) { rawMessageBuilder.Append(EscapeText(textSeg.Text)); } else { rawMessageBuilder.Append("[CQ:"); rawMessageBuilder.Append(segment.Type); foreach (var property in JsonSerializer.SerializeToElement(segment.Data, Options).EnumerateObject()) { rawMessageBuilder.Append(','); rawMessageBuilder.Append(property.Name); rawMessageBuilder.Append('='); rawMessageBuilder.Append(EscapeCQ(property.Value.ToString())); } rawMessageBuilder.Append(']'); } } return rawMessageBuilder.ToString(); } private static void ModifyTypeInfo(JsonTypeInfo ti) { if (ti.Kind != JsonTypeInfoKind.Object) return; foreach (var info in ti.Properties.Where(x => x.AttributeProvider?.IsDefined(typeof(CQPropertyAttribute), false) == false).ToArray()) { ti.Properties.Remove(info); } } }