Skip to content

Commit cccddcb

Browse files
committed
Update
1 parent 260b279 commit cccddcb

21 files changed

Lines changed: 495 additions & 186 deletions

File tree

_config.i2p.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,13 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p
7+
nostr:
8+
spam_api_overwrite: http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/spam.nostr.band/spam_api
9+
profile_relays_overwrite:
10+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/purplepag.es
11+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/hist.nostr.land
12+
relays_overwrite:
13+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr
14+
read_cache_relays_overwrite:
15+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr
16+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/relay.primal.net

_config.tor.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion
7+
nostr:
8+
spam_api_overwrite: http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr/spam.nostr.band/spam_api
9+
profile_relays_overwrite:
10+
- wss://purplepag.es
11+
- wss://hist.nostr.land
12+
relays_overwrite:
13+
- ws://oxtrdevav64z64yb7x6rjg4ntzqjhedm5b5zjqulugknhzr46ny2qbad.onion
14+
- ws://gnostr2jnapk72mnagq3cuykfon73temzp77hcbncn4silgt77boruid.onion
15+
read_cache_relays_overwrite:
16+
- ws://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr
17+
- ws://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr/relay.primal.net

_config.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,27 @@ theme_settings:
126126
zapthreads: true
127127

128128
nostr:
129+
enabled: true
129130
npub: npub1alptdev5srcw2hxg03567p4k6xs3lgj7f6545suc0rzp0xw98svse7rg94
130131
community: codonaft
131132
max_comment_length: 5000
132133
min_read_pow: 7
134+
135+
spam_api: https://spam.nostr.band/spam_api
136+
profile_relays:
137+
- wss://purplepag.es
138+
- wss://hist.nostr.land
133139
relays:
134140
- wss://nostr-pub.wellorder.net
135141
- wss://relay.nostr.band
136142
read_cache_relays:
137143
- wss://relay.primal.net
138144

145+
spam_api_overwrite: ''
146+
profile_relays_overwrite: []
147+
relays_overwrite: []
148+
read_cache_relays_overwrite: []
149+
139150
special_page:
140151
search:
141152
icon: search # Assuming page link and icon are the same

_do.cr

Lines changed: 130 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require "json"
1212
require "uri"
1313
require "yaml"
1414

15-
DEBUG = ARGV.empty? || !["sync", "build", "deploy", "health"].includes?(ARGV[0])
15+
DEBUG = ARGV.empty? || !["sync", "sync-nostr", "build", "deploy", "health"].includes?(ARGV[0])
1616
TRACE = ENV["TRACE"]? == "1"
1717

1818
WILDCARD_HOST = "0.0.0.0"
@@ -74,6 +74,9 @@ def main
7474
elsif ARGV.size == 1 && ARGV[0] == "sync"
7575
check
7676
sync
77+
elsif ARGV.size >= 1 && ARGV[0] == "sync-nostr"
78+
profiles = ARGV.size > 1 && ARGV[1] == "profiles"
79+
sync_nostr(config, profiles: profiles, output_relays: ["ws://localhost:7777", "wss://nostr.codonaft.com", "wss://purplepag.es", "wss://nostr.oxtr.dev", "wss://nostr.girino.org"])
7780
elsif ARGV.size == 1 && ARGV[0] == "health"
7881
check
7982
health(config)
@@ -126,6 +129,10 @@ def usage
126129
download newest non-conflicting versioned files to _ohmyvps and _hosts
127130
upload newest files from _ohmyvps, _hosts and .build
128131
132+
#{script} sync-nostr [profile]
133+
fetch nostr events, push them to my relays
134+
don't send events to profile-specific relays by default
135+
129136
#{script} health
130137
run health checks
131138
@@ -392,6 +399,13 @@ def health(config)
392399
end
393400
step("health")
394401

402+
all_hosts().each { |host|
403+
step(host)
404+
ssh(host, ["sudo /etc/init.d/nginx checkconfig", "sudo hdparm -t /dev/sd[a-z] | sed \"s!.*= !!\""], tty: true)
405+
}
406+
407+
warn("github commit hash is different\n") unless JSON.parse(HTTP::Client.get("https://api.github.com/repos/codonaft/codonaft.github.io/commits").body)[0]["sha"].as_s == `git rev-parse HEAD`.strip
408+
395409
banlists =
396410
if nonempty_exists?(BANLISTS) && File.info(BANLISTS).modification_time + 1.days > Time.utc
397411
File.read_lines(BANLISTS).to_set
@@ -461,7 +475,7 @@ def health(config)
461475
.map { |i| i.strip }
462476
.reject { |i| i.empty? }
463477
.map { |i| URI.parse(i) }
464-
.reject { |i| i.hostname.nil? || i.hostname == "127.0.0.1" } + relay_mirrors
478+
.reject { |i| i.hostname.nil? || i.hostname == "localhost" } + relay_mirrors
465479
wss_uris = (trackers + other_relays + `git grep --only-matching 'wss://[a-zA-Z0-9/._-]*'`
466480
.split('\n')
467481
.map { |i| i.strip.gsub(/.*wss:\/\//, "") }
@@ -714,7 +728,6 @@ def check
714728
step("check")
715729

716730
ps = processes()
717-
718731
current_ps_name = "/#{Path[__FILE__].basename}"
719732
raise "other instance keeps running" if ps
720733
.select { |(_, i)| i.includes?("crystal") && i.includes?(current_ps_name) }
@@ -723,6 +736,8 @@ def check
723736
puts("checking dependencies")
724737
system("which bsdtar bundle css-minify node pnpm podman rsync scour svgcleaner svgo uglifyjs wget >>/dev/null")
725738
raise "missing deps, run: cd && npm install 'css-minify@2.0.0' 'svgo@3.3.2' && USE='lz4 xxhash zstd' emerge '=app-arch/libarchive-3.7.9' '=app-arch/unzip-6.0_p27-r1' '=app-containers/podman-5.3.2' '=dev-ruby/bundler-2.4.22' '=dev-lang/crystal-1.16.1' '=dev-util/uglifyjs-3.16.1' '=media-gfx/scour-0.38.2-r1' '=media-sound/opus-tools-0.2-r1' '=media-libs/libwebp-1.4.0' '=media-video/mediainfo-24.11' '=net-dns/bind-tools-9.18.0-r1' '=net-libs/nodejs-22.13.1' '=net-misc/rsync-3.4.1' '=net-misc/wget-1.25.0' '=sys-apps/pnpm-bin-9.6.0' && cargo install --locked 'svgcleaner@0.9.5' 'websocat@1.13.0'" unless $?.success?
739+
system("podman ps >>/dev/null")
740+
raise "podman failed" unless $?.success?
726741

727742
system(<<-STRING
728743
set -euo pipefail
@@ -1120,6 +1135,117 @@ def check_manual_upload(host : String, *, owner : String, group : String, mode :
11201135
end
11211136
end
11221137

1138+
def sync_nostr(config, *, profiles : Bool, output_relays : Array(String))
1139+
# TODO: do the same thing as cron bash job that uses rust nostr cli client? connect to relays over tor?
1140+
1141+
limit = 100
1142+
now = Time.utc
1143+
from = now - 3.years # TODO: last commit date? last blog date? previous blog date?
1144+
to = now + 15.minutes
1145+
profile_kinds = [0, 3, 10002]
1146+
1147+
nostr_config = config["theme_settings"]["nostr"]
1148+
npub = nostr_config["npub"].as_s
1149+
pk = JSON.parse(`nak decode #{npub}`)["pubkey"].as_s
1150+
1151+
profile_relays = nostr_config["profile_relays"].as_a.map { |i| i.as_s }
1152+
relays = nostr_config["relays"].as_a.map { |i| i.as_s }
1153+
read_cache_relays = nostr_config["read_cache_relays"].as_a.map { |i| i.as_s }
1154+
input_relays : Array(String) = relays + read_cache_relays + profile_relays
1155+
1156+
step("sync-nostr")
1157+
trace("pk=#{pk} input_relays=#{input_relays} output_relays=#{output_relays}")
1158+
1159+
nak = "nak req --limit #{limit} --since #{from.to_unix} --until #{to.to_unix}"
1160+
1161+
mentions = parse_nostr_events(`#{nak} -p #{pk} #{input_relays.join(' ')}`)
1162+
.reject { |i|
1163+
return false unless i["tags"].as_a.any? { |t| t.size > 1 && t[0] == "p" && t[1] == pk }
1164+
k = i["kind"].as_i
1165+
[3, 1984, 4454].includes?(k) || (k >= 5000 && k <= 7000) || (k >= 10000 && k <= 10102) || (k >= 20000 && k < 30000) || (k >= 30000 && k <= 30267)
1166+
}
1167+
puts("fetched #{mentions.size} mentions")
1168+
1169+
parsed_authored_events = parse_nostr_events(`#{nak} -a #{pk} #{input_relays.join(' ')}`)
1170+
authored_events = parsed_authored_events
1171+
.reject { |i|
1172+
return false unless i["pubkey"].as_s == pk
1173+
k = i["kind"].as_i
1174+
[4454, 10044].includes?(k) || (k >= 20000 && k < 30000)
1175+
}
1176+
puts("fetched #{authored_events.size} authored events")
1177+
1178+
puts("writing events")
1179+
nak_raw(["event"] + output_relays, (mentions + authored_events).map { |i| i.to_json })
1180+
1181+
if profiles
1182+
# TODO: check spam
1183+
fetch_comments_command = ([nak, "-k", "1", "-p", pk] + input_relays).join(' ')
1184+
trace(fetch_comments_command)
1185+
comments_pks = parse_nostr_events(`#{fetch_comments_command}`)
1186+
.select { |i| i["kind"].as_i == 1 && i["pubkey"].as_s != pk }
1187+
.map { |i| i["pubkey"].as_s }.to_set
1188+
request_profiles_command = (
1189+
[nak, "--since", "0", "-k", "0"] + comments_pks.map { |i| "-a #{i}" } + input_relays
1190+
).join(' ')
1191+
trace(request_profiles_command)
1192+
comments_profile_events = parse_nostr_events(`#{request_profiles_command}`)
1193+
.select { |i| i["kind"].as_i == 0 }
1194+
.group_by { |i| i["pubkey"].as_s }
1195+
.reject { |p, _| p == pk }
1196+
.map { |_, g| g.max_by { |i| i["created_at"].as_i } }
1197+
puts("profile events of commenters = #{comments_profile_events.size}")
1198+
1199+
request_author_profile_events_command = (
1200+
[nak, "--since", "0", "-a", pk] + profile_kinds.map { |k| "-k #{k}" } + input_relays
1201+
).join(' ')
1202+
trace(request_author_profile_events_command)
1203+
author_profile_events = parse_nostr_events(`#{request_author_profile_events_command}`)
1204+
.select { |i| profile_kinds.includes?(i["kind"].as_i) }
1205+
puts("author profile events = #{author_profile_events.size}")
1206+
nak_raw(["event"] + profile_relays, (comments_profile_events + author_profile_events).map { |i| i.to_json })
1207+
end
1208+
puts("finished writing events")
1209+
end
1210+
1211+
def nak_raw(args, input : Array(String) = [] of String)
1212+
# TODO: exit after timeout
1213+
output = Channel(Tuple(String, Process::Status)).new
1214+
trace("running nak #{args}")
1215+
spawn do
1216+
value = Process.run(command: "nak", args: args) do |p|
1217+
input.each do |line|
1218+
trace("writing stdin")
1219+
p.input.puts(line)
1220+
trace("writing stdin ok")
1221+
end
1222+
trace("closing stdin")
1223+
p.input.close
1224+
trace("closed stdin")
1225+
trace("reading stdout")
1226+
result = p.output.gets_to_end
1227+
trace("finished reading stdout")
1228+
result
1229+
end
1230+
trace("sending stdout")
1231+
output.send({value.strip, $?})
1232+
trace("sent stdout")
1233+
end
1234+
trace("waiting for stdout")
1235+
value, status = output.receive
1236+
trace("received stdout")
1237+
raise "#{args}: unexpected exit code #{status}, value=#{value}" unless status.success?
1238+
value
1239+
end
1240+
1241+
def parse_nostr_events(text)
1242+
text
1243+
.split('\n')
1244+
.reject { |i| i.strip.empty? }
1245+
.to_set
1246+
.map { |i| JSON.parse(i) }
1247+
end
1248+
11231249
def processes
11241250
Dir.entries("/proc")
11251251
.select { |entry| entry =~ /^\d+$/ }
@@ -1153,6 +1279,7 @@ def children_recursive_inner(dir : Path) : Array(Path)
11531279
end
11541280

11551281
def all_hosts : Array(String)
1282+
# TODO: from args
11561283
Dir.children(HOSTS_DIR)
11571284
end
11581285

_hosts/media.codonaft/etc/init.d/rnostr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ name="rnostr"
55
command="/usr/local/bin/${name}"
66
command_user="${name}:${name}"
77
command_background="true"
8-
command_args="relay --watch -c /etc/${name}/${name}.toml"
8+
command_args="relay -c /etc/${name}/${name}.toml"
9+
retry="60"
910

1011
pidfile="/run/${name}.pid"
1112
output_logger="logger"
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
#!/usr/bin/env sh
1+
#!/usr/bin/env bash
22

33
[ -f /etc/init.d/aquatic_ws ] && [ -d /var/www ] && \
4-
inotifywait --event close_write,moved_to,create --monitor --recursive /etc/aquatic/ /etc/init.d/ /var/www/ |
4+
inotifywait --event close_write,moved_to,create --monitor --recursive /etc/aquatic/ /var/www/ |
55
while read -r directory events filename ; do
6-
if [[ "${filename}" == "aquatic_ws.conf" || "${filename}" == "aquatic_ws" || "${filename}" == *".m3u8" ]] ; then
6+
if [[ "${filename}" == "aquatic_ws.conf" || "${filename}" == *".m3u8" ]] ; then
77
rc-service --ifstarted aquatic_ws reload 2>>/dev/stdout | logger
88
fi
99
done &
1010

11-
[ -f /etc/init.d/broadcastr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/broadcastr/ /etc/conf.d/ |
11+
[ -f /etc/init.d/broadcastr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/broadcastr/ |
1212
while read -r directory events filename ; do
13-
if [[ "${filename}" == *".json" || "${filename}" == "broadcastr" ]] ; then
13+
if [[ "${filename}" == *".json" ]] ; then
1414
rc-service --ifstarted broadcastr restart 2>>/dev/stdout | logger
1515
fi
1616
done &
1717

18-
# using rnostr relay --watch for now, however it's unclear whether it's working correctly
19-
# [ -f /etc/init.d/rnostr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/rnostr/ |
20-
# while read -r directory events filename ; do
21-
# if [[ "${filename}" == "rnostr.toml" ]] ; then
22-
# rc-service --ifstarted rnostr restart 2>>/dev/stdout | logger
23-
# fi
24-
# done &
18+
# rnostr relay --watch does nothing (probably under load only)
19+
[ -f /etc/init.d/rnostr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/rnostr/ |
20+
while read -r directory events filename ; do
21+
if [[ "${filename}" == "rnostr.toml" ]] ; then
22+
rc-service --ifstarted rnostr restart 2>>/dev/stdout | logger
23+
fi
24+
done &

0 commit comments

Comments
 (0)