class ActivityTracker(object):
  def __init__(self, scheduler: schedule_lib.HypeScheduler):
  def RecordActivity(self, channel: channel_pb2.Channel, user: user_pb2.User,
                     command: Text):
  def _ResetDelta(self):
  def _LogAndResetDelta(self):
def _HashKeys(dictionary: Dict[Text, int]) -> Dict[Text, int]: class AsyncRunner(object):
  def __init__(self, executor: futures.Executor) -> None:
  def IsIdle(self) -> bool:
  def RunAsync(self,
               func: Callable, *args: Any, **kwargs: Any) -> futures.Future:
  def OnCompletion(self, func: Callable, *args: Any, **kwargs: Any) -> None:
  def _RunAndRecord(self, func: Callable, *args: Any, **kwargs: Any) -> None:
  def _OnCompletion(self) -> None: class HypeScheduler(object):
  def __init__(self, local_tz: str = None):
  def StartScheduler(self):
  def InSeconds(self, seconds: int, fn: Callable, *args, **kwargs) -> Job:
  def DailyCallback(self,
                    schedule_time: arrow.Arrow,
                    fn: Callable, *args, **kwargs) -> Job:
  def FixedRate(self,
                initial_delay: int,
                period: int,
                fn: Callable, *args, **kwargs) -> Job:
  def UnscheduleJob(self, job: Job) -> None: def Access(obj, path, default=None):
def ArrowTime(hour=0, minute=0, second=0, tz='UTC', weekday=None):
def CanonicalizeName(raw_name: Text):
def UnformatHypecoins(value):
def ExtractRegex(pattern: Text,
                 string: Text) -> Optional[Tuple[List[Text], Text]]:
def FormatHypecoins(amount, abbreviate=False):
def SafeCast(value, desired_type, default=None):
def TimeDeltaToHumanDuration(time_delta, precision=1):
def SafeUrl(url, params=None):
  def __init__(self, items: Iterable[Text],
               initial_weights: Optional[Iterable[float]] = None):
  def Freeze(self):
  def GetItem(self) -> Text:
  def GetWeight(self, item: Text) -> Optional[float]:
  def GetAndDownweightItem(self) -> Text:
  def ModifyWeight(self, item: Text, update_fn: Callable[[float],
                                                         float]) -> float:
  def _NormalizeProbs(self):
def Bold(string):
def Italic(string):
def Underline(string):
def Colorize(string, color, irc=True):
def StripColor(string):
def Dankify(string):
def FuzzyBool(value):
def Sparkline(values):
  def __init__(self, user: user_pb2.User):
  def __init__(self):
  def RecordActivity(self, user: user_pb2.User, channel: channel_pb2.Channel):
  def LastActivity(self, user: user_pb2.User):
  def AddUser(self,
              user: user_pb2.User,
              channel: Optional[channel_pb2.Channel] = None):
  def AllHumans(self, channel: Optional[channel_pb2.Channel] = None):
  def AllBots(self, channel: Optional[channel_pb2.Channel] = None):
  def AllUsers(self, channel: Optional[channel_pb2.Channel] = None):
def MatchesAny(channels, channel):
def _CardAsTextList(card: message_pb2.Card) -> List[Text]:
def _AppendToMessage(msg: hype_types.Message,
                     response: hype_types.CommandResponse):
def MakeMessage(response: hype_types.CommandResponse) -> hype_types.Message:
DECIMAL_POWERS = ('', 'k', 'm', 'b', 't', 'q', 'p')
  YEAR_IN_DAYS = 365.25  # Approximate.
  MONTH_IN_DAYS = YEAR_IN_DAYS / 12.0  # Average.
  HOUR_IN_SECONDS = 60 * 60.0
  MINUTE_IN_SECONDS = 60.0
_MIRC_COLORS = {
    'white': (0, '#ffffff'),
    'black': (1, '#000000'),
    'blue': (2, '#000075'),
    'green': (3, '#009300'),
    'red': (4, '#ff0000'),
    'brown': (5, '#750000'),
    'purple': (6, '#9c009c'),
    'orange': (7, '#fc7500'),
    'yellow': (8, '#ffff00'),
    'light green': (9, '#00fc00'),
    'cyan': (10, '#009393'),
    'light cyan': (11, '#00ffff'),
    'light blue': (12, '#0000fc'),
    'pink': (13, '#ff00ff'),
    'grey': (14, '#757575'),
    'light grey': (15, '#d2d2d2'),
}
class WeightedCollection(object):
class _UserTrack(object):
class UserTracker(object): class ZombieManager(object):
  _ZOMBIE_VERBS = ('crawls', 'lumbers', 'shambles', 'lurches', 'careens')
  def __init__(self) -> None:
  def channel_to_corpse(self) -> Dict[str, _ZombieRecord]:
  def channel_to_corpse(self, new_value: Dict[str, _ZombieRecord]) -> None:
  def ChannelHasActiveCorpse(self, channel: Channel) -> bool:
  def GetCorpseForChannel(self, channel: Channel) -> Optional[str]:
  def NewCorpse(self, channel, name) -> None:
  def AnimateCorpse(self, channel: Channel) -> Text:
  def RemoveCorpse(self, channel): class BaseChatInterface(with_metaclass(abc.ABCMeta)):
  DEFAULT_PARAMS = params_lib.HypeParams({
      # Display name used when chatting.
      'name': 'chatbot',
  })
  def __init__(self, params):
  def RegisterHandlers(self, on_message_fn, user_tracker, user_prefs):
  def Join(self, channel: hype_types.Channel):
  def Leave(self, channel: hype_types.Channel):
  def Loop(self):
  def FindUser(self, query: Text) -> Optional[user_pb2.User]:
  def WhoAll(self):
  def SendMessage(self, channel: hype_types.Channel,
                  message: Optional[hype_types.Message]):
  def SendDirectMessage(self, user: user_pb2.User, message: hype_types.Message):
  def Notice(self, channel: hype_types.Channel, message: hype_types.Message):
  def Topic(self, channel: hype_types.Channel, new_topic: Text): def _ShouldGrantLegendaryBadge(user_data: coffee_pb2.CoffeeData) -> bool:
def _ShouldGrantCapitalistBadge(user_data: coffee_pb2.CoffeeData) -> bool:
def _ShouldGrantBuyerSellerBadge(user_data: coffee_pb2.CoffeeData,
                                 positive: bool) -> bool:
def _ShouldGrantVarietalBadge(user_data: coffee_pb2.CoffeeData) -> bool:
def GetBean(bean_id: Text,
            beans: List[coffee_pb2.Bean],
            remove_bean: bool = False) -> Optional[coffee_pb2.Bean]:
def _GetBeanId(bean: coffee_pb2.Bean) -> Text:
  def __init__(self, scheduler: schedule_lib.HypeScheduler,
               store: storage_lib.HypeStore, bot_name: Text,
               params: params_lib.HypeParams):
  def _LoadBadges(self):
  def _InitWeights(self):
  def _RestoreEnergy(self) -> None:
  def _RestoreUserEnergy(
      self,
      username: Text,
      tx: Optional[storage_lib.HypeTransaction] = None) -> None:
  def DrinkCoffee(
      self,
      user: user_pb2.User,
      bean_id: Optional[Text] = None,
      tx: Optional[storage_lib.HypeTransaction] = None
  ) -> Union[Dict[Text, Any], StatusCode]:
  def FindBeans(
      self,
      user: user_pb2.User,
      energy: int,
      tx: Optional[storage_lib.HypeTransaction] = None
  ) -> Union[StatusCode, coffee_pb2.CoffeeData]:
  def _GrantBadges(
      self,
      user: user_pb2.User,
      user_data: coffee_pb2.CoffeeData,
      unused_bean: Optional[coffee_pb2.Bean] = None,
  ) -> Dict[Text, List[int]]:
  def GetOccurrenceChance(self, bean: coffee_pb2.Bean) -> float:
  def GetCoffeeData(
      self,
      user: user_pb2.User,
      tx: Optional[storage_lib.HypeTransaction] = None
  ) -> coffee_pb2.CoffeeData:
  def _SetCoffeeData(self,
                     user: user_pb2.User,
                     coffee_data: coffee_pb2.CoffeeData,
                     tx: Optional[storage_lib.HypeTransaction] = None):
  def TransferBeans(
      self,
      owner: user_pb2.User,
      buyer: user_pb2.User,
      bean_id: Text,
      price: int,
      tx: Optional[storage_lib.HypeTransaction] = None) -> StatusCode:
  def _UpdateGameData(self,
                      user: user_pb2.User,
                      user_data: coffee_pb2.CoffeeData,
                      bean: Optional[coffee_pb2.Bean] = None,
                      tx: Optional[storage_lib.HypeTransaction] = None):
_BADGE_CODE_REGISTRY = {
    0: lambda user_data: user_data.statistics.find_count >= 1,
    1: lambda user_data: user_data.statistics.drink_count >= 1,
    2: lambda user_data: user_data.energy == 0,
    3: _ShouldGrantLegendaryBadge,
    4: lambda user_data:
       any(True for b in user_data.beans if b.rarity == 'legendary'),
    5: lambda user_data: user_data.statistics.sell_count >= 1,
    6: lambda user_data: user_data.statistics.buy_count >= 1,
    7: _ShouldGrantCapitalistBadge,
    8: functools.partial(_ShouldGrantBuyerSellerBadge, positive=True),
    9: functools.partial(_ShouldGrantBuyerSellerBadge, positive=False),
    10: lambda user_data: user_data.energy >= 50,
    11: _ShouldGrantVarietalBadge,
}
  _SUBKEY = 'coffee'
  _DEFAULT_COFFEE_DATA = coffee_pb2.CoffeeData(energy=10)
  DEFAULT_PARAMS = params_lib.HypeParams({
      # Chance of finding a bean when searching with no other modifiers. [0,1)
      'bean_chance': 0.5,
      # Number of beans that can be stored before a user runs out of room.
      'bean_storage_limit': 10,
      # Filepath to load badge data from. Should be a textproto file containing
      # a coffee_pb2.CoffeeData proto with only `badges` set.
      'badge_data_path': 'hypebot/data/coffee_badges.textproto',
      # Weights used to calculate drop rates for bean regions. Data is actual
      # coffee production by country/region in 1000's of 60kg bags.
      #
      # Global data source:
      # http://www.ico.org/historical/1990%20onwards/PDF/1a-total-production.pdf
      # Hawaii data source:
      # https://www.nass.usda.gov/Statistics_by_State/Hawaii/Publications/Fruits_and_Nuts/201807FinalCoffee.pdf
      #
      # Retrieved 2020/04/07, regions with less than 0.1% of global production
      # have been removed.
      'region_weights': {
          'Brazil': 62925,
          'Colombia': 13858,
          'Cote d\'Ivoire': 2294,
          'Ethiopia': 7776,
          'Guatemala': 4007,
          'Honduras': 7328,
          'India': 5302,
          'Indonesia': 9418,
          'Mexico': 4351,
          'Nicaragua': 2510,
          'Peru': 4263,
          'Uganda': 4704,
          'Vietnam': 31174,
      },
      # Weights used to calculate drop rates for bean varities.
      'variety_weights': {
          'Arabica': 6,
          'Robusta': 3,
          'Liberica': 1,
      },
      # Weights used to calculate drop rates for bean rarities.
      'rarity_weights': {
          'common': 50,
          'uncommon': 28,
          'rare': 14,
          'precious': 7,
          'legendary': 1,
      }
  })
class CoffeeLib: BOOKIE_ACCOUNT = user_pb2.User(user_id='_hypebank', display_name='HypeBank')
FEE_ACCOUNT = BOOKIE_ACCOUNT
MINT_ACCOUNT = BOOKIE_ACCOUNT
SCHOLARSHIP_ACCOUNT = user_pb2.User(
    user_id='_hypescholarship', display_name='HypeScholarship')
SUBSCRIPTION_ACCOUNT = BOOKIE_ACCOUNT
HYPECENTS = frozenset([
    BOOKIE_ACCOUNT.user_id,
    FEE_ACCOUNT.user_id,
    MINT_ACCOUNT.user_id,
    SCHOLARSHIP_ACCOUNT.user_id,
    SUBSCRIPTION_ACCOUNT.user_id,
])
  _DECAY_RATE = 0.75
  _DECAY_TIME = util_lib.ArrowTime(2)
  _BASE_BALANCE_PERCENT = 0.02
  _HYPEBOT_SCORE = 1000
  _BET_SUBKEY = 'bets'
  _BALANCE_SUBKEY = 'bank:balance'
  _TRANSACTION_SUBKEY = 'bank:transaction'
  _MIN_OVERDRAFT_FEE = 5
  _MAX_OVERDRAFT_FEE_PERCENT = 0.05
class Thievery(object):
class Bookie(object):
class Bank(object):
  def __init__(self, store, bank, bot_name, timezone):
  def Rob(self, thief, victim, amount, msg_fn):
  def _Sigmoid(self, value, offset, scale=200.0):
  def _GetScores(self, collection, tx=None):
  def _GetPDF(self, collection):
  def _AddToScore(self, collection, name, amount, tx=None):
  def _UpdateScores(self, thief, victim, amount, tx=None):
  def _DecayAllScores(self, tx=None):
  def _DecayScores(self, collection, tx=None):
  def _DistributeToPastVictims(self, msg_fn):
  def __init__(self, store, bank, inventory):
  def LookupBets(self, game, user: user_pb2.User = None, resolver=None):
  def PlaceBet(self, game, bet, msg_fn, more=False):
  def _PlaceBet(self, game, bet, more, msg_fn, *unused_args, **kwargs):
  def SettleBets(self, game, resolver, msg_fn, *args, **kwargs):
  def _SettleBets(self, game, resolver, msg_fn, *args, **kwargs):
  def _GetBets(self, row, tx=None):
  def _SetBets(self, row, bets, tx=None):
  def __init__(self, store, bot_name):
  def GetBalance(self, user):
  def GetUserBalances(self, plebs_only=False):
  def GetTransactions(self, user):
  def GetBankStats(self, plebs_only=False):
  def MintNewHypeCoins(self):
  def ParseAmount(self, user, amount_str, msg_fn):
    def _IntAmount(match, unused_balance):
    def _HumanIntAmount(match, unused_balance):
    def _HexAmount(match, unused_balance):
    def _RandomBalance(unused_match, balance):
    def _MemeTeam(unused_match, unused_balance):
  def FineUser(self, user, amount, details, msg_fn):
  def ProcessPayment(self,
                     customer,
                     merchants,
                     num_coins,
                     details,
                     msg_fn,
                     can_overdraft=False,
                     merchant_weights=None):
  def _Deposit(self, user: user_pb2.User, num_coins: int,
               entry: bank_pb2.LedgerEntry, msg_fn) -> bool:
  def _Withdraw(self,
                user: user_pb2.User,
                num_coins: int,
                entry: bank_pb2.LedgerEntry,
                msg_fn,
                can_overdraft: bool = False) -> bool:
  def _BankTransaction(self,
                       user: user_pb2.User,
                       delta: int,
                       entry: bank_pb2.LedgerEntry,
                       tx=None): class DeploymentManager(object):
  _BOT_CONFIGS = {
  }
  def __init__(self,
               bot_name: str,
               bookie: coin_lib.Bookie,
               # This dep creates a cycle in the build graph, so just Any it.
               output_util: Any,  # hypecore.OutputUtil
               executor: futures.Executor) -> None:
  def IsValidBot(self, bot_name: str) -> bool:
  def RequestBuild(self, user: str, cl: int, bot_name: str,
                   channel: str) -> bool:
  def RequestDeploy(self,
                    user: str,
                    cl: int,
                    bot_name: str,
                    schema_list: List[str],
                    channel: str) -> bool:
  def RequestPush(self,
                  user: str,
                  cl: int,
                  bot_name: str,
                  channel: str) -> bool:
  def RequestSchemaUpdate(self, user: str, cl: int, schema_env: str) -> bool:
  def RequestTest(self,
                  user: str,
                  cl: int,
                  bot_name: str,
                  channel: str) -> bool: class HypeStacks(object):
  _STACK_COUNT_SUBKEY = 'stacks:count'
  _RECENT_STACKS_SUBKEY = 'stacks:recent'
  def __init__(self, store, bank, msg_fn) -> None:
  def AwardStack(self, user: user_pb2.User) -> None:
  def DecayHypeStacks(self) -> None:
  def GetHypeStacks(self, user: user_pb2.User) -> int:
  def PriceForHypeStacks(self, user: user_pb2.User, num_stacks: int) -> int:
    def TotalHypeStackPrice(num_stacks: int) -> int:
  def PurchaseStacks(self, user: user_pb2.User,
                     purchase_details: Dict[Text, Any]) -> None:
  def _UpdateAllStacks(self, user_id: Text, num_stacks: int,
                       tx: storage_lib.HypeTransaction) -> None: class BaseItem(with_metaclass(abc.ABCMeta)):
class CoinPurse(BaseItem):
class HatToken(BaseItem):
class HypeEgg(BaseItem):
class InventoryManager(object):
  def __init__(self, core, user, params):
  def name(self):
  def Use(self):
  def Use(self):
  def Use(self):
  def Use(self):
  def __init__(self, store: storage_lib.HypeStore):
  def GetUserInventory(self, user: user_pb2.User) -> hype_types.JsonType:
  def AddItem(self, user: user_pb2.User, item: BaseItem) -> bool:
  def _AddStack(self, inventory, item_name: Text):
  def RemoveItem(self, user: user_pb2.User, item: BaseItem) -> bool:
  def _RemoveStack(self, inventory, item_name: Text):
  COIN_PURSE_AMOUNTS = {
      'small': PurseDef(15, 3, 0.90),
      'medium': PurseDef(50, 5, 0.70),
      'large': PurseDef(100, 100, 0.5)
  }
  ROLLS_PER_PURSE = 3
  HAT_TOKEN_CHANCE = 0.02
  _GHOST_CHANCE = 0.25
  INVENTORY_SUBKEY = 'inventory' _DATA_URL = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL'
  _US_STATE_NAMES = {
      'AL': 'Alabama',
      'AK': 'Alaska',
      'AZ': 'Arizona',
      'AR': 'Arkansas',
      'CA': 'California',
      'CO': 'Colorado',
      'CT': 'Connecticut',
      'DE': 'Delaware',
      'DC': 'District of Columbia',
      'FL': 'Florida',
      'GA': 'Georgia',
      'HI': 'Hawaii',
      'ID': 'Idaho',
      'IL': 'Illinois',
      'IN': 'Indiana',
      'IA': 'Iowa',
      'KS': 'Kansas',
      'KY': 'Kentucky',
      'LA': 'Louisiana',
      'ME': 'Maine',
      'MD': 'Maryland',
      'MA': 'Massachusetts',
      'MI': 'Michigan',
      'MN': 'Minnesota',
      'MS': 'Mississippi',
      'MO': 'Missouri',
      'MT': 'Montana',
      'NE': 'Nebraska',
      'NV': 'Nevada',
      'NH': 'New Hampshire',
      'NJ': 'New Jersey',
      'NM': 'New Mexico',
      'NY': 'New York',
      'NC': 'North Carolina',
      'ND': 'North Dakota',
      'OH': 'Ohio',
      'OK': 'Oklahoma',
      'OR': 'Oregon',
      'PA': 'Pennsylvania',
      'RI': 'Rhode Island',
      'SC': 'South Carolina',
      'SD': 'South Dakota',
      'TN': 'Tennessee',
      'TX': 'Texas',
      'UT': 'Utah',
      'VT': 'Vermont',
      'VA': 'Virginia',
      'WA': 'Washington',
      'WV': 'West Virginia',
      'WI': 'Wisconsin',
      'WY': 'Wyoming',
      'PR': 'Puerto Rico'
  }
  _US_STATE_POPULATIONS = {
      'AL': 4903185,
      'AK': 731545,
      'AZ': 7278717,
      'AR': 3017804,
      'CA': 39512223,
      'CO': 5758736,
      'CT': 3565287,
      'DE': 973764,
      'DC': 705749,
      'FL': 21477737,
      'GA': 10617423,
      'HI': 1415872,
      'ID': 1787065,
      'IL': 12671821,
      'IN': 6732219,
      'IA': 3155070,
      'KS': 2913314,
      'KY': 4467673,
      'LA': 4648794,
      'ME': 1344212,
      'MD': 6045680,
      'MA': 6892503,
      'MI': 9986857,
      'MN': 5639632,
      'MS': 2976149,
      'MO': 6137428,
      'MT': 1068778,
      'NE': 1934408,
      'NV': 3080156,
      'NH': 1359711,
      'NJ': 8882190,
      'NM': 2096829,
      'NY': 19453561,
      'NC': 10488084,
      'ND': 762062,
      'OH': 11689100,
      'OK': 3956971,
      'OR': 4217737,
      'PA': 12801989,
      'RI': 1059361,
      'SC': 5148714,
      'SD': 884659,
      'TN': 6829174,
      'TX': 28995881,
      'UT': 3205958,
      'VT': 623989,
      'VA': 8535519,
      'WA': 7614893,
      'WV': 1792147,
      'WI': 5822434,
      'WY': 578759,
      'PR': 3193694
  }
class PopulationLib():
  def __init__(self, proxy: proxy_lib.Proxy):
  def GetPopulation(self, raw_region: Text) -> int:
  def GetNameForRegion(self, raw_region: Text) -> Optional[Text]:
  def IsUSState(self, raw_region: Text) -> bool:
  def _UpdatePopulations(self):
  def _NormalizeId(self, raw_region: Text) -> Optional[Text]: _GEOCODE_URL = 'https://api.geocod.io/v1.4/geocode'
_DARKSKY_URL = 'https://api.darksky.net/forecast'
_AIRNOW_URL = 'http://www.airnowapi.org/aq'
  DEFAULT_PARAMS = params_lib.HypeParams({
      'geocode_key': None,
      'darksky_key': None,
      'airnow_key': None,
  })
class WeatherException(Exception):
class WeatherLib(object):
  def __init__(self, code, message):
  def __init__(self,
               proxy: proxy_lib.Proxy,
               params: params_lib.HypeParams):
  def _LocationToGPS(self, location: str) -> Optional[Dict[Any, Any]]:
  def _CallForecast(self, gps: Dict[str, Any]):
  def GetForecast(self, location: str):
  def _CallAQI(self, zip_code: str):
  def GetAQI(self, location: str): class HypeTransaction(abc.ABC):
class HypeStore(abc.ABC):
class CommitAbortError(RuntimeError):
class SyncedDict(dict):
class HypeQueue:
  def __init__(self, tx_name: AnyStr):
  def __str__(self) -> AnyStr:
  def name(self) -> AnyStr:
  def Commit(self) -> bool:
  def __init__(self, params):
  def engine(self):
  def GetValue(self,
               key: AnyStr,
               subkey: AnyStr,
               tx: Optional[HypeTransaction] = None) -> Optional[AnyStr]:
  def SetValue(self,
               key: AnyStr,
               subkey: AnyStr,
               value: Union[int, AnyStr],
               tx: Optional[HypeTransaction] = None) -> None:
  def DeleteKey(self,
                key: AnyStr,
                tx: Optional[HypeTransaction] = None) -> None:
  def GetSubkey(self, subkey: AnyStr, tx: Optional[HypeTransaction] = None
               ) -> List[Tuple[AnyStr, AnyStr]]:
  def GetHistoricalValues(
      self,
      key: AnyStr,
      subkey: AnyStr,
      num_past_values: int,
      tx: Optional[HypeTransaction] = None) -> List[JsonType]:
  def PrependValue(self,
                   key: AnyStr,
                   subkey: AnyStr,
                   new_value: JsonType,
                   max_length: Optional[int] = None,
                   tx: Optional[HypeTransaction] = None) -> None:
  def NewTransaction(self, tx_name: AnyStr) -> HypeTransaction:
  def UpdateValue(self,
                  key: AnyStr,
                  subkey: AnyStr,
                  delta: int,
                  tx: Optional[HypeTransaction] = None) -> None:
  def GetJsonValue(self,
                   key: AnyStr,
                   subkey: AnyStr,
                   tx: Optional[HypeTransaction] = None
                  ) -> (Optional[JsonType]):
  def SetJsonValue(self,
                   key: AnyStr,
                   subkey: AnyStr,
                   json_value: JsonType,
                   tx: Optional[HypeTransaction] = None) -> None:
  def UpdateJson(self,
                 key: AnyStr,
                 subkey: AnyStr,
                 transform_fn: Callable[[JsonType], Any],
                 success_fn: Callable[[JsonType], bool] = lambda _: True,
                 is_set: bool = False,
                 tx: Optional[HypeTransaction] = None) -> bool:
  def RunInTransaction(self, fn: Callable, *args, **kwargs) -> Any:
    def _Internal(tx):
  def __init__(self, store, storage_key, storage_subkey=None):
  def __delitem__(self, key):
  def __setitem__(self, key, value):
  def pop(self, key, default=_POP_SENTINEL):
  def update(self, *args, **kwargs):
  def _FlushToStorage(self):
  def Sync(self):
  def __init__(self, store: HypeStore, name: Text,
               scheduler: schedule_lib.HypeScheduler,
               process_fn: Callable[..., bool]):
  def Enqueue(self, payload: JsonType, tx: HypeTransaction = None) -> None:
  def ProcessQueue(self,
                   batch_size: int = 10,
                   tx: HypeTransaction = None) -> bool:
  DEFAULT_PARAMS = params_lib.HypeParams()
  _POP_SENTINEL = object()
  _DEFAULT_SUBKEY = 'synced_object'
  _SUBKEY = 'hype_queue'
from threading import Lock
from absl import logging
from concurrent import futures
from hypebot import hype_types
from hypebot.core import activity_tracker
from hypebot.core import async_lib
from hypebot.core import schedule_lib
from hypebot.core import util_lib
from hypebot.core import zombie_lib
from hypebot.interfaces import interface_lib
from hypebot.news import news_factory
from hypebot.plugins import coffee_lib
from hypebot.plugins import coin_lib
from hypebot.plugins import deploy_lib
from hypebot.plugins import hypestack_lib
from hypebot.plugins import inventory_lib
from hypebot.plugins import population_lib
from hypebot.plugins import weather_lib
from hypebot.protos import channel_pb2
from hypebot.protos import user_pb2
from hypebot.proxies import proxy_factory
from hypebot.stocks import stock_factory
from hypebot.storage import storage_factory
from hypebot.storage import storage_lib
from typing import Any, Callable, Dict, Optional, Text
import time

class UserPreferences(object):
  """Manages users preferences and stores them across bot reloads."""

  # Master list of useable preferences. Dict of pref => default value.
  _PREFS = {
      'location': 'MTV',
      'temperature_unit': 'F',
      'stocks': 'GOOG,GOOGL',
      # Comma separated list of your summoner names. First is considered main.
      'lol_summoner': None,
      'lol_region': 'NA',
      '_dynamite_dm': None,
      # Maps 'user/####' to '$ldap'.
      '_dynamite_ldap': None,
  }
  _SUBKEY = 'preferences'

  def __init__(self, store: storage_lib.HypeStore):
    self._store = store

  def IsValid(self, pref: Text) -> bool:
    """Returns if a preference is recognized.

    Args:
      pref: The name of the preference.

    Returns:
      True if the preference is recognized.
    """
    return pref in self._PREFS

  def Get(self, user: user_pb2.User, pref: Text) -> Text:
    """Get user's preference value.

    Args:
      user: The user for which to look up the preference.
      pref: The preference of the user.

    Returns:
      The user's preference or the default value for the preferenece. Returns
      None if the preference is invalid.
    """
    if not self.IsValid(pref):
      logging.warning('Tried to access an invalid pref: %s', pref)
      return None

    user_prefs = self.GetAll(user)
    return user_prefs.get(pref, self._PREFS[pref])

  def Set(self, user: user_pb2.User, pref: Text, value: Text) -> None:
    """Set user's preference to value.

    If preference is invalid, nothing happens.

    Args:
      user: The user.
      pref: Name of preference.
      value: Value to set preference.
    """
    if not self.IsValid(pref):
      return
    self._store.RunInTransaction(self._Set, user, pref, value)

  def _Set(self, user: user_pb2.User, pref: Text, value: Text,
           tx: storage_lib.HypeTransaction) -> None:
    """See Set(...) for details."""
    user_prefs = self._store.GetJsonValue(user.user_id, self._SUBKEY, tx) or {}
    if not value:
      del user_prefs[pref]
    else:
      user_prefs[pref] = value
    self._store.SetJsonValue(user.user_id, self._SUBKEY, user_prefs, tx)

  def GetAll(self, user: user_pb2.User) -> Dict[Text, Text]:
    return self._store.GetJsonValue(user.user_id, self._SUBKEY) or {}


class Core(object):
  """The core of hypebot.

  Any state or service that is needed by more than one command.
  """

  def __init__(
      self,
      params: Any,  # HypeParams
      interface: interface_lib.BaseChatInterface) -> None:
    """Constructs core of hypebot.

    Args:
      params: Bot parameters.
      interface: This will always be the original interface that the bot was
        created with, and never the CaptureInterface during nested calls. For
        this reason, you should only call Join/Part and potentially Notice/Topic
        on this interface.  Don't call SendMessage or else it can send messages
        never intended for human consumption.
    """
    self.params = params
    self.name = self.params.name
    self.interface = interface
    self.output_util = OutputUtil(self.Reply)

    self.store = storage_factory.CreateFromParams(self.params.storage)
    cached_type = self.params.storage.get('cached_type')
    if cached_type:
      self.cached_store = storage_factory.Create(
          cached_type, self.params.storage.get(self.params.storage.type))
    else:
      logging.info('No cached_type found for storage, using default store.')
      self.cached_store = self.store

    self.user_tracker = util_lib.UserTracker()
    self.user_prefs = UserPreferences(self.cached_store)
    self.timezone = self.params.time_zone
    self.scheduler = schedule_lib.HypeScheduler(self.timezone)
    self.executor = futures.ThreadPoolExecutor(max_workers=8)
    self.runner = async_lib.AsyncRunner(self.executor)
    self.activity_tracker = activity_tracker.ActivityTracker(self.scheduler)
    self.inventory = inventory_lib.InventoryManager(self.store)

    self.coffee = coffee_lib.CoffeeLib(