diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..83040a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Womsxd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config.json b/config.json new file mode 100644 index 0000000..d8d1322 --- /dev/null +++ b/config.json @@ -0,0 +1,17 @@ +{ + "mihoyobbs_Login_ticket": "", + "mihoyobbs_Stuid": "", + "mihoyobbs_Stoken": "", + "mihoyobbs_Cookies": "", + "mihoyobbs": { + "bbs_Gobal": true, + "bbs_Singin": true, + "bbs_Singin_multi": true, + "bbs_Singin_multi_list": [2, 5], + "bbs_Read_posts": true, + "bbs_Like_posts": true, + "bbs_Unlike": true, + "bbs_Share": true + }, + "genshin_AutoSingin": false +} \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..ed732ed --- /dev/null +++ b/config.py @@ -0,0 +1,86 @@ +import os +import json +from tools import log + +#这里的内容会自动获取 +mihoyobbs_Login_ticket = "" +mihoyobbs_Stuid = "" +mihoyobbs_Stoken = "" +#这里填入你的米游社Cookie +mihoyobbs_Cookies = "" +#这个dist里面的内容和米游社有关 +mihoyobbs = { + #全局开关,关闭之后下面的都不执行 + "bbs_Gobal": True, + #讨论区签到 + "bbs_Singin": True, + #多个讨论区签到 + "bbs_Singin_multi": True, + #指定签到讨论区 + "bbs_Singin_multi_list": [2,5], + #浏览3个帖子 + "bbs_Read_posts": True, + #完成5次点赞 + "bbs_Like_posts": True, + #完成后取消点赞 + "bbs_Unlike": True, + #分享帖子 + "bbs_Share": True, + } +#原神自动签到 +genshin_AutoSingin = True + +path = os.path.dirname(os.path.realpath(__file__)) + +def Load_config(): + with open(f"{path}/config.json", "r") as f: + data = json.load(f) + global mihoyobbs_Login_ticket + global mihoyobbs_Stuid + global mihoyobbs_Stoken + global mihoyobbs_Cookies + global mihoyobbs + mihoyobbs_Login_ticket = data["mihoyobbs_Login_ticket"] + mihoyobbs_Stuid = data["mihoyobbs_Stuid"] + mihoyobbs_Stoken = data["mihoyobbs_Stoken"] + mihoyobbs_Cookies = data["mihoyobbs_Cookies"] + mihoyobbs["bbs_Gobal"] = data["mihoyobbs"]["bbs_Gobal"] + mihoyobbs["bbs_Singin"] = data["mihoyobbs"]["bbs_Singin"] + mihoyobbs["bbs_Singin_multi"] = data["mihoyobbs"]["bbs_Singin_multi"] + mihoyobbs["bbs_Singin_multi_list"] = data["mihoyobbs"]["bbs_Singin_multi_list"] + mihoyobbs["bbs_Read_posts"] = data["mihoyobbs"]["bbs_Read_posts"] + mihoyobbs["bbs_Like_posts"] = data["mihoyobbs"]["bbs_Like_posts"] + mihoyobbs["bbs_Unlike"] = data["mihoyobbs"]["bbs_Unlike"] + mihoyobbs["bbs_Share"] = data["mihoyobbs"]["bbs_Share"] + genshin_AutoSingin = data["genshin_AutoSingin"] + f.close() + log.info("Config加载完毕") + +def Save_config(): + with open(f"{path}/config.json","r+") as f: + data = json.load(f) + data["mihoyobbs_Login_ticket"] = mihoyobbs_Login_ticket + data["mihoyobbs_Stuid"] = mihoyobbs_Stuid + data["mihoyobbs_Stoken"] = mihoyobbs_Stoken + f.seek(0) + f.truncate() + temp_Text = json.dumps(data, sort_keys=False, indent=4, separators=(', ', ': ')) + f.write(temp_Text) + f.flush() + f.close() + log.info("Config保存完毕") + +def Clear_cookies(): + with open(f"{path}/config.json","r+") as f: + data = json.load(f) + data["mihoyobbs_Login_ticket"] = "" + data["mihoyobbs_Stuid"] = "" + data["mihoyobbs_Stoken"] = "" + data["mihoyobbs_Cookies"] = "" + f.seek(0) + f.truncate() + temp_Text = json.dumps(data, sort_keys=False, indent=4, separators=(', ', ': ')) + f.write(temp_Text) + f.flush() + f.close() + log.info("Cookie删除完毕") \ No newline at end of file diff --git a/genshin.py b/genshin.py new file mode 100644 index 0000000..0933050 --- /dev/null +++ b/genshin.py @@ -0,0 +1,34 @@ +import time +import httpx +import tools +import config +import random +import setting + +class genshin: + def __init__(self) -> None: + self.headers = { + 'Accept': 'application/json, text/plain, */*', + 'DS': tools.Get_ds(web=True, web_old=True), + 'Origin': 'https://webstatic.mihoyo.com', + 'x-rpc-app_version': setting.mihoyobbs_Version_old, + 'User-Agent': 'Mozilla/5.0 (Linux; Android 9; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 miHoYoBBS/2.2.0', + 'x-rpc-client_type': setting.mihoyobbs_Client_type_web, + 'Referer': 'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon', + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'zh-CN,en-US;q=0.8', + 'X-Requested-With': 'com.mihoyo.hyperion', + "Cookie": config.mihoyobbs_Cookies + } + self.acc_List = self.Getacc_list() + #获取绑定的账号列表 + def Getacc_list(self) -> list: + temp_List = [] + req = httpx.get(setting.accinfo_Url, headers=self.headers) + data = req.json() + if (data["rercode"] != 0): + tools.log.warn("获取账号列表失败!") + exit() + for i in data["data"]["list"]: + temp_List.append([i["nickname"], i["game_uid"], i["region"]]) + return (temp_List) diff --git a/login.py b/login.py new file mode 100644 index 0000000..7abc1cc --- /dev/null +++ b/login.py @@ -0,0 +1,30 @@ +from tools import log +import config +import request +import setting + +def login(): + if (config.mihoyobbs_Cookies == ''): + log.error("请填入Cookies!") + exit() + temp_Cookies = {} + if ("login_ticket" in config.mihoyobbs_Cookies): + temp_Cookies = config.mihoyobbs_Cookies.split(";") + for i in temp_Cookies: + if (i.split("=")[0] == " login_ticket"): + config.mihoyobbs_Login_ticket = i.split("=")[1] + break + data = request.get(url=setting.cookieUrl.format(config.mihoyobbs_Login_ticket)) + if ("成功" in data["data"]["msg"]): + config.mihoyobbs_Stuid = str(data["data"]["cookie_info"]["account_id"]) + data = request.get(url=setting.cookieUrl2.format(config.mihoyobbs_Login_ticket, config.mihoyobbs_Stuid)) + config.mihoyobbs_Stoken = data["data"]["list"][0]["token"] + log.info("登录成功!") + log.info("正在保存Config!") + config.Save_config() + else: + log.error("cookie已失效,请重新登录米游社抓取cookie") + exit() + else: + log.error("cookie中没有'login_ticket'字段,请重新登录米游社,重新抓取cookie!") + exit() \ No newline at end of file diff --git a/mihoyobbs.py b/mihoyobbs.py new file mode 100644 index 0000000..214f5b2 --- /dev/null +++ b/mihoyobbs.py @@ -0,0 +1,82 @@ +import time +import httpx +import tools +import config +import random +import setting + + +class mihoyobbs: + def __init__(self): + self.headers = { + "DS": tools.Get_ds(web=False, web_old=False), + "cookie": f"stuid={config.mihoyobbs_Stuid};stoken={config.mihoyobbs_Stoken}", + "x-rpc-client_type": setting.mihoyobbs_Client_type, + "x-rpc-app_version": setting.mihoyobbs_Version, + "x-rpc-sys_version": "6.0.1", + "x-rpc-channel": "mihoyo", + "x-rpc-device_id": tools.Random_text(20) + tools.Random_text(12), + "x-rpc-device_name": tools.Random_text(random.randint(1, 10)), + "x-rpc-device_model": "Mi 10", + "Referer": "https://app.mihoyo.com", + "Host": "bbs-api.mihoyo.com", + "User-Agent": "okhttp/4.8.0" + } + self.postsList = self.Getlist() + #进行签到操作 + def Singin(self): + tools.log.info("正在签到......") + for i in setting.mihoyobbs_List_Use: + req = httpx.post(url=setting.signUrl.format(i["id"]), data="" ,headers=self.headers) + data = req.json() + if ("err" not in data["message"]): + tools.log.info(str(i["name"]+ data["message"])) + time.sleep(2) + else: + tools.log.info("签到失败,你的cookie可能已过期,请重新设置cookie。") + config.Clear_cookies() + exit() + #获取要帖子列表 + def Getlist(self) -> list: + temp_List = [] + tools.log.info("正在获取帖子列表......") + for i in setting.mihoyobbs_List_Use: + req = httpx.get(url=setting.listUrl.format(i["forumId"]), headers=self.headers) + data = req.json() + for n in range(10): + temp_List.append([data["data"]["list"][n]["post"]["post_id"], data["data"]["list"][n]["post"]["subject"]]) + tools.log.info("已获取{}个帖子".format(len(temp_List))) + time.sleep(2) + return (temp_List) + #看帖子 + def Readposts(self): + tools.log.info("正在看帖......") + for i in range(3): + req = httpx.get(url=setting.detailUrl.format(self.postsList[i][0]), headers=self.headers) + data = req.json() + if data["message"] == "OK": + tools.log.info("看帖:{} 成功".format(self.postsList[i][1])) + time.sleep(2) + #点赞 + def Likeposts(self): + tools.log.info("正在点赞......") + for i in range(5): + req = httpx.post(url=setting.likeUrl, headers=self.headers, + json={"post_id": self.postsList[i][0], "is_cancel": False}) + data = req.json() + if (data["message"] == "OK"): + tools.log.info("点赞:{} 成功".format(self.postsList[i][1])) + #判断取消点赞是否打开 + if (config.mihoyobbs["bbs_Unlike"] == True): + req = httpx.post(url=setting.likeUrl, headers=self.headers, + json={"post_id": self.postsList[i][0], "is_cancel": True}) + data = req.json() + if (data["message"] == "OK"): + tools.log.info("取消点赞:{} 成功".format(self.postsList[i][1])) + time.sleep(2) + def Share(self): + tools.log.info("正在分享......") + req = httpx.get(url=setting.shareUrl.format(self.postsList[0][0]), cookies=self.Cookie, headers=self.headers) + data = req.json() + if data["message"] == "OK": + tools.log.info("分享:{} 成功".format(self.postsList[0][1])) \ No newline at end of file diff --git a/request.py b/request.py new file mode 100644 index 0000000..0a1c66e --- /dev/null +++ b/request.py @@ -0,0 +1,26 @@ +import httpx + +#这里实际上应该加个"-> dict"但是考虑到请求可能失败的关系,所以直接不声明返回变量 +def get(url:str, **headers:dict): + try: + req = httpx.get(url, headers=headers) + return (req.json()) + except: + print("请求失败,网络错误!") + return ("") + +def post(url:str, data:dict, **headers:dict): + try: + req = httpx.post(url, data=data, headers=headers) + return (req.json()) + except: + print("请求失败,网络错误!") + return ("") + +def post_json(url:str, json, **headers:dict): + try: + req = httpx.post(url, json=json, headers=headers) + return (req.json()) + except: + print("请求失败,网络错误!") + return ("") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0209699 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +httpx == 0.18.1 \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 0000000..27e7f44 --- /dev/null +++ b/run.py @@ -0,0 +1,33 @@ +import login +import config +import setting +import mihoyobbs + +#初始化,加载配置 +config.Load_config() +#检测参数是否齐全,如果缺少就进行登入操作 +if (config.mihoyobbs_Login_ticket == "" or config.mihoyobbs_Stuid == "" or config.mihoyobbs_Stoken == ""): + #登入 + login.login() +#获取要使用的BBS列表 +#判断是否开启bbs_Singin_multi +if (config.mihoyobbs["bbs_Singin_multi"] == True): + for i in setting.mihoyobbs_List: + if (int(i["id"]) in config.mihoyobbs["bbs_Singin_multi_list"]): + setting.mihoyobbs_List_Use.append(i) +else: + #关闭bbs_Singin_multi后只签到大别墅 + for i in setting.mihoyobbs_List: + if (int(i["id"]) == 5): + setting.mihoyobbs_List_Use.append(i) +#米游社签到 +if(config.mihoyobbs["bbs_Gobal"] == True): + bbs = mihoyobbs.mihoyobbs() + if (config.mihoyobbs["bbs_Singin"] == True): + bbs.Singin() + if (config.mihoyobbs["bbs_Read_posts"] == True): + bbs.Readposts() + if (config.mihoyobbs["bbs_Like_posts"] == True): + bbs.Likeposts() + if (config.mihoyobbs["bbs_Share"] == True): + bbs.Share() diff --git a/setting.py b/setting.py new file mode 100644 index 0000000..c006674 --- /dev/null +++ b/setting.py @@ -0,0 +1,53 @@ +#米游社的Salt +mihoyobbs_Salt = "fd3ykrh7o1j54g581upo1tvpam0dsgtf" +mihoyobbs_Salt_web = "14bmu1mz0yuljprsfgpvjh3ju2ni468r" +mihoyobbs_Salt_web_old = "h8w582wxwgqvahcdkpvdhbh2w9casgfl" +#米游社的版本 +mihoyobbs_Version = "2.7.0" #Slat和Version相互对应 +mihoyobbs_Version_old = "2.3.0" +#米游社的客户端类型 +mihoyobbs_Client_type = "2" #1为ios 2为安卓 +mihoyobbs_Client_type_web = "5" #4为pc web 5为mobile web +#米游社的分区列表 +mihoyobbs_List = [{ + "id": "1", + "forumId": "1", + "name": "崩坏3", + "url": "https://bbs.mihoyo.com/bh3/" + },{ + "id": "2", + "forumId": "26", + "name": "原神", + "url": "https://bbs.mihoyo.com/ys/" + },{ + "id": "3", + "forumId": "30", + "name": "崩坏2", + "url": "https://bbs.mihoyo.com/bh2/" + },{ + "id": "4", + "forumId": "37", + "name": "未定事件簿", + "url": "https://bbs.mihoyo.com/wd/" + },{ + "id": "5", + "forumId": "34", + "name": "大别野", + "url": "https://bbs.mihoyo.com/dby/" + }] + +#Config Load之后run里面进行列表的选择 +mihoyobbs_List_Use= [] + +#米游社的API列表 +cookieUrl = "https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}" +cookieUrl2 = "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}" +signUrl = "https://bbs-api.mihoyo.com/apihub/sapi/signIn?gids={}" # post +listUrl = "https://bbs-api.mihoyo.com/post/api/getForumPostList?forum_id={}&is_good=false&is_hot=false&page_size=20&sort_type=1" +detailUrl = "https://bbs-api.mihoyo.com/post/api/getPostFull?post_id={}" +shareUrl = "https://bbs-api.mihoyo.com/apihub/api/getShareConf?entity_id={}&entity_type=1" +likeUrl = "https://bbs-api.mihoyo.com/apihub/sapi/upvotePost" # post json + +#原神自动签到相关的设置 +act_ID = "e202009291139501" +accinfo_Url = "https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn" \ No newline at end of file diff --git a/tools.py b/tools.py new file mode 100644 index 0000000..d4f95de --- /dev/null +++ b/tools.py @@ -0,0 +1,48 @@ +import logging +import time +import random +import string +import hashlib +import setting + +#Log输出 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s %(levelname)s %(message)s', + datefmt='%Y-%m-%dT%H:%M:%S') + +log = logger = logging + +#md5计算 +def MD5(text:str) -> str: + md5 = hashlib.md5() + md5.update(text.encode()) + return (md5.hexdigest()) + +#随机文本 +def Random_text(num:int) -> str: + return(''.join(random.sample(string.ascii_lowercase + string.digits, num))) + +#时间戳 +def Timestamp() -> int: + return(int(time.time())) + +#获取请求Header里的DS 当web为true则生成网页端的DS +def Get_ds(web:bool, web_old:bool) -> str: + if(web == True): + if(web_old == True): + n = setting.mihoyobbs_Salt_web_old + else: + n = setting.mihoyobbs_Salt_web + else: + n = setting.mihoyobbs_Salt + i = str(Timestamp()) + r = Random_text(6) + c = MD5("salt=" + n + "&t=" + i + "&r=" + r) + return (i + "," + r + "," + c) + +#获取明天早晨0点的时间戳 +def Nextday() -> int: + now_time = int(time.time()) + nextday_time = now_time - now_time % 86400 + time.timezone + 86400 + return (nextday_time) \ No newline at end of file