Compare commits
No commits in common. "1dd0083b5d40f114f83bb2829201dab8178411d4" and "38b5defebf2fb67e6fe29d50b4c0ed7c823bf096" have entirely different histories.
1dd0083b5d
...
38b5defebf
12
README.md
12
README.md
|
@ -1,12 +0,0 @@
|
|||
# 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
|
|
@ -1,5 +0,0 @@
|
|||
example.com
|
||||
|
||||
example.org
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
|
||||
example.net
|
53
src/list.rs
53
src/list.rs
|
@ -1,53 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
mod source;
|
||||
mod tests;
|
||||
|
||||
pub use source::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Action {
|
||||
Block,
|
||||
Silence,
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
69
src/list/mod.rs
Normal file
69
src/list/mod.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
use std::{collections::HashMap, fs};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Source {
|
||||
pub actions: HashMap<String, Action>,
|
||||
pub trust: u16,
|
||||
}
|
||||
|
||||
impl Default for Source {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
actions: HashMap::new(),
|
||||
trust: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn add_from_file(&mut self, path: &str, action: Action) -> &mut Self {
|
||||
let contents = fs::read_to_string(path).unwrap();
|
||||
for host in contents.lines().filter(|line| !line.is_empty()) {
|
||||
self.add_action(host, action);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn add_action(&mut self, host: &str, action: Action) -> &mut Self {
|
||||
self.actions.insert(host.to_string(), action);
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_action() {
|
||||
let mut at = ActionTrust::default();
|
||||
|
||||
at.add_action(Action::Block, 123)
|
||||
.add_action(Action::Silence, 456);
|
||||
|
||||
let test_at = ActionTrust {
|
||||
block: 123,
|
||||
silence: 456,
|
||||
};
|
||||
|
||||
assert_eq!(at, test_at);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_action_overlap() {
|
||||
let mut at = ActionTrust::default();
|
||||
|
||||
at.add_action(Action::Block, 123)
|
||||
.add_action(Action::Block, 333)
|
||||
.add_action(Action::Silence, 123);
|
||||
|
||||
let test_at = ActionTrust {
|
||||
block: 456,
|
||||
silence: 123,
|
||||
};
|
||||
|
||||
assert_eq!(at, test_at);
|
||||
}
|
||||
|
||||
#[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 source_add_from_file() {
|
||||
let mut src = Source::default();
|
||||
src.add_from_file("example_blocklist1.txt", Action::Block)
|
||||
.add_from_file("example_mutelist1.txt", Action::Silence);
|
||||
|
||||
let test_src = Source::from(HashMap::from([
|
||||
(String::from("example.com"), Action::Block),
|
||||
(String::from("example.org"), Action::Block),
|
||||
(String::from("example.net"), Action::Silence),
|
||||
]));
|
||||
|
||||
assert_eq!(test_src, src);
|
||||
}
|
||||
|
||||
#[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 mut src2 = Source::default();
|
||||
src2.add_from_file("example_blocklist1.txt", Action::Block)
|
||||
.add_from_file("example_mutelist1.txt", 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);
|
||||
}
|
16
src/main.rs
16
src/main.rs
|
@ -1,10 +1,20 @@
|
|||
// src/main.rs
|
||||
|
||||
mod list;
|
||||
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() {
|
||||
println!("Hello, world!");
|
||||
// TODO argument parsing
|
||||
// TODO logging
|
||||
// TODO config file
|
||||
}
|
62
src/tests.rs
Normal file
62
src/tests.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use list::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn add_lists() {
|
||||
let hosts1 = vec![
|
||||
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);
|
||||
let src2 = SourceList::from(hosts2);
|
||||
let src3 = SourceList::from(hosts3);
|
||||
|
||||
let mut output = TrustMaps::new();
|
||||
output.add_source(&src1).add_source(&src2).add_source(&src3);
|
||||
|
||||
let test_map = HashMap::from([
|
||||
(String::from("example.com"), 300),
|
||||
(String::from("example.org"), 200),
|
||||
(String::from("example.net"), 100),
|
||||
]);
|
||||
assert_eq!(test_map, output.blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_lists() {
|
||||
let hosts1 = vec![
|
||||
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);
|
||||
let src2 = SourceList::from(hosts2);
|
||||
let src3 = SourceList::from(hosts3);
|
||||
|
||||
let mut output = TrustMaps::new();
|
||||
output
|
||||
.add_source(&src1)
|
||||
.add_source(&src2)
|
||||
.add_source(&src3)
|
||||
.remove_source(&src3)
|
||||
.remove_source(&src3)
|
||||
.remove_source(&src3)
|
||||
.remove_source(&src3);
|
||||
|
||||
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