death_by_captcha 0.4.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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in death_by_captcha.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "death_by_captcha/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "death_by_captcha"
7
+ s.version = DeathByCaptcha::VERSION
8
+ s.authors = ["Julien Dumas"]
9
+ s.email = ["julien.dumas@emediad.fr"]
10
+ s.homepage = ""
11
+ s.summary = %q{Death by Captcha Ruby API}
12
+ s.description = %q{Death by Captcha Ruby implementation (HTTP API)}
13
+
14
+ s.rubyforge_project = "death_by_captcha"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ s.add_dependency "nokogiri"
25
+ end
@@ -0,0 +1,124 @@
1
+ require "net/http"
2
+ require "base64"
3
+ require "json"
4
+ require "nokogiri"
5
+ require "death_by_captcha/version"
6
+ require "death_by_captcha/recaptcha"
7
+
8
+ module DeathByCaptcha
9
+ Server = "api.dbcapi.me"
10
+
11
+ @@username = nil
12
+ @@password = nil
13
+ @@debug = false
14
+ @@decode_max_tries = 10
15
+ @@decode_sleep_interval = 8
16
+
17
+ def decode( data )
18
+ req = Net::HTTP::Post.new( "/api/captcha" )
19
+ req.set_form_data( :username => @@username, :password => @@password, :captchafile => "base64:" + Base64.encode64( data ) )
20
+ result = send_request( req )
21
+
22
+ case result.code
23
+ when "303"
24
+ uri = URI.parse( result["Location"] )
25
+ return uri.path.split("/").last
26
+ when "503"
27
+ raise ServiceOverloadedError
28
+ else
29
+ raise RequestError.new( result.code )
30
+ end
31
+ end
32
+
33
+ def decode!( data )
34
+ captcha_status = nil
35
+ captcha_id = decode( data )
36
+
37
+ try = 0
38
+ begin
39
+ raise CaptchaDecodeFailed if try > @@decode_max_tries
40
+ sleep @@decode_sleep_interval
41
+ try += 1
42
+
43
+ begin
44
+ captcha_status = DeathByCaptcha.check( captcha_id )
45
+ rescue RequestError, CaptchaNotFound
46
+ captcha_status = { "text" => "" }
47
+ end
48
+ end while captcha_status["text"].strip == "" && captcha_status["is_correct"] == true
49
+
50
+ return captcha_status
51
+ end
52
+
53
+ def check( id )
54
+ req = Net::HTTP::Get.new( "/api/captcha/#{id}" )
55
+ req["Accept"] = "application/json"
56
+ result = self.send_request( req )
57
+
58
+ case result.code
59
+ when "200"
60
+ return JSON.parse( result.body )
61
+ when "404"
62
+ raise CaptchaNotFound
63
+ else
64
+ raise RequestError.new( result.code )
65
+ end
66
+ end
67
+
68
+ def report( id )
69
+ req = Net::HTTP::Post.new( "/api/captcha/#{id}/report" )
70
+ req.set_form_data( :username => @@username, :password => @@password )
71
+ result = send_request( req )
72
+ return true
73
+ end
74
+
75
+ def account_balance
76
+ req = Net::HTTP::Post.new( "/api/user" )
77
+ req["Accept"] = "application/json"
78
+ req.set_form_data( :username => @@username, :password => @@password )
79
+ result = send_request( req )
80
+ return JSON.parse( result.body )
81
+ end
82
+
83
+ def username=( str )
84
+ @@username = str
85
+ end
86
+
87
+ def password=( str )
88
+ @@password = str
89
+ end
90
+
91
+ def debug=( v )
92
+ @@debug = v
93
+ end
94
+
95
+ def decode_max_tries=( n )
96
+ @@decode_max_tries = n
97
+ end
98
+
99
+ def decode_sleep_interval=( n )
100
+ @@decode_sleep_interval = n
101
+ end
102
+
103
+ def send_request( req )
104
+ http = Net::HTTP.new( Server, 80 )
105
+ http.set_debug_output( $stdout ) if @@debug == true
106
+ return http.request( req )
107
+ end
108
+
109
+ # set module functions
110
+ module_function :username=, :password=, :debug=, :decode_max_tries=, :decode_sleep_interval=, :decode, :decode!, :report, :check, :account_balance, :send_request
111
+
112
+ # exceptions
113
+ class RequestError < Exception
114
+ end
115
+
116
+ class CaptchaNotFound < Exception
117
+ end
118
+
119
+ class CaptchaDecodeFailed < Exception
120
+ end
121
+
122
+ class ServiceOverloadedError < RequestError
123
+ end
124
+ end
@@ -0,0 +1,96 @@
1
+ module DeathByCaptcha
2
+
3
+ class Recaptcha
4
+
5
+ Response = Struct.new( :code, :challenge )
6
+
7
+ def initialize( site_id )
8
+ @site_id = site_id
9
+ end
10
+
11
+ def resolve
12
+ response = nil
13
+ begin
14
+ request = Net::HTTP::Get.new( "/recaptcha/api/challenge?k=#{@site_id}" )
15
+ result = send_request( request )
16
+ matches = result.body.match( /challenge : '([^\']+)',/ )
17
+
18
+ # get recaptcha_challenge_field value
19
+ raise ChallengeFieldMissing if matches.nil?
20
+ recaptcha_challenge_field = matches[1]
21
+
22
+ # get image
23
+ data = Net::HTTP.get( URI.parse( "http://www.google.com/recaptcha/api/image?c=#{recaptcha_challenge_field}" ) )
24
+ captcha_status = DeathByCaptcha.decode!( data )
25
+ code = captcha_status["text"]
26
+ rescue CaptchaInvalid
27
+ DeathByCaptcha.report( captcha_status["captcha"] )
28
+ raise CaptchaInvalid
29
+ end
30
+
31
+ return Response.new( captcha_status["text"], recaptcha_challenge_field )
32
+ end
33
+
34
+ def resolve_with_certification
35
+ begin
36
+ request = Net::HTTP::Get.new( "/recaptcha/api/noscript?k=#{@site_id}" )
37
+ result = send_request( request )
38
+ doc = Nokogiri::HTML.parse( result.body )
39
+
40
+ # get recaptcha_challenge_field value
41
+ recaptcha_challenge_field_input = doc.xpath( "//input[@name='recaptcha_challenge_field']" ).first
42
+ raise ChallengeFieldMissing if recaptcha_challenge_field_input.nil? || recaptcha_challenge_field_input.attributes["value"].nil?
43
+
44
+ # get image
45
+ image_node = doc.xpath( "//center/img" ).first
46
+ raise ImageMissing if image_node.nil? || image_node.attributes["src"].nil?
47
+ data = Net::HTTP.get( URI.parse( "http://www.google.com/recaptcha/api/#{image_node.attributes["src"].value}" ) )
48
+ captcha_status = nil
49
+ code = nil
50
+ begin
51
+ captcha_status = DeathByCaptcha.decode!( data )
52
+ code = captcha_status["text"]
53
+ rescue DeathByCaptcha::RequestError => e
54
+ raise ResolveFailed, e.class.to_s
55
+ rescue DeathByCaptcha::CaptchaDecodeFailed => e
56
+ raise ResolveFailed, e.class.to_s
57
+ end
58
+
59
+ raise ResolveFailed, "no captcha" if code.nil?
60
+
61
+ recaptcha_challenge_field = recaptcha_challenge_field_input.attributes["value"].value
62
+ params = { recaptcha_challenge_field: recaptcha_challenge_field, recaptcha_response_field: code, submit: "Je ne suis pas un robot" }
63
+
64
+ request = Net::HTTP::Post.new( "/recaptcha/api/noscript?k=#{@site_id}" )
65
+ request.set_form_data( params )
66
+ result = send_request( request )
67
+ doc = Nokogiri::HTML.parse( result.body )
68
+ textarea_node = doc.xpath( "//textarea" ).first
69
+
70
+ raise CaptchaInvalid if textarea_node.nil?
71
+ rescue CaptchaInvalid
72
+ DeathByCaptcha.report( captcha_status["captcha"] )
73
+ raise CaptchaInvalid
74
+ end
75
+
76
+ return Response.new( captcha_status["text"], textarea_node.inner_text )
77
+ end
78
+
79
+ private
80
+ def send_request( request )
81
+ request["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"
82
+ request["Accept-Charset"] = "ISO-8859-1,utf-8;q=0.7,*;q=0.3"
83
+ request["Accept-Language"] = "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4"
84
+ http = Net::HTTP.new( "www.google.com" )
85
+ return http.request( request )
86
+ end
87
+
88
+ # exceptions
89
+ class ResolveFailed < Exception; end
90
+ class CaptchaInvalid < Exception; end
91
+ class ImageMissing < Exception; end
92
+ class ChallengeFieldMissing < Exception; end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,3 @@
1
+ module DeathByCaptcha
2
+ VERSION = "0.4.0.0"
3
+ end
data/test.rb ADDED
@@ -0,0 +1,9 @@
1
+ $: << "./lib"
2
+
3
+ require "death_by_captcha"
4
+
5
+ DeathByCaptcha.debug = true
6
+ DeathByCaptcha.username = "USERNAME"
7
+ DeathByCaptcha.password = "PASSWORD"
8
+
9
+ p DeathByCaptcha.decode!( File.read( "IMAGE_PATH" ) )
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: death_by_captcha
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Dumas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Death by Captcha Ruby implementation (HTTP API)
31
+ email:
32
+ - julien.dumas@emediad.fr
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - Rakefile
40
+ - death_by_captcha.gemspec
41
+ - lib/death_by_captcha.rb
42
+ - lib/death_by_captcha/recaptcha.rb
43
+ - lib/death_by_captcha/version.rb
44
+ - test.rb
45
+ homepage: ''
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project: death_by_captcha
65
+ rubygems_version: 1.8.21
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Death by Captcha Ruby API
69
+ test_files: []