From bb3d50da21f305289e4f387445c30a41e7b98a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Gl=C3=BCpker?= Date: Fri, 18 Mar 2016 16:33:14 +0100 Subject: Initial commit of my 9kw client with python3 and PyQt5 --- 9kwpyqt.py | 330 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100755 9kwpyqt.py diff --git a/9kwpyqt.py b/9kwpyqt.py new file mode 100755 index 0000000..9acf9eb --- /dev/null +++ b/9kwpyqt.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python3 + +import random +import sys +import time +from PyQt5.QtCore import Qt, QTimer, QUrl +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 CaptchaGUI(QWidget): + sound = False # QSound() + image = False # QImage() + imageR = False # QImage() resized + timer = False # QTimer() for 30sec timing + + running = False + startCredits = None + currentCredits = 0 + currentCaptchaID = None + currentCommited = 0 + currentSkipped = 0 + currentQueued = 0 + apiurl = API_URL+"?source=pythonapi&apikey="+API_KEY+"&" + offlinemessage = "Click \"Start\" to fetch next captcha." + + 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.timerLabel = QLabel("30 Sek") + # self.timerLabel.setAlignment(Qt.AlignCenter) + # self.timerProgress = QProgressBar() + # self.timerProgress.setRange(0,30) + # self.timerProgress.setValue(23.65) + # self.timerProgress.setTextVisible(True) + # self.timerProgress.text = "Bla und so" + + self.statsLabel = QLabel("online stats") + self.statsLabel.setAlignment(Qt.AlignRight) + + # self.timerLabel.setStyleSheet("QLabel { color: darkred; }") + # self.timerLabel.setStyleSheet("QLabel { background-color: gray; }") + + 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.captchaLabel = QLabel("Captcha:") + self.captchaImage = QLabel("") + self.captchaImage.setAlignment(Qt.AlignCenter) + self.captchaImage.setMinimumHeight(400) + self.captchaImage.setSizePolicy( + QSizePolicy.MinimumExpanding, + QSizePolicy.MinimumExpanding + ) + + self.captchaBox = QHBoxLayout() + # self.captchaBox.addWidget(self.captchaLabel) + self.captchaBox.addWidget(self.captchaImage) + + # Bottom: Input + Submit/Skip-Buttons + self.captchaInputLine = QLineEdit() + self.startstopButton = QPushButton("Start") + self.startstopButton.setCheckable(True) + self.submitButton = QPushButton("&Submit") + self.skipButton = QPushButton("S&kip") + self.startstopButton.clicked.connect(self.toggleRunning) + 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.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") + + # 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) + + ################################################## + # 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 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 in Queue" % (self.currentCommited, self.currentSkipped, self.currentQueued) + 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() + 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.offlinemessage = "Click \"Start\" to fetch next captcha." + self.startstopButton.setText(self.running and "Stop" or "Start") + self.startstopButton.setChecked(self.running) + + ################################################## + # 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.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) + return + self.captchaImage.setText("") + 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': + status = self.captchaImage.text()+"." + self.captchaImage.setText(status) + QTimer.singleShot(1000, self.getCaptchaID) + elif not content.isnumeric(): + self.captchaImage.setText("Unknown error? ID not 'NO CAPTCHA' nor number") + else: + self.currentCaptchaID = int(content) + self.getCaptchaIMG() + + 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.setCaptchaAnswer(answer, skip) + self.captchaInputLine.setText("") + self.removeCaptchaImage() + + 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() -- cgit v1.2.3