akismet-2.0.0/0000755000175000017500000000000012667753760012417 5ustar debiandebianakismet-2.0.0/.gitignore0000644000175000017500000000005712667753760014411 0ustar debiandebian/.bundle/ /.yardoc/ /Gemfile.lock /doc/ /pkg/ akismet-2.0.0/Gemfile0000644000175000017500000000004712667753760013713 0ustar debiandebiansource 'https://rubygems.org' gemspec akismet-2.0.0/.yardopts0000644000175000017500000000004512667753760014264 0ustar debiandebian--no-private - README.md LICENSE.txt akismet-2.0.0/LICENSE.txt0000644000175000017500000000203612667753760014243 0ustar debiandebianCopyright (c) 2012 Jonah Burke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.akismet-2.0.0/Rakefile0000644000175000017500000000030512667753760014062 0ustar debiandebianrequire 'rake/testtask' require 'bundler/gem_tasks' task default: :test Rake::TestTask.new do |t| t.libs.push 'test' t.pattern = 'test/**/*_test.rb' t.warning = true t.verbose = true end akismet-2.0.0/akismet.gemspec0000644000175000017500000000134512667753760015424 0ustar debiandebian# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'akismet/version' Gem::Specification.new do |spec| spec.name = 'akismet' spec.version = Akismet::VERSION spec.author = ['Jonah Burke'] spec.email = ['jonah@jonahb.com'] spec.summary = 'A Ruby client for the Akismet API' spec.homepage = 'http://github.com/jonahb/akismet' spec.license = 'MIT' spec.required_ruby_version = '>= 1.9.3' spec.require_paths = ['lib'] spec.files = Dir[ 'README.md', 'LICENSE.txt', '.yardopts', 'lib/**/*' ] spec.add_development_dependency 'bundler', '~> 1.7' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'yard', '~> 0.8.7' end akismet-2.0.0/CHANGELOG.md0000644000175000017500000000226312667753760014233 0ustar debiandebian# Changelog akismet adheres to [SemVer 2.0.0](http://semver.org/spec/v2.0.0.html). ## 2.0.0 - 2014-02-14 ### Added * Short method aliases in `Client`, e.g. `spam` for `submit_spam` * Class-level convenience methods in `Akismet` * Blatant spam is reported via `Akismet#check` * New method `Client#spam?` * `Client#open` accepts a block * Exceptions contain error messages returned by the Akismet API * Better documentation; new README ### Changed * New parameter names in `Client` instance methods * Environment variables (e.g. HTTP headers) are passed as a hash in `env` parameter of `Client` instance methods * `Client` instance methods raise exceptions if invalid parameters are passed * `Client` instance methods format non-string parameters, e.g. a `DateTime` is formatted per ISO 8601 * `Client#check` returns two Boolean values: whether the comment is spam and whether it is blatant * Parameter values are transcoded to UTF-8 if they have a different encoding * Ruby 1.9.3+ required ## 1.0.2 - 2014-12-07 ### Fixed * Test failures with Ruby 2.0+ ## 1.0.1 - 2014-12-05 ### Fixed * Gem won't build on Ruby 1.9+ * Runtime error when Akismet returns unexpected response body * Typo in README akismet-2.0.0/test/0000755000175000017500000000000012667753760013376 5ustar debiandebianakismet-2.0.0/test/test_helper.rb0000644000175000017500000000037512667753760016246 0ustar debiandebianrequire 'minitest/autorun' require 'akismet' API_KEY = ENV['AKISMET_API_KEY'] || raise("Set the AKISMET_API_KEY environment variable to an API key obtained from akismet.com") Test = defined?(Minitest::Test) ? Minitest::Test : MiniTest::Unit::TestCase akismet-2.0.0/test/akismet_test.rb0000644000175000017500000000144012667753760016416 0ustar debiandebianrequire 'test_helper' class AkismetTest < Test def setup Akismet.api_key = API_KEY Akismet.app_url = 'http://example.com' end [:spam?, :check, :spam, :ham].each do |method| define_method("test_#{method}_succeeds") do Akismet.send method, 'ip', 'ua' end end def test_check_raises_if_api_key_not_set Akismet.api_key = nil assert_raises(RuntimeError) do Akismet.check 'ip', 'ua' end end def test_check_raises_if_app_url_not_set Akismet.app_url = nil assert_raises(RuntimeError) do Akismet.check 'ip', 'ua' end end def test_open_succeeds Akismet.open do |client| client.check 'ip', 'ua' end end def test_open_raises_without_block assert_raises(RuntimeError) do Akismet.open end end end akismet-2.0.0/test/client_test.rb0000644000175000017500000000756012667753760016250 0ustar debiandebianrequire 'test_helper' require 'date' class ClientTest < Test APP_URL = 'http://example.com' APP_NAME = 'Akismet tests' def setup @client = Akismet::Client.new( API_KEY, APP_URL, app_name: APP_NAME, app_version: Akismet::VERSION ) @invalid_client = Akismet::Client.new( 'invalid-api-key', APP_URL, app_name: APP_NAME, app_version: Akismet::VERSION ) end def test_attrs assert_equal @client.api_key, API_KEY assert_equal @client.app_url, APP_URL assert_equal @client.app_name, 'Akismet tests' assert_equal @client.app_version, Akismet::VERSION end def test_verify_key_with_valid_key_returns_true assert_equal true, @client.verify_key end def test_verify_key_with_invalid_key_returns_false assert_equal false, @invalid_client.verify_key end def test_check_with_invalid_api_key_raises assert_raises( Akismet::Error ) do @invalid_client.comment_check 'ip', 'ua' end end # Akismet returns true when author == 'viagra-test-123' def test_check_with_spam_returns_true spam, _ = @client.check('ip', 'ua', author: 'viagra-test-123') assert_equal true, spam end # Akismet returns false when user_role == 'administrator' def test_check_with_ham_returns_false spam, _ = @client.check('ip', 'ua', user_role: 'administrator') assert_equal false, spam end def test_check_with_all_params_succeeds @client.check 'ip', 'ua', type: 'comment', text: 'hello', created_at: DateTime.now, author: 'author', author_url: 'http://example.com', author_email: 'joe@example.com', post_url: 'http://example.com/posts/1', post_modified_at: DateTime.now, languages: %w{en fr}, referrer: 'http://example.com', env: {a: 1, b: 1}, user_role: 'Administrator', test: true end # Akismet returns true when author == 'viagra-test-123' def test_spam_predicate_with_spam_returns_true assert_equal true, @client.spam?('ip', 'ua', author: 'viagra-test-123') end # Akismet returns false when user_role == 'administrator' def test_spam_predicate_with_ham_returns_false assert_equal false, @client.spam?('ip', 'ua', user_role: 'administrator') end def test_spam_predicate_with_invalid_api_key_raises assert_raises( Akismet::Error ) do @invalid_client.spam? 'ip', 'ua' end end def test_ham_succeeds @client.ham 'ip', 'ua', text: 'hello' end def test_ham_with_invalid_api_key_raises assert_raises( Akismet::Error ) do @invalid_client.ham 'ip', 'ua' end end def test_spam_succeeds @client.spam 'ip', 'ua', test: 'hello' end def test_spam_with_invalid_api_key_raises assert_raises( Akismet::Error ) do @invalid_client.spam 'ip', 'ua' end end def test_open_opens_client refute @client.open? @client.open assert @client.open? @client.close end def test_open_with_block_opens_then_closes_client refute @client.open? @client.open { assert @client.open? } refute @client.open? end def test_open_raises_when_client_open assert !@client.open? @client.open assert @client.open? assert_raises( RuntimeError ) { @client.open } end def test_close_closes_client @client.open assert @client.open? @client.close refute @client.open? end def test_close_succeeds_when_client_closed assert !@client.open? @client.close end def test_class_open_yields_open_client Akismet::Client.open( API_KEY, APP_URL ) do |client| assert client.is_a?( Akismet::Client ) assert client.open? end end def test_conflicting_env_var_raises assert_raises(ArgumentError) do @client.check 'ip', 'ua', env: { referrer: 'referrer' } end end def test_invalid_param_raises assert_raises(ArgumentError) do @client.check 'ip', 'ua', invalid_param: 'invalid' end end endakismet-2.0.0/lib/0000755000175000017500000000000012667753760013165 5ustar debiandebianakismet-2.0.0/lib/akismet.rb0000644000175000017500000000335312667753760015153 0ustar debiandebian%w{ version error client }.each do |file| require "akismet/#{file}" end # {Akismet} provides convenience methods that instantiate a {Akismet::Client} # and invoke the Akismet API in one call. Before calling these methods, set # {api_key} and {app_url}. # module Akismet class << self # The API key obtained at akismet.com. Set before calling the {Akismet} # class methods. # @return [String] attr_accessor :api_key # A URL that identifies the application making the request. Set before # calling the {Akismet} class methods. # @return [String] attr_accessor :app_url # The name of the application making the request # @return [String] attr_accessor :app_name # The version of the application making the request # @return [String] attr_accessor :app_version # (see Client#check) def check(user_ip, user_agent, params = {}) with_client { |client| client.check user_ip, user_agent, params } end # (see Client#spam?) def spam?(user_ip, user_agent, params = {}) with_client { |client| client.spam? user_ip, user_agent, params } end # (see Client#spam) def spam(user_ip, user_agent, params = {}) with_client { |client| client.spam user_ip, user_agent, params } end # (see Client#ham) def ham(user_ip, user_agent, params = {}) with_client { |client| client.ham user_ip, user_agent, params } end # (see Client.open) def open(&block) with_client(&block) end private def with_client(&block) raise "Set Akismet.api_key" unless api_key raise "Set Akismet.app_url" unless app_url Akismet::Client.open api_key, app_url, app_name: app_name, app_version: app_version, &block end end end akismet-2.0.0/lib/akismet/0000755000175000017500000000000012667753760014622 5ustar debiandebianakismet-2.0.0/lib/akismet/client.rb0000644000175000017500000002656412667753760016442 0ustar debiandebianrequire 'date' require 'net/http' require 'uri' module Akismet class Client # The API key obtained at akismet.com # @return [String] attr_reader :api_key # A URL that identifies the application making the request # @return [String] attr_reader :app_url # The name of the application making the request # @return [String] attr_reader :app_name # The version of the application making the request # @return [String] attr_reader :app_version # @!group Constructors # @param [String] api_key # The API key obtained at akismet.com # @param [String] app_url # The URL of the home page of the application making the request # @option options [String] :app_name # The name of the application making the request, e.g. "jonahb.com". # Forms part of the User-Agent header submitted to Akismet. # @option options [String] :app_version # The version of the application making the request, e.g. "1.0". Forms # part of the User-Agent header submitted to Akismet. Ignored if # :app_name is not provided. # def initialize(api_key, app_url, options = {}) @api_key = api_key @app_url = app_url @app_name = options[ :app_name ] @app_version = options[ :app_version ] @http_session = nil end # @!group Managing Connections # Initializes a client, opens it, yields it to the given block, and closes # it when the block returns. Allows you to perform several operations over # a single TCP connection. # @param (see #initialize) # @option (see #initialize) # @yieldparam [Client] client # @return [Object] # The return value of the block # @see #open # def self.open(api_key, app_url, options = {}) raise "Block required" unless block_given? client = new(api_key, app_url) client.open { yield client } end # Opens the client, creating a new TCP connection. # # If a block is given, yields to the block, closes the client when the # block returns, and returns the return value of the block. If a # block is not given, returns self and leaves the client open, relying on # the caller to close the client with {#close}. # # Note that opening and closing the client is only required if you want to # make several calls over one TCP connection. Otherwise, you can simply # call {#spam?}, {#check}, {#ham}, or {#spam}, which call {#open} for you # if necessary. # # Due to a peculiarity of the Akismet API, {#verify_key} always creates its # own connection. # # @overload open # Opens the client, yields to the block, and closes the client when the # block returns. # @yield # A block to be called when the client is open # @return [Object] # The return value of the block # @raise [StandardError] # The client is already open # @overload open # @return [self] # @raise [StandardError] # The client is already open # def open raise "Already open" if open? @http_session = Net::HTTP.new( "#{ api_key }.rest.akismet.com", 80 ) begin @http_session.start block_given? ? yield : self ensure close if block_given? end end # Closes the Client. # @return [self] # @see #open # def close @http_session.finish if open? @http_session = nil self end # Whether the Client is open. # @return [Boolean] # def open? @http_session && @http_session.started? end # @!group Verifying Keys # Checks the validity of the API key. # @return [Boolean] # def verify_key response = Net::HTTP.start('rest.akismet.com', 80) do |session| invoke session, 'verify-key', blog: app_url, key: api_key end unless %w{ valid invalid }.include?(response.body) raise_with_response response end response.body == 'valid' end # @!macro akismet_method # @param [String] user_ip # The comment author's IP address # @param [String] user_agent # The comment author's user-agent # @param [Hash{Symbol => Object}] params # Optional parameters. To maximize accuracy, pass as many as possible. # @option params [String] :referrer # The value of the HTTP_REFERER header. Note that the parameter is # spelled with two consecutive 'r's. # @option params [String] :post_url # The URL of the post, article, etc. on which the comment was made # @option params [DateTime] :post_modified_at # The date and time the post was last modified # @option params [String] :type # Suggested values include 'comment', 'trackback', and 'pingback' # @option params [String] :text # The text of the comment # @option params [DateTime] :created_at # The date and time the comment was created # @option params [String] :author # The comment author's name # @option params [String] :author_email # The comment author's email address # @option params [String] :author_url # The comment author's personal URL # @option params [Array] :languages # The ISO 639-1 codes of the languages in use on the site where the # comment appears # @option params [Boolean] :test # When set to true, Akismet does not use the comment to train the filter # @option params [Hash{Symbol, String => Object}] :env # Environment variables such as HTTP headers related to the comment # submission # @raise [Akismet::Error] # The Akismet service returned an error # @raise [ArgumentError] # An environment variable conflicts with a built-in parameter # @raise [ArgumentError] # Invalid param # @!group Checking # Checks whether a comment is spam and whether it is "blatant." # @!macro akismet_method # @return [(Boolean, Boolean)] # An array containing two booleans. The first indicates whether the # comment is spam. The second indicates whether it is "blatant," # i.e. whether it can be deleted without review. # def check(user_ip, user_agent, params = {}) response = invoke_comment_method('comment-check', user_ip, user_agent, params) unless %w{ true false }.include?(response.body) raise_with_response response end [ response.body == 'true', response['X-akismet-pro-tip'] == 'discard' ] end alias_method :comment_check, :check # Checks whether a comment is spam. # @!macro akismet_method # @return [Boolean] # def spam?(user_ip, user_agent, params = {}) check(user_ip, user_agent, params)[0] end # @!group Reporting # Submits a comment that has been identified as not-spam (ham). # @!macro akismet_method # @return [void] # def ham(user_ip, user_agent, params = {}) response = invoke_comment_method('submit-ham', user_ip, user_agent, params) unless response.body == 'Thanks for making the web a better place.' raise_with_response response end end alias_method :submit_ham, :ham # Submits a comment that has been identified as spam. # @!macro akismet_method # @return [void] # def spam(user_ip, user_agent, params = {}) response = invoke_comment_method('submit-spam', user_ip, user_agent, params) unless response.body == 'Thanks for making the web a better place.' raise_with_response response end end alias_method :submit_spam, :spam private # Yields an HTTP session to the given block. Uses this instance's open # session if any; otherwise opens one and closes it when the block # returns. # @yield [Net::HTTP] # def in_http_session if open? yield @http_session else open { yield @http_session } end end # @param [Net::HTTPResponse] response def raise_with_response( response ) raise Error, response['X-akismet-debug-help'] || 'Unknown error' end # @param [String] method_name # @param [String] user_ip # @param [String] user_agent # @param [Hash] params # @return [Net::HTTPResponse] # @raise [ArgumentError] # An environment variable conflicts with a built-in parameter # @raise [ArgumentError] # Invalid parameter # def invoke_comment_method(method_name, user_ip, user_agent, params = {}) env = params[:env] || {} for key in env.keys if PARAM_TO_API_PARAM.has_value?(key.to_sym) raise ArgumentError, "Environment variable '#{key}' conflicts with built-in API parameter" end end params = params.each_with_object(Hash.new) do |(name, value), api_params| next if name == :env api_name = PARAM_TO_API_PARAM[name] || raise(ArgumentError, "Invalid param: #{name}") api_params[api_name] = value end params = env.merge(params).merge(blog: app_url, user_ip: user_ip, user_agent: user_agent) in_http_session do |session| invoke session, method_name, params end end # @param [Net::HTTP] http_session # A started HTTP session # @param [String] method_name # @return [Net::HTTPResponse] # @raise [Akismet::Error] # An HTTP response other than 200 is received. # def invoke(http_session, method_name, params = {}) params[:blog_charset] = 'UTF-8' params = params.collect do |name, value| [name.to_s.encode('UTF-8'), format(value).encode('UTF-8')] end response = http_session.post("/1.1/#{ method_name }", URI.encode_www_form(params), http_headers) unless response.is_a?( Net::HTTPOK ) raise Error, "HTTP #{ response.code } received (expected 200)" end response end # @param [Object] object # @return [String] def format(object) case object when DateTime object.iso8601 when TrueClass '1' when FalseClass '0' when Array object.collect { |element| format(element) }.join(', ') else object.to_s end end # @return [Hash] def http_headers { 'User-Agent' => user_agent, 'Content-Type' => 'application/x-www-form-urlencoded' } end # From the Akismet documentation: # If possible, your user agent string should always use the following # format: Application Name/Version | Plugin Name/Version # @return [String] # def user_agent [user_agent_app, user_agent_plugin].compact.join(" | ") end # Returns nil if the Client was instantiated without an app_name. # @return [String] # def user_agent_app app_name && [app_name, app_version].compact.join("/") end # @return [String] def user_agent_plugin "Ruby Akismet/#{ Akismet::VERSION }" end PARAM_TO_API_PARAM = { referrer: :referrer, post_url: :permalink, post_modified_at: :comment_post_modified_gmt, text: :comment_content, created_at: :comment_date_gmt, type: :comment_type, author: :comment_author, author_url: :comment_author_url, author_email: :comment_author_email, languages: :blog_lang, user_role: :user_role, test: :is_test, } end end akismet-2.0.0/lib/akismet/error.rb0000644000175000017500000000007112667753760016276 0ustar debiandebianmodule Akismet class Error < StandardError end end akismet-2.0.0/lib/akismet/version.rb0000644000175000017500000000021712667753760016634 0ustar debiandebianmodule Akismet # The version of the Akismet gem. VERSION = '2.0.0' # The supported version of the Akismet API. API_VERSION = '1.3' endakismet-2.0.0/README.md0000644000175000017500000000650512667753760013704 0ustar debiandebian# akismet A Ruby client for the [Akismet API](http://akismet.com/development/api/). [![Gem Version](https://badge.fury.io/rb/akismet.svg)](http://badge.fury.io/rb/akismet) [![Build Status](https://travis-ci.org/jonahb/akismet.svg?branch=master)](https://travis-ci.org/jonahb/akismet) ## Getting Started ### Installation ```bash gem install akismet ``` ### Documentation This README provides an overview of `akismet`. Full documentation is available at [rubydoc.info](http://www.rubydoc.info/gems/akismet). ### API Key Sign up at [akismet.com](https://akismet.com/) and retrieve your API key from your [account page](https://akismet.com/account/). ## Usage ### Basics Set your API key and app URL (a URL representing your app): ```ruby Akismet.api_key = '' Akismet.app_url = 'http://example.com' ``` Then check whether a comment is spam ... ```ruby # request is a Rack::Request is_spam = Akismet.spam?(request.ip, request.user_agent, text: 'Poppycock!') ``` ... file a spam report ... ```ruby Akismet.spam request.ip, request.user_agent, text: 'I earn $2,000 a week ...' ``` ... or flag a false positive ("ham" is not-spam): ```ruby Akismet.ham request.ip, request.user_agent, text: '"Viagra" derives from the Sanskrit ...' ``` ### Accuracy To maximize the accuracy of the filter, submit as many of the [documented](http://www.rubydoc.info/gems/akismet) parameters as possible. Also submit environment variables related to the comment as a hash in the `env` parameter (Akismet suggests [these variables](http://php.net/manual/en/reserved.variables.server.php)): ```ruby vars = %w{ HTTP_ACCEPT HTTP_ACCEPT_ENCODING # ... } params = { type: 'comment', text: 'A new life awaits you in the Off-World colonies.', created_at: DateTime.now, author: 'Eldon', author_email: 'eldont@aol.com', author_url: 'http://geocities.com/eldont', post_url: 'http://example.com/posts/1', post_modified_at: DateTime.new(2015, 1, 1), referrer: request.referrer, env: request.env.slice(*vars) # slice courtesy of Active Support } is_spam = Akismet.spam?(request.ip, request.user_agent, params) ``` ### Blatant Spam Akismet flags blatant spam that should be deleted without review. This feature is exposed via `Akismet.check`: ```ruby is_spam, is_blatant = Akismet.check(request.ip, request.user_agent, text: 'Buy everything ... now!') ``` ### Reusing Connections `Akismet.spam?` and friends create a new TCP connection each time you call them. If you have many comments to check or report, use `Akismet.open` to reuse a single connection: ```ruby Akismet.open do |client| for comment in comments is_spam = client.spam?(comment.ip, comment.user_agent, text: comment.text) end end ``` ### Akismet::Client In the example above, the object yielded to the block is an `Akismet::Client`. `Akismet::Client` underlies the `Akismet` class methods. Use it on its own to manually open and close connections or override the global API key: ```ruby begin client = Akismet::Client.new('api-key-2', 'http://example2.com') client.open client.spam request.ip, request.user_agent, text: 'Bank error in your favor!' ensure client.close end ``` ## Tests 1. Set the environment variable AKISMET\_API\_KEY to your API key 2. `rake` ## Contributing Please submit issues and pull requests to [jonahb/akismet](https://github.com/jonahb/akismet) on GitHub. akismet-2.0.0/.travis.yml0000644000175000017500000000005012667753760014523 0ustar debiandebianlanguage: ruby rvm: - 2.1.5 - 1.9.3