Bladeren bron

Send Salmon interactions

Eugen Rochko 8 jaren geleden
bovenliggende
commit
fa7868675d

+ 1 - 0
Gemfile

@@ -19,6 +19,7 @@ gem 'grape'
 gem 'grape-route-helpers'
 gem 'grape-entity'
 gem 'hashie-forbidden_attributes'
+gem 'paranoia', '~> 2.0'
 
 gem 'http'
 gem 'addressable'

+ 3 - 0
Gemfile.lock

@@ -152,6 +152,8 @@ GEM
       addressable (~> 2.4)
       http (~> 1.0)
       nokogiri (~> 1.6)
+    paranoia (2.1.5)
+      activerecord (~> 4.0)
     parser (2.3.0.6)
       ast (~> 2.2)
     pg (0.18.4)
@@ -305,6 +307,7 @@ DEPENDENCIES
   nokogiri
   nyan-cat-formatter
   ostatus2
+  paranoia (~> 2.0)
   pg
   pry-rails
   puma

+ 1 - 1
app/models/favourite.rb

@@ -2,7 +2,7 @@ class Favourite < ActiveRecord::Base
   belongs_to :account, inverse_of: :favourites
   belongs_to :status,  inverse_of: :favourites
 
-  has_one :stream_entry, as: :activity
+  has_one :stream_entry, as: :activity, dependent: :destroy
 
   def verb
     :favorite

+ 3 - 3
app/models/follow.rb

@@ -2,13 +2,13 @@ class Follow < ActiveRecord::Base
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
 
-  has_one :stream_entry, as: :activity
+  has_one :stream_entry, as: :activity, dependent: :destroy
 
   validates :account, :target_account, presence: true
   validates :account_id, uniqueness: { scope: :target_account_id }
 
   def verb
-    :follow
+    self.destroyed? ? :unfollow : :follow
   end
 
   def target
@@ -20,7 +20,7 @@ class Follow < ActiveRecord::Base
   end
 
   def content
-    "#{self.account.acct} started following #{self.target_account.acct}"
+    self.destroyed? ? "#{self.account.acct} is no longer following #{self.target_account.acct}" : "#{self.account.acct} started following #{self.target_account.acct}"
   end
 
   def title

+ 5 - 2
app/models/status.rb

@@ -4,8 +4,11 @@ class Status < ActiveRecord::Base
   belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status'
   belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status'
 
-  has_one :stream_entry, as: :activity
-  has_many :favourites, inverse_of: :status
+  has_one :stream_entry, as: :activity, dependent: :destroy
+
+  has_many :favourites, inverse_of: :status, dependent: :destroy
+  has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status'
+  has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status'
 
   validates :account, presence: true
   validates :uri, uniqueness: true, unless: 'local?'

+ 3 - 0
app/services/base_service.rb

@@ -0,0 +1,3 @@
+class BaseService
+  include ApplicationHelper
+end

+ 16 - 0
app/services/fetch_entry_service.rb

@@ -0,0 +1,16 @@
+class FetchEntryService < BaseService
+  # Knowing nothing but the URL of a remote status, create a local representation of it and return it
+  # @param [String] url Atom URL
+  # @return [Status]
+  def call(url)
+    body = http_client.get(url)
+    xml  = Nokogiri::XML(body)
+    # todo
+  end
+
+  private
+
+  def http_client
+    HTTP
+  end
+end

+ 4 - 2
app/services/fetch_feed_service.rb

@@ -1,4 +1,6 @@
-class FetchFeedService
+class FetchFeedService < BaseService
+  # Fetch an account's feed and process it
+  # @param [Account] account
   def call(account)
     process_service.(http_client.get(account.remote_url), account)
   end
@@ -6,7 +8,7 @@ class FetchFeedService
   private
 
   def process_service
-    ProcessFeedService.new
+    @process_service ||= ProcessFeedService.new
   end
 
   def http_client

+ 7 - 3
app/services/follow_remote_account_service.rb

@@ -1,6 +1,10 @@
-class FollowRemoteAccountService
-  include ApplicationHelper
-
+class FollowRemoteAccountService < BaseService
+  # Find or create a local account for a remote user.
+  # When creating, look up the user's webfinger and fetch all
+  # important information from their feed
+  # @param [String] uri User URI in the form of username@domain
+  # @param [Boolean] subscribe Whether to initiate a PubSubHubbub subscription
+  # @return [Account]
   def call(uri, subscribe = true)
     username, domain = uri.split('@')
     account = Account.where(username: username, domain: domain).first

+ 14 - 3
app/services/follow_service.rb

@@ -1,12 +1,23 @@
-class FollowService
+class FollowService < BaseService
+  # Follow a remote user, notify remote user about the follow
+  # @param [Account] source_account From which to follow
+  # @param [String] uri User URI to follow in the form of username@domain
   def call(source_account, uri)
     target_account = follow_remote_account_service.(uri)
-    source_account.follow!(target_account) unless target_account.nil?
+
+    return if target_account.nil?
+
+    follow = source_account.follow!(target_account)
+    send_interaction_service.(follow.stream_entry, target_account)
   end
 
   private
 
   def follow_remote_account_service
-    FollowRemoteAccountService.new
+    @follow_remote_account_service ||= FollowRemoteAccountService.new
+  end
+
+  def send_interaction_service
+    @send_interaction_service ||= SendInteractionService.new
   end
 end

+ 5 - 4
app/services/process_feed_service.rb

@@ -1,6 +1,7 @@
-class ProcessFeedService
-  include ApplicationHelper
-
+class ProcessFeedService < BaseService
+  # Create local statuses from an Atom feed
+  # @param [String] body Atom feed
+  # @param [Account] account Account this feed belongs to
   def call(body, account)
     xml = Nokogiri::XML(body)
 
@@ -105,6 +106,6 @@ class ProcessFeedService
   end
 
   def follow_remote_account_service
-    FollowRemoteAccountService.new
+    @follow_remote_account_service ||= FollowRemoteAccountService.new
   end
 end

+ 7 - 6
app/services/process_interaction_service.rb

@@ -1,6 +1,7 @@
-class ProcessInteractionService
-  include ApplicationHelper
-
+class ProcessInteractionService < BaseService
+  # Record locally the remote interaction with our user
+  # @param [String] envelope Salmon envelope
+  # @param [Account] target_account Account the Salmon was addressed to
   def call(envelope, target_account)
     body = salmon.unpack(envelope)
     xml  = Nokogiri::XML(body)
@@ -75,14 +76,14 @@ class ProcessInteractionService
   end
 
   def salmon
-    OStatus2::Salmon.new
+    @salmon ||= OStatus2::Salmon.new
   end
 
   def follow_remote_account_service
-    FollowRemoteAccountService.new
+    @follow_remote_account_service ||= FollowRemoteAccountService.new
   end
 
   def process_feed_service
-    ProcessFeedService.new
+    @process_feed_service ||= ProcessFeedService.new
   end
 end

+ 29 - 0
app/services/send_interaction_service.rb

@@ -0,0 +1,29 @@
+class SendInteractionService < BaseService
+  include AtomHelper
+
+  # Send an Atom representation of an interaction to a remote Salmon endpoint
+  # @param [StreamEntry] stream_entry
+  # @param [Account] target_account
+  def call(stream_entry, target_account)
+    envelope = salmon.pack(entry_xml(stream_entry), target_account.keypair)
+    salmon.post(target_account.salmon_url, envelope)
+  end
+
+  private
+
+  def entry_xml(stream_entry)
+    Nokogiri::XML::Builder.new do |xml|
+      entry(xml, true) do
+        author(xml) do
+          include_author xml, stream_entry.account
+        end
+
+        include_entry xml, stream_entry
+      end
+    end.to_xml
+  end
+
+  def salmon
+    @salmon ||= OStatus2::Salmon.new
+  end
+end

+ 5 - 1
app/services/setup_local_account_service.rb

@@ -1,4 +1,8 @@
-class SetupLocalAccountService
+class SetupLocalAccountService < BaseService
+  # Setup an account for a new user instance by generating
+  # an RSA key pair and a profile
+  # @param [User] user Unsaved user instance
+  # @param [String] username
   def call(user, username)
     user.build_account
 

+ 15 - 0
app/services/unfollow_service.rb

@@ -0,0 +1,15 @@
+class UnfollowService < BaseService
+  # Unfollow and notify the remote user
+  # @param [Account] source_account Where to unfollow from
+  # @param [Account] target_account Which to unfollow
+  def call(source_account, target_account)
+    follow = source_account.unfollow!(target_account)
+    send_interaction_service.(follow.stream_entry, target_account) unless target_account.local?
+  end
+
+  private
+
+  def send_interaction_service
+    @send_interaction_service ||= SendInteractionService.new
+  end
+end