feat(scraping): friend page is now scrapped

This commit is contained in:
Maieul BOYER 2026-02-08 16:21:04 +01:00
parent c4ef2a129a
commit 73fe9b85a3
Signed by: maix
SSH key fingerprint: SHA256:iqCzqFFF5KjRixmDExqbAltCIj9ndlBWIGJf3t9Ln9g
3 changed files with 117 additions and 8 deletions

View file

@ -0,0 +1,27 @@
use froxy_scrapper::{state::State, types::ClusterLocation};
#[tokio::main(flavor = "current_thread")]
async fn main() {
let session = std::env::var("FROXY_COOKIE")
.expect("provide `FROXY_COOKIE` with the session cookie from you friends42 instance");
let state = State::new(session);
let res = state.get("/friends/", None).await.unwrap();
let text = res.text().await.unwrap();
let location = froxy_scrapper::parsing::friend_page(&text);
for l in &location.friends {
println!(
"{:^10} | {:^10} | {:^30} | {:^20}",
l.login,
l.position.as_ref().map(|s| s.as_str()).unwrap_or("<>"),
l.last_active.as_ref().map(|s| s.as_str()).unwrap_or("<>"),
l.image
.as_ref()
.map(|s| &s[s.len().saturating_sub(20)..])
.unwrap_or("<>")
);
}
}

View file

@ -1,7 +1,8 @@
use scraper::Selector;
use smol_str::SmolStr;
use url::Url;
use crate::types::{ClusterLocation, ClusterLocationData, Relation};
use crate::types::{ClusterLocation, ClusterLocationData, Friend, Friends, Relation};
macro_rules! sel {
($name:ident, $value:literal) => {
@ -88,17 +89,17 @@ pub fn get_cluster_location(
.has_class("pooled", scraper::CaseSensitivity::AsciiCaseInsensitive);
let mut relation = Relation::None;
if is_me {
relation = Relation::Me;
}
if is_friend {
relation = Relation::Friend;
if is_pooled {
relation = Relation::Pooled;
}
if is_close_friend {
relation = Relation::CloseFriend;
}
if is_pooled {
relation = Relation::Pooled;
if is_friend {
relation = Relation::Friend;
}
if is_me {
relation = Relation::Me;
}
if is_focus {
relation = Relation::Focus;
@ -119,3 +120,69 @@ pub fn get_cluster_location(
out
}
sel!(FRIEND_CONT_SEL, "div.container > div > div.card.pl-fix");
sel!(FRIEND_CARD_TITLE, "h5.card-title");
sel!(FRIEND_LAST_ACTIVE, ".card-text");
pub fn friend_page(page: impl AsRef<str>) -> Friends {
let page = page.as_ref();
let doc = scraper::Html::parse_document(page);
let mut out = Friends {
friends: Vec::new(),
};
for f in doc.select(&FRIEND_CONT_SEL) {
let Some(login) = f
.select(&FRIEND_CARD_TITLE)
.next()
.map(|e| {
e.text()
.fold(String::new(), |mut acc: String, s: &str| -> String {
acc += s;
acc
})
})
.map(|s| SmolStr::new(s.trim()))
else {
continue;
};
let img = f
.select(&IMAGE_SEL)
.next()
.and_then(|e| e.attr("src"))
.map(ToString::to_string);
let pos_la = f
.select(&FRIEND_LAST_ACTIVE)
.next()
.map(|e| {
e.text()
.fold(String::new(), |mut acc: String, s: &str| -> String {
acc += s;
acc
})
})
.map(|s| SmolStr::new(s.trim()))
.and_then(|s| {
s.split_once(' ')
.map(|(pos, last_active)| {
(Some(SmolStr::new(pos)), Some(SmolStr::new(last_active)))
})
.or(Some((Some(s), None)))
});
out.friends.push(Friend {
login,
image: img,
position: pos_la
.as_ref()
.and_then(|(pos, _)| pos.clone())
.filter(|s| s != "Absent"),
last_active: pos_la.as_ref().and_then(|(_, la)| la.clone()),
})
}
out
}

View file

@ -1,3 +1,5 @@
use smol_str::SmolStr;
#[non_exhaustive]
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub enum ClusterLocationStatus {
@ -46,3 +48,16 @@ pub struct CluserInformation {
pub cluster_name: smol_str::SmolStr,
pub locations: Vec<ClusterLocation>,
}
#[derive(Debug, Clone)]
pub struct Friend {
pub login: SmolStr,
pub image: Option<String>,
pub position: Option<SmolStr>,
pub last_active: Option<SmolStr>,
}
#[derive(Debug, Clone)]
pub struct Friends {
pub friends: Vec<Friend>,
}