summaryrefslogtreecommitdiffstats
path: root/module/plugins
diff options
context:
space:
mode:
authorGravatar zoidberg10 <zoidberg@mujmail.cz> 2012-08-02 01:37:54 +0200
committerGravatar zoidberg10 <zoidberg@mujmail.cz> 2012-08-02 01:37:54 +0200
commitb0e1d4a508746972ba92fb35a0616c2208fdd131 (patch)
treecf712ea9cf2c0099f538ea3e6771b53995917da5 /module/plugins
parentCaptchaBrotherhood plugin (diff)
downloadpyload-b0e1d4a508746972ba92fb35a0616c2208fdd131.tar.xz
DeathByCaptcha plugin
Diffstat (limited to 'module/plugins')
-rw-r--r--module/plugins/hooks/DeathByCaptcha.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/module/plugins/hooks/DeathByCaptcha.py b/module/plugins/hooks/DeathByCaptcha.py
new file mode 100644
index 000000000..ab64299dd
--- /dev/null
+++ b/module/plugins/hooks/DeathByCaptcha.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+"""
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ @author: mkaay, RaNaN, zoidberg
+"""
+
+from thread import start_new_thread
+from pycurl import FORM_FILE, HTTPHEADER, RESPONSE_CODE
+from time import sleep
+
+from module.network.RequestFactory import getURL, getRequest
+from module.network.HTTPRequest import BadHeader
+from module.plugins.Hook import Hook
+from module.common.json_layer import json_loads
+
+class DeathByCaptchaException(Exception):
+ DBC_ERRORS = {'not-logged-in': 'Access denied, check your credentials',
+ 'invalid-credentials': 'Access denied, check your credentials',
+ 'banned': 'Access denied, account is suspended',
+ 'insufficient-funds': 'Insufficient account balance to decrypt CAPTCHA',
+ 'invalid-captcha': 'CAPTCHA is not a valid image',
+ 'service-overload': 'CAPTCHA was rejected due to service overload, try again later',
+ 'invalid-request': 'Invalid request',
+ 'timed-out': 'No CAPTCHA solution received in time' }
+
+ def __init__(self, err):
+ self.err = err
+
+ def getCode(self):
+ return self.err
+
+ def getDesc(self):
+ if self.err in self.DBC_ERRORS.keys():
+ return self.DBC_ERRORS[self.err]
+ else:
+ return self.err
+
+ def __str__(self):
+ return "<DeathByCaptchaException %s>" % self.err
+
+ def __repr__(self):
+ return "<DeathByCaptchaException %s>" % self.err
+
+class DeathByCaptcha(Hook):
+ __name__ = "DeathByCaptcha"
+ __version__ = "0.02"
+ __description__ = """send captchas to DeathByCaptcha.com"""
+ __config__ = [("activated", "bool", "Activated", False),
+ ("username", "str", "Username", ""),
+ ("passkey", "password", "Password", ""),
+ ("force", "bool", "Force DBC even if client is connected", False)]
+ __author_name__ = ("RaNaN", "zoidberg")
+ __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz")
+
+ API_URL = "http://api.dbcapi.me/api/"
+
+ def setup(self):
+ self.info = {}
+
+ def call_api(self, api="captcha", post=False, multipart=False):
+ req = getRequest()
+ req.c.setopt(HTTPHEADER, ["Accept: application/json",
+ "User-Agent: pyLoad %s" % self.core.version])
+
+ if post:
+ if not isinstance(post, dict):
+ post = {}
+ post.update({"username": self.getConfig("username"),
+ "password": self.getConfig("passkey")})
+
+ response = None
+ try:
+ json = req.load("%s%s" % (self.API_URL, api),
+ post = post,
+ multipart=multipart)
+ self.logDebug(json)
+ response = json_loads(json)
+
+ if "error" in response:
+ raise DeathByCaptchaException(response['error'])
+ elif "status" not in response:
+ raise DeathByCaptchaException(str(response))
+
+ except BadHeader, e:
+ if 403 == e.code:
+ raise DeathByCaptchaException('not-logged-in')
+ elif 413 == e.code:
+ raise DeathByCaptchaException('invalid-captcha')
+ elif 503 == e.code:
+ raise DeathByCaptchaException('service-overload')
+ elif e.code in (400, 405):
+ raise DeathByCaptchaException('invalid-request')
+ else:
+ raise
+
+ finally:
+ req.close()
+
+ return response
+
+ def getCredits(self):
+ response = self.call_api("user", True)
+
+ if 'is_banned' in response and response['is_banned']:
+ raise DeathByCaptchaException('banned')
+ elif 'balance' in response and 'rate' in response:
+ self.info.update(response)
+ else:
+ raise DeathByCaptchaException(response)
+
+ def submit(self, captcha, captchaType="file", match=None):
+ response = self.call_api("captcha", {"captchafile": (FORM_FILE, captcha)}, True)
+
+ if "captcha" not in response:
+ raise DeathByCaptchaException(response)
+ ticket = response['captcha']
+
+ for i in range(24):
+ sleep(5)
+ response = self.call_api("captcha/%d" % ticket, False)
+ if response['text'] and response['is_correct']:
+ break
+ else:
+ raise DeathByCaptchaException('timed-out')
+
+ result = response['text']
+ self.logDebug("result %s : %s" % (ticket,result))
+
+ return ticket, result
+
+ def newCaptchaTask(self, task):
+ if "service" in task.data:
+ return False
+
+ if not task.isTextual():
+ return False
+
+ if not self.getConfig("username") or not self.getConfig("passkey"):
+ return False
+
+ if self.core.isClientConnected() and not self.getConfig("force"):
+ return False
+
+ try:
+ self.getCredits()
+ except DeathByCaptchaException, e:
+ self.logError(e.getDesc())
+ return False
+
+ balance, rate = self.info["balance"], self.info["rate"]
+ self.logInfo("Account balance: US$%.3f (%d captchas left at %.2f cents each)" % (balance / 100, balance // rate, rate))
+
+ if balance > rate:
+ task.handler.append(self)
+ task.data['service'] = self.__name__
+ task.setWaiting(180)
+ start_new_thread(self.processCaptcha, (task,))
+
+ def captchaInvalid(self, task):
+ if task.data['service'] == self.__name__ and "ticket" in task.data:
+ try:
+ response = self.call_api("captcha/%d/report" % task.data["ticket"], True)
+ except DeathByCaptchaException, e:
+ self.logError(e.getDesc())
+ except Exception, e:
+ self.logError(e)
+
+ def processCaptcha(self, task):
+ c = task.captchaFile
+ try:
+ ticket, result = self.submit(c)
+ except DeathByCaptchaException, e:
+ task.error = e.getCode()
+ self.logError(e.getDesc())
+ return
+
+ task.data["ticket"] = ticket
+ task.setResult(result) \ No newline at end of file