Импорт групповых чатом, мелкие правки, внешний вид чата, GIF тип

This commit is contained in:
Халимов Рустам
2026-03-17 00:57:01 +03:00
parent 97b70250de
commit 1fd6ef0c48
19 changed files with 358 additions and 144 deletions

View File

@@ -117,7 +117,7 @@ public sealed class TelegramImportController : ControllerBase
});
}
public sealed record ExecuteImportRequest(Guid Token, Dictionary<string, Guid> Mapping);
public sealed record ExecuteImportRequest(Guid Token, Dictionary<string, Guid> Mapping, string? GroupName);
[HttpPost("execute")]
public async Task<IActionResult> Execute([FromBody] ExecuteImportRequest req, CancellationToken ct)
@@ -161,7 +161,7 @@ public sealed class TelegramImportController : ControllerBase
else
{
// Create a group
var command = new CreateChatCommand("Импортированный чат", ChatType.Group, chatMembers);
var command = new CreateChatCommand(req.GroupName ?? "Импортированный чат", ChatType.Group, chatMembers);
var res = await _sender.Send(command, ct);
if (res.IsFailure) return BadRequest(res.Error);
chatId = res.Value;
@@ -224,15 +224,25 @@ public sealed class TelegramImportController : ControllerBase
Guid senderGuid = lastSenderGuid;
string content = "";
if (textNode != null)
var mainBodyNode = node.QuerySelector(".body");
var isForwarded = node.QuerySelector(".forwarded") != null;
// To avoid grabbing text from inside the forwarded block as main text,
// we can look for .text that is a direct child of the main .body
// The .forwarded block has its own .text
var contentTextNode = isForwarded
? (node.QuerySelector(".body > .text") ?? node.QuerySelector(".text:not(.forwarded .text)"))
: node.QuerySelector(".text");
if (contentTextNode != null)
{
var html = textNode.InnerHtml
var html = contentTextNode.InnerHtml
.Replace("<br>", "\n", StringComparison.OrdinalIgnoreCase)
.Replace("<br/>", "\n", StringComparison.OrdinalIgnoreCase)
.Replace("<br />", "\n", StringComparison.OrdinalIgnoreCase);
var tempParser = new AngleSharp.Html.Parser.HtmlParser();
var tempDoc = tempParser.ParseDocument("<div>" + html + "</div>");
content = tempDoc.Body.TextContent.Trim();
content = tempDoc.Body?.TextContent.Trim() ?? "";
}
DateTime createdAt = lastCreatedAt;
@@ -282,6 +292,7 @@ public sealed class TelegramImportController : ControllerBase
}
// Parse forwards
Guid? forwardedFromId = null;
var forwardedNode = node.QuerySelector(".forwarded.body");
if (forwardedNode != null)
{
@@ -294,6 +305,15 @@ public sealed class TelegramImportController : ControllerBase
}
var fwdName = fwdNameText != null ? fwdNameText.TextContent.Trim() : "Неизвестного";
if (req.Mapping.TryGetValue(fwdName, out var mappedFwdId) && mappedFwdId != Guid.Empty)
{
forwardedFromId = mappedFwdId;
}
else if (fwdName == "Это я" || fwdName == req.Mapping.FirstOrDefault(x => x.Value == myId).Key)
{
forwardedFromId = myId;
}
var fwdTextNode = forwardedNode.QuerySelector(".text");
string fwdContent = "";
if (fwdTextNode != null)
@@ -304,12 +324,20 @@ public sealed class TelegramImportController : ControllerBase
.Replace("<br />", "\n", StringComparison.OrdinalIgnoreCase);
var tempParser = new AngleSharp.Html.Parser.HtmlParser();
var tempDoc = tempParser.ParseDocument("<div>" + fHtml + "</div>");
fwdContent = tempDoc.Body.TextContent.Trim();
fwdContent = tempDoc.Body?.TextContent.Trim() ?? "";
}
content = string.IsNullOrEmpty(content)
? $"[Переслано от {fwdName}]:\n{fwdContent}"
: $"{content}\n\n[Переслано от {fwdName}]:\n{fwdContent}";
if (forwardedFromId == null)
{
// We don't have this user in the app, map to generic string
content = string.IsNullOrEmpty(content)
? $"[Переслано от {fwdName}]:\n{fwdContent}"
: $"{content}\n\n[Переслано от {fwdName}]:\n{fwdContent}";
}
else if (string.IsNullOrEmpty(content))
{
content = fwdContent;
}
}
// Parse replies
@@ -356,6 +384,15 @@ public sealed class TelegramImportController : ControllerBase
if (firstHref != null)
{
messageType = GetMediaTypes(firstHref).mType;
if (mediaNodes[0].ClassName?.Contains("animated") == true || firstHref.EndsWith(".mp4"))
{
// Treat telegram animated gifs as image format in app (we auto-loop mp4 images)
// But web app specifically handles "image" and "video" and microlink crashes on mp4 video
// Let's just keep "video" or "image". Actually, telegram exports gifs as mp4.
// We should let our app player handle it.
if (mediaNodes[0].ClassName?.Contains("animated") == true)
messageType = "image";
}
}
}
@@ -370,7 +407,7 @@ public sealed class TelegramImportController : ControllerBase
else
{
string finalContent = content;
targetMessage = Message.Import(chatId, senderGuid, finalContent, messageType, createdAt, replyToId, null);
targetMessage = Message.Import(chatId, senderGuid, finalContent, messageType, createdAt, replyToId, forwardedFromId);
var idAttr = node.GetAttribute("id");
if (!string.IsNullOrEmpty(idAttr))
@@ -396,8 +433,10 @@ public sealed class TelegramImportController : ControllerBase
ms.Position = 0;
var types = GetMediaTypes(href);
var finalMType = types.mType;
if (mediaNode.ClassName?.Contains("animated") == true) finalMType = "image";
var fileId = await _fileStorage.UploadFileAsync(ms, Path.GetFileName(href), types.cType);
targetMessage.AddMedia(types.mType, $"/api/files/{fileId}", Path.GetFileName(href), zipEntry.Length);
targetMessage.AddMedia(finalMType, $"/api/files/{fileId}", Path.GetFileName(href), zipEntry.Length);
}
}
}