public
Description: Spam blocking plugin with support for Akismet and Defensio.
Homepage:
Clone URL: git://github.com/technoweenie/viking.git
viking / lib / viking / akismet.rb
100644 190 lines (176 sloc) 6.924 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
require 'net/http'
require 'uri'
require 'set'
 
# Akismet
#
# Author:: David Czarnecki
# Copyright:: Copyright (c) 2005 - David Czarnecki
# License:: BSD
#
# Rewritten to be more agnostic
module Viking
  class Akismet < Base
    class << self
      attr_accessor :valid_responses, :normal_responses, :standard_headers,
                    :host, :port
    end
 
    self.host = 'rest.akismet.com'
    self.port = 80
    self.valid_responses = Set.new(['false', ''])
    self.normal_responses = valid_responses.dup << 'true'
    self.standard_headers = {
      'User-Agent' => "Viking (Ruby Gem) v#{Viking::VERSION::STRING}",
      'Content-Type' => 'application/x-www-form-urlencoded'
    }
  
    # Create a new instance of the Akismet class
    #
    # ==== Arguments
    # Arguments are provided in the form of a Hash with the following keys
    # (as Symbols) available:
    #
    # +api_key+:: your Akismet API key
    # +blog+:: the blog associated with your api key
    #
    # The following keys are available and are entirely optional. They are
    # available incase communication with Akismet's servers requires a
    # proxy port and/or host:
    #
    # * +proxy_port+
    # * +proxy_host+
    def initialize(options)
      super
      self.verified_key = false
    end
 
    # Returns +true+ if the API key has been verified, +false+ otherwise
    def verified?
      (@verified_key ||= verify_api_key) != :false
    end
 
    # This is basically the core of everything. This call takes a number of
    # arguments and characteristics about the submitted content and then
    # returns a thumbs up or thumbs down. Almost everything is optional, but
    # performance can drop dramatically if you exclude certain elements.
    #
    # ==== Arguments
    # +options+ <Hash>:: describes the comment being verified
    #
    # The following keys are available for the +options+ hash:
    #
    # +user_ip+ (*required*)::
    # IP address of the comment submitter.
    # +user_agent+ (*required*)::
    # user agent information.
    # +referrer+ (<i>note spelling</i>)::
    # the content of the HTTP_REFERER header should be sent here.
    # +permalink+::
    # permanent location of the entry the comment was submitted to
    # +comment_type+::
    # may be blank, comment, trackback, pingback, or a made up value like
    # "registration".
    # +comment_author+::
    # submitted name with the comment
    # +comment_author_email+::
    # submitted email address
    # +comment_author_url+::
    # commenter URL
    # +comment_content+::
    # the content that was submitted
    # Other server enviroment variables::
    # In PHP there is an array of enviroment variables called <tt>_SERVER</tt>
    # which contains information about the web server itself as well as a
    # key/value for every HTTP header sent with the request. This data is
    # highly useful to Akismet as how the submited content interacts with
    # the server can be very telling, so please include as much information
    # as possible.
    def check_comment(options={})
      return false if invalid_options?
      message = call_akismet('comment-check', options)
      {:spam => !self.class.valid_responses.include?(message), :message => message}
    end
  
    # This call is for submitting comments that weren't marked as spam but
    # should have been (i.e. false negatives). It takes identical arguments as
    # +check_comment+.
    def mark_as_spam(options={})
      return false if invalid_options?
      {:message => call_akismet('submit-spam', options)}
    end
  
    # This call is intended for the marking of false positives, things that
    # were incorrectly marked as spam. It takes identical arguments as
    # +check_comment+ and +mark_as_spam+.
    def mark_as_ham(options={})
      return false if invalid_options?
      {:message => call_akismet('submit-ham', options)}
    end
    
    # Returns the URL for an Akismet request
    #
    # ==== Arguments
    # +action+ <~to_s>:: a valid Akismet function name
    #
    # ==== Returns
    # String
    def self.url(action)
      "/1.1/#{action}"
    end
 
    protected
      # Internal call to Akismet. Prepares the data for posting to the Akismet
      # service.
      #
      # ==== Arguments
      # +akismet_function+ <String>::
      # the Akismet function that should be called
      #
      # The following keys are available to configure a given call to Akismet:
      #
      # +user_ip+ (*required*)::
      # IP address of the comment submitter.
      # +user_agent+ (*required*)::
      # user agent information.
      # +referrer+ (<i>note spelling</i>)::
      # the content of the HTTP_REFERER header should be sent here.
      # +permalink+::
      # the permanent location of the entry the comment was submitted to.
      # +comment_type+::
      # may be blank, comment, trackback, pingback, or a made up value like
      # "registration".
      # +comment_author+::
      # submitted name with the comment
      # +comment_author_email+::
      # submitted email address
      # +comment_author_url+::
      # commenter URL
      # +comment_content+::
      # the content that was submitted
      # Other server enviroment variables::
      # In PHP there is an array of enviroment variables called <tt>_SERVER</tt>
      # which contains information about the web server itself as well as a
      # key/value for every HTTP header sent with the request. This data is
      # highly useful to Akismet as how the submited content interacts with
      # the server can be very telling, so please include as much information
      # as possible.
      def call_akismet(akismet_function, options={})
        http_post http_instance, akismet_function, options.update(:blog => options[:blog])
      end
 
      # Call to check and verify your API key. You may then call the
      # <tt>verified?</tt> method to see if your key has been validated
      def verify_api_key
        return :false if invalid_options?
        value = http_post http_instance, 'verify-key', :key => options[:api_key], :blog => options[:blog]
        self.verified_key = (value == "valid") ? true : :false
      end
      
      def http_post(http, action, options = {})
        data = options.to_query
        resp = http.post(self.url(action), data, self.class.standard_headers)
        log_request(self.url(action), data, resp)
        resp.body
      end
      
      def url(action)
        "/1.1/#{action}"
      end
      
    private
      attr_accessor :verified_key
 
      def http_instance
        http = Net::HTTP.new([options[:api_key], self.class.host].join("."), options[:proxy_host], options[:proxy_port])
        http.read_timeout = http.open_timeout = Viking.timeout_threshold
        http
      end
  end
end