Beispiel #1
0
    def testArrowTime(self):
        midnight_in_utc = util_lib.ArrowTime()
        raw_arrow_time = arrow.utcnow().replace(hour=0,
                                                minute=0,
                                                second=0,
                                                microsecond=0)
        self.assertEqual(raw_arrow_time, midnight_in_utc)

        est = 'US/Eastern'
        noon_in_est = util_lib.ArrowTime(12, 0, 0, est)
        raw_arrow_time = arrow.now(est).replace(hour=12,
                                                minute=0,
                                                second=0,
                                                microsecond=0)
        self.assertEqual(raw_arrow_time, noon_in_est)

        afternoon_in_local = util_lib.ArrowTime(16, tz='local')
        raw_arrow_time = arrow.now().replace(hour=16,
                                             minute=0,
                                             second=0,
                                             microsecond=0)
        self.assertEqual(raw_arrow_time, afternoon_in_local)

        morning_in_local = util_lib.ArrowTime(8, tz='local')
        morning_tomorrow = arrow.now().shift(days=1)
        morning_tomorrow = morning_tomorrow.replace(hour=8,
                                                    minute=0,
                                                    second=0,
                                                    microsecond=0)
        self.assertNotEqual(morning_tomorrow, morning_in_local)
Beispiel #2
0
    def __init__(self, params):
        self._params = params_lib.HypeParams(self.DEFAULT_PARAMS)
        self._params.Override(params)
        if self._params.interface:
            self._params.interface.Override(
                {'name': self._params.name.lower()})
        self._params.Lock()

        # self.interface always maintains the connected interface that is listening
        # for messages. self._core.interface holds the interface desired for
        # outgoing communication. It may be swapped out on the fly, e.g., to handle
        # nested callbacks.
        self.interface = interface_factory.CreateFromParams(
            self._params.interface)
        self._core = hypecore.Core(self._params, self.interface)

        self.interface.RegisterHandlers(self.HandleMessage,
                                        self._core.user_tracker)

        # TODO(someone): Factory built code change listener.

        # TODO(someone): Should betting on stocks be encapsulated into a
        # `command` so that it can be loaded on demand?
        self._stock_game = vegas_game_lib.StockGame(self._core.stocks)
        self._core.betting_games.append(self._stock_game)
        self._core.scheduler.DailyCallback(
            util_lib.ArrowTime(16, 0, 30, 'America/New_York'),
            self._StockCallback)

        self._commands = [
            command_factory.Create(name, params, self._core)
            for name, params in self._params.commands.AsDict().items()
            if params not in (None, False)
        ]
Beispiel #3
0
    def __init__(self, *args):
        super(StocksCommand, self).__init__(*args)
        if self._params.enable_betting:
            self._game = vegas_game_lib.StockGame(self._core.stocks)
            self._core.betting_games.append(self._game)

            self._core.scheduler.DailyCallback(
                util_lib.ArrowTime(16, 0, 30, 'America/New_York'),
                self._BetCallback)
Beispiel #4
0
 def __init__(self, proxy: proxy_lib.Proxy):
     self._proxy = proxy
     self._scheduler = schedule_lib.HypeScheduler()
     self._ids_to_names = self._US_STATE_NAMES.copy()
     self._populations = self._US_STATE_POPULATIONS.copy()
     # 5am is an arbitrary time, can be changed without any semantic effect.
     self._scheduler.DailyCallback(util_lib.ArrowTime(5),
                                   self._UpdatePopulations)
     self._UpdatePopulations()
Beispiel #5
0
  def __init__(self, *args):
    super(JackpotCommand, self).__init__(*args)
    self._game = vegas_game_lib.LotteryGame(self._core.bets)
    self._core.betting_games.append(self._game)

    lotto_time = util_lib.ArrowTime(*self._params.lottery_time,
                                    tz=self._core.timezone)
    self._core.scheduler.DailyCallback(
        lotto_time, self._LotteryCallback, _jitter=10)
    for warning_sec in self._params.warnings:
      warning_offset = timedelta(seconds=warning_sec)
      warning_time = lotto_time - warning_offset
      self._core.scheduler.DailyCallback(
          warning_time, self._LotteryWarningCallback, warning_offset,
          _jitter=0)
Beispiel #6
0
    def __init__(self, scheduler: schedule_lib.HypeScheduler,
                 store: storage_lib.HypeStore, bot_name: Text,
                 params: params_lib.HypeParams):
        self._scheduler = scheduler
        self._store = store
        self._bot_name = bot_name
        self._params = params_lib.HypeParams(self.DEFAULT_PARAMS)
        self._params.Override(params)
        self._params.Lock()

        self.badges = None
        self._LoadBadges()
        self._InitWeights()
        self._scheduler.DailyCallback(util_lib.ArrowTime(6),
                                      self._RestoreEnergy)
Beispiel #7
0
  def _LoadStage(self, stage_id):
    """Loads a single stage (bracket)."""
    with self._lock:
      stage = self._proxy.FetchJson(
          '/'.join([self._BASE_URL, 'stages', stage_id]), force_lookup=True)
      bracket = esports_pb2.Bracket(
          bracket_id=self._MakeBracketId(stage_id),
          name=stage['name'],
          league_id=self.league_id,
          is_playoffs='playoff' in stage['name'].lower())
      self._brackets[bracket.bracket_id] = bracket

      matches = self._proxy.FetchJson(
          '/'.join([self._BASE_URL, 'stages', stage_id, 'matches']),
          force_lookup=True)
      # Battlefy doesn't provide actual match start times. We assume that
      # matches are only provided for the current week. And then replace with
      # completed time if it exists.
      default_match_time = util_lib.ArrowTime(
          weekday=5, hour=12, tz='America/Los_Angeles')
      for match in matches:
        match_time = default_match_time
        if 'completedAt' in match:
          match_time = arrow.get(match['completedAt'])
        m = bracket.schedule.add(
            match_id=match['_id'],
            bracket_id=bracket.bracket_id,
            red=util_lib.Access(match, 'top.teamID', 'BYE'),
            blue=util_lib.Access(match, 'bottom.teamID', 'BYE'),
            timestamp=match_time.timestamp,
            winner=self._MatchWinner(match))
        self._matches[m.match_id] = m
        stats = None
        if self.stats_enabled and m.winner:
          stats = self._proxy.FetchJson(
              '/'.join([self._BASE_URL, 'matches', m.match_id]),
              params={'extend[stats]': 'true'},
              force_lookup=True)
        for stat_idx, game_id in enumerate(match.get('appliedRiotGameIDs', [])):
          game = m.games.add(
              game_id=game_id, realm=self._realm, hash=match['lolHookUrl'])
          game_stats = util_lib.Access(stats, '0.stats.%d.stats' % stat_idx)
          if game_stats:
            self._ParseGameStats(game, game_stats)

    self._UpdateStandings(bracket)
Beispiel #8
0
class Thievery(object):
    """Allows nefarious behavior.

  The more you steal, the more you get caught. The more you are a victim, the
  more you catch peeps.

  We keep a score which is an exponential decay of the sum of past successful
  theft amounts for victims and thiefs. Your percent of the total score impacts
  future theft chances. Hypebot has a fixed large number in each pool to prevent
  solitary thefts from overloading the system. Periodically, all scores are
  reduced except hypebot's.
  """

    # Rate to decay scores. I.e., score_t+1 = score_t * DECAY_RATE
    _DECAY_RATE = 0.75
    # Arrow object specifying when decay should occur.
    _DECAY_TIME = util_lib.ArrowTime(2)
    # Baseline percentage of victim balance that can be stolen half of the time.
    _BASE_BALANCE_PERCENT = 0.02
    # Fixed thief / victim score for hypebot.
    _HYPEBOT_SCORE = 1000

    def __init__(self, store, bank, bot_name, timezone):
        self._store = store
        self._bank = bank
        self._bot_name = bot_name
        self._protected_peeps = [self._bot_name] + list(HYPECENTS)

        self._scheduler = schedule_lib.HypeScheduler(timezone)
        self._scheduler.DailyCallback(
            # Ensures we schedule this event at 2am local time instead of UTC.
            self._DECAY_TIME.to(timezone),
            self._store.RunInTransaction,
            self._DecayAllScores)

    def Rob(self, thief, victim, amount, msg_fn):
        """Attempt a robbery."""
        if amount < 0:
            msg_fn(None, 'Did you mean !hc gift?')
            return

        if victim.user_id in self._protected_peeps:
            msg_fn(None, 'The Godfather protects his family.')
            self._bank.ProcessPayment(
                thief,
                user_pb2.User(user_id=self._bot_name,
                              display_name=self._bot_name), 500,
                'In Soviet Russia, %s steals from you.' % self._bot_name,
                msg_fn)
            return

        victim_balance = self._bank.GetBalance(victim)
        if victim_balance <= 0:
            msg_fn(None, 'You cannot milk a dead cow.')
            return

        thief_alert = self._GetPDF('thief')[thief.user_id]
        victim_alert = self._GetPDF('victim')[victim.user_id]
        offset = self._BASE_BALANCE_PERCENT * (1 - thief_alert - victim_alert)
        failure_chance = self._Sigmoid(amount / victim_balance, offset)

        rob_attempt_score = random.random()

        logging.info('(%s: %0.2f, %s: %0.2f) %s of %s attempt %0.2f >? %0.2f',
                     thief, thief_alert, victim, victim_alert, amount,
                     victim_balance, rob_attempt_score, failure_chance)

        if rob_attempt_score < failure_chance:
            self._bank.ProcessPayment(
                thief, SCHOLARSHIP_ACCOUNT,
                min(self._bank.GetBalance(thief), amount),
                'Victim scholarship fund', msg_fn)
            self._DistributeToPastVictims(msg_fn)
            if (rob_attempt_score < failure_chance * thief_alert /
                (thief_alert + victim_alert + 1e-6)):
                msg_fn(
                    None,
                    '%s is a known thief and was caught.' % thief.display_name)
            else:
                msg_fn(
                    None, '%s is on high alert and caught %s.' %
                    (victim.display_name, thief.display_name))
            return

        # TODO: Fold ProcessPayment into the UpdateScores tx.
        # We don't worry about the victim having insufficient funds since there is a
        # 0% chance of stealing 100% of someone's money.
        if self._bank.ProcessPayment(victim, thief, amount, 'Highway robbery',
                                     msg_fn):
            self._store.RunInTransaction(self._UpdateScores, thief, victim,
                                         amount)
            formatted_amount = util_lib.FormatHypecoins(amount)
            msg_fn(
                None, '%s stole %s from %s' %
                (thief.display_name, formatted_amount, victim.display_name))
            # We privmsg the victim to make sure they know who stole their hypecoins.
            msg_fn(
                victim, 'You\'ve been robbed! %s stole %s' %
                (thief.display_name, formatted_amount))

    def _Sigmoid(self, value, offset, scale=200.0):
        return 1 / (1 + math.exp(-scale * (value - offset)))

    def _GetScores(self, collection, tx=None):
        """Gets scores for collection.

    Args:
      collection: {string} which set of scores to get.
      tx: {storage_lib.HypeTransaction} an optional transaction to pass along to
        GetJsonValue.

    Returns:
      {dict<string, float>} scores keyed by name.
    """
        scores = self._store.GetJsonValue(self._bot_name,
                                          'scores:%s' % collection, tx)
        return collections.defaultdict(
            int, scores or {self._bot_name: self._HYPEBOT_SCORE})

    def _GetPDF(self, collection):
        """Gets probability density function of scores for collection."""
        scores = self._GetScores(collection)
        total_score = sum(scores.values())
        pdf = {peep: score / total_score for peep, score in scores.items()}
        return collections.defaultdict(float, pdf)

    def _AddToScore(self, collection, name, amount, tx=None):
        """Add {amount} to {names}'s score in {collection}."""
        scores = self._GetScores(collection, tx)
        scores[name] += amount
        logging.info('Updating %s scores: %s', collection, scores)
        self._store.SetJsonValue(self._bot_name, 'scores:%s' % collection,
                                 scores, tx)

    def _UpdateScores(self, thief, victim, amount, tx=None):
        self._AddToScore('thief', thief.user_id, amount, tx)
        self._AddToScore('victim', victim.user_id, amount, tx)
        return True

    def _DecayAllScores(self, tx=None):
        self._DecayScores('thief', tx)
        self._DecayScores('victim', tx)
        return True

    def _DecayScores(self, collection, tx=None):
        """Decay scores for {collection}."""
        scores = {
            peep: int(score * self._DECAY_RATE)
            for peep, score in self._GetScores(collection, tx).items()
            if score > 0
        }
        scores[self._bot_name] = self._HYPEBOT_SCORE
        logging.info('Updating %s scores: %s', collection, scores)
        self._store.SetJsonValue(self._bot_name, 'scores:%s' % collection,
                                 scores, tx)

    def _DistributeToPastVictims(self, msg_fn):
        """Distribute funds in scholarship account to past victims."""
        victim_scores = self._GetPDF('victim')
        scholarship_balance = self._bank.GetBalance(SCHOLARSHIP_ACCOUNT)
        self._bank.ProcessPayment(
            SCHOLARSHIP_ACCOUNT,
            [user_pb2.User(user_id=v) for v in victim_scores.keys()],
            scholarship_balance,
            'Victim scholarship fund',
            msg_fn,
            merchant_weights=victim_scores.values())