From 939d83907998bc635f5e4aa8f44eb827f0e83fa5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Gl=C3=BCpker?= <git@wgmd.de>
Date: Wed, 23 Mar 2016 09:15:40 +0100
Subject: Implement captchaclicking for another captcha type

Try to detect, which image is provided,
by checking some pixels for values.

The first captcha has a big, black infopanel and
an example image with a white border.
The second captcha has a small info panel and
we're testing for a nearly black pixel (jpeg artefacts...)
for the corresponding imagenumbers
---
 9kwpyqt.py | 130 ++++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 95 insertions(+), 35 deletions(-)

diff --git a/9kwpyqt.py b/9kwpyqt.py
index d676013..29e9d28 100755
--- a/9kwpyqt.py
+++ b/9kwpyqt.py
@@ -6,7 +6,7 @@ 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.QtGui import QPixmap, QImage
 from PyQt5.QtMultimedia import QSound
 from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
 
@@ -14,40 +14,109 @@ API_URL = "http://www.9kw.eu/index.cgi"
 API_KEY = ""
 soundfile = "notify.wav"
 
-class ExtendedQLabel(QLabel):
+class PixelInfo():
+    pixelA = 0
+    pixelR = 0
+    pixelG = 0
+    pixelB = 0
+
+    def __init__(self, argb):
+        self.pixelB = argb & 0xff
+        argb = argb >> 8
+        self.pixelG = argb & 0xff
+        argb = argb >> 8
+        self.pixelR = argb & 0xff
+        argb = argb >> 8
+        self.pixelA = argb & 0xff
+
+    def isLower(self, r, g, b):
+        return (self.pixelR < r and self.pixelG < g and self.pixelB < b)
+
+    def isHigher(self, r, g, b):
+        return (self.pixelR > r and self.pixelG > g and self.pixelB > b)
 
-    clicked = pyqtSignal(int)
+    def isExactly(self, r, g, b):
+        return (self.pixelR == r and self.pixelG == g and self.pixelB == b)
+
+
+
+class ExtendedQLabel(QLabel):
+    clicked = pyqtSignal(int, str)
+    image = False # QPixmap()
+    imageR = False # QPixmap() resized
+    imageB = False # QImage()
 
     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.imageB = False
+        self.setPixmap(self.imageR)
+
+    def removeImage(self, image):
+        self.image = False
+        self.imageR = False
+        self.imageB = False
+        QLabel.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)
+
     def mouseReleaseEvent(self, ev):
-        if not self.pixmap():
+        if not self.image:
             return
+        if not self.imageB:
+            self.imageB = self.image.toImage()
 
         # get size of pixmap
         imgx = self.pixmap().size().width()
         imgy = self.pixmap().size().height()
 
-        # get cursor size relative to pixmap
+        # get cursor position relative to pixmap ((0,0) = topleft)
         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)
+        # Which type of captcha is this image?
+        if PixelInfo(self.imageB.pixel(90, 45)).isExactly(0,0,0) and \
+                PixelInfo(self.imageB.pixel( 5, 30)).isHigher(220,220,220) and \
+                PixelInfo(self.imageB.pixel(71, 30)).isHigher(220,220,220) and \
+                PixelInfo(self.imageB.pixel(40,  5)).isHigher(220,220,220) and \
+                PixelInfo(self.imageB.pixel(40, 59)).isHigher(220,220,220):
+            # check if on pixmap (upper 17.5% = infopanel)
+            if x > imgx or y > imgy or x < 0 or y < 0.175*imgy:
+                return
+            xpiece = x // (imgx / 3) + 1
+            ypiece = (y-0.175*imgy) // (imgy*0.825 / 3)
+            self.clicked.emit(3*ypiece+xpiece, ",")
+
+        elif PixelInfo(self.imageB.pixel( 16,  47)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(144,  47)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(269,  47)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel( 16, 172)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(144, 172)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(269, 172)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel( 16, 298)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(144, 298)).isLower(20,20,20) and \
+                PixelInfo(self.imageB.pixel(269, 298)).isLower(20,20,20):
+            # check if on pixmap (upper 7% = infopanel)
+            if x > imgx or y > imgy or x < 0 or y < 0.0735*imgy:
+                return
+            xpiece = x // (imgx / 3) + 1
+            ypiece = (y-0.0735*imgy) // (imgy*0.9265 / 3)
+            self.clicked.emit(3*(2-ypiece)+xpiece, "")
+
 
-        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
@@ -153,17 +222,12 @@ class CaptchaGUI(QWidget):
     ##################################################
     # 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 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:
@@ -190,10 +254,7 @@ class CaptchaGUI(QWidget):
         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.captchaImage.setImage(pixmap)
             self.captchaInputLine.setFocus()
             self.sound.play()
             self.timer.start(30000)
@@ -203,8 +264,7 @@ class CaptchaGUI(QWidget):
             self.skipCaptcha()
 
     def removeCaptchaImage(self):
-        self.image = False
-        self.captchaImage.setPixmap(QPixmap())
+        self.captchaImage.removeImage()
 
     def toggleRunning(self):
         self.running = not self.running
@@ -213,9 +273,9 @@ class CaptchaGUI(QWidget):
         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")
+    # def saveImage(self):
+    #     if self.image:
+    #         self.image.save("captcha-"+str(time.time())+".jpg")
 
     ##################################################
     # Handle network
-- 
cgit v1.2.3