├── .github └── workflows │ ├── extend.yml │ ├── extend_self.yml │ └── main.yml ├── Dockerfile ├── README.md ├── adguard.crx ├── captcha.py ├── globalVal.py ├── main.py ├── requirements.txt └── result.jpeg /.github/workflows/extend.yml: -------------------------------------------------------------------------------- 1 | name: 'HaxExtend' 2 | 3 | on: 4 | push: 5 | schedule: 6 | # run every 3 days on UTC 0/1/2am (8/9/10am CN time) 7 | - cron: '0 1 */2 * *' 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | extend_in_gh: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: install wgcf 17 | run: | 18 | curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg 19 | echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ focal main' | sudo tee /etc/apt/sources.list.d/cloudflare-client.list 20 | sudo apt update 21 | sudo apt install cloudflare-warp -y 22 | warp-cli --accept-tos register 23 | warp-cli --accept-tos connect 24 | - 25 | name: "run" 26 | run: | 27 | docker images 28 | docker run -e USERNAME=${{ secrets.USERNAME }} \ 29 | -e PASSWORD=${{ secrets.PASSWORD }} \ 30 | -e TWOCAPTCHA_TOKEN=${{ secrets.TWOCAPTCHA_TOKEN }} \ 31 | -e HOST=hax.co.id \ 32 | ghcr.io/lyj0309/hax_extend:latest 33 | 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/extend_self.yml: -------------------------------------------------------------------------------- 1 | name: 'HaxExtend' 2 | 3 | on: 4 | # push: 5 | schedule: 6 | # run every 3 days on UTC 0/1/2am (8/9/10am CN time) 7 | - cron: '0 1 */2 * *' 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | extend_hax_in_self: 13 | runs-on: self-hosted 14 | steps: 15 | - name: "run" 16 | run: | 17 | docker pull ghcr.io/lyj0309/hax_extend:latest 18 | docker run -e USERNAME=${{ secrets.USERNAME }} \ 19 | -e PASSWORD=${{ secrets.PASSWORD }} \ 20 | -e TWOCAPTCHA_TOKEN=${{ secrets.TWOCAPTCHA_TOKEN }} \ 21 | -e HOST=hax.co.id \ 22 | ghcr.io/lyj0309/hax_extend:latest 23 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: 'pushDocker' 2 | 3 | on: 4 | # Allows you to run this workflow manually from the Actions tab 5 | workflow_dispatch: 6 | push: 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Set up QEMU 14 | uses: docker/setup-qemu-action@v1 15 | - 16 | name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v1 18 | - 19 | name: Login to GitHub Container Registry 20 | uses: docker/login-action@v2 21 | with: 22 | registry: ghcr.io 23 | username: ${{ github.repository_owner }} 24 | password: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - 27 | name: Build and push 28 | uses: docker/build-push-action@v2 29 | with: 30 | push: true 31 | tags: ghcr.io/lyj0309/hax_extend:latest 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:bullseye 2 | 3 | ARG CHROME_VERSION="101.0.4951.54-1" 4 | # Check available versions here: https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable 5 | RUN apt update && apt install unzip wget -y \ 6 | && wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \ 7 | && apt install -y /tmp/chrome.deb \ 8 | && rm /tmp/chrome.deb \ 9 | && apt clean && rm -rf /var/lib/apt/lists/* 10 | 11 | 12 | WORKDIR /app 13 | 14 | RUN wget --no-verbose https://chromedriver.storage.googleapis.com/101.0.4951.41/chromedriver_linux64.zip \ 15 | && unzip chromedriver_linux64.zip\ 16 | && rm chromedriver_linux64.zip 17 | 18 | ENV PATH="/app:${PATH}" 19 | 20 | 21 | COPY requirements.txt . 22 | RUN pip install --no-cache-dir -r requirements.txt 23 | 24 | COPY . . 25 | ENTRYPOINT ["python3","main.py"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## HaxExtend 2 | ## 简介 3 | Hax和woided的免费vps续费 4 | 支持多种续费方式 5 | ### github action 6 | 使用github的服务器进行续费,方便 7 | 已知问题: 8 | + github服务器大多ip已经被验证码和网站屏蔽,所以大概率不会成功 9 | 10 | ### github action with 自建服务器 11 | 把你自己的服务器添加到github action中,运行,ip大概率不会被ban 12 | [参考教程](https://docs.github.com/cn/actions/hosting-your-own-runners/about-self-hosted-runners) 13 | 14 | ### 自己服务器 + crontab 15 | 将docker镜像pull下来,把命令添加到crontab里面 16 | `docker run -e USERNAME=xxx -e PASSWORD=xxx -e TWOCAPTCHA_TOKEN=xxx -e HOST=hax.co.id -it --rm ghcr.io/lyj0309/hax_extend:latest` 17 | 18 | ### 云函数 19 | 敬请期待 20 | 21 | ## 特性 22 | 使用docker将python,浏览器打包,直接运行即可 23 | 可以自己选择添加`TWOCAPTCHA_TOKEN`,在音频验证码不可用时选择这个验证(收费,一次6分钱) 24 | ![image.png](https://wx1.sinaimg.cn/large/008rgIcAly1h1wi8lbsasj30f80b7ac6.jpg) 25 | 26 | #### 参考 27 | 28 | - https://www.python.org/ 29 | - https://www.selenium.dev/ 30 | - https://www.youtube.com/watch?v=As-_hfZUyIs 31 | - https://github.com/SongLiKod/HaxExtend 32 | - https://github.com/actions/virtual-environments/blob/main/images/macos/macos-12-Readme.md 33 | - https://github.com/win2happy/win2Lee -------------------------------------------------------------------------------- /adguard.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyj0309/HaxExtend/cb01069246588976d674c2b6d5a1880a56ec3585/adguard.crx -------------------------------------------------------------------------------- /captcha.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import time 3 | import os 4 | 5 | from twocaptcha import TwoCaptcha 6 | from selenium.webdriver.common.by import By 7 | from selenium.webdriver.common.keys import Keys 8 | 9 | import globalVal 10 | import main 11 | 12 | delayTime = 2 13 | audioToTextDelay = 10 14 | TWOCAPTCHA_TOKEN = os.environ['TWOCAPTCHA_TOKEN'] 15 | solver = TwoCaptcha(TWOCAPTCHA_TOKEN) 16 | audioFileName = "\\payload.mp3" 17 | 18 | SpeechToTextURL = 'https://speech-to-text-demo.ng.bluemix.net/' 19 | 20 | 21 | def audioToText(audioFile): 22 | globalVal.driver.execute_script('''window.open("","_blank")''') 23 | globalVal.driver.switch_to.window(globalVal.driver.window_handles[1]) 24 | globalVal.driver.get(SpeechToTextURL) 25 | 26 | main.delay() 27 | audioInput = globalVal.driver.find_element(By.XPATH, '//*[@id="root"]/div/input') 28 | print(audioFile) 29 | audioInput.send_keys(audioFile) 30 | 31 | time.sleep(audioToTextDelay) 32 | 33 | text = globalVal.driver.find_element(By.XPATH, '//*[@id="root"]/div/div[7]/div/div/div/span') 34 | while text is None: 35 | text = globalVal.driver.find_element(By.XPATH, '//*[@id="root"]/div/div[7]/div/div/div/span') 36 | 37 | result = text.text 38 | 39 | globalVal.driver.close() 40 | globalVal.driver.switch_to.window(globalVal.driver.window_handles[0]) 41 | 42 | return result 43 | 44 | 45 | def twoCaptcha(g_recaptcha): 46 | # google大概率不会让你用音频,只能用图片 47 | sitekey = g_recaptcha.get_attribute("data-sitekey") 48 | result = solver.recaptcha(sitekey=sitekey, url='https://' + main.origin_host + '/login') 49 | print("recaptcha_res", result) 50 | globalVal.driver.execute_script( 51 | """document.querySelector('[name="g-recaptcha-response"]').innerText='{}'""".format(result['code'])) 52 | 53 | 54 | def reCAPTCHA(): 55 | g_recaptcha = globalVal.driver.find_elements(By.CLASS_NAME, 'g-recaptcha')[0] 56 | outerIframe = g_recaptcha.find_element(By.TAG_NAME, 'iframe') 57 | outerIframe.click() 58 | 59 | iframes = globalVal.driver.find_elements(By.TAG_NAME, 'iframe') 60 | audioBtnFound = False 61 | audioBtnIndex = -1 62 | 63 | for index in range(len(iframes)): 64 | globalVal.driver.switch_to.default_content() 65 | iframe = globalVal.driver.find_elements(By.TAG_NAME, 'iframe')[index] 66 | globalVal.driver.switch_to.frame(iframe) 67 | globalVal.driver.implicitly_wait(delayTime) 68 | try: 69 | audioBtn = globalVal.driver.find_element(By.ID, "recaptcha-audio-button") 70 | audioBtn.click() 71 | audioBtnFound = True 72 | audioBtnIndex = index 73 | break 74 | except Exception as e: 75 | pass 76 | 77 | if audioBtnFound: 78 | try: 79 | while True: 80 | # get the mp3 audio file 81 | src = globalVal.driver.find_element(By.ID, "audio-source").get_attribute("src") 82 | print("[INFO] Audio src: %s" % src) 83 | 84 | # download the mp3 audio file from the source 85 | urllib.request.urlretrieve(src, os.getcwd() + audioFileName) 86 | 87 | # Speech To Text Conversion 88 | key = audioToText(os.getcwd() + audioFileName) 89 | print("[INFO] Recaptcha Key: %s" % key) 90 | 91 | globalVal.driver.switch_to.default_content() 92 | iframe = globalVal.driver.find_elements(By.TAG_NAME, 'iframe')[audioBtnIndex] 93 | globalVal.driver.switch_to.frame(iframe) 94 | 95 | # key in results and submit 96 | inputField = globalVal.driver.find_element(By.ID, "audio-response") 97 | inputField.send_keys(key) 98 | main.delay() 99 | inputField.send_keys(Keys.ENTER) 100 | main.delay() 101 | main.delay() 102 | 103 | err = globalVal.driver.find_elements(By.CLASS_NAME, 'rc-audiochallenge-error-message')[0] 104 | if err.text == "" or err.value_of_css_property('display') == 'none': 105 | print("[INFO] Success!") 106 | break 107 | 108 | except Exception as e: 109 | print(e) 110 | print("[INFO] Possibly blocked by google. Change IP,Use Proxy method for requests") 111 | print("获取语言验证码失败,尝试使用图片fuck reCAPTCHA") 112 | twoCaptcha(g_recaptcha) 113 | # sys.exit("[INFO] Possibly blocked by google. Change IP,Use Proxy method for requests") 114 | else: 115 | # sys.exit("[INFO] Audio Play Button not found! In Very rare cases!") 116 | print('reCAPTCHA not found!') 117 | print('reCAPTCHA done') 118 | 119 | 120 | def numCAPTCHA(): 121 | # 获取 captcha 图片链接 122 | number1 = int( 123 | globalVal.driver.find_element(By.XPATH, '//*[@id="form-submit"]/div[2]/div[1]/img[1]').get_attribute('src').split('-')[1][ 124 | 0]) 125 | caculateMethod = globalVal.driver.find_element(By.XPATH, '//*[@id="form-submit"]/div[2]/div[1]').text[0] 126 | number2 = int( 127 | globalVal.driver.find_element(By.XPATH, '//*[@id="form-submit"]/div[2]/div[1]/img[2]').get_attribute('src').split('-')[1][ 128 | 0]) 129 | print('Method', caculateMethod) 130 | if caculateMethod == '+': 131 | captcha_result = number1 + number2 132 | elif caculateMethod == '-': 133 | captcha_result = number1 - number2 134 | elif caculateMethod == 'X': 135 | captcha_result = number1 * number2 136 | elif caculateMethod == '/': 137 | captcha_result = number1 / number2 138 | return captcha_result 139 | -------------------------------------------------------------------------------- /globalVal.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | 3 | 4 | driver = webdriver.chrome 5 | 6 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | BYPASS reCaptcha By YouTube Channel: NIKO TECH 3 | Captcha + Others By github@Mybdye 2022.03.24 4 | """ 5 | 6 | import os 7 | import sys 8 | import time 9 | import random 10 | import requests 11 | from selenium import webdriver 12 | from selenium.webdriver.common.by import By 13 | from selenium.webdriver.support.ui import WebDriverWait 14 | from selenium.webdriver.support import expected_conditions as EC 15 | 16 | import captcha 17 | import globalVal 18 | 19 | # secret 20 | USERNAME = os.environ['USERNAME'] 21 | PASSWORD = os.environ['PASSWORD'] 22 | origin_host = os.environ['HOST'] 23 | 24 | 25 | # origin_host = 'hax.co.id' 26 | 27 | def delay(): 28 | time.sleep(random.randint(2, 3)) 29 | 30 | 31 | def barkPush(body): 32 | # bark push 33 | # barkUrl = 'https://api.day.app/' + BARKKEY 34 | # title = 'HaxExtend' 35 | # requests.get(url=f'{barkUrl}/{title}/{body}?isArchive=1') 36 | try: 37 | WXURL = os.environ['WXURL'] 38 | data = { 39 | "msgtype": "text", 40 | "text": { 41 | "content": f"{origin_host}:{body}" 42 | } 43 | } 44 | requests.post(WXURL,json=data) 45 | except: 46 | return 47 | 48 | 49 | def run(): 50 | print(origin_host) 51 | globalVal.driver.get('https://' + origin_host + '/login') 52 | # main 53 | time.sleep(10) 54 | print('fill username') 55 | globalVal.driver.find_element(By.XPATH, '//*[@id="text"]').send_keys(USERNAME) 56 | print('fill password') 57 | globalVal.driver.find_element(By.XPATH, '//*[@id="password"]').send_keys(PASSWORD) 58 | delay() 59 | # reCAPTCHA 60 | print('do reCAPTCHA') 61 | captcha.reCAPTCHA() 62 | time.sleep(10) 63 | # login 64 | globalVal.driver.switch_to.default_content() 65 | print('click login') 66 | globalVal.driver.find_element(By.NAME, 'login').click() 67 | time.sleep(10) 68 | # Extend VPS link 69 | print('click Extend VPS') 70 | WebDriverWait(globalVal.driver, 30).until( 71 | EC.visibility_of_element_located((By.LINK_TEXT, 'Extend VPS Expiration'))).click() 72 | time.sleep(10) 73 | # input web address 74 | print('fill web address') 75 | globalVal.driver.find_element(By.XPATH, '//*[@id="web_address"]').send_keys(origin_host) 76 | # captcha 77 | print('do CAPTCHA') 78 | globalVal.driver.find_element(By.XPATH, '//*[@id="captcha"]').send_keys(captcha.numCAPTCHA()) 79 | # agreement check 80 | print('click agreement') 81 | globalVal.driver.find_element(By.NAME, 'agreement').click() 82 | # reCAPTCHA again 83 | # print('do reCAPTCHA') 84 | # reCAPTCHA() 85 | # time.sleep(10) 86 | # globalVal.driver.switch_to.default_content() 87 | # submit_button (Renew VPS) 88 | print('click Renew VPS') 89 | globalVal.driver.find_element(By.NAME, 'submit_button').click() 90 | time.sleep(15) 91 | print('copy text') 92 | body = WebDriverWait(globalVal.driver, 30).until( 93 | EC.visibility_of_element_located((By.XPATH, '//*[@id="response"]/div'))).text 94 | # print('textBody:', body) 95 | delay() 96 | print('bark push',body) 97 | barkPush(body) 98 | delay() 99 | 100 | 101 | if __name__ == '__main__': 102 | try: 103 | # create chrome driver 104 | Options = webdriver.ChromeOptions() 105 | Options.add_argument('--headless') 106 | # Options.add_extension('./adguard.crx') 107 | Options.add_argument('--no-sandbox') 108 | Options.add_argument('--disable-gpu') 109 | Options.add_argument('--disable-dev-shm-usage') 110 | globalVal.driver = webdriver.Chrome(options=Options) 111 | delay() 112 | # go to website which have recaptcha protection 113 | except Exception as e: 114 | print(e) 115 | sys.exit( 116 | "[-] Please update the chromedriver in the webdriver folder according to your chrome version:https://chromedriver.chromium.org/downloads") 117 | run() 118 | globalVal.driver.quit() 119 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.27.1 2 | selenium>=4.1.5 3 | 2captcha-python>=1.1.2 -------------------------------------------------------------------------------- /result.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyj0309/HaxExtend/cb01069246588976d674c2b6d5a1880a56ec3585/result.jpeg --------------------------------------------------------------------------------