1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/env tclsh
# A rough copy of <https://github.com/bluesky-social/feed-generator/blob/main/scripts/publishFeedGen.ts>
package require json
package require json::write
package require http
package require tls
::http::register https 443 ::tls::socket
cd [file dirname [file dirname [dict get [info frame [info frame]] file]]]
source lib/prompt.tcl
source lib/atpagent.tcl
input "Enter your bsky handle:" conf(handle)
paswd "Enter your Bluesky password (preferably an App Password):" conf(password)
input "Optionally, enter a custom PDS service to sign in with:" conf(service) https://bsky.social
input "Enter the host where the feed is available:" conf(feedHost)
input "Enter a short name or the record. This will be shown in the feed's URL:" conf(recordName)
input "Enter a display name for your feed:" conf(displayName)
inopt "Optionally, enter a brief description of your feed:" conf(description)
inopt "Optionally, enter a local path to an avatar that will be used for the feed:" conf(avatar)
atpagent agent $conf(service)
agent login $conf(handle) $conf(password)
if {$conf(description) == ""} {
set description null
} else {
set description [::json::write string $conf(description)]
}
set avatarRef {}
if {$conf(avatar) != {}} {
switch -glob -- $conf(avatar) {
*.png {
set mime image/png
}
*.{jpg,jpeg} {
set mime image/jpeg
}
default {
return -code error "expected png or jpeg avatar"
}
}
set fd [open $conf(avatar) r]
fconfigure $fd -translation binary
set avatar [read $fd]
close $fd
unset fd
lassign [agent xrpc com.atproto.repo.uploadBlob -type $mime $avatar] av_ncode av_data
puts "upload: $av_data"
if {$av_ncode != 200} { return -code error "got $av_ncode uploading avatar!\n$av_data" }
# this sucks.
set blob [::json::write object \
{$type} {"blob"} \
mimeType [::json::write string [dict get $av_data blob mimeType]] \
size [dict get $av_data blob size] \
ref [::json::write object \
{$link} [::json::write string [dict get $av_data blob ref {$link}]]]]
set avatarRef [list avatar $blob]
puts $avatarRef
}
lassign [agent xrpc com.atproto.repo.putRecord \
repo [::json::write string [agent state did]] \
collection {"app.bsky.feed.generator"} \
rkey [::json::write string $conf(recordName)] \
record [::json::write object \
did [::json::write string did:web:$conf(feedHost)] \
displayName [::json::write string $conf(displayName)] \
description $description \
createdAt [::json::write string [clock format [clock seconds] -format "%Y-%m-%dT%T.000Z"]] \
{*}$avatarRef]] pr_ncode pr_data
if {$pr_ncode != 200} { return -code error "got $pr_ncode putting feed record!\n$pr_data" }
puts "All done 🎉"