import copy from enum import Enum, auto from random import uniform from millify import millify #from TwitchChannelPointsMiner.utils import char_decision_as_index, float_round from TwitchChannelPointsMiner.utils import float_round class Strategy(Enum): MOST_VOTED = auto() HIGH_ODDS = auto() PERCENTAGE = auto() SMART_MONEY = auto() SMART = auto() def __str__(self): return self.name class Condition(Enum): GT = auto() LT = auto() GTE = auto() LTE = auto() def __str__(self): return self.name class OutcomeKeys(object): # Real key on Bet dict [''] PERCENTAGE_USERS = "percentage_users" ODDS_PERCENTAGE = "odds_percentage" ODDS = "odds" TOP_POINTS = "top_points" # Real key on Bet dict [''] - Sum() TOTAL_USERS = "total_users" TOTAL_POINTS = "total_points" # This key does not exist DECISION_USERS = "decision_users" DECISION_POINTS = "decision_points" class DelayMode(Enum): FROM_START = auto() FROM_END = auto() PERCENTAGE = auto() def __str__(self): return self.name class FilterCondition(object): __slots__ = [ "by", "where", "value", ] def __init__(self, by=None, where=None, value=None, decision=None): self.by = by self.where = where self.value = value def __repr__(self): return f"FilterCondition(by={self.by.upper()}, where={self.where}, value={self.value})" class BetSettings(object): __slots__ = [ "strategy", "percentage", "percentage_gap", "max_points", "minimum_points", "stealth_mode", "filter_condition", "delay", "delay_mode", ] def __init__( self, strategy: Strategy = None, percentage: int = None, percentage_gap: int = None, max_points: int = None, minimum_points: int = None, stealth_mode: bool = None, filter_condition: FilterCondition = None, delay: float = None, delay_mode: DelayMode = None, ): self.strategy = strategy self.percentage = percentage self.percentage_gap = percentage_gap self.max_points = max_points self.minimum_points = minimum_points self.stealth_mode = stealth_mode self.filter_condition = filter_condition self.delay = delay self.delay_mode = delay_mode def default(self): self.strategy = self.strategy if self.strategy is not None else Strategy.SMART self.percentage = self.percentage if self.percentage is not None else 5 self.percentage_gap = ( self.percentage_gap if self.percentage_gap is not None else 20 ) self.max_points = self.max_points if self.max_points is not None else 50000 self.minimum_points = ( self.minimum_points if self.minimum_points is not None else 0 ) self.stealth_mode = ( self.stealth_mode if self.stealth_mode is not None else False ) self.delay = self.delay if self.delay is not None else 6 self.delay_mode = ( self.delay_mode if self.delay_mode is not None else DelayMode.FROM_END ) def __repr__(self): return f"BetSettings(strategy={self.strategy}, percentage={self.percentage}, percentage_gap={self.percentage_gap}, max_points={self.max_points}, minimum_points={self.minimum_points}, stealth_mode={self.stealth_mode})" class Bet(object): __slots__ = ["outcomes", "decision", "total_users", "total_points", "settings"] def __init__(self, outcomes: list, settings: BetSettings): self.outcomes = outcomes self.__clear_outcomes() self.decision: dict = {} self.total_users = 0 self.total_points = 0 self.settings = settings def update_outcomes(self, outcomes): for index in range(0, len(self.outcomes)): self.outcomes[index][OutcomeKeys.TOTAL_USERS] = int( outcomes[index][OutcomeKeys.TOTAL_USERS] ) self.outcomes[index][OutcomeKeys.TOTAL_POINTS] = int( outcomes[index][OutcomeKeys.TOTAL_POINTS] ) if outcomes[index]["top_predictors"] != []: # Sort by points placed by other users outcomes[index]["top_predictors"] = sorted( outcomes[index]["top_predictors"], key=lambda x: x["points"], reverse=True, ) # Get the first elements (most placed) top_points = outcomes[index]["top_predictors"][0]["points"] self.outcomes[index][OutcomeKeys.TOP_POINTS] = top_points # Inefficient, but otherwise outcomekeys are represented wrong self.total_points = 0 self.total_users = 0 for index in range(0, len(self.outcomes)): self.total_users += self.outcomes[index][OutcomeKeys.TOTAL_USERS] self.total_points += self.outcomes[index][OutcomeKeys.TOTAL_POINTS] if ( self.total_users > 0 and self.total_points > 0 ): for index in range(0, len(self.outcomes)): self.outcomes[index][OutcomeKeys.PERCENTAGE_USERS] = float_round( (100 * self.outcomes[index][OutcomeKeys.TOTAL_USERS]) / self.total_users ) self.outcomes[index][OutcomeKeys.ODDS] = float_round( #self.total_points / max(self.outcomes[index][OutcomeKeys.TOTAL_POINTS], 1) 0 if self.outcomes[index][OutcomeKeys.TOTAL_POINTS] == 0 else self.total_points / self.outcomes[index][OutcomeKeys.TOTAL_POINTS] ) self.outcomes[index][OutcomeKeys.ODDS_PERCENTAGE] = float_round( #100 / max(self.outcomes[index][OutcomeKeys.ODDS], 1) 0 if self.outcomes[index][OutcomeKeys.ODDS] == 0 else 100 / self.outcomes[index][OutcomeKeys.ODDS] ) self.__clear_outcomes() def __repr__(self): return f"Bet(total_users={millify(self.total_users)}, total_points={millify(self.total_points)}), decision={self.decision})\n\t\tOutcome A({self.get_outcome(0)})\n\t\tOutcome B({self.get_outcome(1)})" def get_decision(self, parsed=False): #decision = self.outcomes[0 if self.decision["choice"] == "A" else 1] decision = self.outcomes[self.decision["choice"]] return decision if parsed is False else Bet.__parse_outcome(decision) @staticmethod def __parse_outcome(outcome): return f"{outcome['title']} ({outcome['color']}), Points: {millify(outcome[OutcomeKeys.TOTAL_POINTS])}, Users: {millify(outcome[OutcomeKeys.TOTAL_USERS])} ({outcome[OutcomeKeys.PERCENTAGE_USERS]}%), Odds: {outcome[OutcomeKeys.ODDS]} ({outcome[OutcomeKeys.ODDS_PERCENTAGE]}%)" def get_outcome(self, index): return Bet.__parse_outcome(self.outcomes[index]) def __clear_outcomes(self): for index in range(0, len(self.outcomes)): keys = copy.deepcopy(list(self.outcomes[index].keys())) for key in keys: if key not in [ OutcomeKeys.TOTAL_USERS, OutcomeKeys.TOTAL_POINTS, OutcomeKeys.TOP_POINTS, OutcomeKeys.PERCENTAGE_USERS, OutcomeKeys.ODDS, OutcomeKeys.ODDS_PERCENTAGE, "title", "color", "id", ]: del self.outcomes[index][key] for key in [ OutcomeKeys.PERCENTAGE_USERS, OutcomeKeys.ODDS, OutcomeKeys.ODDS_PERCENTAGE, OutcomeKeys.TOP_POINTS, ]: if key not in self.outcomes[index]: self.outcomes[index][key] = 0 '''def __return_choice(self, key) -> str: return "A" if self.outcomes[0][key] > self.outcomes[1][key] else "B"''' def __return_choice(self, key) -> int: largest=0 for index in range(0, len(self.outcomes)): if self.outcomes[index][key] > self.outcomes[largest][key]: largest = index return largest def skip(self) -> bool: if self.settings.filter_condition is not None: # key == by , condition == where key = self.settings.filter_condition.by condition = self.settings.filter_condition.where value = self.settings.filter_condition.value fixed_key = ( key if key not in [OutcomeKeys.DECISION_USERS, OutcomeKeys.DECISION_POINTS] else key.replace("decision", "total") ) if key in [OutcomeKeys.TOTAL_USERS, OutcomeKeys.TOTAL_POINTS]: compared_value = ( self.outcomes[0][fixed_key] + self.outcomes[1][fixed_key] ) else: #outcome_index = char_decision_as_index(self.decision["choice"]) outcome_index = self.decision["choice"] compared_value = self.outcomes[outcome_index][fixed_key] # Check if condition is satisfied if condition == Condition.GT: if compared_value > value: return False, compared_value elif condition == Condition.LT: if compared_value < value: return False, compared_value elif condition == Condition.GTE: if compared_value >= value: return False, compared_value elif condition == Condition.LTE: if compared_value <= value: return False, compared_value return True, compared_value # Else skip the bet else: return False, 0 # Default don't skip the bet def calculate(self, balance: int) -> dict: self.decision = {"choice": None, "amount": 0, "id": None} if self.settings.strategy == Strategy.MOST_VOTED: self.decision["choice"] = self.__return_choice(OutcomeKeys.TOTAL_USERS) elif self.settings.strategy == Strategy.HIGH_ODDS: self.decision["choice"] = self.__return_choice(OutcomeKeys.ODDS) elif self.settings.strategy == Strategy.PERCENTAGE: self.decision["choice"] = self.__return_choice(OutcomeKeys.ODDS_PERCENTAGE) elif self.settings.strategy == Strategy.SMART_MONEY: self.decision["choice"] = self.__return_choice(OutcomeKeys.TOP_POINTS) elif self.settings.strategy == Strategy.SMART: difference = abs( self.outcomes[0][OutcomeKeys.PERCENTAGE_USERS] - self.outcomes[1][OutcomeKeys.PERCENTAGE_USERS] ) self.decision["choice"] = ( self.__return_choice(OutcomeKeys.ODDS) if difference < self.settings.percentage_gap else self.__return_choice(OutcomeKeys.TOTAL_USERS) ) if self.decision["choice"] is not None: #index = char_decision_as_index(self.decision["choice"]) index = self.decision["choice"] self.decision["id"] = self.outcomes[index]["id"] self.decision["amount"] = min( int(balance * (self.settings.percentage / 100)), self.settings.max_points, ) if ( self.settings.stealth_mode is True and self.decision["amount"] >= self.outcomes[index][OutcomeKeys.TOP_POINTS] ): reduce_amount = uniform(1, 5) self.decision["amount"] = ( self.outcomes[index][OutcomeKeys.TOP_POINTS] - reduce_amount ) self.decision["amount"] = int(self.decision["amount"]) return self.decision