private async Task<string> HandleCaptcha(Captcha captcha) { Brush previous = this.captchaHeader.Foreground; this.captchaHeader.Foreground = Brushes.Red; try { return await this.captcha.Solve(captcha); } catch (TaskCanceledException) { return null; } finally { this.captchaHeader.Foreground = previous; } }
public Task<string> Solve(Captcha captcha) { Action dispose = null, initialize = null; EventHandler timerHandler = null; KeyEventHandler keyHandler = null; RoutedEventHandler playHandler = null; RoutedEventHandler solveHandler = null; RoutedEventHandler reloadHandler = null; RoutedEventHandler cancelHandler = null; RoutedEventHandler toAudioHandler = null; RoutedEventHandler toTextHandler = null; TaskCompletionSource<string> completion = new TaskCompletionSource<string>(); DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; initialize = () => { this.panel.Visibility = Visibility.Visible; this.Refresh(captcha); this.ApplyButtons(captcha); this.play.Click += playHandler; this.solve.Click += solveHandler; this.solve.IsEnabled = false; this.reload.Click += reloadHandler; this.reload.IsEnabled = true; this.cancel.Click += cancelHandler; this.cancel.IsEnabled = true; this.toAudio.Click += toAudioHandler; this.toText.Click += toTextHandler; this.text.KeyDown += keyHandler; timer.Tick += timerHandler; timer.Start(); }; dispose = () => { this.panel.Visibility = Visibility.Collapsed; timer.Tick -= timerHandler; timer.Stop(); this.play.Click -= playHandler; this.solve.Click -= solveHandler; this.reload.Click -= reloadHandler; this.cancel.Click -= cancelHandler; this.toAudio.Click -= toAudioHandler; this.toText.Click -= toTextHandler; this.text.KeyDown -= keyHandler; this.image.Source = null; this.text.Clear(); }; playHandler = async (sender, args) => { this.Lock(); try { await captcha.Play(); } finally { this.Unlock(); } }; solveHandler = (sender, args) => { completion.SetResult(this.text.Text.Trim()); dispose.Invoke(); }; keyHandler = (sender, args) => { if (this.solve.IsEnabled == true && args.Key == Key.Enter) { completion.SetResult(this.text.Text.Trim()); dispose.Invoke(); } }; reloadHandler = async (sender, args) => { this.Lock(); try { await captcha.Reload.Invoke(); this.Refresh(captcha); } finally { this.Unlock(); } }; toAudioHandler = async (sender, args) => { this.Lock(); try { await captcha.ToAudio.Invoke(); this.Refresh(captcha); this.ApplyButtons(captcha); } finally { this.Unlock(); } }; toTextHandler = async (sender, args) => { this.Lock(); try { await captcha.ToImage.Invoke(); this.Refresh(captcha); this.ApplyButtons(captcha); } finally { this.Unlock(); } }; cancelHandler = (sender, args) => { dispose.Invoke(); completion.SetResult(null); }; timerHandler = (sender, args) => { if (captcha.Cancellation.IsCancellationRequested == true) { dispose.Invoke(); completion.TrySetCanceled(captcha.Cancellation); } }; initialize.Invoke(); return completion.Task; }
private void Refresh(Captcha captcha) { if (captcha.Type == "image") { this.image.Source = captcha.ToBitmap(); this.play.Visibility = Visibility.Collapsed; } if (captcha.Type == "audio") { this.image.Source = null; this.play.Visibility = Visibility.Visible; } }
private void ApplyButtons(Captcha captcha) { int count = 3; if (captcha.Type == "image" && captcha.ToAudio != null) { count++; this.toAudio.Visibility = Visibility.Visible; } else { this.toAudio.Visibility = Visibility.Collapsed; } if (captcha.Type == "audio" && captcha.ToImage != null) { count++; this.toText.Visibility = Visibility.Visible; } else { this.toText.Visibility = Visibility.Collapsed; } this.text.Margin = new Thickness(0, 0, 5 + 100 * count, 0); }
private async Task<PhantomResponse> CallPhantom(ResourceTask task) { Regex regex = new Regex(@"to wait (?<minutes>[0-9]+) minutes"); PhantomResponse response = new PhantomResponse { Lines = new List<string>() }; task.OnStatus.Invoke("starting"); task.OnLog.Information("Starting PhantomJS."); ProcessStartInfo info = new ProcessStartInfo { FileName = GetPhantomPath(), Arguments = GetScriptPath(task.Hosting) + " download " + task.Url.ToString(), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, WorkingDirectory = GetDataPath() }; using (Process process = Process.Start(info)) { PhantomCallback callback = new PhantomCallback { OnDownload = url => { response.DownloadUrl = url; return false; }, OnMessage = message => { Match match = regex.Match(message); if (match.Success == true) { if (match.Groups["minutes"].Success == true) { response.Waiting = TimeSpan.FromMinutes(Int32.Parse(match.Groups["minutes"].Value)); } } return true; }, OnDebug = text => { task.OnLog.Debug(text); return true; }, OnFatal = text => { task.OnLog.Debug(text); return true; }, OnDumpImage = base64 => { task.OnLog.Debug("PhantomJS dumped an image.", Convert.FromBase64String(base64), "image"); return true; }, OnDumpHtml = base64 => { task.OnLog.Debug("PhantomJS dumped an html content.", Convert.FromBase64String(base64), "text"); return true; }, OnFileName = text => true, OnFileSize = text => true, OnFileStatus = text => true, OnFallback = text => true, OnRaw = line => { }, }; callback.OnCaptcha = async url => { string solution; task.Cancellation.ThrowIfCancellationRequested(); task.OnLog.Information("Handling captcha."); using (WebClient client = new WebClient()) { task.OnStatus("decaptching"); TimeSpan timeout = TimeSpan.FromMinutes(3); CancellationTokenSource source = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, task.Cancellation); Captcha captcha = new Captcha { Type = "image", Data = client.DownloadData(url), Cancellation = source.Token }; Action debug = () => { switch (captcha.Type) { case "image": task.OnLog.Debug("Got captcha image data.", captcha.Data, "image"); break; case "audio": task.OnLog.Debug("Got captcha audio data.", captcha.Data, "audio"); break; } }; PhantomCallback local = callback.Override(new PhantomCallback { OnCaptcha = async reloadUrl => { source = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, task.Cancellation); captcha.Cancellation = source.Token; captcha.Data = await client.DownloadDataTaskAsync(reloadUrl); debug.Invoke(); return false; } }); debug.Invoke(); captcha.Reload = async () => { await process.StandardInput.WriteLineAsync("::reload::"); task.OnLog.Information("Reloading captcha."); await this.HandleInThread(local, task.Cancellation, process); }; captcha.ToAudio = async () => { await process.StandardInput.WriteLineAsync("::audio::"); task.OnLog.Information("Switching to audio."); captcha.Type = "audio"; await this.HandleInThread(local, task.Cancellation, process); }; captcha.ToImage = async () => { await process.StandardInput.WriteLineAsync("::image::"); task.OnLog.Information("Switching to image."); captcha.Type = "image"; await this.HandleInThread(local, task.Cancellation, process); }; solution = await task.OnCaptcha.Invoke(captcha); task.OnStatus("working"); } task.Cancellation.ThrowIfCancellationRequested(); task.OnLog.Information("Sending captcha."); await process.StandardInput.WriteLineAsync(solution); return true; }; try { task.OnStatus("working"); await this.Handle(callback, task.Cancellation, process); process.WaitForExit(); return response; } finally { if (process.HasExited == false) { process.Kill(); } } } }