98 lines
3.5 KiB
C#
98 lines
3.5 KiB
C#
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
|
||
namespace WxAgent.Core;
|
||
|
||
public sealed record WechatMomentSnapshot(
|
||
string Author,
|
||
string Text,
|
||
string Fingerprint,
|
||
string? Time = null,
|
||
int ImageCount = 0,
|
||
bool IsLiked = false);
|
||
|
||
public sealed record WechatMessageContent(
|
||
ChatMessageType Type,
|
||
string Text,
|
||
Uri? Url = null,
|
||
QuotedMessageSnapshot? Quote = null);
|
||
|
||
public sealed record WechatNoteContentPart(string Kind, string Value);
|
||
|
||
public sealed record WechatNoteContent(IReadOnlyList<WechatNoteContentPart> Parts)
|
||
{
|
||
public string ToMarkdown() => string.Join(
|
||
Environment.NewLine + Environment.NewLine,
|
||
Parts.Select(part => part.Kind == "file" ? $"[{Path.GetFileName(part.Value)}]({part.Value})" : part.Value));
|
||
}
|
||
|
||
public enum WechatMomentPrivacy
|
||
{
|
||
Public,
|
||
TagWhitelist,
|
||
TagBlacklist
|
||
}
|
||
|
||
public sealed record WechatMomentPublishOptions(
|
||
WechatMomentPrivacy Privacy = WechatMomentPrivacy.Public,
|
||
IReadOnlyList<string>? Tags = null)
|
||
{
|
||
public void Validate()
|
||
{
|
||
if (!Enum.IsDefined(Privacy))
|
||
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "Unknown moment privacy mode.");
|
||
if (Privacy != WechatMomentPrivacy.Public)
|
||
WechatOperationPolicy.ValidateNames(Tags ?? [], nameof(Tags));
|
||
}
|
||
}
|
||
|
||
public static partial class WechatMessageContentParser
|
||
{
|
||
[GeneratedRegex(@"https?://[^\s<>\]\[\))]+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||
private static partial Regex UrlRegex();
|
||
|
||
public static Uri? GetUrl(ChatMessageSnapshot message)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(message);
|
||
var match = UrlRegex().Match(message.Text);
|
||
return match.Success && Uri.TryCreate(match.Value.TrimEnd('.', ',', '。', ','), UriKind.Absolute, out var uri) ? uri : null;
|
||
}
|
||
|
||
public static WechatMessageContent GetContent(ChatMessageSnapshot message)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(message);
|
||
return new WechatMessageContent(message.Type, message.Text, GetUrl(message), message.Quote);
|
||
}
|
||
|
||
public static string ToMarkdown(ChatMessageSnapshot message)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(message);
|
||
var content = GetContent(message);
|
||
var body = content.Type switch
|
||
{
|
||
ChatMessageType.Image => $"",
|
||
ChatMessageType.Video => $"[视频]({content.Url?.AbsoluteUri ?? string.Empty})",
|
||
ChatMessageType.File => $"[文件]({content.Url?.AbsoluteUri ?? string.Empty})",
|
||
ChatMessageType.Voice => $"> 语音:{content.Text}",
|
||
ChatMessageType.Link when content.Url is not null => $"[{LinkTitle(content.Text, content.Url)}]({content.Url.AbsoluteUri})",
|
||
_ => content.Text
|
||
};
|
||
if (content.Quote is null) return body;
|
||
var quote = string.Join(Environment.NewLine, content.Quote.Text.Split('\n').Select(line => $"> {line}"));
|
||
return $"{quote}{Environment.NewLine}{Environment.NewLine}{body}";
|
||
}
|
||
|
||
public static string MomentFingerprint(string author, string text, string? time)
|
||
{
|
||
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes($"{author}\n{text}\n{time}"));
|
||
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||
}
|
||
|
||
private static string LinkTitle(string text, Uri url)
|
||
{
|
||
var title = UrlRegex().Replace(text, string.Empty, 1).Trim();
|
||
return string.IsNullOrWhiteSpace(title) ? url.Host : title;
|
||
}
|
||
}
|