__
This commit is contained in:
@@ -107,6 +107,14 @@ public sealed class ForwardWorker : BackgroundService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK wall.post (new): TelegramMessageId={TelegramMessageId}, owner_id={OwnerId}, textLen={TextLen}, textPreview={TextPreview}, attachments={Attachments}",
|
||||||
|
telegramMessageId,
|
||||||
|
ownerId,
|
||||||
|
(text ?? string.Empty).Length,
|
||||||
|
TruncateForLog(text, 200),
|
||||||
|
attachments ?? "(none)");
|
||||||
|
|
||||||
var postId = await _vkApi.WallPostAsync(ownerId, text ?? string.Empty, attachments, ct);
|
var postId = await _vkApi.WallPostAsync(ownerId, text ?? string.Empty, attachments, ct);
|
||||||
await _repo.UpsertAsync(
|
await _repo.UpsertAsync(
|
||||||
telegramChatId: _telegram.ChannelId,
|
telegramChatId: _telegram.ChannelId,
|
||||||
@@ -145,6 +153,14 @@ public sealed class ForwardWorker : BackgroundService
|
|||||||
if (string.IsNullOrWhiteSpace(messageToSend) && string.IsNullOrWhiteSpace(attachmentsToSend))
|
if (string.IsNullOrWhiteSpace(messageToSend) && string.IsNullOrWhiteSpace(attachmentsToSend))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK wall.post (after edit, no mapping): TelegramMessageId={TelegramMessageId}, owner_id={OwnerId}, textLen={TextLen}, textPreview={TextPreview}, attachments={Attachments}",
|
||||||
|
telegramMessageId,
|
||||||
|
ownerId,
|
||||||
|
(messageToSend ?? string.Empty).Length,
|
||||||
|
TruncateForLog(messageToSend, 200),
|
||||||
|
attachmentsToSend ?? "(none)");
|
||||||
|
|
||||||
var newPostId = await _vkApi.WallPostAsync(ownerId, messageToSend ?? string.Empty, attachmentsToSend, ct);
|
var newPostId = await _vkApi.WallPostAsync(ownerId, messageToSend ?? string.Empty, attachmentsToSend, ct);
|
||||||
await _repo.UpsertAsync(_telegram.ChannelId, telegramMessageId, ownerId, newPostId, messageToSend, attachmentsToSend, ct);
|
await _repo.UpsertAsync(_telegram.ChannelId, telegramMessageId, ownerId, newPostId, messageToSend, attachmentsToSend, ct);
|
||||||
return;
|
return;
|
||||||
@@ -155,6 +171,15 @@ public sealed class ForwardWorker : BackgroundService
|
|||||||
|
|
||||||
var messageToSendFinal = messageToSend ?? string.Empty;
|
var messageToSendFinal = messageToSend ?? string.Empty;
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK wall.edit: TelegramMessageId={TelegramMessageId}, owner_id={OwnerId}, post_id={PostId}, textLen={TextLen}, textPreview={TextPreview}, attachments={Attachments}",
|
||||||
|
telegramMessageId,
|
||||||
|
existing.Value.VkOwnerId,
|
||||||
|
existing.Value.VkPostId,
|
||||||
|
messageToSendFinal.Length,
|
||||||
|
TruncateForLog(messageToSendFinal, 200),
|
||||||
|
attachmentsToSend ?? "(none)");
|
||||||
|
|
||||||
await _vkApi.WallEditAsync(existing.Value.VkOwnerId, existing.Value.VkPostId, messageToSendFinal, attachmentsToSend, ct);
|
await _vkApi.WallEditAsync(existing.Value.VkOwnerId, existing.Value.VkPostId, messageToSendFinal, attachmentsToSend, ct);
|
||||||
await _repo.UpsertAsync(_telegram.ChannelId, telegramMessageId, existing.Value.VkOwnerId, existing.Value.VkPostId, messageToSendFinal, attachmentsToSend, ct);
|
await _repo.UpsertAsync(_telegram.ChannelId, telegramMessageId, existing.Value.VkOwnerId, existing.Value.VkPostId, messageToSendFinal, attachmentsToSend, ct);
|
||||||
|
|
||||||
@@ -194,5 +219,14 @@ public sealed class ForwardWorker : BackgroundService
|
|||||||
// Логирование делаем в Worker через исключения, но чтобы не терять ошибки polling'а — просто возвращаем CompletedTask.
|
// Логирование делаем в Worker через исключения, но чтобы не терять ошибки polling'а — просто возвращаем CompletedTask.
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string TruncateForLog(string? text, int maxLen)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return "";
|
||||||
|
if (text.Length <= maxLen)
|
||||||
|
return text;
|
||||||
|
return string.Concat(text.AsSpan(0, maxLen), "…(len=", text.Length.ToString(), ")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -7,6 +8,8 @@ namespace Telegram2VkBot;
|
|||||||
public sealed class VkApiClient
|
public sealed class VkApiClient
|
||||||
{
|
{
|
||||||
private const string VkApiBaseUrl = "https://api.vk.com/method/";
|
private const string VkApiBaseUrl = "https://api.vk.com/method/";
|
||||||
|
private const int LogParamMaxLength = 240;
|
||||||
|
private const int LogBodyMaxLength = 8192;
|
||||||
|
|
||||||
private readonly VkOptions _options;
|
private readonly VkOptions _options;
|
||||||
private readonly HttpClient _http;
|
private readonly HttpClient _http;
|
||||||
@@ -92,8 +95,18 @@ public sealed class VkApiClient
|
|||||||
byteContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
byteContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
||||||
form.Add(byteContent, "photo", "photo.jpg");
|
form.Add(byteContent, "photo", "photo.jpg");
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK upload → POST multipart (photo.jpg, {Size} bytes) to {UploadUrl}",
|
||||||
|
photoBytes.Length,
|
||||||
|
TruncateForLog(uploadUrl, 512));
|
||||||
|
|
||||||
using var uploadResp = await _http.PostAsync(uploadUrl, form, ct);
|
using var uploadResp = await _http.PostAsync(uploadUrl, form, ct);
|
||||||
var uploadRespText = await uploadResp.Content.ReadAsStringAsync(ct);
|
var uploadRespText = await uploadResp.Content.ReadAsStringAsync(ct);
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK upload ← HTTP {StatusCode}, body: {Body}",
|
||||||
|
(int)uploadResp.StatusCode,
|
||||||
|
TruncateForLog(uploadRespText, LogBodyMaxLength));
|
||||||
|
|
||||||
if (!uploadResp.IsSuccessStatusCode)
|
if (!uploadResp.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("VK upload failed: {StatusCode} {Body}", uploadResp.StatusCode, uploadRespText);
|
_logger.LogWarning("VK upload failed: {StatusCode} {Body}", uploadResp.StatusCode, uploadRespText);
|
||||||
@@ -142,12 +155,30 @@ public sealed class VkApiClient
|
|||||||
["v"] = _options.ApiVersion,
|
["v"] = _options.ApiVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var requestUrl = VkApiBaseUrl + method;
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK API → POST {Url}, form: {Form}",
|
||||||
|
requestUrl,
|
||||||
|
FormatFormForLog(query));
|
||||||
|
|
||||||
using var content = new FormUrlEncodedContent(query);
|
using var content = new FormUrlEncodedContent(query);
|
||||||
using var resp = await _http.PostAsync(VkApiBaseUrl + method, content, ct);
|
using var resp = await _http.PostAsync(requestUrl, content, ct);
|
||||||
var body = await resp.Content.ReadAsStringAsync(ct);
|
var body = await resp.Content.ReadAsStringAsync(ct);
|
||||||
|
var bodyForLog = TruncateForLog(body, LogBodyMaxLength);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"VK API ← {Method}: HTTP {StatusCode}, body: {Body}",
|
||||||
|
method,
|
||||||
|
(int)resp.StatusCode,
|
||||||
|
bodyForLog);
|
||||||
|
|
||||||
if (!resp.IsSuccessStatusCode)
|
if (!resp.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
"VK API HTTP error for {Method}: {StatusCode}, body: {Body}",
|
||||||
|
method,
|
||||||
|
(int)resp.StatusCode,
|
||||||
|
bodyForLog);
|
||||||
throw new InvalidOperationException($"VK API HTTP error: {(int)resp.StatusCode} {body}");
|
throw new InvalidOperationException($"VK API HTTP error: {(int)resp.StatusCode} {body}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +189,10 @@ public sealed class VkApiClient
|
|||||||
{
|
{
|
||||||
var message = error.TryGetProperty("error_msg", out var em) ? em.GetString() : null;
|
var message = error.TryGetProperty("error_msg", out var em) ? em.GetString() : null;
|
||||||
var code = error.TryGetProperty("error_code", out var ec) ? ec.ToString() : null;
|
var code = error.TryGetProperty("error_code", out var ec) ? ec.ToString() : null;
|
||||||
|
_logger.LogError(
|
||||||
|
"VK API error body for {Method}: {Body}",
|
||||||
|
method,
|
||||||
|
bodyForLog);
|
||||||
throw new InvalidOperationException($"VK API error: code={code} msg={message}");
|
throw new InvalidOperationException($"VK API error: code={code} msg={message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,8 +201,50 @@ public sealed class VkApiClient
|
|||||||
throw new InvalidOperationException($"VK API unexpected response: {body}");
|
throw new InvalidOperationException($"VK API unexpected response: {body}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var responsePayload = response.GetRawText();
|
||||||
|
_logger.LogDebug("VK API response.extracted for {Method}: {Payload}", method, TruncateForLog(responsePayload, LogBodyMaxLength));
|
||||||
|
|
||||||
// Return detached element by cloning to avoid disposing doc
|
// Return detached element by cloning to avoid disposing doc
|
||||||
return JsonDocument.Parse(response.GetRawText()).RootElement.Clone();
|
return JsonDocument.Parse(responsePayload).RootElement.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatFormForLog(IReadOnlyDictionary<string, string> form)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(capacity: Math.Min(512, form.Count * 32));
|
||||||
|
var first = true;
|
||||||
|
foreach (var (key, value) in form.OrderBy(static kv => kv.Key, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
sb.Append("; ");
|
||||||
|
first = false;
|
||||||
|
sb.Append(key);
|
||||||
|
sb.Append('=');
|
||||||
|
sb.Append(key.Equals("access_token", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "***"
|
||||||
|
: SanitizeParamForLog(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SanitizeParamForLog(string key, string value)
|
||||||
|
{
|
||||||
|
if (value.Length <= LogParamMaxLength)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
// VK передаёт поле photo как длинную строка; message тоже может быть огромным.
|
||||||
|
var previewLen = Math.Min(120, LogParamMaxLength);
|
||||||
|
var head = value[..previewLen];
|
||||||
|
return $"{head}…(truncated, len={value.Length}, key={key})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TruncateForLog(string? text, int maxLen)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return "";
|
||||||
|
if (text.Length <= maxLen)
|
||||||
|
return text;
|
||||||
|
return string.Concat(text.AsSpan(0, maxLen), "…(truncated, totalLen=", text.Length.ToString(), ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user