From 27bc52c0084ee0beffc2169ba51a8a38216bb33f Mon Sep 17 00:00:00 2001 From: jkaczmarkiewicz <46656790+JanKaczmarkiewicz@users.noreply.github.com> Date: Mon, 16 Oct 2023 19:25:15 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20`Brave`=20for=20the=20search=20engi?= =?UTF-8?q?ne=20(#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: implement brave engine * refactor: correct indentations in stylelint config * docs: add dummy config option to config.lua * feat: implement safe_search_level in brave engine * refactor: move var to format * fix: make strict search above level 1 --- .stylelintrc.json | 4 +- src/engines/brave.rs | 95 +++++++++++++++++++++++++++++++++++++ src/engines/mod.rs | 1 + src/models/engine_models.rs | 4 ++ websurfx/config.lua | 1 + 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/engines/brave.rs diff --git a/.stylelintrc.json b/.stylelintrc.json index 9019f4f..05ffdd4 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,10 +1,10 @@ { - "extends": "stylelint-config-standard", + "extends": "stylelint-config-standard", "rules": { "alpha-value-notation": "number", "selector-class-pattern": null }, -"overrides": [ + "overrides": [ { "files": ["*.js"], "customSyntax": "postcss-lit" diff --git a/src/engines/brave.rs b/src/engines/brave.rs new file mode 100644 index 0000000..5c7c126 --- /dev/null +++ b/src/engines/brave.rs @@ -0,0 +1,95 @@ +//! The `brave` module handles the scraping of results from the brave search engine +//! by querying the upstream brave search engine with user provided query and with a page +//! number if provided. + +use std::collections::HashMap; + +use reqwest::header::HeaderMap; +use scraper::Html; + +use crate::models::aggregation_models::SearchResult; +use error_stack::{Report, Result, ResultExt}; + +use crate::models::engine_models::{EngineError, SearchEngine}; + +use super::search_result_parser::SearchResultParser; + +/// Scrapes the results from the Brave search engine. +pub struct Brave { + /// Utilises generic logic for parsing search results. + parser: SearchResultParser, +} + +impl Brave { + /// Creates the Brave parser. + pub fn new() -> Result { + Ok(Self { + parser: SearchResultParser::new( + "#results h4", + "#results [data-pos]", + "a > .url", + "a", + ".snippet-description", + )?, + }) + } +} + +#[async_trait::async_trait] +impl SearchEngine for Brave { + async fn results( + &self, + query: &str, + page: u32, + user_agent: &str, + request_timeout: u8, + safe_search: u8, + ) -> Result, EngineError> { + let url = format!("https://search.brave.com/search?q={query}&offset={page}"); + + let safe_search_level = match safe_search { + 0 => "off", + 1 => "moderate", + _ => "strict", + }; + + let header_map = HeaderMap::try_from(&HashMap::from([ + ("USER_AGENT".to_string(), user_agent.to_string()), + ( + "CONTENT_TYPE".to_string(), + "application/x-www-form-urlencoded".to_string(), + ), + ("REFERER".to_string(), "https://google.com/".to_string()), + ( + "COOKIE".to_string(), + format!("safe_search={safe_search_level}"), + ), + ])) + .change_context(EngineError::UnexpectedError)?; + + let document: Html = Html::parse_document( + &Brave::fetch_html_from_upstream(self, &url, header_map, request_timeout).await?, + ); + + if let Some(no_result_msg) = self.parser.parse_for_no_results(&document).nth(0) { + if no_result_msg + .inner_html() + .contains("Not many great matches came back for your search") + { + return Err(Report::new(EngineError::EmptyResultSet)); + } + } + + self.parser + .parse_for_results(&document, |title, url, desc| { + url.value().attr("href").map(|url| { + SearchResult::new( + title.text().collect::>().join("").trim(), + url.trim(), + desc.inner_html().trim(), + &["brave"], + ) + }) + }) + } +} diff --git a/src/engines/mod.rs b/src/engines/mod.rs index 39b50c8..2892445 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -3,6 +3,7 @@ //! provide a standard functions to be implemented for all the upstream search engine handling //! code. Moreover, it also provides a custom error for the upstream search engine handling code. +pub mod brave; pub mod duckduckgo; pub mod search_result_parser; pub mod searx; diff --git a/src/models/engine_models.rs b/src/models/engine_models.rs index 05b5a11..98367e8 100644 --- a/src/models/engine_models.rs +++ b/src/models/engine_models.rs @@ -150,6 +150,10 @@ impl EngineHandler { let engine = crate::engines::searx::Searx::new()?; ("searx", Box::new(engine)) } + "brave" => { + let engine = crate::engines::brave::Brave::new()?; + ("brave", Box::new(engine)) + } _ => { return Err(Report::from(EngineError::NoSuchEngineFound( engine_name.to_string(), diff --git a/websurfx/config.lua b/websurfx/config.lua index 09b418d..eeeb638 100644 --- a/websurfx/config.lua +++ b/websurfx/config.lua @@ -52,4 +52,5 @@ redis_url = "redis://127.0.0.1:8082" -- redis connection url address on which th upstream_search_engines = { DuckDuckGo = true, Searx = false, + Brave = false, } -- select the upstream search engines from which the results should be fetched.