#!/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 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) def __init(self, parent): QLabel.__init(self, parent) def mouseReleaseEvent(self, ev): if not self.pixmap(): return # get size of pixmap imgx = self.pixmap().size().width() imgy = self.pixmap().size().height() # get cursor size relative to pixmap x = ev.pos().x() - self.size().width() / 2 + imgx/2 y = ev.pos().y() - self.size().height() / 2 + imgy/2 # check if on pixmap (upper 19% = info) if x > imgx or y > imgy or x < 0 or y < 0.19*imgy: print("Not on image") return # which piece is below the mouse? xpiece = x // (imgx / 3) + 1 ypiece = (y-0.19*imgy) // (imgy*0.81 / 3) self.clicked.emit(3*ypiece+xpiece) class CaptchaGUI(QWidget): sound = False # QSound() image = False # QPixmap() imageR = False # QPixmap() resized 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 resizeEvent(self, evt=None): if self.image: self.imageR = self.image.scaled(self.captchaImage.size(), Qt.KeepAspectRatio) self.captchaImage.setPixmap(self.imageR) def captchaClicked(self, clickedpiece): if self.image: text = self.captchaInputLine.text() 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.image = pixmap self.imageR = self.image.scaled(self.captchaImage.size(), Qt.KeepAspectRatio) self.captchaImage.setPixmap(self.imageR) 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.image = False self.captchaImage.setPixmap(QPixmap()) 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.image: self.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()