Class: Jekyll::ActivityPub::Notifier
- Inherits:
-
Object
- Object
- Jekyll::ActivityPub::Notifier
- Defined in:
- lib/jekyll/activity_pub/notifier.rb
Overview
Long term store for notifications.
Needs to be a singleton so we can use the same data across all of Jekyll’s build process.
Defined Under Namespace
Classes: PseudoObject
Class Method Summary collapse
- .actor ⇒ Object
- .actor=(actor) ⇒ Object
- .actor_url ⇒ Object
- .actor_url=(url) ⇒ Object
-
.announce? ⇒ Boolean
Announce the website?.
- .client ⇒ Object
-
.create(path, **opts) ⇒ nil
Creates an activity unless it has been already created.
-
.created?(path) ⇒ Boolean
Already created.
-
.data ⇒ Hash
Return data.
-
.delete(path) ⇒ nil
Removes an activity if it was previously created.
- .deleted?(path) ⇒ Boolean
- .dereferencer ⇒ Object
-
.done?(path) ⇒ Boolean
Detects if an action was already done.
- .exist?(path) ⇒ Boolean
- .followers_url ⇒ String
- .inbox ⇒ Object
-
.likes(activity) ⇒ DistributedPress::V1::Social::Likes
Generates a Likes client per activity.
- .manually_approves_followers? ⇒ Boolean
-
.notify! ⇒ Object
Send notifications.
- .outbox ⇒ Object
-
.path ⇒ String
Returns the path for the storage.
-
.public_key_url ⇒ String
Public key URL, raises error if missing.
-
.public_key_url=(url) ⇒ Object
Save the public key URL for later.
-
.relative_path ⇒ String
Storage path relative to site source.
-
.replies(activity) ⇒ DistributedPress::V1::Social::Replies
Generates a Replies client per activity.
-
.save ⇒ nil
Stores data back to a file and optionally commits it.
-
.shares(activity) ⇒ DistributedPress::V1::Social::Shares
Generates a Shares client per activity.
- .site ⇒ Jekyll::Site
-
.site=(site) ⇒ Jekyll::Site
Set the site and initialize data.
-
.status(path) ⇒ Hash
Gets status.
-
.update(path, **opts) ⇒ nil
Updates an activity if it was previously created, otherwise create it, or update it again if it was modified later.
- .updated?(path) ⇒ Boolean
- .url ⇒ Object
Class Method Details
.actor ⇒ Object
106 107 108 109 110 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 106 def actor data['actor'].tap do |a| abort_if_missing('actor', a) end end |
.actor=(actor) ⇒ Object
102 103 104 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 102 def actor=(actor) data['actor'] = actor end |
.actor_url ⇒ Object
116 117 118 119 120 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 116 def actor_url data['actor_url'].tap do |au| abort_if_missing('actor_url', au) end end |
.actor_url=(url) ⇒ Object
112 113 114 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 112 def actor_url=(url) data['actor_url'] = url end |
.announce? ⇒ Boolean
Announce the website?
67 68 69 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 67 def announce? !!config['announce'] end |
.client ⇒ Object
288 289 290 291 292 293 294 295 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 288 def client @@client ||= DistributedPress::V1::Social::Client.new( private_key_pem: private_key, url: url, public_key_url: public_key_url, logger: Jekyll.logger ) end |
.create(path, **opts) ⇒ nil
Creates an activity unless it has been already created
205 206 207 208 209 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 205 def create(path, **opts) action(path, 'create', **opts) unless created?(path) nil end |
.created?(path) ⇒ Boolean
Already created
231 232 233 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 231 def created?(path) !status(path)['created_at'].nil? end |
.data ⇒ Hash
Return data
173 174 175 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 173 def data @@data ||= site.data['activity_pub'] end |
.delete(path) ⇒ nil
Removes an activity if it was previously created
181 182 183 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 181 def delete(path) action(path, 'delete') if exist?(path) && !deleted?(path) end |
.deleted?(path) ⇒ Boolean
241 242 243 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 241 def deleted?(path) !status(path)['deleted_at'].nil? end |
.dereferencer ⇒ Object
62 63 64 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 62 def dereferencer @@dereferencer ||= DistributedPress::V1::Social::Dereferencer.new(client: client) end |
.done?(path) ⇒ Boolean
Detects if an action was already done
222 223 224 225 226 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 222 def done?(path) (status(path)['action'] == 'done').tap do |done| Jekyll.logger.debug('ActivityPub:', "Skipping notification for #{path}") if done end end |
.exist?(path) ⇒ Boolean
245 246 247 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 245 def exist?(path) !status(path)['action'].nil? end |
.followers_url ⇒ String
82 83 84 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 82 def followers_url "#{url}/v1/#{actor}/followers" end |
.inbox ⇒ Object
297 298 299 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 297 def inbox @@inbox ||= DistributedPress::V1::Social::Inbox.new(client: client, actor: actor) end |
.likes(activity) ⇒ DistributedPress::V1::Social::Likes
Generates a Likes client per activity
318 319 320 321 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 318 def likes(activity) @@likes ||= {} @@likes[activity] ||= DistributedPress::V1::Social::Likes.new(client: client, actor: actor, activity: activity) end |
.manually_approves_followers? ⇒ Boolean
71 72 73 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 71 def manually_approves_followers? !!config['manually_approves_followers'] end |
.notify! ⇒ Object
Send notifications
-
Wait for public key propagation
-
Create/update inbox
-
Send create, update, and delete
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 127 def notify! # TODO: request several times with a timeout response = HTTParty.get(public_key_url) unless response.ok? raise NotificationError, "Could't fetch public key (#{response.code}: #{response.}). It needs to be available at #{public_key_url} before notifications can work, because it's used for signature verification." end unless client.private_key.compare? OpenSSL::PKey::RSA.new(response.parsed_response.dig('publicKey', 'publicKeyPem')) raise NotificationError, "Public key at #{public_key_url} differs from local version" end actor_object = object_for(site.in_dest_dir(URI.parse(actor_url).path)) # Create/Update inbox unless (response = inbox.create(actor_url, announce: announce?, manually_approves_followers: manually_approves_followers?)).ok? raise NotificationError, "Couldn't create/update inbox (#{response.code}: #{response.})" end # Remove notifications already performed and notify data['notifications'].reject do |object_url, _| done? object_url end.each do |object_url, status| process_object(actor_object, object_for(object_url), status) end # Update actor profile if actor_object.updated_at > actor_object.date Jekyll.logger.debug 'ActivityPub:', 'Updating Actor profile' actor_update = Jekyll::ActivityPub::Update.new(site, actor_object, actor_object) unless (response = outbox.post(activity: actor_update)).ok? raise NotificationError, "Couldn't update actor (#{response.code}: #{response.})" end end # Store everything for later save rescue NotificationError => e Jekyll.logger.abort_with 'ActivityPub:', e. end |
.outbox ⇒ Object
301 302 303 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 301 def outbox @@outbox ||= DistributedPress::V1::Social::Outbox.new(client: client, actor: actor) end |
.path ⇒ String
Returns the path for the storage
277 278 279 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 277 def path @@path ||= site.in_source_dir(site.config['data_dir'], 'activity_pub.yml') end |
.public_key_url ⇒ String
Public key URL, raises error if missing
96 97 98 99 100 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 96 def public_key_url data['public_key_url'].tap do |pk| abort_if_missing('public_key_url', pk) end end |
.public_key_url=(url) ⇒ Object
Save the public key URL for later
89 90 91 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 89 def public_key_url=(url) data['public_key_url'] = url end |
.relative_path ⇒ String
Storage path relative to site source
284 285 286 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 284 def relative_path @@relative_path ||= Pathname.new(path).relative_path_from(site.source).to_s end |
.replies(activity) ⇒ DistributedPress::V1::Social::Replies
Generates a Replies client per activity
309 310 311 312 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 309 def replies(activity) @@replies ||= {} @@replies[activity] ||= DistributedPress::V1::Social::Replies.new(client: client, actor: actor, activity: activity) end |
.save ⇒ nil
Stores data back to a file and optionally commits it
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 252 def save # TODO: Send warning if CI is detected Jekyll.logger.info 'ActivityPub:', "Saving data to #{relative_path}" FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') do |f| f.flock(File::LOCK_EX) f.rewind f.write(YAML.dump(data.reject { |_, v| v.empty? })) f.flush f.truncate(f.pos) end if ENV['JEKYLL_ENV'] == 'production' && site.respond_to?(:repository) site.staged_files << relative_path site.repository.commit 'ActivityPub' end nil end |
.shares(activity) ⇒ DistributedPress::V1::Social::Shares
Generates a Shares client per activity
327 328 329 330 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 327 def shares(activity) @@shares ||= {} @@shares[activity] ||= DistributedPress::V1::Social::Shares.new(client: client, actor: actor, activity: activity) end |
.site ⇒ Jekyll::Site
58 59 60 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 58 def site @@site end |
.site=(site) ⇒ Jekyll::Site
Set the site and initialize data
50 51 52 53 54 55 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 50 def site=(site) @@site = site.tap do |s| s.data['activity_pub'] ||= {} s.data['activity_pub']['notifications'] ||= {} end end |
.status(path) ⇒ Hash
Gets status
215 216 217 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 215 def status(path) data['notifications'][path_relative_to_dest(path)] || {} end |
.update(path, **opts) ⇒ nil
Updates an activity if it was previously created, otherwise create it, or update it again if it was modified later
190 191 192 193 194 195 196 197 198 199 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 190 def update(path, **opts) # Compare Unix timestamps if created?(path) && (object_for(path)&.updated_at&.to_i || 0) > (status(path)['updated_at'] || 0) action(path, 'update', **opts) else create(path, **opts) end nil end |
.updated?(path) ⇒ Boolean
236 237 238 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 236 def updated?(path) !status(path)['updated_at'].nil? end |
.url ⇒ Object
75 76 77 78 79 |
# File 'lib/jekyll/activity_pub/notifier.rb', line 75 def url config['url'].tap do |u| abort_if_missing('activity_pub.url', u, '_config.yml') end end |