furmeet-bot/webproj/captcher/generator.py

157 lines
4.7 KiB
Python

# -*- encoding: utf-8 -*-
from io import BytesIO
Path = None
random = None
subprocess = None
ImageCaptcha = None
ImageFilter = None
ImageOps = None
Image = None
def ensure_imported_Path():
global Path
from pathlib import Path
def ensure_imported_ImageFilter():
global ImageFilter
from PIL import ImageFilter
def ensure_imported_ImageOps():
global ImageOps
from PIL import ImageOps
def ensure_imported_ImageCaptcha():
global ImageCaptcha
from captcha.image import ImageCaptcha
def ensure_imported_Image():
global Image
from PIL.Image import Image
def ensure_imported_random():
global random
import random
def ensure_imported_subprocess():
global subprocess
import subprocess
class AbstractImageCaptchaGenerator:
'Abstract class for generators for image captcha'
def generate(self, secret: str, retry: int = 5) -> 'BytesIO':
'Creates an captcha image with given secret'
raise NotImplementedError(
'This method should be overridden on the child class')
def generate_bytes(self, secret: str, retry: int = 5) -> bytes:
'Default implementation for unwrapping BytesIO\'s contents'
return self.generate(secret, retry).getvalue()
class ImageCaptchaGenerator(AbstractImageCaptchaGenerator):
'Generates image captcha using current process'
def __init__(self, *args, **kwargs):
'Initializes protected values'
super().__init__(*args, **kwargs)
self._image_captcha = _gen_image_captcha()
ensure_imported_random()
ensure_imported_Image()
ensure_imported_ImageFilter()
ensure_imported_ImageOps()
def generate(self, secret: str, retry: int = 5) -> BytesIO:
im: Image = self._image_captcha.generate_image(secret)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.SMOOTH_MORE)
im = im.copy()
# if random.random() < 0.25:
# im = im.filter(ImageFilter.BLUR)
# im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.FIND_EDGES)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.CONTOUR)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.EMBOSS)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.DETAIL)
im = im.copy()
if random.random() < 0.25:
im = im.filter(ImageFilter.SHARPEN)
im = im.copy()
if random.random() < 0.5:
im = ImageOps.invert(im)
im = im.copy()
out = BytesIO()
im.save(out, format='PNG')
out.seek(0)
return out
class ImageCaptchaSubprocessGenerator(AbstractImageCaptchaGenerator):
'Generates image captcha on a subprocess'
def __init__(self, *args, **kwargs):
'Initializes protected values'
super().__init__(*args, **kwargs)
ensure_imported_subprocess()
def generate(self, secret: str, retry: int = 5) -> BytesIO:
result = subprocess.run(['python3', 'manage.py', 'gencaptcha', secret],
stdin=None,
stdout=subprocess.PIPE,
stderr=None,
text=False,
)
if result.returncode:
if retry > 0:
return self.generate(secret, retry - 1)
else:
result.check_returncode()
# print(result.stdout[:10])
out = BytesIO(result.stdout)
out.seek(0)
return out
def _gen_image_captcha() -> 'ImageCaptcha':
'Generates ImageCaptcha object'
ensure_imported_Path()
ensure_imported_ImageCaptcha()
image_captcha_fonts = [
str(Path('webproj/fonts/Roboto/Roboto-Regular.ttf').absolute()),
str(Path('webproj/fonts/Fondamento/Fondamento-Regular.ttf').absolute()),
str(Path('webproj/fonts/Josefin_Sans/static/JosefinSans-Medium.ttf').absolute()),
str(Path('webproj/fonts/Lora/static/Lora-Medium.ttf').absolute()),
str(Path('webproj/fonts/Quicksand/static/Quicksand-Regular.ttf').absolute()),
str(Path('webproj/fonts/Work_Sans/static/WorkSans-Medium.ttf').absolute()),
]
image_captcha = ImageCaptcha(300, 150,
fonts=image_captcha_fonts,
font_sizes=[36, 40, 44, 48, 54, 60, 66, 72])
for _ in image_captcha.truefonts:
pass
return image_captcha