├── .circleci ├── auto-dragalia-bot.2019-10-18.private-key.pem ├── comment.py ├── config.yml └── requirements.txt ├── .codeclimate.yml ├── .eslintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── stale.yml ├── .gitignore ├── .mdl-style.rb ├── .mdlrc ├── .prettierrc ├── .vscode ├── .gitignore ├── cspell.json ├── extensions.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── autojs.json ├── commitlint.config.js ├── package-lock.json ├── package.json ├── package.json.d.ts ├── renovate.json ├── scripts ├── build.js ├── generate-images-code.js ├── generate-locale-schema.js ├── postchangelog.js └── push.js ├── src ├── assets │ └── images │ │ ├── 1080x1920 │ │ ├── index.ts │ │ └── rare-item.png │ │ ├── 1080x2160 │ │ ├── auto-battle-switch-off.png │ │ ├── cancel-button.png │ │ ├── close-button.png │ │ ├── clover-button.png │ │ ├── clover-page.png │ │ ├── continue-button-blue.png │ │ ├── continue-button-red.png │ │ ├── enemy-legend.png │ │ ├── give-up-button-blue.png │ │ ├── give-up-button-white.png │ │ ├── index.ts │ │ ├── level-select-beginner.png │ │ ├── level-select-expert.png │ │ ├── level-select-master.png │ │ ├── level-select-standard.png │ │ ├── level-select.png │ │ ├── loading-text.png │ │ ├── lv29-button.png │ │ ├── menu-button.png │ │ ├── next-text.png │ │ ├── ok-button.png │ │ ├── present-button.png │ │ ├── present-price-0.png │ │ ├── present-price-1500.png │ │ ├── present-price-15000.png │ │ ├── present-price-4000.png │ │ ├── present-price-8000.png │ │ ├── rare-item.png │ │ ├── repeat-battle-button.png │ │ ├── repeat-with-stamina-button.png │ │ ├── retry-button-blue.png │ │ ├── retry-button-red.png │ │ ├── start-battle-button.png │ │ ├── support-select-button.png │ │ ├── support-skill-available.png │ │ ├── tap-button.png │ │ ├── transform-gauge-full.png │ │ └── x0.png │ │ ├── 1080x2264-en │ │ ├── auto-battle-switch-off.png │ │ ├── cancel-button.png │ │ ├── close-button.png │ │ ├── continue-button-blue.png │ │ ├── continue-button-red.png │ │ ├── give-up-button-blue.png │ │ ├── give-up-button-white.png │ │ ├── ok-button.png │ │ ├── repeat-battle-button.png │ │ └── repeat-with-stamina-button.png │ │ ├── index.ts │ │ └── type.ts ├── i18n.ts ├── layoutTemplate.xml ├── locales │ ├── en.json │ ├── schema.json │ └── zh.json ├── main.ts ├── runTaskForever.ts ├── setupUI.ts ├── store.ts ├── tasks │ ├── farmRareItem.ts │ ├── feedDragon.ts │ ├── feedFourLeafClover.ts │ ├── index.ts │ └── repeatRaid.ts ├── types │ ├── autojs.d.ts │ ├── define.d.ts │ ├── png-shim.d.ts │ └── xml-shim.d.ts └── utils │ ├── battle.ts │ ├── image │ ├── click.ts │ ├── find.ts │ ├── index.ts │ └── wait.ts │ └── wait.ts ├── tsconfig.json └── webpack.config.js /.circleci/auto-dragalia-bot.2019-10-18.private-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA1Mii/mo494p5EVkLayDtin+k7EM982Cz4M7LYS3R2QCBXsWI 3 | x0Wj/Wz2xLNwz71C/8sCsoqPC3ckyuPBIu7TQudZyIoOoZHZmDbnbeYkNJQnyBM0 4 | +mExwGNSZaNjBvypm0ly2OB9CkJu2qf2yEnsy2uSL6+P0D72AXh81rINZWPV/3AJ 5 | AOjDTwQaQ2mzTW317rp2xk+qQ2Q3YzDHGFs648NAlYgVOhjrt3UkEKWV9ywXy7+O 6 | z++BLYgGOy0P0+1Ig3ntUM9PZKkJdqfxqfbGst3krrWD1pJoaiIzsyIaW0EEjvlW 7 | jop5X2hc5INzlpyY/j/5bM2rbRAENRRjZ7C1uwIDAQABAoIBAQCCN+MgBYulWhXr 8 | Wu4U+orEhoc8m44PInAStRtb9nmE7r09bW/eW60XS5bdG99/EpktB2U4NHmED77x 9 | d15b7YJWYFTe2DZKBIMAlyEcbYpcrwqFc9OpItAap+3mC45v2TJGuTVSqld7btp+ 10 | b+oj+rGyb7t/+VXuoIlWrxDfJwnxsICB5LdS+s/tSKt/lPO0BSj4ncUrOsNoTYcH 11 | RaEV5LkrRfnkzfB1vmZDqVrar8x9e7176s6hmBLy9DUo77+JUQf3sw4csGaWrwdl 12 | WCb9bOO5y4QVgy/QC2ByKtsb6kYrvm6keMJI1FNl0HyBA3C2K2DV46Qjfx+jIj4D 13 | 4YrgPyVpAoGBAPLsNYr9WlzdO4rn10tiwvNcA1A/a7dxfmponfJlrHs57H0ufy08 14 | s+Y5GsmGxq6szCWb6MjFkSkCw7h+bib0LgwE81tc8AETDD/DoswWQ96WDc5eWquB 15 | Ipi5P7vvGDBn9azQpWzVjUABrJJ68MhEJx5SLhqE/Y5TsN+43WRK5b/fAoGBAOA9 16 | EtIrF1pSmvy/Jif0G/ZdLvjern5royEBQjf2P0H6Lgx5wck0/aS/Rq9Aop87XFVh 17 | NYMJT5PFSJ74zAEg84yIDiNm6JJ5osZJ1ri87Uv2cYtGQV5kkcjTahJCcztJsjUf 18 | gvtzXD0me9NJsMkevm37LuGFiizTMtMtwJnn+VWlAoGAfXGzOKIabsgj/lg42ooW 19 | qDtEzsThaCqooGSD4+/TQRMpZ3+CVyb4s5ObnKQHp54+EdHQRRWZH3d5AKXxVhZU 20 | TQqlbPGyUqu2cJsjT4bT53bPjFk3M0eNSRaorfELKbwVpvdb5bblqT70EfSAsLpZ 21 | BRRlEHbKxRu8w9sgILF4sTsCgYEAuHWlANxPto5TTmyTn7QwOGaJGFJeKkKzC5h0 22 | d4KKoOSDcuHJ5pE/wYFTTSEXZXM4TkfL9EH5gy57wcIaVydbEZRuRPMHiE6k9waQ 23 | qqFHdwCAgLhHV7XVMuWu0Nx23COQhCCq59wK2YbJv5JT6e3vapuBAiWO97a4jX2K 24 | fVCVC70CgYEA3aT7QreffPz7uyYt3e3h1JtL7tmRm0SbPGMoePNyjeJadxVMwTAH 25 | cYNX38RbNFXJKyt+gqLVv2oOEoBw58IrXHyDfLKh0hwjXDQ7YHwv9c9888HvIzEC 26 | Z7vVQwqa+E2lnGBQrBAiSYv9vyxKj6NqRXWCgwVhydj3zLoA7Vf08fo= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /.circleci/comment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | "ci comment" 3 | 4 | import datetime as dt 5 | import os 6 | 7 | import jwt 8 | import requests 9 | 10 | __dirname__ = os.path.abspath(os.path.join(__file__, '..')) 11 | 12 | 13 | def create_app_token(private_key: str, iss: int) -> str: 14 | """Create github app jwt 15 | 16 | Args: 17 | private_key (str): rsa private key 18 | 19 | Returns: 20 | bytes: jwt 21 | """ 22 | now = int(dt.datetime.now().timestamp()) 23 | 24 | return jwt.encode({ 25 | "iat": now, 26 | "exp": now + 10 * 60, 27 | "iss": iss 28 | }, private_key, algorithm='RS256').decode('utf-8') 29 | 30 | 31 | def read_private_key(filename: str) -> str: 32 | """Read private key from file 33 | 34 | Args: 35 | filename (str, optional): File path relative to this file. 36 | 37 | Returns: 38 | str: file content 39 | """ 40 | with open(os.path.abspath(os.path.join(__dirname__, filename))) as f: 41 | return f.read() 42 | 43 | 44 | def _app_endpoint_headers(app_token: str) -> dict: 45 | return { 46 | "Authorization": f'Bearer {app_token}', 47 | "Accept": "application/vnd.github.machine-man-preview+json" 48 | } 49 | 50 | 51 | def _installation_endpoint_headers(installation_token: str) -> dict: 52 | return { 53 | "Authorization": f'token {installation_token}', 54 | "Accept": "application/vnd.github.machine-man-preview+json" 55 | } 56 | 57 | 58 | def get_app(app_token: str) -> dict: 59 | "https://developer.github.com/v3/apps/#get-the-authenticated-github-app" 60 | 61 | return requests.get( 62 | 'https://api.github.com/app', 63 | headers=_app_endpoint_headers(app_token)).json() 64 | 65 | 66 | def create_installation_token(app_token: str, installation_id: int) -> dict: 67 | "https://developer.github.com/v3/apps/#create-a-new-installation-token" 68 | 69 | return requests.post( 70 | f'https://api.github.com/app/installations/{installation_id}/access_tokens', 71 | headers=_app_endpoint_headers(app_token)).json() 72 | 73 | 74 | def get_repo_installation( 75 | app_token: str, 76 | owner: str = 'NateScarlet', 77 | repo: str = 'auto-dragalia') -> dict: 78 | "https://developer.github.com/v3/apps/#get-a-repository-installation" 79 | 80 | return requests.get( 81 | f'https://api.github.com/repos/{owner}/{repo}/installation', 82 | headers=_app_endpoint_headers(app_token)).json() 83 | 84 | 85 | def get_user_installation(app_token: str, username: str = 'NateScarlet') -> dict: 86 | "https://developer.github.com/v3/apps/#get-a-user-installation" 87 | 88 | return requests.get( 89 | f'https://api.github.com/users/{username}/installation', 90 | headers=_app_endpoint_headers(app_token)).json() 91 | 92 | 93 | def create_comment( 94 | installation_token: str, 95 | owner: str, 96 | repo: str, 97 | commit_sha: str, 98 | body: str) -> dict: 99 | "https://developer.github.com/v3/repos/comments/#create-a-commit-comment" 100 | 101 | return requests.post( 102 | f'https://api.github.com/repos/{owner}/{repo}/commits/{commit_sha}/comments', 103 | json={"body": body}, 104 | headers=_installation_endpoint_headers(installation_token) 105 | ).json() 106 | 107 | 108 | def main() -> None: 109 | commit_sha = os.environ['CIRCLE_SHA1'] 110 | filename = f"auto-dragalia-{os.getenv('CIRCLE_TAG') or commit_sha[:8]}.zip" 111 | body = (f"自动构建 [{filename}]({os.getenv('CIRCLE_BUILD_URL')}" 112 | f"/artifacts/0{os.getcwd()}/artifacts/{filename})") 113 | 114 | private_key = read_private_key( 115 | 'auto-dragalia-bot.2019-10-18.private-key.pem') 116 | app_token = create_app_token(private_key, 42355) 117 | installation_token = create_installation_token(app_token, 2278043)['token'] 118 | result = create_comment( 119 | installation_token, 120 | owner=os.getenv( 121 | "CIRCLE_PROJECT_USERNAME", "NateScarlet"), 122 | repo=os.getenv( 123 | "CIRCLE_PROJECT_REPONAME", "auto-dragalia"), 124 | commit_sha=commit_sha, 125 | body=body) 126 | print(result) 127 | 128 | 129 | if __name__ == '__main__': 130 | main() 131 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | executors: 3 | node: 4 | docker: 5 | - image: circleci/node:lts 6 | golang: 7 | docker: 8 | - image: circleci/golang:1.17 9 | python: 10 | docker: 11 | - image: circleci/python:3.10 12 | commands: 13 | install_hub: 14 | description: 'Install hub CLI' 15 | steps: 16 | - run: 17 | name: 'Install hub CLI' 18 | command: go get github.com/github/hub && hub --version 19 | jobs: 20 | build: 21 | executor: node 22 | steps: 23 | - checkout 24 | - restore_cache: 25 | key: dependency-cache-{{ checksum "package.json" }} 26 | - run: 27 | name: 'Install dependencies' 28 | command: npm install 29 | - save_cache: 30 | key: dependency-cache-{{ checksum "package.json" }} 31 | paths: 32 | - ./node_modules 33 | - run: 34 | name: Code generation 35 | command: npm run code-generate:images 36 | - run: 37 | name: Build 38 | command: npm run build 39 | - run: 40 | name: Archive 41 | command: mkdir artifacts && python -m zipfile -c artifacts/auto-dragalia-${CIRCLE_TAG:-${CIRCLE_SHA1:0:8}}.zip dist/* 42 | - store_artifacts: 43 | path: artifacts 44 | - persist_to_workspace: 45 | root: artifacts 46 | paths: 47 | - '*.zip' 48 | - run: 49 | name: 'Record CIRCLE_BUILD_URL' 50 | command: echo ${CIRCLE_BUILD_URL} > /tmp/CIRCLE_BUILD_URL 51 | - persist_to_workspace: 52 | root: /tmp 53 | paths: 54 | - 'CIRCLE_BUILD_URL' 55 | github-comment: 56 | executor: python 57 | steps: 58 | - checkout 59 | - run: 60 | name: 'Install dependencies' 61 | command: 'pip install --user -r .circleci/requirements.txt' 62 | - attach_workspace: 63 | at: /tmp/workspace 64 | - run: 65 | name: 'Add github comment' 66 | command: export CIRCLE_BUILD_URL=`cat /tmp/workspace/CIRCLE_BUILD_URL`; .circleci/comment.py 67 | github-release: 68 | executor: golang 69 | steps: 70 | - install_hub 71 | - checkout 72 | - attach_workspace: 73 | at: /tmp/workspace 74 | - run: 75 | name: 'Release' 76 | command: hub release create -d -m "${CIRCLE_TAG}" -a "/tmp/workspace/auto-dragalia-${CIRCLE_TAG}.zip" ${CIRCLE_TAG} 77 | workflows: 78 | version: 2 79 | main: 80 | jobs: 81 | - build: 82 | filters: 83 | tags: 84 | only: /.*/ 85 | - github-comment: 86 | requires: 87 | - build 88 | - github-release: 89 | requires: 90 | - build 91 | filters: 92 | tags: 93 | only: /^v.+/ 94 | branches: 95 | ignore: /.*/ 96 | -------------------------------------------------------------------------------- /.circleci/requirements.txt: -------------------------------------------------------------------------------- 1 | pyjwt==1.7.1 2 | requests==2.27.1 3 | cryptography==2.9.2 4 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | plugins: 3 | markdownlint: 4 | enabled: true 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended" 8 | ], 9 | "rules": { 10 | "@typescript-eslint/no-var-requires": "off" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /src/tasks/feadFourLeafClover.ts @youmei 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 报告 3 | about: 创建报告以帮助我们改进 4 | title: '' 5 | labels: bug 6 | assignees: NateScarlet 7 | --- 8 | 9 | ## 描述 10 | 11 | 对 bug 的清晰简洁描述。 12 | 13 | ## 重现 14 | 15 | 重现行为的步骤: 16 | 17 | 1. 去…… 18 | 2. 点击…… 19 | 3. 向下滚动到…… 20 | 4. 错误发生 21 | 22 | ## 预期行为 23 | 24 | 清楚简洁地描述您期望发生的事情。 25 | 26 | ## 截图 27 | 28 | 如果适用,请添加屏幕截图以帮助解释您的问题。 29 | 30 | ## 日志 31 | 32 | 在 Auto.js 主界面右上角可以查看日志, 请附日志截图 33 | 34 | ## 设备信息 35 | 36 | - 型号:[例如 华为 mate20] 37 | - 屏幕分辨率: [例如 1080x2160] 38 | - 安卓版本:[例如 8.1.0] 39 | - Auto.js 版本: [例如 4.0.5 Alpha] 40 | - auto-dragalia 版本: [例如 0.11.0] 41 | 42 | ## 附加背景 43 | 44 | 在此添加有关此问题的任何其他上下文。 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能请求 3 | about: 为这个项目提供想法和建议 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## 您的功能请求是否与问题有关? 请描述一下。 10 | 11 | 简明扼要地描述了问题所在。 12 | 13 | ## 描述你想要的解决方案 14 | 15 | 简明扼要地描述您想要发生的事情。 16 | 17 | ## 描述您考虑过的替代方案 18 | 19 | 对您考虑的任何替代解决方案或功能的简明扼要描述。 20 | 21 | ## 附加背景 22 | 23 | 在此处添加有关功能请求的任何其他上下文或屏幕截图。 24 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | 此问题已自动标记为陈旧,因为它最近没有活动。 14 | 如果没有进一步的活动,它将被关闭。 感谢您的贡献。 15 | # Comment to post when closing a stale issue. Set to `false` to disable 16 | closeComment: false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/ 3 | /yarn.lock 4 | yarn-error.log 5 | /captures 6 | -------------------------------------------------------------------------------- /.mdl-style.rb: -------------------------------------------------------------------------------- 1 | all 2 | exclude_rule "MD002" 3 | exclude_rule "MD013" 4 | exclude_rule "MD024" 5 | rule "MD029", :style => "ordered" 6 | exclude_rule "MD041" 7 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | style ".mdl-style.rb" 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !cspell.json 4 | !extensions.json 5 | -------------------------------------------------------------------------------- /.vscode/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1", 3 | "language": "en", 4 | "words": ["autojs", "automerge", "dragalia", "polyfill", "Sortby", "zaga"], 5 | "flagWords": [] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "streetsidesoftware.code-spell-checker", 8 | "esbenp.prettier-vscode", 9 | "dbaeumer.vscode-eslint" 10 | ], 11 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 12 | "unwantedRecommendations": [] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | }, 13 | "problemMatcher": ["$eslint-compact"] 14 | }, 15 | { 16 | "type": "npm", 17 | "script": "watch", 18 | "problemMatcher": ["$eslint-compact"], 19 | "isBackground": true, 20 | "runOptions": { 21 | "runOn": "folderOpen" 22 | } 23 | }, 24 | { 25 | "type": "npm", 26 | "script": "capture", 27 | "problemMatcher": [] 28 | }, 29 | { 30 | "type": "npm", 31 | "script": "code-generate:images", 32 | "problemMatcher": [] 33 | }, 34 | { 35 | "type": "npm", 36 | "script": "code-generate:locale", 37 | "problemMatcher": [] 38 | }, 39 | { 40 | "type": "npm", 41 | "script": "adb:push", 42 | "problemMatcher": [] 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.13.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.13.0...v0.13.1) (2020-01-15) 6 | 7 | ### Features 8 | 9 | - support i18n ([7811740](https://github.com/NateScarlet/auto-dragalia/commit/781174085c6535dc0437d2ffc2792f2c4cc1a73e)), closes [#181](https://github.com/NateScarlet/auto-dragalia/issues/181) 10 | 11 | ## [0.13.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.12.0...v0.13.0) (2019-10-26) 12 | 13 | ### Bug Fixes 14 | 15 | - handle too few items in `chainImageClicks` ([6b6e501](https://github.com/NateScarlet/auto-dragalia/commit/6b6e501)) 16 | 17 | ### Features 18 | 19 | - **farm-rare-item:** change matching mechanism ([#127](https://github.com/NateScarlet/auto-dragalia/issues/127)) ([b73645e](https://github.com/NateScarlet/auto-dragalia/commit/b73645e)), closes [#126](https://github.com/NateScarlet/auto-dragalia/issues/126) 20 | 21 | ### BREAKING CHANGES 22 | 23 | - **farm-rare-item:** removed `no-rare-item` assets 24 | 25 | ## [0.12.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.11.0...v0.12.0) (2019-04-20) 26 | 27 | ### Bug Fixes 28 | 29 | - **farm-rare-item:** add more rare item images ([510079c](https://github.com/NateScarlet/auto-dragalia/commit/510079c)), closes [#23](https://github.com/NateScarlet/auto-dragalia/issues/23) 30 | 31 | ### Features 32 | 33 | - remove `IWaitImageOptions.retry` ([16ed02b](https://github.com/NateScarlet/auto-dragalia/commit/16ed02b)) 34 | - support load asset by resolution ([2f5409e](https://github.com/NateScarlet/auto-dragalia/commit/2f5409e)), closes [#26](https://github.com/NateScarlet/auto-dragalia/issues/26) 35 | 36 | ### Performance Improvements 37 | 38 | - only match images for current resolution ([991a3d6](https://github.com/NateScarlet/auto-dragalia/commit/991a3d6)) 39 | 40 | ## [0.11.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.10.1...v0.11.0) (2019-04-11) 41 | 42 | ### Bug Fixes 43 | 44 | - **farm-rare-item:** add back level select detect ([0c427bc](https://github.com/NateScarlet/auto-dragalia/commit/0c427bc)) 45 | - **feed-dragon,feed-four-leaf-clover:** improve dialog handling ([f130501](https://github.com/NateScarlet/auto-dragalia/commit/f130501)), closes [#21](https://github.com/NateScarlet/auto-dragalia/issues/21) 46 | 47 | ### Features 48 | 49 | - add `IWaitImageOptions.retry` ([3a9b1ce](https://github.com/NateScarlet/auto-dragalia/commit/3a9b1ce)) 50 | - modify arguments of waitImage, waitAnyImage ([0242695](https://github.com/NateScarlet/auto-dragalia/commit/0242695)) 51 | - remove `waitLoading` ([1319917](https://github.com/NateScarlet/auto-dragalia/commit/1319917)) 52 | - **feedFourLeafClover:** new task ([c3ffde5](https://github.com/NateScarlet/auto-dragalia/commit/c3ffde5)), closes [#20](https://github.com/NateScarlet/auto-dragalia/issues/20) 53 | 54 | ### Performance Improvements 55 | 56 | - remove screen cache since it has no effect ([41ce5c9](https://github.com/NateScarlet/auto-dragalia/commit/41ce5c9)) 57 | 58 | ## [0.10.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.10.0...v0.10.1) (2019-04-06) 59 | 60 | ### Bug Fixes 61 | 62 | - **farm-rare-item:** increase waiting timeout ([bc6a789](https://github.com/NateScarlet/auto-dragalia/commit/bc6a789)) 63 | 64 | ## [0.10.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.9.2...v0.10.0) (2019-04-06) 65 | 66 | Added image ids for logging. 67 | 68 | ### Bug Fixes 69 | 70 | - **farm-rare-item:** invalid click during animation ([1d1fc6b](https://github.com/NateScarlet/auto-dragalia/commit/1d1fc6b)), closes [#14](https://github.com/NateScarlet/auto-dragalia/issues/14) 71 | 72 | ### Features 73 | 74 | - add `IFindImageOptions` ([601d0cb](https://github.com/NateScarlet/auto-dragalia/commit/601d0cb)) 75 | - add `IWaitImageOptions.id` ([078a2bb](https://github.com/NateScarlet/auto-dragalia/commit/078a2bb)) 76 | - add version to error message ([6ba53c2](https://github.com/NateScarlet/auto-dragalia/commit/6ba53c2)) 77 | - **farm-rare-item:** add menu animation waiting ([b907394](https://github.com/NateScarlet/auto-dragalia/commit/b907394)) 78 | - **farm-rare-item:** set finding ids ([3711f65](https://github.com/NateScarlet/auto-dragalia/commit/3711f65)) 79 | - **farm-rare-item:** set waiting ids ([c6217bb](https://github.com/NateScarlet/auto-dragalia/commit/c6217bb)) 80 | 81 | ## [0.9.2](https://github.com/NateScarlet/auto-dragalia/compare/v0.9.1...v0.9.2) (2019-04-06) 82 | 83 | ### Bug Fixes 84 | 85 | - **farm-rare-item:** add more rare item images ([2cceec5](https://github.com/NateScarlet/auto-dragalia/commit/2cceec5)), closes [#12](https://github.com/NateScarlet/auto-dragalia/issues/12) 86 | - **farm-rare-item:** menu click may be blocked by skill casting ([0df2710](https://github.com/NateScarlet/auto-dragalia/commit/0df2710)) 87 | 88 | ## [0.9.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.9.0...v0.9.1) (2019-04-06) 89 | 90 | ### Bug Fixes 91 | 92 | - **repeat-raid:** fix may click continue button ([69ee5e7](https://github.com/NateScarlet/auto-dragalia/commit/69ee5e7)) 93 | - remove toast that may block skill cast ([bb5201d](https://github.com/NateScarlet/auto-dragalia/commit/bb5201d)) 94 | 95 | ## [0.9.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.8.1...v0.9.0) (2019-04-05) 96 | 97 | ### Features 98 | 99 | - vibrate when task occurs error ([d582d57](https://github.com/NateScarlet/auto-dragalia/commit/d582d57)) 100 | - **farm-rare-item:** add waiting timeout ([87141b9](https://github.com/NateScarlet/auto-dragalia/commit/87141b9)) 101 | 102 | ## [0.8.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.8.0...v0.8.1) (2019-04-05) 103 | 104 | ### Bug Fixes 105 | 106 | - **farm-rare-item:** support low graphics quality ([0490f04](https://github.com/NateScarlet/auto-dragalia/commit/0490f04)), closes [#10](https://github.com/NateScarlet/auto-dragalia/issues/10) 107 | 108 | ### Performance Improvements 109 | 110 | - improve waiting delay logic ([9150bd5](https://github.com/NateScarlet/auto-dragalia/commit/9150bd5)) 111 | - use cache with screen capture ([7028068](https://github.com/NateScarlet/auto-dragalia/commit/7028068)), closes [#11](https://github.com/NateScarlet/auto-dragalia/issues/11) 112 | 113 | ## [0.8.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.7.2...v0.8.0) (2019-04-05) 114 | 115 | refactor with async functions 116 | 117 | ### Features 118 | 119 | - add `IWaitImageOptions.findOptions` ([9387aad](https://github.com/NateScarlet/auto-dragalia/commit/9387aad)) 120 | - auto retry when waiting any image ([94684b7](https://github.com/NateScarlet/auto-dragalia/commit/94684b7)), closes [#9](https://github.com/NateScarlet/auto-dragalia/issues/9) 121 | 122 | ### Performance Improvements 123 | 124 | - **feed-dragon:** improve finished detect ([a7a0fcf](https://github.com/NateScarlet/auto-dragalia/commit/a7a0fcf)) 125 | 126 | ## [0.7.2](https://github.com/NateScarlet/auto-dragalia/compare/v0.7.1...v0.7.2) (2019-04-05) 127 | 128 | ### Bug Fixes 129 | 130 | - support blue retry button ([1c77da2](https://github.com/NateScarlet/auto-dragalia/commit/1c77da2)), closes [#8](https://github.com/NateScarlet/auto-dragalia/issues/8) 131 | 132 | ## [0.7.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.7.0...v0.7.1) (2019-04-05) 133 | 134 | ### Bug Fixes 135 | 136 | - **farm-rare-item:** correct level priority ([db64fc2](https://github.com/NateScarlet/auto-dragalia/commit/db64fc2)), closes [#7](https://github.com/NateScarlet/auto-dragalia/issues/7) 137 | 138 | ## [0.7.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.6.1...v0.7.0) (2019-04-04) 139 | 140 | ### Bug Fixes 141 | 142 | - **farm-rare-item:** click blue continue button ([d2ea378](https://github.com/NateScarlet/auto-dragalia/commit/d2ea378)), closes [#5](https://github.com/NateScarlet/auto-dragalia/issues/5) 143 | - **farm-rare-item:** use other level select button image ([ab71c16](https://github.com/NateScarlet/auto-dragalia/commit/ab71c16)), closes [#4](https://github.com/NateScarlet/auto-dragalia/issues/4) 144 | 145 | ### Features 146 | 147 | - show console when error occurs during task ([44b0eb3](https://github.com/NateScarlet/auto-dragalia/commit/44b0eb3)) 148 | - **farm-rare-item:** reduce menu click delay ([23c7c3b](https://github.com/NateScarlet/auto-dragalia/commit/23c7c3b)) 149 | 150 | ## [0.6.1](https://github.com/NateScarlet/auto-dragalia/compare/v0.6.0...v0.6.1) (2019-04-03) 151 | 152 | ### Bug Fixes 153 | 154 | - auto retry connect to server ([7320c2b](https://github.com/NateScarlet/auto-dragalia/commit/7320c2b)) 155 | - improve rare item detect accuracy ([10d4d41](https://github.com/NateScarlet/auto-dragalia/commit/10d4d41)) 156 | 157 | ## [0.6.0](https://github.com/NateScarlet/auto-dragalia/compare/v0.5.9...v0.6.0) (2019-04-03) 158 | 159 | ### Features 160 | 161 | - new task `farmRareItem` ([df8dcd9](https://github.com/NateScarlet/auto-dragalia/commit/df8dcd9)), closes [#1](https://github.com/NateScarlet/auto-dragalia/issues/1) 162 | 163 | ## [0.5.9](https://github.com/NateScarlet/auto-dragalia/compare/0.5.8...0.5.9) (2019-04-03) 164 | 165 | ### Bug Fixes 166 | 167 | - remove task name toast message ([1c0f2ee](https://github.com/NateScarlet/auto-dragalia/commit/1c0f2ee)), closes [#2](https://github.com/NateScarlet/auto-dragalia/issues/2) 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 NateScarlet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # auto-dragalia 2 | 3 | [![Build Status](https://img.shields.io/circleci/project/github/NateScarlet/auto-dragalia.svg)](https://circleci.com/gh/NateScarlet/auto-dragalia) 4 | [![Release](https://img.shields.io/github/release/NateScarlet/auto-dragalia.svg)](https://github.com/NateScarlet/auto-dragalia/releases/latest) 5 | [![Auto.js 4.x](https://img.shields.io/badge/Auto.js-4.x-009688.svg)](https://github.com/hyb1996/Auto.js) 6 | ![Android 7.x](https://img.shields.io/badge/Android-7+-a4c639.svg?logo=android) 7 | [![Maintainability](https://api.codeclimate.com/v1/badges/619eae52db0e72683d02/maintainability)](https://codeclimate.com/github/NateScarlet/auto-dragalia/maintainability) 8 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/NateScarlet/auto-dragalia.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/NateScarlet/auto-dragalia/alerts/) 9 | [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/NateScarlet/auto-dragalia.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/NateScarlet/auto-dragalia/context:javascript) 10 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 11 | [![Gitter](https://badges.gitter.im/auto-dragalia/community.svg)](https://gitter.im/auto-dragalia/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 12 | 13 | 《失落的龙约》 [Auto.js] 辅助脚本 14 | 15 | 感谢 @pansx 提供灵感 16 | 17 | [发布页面](https://github.com/NateScarlet/auto-dragalia/releases) 18 | 19 | ## 需求 20 | 21 | - [Auto.js 4.x](https://github.com/hyb1996/Auto.js/releases) 当前应用商店没有发布 Auto.js 4.x 版本 需要前往官方仓库发布页面下载 22 | - 安卓 7.0 版本以上不需要 Root 权限 23 | 24 | 2019-09-07: 由于 [Auto.js] 作者删除了官方发布页面,提供 4.0.5-alpha 版本磁力链接: `magnet:?xt=urn:btih:982942ff53262eb60b6a1367a947750f14498b3b&dn=autojs-4.0.5.alpha.apk&tr=udp%3a%2f%2f195.154.52.99%3a80%2fannounce&tr=http%3a%2f%2f176.113.71.19%3a6961%2fannounce&tr=udp%3a%2f%2f208.83.20.20%3a6969%2fannounce&tr=udp%3a%2f%2f54.37.235.149%3a6969%2fannounce&tr=udp%3a%2f%2f37.235.174.46%3a2710%2fannounce&tr=udp%3a%2f%2f5.206.19.247%3a6969%2fannounce&tr=udp%3a%2f%2f95.211.168.204%3a2710%2fannounce&tr=udp%3a%2f%2f89.234.156.205%3a451%2fannounce&tr=udp%3a%2f%2f184.105.151.164%3a6969%2fannounce&tr=udp%3a%2f%2f93.158.213.92%3a1337%2fannounce&tr=udp%3a%2f%2f185.19.107.254%3a80%2fannounce&tr=udp%3a%2f%2f142.44.243.4%3a1337%2fannounce&tr=udp%3a%2f%2f159.100.245.181%3a6969%2fannounce` 25 | 26 | ## 功能 27 | 28 | - [x] i18n 29 | 30 | - 中文 31 | 32 | - English 33 | 34 | - [x] 重复战斗 35 | 36 | 重复单人战斗 37 | 38 | - [x] 自动打开自动战斗开关 39 | 40 | - [x] 自动龙化 41 | 42 | - [x] 只在周围有敌人的时候龙化 43 | 44 | - [x] 自动使用龙技能 45 | 46 | - [x] 自动使用支援技能 47 | 48 | - [x] 自动喂龙 49 | 50 | 龙之庭院自动送金币礼物 51 | 52 | - [x] 喂四叶草 53 | 54 | 龙之庭院自动送幸运四叶草 55 | 56 | - [x] 到 29 级自动停止 57 | 58 | - [x] 刷稀有 59 | 60 | 金币任务刷幸运四叶草 61 | 62 | - [x] 使用可见任务中目标全部完成并且最高难度的任务 63 | 64 | - [x] 载入第二张图后如果没刷到稀有物品直接放弃任务 65 | 66 | - [ ] 重复多人战斗 67 | 68 | [auto.js]: https://github.com/hyb1996/Auto.js 69 | 70 | ## 屏幕分辨率支持 71 | 72 | 基础适配分辨率为 1080x2160 73 | 74 | 其他适配过的分辨率见 [src/assets/images](./src/assets/images) 75 | 76 | 脚本功能利用识图实现, 所以相近分辨率的都应该可以用 77 | 78 | 欢迎发 PR 添加更多分辨率 79 | 80 | ## 关于刷稀有出错 81 | 82 | ### 结算界面停留超时 83 | 84 | 打太快 脚本来不及点菜单 85 | 86 | 降倍速或者降战力解决 87 | 88 | 参见 [#13](https://github.com/NateScarlet/auto-dragalia/issues/13) 89 | -------------------------------------------------------------------------------- /autojs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-dragalia", 3 | "$schema": "./node_modules/autojs-dev/template/schema.json", 4 | "project": { 5 | "name": "" 6 | }, 7 | "generate": { 8 | "language": "typescript", 9 | "dir": "./src" 10 | }, 11 | "screencap": { 12 | "dir": "./captures" 13 | }, 14 | "images": { 15 | "dir": "./images", 16 | "port": 3400 17 | }, 18 | "compile": { 19 | "src": "./src", 20 | "output": "./output" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rule: { 4 | 'scope-enum': [ 5 | 'farm-rare-item', 6 | 'feed-dragon', 7 | 'repeat-raid', 8 | 'feed-four-leaf-clover', 9 | 'deps' 10 | ] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-dragalia", 3 | "version": "0.13.1", 4 | "private": true, 5 | "main": "src/main.ts", 6 | "repository": "https://github.com/NateScarlet/auto-dragalia.git", 7 | "author": "NateScarlet ", 8 | "license": "MIT", 9 | "scripts": { 10 | "build": "node ./scripts/build.js", 11 | "watch": "webpack --watch", 12 | "capture": "autojs cap", 13 | "lint": "eslint --ext .js,.ts src", 14 | "code-generate:images": "node ./scripts/generate-images-code.js", 15 | "code-generate:locale": "node ./scripts/generate-locale-schema.js", 16 | "adb:push": "node ./scripts/push.js", 17 | "release": "standard-version" 18 | }, 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "@commitlint/cli": "8.3.6", 22 | "@commitlint/config-conventional": "8.3.6", 23 | "@types/webpack-env": "1.16.4", 24 | "@typescript-eslint/eslint-plugin": "2.34.0", 25 | "@typescript-eslint/parser": "2.34.0", 26 | "ajv": "6.12.6", 27 | "autojs-dev": "0.7.1", 28 | "eslint": "6.8.0", 29 | "husky": "4.3.8", 30 | "lint-staged": "9.5.0", 31 | "prettier": "1.19.1", 32 | "raw-loader": "4.0.2", 33 | "standard-version": "7.1.0", 34 | "ts-loader": "6.2.2", 35 | "typescript": "3.9.10", 36 | "webpack": "4.46.0", 37 | "webpack-cli": "3.3.12" 38 | }, 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "lint-staged", 42 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 43 | } 44 | }, 45 | "lint-staged": { 46 | "*.{md,ts,js,yml,xml,json}": [ 47 | "prettier --write", 48 | "git add" 49 | ] 50 | }, 51 | "standard-version": { 52 | "scripts": { 53 | "postchangelog": "node ./scripts/postchangelog.js" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /package.json.d.ts: -------------------------------------------------------------------------------- 1 | export const name: string; 2 | export const version: string; 3 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:js-app", ":automergeMinor", ":pinSkipCi"] 3 | } 4 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const ajv = require('ajv'); 5 | 6 | function listDir(pathname) { 7 | return fs.readdirSync(pathname).map(i => path.join(pathname, i)); 8 | } 9 | 10 | function buildAllAssets(locale) { 11 | for (const i of listDir('src/assets/images')) { 12 | if (!fs.statSync(i).isDirectory()) { 13 | continue; 14 | } 15 | execSync(`npx webpack -p`, { 16 | env: { 17 | ...process.env, 18 | TARGET_ASSET: path.basename(i), 19 | TARGET_LOCALE: locale 20 | }, 21 | stdio: 'inherit' 22 | }); 23 | } 24 | } 25 | 26 | (() => { 27 | const validate = new ajv().compile(require('../src/locales/schema.json')); 28 | const locales = []; 29 | for (const i of listDir('src/locales').filter(i => i.endsWith('.json'))) { 30 | if (i.endsWith('schema.json')) { 31 | continue; 32 | } 33 | const locale = path.basename(i, '.json'); 34 | if (!validate(require(`../src/locales/${locale}.json`))) { 35 | console.log({ 36 | msg: 'Invalid locale data', 37 | locale, 38 | errors: validate.errors 39 | }); 40 | process.exit(1); 41 | } 42 | locales.push(locale); 43 | } 44 | for (const locale of locales) { 45 | buildAllAssets(locale); 46 | } 47 | })(); 48 | -------------------------------------------------------------------------------- /scripts/generate-images-code.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | async function prettier(code) { 5 | const { format, resolveConfig } = require('prettier'); 6 | const options = await resolveConfig(); 7 | return format(code, { ...options, parser: 'typescript' }); 8 | } 9 | 10 | /** @param {string} str */ 11 | function toCamelCase(str) { 12 | return str.replace(/[-\.](.)/g, match => match[1].toUpperCase()); 13 | } 14 | const imageNames = new Set(); 15 | const commonHeader = [ 16 | '// This file is auto generated', 17 | '// Use `npm run code-generate:images` to update this file' 18 | ]; 19 | 20 | async function generateImageIndex(folder) { 21 | return new Promise((resolve, reject) => { 22 | const relativePath = path.posix.relative('src', folder); 23 | fs.readdir(folder, (err, files) => { 24 | if (err) { 25 | throw err; 26 | } 27 | /** @type {string[]} */ 28 | const importLines = []; 29 | /** @type {string[]} */ 30 | const exportLines = []; 31 | for (const i of files) { 32 | if (i.endsWith('.ts')) { 33 | continue; 34 | } 35 | const importName = toCamelCase(i); 36 | const imageName = toCamelCase(path.posix.parse(i).name); 37 | imageNames.add(imageName); 38 | 39 | importLines.push( 40 | `import * as ${importName} from '@/${relativePath}/${i}';` 41 | ); 42 | exportLines.push( 43 | `index.${imageName} = images.fromBase64(${importName});` 44 | ); 45 | } 46 | updateIndex(folder, importLines, exportLines).then(resolve, reject); 47 | }); 48 | }); 49 | } 50 | 51 | (async function() { 52 | const baseDir = 'src/assets/images'; 53 | 54 | const folders = fs 55 | .readdirSync(baseDir) 56 | .map(i => path.posix.join(baseDir, i)) 57 | .filter(i => fs.lstatSync(i).isDirectory()); 58 | await Promise.all(folders.map(generateImageIndex)); 59 | 60 | fs.writeFileSync( 61 | path.posix.join(baseDir, 'type.ts'), 62 | await prettier( 63 | [ 64 | ...commonHeader, 65 | 'export type ImageAssets = Readonly `| '${i}'`), 69 | ', Image>>;' 70 | ].join('\n') 71 | ) 72 | ); 73 | })(); 74 | 75 | async function updateIndex(folder, importLines, exportLines) { 76 | const data = await prettier( 77 | [ 78 | ...commonHeader, 79 | ...importLines, 80 | '', 81 | 'export const index: Record = {};', 82 | '', 83 | ...exportLines, 84 | '', 85 | 'Object.freeze(index);', 86 | '' 87 | ].join('\n') 88 | ); 89 | return new Promise((resolve, reject) => { 90 | fs.writeFile(`${folder}/index.ts`, data, err => { 91 | if (err) { 92 | reject(err); 93 | return; 94 | } 95 | resolve(); 96 | }); 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /scripts/generate-locale-schema.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const fallbackLocale = require('../src/locales/zh.json'); 4 | 5 | (() => { 6 | const keys = Object.keys(fallbackLocale); 7 | fs.writeFileSync( 8 | 'src/locales/schema.json', 9 | JSON.stringify( 10 | { 11 | $schema: 'http://json-schema.org/draft-07/schema#', 12 | $id: 13 | 'https://raw.githubusercontent.com/NateScarlet/auto-dragalia/master/src/locale/schema.json', 14 | type: 'object', 15 | properties: (() => { 16 | const ret = {}; 17 | for (const i of keys) { 18 | ret[i] = { type: 'string' }; 19 | } 20 | return ret; 21 | })(), 22 | required: keys 23 | }, 24 | null, 25 | 4 26 | ) 27 | ); 28 | })(); 29 | -------------------------------------------------------------------------------- /scripts/postchangelog.js: -------------------------------------------------------------------------------- 1 | const { readFile, writeFileSync } = require('fs'); 2 | const { resolve } = require('path'); 3 | const changelog = resolve(__dirname, '../CHANGELOG.md'); 4 | 5 | readFile(changelog, (err, data) => { 6 | writeFileSync( 7 | changelog, 8 | data 9 | .toString() 10 | .replace(/\n# /, '\n## ') 11 | .replace('Change Log', 'Changelog') 12 | ); 13 | }); 14 | -------------------------------------------------------------------------------- /scripts/push.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const version = require('../package.json').version; 3 | const glob = require('glob'); 4 | 5 | glob.Glob(`dist/auto-dragalia-*-${version}-*.auto.js`, {}, (err, files) => { 6 | for (const i of files) { 7 | execSync(`adb push ${i} /sdcard/脚本/`, { 8 | stdio: 'inherit' 9 | }); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /src/assets/images/1080x1920/index.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated 2 | // Use `npm run code-generate:images` to update this file 3 | import * as rareItemPng from '@/assets/images/1080x1920/rare-item.png'; 4 | 5 | export const index: Record = {}; 6 | 7 | index.rareItem = images.fromBase64(rareItemPng); 8 | 9 | Object.freeze(index); 10 | -------------------------------------------------------------------------------- /src/assets/images/1080x1920/rare-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x1920/rare-item.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/auto-battle-switch-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/auto-battle-switch-off.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/cancel-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/cancel-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/close-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/close-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/clover-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/clover-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/clover-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/clover-page.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/continue-button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/continue-button-blue.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/continue-button-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/continue-button-red.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/enemy-legend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/enemy-legend.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/give-up-button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/give-up-button-blue.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/give-up-button-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/give-up-button-white.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/index.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated 2 | // Use `npm run code-generate:images` to update this file 3 | import * as autoBattleSwitchOffPng from '@/assets/images/1080x2160/auto-battle-switch-off.png'; 4 | import * as cancelButtonPng from '@/assets/images/1080x2160/cancel-button.png'; 5 | import * as closeButtonPng from '@/assets/images/1080x2160/close-button.png'; 6 | import * as cloverButtonPng from '@/assets/images/1080x2160/clover-button.png'; 7 | import * as cloverPagePng from '@/assets/images/1080x2160/clover-page.png'; 8 | import * as continueButtonBluePng from '@/assets/images/1080x2160/continue-button-blue.png'; 9 | import * as continueButtonRedPng from '@/assets/images/1080x2160/continue-button-red.png'; 10 | import * as enemyLegendPng from '@/assets/images/1080x2160/enemy-legend.png'; 11 | import * as giveUpButtonBluePng from '@/assets/images/1080x2160/give-up-button-blue.png'; 12 | import * as giveUpButtonWhitePng from '@/assets/images/1080x2160/give-up-button-white.png'; 13 | import * as levelSelectBeginnerPng from '@/assets/images/1080x2160/level-select-beginner.png'; 14 | import * as levelSelectExpertPng from '@/assets/images/1080x2160/level-select-expert.png'; 15 | import * as levelSelectMasterPng from '@/assets/images/1080x2160/level-select-master.png'; 16 | import * as levelSelectStandardPng from '@/assets/images/1080x2160/level-select-standard.png'; 17 | import * as levelSelectPng from '@/assets/images/1080x2160/level-select.png'; 18 | import * as loadingTextPng from '@/assets/images/1080x2160/loading-text.png'; 19 | import * as lv29ButtonPng from '@/assets/images/1080x2160/lv29-button.png'; 20 | import * as menuButtonPng from '@/assets/images/1080x2160/menu-button.png'; 21 | import * as nextTextPng from '@/assets/images/1080x2160/next-text.png'; 22 | import * as okButtonPng from '@/assets/images/1080x2160/ok-button.png'; 23 | import * as presentButtonPng from '@/assets/images/1080x2160/present-button.png'; 24 | import * as presentPrice0Png from '@/assets/images/1080x2160/present-price-0.png'; 25 | import * as presentPrice1500Png from '@/assets/images/1080x2160/present-price-1500.png'; 26 | import * as presentPrice15000Png from '@/assets/images/1080x2160/present-price-15000.png'; 27 | import * as presentPrice4000Png from '@/assets/images/1080x2160/present-price-4000.png'; 28 | import * as presentPrice8000Png from '@/assets/images/1080x2160/present-price-8000.png'; 29 | import * as rareItemPng from '@/assets/images/1080x2160/rare-item.png'; 30 | import * as repeatBattleButtonPng from '@/assets/images/1080x2160/repeat-battle-button.png'; 31 | import * as repeatWithStaminaButtonPng from '@/assets/images/1080x2160/repeat-with-stamina-button.png'; 32 | import * as retryButtonBluePng from '@/assets/images/1080x2160/retry-button-blue.png'; 33 | import * as retryButtonRedPng from '@/assets/images/1080x2160/retry-button-red.png'; 34 | import * as startBattleButtonPng from '@/assets/images/1080x2160/start-battle-button.png'; 35 | import * as supportSelectButtonPng from '@/assets/images/1080x2160/support-select-button.png'; 36 | import * as supportSkillAvailablePng from '@/assets/images/1080x2160/support-skill-available.png'; 37 | import * as tapButtonPng from '@/assets/images/1080x2160/tap-button.png'; 38 | import * as transformGaugeFullPng from '@/assets/images/1080x2160/transform-gauge-full.png'; 39 | import * as x0Png from '@/assets/images/1080x2160/x0.png'; 40 | 41 | export const index: Record = {}; 42 | 43 | index.autoBattleSwitchOff = images.fromBase64(autoBattleSwitchOffPng); 44 | index.cancelButton = images.fromBase64(cancelButtonPng); 45 | index.closeButton = images.fromBase64(closeButtonPng); 46 | index.cloverButton = images.fromBase64(cloverButtonPng); 47 | index.cloverPage = images.fromBase64(cloverPagePng); 48 | index.continueButtonBlue = images.fromBase64(continueButtonBluePng); 49 | index.continueButtonRed = images.fromBase64(continueButtonRedPng); 50 | index.enemyLegend = images.fromBase64(enemyLegendPng); 51 | index.giveUpButtonBlue = images.fromBase64(giveUpButtonBluePng); 52 | index.giveUpButtonWhite = images.fromBase64(giveUpButtonWhitePng); 53 | index.levelSelectBeginner = images.fromBase64(levelSelectBeginnerPng); 54 | index.levelSelectExpert = images.fromBase64(levelSelectExpertPng); 55 | index.levelSelectMaster = images.fromBase64(levelSelectMasterPng); 56 | index.levelSelectStandard = images.fromBase64(levelSelectStandardPng); 57 | index.levelSelect = images.fromBase64(levelSelectPng); 58 | index.loadingText = images.fromBase64(loadingTextPng); 59 | index.lv29Button = images.fromBase64(lv29ButtonPng); 60 | index.menuButton = images.fromBase64(menuButtonPng); 61 | index.nextText = images.fromBase64(nextTextPng); 62 | index.okButton = images.fromBase64(okButtonPng); 63 | index.presentButton = images.fromBase64(presentButtonPng); 64 | index.presentPrice0 = images.fromBase64(presentPrice0Png); 65 | index.presentPrice1500 = images.fromBase64(presentPrice1500Png); 66 | index.presentPrice15000 = images.fromBase64(presentPrice15000Png); 67 | index.presentPrice4000 = images.fromBase64(presentPrice4000Png); 68 | index.presentPrice8000 = images.fromBase64(presentPrice8000Png); 69 | index.rareItem = images.fromBase64(rareItemPng); 70 | index.repeatBattleButton = images.fromBase64(repeatBattleButtonPng); 71 | index.repeatWithStaminaButton = images.fromBase64(repeatWithStaminaButtonPng); 72 | index.retryButtonBlue = images.fromBase64(retryButtonBluePng); 73 | index.retryButtonRed = images.fromBase64(retryButtonRedPng); 74 | index.startBattleButton = images.fromBase64(startBattleButtonPng); 75 | index.supportSelectButton = images.fromBase64(supportSelectButtonPng); 76 | index.supportSkillAvailable = images.fromBase64(supportSkillAvailablePng); 77 | index.tapButton = images.fromBase64(tapButtonPng); 78 | index.transformGaugeFull = images.fromBase64(transformGaugeFullPng); 79 | index.x0 = images.fromBase64(x0Png); 80 | 81 | Object.freeze(index); 82 | -------------------------------------------------------------------------------- /src/assets/images/1080x2160/level-select-beginner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/level-select-beginner.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/level-select-expert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/level-select-expert.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/level-select-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/level-select-master.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/level-select-standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/level-select-standard.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/level-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/level-select.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/loading-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/loading-text.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/lv29-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/lv29-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/menu-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/menu-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/next-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/next-text.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/ok-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/ok-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-price-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-price-0.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-price-1500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-price-1500.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-price-15000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-price-15000.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-price-4000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-price-4000.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/present-price-8000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/present-price-8000.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/rare-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/rare-item.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/repeat-battle-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/repeat-battle-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/repeat-with-stamina-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/repeat-with-stamina-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/retry-button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/retry-button-blue.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/retry-button-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/retry-button-red.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/start-battle-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/start-battle-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/support-select-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/support-select-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/support-skill-available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/support-skill-available.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/tap-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/tap-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/transform-gauge-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/transform-gauge-full.png -------------------------------------------------------------------------------- /src/assets/images/1080x2160/x0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2160/x0.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/auto-battle-switch-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/auto-battle-switch-off.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/cancel-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/cancel-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/close-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/close-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/continue-button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/continue-button-blue.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/continue-button-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/continue-button-red.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/give-up-button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/give-up-button-blue.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/give-up-button-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/give-up-button-white.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/ok-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/ok-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/repeat-battle-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/repeat-battle-button.png -------------------------------------------------------------------------------- /src/assets/images/1080x2264-en/repeat-with-stamina-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NateScarlet/auto-dragalia/4460ae6b06f0a4ea27eab723dae44d5b80465a29/src/assets/images/1080x2264-en/repeat-with-stamina-button.png -------------------------------------------------------------------------------- /src/assets/images/index.ts: -------------------------------------------------------------------------------- 1 | import { ImageAssets } from '@/assets/images/type'; 2 | 3 | export const img = ((): ImageAssets => { 4 | const fallback: ImageAssets = require(`./${FALLBACK_ASSET}`); 5 | const target: Partial = require(`./${TARGET_ASSET}`); 6 | return { ...fallback, ...target }; 7 | })(); 8 | -------------------------------------------------------------------------------- /src/assets/images/type.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated 2 | // Use `npm run code-generate:images` to update this file 3 | export type ImageAssets = Readonly< 4 | Record< 5 | | 'autoBattleSwitchOff' 6 | | 'cancelButton' 7 | | 'closeButton' 8 | | 'cloverButton' 9 | | 'cloverPage' 10 | | 'continueButtonBlue' 11 | | 'continueButtonRed' 12 | | 'enemyLegend' 13 | | 'giveUpButtonBlue' 14 | | 'giveUpButtonWhite' 15 | | 'levelSelect' 16 | | 'levelSelectBeginner' 17 | | 'levelSelectExpert' 18 | | 'levelSelectMaster' 19 | | 'levelSelectStandard' 20 | | 'loadingText' 21 | | 'lv29Button' 22 | | 'menuButton' 23 | | 'nextText' 24 | | 'okButton' 25 | | 'presentButton' 26 | | 'presentPrice0' 27 | | 'presentPrice1500' 28 | | 'presentPrice15000' 29 | | 'presentPrice4000' 30 | | 'presentPrice8000' 31 | | 'rareItem' 32 | | 'repeatBattleButton' 33 | | 'repeatWithStaminaButton' 34 | | 'retryButtonBlue' 35 | | 'retryButtonRed' 36 | | 'startBattleButton' 37 | | 'supportSelectButton' 38 | | 'supportSkillAvailable' 39 | | 'tapButton' 40 | | 'transformGaugeFull' 41 | | 'x0', 42 | Image 43 | > 44 | >; 45 | -------------------------------------------------------------------------------- /src/i18n.ts: -------------------------------------------------------------------------------- 1 | const locale: Record< 2 | string, 3 | string 4 | > = require(`@/locales/${TARGET_LOCALE}.json`); 5 | 6 | function getTemplate(key: string): string { 7 | if (key in locale) { 8 | return locale[key]; 9 | } 10 | return key; 11 | } 12 | 13 | function formatTemplate( 14 | template: string, 15 | values: Record = {} 16 | ): string { 17 | let ret = template; 18 | for (const k in values) { 19 | const v = values[k] as unknown; 20 | ret = ret.split('${' + k + '}').join(String(v)); 21 | } 22 | return ret; 23 | } 24 | 25 | export function tr(key: string, values?: Record): string { 26 | return formatTemplate(getTemplate(key), values); 27 | } 28 | -------------------------------------------------------------------------------- /src/layoutTemplate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./schema.json", 3 | "$id": "https://raw.githubusercontent.com/NateScarlet/auto-dragalia/master/src/locale/en.json", 4 | "banner": "${name}: version=${version}, asset=${asset}, locale=${locale}", 5 | "idle-text": "Stopped", 6 | "repeat-raid": "RepeatRaid", 7 | "feed-dragon": "AutoDragonFeeding", 8 | "feed-four-leaf-clover": "FeedClovers", 9 | "farm-rare-item": "FarmRareItem", 10 | "present-button-not-found": "present button not found, Please go to the Dragons Roost manually", 11 | "no-present-available": "No presents available", 12 | "transform-to-dragon": "Transform to dragon", 13 | "use-dragon-skill": "Use dragon skill", 14 | "no-support-skill-available": "No support skill available", 15 | "use-support-skill": "Use support skill: ${pos}", 16 | "image-not-found": "Image not found: ${id}", 17 | "image-found": "Image found: ${id}: ${pos}", 18 | "wait-timeout": "Waiting timeout", 19 | "wait-image-appear": "Waiting image appear: ${id}", 20 | "wait-image-disappear": "Waiting image disappear: ${id}", 21 | "run-task": "Run task: ${task}", 22 | "team-too-weak": "Team not strong enough to clear the stage", 23 | "closeness-reach-lv29": "Closeness has reached 29,please,continue feeding manually so you don't waste anything", 24 | "no-clover": "You don't have any clovers,Please farm some", 25 | "entering-stage-1": "Loading of Stage 1 detected", 26 | "entered-stage-1": "Detected that Stage 1 has started", 27 | "entering-stage-2": "Detected entering 2nd Stage", 28 | "require-level-select-page": "Please go to the quest selection page to start", 29 | "rare-item-dropped": "Successfully obtained rare item, Continuing to complete the quest", 30 | "no-rare-item-drop": "No rare items were dropped, Move to the next round" 31 | } 32 | -------------------------------------------------------------------------------- /src/locales/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/NateScarlet/auto-dragalia/master/src/locale/schema.json", 4 | "type": "object", 5 | "properties": { 6 | "$schema": { 7 | "type": "string" 8 | }, 9 | "$id": { 10 | "type": "string" 11 | }, 12 | "banner": { 13 | "type": "string" 14 | }, 15 | "idle-text": { 16 | "type": "string" 17 | }, 18 | "repeat-raid": { 19 | "type": "string" 20 | }, 21 | "feed-dragon": { 22 | "type": "string" 23 | }, 24 | "feed-four-leaf-clover": { 25 | "type": "string" 26 | }, 27 | "farm-rare-item": { 28 | "type": "string" 29 | }, 30 | "present-button-not-found": { 31 | "type": "string" 32 | }, 33 | "no-present-available": { 34 | "type": "string" 35 | }, 36 | "transform-to-dragon": { 37 | "type": "string" 38 | }, 39 | "use-dragon-skill": { 40 | "type": "string" 41 | }, 42 | "no-support-skill-available": { 43 | "type": "string" 44 | }, 45 | "use-support-skill": { 46 | "type": "string" 47 | }, 48 | "image-not-found": { 49 | "type": "string" 50 | }, 51 | "image-found": { 52 | "type": "string" 53 | }, 54 | "wait-timeout": { 55 | "type": "string" 56 | }, 57 | "wait-image-appear": { 58 | "type": "string" 59 | }, 60 | "wait-image-disappear": { 61 | "type": "string" 62 | }, 63 | "run-task": { 64 | "type": "string" 65 | }, 66 | "team-too-weak": { 67 | "type": "string" 68 | }, 69 | "closeness-reach-lv29": { 70 | "type": "string" 71 | }, 72 | "no-clover": { 73 | "type": "string" 74 | }, 75 | "entering-stage-1": { 76 | "type": "string" 77 | }, 78 | "entered-stage-1": { 79 | "type": "string" 80 | }, 81 | "entering-stage-2": { 82 | "type": "string" 83 | }, 84 | "require-level-select-page": { 85 | "type": "string" 86 | }, 87 | "rare-item-dropped": { 88 | "type": "string" 89 | }, 90 | "no-rare-item-drop": { 91 | "type": "string" 92 | } 93 | }, 94 | "required": [ 95 | "$schema", 96 | "$id", 97 | "banner", 98 | "idle-text", 99 | "repeat-raid", 100 | "feed-dragon", 101 | "feed-four-leaf-clover", 102 | "farm-rare-item", 103 | "present-button-not-found", 104 | "no-present-available", 105 | "transform-to-dragon", 106 | "use-dragon-skill", 107 | "no-support-skill-available", 108 | "use-support-skill", 109 | "image-not-found", 110 | "image-found", 111 | "wait-timeout", 112 | "wait-image-appear", 113 | "wait-image-disappear", 114 | "run-task", 115 | "team-too-weak", 116 | "closeness-reach-lv29", 117 | "no-clover", 118 | "entering-stage-1", 119 | "entered-stage-1", 120 | "entering-stage-2", 121 | "require-level-select-page", 122 | "rare-item-dropped", 123 | "no-rare-item-drop" 124 | ] 125 | } 126 | -------------------------------------------------------------------------------- /src/locales/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./schema.json", 3 | "$id": "https://raw.githubusercontent.com/NateScarlet/auto-dragalia/master/src/locale/zh.json", 4 | "banner": "${name}: 版本=${version}, 资产=${asset}, 语言=${locale}", 5 | "idle-text": "停止", 6 | "repeat-raid": "重复战斗", 7 | "feed-dragon": "自动喂龙", 8 | "feed-four-leaf-clover": "喂四叶草", 9 | "farm-rare-item": "刷稀有", 10 | "present-button-not-found": "未找到礼物按钮, 请手动前往龙之庭院", 11 | "no-present-available": "无可用礼物", 12 | "transform-to-dragon": "龙化", 13 | "use-dragon-skill": "使用龙技能", 14 | "no-support-skill-available": "支援技能不可用", 15 | "use-support-skill": "使用支援技能: ${pos}", 16 | "image-not-found": "未找到图像: ${id}", 17 | "image-found": "找到图像: ${id}: ${pos}", 18 | "wait-timeout": "等待超时", 19 | "wait-image-appear": "等待图像出现: ${id}", 20 | "wait-image-disappear": "等待图像消失: ${id}", 21 | "run-task": "运行任务: ${task}", 22 | "team-too-weak": "队伍战力不足, 无法通关", 23 | "closeness-reach-lv29": "信赖度已达到29级,若继续,请手动喂食", 24 | "no-clover": "没有四叶草,请去炼草", 25 | "entering-stage-1": "检测到正在进入第一关卡", 26 | "entered-stage-1": "检测到已进入第一关卡", 27 | "entering-stage-2": "检测到正在进入第二关卡", 28 | "require-level-select-page": "请至关卡选择页面再开始", 29 | "rare-item-dropped": "成功刷到稀有物品, 继续完成战斗", 30 | "no-rare-item-drop": "没有刷到稀有物品, 直接下一轮" 31 | } 32 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { runTaskForever } from '@/runTaskForever'; 2 | import { setupUI } from '@/setupUI'; 3 | import { setupTaskRegistry } from '@/tasks'; 4 | import { name, version } from 'package.json'; 5 | import { tr } from '@/i18n'; 6 | 7 | (async (): Promise => { 8 | console.log( 9 | tr('banner', { 10 | name, 11 | version, 12 | asset: TARGET_ASSET, 13 | locale: TARGET_LOCALE 14 | }) 15 | ); 16 | 17 | try { 18 | launch('com.nintendo.zaga'); 19 | 20 | device.keepScreenOn(10000); 21 | 22 | setupTaskRegistry(); 23 | setupUI(); 24 | await runTaskForever(); 25 | } catch (err) { 26 | console.error(JSON.stringify(err)); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /src/runTaskForever.ts: -------------------------------------------------------------------------------- 1 | import { store } from '@/store'; 2 | import { taskRegistry } from '@/tasks'; 3 | import { wait } from '@/utils/wait'; 4 | import { version } from 'package.json'; 5 | import { tr } from '@/i18n'; 6 | 7 | export async function runTaskForever(): Promise { 8 | if (!store.currentTask) { 9 | await wait(1000); 10 | await runTaskForever(); 11 | 12 | return; 13 | } 14 | 15 | const handler: (() => Promise | void) | undefined = 16 | taskRegistry[store.currentTask]; 17 | if (!handler) { 18 | throw new Error(`Unknown task: ${store.currentTask}`); 19 | } 20 | 21 | console.log(tr('run-task', { task: store.currentTask })); 22 | try { 23 | await handler(); 24 | } catch (err) { 25 | console.show(); 26 | console.error(`${version}: ${err}`); 27 | device.vibrate(0.5e3); 28 | store.currentTask = undefined; 29 | } 30 | await wait(1000); 31 | await runTaskForever(); 32 | } 33 | -------------------------------------------------------------------------------- /src/setupUI.ts: -------------------------------------------------------------------------------- 1 | import layoutTemplateXml from '@/layoutTemplate.xml'; 2 | import { store } from '@/store'; 3 | import { taskRegistry } from '@/tasks'; 4 | import { tr } from '@/i18n'; 5 | 6 | interface Spinner { 7 | setOnTouchListener( 8 | listener: (view: object, event: TouchEvent) => boolean 9 | ): void; 10 | setOnItemSelectedListener(listener: { 11 | onItemSelected( 12 | parent: object, 13 | view: object, 14 | position: number, 15 | id: number 16 | ): void; 17 | onNothingSelected(parent: object): void; 18 | }): void; 19 | getSelectedItem(): string; 20 | setSelection(pos: number): void; 21 | } 22 | interface TouchEvent { 23 | ACTION_UP: object; 24 | ACTION_DOWN: object; 25 | ACTION_MOVE: object; 26 | getAction(): object; 27 | getRawX(): number; 28 | getRawY(): number; 29 | } 30 | 31 | type Window = floaty.FloatyWindow & { 32 | taskSpinner: Spinner; 33 | }; 34 | 35 | const idleText: string = tr('idle-text'); 36 | 37 | function setupSpinner(window: Window, spinnerItems: string[]): void { 38 | window.taskSpinner.setOnItemSelectedListener({ 39 | onItemSelected(): void { 40 | const taskName: string = window.taskSpinner.getSelectedItem(); 41 | if (taskName === idleText) { 42 | store.currentTask = undefined; 43 | } else { 44 | store.currentTask = taskName; 45 | } 46 | }, 47 | onNothingSelected(): void { 48 | throw new Error('This should never happen'); 49 | } 50 | }); 51 | store.taskChangeListeners.push((newValue?: string) => { 52 | ui.run(() => { 53 | window.taskSpinner.setSelection( 54 | spinnerItems.indexOf(newValue || idleText) 55 | ); 56 | }); 57 | }); 58 | } 59 | 60 | export function setupUI(): { 61 | window: Window; 62 | spinnerItems: string[]; 63 | } { 64 | const spinnerItems: string[] = [idleText, ...Object.keys(taskRegistry)]; 65 | const layout: string = layoutTemplateXml.replace( 66 | /\${entries}/g, 67 | spinnerItems.join('|') 68 | ); 69 | const window: Window = floaty.window(layout) as Window; 70 | window.setAdjustEnabled(true); 71 | window.exitOnClose(); 72 | 73 | setupSpinner(window, spinnerItems); 74 | 75 | // Empty timer for async syntax 76 | // https://hyb1996.github.io/AutoJs-Docs/#/floaty?id=floaty 77 | // eslint-disable-next-line @typescript-eslint/no-empty-function 78 | setInterval(() => {}, 1e3); 79 | 80 | return { window, spinnerItems }; 81 | } 82 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Application state storage 3 | */ 4 | class Store { 5 | public taskChangeListeners: (( 6 | newValue?: string, 7 | oldValue?: string 8 | ) => void)[] = []; 9 | 10 | private readonly state: { 11 | task?: string; 12 | }; 13 | 14 | constructor() { 15 | this.state = {}; 16 | } 17 | 18 | get currentTask(): string | undefined { 19 | return this.state.task; 20 | } 21 | set currentTask(value: string | undefined) { 22 | if (value === this.state.task) { 23 | return; 24 | } 25 | const oldValue: string | undefined = this.state.task; 26 | this.state.task = value; 27 | for (const i of this.taskChangeListeners) { 28 | i(value, oldValue); 29 | } 30 | } 31 | } 32 | export const store: Store = new Store(); 33 | -------------------------------------------------------------------------------- /src/tasks/farmRareItem.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { 3 | chainImageClicks, 4 | clickImage, 5 | findImage, 6 | handleRetry, 7 | tryClickImage, 8 | tryFindAnyImage, 9 | tryFindImage, 10 | waitAndClickImage, 11 | waitAnyImage, 12 | waitImage, 13 | waitLoading 14 | } from '@/utils/image'; 15 | import { wait } from '@/utils/wait'; 16 | import { tr } from '@/i18n'; 17 | 18 | function selectLevel(): void { 19 | const levelSelectPosition: Point | undefined = tryFindAnyImage( 20 | [ 21 | img.levelSelectMaster, 22 | img.levelSelectExpert, 23 | img.levelSelectStandard, 24 | img.levelSelectBeginner 25 | ], 26 | { 27 | id: 'level-select' 28 | } 29 | ); 30 | if (!levelSelectPosition) { 31 | throw new Error(tr('require-level-select-page')); 32 | } 33 | 34 | click(levelSelectPosition.x, levelSelectPosition.y); 35 | } 36 | 37 | async function enterStage1(): Promise { 38 | await chainImageClicks( 39 | { 40 | image: img.supportSelectButton, 41 | timeout: 30e3, 42 | id: 'support-select', 43 | onDelay: handleRetry 44 | }, 45 | { 46 | image: img.startBattleButton, 47 | id: 'start-battle', 48 | timeout: 30e3, 49 | onDelay: handleRetry 50 | }, 51 | { 52 | image: img.loadingText, 53 | id: 'stage-1-loading', 54 | timeout: 30e3, 55 | onDelay: handleRetry 56 | } 57 | ); 58 | } 59 | 60 | async function enterStage2(): Promise { 61 | tryClickImage(img.autoBattleSwitchOff, { id: 'auto-battle-switch-off' }); 62 | await waitImage(true, img.loadingText, { id: 'stage-2-loading' }); 63 | } 64 | 65 | async function enterMenu(): Promise { 66 | await waitAnyImage(true, [img.rareItem], { 67 | timeout: 60e3, 68 | onDelay(): void { 69 | handleRetry(); 70 | tryClickImage(img.menuButton, { 71 | id: 'menu-button' 72 | }); 73 | }, 74 | id: 'rare-item' 75 | }); 76 | await wait(500); // Wait menu animation finish; 77 | } 78 | 79 | function onSuccess(): void { 80 | toastLog(tr('rare-item-dropped')); 81 | while ( 82 | !tryClickImage(img.continueButtonBlue, { 83 | id: 'finish-phrase-continue-button' 84 | }) 85 | ) { 86 | tryClickImage(img.okButton, { id: 'finish-phrase-ok-button' }); 87 | tryClickImage(img.closeButton, { 88 | id: 'finish-phrase-close-button' 89 | }); 90 | tryClickImage(img.cancelButton, { id: 'finish-phrase-cancel-button' }); 91 | tryClickImage(img.tapButton, { id: 'finish-phrase-tap-button' }); 92 | tryClickImage(img.nextText, { id: 'finish-phrase-next-text' }); 93 | } 94 | } 95 | 96 | async function onFail(): Promise { 97 | log(tr('no-rare-item-drop')); 98 | clickImage(img.giveUpButtonBlue, { id: 'give-up-button-1' }); 99 | await wait(500); 100 | await waitAndClickImage(img.giveUpButtonBlue, { 101 | timeout: 5e3, 102 | id: 'give-up-button-2' 103 | }); 104 | } 105 | 106 | async function checkRareItem(): Promise { 107 | const p: Point = findImage(img.rareItem, { id: 'rare-item' }); 108 | const region: [number, number, number, number] = [ 109 | p.x, 110 | p.y + img.rareItem.getHeight(), 111 | img.rareItem.getWidth(), 112 | img.rareItem.getHeight() 113 | ]; 114 | if (tryFindImage(img.x0, { id: 'x0', region })) { 115 | await onFail(); 116 | } else { 117 | onSuccess(); 118 | } 119 | } 120 | export async function farmRareItem(): Promise { 121 | selectLevel(); 122 | await enterStage1(); 123 | log(tr('entering-stage-1')); 124 | await waitLoading({ id: 'stage-1-loading' }); 125 | toastLog(tr('entered-stage-1')); 126 | await enterStage2(); 127 | toastLog(tr('entering-stage-2')); 128 | await enterMenu(); 129 | await checkRareItem(); 130 | await waitImage(true, img.levelSelect, { 131 | timeout: 60e3, 132 | id: 'level-select', 133 | onDelay: handleRetry 134 | }); 135 | } 136 | -------------------------------------------------------------------------------- /src/tasks/feedDragon.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { 3 | clickImage, 4 | findImage, 5 | keepClickAnyImage, 6 | tryFindImage 7 | } from '@/utils/image'; 8 | import { wait } from '@/utils/wait'; 9 | import { tr } from '@/i18n'; 10 | 11 | export async function enterPresentPage(): Promise { 12 | try { 13 | clickImage(img.presentButton, { 14 | id: 'present-button' 15 | }); 16 | await wait(500); 17 | } catch { 18 | throw new Error(tr('present-not-found')); 19 | } 20 | } 21 | 22 | export async function handlePresentResponse(): Promise { 23 | if ( 24 | !(await keepClickAnyImage([img.closeButton, img.cancelButton], { 25 | findOptions: { 26 | id: 'dialog-button' 27 | }, 28 | onDelay(): boolean { 29 | return !tryFindImage(img.presentButton, { 30 | id: 'present-button' 31 | }); 32 | } 33 | })) 34 | ) { 35 | throw new Error(tr('no-present-available')); 36 | } 37 | } 38 | 39 | const allPresentPriceImages: Image[] = [ 40 | img.presentPrice0, 41 | img.presentPrice1500, 42 | img.presentPrice4000, 43 | img.presentPrice8000, 44 | img.presentPrice15000 45 | ]; 46 | 47 | export async function feedDragon(): Promise { 48 | await enterPresentPage(); 49 | 50 | for (const i of allPresentPriceImages) { 51 | try { 52 | const pos: Point = findImage(i, { 53 | id: 'present-price' 54 | }); 55 | swipe(pos.x, pos.y, pos.x, pos.y - 300, 300); 56 | } catch { 57 | break; 58 | } 59 | } 60 | 61 | await handlePresentResponse(); 62 | } 63 | -------------------------------------------------------------------------------- /src/tasks/feedFourLeafClover.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { enterPresentPage, handlePresentResponse } from '@/tasks/feedDragon'; 3 | import { findImage, tryClickImage, tryFindImage } from '@/utils/image'; 4 | import { wait } from '@/utils/wait'; 5 | import { tr } from '@/i18n'; 6 | 7 | export async function feedFourLeafClover(): Promise { 8 | if ( 9 | tryFindImage(img.lv29Button, { 10 | threshold: 0.95, 11 | id: 'lv29-button' 12 | }) 13 | ) { 14 | throw new Error(tr('closeness-reach-lv29')); 15 | } 16 | 17 | await enterPresentPage(); 18 | 19 | tryClickImage(img.cloverPage, { id: 'clover-page' }); 20 | await wait(500); 21 | 22 | try { 23 | const pos: Point = findImage(img.cloverButton, { 24 | id: 'clover-button' 25 | }); 26 | swipe(pos.x, pos.y, pos.x, pos.y - 300, 300); 27 | } catch { 28 | throw new Error(tr('no-clover')); 29 | } 30 | 31 | await handlePresentResponse(); 32 | } 33 | -------------------------------------------------------------------------------- /src/tasks/index.ts: -------------------------------------------------------------------------------- 1 | import { farmRareItem } from '@/tasks/farmRareItem'; 2 | import { feedDragon } from '@/tasks/feedDragon'; 3 | import { feedFourLeafClover } from '@/tasks/feedFourLeafClover'; 4 | import { repeatRaid } from '@/tasks/repeatRaid'; 5 | import { tr } from '@/i18n'; 6 | 7 | export const taskRegistry: Record< 8 | string, 9 | (() => void | Promise) | undefined 10 | > = {}; 11 | 12 | export function setupTaskRegistry(): void { 13 | taskRegistry[tr('repeat-raid')] = repeatRaid; 14 | taskRegistry[tr('feed-dragon')] = feedDragon; 15 | taskRegistry[tr('feed-four-leaf-clover')] = feedFourLeafClover; 16 | taskRegistry[tr('farm-rare-item')] = farmRareItem; 17 | } 18 | -------------------------------------------------------------------------------- /src/tasks/repeatRaid.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { tryCastSupportSkill, tryTransform2dragon } from '@/utils/battle'; 3 | import { clickImage, tryClickImage, waitAndClickImage } from '@/utils/image'; 4 | import { tr } from '@/i18n'; 5 | 6 | async function repeatWithStamina(): Promise { 7 | clickImage(img.repeatBattleButton); 8 | await waitAndClickImage(img.repeatWithStaminaButton); 9 | await waitAndClickImage(img.okButton); 10 | } 11 | 12 | async function tryRepeatWithStamina(): Promise { 13 | try { 14 | await repeatWithStamina(); 15 | } catch { 16 | tryClickImage(img.continueButtonRed); 17 | } 18 | } 19 | 20 | export async function repeatRaid(): Promise { 21 | tryClickImage(img.startBattleButton); 22 | tryClickImage(img.autoBattleSwitchOff); 23 | tryClickImage(img.retryButtonRed); 24 | tryClickImage(img.okButton); 25 | tryClickImage(img.closeButton); 26 | tryClickImage(img.cancelButton); 27 | tryClickImage(img.tapButton); 28 | tryClickImage(img.nextText); 29 | if (tryClickImage(img.giveUpButtonWhite)) { 30 | await waitAndClickImage(img.giveUpButtonBlue, { timeout: 60e3 }); 31 | throw new Error(tr('team-too-weak')); 32 | } 33 | tryTransform2dragon(); 34 | tryCastSupportSkill(); 35 | await tryRepeatWithStamina(); 36 | } 37 | -------------------------------------------------------------------------------- /src/types/autojs.d.ts: -------------------------------------------------------------------------------- 1 | declare type IRegion = [number, number, number, number]; 2 | // declare function click(x: number, y: number): boolean; 3 | declare const console: Console; 4 | declare namespace images { 5 | function fromBase64(base64: string): null; 6 | /** 4.1+ https://hyb1996.github.io/AutoJs-Docs/#/images?id=imagesmatchtemplateimg-template-options */ 7 | function matchTemplate( 8 | img: Image, 9 | template: Image, 10 | options?: { 11 | max?: number; 12 | level?: number; 13 | } & FindColorOptions 14 | ): MatchingResult; 15 | interface Match { 16 | point: Point; 17 | similarity: number; 18 | } 19 | /** https://hyb1996.github.io/AutoJs-Docs/#/images?id=matchingresult */ 20 | interface MatchingResult { 21 | matches: Match[]; 22 | points: Point[]; 23 | first(): Match | null; 24 | last(): Match | null; 25 | leftmost(): Match | null; 26 | topmost(): Match | null; 27 | rightmost(): Match | null; 28 | bottommost(): Match | null; 29 | best(): Match | null; 30 | worst(): Match | null; 31 | sortBy(cmp: (a: Match, b: Match) => number | string): MatchingResult; 32 | } 33 | } 34 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=settimeoutcallback-delay-args */ 35 | declare function setTimeout( 36 | callback: () => unknown, 37 | delay: number, 38 | ...args: unknown[] 39 | ): number; 40 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=setintervalcallback-delay-args */ 41 | declare function setInterval( 42 | callback: () => unknown, 43 | delay: number, 44 | ...args: unknown[] 45 | ): number; 46 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=setimmediatecallback-args */ 47 | declare function setImmediate( 48 | callback: () => unknown, 49 | ...args: unknown[] 50 | ): number; 51 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=clearintervalid */ 52 | declare function clearInterval(id: number): void; 53 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=cleartimeoutid */ 54 | declare function clearTimeout(id: number): void; 55 | /** https://hyb1996.github.io/AutoJs-Docs/#/timers?id=clearimmediateid */ 56 | declare function clearImmediate(id: number): void; 57 | -------------------------------------------------------------------------------- /src/types/define.d.ts: -------------------------------------------------------------------------------- 1 | declare const TARGET_ASSET: string; 2 | declare const FALLBACK_ASSET: string; 3 | declare const TARGET_LOCALE: string; 4 | -------------------------------------------------------------------------------- /src/types/png-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | const dataUrl: string; 3 | export = dataUrl; 4 | } 5 | -------------------------------------------------------------------------------- /src/types/xml-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.xml' { 2 | const data: string; 3 | export = data; 4 | } 5 | -------------------------------------------------------------------------------- /src/utils/battle.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { findImage, waitImage } from '@/utils/image'; 3 | import { wait } from '@/utils/wait'; 4 | import { tr } from '@/i18n'; 5 | 6 | export async function waitForEnemy(): Promise { 7 | await waitImage(true, img.enemyLegend, { 8 | timeout: 3e3, 9 | findOptions: { 10 | region: [device.width / 4, 0, (device.width / 4) * 3, device.height / 2], 11 | threshold: 0.8 12 | } 13 | }); 14 | } 15 | 16 | export async function transform2dragon(): Promise { 17 | const pos: Point = findImage(img.transformGaugeFull); 18 | await waitForEnemy(); 19 | console.log(tr('transform-to-dragon')); 20 | click(pos.x + 100, pos.y); 21 | await wait(2500); 22 | await waitForEnemy(); 23 | console.log(tr('use-dragon-skill')); 24 | click(pos.x + 360, pos.y + 300); 25 | } 26 | 27 | export async function tryTransform2dragon(): Promise { 28 | try { 29 | await transform2dragon(); 30 | } catch { 31 | return; 32 | } 33 | } 34 | 35 | export async function castSupportSkill(): Promise { 36 | const pos: Point | undefined = findImage(img.supportSkillAvailable, { 37 | region: [device.width / 3, device.height / 4] 38 | }); 39 | if (!pos) { 40 | console.verbose(tr('no-support-skill-available')); 41 | 42 | return; 43 | } 44 | await waitForEnemy(); 45 | console.log(tr('use-support-skill', { pos })); 46 | click(pos.x, pos.y - 80); 47 | } 48 | 49 | export async function tryCastSupportSkill(): Promise { 50 | try { 51 | await castSupportSkill(); 52 | } catch { 53 | return; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/utils/image/click.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { 3 | findImage, 4 | FindImageOptions, 5 | WaitImageOptions, 6 | tryFindAnyImage, 7 | waitImage 8 | } from '@/utils/image'; 9 | import { wait } from '@/utils/wait'; 10 | 11 | export function clickImage(image: Image, options?: FindImageOptions): Point { 12 | const pos: Point = findImage(image, options); 13 | click(pos.x, pos.y); 14 | 15 | return pos; 16 | } 17 | 18 | export function tryClickImage( 19 | image: Image, 20 | options?: FindImageOptions 21 | ): Point | undefined { 22 | try { 23 | return clickImage(image, options); 24 | } catch (err) { 25 | return; 26 | } 27 | } 28 | 29 | export function tryClickAnyImage( 30 | images: Image[], 31 | options?: FindImageOptions 32 | ): Point | undefined { 33 | const pos: Point | undefined = tryFindAnyImage(images, options); 34 | if (!pos) { 35 | return; 36 | } 37 | 38 | click(pos.x, pos.y); 39 | 40 | return pos; 41 | } 42 | 43 | export async function waitAndClickImage( 44 | image: Image, 45 | options?: WaitImageOptions 46 | ): Promise { 47 | const pos: Point = await waitImage(true, image, options); 48 | click(pos.x, pos.y); 49 | 50 | return pos; 51 | } 52 | 53 | export function handleRetry(): void { 54 | tryClickImage(img.retryButtonRed, { id: 'retry-button-red' }); 55 | tryClickImage(img.retryButtonBlue, { id: 'retry-button-blue' }); 56 | } 57 | interface KeepClickAnyImageContext { 58 | waitEndTime: number; 59 | clickCount: number; 60 | isFinished: boolean; 61 | images: Image[]; 62 | options: KeepClickImageOptions; 63 | } 64 | 65 | async function handleKeepAnyImageClicking( 66 | context: KeepClickAnyImageContext 67 | ): Promise { 68 | if (Date.now() > context.waitEndTime) { 69 | context.isFinished = true; 70 | 71 | return; 72 | } 73 | 74 | const { 75 | nextTimeout = 5e3, 76 | findOptions, 77 | onDelay = (): boolean => true 78 | }: KeepClickImageOptions = context.options; 79 | 80 | if (tryClickAnyImage(context.images, findOptions)) { 81 | context.clickCount += 1; 82 | context.waitEndTime = Date.now() + nextTimeout; 83 | await wait(500); 84 | } else { 85 | await wait(1000); 86 | } 87 | if (!(await onDelay())) { 88 | context.isFinished = true; 89 | } 90 | } 91 | 92 | export async function keepClickAnyImage( 93 | images: Image[], 94 | options?: KeepClickImageOptions 95 | ): Promise { 96 | const { firstTimeout = 20e3 }: KeepClickImageOptions = options || {}; 97 | const context: KeepClickAnyImageContext = { 98 | images, 99 | waitEndTime: Date.now() + firstTimeout, 100 | clickCount: 0, 101 | isFinished: false, 102 | options: options || {} 103 | }; 104 | while (!context.isFinished) { 105 | await handleKeepAnyImageClicking(context); 106 | } 107 | 108 | return context.clickCount; 109 | } 110 | 111 | async function chainImageClick( 112 | left: ImageClickChainItem, 113 | right: ImageClickChainItem 114 | ): Promise { 115 | return waitImage(true, right.image, { 116 | id: right.id, 117 | delay: right.delay, 118 | findOptions: right.findOptions, 119 | async onDelay(): Promise { 120 | if (right.onDelay) { 121 | await right.onDelay(); 122 | } 123 | tryClickImage(left.image, { 124 | id: left.id, 125 | level: left.level, 126 | region: left.region, 127 | threshold: left.threshold 128 | }); 129 | }, 130 | timeout: right.timeout 131 | }); 132 | } 133 | 134 | /** 135 | * Click multiple images one by one, last image waited but not clicked 136 | * @param items Options for each image to click 137 | * @return image positions in order 138 | */ 139 | export async function chainImageClicks( 140 | ...items: ImageClickChainItem[] 141 | ): Promise { 142 | if (items.length < 2) { 143 | throw new Error('Need at least 2 image to chain click'); 144 | } 145 | const ret: Point[] = []; 146 | for (let pos = 0; pos + 1 < items.length; pos += 1) { 147 | ret.push(await chainImageClick(items[pos], items[pos + 1])); 148 | } 149 | 150 | return ret; 151 | } 152 | 153 | export interface ImageClickChainItem 154 | extends WaitImageOptions, 155 | images.FindImageOptions { 156 | image: Image; 157 | } 158 | 159 | export interface KeepClickImageOptions { 160 | firstTimeout?: number; 161 | nextTimeout?: number; 162 | findOptions?: FindImageOptions; 163 | onDelay?(): boolean | Promise; 164 | } 165 | -------------------------------------------------------------------------------- /src/utils/image/find.ts: -------------------------------------------------------------------------------- 1 | import { tr } from '@/i18n'; 2 | 3 | images.requestScreenCapture(false); 4 | 5 | export function findImage(image: Image, options?: FindImageOptions): Point { 6 | const { id = '' } = options || {}; 7 | const ret: Point | null = images.findImage( 8 | images.captureScreen(), 9 | image, 10 | options 11 | ); 12 | if (ret === null) { 13 | throw new Error(tr('image-not-found', { id })); 14 | } 15 | console.verbose(tr('image-found', { id, pos: ret })); 16 | 17 | return ret; 18 | } 19 | 20 | export function tryFindImage( 21 | ...args: Parameters 22 | ): Point | undefined { 23 | try { 24 | return findImage(...args); 25 | } catch { 26 | return; 27 | } 28 | } 29 | 30 | export function tryFindAnyImage( 31 | images: Image[], 32 | options?: FindImageOptions 33 | ): Point | undefined { 34 | for (const i of images) { 35 | const pos: Point | undefined = tryFindImage(i, options); 36 | if (pos) { 37 | return pos; 38 | } 39 | } 40 | } 41 | 42 | export interface FindImageOptions extends images.FindImageOptions { 43 | id?: string; 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/image/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@/utils/image/find'; 2 | export * from '@/utils/image/click'; 3 | export * from '@/utils/image/wait'; 4 | -------------------------------------------------------------------------------- /src/utils/image/wait.ts: -------------------------------------------------------------------------------- 1 | import { img } from '@/assets/images'; 2 | import { handleRetry, tryFindAnyImage } from '@/utils/image'; 3 | import { wait } from '@/utils/wait'; 4 | import { tr } from '@/i18n'; 5 | 6 | let waitingCount = 0; 7 | 8 | async function handleAnyImageWaiting( 9 | appear: boolean, 10 | images: Image[], 11 | options: WaitImageOptions | undefined, 12 | waitingEndTime: number 13 | ): Promise { 14 | const { 15 | delay = 500, 16 | findOptions = {}, 17 | onDelay = (): void | Promise => undefined, 18 | id = String(waitingCount) 19 | } = options || {}; 20 | 21 | if (Date.now() > waitingEndTime) { 22 | throw new Error(tr('wait-timeout')); 23 | } 24 | 25 | const roundStartTime: number = Date.now(); 26 | const ret: Point | undefined = tryFindAnyImage(images, { 27 | ...findOptions, 28 | id 29 | }); 30 | 31 | if (Boolean(ret) === appear) { 32 | return ret; 33 | } 34 | 35 | console.verbose( 36 | tr(appear ? 'wait-image-appear' : 'wait-image-disappear', { id }) 37 | ); 38 | await onDelay(); 39 | await wait(delay - (Date.now() - roundStartTime)); 40 | 41 | return ret; 42 | } 43 | 44 | export async function waitAnyImage( 45 | appear: true, 46 | images: Image[], 47 | options?: WaitImageOptions 48 | ): Promise; 49 | export async function waitAnyImage( 50 | appear: false, 51 | images: Image[], 52 | options?: WaitImageOptions 53 | ): Promise; 54 | export async function waitAnyImage( 55 | appear: boolean, 56 | images: Image[], 57 | options?: WaitImageOptions 58 | ): Promise { 59 | const { timeout = 600e3 } = options || {}; 60 | const waitingEndTime: number = Date.now() + timeout; 61 | let ret: Point | undefined; 62 | waitingCount += 1; 63 | 64 | for ( 65 | let isFinished = false; 66 | !isFinished; 67 | isFinished = Boolean(ret) === appear 68 | ) { 69 | ret = await handleAnyImageWaiting(appear, images, options, waitingEndTime); 70 | } 71 | 72 | return ret; 73 | } 74 | 75 | export async function waitImage( 76 | appear: true, 77 | image: Image, 78 | options?: WaitImageOptions 79 | ): Promise; 80 | export async function waitImage( 81 | appear: false, 82 | image: Image, 83 | options?: WaitImageOptions 84 | ): Promise; 85 | export async function waitImage( 86 | appear: boolean, 87 | image: Image, 88 | options?: WaitImageOptions 89 | ): Promise { 90 | if (appear) { 91 | return waitAnyImage(appear, [image], options); 92 | } 93 | 94 | return waitAnyImage(appear, [image], options); 95 | } 96 | 97 | export async function waitLoading(options?: WaitImageOptions): Promise { 98 | await waitImage(false, img.loadingText, { 99 | ...options, 100 | async onDelay(): Promise { 101 | handleRetry(); 102 | if (options && options.onDelay) { 103 | await options.onDelay(); 104 | } 105 | } 106 | }); 107 | } 108 | 109 | export interface WaitImageOptions { 110 | timeout?: number; 111 | delay?: number; 112 | findOptions?: images.FindImageOptions; 113 | id?: string; 114 | onDelay?(): void | Promise; 115 | } 116 | -------------------------------------------------------------------------------- /src/utils/wait.ts: -------------------------------------------------------------------------------- 1 | export async function wait(delay: number): Promise { 2 | if (delay <= 0) { 3 | return; 4 | } 5 | 6 | return new Promise( 7 | (resolve: () => void): void => { 8 | setTimeout(resolve, delay); 9 | } 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2016"], 4 | "target": "es5", 5 | "module": "esnext", 6 | "removeComments": false, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "jsx": "preserve", 10 | "moduleResolution": "node", 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "allowSyntheticDefaultImports": true, 14 | "baseUrl": ".", 15 | "sourceMap": true, 16 | "types": ["autojs-dev", "webpack-env"], 17 | "paths": { 18 | "@/*": ["src/*"] 19 | } 20 | }, 21 | "include": ["src/**/*.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { name, version } = require('./package.json'); 3 | const webpack = require('webpack'); 4 | 5 | const TARGET_ASSET = process.env.TARGET_ASSET || '1080x2160'; 6 | const FALLBACK_ASSET = process.env.FALLBACK_ASSET || '1080x2160'; 7 | const TARGET_LOCALE = process.env.TARGET_LOCALE || 'zh'; 8 | 9 | module.exports = { 10 | entry: ['./src/main.ts'], 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | exclude: /node_modules/, 16 | use: [{ loader: 'ts-loader' }] 17 | }, 18 | { 19 | test: /\.(png|jpg|gif)$/i, 20 | use: [{ loader: 'url-loader' }] 21 | }, 22 | { 23 | test: /\.xml$/i, 24 | use: [{ loader: 'raw-loader' }] 25 | } 26 | ] 27 | }, 28 | 29 | resolve: { 30 | extensions: ['.tsx', '.ts', '.js'], 31 | alias: { 32 | '@': path.resolve(__dirname, 'src'), 33 | 'package.json': path.resolve(__dirname, 'package.json') 34 | } 35 | }, 36 | output: { 37 | filename: `${name}-${TARGET_LOCALE}-${version}-${TARGET_ASSET}.auto.js`, 38 | path: path.resolve(__dirname, 'dist') 39 | }, 40 | plugins: [ 41 | new webpack.DefinePlugin({ 42 | TARGET_ASSET: JSON.stringify(TARGET_ASSET), 43 | FALLBACK_ASSET: JSON.stringify(FALLBACK_ASSET), 44 | TARGET_LOCALE: JSON.stringify(TARGET_LOCALE) 45 | }) 46 | ], 47 | mode: 'development', 48 | performance: { 49 | hints: false 50 | } 51 | }; 52 | --------------------------------------------------------------------------------