ba-webby-pingback 1.0 → 1.0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +1 -1
- data/lib/webby-pingback-rake.rb +7 -0
- data/lib/webby-pingback-xmlrpc.cgi.rb +19 -0
- data/lib/webby-pingback.rb +208 -0
- data/webby-pingback.gemspec +3 -3
- metadata +4 -1
data/README.textile
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
$KCODE = 'u'
|
4
|
+
|
5
|
+
require 'xmlrpc/server'
|
6
|
+
require 'webby-pingback'
|
7
|
+
|
8
|
+
s = XMLRPC::CGIServer.new
|
9
|
+
s.add_handler('pingback.ping') do |source, target|
|
10
|
+
pinger = Pingback::Receiver.new(source, target)
|
11
|
+
{:message => pinger.response_message}
|
12
|
+
end
|
13
|
+
|
14
|
+
s.set_default_handler do |name, *args|
|
15
|
+
raise XMLRPC::FaultException.new(-99, "Method #{name} missing or wrong number of parameters")
|
16
|
+
end
|
17
|
+
|
18
|
+
s.serve
|
19
|
+
|
@@ -0,0 +1,208 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
$KCODE = 'u'
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
require 'xmlrpc/client'
|
8
|
+
require 'open-uri'
|
9
|
+
|
10
|
+
require 'hpricot'
|
11
|
+
require 'amalgalite'
|
12
|
+
require 'json'
|
13
|
+
|
14
|
+
# This modules expects there to be a constant called Conf that is a
|
15
|
+
# hash with two values:
|
16
|
+
#
|
17
|
+
# :db:: Path to the SQLite database file
|
18
|
+
# :forum_api_key:: The Disqus forum api key to fetch the trackback url
|
19
|
+
#
|
20
|
+
module Pingback
|
21
|
+
# So we don't try to ping twice!
|
22
|
+
def self.toggle_trackback_done(filename, mode = true)
|
23
|
+
File.open(filename, 'r+') do |file|
|
24
|
+
pos = file.each_line do |line|
|
25
|
+
break pos if line.match(/^trackback_done: (\w+)/)
|
26
|
+
pos = file.pos
|
27
|
+
end
|
28
|
+
|
29
|
+
file.seek(pos + 16)
|
30
|
+
file.write(' true') # Has to be 5 chars to cover all of false :)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Sender
|
35
|
+
# +source_url+ is the page that contains outgoing links.
|
36
|
+
# +urls+ are the links on that page. +urls+ can also be added dynamically
|
37
|
+
# by using +obj.urls << 'url'.
|
38
|
+
def initialize(source_url, urls = [])
|
39
|
+
@source_url = source_url
|
40
|
+
@urls = ([] << urls).flatten
|
41
|
+
end
|
42
|
+
attr_accessor :urls
|
43
|
+
|
44
|
+
# Tries to find a pingback url from a given URL. If found ping it.
|
45
|
+
def start(verbose = false)
|
46
|
+
@urls.each do |url|
|
47
|
+
pingback_url = find_pingback_url(url)
|
48
|
+
if pingback_url
|
49
|
+
send_ping(pingback_url, url)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# From a given URL try to find a pingback source from HTTP Header,
|
55
|
+
# if not found try to download the page and look for a
|
56
|
+
# <link rel="pingback"> element and use that.
|
57
|
+
def find_pingback_url(url)
|
58
|
+
res = fetch_pingback_url(url)
|
59
|
+
if res['X-Pingback'].nil?
|
60
|
+
res = fetch_pingback_url(url, :Get)
|
61
|
+
doc = Hpricot(res.body)
|
62
|
+
(doc/"//link[@rel='pingback']")[0]['href'] rescue false
|
63
|
+
else
|
64
|
+
res['X-Pingback']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
# Connect to +url+ with HTTP-method +type+ and return the result-object.
|
70
|
+
def fetch_pingback_url(url, type = :Head)
|
71
|
+
uri = URI.parse url
|
72
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
73
|
+
http.request(Net::HTTP.const_get(type).new(uri.path))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Using the XML-RPC protocoll send a ping.
|
78
|
+
# +xmlrpc_uri+ is the XML-RPC endpoint
|
79
|
+
# +target+ is the url that we're linking to and want to tell the endpoint
|
80
|
+
def send_ping(xmlrpc_uri, target)
|
81
|
+
uri = URI.parse(xmlrpc_uri)
|
82
|
+
server = XMLRPC::Client.new(uri.host, uri.path, uri.port)
|
83
|
+
begin
|
84
|
+
result = server.call('pingback.ping',
|
85
|
+
@source_url,
|
86
|
+
target)
|
87
|
+
puts "#{uri.host}#{uri.path}: #{result['message']}"
|
88
|
+
rescue XMLRPC::FaultException => e
|
89
|
+
puts "#{uri.host}#{uri.path}: Error -- #{e.message}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end # / Sender
|
93
|
+
|
94
|
+
class Receiver
|
95
|
+
def initialize(source, target)
|
96
|
+
@@db = Amalgalite::Database.new(Pingback::Conf[:db])
|
97
|
+
@@forum_api_key = Pingback::Conf[:forum_api_key]
|
98
|
+
@source = source
|
99
|
+
@target = target
|
100
|
+
@response_message = ''
|
101
|
+
|
102
|
+
db_schema
|
103
|
+
if pingback_exists?
|
104
|
+
@response_message = 'The pingback has already been registered'
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
108
|
+
trackback_url = fetch_trackback_url()
|
109
|
+
if trackback_url.nil?
|
110
|
+
@response_message = 'The specified target URI cannot be used as a target.'
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
trackback_data = fetch_source_data()
|
115
|
+
if trackback_data[:excerpt].empty?
|
116
|
+
@response_message = 'No link to target page!'
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
|
120
|
+
send_trackback(trackback_url, trackback_data)
|
121
|
+
end
|
122
|
+
attr :response_message
|
123
|
+
|
124
|
+
private
|
125
|
+
def pingback_exists?
|
126
|
+
!@@db.execute('SELECT id FROM received_pingbacks
|
127
|
+
WHERE source_url = ?
|
128
|
+
AND target_url = ? ', @source, @target).empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Looks for the link to the target page so we know it's not just a spambot
|
132
|
+
# trying to link us
|
133
|
+
def fetch_source_data
|
134
|
+
doc = Hpricot(open(@source))
|
135
|
+
p doc
|
136
|
+
{
|
137
|
+
:excerpt => (doc/"//a[@href='#{@target}']/..").inner_html,
|
138
|
+
:title => (doc/'//title').inner_html,
|
139
|
+
:url => @source
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def send_trackback(url, data)
|
144
|
+
uri = URI.parse url
|
145
|
+
res = Net::HTTP.start(uri.host, uri.port) do |http|
|
146
|
+
req = Net::HTTP::Post.new(uri.path)
|
147
|
+
req.form_data = data
|
148
|
+
req['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
|
149
|
+
http.request req
|
150
|
+
end
|
151
|
+
|
152
|
+
doc = Hpricot.XML(res.body)
|
153
|
+
if res.code == '200' and (doc/'//error')[0].inner_html == '0'
|
154
|
+
record_trackback(1)
|
155
|
+
@response_message = 'OK'
|
156
|
+
else
|
157
|
+
record_trackback(0, doc.to_html)
|
158
|
+
@response_message = 'Unknown error ' + doc.to_html
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def record_trackback(status, message = nil)
|
163
|
+
@@db.execute('INSERT INTO received_pingbacks (source_url, target_url, status, message)
|
164
|
+
VALUES (?, ?, ?, ?);', @source, @target, status, message)
|
165
|
+
end
|
166
|
+
|
167
|
+
def fetch_trackback_url
|
168
|
+
res = @@db.execute('SELECT trackback_url FROM trackback_url WHERE target_url = ?', @target)
|
169
|
+
if res.empty?
|
170
|
+
url = fetch_disqus_trackback_url
|
171
|
+
@@db.execute('INSERT INTO trackback_url (target_url, trackback_url) VALUES (?, ?)', @target, url) if url
|
172
|
+
return url
|
173
|
+
else
|
174
|
+
res.flatten[0]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def fetch_disqus_trackback_url
|
179
|
+
data = JSON.parse(open("http://disqus.com/api/get_thread_by_url/?forum_api_key=#{@@forum_api_key}&url=#{@target}").read)
|
180
|
+
if data['succeeded'] and not data['message'].nil?
|
181
|
+
x = data['message']
|
182
|
+
return "http://#{x['forum_obj']['shortname']}.disqus.com/#{x['slug']}/trackback/"
|
183
|
+
else
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def db_schema
|
189
|
+
if @@db.schema.tables.empty?
|
190
|
+
@@db.execute_batch('CREATE TABLE received_pingbacks (
|
191
|
+
id INTEGER AUTO INCREMENT PRIMARY KEY,
|
192
|
+
source_url VARCHAR NOT NULL,
|
193
|
+
target_url VARCHAT NOT NULL,
|
194
|
+
status INTEGER DEFAULT 0,
|
195
|
+
message VARCHAR NULL
|
196
|
+
);
|
197
|
+
|
198
|
+
CREATE TABLE trackback_url (
|
199
|
+
target_url VARCHAR NOT NULL PRIMARY KEY,
|
200
|
+
trackback_url VARCHAR NOT NULL
|
201
|
+
);
|
202
|
+
|
203
|
+
CREATE TABLE version (number INTEGER);
|
204
|
+
INSERT INTO version VALUES(1);')
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end # /Receiver
|
208
|
+
end # /Pingback
|
data/webby-pingback.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = "webby-pingback"
|
4
|
-
s.version = "1.0"
|
4
|
+
s.version = "1.0.0.1"
|
5
5
|
s.date = Time.now.strftime('%Y-%m-%d')
|
6
6
|
s.description = "Using Webby and some custom meta-data tags enable pingbacks to blogs and pages"
|
7
7
|
s.authors = ["Björn Andersson"]
|
@@ -10,9 +10,9 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = s.description
|
11
11
|
s.require_path = 'lib'
|
12
12
|
s.has_rdoc = false
|
13
|
-
s.files = %w[README.textile tasks/pingback.rake webby-pingback.gemspec ext/pingback.cgi]
|
14
|
-
Dir.glob('lib/*.rb')
|
13
|
+
s.files = %w[README.textile tasks/pingback.rake webby-pingback.gemspec ext/pingback.cgi lib/webby-pingback-rake.rb lib/webby-pingback-xmlrpc.cgi.rb lib/webby-pingback.rb]
|
15
14
|
s.add_dependency('amalgalite', '>= 0.10.2')
|
16
15
|
s.add_dependency('json', '>= 1.1.7')
|
17
16
|
s.add_dependency('hpricot', '>= 0.8.1')
|
18
17
|
end
|
18
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ba-webby-pingback
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Bj\xC3\xB6rn Andersson"
|
@@ -55,6 +55,9 @@ files:
|
|
55
55
|
- tasks/pingback.rake
|
56
56
|
- webby-pingback.gemspec
|
57
57
|
- ext/pingback.cgi
|
58
|
+
- lib/webby-pingback-rake.rb
|
59
|
+
- lib/webby-pingback-xmlrpc.cgi.rb
|
60
|
+
- lib/webby-pingback.rb
|
58
61
|
has_rdoc: false
|
59
62
|
homepage: http://github.com/ba/webby-pingback
|
60
63
|
licenses:
|