Skip to content
This repository was archived by the owner on May 27, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lagrange.Core/Internal/Packets/Login/WtLogin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public async Task<ReadOnlyMemory<byte>> BuildOicq09Android(string password)
tlvs.Tlv001();
tlvs.Tlv106Pwd(password);
tlvs.Tlv116();
tlvs.Tlv100Android((uint)AppInfo.SdkInfo.MainSigMap);
tlvs.Tlv100Android(16724722);

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tlv100Android was changed from using AppInfo.SdkInfo.MainSigMap to a magic number (16724722). This makes the login packet behavior opaque and ignores any future changes to the configured AppInfo/protocol; please either keep using AppInfo.SdkInfo.MainSigMap or define a well-named constant (with rationale) derived from Sig flags so it stays maintainable.

Suggested change
tlvs.Tlv100Android(16724722);
tlvs.Tlv100Android(AppInfo.SdkInfo.MainSigMap);

Copilot uses AI. Check for mistakes.
tlvs.Tlv107Android();
tlvs.Tlv142();
tlvs.Tlv144Report(false);
Expand Down
206 changes: 123 additions & 83 deletions Lagrange.Core/Internal/Packets/Message/Elem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,107 +114,147 @@ internal partial class TransElem
[ProtoPackable]
internal partial class CustomFace
{
[ProtoMember(1)] public byte[] Guid { get; set; }
[ProtoMember(2)] public string FilePath { get; set; }
[ProtoMember(3)] public string Shortcut { get; set; }
[ProtoMember(4)] public byte[] Buffer { get; set; }
[ProtoMember(5)] public byte[] Flag { get; set; }
[ProtoMember(6)] public byte[]? OldData { get; set; }
[ProtoMember(7)] public uint FileId { get; set; }
[ProtoMember(8)] public int? ServerIp { get; set; }
[ProtoMember(9)] public int? ServerPort { get; set; }
[ProtoMember(10)] public int FileType { get; set; }
[ProtoMember(11)] public byte[] Signature { get; set; }
[ProtoMember(12)] public int Useful { get; set; }
[ProtoMember(13)] public byte[] Md5 { get; set; }
[ProtoMember(1)] public string FileMd5Hex { get; set; } // MD5 十六进制字符串

[ProtoMember(2)] public uint FileSize { get; set; } // 文件大小

[ProtoMember(3)] public string DownloadToken { get; set; } // 下载 Token

[ProtoMember(4)] public byte[] Unknown4 { get; set; }

[ProtoMember(5)] public uint Unknown5 { get; set; } // 标志位

[ProtoMember(6)] public byte[] Unknown6 { get; set; }

[ProtoMember(7)] public byte[] FileMd5 { get; set; } // MD5 二进制

[ProtoMember(8)] public uint Height { get; set; } // 图片高度

[ProtoMember(9)] public uint Width { get; set; } // 图片宽度

[ProtoMember(10)] public string FileId { get; set; } // 文件 ID

[ProtoMember(11)] public byte[] Unknown11 { get; set; }

[ProtoMember(12)] public int Unknown12 { get; set; }

[ProtoMember(13)] public uint Unknown13 { get; set; }

[ProtoMember(14)] public string ThumbUrl { get; set; }

[ProtoMember(15)] public string BigUrl { get; set; }

[ProtoMember(16)] public string OrigUrl { get; set; }

[ProtoMember(17)] public int BizType { get; set; }
[ProtoMember(18)] public int RepeatIndex { get; set; }
[ProtoMember(19)] public int RepeatImage { get; set; }

[ProtoMember(18)] public int Unknown18 { get; set; }

[ProtoMember(19)] public int Unknown19 { get; set; }

[ProtoMember(20)] public int ImageType { get; set; }

[ProtoMember(21)] public int Index { get; set; }

[ProtoMember(22)] public int Width { get; set; }

[ProtoMember(23)] public int Height { get; set; }

[ProtoMember(24)] public int Source { get; set; }

[ProtoMember(25)] public uint Size { get; set; }

[ProtoMember(26)] public int Origin { get; set; }

[ProtoMember(27)] public int? ThumbWidth { get; set; }

[ProtoMember(28)] public int? ThumbHeight { get; set; }

[ProtoMember(29)] public int ShowLen { get; set; }

[ProtoMember(30)] public int DownloadLen { get; set; }

[ProtoMember(31)] public string? X400Url { get; set; }

[ProtoMember(32)] public int X400Width { get; set; }

[ProtoMember(33)] public int X400Height { get; set; }

[ProtoMember(34)] public PbReserve1? PbReserve { get; set; }

[ProtoPackable]
public partial class PbReserve1
{
[ProtoMember(1)] public int SubType { get; set; }
[ProtoMember(21)] public int Unknown21 { get; set; }

[ProtoMember(22)] public int Unknown22 { get; set; }

[ProtoMember(23)] public uint Unknown23 { get; set; } // 与 FileId 相同的 ID

[ProtoMember(24)] public int Unknown24 { get; set; }

[ProtoMember(25)] public uint Unknown25 { get; set; }

[ProtoMember(26)] public int Unknown26 { get; set; }

[ProtoMember(27)] public int Unknown27 { get; set; }

[ProtoMember(28)] public int Unknown28 { get; set; }

[ProtoMember(29)] public CustomFacePbReserve? PbReserve { get; set; } // 扩展信息 (含 rkey)

[ProtoMember(30)] public int Unknown30 { get; set; }

[ProtoMember(31)] public string Unknown31 { get; set; }

[ProtoMember(32)] public int Unknown32 { get; set; }

[ProtoMember(33)] public int Unknown33 { get; set; }

[ProtoMember(34)] public CustomFaceOldPbReserve? OldPbReserve { get; set; }
}

[ProtoPackable]
internal partial class CustomFacePbReserve
{
[ProtoMember(1)] public int Unknown1 { get; set; }

[ProtoMember(3)] public int Unknown3 { get; set; }

[ProtoMember(4)] public int Unknown4 { get; set; }

[ProtoMember(3)] public int Field3 { get; set; }
[ProtoMember(9)] public string Unknown9 { get; set; }

[ProtoMember(4)] public int Field4 { get; set; }
[ProtoMember(10)] public int Unknown10 { get; set; }

[ProtoMember(9)] public string Summary { get; set; }
[ProtoMember(12)] public string Unknown12 { get; set; }

[ProtoMember(10)] public int Field10 { get; set; }
[ProtoMember(18)] public string Unknown18 { get; set; }

[ProtoMember(21)] public PbReserve2 Field21 { get; set; }
[ProtoMember(19)] public string Unknown19 { get; set; }

[ProtoMember(31)] public string Field31 { get; set; }
}
[ProtoMember(21)] public CustomFacePbReserveInner? Inner { get; set; }

[ProtoPackable]
public partial class PbReserve2
{
[ProtoMember(1)] public int Field1 { get; set; }
[ProtoMember(30)] public string Rkey { get; set; } // 下载密钥
}

[ProtoPackable]
internal partial class CustomFacePbReserveInner
{
[ProtoMember(1)] public int Unknown1 { get; set; }

[ProtoMember(2)] public string Unknown2 { get; set; }

[ProtoMember(3)] public int Unknown3 { get; set; }

[ProtoMember(4)] public int Unknown4 { get; set; }

[ProtoMember(5)] public int Unknown5 { get; set; }

[ProtoMember(7)] public string Unknown7 { get; set; }
}

[ProtoPackable]
internal partial class CustomFaceOldPbReserve
{
[ProtoMember(1)] public int SubType { get; set; }

[ProtoMember(3)] public int Field3 { get; set; }

[ProtoMember(4)] public int Field4 { get; set; }

[ProtoMember(9)] public string Summary { get; set; }

[ProtoMember(10)] public int Field10 { get; set; }

[ProtoMember(21)] public CustomFaceOldPbReserve2 Field21 { get; set; }

[ProtoMember(31)] public string Field31 { get; set; }
}

[ProtoPackable]
internal partial class CustomFaceOldPbReserve2
{
[ProtoMember(1)] public int Field1 { get; set; }

[ProtoMember(2)] public string Field2 { get; set; }
[ProtoMember(2)] public string Field2 { get; set; }

[ProtoMember(3)] public int Field3 { get; set; }
[ProtoMember(3)] public int Field3 { get; set; }

[ProtoMember(4)] public int Field4 { get; set; }
[ProtoMember(4)] public int Field4 { get; set; }

[ProtoMember(5)] public int Field5 { get; set; }
[ProtoMember(5)] public int Field5 { get; set; }

[ProtoMember(7)] public string Md5Str { get; set; }
}
[ProtoMember(7)] public string Md5Str { get; set; }
}

[ProtoPackable]
Expand Down
2 changes: 0 additions & 2 deletions Lagrange.Milky/Configuration/SignerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ public class SignerConfiguration
{
public string? Base { get; set; }

public string? Version { get; set; }

public string? Token { get; set; }

public string? ProxyUrl { get; set; }
Expand Down
5 changes: 1 addition & 4 deletions Lagrange.Milky/Resources/appsettings.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
// Signer Base URL
"Base": "https://sign.lagrangecore.org/api/sign",

// Signer version
"Version": "30366",

// Signer token
// "Token": null

Expand All @@ -34,7 +31,7 @@
// Account uin
// If the Uin is inconsistent with the actual login account, quick login will not be possible
"Uin": 0,

// Account password
// Set to null to login via QrCode
// "Password": null,
Expand Down
6 changes: 1 addition & 5 deletions Lagrange.Milky/Resources/appsettings_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,12 @@
},
"Signer": {
"type": "object",
"required": ["Base", "Version"],
"required": ["Base"],
"properties": {
"Base": {
"type": "string",
"description": "Signer Base URL"
},
"Version": {
"type": "string",
"description": "Signer version"
},
"Token": {
"type": ["string", "null"],
"description": "Signer token"
Expand Down
27 changes: 14 additions & 13 deletions Lagrange.Milky/Utility/CaptchaResolver/OnlineCaptchaResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,22 @@ public class OnlineCaptchaResolver(ILogger<OnlineCaptchaResolver> logger, IOptio
{
token.ThrowIfCancellationRequested();

string queryUrl = string.Format(QueryUrl, _bot.BotUin);
var response = await _client.GetAsync(queryUrl, token);
if (response.StatusCode == HttpStatusCode.NotFound)
{
_logger.LogCaptchaWaiting();
continue;
}
// string queryUrl = string.Format(QueryUrl, _bot.BotUin);
// var response = await _client.GetAsync(queryUrl, token);
// if (response.StatusCode == HttpStatusCode.NotFound)
// {
// _logger.LogCaptchaWaiting();
// continue;
// }

if (!response.IsSuccessStatusCode)
{
throw new Exception($"Unexpected http status code({response.StatusCode})");
}
// if (!response.IsSuccessStatusCode)
// {
// throw new Exception($"Unexpected http status code({response.StatusCode})");
// }

string result = await response.Content.ReadAsStringAsync(token);
string? json = JsonNode.Parse(result)?["data"]?.GetValue<string>();
// string result = await response.Content.ReadAsStringAsync(token);
// string? json = JsonNode.Parse(result)?["data"]?.GetValue<string>();
var json = await Console.In.ReadLineAsync(token);
Comment on lines +31 to +46

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnlineCaptchaResolver no longer queries the captcha backend and instead blocks on Console.In.ReadLineAsync. Since this resolver is the default when UseOnlineCaptchaResolver=true, this effectively breaks captcha resolution in non-interactive/service environments and changes the feature semantics. Please restore the HTTP polling logic (or introduce a separate manual/console resolver behind config) rather than hard-coding console input here.

Copilot uses AI. Check for mistakes.
if (json == null) continue;

return (json.Split('|')[0], json.Split('|')[1]);
Comment on lines 47 to 49

Copilot AI Feb 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned json string is split twice and indexed at [0] and [1] without validating the delimiter/segment count. With the new console-driven input path, malformed or empty input will throw IndexOutOfRangeException and loop termination becomes unclear; parse once and validate the expected format before returning.

Suggested change
if (json == null) continue;
return (json.Split('|')[0], json.Split('|')[1]);
if (string.IsNullOrWhiteSpace(json))
{
_logger.LogWarning("Received empty captcha response from console, waiting for valid input.");
continue;
}
var parts = json.Split('|');
if (parts.Length < 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))
{
_logger.LogWarning("Invalid captcha response format: {Response}. Expected 'ticket|randstr'.", json);
continue;
}
return (parts[0], parts[1]);

Copilot uses AI. Check for mistakes.
Expand Down
1 change: 1 addition & 0 deletions Lagrange.Milky/Utility/JsonUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static partial class JsonUtility
[JsonSerializable(typeof(AndroidDebugXwidRequest))]
[JsonSerializable(typeof(AndroidSignerResponse<AndroidSecSignResponseData>))]
[JsonSerializable(typeof(AndroidSignerResponse<string>))]
[JsonSerializable(typeof(AndroidSignerResponse<BotAppInfo>))]

// === api ===
[JsonSerializable(typeof(ApiOkResult))]
Expand Down
Loading
Loading