From ab06868f06bb99b6dfa625dff8df2d8ddeac9b58 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Sun, 20 Jan 2019 23:10:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=83=A8=E5=88=86=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=A2=9E=E5=8A=A0Linux=E5=8F=AF=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E7=9A=84=E7=89=88=E6=9C=AC=EF=BC=8C=E5=8F=AF=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E9=80=89=E6=8B=A9=E6=83=B3=E8=A6=81=E8=BF=9B=E5=85=A5?= =?UTF-8?q?=E7=9A=84=E6=88=BF=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jerry Yan <792602257@qq.com> --- Chat.py | 23 ++++ GiftStruct.py => Gift.py | 19 +-- UserStruct.py => User.py | 26 +++-- WinMain.py | 193 ++++++++++++++++++++++++++++++ api.py | 246 +++++++++++---------------------------- 5 files changed, 316 insertions(+), 191 deletions(-) create mode 100644 Chat.py rename GiftStruct.py => Gift.py (70%) rename UserStruct.py => User.py (61%) create mode 100644 WinMain.py diff --git a/Chat.py b/Chat.py new file mode 100644 index 0000000..a800964 --- /dev/null +++ b/Chat.py @@ -0,0 +1,23 @@ +from User import User + +class Chat: + + content:str="" + user:User=None + + def __init__(self, json=None): + if json: + self.parse(json) + + def parse(self, json): + self.user = User(json) + if "Msg" in json: + if "content" in json["Msg"]: + self.content = json["Msg"]['content'] + + def __str__(self): + return "{} : {}".format(self.user,self.content) + + def __unicode__(self): + return self.__str__() + diff --git a/GiftStruct.py b/Gift.py similarity index 70% rename from GiftStruct.py rename to Gift.py index f95d655..c6e5a3a 100644 --- a/GiftStruct.py +++ b/Gift.py @@ -1,17 +1,21 @@ import requests +from User import User -class GiftStruct: - ID = 0 - count = 0 - giftList={10001: {"Name": "西瓜", "Price": 0}} - amount = 0 +class Gift: + ID:int = 0 + count:int = 0 + roomID:int = 0 + giftList:dict = {10001: {"Name": "西瓜", "Price": 0}} + amount:int = 0 + user:User = None def __init__(self, json=None): if json: self.parse(json) def parse(self, json): + self.user = User(json) if "Msg" in json: if "present_end_info" in json["Msg"]: self.ID = json["Msg"]['present_end_info']['id'] @@ -24,20 +28,21 @@ class GiftStruct: @staticmethod def update(roomID): + Gift.roomID = roomID p = requests.get("https://live.ixigua.com/api/gifts/{roomID}".format(roomID= roomID)) d = p.json() if isinstance(d, int) or "data" not in d: print("错误:礼物更新失败") else: for i in d["data"]: - GiftStruct.giftList[i["ID"]] = {"Name": i["Name"], "Price": i["DiamondCount"]} + Gift.giftList[i["ID"]] = {"Name": i["Name"], "Price": i["DiamondCount"]} def __str__(self): if self.ID in self.giftList: giftN = self.giftList[self.ID]["Name"] else: giftN = "未知礼物[{}]".format(self.ID) - return "{count} 个 {name}".format(count= self.count, name= giftN) + return "感谢 {user} 送出的 {count} 个 {name}".format(user= self.user, count= self.count, name= giftN) def __unicode__(self): return self.__str__() \ No newline at end of file diff --git a/UserStruct.py b/User.py similarity index 61% rename from UserStruct.py rename to User.py index 2b350af..c2314a7 100644 --- a/UserStruct.py +++ b/User.py @@ -1,9 +1,9 @@ -class UserStruct: - ID = 0 - name = "" - brand= "" - level= 0 - type = 0 +class User: + ID: int = 0 + name: str = "" + brand: str = "" + level: int = 0 + type: int = 0 def __init__(self, json=None): if json: @@ -18,14 +18,22 @@ class UserStruct: if "discipulus_info" in json["Msg"]: self.level = json["Msg"]["discipulus_info"]["level"] self.brand = json["Msg"]["discipulus_info"]["discipulus_group_title"] + elif "data" in json: + if "anchorInfo" in json["data"]: + self.ID = json["data"]['anchorInfo']['user_id'] + self.name = json["data"]['anchorInfo']['name'] + self.type = 0 if self.type is None: self.type = 0 def __str__(self): if self.level == 0: - if self.type != 0: - return "[]{}".format(self.name) - return "{}".format(self.name) + if self.type == 1: + return "[房管]{}".format(self.name) + elif self.type == 2: + return "[主播]{}".format(self.name) + else: + return "{}".format(self.name) else: if self.type != 0: return "[{}{}]{}".format(self.brand, self.level, self.name) diff --git a/WinMain.py b/WinMain.py new file mode 100644 index 0000000..7e51359 --- /dev/null +++ b/WinMain.py @@ -0,0 +1,193 @@ +import os +import sys +import time + +from Gift import Gift +from User import User + +from Chat import Chat +from api import XiGuaLiveApi as Api +import msvcrt +import ctypes + + +def readInput(caption, default, timeout: int = 5): + start_time = time.time() + print('{}({})\r\n>'.format(caption, default), end="") + input = '' + while True: + if msvcrt.kbhit(): + chr = msvcrt.getche() + if ord(chr) == 13: # enter_key + break + elif ord(chr) == 27: + break + elif ord(chr) == 8: + if input != "": + input = input[:-1] + msvcrt.putch(b" ") + msvcrt.putch(b"\b") + if len(input) == 0: + start_time = time.time() + elif 32 <= ord(chr) <= 126: # space_char + input += chr.decode("utf8") + if len(input) == 0 and (time.time() - start_time) > timeout: + break + + if len(input) > 0: + print() + return input + else: + print("使用默认值") + return default + + +STD_INPUT_HANDLE = -10 +STD_OUTPUT_HANDLE = -11 +STD_ERROR_HANDLE = -12 +std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) +# 字体颜色定义 ,关键在于颜色编码,由2位十六进制组成,分别取0~f,前一位指的是背景色,后一位指的是字体色 +# 由于该函数的限制,应该是只有这16种,可以前景色与背景色组合。也可以几种颜色通过或运算组合,组合后还是在这16种颜色中 + +# Windows CMD命令行 字体颜色定义 text colors +FOREGROUND_BLACK = 0x00 # black. +FOREGROUND_DARKBLUE = 0x01 # dark blue. +FOREGROUND_DARKGREEN = 0x02 # dark green. +FOREGROUND_DARKSKYBLUE = 0x03 # dark skyblue. +FOREGROUND_DARKRED = 0x04 # dark red. +FOREGROUND_DARKPINK = 0x05 # dark pink. +FOREGROUND_DARKYELLOW = 0x06 # dark yellow. +FOREGROUND_DARKWHITE = 0x07 # dark white. +FOREGROUND_DARKGRAY = 0x08 # dark gray. +FOREGROUND_BLUE = 0x09 # blue. +FOREGROUND_GREEN = 0x0a # green. +FOREGROUND_SKYBLUE = 0x0b # skyblue. +FOREGROUND_RED = 0x0c # red. +FOREGROUND_PINK = 0x0d # pink. +FOREGROUND_YELLOW = 0x0e # yellow. +FOREGROUND_WHITE = 0x0f # white. + +# Windows CMD命令行 背景颜色定义 background colors +BACKGROUND_BLUE = 0x10 # dark blue. +BACKGROUND_GREEN = 0x20 # dark green. +BACKGROUND_DARKSKYBLUE = 0x30 # dark skyblue. +BACKGROUND_DARKRED = 0x40 # dark red. +BACKGROUND_DARKPINK = 0x50 # dark pink. +BACKGROUND_DARKYELLOW = 0x60 # dark yellow. +BACKGROUND_DARKWHITE = 0x70 # dark white. +BACKGROUND_DARKGRAY = 0x80 # dark gray. +BACKGROUND_BLUE = 0x90 # blue. +BACKGROUND_GREEN = 0xa0 # green. +BACKGROUND_SKYBLUE = 0xb0 # skyblue. +BACKGROUND_RED = 0xc0 # red. +BACKGROUND_PINK = 0xd0 # pink. +BACKGROUND_YELLOW = 0xe0 # yellow. +BACKGROUND_WHITE = 0xf0 # white. + + +def set_cmd_text_color(color, handle=std_out_handle): + Bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color) + return Bool + + +def resetColor(): + set_cmd_text_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) + + +class WinMain(Api): + _tmp = 0 + + def getTitle(self): + self._tmp += 1 + if self._tmp > 10: + self._tmp = 0 + if self._tmp < 5 : + return "{} {} --弹幕助手 by JerryYan".format(self.roomLiver, "的直播间") + else: + if self.roomPopularity == 0: + self._tmp = 0 + return self.getTitle() + else: + if self.roomMember > 0: + return "观看人数:{} 人气:{} --弹幕助手 by JerryYan".format(self.roomMember, self.roomPopularity) + else: + return "观看人数:待刷新数据 人气:{} --弹幕助手 by JerryYan".format(self.roomPopularity) + + + def onMessage(self, msg: str): + set_cmd_text_color(FOREGROUND_DARKGRAY) + print("消息:", msg) + resetColor() + + def onJoin(self, user: User): + set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK) + print("感谢", user, "加入了粉丝团") + resetColor() + + def onSubscribe(self, user: User): + return + + def onEnter(self, user: User, content: str == ""): + if content == "": + if user.name == "三国空白" or user.name == "四维v": + set_cmd_text_color(FOREGROUND_DARKGRAY) + print("消息:", user, "进入直播间") + resetColor() + else: + set_cmd_text_color(FOREGROUND_DARKGRAY) + print("消息:", content.format(user)) + resetColor() + + def onChat(self, chat: Chat): + print(chat) + + def onPresent(self, gift: Gift): + return + + def onPresentEnd(self, gift: Gift): + set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK) + print(gift) + resetColor() + + def onLike(self, user: User): + return + + def onLeave(self, json: any): + return + +def warning(*args): + print(*args) + + +if __name__ == "__main__": + room = 97621754276 # 永恒 + # room = 75366565294 + # room = 83940182312 #Dae + resetColor() + print("西瓜直播弹幕助手 by JerryYan") + if len(sys.argv) > 1: + room = int(sys.argv[1]) + else: + try: + room = int(readInput("请输入房间号,默认为永恒的直播间", room, 3)) + except ValueError: + pass + api = WinMain(room) + print("进入", api.roomLiver, "的直播间") + if not api.isValidRoom: + input("房间不存在") + sys.exit() + os.system("title {}".format(api.getTitle())) + print("=" * 30) + while True: + if api.isLive: + try: + os.system("title {}".format(api.getTitle())) + api.getDanmaku() + except Exception as e: + warning(e) + time.sleep(1) + else: + print("主播未开播,等待1分钟后重试") + time.sleep(60) + api.updRoomInfo() diff --git a/api.py b/api.py index 6aac16f..e4c590c 100644 --- a/api.py +++ b/api.py @@ -1,194 +1,98 @@ -import msvcrt import sys -from UserStruct import UserStruct -from GiftStruct import GiftStruct +from User import User +from Gift import Gift +from Chat import Chat import requests import time -import ctypes -import os - - -def warning(*args): - print(*args) - - -def debug(*args): - # print(args) - pass - - - - -def readInput(caption, default, timeout:int=5): - start_time = time.time() - print('{}({})\r\n>'.format(caption,default), end="") - input = '' - while True: - if msvcrt.kbhit(): - chr = msvcrt.getche() - if ord(chr) == 13: # enter_key - break - elif ord(chr) == 27: - break - elif ord(chr) == 8: - if input != "": - input = input[:-1] - elif 32 <= ord(chr) <= 126: # space_char - input += chr.decode("utf8") - if len(input) == 0 and (time.time() - start_time) > timeout: - break - - if len(input) > 0: - print() - return input - else: - print("使用默认值") - return default - - -STD_INPUT_HANDLE = -10 -STD_OUTPUT_HANDLE = -11 -STD_ERROR_HANDLE = -12 -std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) -# 字体颜色定义 ,关键在于颜色编码,由2位十六进制组成,分别取0~f,前一位指的是背景色,后一位指的是字体色 -#由于该函数的限制,应该是只有这16种,可以前景色与背景色组合。也可以几种颜色通过或运算组合,组合后还是在这16种颜色中 - -# Windows CMD命令行 字体颜色定义 text colors -FOREGROUND_BLACK = 0x00 # black. -FOREGROUND_DARKBLUE = 0x01 # dark blue. -FOREGROUND_DARKGREEN = 0x02 # dark green. -FOREGROUND_DARKSKYBLUE = 0x03 # dark skyblue. -FOREGROUND_DARKRED = 0x04 # dark red. -FOREGROUND_DARKPINK = 0x05 # dark pink. -FOREGROUND_DARKYELLOW = 0x06 # dark yellow. -FOREGROUND_DARKWHITE = 0x07 # dark white. -FOREGROUND_DARKGRAY = 0x08 # dark gray. -FOREGROUND_BLUE = 0x09 # blue. -FOREGROUND_GREEN = 0x0a # green. -FOREGROUND_SKYBLUE = 0x0b # skyblue. -FOREGROUND_RED = 0x0c # red. -FOREGROUND_PINK = 0x0d # pink. -FOREGROUND_YELLOW = 0x0e # yellow. -FOREGROUND_WHITE = 0x0f # white. - - -# Windows CMD命令行 背景颜色定义 background colors -BACKGROUND_BLUE = 0x10 # dark blue. -BACKGROUND_GREEN = 0x20 # dark green. -BACKGROUND_DARKSKYBLUE = 0x30 # dark skyblue. -BACKGROUND_DARKRED = 0x40 # dark red. -BACKGROUND_DARKPINK = 0x50 # dark pink. -BACKGROUND_DARKYELLOW = 0x60 # dark yellow. -BACKGROUND_DARKWHITE = 0x70 # dark white. -BACKGROUND_DARKGRAY = 0x80 # dark gray. -BACKGROUND_BLUE = 0x90 # blue. -BACKGROUND_GREEN = 0xa0 # green. -BACKGROUND_SKYBLUE = 0xb0 # skyblue. -BACKGROUND_RED = 0xc0 # red. -BACKGROUND_PINK = 0xd0 # pink. -BACKGROUND_YELLOW = 0xe0 # yellow. -BACKGROUND_WHITE = 0xf0 # white. - s = requests.Session() -def set_cmd_text_color(color, handle=std_out_handle): - Bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color) - return Bool - -def resetColor(): - set_cmd_text_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) class XiGuaLiveApi: - isLive = False - roomInfo = {} - roomID = 0 - cursor = "" + isLive: bool = False + isValidRoom: bool = False + _rawRoomInfo = {} + roomID: int = 0 + roomTitle: str = "" + roomLiver: User = None + roomPopularity: int = 0 + roomMember: int = 0 + _cursor = "" def __init__(self, room: int): self.room = room self.updRoomInfo() - GiftStruct.update(self.roomID) + Gift.update(self.roomID) + self._enterRoom() def notLiveError(self): print("主播未开播") - def apiChangedError(self, msg:str): + def _updateRoomInfo(self, json): + if "Msg" in json: + if "member_count" in json["Msg"]: + self.roomMember = json["Msg"]["member_count"] + if "popularity" in json["Msg"]: + self.roomPopularity = json["Msg"]["popularity"] + + def apiChangedError(self, msg: str, *args): print(msg) + print(*args) - def onPresent(self, user:UserStruct, gift:GiftStruct): - return - # print("礼物连击:", user, "的", gift) + def onPresent(self, gift: Gift): + print("礼物连击:", gift) - def onPresentEnd(self, user:UserStruct, gift:GiftStruct): - set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK) - print("感谢", user, "送出的", gift) - resetColor() + def onPresentEnd(self, gift: Gift): + print("礼物", gift) def onAd(self, i): # print(i) pass - def onChat(self, user:UserStruct, content:str): - print(user, ":", content) - # pass + def onChat(self, chat: Chat): + print(chat) - def onEnter(self, user:UserStruct, content:str == ""): + def onEnter(self, user: User, content: str == ""): if content == "": - if user.name == "三国空白" or user.name == "四维v": - set_cmd_text_color(FOREGROUND_DARKGRAY) - print("消息:", user, "进入直播间") - resetColor() + print("消息:", user, "进入直播间") else: - set_cmd_text_color(FOREGROUND_DARKGRAY) print("消息:", content.format(user)) - resetColor() - def onSubscribe(self, user:UserStruct): - if user.level >= 6 and user.brand == "永恒": - set_cmd_text_color(FOREGROUND_DARKGRAY) - print("消息:", user, "关注了主播") - resetColor() + def onSubscribe(self, user: User): + print("消息:", user, "关注了主播") - def onJoin(self, user:UserStruct): - set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK) + def onJoin(self, user: User): print("感谢", user, "加入了粉丝团") - resetColor() - def onMessage(self, msg:str): - set_cmd_text_color(FOREGROUND_DARKGRAY) + def onMessage(self, msg: str): print("消息:", msg) - resetColor() - def onLike(self, user:UserStruct): - return - # set_cmd_text_color(FOREGROUND_DARKGRAY) - # print("用户", user, "点了喜欢") - # resetColor() + def onLike(self, user: User): + print("用户", user, "点了喜欢") - def onLeave(self, json:any): + def onLeave(self, json: any): print("消息:", "主播离开一小会") - debug(json) - return - def enterRoom(self): + def _enterRoom(self): + if not self.isValidRoom: + return p = s.post("https://live.ixigua.com/api/room/enter/{roomID}".format(roomID=self.roomID)) - debug(p.json()) def updRoomInfo(self): p = s.get("https://live.ixigua.com/api/room/{room}".format(room=self.room)) d = p.json() - debug(d) if "data" not in d: - self.apiChangedError("数据结构改变,请与我联系") - debug(d) + self.apiChangedError("无法获取RoomID,请与我联系") return - self.roomInfo = d["data"] - print("进入", self.roomInfo["anchorInfo"]["name"], "的直播间") + self.isValidRoom = True + self._rawRoomInfo = d["data"] + self.roomLiver = User(d) + self.roomTitle = self._rawRoomInfo["Title"] + self.roomPopularity = self._rawRoomInfo["Extra2"]["Popularity"] if "Id" in d["data"]: self.roomID = d["data"]["Id"] else: - warning("无法获取RoomID,请与我联系") + self.apiChangedError("无法获取RoomID,请与我联系") if "FinishTime" in d["data"]: self.isLive = False self.notLiveError() @@ -196,76 +100,68 @@ class XiGuaLiveApi: self.isLive = True def getDanmaku(self): + if not self.isValidRoom: + return p = s.get("https://live.ixigua.com/api/msg/list/{roomID}?AnchorID={room}&Cursor={cursor}".format( roomID=self.roomID, room=self.room, - cursor=self.cursor + cursor=self._cursor )) d = p.json() - debug(d) if "data" not in d: - self.apiChangedError("数据结构改变,请与我联系") - debug(d) + self.apiChangedError("数据结构改变,请与我联系", d) return if "Extra" not in d["data"]: - self.apiChangedError("数据结构改变,请与我联系") - debug(d["data"]) + self.apiChangedError("数据结构改变,请与我联系", d) return if "Cursor" not in d["data"]["Extra"]: - self.apiChangedError("数据结构改变,请与我联系") - debug(d["data"]) + self.apiChangedError("数据结构改变,请与我联系", d) return else: - self.cursor = d["data"]["Extra"]["Cursor"] + self._cursor = d["data"]["Extra"]["Cursor"] if "LiveMsgs" not in d["data"]: return for i in d['data']['LiveMsgs']: if i['Method'] == "VideoLivePresentMessage": - self.onPresent(UserStruct(i), GiftStruct(i)) + self.onPresent(Gift(i)) elif i['Method'] == "VideoLivePresentEndTipMessage": - self.onPresentEnd(UserStruct(i), GiftStruct(i)) + self.onPresentEnd(Gift(i)) elif i['Method'] == "VideoLiveRoomAdMessage": self.onAd(i) elif i['Method'] == "VideoLiveChatMessage": - self.onChat(UserStruct(i), i["Msg"]['content']) + self.onChat(Chat(i)) elif i['Method'] == "VideoLiveMemberMessage": - self.onEnter(UserStruct(i), i["Msg"]["content"]) + self._updateRoomInfo(i) + self.onEnter(User(i), i["Msg"]["content"]) elif i['Method'] == "VideoLiveSocialMessage": - self.onSubscribe(UserStruct(i)) + self.onSubscribe(User(i)) elif i['Method'] == "VideoLiveJoinDiscipulusMessage": - self.onJoin(UserStruct(i)) + self.onJoin(User(i)) elif i['Method'] == "VideoLiveControlMessage": print("消息:", "主播离开一小会") elif i['Method'] == "VideoLiveDiggMessage": - self.onLike(UserStruct(i)) + self.onLike(User(i)) else: - debug(i) + pass if __name__ == "__main__": - room = 97621754276 #永恒 + room = 97621754276 # 永恒 # room = 75366565294 # room = 83940182312 #Dae - resetColor() - if len(sys.argv)>1: - room = int(sys.argv[1]) - else: - try: - room = int(readInput("请输入房间号,默认为永恒的直播间", room, 3)) - except ValueError: - pass print("西瓜直播弹幕助手 by JerryYan") - print("正在进入房间", room) api = XiGuaLiveApi(room) - os.system("title {} {}".format(api.roomInfo["anchorInfo"]["name"],"的直播间 --西瓜直播弹幕助手 by JerryYan")) - api.enterRoom() - print("="*30) + print("进入", api.roomLiver, "的直播间") + if not api.isValidRoom: + input("房间不存在") + sys.exit() + print("=" * 30) while True: if api.isLive: try: api.getDanmaku() except Exception as e: - warning(e) + print(e) time.sleep(1) else: print("主播未开播,等待1分钟后重试")