using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BetterGenshinImpact.Service.Notification.Model;
using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Interface;
namespace BetterGenshinImpact.Service.Notifier;
public class TelegramNotifier : INotifier, IDisposable
{
// 默认Telegram API的标准URL前缀
private const string DEFAULT_TELEGRAM_API_URL = "https://api.telegram.org/bot";
private readonly bool _createdHttpClient;
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
///
/// 创建一个新的Telegram通知器实例
///
/// 可选的HttpClient,如果不提供则创建新的
/// Telegram机器人Token
/// Telegram聊天ID
/// 自定义Telegram API基础URL(可以只填写域名,如"xxx.xxx.xxx"),为空则使用默认URL
public TelegramNotifier(HttpClient httpClient = null, string telegramBotToken = "", string telegramChatId = "",
string telegramApiBaseUrl = "")
{
TelegramBotToken = telegramBotToken;
TelegramChatId = telegramChatId;
if (httpClient != null)
{
_httpClient = httpClient;
_createdHttpClient = false;
}
else
{
_httpClient = new HttpClient();
_createdHttpClient = true;
_httpClient.Timeout = TimeSpan.FromSeconds(30);
}
// 使用自定义API URL,如果为空则使用默认Telegram API URL
if (string.IsNullOrEmpty(telegramApiBaseUrl))
{
TelegramApiBaseUrl = DEFAULT_TELEGRAM_API_URL;
}
else
{
// 格式化用户提供的API URL
var formattedUrl = telegramApiBaseUrl.Trim();
// 添加协议前缀(如果没有)
if (!formattedUrl.StartsWith("http://") && !formattedUrl.StartsWith("https://"))
formattedUrl = "https://" + formattedUrl;
// 确保URL以斜杠结尾
if (!formattedUrl.EndsWith("/")) formattedUrl += "/";
// 添加bot路径(如果需要)
if (!formattedUrl.EndsWith("/bot")) formattedUrl += "bot";
TelegramApiBaseUrl = formattedUrl;
}
}
///
/// Telegram机器人Token
///
public string TelegramBotToken { get; set; }
///
/// Telegram聊天ID
///
public string TelegramChatId { get; set; }
///
/// Telegram API基础URL
///
public string TelegramApiBaseUrl { get; set; }
public void Dispose()
{
if (_createdHttpClient)
{
_httpClient?.Dispose();
}
}
///
/// 通知器名称
///
public string Name { get; set; } = "Telegram";
public async Task SendAsync(BaseNotificationData content)
{
if (string.IsNullOrEmpty(TelegramBotToken))
{
throw new NotifierException("Telegram bot token is not set");
}
if (string.IsNullOrEmpty(TelegramChatId))
{
throw new NotifierException("Telegram chat ID is not set");
}
try
{
var message = content.Message;
var fullMessage = !string.IsNullOrEmpty(message) ? message : "";
if (!string.IsNullOrEmpty(fullMessage))
{
await SendTextMessageAsync(fullMessage);
}
else
{
throw new NotifierException("No message content to send");
}
}
catch (HttpRequestException ex)
{
throw new NotifierException("Network error sending Telegram notification: " + ex.Message);
}
catch (TaskCanceledException)
{
throw new NotifierException("Telegram API request timed out. Check your internet connection.");
}
catch (NotifierException)
{
throw;
}
catch (System.Exception ex)
{
throw new NotifierException("Error sending Telegram notification: " + ex.Message);
}
}
private async Task SendTextMessageAsync(string message)
{
// 构建Telegram API URL - 使用自定义或默认API基础URL
var endpoint = $"{TelegramApiBaseUrl}{TelegramBotToken}/sendMessage";
try
{
var jsonContent = new
{
chat_id = TelegramChatId,
text = message,
disable_web_page_preview = true
};
var json = JsonSerializer.Serialize(jsonContent, _jsonSerializerOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
{
Content = content
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("BetterGenshinImpact", "1.0"));
var response = await _httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new NotifierException(
$"Telegram message failed with code: {response.StatusCode}, Error: {responseContent}");
}
// Check for API errors in the response
var (isSuccess, errorCode, errorDescription) = ValidateApiResponse(responseContent);
if (!isSuccess)
{
if (errorCode == 400)
{
throw new NotifierException(
"Please send a message to the bot first and check that the chat ID is correct.");
}
if (errorCode == 401)
{
throw new NotifierException("Telegram bot token is incorrect.");
}
if (errorCode == 404)
throw new NotifierException(
$"Telegram API not found (404). Please verify your bot token is correct. URL: {endpoint}");
throw new NotifierException($"Telegram API error: {errorDescription} (Code: {errorCode})");
}
}
catch (System.Exception ex) when (!(ex is NotifierException))
{
throw new NotifierException("Error sending Telegram notification: " + ex.Message);
}
}
private (bool isSuccess, int errorCode, string errorDescription) ValidateApiResponse(string responseJson)
{
try
{
using (var doc = JsonDocument.Parse(responseJson))
{
var root = doc.RootElement;
// Telegram API returns "ok": true for success
if (root.TryGetProperty("ok", out var okElement))
{
var isOk = okElement.GetBoolean();
if (!isOk)
{
var errorDescription = "Unknown Telegram API error";
if (root.TryGetProperty("description", out var descriptionElement))
errorDescription = descriptionElement.GetString();
var errorCode = 0;
if (root.TryGetProperty("error_code", out var errorCodeElement))
errorCode = errorCodeElement.GetInt32();
return (false, errorCode, errorDescription);
}
return (true, 0, string.Empty);
}
return (false, 0, "Invalid API response: 'ok' field missing");
}
}
catch (JsonException ex)
{
return (false, 0, "Failed to parse API response: " + ex.Message);
}
}
}