Welcome to DisCapTy’s documentation!#

_images/logo.png _images/logo-dark.png

DisCapTy is a highly type hinted Python module to generate Captcha images & challenges without struggling your mind on how to make your own.

Features:

  • Highly customizable Captcha generation / Hackable

  • Developer & user friendly

  • Highly type hinted

  • Extendable with your own/third-party generators

Installation#

To install DisCapTy, run the following command in your wished environment:

pip install -U DisCapTy

With Poetry:

peotry add DisCapTy

Example#

from discapty import Challenge, ImageGenerator, TooManyRetriesError

challenge = Challenge(ImageGenerator(width=500, height=250, fonts=["./my-fonts.ttf"]), allowed_retries=5)
captcha = challenge.begin()  # Thank to DisCapTy's typehint, we know we're getting an image here!
send_captcha_to_user(captcha)

user_input = get_user_input()

try:
    is_valid = challenge.check(user_input)
    if is_valid:
        print("You're correct!")
    else:
        print("You're incorrect, try again!")
        # And here, do something to get user's input & check again.
except TooManyRetriesError:
    print("You've made too many errors!")

Pages#

Introduction#

This is a simplified guide to show everyone how to use the DisCapTy library. This guide is meant for everyone, however if you’re an experienced developer, you might be interested to read the DisCapTy API (Package) directly.

To read this guide#

Before you read this guide, we need to explain some terms we will use here. The word we will define here will be reference in uppercase and italic through this guide, for example, the word CAPTCHA, defined below, will be used in the guide as “CAPTCHA”.

  1. GENERATOR: A GENERATOR is a class that can be initialized with required/optional arguments (Or none at all), and that will generate a CAPTCHA OBJECT based on any given input. The result can be of any form.

  2. RAW CODE/CODE: The RAW CODE or CODE is the code that you’ll provide to DisCapTy, it is a clear code, and it is the expected input of your user.

  3. CAPTCHA OBJECT: The CAPTCHA OBJECT is the result of a GENERATOR, it can be anything (Text, audio, image, etc…) and it is what the user will face during it’s CHALLENGE.

  4. CHALLENGE: A CHALLENGE is a temporary question-answer made for one single user. A CHALLENGE can have different states:

    • PENDING: The CHALLENGE is waiting to begin.

    • WAITING: The CHALLENGE has begun, he is now waiting for user’s input.

    • COMPLETED: The CHALLENGE has been completed with success.

    • FAILED: The CHALLENGE has been failed by the user. (Generally by giving too many wrong answers)

    • FAILURE: The CHALLENGE has unexpectedly failed (Such as cancelled)

    It is generally thrown away after being completed, or failed.

  5. CAPTCHA CLASS: Refers to the discapty.captcha.Captcha class.

  6. CHALLENGE CLASS: Refers to the discapty.challenge.Challenge class.

Objects of DisCapTy#

Assuming you’re reading this guide because this is the first time you’re interacting with DisCapTy, you may need to understand what objects will DisCapTy serves you. This is important because if your codebase doesn’t understand what you are using, you might find yourself into a mess that is not a captcha. 🤔

discapty.captcha.Captcha#

A CAPTCHA CLASS contain the RAW CODE and it’s CAPTCHA OBJECT in the same place. It is where you can check for user’s input directly using the .check function.

Attention

This class does not generates the CAPTCHA OBJECT itself, a GENERATOR do. The CAPTCHA CLASS just wrap the RAW CODE and the CAPTCHA OBJECT together.

discapty.challenge.Challenge#

The CHALLENGE CLASS is the DisCapTy’s implementation of a CHALLENGE. With the CHALLENGE CLASS, you are able to generate the captcha, verify’s user input, set a defined limit of retries, use a custom CODE, etc…

The CHALLENGE CLASS has different states, as stated in CHALLENGE. If the challenge state’s is either FAILED, FAILURE or COMPLETED, it cannot be edited. While properties are writable, you’re advised to not touch them manually.

You can access the states in discapty.challenge.States

Subclasses of discapty.generators.Generator#

Any subclasses of discapty.generators.Generator are considered to be GENERATORS. They can be used in Challenges, or directly, like this:

# TextGenerator is a subclass of Generator
from discapty import TextGenerator

captcha_object = TextGenerator().generate('My Code')

send_to_user(captcha_object)

A generator can have default arguments arguments. You can change them directly when initializing the class:

# An image based Captcha object generator
from discapty import WheezyGenerator

captcha_object = WheezyGenerator(width=500, height=200, noise_level=3).generate('My code')  # Returns a PIL.Image.Image object

send_image_to_user(captcha_object)

Certain generators will requires you to give certain arguments. In the case of DisCapTy’s builtin generators, they all have optional arguments.

Creating a Challenge#

Now that you know what you’ll interact with, it’s time for you to create your first CHALLENGE CLASS.

To create a CHALLENGE, you just have to initialize the CHALLENGE CLASS with an initialized generator you want to use.

from discapty import Challenge, TextGenerator

challenge = Challenge(TextGenerator())
captcha_object = challenge.begin()  # You'll obtain your CAPTCHA_OBJECT HERE

From here you can send your CAPTCHA OBJECT to your user, and you can validate the user’s input like this:

user_input = get_user_input()

is_valid_input = challenge.check(user_input)

This is a basic example, and it is a bad one, because the .check function can raise TooManyRetriesError if .check has been used more than the allowed_retries attributes allows it. The allowed_retries attribute can be edited when creating the CHALLENGE CLASS.

If you do like a more complete example, check the following:

from discapty import Challenge, TextGenerator, TooManyRetriesError

challenge = Challenge(TextGenerator(), allowed_retries=3)

first_captcha = challenge.begin()
send_to_user(first_captcha)

# challenge.is_completed returns `True` when the Challenge's state is either completed or failed.
while not challenge.is_completed:
    user_input = get_user_input()

    try:
        is_right = challenge.check(user_input)
        # If it is right, the challenge will be completed.
    except TooManyRetriesError:
        # From here, challenge will be completed.
        is_right = False

    # The loop will continue until a right answer has been completed or if there is too many retries.

if is_right:
    do_something_for_completing_the_captcha()
else:
    do_something_for_failing_the_captcha()

This code is already more suitable for your needs.

Creating a Captcha queue#

The DisCapTy’s Captcha queue permit the developers to store many CHALLENGE CLASS in one place, it takes cares of managing all of them. Putting in place the Captcha queue is fairly easy. The Captcha queue will always give an ID to a challenge, if you don’t pass one, an UUID will be generated for you.

To use the queue, as always you just need to initialize it with one or more initialized generator(s):

from discapty import CaptchaQueue, WheezyGenerator, TextGenerator

# With one generator
my_queue = CaptchaQueue(TextGenerator())

# With multiple generators
my_queue = CaptchaQueue([TextGenerator(), WheezyGenerator()])

if you use multiple generators, this mean that one generator will be picked randomly when creating a CHALLENGE CLASS.

Warning

This may create inconsistency when generating CAPTCHA OBJECTS where you’ll need to check in your code what kind of CAPTCHA OBJECT you receive, for example, you may send an image differently from a string.

After then, you can create a challenge by calling .create_challenge:

from discapty import CaptchaQueue, TextGenerator

queue = CaptchaQueue(TextGenerator())

challenge = queue.create_challenge()  # You'll obtain a challenge here

send_captcha_to_user(challenge.captcha)

challenge_id = challenge.id
# To obtain your challenge through it's ID
challenge = queue.get_challenge(challenge_id)

# To delete/cancel your challenge
queue.delete_challenge(challenge_id)

Builtin generators#

Here is the following generators coming with DisCapTy:

TextGenerator#

class discapty.TextGenerator(*, separator: Union[str, List[str]] = '\u200b')

A text-based Captcha generator. Most insecure, but it is the most tricky.

It adds a specific separator between each character of the given text. The default separator is an invisible space. (\u200B)

Important

The following generators are image-based generators, meaning you’ll receive images. If you use the color arguments, we make use of pydantic.Color. While you CAN pass a str object, you IDE might complain that you didn’t pass a pydantic.Color object. This is fine, you can just ignore this error, your string will be processed without trouble. We tried our best

WheezyGenerator#

class discapty.WheezyGenerator(*, fonts: Sequence[Union[pydantic.types.FilePath, str]] = ['/home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/discapty/fonts/Roboto-Regular.ttf'], fonts_size: Tuple[int, ...] = (50,), width: int = 300, height: int = 125, background_color: pydantic.color.Color = '#EEEECC', text_color: pydantic.color.Color = '#5C87B2', text_squeeze_factor: float = 0.8, noise_number: int = 30, noise_color: pydantic.color.Color = '#EEEECC', noise_level: int = 2)

A wheezy image Captcha generator. Comes with many customizable settings. Easier to read than Image.

Example: https://imgur.com/a/l9V09PN

ImageGenerator#

class discapty.ImageGenerator(*, fonts: Sequence[Union[pydantic.types.FilePath, str]] = ['/home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/discapty/fonts/Roboto-Regular.ttf'], fonts_size: Tuple[int, ...] = (50,), background_color: pydantic.color.Color = '#EFFCF400', text_color: pydantic.color.Color = '#78862864', number_of_dots: int = 100, width_of_dots: int = 3, number_of_curves: int = 1, width: int = 300, height: int = 125)

An image Captcha generator. Comes with many customizable settings. More harder than the Wheezy generator.

Example: https://imgur.com/a/wozYgW0

Migration Guide#

From 2.0.x to 2.1.0#

DisCapTy 2.1.0 has been released around the beginning august to resolve one major problem of DisCapTy 2.0.x: Type hinting in discapty.captcha.Captcha, discapty.challenge.Challenge and discapty.captcha_queue.CaptchaQueue. These classes, when returning a captcha object, would give the typing.Any, which is vague - it actually doesn’t tell the developer what type could the captcha object be.

To resolve this issue, generics classes were implemented into needed classes and will now take the type hint from generators to mirror them into methods who return captcha objects.

While this shouldn’t require any code changes, if you directly typed your variable, here’s how to migrate:

Custom generator#
from discapty import Generator

# Before
class MyGenerator(Generator):
    def generate(self, text: str) -> str:
        return complexify_text(text)

# After
class MyGenerator(Generator[str]):  # Indicate here what ".generate" type will return!
    def generate(self, text: str):  # Type can be disregarded/optional, but good to add too!
        return complexify_text(text)
discapty.Captcha, discapty.Challenge, discapty.CaptchaQueue stored in variable with implicit type hint (Python >3.10, else use Union)#
from discapty import Captcha, Challenge, CaptchaQueue, TextGenerator, WheezyGenerator
import PIL.Image.Image

# Before
queue: CaptchaQueue = CaptchaQueue([TextGenerator(), WheezyGenerator()])  # Wait... What's the generator type?
challenge: Challenge = queue.create_challenge()  # Wait... What's the generator type?
captcha_class: Captcha = challenge.captcha  # Wait... What's the generator type?

# After
queue: CaptchaQueue[str | PIL.Image.Image] = CaptchaQueue([TextGenerator(), WheezyGenerator()])  # Generator's type is "str" or "PIL.Image.Image"!
challenge: Challenge[str | PIL.Image.Image] = queue.create_challenge()  # Generator's type is "str" or "PIL.Image.Image"!
captcha_class: Captcha[str | PIL.Image.Image] = challenge.captcha  # Generator's type is "str" or "PIL.Image.Image"!

captcha_class.captcha_object  # We know this is either a str or a PIL.Image.Image!
challenge.begin()  # We also know it is either a str or a PIL.Image.Image!

Note

It is not necessary to indicate the variable’s type hint, it is even suggested to not do that unless you know what you’re doing/be sure of what you want to get.

From 1.0.x to 2.0#

DisCapTy has been created to be a supplementary tool for discord.py, however, its owner had announced that this library would not be supported anymore. From here, many peoples has created forks of this library, which was making DisCapTy completely unusable for others library. This thought has dragged me to think about what DisCapTy should be & become. As such, it has been decided that DisCapTy should NOT be related to discord.py anymore but as a standalone library with no restriction on where it could be used.

The most important points are:

  1. Deprecating generating Captcha in the discapty.Captcha class, but rather in a discapty.generators.Generator subclass.

  2. Featuring discapty.Challenge & discapty.CaptchaQueue.

  3. Added more specific errors to the library.

  4. A documentation has been created.

Rewrite of Captcha class#

The discapty.Captcha object does not longer generates the Captcha object anymore, what does is a generator. There is no exact alternative to generates the Captcha object in the same place as the Captcha class, since now the Captcha class only include the Captcha object and its code. However, you can do this:

Regarding diff block

The “-” represent the old version, the “+” represent the actual, new version.

--- /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/captcha_object/captcha_object_20.old.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/captcha_object/captcha_object_20.py
@@ -1,7 +1,10 @@
 import discapty
 
-captcha = discapty.Captcha("whezzy")
-captcha_object = captcha.generate_captcha()
+code = "My code"
+generator = discapty.WheezyGenerator()
+
+captcha_object = generator.generate(code)
+captcha = discapty.Captcha(code, captcha_object)
 
 # Checking the code
-is_correct = captcha.verify_code(user_input)
+is_correct: bool = captcha.check(user_input)
Removal of .setup function#

Along with the rewrite of the Captcha class, the .setup function has been removed, instead, parameters can be provided to a generator when initializing a generator class.

--- /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/generator_init/gen_init_20.old.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/generator_init/gen_init_20.py
@@ -1,4 +1,4 @@
 import discapty
 
-captcha = discapty.Captcha("wheezy")
-captcha.setup(width=200, height=100)
+generator = discapty.WheezyGenerator(width=200, height=100)
+# Do the rest...
Added Challenge class#

The discapty.Challenge class is the new preferred way to create Captcha now. You can read more about challenges here: Introduction to Challenges - Creating a Challenge

To use discapty.Challenge rather than the old discapty.Captcha, you can do these changes:

--- /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/captcha_to_challenge/c_to_c.old.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/docs/source/docs/source/code_sample/captcha_to_challenge/c_to_c.py
@@ -1,9 +1,8 @@
-import discapty
+from discapty import Challenge, WheezyGenerator
 
-captcha = discapty.Captcha("whezzy")
-captcha.setup(width=200, height=100)
+challenge = Challenge(WheezyGenerator(width=200, height=100))
 
-captcha_object = captcha.generate_captcha()
+captcha_object = challenge.begin()
 
 # Checking the code
-is_correct = captcha.verify_code(user_input)
+is_correct = challenge.check(user_input)

DisCapTy API (Package)#

Full and complete API of the DisCapTy package.

discapty.captcha module#

class discapty.captcha.Captcha(code: str, captcha_object: discapty.captcha._CR)#

Bases: Generic[discapty.captcha._CR]

Represent a Captcha object.

Changed in version 2.0.0: The Captcha object is no longer what creates the Captcha image, it just is the representation of the Captcha that the user will face.

Changed in version 2.1.0: This class is now a generic class and requires to indicate which type it will receive. (If necessary)

code: str#
captcha_object: discapty.captcha._CR#
type: Type[discapty.captcha._CR]#
check(text: str, *, force_casing: bool = False, remove_spaces: bool = True) bool#

Check if a text is correct against the captcha code.

Parameters
  • text (str) – The answer to check against the Captcha’s code.

  • force_casing (bool) – If True, the casing must be respected. Defaults to False.

  • remove_spaces (bool) – If True, spaces will be removed when checking the answer. Defaults to True.

Returns

True if the answer is correct, False otherwise.

Return type

bool

discapty.captcha_queue module#

class discapty.captcha_queue.CaptchaQueue(generators: Union[discapty.generators.Generator[discapty.captcha_queue._GR], Iterable[discapty.generators.Generator[discapty.captcha_queue._GR]]], *, queue: Optional[Dict[str, discapty.challenge.Challenge[discapty.captcha_queue._GR]]] = None)#

Bases: Generic[discapty.captcha_queue._GR]

A safe handler for taking cares of managing the challenges for the developer.

It basically offers a sane & internal way to manage your captcha using a key-value pair without ever having to touch the challenges/captcha directly.

Parameters
Raises

ValueError – If no generators has been passed.

New in version 2.0.0.

Changed in version 2.1.0: This class is now a generic class and requires to indicate which type it will receive. (If necessary) If the type is not especially indicated in your variable, it should be automatically done.

generators: List[discapty.generators.Generator[discapty.captcha_queue._GR]]#
queue: Dict[str, discapty.challenge.Challenge[discapty.captcha_queue._GR]]#
create_challenge(challenge_id: Optional[str] = None, *, retries: Optional[int] = None, code: Optional[str] = None, code_length: Optional[int] = None) discapty.challenge.Challenge[discapty.captcha_queue._GR]#

Create a challenge for an id. Overwrite the challenge created before, unless the challenge is not fully completed.

Parameters
  • challenge_id (Optional, str) – The id associated to the challenge. If not given, a random id will be generated.

  • retries (Optional, int) – The number of allowed retries. Defaults to 3.

  • code (Optional, str) – The code to use. Defaults to a random code.

  • code_length (Optional, int) – The length of the code to generate if no code is supplied. Defaults to 4.

Returns

The generated challenge.

Return type

discapty.challenge.Challenge

Changed in version 2.1.0: The return type will now be dynamically acquired and adapt to the given generator(s).

get_challenge(challenge_id: str) discapty.challenge.Challenge[discapty.captcha_queue._GR]#

Get the challenge of an id, if it exist.

Parameters

challenge_id (str) – The id associated to the challenge.

Raises

UnexistingChallengeError – If the given id does not have any associated challenge.

Returns

The challenge associated to the id.

Return type

discapty.challenge.Challenge

Changed in version 2.1.0: The return type will now be dynamically acquired and adapt to the given generator(s).

delete_challenge(challenge_id: str) None#

Delete a challenge of an id, if it exist.

Parameters

challenge_id (int) – The id associated to the challenge.

Raises

UnexistingChallengeError – If the given id does not have any associated challenge.

discapty.challenge module#

class discapty.challenge.FailReason(value)#

Bases: enum.Enum

An enum with all possible reasons of failing the captcha.

TOO_MANY_RETRIES = 'Too many retries'#
CANCELLED = 'Challenge has been cancelled'#
class discapty.challenge.States(value)#

Bases: enum.Enum

An enum representing the different states of a challenge.

Available states are:

  • PENDING : The challenge is waiting to begin.

  • WAITING : The challenge is waiting for user’s input.

  • COMPLETED : The challenge has been completed.

  • FAILED : The challenge has been failed without trouble.

  • FAILURE : The challenge has been completed without user’s input and in an unexpected way. (e.g. manually cancelled)

PENDING = 'Pending'#
WAITING = 'Waiting'#
COMPLETED = 'Completed'#
FAILED = 'Failed'#
FAILURE = 'Failure (Unexpected)'#
class discapty.challenge.Challenge(generator: discapty.generators.Generator[discapty.challenge._CR], challenge_id: Optional[str] = None, *, allowed_retries: Optional[int] = None, code: Optional[str] = None, code_length: Optional[int] = None)#

Bases: Generic[discapty.challenge._CR]

Representation of a challenge. A challenge represent the user’s Captcha question-answer he must face.

This class takes cares of:
  • Generating the captcha

  • Verify inputs

  • Manage the “Captcha” object

It frees your mind from managing all the process of a captcha challenge, keeping your code short and easy.

Parameters
  • generator (Subclass of discapty.generators.Generator) – The generator class to use.

  • challenge_id (Optional, str) – The id of the challenge. Can be a string or an id. If none is supplied, a random UUID will be generated.

  • allowed_retries (Optional, int) – The number of retries allowed. Defaults to 3.

  • code (Optional, str) – The code to use. If none is supplied, a random code will be generated.

  • code_length (Optional, int) – The length of the code to generate if no code is supplied. Defaults to 4.

New in version 2.0.0.

Changed in version 2.1.0: This class is now a generic class and requires to indicate which type it will receive. (If necessary) If the type is not especially indicated in your variable, it should be automatically done.

generator: discapty.generators.Generator[discapty.challenge._CR]#

The generator used with this challenge.

code: str#

The clear code.

challenge_id: str#

The ID of this challenge.

allowed_retries: int#

The total allowed retried of this challenge.

failures: int#

The total failures since this challenge has been created.

attempted_tries: int#

The total attempted tried since this challenge has been created.

state: discapty.challenge.States#

The actual state of the challenge.

fail_reason: Optional[str]#

The fail reason, if applicable.

property captcha_object: discapty.challenge._CR#

Get the Captcha object.

Returns

The Captcha object.

Return type

discapty.constants.GeneratorReturnType

property captcha: discapty.captcha.Captcha[discapty.challenge._CR]#

The Captcha class associated to this challenge.

Returns

The Captcha class.

Return type

discapty.captcha.Captcha

property is_completed: bool#

Check if the challenge has been completed or failed.

Returns

If the challenge has been completed or failed.

Return type

bool

property is_correct: Optional[bool]#

Check if the challenge has been completed. If not, return None. If failed, return False.

Returns

If the challenge has been completed with success. If not, return False. If not completed, return None.

Return type

Optional, bool

property is_wrong: Optional[bool]#

Check if the challenge has been failed. If not, return None. If completed, return False.

Returns

If the challenge has been failed. If not, return False. If not completed, return None.

Return type

Optional, bool

begin() discapty.challenge._CR#

Begins the challenge.

Raises
  • AlreadyCompletedError – If the challenge has already been completed. You cannot start a challenge twice, you need to create a new one.

  • AlreadyRunningError – If the challenge is already running.

  • TooManyRetriesError – If the number of failures is greater than the number of retries allowed. In other words, the challenge has failed.

  • ChallengeCompletionError – If the challenge had a failure. Returns the failure’s reason.

Return type

The Captcha object to send to the user.

Changed in version 2.1.0: The return type will now be dynamically acquired and adapt to the given generator.

check(answer: str, *, force_casing: bool = False, remove_spaces: bool = True) bool#

Check an answer. This will always add +1 to attempted_tries and failures if necessary.

Parameters
  • answer (str) – The answer to check against the Captcha’s code.

  • force_casing (bool) – If True, the casing must be respected. Defaults to False.

  • remove_spaces (bool) – If True, spaces will be removed when checking the answer. Defaults to True.

Raises
  • TooManyRetriesError – If the number of failures is greater than the number of retries allowed. We are still adding +1 to the failure even when raising the exception.

  • TypeError – The challenge cannot be edited (State is either not PENDING or not WAITING)

Returns

True if the answer is correct, False otherwise.

Return type

bool

reload(*, increase_attempted_tries: bool = True, increase_failures: bool = False) discapty.challenge._CR#

Reload the Challenge and its code.

This method will create a new random code + captcha object. It will also increase the attempted_tries counter if requested. By defaults, this behavior is executed.

Parameters
  • increase_attempted_tries (bool) – If True, the attempted_tries counter will be increased.

  • increase_failures (bool) – If True, the failures counter will be increased.

Raises

TypeError – If the challenge cannot be edited or is not already running.

Returns

The Captcha object to send to the user.

Return type

discapty.constants.GeneratorReturnType

Changed in version 2.1.0: The return type will now be dynamically acquired and adapt to the given generator.

cancel() None#

Cancel the challenge.

Raises

TypeError – If the challenge cannot be edited.

discapty.constants module#

discapty.errors module#

exception discapty.errors.NonexistingChallengeError#

Bases: KeyError

Raised when trying to get a challenge that does not exist. Subclass of “KeyError” as this error will appear when trying to get the challenge from a dict.

exception discapty.errors.InvalidFontError#

Bases: Exception

Raised when one or more fonts are invalid.

exception discapty.errors.ChallengeCompletionError#

Bases: Exception

Raised when a challenge has an issue regarding its completion.

exception discapty.errors.TooManyRetriesError#

Bases: discapty.errors.ChallengeCompletionError

Raised when a challenge received more retries than allowed.

exception discapty.errors.AlreadyCompletedError#

Bases: discapty.errors.ChallengeCompletionError

Raised when a challenge has already been completed.

exception discapty.errors.AlreadyRunningError#

Bases: discapty.errors.ChallengeCompletionError

Raised when a challenge is already running.

discapty.generators module#

discapty.generators.random() x in the interval [0, 1).#
class discapty.generators.Generator#

Bases: abc.ABC, pydantic.main.BaseModel, Generic[discapty.generators._GR]

Base class for all generators.

A generator is used to especially generate a Captcha object based on a given text. A generator looks like this:

class MyGenerator(Generator[str]):
    def generate(self, text: str) -> str:
        return "+".join(text)

A generator can be supplied with parameters using class’s attributes, for example:

class MyGenerator(Generator[str]):
    separator = "+"

    def generate(self, text: str) -> str:
        return self.separator.join(text)

gen1 = MyGenerator()  # Separator here is "+"
gen2 = MyGenerator(separator="-")  # Separator here is "-"

Here, separator has a default value, which can be overridden by the user, or not. If you wish to create a generator with a required value, you can use “…”, like this:

class MyGenerator(Generator[str]):
    separator: str = ...

    ...

MyGenerator(separator="+")  # Works! 👍
MyGenerator()  # Raises an error! 👎

If you wish to know more on that subject, visit Pydantic’s documentation as this is what Generator uses under the hood. https://pydantic-docs.helpmanual.io/

New in version 2.0.0.

Changed in version 2.1.0: This class is now a generic class, taking as the parameter the type output of “.generate”.

property required_keys: List[str]#

List of all child’s required keys.

Returns

The list of required keys.

Return type

List of str

property optional_keys: List[str]#

List of all child’s optional keys.

Returns

The list of optional keys.

Return type

List of str

abstract generate(text: str) discapty.generators._GR#

A method that needs to be implemented by the child class. This method will return the Captcha that the user has requested. See class’s docstring.

Parameters

text (str) – The text to generate the captcha with.

class discapty.generators.WheezyGenerator(*, fonts: Sequence[Union[pydantic.types.FilePath, str]] = ['/home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/discapty/fonts/Roboto-Regular.ttf'], fonts_size: Tuple[int, ...] = (50,), width: int = 300, height: int = 125, background_color: pydantic.color.Color = '#EEEECC', text_color: pydantic.color.Color = '#5C87B2', text_squeeze_factor: float = 0.8, noise_number: int = 30, noise_color: pydantic.color.Color = '#EEEECC', noise_level: int = 2)#

Bases: discapty.generators.Generator[PIL.Image.Image]

A wheezy image Captcha generator. Comes with many customizable settings. Easier to read than Image.

Example: https://imgur.com/a/l9V09PN

fonts: Sequence[Union[pydantic.types.FilePath, str]]#
fonts_size: Tuple[int, ...]#
width: int#
height: int#
background_color: pydantic.color.Color#
text_color: pydantic.color.Color#
text_squeeze_factor: float#
noise_number: int#
noise_color: pydantic.color.Color#
noise_level: int#
classmethod as_many_size_as_fonts(v: Tuple[int, ...], values: Dict[str, Any])#
generate(text: str) PIL.Image.Image#

Generate a wheezy image. See https://imgur.com/a/l9V09PN.

Parameters

text (str) – The text to generate the captcha with.

Returns

The captcha image.

Return type

PIL.Image.Image

class discapty.generators.ImageGenerator(*, fonts: Sequence[Union[pydantic.types.FilePath, str]] = ['/home/docs/checkouts/readthedocs.org/user_builds/discapty/checkouts/latest/discapty/fonts/Roboto-Regular.ttf'], fonts_size: Tuple[int, ...] = (50,), background_color: pydantic.color.Color = '#EFFCF400', text_color: pydantic.color.Color = '#78862864', number_of_dots: int = 100, width_of_dots: int = 3, number_of_curves: int = 1, width: int = 300, height: int = 125)#

Bases: discapty.generators.Generator[PIL.Image.Image]

An image Captcha generator. Comes with many customizable settings. More harder than the Wheezy generator.

Example: https://imgur.com/a/wozYgW0

fonts: Sequence[Union[pydantic.types.FilePath, str]]#
fonts_size: Tuple[int, ...]#
background_color: pydantic.color.Color#
text_color: pydantic.color.Color#
number_of_dots: int#
width_of_dots: int#
number_of_curves: int#
width: int#
height: int#
get_truefonts() Tuple[PIL.ImageFont.FreeTypeFont, ...]#
static create_noise_curve(image: PIL.Image.Image, color: pydantic.color.Color, number: int = 1) PIL.Image.Image#
static create_noise_dots(image: PIL.Image.Image, color: pydantic.color.Color, width: int = 3, number: int = 30) PIL.Image.Image#
create_captcha_image(*, chars: str) PIL.Image.Image#
generate(text: str) PIL.Image.Image#

Generate a Captcha image. See https://imgur.com/a/wozYgW0

Parameters

text (str) – The text to generate the captcha with.

Returns

The captcha image.

Return type

PIL.Image.Image

class discapty.generators.TextGenerator(*, separator: Union[str, List[str]] = '\u200b')#

Bases: discapty.generators.Generator[str]

A text-based Captcha generator. Most insecure, but it is the most tricky.

It adds a specific separator between each character of the given text. The default separator is an invisible space. (\u200B)

separator: Union[str, List[str]]#
generate(text: str) str#

Generate a Captcha text.

Parameters

text (str) – The text to generate the captcha with.

Returns

The captcha text.

Return type

str