Skip to content

Commit 24597df

Browse files
committed
Update to edit and also truncate better for Bluesky
1 parent 2521e7b commit 24597df

2 files changed

Lines changed: 70 additions & 16 deletions

File tree

bin/event

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -147,29 +147,45 @@ def cmd_publish(args)
147147
exit 1
148148
end
149149

150-
# Replace escaped newlines so they render correctly in Readline
150+
# Replace escaped newlines so they render correctly
151151
default_text = default_text.gsub('\\n', "\n")
152152

153-
puts "\nEdit your post text (press Enter to accept):"
153+
puts "\nOpening your editor to tweak the post text..."
154+
require "tempfile"
154155

155-
Readline.pre_input_hook = -> {
156-
Readline.insert_text(default_text)
157-
Readline.redisplay
158-
Readline.pre_input_hook = nil
159-
}
156+
file = Tempfile.new(["social_post", ".md"])
157+
file.write(default_text)
158+
file.close
160159

161-
final_text = Readline.readline("> ", false)
160+
editor = ENV['EDITOR'] || 'nano'
161+
final_text = ""
162162

163-
puts "\nFinal Text:"
164-
puts "---"
165-
puts final_text
166-
puts "---"
167-
print "Publish this? (y/n): "
168-
unless $stdin.gets.strip.downcase == 'y'
169-
puts "Aborted."
170-
exit 0
163+
loop do
164+
system("#{editor} #{file.path}")
165+
final_text = File.read(file.path).strip
166+
167+
puts "\nFinal Text:"
168+
puts "---"
169+
puts final_text
170+
puts "---"
171+
print "Publish this? (y)es/(N)o/(e)dit: "
172+
173+
answer = $stdin.gets.strip.downcase
174+
175+
if answer == 'y' || answer == 'yes'
176+
break
177+
elsif answer == 'e' || answer == 'edit'
178+
puts "\nReopening editor..."
179+
next
180+
else
181+
puts "Aborted."
182+
file.unlink
183+
exit 0
184+
end
171185
end
172186

187+
file.unlink
188+
173189
updates_made = false
174190
new_content = selected[:content]
175191

lib/publishers/bluesky_publisher.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def name = "Bluesky"
1414
def front_matter_key = "bluesky_post_url"
1515

1616
def post(text, reply_to: nil)
17+
text = smart_truncate(text)
18+
1719
handle = fetch_op_secret(OP_HANDLE)
1820
return { error: "Failed to retrieve Bluesky handle from 1Password at #{OP_HANDLE}" } unless handle
1921

@@ -62,6 +64,42 @@ def post(text, reply_to: nil)
6264

6365
private
6466

67+
def smart_truncate(text, limit = 300)
68+
return text if text.grapheme_clusters.length <= limit
69+
70+
lines = text.lines
71+
footer_start = lines.size
72+
73+
# Identify footer: from the bottom up, collect lines that are empty, contain a URL, or contain a hashtag
74+
lines.reverse.each_with_index do |line, i|
75+
if line.strip.empty? || line.match?(/https?:\/\//) || line.match?(/(?<=\s|^)#\w+/)
76+
footer_start = lines.size - 1 - i
77+
else
78+
break
79+
end
80+
end
81+
82+
footer_start = lines.size - 1 if footer_start == 0
83+
84+
header_and_body = lines[0...footer_start].join
85+
footer = lines[footer_start..-1].join
86+
clean_footer = footer.strip
87+
88+
if clean_footer.empty?
89+
return text.grapheme_clusters.take(limit - 1).join.strip + "…"
90+
end
91+
92+
# We add "…\n\n" between body and footer, which is 3 graphemes
93+
allowed_len = limit - clean_footer.grapheme_clusters.length - 3
94+
95+
if allowed_len <= 0
96+
return text.grapheme_clusters.take(limit - 1).join.strip + "…"
97+
end
98+
99+
truncated_body = header_and_body.grapheme_clusters.take(allowed_len).join.strip
100+
"#{truncated_body}\n\n#{clean_footer}"
101+
end
102+
65103
def create_session(handle, app_password)
66104
uri = URI("#{API}/xrpc/com.atproto.server.createSession")
67105
body = { "identifier" => handle, "password" => app_password }

0 commit comments

Comments
 (0)