lib/defender.rb in defender-0.1.1 vs lib/defender.rb in defender-0.2.0

- old
+ new

@@ -1,299 +1,67 @@ -require 'yaml' -require 'net/http' +require 'httparty' -class Defender +require 'defender/document' +require 'defender/statistics' + +module Defender + VERSION = "0.2.0" + + include HTTParty + # The Defensio API version currently supported by Defender - API_VERSION = "1.2" - - ROOT_URL = "http://api.defensio.com/" - - DEFAULT_OPTIONS = { - :service_type => "blog", - :api_key => "", - :owner_url => "" - } - - ## - # Raised if an invalid or no API key is given - class APIKeyError < StandardError; end - - ## - # The response from the {Defender#audit_comment} method. Should only be - # initialized by the library. - class CommentResponse + API_VERSION = "2.0" + + # HTTParty config + format :json + base_uri "api.defensio.com/#{API_VERSION}/users" + + class << self ## - # A message signature that uniquely identifies the comment in the Defensio - # system. This signature should be stored by the client for retraining - # purposes. - attr_reader :signature - + # Your Defensio API key. You need to register at defensio.com to get a key. + attr_accessor :api_key + ## - # A value indicating the relative likelihood of the comment being spam. - # This value should be stored by the client for use in building convenient - # spam sorting user-interfaces. + # The URL that will be called when Defensio is done analyzing a comment with + # asynchronous callbacks. You should be able to pass the request parameters + # straight into {Document#set_attributes}. The signature will be in the + # `signature` parameter. # - # @return [Float] A value between 0 and 1. - attr_reader :spaminess - - ## - # Initialize a CommentResponse. Should only be called by the library. + # *IMPORTANT*: Defensio will NOT retry unsuccessful callbacks to your + # server. If you do not see a POST originating from Defensio after 5 + # minutes, call {Document#refresh!} on the document to obtain the analysis + # result. # - # @param [Hash] response The response from the audit-comment call. - def initialize(response) - @signature = response["signature"] - @spam = response["spam"] - @spaminess = response["spaminess"].to_f - end - - ## - # Returns true if Defensio marked the comment as spam, returns false - # otherwise. + # Occasionally, Defensio may perform more than one POST request to your + # server for the same document. For example, if new evidence indicates that + # a document is unwanted, even though it was originally identified as + # legitimate, Defensio might notify you that the classification has changed. # - # @return [Boolean] - def spam?; @spam; end - - def to_s; @signature; end - end - - ## - # The response from the {Defender#statistics} method. Should only be - # initialized by the library. - class Statistics - ## - # Describes the percentage of comments correctly identified as spam/ham by - # Defensio on this blog. + # If you do not provide this and use asynchronous calling, you need to call + # {Document#refresh!} to get the analysis result. # - # @return [Float<0..1>] - def accuracy; @response["accuracy"]; end - - ## - # The number of spam comments caught by the filter. - def spam; @response["spam"]; end - - ## - # The number of ham (legitimate) comments accepted by the filter. - def ham; @response["ham"]; end - - ## - # The number of times a legitimate message was retrained from the spambox - # (i.e. "de-spammed" by the user) - def false_positives; @response["false-positives"]; end - - ## - # The number of times a spam message was retrained from comments box (i.e. - # "de-legitimized" by the user) - def false_negatives; @response["false-negatives"]; end - - ## - # A boolean value indicating whether Defensio is still in its initial - # learning phase. + # You can debug callbacks using http://postbin.org. See the Defensio API + # documents for the format of the requests. # - # @return [Boolean] - def learning; @response["learning"]; end - - ## - # More details on the reason(s) why Defensio is still in its initial - # learning phase. - def learning_status; @response["learning-status"]; end - - def initialize(response); @response = response; end + # @return [String] + attr_accessor :async_callback end - - attr_accessor :service_type, :api_key, :owner_url - - ## - # Raises a StandardError with the error message from Defensio if the - # response is a "failed" one. - # - # @param [Hash] response The return value from {#call_action}. - def self.raise_if_error(response) - if response["status"] == "fail" - raise StandardError, response["message"] - end - response - end - - ## - # Converts a hash with symbol keys and underscores to a hash with string - # keys and hyphens. Calls #strftime or #to_s on the values. - # - # @param [Hash] options Input options. - # @return [Hash] - def self.options_to_parameters(options) - opts = {} - options.each do |key, val| - opts[key.to_s.gsub("_", "-").downcase] = val.respond_to?(:strftime) ? - val.strftime("%Y/%m/%d") : val.to_s - end - opts - end ## - # Initialize Defender + # Determines if the given API key is valid or not. This should only be used + # when configuring the client and prior to every content analysis (Document + # POST). # - # @param [Hash] opts The options hash. - # @option opts ["blog","app"] :service_type ("blog") The service type. May be - # "app" (use of Defender within an application) or "blog" (use of Defender - # to support a blogging platform). - # @option opts [String] :api_key Your API key. This option is required, the - # method calls will fail without it. - def initialize(opts={}) - opts = DEFAULT_OPTIONS.merge(opts) - @service_type = opts[:service_type] - @api_key = opts[:api_key] - @owner_url = opts[:owner_url] - end - - ## - # Checks if the given key is valid. + # Set the API key using {Defender.api_key}. # - # @return [Boolean] - # @see http://defensio.com/api/#validate-key - def valid_key? - call_action("validate-key")["status"] == "success" ? true : false - end - - ## - # Announce an article existence. This should (if feasible) be called when an - # article or blogpost is created so Defensio can analyse it. - # - # @param [Hash] opts All options are required. - # @option opts [#to_s] :article_title The title of the article - # @option opts [#to_s] :article_author The name of the author of the article - # @option opts [#to_s] :article_author_email The email address of the person posting the - # article. - # @option opts [#to_s] :article_content The content of the article itself. - # @option opts [#to_s] :permalink The permalink of the article just posted. - # @raise [StandardError] If the call fails, a StandardError is raised with - # the error message given from Defensio. - # @return [Boolean] Returns true if the article was successfully announced, - # raises StandardError otherwise. - # @see http://defensio.com/api/#announce-article - def announce_article(opts={}) - response = call_action(Defender.options_to_parameters(opts)) - true - end - - ## - # Check if a comment is spam. This is the central action of Defensio. - # - # @param [Hash] opts All options are recommended, but only required if noted. - # @option opts [#to_s] :user_ip The IP address of whomever is posting the - # comment. This option is required. - # @option opts [#to_s, #strftime] :article_date The date the original blog - # article was posted. If a string is given, it must be in the format - # "yyyy/mm/dd". This option is required. - # @option opts [#to_s] :comment_author The name of the author of the comment. - # This option is required. - # @option opts ["comment", "trackback", "pingback", "other"] :comment_type - # The type of the comment being posted to the blog. This option is required - # @option opts [#to_s] :comment_content The actual content of the comment - # (strongly recommended to be included where ever possible). - # @option opts [#to_s] :comment_author_email The email address of the person - # posting the comment. - # @option opts [#to_s] :comment_author_url The URL of the person posting the - # comment. - # @option opts [#to_s] :permalink The permalink of the blog post to which - # the comment is being posted. - # @option opts [#to_s] :referrer The URL of the site that brought commenter - # to this page. - # @option opts [Boolean] :user_logged_in Whether or not the user posting - # the comment is logged-into the blogging platform - # @option opts [Boolean] :trusted_user Whether or not the user is an - # administrator, moderator or editor of this blog; the client should pass - # true only if blogging platform can guarantee that the user has been - # authenticated and has a role of responsibility on this blog. - # @option opts [#to_s] :openid The OpenID URL of the currently logged in - # user. Must be used in conjunction with :user_logged_in => true. OpenID - # authentication must be taken care of by your application. - # @option opts [#to_s] :test_force For testing purposes only: Use this - # parameter to force the outcome of audit_comment. Optionally affix (with - # a comma) a desired spaminess return value (in the range 0 to 1). - # Example: "spam,0.5000" or "ham,0.0010". - # @raise [StandardError] If the call fails, a StandardError is raised with - # the error message given from Defensio. - # @return [Defender::CommentResponse] - # @see http://defensio.com/api/#audit-comment - def audit_comment(opts={}) - response = call_action("audit-comment", Defender.options_to_parameters(opts)) - return CommentResponse.new(response) - end - - ## - # This action is used to retrain false negatives. False negatives are - # comments that were originally tagged as "ham" (i.e. legitimate) but were - # in fact spam. - # - # @param [Array<#to_s, CommentResponse>] signatures List of signatures (may - # contain a single entry) of the comments to be submitted for retraining. - # Note that a signature for each comment was originally provided by the - # {#audit_comment} method. - # @raise [StandardError] If the call fails, a StandardError is raised with - # the error message given from Defensio. - # @return [Boolean] Returns true if the comments were successfully marked, - # raises StandardError otherwise. - def report_false_negatives(signatures) - report_false(:negatives, signatures) - end - - ## - # This action is used to retrain false negatives. False negatives are - # comments that were originally tagged as spam but were in fact "ham" (i.e. - # legitimate). - # - # @param [Array<#to_s, CommentResponse>] signatures List of signatures (may - # contain a single entry) of the comments to be submitted for retraining. - # Note that a signature for each comment was originally provided by the - # {#audit_comment} method. - # @raise [StandardError] If the call fails, a StandardError is raised with - # the error message given from Defensio. - # @return [Boolean] Returns true if the comments were successfully marked, - # raises StandardError otherwise. - def report_false_positives(signatures) - report_false(:positives, signatures) - end - - ## - # This action returns basic statistics regarding the performance of Defensio - # since activation. - # - # @return [Defender::Statistics] - def statistics - response = call_action("get-stats") - return Statistics.new(response) - end - - private - def report_false(type, signatures) - call_action("report-false-#{type}", - "signatures" => signatures.join(",")) - true + # @return [Boolean] Whether the API key was valid or not. + def self.check_api_key + key = Defender.api_key + return false unless key + resp = get("/#{key}.json")['defensio-result'] + if resp['status'] == 'success' + return true + else + return false end - - ## - # Returns the url for the given action. - # - # @param [#to_s] action The action to generate the URL for. - # @return [String] The URL for the action. - # @raise [APIKeyError] Raises this if no API key is given. - def url(action) - raise APIKeyError unless @api_key.length > 0 - "#{ROOT_URL}#{@service_type}/#{API_VERSION}/#{action}/#{@api_key}.yaml" - end - - ## - # Backend function for calling an action. - # - # @param [#to_s] action The action to call. - # @param [Hash] params The parameters for the action. - # @return [Hash] The raw response, only parsed from YAML. - # @raise [APIKeyError] If an invalid (or no) API key is given, this is - # raised - def call_action(action, params={}) - response = Net::HTTP.post_form(URI.parse(url(action)), - {"owner-url" => @owner_url}.merge(params)) - response.code == 401 ? - raise(APIKeyError) : - Defender.raise_if_error(YAML.load(response.body)["defensio-result"]) - end + end end