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:

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:



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

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



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

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



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

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



236
237
238
239
240
241
242
243
244
245
246
247
# File 'app/models/federails/actor.rb', line 236

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



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

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



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

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

  actor
end

.find_or_create_by_federation_url(url) ⇒ Object



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

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)



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

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



116
117
118
# File 'app/models/federails/actor.rb', line 116

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

#actor_typeObject



79
80
81
# File 'app/models/federails/actor.rb', line 79

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

#at_address(prefix: '@') ⇒ Object



108
109
110
# File 'app/models/federails/actor.rb', line 108

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

#distant?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'app/models/federails/actor.rb', line 55

def distant?
  !local?
end

#entity_configurationObject



140
141
142
143
144
# File 'app/models/federails/actor.rb', line 140

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



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

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:



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

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

  false
end

#followers_urlObject



91
92
93
# File 'app/models/federails/actor.rb', line 91

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

#followings_urlObject



95
96
97
# File 'app/models/federails/actor.rb', line 95

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:



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

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

  false
end

#inbox_urlObject



83
84
85
# File 'app/models/federails/actor.rb', line 83

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

#key_idObject



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

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

#nameObject



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

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

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

#outbox_urlObject



87
88
89
# File 'app/models/federails/actor.rb', line 87

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

#private_keyObject



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

def private_key
  ensure_key_pair_exists!
  self[:private_key]
end

#profile_urlObject



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

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



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

def public_key
  ensure_key_pair_exists!
  self[:public_key]
end

#serverObject



75
76
77
# File 'app/models/federails/actor.rb', line 75

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

#short_at_addressObject



112
113
114
# File 'app/models/federails/actor.rb', line 112

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



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

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



165
166
167
# File 'app/models/federails/actor.rb', line 165

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

#tombstoned?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'app/models/federails/actor.rb', line 161

def tombstoned?
  tombstoned_at.present?
end

#untombstone!Object



169
170
171
# File 'app/models/federails/actor.rb', line 169

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

#usernameObject



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

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

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