class CaptchaMixin(object): def __init__(self, *args, **kwargs): super(CaptchaMixin, self).__init__(*args, **kwargs) self.captcha_weboob = Weboob() self.captcha_weboob.load_backends(caps=[CapCaptchaSolver]) def solve_captcha(self, job, backend): def call_solver(solver_backend, job): with lock: if solved.is_set(): solver_backend.logger.info('already solved, ignoring') return ret = solver_backend.solve_catpcha_blocking(job) if ret: solver_backend.logger.info('backend solved job') backend.config['captcha_response'].set(ret.solution) solved.set() def all_solvers_finished(): if not solved.is_set(): print('Error(%s): CAPTCHA could not be solved.' % backend.name, file=self.stderr) else: print('Info(%s): CAPTCHA was successfully solved. Please retry operation.' % backend.name, file=self.stderr) lock = Lock() solved = Event() bres = self.captcha_weboob.do(call_solver, job) bres.callback_thread(None, None, all_solvers_finished)
def create_weboob(self): return Weboob(scheduler=QtScheduler(self))
def __init__(self, *args, **kwargs): super(CaptchaMixin, self).__init__(*args, **kwargs) self.captcha_weboob = Weboob() self.captcha_weboob.load_backends(caps=[CapCaptchaSolver])
class Boobill(ReplApplication): APPNAME = 'boobill' VERSION = '1.4' COPYRIGHT = 'Copyright(C) 2012-YEAR Florent Fourcot' DESCRIPTION = 'Console application allowing to get/download documents and bills.' SHORT_DESCRIPTION = "get/download documents and bills" CAPS = CapDocument COLLECTION_OBJECTS = (Subscription, ) EXTRA_FORMATTERS = {'subscriptions': SubscriptionsFormatter, } DEFAULT_FORMATTER = 'table' COMMANDS_FORMATTERS = {'subscriptions': 'subscriptions', 'ls': 'subscriptions', } def __init__(self, *args, **kwargs): super(Boobill, self).__init__(*args, **kwargs) self.captcha_weboob = Weboob() self.captcha_weboob.load_backends(caps=[CapCaptchaSolver]) def main(self, argv): self.load_config() return ReplApplication.main(self, argv) def exec_method(self, id, method): l = [] id, backend_name = self.parse_id(id) if not id: for subscrib in self.get_object_list('iter_subscription'): l.append((subscrib.id, subscrib.backend)) else: l.append((id, backend_name)) more_results = [] not_implemented = [] self.start_format() for id, backend in l: names = (backend,) if backend is not None else None try: for result in self.do(method, id, backends=names): self.format(result) except CallErrors as errors: for backend, error, backtrace in errors: if isinstance(error, MoreResultsAvailable): more_results.append(id + u'@' + backend.name) elif isinstance(error, NotImplementedError): if backend not in not_implemented: not_implemented.append(backend) else: self.bcall_error_handler(backend, error, backtrace) if len(more_results) > 0: print('Hint: There are more results available for %s (use option -n or count command)' % (', '.join(more_results)), file=self.stderr) for backend in not_implemented: print(u'Error(%s): This feature is not supported yet by this backend.' % backend.name, file=self.stderr) def do_subscriptions(self, line): """ subscriptions List all subscriptions. """ return self.do_ls(line) def do_details(self, id): """ details [ID] Get details of subscriptions. If no ID given, display all details of all backends. """ l = [] id, backend_name = self.parse_id(id) if not id: for subscrib in self.get_object_list('iter_subscription'): l.append((subscrib.id, subscrib.backend)) else: l.append((id, backend_name)) for id, backend in l: names = (backend,) if backend is not None else None # XXX: should be generated by backend? -Flo # XXX: no, but you should do it in a specific formatter -romain # TODO: do it, and use exec_method here. Code is obsolete mysum = Detail() mysum.label = u"Sum" mysum.infos = u"Generated by boobill" mysum.price = Decimal("0.") self.start_format() for detail in self.do('get_details', id, backends=names): self.format(detail) mysum.price = detail.price + mysum.price self.format(mysum) def do_balance(self, id): """ balance [ID] Get balance of subscriptions. If no ID given, display balance of all backends. """ self.exec_method(id, 'get_balance') @defaultcount(10) def do_history(self, id): """ history [ID] Get the history of subscriptions. If no ID given, display histories of all backends. """ self.exec_method(id, 'iter_bills_history') @defaultcount(10) def do_documents(self, id): """ documents [ID] Get the list of documents for subscriptions. If no ID given, display documents of all backends """ self.exec_method(id, 'iter_documents') @defaultcount(10) def do_bills(self, id): """ bills [ID] Get the list of bills documents for subscriptions. If no ID given, display bills of all backends """ self.exec_method(id, 'iter_bills') def do_download(self, line, force_pdf=False): """ download [DOC_ID | all] [FILENAME] download DOC_ID [FILENAME] download the document DOC_ID is the identifier of the document (hint: try documents command) FILENAME is where to write the file. If FILENAME is '-', the file is written to stdout. download all [SUB_ID] You can use special word "all" and download all documents of subscription identified by SUB_ID. If SUB_ID is not given, download documents of all subscriptions. """ id, dest = self.parse_command_args(line, 2, 1) id, backend_name = self.parse_id(id) if not id: print('Error: please give a document ID (hint: use documents command)', file=self.stderr) return 2 if id == 'all': return self.download_all(dest, force_pdf) names = (backend_name,) if backend_name is not None else None document, = self.do('get_document', id, backends=names) if not document: print('Error: document not found') return 1 if dest is None: dest = id + "." + (document.format if not force_pdf else 'pdf') for buf in self.do('download_document' if not force_pdf else 'download_document_pdf', document, backends=names): if buf: if dest == "-": if sys.version_info.major >= 3: self.stdout.buffer.write(buf) else: self.stdout.stream.write(buf) else: try: with open(dest, 'wb') as f: f.write(buf) except IOError as e: print('Unable to write document in "%s": %s' % (dest, e), file=self.stderr) return 1 return def do_download_pdf(self, line): """ download_pdf [id | all] download function with forced PDF conversion. """ return self.do_download(line, force_pdf=True) def download_all(self, sub_id, force_pdf): if sub_id: sub_id, backend_name = self.parse_id(sub_id) names = (backend_name,) if backend_name else None subscription, = self.do('get_subscription', sub_id, backends=names) if not self.download_subscription(subscription, force_pdf): return 1 else: for subscription in self.do('iter_subscription'): if not self.download_subscription(subscription, force_pdf): return 1 def download_subscription(self, subscription, force_pdf): for document in self.do('iter_documents', subscription, backends=(subscription.backend,)): if not self.download_doc(document, force_pdf): return False return True def download_doc(self, document, force_pdf): if force_pdf: method = 'download_document_pdf' else: method = 'download_document' dest = document.id + "." + (document.format if not force_pdf else 'pdf') for buf in self.do(method, document, backends=(document.backend,)): if buf: try: with open(dest, 'wb') as f: f.write(buf) except IOError as e: print('Unable to write bill in "%s": %s' % (dest, e), file=self.stderr) return False return True def do_profile(self, line): """ profile Display detailed information about person or company. """ self.start_format() for profile in self.do('get_profile', caps=CapProfile): self.format(profile) def bcall_error_handler(self, backend, error, backtrace): """ Handler for an exception inside the CallErrors exception. This method can be overridden to support more exceptions types. """ if isinstance(error, CaptchaQuestion): if not self.captcha_weboob.count_backends(): print('Error(%s): Site requires solving a CAPTCHA but no CapCaptchaSolver backends were configured' % backend.name, file=self.stderr) return False print('Info(%s): Encountered CAPTCHA, please wait for its resolution, it can take dozens of seconds' % backend.name, file=self.stderr) job = exception_to_job(error) self.solve_captcha(job, backend) return False return super(ReplApplication, self).bcall_error_handler(backend, error, backtrace) def solve_captcha(self, job, backend): def call_solver(solver_backend, job): with lock: if solved.is_set(): solver_backend.logger.info('already solved, ignoring') return ret = solver_backend.solve_catpcha_blocking(job) if ret: solver_backend.logger.info('backend solved job') backend.config['captcha_response'].set(ret.solution) solved.set() def all_solvers_finished(): if not solved.is_set(): print('Error(%s): CAPTCHA could not be solved.' % backend.name, file=self.stderr) else: print('Info(%s): CAPTCHA was successfully solved. Please retry operation.' % backend.name, file=self.stderr) lock = Lock() solved = Event() bres = self.captcha_weboob.do(call_solver, job) bres.callback_thread(None, None, all_solvers_finished)