Class: Fedipub::Actor

Inherits:
ApplicationRecord show all
Includes:
Announceable, ApplicationActor, HandlesDeleteRequests, HasUuid, Likeable
Defined in:
app/models/fedipub/actor.rb

Overview

Model storing distant actors and links to local ones.

To make a model act as an actor, use the Fedipub::ActorEntity concern

See also:

Defined Under Namespace

Classes: TombstonedError

Constant Summary

Constants included from ApplicationActor

Fedipub::ApplicationActor::APPLICATION_ACTOR_ATTRIBUTES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Announceable

#announce!

Methods included from Likeable

#dislike!, #like!

Methods included from HasUuid

#to_param, #uuid

Class Method Details

.find_by_account(account) ⇒ Fedipub::Actor?

Searches for an actor from account URI

Parameters:

  • account (String) —

    Account URI (username@host)

Returns:



179
180
181
182
183
184
185
186
187
188
189
190
# File 'app/models/fedipub/actor.rb', line 179

def ()
  parts = Fediverse::Webfinger. 

  if Fediverse::Webfinger.local_user? parts
    actor = find_local_by_username! parts[:username]
  else
    actor = find_by username: parts[:username], server: parts[:domain]
    actor ||= Fediverse::Webfinger.fetch_actor(parts[:username], parts[:domain])
  end

  actor
end

.find_by_federation_url(federated_url) ⇒ Object



192
193
194
195
196
197
198
199
200
# File 'app/models/fedipub/actor.rb', line 192

def find_by_federation_url(federated_url)
  local_route = Utils::Host.local_route federated_url
  return find_param(local_route[:id]) if local_route && local_route[:controller] == 'fedipub/server/actors' && local_route[:action] == 'show'

  actor = find_by federated_url: federated_url
  return actor if actor

  Fediverse::Webfinger.fetch_actor_url(federated_url)
end

.find_by_federation_url!(federated_url) ⇒ Object



202
203
204
205
206
207
# File 'app/models/fedipub/actor.rb', line 202

def find_by_federation_url!(federated_url)
  find_by_federation_url(federated_url).tap do |actor|
    raise Fedipub::Actor::TombstonedError if actor.tombstoned?
    raise ActiveRecord::RecordNotFound if actor.nil?
  end
end

.find_local_by_username(username) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'app/models/fedipub/actor.rb', line 237

def find_local_by_username(username)
  actor = nil
  Fedipub::Configuration.actor_types.each_value do |entity|
    break if actor.present?

    actor = entity[:class].find_by(entity[:username_field] => username)&.fedipub_actor
  end
  return actor if actor

  # Look for pure actors (e.g. the application actor)
  actor = Fedipub::Actor.local.find_by username: username
  return actor if actor

  # Last hope: Search for tombstoned actors
  Fedipub::Actor.local.tombstoned.find_by username: username
end

.find_local_by_username!(username) ⇒ Object



254
255
256
257
258
# File 'app/models/fedipub/actor.rb', line 254

def find_local_by_username!(username)
  find_local_by_username(username).tap do |actor|
    raise ActiveRecord::RecordNotFound if actor.nil?
  end
end

.find_or_create_by_account(account) ⇒ Object



209
210
211
212
213
214
215
# File 'app/models/fedipub/actor.rb', line 209

def ()
  actor =  
  # Create/update distant actors
  actor.save! unless actor.local?

  actor
end

.find_or_create_by_federation_url(url) ⇒ Object



217
218
219
220
221
222
223
# File 'app/models/fedipub/actor.rb', line 217

def find_or_create_by_federation_url(url)
  actor = find_by_federation_url url
  # Create/update distant actors
  actor.save! unless actor.local?

  actor
end

.find_or_create_by_object(object) ⇒ Object

Find or create actor from a given actor hash or actor id (actor's URL)



226
227
228
229
230
231
232
233
234
235
# File 'app/models/fedipub/actor.rb', line 226

def find_or_create_by_object(object)
  case object
  when String
    find_or_create_by_federation_url object
  when Hash
    find_or_create_by_federation_url object['id']
  else
    raise "Unsupported object type for actor (#{object.class})"
  end
end

Instance Method Details

#acct_uri ⇒ Object



117
118
119
# File 'app/models/fedipub/actor.rb', line 117

def acct_uri
  "acct:#{username}@#{server}"
end

#actor_type ⇒ Object



80
81
82
# File 'app/models/fedipub/actor.rb', line 80

def actor_type
  use_entity_attributes? ? entity_configuration[:actor_type] : attributes['actor_type']
end

#at_address(prefix: '@') ⇒ Object



109
110
111
# File 'app/models/fedipub/actor.rb', line 109

def at_address(prefix: '@')
  "#{prefix}#{username}@#{server}"
end

#distant? ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
# File 'app/models/fedipub/actor.rb', line 56

def distant?
  !local?
end

#entity_configuration ⇒ Object



141
142
143
144
145
# File 'app/models/fedipub/actor.rb', line 141

def entity_configuration
  raise("Entity not configured for #{entity_type}. Did you use \"acts_as_fedipub_actor\"?") unless Fedipub.actor_entity? entity_type

  Fedipub.actor_entity entity_type
end

#federated_url ⇒ Object



60
61
62
# File 'app/models/fedipub/actor.rb', line 60

def federated_url
  use_entity_attributes? || application_actor? ? Fedipub::Engine.routes.url_helpers.server_actor_url(self) : attributes['federated_url'].presence
end

#followed_by?(actor) ⇒ Fedipub::Following, false

Checks if current actor is followed by the given actor

Returns:



134
135
136
137
138
139
# File 'app/models/fedipub/actor.rb', line 134

def followed_by?(actor)
  list = following_followers.where actor: actor
  return list.first if list.one?

  false
end

#followers_url ⇒ Object



92
93
94
# File 'app/models/fedipub/actor.rb', line 92

def followers_url
  use_entity_attributes? || application_actor? ? Fedipub::Engine.routes.url_helpers.followers_server_actor_url(self) : attributes['followers_url']
end

#followings_url ⇒ Object



96
97
98
# File 'app/models/fedipub/actor.rb', line 96

def followings_url
  use_entity_attributes? || application_actor? ? Fedipub::Engine.routes.url_helpers.following_server_actor_url(self) : attributes['followings_url']
end

#follows?(actor) ⇒ Fedipub::Following, false

Checks if a given actor follows the current actor

Returns:



124
125
126
127
128
129
# File 'app/models/fedipub/actor.rb', line 124

def follows?(actor)
  list = following_follows.where target_actor: actor
  return list.first if list.one?

  false
end

#inbox_url ⇒ Object



84
85
86
# File 'app/models/fedipub/actor.rb', line 84

def inbox_url
  use_entity_attributes? || application_actor? ? Fedipub::Engine.routes.url_helpers.server_actor_inbox_url(self) : attributes['inbox_url']
end

#key_id ⇒ Object



271
272
273
# File 'app/models/fedipub/actor.rb', line 271

def key_id
  "#{federated_url}#main-key"
end

#name ⇒ Object



70
71
72
73
74
# File 'app/models/fedipub/actor.rb', line 70

def name
  value = (entity.send(entity_configuration[:name_field]).to_s if use_entity_attributes?)

  value || attributes['name'] || username
end

#outbox_url ⇒ Object



88
89
90
# File 'app/models/fedipub/actor.rb', line 88

def outbox_url
  use_entity_attributes? || application_actor? ? Fedipub::Engine.routes.url_helpers.server_actor_outbox_url(self) : attributes['outbox_url']
end

#private_key ⇒ Object



266
267
268
269
# File 'app/models/fedipub/actor.rb', line 266

def private_key
  ensure_key_pair_exists!
  self[:private_key]
end

#profile_url ⇒ Object



100
101
102
103
104
105
106
107
# File 'app/models/fedipub/actor.rb', line 100

def profile_url
  return attributes['profile_url'].presence unless use_entity_attributes?

  method = entity_configuration[:profile_url_method]
  return Fedipub::Engine.routes.url_helpers.server_actor_url self unless method

  Rails.application.routes.url_helpers.send method, [entity]
end

#public_key ⇒ Object



261
262
263
264
# File 'app/models/fedipub/actor.rb', line 261

def public_key
  ensure_key_pair_exists!
  self[:public_key]
end

#server ⇒ Object



76
77
78
# File 'app/models/fedipub/actor.rb', line 76

def server
  use_entity_attributes? || application_actor? ? Utils::Host.localhost : attributes['server']
end

#short_at_address ⇒ Object



113
114
115
# File 'app/models/fedipub/actor.rb', line 113

def short_at_address
  use_entity_attributes? ? "@#{username}" : at_address
end

#sync! ⇒ Object

Synchronizes actor with distant data

Raises:

  • (ActiveRecord::RecordNotFound) —

    when distant data was not found



150
151
152
153
154
155
156
157
158
159
160
# File 'app/models/fedipub/actor.rb', line 150

def sync!
  if local?
    Rails.logger.info 'Ignored attempt to sync a local actor'
    return false
  end

  response = Fediverse::Webfinger.fetch_actor_url(federated_url)
  new_attributes = response.attributes.except 'id', 'uuid', 'created_at', 'updated_at', 'local', 'entity_id', 'entity_type'

  update! new_attributes
end

#tombstone! ⇒ Object



166
167
168
# File 'app/models/fedipub/actor.rb', line 166

def tombstone!
  Fedipub::Utils::Actor.tombstone! self
end

#tombstoned? ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'app/models/fedipub/actor.rb', line 162

def tombstoned?
  tombstoned_at.present?
end

#untombstone! ⇒ Object



170
171
172
# File 'app/models/fedipub/actor.rb', line 170

def untombstone!
  Fedipub::Utils::Actor.untombstone! self
end

#username ⇒ Object



64
65
66
67
68
# File 'app/models/fedipub/actor.rb', line 64

def username
  return attributes['username'] unless use_entity_attributes?

  entity.send(entity_configuration[:username_field]).to_s
end