import re from datetime import datetime from decimal import Decimal from pathlib import Path import attr import pytz from lxml import etree from zope.interface import implementer from .. import handhistory as hh from ..card import Card from ..constants import Action, Currency, Game, GameType, Limit, MoneyType from ..hand import Combo __all__ = ["PokerStarsHandHistory", "Notes"] @implementer(hh.IStreet) class _Street(hh._BaseStreet): def _parse_cards(self, boardline): self.cards = (Card(boardline[1:3]), Card(boardline[4:6]), Card(boardline[7:9])) def _parse_actions(self, actionlines): actions = [] for line in actionlines: if line.startswith("Uncalled bet"): action = self._parse_uncalled(line) elif "collected" in line: action = self._parse_collected(line) elif "doesn't show hand" in line: action = self._parse_muck(line) elif ' said, "' in line: # skip chat lines continue elif ":" in line: action = self._parse_player_action(line) else: raise RuntimeError("bad action line: " + line) actions.append(hh._PlayerAction(*action)) self.actions = tuple(actions) if actions else None def _parse_uncalled(self, line): first_paren_index = line.find("(") second_paren_index = line.find(")") amount = line[first_paren_index + 1 : second_paren_index] name_start_index = line.find("to ") + 3 name = line[name_start_index:] return name, Action.RETURN, Decimal(amount) def _parse_collected(self, line): first_space_index = line.find(" ") name = line[:first_space_index] second_space_index = line.find(" ", first_space_index + 1) third_space_index = line.find(" ", second_space_index + 1) amount = line[second_space_index + 1 : third_space_index] self.pot = Decimal(amount) return name, Action.WIN, self.pot def _parse_muck(self, line): colon_index = line.find(":") name = line[:colon_index] return name, Action.MUCK, None def _parse_player_action(self, line): name, _, action = line.partition(": ") action, _, amount = action.partition(" ") amount, _, _ = amount.partition(" ") if amount: return name, Action(action), Decimal(amount) else: return name, Action(action), None @implementer(hh.IHandHistory) class PokerStarsHandHistory(hh._SplittableHandHistoryMixin, hh._BaseHandHistory): """Parses PokerStars Tournament hands.""" _DATE_FORMAT = "%Y/%m/%d %H:%M:%S ET" _TZ = pytz.timezone("US/Eastern") # ET _split_re = re.compile(r" ?\*\*\* ?\n?|\n") _header_re = re.compile( r""" ^PokerStars\s+ # Poker Room Hand\s+\#(?P\d+):\s+ # Hand history id (Tournament\s+\#(?P\d+),\s+ # Tournament Number ((?PFreeroll)|( # buyin is Freeroll \$?(?P\d+(\.\d+)?) # or buyin (\+\$?(?P\d+(\.\d+)?))? # and rake (\s+(?P[A-Z]+))? # and currency ))\s+ )? (?P.+?)\s+ # game (?P(?:Pot\s+|No\s+|)Limit)\s+ # limit (-\s+Level\s+(?P\S+)\s+)? # Level (optional) \( (((?P\d+)/(?P\d+))|( # tournament blinds \$(?P\d+(\.\d+)?)/ # cash small blind \$(?P\d+(\.\d+)?) # cash big blind (\s+(?P\S+))? # cash currency )) \)\s+ -\s+.+?\s+ # localized date \[(?P.+?)\] # ET date """, re.VERBOSE, ) _table_re = re.compile( r"^Table '(.*)' (\d+)-max Seat #(?P