A bin/importlist.tcl => bin/importlist.tcl +84 -0
@@ 0,0 1,84 @@
+#!/bin/env tclsh
+
+if {$argc < 2} { return -code error "Usage: bin/importlist.tcl DB ?-pds PDS? ?-feed FEED? LIST ?LIST ...?\n-feed may be passed repeatedly." }
+
+cd [file dirname [file dirname [dict get [info frame [info frame]] file]]]
+
+package require sqlite3
+package require json
+package require http
+package require tls
+::http::register https 443 ::tls::socket
+
+set lists [lassign $argv db]
+sqlite3 db $db
+
+set pds "bsky.social"
+if {[lindex $lists 0] == "-pds"} {
+ set lists [lassign $lists _ pds]
+}
+
+set block 1
+while {[lindex $lists 0] == "-feed"} {
+ set block 0
+ set lists [lassign $lists _ feed]
+ lappend feeds $feed
+}
+
+if ![info exists feeds] {
+ set feeds [db eval {SELECT name FROM sqlite_master WHERE type = 'table' AND sql LIKE '%uri TEXT%'}]
+}
+
+foreach list $lists {
+ switch -regexp -matchvar matches -- $list {
+ {^at://.+} {}
+ {^(?:(?:(?:(?:(?:https://)?bsky.app)?/)?profile)?/)?([^/]+)/lists/(.+)$} {
+ lassign $matches _ pub rkey
+
+ set endpoint "https://$pds/xrpc/com.atproto.repo.describeRepo"
+
+ set http_state [http::geturl "$endpoint?repo=$pub"]
+ if {[::http::ncode $http_state] != 200} { return -code error "couldn't resolve handle $repo!\n[::http::data $http_state]" }
+ set pub [dict get [::json::json2dict [::http::data $http_state]] did]
+
+
+ set list "at://$pub/app.bsky.graph.list/$rkey"
+ }
+ default {
+ return -code error "unknown list URI format $list!"
+ }
+ }
+
+ set endpoint "https://public.api.bsky.app/xrpc/app.bsky.graph.getList"
+
+ set http_state [http::geturl "$endpoint?list=$list"]
+ if {[::http::ncode $http_state] != 200} { return -code error "couldn't resolve list $list!\n[::http::data $http_state]" }
+ set data [::json::json2dict [::http::data $http_state]]
+ set items [dict get $data items]
+
+ while {[llength $items]} {
+ foreach item $items {
+ set repo [dict get $item subject did]
+ if $block {
+ puts "Blocking $repo from $list."
+ db eval {INSERT OR IGNORE INTO blocked_repos VALUES (:repo);}
+ } else { puts "Removing $repo from feeds." }
+ set repo_pat "%${repo}%"
+ foreach table $feeds {
+ db eval "DELETE FROM $table WHERE uri LIKE :repo_pat;"
+ }
+ }
+
+ if ![dict exists $data cursor] {
+ break
+ }
+
+ set cursor [dict get $data cursor]
+
+ set http_state [http::geturl "$endpoint?list=$list&cursor=$cursor"]
+ if {[::http::ncode $http_state] != 200} { return -code error "couldn't read list $list at pos $cursor!\n[::http::data $http_state]" }
+ set data [::json::json2dict [::http::data $http_state]]
+ set items [dict get $data items]
+ }
+}
+
M bin/rmrepo.tcl => bin/rmrepo.tcl +5 -1
@@ 17,7 17,9 @@ set pds "bsky.social"
if {[lindex $repos 0] == "-pds"} {
set repos [lassign $repos _ pds]
}
+set block 1
while {[lindex $repos 0] == "-feed"} {
+ set block 0
set repos [lassign $repos _ feed]
lappend feeds $feed
}
@@ 38,7 40,9 @@ foreach repo $repos {
}
}
- db eval {INSERT OR IGNORE INTO blocked_repos VALUES (:repo);}
+ if $block {
+ db eval {INSERT OR IGNORE INTO blocked_repos VALUES (:repo);}
+ }
set repo_pat "%${repo}%"
foreach table $feeds {
db eval "DELETE FROM $table WHERE uri LIKE :repo_pat;"
A bin/updatelists.tcl => bin/updatelists.tcl +18 -0
@@ 0,0 1,18 @@
+#!/bin/env tclsh
+
+if {$argc != 1} { return -code error "Usage: bin/updatelists.tcl DB" }
+set uris [lassign $argv db]
+
+cd [file dirname [file dirname [dict get [info frame [info frame]] file]]]
+
+puts "importing rahaeli's lists..."
+exec bin/importlist.tcl $db \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3lbh3ebjhfv24 \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3lbgroz3w4c2i \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3lasodgegrc2a \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3l6do6yblno2a \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3l4d4bgyso72w \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3l42hr55vwl2o \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3l42hebnldp2w \
+ https://bsky.app/profile/rahaeli.bsky.social/lists/3l42gs6pmns22
+puts "imported!"