Skip to content

Commit b21654f

Browse files
committed
[Core] Implemented SendFriendFile
1 parent c7d4eaf commit b21654f

15 files changed

Lines changed: 516 additions & 16 deletions

File tree

Lagrange.Core/Common/Interface/MessageExt.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ public static Task<BotMessage> SendFriendMessage(this BotContext context, Messag
1010

1111
public static Task<BotMessage> SendGroupMessage(this BotContext context, MessageChain chain, long groupUin)
1212
=> context.EventContext.GetLogic<MessagingLogic>().SendGroupMessage(chain, groupUin);
13+
14+
public static Task<bool> SendFriendFile(this BotContext context, long targetUin, Stream fileStream, string? fileName = null)
15+
=> context.EventContext.GetLogic<OperationLogic>().SendFriendFile(targetUin, fileStream, fileName);
1316
}

Lagrange.Core/Internal/Context/HighwayContext.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public void OnDisconnect() { }
7272
public void OnSocketError(Exception e, ReadOnlyMemory<byte> data = default)
7373
{
7474
_context.LogError(Tag, $"Highway Socket error: {e.Message}");
75+
if (e.StackTrace is { } stack) _context.LogDebug(Tag, stack);
7576
}
7677

7778
public async Task<bool> UploadFile(Stream stream, int commandId, ReadOnlyMemory<byte> extendInfo)
@@ -84,12 +85,14 @@ public async Task<bool> UploadFile(Stream stream, int commandId, ReadOnlyMemory<
8485
}
8586

8687
var client = _clients.Get();
88+
if (client.Connected) client.Disconnect();
89+
8790
string url = _url.Split(":")[0];
8891
ushort port = ushort.Parse(_url.Split(":")[1]);
8992
if (!await client.Connect(url, port)) return false;
9093

9194
var tasks = new List<Task<bool>>();
92-
bool result = false;
95+
bool result = true;
9396

9497
ulong fileSize = (ulong)stream.Length;
9598
ulong offset = 0;
@@ -161,7 +164,7 @@ public async Task<bool> UploadFile(Stream stream, int commandId, ReadOnlyMemory<
161164

162165

163166
tasks.Add(task);
164-
if (tasks.Count == _concurrent)
167+
if (tasks.Count == (commandId == 95 ? 1 : _concurrent))
165168
{
166169
var successBlocks = await Task.WhenAll(tasks);
167170
foreach (bool t in successBlocks) result &= t;
@@ -178,7 +181,6 @@ public async Task<bool> UploadFile(Stream stream, int commandId, ReadOnlyMemory<
178181
tasks.Clear();
179182
}
180183

181-
client.Disconnect();
182184
_clients.Return(client);
183185

184186
return result;

Lagrange.Core/Internal/Events/Message/SendMessageEvent.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Lagrange.Core.Common.Entity;
2+
using Lagrange.Core.Internal.Events.System;
13
using Lagrange.Core.Message;
24

35
namespace Lagrange.Core.Internal.Events.Message;
@@ -7,6 +9,19 @@ internal class SendMessageEventReq(BotMessage message) : ProtocolEvent
79
public BotMessage Message { get; } = message;
810
}
911

12+
internal class SendFriendFileEventReq(BotFriend friend, FileUploadEventReq request, FileUploadEventResp response, int clientSequence, uint sequence) : ProtocolEvent
13+
{
14+
public BotFriend Friend { get; } = friend;
15+
16+
public FileUploadEventReq Request { get; } = request;
17+
18+
public FileUploadEventResp Response { get; } = response;
19+
20+
public int ClientSequence { get; } = clientSequence;
21+
22+
public uint Sequence { get; } = sequence;
23+
}
24+
1025
internal class SendMessageEventResp(int result, long sendTime, int sequence) : ProtocolEvent
1126
{
1227
public int Result { get; } = result;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Lagrange.Core.Utility.Extension;
2+
3+
namespace Lagrange.Core.Internal.Events.System;
4+
5+
internal class FileUploadEventReq(string targetUid, Stream fileStream, string fileName) : ProtocolEvent
6+
{
7+
public string TargetUid { get; } = targetUid;
8+
9+
public Stream FileStream { get; } = fileStream;
10+
11+
public string FileName { get; } = fileName;
12+
13+
public byte[] FileMd5 { get; } = fileStream.Md5();
14+
15+
public byte[] FileSha1 { get; } = fileStream.Sha1();
16+
}
17+
18+
internal class FileUploadEventResp(bool isExist, string fileId, byte[] uploadKey, List<(string, uint)> rtpMediaPlatformUploadAddress, string crcMedia) : ProtocolEvent
19+
{
20+
public bool IsExist { get; } = isExist;
21+
22+
public string FileId { get; } = fileId;
23+
24+
public byte[] UploadKey { get; } = uploadKey;
25+
26+
public List<(string, uint)> RtpMediaPlatformUploadAddress { get; } = rtpMediaPlatformUploadAddress;
27+
28+
public string CrcMedia { get; } = crcMedia;
29+
}

Lagrange.Core/Internal/Logic/MessagingLogic.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ internal class MessagingLogic(BotContext context) : ILogic
1414

1515
public Task<BotMessage> Parse(MsgPush msg) => _packer.Parse(msg);
1616

17-
public ReadOnlyMemory<byte> Build(BotMessage message) => _packer.Build(message);
18-
1917
public async Task<BotMessage> SendGroupMessage(MessageChain chain, long groupUin)
2018
{
2119
var (group, member) = await context.CacheContext.ResolveMember(groupUin, context.BotUin) ?? throw new InvalidTargetException(context.BotUin, groupUin);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System.Buffers;
2+
using System.Security.Cryptography;
3+
using Lagrange.Core.Exceptions;
4+
using Lagrange.Core.Internal.Events.Message;
5+
using Lagrange.Core.Internal.Events.System;
6+
using Lagrange.Core.Internal.Packets.Service;
7+
using Lagrange.Core.Utility;
8+
using Lagrange.Core.Utility.Cryptography;
9+
10+
namespace Lagrange.Core.Internal.Logic;
11+
12+
internal class OperationLogic(BotContext context) : ILogic
13+
{
14+
public async Task<bool> SendFriendFile(long targetUin, Stream fileStream, string? fileName)
15+
{
16+
if (fileName == null)
17+
{
18+
if (fileStream is FileStream file)
19+
{
20+
fileName = Path.GetFileName(file.Name);
21+
}
22+
else
23+
{
24+
Span<byte> bytes = stackalloc byte[16];
25+
Random.Shared.NextBytes(bytes);
26+
fileName = Convert.ToHexString(bytes);
27+
}
28+
}
29+
30+
var friend = await context.CacheContext.ResolveFriend(targetUin) ?? throw new InvalidTargetException(targetUin);
31+
var request = new FileUploadEventReq(friend.Uid, fileStream, fileName);
32+
var result = await context.EventContext.SendEvent<FileUploadEventResp>(request);
33+
34+
var buffer = ArrayPool<byte>.Shared.Rent(10 * 1024 * 1024);
35+
int payload = request.FileStream.Read(buffer);
36+
var md510m = MD5.HashData(buffer[..payload]);
37+
ArrayPool<byte>.Shared.Return(buffer);
38+
request.FileStream.Seek(0, SeekOrigin.Begin);
39+
40+
if (!result.IsExist)
41+
{
42+
var ext = new FileUploadExt
43+
{
44+
Unknown1 = 100,
45+
Unknown2 = 1,
46+
Entry = new FileUploadEntry
47+
{
48+
BusiBuff = new ExcitingBusiInfo { SenderUin = context.Keystore.Uin },
49+
FileEntry = new ExcitingFileEntry
50+
{
51+
FileSize = fileStream.Length,
52+
Md5 = request.FileMd5,
53+
CheckKey = request.FileSha1,
54+
Md510M = md510m,
55+
Sha3 = TriSha1Provider.CalculateTriSha1(fileStream),
56+
FileId = result.FileId,
57+
UploadKey = result.UploadKey
58+
},
59+
ClientInfo = new ExcitingClientInfo
60+
{
61+
ClientType = 3,
62+
AppId = "100",
63+
TerminalType = 3,
64+
ClientVer = "1.1.1",
65+
Unknown = 4
66+
},
67+
FileNameInfo = new ExcitingFileNameInfo { FileName = fileName },
68+
Host = new ExcitingHostConfig
69+
{
70+
Hosts = result.RtpMediaPlatformUploadAddress.Select(x => new ExcitingHostInfo
71+
{
72+
Url = new ExcitingUrlInfo { Unknown = 1, Host = x.Item1 },
73+
Port = x.Item2
74+
}).ToList()
75+
}
76+
},
77+
Unknown200 = 1
78+
};
79+
80+
bool success = await context.HighwayContext.UploadFile(fileStream, 95, ProtoHelper.Serialize(ext));
81+
if (!success) return false;
82+
}
83+
84+
int sequence = Random.Shared.Next(10000, 99999);
85+
uint random = (uint)Random.Shared.Next();
86+
var sendResult = await context.EventContext.SendEvent<SendMessageEventResp>(new SendFriendFileEventReq(friend, request, result, sequence, random));
87+
if (sendResult.Result != 0) throw new OperationException(sendResult.Result);
88+
89+
return true;
90+
}
91+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Lagrange.Proto;
2+
3+
namespace Lagrange.Core.Internal.Packets.Message;
4+
5+
[ProtoPackable]
6+
internal partial class FileExtra
7+
{
8+
[ProtoMember(1)] public NotOnlineFile? File { get; set; }
9+
}

Lagrange.Core/Internal/Packets/Message/NTMessageCommon.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ internal partial class MessageBody
103103
{
104104
[ProtoMember(1)] public RichText RichText { get; set; } = new();
105105

106-
[ProtoMember(2)] public byte[]? MsgContent { get; set; }
106+
[ProtoMember(2)] public ReadOnlyMemory<byte> MsgContent { get; set; }
107107

108108
[ProtoMember(3)] public byte[]? MsgEncryptContent { get; set; }
109109
}
@@ -155,11 +155,11 @@ internal partial class NotOnlineFile
155155

156156
[ProtoMember(2)] public byte[]? Sig { get; set; }
157157

158-
[ProtoMember(3)] public byte[]? FileUuid { get; set; }
158+
[ProtoMember(3)] public string FileUuid { get; set; }
159159

160160
[ProtoMember(4)] public byte[]? FileMd5 { get; set; }
161161

162-
[ProtoMember(5)] public byte[]? FileName { get; set; }
162+
[ProtoMember(5)] public string FileName { get; set; }
163163

164164
[ProtoMember(6)] public ulong FileSize { get; set; }
165165

Lagrange.Core/Internal/Packets/Message/NTSendMessage.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ internal partial class SendRoutingHead // EncodeSendMsgReqRoutingHead
5151
[ProtoMember(1)] public C2C C2C { get; set; }
5252

5353
[ProtoMember(2)] public Grp Group { get; set; }
54+
55+
[ProtoMember(15)] public Trans0X211 Trans0X211 { get; set; }
5456
}
5557

5658
[ProtoPackable]
@@ -65,4 +67,14 @@ internal partial class C2C
6567
internal partial class Grp
6668
{
6769
[ProtoMember(1)] public long GroupUin { get; set; } // group_uin
70+
}
71+
72+
[ProtoPackable]
73+
internal partial class Trans0X211
74+
{
75+
[ProtoMember(1)] public long ToUin { get; set; }
76+
77+
[ProtoMember(2)] public uint CcCmd { get; set; }
78+
79+
[ProtoMember(8)] public string Uid { get; set; }
6880
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using Lagrange.Proto;
2+
3+
namespace Lagrange.Core.Internal.Packets.Service;
4+
5+
#pragma warning disable CS8618
6+
7+
[ProtoPackable]
8+
internal partial class FileUploadExt
9+
{
10+
[ProtoMember(1)] public int Unknown1 { get; set; }
11+
12+
[ProtoMember(2)] public int Unknown2 { get; set; }
13+
14+
[ProtoMember(3)] public int Unknown3 { get; set; }
15+
16+
[ProtoMember(100)] public FileUploadEntry Entry { get; set; }
17+
18+
[ProtoMember(200)] public int Unknown200 { get; set; }
19+
}
20+
21+
[ProtoPackable]
22+
internal partial class FileUploadEntry
23+
{
24+
[ProtoMember(100)] public ExcitingBusiInfo BusiBuff { get; set; }
25+
26+
[ProtoMember(200)] public ExcitingFileEntry FileEntry { get; set; }
27+
28+
[ProtoMember(300)] public ExcitingClientInfo ClientInfo { get; set; }
29+
30+
[ProtoMember(400)] public ExcitingFileNameInfo FileNameInfo { get; set; }
31+
32+
[ProtoMember(500)] public ExcitingHostConfig Host { get; set; }
33+
}
34+
35+
[ProtoPackable]
36+
internal partial class ExcitingBusiInfo
37+
{
38+
[ProtoMember(1)] public int BusId { get; set; }
39+
40+
[ProtoMember(100)] public long SenderUin { get; set; }
41+
42+
[ProtoMember(200)] public long ReceiverUin { get; set; }
43+
44+
[ProtoMember(400)] public long GroupCode { get; set; }
45+
}
46+
47+
[ProtoPackable]
48+
internal partial class ExcitingFileEntry
49+
{
50+
[ProtoMember(100)] public long FileSize { get; set; }
51+
52+
[ProtoMember(200)] public byte[] Md5 { get; set; }
53+
54+
[ProtoMember(300)] public byte[] CheckKey { get; set; }
55+
56+
[ProtoMember(400)] public byte[] Md510M { get; set; }
57+
58+
[ProtoMember(500)] public byte[] Sha3 { get; set; }
59+
60+
[ProtoMember(600)] public string FileId { get; set; }
61+
62+
[ProtoMember(700)] public byte[] UploadKey { get; set; }
63+
}
64+
65+
[ProtoPackable]
66+
internal partial class ExcitingClientInfo
67+
{
68+
[ProtoMember(100)] public int ClientType { get; set; }
69+
70+
[ProtoMember(200)] public string AppId { get; set; }
71+
72+
[ProtoMember(300)] public int TerminalType { get; set; }
73+
74+
[ProtoMember(400)] public string ClientVer { get; set; }
75+
76+
[ProtoMember(600)] public int Unknown { get; set; }
77+
}
78+
79+
[ProtoPackable]
80+
internal partial class ExcitingFileNameInfo
81+
{
82+
[ProtoMember(100)] public string FileName { get; set; }
83+
}
84+
85+
[ProtoPackable]
86+
internal partial class ExcitingHostConfig
87+
{
88+
[ProtoMember(200)] public List<ExcitingHostInfo> Hosts { get; set; }
89+
}
90+
91+
[ProtoPackable]
92+
internal partial class ExcitingHostInfo
93+
{
94+
[ProtoMember(1)] public ExcitingUrlInfo Url { get; set; }
95+
96+
[ProtoMember(2)] public uint Port { get; set; }
97+
}
98+
99+
[ProtoPackable]
100+
internal partial class ExcitingUrlInfo
101+
{
102+
[ProtoMember(1)] public int Unknown { get; set; }
103+
104+
[ProtoMember(2)] public string Host { get; set; }
105+
}

0 commit comments

Comments
 (0)