From 467fb8ecca7047c338c69d49af9f73ecbba3005e Mon Sep 17 00:00:00 2001 From: Alessandro Maggio Date: Sat, 23 Jan 2021 11:55:06 +0100 Subject: [PATCH] better 'Watch Streak' strategy in priority system #11 --- .../TwitchChannelPointsMiner.py | 10 +++- TwitchChannelPointsMiner/classes/Streamer.py | 29 +++++++++-- TwitchChannelPointsMiner/classes/Twitch.py | 51 ++++++++++++++++--- .../classes/TwitchLogin.py | 7 ++- .../classes/TwitchWebSocket.py | 5 +- .../classes/WebSocketsPool.py | 7 +-- 6 files changed, 86 insertions(+), 23 deletions(-) diff --git a/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py b/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py index cfa46dc9..679e3258 100644 --- a/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py +++ b/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py @@ -38,6 +38,7 @@ def __init__( username: str, make_predictions: bool = True, follow_raid: bool = True, + watch_streak: bool = False, logger_settings: LoggerSettings = LoggerSettings(), browser_settings: BrowserSettings = BrowserSettings(), bet_settings: BetSettings = BetSettings(), @@ -46,6 +47,7 @@ def __init__( self.twitch = Twitch(self.username) self.twitch_browser = None self.follow_raid = follow_raid + self.watch_streak = watch_streak self.streamers = [] self.events_predictions = {} self.minute_watcher_thread = None @@ -135,7 +137,11 @@ def run(self, streamers: list = [], followers=False): self.twitch_browser.init() self.minute_watcher_thread = threading.Thread( - target=self.twitch.send_minute_watched_events, args=(self.streamers,) + target=self.twitch.send_minute_watched_events, + args=( + self.streamers, + self.watch_streak, + ), ) # self.minute_watcher_thread.daemon = True self.minute_watcher_thread.start() @@ -186,6 +192,8 @@ def run(self, streamers: list = [], followers=False): WebSocketsPool.handle_websocket_reconnection(self.ws_pool.ws) def end(self, signum, frame): + logger.info("CTRL+C Detected! Please wait, just a moment\n") + if self.twitch_browser is not None: self.twitch_browser.browser.quit() diff --git a/TwitchChannelPointsMiner/classes/Streamer.py b/TwitchChannelPointsMiner/classes/Streamer.py index e4eff4ba..054b85a5 100644 --- a/TwitchChannelPointsMiner/classes/Streamer.py +++ b/TwitchChannelPointsMiner/classes/Streamer.py @@ -12,8 +12,13 @@ def __init__(self, username, channel_id, less_printing: bool = False): self.online_at = 0 self.offline_at = 0 self.channel_points = 0 + self.minute_watched_requests = None + self.minute_watched = 0 + self.last_minute_watched = 0 + self.raid = None + self.watch_streak_missing = True self.history = {} self.streamer_url = f"https://www.twitch.tv/{self.username}" @@ -36,15 +41,28 @@ def __str__(self): ) def set_offline(self): - self.offline_at = time.time() - self.is_online = False + if self.is_online is True: + self.offline_at = time.time() + self.is_online = False + logger.info(f"{self} is Offline!", extra={"emoji": ":sleeping:"}) def set_online(self): - self.online_at = time.time() - self.is_online = True + if self.is_online is False: + self.online_at = time.time() + self.is_online = True + # Watch streak variables + self.watch_streak_missing = True + self.minute_watched = 0 + self.last_minute_watched = 0 + logger.info(f"{self} is Online!", extra={"emoji": ":partying_face:"}) + def update_minute_watched(self): + if self.last_minute_watched != 0: + self.minute_watched += round((time.time() - self.last_minute_watched) / 60, 5) + self.last_minute_watched = time.time() + def print_history(self): return ", ".join( [ @@ -59,5 +77,8 @@ def update_history(self, reason_code, earned): self.history[reason_code]["counter"] += 1 self.history[reason_code]["amount"] += earned + if reason_code == "WATCH_STREAK": + self.watch_streak_missing = False + def set_less_printing(self, value): self.less_printing = value diff --git a/TwitchChannelPointsMiner/classes/Twitch.py b/TwitchChannelPointsMiner/classes/Twitch.py index 29d0a639..c61c8b93 100644 --- a/TwitchChannelPointsMiner/classes/Twitch.py +++ b/TwitchChannelPointsMiner/classes/Twitch.py @@ -180,25 +180,60 @@ def make_predictions(self, event): } ) - def send_minute_watched_events(self, streamers): + def send_minute_watched_events(self, streamers, watch_streak=False): headers = {"user-agent": USER_AGENT} while self.running: # Twitch has a limit - you can't watch more than 2 channels at one time. # We take the first two streamers from the list as they have the highest priority. - streamers_watching = [ - streamer for streamer in streamers if streamer.is_online - ][:2] - for streamer in streamers_watching: + streamers_index = [ + i for i in range(0, len(streamers)) if streamers[i].is_online + ] + + # Check if we need need to change priority based on watch streak + """ + Viewers receive points for returning for x consecutive streams. + Each stream must be at least 10 minutes long and it must have been at least 30 minutes since the last stream ended. + """ + streamers_watching = [] + if watch_streak is True: + for index in streamers_index: + if ( + streamers[index].watch_streak_missing + and ( + streamers[index].offline_at == 0 + or ((time.time() - streamers[index].offline_at) // 60) > 30 + ) + and streamers[index].minute_watched <= 6 + ): + logger.info( + f"Switch priority: {streamers[index]}, WatchStreak missing is {streamers[index].watch_streak_missing} and minute_watched: {streamers[index].minute_watched}" + ) + streamers_watching.append(index) + if len(streamers_watching) == 2: + break + + if streamers_watching == []: + streamers_watching = streamers_index + else: + while len(streamers_watching) < 2 and len(streamers_index) > 1: + another_streamer_index = streamers_index.pop(0) + if another_streamer_index not in streamers_watching: + streamers_watching.append(another_streamer_index) + + streamers_watching = streamers_watching[:2] + for index in streamers_watching: next_iteration = time.time() + 60 / len(streamers_watching) try: response = requests.post( - streamer.minute_watched_requests.url, - data=streamer.minute_watched_requests.payload, + streamers[index].minute_watched_requests.url, + data=streamers[index].minute_watched_requests.payload, headers=headers, ) logger.debug( - f"Send minute watched request for {streamer} - Status code: {response.status_code}" + f"Send minute watched request for {streamers[index]} - Status code: {response.status_code}" ) + if response.status_code == 204: + streamers[index].update_minute_watched() except requests.exceptions.ConnectionError as e: logger.error(f"Error while trying to watch a minute: {e}") diff --git a/TwitchChannelPointsMiner/classes/TwitchLogin.py b/TwitchChannelPointsMiner/classes/TwitchLogin.py index e0f9c3e1..2c184cc8 100644 --- a/TwitchChannelPointsMiner/classes/TwitchLogin.py +++ b/TwitchChannelPointsMiner/classes/TwitchLogin.py @@ -20,10 +20,9 @@ def __init__(self, client_id, username, user_agent): self.token = None self.login_check_result = False self.session = requests.session() - self.session.headers.update({ - "Client-ID": self.client_id, - "User-Agent": user_agent - }) + self.session.headers.update( + {"Client-ID": self.client_id, "User-Agent": user_agent} + ) self.username = username self.user_id = None self.email = None diff --git a/TwitchChannelPointsMiner/classes/TwitchWebSocket.py b/TwitchChannelPointsMiner/classes/TwitchWebSocket.py index 1ad8165f..12fad55b 100644 --- a/TwitchChannelPointsMiner/classes/TwitchWebSocket.py +++ b/TwitchChannelPointsMiner/classes/TwitchWebSocket.py @@ -2,7 +2,6 @@ import logging import time -from datetime import timedelta from random import randrange from websocket import WebSocketApp @@ -66,7 +65,7 @@ def reset(self, parent_pool): self.last_ping = time.time() def elapsed_last_pong(self): - return (time.time - self.last_pong) // 60 + return (time.time() - self.last_pong) // 60 def elapsed_last_ping(self): - return (time.time - self.last_ping) // 60 + return (time.time() - self.last_ping) // 60 diff --git a/TwitchChannelPointsMiner/classes/WebSocketsPool.py b/TwitchChannelPointsMiner/classes/WebSocketsPool.py index 6372f92c..d89ce839 100644 --- a/TwitchChannelPointsMiner/classes/WebSocketsPool.py +++ b/TwitchChannelPointsMiner/classes/WebSocketsPool.py @@ -167,15 +167,16 @@ def on_message(ws, message): ) elif topic == "video-playback-by-id": + # There is stream-up message type, but it's sent earlier than the API updates streamer_index = get_streamer_index(ws.streamers, topic_user) if streamer_index != -1: if message_type == "stream-down": - ws.streamers[streamer_index].set_offline() + if ws.streamers[streamer_index].is_online is True: + ws.streamers[streamer_index].set_offline() elif message_type == "viewcount": ws.twitch.check_streamer_online( ws.streamers[streamer_index] ) - # There is stream-up message type, but it's sent earlier than the API updates elif topic == "raid": streamer_index = get_streamer_index(ws.streamers, topic_user) @@ -234,7 +235,6 @@ def on_message(ws, message): ) if start_bet_status is True: # place_bet_thread = threading.Timer(event.closing_bet_after(current_timestamp), ws.twitch.make_predictions, (ws.events_predictions[event_id],)) - execution_time = round(execution_time, 2) start_after = ( event.closing_bet_after( current_timestamp @@ -242,6 +242,7 @@ def on_message(ws, message): - execution_time ) start_after = max(1, start_after) + start_after = round(start_after, 2) place_bet_thread = threading.Timer( start_after, ws.twitch_browser.place_bet,