ruby-akismet 0.9.3 → 1.0.0
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.rdoc +38 -12
- data/lib/akismet.rb +101 -32
- data/test/akismet_test.rb +94 -25
- metadata +5 -5
data/README.rdoc
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
Ruby library for the Akismet anti-spam service.
|
2
2
|
|
3
3
|
= Usage
|
4
4
|
|
5
5
|
First you need an Akismet (or Typepad Antispam) API key. Then you need
|
6
|
-
to setup a few configuration
|
6
|
+
to setup a few configuration variables:
|
7
7
|
|
8
8
|
Akismet.key = '123456789'
|
9
9
|
Akismet.blog = 'http://example.com'
|
@@ -12,37 +12,61 @@ To use Typepad Antispam, just specify the host:
|
|
12
12
|
|
13
13
|
Akismet.host = 'api.antispam.typepad.com'
|
14
14
|
|
15
|
-
Then you need to call any of the methods with a few attributes,
|
16
|
-
|
15
|
+
Then you need to call any of the methods with a few attributes, and possibly
|
16
|
+
an ActionDispatch::Request object.
|
17
17
|
|
18
|
-
|
18
|
+
== Documentation
|
19
|
+
|
20
|
+
Check rubydoc.info:
|
19
21
|
|
20
22
|
http://rubydoc.info/github/ysbaddaden/ruby-akismet/master/frames
|
21
23
|
|
24
|
+
Or generate your own:
|
25
|
+
|
26
|
+
~/src/ruby-akismet$ rake rdoc
|
27
|
+
|
22
28
|
= Integrate with Ruby on Rails
|
23
29
|
|
24
|
-
|
30
|
+
ruby-akismet integrates nicely with Ruby on Rails, but isn't tied to it except
|
31
|
+
for the ActionDispatch::Request object, which isn't required. It should be
|
32
|
+
easily integratable with your favorite framework like Sinatra and Merb.
|
33
|
+
|
34
|
+
== Rails 3
|
35
|
+
|
36
|
+
Add this gem to your Gemfile:
|
25
37
|
|
26
38
|
gem 'ruby-akismet', :require => 'akismet'
|
27
39
|
|
28
|
-
|
40
|
+
== Rails 2
|
41
|
+
|
42
|
+
First install the gem:
|
29
43
|
|
30
44
|
gem install ruby-akismet
|
31
45
|
|
32
|
-
Then add
|
46
|
+
Then add it to your app:
|
33
47
|
|
34
48
|
config.gem 'ruby-akismet', :lib => 'akismet'
|
35
49
|
|
36
|
-
|
50
|
+
== Configuration
|
51
|
+
|
52
|
+
Create an initializer file like <tt>config/initializers/akismet.rb</tt>
|
37
53
|
with your configuration:
|
38
54
|
|
39
|
-
Akismet.key
|
40
|
-
Akismet.blog
|
55
|
+
Akismet.key = '123456789'
|
56
|
+
Akismet.blog = 'http://example.com'
|
57
|
+
Akismet.logger = Rails.logger
|
58
|
+
|
59
|
+
== Usage
|
41
60
|
|
42
|
-
|
61
|
+
ruby-akismet is meant to be used on the controller side and not on the model
|
62
|
+
side, because the Akismet API requires some data that's only available from
|
63
|
+
the HTTP request --like the user and proxy IP, referer, etc.
|
64
|
+
|
65
|
+
Here is a Rails 3 example:
|
43
66
|
|
44
67
|
class CommentsController < ApplicationController
|
45
68
|
before_filter :set_post
|
69
|
+
|
46
70
|
respond_to :html, :xml
|
47
71
|
|
48
72
|
def create
|
@@ -84,6 +108,8 @@ Then in your controller call the appropriate methods (rails 3 example):
|
|
84
108
|
|
85
109
|
= Author
|
86
110
|
|
111
|
+
- Julien Portalier <ysbaddaden@gmail.com>
|
112
|
+
|
87
113
|
ruby-akismet is a complete rewrite of Akismetor by Ryan Bates and
|
88
114
|
Levy Carneiro Jr. that you can find at http://github.com/levycarneiro/akismetor
|
89
115
|
|
data/lib/akismet.rb
CHANGED
@@ -1,46 +1,91 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
|
3
3
|
# Akismet compatible library for checking spams.
|
4
|
+
#
|
5
|
+
# Before calling any method, you must configure a blog (your website
|
6
|
+
# homepage) and your Akismet or Typepad Antispam API key.
|
7
|
+
#
|
8
|
+
# Akismet.key = '123456789'
|
9
|
+
# Akismet.blog = 'http://example.com'
|
10
|
+
#
|
4
11
|
class Akismet
|
5
|
-
|
6
|
-
|
12
|
+
# Raised whenever a command is issued but the API key hasn't been configured.
|
13
|
+
class MissingKey < StandardError
|
14
|
+
end
|
7
15
|
|
8
|
-
|
9
|
-
|
10
|
-
@@blog = nil
|
11
|
-
@@extra_headers = [
|
12
|
-
'HTTP_REMOTE_ADDR',
|
13
|
-
'HTTP_CLIENT_IP',
|
14
|
-
'HTTP_X_FORWARDED_FOR',
|
15
|
-
'HTTP_CONNECTION'
|
16
|
-
]
|
16
|
+
VERSION = '1.0.0'.freeze
|
17
|
+
API_VERSION = '1.1'.freeze
|
17
18
|
|
18
19
|
class << self
|
19
|
-
# Configure an alternate API server
|
20
|
+
# Configure an alternate API host server (defaults to
|
21
|
+
# <tt>'rest.akismet.com'</tt>).
|
20
22
|
def host=(host)
|
21
23
|
@@host = host
|
22
24
|
end
|
23
25
|
|
26
|
+
def host
|
27
|
+
@@host
|
28
|
+
end
|
29
|
+
|
24
30
|
# Configure your API key (required).
|
25
31
|
def key=(key)
|
26
32
|
@@key = key
|
27
33
|
end
|
28
34
|
|
29
|
-
|
35
|
+
def key
|
36
|
+
@@key
|
37
|
+
end
|
38
|
+
|
39
|
+
# Configure your homepage URL (required).
|
30
40
|
def blog=(blog)
|
31
41
|
@@blog = blog
|
32
42
|
end
|
33
43
|
|
34
|
-
|
35
|
-
|
44
|
+
def blog
|
45
|
+
@@blog
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger=(logger)
|
49
|
+
@@logger = logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def logger
|
53
|
+
@@logger
|
54
|
+
end
|
55
|
+
|
56
|
+
# Configure an Array of extra HTTP headers to pass to the Akismet server
|
57
|
+
# to extract from the request object.
|
58
|
+
#
|
59
|
+
# Defaults to:
|
60
|
+
#
|
61
|
+
# [
|
62
|
+
# 'HTTP_REMOTE_ADDR',
|
63
|
+
# 'HTTP_CLIENT_IP',
|
64
|
+
# 'HTTP_X_FORWARDED_FOR',
|
65
|
+
# 'HTTP_CONNECTION'
|
66
|
+
# ]
|
36
67
|
#
|
37
|
-
#
|
68
|
+
# Examples:
|
69
|
+
#
|
70
|
+
# # replaces the actual list:
|
71
|
+
# Akismet.extra_headers = ['HTTP_REMOTE_ADDR']
|
72
|
+
#
|
73
|
+
# # appends a header to the list:
|
74
|
+
# Akismet.extra_headers << 'HTTP_ACCEPT_CHARSET'
|
75
|
+
#
|
76
|
+
# # appends multiple headers to the list:
|
77
|
+
# Akismet.extra_headers << ['HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_LANGUAGE']
|
38
78
|
#
|
39
|
-
# Akismet.extra_headers = ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR']
|
40
79
|
def extra_headers=(headers)
|
41
80
|
@@extra_headers = headers
|
42
81
|
end
|
43
82
|
|
83
|
+
def extra_headers
|
84
|
+
@@extra_headers.flatten!
|
85
|
+
@@extra_headers.uniq!
|
86
|
+
@@extra_headers
|
87
|
+
end
|
88
|
+
|
44
89
|
# Checks if a key is valid or not.
|
45
90
|
def valid_key?(key)
|
46
91
|
call('verify-key', :key => key) == "valid"
|
@@ -50,18 +95,25 @@ class Akismet
|
|
50
95
|
#
|
51
96
|
# Required attributes:
|
52
97
|
#
|
53
|
-
# - <
|
54
|
-
# - <
|
55
|
-
# - <
|
56
|
-
# - <
|
57
|
-
# - <
|
98
|
+
# - <tt>:permalink</tt>
|
99
|
+
# - <tt>:comment_author</tt>
|
100
|
+
# - <tt>:comment_author_url</tt>
|
101
|
+
# - <tt>:comment_author_email</tt>
|
102
|
+
# - <tt>:comment_content</tt>
|
103
|
+
#
|
104
|
+
# Those are also required, but will be extracted from the
|
105
|
+
# +request+ object if available:
|
58
106
|
#
|
59
|
-
#
|
107
|
+
# - <tt>:user_ip</tt>
|
108
|
+
# - <tt>:user_agent</tt>
|
109
|
+
# - <tt>:referrer</tt> (check spelling!)
|
60
110
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
111
|
+
# Plus more relevant HTTP headers from extra_headers.
|
112
|
+
#
|
113
|
+
# Note that request is supposed to be an instance of
|
114
|
+
# ActionDispatch::Request or ActionController::Request. If not, the object
|
115
|
+
# must respond to +remote_ip+ (IP as string) and +headers+
|
116
|
+
# (an array of HTTP headers).
|
65
117
|
#
|
66
118
|
def spam?(attributes, request = nil)
|
67
119
|
call('comment-check', attributes, request) == "true"
|
@@ -79,7 +131,7 @@ class Akismet
|
|
79
131
|
end
|
80
132
|
|
81
133
|
# Submits a false-positive comment as non-spam to Akismet.
|
82
|
-
# Takes the same attributes than
|
134
|
+
# Takes the same attributes than spam?.
|
83
135
|
def submit_ham(attributes)
|
84
136
|
call('submit-ham', attributes)
|
85
137
|
end
|
@@ -90,6 +142,17 @@ class Akismet
|
|
90
142
|
end
|
91
143
|
end
|
92
144
|
|
145
|
+
self.host = 'rest.akismet.com'
|
146
|
+
self.key = nil
|
147
|
+
self.blog = nil
|
148
|
+
self.logger = nil
|
149
|
+
self.extra_headers = [
|
150
|
+
'HTTP_REMOTE_ADDR',
|
151
|
+
'HTTP_CLIENT_IP',
|
152
|
+
'HTTP_X_FORWARDED_FOR',
|
153
|
+
'HTTP_CONNECTION'
|
154
|
+
]
|
155
|
+
|
93
156
|
def initialize(command, attributes, request = nil)
|
94
157
|
@command = command
|
95
158
|
@attributes = attributes
|
@@ -97,13 +160,15 @@ class Akismet
|
|
97
160
|
end
|
98
161
|
|
99
162
|
def call
|
163
|
+
self.class.logger.debug { " AKISMET #{@command} #{post_attributes}" } if self.class.logger
|
164
|
+
|
100
165
|
http = Net::HTTP.new(http_host, 80)
|
101
166
|
http.post(http_path, post_attributes, http_headers).body
|
102
167
|
end
|
103
168
|
|
104
169
|
private
|
105
170
|
def attributes
|
106
|
-
@attributes[:blog] ||=
|
171
|
+
@attributes[:blog] ||= self.class.blog
|
107
172
|
|
108
173
|
unless @command == 'verify-key'
|
109
174
|
@attributes[:comment_type] ||= 'comment'
|
@@ -112,7 +177,10 @@ class Akismet
|
|
112
177
|
@attributes[:user_ip] = @request.remote_ip
|
113
178
|
@attributes[:user_agent] = @request.headers["HTTP_USER_AGENT"]
|
114
179
|
@attributes[:referrer] = @request.headers["HTTP_REFERER"]
|
115
|
-
|
180
|
+
|
181
|
+
self.class.extra_headers.each do |h|
|
182
|
+
@attributes[h] = @request.headers[h]
|
183
|
+
end
|
116
184
|
end
|
117
185
|
end
|
118
186
|
|
@@ -133,9 +201,10 @@ class Akismet
|
|
133
201
|
|
134
202
|
def http_host
|
135
203
|
unless @command == 'verify-key'
|
136
|
-
"
|
204
|
+
raise MissingKey.new("Required Akismet.key is nil.") unless self.class.key
|
205
|
+
"#{self.class.key}.#{self.class.host}"
|
137
206
|
else
|
138
|
-
"#{
|
207
|
+
"#{self.class.host}"
|
139
208
|
end
|
140
209
|
end
|
141
210
|
|
data/test/akismet_test.rb
CHANGED
@@ -1,60 +1,129 @@
|
|
1
1
|
require 'test/unit'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'actionpack'
|
6
|
+
gem 'rack'
|
7
|
+
require 'action_dispatch'
|
8
|
+
|
2
9
|
require File.expand_path("../../lib/akismet.rb", __FILE__)
|
3
10
|
|
4
|
-
# TODO: Test with an ActionDispatch::TestRequest object.
|
5
11
|
class AkismetTest < Test::Unit::TestCase
|
6
12
|
def setup
|
7
13
|
Akismet.host = 'api.antispam.typepad.com'
|
8
14
|
Akismet.key = '123456789'
|
9
15
|
Akismet.blog = 'http://www.example.com/'
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
{
|
14
|
-
:user_ip => '127.0.0.1',
|
15
|
-
:user_agent => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3',
|
16
|
-
:referrer => 'http://www.example.com/posts',
|
17
|
-
:permalink => 'http://www.example.com/posts/1',
|
18
|
-
:comment_author => 'Julien Portalier',
|
19
|
-
:comment_author_url => 'http://ysbaddaden.wordpress.com/',
|
20
|
-
:comment_author_email => 'julien@example.com',
|
21
|
-
:comment_content => 'this is a normal comment',
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
def invalid_attributes
|
26
|
-
invalid = valid_attributes.dup
|
27
|
-
invalid[:comment_author] = 'viagra-test-123'
|
28
|
-
invalid
|
16
|
+
|
17
|
+
Akismet.logger = Logger.new(File.expand_path("../test.log", __FILE__))
|
18
|
+
Akismet.logger.level = Logger::DEBUG
|
29
19
|
end
|
30
20
|
|
31
21
|
def test_valid_key
|
32
|
-
assert Akismet.valid_key?(
|
22
|
+
assert Akismet.valid_key?(Akismet.key)
|
33
23
|
end
|
34
24
|
|
35
25
|
# def test_invalid_key
|
36
26
|
# assert !Akismet.valid_key?('abc123')
|
37
27
|
# end
|
38
28
|
|
29
|
+
def test_should_not_fail_with_no_logger
|
30
|
+
Akismet.logger = nil
|
31
|
+
assert Akismet.valid_key?(Akismet.key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_should_raise_missing_key
|
35
|
+
Akismet.key = nil
|
36
|
+
assert_raise(Akismet::MissingKey) { Akismet.spam?(full_spam_attributes) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_not_raise_missing_key_for_valid_key
|
40
|
+
Akismet.key = nil
|
41
|
+
assert_nothing_raised { Akismet.valid_key?('123456789') }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_extra_headers
|
45
|
+
Akismet.extra_headers = ['HTTP_REMOTE_ADDR']
|
46
|
+
assert_equal ['HTTP_REMOTE_ADDR'], Akismet.extra_headers
|
47
|
+
|
48
|
+
Akismet.extra_headers << 'HTTP_CLIENT_IP'
|
49
|
+
assert_equal ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP'], Akismet.extra_headers
|
50
|
+
|
51
|
+
Akismet.extra_headers << ['HTTP_X_FORWARDED_FOR', 'HTTP_CONNECTION']
|
52
|
+
assert_equal ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CONNECTION'],
|
53
|
+
Akismet.extra_headers
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_spam_with_actiondispatch_request
|
57
|
+
assert Akismet.spam?(spam_attributes, actiondispatch_request)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_ham_with_actiondispatch_request
|
61
|
+
assert !Akismet.spam?(ham_attributes, actiondispatch_request)
|
62
|
+
end
|
63
|
+
|
39
64
|
def test_spam
|
40
|
-
assert Akismet.spam?(
|
65
|
+
assert Akismet.spam?(full_spam_attributes)
|
41
66
|
end
|
42
67
|
|
43
68
|
def test_not_spam
|
44
|
-
assert !Akismet.spam?(
|
69
|
+
assert !Akismet.spam?(full_ham_attributes)
|
45
70
|
end
|
46
71
|
|
47
72
|
def test_ham
|
48
|
-
assert Akismet.ham?(
|
73
|
+
assert Akismet.ham?(full_ham_attributes)
|
49
74
|
end
|
50
75
|
|
51
76
|
def test_not_ham
|
52
|
-
assert !Akismet.ham?(
|
77
|
+
assert !Akismet.ham?(full_spam_attributes)
|
53
78
|
end
|
54
79
|
|
55
80
|
def test_submit_spam
|
81
|
+
assert Akismet.submit_spam(spam_attributes)
|
56
82
|
end
|
57
83
|
|
58
84
|
def test_submit_ham
|
85
|
+
assert Akismet.submit_ham(ham_attributes)
|
59
86
|
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
def ham_attributes
|
90
|
+
{
|
91
|
+
:permalink => 'http://www.example.com/posts/1',
|
92
|
+
:comment_author => 'Julien Portalier',
|
93
|
+
:comment_author_url => 'http://ysbaddaden.wordpress.com/',
|
94
|
+
:comment_author_email => 'julien@example.com',
|
95
|
+
:comment_content => 'this is a normal comment'
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def spam_attributes
|
100
|
+
ham_attributes.merge(:comment_author => 'viagra-test-123')
|
101
|
+
end
|
102
|
+
|
103
|
+
def additional_attributes
|
104
|
+
{
|
105
|
+
:user_ip => '127.0.0.1',
|
106
|
+
:user_agent => 'Mozilla/5.0 (X11; U; Linux i686; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Ubuntu/10.04 Chromium/7.0.517.41 Chrome/7.0.517.41 Safari/534.7',
|
107
|
+
:referrer => 'http://www.example.com/posts'
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
def full_spam_attributes
|
112
|
+
spam_attributes.merge(additional_attributes)
|
113
|
+
end
|
114
|
+
|
115
|
+
def full_ham_attributes
|
116
|
+
ham_attributes.merge(additional_attributes)
|
117
|
+
end
|
118
|
+
|
119
|
+
def actiondispatch_request
|
120
|
+
request = ActionDispatch::TestRequest.new(
|
121
|
+
'HTTP_REFERER' => 'http://www.example.com/posts/1',
|
122
|
+
'HTTP_REMOTE_ADDR' => '127.0.0.1',
|
123
|
+
'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; U; Linux i686; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Ubuntu/10.04 Chromium/7.0.517.41 Chrome/7.0.517.41 Safari/534.7',
|
124
|
+
'HTTP_CONNECTION' => 'close'
|
125
|
+
)
|
126
|
+
request.remote_addr = '127.0.0.1'
|
127
|
+
request
|
128
|
+
end
|
60
129
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-akismet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 0.9.3
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Julien Portalier
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-24 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|