Add README, rewrite key structs/methods
This commit is contained in:
parent
38b5defebf
commit
88a0b80e45
12
README.md
Normal file
12
README.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# fediloom 🪢
|
||||||
|
|
||||||
|
Goal - a commandline-based tool for creating blocklists for ActivityPub software ("the Fediverse")
|
||||||
|
|
||||||
|
## Planned features
|
||||||
|
|
||||||
|
- [ ] Accept and parse commandline arguments
|
||||||
|
- [ ] Add ability to specify reasons
|
||||||
|
- [ ] Use URLs as sources
|
||||||
|
- [ ] Directly request lists from API endpoints
|
||||||
|
- [ ] Create tiered or thresholded lists
|
||||||
|
- [ ] Support CSV and JSON1
|
71
src/list.rs
Normal file
71
src/list.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum Action {
|
||||||
|
Block,
|
||||||
|
Silence,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Source {
|
||||||
|
pub actions: HashMap<String, Action>,
|
||||||
|
pub trust: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HashMap<String, Action>> for Source {
|
||||||
|
fn from(map: HashMap<String, Action>) -> Self {
|
||||||
|
Self {
|
||||||
|
actions: map,
|
||||||
|
trust: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Source {
|
||||||
|
pub fn build(map: HashMap<String, Action>, trust: u16) -> Self {
|
||||||
|
let mut src = Self::from(map);
|
||||||
|
src.trust = trust;
|
||||||
|
src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct ActionTrust {
|
||||||
|
pub block: u16,
|
||||||
|
pub silence: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionTrust {
|
||||||
|
pub fn add_action(&mut self, action: Action, trust: u16) -> &mut Self {
|
||||||
|
match action {
|
||||||
|
Action::Block => self.block += trust,
|
||||||
|
Action::Silence => self.silence += trust,
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(u16, u16)> for ActionTrust {
|
||||||
|
fn from(value: (u16, u16)) -> Self {
|
||||||
|
Self {
|
||||||
|
block: value.0,
|
||||||
|
silence: value.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct ModerationList(pub HashMap<String, ActionTrust>);
|
||||||
|
|
||||||
|
impl ModerationList {
|
||||||
|
pub fn add_source(&mut self, src: Source) -> &mut Self {
|
||||||
|
let items = src.actions.into_iter();
|
||||||
|
|
||||||
|
for (host, action) in items {
|
||||||
|
let entry = self.0.entry(host).or_default();
|
||||||
|
entry.add_action(action, src.trust);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub struct SourceList {
|
|
||||||
pub blocks: Vec<String>,
|
|
||||||
pub confidence: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceList {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SourceList {
|
|
||||||
blocks: Vec::new(),
|
|
||||||
confidence: 100,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<String>> for SourceList {
|
|
||||||
fn from(blocked: Vec<String>) -> Self {
|
|
||||||
SourceList {
|
|
||||||
blocks: blocked,
|
|
||||||
confidence: 100,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TrustMaps {
|
|
||||||
pub blocks: HashMap<String, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrustMaps {
|
|
||||||
pub fn new() -> TrustMaps {
|
|
||||||
TrustMaps {
|
|
||||||
blocks: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_source(&mut self, src: &SourceList) -> &mut TrustMaps {
|
|
||||||
for host in src.blocks.iter() {
|
|
||||||
let entry = self.blocks.entry(host.to_string()).or_insert(0);
|
|
||||||
*entry += src.confidence as usize;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_source(&mut self, src: &SourceList) -> &mut TrustMaps {
|
|
||||||
// If blocks already empty, nothing to remove so just return
|
|
||||||
if self.blocks.is_empty() {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over host strings in source list
|
|
||||||
for host in src.blocks.iter() {
|
|
||||||
// If the host isn't in the output list, then skip
|
|
||||||
if !self.blocks.contains_key(host) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Host is valid entry, subtract confidence value and saturate at 0
|
|
||||||
self.blocks.entry(host.to_string()).and_modify(|v| {
|
|
||||||
*v = v.saturating_sub(src.confidence as usize);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, if any value became zero, remove the pair from the map
|
|
||||||
self.blocks.retain(|_, v| *v != 0);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
12
src/main.rs
12
src/main.rs
|
@ -3,18 +3,6 @@
|
||||||
mod list;
|
mod list;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
// things we need to do:
|
|
||||||
// - import 2 or more lists
|
|
||||||
// - assign "trust" percent to each list
|
|
||||||
// - combine lists together and weight each element by trust %s
|
|
||||||
// - export 1 or more lists filtered by heat rating
|
|
||||||
// - tiered lists, e.g.:
|
|
||||||
// - tier 0: instances uniformly blocked
|
|
||||||
// - tier 1: instances mixed blocked and silenced
|
|
||||||
// - tier 2: instances silenced
|
|
||||||
// - tier 3: instances mixed silenced and no action
|
|
||||||
// - tier 4: instances no action (i.e., below all thresholds)
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
}
|
}
|
161
src/tests.rs
161
src/tests.rs
|
@ -1,62 +1,131 @@
|
||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use list::*;
|
use list::*;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_lists() {
|
fn add_action() {
|
||||||
let hosts1 = vec![
|
let mut at = ActionTrust::default();
|
||||||
String::from("example.com"),
|
|
||||||
String::from("example.org"),
|
|
||||||
String::from("example.net"),
|
|
||||||
];
|
|
||||||
let hosts2 = vec![String::from("example.com"), String::from("example.org")];
|
|
||||||
let hosts3 = vec![String::from("example.com")];
|
|
||||||
|
|
||||||
let src1 = SourceList::from(hosts1);
|
at.add_action(Action::Block, 123)
|
||||||
let src2 = SourceList::from(hosts2);
|
.add_action(Action::Silence, 456);
|
||||||
let src3 = SourceList::from(hosts3);
|
|
||||||
|
|
||||||
let mut output = TrustMaps::new();
|
let test_at = ActionTrust {
|
||||||
output.add_source(&src1).add_source(&src2).add_source(&src3);
|
block: 123,
|
||||||
|
silence: 456,
|
||||||
|
};
|
||||||
|
|
||||||
let test_map = HashMap::from([
|
assert_eq!(at, test_at);
|
||||||
(String::from("example.com"), 300),
|
|
||||||
(String::from("example.org"), 200),
|
|
||||||
(String::from("example.net"), 100),
|
|
||||||
]);
|
|
||||||
assert_eq!(test_map, output.blocks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_lists() {
|
fn add_action_overlap() {
|
||||||
let hosts1 = vec![
|
let mut at = ActionTrust::default();
|
||||||
String::from("example.com"),
|
|
||||||
String::from("example.org"),
|
|
||||||
String::from("example.net"),
|
|
||||||
];
|
|
||||||
let hosts2 = vec![String::from("example.com"), String::from("example.org")];
|
|
||||||
let hosts3 = vec![String::from("example.com")];
|
|
||||||
|
|
||||||
let src1 = SourceList::from(hosts1);
|
at.add_action(Action::Block, 123)
|
||||||
let src2 = SourceList::from(hosts2);
|
.add_action(Action::Block, 333)
|
||||||
let src3 = SourceList::from(hosts3);
|
.add_action(Action::Silence, 123);
|
||||||
|
|
||||||
let mut output = TrustMaps::new();
|
let test_at = ActionTrust {
|
||||||
output
|
block: 456,
|
||||||
.add_source(&src1)
|
silence: 123,
|
||||||
.add_source(&src2)
|
};
|
||||||
.add_source(&src3)
|
|
||||||
.remove_source(&src3)
|
assert_eq!(at, test_at);
|
||||||
.remove_source(&src3)
|
}
|
||||||
.remove_source(&src3)
|
|
||||||
.remove_source(&src3);
|
#[test]
|
||||||
|
fn source_new_from_hashmap() {
|
||||||
|
let src1 = Source::from(HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
(String::from("example.net"), Action::Block),
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
src1.actions,
|
||||||
|
HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
(String::from("example.net"), Action::Block),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(src1.trust, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn source_build_from_hashmap() {
|
||||||
|
let src2 = Source::build(
|
||||||
|
HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
(String::from("example.net"), Action::Block),
|
||||||
|
]),
|
||||||
|
123,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
src2.actions,
|
||||||
|
HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
(String::from("example.net"), Action::Block),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
assert_eq!(src2.trust, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_moderation_list() {
|
||||||
|
let mut ml = ModerationList::default();
|
||||||
|
|
||||||
|
let src1 = Source::from(HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
(String::from("example.net"), Action::Block),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let src2 = Source::from(HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Block),
|
||||||
|
(String::from("example.net"), Action::Silence),
|
||||||
|
]));
|
||||||
|
|
||||||
|
ml.add_source(src1).add_source(src2);
|
||||||
|
|
||||||
|
let test_ml = ModerationList(
|
||||||
|
HashMap::from([
|
||||||
|
(String::from("example.com"), ActionTrust::from((200, 0))),
|
||||||
|
(String::from("example.org"), ActionTrust::from((100, 100))),
|
||||||
|
(String::from("example.net"), ActionTrust::from((100, 100))),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(ml, test_ml);
|
||||||
|
|
||||||
|
let src3 = Source::build(HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.org"), Action::Silence),
|
||||||
|
]), 200);
|
||||||
|
|
||||||
|
let src4 = Source::build(HashMap::from([
|
||||||
|
(String::from("example.com"), Action::Block),
|
||||||
|
(String::from("example.net"), Action::Silence),
|
||||||
|
]), 50);
|
||||||
|
|
||||||
|
ml.add_source(src3).add_source(src4);
|
||||||
|
|
||||||
|
let test_ml = ModerationList(
|
||||||
|
HashMap::from([
|
||||||
|
(String::from("example.com"), ActionTrust::from((450, 0))),
|
||||||
|
(String::from("example.org"), ActionTrust::from((100, 300))),
|
||||||
|
(String::from("example.net"), ActionTrust::from((100, 150))),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(ml, test_ml);
|
||||||
|
|
||||||
let test_map = HashMap::from([
|
|
||||||
(String::from("example.org"), 200),
|
|
||||||
(String::from("example.net"), 100),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert_eq!(test_map, output.blocks);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue