Class: Tempest::Post
- Inherits:
-
Data
- Object
- Data
- Tempest::Post
- Defined in:
- lib/tempest/post.rb
Constant Summary collapse
- EMBED_KINDS =
AT Protocol embed ‘$type` values mapped to short symbols used by the REPL. `record` (quote) and `external` (link card) are intentionally absent: they’re surfaced through other UI (URL annotation), so they don’t get a media-marker emoji.
{ "app.bsky.embed.images" => :images, "app.bsky.embed.video" => :video, }.freeze
Instance Attribute Summary collapse
-
#cid ⇒ Object
readonly
Returns the value of attribute cid.
-
#created_at ⇒ Object
readonly
Returns the value of attribute created_at.
-
#display_name ⇒ Object
readonly
Returns the value of attribute display_name.
-
#embed_kind ⇒ Object
readonly
Returns the value of attribute embed_kind.
-
#facets ⇒ Object
readonly
Returns the value of attribute facets.
-
#handle ⇒ Object
readonly
Returns the value of attribute handle.
-
#reply_parent_uri ⇒ Object
readonly
Returns the value of attribute reply_parent_uri.
-
#text ⇒ Object
readonly
Returns the value of attribute text.
-
#uri ⇒ Object
readonly
Returns the value of attribute uri.
Class Method Summary collapse
-
.create(client, did:, text:, reply: nil, langs: nil, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) ⇒ Object
Compose a record for com.atproto.repo.createRecord (app.bsky.feed.post).
-
.detect_link_facets(text) ⇒ Object
Scans ‘text` for bare URLs and builds AT Protocol link facets pointing at each match.
-
.embed_kind_from(embed) ⇒ Object
The view-side ‘$type` carries a `#view` suffix (e.g. `app.bsky.embed.images#view`); the raw record uses the bare form.
- .from_feed_view(post) ⇒ Object
-
.like(client, did:, subject_uri:, subject_cid:, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) ⇒ Object
Compose an app.bsky.feed.like record referencing the subject post and send it via com.atproto.repo.createRecord.
Instance Method Summary collapse
-
#initialize(uri:, cid:, handle:, display_name:, text:, created_at:, facets: [], reply_parent_uri: nil, embed_kind: nil) ⇒ Post
constructor
A new instance of Post.
Constructor Details
#initialize(uri:, cid:, handle:, display_name:, text:, created_at:, facets: [], reply_parent_uri: nil, embed_kind: nil) ⇒ Post
Returns a new instance of Post.
15 16 17 18 |
# File 'lib/tempest/post.rb', line 15 def initialize(uri:, cid:, handle:, display_name:, text:, created_at:, facets: [], reply_parent_uri: nil, embed_kind: nil) super end |
Instance Attribute Details
#cid ⇒ Object (readonly)
Returns the value of attribute cid
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def cid @cid end |
#created_at ⇒ Object (readonly)
Returns the value of attribute created_at
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def created_at @created_at end |
#display_name ⇒ Object (readonly)
Returns the value of attribute display_name
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def display_name @display_name end |
#embed_kind ⇒ Object (readonly)
Returns the value of attribute embed_kind
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def @embed_kind end |
#facets ⇒ Object (readonly)
Returns the value of attribute facets
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def facets @facets end |
#handle ⇒ Object (readonly)
Returns the value of attribute handle
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def handle @handle end |
#reply_parent_uri ⇒ Object (readonly)
Returns the value of attribute reply_parent_uri
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def reply_parent_uri @reply_parent_uri end |
#text ⇒ Object (readonly)
Returns the value of attribute text
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def text @text end |
#uri ⇒ Object (readonly)
Returns the value of attribute uri
5 6 7 |
# File 'lib/tempest/post.rb', line 5 def uri @uri end |
Class Method Details
.create(client, did:, text:, reply: nil, langs: nil, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) ⇒ Object
Compose a record for com.atproto.repo.createRecord (app.bsky.feed.post). When ‘reply` is provided, both root and parent are set to the same target. This is correct for top-level replies and a known v1 trade-off for replies deeper in a thread (AppView will nest the reply under `parent` instead of the original conversation root).
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 |
# File 'lib/tempest/post.rb', line 54 def self.create(client, did:, text:, reply: nil, langs: nil, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) record = { "$type" => "app.bsky.feed.post", "text" => text, "createdAt" => created_at, } if reply ref = { "uri" => reply[:uri], "cid" => reply[:cid] } record["reply"] = { "root" => ref, "parent" => ref } end record["langs"] = langs if langs && !langs.empty? link_facets = detect_link_facets(text) record["facets"] = link_facets unless link_facets.empty? client.post( "com.atproto.repo.createRecord", body: { repo: did, collection: "app.bsky.feed.post", record: record, }, ) end |
.detect_link_facets(text) ⇒ Object
Scans ‘text` for bare URLs and builds AT Protocol link facets pointing at each match. Without this, the AppView treats URLs as plain text and does not render them as clickable links.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/tempest/post.rb', line 104 def self.detect_link_facets(text) return [] if text.nil? || text.empty? bytes = text.b facets = [] pos = 0 while (match = /https?:\/\/\S+/n.match(bytes, pos)) byte_start = match.begin(0) byte_end = match.end(0) uri = match[0].dup.force_encoding(Encoding::UTF_8) facets << { "index" => { "byteStart" => byte_start, "byteEnd" => byte_end }, "features" => [ { "$type" => "app.bsky.richtext.facet#link", "uri" => uri }, ], } pos = byte_end end facets end |
.embed_kind_from(embed) ⇒ Object
The view-side ‘$type` carries a `#view` suffix (e.g. `app.bsky.embed.images#view`); the raw record uses the bare form. Strip the suffix before looking up so both feed and Jetstream payloads classify identically.
43 44 45 46 47 |
# File 'lib/tempest/post.rb', line 43 def self.() return nil unless .is_a?(Hash) type = ["$type"].to_s.sub(/#view\z/, "") EMBED_KINDS[type] end |
.from_feed_view(post) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/tempest/post.rb', line 20 def self.from_feed_view(post) post = post || {} = post["author"] || {} record = post["record"] || {} reply = record["reply"] reply_parent = reply.is_a?(Hash) ? reply["parent"] : nil new( uri: post["uri"], cid: post["cid"], handle: ["handle"], display_name: ["displayName"], text: record["text"], created_at: record["createdAt"], facets: Facet.parse(record["facets"]), reply_parent_uri: reply_parent.is_a?(Hash) ? reply_parent["uri"] : nil, embed_kind: (post["embed"] || record["embed"]), ) end |
.like(client, did:, subject_uri:, subject_cid:, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) ⇒ Object
Compose an app.bsky.feed.like record referencing the subject post and send it via com.atproto.repo.createRecord. The AppView surfaces this in like counts and notifications for the target post.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/tempest/post.rb', line 84 def self.like(client, did:, subject_uri:, subject_cid:, created_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ")) record = { "$type" => "app.bsky.feed.like", "subject" => { "uri" => subject_uri, "cid" => subject_cid }, "createdAt" => created_at, } client.post( "com.atproto.repo.createRecord", body: { repo: did, collection: "app.bsky.feed.like", record: record, }, ) end |