captched_to_death 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ *.rvmrc
3
+ .bundle
4
+ .DS_Store
5
+ Gemfile.lock
6
+ doc/
7
+ pkg/*
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
@@ -0,0 +1,47 @@
1
+ Hi Captcha Killers,
2
+
3
+ For security and stability reasons, we’ve decided to change our domain
4
+ name. The new domain name will be http://deathbycaptcha.eu. This
5
+ change will allow us to keep operating our service securely. Our
6
+ deathbycaptcha.com domain will close in April.
7
+
8
+ We’re also changing our API server domain. The API server domain is
9
+ the one used by your software to send CAPTCHAs to our service. There’s
10
+ a small minority of software that require you to manually input some
11
+ Host or API details. Please find below instructions on how to identify
12
+ if your software requires manual changes, and how to make changes to
13
+ the configuration if needed:
14
+
15
+ * For software that doesn’t have an API (or Host) URL settings textbox:
16
+ ** Don’t do anything! We’ve already informed most software vendors
17
+ about the API update :)
18
+
19
+ *For Software that asks for the DeathByCaptcha HTTP API:
20
+ ** host: http://api.dbcapi.me/api
21
+
22
+ * For software that uses the Decaptcher POST URL:
23
+ ** host: http://api.dbcapi.me/decaptcher
24
+
25
+ * For all other software, scripts or tools:
26
+ ** host: api.dbcapi.me
27
+
28
+ Please notice that these changes must to be in place by the first week
29
+ of April 2012 the latest.
30
+
31
+ Are you a software vendor and you haven’t heard anything from us? Send
32
+ us an email! All of our API Clients are updated with these new
33
+ settings.
34
+
35
+ We would like this information to be known only to our users and not
36
+ the rest of the internet. Please avoid sharing the new API details
37
+ publicly. We trust you guys!
38
+
39
+ On the other hand, we do encourage you to inform all of your fellow
40
+ captcha killers about our new website domain name:
41
+ http://deathbycaptcha.eu
42
+
43
+ Happy Captcha Killing,
44
+ Jose M
45
+ Death By Captcha
46
+
47
+ http://pastebin.com/ns9xKuqi
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in captched_to_death.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Cristian R. Arroyo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,85 @@
1
+ = CaptchedToDeath
2
+
3
+ {<img src="https://travis-ci.org/vivaserver/captched_to_death.png" />}[https://travis-ci.org/vivaserver/captched_to_death]
4
+ {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/vivaserver/captched_to_death]
5
+
6
+ CaptchedToDeath is a simple HTTP client for the DeathByCaptcha API written in Ruby. Note that while the DeathByCaptcha service supports both a HTTP API and a Sockets API, this library supports only the former.
7
+
8
+ By default, all successful CaptchedToDeath responses are JSON-formatted. This cannot be changed yet.
9
+
10
+ == Usage
11
+
12
+ Some actions require a valid DeathByCaptcha user account credentials, name and password. Some requests to the API will be rejected if they're not provided. Note that also a CaptchedToDeath::NoCreditError exception will be raised if the account has no more credits left on it's balance.
13
+
14
+ === CaptchedToDeath::Client
15
+
16
+ Initialization of the client can be done in many ways. Beginning with the simplest:
17
+
18
+ client = CaptchedToDeath::Client.new
19
+
20
+ Pass the user credentials if you want to decode some CAPTCHA challenges with the remaining credits on your balance.
21
+
22
+ client = CaptchedToDeath::Client.new('username','password')
23
+
24
+ Pass a block if you want to set the verbose option, that enables RestClient responses logging (only to STDOUT).
25
+
26
+ client = CaptchedToDeath::Client.new do |c|
27
+ c.username = 'username'
28
+ c.password = 'password'
29
+ c.verbose = true
30
+ end
31
+
32
+ === Balance checking
33
+
34
+ With your client initialized using you account credentials, you can check your current balance like so:
35
+
36
+ client.balance
37
+ => {"is_banned"=>false, "status"=>0, "rate"=>0.139, "balance"=>672.204, "user"=>99999}
38
+
39
+ Note that the "balance" in the response means your cents left.
40
+
41
+ === CAPTCHA decoding
42
+
43
+ With your client initialized using you account credentials, you can decode a CAPTCHA challenge if you still have credits left on your account. To do do so, just pass the CAPTCHA image URL:
44
+
45
+ client.decode('http://i.imgur.com/iWlb4.png')
46
+ => {"status"=>0, "captcha"=>36923242, "is_correct"=>true, "text"=>"jd472tfo"}
47
+
48
+ Note that only GIF, JPEG and PNG are supported as valid CAPTCHA challenges. Also, the response time depends on the current DeathByCaptcha server load.
49
+
50
+ === CAPTCHA status checking
51
+
52
+ Once the challenge has already been accepted, it's status can be checked at any time using it's CAPTCHA ID.
53
+
54
+ client.captcha(36723349)
55
+ => {"status"=>0, "captcha"=>36723349, "is_correct"=>true, "text"=>"jd472tfo"}
56
+
57
+ === CAPTCHA reporting
58
+
59
+ If you think the result of the challenge decoding is not correct you can report it to get a refund. But mind that you have to report it within the hour of submitting it and that abusing this feature might get you banned.
60
+
61
+ client.report(36723349)
62
+ => {"status"=>0, "captcha"=>36723349, "is_correct"=>false, "text"=>"jd472tfo"}
63
+
64
+ === DeathByCaptcha server status checking
65
+
66
+ You can check the current status of the DeathByCaptcha server to find out the average decoding time at any particular moment. This time will directly affect the response time of any decoding you submit afterwards.
67
+
68
+ CaptchedToDeath::Server.status
69
+ => {"status"=>0, "todays_accuracy"=>0.890402, "solved_in"=>13, "is_service_overloaded"=>false}
70
+
71
+ == Contributing
72
+
73
+ 1. Fork it
74
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
75
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
76
+ 4. Push to the branch (`git push origin my-new-feature`)
77
+ 5. Create new Pull Request
78
+
79
+ == License
80
+
81
+ CaptchedToDeath is released under the {MIT License}[http://www.opensource.org/licenses/MIT].
82
+
83
+ == Copyright
84
+
85
+ Copyright (c)2012 {Cristian R. Arroyo}[mailto:cristian.arroyo@vivaserver.com]
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ # ref: http://rpheath.com/posts/394-get-the-most-out-of-test-unit
5
+ # ref: http://stackoverflow.com/a/8395163
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'lib' << 'spec'
8
+ t.verbose = false
9
+ t.pattern = 'spec/*_spec.rb'
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "captched_to_death/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "captched_to_death"
7
+ s.version = CaptchedToDeath::VERSION
8
+ s.authors = ["Cristian R. Arroyo"]
9
+ s.email = ["cristian.arroyo@vivaserver.com"]
10
+ s.homepage = "https://github.com/vivaserver/captched_to_death"
11
+ s.summary = %q{A simple HTTP client to DeathByCaptcha API}
12
+ s.description = %q{A simple HTTP client to DeathByCaptcha API using just RestClient}
13
+
14
+ s.add_runtime_dependency 'rest-client', "~> 1.6"
15
+ s.add_development_dependency 'minitest'
16
+ s.add_development_dependency 'rake'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ # specify any dependencies here; for example:
24
+ # s.add_development_dependency "rspec"
25
+ # s.add_runtime_dependency "rest-client"
26
+ end
@@ -0,0 +1,132 @@
1
+ require 'base64'
2
+ require 'logger'
3
+
4
+ module CaptchedToDeath
5
+ class Client
6
+ attr_writer :username, :password, :accept, :verbose
7
+
8
+ # Sensible defaults that can be overriden by configuration block:
9
+ #
10
+ # client = CaptchedToDeath::Client.new do |c|
11
+ # c.username = 'username'
12
+ # c.password = 'password'
13
+ # c.verbose = true
14
+ # end
15
+ #
16
+ # or just:
17
+ #
18
+ # client = CaptchedToDeath::Client.new('username','password')
19
+ #
20
+ def initialize(*credentials)
21
+ @accept = :json
22
+ @verbose = false
23
+ if credentials.size == 2
24
+ @username = credentials[0].to_s
25
+ @password = credentials[1].to_s
26
+ end
27
+ yield self if block_given?
28
+
29
+ RestClient.log = Logger.new(STDOUT) if @verbose
30
+ end
31
+
32
+ # User credit balance, includes account details.
33
+ #
34
+ def balance
35
+ fail RejectedError if empty_credentials?
36
+ response = RestClient.post "#{API_URI}/user", {:username => @username, :password => @password}, :accept => @accept
37
+ fail ServiceError unless response.code == 200
38
+ JSON.parse(response)
39
+ end
40
+
41
+ # Polls for uploaded CAPTCHA status.
42
+ # You don't have to supply your Death by Captcha credentials this time.
43
+ # Please don't poll for a CAPTCHA status more than once in a couple of seconds.
44
+ # This is considered abusive and might get you banned.
45
+ #
46
+ def captcha(captcha_id)
47
+ response = RestClient.get "#{API_URI}/captcha/#{captcha_id}", {:accept => @accept}
48
+ JSON.parse(response)
49
+ rescue RestClient::Exception => e
50
+ case e.http_code
51
+ when 404
52
+ fail NotFound
53
+ #503 (Service Temporarily Unavailable) when our service is overloaded (usually around 3:00–6:00 PM EST)
54
+ when 503
55
+ # not sure 503 is ever sent, but retry if it is
56
+ sleep Server.status['solved_in']
57
+ retry
58
+ else
59
+ raise e
60
+ end
61
+ end
62
+
63
+ # Solving a CAPTCHA using Death by Captcha HTTP API requires performing at least two steps.
64
+ #
65
+ def decode(challenge_url, referer=nil, agent=nil)
66
+ fail RejectedError if empty_credentials?
67
+
68
+ response = RestClient.post "#{API_URI}/captcha", {
69
+ :username => @username,
70
+ :password => @password,
71
+ :captchafile => captcha_file(challenge_url,referer,agent)
72
+ }, :accept => @accept
73
+ resolved = JSON.parse(response)
74
+ begin
75
+ sleep Server.status['solved_in']
76
+ resolved = captcha(resolved['captcha'])
77
+ end while resolved['text'].empty?
78
+ resolved
79
+ rescue RestClient::Exception => e
80
+ case e.http_code
81
+ #303 (See Other) CAPTCHA successfully uploaded: Location HTTP header will point to the status page
82
+ when 303
83
+ # RestClient: for result code 303 the redirection will be followed and the request transformed into a get
84
+ # (...so it'll be returned as a 200)
85
+ #403 (Forbidden) credentials were rejected, or you don't have enough credits
86
+ when 403
87
+ # TODO: discrimate wrong credentials
88
+ fail NoCreditError
89
+ #400 (Bad Request) if your request was not following the specification or not a valid image
90
+ when 400
91
+ fail RejectedError
92
+ #500 (Internal Server Error)
93
+ #503 (Service Temporarily Unavailable) when our service is overloaded (usually around 3:00–6:00 PM EST)
94
+ when 500, 503
95
+ fail ServiceError, e.http_body
96
+ else
97
+ raise e
98
+ end
99
+ end
100
+
101
+ # Reports incorrectly solved CAPTCHAs.
102
+ # If you think your CAPTCHA was solved incorrectly, report it to Death by Captcha to get your money back.
103
+ # You'll get refunded if the CAPTCHA was uploaded less than an hour ago.
104
+ #
105
+ def report(captcha_id)
106
+ fail RejectedError if empty_credentials?
107
+
108
+ response = RestClient.post "#{API_URI}/captcha/#{captcha_id}/report", {
109
+ :username => @username,
110
+ :password => @password,
111
+ }, :accept => @accept
112
+
113
+ JSON.parse(response)
114
+ end
115
+
116
+ private
117
+
118
+ def captcha_file(challenge_url, referer, agent) #:nodoc:
119
+ file = RestClient.get challenge_url, {'Referer' => referer, 'User-Agent' => agent}
120
+ if file =~ TYPE_EXIF || file =~ TYPE_JFIF || file =~ TYPE_GIF || file =~ TYPE_PNG
121
+ 'base64:'+Base64.encode64(file)
122
+ else
123
+ raise RejectedError
124
+ end
125
+ end
126
+
127
+ def empty_credentials? #:nodoc:
128
+ return true if @username.to_s.empty? || @password.to_s.empty?
129
+ false
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,10 @@
1
+ module CaptchedToDeath
2
+ class Server
3
+ # Current server status
4
+ def self.status
5
+ response = RestClient.get "#{API_URI}/status", :accept => :json
6
+ fail ServiceError unless response.code == 200
7
+ JSON.parse(response)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module CaptchedToDeath
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'rest_client'
5
+
6
+ module CaptchedToDeath
7
+ # The base URI for the new DeathByCaptcha API. See DeathByCaptcha.txt
8
+ API_URI = 'http://api.dbcapi.me/api'
9
+
10
+ # Exception for insufficient user credits
11
+ class NoCreditError < StandardError; end
12
+
13
+ # Exception for unexisting CAPTCHA on status retreiving
14
+ class NotFound < StandardError; end
15
+
16
+ # Exception for missing/wrong user credentials; invalid captcha challenge
17
+ class RejectedError < StandardError; end
18
+
19
+ # Exception for server overloaded outage
20
+ class ServiceError < StandardError; end
21
+
22
+ # RegExps stolen from http://github.com/dim/ruby-imagespec
23
+ # see related blog post at http://boonedocks.net/mike/archives/162-Determining-Image-File-Types-in-Ruby.html
24
+ #
25
+ TYPE_EXIF = /^\xff\xd8\xff\xe1(.*){2}Exif/
26
+ TYPE_JFIF = /^\xff\xd8\xff\xe0\x00\x10JFIF/
27
+ TYPE_GIF = /^GIF8/
28
+ TYPE_PNG = /^\x89PNG/
29
+ end
30
+
31
+ require "captched_to_death/client"
32
+ require "captched_to_death/server"
33
+ require "captched_to_death/version"
@@ -0,0 +1,79 @@
1
+ require_relative 'spec_helper'
2
+ require 'minitest/mock'
3
+
4
+ describe CaptchedToDeath::Client do
5
+ before do
6
+ @username = ENV['DECAPTCHER_USERNAME']
7
+ @password = ENV['DECAPTCHER_PASSWORD']
8
+
9
+ # some CAPTCHA challenges for testing purposes:
10
+ # http://i.imgur.com/vOj5f.jpg (jd472tFO)
11
+ # http://i.imgur.com/iWlb4.png
12
+ # http://i.imgur.com/LsbEV.gif
13
+ # http://i.imgur.com/9a1da.jpg (jFnq60dd)
14
+ # http://i.imgur.com/nRITM.jpg (ByKBPX9Z)
15
+ # http://i.imgur.com/mr3E0.jpg (6gOFiI01)
16
+ # http://i.imgur.com/uY3RN.jpg (gTKDrGtF)
17
+ @challenge = 'http://i.imgur.com/vOj5f.jpg'
18
+ end
19
+
20
+ it 'is the correct way of using mocks' do
21
+ client = MiniTest::Mock.new
22
+ response = {"captcha" => 123456789, "is_correct" => true, "text" => "jd472tFO"}
23
+
24
+ client.expect :captcha, response, [123456789]
25
+ client.captcha(123456789).must_equal response
26
+ assert client.verify
27
+
28
+ client.expect :decode, response, [@challenge]
29
+ client.decode(@challenge).must_equal response
30
+ assert client.verify
31
+ end
32
+
33
+ describe 'when checking balance' do
34
+ subject do
35
+ CaptchedToDeath::Client.new(@username,@password)
36
+ end
37
+
38
+ it 'responds with account details or does not work if missing API credentials' do
39
+ if @username && @password
40
+ balance = subject.balance
41
+ balance.must_be_instance_of Hash
42
+ balance.keys.must_equal ["is_banned", "status", "rate", "balance", "user"] # NOTE: "balance" float means Cents
43
+ refute balance["is_banned"]
44
+ else
45
+ proc { subject.balance }.must_raise CaptchedToDeath::RejectedError
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'when polling for uploaded CAPTCHA status' do
51
+ subject do
52
+ CaptchedToDeath::Client.new
53
+ end
54
+
55
+ it 'raises custom exception on unexisting CAPTCHA id' do
56
+ proc { subject.captcha(:captcha_id) }.must_raise CaptchedToDeath::NotFound
57
+ end
58
+ end
59
+
60
+ describe 'when decoding challenges' do
61
+ subject do
62
+ CaptchedToDeath::Client.new
63
+ end
64
+
65
+ it 'does not work if missing API credentials' do
66
+ proc { subject.decode(@challenge) }.must_raise CaptchedToDeath::RejectedError
67
+ end
68
+ end
69
+
70
+ describe 'when reporting wrongly resolved captchas' do
71
+ subject do
72
+ CaptchedToDeath::Client.new
73
+ end
74
+
75
+ it 'does not work if missing API credentials' do
76
+ proc { subject.report(:captcha_id) }.must_raise CaptchedToDeath::RejectedError
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe CaptchedToDeath::Server do
4
+ it 'checks for remote server status' do
5
+ status = CaptchedToDeath::Server.status
6
+ status.must_be_instance_of Hash
7
+ status.keys.must_equal ["status", "todays_accuracy", "solved_in", "is_service_overloaded"]
8
+ # NOTE: status["solved_in"] integer means Seconds
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ require_relative '../lib/captched_to_death'
2
+ require 'minitest/autorun'
3
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: captched_to_death
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Cristian R. Arroyo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: &72562580 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.6'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *72562580
25
+ - !ruby/object:Gem::Dependency
26
+ name: minitest
27
+ requirement: &72562370 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *72562370
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &72562140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *72562140
47
+ description: A simple HTTP client to DeathByCaptcha API using just RestClient
48
+ email:
49
+ - cristian.arroyo@vivaserver.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .travis.yml
56
+ - DeathByCaptcha.txt
57
+ - Gemfile
58
+ - LICENSE.txt
59
+ - README.rdoc
60
+ - Rakefile
61
+ - captched_to_death.gemspec
62
+ - lib/captched_to_death.rb
63
+ - lib/captched_to_death/client.rb
64
+ - lib/captched_to_death/server.rb
65
+ - lib/captched_to_death/version.rb
66
+ - spec/client_spec.rb
67
+ - spec/server_spec.rb
68
+ - spec/spec_helper.rb
69
+ homepage: https://github.com/vivaserver/captched_to_death
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.10
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: A simple HTTP client to DeathByCaptcha API
93
+ test_files: []