protected override async Task <int> OnExecuteAuthenticatedAsync(QBittorrentClient client, CommandLineApplication app, IConsole console) { var(apiVersion, legacyApiVersion, legacyApiMinVersion, qVersion) = await TaskHelper.WhenAll( client.GetApiVersionAsync(), client.GetLegacyApiVersionAsync(), client.GetLegacyMinApiVersionAsync(), client.GetQBittorrentVersionAsync()); var doc = new Document( new Grid { Stroke = UIHelper.NoneStroke, Columns = { UIHelper.FieldsColumns }, Children = { UIHelper.Row("QBittorrent version", qVersion), UIHelper.Row("API version", apiVersion), UIHelper.Row("Legacy API version", legacyApiVersion), UIHelper.Row("Legacy API min version", legacyApiMinVersion) } } ).SetColors(ColorScheme.Current.Normal); ConsoleRenderer.RenderDocument(doc); return(ExitCodes.Success); }
protected override async Task <int> OnExecuteAuthenticatedAsync(QBittorrentClient client, CommandLineApplication app, IConsole console) { var apiVersion = await client.GetApiVersionAsync(); var categories = apiVersion >= new ApiVersion(2, 1, 1) ? await client.GetCategoriesAsync() : (await client.GetPartialDataAsync()).CategoriesChanged; Print(categories.Values); return(ExitCodes.Success); }
protected override async Task <int> OnExecuteAuthenticatedAsync(QBittorrentClient client, CommandLineApplication app, IConsole console) { var apiVersion = await client.GetApiVersionAsync(); if (apiVersion < new ApiVersion(2, 1)) { console.WriteLineColored("The --save-path parameter requires qBittorrent 4.1.3 or later.", ColorScheme.Current.Warning); return(ExitCodes.Failure); } await client.EditCategoryAsync(Name, SavePath); return(ExitCodes.Success); }
public async Task EnsureLoggedInAsync() { try { await _client.GetApiVersionAsync(); } catch (QBittorrentClientRequestException e) { if (e.StatusCode == HttpStatusCode.Forbidden) { _logger.LogWarning("Qbittorrent logged out, logging in Now"); await _client.LoginAsync(_qbittorrentSetting.Username, _qbittorrentSetting.Password); } } }
protected async Task WarnIfNotSupported(QBittorrentClient client, IConsole console, ApiVersion version, string message, bool max, params object[] properties) { if (properties == null || properties.All(p => p == null)) { return; } if ((await client.GetApiVersionAsync() < version) ^ max) { console.WriteLineColored(message, ColorScheme.Current.Warning); } }
private static async void Bot_OnMessage(object sender, MessageEventArgs e) { string messageText = e.Message.Text; ChatId chatId = e.Message.Chat.Id; try { if (chatId.Identifier == _chatId.Identifier) { if (messageText.ToLower() == "/status") { var startTimeInEt = TimeZoneInfo.ConvertTime(_startDateTime, GetEasternTimeZone()); await _botClient.SendTextMessageAsync( chatId : _chatId, text : string.Join(Environment.NewLine, $"Status is good. Running on container '{Environment.MachineName}'. Platform is {GetOsPlatform()}.", $"Bot uptime is {DateTimeOffset.UtcNow - _startDateTime} (since {startTimeInEt})."), parseMode : ParseMode.Html ); } else if (messageText.ToLower() == "/qbittorrentstatus" && string.IsNullOrEmpty(_qBittorrentServer) == false) { // Instantiate the client QBittorrentClient qBittorrentClient = new QBittorrentClient(new Uri($"http://{_qBittorrentServer}:8080")); ApiVersion apiVersion = default; try { await qBittorrentClient.LoginAsync(_qBittorrentUsername, _qBittorrentPassword); apiVersion = await qBittorrentClient.GetApiVersionAsync(); } catch (HttpRequestException ex) when(ex.InnerException is SocketException || ex is QBittorrentClientRequestException) { if ((ex.InnerException as SocketException)?.ErrorCode == 111) { // This means the login failed, which we will handle below. await _botClient.SendTextMessageAsync( chatId : _chatId, text : "There was an error communicating with the qBittorrent server. It may be offline."); return; } if ((ex as QBittorrentClientRequestException)?.StatusCode == HttpStatusCode.Forbidden) { // This means the login failed, which we will handle below. await _botClient.SendTextMessageAsync( chatId : _chatId, text : "There was an error communicating with the qBittorrent server. You may have the wrong username/password."); return; } throw; } // If we get here the server is online. await _botClient.SendTextMessageAsync( chatId : _chatId, text : $"The qBittorrent server is online and running API version {apiVersion}."); } else { foreach (string messageLine in messageText.Split(Environment.NewLine)) { if (Uri.TryCreate(messageLine, UriKind.Absolute, out Uri uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { // Check if our data directory exists. If not, we can't download anything. if (Directory.Exists("/data/") == false) { await _botClient.SendTextMessageAsync( chatId : _chatId, text : "Data directory mapping does not exist. Cannot process video download request."); break; } // We got a valid URL, now check if it's a YouTube URL // (This handles youtube.com and youtu.be) if (messageLine.Contains("youtu", StringComparison.OrdinalIgnoreCase)) { string processName = "yt-dlp"; string args = $"-f bestvideo+bestaudio -o \"%(uploader)s [%(channel_id)s]/%(upload_date)s [%(id)s].%(ext)s\" --sponsorblock-mark all --sponsorblock-chapter-title \"%(category_names)l\" {messageLine}"; Console.WriteLine($"Got a YouTube URL: {uri}. Executing command '{processName} {args}'"); Process process = Process.Start(new ProcessStartInfo { FileName = processName, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true, WorkingDirectory = "/data/" }); // This is important to cause OutputDataReceived to be fired process.BeginOutputReadLine(); Message outputMessage = default; List <string> output = new List <string>(); string lastLine = string.Empty; DateTimeOffset lastMessageSent = DateTimeOffset.MinValue; process.OutputDataReceived += async(_, dataReceivedEventArgs) => { string newLine = dataReceivedEventArgs.Data; // Don't print blank lines if (string.IsNullOrWhiteSpace(newLine)) { return; } Console.WriteLine(newLine); // If the last line contains a % and this line contains a %, replace the last line with this one. if (lastLine.Contains('%') && newLine.Contains('%')) { output.Remove(lastLine); } output.Add(newLine); lastLine = newLine; // Don't send messages more often than 1/s // Do this skip after we've updated the output so that we get all of it by the end, even if we don't send it. if (DateTimeOffset.Now - lastMessageSent < TimeSpan.FromSeconds(1)) { return; } lastMessageSent = DateTimeOffset.Now; if (outputMessage is null) { outputMessage = await _botClient.SendTextMessageAsync( chatId : _chatId, text : string.Join(Environment.NewLine, output)); } // Only edit the message if the text has changed else { try { await _botClient.EditMessageTextAsync( chatId : _chatId, messageId : outputMessage.MessageId, text : string.Join(Environment.NewLine, output)); } catch { // Don't let this any exceptions kill us. // We could get MessageIsNotModifiedException if we try to edit with the same text. // We could get HttpRequestException if we try to edit in too rapid succession. } } }; process.WaitForExit(); // After the process exits, make sure we actually wrote the whole output while (true) { try { await _botClient.EditMessageTextAsync( chatId : _chatId, messageId : outputMessage.MessageId, text : string.Join(Environment.NewLine, output)); break; } catch (HttpRequestException) { } catch (MessageIsNotModifiedException) { // This means we were able to successfully write the final message. break; } } Console.WriteLine($"yt-dlp return value is {process.ExitCode}"); if (process.ExitCode == 0) { outputMessage = await _botClient.SendTextMessageAsync( chatId : _chatId, text : "Video downloaded successfully!"); } else { outputMessage = await _botClient.SendTextMessageAsync( chatId : _chatId, text : "Video download failed."); } } } } } } } catch (Exception ex) { await _botClient.SendTextMessageAsync( chatId : _chatId, text : "There was an error processing your request."); Console.WriteLine(string.Join(Environment.NewLine, "Processing Error.", $"Message: {messageText}", $"Sender name / ID: {e.Message.Chat.Username} / {chatId}", "", ex.ToString())); } }