Update Current Progress

This commit is contained in:
Linwenxuan05 2023-08-26 05:47:29 +08:00
parent 50b51875a7
commit 5df500be51
13 changed files with 244 additions and 52 deletions

View file

@ -1,7 +1,6 @@
using System.Collections.Concurrent;
using System.Text;
using Lagrange.Core.Common;
using Lagrange.Core.Core.Network;
using Lagrange.Core.Core.Packets;
using Lagrange.Core.Core.Event.Protocol.System;
using Lagrange.Core.Core.Packets.Service.Highway;
using Lagrange.Core.Utility.Binary;
using Lagrange.Core.Utility.Extension;
@ -15,17 +14,21 @@ namespace Lagrange.Core.Core.Context;
internal class HighwayContext : ContextBase
{
private readonly HttpClient _client;
private Dictionary<uint, List<Uri>>? _highwayUrls;
private byte[]? _sigSession;
public HighwayContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device)
: base(collection, keystore, appInfo, device)
{
_client = new HttpClient
var handler = new HttpClientHandler
{
DefaultRequestHeaders =
{
{ "Accept-Encoding", "identity" },
}
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
};
_client = new HttpClient(handler);
_client.DefaultRequestHeaders.Add("Accept-Encoding", "identity");
_client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2)");
}
public async Task<bool> EchoAsync(Uri uri, uint seq)
@ -56,9 +59,40 @@ internal class HighwayContext : ContextBase
}
}
public async Task<bool> UploadSrcByStream(int commonId, uint uin, byte[] upKey, BinaryPacket data, long fileSize, byte[] md5)
public async Task<bool> UploadSrcByStreamAsync(int commonId, uint uin, Stream data, byte[] md5, byte[]? extendInfo = null)
{
return false;
if (_highwayUrls is null) await FetchHttpConnUri();
bool success = true;
var upBlocks = new List<UpBlock>();
var uri = _highwayUrls?[(uint)commonId][0] ?? throw new InvalidOperationException("No highway url");
long fileSize = data.Length;
int sequence = 1;
int offset = 0;
int chunkSize = fileSize is >= 1024 and <= 1048575 ? 8192 : 1024 * 1024;
while (data.Position < data.Length)
{
var buffer = new byte[Math.Min(chunkSize, fileSize - offset)];
int payload = await data.ReadAsync(buffer.AsMemory());
var reqBody = new UpBlock(commonId, uin, Interlocked.Increment(ref sequence),
(ulong)fileSize, (ulong)offset, md5, buffer, extendInfo,
(ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds());
upBlocks.Add(reqBody);
offset = (int)data.Position;
if (upBlocks.Count >= 32 || data.Position == data.Length)
{
var tasks = upBlocks.Select(x => SendUpBlockAsync(x, uri)).ToArray();
var results = await Task.WhenAll(tasks);
success &= results.All(x => x);
upBlocks.Clear();
}
}
return success;
}
private async Task<bool> SendUpBlockAsync(UpBlock upBlock, Uri server)
@ -69,22 +103,17 @@ internal class HighwayContext : ContextBase
Uin = upBlock.Uin.ToString(),
Command = "PicUp.DataUp",
Seq = (uint)upBlock.Sequence,
RetryTimes = 0,
AppId = (uint)AppInfo.SubAppId,
DataFlag = 4096,
CommandId = (uint)upBlock.CommandId,
LocaleId = 2052
};
var segHead = new SegHead
{
Filesize = upBlock.FileSize,
DataOffset = upBlock.Offset,
DataLength = (uint)upBlock.Block.Length,
ServiceTicket = upBlock.UpKey,
ServiceTicket = _sigSession ?? throw new InvalidOperationException("No login sig"),
Md5 = (await upBlock.Block.Md5Async()).UnHex(),
FileMd5 = upBlock.FileMd5,
CachePort = 0,
CacheAddr = 0
};
var highwayHead = new ReqDataHighwayHead
@ -93,13 +122,13 @@ internal class HighwayContext : ContextBase
MsgSegHead = segHead,
BytesReqExtendInfo = upBlock.ExtendInfo,
Timestamp = upBlock.Timestamp,
MsgLoginSigHead = upBlock.LoginSig
};
bool isEnd = upBlock.Offset + (ulong)upBlock.Block.Length == upBlock.FileSize;
await SendPacketAsync(highwayHead, new BinaryPacket(upBlock.Block), server, isEnd);
var payload = await SendPacketAsync(highwayHead, new BinaryPacket(upBlock.Block), server, isEnd);
var (respHead, resp) = ParsePacket(payload);
return true;
return respHead.ErrorCode == 0;
}
private Task<BinaryPacket> SendPacketAsync(ReqDataHighwayHead head, BinaryPacket buffer, Uri server, bool end = true)
@ -109,8 +138,8 @@ internal class HighwayContext : ContextBase
var writer = new BinaryPacket()
.WriteByte(0x28) // packet start
.WriteInt((int)stream.Length)
.WriteInt((int)buffer.Length)
.WriteInt((int)stream.Length, false)
.WriteInt((int)buffer.Length, false)
.WriteBytes(stream.ToArray())
.WritePacket(buffer)
.WriteByte(0x29); // packet end
@ -122,8 +151,8 @@ internal class HighwayContext : ContextBase
{
if (packet.ReadByte() == 0x28)
{
int headLength = packet.ReadInt();
int bodyLength = packet.ReadInt();
int headLength = packet.ReadInt(false);
int bodyLength = packet.ReadInt(false);
var head = Serializer.Deserialize<RespDataHighwayHead>(packet.ReadBytes(headLength).AsSpan());
var body = packet.ReadPacket(bodyLength);
@ -142,24 +171,33 @@ internal class HighwayContext : ContextBase
Headers =
{
{ "Connection" , end ? "close" : "keep-alive" },
{ "Content-Type", "application/x-www-form-urlencoded" },
}
};
var response = await _client.SendAsync(request);
var data = await response.Content.ReadAsByteArrayAsync();
return new BinaryPacket(data);
}
private async Task FetchHttpConnUri()
{
var highwayUrlEvent = HighwayUrlEvent.Create();
var results = await Collection.Business.SendEvent(highwayUrlEvent);
if (results.Count != 0)
{
var result = (HighwayUrlEvent)results[0];
_highwayUrls = result.HighwayUrls;
_sigSession = result.SigSession;
}
}
private record UpBlock(
private record struct UpBlock(
int CommandId,
uint Uin,
int Sequence,
ulong FileSize,
ulong Offset,
byte[] FileMd5,
byte[] UpKey,
byte[] Block,
byte[]? ExtendInfo = null,
ulong Timestamp = 0,
LoginSigHead? LoginSig = null);
ulong Timestamp = 0);
}

View file

@ -7,6 +7,7 @@ using Lagrange.Core.Message.Entity;
using Lagrange.Core.Core.Event;
using Lagrange.Core.Core.Event.EventArg;
using Lagrange.Core.Core.Event.Protocol.Notify;
using Lagrange.Core.Utility.Extension;
namespace Lagrange.Core.Core.Context.Logic.Implementation;
@ -83,7 +84,8 @@ internal class MessagingLogic : LogicBase
switch (e)
{
case SendMessageEvent send: // resolve Uin to Uid
await ResolveChainUid(send.Chain);
await ResolveChainMetadata(send.Chain);
await ResolveChainUid(send.Chain);
break;
}
}
@ -144,13 +146,24 @@ internal class MessagingLogic : LogicBase
}
case ImageEntity image:
{
var imageUploadEvent = ImageUploadEvent.Create(image.ImageStream, Collection.Keystore.Uid ?? "");
string uid = await Collection.Business.CachingLogic.ResolveUid(chain.GroupUin, chain.FriendUin) ?? throw new Exception($"Failed to resolve Uid for Uin {chain.FriendUin}");
var imageUploadEvent = ImageUploadEvent.Create(image.ImageStream, uid);
var results = await Collection.Business.SendEvent(imageUploadEvent);
if (results.Count != 0)
{
var result = (ImageUploadEvent)results[0];
bool hwSuccess = await Collection.Highway.UploadSrcByStreamAsync(21, Collection.Keystore.Uin, image.ImageStream, imageUploadEvent.FileMd5.UnHex());
if (!hwSuccess)
{
Collection.Log.LogFatal(Tag, "Failed to upload image to highway");
continue;
}
image.CommonAdditional = result.CommonAdditional;
image.ImageInfo = result.ImageInfo;
var successEvent = ImageUploadSuccessEvent.Create(uid, result.CommonAdditional);
_ = await Collection.Business.SendEvent(successEvent);
}
break;
}

View file

@ -16,11 +16,13 @@ internal class ImageUploadEvent : ProtocolEvent
public string FileSha1 { get; }
public string Ticket { get; }
public byte[] CommonAdditional { get; } // Field 6 in Response
public NotOnlineImage ImageInfo { get; } // Field 8 in Response
protected ImageUploadEvent(Stream stream, string targetUid) : base(true)
private ImageUploadEvent(Stream stream, string targetUid) : base(true)
{
Stream = stream;
TargetUid = targetUid;
@ -29,14 +31,15 @@ internal class ImageUploadEvent : ProtocolEvent
FileSha1 = stream.Sha1(true);
}
protected ImageUploadEvent(int resultCode, byte[] commonAdditional, NotOnlineImage image) : base(resultCode)
private ImageUploadEvent(int resultCode, string ticket, byte[] commonAdditional, NotOnlineImage image) : base(resultCode)
{
Ticket = ticket;
CommonAdditional = commonAdditional;
ImageInfo = image;
}
public static ImageUploadEvent Create(Stream stream, string targetUid) => new(stream, targetUid);
public static ImageUploadEvent Result(int resultCode, byte[] commonAdditional, NotOnlineImage image)
=> new(resultCode, commonAdditional, image);
public static ImageUploadEvent Result(int resultCode, string ticket, byte[] commonAdditional, NotOnlineImage image)
=> new(resultCode, ticket, commonAdditional, image);
}

View file

@ -0,0 +1,24 @@
using Lagrange.Core.Core.Packets.Message.Element.Implementation;
using Lagrange.Core.Utility.Extension;
#pragma warning disable CS8618
namespace Lagrange.Core.Core.Event.Protocol.Message;
internal class ImageUploadSuccessEvent : ProtocolEvent
{
public string TargetUid { get; }
public byte[] CommonAdditional { get; } // Field 6 in Response
private ImageUploadSuccessEvent(string targetUid, byte[] commonAdditional) : base(true)
{
TargetUid = targetUid;
CommonAdditional = commonAdditional;
}
private ImageUploadSuccessEvent(int resultCode) : base(resultCode) { }
public static ImageUploadSuccessEvent Create(string targetUid, byte[] commonAdditional) => new(targetUid, commonAdditional);
public static ImageUploadSuccessEvent Result(int resultCode) => new(resultCode);
}

View file

@ -4,19 +4,20 @@ namespace Lagrange.Core.Core.Event.Protocol.System;
internal class HighwayUrlEvent : ProtocolEvent
{
private readonly Dictionary<uint, List<Uri>> _highwayUrls;
public Dictionary<uint, List<Uri>> HighwayUrls { get; }
public byte[] SigSession { get; }
private HighwayUrlEvent() : base(true) { }
private HighwayUrlEvent(int resultCode, Dictionary<uint, List<Uri>> highwayUrls) : base(resultCode)
private HighwayUrlEvent(int resultCode, byte[] sigSession, Dictionary<uint, List<Uri>> highwayUrls) : base(resultCode)
{
_highwayUrls = highwayUrls;
SigSession = sigSession;
HighwayUrls = highwayUrls;
}
public static HighwayUrlEvent Create() => new();
public static HighwayUrlEvent Result(int resultCode, Dictionary<uint, List<Uri>> highwayUrls)
{
return new HighwayUrlEvent(resultCode, highwayUrls);
}
public static HighwayUrlEvent Result(int resultCode, byte[] sigSession, Dictionary<uint, List<Uri>> highwayUrls) =>
new(resultCode, sigSession, highwayUrls);
}

View file

@ -9,5 +9,5 @@ namespace Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra;
[ProtoContract]
internal class ImageExtra
{
[ProtoMember(85)] public uint Field85 { get; set; } // 1
}

View file

@ -18,7 +18,7 @@ internal class OidbSvcTrpcTcp0x11C5_100
[ProtoContract]
internal class OidbSvcTrpcTcp0x11C5_100Body1
{
[ProtoMember(1)] public OidbTwoNumber Field1 { get; set; } // 1, 100
[ProtoMember(1)] public OidbTwoNumber Command { get; set; } // 1, 100
[ProtoMember(2)] public OidbSvcTrpcTcp0x11C5_100Body1Field2 Field2 { get; set; }

View file

@ -0,0 +1,46 @@
using Lagrange.Core.Core.Packets.Service.Oidb.Generics;
using ProtoBuf;
namespace Lagrange.Core.Core.Packets.Service.Oidb.Request;
// ReSharper disable InconsistentNaming
#pragma warning disable CS8618
[ProtoContract]
[OidbSvcTrpcTcp(0x11C5, 101)]
internal class OidbSvcTrpcTcp0x11C5_101
{
[ProtoMember(1)] public OidbSvcTrpcTcp0x11C5_101Body1 Body1 { get; set; }
[ProtoMember(6)] public byte[] CommandAdditional { get; set; }
}
[ProtoContract]
internal class OidbSvcTrpcTcp0x11C5_101Body1
{
[ProtoMember(1)] public OidbTwoNumber Command { get; set; } // 2, 101
[ProtoMember(2)] public OidbSvcTrpcTcp0x11C5_100Body1Field2 Field2 { get; set; }
[ProtoMember(3)] public OidbSvcTrpcTcp0x11C5_100Body1Field3 Field3 { get; set; }
}
[ProtoContract]
internal class OidbSvcTrpcTcp0x11C5_101Body1Field2
{
[ProtoMember(101)] public uint Field101 { get; set; } // 2
[ProtoMember(102)] public uint Field102 { get; set; } // 1
[ProtoMember(200)] public uint Field200 { get; set; } // 1
[ProtoMember(201)] public OidbSvcTrpcTcp0x11C5_100Body1Field2Field201 Field201 { get; set; }
}
[ProtoContract]
internal class OidbSvcTrpcTcp0x11C5_101Body1Field2Field201
{
[ProtoMember(1)] public uint Type { get; set; } // 2
[ProtoMember(2)] public string TargetUid { get; set; }
}

View file

@ -39,7 +39,7 @@ internal class ImageUploadService : BaseService<ImageUploadEvent>
{
Body1 = new OidbSvcTrpcTcp0x11C5_100Body1
{
Field1 = new OidbTwoNumber { Number1 = 1, Number2 = 100 },
Command = new OidbTwoNumber { Number1 = 1, Number2 = 100 },
Field2 = new OidbSvcTrpcTcp0x11C5_100Body1Field2
{
Field101 = 2,
@ -95,7 +95,7 @@ internal class ImageUploadService : BaseService<ImageUploadEvent>
var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix);
var packet = Serializer.Deserialize<OidbSvcTrpcTcpResponse<OidbSvcTrpcTcp0x11C5_100Response>>(payload.AsSpan());
output = ImageUploadEvent.Result(0, packet.Body.Data.CommonAdditional, packet.Body.Data.ImageInfo);
output = ImageUploadEvent.Result(0, packet.Body.Data.HighwayTicket, packet.Body.Data.CommonAdditional, packet.Body.Data.ImageInfo);
extraEvents = null;
return true;
}

View file

@ -0,0 +1,57 @@
using Lagrange.Core.Common;
using Lagrange.Core.Core.Event.Protocol;
using Lagrange.Core.Core.Event.Protocol.Message;
using Lagrange.Core.Core.Packets;
using Lagrange.Core.Core.Packets.Service.Oidb;
using Lagrange.Core.Core.Packets.Service.Oidb.Generics;
using Lagrange.Core.Core.Packets.Service.Oidb.Request;
using Lagrange.Core.Core.Service.Abstraction;
using Lagrange.Core.Utility.Binary;
using Lagrange.Core.Utility.Extension;
using ProtoBuf;
namespace Lagrange.Core.Core.Service.Message;
[EventSubscribe(typeof(ImageUploadSuccessEvent))]
[Service("OidbSvcTrpcTcp.0x11c5_101")]
internal class ImageUploadSuccessService : BaseService<ImageUploadSuccessEvent>
{
protected override bool Build(ImageUploadSuccessEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device,
out BinaryPacket output, out List<BinaryPacket>? extraPackets)
{
var packet = new OidbSvcTrpcTcpBase<OidbSvcTrpcTcp0x11C5_101>(new OidbSvcTrpcTcp0x11C5_101
{
Body1 = new OidbSvcTrpcTcp0x11C5_101Body1
{
Command = new OidbTwoNumber { Number1 = 2, Number2 = 101 },
Field2 = new OidbSvcTrpcTcp0x11C5_100Body1Field2
{
Field101 = 2,
Field102 = 1,
Field200 = 1,
Field201 = new OidbSvcTrpcTcp0x11C5_100Body1Field2Field201 { Type = 2, TargetUid = input.TargetUid }
},
Field3 = new OidbSvcTrpcTcp0x11C5_100Body1Field3 { Type = 2 }
},
CommandAdditional = input.CommonAdditional
});
using var stream = new MemoryStream();
Serializer.Serialize(stream, packet);
output = new BinaryPacket(stream);
extraPackets = null;
return true;
}
protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device,
out ImageUploadSuccessEvent output, out List<ProtocolEvent>? extraEvents)
{
var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix);
Console.WriteLine("ImageUploadSuccessService: " + payload.Hex());
output = ImageUploadSuccessEvent.Result(0);
extraEvents = null;
return true;
}
}

View file

@ -59,11 +59,11 @@ internal class HighwayUrlService : BaseService<HighwayUrlEvent>
foreach (var serverAddr in serverInfo.ServerAddrs)
{
var ip = BitConverter.GetBytes(serverAddr.Ip);
servers[type].Add(new Uri($"https://{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}:{serverAddr.Port}"));
servers[type].Add(new Uri($"https://{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}:{serverAddr.Port}/cgi-bin/httpconn?htcmd=0x6FF0087&uin={keystore.Uin}"));
}
}
output = HighwayUrlEvent.Result(0, servers);
output = HighwayUrlEvent.Result(0, packet.HttpConn.SigSession, servers);
extraEvents = null;
return true;
}

View file

@ -1,6 +1,8 @@
using System.Numerics;
using Lagrange.Core.Core.Packets.Message.Element;
using Lagrange.Core.Core.Packets.Message.Element.Implementation;
using Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra;
using ProtoBuf;
namespace Lagrange.Core.Message.Entity;
@ -44,6 +46,10 @@ public class ImageEntity : IMessageEntity
{
ImageStream?.Close();
ImageStream?.Dispose();
var stream = new MemoryStream();
var imageExtra = new ImageExtra { Field85 = 1 };
Serializer.Serialize(stream, imageExtra);
return new Elem[]
{
@ -59,6 +65,10 @@ public class ImageEntity : IMessageEntity
new()
{
NotOnlineImage = ImageInfo
},
new()
{
GeneralFlags = new GeneralFlags { PbReserve = stream.ToArray() }
}
};
}

View file

@ -156,7 +156,7 @@ internal class MessagePacker
{
C2C = chain.IsGroup ? null : new C2C
{
Uid = chain.Uid,
Uid = chain.FriendInfo?.Uid,
Uin = chain.FriendUin
},
Grp = !chain.IsGroup ? null : new Grp // for consistency of code so inverted condition