├── .github └── workflows │ └── build.yml ├── Dockerfile ├── README.md ├── SenKongDao.py ├── SenKongDao_config.txt ├── constants.py ├── headersGenerator.py └── requirements.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Docker Hub Release 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | 15 | - name: Setup Docker 16 | uses: docker/setup-buildx-action@v2 17 | 18 | - name: Login to DockerHub 19 | uses: docker/login-action@v2 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 23 | 24 | - name: Generate Tags 25 | uses: docker/metadata-action@v4 26 | id: metadata 27 | with: 28 | images: | 29 | ${{ secrets.DOCKERHUB_USERNAME }}/senkongdao 30 | tags: | 31 | type=raw,value=latest 32 | 33 | - name: Build and Publish 34 | uses: docker/build-push-action@v4 35 | with: 36 | context: . 37 | file: Dockerfile 38 | push: true 39 | tags: ${{ steps.metadata.outputs.tags }} 40 | labels: ${{ steps.metadata.outputs.labels }} 41 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build Stage 2 | FROM python:3.9 as build 3 | WORKDIR /usr/app 4 | RUN python -m venv /usr/app/venv 5 | ENV PATH="/usr/app/venv/bin:$PATH" 6 | 7 | COPY requirements.txt . 8 | RUN pip install -r requirements.txt 9 | 10 | # Final Deployment Stage 11 | FROM python:3.9-slim 12 | 13 | WORKDIR /app 14 | COPY --from=build /usr/app/venv . 15 | COPY . . 16 | 17 | ENV PATH="/app/bin:$PATH" 18 | 19 | CMD ["python", "SenKongDao.py"] 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SenKongDao 2 | 明日方舟森空岛签到Python程序 3 | 4 | --- 5 | 6 | ## 加密参引用说明 7 | 8 | 加密参数计算方法使用[xxyz30的仓库](https://gitee.com/FancyCabbage/skyland-auto-sign/tree/master)的计算方法, 9 | 10 | --- 11 | 12 | 为了防止仓库消失,已删除GitHub Actions使用方法。 13 | 14 | --- 15 | 16 | ## 新版本脚本的使用说明(重要变动,cred变为token) 17 | 18 | 1.使用pip install -r requirments.txt来安装依赖(实际上只需要requests,已安装可以跳过这步) 19 | 20 | 2.在SenKongDao_config.txt中填入uid以及token字段,其中xxx为uid(游戏内名称下方ID,一串数字),yyy为token字段,uid与token字段用&符号隔开 21 | 22 | > **Token字段获取方式** 23 | > 1. 打开[森空岛](https://www.skland.com),按F12开启抓包模式,选择网络(或network)选项卡,选中保留日志(或preserve log),点击选中右边的XHR(或Fetch/XHR),然后接收验证码登录账号(已登录的话需要退出重新登陆) 24 | > 25 | > 2. 找到token_by_phone_code点击选中,然后点击右边的响应(或Response),可以看到右侧这种样式的一串,其中token右侧引号里的就是要获取的token字段,{"status":0,"type":"A","msg":"OK","data":{"token":"aaaaaaaaaaaaa"}} 26 | > 27 | > 3. 将该字段填入SenKongDao_config.txt即可使用脚本,示例:123345&aaaaaaaaaaaaa。(其中12345是示例的uid(游戏内uid),aaaaaaaaaaaaa为第二步中获取的token) 28 | > 29 | > 4. 如果有多个账号需要获取,建议使用浏览器的隐身模式 30 | 支持多账号,每行一个账号即可 31 | 32 | 以下为某个账户的实例: 33 | 34 | xxxxxx&yyyyyyyyyy 35 | 36 | ### 或使用 docker 运行 37 | 1. 参考上面教程新建 `SenKongDao_config.txt` 文件,填入 `uid` 以及 `cred` 字段 38 | 2. 运行 `docker run -v ./SenKongDao_config.txt:/app/SenKongDao_config.txt maojuan180/senkongdao` 其中 `./SenKongDao_config.txt` 为配置文件路径,可自行修改 39 | 40 | ### 定时任务相关使用说明 41 | 1. 在任意目录使用`crontab -e`命令来编辑 cron 文件,第一次运行会让你选择一个编辑器,选择推荐的第一个编辑器即可。 42 | 2. 在最后一行添加`10 0 * * * /usr/bin/docker start xxxx(容器名称或容器id)`,按`Ctrl+X`退出编辑,按`Y`确定,最后按`Enter(回车)`来保存。 43 | 3. 使用`docker ps -a`命令来查询docker容器的名称或者id。 44 | 4. 第二步中cron表达式意思为每天的凌晨0:10运行,可以自行修改。 45 | 5. 如果docker目录不对,可以用命令`which docker`查看一下自己docker的位置,修改第二步的命令。 46 | 47 | ## 贡献者 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /SenKongDao.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import time 4 | import datetime 5 | import headersGenerator 6 | import requests 7 | import constants 8 | 9 | # 常量引入 10 | success_code = constants.success_code 11 | sleep_time = constants.sleep_time 12 | sign_url = constants.sign_url 13 | app_version = constants.app_version 14 | 15 | # 打印当前时间 16 | print("当前时间为:" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 17 | 18 | # 读取cookie 19 | cookie_file = open("SenKongDao_config.txt", "r+", encoding="utf8") 20 | cookie_lines = cookie_file.readlines() 21 | cookie_file.close() 22 | print("已读取" + str(len(cookie_lines)) + "个cookie") 23 | print(str(sleep_time) + "秒后进行签到...") 24 | time.sleep(sleep_time) 25 | 26 | # 遍历cookie 27 | for cookie_line in cookie_lines: 28 | 29 | # 准备签到信息 30 | configs = cookie_line.split("&") 31 | uid = configs[0].strip() 32 | atoken = configs[1].strip() 33 | 34 | # 获取签到用的值 35 | cred_resp = headersGenerator.get_cred_by_token(atoken) 36 | sign_token = cred_resp['token'] 37 | sign_cred = cred_resp['cred'] 38 | 39 | # headers初始化 40 | init_headers = { 41 | 'user-agent': 'Skland/'+app_version+' (com.hypergryph.skland; build:100501001; Android 25; ) Okhttp/4.11.0', 42 | 'cred': sign_cred, 43 | 'Accept-Encoding': 'gzip', 44 | 'Connection': 'close', 45 | } 46 | 47 | # body 48 | data = { 49 | "uid": str(uid), 50 | "gameId": 1 51 | } 52 | 53 | # headers添加加密参 54 | headers = headersGenerator.get_sign_header(sign_url, 'post', data, init_headers, sign_token) 55 | 56 | # 签到请求 57 | sign_response = requests.post(headers=headers, url=sign_url, json=data) 58 | 59 | # 检验返回是否为json格式 60 | try: 61 | sign_response_json = json.loads(sign_response.text) 62 | except: 63 | print(sign_response.text) 64 | print("返回结果非json格式,请检查...") 65 | time.sleep(sleep_time) 66 | sys.exit() 67 | 68 | # 如果为json则解析 69 | code = sign_response_json.get("code") 70 | message = sign_response_json.get("message") 71 | data = sign_response_json.get("data") 72 | 73 | # 返回成功的话,打印详细信息 74 | if code == success_code: 75 | print("签到成功") 76 | awards = sign_response_json.get("data").get("awards") 77 | for award in awards: 78 | print("签到获得的奖励ID为:" + award.get("resource").get("id")) 79 | print("此次签到获得了" + str(award.get("count")) + "单位的" + award.get("resource").get("name") + "(" + award.get( 80 | "resource").get("type") + ")") 81 | print("奖励类型为:" + award.get("type")) 82 | else: 83 | print(sign_response_json) 84 | print("签到失败,请检查以上信息...") 85 | 86 | # 休眠指定时间后,继续下个账户 87 | time.sleep(sleep_time) 88 | 89 | print("程序运行结束") -------------------------------------------------------------------------------- /SenKongDao_config.txt: -------------------------------------------------------------------------------- 1 | xxxxxxxx&yyyyyyyyyyyyyyyyyyyyy -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | # app信息 2 | app_code = '4ca99fa6b56cc2ba' 3 | app_version = '1.5.1' 4 | 5 | # 使用认证代码获得cred 6 | cred_code_url = "https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code" 7 | # 使用token获得认证代码 8 | grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant" 9 | # 签到url post请求 10 | sign_url = "https://zonai.skland.com/api/v1/game/attendance" 11 | 12 | # 请求成功的code码 13 | success_code = 0 14 | # 休眠x秒继续其他账号签到 15 | sleep_time = 3 -------------------------------------------------------------------------------- /headersGenerator.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import hmac 3 | import json 4 | import time 5 | from urllib import parse 6 | import requests 7 | import constants 8 | 9 | # headers参数解密来源于Gitee作者xxyz30 10 | # 原仓库地址:https://gitee.com/FancyCabbage/skyland-auto-sign 11 | 12 | # 常量引入 13 | cred_code_url = constants.cred_code_url 14 | grant_code_url = constants.grant_code_url 15 | app_version = constants.app_version 16 | app_code = constants.app_code 17 | 18 | header_login = { 19 | 'User-Agent': 'Skland/'+app_version+' (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0', 20 | 'Accept-Encoding': 'gzip', 21 | 'Connection': 'close' 22 | } 23 | 24 | header_for_sign = { 25 | 'platform': '1', 26 | 'timestamp': '', 27 | 'dId': '', 28 | 'vName': app_version 29 | } 30 | 31 | def get_grant_code(token): 32 | response = requests.post(grant_code_url, json={ 33 | 'appCode': app_code, 34 | 'token': token, 35 | 'type': 0 36 | }, headers=header_login) 37 | resp = response.json() 38 | if response.status_code != 200: 39 | raise Exception(f'获得认证代码失败:{resp}') 40 | if resp.get('status') != 0: 41 | raise Exception(f'获得认证代码失败:{resp["msg"]}') 42 | return resp['data']['code'] 43 | 44 | def get_cred_by_token(token): 45 | grant_code = get_grant_code(token) 46 | return get_cred(grant_code) 47 | 48 | def get_cred(grant): 49 | resp = requests.post(cred_code_url, json={ 50 | 'code': grant, 51 | 'kind': 1 52 | }, headers=header_login).json() 53 | if resp['code'] != 0: 54 | raise Exception(f'获得cred失败:{resp["message"]}') 55 | return resp['data'] 56 | 57 | def get_sign_header(url: str, method, body, old_header,sign_token): 58 | h = json.loads(json.dumps(old_header)) 59 | p = parse.urlparse(url) 60 | if method.lower() == 'get': 61 | h['sign'], header_ca = generate_signature(sign_token, p.path, p.query) 62 | else: 63 | h['sign'], header_ca = generate_signature(sign_token, p.path, json.dumps(body)) 64 | for i in header_ca: 65 | h[i] = header_ca[i] 66 | return h 67 | 68 | def generate_signature(token: str, path, body_or_query): 69 | """ 70 | 获得签名头 71 | 接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dId,platform,timestamp,vName).toJSON() 72 | 将此字符串做HMAC加密,算法为SHA-256,密钥token为请求cred接口会返回的一个token值 73 | 再将加密后的字符串做MD5即得到sign 74 | :param token: 拿cred时候的token 75 | :param path: 请求路径(不包括网址) 76 | :param body_or_query: 如果是GET,则是它的query。POST则为它的body 77 | :return: 计算完毕的sign 78 | """ 79 | # 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2 80 | t = str(int(time.time()) - 2) 81 | token = token.encode('utf-8') 82 | header_ca = json.loads(json.dumps(header_for_sign)) 83 | header_ca['timestamp'] = t 84 | header_ca_str = json.dumps(header_ca, separators=(',', ':')) 85 | s = path + body_or_query + t + header_ca_str 86 | hex_s = hmac.new(token, s.encode('utf-8'), hashlib.sha256).hexdigest() 87 | md5 = hashlib.md5(hex_s.encode('utf-8')).hexdigest().encode('utf-8').decode('utf-8') 88 | print(f'算出签名: {md5}') 89 | return md5, header_ca -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi>=2021.10.8 2 | requests>=2.26.0 3 | --------------------------------------------------------------------------------