# ======================================================================================================================

# Authors: BERKYT, Walgala
# Group: BSBO-08-19

# ======================================================================================================================

# Формат флага: XXX{}
# Формат ссылки: https://task1.tasks.rubikoid.ru/md5(c3e97dd6e97fb5125688c97f36720cbe + secret )+ .php

import hashlib
import random
import re
import easyocr
import threading
import time

from rich.console import Console
from selenium import webdriver
from selenium.webdriver.common.by import By


link = 'https://task1.tasks.rubikoid.ru/c3e97dd6e97fb5125688c97f36720cbe.php'
path_to_browser = r"D:\Downloads\chromedriver.exe"
console = Console()
ignore_words = ['Approved! Use secret to create a link for next step! next Chain step secret is:', 
'gimmie a value whose MD5 ends with this 4 chars from captcha:', 'Cmon Human!', '<img src="captcha.php" alt="Captcha Image">']
im_1_path = 'unknown.png'


class Wait():
    wait_flag = True
    on_switch = True


def switch():
    while True:
        waiting(10)
        Wait.wait_flag = False
        console.print(f'{Wait.wait_flag=}', style='bold underline yellow')


def waiting(wait_time):
    time.sleep(wait_time)


def recognize_text(img_path):
    reader = easyocr.Reader(['en'])
    return reader.readtext(img_path)


def replace_dict(dict_words: dict, line: str) -> str:
    for old_word, new_word in dict_words.items():
        line = line.replace(str(old_word), str(new_word))

    return line


def log(data: str):
    with open('tmp_log.txt', 'a') as log_file:
        log_file.write(str(data) + '\n')


def generate_key(alphabet: str = '0123456789abcdefghijklmnopqrstuvwxyz', length: int = 4) -> str:
    key = ''
 
    for _ in range(length):
        key += random.choice(alphabet)
 
    return key
 
 
def parse_html( responce: str, pattern: str = r'(?<=name="hash" value=").+?(?=")') -> list:
    return re.findall(pattern, responce)
 
 
def get_secret(sourse_code_site: list) -> str:
    for line in sourse_code_site:
        secret = parse_html(line, r'(?<=<br>).+?(?=<br>)')
        secret = str(list(set(secret) - set(ignore_words))[0]).replace('<br>', '')

        if secret:
            console.print(f'Вариант секрета: {secret=}')
            if secret not in ignore_words: 
                return secret

            console.print(f'Неверно!', style='bold underline red')


def create_new_link(secret: str, old_link: str) -> str:
    hash_object = hashlib.md5(old_link.encode('utf-8') + secret.encode('utf-8'))
    new_link = 'https://task1.tasks.rubikoid.ru/' + str(hash_object.hexdigest()) + '.php'

    return new_link


def get_hash(sourse_code_site: list) -> str:
    for line in sourse_code_site:
        hash_capture = parse_html(line)
 
        if hash_capture:
            return hash_capture[0]
    else:
        raise Exception('На сайте нет хеша.')
 
 
def decrypt_md5(target_hash: str) -> str:
    with console.status('[bold green]Waiting...[/bold green]'):
        while True:
            decrypt_hash = generate_key()
            hash_object = hashlib.md5(decrypt_hash.encode('utf-8'))
 
            if hash_object.hexdigest() == target_hash:
                return decrypt_hash
 

# создает хеш с 4 символами который совпадает с 4 последними символами капчи 
def find_target_hash(sub_line: str) -> tuple[str, str]:
    with console.status('[bold green]Waiting...[/bold green]'):
        while Wait.wait_flag:
            target_decrypt_hash = generate_key()
            hash_object = hashlib.md5(target_decrypt_hash.encode('utf-8'))
            hash_str = str(hash_object.hexdigest())
 
            if hash_str[-4:] == sub_line:
                return hash_str, target_decrypt_hash
        else:
            return False
 
 
def get_sourse_code_site() -> list:
    return str(driver.page_source).split(r'\n')
 

def write_secret_in_file(sub_secret: str, name_file: str = 'result_secret.txt') -> None:
    with open(name_file, mode='a') as file_with_secret:
        file_with_secret.write(sub_secret + ' ')


def find_secret_auto():
    while True:
        global driver
        global link
        #  гавно с открытием 
        options = webdriver.ChromeOptions() 
        options.add_argument("--headless")
        driver = webdriver.Chrome(options=options, executable_path=path_to_browser)
        driver.get(link)

        sourse_code_site = get_sourse_code_site()
        hash_capture = get_hash(sourse_code_site)
        decrypt_hash_capture = decrypt_md5(hash_capture)
        new_hash, key = find_target_hash(decrypt_hash_capture)
        
        print(f'{hash_capture=}; {decrypt_hash_capture=}')
        print(f'{new_hash=}; {key=}')
        
        driver.find_element(By.NAME, "ch").send_keys(key)
        driver.find_element(By.NAME, "s").click()

        refreshed_sourse_code_site = get_sourse_code_site()
        secret = get_secret(refreshed_sourse_code_site)
        console.print(f'{secret=}', style='bold underline red')

        log([secret, new_hash, key, decrypt_hash_capture, hash_capture])

        if secret not in ignore_words and secret is not None:
            link = create_new_link(secret=secret, old_link=replace_dict(line=link, dict_words={
                'https://task1.tasks.rubikoid.ru/':'',
                '.php': '',
                }
                ))
            console.print('Секрет найден!', style='bold underline green')
            console.print(f'{secret=}, {link=}', style='bold underline red')
            write_secret_in_file(secret)


def find_secret():
    capture = input('Введите капчу:\n>>>')
    if capture:
        new_hash, key = find_target_hash(capture)
        print(f'{new_hash=}, {key=}')
    

def find_with_easyocr():
    global driver
    global link

    thread = threading.Thread(
        target=switch,
        args=()
    )

    thread.start()

    while True:
        console.print(f'{Wait.wait_flag=}', style='bold underline yellow')
        #  гавно с открытием 
        options = webdriver.ChromeOptions() 
        options.add_argument("--headless")
        driver = webdriver.Chrome(options=options, executable_path=path_to_browser)
        driver.get(link)
        driver.find_element(By.TAG_NAME, "img").screenshot("img.jpg")

        sourse_code_site = get_sourse_code_site()
        hash_capture = get_hash(sourse_code_site)
        decrypt_hash_capture = recognize_text("img.jpg")[0][1]

        print(decrypt_hash_capture)

        Wait.wait_flag = True
        res = find_target_hash(decrypt_hash_capture)
        new_hash, key = res if res else (False, False)

        print(new_hash, key)

        if not res:
            continue
        
        print(f'{hash_capture=}; {decrypt_hash_capture=}')
        print(f'{new_hash=}; {key=}')
        
        driver.find_element(By.NAME, "ch").send_keys(key)
        driver.find_element(By.NAME, "s").click()

        refreshed_sourse_code_site = get_sourse_code_site()
        secret = get_secret(refreshed_sourse_code_site)
        console.print(f'{secret=}', style='bold underline red')

        log([secret, new_hash, key, decrypt_hash_capture, hash_capture])

        if secret not in ignore_words and secret is not None:
            link = create_new_link(secret=secret, old_link=replace_dict(line=link, dict_words={
                'https://task1.tasks.rubikoid.ru/':'',
                '.php': '',
                }
                ))
            console.print('Секрет найден!', style='bold underline green')
            console.print(f'{secret=}, {link=}', style='bold underline red')
            write_secret_in_file(secret, name_file='result_secret_easyocr.txt')


mode = input('Ручной режим - 1; Автоматический режим - 2; Поиск с помощью компьютерного зрения - 3\n>>>')

if mode == '1':
    find_secret()
elif mode == '2':
    find_secret_auto()
elif mode == '3':
    find_with_easyocr()

input('Нажмите любую кнопку, чтобы закрыть...')
