Class: Federails::Actor

Inherits:
ApplicationRecord show all
Includes:
HandlesDeleteRequests, HasUuid
Defined in:
app/models/federails/actor.rb

Overview

Model storing distant actors and links to local ones.

To make a model act as an actor, use the ‘Federails::ActorEntity` concern

See also:

- https://www.w3.org/TR/activitypub/#actor-objects

Defined Under Namespace

Classes: TombstonedError

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HasUuid

#to_param, #uuid

Class Method Details

.find_by_account(account) ⇒ Federails::Actor?

Searches for an actor from account URI

Parameters:

  • account (String)

    Account URI (username@host)

Returns:



172
173
174
175
176
177
178
179
180
181
182
183
# File 'app/models/federails/actor.rb', line 172

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



185
186
187
188
189
190
191
192
193
# File 'app/models/federails/actor.rb', line 185

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] == 'federails/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



195
196
197
198
199
200
# File 'app/models/federails/actor.rb', line 195

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

.find_local_by_username(username) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
# File 'app/models/federails/actor.rb', line 230

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

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

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

.find_local_by_username!(username) ⇒ Object



243
244
245
246
247
# File 'app/models/federails/actor.rb', line 243

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



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

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

  actor
end

.find_or_create_by_federation_url(url) ⇒ Object



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

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)



219
220
221
222
223
224
225
226
227
228
# File 'app/models/federails/actor.rb', line 219

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_uriObject



110
111
112
# File 'app/models/federails/actor.rb', line 110

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

#actor_typeObject



73
74
75
# File 'app/models/federails/actor.rb', line 73

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

#at_address(prefix: '@') ⇒ Object



102
103
104
# File 'app/models/federails/actor.rb', line 102

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

#distant?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'app/models/federails/actor.rb', line 49

def distant?
  !local?
end

#entity_configurationObject



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

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

  Federails.actor_entity entity_type
end

#federated_urlObject



53
54
55
# File 'app/models/federails/actor.rb', line 53

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

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

Checks if current actor is followed by the given actor

Returns:



127
128
129
130
131
132
# File 'app/models/federails/actor.rb', line 127

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

  false
end

#followers_urlObject



85
86
87
# File 'app/models/federails/actor.rb', line 85

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

#followings_urlObject



89
90
91
# File 'app/models/federails/actor.rb', line 89

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

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

Checks if a given actor follows the current actor

Returns:



117
118
119
120
121
122
# File 'app/models/federails/actor.rb', line 117

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

  false
end

#inbox_urlObject



77
78
79
# File 'app/models/federails/actor.rb', line 77

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

#key_idObject



260
261
262
# File 'app/models/federails/actor.rb', line 260

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

#nameObject



63
64
65
66
67
# File 'app/models/federails/actor.rb', line 63

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

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

#outbox_urlObject



81
82
83
# File 'app/models/federails/actor.rb', line 81

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

#private_keyObject



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

def private_key
  ensure_key_pair_exists!
  self[:private_key]
end

#profile_urlObject



93
94
95
96
97
98
99
100
# File 'app/models/federails/actor.rb', line 93

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

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

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

#public_keyObject



250
251
252
253
# File 'app/models/federails/actor.rb', line 250

def public_key
  ensure_key_pair_exists!
  self[:public_key]
end

#serverObject



69
70
71
# File 'app/models/federails/actor.rb', line 69

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

#short_at_addressObject



106
107
108
# File 'app/models/federails/actor.rb', line 106

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



143
144
145
146
147
148
149
150
151
152
153
# File 'app/models/federails/actor.rb', line 143

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



159
160
161
# File 'app/models/federails/actor.rb', line 159

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

#tombstoned?Boolean

Returns:

  • (Boolean)


155
156
157
# File 'app/models/federails/actor.rb', line 155

def tombstoned?
  tombstoned_at.present?
end

#untombstone!Object



163
164
165
# File 'app/models/federails/actor.rb', line 163

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

#usernameObject



57
58
59
60
61
# File 'app/models/federails/actor.rb', line 57

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

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