diff --git a/OneShelf.Billing/OneShelf.Billing.Api/Functions/All.cs b/OneShelf.Billing/OneShelf.Billing.Api/Functions/All.cs index 72a44b54..2e920507 100644 --- a/OneShelf.Billing/OneShelf.Billing.Api/Functions/All.cs +++ b/OneShelf.Billing/OneShelf.Billing.Api/Functions/All.cs @@ -53,6 +53,7 @@ protected override async Task Execute(AllRequest allRequest) "gpt-4-1106-preview" or "gpt-4-0125-preview" => .01f * x.InputTokens / 1000 + .03f * x.OutputTokens / 1000, "gpt-4o" when x.CreatedOn < new DateTime(2024, 10, 2) => (.01f * x.InputTokens / 1000 + .03f * x.OutputTokens / 1000) * 0.5f, "gpt-4o" => .01f * x.InputTokens / 1000 / 4 + .01f * x.OutputTokens / 1000, + "whisper-1" => x.Count / 60f * .006f, _ => null, }, Category = x.UseCase switch @@ -62,6 +63,7 @@ protected override async Task Execute(AllRequest allRequest) "gpt-4-1106-preview" or "gpt-4-0125-preview" or "gpt-4o" => "chat text", _ => "chat images", }, + "audio" => "audio transcription", _ => x.Model switch { "gpt-4-1106-preview" or "gpt-4-0125-preview" or "gpt-4o" => "song text", diff --git a/OneShelf.Common/OneShelf.Common.Database.Songs/Model/Enums/InteractionType.cs b/OneShelf.Common/OneShelf.Common.Database.Songs/Model/Enums/InteractionType.cs index 8ffba2d4..ea9b3b6b 100644 --- a/OneShelf.Common/OneShelf.Common.Database.Songs/Model/Enums/InteractionType.cs +++ b/OneShelf.Common/OneShelf.Common.Database.Songs/Model/Enums/InteractionType.cs @@ -39,4 +39,6 @@ public enum InteractionType ImagesSuccess, OwnChatterImageMessage, + + OwnChatterAudio, } \ No newline at end of file diff --git a/OneShelf.Common/OneShelf.Common.Database.Songs/SongsDatabase.cs b/OneShelf.Common/OneShelf.Common.Database.Songs/SongsDatabase.cs index 665b83a0..f29afbbd 100644 --- a/OneShelf.Common/OneShelf.Common.Database.Songs/SongsDatabase.cs +++ b/OneShelf.Common/OneShelf.Common.Database.Songs/SongsDatabase.cs @@ -223,6 +223,11 @@ async Task IInteractionsRepository.Add(List.Update(IInteraction interaction) + { + await SaveChangesAsyncX(); + } + InteractionType IInteractionsRepository.OwnChatterMessage => InteractionType.OwnChatterMessage; InteractionType IInteractionsRepository.OwnChatterImageMessage => InteractionType.OwnChatterImageMessage; @@ -235,5 +240,7 @@ async Task IInteractionsRepository.Add(List.ImagesSuccess => InteractionType.ImagesSuccess; + InteractionType IInteractionsRepository.Audio => InteractionType.OwnChatterAudio; + #endregion } \ No newline at end of file diff --git a/OneShelf.Common/OneShelf.Common.OpenAi/ServiceCollectionExtensions.cs b/OneShelf.Common/OneShelf.Common.OpenAi/ServiceCollectionExtensions.cs index 61f57ac3..e768568b 100644 --- a/OneShelf.Common/OneShelf.Common.OpenAi/ServiceCollectionExtensions.cs +++ b/OneShelf.Common/OneShelf.Common.OpenAi/ServiceCollectionExtensions.cs @@ -14,6 +14,7 @@ public static IServiceCollection AddOpenAi(this IServiceCollection services, ICo services .AddScoped() + .AddScoped() .AddBillingApiClient(configuration) .AddHttpClient(); diff --git a/OneShelf.Common/OneShelf.Common.OpenAi/Services/DialogRunner.cs b/OneShelf.Common/OneShelf.Common.OpenAi/Services/DialogRunner.cs index f1194370..93b60125 100644 --- a/OneShelf.Common/OneShelf.Common.OpenAi/Services/DialogRunner.cs +++ b/OneShelf.Common/OneShelf.Common.OpenAi/Services/DialogRunner.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; +using Azure.Core; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OneShelf.Billing.Api.Client; diff --git a/OneShelf.Common/OneShelf.Common.OpenAi/Services/Transcriber.cs b/OneShelf.Common/OneShelf.Common.OpenAi/Services/Transcriber.cs new file mode 100644 index 00000000..e6034848 --- /dev/null +++ b/OneShelf.Common/OneShelf.Common.OpenAi/Services/Transcriber.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OneShelf.Billing.Api.Client; +using OneShelf.Common.OpenAi.Models; +using OpenAI; +using OpenAI.Audio; + +namespace OneShelf.Common.OpenAi.Services; + +public class Transcriber +{ + private readonly ILogger _logger; + private readonly BillingApiClient _billingApiClient; + private readonly OpenAIClient _client; + private readonly OpenAiOptions _options; + + public Transcriber(IOptions options, ILogger logger, BillingApiClient billingApiClient) + { + _logger = logger; + _billingApiClient = billingApiClient; + _options = options.Value; + _client = new(new(options.Value.OpenAiApiKey)); + } + + public async Task TranscribeAudio(byte[] audio, DialogConfiguration configuration) + { + var started = DateTime.Now; + using var stream = new MemoryStream(audio); + var model = "whisper-1"; + var response = await _client.AudioEndpoint.CreateTranscriptionJsonAsync(new(stream, "stream.webm", model, responseFormat: AudioResponseFormat.Verbose_Json)); + _logger.LogInformation("Audio transcribed. Took {ms} ms.", DateTime.Now - started); + await _billingApiClient.Add(new() + { + Count = (int)Math.Ceiling(response.Duration ?? 0), + UserId = configuration.UserId, + Model = model, + UseCase = configuration.UseCase, + AdditionalInfo = configuration.AdditionalBillingInfo, + DomainId = configuration.DomainId, + ChatId = configuration.ChatId, + }); + + return response.Text; + } +} \ No newline at end of file diff --git a/OneShelf.OneDog/OneShelf.OneDog.Database/DogDatabase.cs b/OneShelf.OneDog/OneShelf.OneDog.Database/DogDatabase.cs index 4813dd42..ab803aed 100644 --- a/OneShelf.OneDog/OneShelf.OneDog.Database/DogDatabase.cs +++ b/OneShelf.OneDog/OneShelf.OneDog.Database/DogDatabase.cs @@ -52,6 +52,11 @@ async Task IInteractionsRepository.Add(List.Update(IInteraction interaction) + { + await SaveChangesAsync(); + } + InteractionType IInteractionsRepository.OwnChatterMessage => InteractionType.OwnChatterMessage; InteractionType IInteractionsRepository.OwnChatterImageMessage => InteractionType.OwnChatterImageMessage; @@ -64,5 +69,7 @@ async Task IInteractionsRepository.Add(List.ImagesSuccess => InteractionType.ImagesSuccess; + InteractionType IInteractionsRepository.Audio => InteractionType.OwnChatterAudio; + #endregion } \ No newline at end of file diff --git a/OneShelf.OneDog/OneShelf.OneDog.Database/Model/Enums/InteractionType.cs b/OneShelf.OneDog/OneShelf.OneDog.Database/Model/Enums/InteractionType.cs index bca08b0b..3ead2173 100644 --- a/OneShelf.OneDog/OneShelf.OneDog.Database/Model/Enums/InteractionType.cs +++ b/OneShelf.OneDog/OneShelf.OneDog.Database/Model/Enums/InteractionType.cs @@ -15,4 +15,6 @@ public enum InteractionType ImagesLimit, OwnChatterImageMessage, + + OwnChatterAudio } \ No newline at end of file diff --git a/OneShelf.OneDog/OneShelf.OneDog.Processor/Services/PipelineHandlers/AiDialogHandler.cs b/OneShelf.OneDog/OneShelf.OneDog.Processor/Services/PipelineHandlers/AiDialogHandler.cs index 78ac927c..4edaf7ac 100644 --- a/OneShelf.OneDog/OneShelf.OneDog.Processor/Services/PipelineHandlers/AiDialogHandler.cs +++ b/OneShelf.OneDog/OneShelf.OneDog.Processor/Services/PipelineHandlers/AiDialogHandler.cs @@ -21,9 +21,10 @@ public AiDialogHandler(ILogger logger, DogDatabase dogDatabase, DialogRunner dialogRunner, IScopedAbstractions scopedAbstractions, - DogContext dogContext, - IHttpClientFactory httpClientFactory) - : base(scopedAbstractions, logger, dogDatabase, dialogRunner, httpClientFactory) + DogContext dogContext, + IHttpClientFactory httpClientFactory, + Transcriber transcriber) + : base(scopedAbstractions, logger, dogDatabase, dialogRunner, httpClientFactory, transcriber) { _dogDatabase = dogDatabase; _dogContext = dogContext; @@ -34,6 +35,11 @@ protected override void OnInitializing(long userId, long chatId) _dogDatabase.InitializeInteractionsRepositoryScope(_dogContext.DomainId); } + protected override bool TranscribeAudio(Update update) + { + return false; + } + protected override bool CheckRelevant(Update update) { if (update.Message?.Chat.Id != _dogContext.Domain.ChatId) return false; diff --git a/OneShelf.OneDragon/OneShelf.OneDragon.Database/DragonDatabase.cs b/OneShelf.OneDragon/OneShelf.OneDragon.Database/DragonDatabase.cs index f6e81459..e32b3c7f 100644 --- a/OneShelf.OneDragon/OneShelf.OneDragon.Database/DragonDatabase.cs +++ b/OneShelf.OneDragon/OneShelf.OneDragon.Database/DragonDatabase.cs @@ -56,6 +56,11 @@ async Task IInteractionsRepository.Add(List.Update(IInteraction interaction) + { + await SaveChangesAsync(); + } + InteractionType IInteractionsRepository.OwnChatterMessage => InteractionType.AiMessage; InteractionType IInteractionsRepository.OwnChatterImageMessage => InteractionType.AiImageMessage; @@ -68,5 +73,7 @@ async Task IInteractionsRepository.Add(List.ImagesSuccess => InteractionType.AiImagesSuccess; + InteractionType IInteractionsRepository.Audio => InteractionType.AiAudio; + #endregion } \ No newline at end of file diff --git a/OneShelf.OneDragon/OneShelf.OneDragon.Database/Model/Enums/InteractionType.cs b/OneShelf.OneDragon/OneShelf.OneDragon.Database/Model/Enums/InteractionType.cs index 052a31ae..bd63715e 100644 --- a/OneShelf.OneDragon/OneShelf.OneDragon.Database/Model/Enums/InteractionType.cs +++ b/OneShelf.OneDragon/OneShelf.OneDragon.Database/Model/Enums/InteractionType.cs @@ -8,6 +8,7 @@ public enum InteractionType AiResetDialog, AiImagesLimit, AiImagesSuccess, + AiAudio, DirectImagesSuccess, DirectImagesLimit, diff --git a/OneShelf.OneDragon/OneShelf.OneDragon.Processor/PipelineHandlers/AiDialogHandler.cs b/OneShelf.OneDragon/OneShelf.OneDragon.Processor/PipelineHandlers/AiDialogHandler.cs index b3e01efa..3ecc7611 100644 --- a/OneShelf.OneDragon/OneShelf.OneDragon.Processor/PipelineHandlers/AiDialogHandler.cs +++ b/OneShelf.OneDragon/OneShelf.OneDragon.Processor/PipelineHandlers/AiDialogHandler.cs @@ -29,8 +29,9 @@ public AiDialogHandler(IScopedAbstractions scopedAbstractions, DragonScope dragonScope, Availability availability, IOptions options, - IHttpClientFactory httpClientFactory) - : base(scopedAbstractions, logger, dragonDatabase, dialogRunner, httpClientFactory) + IHttpClientFactory httpClientFactory, + Transcriber transcriber) + : base(scopedAbstractions, logger, dragonDatabase, dialogRunner, httpClientFactory, transcriber) { _dragonDatabase = dragonDatabase; _dragonScope = dragonScope; diff --git a/OneShelf.Telegram/OneShelf.Telegram.Ai.Model/IInteractionsRepository.cs b/OneShelf.Telegram/OneShelf.Telegram.Ai.Model/IInteractionsRepository.cs index 32491bb4..f70ba19a 100644 --- a/OneShelf.Telegram/OneShelf.Telegram.Ai.Model/IInteractionsRepository.cs +++ b/OneShelf.Telegram/OneShelf.Telegram.Ai.Model/IInteractionsRepository.cs @@ -6,10 +6,13 @@ public interface IInteractionsRepository Task Add(List> interactions); + Task Update(IInteraction interaction); + TInteractionType OwnChatterMessage { get; } TInteractionType OwnChatterImageMessage { get; } TInteractionType OwnChatterMemoryPoint { get; } TInteractionType OwnChatterResetDialog { get; } TInteractionType ImagesLimit { get; } TInteractionType ImagesSuccess { get; } + TInteractionType Audio { get; } } \ No newline at end of file diff --git a/OneShelf.Telegram/OneShelf.Telegram.Ai/PipelineHandlers/AiDialogHandlerBase.cs b/OneShelf.Telegram/OneShelf.Telegram.Ai/PipelineHandlers/AiDialogHandlerBase.cs index ef144f3f..900eb681 100644 --- a/OneShelf.Telegram/OneShelf.Telegram.Ai/PipelineHandlers/AiDialogHandlerBase.cs +++ b/OneShelf.Telegram/OneShelf.Telegram.Ai/PipelineHandlers/AiDialogHandlerBase.cs @@ -1,5 +1,6 @@ using System.Text; using System.Text.Json; +using Azure.Core; using Microsoft.Extensions.Logging; using Nito.AsyncEx; using OneShelf.Common; @@ -7,6 +8,7 @@ using OneShelf.Common.OpenAi.Models.Memory; using OneShelf.Common.OpenAi.Services; using OneShelf.Telegram.Ai.Model; +using OneShelf.Telegram.Helpers; using OneShelf.Telegram.Services.Base; using Telegram.BotAPI; using Telegram.BotAPI.AvailableMethods; @@ -25,20 +27,23 @@ public abstract class AiDialogHandlerBase : PipelineHandler private const string ShowImageActionsCallbackCommand = "imagegroup"; private const string ImageCloseCallbackCommand = "imageclose"; private const string ImageCallbackCommand = "image"; + private const string AudioApplyCallbackCommand = "audio"; private const string CallbackDataSeparator = ", "; protected readonly ILogger> _logger; protected readonly IInteractionsRepository _repository; protected readonly DialogRunner _dialogRunner; protected readonly IHttpClientFactory _httpClientFactory; + protected readonly Transcriber _transcriber; - protected AiDialogHandlerBase(IScopedAbstractions scopedAbstractions, ILogger> logger, IInteractionsRepository repository, DialogRunner dialogRunner, IHttpClientFactory httpClientFactory) + protected AiDialogHandlerBase(IScopedAbstractions scopedAbstractions, ILogger> logger, IInteractionsRepository repository, DialogRunner dialogRunner, IHttpClientFactory httpClientFactory, Transcriber transcriber) : base(scopedAbstractions) { _logger = logger; _repository = repository; _dialogRunner = dialogRunner; _httpClientFactory = httpClientFactory; + _transcriber = transcriber; } private async Task Log(Update update) @@ -65,9 +70,7 @@ private async Task Log(Update update) if (update.Message?.Photo != null) { - var path = (await GetApi().GetFileAsync(update.Message.Photo.OrderByDescending(x => x.Height).First().FileId)).FilePath; - using var client = _httpClientFactory.CreateClient(); - var bytes = await client.GetByteArrayAsync($"https://api.telegram.org/file/bot{ScopedAbstractions.GetBotToken()}/{path}"); + var bytes = await Download(update.Message.Photo.OrderByDescending(x => x.Height).First().FileId); var interaction = CreateInteraction(update); interaction.CreatedOn = DateTime.Now; @@ -81,6 +84,13 @@ private async Task Log(Update update) return true; } + private async Task Download(string fileId) + { + var path = (await GetApi().GetFileAsync(fileId)).FilePath; + using var client = _httpClientFactory.CreateClient(); + return await client.GetByteArrayAsync($"https://api.telegram.org/file/bot{ScopedAbstractions.GetBotToken()}/{path}"); + } + protected abstract IInteraction CreateInteraction(Update update); protected async Task CheckNoUpdates(CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken, int lastUpdateId) @@ -277,15 +287,118 @@ protected override async Task HandleSync(Update update) return true; } + if (TranscribeAudio(update)) + { + Transcribe(update); + return true; + } + return false; } + private void Transcribe(Update update) + { + QueueApi(update.Message!.From!.Id.ToString(), async client => + { + var interaction = CreateInteraction(update); + interaction.InteractionType = _repository.Audio; + interaction.UserId = update.Message.From.Id; + interaction.CreatedOn = DateTime.Now; + interaction.Serialized = JsonSerializer.Serialize(update); + await _repository.Add([interaction]); + + var autoRespond = update.Message.ForwardOrigin == null; + var replyMarkup = autoRespond ? null : new InlineKeyboardMarkup([[new("✅") + { + CallbackData = GetCallbackData(AudioApplyCallbackCommand, interaction.Id), + }]]); + + var message = await client.SendMessageAsync(update.Message.Chat.Id, "Transcribing...", null, + update.Message.MessageThreadId, replyParameters: new() + { + MessageId = update.Message.MessageId, + }, replyMarkup: replyMarkup); + + try + { + var bytes = await Download(update.Message.Voice!.FileId); + var (additionalBillingInfo, domainId) = GetDialogConfigurationParameters(); + var text = await _transcriber.TranscribeAudio(bytes, new() + { + SystemMessage = null!, + Version = null!, + UserId = update.Message.From.Id, + UseCase = "audio", + AdditionalBillingInfo = additionalBillingInfo, + DomainId = domainId, + ChatId = update.Message.Chat.Id, + }); + + interaction.ShortInfoSerialized = text; + await _repository.Update(interaction); + + await client.EditMessageTextAsync(message.Chat.Id, message.MessageId, string.Join("\n", text.Replace("\r\n", "\n").Split('\n').Select(x => x.ToMarkdown())).SelectSingle(x => $"`{x}`"), replyMarkup: replyMarkup, parseMode: Constants.MarkdownV2); + + if (autoRespond) + { + update.Message.Text = text; + if (await Log(update)) + { + await Respond(update, message.MessageId); + } + } + } + catch (Exception e) + { + _logger.LogError(e, "Error transcribing the audio."); + await client.EditMessageTextAsync(message.Chat.Id, message.MessageId, "Случилась ошибка."); + } + }); + } + private async Task HandleCallback(Update update) { var callbackQuery = update.CallbackQuery!; - var data = callbackQuery.Data!.Split(CallbackDataSeparator); + if (data is [AudioApplyCallbackCommand, { } audioInteractionIdValue] + && int.TryParse(audioInteractionIdValue, out var audioInteractionId)) + { + QueueApi( + callbackQuery.From.Id.ToString(), + async api => + { + var interaction = (await _repository.Get(x => x.Where(x => x.Id == audioInteractionId))).Single(); + while (interaction.ShortInfoSerialized == null + && DateTime.Now - interaction.CreatedOn < TimeSpan.FromSeconds(10)) + { + await Task.Delay(500); + interaction = (await _repository.Get(x => x.Where(x => x.Id == audioInteractionId))).Single(); + } + + if (interaction.ShortInfoSerialized == null) + { + return; + } + + var text = interaction.ShortInfoSerialized; + var update2 = JsonSerializer.Deserialize(interaction.Serialized); + update2.Message.Text = text; + + if (callbackQuery.Message != null) + { + await api.EditMessageReplyMarkupAsync(callbackQuery.Message.Chat.Id, callbackQuery.Message.MessageId, replyMarkup: null); + } + + if (await Log(update2)) + { + await Respond(update2, callbackQuery.Message?.MessageId); + } + }); + + return true; + } + if (data is [ShowImageActionsCallbackCommand, { } imageGroupInteractionIdValue, { } imageGroupImageIndexValue, { } imageGroupImagesCountValue] && int.TryParse(imageGroupInteractionIdValue, out var interactionId) && int.TryParse(imageGroupImageIndexValue, out var imageIndex) @@ -324,35 +437,37 @@ private async Task HandleCallback(Update update) return true; } - if (data is not [ImageCallbackCommand, { } interactionIdValue, { } imageIndexValue, { } timesValue] - || !int.TryParse(interactionIdValue, out interactionId) - || !int.TryParse(imageIndexValue, out imageIndex) - || !int.TryParse(timesValue, out var times)) + if (data is [ImageCallbackCommand, { } interactionIdValue, { } imageIndexValue, { } timesValue] + && int.TryParse(interactionIdValue, out interactionId) + && int.TryParse(imageIndexValue, out imageIndex) + && int.TryParse(timesValue, out var times)) { - return false; - } - - var interaction = (await _repository.Get(x => x.Where(x => x.Id == interactionId))).Single(); - DateTime? imagesUnavailableUntil = null; - if (times > 0) - { - var @lock = _databaseLocks.GetValueOrDefault(callbackQuery.From.Id) ?? (_databaseLocks[callbackQuery.From.Id] = new()); - using var _ = await @lock.LockAsync(); - imagesUnavailableUntil = await GetImagesUnavailableUntil(DateTime.Now); - if (!imagesUnavailableUntil.HasValue) + var interaction = (await _repository.Get(x => x.Where(x => x.Id == interactionId))).Single(); + DateTime? imagesUnavailableUntil = null; + if (times > 0) { - var interaction2 = CreateInteraction(update); - interaction2.CreatedOn = DateTime.Now; - interaction2.InteractionType = _repository.ImagesSuccess; - interaction2.Serialized = timesValue; - interaction2.ShortInfoSerialized = callbackQuery.Data; - interaction2.UserId = callbackQuery.From.Id; - await _repository.Add(interaction2.Once().ToList()); + var @lock = _databaseLocks.GetValueOrDefault(callbackQuery.From.Id) ?? + (_databaseLocks[callbackQuery.From.Id] = new()); + using var _ = await @lock.LockAsync(); + imagesUnavailableUntil = await GetImagesUnavailableUntil(DateTime.Now); + if (!imagesUnavailableUntil.HasValue) + { + var interaction2 = CreateInteraction(update); + interaction2.CreatedOn = DateTime.Now; + interaction2.InteractionType = _repository.ImagesSuccess; + interaction2.Serialized = timesValue; + interaction2.ShortInfoSerialized = callbackQuery.Data; + interaction2.UserId = callbackQuery.From.Id; + await _repository.Add(interaction2.Once().ToList()); + } } + + QueueApi(callbackQuery.From.Id.ToString(), + api => ImageCallback(api, callbackQuery, interaction, imageIndex, times, imagesUnavailableUntil)); + return true; } - QueueApi(null, api => ImageCallback(api, callbackQuery, interaction, imageIndex, times, imagesUnavailableUntil)); - return true; + return false; } private async Task ImageCallback(TelegramBotClient api, CallbackQuery callbackQuery, IInteraction interaction, int imageIndex, int times, DateTime? imagesUnavailableUntil) @@ -434,7 +549,15 @@ protected virtual void OnInitializing(long userId, long chatId) protected virtual bool TraceImages => false; - private async Task Respond(Update update) + protected virtual bool TranscribeAudio(Update update) + { + if (update.Message?.Voice == null) return false; + if (update.Message.Voice.Duration > 120) return false; + + return true; + } + + private async Task Respond(Update update, int? replyToMessageId = null) { var now = DateTime.Now; var since = now.AddDays(-1); @@ -573,11 +696,11 @@ private async Task Respond(Update update) if (!string.IsNullOrWhiteSpace(text)) { - await SendMessage(update.Message!.Chat.Id, update.Message.MessageThreadId, update.Message.MessageId, text, result.Images, false, replyMarkup); + await SendMessage(update.Message!.Chat.Id, update.Message.MessageThreadId, replyToMessageId ?? update.Message.MessageId, text, result.Images, replyToMessageId.HasValue, replyMarkup); } else { - await SendMessage(update.Message!.Chat.Id, update.Message!.MessageThreadId, update.Message!.MessageId, result.Images, false); + await SendMessage(update.Message!.Chat.Id, update.Message!.MessageThreadId, replyToMessageId ?? update.Message.MessageId, result.Images, replyToMessageId.HasValue); } return; @@ -590,7 +713,7 @@ private async Task Respond(Update update) if (!string.IsNullOrWhiteSpace(text)) { - await SendMessage(update.Message!.Chat.Id, update.Message.MessageThreadId, update.Message.MessageId, text, false); + await SendMessage(update.Message!.Chat.Id, update.Message.MessageThreadId, replyToMessageId ?? update.Message.MessageId, text, replyToMessageId.HasValue); } } diff --git a/OneShelf.Telegram/OneShelf.Telegram.Processor/Services/PipelineHandlers/AiDialogHandler.cs b/OneShelf.Telegram/OneShelf.Telegram.Processor/Services/PipelineHandlers/AiDialogHandler.cs index 4c8be852..6ca84811 100644 --- a/OneShelf.Telegram/OneShelf.Telegram.Processor/Services/PipelineHandlers/AiDialogHandler.cs +++ b/OneShelf.Telegram/OneShelf.Telegram.Processor/Services/PipelineHandlers/AiDialogHandler.cs @@ -24,8 +24,9 @@ public AiDialogHandler(ILogger logger, SongsDatabase songsDatabase, DialogRunner dialogRunner, IScopedAbstractions scopedAbstractions, - IHttpClientFactory httpClientFactory) - : base(scopedAbstractions, logger, songsDatabase, dialogRunner, httpClientFactory) + IHttpClientFactory httpClientFactory, + Transcriber transcriber) + : base(scopedAbstractions, logger, songsDatabase, dialogRunner, httpClientFactory, transcriber) { _songsDatabase = songsDatabase; _telegramOptions = telegramOptions.Value;