Skip to content

Commit

Permalink
send webmentions
Browse files Browse the repository at this point in the history
Merge #535
  • Loading branch information
pushcx committed Oct 3, 2018
1 parent 12644b2 commit 49cb7af
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/models/story.rb
Expand Up @@ -874,7 +874,7 @@ def fetched_attributes
s.timeout = 3
@fetched_content = s.fetch(self.url, :get, nil, nil, {
"User-agent" => "#{Rails.application.domain} for #{self.fetching_ip}",
}, 3)
}, 3).body
rescue
return @fetched_attributes
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Expand Up @@ -313,7 +313,7 @@ def fetched_avatar(size = 100)
begin
s = Sponge.new
s.timeout = 3
res = s.fetch(gravatar_url)
res = s.fetch(gravatar_url).body
if res.present?
return res
end
Expand Down
16 changes: 10 additions & 6 deletions app/views/stories/_listdetail.html.erb
@@ -1,3 +1,7 @@
<%#
The h-entry, u-report-of, u-url and h-card css classes are necessary
for the webmentions support. http://microformats.org/wiki/h-entry
%>
<li id="story_<%= story.short_id %>" data-shortid="<%= story.short_id %>"
class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<%= story.vote && story.vote[:vote] == -1 ? "downvoted" : "" %>
Expand All @@ -7,7 +11,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<%= story.is_hidden_by_cur_user ? "hidden" : "" %>
<%= story.is_saved_by_cur_user ? "saved" : "" %>
<%= story.is_expired? ? "expired" : "" %>">
<div class="story_liner">
<div class="story_liner h-entry">
<div class="voters">
<% if @user %>
<a class="upvoter"></a>
Expand All @@ -17,9 +21,9 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<div class="score"><%= story.score %></div>
</div>
<div class="details">
<span class="link">
<span class="link h-cite u-repost-of">
<% if story.can_be_seen_by_user?(@user) %>
<a href="<%= story.url_or_comments_path %>"><%=
<a class="u-url" href="<%= story.url_or_comments_path %>"><%=
break_long_words(story.title) %></a>
<% end %>
<% if story.is_gone? %>
Expand Down Expand Up @@ -73,7 +77,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<% else %>
via
<% end %>
<a href="/u/<%= ms.user.username %>" class="<%=
<a href="/u/<%= ms.user.username %>" class="h-card u-url <%=
ms.html_class_for_user %>"><%= ms.user.username %></a>

<%= time_ago_in_words_label(ms.created_at) %>
Expand Down Expand Up @@ -106,7 +110,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<% else %>
via
<% end %>
<a class="<%= story.html_class_for_user %>"><%=
<a class="h-card u-url <%= story.html_class_for_user %>"><%=
story.user.username %></a>
just now
<% else %>
Expand All @@ -115,7 +119,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
<% else %>
via
<% end %>
<a href="/u/<%= story.user.username %>" class="<%=
<a href="/u/<%= story.user.username %>" class="h-card u-url <%=
story.html_class_for_user %>"><%= story.user.username %></a>

<%= time_ago_in_words_label(story.created_at) %>
Expand Down
4 changes: 2 additions & 2 deletions extras/github.rb
Expand Up @@ -21,12 +21,12 @@ def self.token_and_user_from_code(code)
:client_id => self.CLIENT_ID,
:client_secret => self.CLIENT_SECRET,
:code => code,
)
).body
ps = CGI.parse(res)
tok = ps["access_token"].first

if tok.present?
res = s.fetch("https://api.github.com/user?access_token=#{tok}")
res = s.fetch("https://api.github.com/user?access_token=#{tok}").body
js = JSON.parse(res)
if js && js["login"].present?
return [tok, js["login"]]
Expand Down
2 changes: 1 addition & 1 deletion extras/sponge.rb
Expand Up @@ -218,7 +218,7 @@ def fetch(url, method = :get, fields = nil, raw_post_data = nil, headers = {}, l

case res
when Net::HTTPSuccess
return res.body
return res
when Net::HTTPRedirection
# follow
newuri = URI.parse(res["location"])
Expand Down
2 changes: 1 addition & 1 deletion extras/story_cacher.rb
Expand Up @@ -22,7 +22,7 @@ def self.get_story_text(story)
s = Sponge.new
# we're not doing this interactively, so take a while
s.timeout = 45
res = s.fetch(db_url)
res = s.fetch(db_url).body
if res.present?
j = JSON.parse(res)

Expand Down
90 changes: 90 additions & 0 deletions script/send_new_webmentions
@@ -0,0 +1,90 @@
#!/usr/bin/env ruby

ENV["RAILS_ENV"] ||= "production"

APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require APP_PATH
Rails.application.require_environment!

LAST_STORY_KEY = "webmentions:last_story_id".freeze

def endpoint_from_body(html)
doc = Nokogiri::HTML(html)

if !doc.css('[rel~="webmention"]').css('[href]').empty?
doc.css('[rel~="webmention"]').css('[href]').attribute('href').value
elsif !doc.css('[rel="http://webmention.org/"]').css('[href]').empty?
doc.css('[rel="http://webmention.org/"]').css('[href]').attribute('href').value
elsif !doc.css('[rel="http://webmention.org"]').css('[href]').empty?
doc.css('[rel="http://webmention.org"]').css('[href]').attribute('href').value
end
end

def endpoint_from_headers(header)
return unless header

if (matches = header.match(/<([^>]+)>; rel="[^"]*\s?webmention\s?[^"]*"/))
return matches[1]
elsif (matches = header.match(/<([^>]+)>; rel=webmention/))
return matches[1]
elsif (matches = header.match(/rel="[^"]*\s?webmention\s?[^"]*"; <([^>]+)>/))
return matches[1]
elsif (matches = header.match(/rel=webmention; <([^>]+)>/))
return matches[1]
elsif (matches = header.match(/<([^>]+)>; rel="http:\/\/webmention\.org\/?"/))
return matches[1]
elsif (matches = header.match(/rel="http:\/\/webmention\.org\/?"; <([^>]+)>/))
return matches[1]
end
end

# Some pages could return a relative link as their webmention endpoint.
# We need to translate this relative likn to an absolute one.
def uri_to_absolute(uri, req_uri)
abs_uri = URI.parse(uri)
if abs_uri.host
# Already absolute.
return uri
else
abs_uri.host = req_uri.host
abs_uri.scheme = req_uri.scheme
abs_uri.port = req_uri.port
return abs_uri
end
end

def send_webmention(source, target, endpoint)
sp = Sponge.new
sp.timeout = 10
sp.fetch(endpoint.to_s, :post, {
"source" => URI.encode_www_form_component(source),
"target" => URI.encode_www_form_component(target),
}, nil, {}, 3)
end

if __FILE__ == $PROGRAM_NAME
last_story_id = (Keystore.value_for(LAST_STORY_KEY) || Story.order('id desc').limit(1).offset(1).pluck(:id).try(:first)).to_i

Story.where("id > ? AND is_expired = ?", last_story_id, false).order(:id).each do |s|
next unless s.url

# mark it done so we don't hit them again if we or they crash
Keystore.put(LAST_STORY_KEY, s.id)

sp = Sponge.new
sp.timeout = 10
response = sp.fetch(s.url, :get, nil, nil, {
"User-agent" => "#{Rails.application.domain} webmention endpoint lookup",
}, 3)
next unless response

wm_endpoint_raw = endpoint_from_headers(response['link']) ||
endpoint_from_body(response.body.to_s)
next unless wm_endpoint_raw

wm_endpoint = uri_to_absolute(wm_endpoint_raw, URI.parse(s.url))
send_webmention(s.short_id_url, s.url, wm_endpoint)
last_story_id = s.id
end
end

0 comments on commit 49cb7af

Please sign in to comment.