see you later
This commit is contained in:
parent
db6817d969
commit
2292fa4e43
27
.github/workflows/main.yml
vendored
27
.github/workflows/main.yml
vendored
@ -1,27 +0,0 @@
|
||||
name: "Genshin Impact Helper"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 22 * * *" # scheduled at 06:00 (UTC+8) everyday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Run sign
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
echo "${{ secrets.COOKIE }}" | tr '#' "\n" | xargs -I {} sh -c 'echo "{}" | python3 ./genshin.py'
|
||||
20
.github/workflows/sync.yml
vendored
20
.github/workflows/sync.yml
vendored
@ -1,20 +0,0 @@
|
||||
name: "Auto Sync Fork"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 16 * * *" # scheduled at 00:00 (UTC+8) everyday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
steps:
|
||||
- name: Fork sync
|
||||
uses: tgymnich/fork-sync@v1.2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
owner: y1ndan
|
||||
base: master
|
||||
head: master
|
||||
160
README.md
160
README.md
@ -1,159 +1 @@
|
||||
<div align="center">
|
||||
<h1 align="center">
|
||||
Genshin Impact Helper
|
||||
</h1>
|
||||
|
||||

|
||||
[](https://github.com/y1ndan/genshin-impact-helper/stargazers)
|
||||
[](https://github.com/y1ndan/genshin-impact-helper/network)
|
||||
[](https://github.com/y1ndan/genshin-impact-helper/issues)
|
||||
[](https://github.com/y1ndan/genshin-impact-helper/graphs/contributors)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=_M9lYFxkYD7yQQR2btyG3pkZWFys_I-l&authKey=evGDzE2eFVBm46jsHpgcWrokveg70Z9GKl3H45o0oJuia620UGeO27lDPG9gKb/2&noverify=0)
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 💭前言
|
||||
|
||||
> 吹水交流:[130516740](https://qm.qq.com/cgi-bin/qm/qr?k=_M9lYFxkYD7yQQR2btyG3pkZWFys_I-l&authKey=evGDzE2eFVBm46jsHpgcWrokveg70Z9GKl3H45o0oJuia620UGeO27lDPG9gKb/2&noverify=0)
|
||||
|
||||
原神是我见过的唯一一个游戏本体和签到福利分离的游戏,玩家为了签到还要额外下载米游社 App。
|
||||
|
||||
平心而论,目前的每日签到奖励真的不咋地,都知道是蚊子腿。事实上,你完全可以选择无视签到,不签也没啥大的损失;或者选择手动签到,但这样的话哪天忘记打卡了就很头疼。
|
||||
|
||||
我承认是馋了这 **6W+** 摩拉和紫色经验书的奖励,于是撸了这个项目,实现自动每日签到。
|
||||
|
||||
**如果觉得本项目对你有帮助,请顺手点个`Star`吧QAQ ♥**
|
||||
|
||||
## 🌀简介
|
||||
|
||||
Genshin Impact Helper 可以自动化为你获取原神每日福利。
|
||||
|
||||
## 💡特性
|
||||
|
||||
- [x] **自动签到** 程序会在每天早上自动执行签到流程,也可以随时通过部署教程的`步骤4`手动触发,具体时间参照[此处](.github/workflows/main.yml)
|
||||
- [x] **自动同步** 自动从上游源仓库拉取代码至复刻仓库
|
||||
- [x] **支持多服务器** 支持绑定单个官服或 Bilibili 服的米游社账号,目前不支持同时绑定多个服务器的账号
|
||||
- [x] **支持多账号** 不同`Cookie`值之间用`#`分开即可,如:`<Cookie1>#<Cookie2>`
|
||||
|
||||
## 📐部署
|
||||
|
||||
<details>
|
||||
<summary>查看教程</summary>
|
||||
|
||||
### 1. Fork 仓库
|
||||
|
||||
- 项目地址:[github/genshin-impact-helper](https://github.com/y1ndan/genshin-impact-helper)
|
||||
- 点击右上角`Fork`到自己的账号下
|
||||
|
||||
> 
|
||||
|
||||
### 2. 获取 Cookie
|
||||
|
||||
浏览器打开 https://bbs.mihoyo.com/ys/ 并登录账号
|
||||
|
||||
#### 2.1 方法一
|
||||
|
||||
- 按`F12`,打开`开发者工具`,找到`Network`并点击
|
||||
- 按`F5`刷新页面,按下图复制`Cookie`
|
||||
|
||||
> 
|
||||
|
||||
#### 2.2 方法二
|
||||
|
||||
- 复制以下代码
|
||||
|
||||
```
|
||||
var cookie = document.cookie;
|
||||
var ask = confirm('Cookie:' + cookie + '\n\n是否复制内容到剪切板?');
|
||||
if (ask == true) {
|
||||
copy(cookie);
|
||||
msg = cookie;
|
||||
} else {
|
||||
msg = 'Cancel';
|
||||
}
|
||||
```
|
||||
|
||||
- 按`F12`,打开`开发者工具`,找到`Console`并点击
|
||||
- 命令行粘贴代码并运行,获得类似`Cookie:xxxxxx`的输出信息
|
||||
- `xxxxxx`部分即为所需复制的`Cookie`,点击确定复制
|
||||
|
||||
### 3. 添加 Cookie 至 Secrets
|
||||
|
||||
- 回到项目页面,依次点击`Settings`-->`Secrets`-->`New secret`
|
||||
|
||||
> 
|
||||
|
||||
- 建立名为`COOKIE`的 secret,值为`步骤2`中复制的`Cookie`内容,最后点击`Add secret`
|
||||
|
||||
> 
|
||||
|
||||
### 4. 启用 Actions
|
||||
|
||||
> Actions 默认为关闭状态,Fork 之后需要手动执行一次,若成功运行其才会激活。
|
||||
|
||||
返回项目主页面,点击上方的`Actions`,再点击左侧的`Genshin Impact Helper`,再点击`Run workflow`
|
||||
|
||||
> 
|
||||
|
||||
</details>
|
||||
|
||||
至此,部署完毕。
|
||||
|
||||
## 🔍结果
|
||||
|
||||
当你完成上述流程,可以在`Actions`页面点击`Genshin Impact Helper`-->`build`-->`Run sign`查看结果。
|
||||
|
||||
<details>
|
||||
<summary>查看结果</summary>
|
||||
|
||||
### 签到成功
|
||||
|
||||
如果成功,会输出类似`"result": "Success"`的信息:
|
||||
|
||||
```
|
||||
2020-11-18T22:11:45 INFO Sleep for 100 seconds ...
|
||||
2020-11-18T22:13:26 INFO UID is 100***000
|
||||
2020-11-18T22:13:27 INFO {
|
||||
"result": "Success",
|
||||
"message": "{\"retcode\": 0, \"message\": \"OK\", \"data\": {\"code\": \"ok\"}}"
|
||||
}
|
||||
```
|
||||
|
||||
### 签到失败
|
||||
|
||||
如果失败,会输出类似`"result": "Failed"`的信息:
|
||||
|
||||
```
|
||||
2020-11-17T22:11:33 INFO Sleep for 54 seconds ...
|
||||
2020-11-17T22:12:28 INFO UID is 100***000
|
||||
2020-11-17T22:12:29 INFO {
|
||||
"result": "Failed",
|
||||
"message": "{\"data\": null, \"message\": \"请求异常\", \"retcode\": -401}"
|
||||
}
|
||||
Error: Process completed with exit code 255.
|
||||
```
|
||||
|
||||
同时你会收到一封来自GitHub、标题为`Run failed: Genshin Impact Helper - master`的邮件。
|
||||
|
||||
</details>
|
||||
|
||||
## 🔄更新
|
||||
|
||||
因为接口请求上可能发生一些变化,所以上游源代码需要作出更改来适配这些变化,如果你没有及时更新项目源代码,可能会导致签到失败。
|
||||
|
||||
为解决此问题,项目开启了自动同步上游源代码的工作流程。该功能生效于 2020 年 12 月 04 日之后复刻的项目。
|
||||
|
||||
若在此时间之前复刻,可按照以下步骤更新:
|
||||
|
||||
- 下载[sync.yml](https://raw.githubusercontent.com/y1ndan/genshin-impact-helper/master/.github/workflows/sync.yml)文件
|
||||
- 自行上传`sync.yml`文件至**你的 Fork 仓库**的`.github/workflows`目录下
|
||||
- 到`Actions`页面手动触发一次名为`Auto Sync Fork`的工作流程
|
||||
|
||||
## ❗️协议
|
||||
|
||||
使用 Genshin Impact Helper 即表明,您知情并同意:
|
||||
|
||||
- 此代码通过模拟浏览器使用 Cookies 登录米游社网页,点击页面完成签到来实现签到。功能通过官方公开的 API 实现,并非游戏外挂
|
||||
- 用户之 Cookie 被储存于 Github 服务器,只供本项目使用。若 Github 服务器被攻破,则您的 Cookie 有遭到泄露的风险。除此之外,开发者无权获取您的 Cookie;即使是用户,一旦创建完成`Secrets`,也无法再次从中查看 Cookie
|
||||
- Genshin Impact Helper 不会对您的任何损失负责,包括但不限于奖励回收、账号异常、刻晴被削、矿产被挖、核弹爆炸、第三次世界大战等
|
||||
# See you later
|
||||
251
genshin.py
251
genshin.py
@ -1,251 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import json
|
||||
import uuid
|
||||
import logging
|
||||
import time
|
||||
import random
|
||||
import hashlib
|
||||
import string
|
||||
from requests.exceptions import *
|
||||
|
||||
logging.basicConfig(
|
||||
level = logging.INFO,
|
||||
format = '%(asctime)s %(levelname)s %(message)s',
|
||||
datefmt = '%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
|
||||
class ConfMeta(type):
|
||||
@property
|
||||
def index_url(self):
|
||||
return 'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html'
|
||||
|
||||
@property
|
||||
def app_version(self):
|
||||
return '2.1.0'
|
||||
|
||||
@property
|
||||
def ua(self):
|
||||
return 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) ' \
|
||||
'AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/%s' \
|
||||
%(self.app_version)
|
||||
|
||||
|
||||
class Conf(metaclass=ConfMeta):
|
||||
pass
|
||||
|
||||
|
||||
class Roles(object):
|
||||
def __init__(self, cookie:str=None):
|
||||
if type(cookie) is not str:
|
||||
raise TypeError("%s want a %s but got %s" %(
|
||||
self.__class__, type(__name__), type(cookie)))
|
||||
|
||||
self._cookie = cookie
|
||||
self._url = "https://api-takumi.mihoyo.com/binding/api/" \
|
||||
"getUserGameRolesByCookie?game_biz=%s" %('hk4e_cn')
|
||||
|
||||
def get_header(self):
|
||||
actid = 'e202009291139501'
|
||||
ref = "%s?bbs_auth_required=%s&act_id=%s&utm_source=%s" \
|
||||
"&utm_medium=%s&utm_campaign=%s" %(
|
||||
Conf.index_url, 'true', actid, 'bbs', 'mys', 'icon')
|
||||
|
||||
return {
|
||||
'User-Agent': Conf.ua,
|
||||
'Referer': ref,
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Cookie': self._cookie
|
||||
}
|
||||
|
||||
def get_roles(self):
|
||||
try:
|
||||
jdict = json.loads(
|
||||
requests.Session().get(
|
||||
self._url, headers = self.get_header()).text)
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
raise HTTPError
|
||||
|
||||
return jdict
|
||||
|
||||
def get_rolesInfo(self):
|
||||
logging.info('Start getting user information ...')
|
||||
errstr = None
|
||||
|
||||
for i in range(1, 4):
|
||||
try:
|
||||
self._rolesInfo = self.get_roles()
|
||||
except HTTPError as e:
|
||||
logging.error("HTTP error when get user game roles, " \
|
||||
"retry %s time(s) ..." %(i))
|
||||
logging.error("error is %s" %(e))
|
||||
errstr = str(e)
|
||||
continue
|
||||
except KeyError as e:
|
||||
logging.error("Wrong response to get user game roles, " \
|
||||
"retry %s time(s) ..." %(i))
|
||||
logging.error("response is %s" %(e))
|
||||
errstr = str(e)
|
||||
continue
|
||||
except Exception as e:
|
||||
logging.error("Unknown error %s, die" %(e))
|
||||
errstr = str(e)
|
||||
raise
|
||||
else:
|
||||
break
|
||||
|
||||
try:
|
||||
self._rolesInfo
|
||||
except AttributeError:
|
||||
raise Exception(errstr)
|
||||
|
||||
return self._rolesInfo
|
||||
|
||||
|
||||
class Sign(object):
|
||||
def __init__(self, cookie:str=None):
|
||||
if type(cookie) is not str:
|
||||
raise TypeError("%s want a %s but got %s" %(
|
||||
self.__class__, type(__name__), type(cookie)))
|
||||
|
||||
self._cookie = cookie
|
||||
self._url = 'https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign'
|
||||
|
||||
# Provided by Steesha
|
||||
def md5(self, text):
|
||||
md5 = hashlib.md5()
|
||||
md5.update(text.encode())
|
||||
return (md5.hexdigest())
|
||||
|
||||
def get_DS(self):
|
||||
n = self.md5(Conf.app_version)
|
||||
i = str(int(time.time()))
|
||||
r = ''.join(random.sample(string.ascii_lowercase + string.digits, 6))
|
||||
c = self.md5("salt=" + n + "&t="+ i + "&r=" + r)
|
||||
return i + "," + r + "," + c
|
||||
|
||||
def get_header(self):
|
||||
actid = 'e202009291139501'
|
||||
ref = "%s?bbs_auth_required=%s&act_id=%s&utm_source=%s" \
|
||||
"&utm_medium=%s&utm_campaign=%s" %(
|
||||
Conf.index_url, 'true', actid, 'bbs', 'mys', 'icon')
|
||||
|
||||
return {
|
||||
'x-rpc-device_id': str(uuid.uuid3(
|
||||
uuid.NAMESPACE_URL, self._cookie)).replace('-','').upper(),
|
||||
# 1: ios
|
||||
# 2: android
|
||||
# 4: pc web
|
||||
# 5: mobile web
|
||||
'x-rpc-client_type': '5',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'User-Agent': Conf.ua,
|
||||
'Referer': ref,
|
||||
'x-rpc-app_version': Conf.app_version,
|
||||
'DS': self.get_DS(),
|
||||
'Cookie': self._cookie
|
||||
}
|
||||
|
||||
def run(self):
|
||||
# cn_gf01: 天空岛
|
||||
# cn_qd01: 世界树
|
||||
self._region = rolesList[i]['region']
|
||||
self._region_name = rolesList[i]['region_name']
|
||||
self._uid = rolesList[i]['game_uid']
|
||||
|
||||
data = {
|
||||
'act_id': 'e202009291139501',
|
||||
'region': self._region,
|
||||
'uid': self._uid
|
||||
}
|
||||
|
||||
logging.info('Start signing in the NO.%s role which UID is %s in %s ...' %(
|
||||
i+1, str(self._uid).replace(str(self._uid)[3:6],'***',1), self._region_name))
|
||||
try:
|
||||
jdict = json.loads(requests.Session().post(
|
||||
self._url, headers = self.get_header(),
|
||||
data = json.dumps(data, ensure_ascii=False)).text)
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
return jdict
|
||||
|
||||
|
||||
def makeResult(result:str, data=None):
|
||||
return json.dumps(
|
||||
{
|
||||
'result': result,
|
||||
'message': data
|
||||
},
|
||||
sort_keys=False, indent=2, ensure_ascii=False
|
||||
)
|
||||
|
||||
def notify(key, massage):
|
||||
if key != '':
|
||||
logging.info('正在推送通知...')
|
||||
url = 'https://sc.ftqq.com/%s.send' %(key)
|
||||
data = {'text':'原神签到小助手', 'desp':massage}
|
||||
try:
|
||||
jdict = json.loads(
|
||||
requests.Session().post(url, data = data).text)
|
||||
errmsg = jdict['errmsg']
|
||||
if errmsg == 'success':
|
||||
logging.info('推送通知成功')
|
||||
else:
|
||||
logging.info('推送通知失败')
|
||||
logging.error(jdict)
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
raise HTTPError
|
||||
|
||||
return jdict
|
||||
else:
|
||||
logging.info('未配置SCKEY,正在跳过推送...')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
secret = input().strip().rsplit(' ', 1)
|
||||
secret.append('')
|
||||
cookie=secret[0]
|
||||
sckey=secret[1]
|
||||
jstr = Roles(cookie).get_rolesInfo()
|
||||
result = makeResult('Failed', jstr)
|
||||
ret = -1
|
||||
|
||||
try:
|
||||
rolesList = jstr['data']['list']
|
||||
logging.info('Your account has been bound %s role(s)' %(len(rolesList)))
|
||||
|
||||
for i in range(len(rolesList)):
|
||||
seconds = random.randint(10, 300)
|
||||
logging.info('Sleep for %s seconds ...' %(seconds))
|
||||
time.sleep(seconds)
|
||||
|
||||
try:
|
||||
jdict = Sign(cookie).run()
|
||||
jstr = json.dumps(jdict, ensure_ascii=False)
|
||||
code = jdict['retcode']
|
||||
except Exception as e:
|
||||
jstr = str(e)
|
||||
|
||||
try:
|
||||
code
|
||||
except NameError:
|
||||
code = -1
|
||||
|
||||
# 0: success
|
||||
# -5003: already signed in
|
||||
if code in [0, -5003]:
|
||||
result = makeResult('Success', jstr)
|
||||
ret = 0
|
||||
|
||||
logging.info(result)
|
||||
|
||||
except Exception as e:
|
||||
logging.info(result)
|
||||
|
||||
notify(sckey, result)
|
||||
logging.info('签到完成!')
|
||||
exit(ret)
|
||||
@ -1 +0,0 @@
|
||||
requests==2.20.0
|
||||
Loading…
Reference in New Issue
Block a user