akismet 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Jonah Burke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ Akismet
2
+ =======
3
+
4
+ A Ruby client for the Akismet API.
5
+
6
+ Instantiate an `Akismet::Client` with your API key and home page URL. Then
7
+ call `verify_key`, `check_comment`, `submit_ham`, or `submit_spam`.
8
+
9
+ Use `Akismet::Client.open` or `Akismet::Client#open` to submit multiple
10
+ requests over a single TCP connection.
11
+
12
+ See lib/akismet/client.rb for more documentation or generate docs with YARD.
13
+
14
+ Verify an API key
15
+ -----------------
16
+
17
+ Akismet::Client.new( 'apikey123', 'http://jonahb.com' ).verify_key
18
+
19
+
20
+ Check whether a comment is spam
21
+ -------------------------------
22
+
23
+ client = Akismet::Client.new( 'apikey123',
24
+ 'http://jonahb.com',
25
+ :app_name => 'jonahb.com',
26
+ :app_version => '1.0' )
27
+
28
+ # assumes variables comment, post_url, request (a racklike HTTP request)
29
+ spam = client.comment_check( request.remote_ip,
30
+ request.user_agent,
31
+ :content_type => 'comment',
32
+ :referrer => request.headers[ 'HTTP_REFERER' ],
33
+ :permalink => post_url,
34
+ :comment_author => comment.author,
35
+ :comment_author_email => comment.author_email,
36
+ :comment_content => comment.body )
37
+
38
+ if spam
39
+ # ...
40
+ end
41
+
42
+ Submit a batch of checks using a single TCP connection
43
+ ------------------------------------------------------
44
+
45
+ client = Akismet::Client.new( 'apikey123',
46
+ 'http://jonahb.com',
47
+ :app_name => 'jonahb.com',
48
+ :app_version => '1.0' )
49
+
50
+ begin
51
+ client.open
52
+ comments.each do |comment|
53
+ client.comment_check( ... ) # see example above
54
+ end
55
+ ensure
56
+ client.close
57
+ end
58
+
59
+ # ... or ...
60
+
61
+ Akismet::Client.open( 'apikey123',
62
+ 'http://jonahb.com',
63
+ :app_name => 'jonahb.com',
64
+ :app_version => '1.0' ) do |client|
65
+ comments.each do |comment|
66
+ client.comment_check( ... ) # see example above
67
+ end
68
+ end
@@ -1,12 +1,7 @@
1
- require File.dirname( __FILE__ ) + '/akismet/version'
2
- require File.dirname( __FILE__ ) + '/akismet/service'
3
- require File.dirname( __FILE__ ) + '/akismet/client'
4
-
5
- module Akismet
6
-
7
- # convenience method
8
- def self.verify_key( api_key, home_url )
9
- Service.new.verify_key api_key, home_url
10
- end
11
-
1
+ %w{
2
+ version
3
+ error
4
+ client
5
+ }.each do |file|
6
+ require File.join( File.dirname( __FILE__ ), 'akismet', file )
12
7
  end
@@ -1,41 +1,410 @@
1
+ require 'net/http'
2
+ require 'cgi'
3
+
1
4
  module Akismet
5
+
6
+ # A Ruby client for the Akismet API.
7
+ #
8
+ # @example
9
+ #
10
+ # # Verify an API key
11
+ # #
12
+ #
13
+ # Akismet::Client.new( 'apikey123', 'http://jonahb.com' ).verify_key
14
+ #
15
+ # @example
16
+ #
17
+ # # Check whether a comment is spam
18
+ # #
19
+ #
20
+ # client = Akismet::Client.new( 'apikey123',
21
+ # 'http://jonahb.com',
22
+ # :app_name => 'jonahb.com',
23
+ # :app_version => '1.0' )
24
+ #
25
+ # # assumes variables comment, post_url, request (a racklike HTTP request)
26
+ # spam = client.comment_check( request.remote_ip,
27
+ # request.user_agent,
28
+ # :content_type => 'comment',
29
+ # :referrer => request.headers[ 'HTTP_REFERER' ],
30
+ # :permalink => post_url,
31
+ # :comment_author => comment.author,
32
+ # :comment_author_email => comment.author_email,
33
+ # :comment_content => comment.body )
34
+ #
35
+ # if spam
36
+ # # ...
37
+ # end
38
+ #
39
+ # @example
40
+ #
41
+ # # Submit a batch of checks using a single TCP connection
42
+ # #
43
+ #
44
+ #
45
+ # client = Akismet::Client.new( 'apikey123',
46
+ # 'http://jonahb.com',
47
+ # :app_name => 'jonahb.com',
48
+ # :app_version => '1.0' )
49
+ #
50
+ # begin
51
+ # client.open
52
+ # comments.each do |comment|
53
+ # client.comment_check( ... ) # see example above
54
+ # end
55
+ # ensure
56
+ # client.close
57
+ # end
58
+ #
59
+ # # ... or ...
60
+ #
61
+ # Akismet::Client.open( 'apikey123',
62
+ # 'http://jonahb.com',
63
+ # :app_name => 'jonahb.com',
64
+ # :app_version => '1.0' ) do |client|
65
+ # comments.each do |comment|
66
+ # client.comment_check( ... ) # see example above
67
+ # end
68
+ # end
69
+ #
70
+ #
2
71
  class Client
3
72
 
4
- attr_accessor :api_key, :home_url
73
+ # The API key obtained at akismet.com.
74
+ # @return [String]
75
+ #
76
+ attr_reader :api_key
77
+
78
+
79
+ # The URL of the home page of the application making the request.
80
+ # @return [String]
81
+ #
82
+ attr_reader :home_url
83
+
84
+
85
+ # The name of the application making the request, e.g "jonahb.com".
86
+ # @return [String]
87
+ #
88
+ attr_reader :app_name
89
+
90
+
91
+ # The version of the application making the request, e.g. "1.0".
92
+ # @return [String]
93
+ #
94
+ attr_reader :app_version
5
95
 
6
- def initialize( api_key, home_url )
96
+
97
+ # @param [String] api_key
98
+ # The API key obtained at akismet.com.
99
+ # @param [String] home_url
100
+ # The URL of the home page of the application making the request.
101
+ # @option options [String] :app_name
102
+ # The name of the application making the request, e.g. "jonahb.com".
103
+ # Forms part of the User-Agent header submitted to Akismet.
104
+ # @option options [String] :app_version
105
+ # The version of the application making the request, e.g. "1.0". Forms
106
+ # part of the User-Agent header submitted to Akismet. Ignored if
107
+ # :app_name is not privded.
108
+ #
109
+ def initialize( api_key, home_url, options = {} )
7
110
  @api_key = api_key
8
111
  @home_url = home_url
9
- @service = Service.new
112
+ @app_name = options[ :app_name ]
113
+ @app_version = options[ :app_version ]
114
+ end
115
+
116
+
117
+ # Yields a new open Client, closing the Client when the block returns.
118
+ # Takes the same arguments as {#initialize}. Use for submitting a batch of
119
+ # requests using a single TCP and HTTP session.
120
+ #
121
+ # @yield [Akismet::Client]
122
+ # @return [nil]
123
+ # @see #initialize
124
+ # @see #open
125
+ #
126
+ def self.open( api_key, home_url, options = {} )
127
+ raise "Block required" unless block_given?
128
+ client = nil
129
+ begin
130
+ client = new( api_key, home_url, options )
131
+ client.open
132
+ yield client
133
+ ensure
134
+ client.close if client
135
+ end
136
+ nil
137
+ end
138
+
139
+
140
+ # Opens the client. You may use this method in combination with {#close}
141
+ # to submit a batch of requests using a single TCP and HTTP session.
142
+ #
143
+ # If you don't call this before calling {#comment_check}, {#submit_ham},
144
+ # or {#submit_spam}, a session will be created and opened for the duration
145
+ # of the call.
146
+ #
147
+ # Note that calls to {#verify_key} always create a session. This is due to
148
+ # a peculiarity of the Akismet API where {#verify_key} requests must be
149
+ # sent to a different host.
150
+ #
151
+ # @return [self]
152
+ # @raise [RuntimeError]
153
+ # The client is already open.
154
+ # @see #close
155
+ # @see Akismet::Client.open
156
+ #
157
+ def open
158
+ raise "Already open" if open?
159
+ @http_session = Net::HTTP.new( "#{ api_key }.rest.akismet.com", 80 )
160
+ @http_session.start
161
+ self
162
+ end
163
+
164
+
165
+ # Closes the Client.
166
+ # @return [self]
167
+ # @see #open
168
+ #
169
+ def close
170
+ @http_session.finish if open?
171
+ @http_session = nil
172
+ self
173
+ end
174
+
175
+
176
+ # Whether the Client is open.
177
+ # @return [boolean]
178
+ #
179
+ def open?
180
+ @http_session && @http_session.started?
10
181
  end
11
182
 
183
+
184
+ # Checks the validity of the API key.
185
+ # @return [boolean]
186
+ #
12
187
  def verify_key
13
- @service.verify_key @api_key, @home_url
188
+ response = Net::HTTP.start( 'rest.akismet.com', 80 ) do |session|
189
+ invoke( session, 'verify-key', :blog => home_url, :key => api_key )
190
+ end
191
+
192
+ unless %w{ valid invalid }.include?( response.body )
193
+ raise_with_response response
194
+ end
195
+
196
+ response.body == 'valid'
14
197
  end
15
198
 
16
- def check( user_ip, user_agent, params = {} )
17
- @service.comment_check @api_key,
18
- @home_url,
199
+
200
+ # Checks whether a comment is spam. You are encouraged the submit, in
201
+ # addition to the documented parameters, data about the client and the
202
+ # comment submission. For example, if the client is an HTTP server,
203
+ # include HTTP headers and environment variables.
204
+ #
205
+ # If the Client is not open, opens it for the duration of the call.
206
+ #
207
+ # @return [boolean]
208
+ # @raise [Akismet::Error]
209
+ # @param [String] user_ip
210
+ # The IP address of the submitter of the comment.
211
+ # @param [String] user_agent
212
+ # The user agent of the web browser submitting the comment. Typically
213
+ # the HTTP_USER_AGENT CGI variable. Not to be confused with the user
214
+ # agent of the Akismet library.
215
+ # @option params [String] :referrer
216
+ # The value of the HTTP_REFERER header. Note that the parameter is
217
+ # spelled with two consecutive 'r's.
218
+ # @option params [String] :permalink
219
+ # The permanent URL of the entry to which the comment pertains.
220
+ # @option params [String] :comment_type
221
+ # 'comment', 'trackback', 'pingback', or a made-up value like
222
+ # 'registration'
223
+ # @option params [String] :comment_author
224
+ # The name of the author of the comment.
225
+ # @option params [String] :comment_author_email
226
+ # The email address of the author of the comment.
227
+ # @option params [String] :comment_author_url
228
+ # A URL submitted with the comment.
229
+ # @option params [String] :comment_content
230
+ # The text of the comment.
231
+ #
232
+ def comment_check( user_ip, user_agent, params = {} )
233
+ response = invoke_comment_method( 'comment-check',
19
234
  user_ip,
20
235
  user_agent,
21
- params
236
+ params )
237
+
238
+ unless %w{ true false }.include?( response.body )
239
+ raise_with_response response
240
+ end
241
+
242
+ response.body == 'true'
22
243
  end
23
244
 
24
- def ham( user_ip, user_agent, params = {} )
25
- @service.submit_ham @api_key,
26
- @home_url,
245
+
246
+ # Submits a comment that has been identified as not-spam (ham). Takes
247
+ # the same arguments as {#comment_check}. If the Client is not open, opens
248
+ # it for the duration of the call.
249
+ #
250
+ # @return [nil]
251
+ # @raise [Akismet::Error]
252
+ # @see #comment_check
253
+ #
254
+ def submit_ham( user_ip, user_agent, params = {} )
255
+ response = invoke_comment_method( 'submit-ham',
27
256
  user_ip,
28
257
  user_agent,
29
- params
258
+ params )
259
+
260
+ unless response.body == 'Thanks for making the web a better place.'
261
+ raise_from_response response
262
+ end
263
+
264
+ nil
30
265
  end
31
266
 
32
- def spam( user_ip, user_agent, params = {} )
33
- @service.submit_spam @api_key,
34
- @home_url,
267
+
268
+ # Submits a comment that has been identified as spam. Takes the same
269
+ # arguments as {#comment_check}. If the Client is not open, opens it for
270
+ # the duration of the call.
271
+ #
272
+ # @return [nil]
273
+ # @raise [Akismet::Error]
274
+ # @see #comment_check
275
+ #
276
+ def submit_spam( user_ip, user_agent, params = {} )
277
+ response = invoke_comment_method( 'submit-spam',
35
278
  user_ip,
36
279
  user_agent,
37
- params
280
+ params )
281
+
282
+ unless response.body == 'Thanks for making the web a better place.'
283
+ raise_from_response response
284
+ end
285
+
286
+ nil
287
+ end
288
+
289
+
290
+ private
291
+
292
+
293
+ # Yields an HTTP session to the given block. Uses this instance's open
294
+ # session if any; otherwise opens one and closes it when the block
295
+ # returns.
296
+ # @yield [Net::HTTP]
297
+ #
298
+ def in_http_session
299
+ raise "Block required" unless block_given?
300
+ if open?
301
+ yield @http_session
302
+ else
303
+ begin
304
+ open
305
+ yield @http_session
306
+ ensure
307
+ close
308
+ end
309
+ end
310
+ end
311
+
312
+
313
+ # Raises an error given a response. The Akismet documentation states that
314
+ # the HTTP headers of the response may contain error strings. I can't
315
+ # seem to find them, so for now just raise an unknown error.
316
+ # @param [Net::HTTPResponse] response
317
+ #
318
+ def raise_with_response( response )
319
+ raise Error.new( Error::UNKNOWN, 'Unknown error' )
320
+ end
321
+
322
+
323
+ # @param [String] method_name
324
+ # @param [String] user_ip
325
+ # @param [String] user_agent
326
+ # @param [Hash] params
327
+ # @return [Net::HTTPResponse]
328
+ # @raise [Akismet::Error]
329
+ # The API key is invalid.
330
+ #
331
+ def invoke_comment_method( method_name, user_ip, user_agent, params = {} )
332
+ params = params.merge :blog => home_url,
333
+ :user_ip => user_ip,
334
+ :user_agent => user_agent
335
+
336
+ response = in_http_session do |session|
337
+ invoke( session, method_name, params )
338
+ end
339
+
340
+ if response.body == 'invalid'
341
+ raise Error.new( Error::INVALID_API_KEY, 'Invalid API key' )
342
+ raise Error, 'Invalid API key'
343
+ end
344
+
345
+ response
346
+ end
347
+
348
+
349
+ # @param [Net::HTTP] http_session
350
+ # A started HTTP session
351
+ # @param [String] method_name
352
+ # @return [Net::HTTPResponse]
353
+ # @raise [Akismet::Error]
354
+ # An HTTP response other than 200 is received.
355
+ #
356
+ def invoke( http_session, method_name, params = {} )
357
+ response = http_session.post( "/1.1/#{ method_name }",
358
+ url_encode( params ),
359
+ http_headers )
360
+
361
+ unless response.is_a?( Net::HTTPOK )
362
+ raise Error, "HTTP #{ response.code } received (expected 200)"
363
+ end
364
+
365
+ response
366
+ end
367
+
368
+
369
+ # @return [Hash]
370
+ #
371
+ def http_headers
372
+ { 'User-Agent' => user_agent }
373
+ end
374
+
375
+
376
+ # @return [String]
377
+ #
378
+ def url_encode( hash = {} )
379
+ hash.collect do |k, v|
380
+ "#{ CGI.escape( k.to_s ) }=#{ CGI.escape( v.to_s ) }"
381
+ end.join( "&" )
382
+ end
383
+
384
+
385
+ # From the Akismet documentation:
386
+ # If possible, your user agent string should always use the following
387
+ # format: Application Name/Version | Plugin Name/Version
388
+ # @return [String]
389
+ #
390
+ def user_agent
391
+ [ user_agent_app, user_agent_plugin ].compact.join( " | " )
38
392
  end
39
393
 
394
+
395
+ # Returns nil if the Client was instantiated without an app_name.
396
+ # @return [String]
397
+ #
398
+ def user_agent_app
399
+ app_name && [ app_name, app_version ].compact.join( "/" )
400
+ end
401
+
402
+
403
+ # @return [String]
404
+ #
405
+ def user_agent_plugin
406
+ "Ruby Akismet/#{ Akismet::VERSION }"
407
+ end
408
+
40
409
  end
41
- end
410
+ end
@@ -0,0 +1,23 @@
1
+ module Akismet
2
+
3
+ class Error < StandardError
4
+
5
+ UNKNOWN = 1
6
+ INVALID_API_KEY = 2
7
+
8
+ # An error code corresponding to a constant in Akismet::Error.
9
+ # @return [Integer]
10
+ attr_reader :code
11
+
12
+ # @param [String] message
13
+ # A human-readable description of the error.
14
+ # @param [Integer] code
15
+ # An error code corresponding to a constant in Akismet::Error.
16
+ #
17
+ def initialize( code, message = nil )
18
+ super( message )
19
+ @code = code
20
+ end
21
+ end
22
+
23
+ end
@@ -1,3 +1,7 @@
1
1
  module Akismet
2
- VERSION = '0.1.2'
2
+ # The version of the Akismet gem.
3
+ VERSION = '1.0.0'
4
+
5
+ # The supported version of the Akismet API.
6
+ API_VERSION = '1.3'
3
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: akismet
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease: false
4
+ hash: 23
5
+ prerelease:
6
6
  segments:
7
- - 0
8
7
  - 1
9
- - 2
10
- version: 0.1.2
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jonah Burke
@@ -15,13 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-01 00:00:00 -05:00
19
- default_executable:
18
+ date: 2012-01-14 00:00:00 Z
20
19
  dependencies: []
21
20
 
22
21
  description: A Ruby client for the Akismet API
23
- email:
24
- - jonah@bigthink.com
22
+ email: jonah@jonahb.com
25
23
  executables: []
26
24
 
27
25
  extensions: []
@@ -29,14 +27,15 @@ extensions: []
29
27
  extra_rdoc_files: []
30
28
 
31
29
  files:
30
+ - README.md
31
+ - MIT-LICENSE
32
32
  - lib/akismet/client.rb
33
- - lib/akismet/service.rb
33
+ - lib/akismet/error.rb
34
34
  - lib/akismet/version.rb
35
35
  - lib/akismet.rb
36
- has_rdoc: true
37
- homepage: http://github.com/bigthink/akismet
38
- licenses: []
39
-
36
+ homepage: http://github.com/jonahb/akismet
37
+ licenses:
38
+ - MIT
40
39
  post_install_message:
41
40
  rdoc_options: []
42
41
 
@@ -45,31 +44,28 @@ require_paths:
45
44
  required_ruby_version: !ruby/object:Gem::Requirement
46
45
  none: false
47
46
  requirements:
48
- - - ~>
47
+ - - ">="
49
48
  - !ruby/object:Gem::Version
50
- hash: 59
49
+ hash: 3
51
50
  segments:
52
- - 1
53
- - 8
54
- - 6
55
- version: 1.8.6
51
+ - 0
52
+ version: "0"
56
53
  required_rubygems_version: !ruby/object:Gem::Requirement
57
54
  none: false
58
55
  requirements:
59
56
  - - ">="
60
57
  - !ruby/object:Gem::Version
61
- hash: 17
58
+ hash: 3
62
59
  segments:
63
- - 1
64
- - 3
65
- - 5
66
- version: 1.3.5
60
+ - 0
61
+ version: "0"
67
62
  requirements: []
68
63
 
69
64
  rubyforge_project:
70
- rubygems_version: 1.3.7
65
+ rubygems_version: 1.8.12
71
66
  signing_key:
72
67
  specification_version: 3
73
68
  summary: A Ruby client for the Akismet API
74
69
  test_files: []
75
70
 
71
+ has_rdoc: yard
@@ -1,87 +0,0 @@
1
- require 'net/http'
2
- require 'cgi'
3
-
4
- module Akismet
5
-
6
- AKISMET_VERSION = 1.1
7
- PORT = 80
8
- HEADERS = { 'User-Agent' => "Ruby/Akismet #{ VERSION }" }
9
-
10
- module CommentType
11
- COMMENT = 'comment'
12
- TRACKBACK = 'trackback'
13
- PINGBACK = 'pingback'
14
- end
15
-
16
- # Parameters to +comment_check+, +submit_ham+, and +submit_spam+ may
17
- # include:
18
- #
19
- # * content_type
20
- # * comment_author
21
- # * comment_author_email
22
- # * comment_author_url
23
- # * comment_content
24
- # * others representing HTTP headers and server environment vars
25
- #
26
- # See http://akismet.com/development/api/#comment-check for more info.
27
- #
28
- class Service
29
-
30
- def verify_key( api_key, home_url )
31
- response = invoke( 'verify-key', 'rest.akismet.com', :blog => home_url,
32
- :key => api_key )
33
-
34
- response.is_a?( Net::HTTPOK ) && response.body == 'valid'
35
- end
36
-
37
- def comment_check( api_key, home_url, user_ip, user_agent, params = {} )
38
- response = invoke_comment_method 'comment-check', api_key, home_url,
39
- user_ip, user_agent, params
40
-
41
- response.is_a?( Net::HTTPOK ) && response.body == 'true'
42
- end
43
-
44
- def submit_ham( api_key, home_url, user_ip, user_agent, params = {} )
45
- invoke_comment_method 'submit-ham', api_key, home_url, user_ip,
46
- user_agent, params
47
-
48
- nil
49
- end
50
-
51
- def submit_spam( api_key, home_url, user_ip, user_agent, params = {} )
52
- invoke_comment_method 'submit-spam', api_key, home_url, user_ip,
53
- user_agent, params
54
-
55
- nil
56
- end
57
-
58
- private
59
-
60
- def invoke_comment_method( method_name, api_key, home_url, user_ip, user_agent, params = {} )
61
- host = "#{ api_key }.rest.akismet.com"
62
-
63
- params = params.merge :blog => home_url,
64
- :user_ip => user_ip,
65
- :user_agent => user_agent
66
-
67
- invoke method_name, host, params
68
- end
69
-
70
- # returns an HTTP response
71
- def invoke( method_name, host, params )
72
- path = "/#{ AKISMET_VERSION }/#{ method_name }"
73
- data = url_encode( params )
74
-
75
- Net::HTTP.start( host, PORT ) do |http|
76
- http.post( path, data, HEADERS )
77
- end
78
- end
79
-
80
- def url_encode( hash = {} )
81
- hash.collect do |k, v|
82
- "#{ CGI.escape( k.to_s ) }=#{ CGI.escape( v.to_s ) }"
83
- end.join( "&" )
84
- end
85
-
86
- end
87
- end