├── .gitignore ├── requirements.txt ├── README.md ├── LotteryResult.py ├── conf ├── user.conf └── bilibili.conf ├── LICENSE ├── Dockerfile ├── printer.py ├── rafflehandler.py ├── main.py ├── configloader.py ├── run.py ├── MultiRoom.py ├── biliconsole.py ├── schedule.py ├── OnlineHeart.py ├── TCP_monitor.py ├── statistics.py ├── connect.py ├── Tasks.py ├── login.py ├── utils.py ├── bilibiliCilent.py └── bilibili.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.iml 4 | *.xml 5 | log*.txt 6 | __pycache__/ 7 | .idea/ 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | rsa 3 | aiohttp 4 | termcolor 5 | colorama 6 | six 7 | brotli -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bilibili-live-tools 2 | 3 | ## 所有详情请移步[Wiki](https://github.com/Dawnnnnnn/bilibili-live-tools/wiki) 4 | -------------------------------------------------------------------------------- /LotteryResult.py: -------------------------------------------------------------------------------- 1 | from statistics import Statistics 2 | import asyncio 3 | 4 | 5 | class LotteryResult(): 6 | 7 | async def query(self): 8 | while 1: 9 | await Statistics().clean_TV() 10 | 11 | await asyncio.sleep(30) 12 | -------------------------------------------------------------------------------- /conf/user.conf: -------------------------------------------------------------------------------- 1 | # 自动送出过期礼物 1代表开启,0代表关闭,send_to_room填入房间号 2 | [gift] 3 | on/off = 0 4 | send_to_room = 0 5 | 6 | # 瓜子换硬币 7 | [coin] 8 | on/off = 0 9 | 10 | # 将当前佩戴的勋章亲密度送满 11 | [auto-gift] 12 | on/off = 0 13 | 14 | # 硬币换瓜子 15 | [coin2silver] 16 | on/off = 0 17 | num = 0 18 | 19 | # 保存所有运行信息到log文件,0为只保存Error和Warning信息 20 | [thoroughly_log] 21 | on/off = 1 22 | 23 | # 设置定时休眠时段,24h制,不同时间段间用;连接,如 12:30:00-14:00:00;01:00:00-08:00:00 24 | [regular_sleep] 25 | on/off = 0 26 | schedule = 27 | 28 | # 刷新所有灰色勋章 29 | [refresh_medals] 30 | on/off = 0 31 | 32 | # 根据房间号刷新灰色勋章(同时支持长短位房间号) 33 | [refresh_medals_by_roomid] 34 | on/off = 0 35 | room_ids = [3,6] 36 | 37 | # 自动送礼时把小心心也当作能送的礼物(优先刷新灰色勋章,剩余的才会送) 38 | [send_exheart] 39 | on/off = 0 40 | 41 | 42 | # 监控服务器 43 | [monitoy_server] 44 | on/off = 1 45 | host = 139.9.83.34 46 | port = 12344 47 | key = root 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dawnspace 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 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | MAINTAINER Dawnnnnnn <1050596704@qq.com> 3 | 4 | ENV LIBRARY_PATH=/lib:/usr/lib \ 5 | USER_NAME='' \ 6 | USER_PASSWORD='' 7 | 8 | WORKDIR /app 9 | 10 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ 11 | apk add --no-cache tzdata && \ 12 | cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ 13 | echo "Asia/Shanghai" > /etc/timezone && \ 14 | apk del tzdata && \ 15 | apk add --no-cache build-base git && \ 16 | git clone https://github.com/Dawnnnnnn/bilibili-live-tools.git /app && \ 17 | pip3 install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ && \ 18 | rm -r /var/cache/apk && \ 19 | rm -r /usr/share/man 20 | 21 | ENTRYPOINT git pull && \ 22 | pip3 install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ && \ 23 | sed -i ''"$(cat conf/bilibili.conf -n | grep "username =" | awk '{print $1}')"'c '"$(echo "username = ${USER_NAME}")"'' conf/bilibili.conf && \ 24 | sed -i ''"$(cat conf/bilibili.conf -n | grep "password =" | awk '{print $1}')"'c '"$(echo "password = ${USER_PASSWORD}")"'' conf/bilibili.conf && \ 25 | python ./run.py -------------------------------------------------------------------------------- /printer.py: -------------------------------------------------------------------------------- 1 | from colorama import init 2 | from termcolor import * 3 | import time 4 | import inspect 5 | 6 | init() 7 | 8 | 9 | class Printer(): 10 | instance = None 11 | 12 | def __new__(cls, thoroughly_log=None, *args, **kw): 13 | if not cls.instance: 14 | cls.instance = super(Printer, cls).__new__(cls) 15 | cls.instance.thoroughly_log = True if thoroughly_log == "1" else False 16 | return cls.instance 17 | 18 | def current_time(self): 19 | tmp = str( 20 | time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) 21 | return "[" + tmp + "]" 22 | 23 | def printer(self, string, info, color, printable=True): 24 | ctm = self.current_time() 25 | tmp = "[" + str(info) + "]" 26 | row = "[" + str(inspect.stack()[1][3]) + ":" + str( 27 | inspect.stack()[1][2]) + "]" 28 | if printable: 29 | msg = ("{:<22}{:<20}{:<10}{:<20}".format(str(ctm), str(row), str(tmp), str(string))) 30 | print(colored(msg, color), flush=True) 31 | if self.thoroughly_log or info in ["Error", "Warning"]: 32 | with open(f"log_{time.strftime('%Y%m')}.txt", "a+", encoding="utf-8") as f: 33 | f.write(msg + "\n") 34 | else: 35 | pass 36 | -------------------------------------------------------------------------------- /conf/bilibili.conf: -------------------------------------------------------------------------------- 1 | [normal] 2 | appkey = bca7e84c2d947ac6 3 | actionKey = appkey 4 | build = 6070600 5 | device = android 6 | mobi_app = android 7 | platform = android 8 | app_secret = 60698ba2f68e01ce44738920a0ffe768 9 | _CIDInfoUrl = http://live.bilibili.com/api/player?id=cid: 10 | _ChatPort = 2243 11 | _protocolversion = 2 12 | _ChatHost = livecmt-2.bilibili.com 13 | activity_name = 14 | access_key = 15 | cookie = 16 | csrf = 17 | uid = 18 | 19 | [dic-pcheaders] 20 | Accept = application/json, text/plain, */* 21 | User-Agent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 22 | Accept-Language = zh-CN,zh;q=0.9 23 | accept-encoding = gzip, deflate 24 | Host = api.live.bilibili.com 25 | cookie = 26 | 27 | [dic-appheaders] 28 | User-Agent = bili-universal/6570 CFNetwork/894 Darwin/17.4.0 29 | Accept-encoding = gzip 30 | Buvid = 000ce0b9b9b4e342ad4f421bcae5e0ce 31 | Display-ID = 146771405-1521008435 32 | Accept-Language = zh-CN 33 | Accept = text/html,application/xhtml+xml,*/*;q=0.8 34 | Connection = keep-alive 35 | Host = api.live.bilibili.com 36 | cookie = 37 | 38 | [account] 39 | username = 40 | password = 41 | 42 | [saved-session] 43 | access_key = 44 | cookie = 45 | csrf = 46 | uid = 47 | refresh_token = 48 | 49 | [types] 50 | int = _ChatPort _protocolversion 51 | 52 | -------------------------------------------------------------------------------- /rafflehandler.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import bilibiliCilent 3 | 4 | 5 | class Rafflehandler: 6 | instance = None 7 | 8 | def __new__(cls, *args, **kw): 9 | if not cls.instance: 10 | cls.instance = super(Rafflehandler, cls).__new__(cls, *args, **kw) 11 | cls.instance.list_activity = [] 12 | cls.instance.list_TV = [] 13 | return cls.instance 14 | 15 | async def run(self): 16 | while True: 17 | len_list_activity = len(self.list_activity) 18 | len_list_TV = len(self.list_TV) 19 | set_TV = set(self.list_TV) 20 | tasklist = [] 21 | for i in set_TV: 22 | task = asyncio.ensure_future(bilibiliCilent.handle_1_room_TV(i)) 23 | tasklist.append(task) 24 | if tasklist: 25 | await asyncio.wait(tasklist, return_when=asyncio.ALL_COMPLETED) 26 | else: 27 | pass 28 | 29 | del self.list_activity[:len_list_activity] 30 | del self.list_TV[:len_list_TV] 31 | if len_list_activity == 0 and len_list_TV == 0: 32 | await asyncio.sleep(1.1) 33 | else: 34 | await asyncio.sleep(1.0) 35 | 36 | def append2list_TV(self, real_roomid): 37 | self.list_TV.append(real_roomid) 38 | return 39 | 40 | def append2list_activity(self, text1): 41 | self.list_activity.append(text1) 42 | return 43 | 44 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | import subprocess, time, sys 4 | 5 | TIME = 3600 6 | CMD = "run.py" 7 | 8 | 9 | class Auto_Run(): 10 | def __init__(self, sleep_time, cmd): 11 | if sys.version_info < (3, 6): 12 | print("only support python 3.6 and later version") 13 | sys.exit(1111) 14 | self.sleep_time = sleep_time 15 | self.cmd = cmd 16 | self.ext = (cmd[-3:]).lower() 17 | self.p = None 18 | self.run() 19 | 20 | try: 21 | while 1: 22 | time.sleep(sleep_time * 20) 23 | if self.p.poll() is None: 24 | print("restarting......") 25 | self.p.kill() 26 | self.run() 27 | 28 | else: 29 | print("starting......") 30 | self.run() 31 | except KeyboardInterrupt as e: 32 | self.p.kill() 33 | print("exit???") 34 | 35 | def run(self): 36 | if self.ext == ".py": 37 | print('start OK!') 38 | # use now running python version, think multiple python installed and now use python3.6 to run 39 | python_path = sys.executable 40 | print("use the absolute path of python to run", python_path) 41 | self.p = subprocess.Popen([python_path, '%s' % self.cmd], stdin=sys.stdin, stdout=sys.stdout, 42 | stderr=sys.stderr, shell=False) 43 | else: 44 | pass 45 | 46 | 47 | app = Auto_Run(TIME, CMD) 48 | -------------------------------------------------------------------------------- /configloader.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import codecs 3 | 4 | def load_bilibili(file): 5 | cf_bilibili = configparser.ConfigParser() 6 | cf_bilibili.optionxform = str 7 | cf_bilibili.read_file(codecs.open(file, "r", "utf8")) 8 | dic_bilibili = cf_bilibili._sections 9 | dic_nomalised_bilibili = dic_bilibili['normal'].copy() 10 | dic_nomalised_bilibili['saved-session'] = dic_bilibili['saved-session'].copy() 11 | dic_nomalised_bilibili['account'] = dic_bilibili['account'].copy() 12 | if dic_nomalised_bilibili['account']['username']: 13 | pass 14 | else: 15 | username = input("# 输入帐号: ") 16 | password = input("# 输入密码: ") 17 | cf_bilibili.set('account', 'username', username) 18 | cf_bilibili.set('account', 'password', password) 19 | cf_bilibili.write(codecs.open(file, "w+", "utf8")) 20 | dic_nomalised_bilibili['account']['username'] = username 21 | dic_nomalised_bilibili['account']['password'] = password 22 | dic_bilibili_type = dic_bilibili['types'] 23 | # str to int 24 | for i in dic_bilibili_type['int'].split(): 25 | dic_nomalised_bilibili[i] = int(dic_bilibili['normal'][i]) 26 | for i in dic_bilibili.keys(): 27 | # print(i) 28 | if i[0:3] == 'dic': 29 | dic_nomalised_bilibili[i[4:]] = dic_bilibili[i] 30 | return dic_nomalised_bilibili 31 | 32 | 33 | 34 | 35 | def load_user(file): 36 | cf_user = configparser.ConfigParser() 37 | cf_user.read_file(codecs.open(file, "r", "utf8")) 38 | dic_user = cf_user._sections 39 | return dic_user 40 | 41 | 42 | def write2bilibili(dic): 43 | cf_bilibili = configparser.ConfigParser(interpolation=None) 44 | cf_bilibili.optionxform = str 45 | 46 | cf_bilibili.read_file(codecs.open("conf/bilibili.conf", "r", "utf8")) 47 | 48 | for i in dic.keys(): 49 | cf_bilibili.set('saved-session', i, dic[i]) 50 | 51 | cf_bilibili.write(codecs.open("conf/bilibili.conf", "w+", "utf8")) 52 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import utils 2 | from TCP_monitor import TCP_monitor 3 | from OnlineHeart import OnlineHeart 4 | from LotteryResult import LotteryResult 5 | from Tasks import Tasks 6 | from connect import connect 7 | from rafflehandler import Rafflehandler 8 | import asyncio 9 | from login import login 10 | from printer import Printer 11 | from statistics import Statistics 12 | from bilibili import bilibili 13 | import threading 14 | import biliconsole 15 | from schedule import Schedule 16 | import configloader 17 | import os 18 | 19 | fileDir = os.path.dirname(os.path.realpath('__file__')) 20 | file_user = fileDir + "/conf/user.conf" 21 | dic_user = configloader.load_user(file_user) 22 | 23 | 24 | loop = asyncio.get_event_loop() 25 | printer = Printer(dic_user['thoroughly_log']['on/off']) 26 | bilibili() 27 | Statistics() 28 | rafflehandler = Rafflehandler() 29 | biliconsole.Biliconsole() 30 | 31 | task = OnlineHeart() 32 | task2 = Tasks() 33 | task3 = LotteryResult() 34 | task4 = connect() 35 | 36 | tasks1 = [ 37 | login().login_new() 38 | ] 39 | loop.run_until_complete(asyncio.wait(tasks1)) 40 | 41 | console_thread = threading.Thread(target=biliconsole.controler) 42 | console_thread.start() 43 | 44 | 45 | 46 | tasks = [ 47 | task.run(), 48 | task2.run(), 49 | biliconsole.Biliconsole().run(), 50 | task4.create(), 51 | task3.query(), 52 | rafflehandler.run(), 53 | ] 54 | 55 | 56 | if dic_user['monitoy_server']['on/off'] == "1": 57 | monitor = TCP_monitor() 58 | task_tcp_conn = monitor.connectServer( 59 | dic_user['monitoy_server']['host'], dic_user['monitoy_server']['port'], dic_user['monitoy_server']['key']) 60 | task_tcp_heart = monitor.HeartbeatLoop() 61 | tasks.append(task_tcp_conn) 62 | tasks.append(task_tcp_heart) 63 | 64 | schedule = Schedule() 65 | if dic_user['regular_sleep']['on/off'] == "1": 66 | tasks.append(schedule.run(dic_user['regular_sleep']['schedule'])) 67 | Schedule().scheduled_sleep = True 68 | 69 | 70 | tasks = list(map(asyncio.ensure_future, tasks)) 71 | loop.run_until_complete(asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)) 72 | Printer().printer('\n'.join(map(repr, asyncio.Task.all_tasks())), "Info", "green") 73 | for task in tasks: 74 | Printer().printer(repr(task._state), "Info", "green") 75 | if task._state == 'FINISHED': 76 | Printer().printer(f"Task err: {repr(task.exception())}", "Error", "red") 77 | loop.close() 78 | 79 | console_thread.join() 80 | -------------------------------------------------------------------------------- /MultiRoom.py: -------------------------------------------------------------------------------- 1 | import random 2 | import asyncio 3 | from bilibili import bilibili 4 | from printer import Printer 5 | 6 | 7 | async def get_area_list(): 8 | response = await bilibili().req_area_list() 9 | json_response = await response.json(content_type=None) 10 | return [ area_info['id'] for area_info in json_response['data'] ] 11 | 12 | 13 | async def area2room(area_id): 14 | while True: 15 | try: 16 | url = "https://api.live.bilibili.com/room/v1/area/getRoomList?platform=web&parent_area_id=" + \ 17 | str(area_id) + "&cate_id=0&area_id=0&sort_type=online&page=1&page_size=30" 18 | response = await bilibili().bili_section_get(url) 19 | json_response = await response.json(content_type=None) 20 | checklen = len(json_response['data']) 21 | if not checklen: 22 | Printer().printer(f"{area_id}号分区当前无开播房间,5分钟后重新获取", "Error", "red") 23 | await asyncio.sleep(300) 24 | continue 25 | rand_num = random.randint(0, checklen-1) 26 | new_area_id = json_response['data'][rand_num]['parent_id'] 27 | if not new_area_id == int(area_id): 28 | continue 29 | area_room = json_response['data'][rand_num]['roomid'] 30 | state = await bilibili().check_room_state(area_room) 31 | if state == 1: 32 | new_area = str(new_area_id) + json_response['data'][rand_num]['parent_name'] 33 | return [area_room, new_area] 34 | else: 35 | Printer().printer("检测到获取房间未开播,1秒后尝试重新获取", "Error", "red") 36 | await asyncio.sleep(1) 37 | except Exception as e: 38 | Printer().printer(f"获取房间列表失败,5s后进行下次尝试 {repr(e)}", "Error", "red") 39 | await asyncio.sleep(5) 40 | 41 | 42 | async def check_state(area, roomid=None): 43 | if roomid is not None: 44 | response = await bilibili().check_room_info(roomid) 45 | json_response = await response.json(content_type=None) 46 | live_status = json_response['data']['live_status'] 47 | curr_area_name = json_response['data']['parent_area_name'] 48 | if live_status == 1 and curr_area_name in area: 49 | Printer().printer(f'[{area}分区] 房间 {roomid} 直播状态正常', "Info", "green") 50 | return [roomid, area] 51 | elif live_status != 1: 52 | Printer().printer(f"[{area}分区] 房间 {roomid} 已未直播!将切换监听房间", "Info", "green") 53 | else: 54 | # print(type(live_status), live_status, curr_area_name) 55 | Printer().printer(f"[{area}分区] 房间 {roomid} 已切换分区[{curr_area_name}]!将切换监听房间", "Info", "green") 56 | 57 | return await area2room(area[0]) 58 | 59 | 60 | async def get_all(area_list): 61 | return [await area2room(i) for i in area_list] 62 | -------------------------------------------------------------------------------- /biliconsole.py: -------------------------------------------------------------------------------- 1 | import utils 2 | from statistics import Statistics 3 | from printer import Printer 4 | import traceback 5 | import threading 6 | import asyncio 7 | 8 | 9 | def guide_of_console(): 10 | print(' _______________ ') 11 | print('| 欢迎使用本控制台      |') 12 | print('|1输出本次的参与抽奖统计   |') 13 | print('|2输出本次的抽奖结果统计   |') 14 | print('|3查看目前拥有礼物的统计   |') 15 | print('|4查看持有勋章状态      |') 16 | print('|5获取直播个人的基本信息   |') 17 | print('|6检查今日任务的完成情况   |') 18 | print('|7检查监控房间的开播情况   |') 19 | print('  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ') 20 | 21 | 22 | options = { 23 | '1': Statistics().getlist, 24 | '2': Statistics().getresult, 25 | '3': utils.fetch_bag_list, # async 26 | '4': utils.fetch_medal, # async 27 | '5': utils.fetch_user_info, # async 28 | '6': utils.check_taskinfo, # async 29 | '7': utils.reconnect, # async 30 | 'help': guide_of_console 31 | } 32 | 33 | 34 | def return_error(): 35 | print('命令无法识别,请重新输入') 36 | 37 | 38 | def controler(): 39 | while True: 40 | x = input('') 41 | # input and async 42 | if x in ['3', '4', '5', '6', '7']: 43 | answer = options.get(x, return_error) 44 | Biliconsole().append2list_console(answer) 45 | # normal 46 | else: 47 | options.get(x, return_error)() 48 | 49 | 50 | class Biliconsole(): 51 | instance = None 52 | 53 | def __new__(cls, *args, **kw): 54 | if not cls.instance: 55 | cls.instance = super(Biliconsole, cls).__new__(cls, *args, **kw) 56 | cls.instance.list_console = [] 57 | cls.lock = threading.Lock() 58 | return cls.instance 59 | 60 | def append2list_console(self, request): 61 | self.lock.acquire() 62 | self.list_console.append(request) 63 | self.lock.release() 64 | 65 | async def run(self): 66 | while True: 67 | len_list_console = len(self.list_console) 68 | tasklist = [] 69 | for i in self.list_console: 70 | if isinstance(i, list): 71 | # 对10号单独简陋处理 72 | for j in range(len(i[0])): 73 | if isinstance(i[0][j], list): 74 | i[0][j] = await i[0][j][1](*(i[0][j][0])) 75 | task = asyncio.ensure_future(i[1](*i[0])) 76 | else: 77 | task = asyncio.ensure_future(i()) 78 | tasklist.append(task) 79 | if tasklist: 80 | try: 81 | await asyncio.wait(tasklist, return_when=asyncio.ALL_COMPLETED) 82 | except Exception: 83 | Printer().printer(traceback.format_exc(), "Error", "red") 84 | # print('本批次结束') 85 | else: 86 | # print('本批次轮空') 87 | pass 88 | 89 | if len_list_console == 0: 90 | await asyncio.sleep(1) 91 | else: 92 | self.lock.acquire() 93 | del self.list_console[:len_list_console] 94 | self.lock.release() 95 | await asyncio.sleep(0.3) 96 | -------------------------------------------------------------------------------- /schedule.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | 4 | from printer import Printer 5 | 6 | 7 | sec_calc = lambda h, m, s: 3600 * int(h) + 60 * int(m) + float(s) 8 | time_minus = lambda t2, t1: (t2 - t1) % 86400 9 | time_str_calc = lambda sec: f'{sec//3600:02.0f}:{sec%3600//60:02.0f}:{sec%60:02.0f}' 10 | 11 | 12 | def sec_now(): 13 | time_tuple = time.localtime() 14 | return sec_calc(time_tuple.tm_hour, time_tuple.tm_min, time_tuple.tm_sec) 15 | 16 | 17 | class Schedule: 18 | instance = None 19 | 20 | def __new__(cls, *args, **kw): 21 | if not cls.instance: 22 | cls.instance = super(Schedule, cls).__new__(cls) 23 | cls.instance.scheduled_sleep = False 24 | return cls.instance 25 | 26 | async def run(self, schedule_str): 27 | if schedule_str == '': 28 | Printer().printer("请填入定时休眠时间段", "Warning", "red") 29 | self.scheduled_sleep = False 30 | return 31 | second_array = sorted([[sec_calc(*time_str.split(':')) for time_str in 32 | time_str_pair.split('-')] for time_str_pair in schedule_str.split(';')]) 33 | second_array = [[start, end] for (start, end) in second_array if start != end] 34 | if not len(second_array): 35 | Printer().printer("请填入有效时间段", "Warning", "red") 36 | self.scheduled_sleep = False 37 | return 38 | # 按顺序合并有overlap的时间段 39 | second_rearrng = [second_array[0]] 40 | pos = 1 41 | while pos < len(second_array): 42 | if time_minus(second_array[pos][0], second_rearrng[-1][0]) <= \ 43 | time_minus(second_rearrng[-1][1], second_rearrng[-1][0]): 44 | if time_minus(second_rearrng[-1][1], second_rearrng[-1][0]) < \ 45 | time_minus(second_array[pos][1], second_rearrng[-1][0]): 46 | second_rearrng[-1][1] = second_array[pos][1] 47 | else: 48 | second_rearrng.append(second_array[pos]) 49 | pos += 1 50 | # 考虑最后一个跨0点时间段覆盖最开始几个时间段端点的情况 51 | if second_rearrng[-1][1] < second_rearrng[-1][0]: 52 | while len(second_rearrng) > 1: 53 | if second_rearrng[-1][1] > second_rearrng[0][0]: 54 | if second_rearrng[-1][1] < second_rearrng[0][1]: 55 | second_rearrng[-1][1] = second_rearrng[0][1] 56 | del second_rearrng[0] 57 | else: 58 | break 59 | sec_sequence = __import__('functools').reduce(lambda x, y: x+y, second_rearrng) 60 | 61 | sec_init = sec_now() 62 | for i in range(len(sec_sequence)): 63 | if sec_sequence[i] > sec_init: 64 | stage = i 65 | break 66 | else: 67 | stage = len(sec_sequence)-1 if sec_sequence[-1] < sec_sequence[-2] else 0 68 | # 当前时间在0时后且在最后一个包含0时的时间段内 69 | if stage == 0 and sec_init < sec_sequence[-1] < sec_sequence[-2]: 70 | stage = len(sec_sequence)-1 71 | 72 | if stage % 2 == 1: 73 | self.scheduled_sleep = True 74 | Printer().printer(f"当前处于定时休眠时间段内,下一次取消休眠时间为 {time_str_calc(sec_sequence[stage])}", "Info", "green") 75 | else: 76 | self.scheduled_sleep = False 77 | Printer().printer(f"当前处于定时休眠时间段外,下一次开始休眠时间为 {time_str_calc(sec_sequence[stage])}", "Info", "green") 78 | while True: 79 | sleep_time = (sec_sequence[stage] - sec_now()) % 86400 80 | # 避免因误差刚好过了下个时间点 81 | sleep_time = 0 if sleep_time > 86395 else sleep_time 82 | await asyncio.sleep(sleep_time) 83 | stage += 1 84 | stage = stage % len(sec_sequence) 85 | if stage % 2 == 0: 86 | Printer().printer(f"结束定时休眠,下一次开始休眠时间为 {time_str_calc(sec_sequence[stage])}", "Info", "green") 87 | self.scheduled_sleep = False 88 | else: 89 | Printer().printer(f"开始定时休眠,本次结束休眠时间为 {time_str_calc(sec_sequence[stage])}", "Info", "green") 90 | self.scheduled_sleep = True 91 | -------------------------------------------------------------------------------- /OnlineHeart.py: -------------------------------------------------------------------------------- 1 | from bilibili import bilibili 2 | from login import login 3 | import time 4 | import traceback 5 | import datetime 6 | import asyncio 7 | import queue 8 | from statistics import Statistics 9 | from printer import Printer 10 | 11 | 12 | def CurrentTime(): 13 | currenttime = int(time.mktime(datetime.datetime.now().timetuple())) 14 | return str(currenttime) 15 | 16 | 17 | class OnlineHeart: 18 | 19 | async def apppost_heartbeat(self): 20 | return await bilibili().apppost_heartbeat() 21 | 22 | async def pcpost_heartbeat(self): 23 | response = await bilibili().pcpost_heartbeat() 24 | return response 25 | 26 | async def heart_gift(self): 27 | await bilibili().heart_gift() 28 | 29 | async def check_winner(self, i, g, start_time): 30 | # 开奖5s后检查是否中奖 31 | await asyncio.sleep(time.mktime(time.strptime(start_time, '%Y-%m-%d %H:%M:%S')) - time.time() + 5) 32 | response2 = await bilibili().get_winner_info(i, g) 33 | json_response2 = await response2.json(content_type=None) 34 | for winner in json_response2["data"]["winnerList"]: 35 | if winner["uid"] == bilibili().dic_bilibili['uid']: 36 | Printer().printer(f'实物抽奖中中奖: {winner["giftTitle"]}', "Lottery", "cyan") 37 | Statistics().add_to_result(winner["giftTitle"], 1) 38 | 39 | async def draw_lottery(self): 40 | black_list = ["123", "1111", "测试", "測試", "测一测", "ce-shi", "test", "T-E-S-T", "lala", "我是抽奖标题", "压测", # 已经出现 41 | "測一測", "TEST", "Test", "t-e-s-t"] # 合理猜想 42 | former_lottery = queue.Queue(maxsize=4) 43 | [former_lottery.put(True) for _ in range(4)] 44 | for i in range(590, 800): 45 | response = await bilibili().get_lotterylist(i) 46 | json_response = await response.json() 47 | former_lottery.get() 48 | former_lottery.put(not json_response['code']) 49 | if json_response['code'] == 0: 50 | title = json_response['data']['title'] 51 | check = len(json_response['data']['typeB']) 52 | for g in range(check): 53 | join_end_time = json_response['data']['typeB'][g]['join_end_time'] 54 | join_start_time = json_response['data']['typeB'][g]['join_start_time'] 55 | status = json_response['data']['typeB'][g]['status'] 56 | ts = CurrentTime() 57 | if int(join_end_time) > int(ts) > int(join_start_time) and status == 0: 58 | jp_list = '&'.join([jp['jp_name'] for jp in json_response['data']['typeB'][g]['list']]) 59 | for k in black_list: 60 | if k in title or k in jp_list: 61 | Printer().printer(f"检测到 {i} 号疑似钓鱼类测试抽奖『{title}>>>{jp_list}』" + \ 62 | ",默认不参与,请自行判断抽奖可参与性", "Warning", "red") 63 | break 64 | else: 65 | if bilibili().black_status: 66 | Printer().printer(f"黑屋休眠,跳过『{title}>>>{jp_list}』抽奖", "Info", "green") 67 | continue 68 | response1 = await bilibili().get_gift_of_lottery(i, g) 69 | json_response1 = await response1.json(content_type=None) 70 | Printer().printer(f"参与『{title}>>>{jp_list}』抽奖回显: {json_response1}", "Lottery", "cyan") 71 | start_time = json_response['data']['typeB'][g]["startTime"] 72 | asyncio.ensure_future(self.check_winner(i, g, start_time)) 73 | else: 74 | if not any(former_lottery.queue): # 检查最近4个活动id是否都-400 75 | break 76 | await asyncio.sleep(0.2) 77 | del former_lottery 78 | 79 | async def run(self): 80 | while 1: 81 | try: 82 | Printer().printer("心跳", "Info", "green") 83 | response = await self.pcpost_heartbeat() 84 | json_response = await response.json(content_type=None) 85 | if json_response['code'] in [3, -101]: 86 | Printer().printer(f"cookie过期,将重新登录", "Error", "red") 87 | login().login() 88 | response1 = await self.apppost_heartbeat() 89 | json_response1 = await response1.json(content_type=None) 90 | if json_response1['code'] == -101: 91 | # '{"code":-101,"message":"账号未登录","ttl":1}' 92 | Printer().printer(f"token过期,尝试刷新", "Error", "red") 93 | login().refresh_token() 94 | await self.heart_gift() 95 | await self.draw_lottery() 96 | await asyncio.sleep(300) 97 | except: 98 | await asyncio.sleep(10) 99 | Printer().printer(traceback.format_exc(), "Error", "red") 100 | -------------------------------------------------------------------------------- /TCP_monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2019/12/30 13:24 4 | # @Author : Dawnnnnnn 5 | # @Contact: 1050596704@qq.com 6 | 7 | import asyncio 8 | import json 9 | import traceback 10 | from struct import Struct 11 | 12 | from printer import Printer 13 | from rafflehandler import Rafflehandler 14 | 15 | 16 | class TCP_monitor(): 17 | header_struct = Struct('>I') 18 | 19 | def __init__(self): 20 | self._reader = None 21 | self._writer = None 22 | self.connected = False 23 | 24 | def _encapsulate(self, str_body): 25 | body = str_body.encode('utf-8') 26 | len_body = len(body) 27 | len_header = 4 28 | header = self.header_struct.pack(len_body + len_header) 29 | return header + body 30 | 31 | def close_connection(self): 32 | self._writer.close() 33 | self.connected = False 34 | 35 | async def connectServer(self, host, port, key): 36 | while True: 37 | try: 38 | reader, writer = await asyncio.open_connection(host, port) 39 | self._reader = reader 40 | self._writer = writer 41 | Printer().printer(f'监控服务器连接成功', "Info", "green") 42 | await self.send_bytes(self.Auth_Key(key)) 43 | self.connected = True 44 | except Exception: 45 | Printer().printer(f'连接无法建立,请检查本地网络状况,2s后重连', "Error", "red") 46 | self.connected = False 47 | await asyncio.sleep(2) 48 | continue 49 | await self.ReceiveMessageLoop() 50 | Printer().printer(f'与服务器连接断开,2s后尝试重连', "Error", "red") 51 | self.connected = False 52 | await asyncio.sleep(2) 53 | 54 | async def HeartbeatLoop(self): 55 | while True: 56 | try: 57 | while not self.connected: 58 | await asyncio.sleep(0.5) 59 | while self.connected: 60 | await self.send_bytes(self.Heartbeat()) 61 | await asyncio.sleep(25) 62 | except: 63 | traceback.print_exc() 64 | 65 | def Auth_Key(self, key): 66 | dict_enter = { 67 | "cmd": "Auth", 68 | "data": {"key": key}, 69 | "code": 0 70 | } 71 | str_enter = json.dumps(dict_enter) 72 | bytes_enter = self._encapsulate(str_body=str_enter) 73 | return bytes_enter 74 | 75 | def Heartbeat(self): 76 | dict_enter = { 77 | "cmd": "HeartBeat", 78 | "data": {}, 79 | "code": 0 80 | } 81 | str_enter = json.dumps(dict_enter) 82 | bytes_enter = self._encapsulate(str_body=str_enter) 83 | return bytes_enter 84 | 85 | async def send_bytes(self, bytes_data) -> bool: 86 | try: 87 | self._writer.write(bytes_data) 88 | await self._writer.drain() 89 | except asyncio.CancelledError: 90 | return False 91 | except Exception: 92 | return False 93 | return True 94 | 95 | async def ReadSocketData(self): 96 | try: 97 | header = await asyncio.wait_for(self._reader.read(4), timeout=35.0) 98 | except Exception: 99 | Printer().printer("与服务器连接断开", "Error", "red") 100 | self.connected = False 101 | return False 102 | if len(header) == 0: 103 | return False 104 | try: 105 | len_body, = self.header_struct.unpack_from(header) 106 | except: 107 | Printer().printer(f"unpack_from出现错误,header:{header} len(header):{len(header)}", "Error", "red") 108 | self.connected = False 109 | return False 110 | if not len_body: 111 | return False 112 | try: 113 | body = await self._reader.read(len_body - 4) 114 | Printer().printer(f"接收到服务器的header数据{header} body数据{body}", "DEBUG", "yellow") 115 | except Exception: 116 | Printer().printer("与服务器连接断开", "Error", "red") 117 | self.connected = False 118 | return False 119 | if body is None: 120 | return False 121 | try: 122 | body = body.decode('utf-8') 123 | body = body.replace("True", "true").replace("False", "false").replace("None", "null") 124 | except: 125 | Printer().printer(f"body.decode出现错误,body:{body}", "Error", "red") 126 | self.connected = False 127 | return False 128 | try: 129 | json_data = json.loads(body) 130 | await self.parseDanMu(json_data) 131 | except Exception: 132 | Printer().printer(f"json.loads出现错误,body:{body}", "Error", "red") 133 | self.connected = False 134 | return False 135 | return True 136 | 137 | async def ReceiveMessageLoop(self): 138 | while self.connected: 139 | tmp = await self.ReadSocketData() 140 | if not tmp: 141 | break 142 | 143 | async def parseDanMu(self, dic): 144 | 145 | cmd = dic.get('cmd') 146 | if cmd is None: 147 | Printer().printer(dic, "Error", "red") 148 | return 149 | if cmd == 'HeartBeat': 150 | pass 151 | elif cmd == 'Storm': 152 | pass 153 | elif cmd == 'Guard': 154 | pass 155 | elif cmd == 'PKLottery': 156 | pass 157 | elif cmd == 'Raffle': 158 | Rafflehandler().append2list_TV(dic["data"]["RoomId"]) 159 | else: 160 | Printer().printer(dic, "Info", "green") 161 | -------------------------------------------------------------------------------- /statistics.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import asyncio 3 | import traceback 4 | import time 5 | import utils 6 | from printer import Printer 7 | 8 | 9 | # 13:30 ---> 13.5 10 | def decimal_time(): 11 | now = datetime.datetime.now() 12 | return now.hour + now.minute / 60.0 13 | 14 | 15 | class Statistics: 16 | instance = None 17 | 18 | def __new__(cls, *args, **kw): 19 | if not cls.instance: 20 | cls.instance = super(Statistics, cls).__new__(cls, *args, **kw) 21 | cls.instance.init_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) 22 | cls.instance.activity_raffleid_list = [] 23 | cls.instance.activity_roomid_list = [] 24 | cls.instance.TV_raffleid_dict = {} 25 | # cls.instance.TV_roomid_list = [] 26 | 27 | # cls.instance.pushed_event = [] 28 | cls.instance.pushed_TV = [] 29 | cls.instance.monitor = {} 30 | 31 | # cls.instance.joined_event = [] 32 | cls.instance.joined_TV = [] 33 | cls.instance.total_area = 1 34 | cls.instance.area_basis = 1 35 | cls.instance.result = {} 36 | 37 | return cls.instance 38 | 39 | def adjust_basis(self, area_list: [int]): 40 | self.total_area = len(area_list) 41 | self.area_basis = sum([2**(area_id-1) for area_id in area_list]) 42 | 43 | def add_to_result(self, type, num): 44 | self.result[type] = self.result.get(type, 0) + int(num) 45 | 46 | def getlist(self): 47 | print('本次运行开始于:', self.init_time) 48 | # print(self.joined_event) 49 | # print(self.joined_TV) 50 | # print('本次推送活动抽奖次数:', len(self.pushed_event)) 51 | print('本次推送广播抽奖次数:', len(self.pushed_TV)) 52 | # print('本次参与活动抽奖次数:', len(self.joined_event)) 53 | print('本次参与广播抽奖次数:', len(self.joined_TV)) 54 | 55 | def getresult(self): 56 | print('本次参与抽奖结果为:') 57 | for k, v in self.result.items(): 58 | print('{}X{}'.format(k, v)) 59 | 60 | def delete_0st_TVlist(self): 61 | del self.TV_roomid_list[0] 62 | del self.TV_raffleid_dict[0] 63 | 64 | async def clean_TV(self): 65 | for raffleid in list(self.TV_raffleid_dict): 66 | await asyncio.sleep(0.2) 67 | if self.TV_raffleid_dict[raffleid] < time.time(): 68 | del self.TV_raffleid_dict[raffleid] 69 | 70 | # response = await bilibili().get_TV_result(self.TV_roomid_list[0], self.TV_raffleid_list[0]) 71 | # json_response = await response.json() 72 | # try: 73 | # if json_response['msg'] == '正在抽奖中..': 74 | # break 75 | # data = json_response['data'] 76 | # if not len(data): 77 | # # Printer().printer(f"房间 {self.TV_roomid_list[0]} 广播道具抽奖 {self.TV_raffleid_list[0]} 结果: {json_response['msg']}", 78 | # # "Lottery", "cyan") 79 | # # print('B站错误返回,报已错过') 80 | # continue 81 | # if data['gift_id'] != '-1': 82 | # Printer().printer(f"房间 {self.TV_roomid_list[0]} 广播道具抽奖 {self.TV_raffleid_list[0]} 结果: {data['gift_name']}X{data['gift_num']}", 83 | # "Lottery", "cyan") 84 | # self.add_to_result(data['gift_name'], int(data['gift_num'])) 85 | # else: 86 | # Printer().printer(f"房间 {self.TV_roomid_list[0]} 广播道具抽奖 {self.TV_raffleid_list[0]} 结果: {json_response['msg']}", 87 | # "Lottery", "cyan") 88 | # 89 | # self.delete_0st_TVlist() 90 | # except Exception: 91 | # Printer().printer(f'获取到异常抽奖结果: {json_response}', "Warning", "red") 92 | 93 | if self.monitor: 94 | check_list = list(self.monitor) 95 | await asyncio.sleep(3) 96 | 97 | for roomid in check_list: 98 | check_str = bin(self.monitor[roomid]).replace('0b', '') 99 | # print(roomid, check_str) 100 | check_int = [int(check) for check in check_str] 101 | area_sum = sum(check_int) 102 | try: 103 | if area_sum in [1, self.total_area]: 104 | pass 105 | elif area_sum == 2: 106 | to_check = [-index for index in range(-1, -len(check_int)-1, -1) if check_int[index] == 1] 107 | Printer().printer(f"发现监控重复 {to_check}", "Info", "green") 108 | await utils.check_area_list(to_check) 109 | elif area_sum == self.total_area-1: 110 | to_check = [len(bin(self.area_basis-self.monitor[roomid]).replace('0b', ''))] 111 | Printer().printer(f"发现监控缺失 {to_check}", "Info", "green") 112 | await utils.check_area_list(to_check) 113 | else: 114 | Printer().printer(f"对房间 {roomid} 的抽奖出现意外的监控情况({check_str}/{self.area_basis:b}),启动分区检查", "Info", "green") 115 | await utils.reconnect() 116 | except Exception: 117 | Printer().printer(traceback.format_exc(), "Error", "red") 118 | finally: 119 | del self.monitor[roomid] 120 | 121 | def append_to_TVlist(self, raffleid, time_limit): 122 | self.TV_raffleid_dict[raffleid] = time.time() + time_limit 123 | self.joined_TV.append(decimal_time()) 124 | 125 | def append2pushed_TVlist(self, real_roomid, area_id): 126 | self.pushed_TV.append(decimal_time()) 127 | self.monitor[real_roomid] = self.monitor.get(real_roomid, 0) | 2**(int(area_id)-1) 128 | 129 | def check_TVlist(self, raffleid): 130 | if self.TV_raffleid_dict.get(raffleid, None) is None: 131 | return True 132 | return False 133 | -------------------------------------------------------------------------------- /connect.py: -------------------------------------------------------------------------------- 1 | import time 2 | import asyncio 3 | import traceback 4 | import MultiRoom 5 | from statistics import Statistics 6 | from bilibiliCilent import bilibiliClient 7 | from printer import Printer 8 | 9 | 10 | class connect(): 11 | instance = None 12 | areas = [] 13 | roomids = [] 14 | tasks = {} 15 | 16 | def __new__(cls, *args, **kw): 17 | if not cls.instance: 18 | cls.instance = super(connect, cls).__new__(cls, *args, **kw) 19 | cls.instance.danmuji = None 20 | cls.instance.tag_reconnect = False 21 | cls.instance.check_time = {} 22 | cls.instance.handle_area = [] 23 | return cls.instance 24 | 25 | async def create(self): 26 | area_list = await MultiRoom.get_area_list() 27 | tmp = await MultiRoom.get_all(area_list) 28 | # 新的战疫分区直播间实际上没有弹幕区 29 | tmp = [x for x in tmp if '战疫' not in x[1]] 30 | for i in range(len(tmp)): 31 | connect.roomids.append(tmp[i][0]) 32 | for n in range(len(tmp)): 33 | connect.areas.append(tmp[n][1]) 34 | Printer().printer(f"获取到分区列表: {connect.areas}", "Info", "green") 35 | ckd_area_list = [int(area[:1]) for area in connect.areas] 36 | Statistics().adjust_basis(ckd_area_list) 37 | init_time = time.time() 38 | for area in connect.areas: 39 | self.check_time[area] = init_time 40 | for roomid,area in zip(connect.roomids, connect.areas): 41 | self.danmuji = bilibiliClient(roomid,area) 42 | task1 = asyncio.ensure_future(self.danmuji.connectServer()) 43 | task2 = asyncio.ensure_future(self.danmuji.HeartbeatLoop()) 44 | connect.tasks[roomid] = [task1, task2] 45 | 46 | while True: 47 | await asyncio.sleep(10) 48 | try: 49 | for roomid in list(connect.tasks): 50 | item = connect.tasks.get(roomid, None) 51 | if (item is None) or (not len(item)): 52 | Printer().printer(f"房间 {roomid} 任务已被清理,跳过", "Info", "green") 53 | continue 54 | task1 = item[0] 55 | task2 = item[1] 56 | if task1.done() == True or task2.done() == True: 57 | area = connect.areas[connect.roomids.index(roomid)] 58 | Printer().printer(f"[{area}分区] 房间 {roomid} 任务出现异常", "Info", "green") 59 | await self.check_area(roomid=roomid, area=area, mandatory_recreate=True) 60 | else: 61 | # Printer().printer(f"[{area}分区] 房间 {roomid} 任务保持正常", "Info", "green") 62 | pass 63 | except Exception: 64 | Printer().printer(traceback.format_exc(), "Error", "red") 65 | 66 | async def check_connect(self, skip_area=None): 67 | if self.tag_reconnect: 68 | Printer().printer("connect检查任务已在运行", "Info", "green") 69 | return 70 | else: 71 | self.tag_reconnect = True 72 | # print('connect类属性:', connect.roomids, connect.areas) 73 | if not len(connect.roomids): 74 | # 说明程序刚启动还没获取监控房间,此时也不需要检查 75 | self.tag_reconnect = False 76 | return 77 | else: 78 | for roomid, area in list(zip(connect.roomids, connect.areas)): 79 | if (skip_area is not None) and (skip_area == area): 80 | continue 81 | else: 82 | await self.check_area(roomid=roomid, area=area) 83 | Printer().printer("connect检查任务已完成", "Info", "green") 84 | self.tag_reconnect = False 85 | 86 | async def check_area(self, area, roomid=None, mandatory_check=False, mandatory_recreate=False): 87 | if len(str(area)) == 1: 88 | area = [tem_area for tem_area in connect.areas if str(area) in tem_area][0] 89 | if roomid is None: 90 | roomid = connect.roomids[connect.areas.index(area)] 91 | 92 | if not mandatory_check and time.time() - self.check_time[area] < 60: 93 | Printer().printer(f"[{area}分区] 近已检查,跳过", "Info", "green") 94 | [ckd_roomid, ckd_area] = [roomid, area] 95 | else: 96 | # Printer().printer(f"[{area}分区] {roomid} 检查开始", "Info", "green") 97 | self.check_time[area] = time.time() 98 | [ckd_roomid, ckd_area] = await MultiRoom.check_state(roomid=roomid, area=area) 99 | self.check_time[area] = time.time() 100 | if mandatory_recreate or ckd_roomid != roomid: 101 | await self.recreate(new_roomid=ckd_roomid, area=ckd_area) 102 | 103 | async def recreate(self, area, new_roomid=None): 104 | if area in self.handle_area: 105 | Printer().printer(f"[{area}分区] 重连任务已在处理", "Info", "green") 106 | return 107 | else: 108 | self.handle_area.append(area) 109 | # Printer().printer(f"[{area}分区] 重连任务开始处理", "Info", "green") 110 | try: 111 | old_roomid = connect.roomids[connect.areas.index(area)] 112 | item = connect.tasks[old_roomid] 113 | task1 = item[0] 114 | task2 = item[1] 115 | if not task1.done(): 116 | task1.cancel() 117 | if not task2.done(): 118 | task2.cancel() 119 | connect.tasks[old_roomid] = [] 120 | 121 | if new_roomid is None: 122 | self.check_time[area] = time.time() 123 | [new_roomid, new_area] = await MultiRoom.check_state(area) 124 | self.check_time[area] = time.time() 125 | else: 126 | new_area = area 127 | 128 | if not new_roomid == old_roomid: 129 | connect.roomids.remove(old_roomid) 130 | connect.areas.remove(area) 131 | del connect.tasks[old_roomid] 132 | connect.roomids.append(new_roomid) 133 | connect.areas.append(new_area) 134 | connect.tasks[new_roomid] = [] 135 | Printer().printer(f"更新监听房间列表{connect.roomids} {connect.areas}","Info","green") 136 | 137 | self.danmuji = bilibiliClient(new_roomid, new_area) 138 | task11 = asyncio.ensure_future(self.danmuji.connectServer()) 139 | task21 = asyncio.ensure_future(self.danmuji.HeartbeatLoop()) 140 | connect.tasks[new_roomid] = [task11, task21] 141 | except Exception: 142 | Printer().printer(traceback.format_exc(), "Error", "red") 143 | # Printer().printer(f"[{area}分区] 重连任务处理完毕", "Info", "green") 144 | self.handle_area.remove(area) 145 | -------------------------------------------------------------------------------- /Tasks.py: -------------------------------------------------------------------------------- 1 | from bilibili import bilibili 2 | import datetime 3 | import time 4 | import asyncio 5 | import traceback 6 | import os 7 | import configloader 8 | import utils 9 | import ast 10 | from printer import Printer 11 | 12 | 13 | class Tasks: 14 | 15 | def __init__(self): 16 | fileDir = os.path.dirname(os.path.realpath('__file__')) 17 | file_user = fileDir + "/conf/user.conf" 18 | self.dic_user = configloader.load_user(file_user) 19 | 20 | # 获取每日包裹奖励 21 | async def Daily_bag(self): 22 | response = await bilibili().get_dailybag() 23 | json_response = await response.json() 24 | for i in range(0, len(json_response['data']['bag_list'])): 25 | Printer().printer(f"获得-{json_response['data']['bag_list'][i]['bag_name']}-成功", "Info", "green") 26 | 27 | def CurrentTime(self): 28 | currenttime = str(int(time.mktime(datetime.datetime.now().timetuple()))) 29 | return currenttime 30 | 31 | # 签到功能 32 | async def DoSign(self): 33 | response = await bilibili().get_dosign() 34 | temp = await response.json(content_type=None) 35 | Printer().printer(f"签到状态:{temp['message']}", "Info", "green") 36 | 37 | # 应援团签到 38 | async def link_sign(self): 39 | response = await bilibili().get_grouplist() 40 | json_response = await response.json(content_type=None) 41 | check = len(json_response['data']['list']) 42 | group_id_list = [] 43 | owner_uid_list = [] 44 | for i in range(0, check): 45 | group_id = json_response['data']['list'][i]['group_id'] 46 | owner_uid = json_response['data']['list'][i]['owner_uid'] 47 | group_id_list.append(group_id) 48 | owner_uid_list.append(owner_uid) 49 | for (i1, i2) in zip(group_id_list, owner_uid_list): 50 | response = await bilibili().assign_group(i1, i2) 51 | json_response = await response.json(content_type=None) 52 | if json_response['code'] == 0: 53 | if (json_response['data']['status']) == 1: 54 | Printer().printer(f"应援团{i1}已应援过", "Info", "green") 55 | if (json_response['data']['status']) == 0: 56 | Printer().printer(f"应援团{i1}应援成功,获得{json_response['data']['add_num']}点亲密度", "Info", "green") 57 | else: 58 | Printer().printer(f"应援团{i1}应援失败,{json_response}", "Error", "red") 59 | 60 | async def send_gift(self): 61 | if self.dic_user['gift']['on/off'] == '1': 62 | argvs, x = await utils.fetch_bag_list(printer=False) 63 | for i in range(0, len(argvs)): 64 | giftID = argvs[i][0] 65 | giftNum = argvs[i][1] 66 | bagID = argvs[i][2] 67 | roomID = self.dic_user['gift']['send_to_room'] 68 | await utils.send_gift_web(roomID, giftID, giftNum, bagID) 69 | if not argvs: 70 | Printer().printer(f"没有将要过期的礼物~", "Info", "green") 71 | 72 | async def auto_send_gift(self): 73 | if self.dic_user['auto-gift']['on/off'] == "1": 74 | a = await utils.fetch_medal(printer=False) 75 | # res = await bilibili().gift_list() 76 | # json_res = await res.json() 77 | # temp_dic = {} 78 | # for j in range(0, len(json_res['data'])): 79 | # price = json_res['data'][j]['price'] 80 | # id = json_res['data'][j]['id'] 81 | # temp_dic[id] = price 82 | temp_dic = {1: 100, 6: 1000} 83 | if self.dic_user['send_exheart']['on/off'] == "1": 84 | temp_dic = {1: 100, 6: 1000, 30607: 5000} 85 | x, temp = await utils.fetch_bag_list(printer=False) 86 | roomid = a[0] 87 | today_feed = a[1] 88 | day_limit = a[2] 89 | left_num = int(day_limit) - int(today_feed) 90 | calculate = 0 91 | for i in range(0, len(temp)): 92 | gift_id = int(temp[i][0]) 93 | gift_num = int(temp[i][1]) 94 | bag_id = int(temp[i][2]) 95 | expire = int(temp[i][3]) 96 | if gift_id in [1, 6] and expire != 0: 97 | if (gift_num * (temp_dic[gift_id] / 100) < left_num): 98 | calculate = calculate + temp_dic[gift_id] / 100 * gift_num 99 | tmp2 = temp_dic[gift_id] / 100 * gift_num 100 | await utils.send_gift_web(roomid, gift_id, gift_num, bag_id) 101 | left_num = left_num - tmp2 102 | elif left_num - temp_dic[gift_id] / 100 >= 0: 103 | tmp = (left_num) / (temp_dic[gift_id] / 100) 104 | tmp1 = (temp_dic[gift_id] / 100) * int(tmp) 105 | calculate = calculate + tmp1 106 | await utils.send_gift_web(roomid, gift_id, tmp, bag_id) 107 | left_num = left_num - tmp1 108 | Printer().printer(f"自动送礼共送出亲密度为{int(calculate)}的礼物", "Info", "green") 109 | 110 | async def doublegain_coin2silver(self): 111 | if self.dic_user['doublegain_coin2silver']['on/off'] == "1": 112 | response0 = await bilibili().request_doublegain_coin2silver() 113 | json_response0 = await response0.json() 114 | response1 = await bilibili().request_doublegain_coin2silver() 115 | json_response1 = await response1.json() 116 | print(json_response0['msg'], json_response1['msg']) 117 | 118 | async def coin2silver(self): 119 | if self.dic_user['coin2silver']['on/off'] == '1' and int(self.dic_user['coin2silver']['num']) > 0: 120 | response = await bilibili().coin2silver_web(self.dic_user['coin2silver']['num']) 121 | json_response = await response.json() 122 | Printer().printer(f"硬币兑换银瓜子状态:{json_response['msg']}", "Info", "green") 123 | 124 | async def sliver2coin(self): 125 | if self.dic_user['coin']['on/off'] == '1': 126 | response1 = await bilibili().silver2coin_app() 127 | json_response1 = await response1.json() 128 | Printer().printer(f"银瓜子兑换硬币状态:{json_response1['msg']}", "Info", "green") 129 | 130 | async def refresh_medals(self): 131 | if self.dic_user['refresh_medals']['on/off'] == '1': 132 | await utils.refresh_all_gray_medals() 133 | 134 | async def refresh_medals_by_roomid(self): 135 | if self.dic_user['refresh_medals_by_roomid']['on/off'] == "1": 136 | roomids = ast.literal_eval(self.dic_user['refresh_medals_by_roomid']['room_ids']) 137 | await utils.refresh_medals_by_roomids(roomids) 138 | 139 | async def get_rooms(self): 140 | room_ids = [] 141 | for _ in range(3): 142 | response = await bilibili().request_fetchmedal() 143 | json_response = await response.json(content_type=None) 144 | if json_response['code']: 145 | continue 146 | # 有时候dict获取不完整,包括最后一项"roomid"的后半部分缺失 147 | elif all(["roomid" not in medal for medal in json_response['data']['fansMedalList']]): 148 | continue 149 | else: 150 | break 151 | 152 | for i in range(0, len(json_response['data']['fansMedalList'])): 153 | short_room_id = json_response['data']['fansMedalList'][i].get('roomid', None) 154 | if short_room_id is None: 155 | continue 156 | response1 = await bilibili().get_room_info(short_room_id) 157 | json_response1 = await response1.json(content_type=None) 158 | long_room_id = json_response1['data']['room_info']['room_id'] 159 | room_ids.append(long_room_id) 160 | return room_ids 161 | 162 | async def XE_heartbeat(self, room_ids, room_id): 163 | index_num = 24 // len(room_ids) 164 | index_num += 1 if 24 % len(room_ids) else 0 165 | data = await bilibili().heart_beat_e(room_id) 166 | for index in range(1, index_num + 1): 167 | try: 168 | # print(f"房间{room_id}休眠{data['heartbeat_interval']}s后开始第 {index} 次") 169 | await asyncio.sleep(data['heartbeat_interval']) 170 | response = await bilibili().heart_beat_x(index, data, room_id) 171 | response = await response.json(content_type=None) 172 | data['ets'] = response['data']['timestamp'] 173 | data['secret_key'] = response['data']['secret_key'] 174 | data['heartbeat_interval'] = response['data']['heartbeat_interval'] 175 | except: 176 | pass 177 | 178 | async def run(self): 179 | while 1: 180 | try: 181 | Printer().printer(f"开始执行每日任务", "Info", "green") 182 | await self.DoSign() 183 | room_ids = await self.get_rooms() 184 | coroutine_list = [] 185 | for room_id in room_ids: 186 | coroutine_list.append(self.XE_heartbeat(room_ids, room_id)) 187 | if coroutine_list: 188 | await asyncio.wait(coroutine_list) 189 | await self.refresh_medals_by_roomid() 190 | await self.refresh_medals() 191 | await self.Daily_bag() 192 | await self.link_sign() 193 | await self.send_gift() 194 | await self.sliver2coin() 195 | await self.coin2silver() 196 | await self.auto_send_gift() 197 | await utils.reconnect() 198 | await asyncio.sleep(21600) 199 | except: 200 | await asyncio.sleep(10) 201 | Printer().printer(traceback.format_exc(), "Error", "red") 202 | -------------------------------------------------------------------------------- /login.py: -------------------------------------------------------------------------------- 1 | from bilibili import bilibili 2 | from printer import Printer 3 | import base64 4 | import configloader 5 | import requests 6 | import time 7 | 8 | 9 | # temporary app parameter 10 | # appkey = '4409e2ce8ffd12b8' 11 | # build = '101800' 12 | # device = 'android_tv_yst' 13 | # mobi_app = 'android_tv_yst' 14 | # app_secret = '59b43e04ad6965f34319062b478f83dd' 15 | 16 | app_headers = { 17 | "User-Agent": "Mozilla/5.0 BiliDroid/5.58.0 (bbcallen@gmail.com)", 18 | "Accept-encoding": "gzip", 19 | "Buvid": "XZ11bfe2654a9a42d885520a680b3574582eb3", 20 | "Display-ID": "146771405-1521008435", 21 | "Device-Guid": "2d0bbec5-df49-43c5-8a27-ceba3f74ffd7", 22 | "Device-Id": "469a6aaf431b46f8b58a1d4a91d0d95b202004211125026456adffe85ddcb44818", 23 | "Accept-Language": "zh-CN", 24 | "Accept": "text/html,application/xhtml+xml,*/*;q=0.8", 25 | "Connection": "keep-alive", 26 | 'cookie': '' 27 | } 28 | 29 | class login(): 30 | auto_captcha_times = 3 31 | 32 | def normal_login(self, username, password): 33 | # url = 'https://passport.bilibili.com/api/oauth2/login' //旧接口 34 | # url = "https://passport.bilibili.com/api/v2/oauth2/login" 35 | url = "https://passport.bilibili.com/api/v3/oauth2/login" 36 | params_dic = { 37 | "actionKey": bilibili().dic_bilibili["actionKey"], 38 | "appkey": bilibili().dic_bilibili["appkey"], 39 | "build": bilibili().dic_bilibili["build"], 40 | "captcha": '', 41 | "device": bilibili().dic_bilibili["device"], 42 | "mobi_app": bilibili().dic_bilibili["mobi_app"], 43 | "password": password, 44 | "platform": bilibili().dic_bilibili["platform"], 45 | "username": username 46 | } 47 | temp_params = '&'.join([f'{key}={value}' for key, value in params_dic.items()]) 48 | sign = bilibili().calc_sign(temp_params) 49 | # headers = {"Content-type": "application/x-www-form-urlencoded"} 50 | payload = f'{temp_params}&sign={sign}' 51 | response = requests.post(url, params=payload, headers=app_headers) 52 | return response 53 | 54 | def login_with_captcha(self, username, password): 55 | headers = { 56 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 57 | 'cookie': "sid=hxt5szbb" 58 | } 59 | s = requests.session() 60 | url = "https://passport.bilibili.com/captcha" 61 | res = s.get(url, headers=headers) 62 | tmp1 = base64.b64encode(res.content) 63 | for _ in range(login.auto_captcha_times): 64 | try: 65 | captcha = bilibili().cnn_captcha(tmp1) 66 | break 67 | except Exception: 68 | Printer().printer("验证码识别服务器连接失败", "Error", "red") 69 | login.auto_captcha_times -= 1 70 | else: 71 | try: 72 | from PIL import Image 73 | from io import BytesIO 74 | img = Image.open(BytesIO(res.content)) 75 | img.show() 76 | captcha = input('输入验证码\n').strip() 77 | except ImportError: 78 | Printer().printer("安装 Pillow 库后重启,以弹出验证码图片", "Error", "red") 79 | exit() 80 | 81 | params_dic = { 82 | "actionKey": bilibili().dic_bilibili["actionKey"], 83 | "appkey": bilibili().dic_bilibili["appkey"], 84 | "build": bilibili().dic_bilibili["build"], 85 | "captcha": captcha, 86 | "device": bilibili().dic_bilibili["device"], 87 | "mobi_app": bilibili().dic_bilibili["mobi_app"], 88 | "password": password, 89 | "platform": bilibili().dic_bilibili["platform"], 90 | "username": username 91 | } 92 | temp_params = '&'.join([f'{key}={value}' for key, value in params_dic.items()]) 93 | sign = bilibili().calc_sign(temp_params) 94 | payload = f'{temp_params}&sign={sign}' 95 | headers = app_headers.copy() 96 | headers['cookie'] = "sid=hxt5szbb" 97 | url = "https://passport.bilibili.com/api/v3/oauth2/login" 98 | response = s.post(url, params=payload, headers=headers) 99 | return response 100 | 101 | # def access_token_2_cookies(self, access_token): 102 | # params = f"access_key={access_token}&appkey={appkey}&gourl=https%3A%2F%2Faccount.bilibili.com%2Faccount%2Fhome" 103 | # url = f"https://passport.bilibili.com/api/login/sso?{params}&sign={bilibili().calc_sign(params, app_secret)}" 104 | # response = requests.get(url, allow_redirects=False) 105 | # return response.cookies.get_dict(domain=".bilibili.com") 106 | 107 | def login(self): 108 | username = str(bilibili().dic_bilibili['account']['username']) 109 | password = str(bilibili().dic_bilibili['account']['password']) 110 | if username != "": 111 | while True: 112 | response = bilibili().request_getkey() 113 | value = response.json()['data'] 114 | key = value['key'] 115 | Hash = str(value['hash']) 116 | calcd_username, calcd_password = bilibili().calc_name_passw(key, Hash, username, password) 117 | response = self.normal_login(calcd_username, calcd_password) 118 | while response.json()['code'] == -105: 119 | response = self.login_with_captcha(calcd_username, calcd_password) 120 | if response.json()['code'] == -662: # "can't decrypt rsa password~" 121 | Printer().printer("打码时间太长key失效,重试", "Error", "red") 122 | continue 123 | if response.json()['code'] == -449: 124 | # {'code': -449, 'message': '服务繁忙, 请稍后再试', 'ts': 1593853665} 125 | Printer().printer("服务繁忙,10分钟后重试", "Error", "red") 126 | Printer().printer(f"疑似登录接口失效,请联系开发者 {response.json()}", "Warning", "red") 127 | time.sleep(600) 128 | continue 129 | break 130 | try: 131 | access_key = response.json()['data']['token_info']['access_token'] 132 | refresh_token = response.json()['data']['token_info']['refresh_token'] 133 | cookie = response.json()['data']['cookie_info']['cookies'] 134 | cookie_format = "" 135 | for i in range(len(cookie)): 136 | cookie_format = cookie_format + cookie[i]['name'] + "=" + cookie[i]['value'] + ";" 137 | bilibili().dic_bilibili['csrf'] = cookie[0]['value'] 138 | bilibili().dic_bilibili['access_key'] = access_key 139 | bilibili().dic_bilibili['refresh_token'] = refresh_token 140 | bilibili().dic_bilibili['cookie'] = cookie_format 141 | bilibili().dic_bilibili['uid'] = cookie[1]['value'] 142 | bilibili().dic_bilibili['pcheaders']['cookie'] = cookie_format 143 | bilibili().dic_bilibili['appheaders']['cookie'] = cookie_format 144 | dic_saved_session = { 145 | 'csrf': cookie[0]['value'], 146 | 'access_key': access_key, 147 | 'refresh_token': refresh_token, 148 | 'cookie': cookie_format, 149 | 'uid': cookie[1]['value'] 150 | } 151 | configloader.write2bilibili(dic_saved_session) 152 | Printer().printer(f"登录成功", "Info", "green") 153 | except: 154 | Printer().printer(f"登录失败,错误信息为:{response.json()}", "Error", "red") 155 | 156 | async def login_new(self): 157 | if bilibili().dic_bilibili['saved-session']['cookie']: 158 | Printer().printer(f"复用cookie", "Info", "green") 159 | bilibili().load_session(bilibili().dic_bilibili['saved-session']) 160 | else: 161 | return self.login() 162 | 163 | def refresh_token(self): 164 | url = "https://passport.bilibili.com/api/v2/oauth2/refresh_token" 165 | params_dic = { 166 | "access_token": bilibili().dic_bilibili["access_key"], 167 | "actionKey": bilibili().dic_bilibili["actionKey"], 168 | "appkey": bilibili().dic_bilibili["appkey"], 169 | "build": bilibili().dic_bilibili["build"], 170 | "device": bilibili().dic_bilibili["device"], 171 | "mobi_app": bilibili().dic_bilibili["mobi_app"], 172 | "platform": bilibili().dic_bilibili["platform"], 173 | 'refresh_token': bilibili().dic_bilibili["refresh_token"], 174 | } 175 | temp_params = '&'.join([f'{key}={value}' for key, value in params_dic.items()]) 176 | sign = bilibili().calc_sign(temp_params) 177 | payload = f'{temp_params}&sign={sign}' 178 | response = requests.post(url, params=payload, headers=app_headers) 179 | json_response = response.json() 180 | if json_response["code"] == 0: 181 | access_key = json_response['data']['token_info']['access_token'] 182 | refresh_token = json_response['data']['token_info']['refresh_token'] 183 | cookie = json_response['data']['cookie_info']['cookies'] 184 | cookie_format = "" 185 | for i in range(len(cookie)): 186 | cookie_format = cookie_format + cookie[i]['name'] + "=" + cookie[i]['value'] + ";" 187 | bilibili().dic_bilibili['csrf'] = cookie[0]['value'] 188 | bilibili().dic_bilibili['access_key'] = access_key 189 | bilibili().dic_bilibili['refresh_token'] = refresh_token 190 | bilibili().dic_bilibili['cookie'] = cookie_format 191 | bilibili().dic_bilibili['uid'] = cookie[1]['value'] 192 | bilibili().dic_bilibili['pcheaders']['cookie'] = cookie_format 193 | bilibili().dic_bilibili['appheaders']['cookie'] = cookie_format 194 | dic_saved_session = { 195 | 'csrf': cookie[0]['value'], 196 | 'access_key': access_key, 197 | 'refresh_token': refresh_token, 198 | 'cookie': cookie_format, 199 | 'uid': cookie[1]['value'] 200 | } 201 | configloader.write2bilibili(dic_saved_session) 202 | Printer().printer(f"token刷新成功", "Info", "green") 203 | else: 204 | Printer().printer(f"token刷新失败,将重新登录 {json_response}", "Info", "green") 205 | self.login() 206 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from bilibili import bilibili 2 | from printer import Printer 3 | from connect import connect 4 | import time 5 | import random 6 | import re 7 | import datetime 8 | 9 | 10 | def adjust_for_chinese(str): 11 | SPACE = '\N{IDEOGRAPHIC SPACE}' 12 | EXCLA = '\N{FULLWIDTH EXCLAMATION MARK}' 13 | TILDE = '\N{FULLWIDTH TILDE}' 14 | 15 | # strings of ASCII and full-width characters (same order) 16 | west = ''.join(chr(i) for i in range(ord(' '), ord('~'))) 17 | east = SPACE + ''.join(chr(i) for i in range(ord(EXCLA), ord(TILDE))) 18 | 19 | # build the translation table 20 | full = str.maketrans(west, east) 21 | str = str.translate(full).rstrip().split('\n') 22 | md = '{:^10}'.format(str[0]) 23 | return md.translate(full) 24 | 25 | 26 | def CurrentTime(): 27 | currenttime = int(time.mktime(datetime.datetime.now().timetuple())) 28 | return str(currenttime) 29 | 30 | 31 | def seconds_until_tomorrow(): 32 | today = datetime.date.today() 33 | tomorrow = today + datetime.timedelta(days=1) 34 | tomorrow_start_time = int(time.mktime(time.strptime(str(tomorrow), '%Y-%m-%d'))) 35 | current_time = int(time.mktime(datetime.datetime.now().timetuple())) 36 | return tomorrow_start_time - current_time 37 | 38 | 39 | async def fetch_medal(printer=True): 40 | if printer: 41 | Printer().printer('查询勋章信息', "Info", "green") 42 | print( 43 | '{} {} {:^12} {:^10} {} {:^6} '.format(adjust_for_chinese('勋章'), adjust_for_chinese('主播昵称'), '亲密度', 44 | '今日的亲密度', 45 | adjust_for_chinese('排名'), '勋章状态')) 46 | dic_worn = {'1': '正在佩戴', '0': '待机状态'} 47 | for _ in range(3): 48 | response = await bilibili().request_fetchmedal() 49 | json_response = await response.json(content_type=None) 50 | if json_response['code']: 51 | continue 52 | # 有时候dict获取不完整,包括最后一项"roomid"的后半部分缺失 53 | elif all(["roomid" not in medal for medal in json_response['data']['fansMedalList']]): 54 | continue 55 | else: 56 | break 57 | roomid = 0 58 | today_feed = 0 59 | day_limit = 0 60 | if json_response['code'] == 0: 61 | for i in json_response['data']['fansMedalList']: 62 | if i['status'] == 1: 63 | roomid = i.get('roomid', 0) # 主站获取的勋章没有直播间 64 | today_feed = i['today_feed'] 65 | day_limit = i['day_limit'] 66 | if printer: 67 | print( 68 | '{} {} {:^14} {:^14} {} {:^6} '.format(adjust_for_chinese(i['medal_name'] + '|' + str(i['level'])), 69 | adjust_for_chinese(i['anchorInfo']['uname']), 70 | str(i['intimacy']) + '/' + str(i['next_intimacy']), 71 | str(i['todayFeed']) + '/' + str(i['dayLimit']), 72 | adjust_for_chinese(str(i['rank'])), 73 | dic_worn[str(i['status'])])) 74 | return roomid, today_feed, day_limit 75 | 76 | 77 | async def fetch_user_info(): 78 | response = await bilibili().request_fetch_user_info() 79 | response_ios = await bilibili().request_fetch_user_infor_ios() 80 | print('[{}] 查询用户信息'.format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))) 81 | json_response = await response.json() 82 | json_response_ios = await response_ios.json() 83 | if json_response_ios['code'] == 0: 84 | gold_ios = json_response_ios['data']['gold'] 85 | else: 86 | # {'code': 3, 'msg': 'user no login', 'message': 'user no login', 'data': []} 87 | gold_ios = None 88 | # print(json_response_ios) 89 | if (json_response['code'] == 0): 90 | data = json_response['data'] 91 | # print(data) 92 | userInfo = data['userInfo'] 93 | userCoinIfo = data['userCoinIfo'] 94 | uname = userInfo['uname'] 95 | achieve = data['achieves'] 96 | user_level = userCoinIfo['user_level'] 97 | silver = userCoinIfo['silver'] 98 | gold = userCoinIfo['gold'] 99 | identification = bool(userInfo['identification']) 100 | mobile_verify = bool(userInfo['mobile_verify']) 101 | user_next_level = userCoinIfo['user_next_level'] 102 | user_intimacy = userCoinIfo['user_intimacy'] 103 | user_next_intimacy = userCoinIfo['user_next_intimacy'] 104 | user_level_rank = userCoinIfo['user_level_rank'] 105 | billCoin = userCoinIfo['coins'] 106 | bili_coins = userCoinIfo['bili_coins'] 107 | print('# 用户名', uname) 108 | print('# 手机认证状况 {} | 实名认证状况 {}'.format(mobile_verify, identification)) 109 | print('# 银瓜子', silver) 110 | print('# 通用金瓜子', gold) 111 | print('# ios可用金瓜子', gold_ios) 112 | print('# 硬币数', billCoin) 113 | print('# B币数', bili_coins) 114 | print('# 成就值', achieve) 115 | print('# 等级值', user_level, '———>', user_next_level) 116 | print('# 经验值', user_intimacy) 117 | print('# 剩余值', user_next_intimacy - user_intimacy) 118 | arrow = int(user_intimacy * 30 / user_next_intimacy) 119 | line = 30 - arrow 120 | percent = user_intimacy / user_next_intimacy * 100.0 121 | process_bar = '# [' + '>' * arrow + '-' * line + ']' + '%.2f' % percent + '%' 122 | print(process_bar) 123 | print('# 等级榜', user_level_rank) 124 | 125 | 126 | async def fetch_bag_list(verbose=False, bagid=None, printer=True): 127 | response = await bilibili().request_fetch_bag_list() 128 | temp = [] 129 | gift_list = [] 130 | json_response = await response.json() 131 | if printer == True: 132 | print('[{}] 查询可用礼物'.format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))) 133 | for i in range(len(json_response['data']['list'])): 134 | bag_id = (json_response['data']['list'][i]['bag_id']) 135 | gift_id = (json_response['data']['list'][i]['gift_id']) 136 | gift_num = str((json_response['data']['list'][i]['gift_num'])).center(4) 137 | gift_name = json_response['data']['list'][i]['gift_name'] 138 | expireat = (json_response['data']['list'][i]['expire_at']) 139 | if expireat != 0: 140 | left_time = (expireat - int(CurrentTime())) 141 | left_days = (expireat - int(CurrentTime())) / 86400 142 | gift_list.append([gift_id, gift_num, bag_id, expireat]) 143 | else: 144 | left_time = 999999999999999999 145 | left_days = 999999999999999999 146 | gift_list.append([gift_id, gift_num, bag_id, expireat]) 147 | if bagid is not None: 148 | if bag_id == int(bagid): 149 | return gift_id 150 | else: 151 | if verbose: 152 | print("# 编号为" + str(bag_id) + '的' + gift_name + 'X' + gift_num, '(在' + str((left_days)) + '天后过期)') 153 | elif printer == True: 154 | print("# " + gift_name + 'X' + gift_num, '(在' + str((left_days)) + '天后过期)') 155 | 156 | if 0 < int(left_time) < 43200: # 剩余时间少于半天时自动送礼 157 | temp.append([gift_id, gift_num, bag_id, expireat]) 158 | # print(temp) 159 | return temp, gift_list 160 | 161 | 162 | async def check_taskinfo(): 163 | response = await bilibili().request_check_taskinfo() 164 | json_response = await response.json(content_type=None) 165 | # print(json_response) 166 | if json_response['code'] == 0: 167 | data = json_response['data'] 168 | box_info = data['box_info'] 169 | sign_info = data['sign_info'] 170 | live_time_info = data['live_time_info'] 171 | print('每日签到:') 172 | if sign_info['status'] == 1: 173 | print('# 该任务已完成') 174 | else: 175 | print('# 该任务未完成') 176 | 177 | # sign_info['signDaysList']已永远为空 178 | # if sign_info['signDaysList'] == list(range(1, sign_info['curDay'] + 1)): 179 | # print('# 当前全勤') 180 | # else: 181 | # print('# 出现断签') 182 | 183 | print('直播奖励:') 184 | if live_time_info['status'] == 1: 185 | print('# 已完成') 186 | else: 187 | print('# 未完成(目前本项目未实现自动完成直播任务)') 188 | 189 | 190 | async def send_gift_web(roomid, giftid, giftnum, bagid): 191 | response = await bilibili().request_check_room(roomid) 192 | json_response = await response.json() 193 | if json_response["code"] != 0: 194 | Printer().printer(f"获取送礼房间{roomid}信息出错: {json_response}", "Error", "red") 195 | return 196 | ruid = json_response['data']['uid'] 197 | biz_id = json_response['data']['room_id'] 198 | response1 = await bilibili().request_send_gift_web(giftid, giftnum, bagid, ruid, biz_id) 199 | json_response1 = await response1.json() 200 | if json_response1['code'] == 0: 201 | Printer().printer(f"送出礼物{json_response1['data']['gift_name']}x{json_response1['data']['gift_num']}到{roomid}房间", 202 | "Info", 203 | "green") 204 | else: 205 | Printer().printer(f"错误:{json_response1['msg']}", "Error", "red") 206 | 207 | 208 | async def check_room_true(roomid): 209 | response = await bilibili().request_check_room(roomid) 210 | json_response = await response.json(content_type=None) 211 | 212 | if json_response['code'] == 0: 213 | data = json_response['data'] 214 | param1 = data['is_hidden'] 215 | param2 = data['is_locked'] 216 | param3 = data['encrypted'] 217 | return param1, param2, param3 218 | else: 219 | Printer().printer(f"获取房间信息出错: {json_response}", "Error", "red") 220 | return [None] 221 | 222 | 223 | async def check_up_name(name): 224 | roomid = 0 225 | response = await bilibili().query_guard(name) 226 | json_response = await response.json() 227 | while json_response['code'] == -111: 228 | temp = random.randint(0, len(name) - 1) 229 | split_str = name[temp:min(temp + 2, len(name))] 230 | response = await bilibili().query_guard(split_str) 231 | json_response = await response.json() 232 | for i in range(0, len(json_response['result'])): 233 | uname = str(json_response['result'][i]['uname']) 234 | temp = re.compile(r'<[^>]+>', re.S) 235 | ser_name = temp.sub('', uname) 236 | if str(ser_name) == str(name): 237 | roomid = json_response['result'][i]['roomid'] 238 | return roomid 239 | return roomid 240 | 241 | 242 | async def reconnect(area=None): 243 | if area is not None: 244 | await connect().recreate(area) 245 | await connect().check_connect(area) 246 | 247 | 248 | async def check_area_list(area_list, **kwargs): 249 | for area_id in area_list: 250 | await connect().check_area(area_id, **kwargs) 251 | 252 | 253 | async def fetch_gray_medals(): 254 | gray_medals = [] 255 | response = await bilibili().request_fetchmedal() 256 | json_response = await response.json(content_type=None) 257 | for i in range(0, len(json_response['data']['fansMedalList'])): 258 | if json_response['data']['fansMedalList'][i]['is_lighted'] == 0: 259 | gray_medal = {} 260 | gray_medal['medal_name'] = json_response['data']['fansMedalList'][i]['medal_name'] 261 | gray_medal['roomid'] = json_response['data']['fansMedalList'][i]['roomid'] 262 | gray_medals.append(gray_medal) 263 | return gray_medals 264 | 265 | 266 | async def get_all_of_my_hearts(): 267 | hearts = [] 268 | response = await bilibili().request_fetch_bag_list() 269 | json_response = await response.json(content_type=None) 270 | for i in range(len(json_response['data']['list'])): 271 | gift_name = json_response['data']['list'][i]['gift_name'] 272 | gift_id = (json_response['data']['list'][i]['gift_id']) 273 | if gift_name == "小心心" or gift_id == 30607: 274 | expireat = json_response['data']['list'][i]['expire_at'] 275 | bag_id = json_response['data']['list'][i]['bag_id'] 276 | gift_num = json_response['data']['list'][i]['gift_num'] 277 | for _ in range(gift_num): 278 | hearts.append([bag_id, 1, expireat]) 279 | 280 | return hearts 281 | 282 | 283 | async def refresh_all_gray_medals(): 284 | gray_medals = await fetch_gray_medals() 285 | hearts = await get_all_of_my_hearts() 286 | if hearts: 287 | for gray_medal in gray_medals: 288 | if len(hearts) > 0: 289 | heart = hearts.pop() 290 | await send_gift_web(gray_medal['roomid'], 30607, 1, heart[0]) 291 | else: 292 | pass 293 | else: 294 | pass 295 | 296 | 297 | async def refresh_medals_by_roomids(roomids): 298 | hearts = await get_all_of_my_hearts() 299 | if hearts: 300 | for roomid in roomids: 301 | if len(hearts) > 0: 302 | heart = hearts.pop() 303 | await send_gift_web(roomid, 30607, 1, heart[0]) 304 | else: 305 | pass 306 | else: 307 | pass 308 | -------------------------------------------------------------------------------- /bilibiliCilent.py: -------------------------------------------------------------------------------- 1 | from bilibili import bilibili 2 | from statistics import Statistics 3 | from printer import Printer 4 | from rafflehandler import Rafflehandler 5 | from schedule import Schedule 6 | import utils 7 | import traceback 8 | import asyncio 9 | import random 10 | import struct 11 | import zlib 12 | import json 13 | import sys 14 | 15 | 16 | async def handle_1_TV_raffle(type, raffleid, time_wait, time_limit, num, real_roomid): 17 | Statistics().append_to_TVlist(raffleid, time_limit) 18 | if Schedule().scheduled_sleep: 19 | Printer().printer(f"定时休眠,跳过房间 {real_roomid} 广播道具抽奖 {raffleid}", "Info", "green") 20 | return 21 | if bilibili().black_status: 22 | Printer().printer(f"黑屋休眠,跳过房间 {real_roomid} 广播道具抽奖 {raffleid}", "Info", "green") 23 | return 24 | await asyncio.sleep(min(max(0, time_wait) + random.uniform(0, min(num, 30)), time_limit-1)) 25 | response2 = await bilibili().get_gift_of_TV(type, real_roomid, raffleid) 26 | # Printer().printer(f"参与了房间 {real_roomid} 的广播抽奖 {raffleid}", "Lottery", "cyan") 27 | json_response2 = await response2.json(content_type=None) 28 | # Printer().printer(f"参与房间 {real_roomid} 广播道具抽奖 {raffleid} 状态: {json_response2['msg']}", "Lottery", "cyan") 29 | if json_response2['code'] == 0: 30 | data = json_response2["data"] 31 | Printer().printer(f"房间 {real_roomid} 广播道具抽奖 {raffleid} 结果: {data['award_name']}X{data['award_num']}", 32 | "Lottery", "cyan") 33 | Statistics().add_to_result(data['award_name'], int(data['award_num'])) 34 | else: 35 | # {"code":-403,"data":null,"message":"访问被拒绝","msg":"访问被拒绝"} 36 | # {'code': 503, 'data': None, 'message': '请求太多了!', 'msg': '请求太多了!'} 37 | # {'code': -509, 'message': '请求过于频繁,请稍后再试', 'ttl': 1} 38 | Printer().printer(f"房间 {real_roomid} 广播道具抽奖 {raffleid} 结果: {json_response2['message']}", 39 | "Lottery", "cyan") 40 | print(json_response2) 41 | 42 | 43 | async def handle_1_room_TV(real_roomid): 44 | await asyncio.sleep(random.uniform(0, 1)) 45 | response = await bilibili().get_giftlist_of_TV(real_roomid) 46 | json_response = await response.json(content_type=None) 47 | checklen = json_response['data']['gift'] 48 | num = len(checklen) 49 | if num: 50 | result = await utils.check_room_true(real_roomid) 51 | if True in result: 52 | Printer().printer(f"检测到房间 {real_roomid} 的钓鱼操作", "Warning", "red") 53 | else: 54 | await bilibili().post_watching_history(real_roomid) 55 | list_available_raffleid = [] 56 | for j in range(0, num): 57 | raffleid = json_response['data']['gift'][j]['raffleId'] 58 | if Statistics().check_TVlist(raffleid): 59 | type = json_response['data']['gift'][j]['type'] 60 | time_wait = json_response['data']['gift'][j]['time_wait'] 61 | time_limit = json_response['data']['gift'][j]['time'] 62 | list_available_raffleid.append([type, raffleid, time_wait, time_limit]) 63 | tasklist = [] 64 | num_available = len(list_available_raffleid) 65 | for k in list_available_raffleid: 66 | task = asyncio.ensure_future(handle_1_TV_raffle(*k, num_available, real_roomid)) 67 | tasklist.append(task) 68 | if tasklist: 69 | await asyncio.wait(tasklist, return_when=asyncio.ALL_COMPLETED) 70 | 71 | 72 | class bilibiliClient(): 73 | 74 | def __init__(self, roomid, area): 75 | self.bilibili = bilibili() 76 | self._protocol_version = self.bilibili.dic_bilibili['_protocolversion'] 77 | self._reader = None 78 | self._writer = None 79 | self._uid = int(100000000000000 + 200000000000000 * random.random()) 80 | self.connected = False 81 | self._UserCount = 0 82 | self.dic_bulletin = { 83 | 'cmd': 'str', 84 | 'msg': 'str', 85 | 'rep': 'int', 86 | 'url': 'str' 87 | } 88 | self._roomId = roomid 89 | self.area = area 90 | 91 | def close_connection(self): 92 | self._writer.close() 93 | self.connected = False 94 | 95 | async def connectServer(self): 96 | try: 97 | self._reader, self._writer = await asyncio.open_connection(self.bilibili.dic_bilibili['_ChatHost'], 98 | self.bilibili.dic_bilibili['_ChatPort']) 99 | except: 100 | print("连接无法建立,请检查本地网络状况") 101 | await asyncio.sleep(5) 102 | return 103 | if await self.SendJoinChannel(): 104 | self.connected = True 105 | Printer().printer(f'[{self.area}分区] 连接 {self._roomId} 弹幕服务器成功', "Info", "green") 106 | await self.ReceiveMessageLoop() 107 | 108 | async def HeartbeatLoop(self): 109 | while not self.connected: 110 | await asyncio.sleep(0.5) 111 | 112 | while self.connected: 113 | await self.SendSocketData(ver=self._protocol_version, action=2, body="") 114 | await asyncio.sleep(30) 115 | 116 | async def SendJoinChannel(self): 117 | body = json.dumps({ 118 | "roomid": self._roomId, 119 | "uid": self._uid, 120 | # "protover": 2, 121 | # "key": token, 122 | }, separators=(',', ':')) 123 | return await self.SendSocketData(ver=self._protocol_version, action=7, body=body) 124 | 125 | async def SendSocketData(self, ver, action, body): 126 | bytearr = body.encode('utf-8') 127 | header_len = 16 128 | body_len = len(bytearr) 129 | packet_len = 16 + body_len 130 | sequence = 1 131 | sendbytes = struct.pack(f'!IHHII{body_len}s', 132 | packet_len, header_len, ver, action, sequence, bytearr) 133 | try: 134 | self._writer.write(sendbytes) 135 | except Exception: 136 | Printer().printer(f"Error when self._writer.write(sendbytes): {sys.exc_info()[0]}, {sys.exc_info()[1]}", 137 | "Error", "red") 138 | self.connected = False 139 | return False 140 | try: 141 | await self._writer.drain() 142 | except (ConnectionResetError, ConnectionAbortedError) as e: 143 | # [WinError 10054] 远程主机强迫关闭了一个现有的连接。 144 | # [WinError 10053] 你的主机中的软件中止了一个已建立的连接。 145 | Printer().printer(f"Failed @ self._writer.drain(): {repr(e)}", "Error", "red") 146 | return False 147 | except Exception: 148 | Printer().printer(f"Error when self._writer.drain(): {sys.exc_info()[0]}, {sys.exc_info()[1]}", 149 | "Error", "red") 150 | return False 151 | return True 152 | 153 | async def ReadSocketData(self, len_wanted): 154 | bytes_data = b'' 155 | if len_wanted == 0: 156 | return bytes_data 157 | len_remain = len_wanted 158 | while len_remain != 0: 159 | try: 160 | tmp = await asyncio.wait_for(self._reader.read(len_remain), timeout=35.0) 161 | except asyncio.TimeoutError: 162 | # 由于心跳包30s一次,但是发现35s内没有收到任何包,说明已经悄悄失联了,主动断开 163 | Printer().printer(f'心跳失联,主动断开 @[{self.area}分区]{self._roomId}',"Error","red") 164 | self.close_connection() 165 | await asyncio.sleep(1) 166 | return None 167 | except ConnectionResetError: 168 | Printer().printer(f'RESET,网络不稳定或者远端不正常断开 @[{self.area}分区]{self._roomId}',"Error","red") 169 | self.close_connection() 170 | await asyncio.sleep(5) 171 | return None 172 | except ConnectionAbortedError: 173 | Printer().printer(f'你的主机中的软件中止了一个已建立的连接 @[{self.area}分区]{self._roomId}',"Error","red") 174 | self.close_connection() 175 | await asyncio.sleep(5) 176 | return None 177 | except OSError: 178 | Printer().printer(f'[WinError 121] 信号灯超时时间已到 @[{self.area}分区]{self._roomId}',"Error","red") 179 | self.close_connection() 180 | await asyncio.sleep(5) 181 | return None 182 | except asyncio.CancelledError: 183 | 184 | return None 185 | except: 186 | Printer().printer(f"{sys.exc_info()[0]}, {sys.exc_info()[1]} @[{self.area}分区]{self._roomId}","Error","red") 187 | Printer().printer(f'请联系开发者',"Warning","red") 188 | self.close_connection() 189 | return None 190 | 191 | if not len(tmp): 192 | Printer().printer(f"主动关闭或者远端主动发来FIN @[{self.area}分区]{self._roomId}","Error","red") 193 | self.close_connection() 194 | await asyncio.sleep(1) 195 | return None 196 | else: 197 | bytes_data += tmp 198 | len_remain -= len(tmp) 199 | 200 | return bytes_data 201 | 202 | async def ReceiveMessageLoop(self): 203 | while self.connected: 204 | length = await self.ReadSocketData(4) 205 | if length is None: 206 | break 207 | 208 | packet_len, = struct.unpack('!I', length) 209 | body = await self.ReadSocketData(packet_len-4) 210 | if body is None: 211 | break 212 | 213 | await self.parse_packet(packet_len, body) 214 | 215 | async def parse_packet(self, packet_len: int, body: bytes): 216 | """ 217 | len(body) == packet_len - 4 218 | :param packet_len: length of whole packet 219 | :param body: header except packet_len + subsequent main body 220 | """ 221 | # 基础假设是每个packet都有16字节header的保守序列,没有另外再先读取header_len然后读header 222 | header_len, ver, action, sequence = struct.unpack('!HHII', body[:12]) 223 | body = body[12:] 224 | # print(packet_len, header_len, ver, action, sequence, body) 225 | 226 | if action == 3: 227 | # 3 人气值,数据不是JSON,是4字节整数 228 | self._UserCount, = struct.unpack('!I', body) 229 | else: 230 | try: 231 | dic = json.loads(body) 232 | except UnicodeDecodeError: 233 | inner_packet = zlib.decompress(body) 234 | # print(f'{packet_len} 字节解压出 {inner_packet.count(b"cmd")} 条消息') 235 | 236 | pack_p = 0 237 | packs_len = len(inner_packet) 238 | while pack_p < packs_len: 239 | pack_len, = struct.unpack('!I', inner_packet[pack_p:pack_p+4]) 240 | await self.parse_packet(pack_len, inner_packet[pack_p+4:pack_p+pack_len]) 241 | pack_p += pack_len 242 | return 243 | except json.JSONDecodeError as e: 244 | Printer().printer(f"{repr(e)} when json decode: {body}", "Error", "red") 245 | return 246 | except Exception: 247 | Printer().printer(f"Failed when parse_packet: {body}\n{traceback.format_exc()}", "Error", "red") 248 | return 249 | 250 | if action == 5: 251 | try: 252 | await self.parseDanMu(dic) 253 | except Exception: 254 | Printer().printer(f"Failed when parsing: {dic}\n{traceback.format_exc()}", "Error", "red") 255 | elif action == 8: 256 | # 26, 16, 2, 8, 1, b'{"code":0}' 257 | pass 258 | else: 259 | # lyyyuna原版有action=17就不去请求body的逻辑 260 | Printer().printer(f"异常action值: {packet_len, header_len, ver, action, sequence, dic}", "Warning", "red") 261 | 262 | async def parseDanMu(self, dic): 263 | cmd = dic.get('cmd') 264 | if cmd is None: 265 | Printer().printer(f"No cmd: {dic}", "Warning", "red") 266 | return 267 | 268 | if cmd == 'LIVE': 269 | # Printer().printer(f"[{self.area}分区] 房间 {self._roomId} 疑似切换分区!启动分区检查", "Info", "green") 270 | # await utils.check_area_list([self.area], mandatory_check=True) 271 | pass 272 | elif cmd == 'PREPARING': 273 | Printer().printer(f"[{self.area}分区] 房间 {self._roomId} 下播!将切换监听房间", "Info", "green") 274 | self.close_connection() 275 | await utils.reconnect(self.area) 276 | elif cmd == 'DANMU_MSG': 277 | # Printer().printer(f"{dic}", "Message", "cyan", printable=False) 278 | pass 279 | elif cmd == 'SYS_GIFT': 280 | # Printer().printer(f"出现了远古的SYS_GIFT,请尽快联系开发者{dic}", "Warning", "red") 281 | pass 282 | elif cmd == 'NOTICE_MSG': 283 | # msg_type: 1 小时榜首绘马大奖等通报 284 | # 2 抽奖 (TV, 大楼, etc.) 285 | # 3 舰队 286 | # 4 总督进入直播间 287 | # 5 当前房间高能大奖 288 | # 6 风暴 289 | # 8 任意门 290 | # 9 活动中主播达成星级通报 291 | try: 292 | if dic.get('msg_type') in [2, 8]: 293 | real_roomid = dic.get('real_roomid') 294 | Printer().printer(f"检测到房间 {real_roomid} 的广播抽奖 @[{self.area}分区]{self._roomId}", "Lottery", "cyan") 295 | Rafflehandler().append2list_TV(real_roomid) 296 | Statistics().append2pushed_TVlist(real_roomid, self.area[0]) 297 | else: 298 | Printer().printer(f"{dic['msg_common']} @[{self.area}分区]{self._roomId}", "Info", "green") 299 | except Exception: 300 | Printer().printer(f"NOTICE_MSG出错,请联系开发者 {dic}", "Warning", "red") 301 | elif cmd == 'SYS_MSG': 302 | if set(dic) in [set(self.dic_bulletin), {'cmd', 'msg', 'msg_text'}, {'cmd', 'msg', 'url'}]: 303 | Printer().printer(f"{dic['msg']} @[{self.area}分区]{self._roomId}", "Info", "green") 304 | else: 305 | try: 306 | real_roomid = dic['real_roomid'] 307 | Printer().printer(f"检测到房间 {real_roomid} 的广播抽奖 @[{self.area}分区]{self._roomId}", "Lottery", "cyan") 308 | Rafflehandler().append2list_TV(real_roomid) 309 | Statistics().append2pushed_TVlist(real_roomid, self.area[0]) 310 | except: 311 | Printer().printer(f"SYS_MSG出错,请联系开发者 {dic}", "Warning", "red") 312 | 313 | # 观众相关 [欢迎入场,送礼,发弹幕] 314 | elif cmd in ["WELCOME", "SEND_GIFT", "DANMU_MSG"]: 315 | pass 316 | # 各种通知 [通知(当前房间开奖 活动小时榜 各种SYS_MSG都会同时有NOTICE_MSG),系统通知(友爱社 心愿达成 绘马 主播招募 直播间强推)] 317 | elif cmd in ["NOTICE_MSG", "SYS_MSG"]: 318 | pass 319 | # 各种高能 [节奏风暴(开始 结束),高能广播(无抽奖 活动高能 全频风暴),抽奖通知(现在广播全在这里了),总督广播] 320 | elif cmd in ["SPECIAL_GIFT", "SYS_GIFT", "SYS_MSG", "GUARD_MSG", "GUIARD_MSG"]: 321 | pass 322 | # 礼物效果 [连击开始,连击结束,使用积分加成卡] 323 | elif cmd in ["COMBO_SEND", "COMBO_END", "SCORE_CARD"]: 324 | pass 325 | # PK相关 326 | elif cmd in ["PK_INVITE_INIT", "PK_INVITE_FAIL", "PK_INVITE_CANCEL", "PK_INVITE_SWITCH_OPEN", "PK_INVITE_SWITCH_CLOSE", 327 | "PK_PRE", "PK_START", "PK_PROCESS", "PK_SETTLE", "PK_END", "PK_MIC_END", 328 | "PK_MATCH", "PK_CLICK_AGAIN", "PK_AGAIN"]: 329 | pass 330 | # 当前房间抽奖相关 331 | elif cmd in ["RAFFLE_START", "RAFFLE_END", "TV_START", "TV_END", "GUARD_LOTTERY_START", "LOTTERY_START"]: 332 | pass 333 | # 房间管理相关 [屏蔽关键词,用户被加入黑名单,禁言开启,禁言关闭,新设房管,房管变更] 334 | elif cmd in ["ROOM_SHIELD", "ROOM_BLOCK_MSG", "ROOM_SILENT_ON", "ROOM_SILENT_OFF", "room_admin_entrance", "ROOM_ADMINS"]: 335 | pass 336 | # 舰队相关 [本房间购买舰长,船票购买,本房间舰队消息(登船),船员进房间,进房间特效] 337 | elif cmd in ["USER_TOAST_MSG", "GUARD_BUY", "GUARD_MSG", "WELCOME_GUARD", "ENTRY_EFFECT"]: 338 | pass 339 | # 直播状态相关 [开播,下播,警告,被切直播,房间被封] 340 | elif cmd in ["LIVE", "PREPARING", "WARNING", "CUT_OFF", "ROOM_LOCK"]: 341 | pass 342 | # 活动榜单相关 [进入小时榜,未知,获小时榜第一道具奖励] 343 | elif cmd in ["ROOM_RANK", "new_anchor_reward", "HOUR_RANK_AWARDS"]: 344 | pass 345 | # 活动相关 [活动获得的直播间入场特效,活动事件(如充能值信息),以前的高能事件,送礼抽奖活动开奖,LOL竞猜活动,LOL助力活动,?(不知道是啥,每个直播间都有,无论开播,每分钟发一次),?,?,冲鸭!机甲大作战相关,周星活动相关] 346 | elif cmd in ["WELCOME_ACTIVITY", "ACTIVITY_EVENT", "EVENT_CMD", "BOX_LOTTERY_WIN", "LOL_ACTIVITY", "ACTIVITY_MATCH_GIFT", 347 | "ACTIVITY_BANNER_RED_NOTICE_CLOSE", "ACTIVITY_BANNER_CLOSE", "DAILY_QUEST_NEWDAY", 348 | "BOSS_ENERGY", "NOTICE_MSG_H5", "BOSS_INJURY", "BOSS_BATTLE", "ANIMATION", "BOSS_INFO", 349 | "WEEK_STAR_CLOCK", "ROOM_BOX_MASTER", "ROOM_BOX_USER"]: 350 | pass 351 | # 直播间信息相关 [直播间更换壁纸,直播间界面皮肤变化,许愿瓶进度变化,关注数变化,直播间更名,实物抽奖宝箱提醒,实物抽奖宝箱开奖,弹幕抽奖结束] 352 | elif cmd in ["CHANGE_ROOM_INFO", "ROOM_SKIN_MSG", "WISH_BOTTLE", "ROOM_REAL_TIME_MESSAGE_UPDATE", "ROOM_CHANGE", "BOX_ACTIVITY_START", "WIN_ACTIVITY", "DANMU_LOTTERY_END"]: 353 | pass 354 | # 大乱斗活动 355 | elif cmd in ["PK_BATTLE_ENTRANCE", "PK_BATTLE_PRE", "PK_BATTLE_MATCH_TIMEOUT", "PK_BATTLE_START", "PK_BATTLE_VOTES_ADD", 356 | "PK_BATTLE_PROCESS", "PK_BATTLE_PRO_TYPE", "PK_BATTLE_GIFT", "PK_BATTLE_END", "PK_BATTLE_RANK_CHANGE", 357 | "PK_BATTLE_SETTLE_USER", "PK_BATTLE_SETTLE", "PK_LOTTERY_START", "ACTIVITY_BANNER_UPDATE"]: 358 | pass 359 | else: 360 | # Printer().printer(f"出现一个未知msg @[{self.area}分区]{self._roomId} {dic}", "Warning", "red") 361 | pass -------------------------------------------------------------------------------- /bilibili.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from imp import reload 3 | import configloader 4 | import os 5 | import hashlib 6 | import datetime 7 | import time 8 | import requests 9 | import rsa 10 | import base64 11 | import uuid 12 | from urllib import parse 13 | from printer import Printer 14 | import aiohttp 15 | import asyncio 16 | 17 | reload(sys) 18 | 19 | 20 | def CurrentTime(): 21 | currenttime = int(time.mktime(datetime.datetime.now().timetuple())) 22 | return str(currenttime) 23 | 24 | 25 | class bilibili(): 26 | instance = None 27 | 28 | def __new__(cls, *args, **kw): 29 | if not cls.instance: 30 | cls.instance = super(bilibili, cls).__new__(cls, *args, **kw) 31 | fileDir = os.path.dirname(os.path.realpath('__file__')) 32 | file_bilibili = fileDir + "/conf/bilibili.conf" 33 | cls.instance.dic_bilibili = configloader.load_bilibili( 34 | file_bilibili) 35 | cls.instance.bili_session = None 36 | cls.instance.black_status = False 37 | return cls.instance 38 | 39 | @property 40 | def bili_section(self): 41 | if self.bili_session is None: 42 | self.bili_session = aiohttp.ClientSession() 43 | # print(0) 44 | return self.bili_session 45 | 46 | @staticmethod 47 | def load_session(dic): 48 | inst = bilibili.instance 49 | for i in dic.keys(): 50 | inst.dic_bilibili[i] = dic[i] 51 | if i == 'cookie': 52 | inst.dic_bilibili['pcheaders']['cookie'] = dic[i] 53 | inst.dic_bilibili['appheaders']['cookie'] = dic[i] 54 | 55 | def calc_sign(self, str, app_secret=None): 56 | str += app_secret or self.dic_bilibili['app_secret'] 57 | hash = hashlib.md5() 58 | hash.update(str.encode('utf-8')) 59 | sign = hash.hexdigest() 60 | return sign 61 | 62 | def cnn_captcha(self, img): 63 | url = "http://106.75.36.27:19951/captcha/v1" 64 | img = str(img, encoding='utf-8') 65 | json = {"image": img} 66 | ressponse = requests.post(url, json=json) 67 | captcha = ressponse.json() 68 | Printer().printer( 69 | f"此次登录出现验证码,识别结果为{captcha['message']}", "Info", "green") 70 | return captcha['message'] 71 | 72 | def calc_name_passw(self, key, Hash, username, password): 73 | pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(key.encode()) 74 | password = base64.b64encode(rsa.encrypt( 75 | (Hash + password).encode('utf-8'), pubkey)) 76 | password = parse.quote_plus(password) 77 | username = parse.quote_plus(username) 78 | return username, password 79 | 80 | async def reset_black_status(self): 81 | struct_time = time.localtime(time.time()) 82 | sleep_min = 40 if 1 <= struct_time.tm_hour <= 17 else 20 83 | Printer().printer(f"当前处于小黑屋,休眠 {sleep_min} min", "Info", "green") 84 | await asyncio.sleep(sleep_min * 60) 85 | Printer().printer(f"黑屋休眠结束,尝试进行抽奖", "Info", "green") 86 | self.black_status = False 87 | 88 | async def replay_request(self, response): 89 | json_response = await response.json(content_type=None) 90 | if json_response['code'] == 1024: 91 | Printer().printer(f'b站炸了,暂停所有请求5s后重试,请耐心等待', "Error", "red") 92 | await asyncio.sleep(5) 93 | return True 94 | elif json_response['code'] in [503, -509]: 95 | Printer().printer(f'『{json_response["message"]}』5s后重试', "Error", "red") 96 | await asyncio.sleep(5) 97 | return True 98 | elif json_response['code'] == -403 and json_response.get("msg") == '访问被拒绝': 99 | self.black_status = True 100 | asyncio.ensure_future(self.reset_black_status()) 101 | return False 102 | else: 103 | return False 104 | 105 | async def bili_section_request(self, method, url, replay=True, **kwargs): 106 | while True: 107 | try: 108 | response = await self.bili_section.request(method, url, **kwargs) 109 | if response.status in [403, 412]: 110 | if replay: 111 | Printer().printer(f'<{response.status} {response.reason}>,60s后重试: {url}', "Error", "red") 112 | await asyncio.sleep(60) 113 | continue 114 | else: 115 | Printer().printer(f'<{response.status} {response.reason}>,放弃抽奖', "Error", "red") 116 | break 117 | elif response.status == 404: 118 | Printer().printer(f'<{response.status} {response.reason}>,接口疑似已失效,请联系开发者', "Error", "red") 119 | return None 120 | tag = await self.replay_request(response) 121 | if tag: 122 | continue 123 | return response 124 | except: 125 | # print('当前网络不好,正在重试,请反馈开发者!!!!') 126 | # print(sys.exc_info()[0], sys.exc_info()[1]) 127 | await asyncio.sleep(1) 128 | continue 129 | 130 | async def bili_section_post(self, url, **kwargs): 131 | return await self.bili_section_request('POST', url, **kwargs) 132 | 133 | async def bili_section_get(self, url, **kwargs): 134 | return await self.bili_section_request('GET', url, **kwargs) 135 | 136 | # 1:900兑换 137 | async def request_doublegain_coin2silver(self): 138 | # url: "/exchange/coin2silver", 139 | data = {'coin': 10} 140 | url = "https://api.live.bilibili.com/exchange/coin2silver" 141 | response = await self.bili_section_post(url, data=data, headers=self.dic_bilibili['pcheaders']) 142 | return response 143 | 144 | # 1:450兑换 145 | async def coin2silver_web(self, num): 146 | url = "https://api.live.bilibili.com/pay/v1/Exchange/coin2silver" 147 | data = { 148 | "num": int(num), 149 | "platform": 'pc', 150 | "csrf_token": self.dic_bilibili['csrf'], 151 | "csrf": self.dic_bilibili['csrf'] 152 | } 153 | response = await self.bili_section_post(url, data=data, headers=self.dic_bilibili['pcheaders']) 154 | return response 155 | 156 | async def post_watching_history(self, room_id): 157 | data = { 158 | "room_id": room_id, 159 | "platform": 'pc', 160 | "csrf_token": self.dic_bilibili['csrf'], 161 | "csrf": self.dic_bilibili['csrf'] 162 | } 163 | url = "https://api.live.bilibili.com/room/v1/Room/room_entry_action" 164 | response = await self.bili_section_post(url, data=data, headers=self.dic_bilibili['pcheaders']) 165 | return response 166 | 167 | async def silver2coin_web(self): 168 | url = "https://api.live.bilibili.com/exchange/silver2coin" 169 | data = { 170 | "csrf_token": self.dic_bilibili['csrf'], 171 | "csrf": self.dic_bilibili['csrf'] 172 | } 173 | response = await self.bili_section_post(url, data=data, headers=self.dic_bilibili['pcheaders']) 174 | return response 175 | 176 | async def silver2coin_app(self): 177 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 178 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 179 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&mobi_app=' + self.dic_bilibili[ 180 | 'mobi_app'] + '&platform=' + self.dic_bilibili['platform'] + '&ts=' + CurrentTime() 181 | sign = self.calc_sign(temp_params) 182 | app_url = "https://api.live.bilibili.com/AppExchange/silver2coin?" + \ 183 | temp_params + "&sign=" + sign 184 | response1 = await self.bili_section_post(app_url, headers=self.dic_bilibili['appheaders']) 185 | return response1 186 | 187 | async def request_check_room(self, roomid): 188 | url = "https://api.live.bilibili.com/room/v1/Room/room_init?id=" + \ 189 | str(roomid) 190 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 191 | return response 192 | 193 | async def request_fetch_bag_list(self): 194 | url = "https://api.live.bilibili.com/gift/v2/gift/bag_list" 195 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 196 | return response 197 | 198 | async def request_check_taskinfo(self): 199 | url = 'https://api.live.bilibili.com/i/api/taskInfo' 200 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 201 | return response 202 | 203 | async def request_send_gift_web(self, giftid, giftnum, bagid, ruid, biz_id): 204 | url = "https://api.live.bilibili.com/gift/v2/live/bag_send" 205 | data = { 206 | 'uid': self.dic_bilibili['uid'], 207 | 'gift_id': giftid, 208 | 'ruid': ruid, 209 | 'gift_num': giftnum, 210 | 'bag_id': bagid, 211 | 'platform': 'pc', 212 | 'biz_code': 'live', 213 | 'biz_id': biz_id, 214 | 'rnd': CurrentTime(), 215 | 'storm_beat_id': '0', 216 | 'metadata': '', 217 | 'price': '0', 218 | 'csrf_token': self.dic_bilibili['csrf'] 219 | } 220 | response = await self.bili_section_post(url, headers=self.dic_bilibili['pcheaders'], data=data) 221 | return response 222 | 223 | async def request_fetch_user_info(self): 224 | url = "https://api.live.bilibili.com/live_user/v1/UserInfo/live_info" 225 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 226 | return response 227 | 228 | async def request_fetch_user_infor_ios(self): 229 | # 长串请求起作用的就这几个破玩意儿 230 | url = 'https://api.live.bilibili.com/mobile/getUser?access_key={}&platform=ios'.format( 231 | self.dic_bilibili['access_key']) 232 | response = await self.bili_section_get(url) 233 | return response 234 | 235 | async def request_fetch_liveuser_info(self, real_roomid): 236 | url = 'https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room?roomid={}'.format( 237 | real_roomid) 238 | response = await self.bili_section_get(url) 239 | return response 240 | 241 | def request_load_img(self, url): 242 | return requests.get(url) 243 | 244 | async def request_fetchmedal(self): 245 | url = 'https://api.live.bilibili.com/i/api/medal?page=1&pageSize=168' 246 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 247 | return response 248 | 249 | def request_getkey(self): 250 | url = 'https://passport.bilibili.com/api/oauth2/getKey' 251 | temp_params = 'appkey=' + self.dic_bilibili['appkey'] 252 | sign = self.calc_sign(temp_params) 253 | params = {'appkey': self.dic_bilibili['appkey'], 'sign': sign} 254 | response = requests.post(url, data=params) 255 | return response 256 | 257 | async def get_gift_of_events_web(self, text1, text2, raffleid): 258 | headers = { 259 | 'Accept': 'application/json, text/plain, */*', 260 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 261 | 'cookie': self.dic_bilibili['cookie'], 262 | 'referer': text2 263 | } 264 | pc_url = 'https://api.live.bilibili.com/activity/v1/Raffle/join?roomid=' + str( 265 | text1) + '&raffleId=' + str(raffleid) 266 | pc_response = await self.bili_section_get(pc_url, headers=headers) 267 | return pc_response 268 | 269 | async def get_gift_of_events_app(self, text1, raffleid): 270 | headers = { 271 | 'Accept': 'application/json, text/plain, */*', 272 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 273 | 'cookie': self.dic_bilibili['cookie'], 274 | # 'referer': text2 275 | } 276 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 277 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 278 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&event_type=' + str( 279 | raffleid) + '&mobi_app=' + self.dic_bilibili['mobi_app'] + '&platform=' + self.dic_bilibili[ 280 | 'platform'] + '&room_id=' + str( 281 | text1) + '&ts=' + CurrentTime() 282 | params = temp_params + self.dic_bilibili['app_secret'] 283 | sign = self.calc_sign(temp_params) 284 | true_url = 'https://api.live.bilibili.com/YunYing/roomEvent?' + \ 285 | temp_params + '&sign=' + sign 286 | response1 = await self.bili_section_get(true_url, params=params, headers=headers) 287 | return response1 288 | 289 | async def get_gift_of_TV(self, type, real_roomid, raffleid): 290 | url = "https://api.live.bilibili.com/xlive/lottery-interface/v5/smalltv/join" 291 | data = { 292 | "roomid": real_roomid, 293 | "id": raffleid, 294 | "type": type, 295 | "csrf_token": self.dic_bilibili['csrf'], 296 | "csrf": self.dic_bilibili['csrf'], 297 | "visit_id": "8u0aig7b8100" 298 | } 299 | response2 = await self.bili_section_post(url, replay=False, data=data, headers=self.dic_bilibili['pcheaders']) 300 | return response2 301 | 302 | async def get_gift_of_captain(self, roomid, id): 303 | join_url = "https://api.live.bilibili.com/xlive/lottery-interface/v3/guard/join" 304 | payload = {"roomid": roomid, "id": id, "type": "guard", 305 | "csrf_token": self.dic_bilibili['csrf']} 306 | response2 = await self.bili_section_post(join_url, replay=False, data=payload, 307 | headers=self.dic_bilibili['pcheaders']) 308 | return response2 309 | 310 | async def get_gift_of_pk(self, roomid, id): 311 | join_url = "https://api.live.bilibili.com/xlive/lottery-interface/v1/pk/join" 312 | payload = {"roomid": roomid, "id": id, "csrf": self.dic_bilibili['csrf'], 313 | "csrf_token": self.dic_bilibili['csrf']} 314 | response2 = await self.bili_section_post(join_url, replay=False, data=payload, 315 | headers=self.dic_bilibili['pcheaders']) 316 | return response2 317 | 318 | async def get_giftlist_of_events(self, text1): 319 | headers = { 320 | 'Accept': 'application/json, text/plain, */*', 321 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 322 | 'Accept-Language': 'zh-CN,zh;q=0.9', 323 | 'accept-encoding': 'gzip, async deflate', 324 | 'Host': 'api.live.bilibili.com', 325 | } 326 | # url = f'{base_url}/activity/v1/Raffle/check?roomid={text1}' 327 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 328 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 329 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&mobi_app=' + self.dic_bilibili[ 330 | 'mobi_app'] + '&platform=' + self.dic_bilibili[ 331 | 'platform'] + '&roomid=' + str( 332 | text1) + '&ts=' + CurrentTime() 333 | sign = self.calc_sign(temp_params) 334 | url = "https://api.live.bilibili.com/activity/v1/Common/mobileRoomInfo?" + \ 335 | temp_params + "&sign=" + sign 336 | response = await bilibili.instance.bili_section_get(url, headers=self.dic_bilibili['appheaders']) 337 | # url = 'https://api.live.bilibili.com/activity/v1/Raffle/check?roomid=' + str(text1) 338 | # response = await self.bili_section_get(url, headers=headers) 339 | return response 340 | 341 | async def get_giftlist_of_TV(self, real_roomid): 342 | url = "https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/Check?roomid=" + \ 343 | str(real_roomid) 344 | headers = { 345 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36", 346 | } 347 | response = await self.bili_section_get(url, headers=headers) 348 | return response 349 | 350 | async def get_giftlist_of_captain(self, roomid): 351 | true_url = 'https://api.live.bilibili.com/lottery/v1/lottery/guard_check?roomid=' + \ 352 | str(roomid) 353 | headers = { 354 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q = 0.8", 355 | "Accept-Encoding": "gzip,async deflate,br", 356 | "Accept-Language": "zh-CN", 357 | "DNT": "1", 358 | "Cookie": "LIVE_BUVID=AUTO7715232653604550", 359 | "Connection": "keep-alive", 360 | "Cache-Control": "max-age =0", 361 | "Host": "api.live.bilibili.com", 362 | "Upgrade-Insecure-Requests": "1", 363 | "User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:59.0) Gecko/20100101 Firefox/59.0' 364 | } 365 | response1 = await self.bili_section_get(true_url, headers=headers) 366 | return response1 367 | 368 | async def get_lotterylist_of_pk(self, roomid): 369 | url = 'http://api.live.bilibili.com/xlive/lottery-interface/v1/pk/check?roomid=' + str(roomid) 370 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 371 | return response 372 | 373 | def get_giftids_raffle(self, str): 374 | return self.dic_bilibili['giftids_raffle'][str] 375 | 376 | def get_giftids_raffle_keys(self): 377 | return self.dic_bilibili['giftids_raffle'].keys() 378 | 379 | async def get_activity_result(self, activity_roomid, activity_raffleid): 380 | url = "https://api.live.bilibili.com/activity/v1/Raffle/notice?roomid=" + str( 381 | activity_roomid) + "&raffleId=" + str(activity_raffleid) 382 | headers = { 383 | 'Accept': 'application/json, text/plain, */*', 384 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 385 | 'Accept-Language': 'zh-CN,zh;q=0.9', 386 | 'accept-encoding': 'gzip, async deflate', 387 | 'Host': 'api.live.bilibili.com', 388 | 'cookie': self.dic_bilibili['cookie'], 389 | } 390 | response = await self.bili_section_get(url, headers=headers) 391 | return response 392 | 393 | async def get_TV_result(self, TV_roomid, TV_raffleid): 394 | url = "https://api.live.bilibili.com/gift/v3/smalltv/notice?raffleId=" + \ 395 | str(TV_raffleid) 396 | headers = { 397 | 'Accept': 'application/json, text/plain, */*', 398 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 399 | 'Accept-Language': 'zh-CN,zh;q=0.9', 400 | 'accept-encoding': 'gzip, async deflate', 401 | 'Host': 'api.live.bilibili.com', 402 | 'cookie': self.dic_bilibili['cookie'], 403 | } 404 | response = await self.bili_section_get(url, headers=headers) 405 | return response 406 | 407 | async def pcpost_heartbeat(self): 408 | url = 'https://api.live.bilibili.com/User/userOnlineHeart' 409 | data = { 410 | "csrf_token": self.dic_bilibili['csrf'], 411 | "csrf": self.dic_bilibili['csrf'] 412 | } 413 | response = await self.bili_section_post(url, data=data, headers=self.dic_bilibili['pcheaders']) 414 | return response 415 | 416 | # 发送app心跳包 417 | async def apppost_heartbeat(self): 418 | time = CurrentTime() 419 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 420 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 421 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&mobi_app=' + self.dic_bilibili[ 422 | 'mobi_app'] + '&platform=' + self.dic_bilibili['platform'] + '&ts=' + time 423 | sign = self.calc_sign(temp_params) 424 | url = 'https://api.live.bilibili.com/mobile/userOnlineHeart?' + \ 425 | temp_params + '&sign=' + sign 426 | payload = {'roomid': 23058, 'scale': 'xhdpi'} 427 | response = await self.bili_section_post(url, data=payload, headers=self.dic_bilibili['appheaders']) 428 | return response 429 | 430 | # 心跳礼物 431 | async def heart_gift(self): 432 | url = "https://api.live.bilibili.com/gift/v2/live/heart_gift_receive?roomid=3&area_v2_id=34" 433 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 434 | return response 435 | 436 | async def get_room_info(self, roomid): 437 | url = f"https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id={roomid}" 438 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders'], timeout=3) 439 | return response 440 | 441 | async def pk_list(self): 442 | url = "http://118.25.108.153:8080/pk" 443 | headers = { 444 | "User-Agent": "bilibili-live-tools/" + str(self.dic_bilibili['uid']) 445 | } 446 | response = requests.get(url, headers=headers, timeout=10) 447 | return response 448 | 449 | async def get_lotterylist(self, i): 450 | url = "https://api.live.bilibili.com/lottery/v1/box/getStatus?aid=" + \ 451 | str(i) 452 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 453 | return response 454 | 455 | async def get_gift_of_lottery(self, i, g): 456 | url1 = 'https://api.live.bilibili.com/xlive/lottery-interface/v2/Box/draw?aid=' + \ 457 | str(i) + '&number=' + str(g + 1) 458 | response1 = await self.bili_section_get(url1, headers=self.dic_bilibili['pcheaders']) 459 | return response1 460 | 461 | async def get_winner_info(self, i, g): 462 | url2 = 'https://api.live.bilibili.com/lottery/v1/box/getWinnerGroupInfo?aid=' + \ 463 | str(i) + '&number=' + str(g + 1) 464 | response2 = await self.bili_section_get(url2, headers=self.dic_bilibili['pcheaders']) 465 | return response2 466 | 467 | async def get_time_about_silver(self): 468 | time = CurrentTime() 469 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 470 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 471 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&mobi_app=' + self.dic_bilibili[ 472 | 'mobi_app'] + '&platform=' + self.dic_bilibili['platform'] + '&ts=' + time 473 | sign = self.calc_sign(temp_params) 474 | GetTask_url = 'https://api.live.bilibili.com/mobile/freeSilverCurrentTask?' + \ 475 | temp_params + '&sign=' + sign 476 | response = await self.bili_section_get(GetTask_url, headers=self.dic_bilibili['appheaders']) 477 | return response 478 | 479 | async def get_silver(self, timestart, timeend): 480 | time = CurrentTime() 481 | temp_params = 'access_key=' + self.dic_bilibili['access_key'] + '&actionKey=' + self.dic_bilibili[ 482 | 'actionKey'] + '&appkey=' + self.dic_bilibili['appkey'] + '&build=' + self.dic_bilibili[ 483 | 'build'] + '&device=' + self.dic_bilibili['device'] + '&mobi_app=' + self.dic_bilibili[ 484 | 'mobi_app'] + '&platform=' + self.dic_bilibili[ 485 | 'platform'] + '&time_end=' + timeend + '&time_start=' + timestart + '&ts=' + time 486 | sign = self.calc_sign(temp_params) 487 | url = 'https://api.live.bilibili.com/mobile/freeSilverAward?' + \ 488 | temp_params + '&sign=' + sign 489 | response = await self.bili_section_get(url, headers=self.dic_bilibili['appheaders']) 490 | return response 491 | 492 | async def get_dailybag(self): 493 | url = 'https://api.live.bilibili.com/gift/v2/live/receive_daily_bag' 494 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 495 | return response 496 | 497 | async def get_dosign(self): 498 | url = 'https://api.live.bilibili.com/sign/doSign' 499 | response = await self.bili_section_get(url, headers=self.dic_bilibili['pcheaders']) 500 | return response 501 | 502 | async def get_grouplist(self): 503 | url = "https://api.vc.bilibili.com/link_group/v1/member/my_groups" 504 | pcheaders = self.dic_bilibili['pcheaders'].copy() 505 | pcheaders['Host'] = "api.vc.bilibili.com" 506 | response = await self.bili_section_get(url, headers=pcheaders) 507 | return response 508 | 509 | async def assign_group(self, i1, i2): 510 | temp_params = "_device=" + self.dic_bilibili[ 511 | 'device'] + "&_hwid=SX1NL0wuHCsaKRt4BHhIfRguTXxOfj5WN1BkBTdLfhstTn9NfUouFiUV&access_key=" + \ 512 | self.dic_bilibili['access_key'] + "&appkey=" + self.dic_bilibili['appkey'] + "&build=" + \ 513 | self.dic_bilibili['build'] + "&group_id=" + str(i1) + "&mobi_app=" + self.dic_bilibili[ 514 | 'mobi_app'] + "&owner_id=" + str(i2) + "&platform=" + self.dic_bilibili[ 515 | 'platform'] + "&src=xiaomi&trace_id=20171224024300024&ts=" + CurrentTime() + "&version=5.20.1.520001" 516 | sign = self.calc_sign(temp_params) 517 | url = "https://api.vc.bilibili.com/link_setting/v1/link_setting/sign_in?" + \ 518 | temp_params + "&sign=" + sign 519 | appheaders = self.dic_bilibili['appheaders'].copy() 520 | appheaders['Host'] = "api.vc.bilibili.com" 521 | response = await self.bili_section_get(url, headers=appheaders) 522 | return response 523 | 524 | async def gift_list(self): 525 | url = "https://api.live.bilibili.com/gift/v2/live/room_gift_list?roomid=2721650&area_v2_id=86" 526 | res = await self.bili_section_get(url) 527 | return res 528 | 529 | async def check_activity_exist(self): 530 | url = "https://api.live.bilibili.com/activity/v1/Common/roomInfo?roomid=128666&ruid=18174739" 531 | response = await self.bili_section_get(url) 532 | return response 533 | 534 | async def query_guard(self, name): 535 | search_url = "https://search.bilibili.com/api/search?search_type=live_user&keyword=" + \ 536 | str(name) + "&page=1" 537 | response = await self.bili_section_get(search_url) 538 | return response 539 | 540 | async def check_room_state(self, roomid): 541 | init_url = "https://api.live.bilibili.com/room/v1/Room/room_init?id=" + \ 542 | str(roomid) 543 | response = await self.bili_section_get(init_url) 544 | json_response = await response.json(content_type=None) 545 | state = json_response['data']['live_status'] 546 | return state 547 | 548 | async def check_room_info(self, roomid): 549 | url = "https://api.live.bilibili.com/room/v1/Room/get_info?room_id=" + \ 550 | str(roomid) 551 | response = await self.bili_section_get(url) 552 | return response 553 | 554 | async def req_area_list(self): 555 | url = "http://api.live.bilibili.com/room/v1/Area/getList" 556 | response = await self.bili_section_get(url) 557 | return response 558 | 559 | async def heart_beat_e(self, room_id): 560 | response = await self.get_room_info(room_id) 561 | json_response = await response.json(content_type=None) 562 | parent_area_id = json_response['data']['room_info']['parent_area_id'] 563 | area_id = json_response['data']['room_info']['area_id'] 564 | 565 | url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E' 566 | headers = { 567 | 'Content-Type': 'application/x-www-form-urlencoded', 568 | 'Origin': 'https://live.bilibili.com', 569 | 'Referer': f'https://live.bilibili.com/{room_id}', 570 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', 571 | 'Cookie': self.dic_bilibili['cookie'], 572 | } 573 | payload = { 574 | 'id': [parent_area_id, area_id, 0, room_id], 575 | 'device': f'["{self.calc_sign(str(uuid.uuid4()))}","{uuid.uuid4()}"]', 576 | 'ts': int(time.time()) * 1000, 577 | 'is_patch': 0, 578 | 'heart_beat': [], 579 | 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36', 580 | 'csrf_token': self.dic_bilibili['csrf'], 581 | 'csrf': self.dic_bilibili['csrf'], 582 | 'visit_id': '' 583 | } 584 | data = parse.urlencode(payload) 585 | # {"code":0,"message":"0","ttl":1,"data":{"timestamp":1595342828,"heartbeat_interval":300,"secret_key":"seacasdgyijfhofiuxoannn","secret_rule":[2,5,1,4],"patch_status":2}} 586 | response = await self.bili_section_post(url, headers=headers, data=data) 587 | response = await response.json(content_type=None) 588 | payload['ets'] = response['data']['timestamp'] 589 | payload['secret_key'] = response['data']['secret_key'] 590 | payload['heartbeat_interval'] = response['data']['heartbeat_interval'] 591 | payload['secret_rule'] = response['data']['secret_rule'] 592 | return payload 593 | 594 | async def heart_beat_x(self, index, payload, room_id): 595 | response = await self.get_room_info(room_id) 596 | json_response = await response.json(content_type=None) 597 | parent_area_id = json_response['data']['room_info']['parent_area_id'] 598 | area_id = json_response['data']['room_info']['area_id'] 599 | url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X' 600 | headers = { 601 | 'Content-Type': 'application/x-www-form-urlencoded', 602 | 'Origin': 'https://live.bilibili.com', 603 | 'Referer': f'https://live.bilibili.com/{room_id}', 604 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', 605 | 'Cookie': self.dic_bilibili['cookie'], 606 | } 607 | s_data = { 608 | "t": { 609 | 'id': [parent_area_id, area_id, index, room_id], 610 | "device": payload['device'], # LIVE_BUVID 611 | "ets": payload['ets'], 612 | "benchmark": payload['secret_key'], 613 | "time": payload['heartbeat_interval'], 614 | "ts": int(time.time()) * 1000, 615 | "ua": payload['ua'] 616 | }, 617 | "r": payload['secret_rule'] 618 | } 619 | t = s_data['t'] 620 | payload = { 621 | 's': self.generate_s(s_data), 622 | 'id': t['id'], 623 | 'device': t['device'], 624 | 'ets': t['ets'], 625 | 'benchmark': t['benchmark'], 626 | 'time': t['time'], 627 | 'ts': t['ts'], 628 | "ua": t['ua'], 629 | 'csrf_token': self.dic_bilibili['csrf'], 630 | 'csrf': self.dic_bilibili['csrf'], 631 | 'visit_id': '', 632 | } 633 | payload = parse.urlencode(payload) 634 | # {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":300,"timestamp":1595346846,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}} 635 | response = await self.bili_section_post(url, headers=headers, data=payload) 636 | return response 637 | 638 | # 加密s 639 | def generate_s(self, data): 640 | url = 'http://127.0.0.1:3000/enc' 641 | response = requests.post(url, json=data).json() 642 | return response['s'] 643 | --------------------------------------------------------------------------------