kaliiiiiiiiii / Selenium-Driverless

undetected Selenium without usage of chromedriver
https://kaliiiiiiiiii.github.io/Selenium-Driverless/
Other
430 stars 52 forks source link

Update webelement.py: basic implementation of 'send_keys' #191

Closed boludoz closed 3 months ago

kaliiiiiiiiii commented 3 months ago

mhh nope, there's more needed than that. This will only work for input, not key events & is detectable.

boludoz commented 3 months ago

mhh nope, there's more needed than that. This will only work for input, not key events & is detectable.

Sorry, I thought insert was even more detectable, I'll just add it to my personal code as a monkey patch to make completing OTPs easier.

kaliiiiiiiiii commented 3 months ago

mhh nope, there's more needed than that. This will only work for input, not key events & is detectable.

Sorry, I thought insert was even more detectable, I'll just add it to my personal code as a monkey patch to make completing OTPs easier.

Indeed, elem.write might be more detectable. Tho, I'd prefer to directly implement it the right way:)

Will do as soon I find some time👀

boludoz commented 3 months ago

mhh nope, there's more needed than that. This will only work for input, not key events & is detectable.

Sorry, I thought insert was even more detectable, I'll just add it to my personal code as a monkey patch to make completing OTPs easier.

Indeed, elem.write might be more detectable. Tho, I'd prefer to directly implement it the right way:)

Will do as soon I find some time👀

This code is wonderful. I only found two problems with this driver, the first is the click, it doesn't always click, suppose there is a banner and a button below it, the code will fail silently. The other issue is asynchronicity within the same code, certain codes that will be executed simultaneously will generate an error in the websocket but I know that it is a feature that you plan to improve when you have time. But as I told you, this code is a lost gem, the mere fact that it is undetectable and the concept you implemented in managing iframes is incredible.

You could use the CDP kit and add it here, that would make your job a lot easier because it is generated automatically.

I congratulate you for this excellent job, I'm nobody but you should be working in security companies or something like that, I hope you are already doing it.

boludoz commented 3 months ago

And if you want, I can make a complete implementation of send_keys, you just have to tell me what feature you want me to add and if you like the result, you can add it without commitment.

kaliiiiiiiiii commented 3 months ago

And if you want, I can make a complete implementation of send_keys, you just have to tell me what feature you want me to add and if you like the result, you can add it without commitment.

Basend on the following code:

async def keyboard(driver, text):
    if text == 'Enter':
                key_event = {
                    "type": "keyDown",
                    "code": "Enter",
                    "windowsVirtualKeyCode": 13,
                    "key": "Enter",
                    "modifiers": 0
                }
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                # Simulate key press for the actual character
                key_event["type"] = "char"
                key_event["text"] = text
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                del key_event['text']
                # Simulate key release
                key_event["type"] = "keyUp"
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))

    elif text == 'Tab':
                key_event = {
                    "type": "keyDown",
                    "code": "Tab",
                    "windowsVirtualKeyCode": 9,
                    "key": "Tab",
                    "modifiers": 0
                }
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                # Simulate key release
                key_event["type"] = "keyUp"
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))

    else:      
            key_mapping = {
                'a': ('KeyA', 65), 'b': ('KeyB', 66), 'c': ('KeyC', 67), 'd': ('KeyD', 68), 'e': ('KeyE', 69),
                'f': ('KeyF', 70), 'g': ('KeyG', 71), 'h': ('KeyH', 72), 'i': ('KeyI', 73), 'j': ('KeyJ', 74),
                'k': ('KeyK', 75), 'l': ('KeyL', 76), 'm': ('KeyM', 77), 'n': ('KeyN', 78), 'o': ('KeyO', 79),
                'p': ('KeyP', 80), 'q': ('KeyQ', 81), 'r': ('KeyR', 82), 's': ('KeyS', 83), 't': ('KeyT', 84),
                'u': ('KeyU', 85), 'v': ('KeyV', 86), 'w': ('KeyW', 87), 'x': ('KeyX', 88), 'y': ('KeyY', 89),
                'z': ('KeyZ', 90), 'A': ('KeyA', 65), 'B': ('KeyB', 66), 'C': ('KeyC', 67), 'D': ('KeyD', 68),
                'E': ('KeyE', 69), 'F': ('KeyF', 70), 'G': ('KeyG', 71), 'H': ('KeyH', 72), 'I': ('KeyI', 73),
                'J': ('KeyJ', 74), 'K': ('KeyK', 75), 'L': ('KeyL', 76), 'M': ('KeyM', 77), 'N': ('KeyN', 78),
                'O': ('KeyO', 79), 'P': ('KeyP', 80), 'Q': ('KeyQ', 81), 'R': ('KeyR', 82), 'S': ('KeyS', 83),
                'T': ('KeyT', 84), 'U': ('KeyU', 85), 'V': ('KeyV', 86), 'W': ('KeyW', 87), 'X': ('KeyX', 88),
                'Y': ('KeyY', 89), 'Z': ('KeyZ', 90), '0': ('Digit0', 48), '1': ('Digit1', 49), '2': ('Digit2', 50),
                '3': ('Digit3', 51), '4': ('Digit4', 52), '5': ('Digit5', 53), '6': ('Digit6', 54), '7': ('Digit7', 55),
                '8': ('Digit8', 56), '9': ('Digit9', 57), '!': ('Digit1', 49), '"': ('Quote', 222), '#': ('Digit3', 51),
                '$': ('Digit4', 52), '%': ('Digit5', 53), '&': ('Digit7', 55), "'": ('Quote', 222), '(': ('Digit9', 57),
                ')': ('Digit0', 48), '*': ('Digit8', 56), '+': ('Equal', 187), ',': ('Comma', 188), '-': ('Minus', 189),
                '.': ('Period', 190), '/': ('Slash', 191), ':': ('Semicolon', 186), ';': ('Semicolon', 186), '<': ('Comma', 188),
                '=': ('Equal', 187), '>': ('Period', 190), '?': ('Slash', 191), '@': ('Digit2', 50), '[': ('BracketLeft', 219),
                '\\': ('Backslash', 220), ']': ('BracketRight', 221), '^': ('Digit6', 54), '_': ('Minus', 189), '`': ('Backquote', 192),
                '{': ('BracketLeft', 219), '|': ('Backslash', 220), '}': ('BracketRight', 221), '~': ('Backquote', 192), ' ': ('Space', 32)
            }

            for letter in text:
                    if letter in key_mapping:
                        key_code, virtual_key_code = key_mapping[letter]
                        # Determine if shift key is needed
                        shift_pressed = False
                        if letter.isupper() or letter in '~!@#$%^&*()_+{}|:"<>?':
                            shift_pressed = True
                            await driver.execute_cdp_cmd("Input.dispatchKeyEvent", {
                                "type": "keyDown",
                                "code": "ShiftLeft",
                                "windowsVirtualKeyCode": 16,
                                "key": "Shift",
                                "modifiers": 8 if shift_pressed else 0
                            })
                            await asyncio.sleep(uniform(0.05, 0.1))  # Simulate human typing speed

                        key_event = {
                            "type": "keyDown",
                            "code": key_code,
                            "windowsVirtualKeyCode": virtual_key_code,
                            "key": letter,
                            "modifiers": 8 if shift_pressed else 0
                        }

                        # Send keydown event
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))

                        # Simulate key press for the actual character
                        key_event["type"] = "char"
                        key_event["text"] = letter
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))
                        del key_event['text']

                        # Simulate key release
                        key_event["type"] = "keyUp"
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))

                        # Release the shift key if it was pressed
                        if shift_pressed:
                            await driver.execute_cdp_cmd("Input.dispatchKeyEvent", {
                                "type": "keyUp",
                                "code": "ShiftLeft",
                                "windowsVirtualKeyCode": 16,
                                "key": "Shift",
                                "modifiers": 0
                            })
                            await asyncio.sleep(uniform(0.05, 0.1))  # Simulate human typing speed

add a class similar to https://github.com/kaliiiiiiiiii/Selenium-Driverless/blob/29204358375fbfcf4025a95a64a128faec6e7f38/src/selenium_driverless/input/keyboard.py

Would be nice to support a custom keyboar layout as well.

Then, add an instance of that class as selenium_driverless.types.target.Target.keyboard

=> add async Target.send_keys => make elem.send_keys first click on it & then Target.send_keys

Notes:

kaliiiiiiiiii commented 3 months ago

You could use the CDP kit and add it here, that would make your job a lot easier because it is generated automatically.

Mh I prefer to control all the stuff fully & build from scratch. Tho, adding this as an option ofc is considerable.

I congratulate you for this excellent job, I'm nobody but you should be working in security companies or something like that, I hope you are already doing it.

Well fun fact: I'm still in school haha👀

boludoz commented 3 months ago

And if you want, I can make a complete implementation of send_keys, you just have to tell me what feature you want me to add and if you like the result, you can add it without commitment.

Basend on the following code:

async def keyboard(driver, text):
    if text == 'Enter':
                key_event = {
                    "type": "keyDown",
                    "code": "Enter",
                    "windowsVirtualKeyCode": 13,
                    "key": "Enter",
                    "modifiers": 0
                }
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                # Simulate key press for the actual character
                key_event["type"] = "char"
                key_event["text"] = text
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                del key_event['text']
                # Simulate key release
                key_event["type"] = "keyUp"
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))

    elif text == 'Tab':
                key_event = {
                    "type": "keyDown",
                    "code": "Tab",
                    "windowsVirtualKeyCode": 9,
                    "key": "Tab",
                    "modifiers": 0
                }
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))
                # Simulate key release
                key_event["type"] = "keyUp"
                await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                await asyncio.sleep(uniform(0.02, 0.7))

    else:      
            key_mapping = {
                'a': ('KeyA', 65), 'b': ('KeyB', 66), 'c': ('KeyC', 67), 'd': ('KeyD', 68), 'e': ('KeyE', 69),
                'f': ('KeyF', 70), 'g': ('KeyG', 71), 'h': ('KeyH', 72), 'i': ('KeyI', 73), 'j': ('KeyJ', 74),
                'k': ('KeyK', 75), 'l': ('KeyL', 76), 'm': ('KeyM', 77), 'n': ('KeyN', 78), 'o': ('KeyO', 79),
                'p': ('KeyP', 80), 'q': ('KeyQ', 81), 'r': ('KeyR', 82), 's': ('KeyS', 83), 't': ('KeyT', 84),
                'u': ('KeyU', 85), 'v': ('KeyV', 86), 'w': ('KeyW', 87), 'x': ('KeyX', 88), 'y': ('KeyY', 89),
                'z': ('KeyZ', 90), 'A': ('KeyA', 65), 'B': ('KeyB', 66), 'C': ('KeyC', 67), 'D': ('KeyD', 68),
                'E': ('KeyE', 69), 'F': ('KeyF', 70), 'G': ('KeyG', 71), 'H': ('KeyH', 72), 'I': ('KeyI', 73),
                'J': ('KeyJ', 74), 'K': ('KeyK', 75), 'L': ('KeyL', 76), 'M': ('KeyM', 77), 'N': ('KeyN', 78),
                'O': ('KeyO', 79), 'P': ('KeyP', 80), 'Q': ('KeyQ', 81), 'R': ('KeyR', 82), 'S': ('KeyS', 83),
                'T': ('KeyT', 84), 'U': ('KeyU', 85), 'V': ('KeyV', 86), 'W': ('KeyW', 87), 'X': ('KeyX', 88),
                'Y': ('KeyY', 89), 'Z': ('KeyZ', 90), '0': ('Digit0', 48), '1': ('Digit1', 49), '2': ('Digit2', 50),
                '3': ('Digit3', 51), '4': ('Digit4', 52), '5': ('Digit5', 53), '6': ('Digit6', 54), '7': ('Digit7', 55),
                '8': ('Digit8', 56), '9': ('Digit9', 57), '!': ('Digit1', 49), '"': ('Quote', 222), '#': ('Digit3', 51),
                '$': ('Digit4', 52), '%': ('Digit5', 53), '&': ('Digit7', 55), "'": ('Quote', 222), '(': ('Digit9', 57),
                ')': ('Digit0', 48), '*': ('Digit8', 56), '+': ('Equal', 187), ',': ('Comma', 188), '-': ('Minus', 189),
                '.': ('Period', 190), '/': ('Slash', 191), ':': ('Semicolon', 186), ';': ('Semicolon', 186), '<': ('Comma', 188),
                '=': ('Equal', 187), '>': ('Period', 190), '?': ('Slash', 191), '@': ('Digit2', 50), '[': ('BracketLeft', 219),
                '\\': ('Backslash', 220), ']': ('BracketRight', 221), '^': ('Digit6', 54), '_': ('Minus', 189), '`': ('Backquote', 192),
                '{': ('BracketLeft', 219), '|': ('Backslash', 220), '}': ('BracketRight', 221), '~': ('Backquote', 192), ' ': ('Space', 32)
            }

            for letter in text:
                    if letter in key_mapping:
                        key_code, virtual_key_code = key_mapping[letter]
                        # Determine if shift key is needed
                        shift_pressed = False
                        if letter.isupper() or letter in '~!@#$%^&*()_+{}|:"<>?':
                            shift_pressed = True
                            await driver.execute_cdp_cmd("Input.dispatchKeyEvent", {
                                "type": "keyDown",
                                "code": "ShiftLeft",
                                "windowsVirtualKeyCode": 16,
                                "key": "Shift",
                                "modifiers": 8 if shift_pressed else 0
                            })
                            await asyncio.sleep(uniform(0.05, 0.1))  # Simulate human typing speed

                        key_event = {
                            "type": "keyDown",
                            "code": key_code,
                            "windowsVirtualKeyCode": virtual_key_code,
                            "key": letter,
                            "modifiers": 8 if shift_pressed else 0
                        }

                        # Send keydown event
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))

                        # Simulate key press for the actual character
                        key_event["type"] = "char"
                        key_event["text"] = letter
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))
                        del key_event['text']

                        # Simulate key release
                        key_event["type"] = "keyUp"
                        await driver.execute_cdp_cmd("Input.dispatchKeyEvent", key_event)
                        await asyncio.sleep(uniform(0.05, 0.1))

                        # Release the shift key if it was pressed
                        if shift_pressed:
                            await driver.execute_cdp_cmd("Input.dispatchKeyEvent", {
                                "type": "keyUp",
                                "code": "ShiftLeft",
                                "windowsVirtualKeyCode": 16,
                                "key": "Shift",
                                "modifiers": 0
                            })
                            await asyncio.sleep(uniform(0.05, 0.1))  # Simulate human typing speed

add a class similar to https://github.com/kaliiiiiiiiii/Selenium-Driverless/blob/29204358375fbfcf4025a95a64a128faec6e7f38/src/selenium_driverless/input/keyboard.py

Would be nice to support a custom keyboar layout as well.

Then, add an instance of that class as selenium_driverless.types.target.Target.keyboard

=> add async Target.send_keys => make elem.send_keys first click on it & then Target.send_keys

Notes:

  • Development should be based on dev branch
  • usage of semaphore//lock recommended

But not everyone uses those characters.