├── log └── daily.log ├── common ├── __init__.py ├── get_secrets.py ├── __pycache__ │ ├── config.cpython-36.pyc │ ├── dirs.cpython-36.pyc │ ├── logger.cpython-36.pyc │ ├── __init__.cpython-36.pyc │ ├── dy_badge.cpython-36.pyc │ ├── dy_glows.cpython-36.pyc │ ├── get_secrets.cpython-36.pyc │ ├── login_check.cpython-36.pyc │ ├── douyu_request.cpython-36.pyc │ └── send_message.cpython-36.pyc ├── dirs.py ├── logger.py ├── login_check.py ├── config.py ├── send_message.py ├── douyu_request.py ├── dy_badge.py └── dy_glows.py ├── .gitignore ├── docs └── img │ ├── Config.png │ ├── Linux1.png │ ├── Linux2.png │ ├── Path1.png │ ├── Path2.png │ ├── Path3.jpg │ ├── Secrets.png │ ├── cookie.png │ ├── Workfelow.png │ ├── WatchAction.png │ ├── WatchAction2.png │ └── WatchAction3.png ├── requirements.txt ├── config └── config.ini ├── .github └── workflows │ └── auto_donate_douyu.yml ├── main.py └── README.md /log/daily.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | venv/ 3 | log/ 4 | 5 | # Generated by MacOS 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /docs/img/Config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Config.png -------------------------------------------------------------------------------- /docs/img/Linux1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Linux1.png -------------------------------------------------------------------------------- /docs/img/Linux2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Linux2.png -------------------------------------------------------------------------------- /docs/img/Path1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Path1.png -------------------------------------------------------------------------------- /docs/img/Path2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Path2.png -------------------------------------------------------------------------------- /docs/img/Path3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Path3.jpg -------------------------------------------------------------------------------- /docs/img/Secrets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Secrets.png -------------------------------------------------------------------------------- /docs/img/cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/cookie.png -------------------------------------------------------------------------------- /common/get_secrets.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def get_secrets(item): 5 | return os.environ[item] 6 | -------------------------------------------------------------------------------- /docs/img/Workfelow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/Workfelow.png -------------------------------------------------------------------------------- /docs/img/WatchAction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/WatchAction.png -------------------------------------------------------------------------------- /docs/img/WatchAction2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/WatchAction2.png -------------------------------------------------------------------------------- /docs/img/WatchAction3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/docs/img/WatchAction3.png -------------------------------------------------------------------------------- /common/__pycache__/config.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/config.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/dirs.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/dirs.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/logger.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/logger.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/dy_badge.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/dy_badge.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/dy_glows.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/dy_glows.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/get_secrets.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/get_secrets.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/login_check.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/login_check.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/douyu_request.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/douyu_request.cpython-36.pyc -------------------------------------------------------------------------------- /common/__pycache__/send_message.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSlientnight/douyu_helper/HEAD/common/__pycache__/send_message.cpython-36.pyc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | packaging 2 | requests>=2.28.1 3 | lxml>=4.9.1 4 | selenium>=4.3.0 5 | jsonpath>=0.82 6 | loguru>=0.6.0 7 | webdriver-manager>=3.8.0 8 | 9 | -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | [Modechoose] 2 | # givemode为赠送模式,1为自定义模式,0为平均分配模式 3 | giveMode = 0 4 | 5 | [selfMode] 6 | # 房间号和荧光棒分配数量用逗号做分隔符,上下对应 7 | roomId = 93589 8 | giftCount = 50 9 | 10 | [log] 11 | logger_level = INFO 12 | stream_level = INFO 13 | file_level = INFO 14 | file_level_rf = INFO 15 | -------------------------------------------------------------------------------- /common/dirs.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | import os 3 | import time 4 | 5 | # 根目录 6 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 7 | # 设置地址 8 | CONFIG_DIR = os.path.join(BASE_DIR, "config") 9 | # 日志目录 10 | LOGS_DIR = os.path.join(BASE_DIR, "log") 11 | # 日志地址 12 | LOG_FILE = os.path.join(LOGS_DIR, "daily.log") 13 | 14 | 15 | def file_log(filepath): 16 | dir = LOGS_DIR 17 | if not os.path.exists(dir): 18 | os.makedirs(dir) 19 | return os.path.join(dir, "daily.log") 20 | 21 | 22 | if __name__ == '__main__': 23 | print(LOGS_DIR) 24 | -------------------------------------------------------------------------------- /common/logger.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from loguru import logger 4 | 5 | 6 | class Logger: 7 | __instance = None 8 | 9 | def __new__(cls, *args, **kwargs): 10 | if not cls.__instance: 11 | cls.__instance = super(Logger, cls).__new__(cls, *args, **kwargs) 12 | 13 | return cls.__instance 14 | 15 | def __init__(self): 16 | self.log = logger 17 | 18 | def info(self, msg): 19 | return self.log.info(msg) 20 | 21 | def debug(self, msg): 22 | return self.log.debug(msg) 23 | 24 | def warning(self, msg): 25 | return self.log.warning(msg) 26 | 27 | def error(self, msg): 28 | return self.log.error(msg) 29 | 30 | 31 | loggers = Logger() 32 | -------------------------------------------------------------------------------- /.github/workflows/auto_donate_douyu.yml: -------------------------------------------------------------------------------- 1 | name: Daily donate in 12306 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | schedule: 9 | - cron: '02 16 * * *' 10 | - cron: '02 19 * * *' 11 | 12 | jobs: 13 | dy_auto_donate_helper: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: 'Checkout' 17 | uses: actions/checkout@v2 18 | - name: 'Set up Python' 19 | uses: actions/setup-python@v1 20 | with: 21 | python-version: 3.7 22 | - name: 'Install requirements' 23 | run: pip install -r requirements.txt 24 | - name: 'Working' 25 | env: 26 | SERVERPUSHKEY: ${{ secrets.SERVERPUSHKEY }} 27 | COOKIES: ${{ secrets.COOKIES }} 28 | BARKURL: ${{ secrets.BARKURL }} 29 | run: | 30 | python main.py 31 | -------------------------------------------------------------------------------- /common/login_check.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | from common.douyu_request import dyreq 3 | from common.logger import logger 4 | import requests 5 | from common.get_secrets import get_secrets 6 | 7 | 8 | Is_login = 0 9 | login_url = "/wgapi/livenc/liveweb/follow/list" 10 | 11 | 12 | def is_login(): 13 | """ 14 | :return:返回登陆结果,用于主程序判断 15 | """ 16 | global Is_login 17 | login = dyreq.request("get", login_url).json() 18 | if login['error'] == 0: 19 | Is_login = 1 20 | logger.info("Cookie有效,登陆成功") 21 | else: 22 | logger.warning("登陆失败,请检查Cookie有效性") 23 | # check if get_secrets('BARKURL') starts with http 24 | barkurl = get_secrets('BARKURL') 25 | if barkurl.startswith('http'): 26 | requests.get(barkurl + "/斗鱼+Cookie+失效/登陆失败,请检查Cookie有效性") 27 | logger.warning("Notification Sent") 28 | 29 | return Is_login 30 | 31 | 32 | if __name__ == '__main__': 33 | is_login() 34 | -------------------------------------------------------------------------------- /common/config.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | import configparser 3 | from common.dirs import * 4 | import os 5 | 6 | conf_path = os.path.join(CONFIG_DIR, "config.ini") 7 | 8 | 9 | class Config(configparser.ConfigParser): 10 | 11 | def __init__(self): 12 | super().__init__() 13 | self.file = conf_path 14 | self.read(self.file, encoding="utf8") 15 | 16 | def get_conf(self, section, *options) -> dict: 17 | op_list = [] 18 | if options: 19 | for option in options: 20 | res = self.get(section, option) 21 | op_list.append(res) 22 | options_list = dict(zip(options, op_list)) 23 | else: 24 | options_list = dict(self.items(section)) 25 | return options_list 26 | 27 | def get_conf_list(self, section, option): 28 | return Config.get_conf(self, section, option)[option].split(",") 29 | 30 | 31 | conf = Config() 32 | 33 | if __name__ == '__main__': 34 | a = Config() 35 | b = a.get_conf("Modechoose")['givemode'] 36 | print(b) 37 | -------------------------------------------------------------------------------- /common/send_message.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | import requests 3 | from common.dirs import LOGS_DIR, LOG_FILE 4 | from common.logger import logger 5 | import re 6 | 7 | 8 | def log_reader(): 9 | with open(LOG_FILE, 'r', encoding="UTF-8") as lg: 10 | logs = lg.readlines() 11 | logs_str = ''.join(logs).replace("\n","\n\n") 12 | return logs_str 13 | 14 | 15 | def send_message(send_key): 16 | url = "https://sctapi.ftqq.com/{}.send".format(send_key) 17 | data = { 18 | "title": u"DouYu-Helper执行结果", 19 | "desp": log_reader() 20 | } 21 | if data['desp']: 22 | try: 23 | logger.info("------执行server酱推送------") 24 | requests.post(url, data=data) 25 | logger.info("------推送成功------") 26 | except Exception as e: 27 | logger.error(e) 28 | else: 29 | data = { 30 | "title": u"DouYu-Helper执行结果", 31 | "desp": "执行出现问题,日志为空" 32 | } 33 | requests.post(url, data=data) 34 | 35 | 36 | if __name__ == '__main__': 37 | send_message() -------------------------------------------------------------------------------- /common/douyu_request.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | from requests.sessions import session 3 | from common.get_secrets import get_secrets 4 | 5 | 6 | # 重写请求方法,便于直接获取结果 7 | class DYHTTPRequests: 8 | 9 | def __init__(self): 10 | self.cookie = get_secrets('COOKIES') 11 | self.session = session() 12 | self.header = { 13 | "Content-Type": "application/x-www-form-urlencoded", 14 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " 15 | "Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.81", 16 | "referer": "https://www.douyu.com", 17 | "Cookie": self.cookie 18 | } 19 | 20 | def request(self, method, path, **kwargs): 21 | url = "https://www.douyu.com" + path 22 | method.upper() 23 | return self.session.request(method, url=url, headers=self.header, **kwargs) 24 | 25 | def __del__(self): 26 | self.session.close() 27 | 28 | 29 | dyreq = DYHTTPRequests() 30 | if __name__ == '__main__': 31 | print(dyreq.request("get", "/lapi/member/api/getInfo").json()) 32 | glow_url = "/japi/prop/backpack/web/v1?rid=12306" 33 | print(dyreq.request("get", glow_url).json()) 34 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | from common.dy_glows import * 3 | from common.login_check import * 4 | from common.config import conf 5 | from common.dy_badge import * 6 | from common.logger import logger 7 | import math 8 | from common.get_secrets import get_secrets 9 | from common.send_message import send_message 10 | 11 | 12 | def run(): 13 | logger.info("------登录检查开始------") 14 | login_res = is_login() 15 | logger.info("------登录检查结束------") 16 | mode = int(conf.get_conf("Modechoose")['givemode']) 17 | if login_res: 18 | get_glow() 19 | try: 20 | glow_nums = get_own() 21 | assert glow_nums != 0 22 | if mode == 1: 23 | logger.info("当前选择模式为:自选模式") 24 | nums = conf.get_conf_list('selfMode', 'giftCount') 25 | room_list = conf.get_conf_list('selfMode', 'roomId') 26 | logger.info("------开始捐赠荧光棒------") 27 | for i in range(len(nums)): 28 | glow_donate(nums[i], room_list[i]) 29 | logger.info("------荧光棒捐赠结束------") 30 | get_need_exp() 31 | elif mode == 0: 32 | logger.info("当前选择模式为:平均分配模式") 33 | room_list = get_room_list() 34 | every_give = math.ceil(glow_nums / len(room_list)) 35 | left = int(glow_nums) - int(every_give) * (len(room_list) - 1) 36 | logger.info("------开始捐赠荧光棒------") 37 | for room in room_list: 38 | if room == room_list[-1]: 39 | glow_donate(left, room) 40 | else: 41 | glow_donate(every_give, room) 42 | logger.info("------荧光棒捐赠结束------") 43 | get_need_exp() 44 | else: 45 | logger.warning("配置错误,没有这种选项,请修改配置并重新执行") 46 | except Exception as e: 47 | logger.warning("背包中没有荧光棒,无法执行赠送,任务即将结束") 48 | logger.debug(e) 49 | else: 50 | logger.warning("未登录状态无法进行后续操作,任务已结束") 51 | try: 52 | server_key = get_secrets("SERVERPUSHKEY") 53 | send_message(server_key) 54 | except Exception as e: 55 | logger.info("当前未配置Server酱推送,任务结束") 56 | logger.debug(e) 57 | 58 | 59 | if __name__ == '__main__': 60 | run() 61 | -------------------------------------------------------------------------------- /common/dy_badge.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | from common.douyu_request import dyreq 3 | from common.logger import logger 4 | from common.config import conf 5 | from common.get_secrets import get_secrets 6 | from lxml import etree 7 | import re 8 | import math 9 | import requests 10 | 11 | 12 | def get_badge(): 13 | """ 14 | :return: 获取具有粉丝牌的房间号、当前经验、升级所需经验、升级还需要的经验 15 | """ 16 | badges_url = "/member/cp/getFansBadgeList" 17 | badges = dyreq.request("get", badges_url) 18 | html = etree.HTML(badges.text, etree.HTMLParser()) 19 | num = len(html.xpath('//*[@id="wrap"]/div/div[2]/div[2]/div[3]/table/tbody/tr')) 20 | re_now = r'(?<= )\d.*\d(?=\/\d)' 21 | re_up = r'(?<=\/)\d.*\d' 22 | badge_dict = {} 23 | exp_list = [] 24 | for path in range(num): 25 | path += 1 26 | room_id = html.xpath('//*[@id="wrap"]/div/div[2]/div[2]/div[3]/table/tbody/tr[%s]/@data-fans-room' % path)[ 27 | 0] 28 | anchor = html.xpath('//*[@id="wrap"]/div/div[2]/div[2]/div[3]/table/tbody/tr[%s]/td[2]/a/text()' % path)[0] 29 | exp = html.xpath('//*[@id="wrap"]/div/div[2]/div[2]/div[3]/table/tbody/tr[%s]/td[3]/text()' % path)[0] 30 | exp_now = float(re.findall(re_now, exp)[0]) 31 | up_grade = float(re.findall(re_up, exp)[0]) 32 | exp_need = round((up_grade - exp_now), 1) 33 | exp_list.append(exp_need) 34 | badge_dict[room_id] = anchor 35 | return badge_dict, exp_list 36 | 37 | 38 | def get_room_list(): 39 | """ 40 | :return:通过数组方式返回房间号 41 | """ 42 | room_list = [] 43 | for room in get_badge()[0]: 44 | room_list.append(room) 45 | return room_list 46 | 47 | 48 | def get_need_exp(): 49 | """ 50 | :return:通过数组方式返回升级所需经验 51 | """ 52 | nums = conf.get_conf_list('selfMode', 'giftCount') 53 | for i in range(len(get_badge()[1])): 54 | days_require = int(math.ceil(int(math.ceil(get_badge()[1][i])) / int(nums[i]))) 55 | logger.info("房间号%s升级还需%s点经验,还需%s天" % (get_room_list()[i], get_badge()[1][i], days_require)) 56 | barkurl = get_secrets('BARKURL') 57 | if barkurl.startswith('http'): 58 | requests.get(barkurl + "/房间号%s/升级还需%s点经验,+%s天" % (get_room_list()[i], get_badge()[1][i], days_require)) 59 | 60 | 61 | if __name__ == '__main__': 62 | a = get_room_list() 63 | get_need_exp() 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |