#!/usr/bin/env python3 import sys import time from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtWidgets import QLayout, QHBoxLayout, QVBoxLayout, QSizePolicy from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton, QProgressBar from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtMultimedia import QSound from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply API_URL = "http://www.9kw.eu/index.cgi" API_KEY = "" soundfile = "notify.wav" class ExtendedQLabel(QLabel): clicked = pyqtSignal(int, str) image = False # QPixmap() imageR = False # QPixmap() resized def __init(self, parent): QLabel.__init(self, parent) def setImage(self, image): self.image = image self.imageR = self.image.scaled(self.size(), Qt.KeepAspectRatio) self.setPixmap(self.imageR) def removeImage(self): self.image = False self.imageR = False self.setPixmap(QPixmap()) def resizeEvent(self, evt=None): QLabel.resizeEvent(self, evt) if self.image: self.imageR = self.image.scaled(self.size(), Qt.KeepAspectRatio) self.setPixmap(self.imageR) class CaptchaGUI(QWidget): sound = False # QSound() timer = False # QTimer() for 30sec timing running = False startCredits = None currentCredits = 0 currentCaptchaID = None currentCommited = 0 currentSkipped = 0 currentQueued = 0 currentWorker = 0 apiurl = API_URL+"?source=pythonapi&apikey="+API_KEY+"&" offlinemessage = "Click \"Start\" to fetch next captcha." waitingoncaptcha = 0 def __init__(self, parent=None): super(CaptchaGUI, self).__init__(parent) self.setMinimumWidth(400) # Top: Credits and Stats self.accountLabel = QLabel("credits") self.accountLabel.setAlignment(Qt.AlignLeft) self.statsLabel = QLabel("online stats") self.statsLabel.setAlignment(Qt.AlignRight) # self.timerLabel.setStyleSheet("QLabel { color: darkred; }") self.LayoutStats = QHBoxLayout() self.LayoutStats.addWidget(self.accountLabel) # self.LayoutStats.addWidget(self.timerLabel) # self.LayoutStats.addWidget(self.timerProgress) self.LayoutStats.addWidget(self.statsLabel) self.LayoutStats.sizeConstraint = QLayout.SetMinimumSize # Middle: Captchaimage self.captchaImage = ExtendedQLabel("") self.captchaImage.setAlignment(Qt.AlignCenter) self.captchaImage.setMinimumHeight(400) self.captchaImage.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding ) self.captchaImage.clicked.connect(self.captchaClicked) self.captchaBox = QHBoxLayout() self.captchaBox.addWidget(self.captchaImage) # Bottom: Input + Submit/Skip-Buttons self.captchaInputLine = QLineEdit() self.startstopButton = QPushButton("Start") self.startstopButton.setCheckable(True) # self.saveImageButton = QPushButton("Sa&ve Image") self.submitButton = QPushButton("&Submit") self.skipButton = QPushButton("S&kip") self.startstopButton.clicked.connect(self.toggleRunning) # self.saveImageButton.clicked.connect(self.saveImage) self.submitButton.clicked.connect(self.submitCaptcha) self.skipButton.clicked.connect(self.skipCaptcha) self.LayoutSubmitLine = QHBoxLayout() self.LayoutSubmitLine.addWidget(self.startstopButton) self.LayoutSubmitLine.addWidget(self.captchaInputLine) # self.LayoutSubmitLine.addWidget(self.saveImageButton) self.LayoutSubmitButtons = QHBoxLayout() self.LayoutSubmitButtons.addWidget(self.submitButton) self.LayoutSubmitButtons.addWidget(self.skipButton) self.LayoutSubmit = QVBoxLayout() self.LayoutSubmit.addLayout(self.LayoutSubmitLine) self.LayoutSubmit.addLayout(self.LayoutSubmitButtons) # Compile layout mainLayout = QVBoxLayout() mainLayout.addLayout(self.LayoutStats) mainLayout.addLayout(self.captchaBox) mainLayout.addLayout(self.LayoutSubmit) self.setLayout(mainLayout) self.setWindowTitle("Captcha 9kw PyQt") if not API_KEY: self.captchaImage.setText("API_KEY is missing.\nPlease edit this file and enter your key at the top.") self.startstopButton.setEnabled(False) return # load soundfile self.sound = QSound(soundfile) # Initialize network self.NetworkManager = QNetworkAccessManager() QTimer.singleShot(500, self.getCredits) QTimer.singleShot(500, self.getQueue) QTimer.singleShot(500, self.getCaptchaID) self.timer = QTimer() self.timer.timeout.connect(self.onTimeout) self.timer.setSingleShot(True) self.startstopButton.setFocus() ################################################## # Handle gui ################################################## def captchaClicked(self, clickedpiece, separator): text = self.captchaInputLine.text() if len(text) > 0 and separator: text += separator text += str(clickedpiece) self.captchaInputLine.setText(text) def updateStats(self): if self.startCredits: credittext = "%d ¥ (%s%d)" % (self.currentCredits, (self.currentCredits - self.startCredits > 0) and '+' or '', (self.currentCredits - self.startCredits)) else: credittext = "%d ¥" % (self.currentCredits) self.accountLabel.setText(credittext) labeltext = "%d Commited | %d Skipped | %d Queue | %d Worker" % (self.currentCommited, self.currentSkipped, self.currentQueued, self.currentWorker) self.statsLabel.setText(labeltext) def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Escape: self.skipCaptcha() elif key == Qt.Key_Enter or key == Qt.Key_Return: if self.skipButton.hasFocus(): self.skipCaptcha() elif self.startstopButton.hasFocus(): self.toggleRunning() else: self.submitCaptcha() def setCaptchaImage(self, image=None): pixmap = QPixmap() result = pixmap.loadFromData(image) if result: self.captchaImage.setImage(pixmap) self.captchaInputLine.setFocus() self.sound.play() self.timer.start(30000) self.captchaImage.setText("") else: self.captchaImage.setText("Could not display image. Skip!") self.skipCaptcha() def removeCaptchaImage(self): self.captchaImage.removeImage() def toggleRunning(self): self.running = not self.running self.captchaImage.setText("") self.offlinemessage = "Click \"Start\" to fetch next captcha." self.startstopButton.setText(self.running and "Stop" or "Start") self.startstopButton.setChecked(self.running) def saveImage(self): if self.captchaImage.image: self.captchaImage.image.save("captcha-"+str(time.time())+".jpg") ################################################## # Handle network ################################################## def getCredits(self): url = QUrl(self.apiurl+"action=usercaptchaguthaben") self.networkCredits = QNetworkRequest(url) self.networkCreditsReply = self.NetworkManager.get(self.networkCredits) self.networkCreditsReply.finished.connect(self.getCreditsFinished) def getCreditsFinished(self): content = str(self.networkCreditsReply.readAll(), encoding='utf-8') if content.isnumeric(): self.currentCredits = int(content) if not self.startCredits: self.startCredits = self.currentCredits self.updateStats() else: self.accountLabel.setText("Error?") self.networkCreditsReply.deleteLater() QTimer.singleShot(10000, self.getCredits) def getQueue(self): url = QUrl("http://www.9kw.eu/grafik/servercheck.txt") self.networkQueue = QNetworkRequest(url) self.networkQueueReply = self.NetworkManager.get(self.networkQueue) self.networkQueueReply.finished.connect(self.getQueueFinished) def getQueueFinished(self): content = str(self.networkQueueReply.readAll(), encoding='utf-8') datatmp = content.split('|') data = dict() for stat in datatmp: tmp = stat.split('=') data[tmp[0]] = tmp[1] self.currentQueued = int(data['queue']) self.currentWorker = int(data['workertext']) self.updateStats() self.networkQueueReply.deleteLater() QTimer.singleShot(5000, self.getQueue) def getCaptchaID(self): if not self.running: self.captchaImage.setText(self.offlinemessage) QTimer.singleShot(500, self.getCaptchaID) self.startstopButton.setStyleSheet("QPushButton { background-color: green; }") return self.startstopButton.setStyleSheet("QPushButton { background-color: red; }") url = QUrl(self.apiurl+"action=usercaptchanew&text=yes&mouse=0&confirm=0") self.networkCaptchaID = QNetworkRequest(url) self.networkCaptchaIDReply = self.NetworkManager.get(self.networkCaptchaID) self.networkCaptchaIDReply.finished.connect(self.getCaptchaIDFinished) def getCaptchaIDFinished(self): content = str(self.networkCaptchaIDReply.readAll(), encoding='utf-8') self.networkCaptchaIDReply.deleteLater() if content == 'NO CAPTCHA': self.waitingoncaptcha += 1 status = "Server responded with 'NO CAPTCHA' "+str(self.waitingoncaptcha)+" times." self.captchaImage.setText(status) QTimer.singleShot(1000, self.getCaptchaID) elif content.isnumeric(): self.waitingoncaptcha = 0 print("New Captcha:", int(content)) self.currentCaptchaID = int(content) self.getCaptchaIMG() else: self.waitingoncaptcha += 1 print("Unknown error? ID not 'NO CAPTCHA' nor number") print(content) def getCaptchaIMG(self): url = QUrl(self.apiurl+"action=usercaptchashow&id="+str(self.currentCaptchaID)) self.networkCaptchaIMG = QNetworkRequest(url) self.networkCaptchaIMGReply = self.NetworkManager.get(self.networkCaptchaIMG) self.networkCaptchaIMGReply.finished.connect(self.getCaptchaIMGFinished) def getCaptchaIMGFinished(self): image = self.networkCaptchaIMGReply.readAll() self.setCaptchaImage(image) self.networkCaptchaIMGReply.deleteLater() def setCaptchaAnswer(self, answer, skip=False): if skip: url = QUrl(self.apiurl+"action=usercaptchaskip&id="+str(self.currentCaptchaID)) self.networkCaptchaAnswer = QNetworkRequest(url) self.networkCaptchaAnswerReply = self.NetworkManager.get(self.networkCaptchaAnswer) self.networkCaptchaAnswerReply.finished.connect(self.setCaptchaAnswerSkipped) else: url = QUrl(self.apiurl+"action=usercaptchacorrect&id="+str(self.currentCaptchaID)+"&antwort="+answer+"&extended=1") self.networkCaptchaAnswer = QNetworkRequest(url) self.networkCaptchaAnswerReply = self.NetworkManager.get(self.networkCaptchaAnswer) self.networkCaptchaAnswerReply.finished.connect(self.setCaptchaAnswerCommit) def setCaptchaAnswerCommit(self): message = str(self.networkCaptchaAnswerReply.readAll(), encoding='utf-8') self.networkCaptchaAnswerReply.deleteLater() if not message: # TODO: Server error? Retry! (Status 200 + no 'OK'?) self.networkCaptchaAnswerReply = self.NetworkManager.get(self.networkCaptchaAnswer) self.networkCaptchaAnswerReply.finished.connect(self.setCaptchaAnswerCommit) else: self.currentCommited += 1 self.currentCaptchaID = None if message.startswith('OK') and '|' in message: parts = message.split('|') creds = int(parts[1]) self.currentCredits += creds self.updateStats() self.getCaptchaID() def setCaptchaAnswerSkipped(self): message = str(self.networkCaptchaAnswerReply.readAll(), encoding='utf-8') self.networkCaptchaAnswerReply.deleteLater() if not message: # Server error? Retry! (Status 200 + no 'OK'?) self.networkCaptchaAnswerReply = self.NetworkManager.get(self.networkCaptchaAnswer) self.networkCaptchaAnswerReply.finished.connect(self.setCaptchaAnswerSkipped) else: self.currentSkipped += 1 self.currentCaptchaID = None self.updateStats() self.getCaptchaID() ################################################## # Handle logic ################################################## def skipCaptcha(self): self.submitCaptcha(skip=True) def submitCaptcha(self, skip=False): answer = self.captchaInputLine.text() if not answer: skip = True self.timer.stop() self.captchaInputLine.setText("") self.removeCaptchaImage() if self.currentCaptchaID: self.setCaptchaAnswer(answer, skip) def onTimeout(self): self.running = False self.offlinemessage = "30 seconds passed without input." self.startstopButton.setText(self.running and "Stop" or "Start") self.startstopButton.setChecked(self.running) self.skipCaptcha() def onQuit(self): if self.currentCaptchaID: self.running = False self.setCaptchaAnswer("", skip=True, queue=False) def main(): # NOTE(andre): urlopen() will first try ipv6, if available # hacky way to remove IPv6: http://stackoverflow.com/questions/2014534/force-python-mechanize-urllib2-to-only-use-a-requests/6319043 # better way: disable IPv6, if your isp does not support it # import pudb; pudb.set_trace() app = QApplication(sys.argv) screen = CaptchaGUI() screen.show() sys.exit(app.exec_()) if __name__ == '__main__': main()