ba-webby-pingback 1.0 → 1.0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|