submodule对gitea不管用,所以直接拉了一份拉格兰
This commit is contained in:
168
Lagrange.Core/Lagrange.OneBot/Utility/AudioHelper.cs
Normal file
168
Lagrange.Core/Lagrange.OneBot/Utility/AudioHelper.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public static class AudioHelper
|
||||
{
|
||||
public enum AudioFormat
|
||||
{
|
||||
Unknown,
|
||||
Wav,
|
||||
Mp3,
|
||||
SilkV3,
|
||||
TenSilkV3,
|
||||
Amr,
|
||||
Ogg,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect audio type
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("public static AudioFormat DetectAudio(byte[] data)")]
|
||||
public static bool DetectAudio(byte[] data, out AudioFormat type)
|
||||
{
|
||||
return (type = DetectAudio(data)) != AudioFormat.Unknown;
|
||||
}
|
||||
|
||||
// WAV
|
||||
// R I F F W A V E f m t
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |52|49|46|46| | | | |57|41|56|45|66|6D|74|
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// 0 4 8 12 15
|
||||
private const ulong WAV_HEAD_LOWER = 0x5249464600000000UL;
|
||||
private const ulong WAV_HEAD_UPPER = 0x57415645666D7400UL;
|
||||
|
||||
// TENSILKV3
|
||||
// # ! S I L K
|
||||
// +--+--+--+--+--+--+--+
|
||||
// |02|23|21|53|49|4C|4B|
|
||||
// +--+--+--+--+--+--+--+
|
||||
// 0 4 7
|
||||
private const ulong TENSILKV3_HEAD_LOWER = 0x02232153494C4B00UL;
|
||||
|
||||
// AMR
|
||||
// # ! A M R
|
||||
// +--+--+--+--+--+
|
||||
// |23|21|41|4D|52|
|
||||
// +--+--+--+--+--+
|
||||
// 0 4 5
|
||||
private const ulong AMR_HEAD_LOWER = 0x2321414D52000000UL;
|
||||
|
||||
// SILKV3
|
||||
// # ! S I L K
|
||||
// +--+--+--+--+--+--+
|
||||
// |23|21|53|49|4C|4B|
|
||||
// +--+--+--+--+--+--+
|
||||
// 0 4 6
|
||||
private const ulong SILKV3_HEAD_LOWER = 0x232153494C4B0000UL;
|
||||
|
||||
// Ogg
|
||||
// O g g S
|
||||
// +--+--+--+--+
|
||||
// |4F|67|67|53|
|
||||
// +--+--+--+--+
|
||||
// 0 4
|
||||
private const ulong OGG_HEAD_LOWER = 0x4F67675300000000UL;
|
||||
|
||||
// MP3 ID3
|
||||
// I D 3
|
||||
// +--+--+--+
|
||||
// |49|44|33|
|
||||
// +--+--+--+
|
||||
// 0 3
|
||||
private const ulong MP3ID3_HEAD_LOWER = 0x4944330000000000UL;
|
||||
|
||||
// MP3 no ID3
|
||||
// ÿ û
|
||||
// +--+--+
|
||||
// |FF|F2|
|
||||
// +--+--+
|
||||
// 0 2
|
||||
private const ulong MP3FFF2_HEAD_LOWER = 0xFFF2000000000000UL;
|
||||
|
||||
// MP3 no ID3
|
||||
// ÿ û
|
||||
// +--+--+
|
||||
// |FF|F3|
|
||||
// +--+--+
|
||||
// 0 2
|
||||
private const ulong MP3FFF3_HEAD_LOWER = 0xFFF3000000000000UL;
|
||||
|
||||
// MP3 no ID3
|
||||
// ÿ û
|
||||
// +--+--+
|
||||
// |FF|FB|
|
||||
// +--+--+
|
||||
// 0 2
|
||||
private const ulong MP3FFFB_HEAD_LOWER = 0xFFFB000000000000UL;
|
||||
|
||||
/// <summary>
|
||||
/// Detect audio type
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static AudioFormat DetectAudio(byte[] data)
|
||||
{
|
||||
ulong lower = BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(0, sizeof(ulong)));
|
||||
ulong upper = BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(8, sizeof(ulong)));
|
||||
|
||||
if ((lower & WAV_HEAD_LOWER) == WAV_HEAD_LOWER &&
|
||||
(upper & WAV_HEAD_UPPER) == WAV_HEAD_UPPER
|
||||
) return AudioFormat.Wav;
|
||||
|
||||
if ((lower & TENSILKV3_HEAD_LOWER) == TENSILKV3_HEAD_LOWER) return AudioFormat.TenSilkV3;
|
||||
|
||||
if ((lower & AMR_HEAD_LOWER) == AMR_HEAD_LOWER) return AudioFormat.Amr;
|
||||
|
||||
if ((lower & SILKV3_HEAD_LOWER) == SILKV3_HEAD_LOWER) return AudioFormat.SilkV3;
|
||||
|
||||
if ((lower & OGG_HEAD_LOWER) == OGG_HEAD_LOWER) return AudioFormat.Ogg;
|
||||
|
||||
if ((lower & MP3ID3_HEAD_LOWER) == MP3ID3_HEAD_LOWER) return AudioFormat.Mp3;
|
||||
|
||||
if ((lower & MP3FFF2_HEAD_LOWER) == MP3FFF2_HEAD_LOWER) return AudioFormat.Mp3;
|
||||
|
||||
if ((lower & MP3FFF3_HEAD_LOWER) == MP3FFF3_HEAD_LOWER) return AudioFormat.Mp3;
|
||||
|
||||
if ((lower & MP3FFFB_HEAD_LOWER) == MP3FFFB_HEAD_LOWER) return AudioFormat.Mp3;
|
||||
|
||||
return AudioFormat.Unknown;
|
||||
}
|
||||
|
||||
private static readonly byte[] _silk_end = [0xFF, 0xFF];
|
||||
|
||||
public static double GetSilkTime(byte[] data, int offset = 0)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 9 + offset; i < data.Length && !data.AsSpan(i, 2).SequenceEqual(_silk_end); i += 2 + BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(i, 2)))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
// Because the silk encoder encodes each 20ms sample as a block,
|
||||
// So that we can calculate the total time easily.
|
||||
return count * 0.02;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get silk total time
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetTenSilkTime(byte[] data)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 10; i < data.Length; i += 2 + BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(i, 2)))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
// Because the silk encoder encodes each 20ms sample as a block,
|
||||
// So that we can calculate the total time easily.
|
||||
return count * 0.02;
|
||||
}
|
||||
}
|
||||
10
Lagrange.Core/Lagrange.OneBot/Utility/DateTimeExt.cs
Normal file
10
Lagrange.Core/Lagrange.OneBot/Utility/DateTimeExt.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public static class DateTimeExt
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ToTimestamp(this DateTime time) =>
|
||||
(uint)(time - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||||
}
|
||||
29
Lagrange.Core/Lagrange.OneBot/Utility/FallbackAsync.cs
Normal file
29
Lagrange.Core/Lagrange.OneBot/Utility/FallbackAsync.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Lagrange.OneBot.Utility.Fallbacks;
|
||||
|
||||
public class FallbackAsync
|
||||
{
|
||||
private readonly List<Func<CancellationToken, Task<bool>>> _executors = [];
|
||||
|
||||
internal static FallbackAsync Create()
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
public FallbackAsync Add(Func<CancellationToken, Task<bool>> executor)
|
||||
{
|
||||
_executors.Add(executor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<bool> ExecuteAsync(CancellationToken token = default)
|
||||
{
|
||||
foreach (var executor in _executors)
|
||||
{
|
||||
if (await executor(token)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
27
Lagrange.Core/Lagrange.OneBot/Utility/FallbackAsyncT.cs
Normal file
27
Lagrange.Core/Lagrange.OneBot/Utility/FallbackAsyncT.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace Lagrange.OneBot.Utility.Fallbacks;
|
||||
|
||||
public class FallbackAsync<T>
|
||||
{
|
||||
private readonly List<Func<CancellationToken, Task<T?>>> _executors = [];
|
||||
|
||||
internal static FallbackAsync<T> Create()
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
public FallbackAsync<T> Add(Func<CancellationToken, Task<T?>> executor)
|
||||
{
|
||||
_executors.Add(executor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<T> ExecuteAsync(Func<CancellationToken, Task<T>> @default, CancellationToken token = default)
|
||||
{
|
||||
foreach (var executor in _executors)
|
||||
{
|
||||
var result = await executor(token);
|
||||
if (result != null) return result;
|
||||
}
|
||||
return await @default(token);
|
||||
}
|
||||
}
|
||||
49
Lagrange.Core/Lagrange.OneBot/Utility/LiteDbUtility.cs
Normal file
49
Lagrange.Core/Lagrange.OneBot/Utility/LiteDbUtility.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Lagrange.Core.Message;
|
||||
using Lagrange.Core.Message.Entity;
|
||||
using LiteDB;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public static class LiteDbUtility
|
||||
{
|
||||
public static BsonValue IMessageEntitySerialize(IMessageEntity entity)
|
||||
{
|
||||
var type = entity.GetType();
|
||||
var result = BsonMapper.Global.Serialize(type, entity);
|
||||
result["_type"] = new BsonValue(DefaultTypeNameBinder.Instance.GetName(type));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IMessageEntity IMessageEntityDeserialize(BsonValue bson)
|
||||
{
|
||||
if (!bson.IsDocument) throw new Exception("bson not BsonDocument");
|
||||
|
||||
var doc = bson.AsDocument;
|
||||
if (!doc.TryGetValue("_type", out var typeBson) || !typeBson.IsString)
|
||||
{
|
||||
throw new Exception("no `_type` or `_type` not string");
|
||||
}
|
||||
|
||||
var type = DefaultTypeNameBinder.Instance.GetType(typeBson.AsString);
|
||||
|
||||
if (type == typeof(MarkdownEntity)) return MarkdownEntityDeserialize(doc);
|
||||
|
||||
return (IMessageEntity)BsonMapper.Global.Deserialize(type, bson);
|
||||
}
|
||||
|
||||
private static MarkdownEntity MarkdownEntityDeserialize(BsonDocument doc)
|
||||
{
|
||||
if (!doc.TryGetValue("Data", out var dataBson) || !dataBson.IsDocument)
|
||||
{
|
||||
throw new Exception("no `Data` or `Data` not document");
|
||||
}
|
||||
|
||||
var dataDocument = dataBson.AsDocument;
|
||||
if (!dataDocument.TryGetValue("Content", out var contentBson) || !contentBson.IsString)
|
||||
{
|
||||
throw new InvalidCastException("no `Data.Content` or `Data.Content` not string");
|
||||
}
|
||||
|
||||
return new(new MarkdownData() { Content = contentBson.AsString });
|
||||
}
|
||||
}
|
||||
77
Lagrange.Core/Lagrange.OneBot/Utility/MusicSigner.cs
Normal file
77
Lagrange.Core/Lagrange.OneBot/Utility/MusicSigner.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using Lagrange.OneBot.Message.Entity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public class MusicSigner
|
||||
{
|
||||
private static string? _signServer;
|
||||
|
||||
private readonly static HttpClient _client = new();
|
||||
|
||||
public MusicSigner(IConfiguration config, ILogger<MusicSigner> logger)
|
||||
{
|
||||
_signServer = config["MusicSignServerUrl"] ?? "";
|
||||
|
||||
if (string.IsNullOrEmpty(_signServer))
|
||||
{
|
||||
logger.LogWarning("MusicSignServer is not available, sign may be failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("MusicSignServer Service is successfully established");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string? Sign(MusicSegment musicSegment)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_signServer)) return null;
|
||||
|
||||
JsonObject payload;
|
||||
if (musicSegment.Type != null)
|
||||
{
|
||||
payload = new JsonObject()
|
||||
{
|
||||
{ "type" , musicSegment.Type },
|
||||
{ "url" , musicSegment.Url },
|
||||
{ "audio" , musicSegment.Audio },
|
||||
{ "title" , musicSegment.Title },
|
||||
{ "image" , musicSegment.Image },
|
||||
{ "singer" , musicSegment.Content },
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = new JsonObject()
|
||||
{
|
||||
{ "url" , musicSegment.Url },
|
||||
{ "audio" , musicSegment.Audio },
|
||||
{ "title" , musicSegment.Title },
|
||||
{ "image" , musicSegment.Image },
|
||||
{ "singer" , musicSegment.Content },
|
||||
{ "appid", ulong.Parse(musicSegment.Appid) },
|
||||
{ "sign", musicSegment.Sign },
|
||||
{ "package_name", musicSegment.PackageName },
|
||||
};
|
||||
}
|
||||
|
||||
if (musicSegment.Id.Length > 0)
|
||||
{
|
||||
payload.Add("id", musicSegment.Id);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var message = _client.PostAsJsonAsync(_signServer, payload).Result;
|
||||
return message.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Lagrange.Core/Lagrange.OneBot/Utility/OneBotExt.cs
Normal file
16
Lagrange.Core/Lagrange.OneBot/Utility/OneBotExt.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using static Lagrange.Core.Common.Entity.BotUserInfo;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public static class GenderExt
|
||||
{
|
||||
public static string ToOneBotString(this GenderInfo info)
|
||||
{
|
||||
return info switch
|
||||
{
|
||||
GenderInfo.Male => "male",
|
||||
GenderInfo.Female => "female",
|
||||
GenderInfo.Unset or _ => "unknown"
|
||||
};
|
||||
}
|
||||
}
|
||||
148
Lagrange.Core/Lagrange.OneBot/Utility/OneBotSigner.cs
Normal file
148
Lagrange.Core/Lagrange.OneBot/Utility/OneBotSigner.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Lagrange.Core.Common;
|
||||
using Lagrange.Core.Utility.Sign;
|
||||
using Lagrange.OneBot.Utility.Fallbacks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public class OneBotSigner : SignProvider
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly ILogger<OneBotSigner> _logger;
|
||||
|
||||
private const string Url = "https://sign.lagrangecore.org/api/sign/30366";
|
||||
|
||||
private readonly string? _signServer;
|
||||
|
||||
private readonly HttpClient _client;
|
||||
|
||||
private readonly BotAppInfo? _info;
|
||||
|
||||
private readonly string platform;
|
||||
|
||||
private readonly string version;
|
||||
|
||||
public OneBotSigner(IConfiguration config, ILogger<OneBotSigner> logger)
|
||||
{
|
||||
_configuration = config;
|
||||
_logger = logger;
|
||||
|
||||
_signServer = string.IsNullOrEmpty(config["SignServerUrl"]) ? Url : config["SignServerUrl"];
|
||||
string? signProxyUrl = config["SignProxyUrl"]; // Only support HTTP proxy
|
||||
|
||||
_client = new HttpClient(handler: new HttpClientHandler
|
||||
{
|
||||
Proxy = string.IsNullOrEmpty(signProxyUrl) ? null : new WebProxy()
|
||||
{
|
||||
Address = new Uri(signProxyUrl),
|
||||
BypassProxyOnLocal = false,
|
||||
UseDefaultCredentials = false,
|
||||
},
|
||||
}, disposeHandler: true);
|
||||
|
||||
if (string.IsNullOrEmpty(_signServer)) logger.LogWarning("Signature Service is not available, login may be failed");
|
||||
|
||||
_info ??= GetAppInfo();
|
||||
platform = _info.Os switch
|
||||
{
|
||||
"Windows" => "Windows",
|
||||
"Mac" => "MacOs",
|
||||
"Linux" => "Linux",
|
||||
_ => "Unknown"
|
||||
};
|
||||
version = _info.CurrentVersion;
|
||||
}
|
||||
|
||||
public override byte[]? Sign(string cmd, uint seq, byte[] body, [UnscopedRef] out byte[]? e, [UnscopedRef] out string? t)
|
||||
{
|
||||
e = null;
|
||||
t = null;
|
||||
|
||||
if (!WhiteListCommand.Contains(cmd)) return null;
|
||||
if (_signServer == null) throw new Exception("Sign server is not configured");
|
||||
|
||||
using var request = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri(_signServer),
|
||||
Content = JsonContent.Create(new JsonObject
|
||||
{
|
||||
{ "cmd", cmd },
|
||||
{ "seq", seq },
|
||||
{ "src", Convert.ToHexString(body) }
|
||||
})
|
||||
};
|
||||
|
||||
using var message = _client.Send(request);
|
||||
if (message.StatusCode != HttpStatusCode.OK) throw new Exception($"Signer server returned a {message.StatusCode}");
|
||||
var json = JsonDocument.Parse(message.Content.ReadAsStream()).RootElement;
|
||||
|
||||
if (json.TryGetProperty("platform", out JsonElement platformJson))
|
||||
{
|
||||
if (platformJson.GetString() != platform) throw new Exception("Signer platform mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Signer platform miss");
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("version", out JsonElement versionJson))
|
||||
{
|
||||
if (versionJson.GetString() != version) throw new Exception("Signer version mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Signer version miss");
|
||||
}
|
||||
|
||||
var valueJson = json.GetProperty("value");
|
||||
var extraJson = valueJson.GetProperty("extra");
|
||||
var tokenJson = valueJson.GetProperty("token");
|
||||
var signJson = valueJson.GetProperty("sign");
|
||||
|
||||
string? token = tokenJson.GetString();
|
||||
string? extra = extraJson.GetString();
|
||||
e = extra != null ? Convert.FromHexString(extra) : [];
|
||||
t = token != null ? Encoding.UTF8.GetString(Convert.FromHexString(token)) : "";
|
||||
string sign = signJson.GetString() ?? throw new Exception("Signer server returned an empty sign");
|
||||
return Convert.FromHexString(sign);
|
||||
}
|
||||
|
||||
public BotAppInfo GetAppInfo()
|
||||
{
|
||||
if (_info != null) return _info;
|
||||
|
||||
return FallbackAsync<BotAppInfo>.Create()
|
||||
.Add(async token =>
|
||||
{
|
||||
try { return await _client.GetFromJsonAsync<BotAppInfo>($"{_signServer}/appinfo", token); }
|
||||
catch { return null; }
|
||||
})
|
||||
.Add(token =>
|
||||
{
|
||||
string path = _configuration["ConfigPath:AppInfo"] ?? "appinfo.json";
|
||||
|
||||
if (!File.Exists(path)) return Task.FromResult(null as BotAppInfo);
|
||||
|
||||
try { return Task.FromResult(JsonSerializer.Deserialize<BotAppInfo>(File.ReadAllText(path))); }
|
||||
catch { return Task.FromResult(null as BotAppInfo); }
|
||||
})
|
||||
.ExecuteAsync(token => Task.FromResult(
|
||||
BotAppInfo.ProtocolToAppInfo[_configuration["Account:Protocol"] switch
|
||||
{
|
||||
"Windows" => Protocols.Windows,
|
||||
"MacOs" => Protocols.MacOs,
|
||||
_ => Protocols.Linux,
|
||||
}]
|
||||
))
|
||||
.Result;
|
||||
}
|
||||
}
|
||||
49
Lagrange.Core/Lagrange.OneBot/Utility/QrCodeHelper.cs
Normal file
49
Lagrange.Core/Lagrange.OneBot/Utility/QrCodeHelper.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Net.Codecrete.QrCodeGenerator;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
internal static class QrCodeHelper
|
||||
{
|
||||
// This part of the code is from "https://github.com/eric2788/Lagrange.Core/blob/fd20a5aec81cacd56d60f3130cf057461300fd3f/Lagrange.OneBot/Utility/QrCodeHelper.cs#L30C52-L30C52"
|
||||
// Thanks to "https://github.com/eric2788"
|
||||
internal static void Output(string text, bool compatibilityMode)
|
||||
{
|
||||
var segments = QrSegment.MakeSegments(text);
|
||||
var qrCode = QrCode.EncodeSegments(segments, QrCode.Ecc.Low);
|
||||
|
||||
var (bottomHalfBlock, topHalfBlock, emptyBlock, fullBlock) = compatibilityMode ? (".", "^", " ", "@") : ("▄", "▀", " ", "█");
|
||||
|
||||
for (var y = 0; y < qrCode.Size + 2; y += 2)
|
||||
{
|
||||
for (var x = 0; x < qrCode.Size + 2; ++x)
|
||||
{
|
||||
var foregroundBlack = qrCode.GetModule(x - 1, y - 1);
|
||||
var backgroundBlack = qrCode.GetModule(x - 1, y) || y > qrCode.Size;
|
||||
|
||||
if (foregroundBlack && !backgroundBlack)
|
||||
{
|
||||
Console.Write(bottomHalfBlock);
|
||||
}
|
||||
else if (!foregroundBlack && backgroundBlack)
|
||||
{
|
||||
Console.Write(topHalfBlock);
|
||||
}
|
||||
else if (foregroundBlack && backgroundBlack)
|
||||
{
|
||||
Console.Write(emptyBlock);
|
||||
|
||||
}
|
||||
else if (!foregroundBlack && !backgroundBlack)
|
||||
{
|
||||
Console.Write(fullBlock);
|
||||
}
|
||||
}
|
||||
Console.Write("\n");
|
||||
}
|
||||
|
||||
if (compatibilityMode)
|
||||
{
|
||||
Console.WriteLine("Please scan this QR code from a distance with your smart phone.\nScanning may fail if you are too close.");
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Lagrange.Core/Lagrange.OneBot/Utility/SongHelper.cs
Normal file
23
Lagrange.Core/Lagrange.OneBot/Utility/SongHelper.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Lagrange.Core.Utility.Network;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public class SongHelper
|
||||
{
|
||||
public async Task<dynamic> GetQMusicInfo(string id)
|
||||
{
|
||||
const string template = """https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"songinfo":{"method":"get_song_detail_yqq","param":{"song_type":0,"song_mid":"","song_id":*},"module":"music.pf_song_detail_svr"}}""";
|
||||
string url = template.Replace("*", id);
|
||||
string response = await Http.GetAsync(url);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetNetEaseMusicInfo(string id)
|
||||
{
|
||||
string url = $"http://music.163.com/api/song/detail/?id={id}&ids=[{id}]";
|
||||
string response = await Http.GetAsync(url);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
22
Lagrange.Core/Lagrange.OneBot/Utility/Vector2Mapper.cs
Normal file
22
Lagrange.Core/Lagrange.OneBot/Utility/Vector2Mapper.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Numerics;
|
||||
using LiteDB;
|
||||
|
||||
namespace Lagrange.OneBot.Utility;
|
||||
|
||||
public static class Vector2Mapper
|
||||
{
|
||||
public static void RegisterType() => BsonMapper.Global.RegisterType(Serialize, Deserialize);
|
||||
|
||||
public static BsonValue Serialize(Vector2 parameter) => new BsonDocument(new Dictionary<string, BsonValue>
|
||||
{
|
||||
{ "X", parameter.X },
|
||||
{ "Y", parameter.Y }
|
||||
});
|
||||
|
||||
public static Vector2 Deserialize(BsonValue bsonValue)
|
||||
{
|
||||
float x = (float)bsonValue["X"].AsDouble;
|
||||
float y = (float)bsonValue["Y"].AsDouble;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user