Send Salmon interactions
This commit is contained in:
parent
10eb47a33e
commit
fa7868675d
1
Gemfile
1
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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
app/services/base_service.rb
Normal file
3
app/services/base_service.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
class BaseService
|
||||
include ApplicationHelper
|
||||
end
|
16
app/services/fetch_entry_service.rb
Normal file
16
app/services/fetch_entry_service.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
app/services/send_interaction_service.rb
Normal file
29
app/services/send_interaction_service.rb
Normal file
|
@ -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
|
|
@ -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
app/services/unfollow_service.rb
Normal file
15
app/services/unfollow_service.rb
Normal file
|
@ -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
|
Reference in a new issue