ps3838api 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ps3838api/__init__.py +7 -0
- ps3838api/api/__init__.py +45 -0
- ps3838api/api/client.py +516 -0
- ps3838api/api/default_client.py +231 -0
- ps3838api/api/v4client.py +107 -0
- ps3838api/matching.py +141 -0
- ps3838api/models/__init__.py +0 -0
- ps3838api/models/bets.py +250 -0
- ps3838api/models/client.py +48 -0
- ps3838api/models/errors.py +43 -0
- ps3838api/models/event.py +61 -0
- ps3838api/models/fixtures.py +53 -0
- ps3838api/models/lines.py +27 -0
- ps3838api/models/odds.py +221 -0
- ps3838api/models/sports.py +256 -0
- ps3838api/models/tank.py +6 -0
- ps3838api/py.typed +0 -0
- ps3838api/tank.py +93 -0
- ps3838api/totals.py +62 -0
- ps3838api/utils/match_leagues.py +90 -0
- ps3838api/utils/ops.py +146 -0
- ps3838api-1.1.0.dist-info/METADATA +176 -0
- ps3838api-1.1.0.dist-info/RECORD +26 -0
- ps3838api-1.1.0.dist-info/WHEEL +5 -0
- ps3838api-1.1.0.dist-info/licenses/LICENSE +8 -0
- ps3838api-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Legacy helper functions that use a shared default :class:`Client`.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import warnings
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
|
|
10
|
+
if sys.version_info >= (3, 13):
|
|
11
|
+
from warnings import deprecated
|
|
12
|
+
else:
|
|
13
|
+
|
|
14
|
+
def deprecated(reason: str): # type: ignore
|
|
15
|
+
def decorator(func): # type: ignore
|
|
16
|
+
def wrapper(*args, **kwargs): # type: ignore
|
|
17
|
+
warnings.warn(
|
|
18
|
+
f"{func.__name__} is deprecated: {reason}", # type: ignore
|
|
19
|
+
DeprecationWarning,
|
|
20
|
+
stacklevel=2,
|
|
21
|
+
)
|
|
22
|
+
return func(*args, **kwargs) # type: ignore
|
|
23
|
+
|
|
24
|
+
wrapper.__name__ = func.__name__ # type: ignore
|
|
25
|
+
wrapper.__doc__ = func.__doc__ # type: ignore
|
|
26
|
+
wrapper.__dict__.update(func.__dict__) # type: ignore
|
|
27
|
+
return wrapper # type: ignore
|
|
28
|
+
|
|
29
|
+
return decorator # type: ignore
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
from ps3838api.models.bets import BetType, FillType, OddsFormat, PlaceStraightBetResponse, Side, Team
|
|
33
|
+
from ps3838api.models.client import BalanceData, BettingStatusResponse, LeagueV3, PeriodData
|
|
34
|
+
from ps3838api.models.fixtures import FixturesResponse
|
|
35
|
+
from ps3838api.models.lines import LineResponse
|
|
36
|
+
from ps3838api.models.odds import OddsResponse
|
|
37
|
+
from ps3838api.models.sports import SOCCER_SPORT_ID
|
|
38
|
+
|
|
39
|
+
from .client import PinnacleClient
|
|
40
|
+
|
|
41
|
+
_default_client: PinnacleClient | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_default_client() -> PinnacleClient:
|
|
45
|
+
global _default_client # noqa: PLW0603
|
|
46
|
+
if _default_client is None:
|
|
47
|
+
_default_client = PinnacleClient()
|
|
48
|
+
return _default_client
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
52
|
+
def get_client_balance() -> BalanceData:
|
|
53
|
+
return _get_default_client().get_client_balance()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
57
|
+
def get_periods(sport_id: int | None = None) -> list[PeriodData]:
|
|
58
|
+
return _get_default_client().get_periods(sport_id=sport_id)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@deprecated("Use `ps3838api.models.sports` sports constant")
|
|
62
|
+
def get_sports() -> Any:
|
|
63
|
+
return _get_default_client().get_sports()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
67
|
+
def get_leagues(sport_id: int | None = None) -> list[LeagueV3]:
|
|
68
|
+
return _get_default_client().get_leagues(sport_id=sport_id)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
72
|
+
def get_fixtures(
|
|
73
|
+
sport_id: int | None = None,
|
|
74
|
+
league_ids: list[int] | None = None,
|
|
75
|
+
is_live: bool | None = None,
|
|
76
|
+
since: int | None = None,
|
|
77
|
+
event_ids: list[int] | None = None,
|
|
78
|
+
settled: bool = False,
|
|
79
|
+
) -> FixturesResponse:
|
|
80
|
+
return _get_default_client().get_fixtures(
|
|
81
|
+
sport_id=sport_id,
|
|
82
|
+
league_ids=league_ids,
|
|
83
|
+
is_live=is_live,
|
|
84
|
+
since=since,
|
|
85
|
+
event_ids=event_ids,
|
|
86
|
+
settled=settled,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
91
|
+
def get_odds(
|
|
92
|
+
sport_id: int | None = None,
|
|
93
|
+
is_special: bool = False,
|
|
94
|
+
league_ids: list[int] | None = None,
|
|
95
|
+
odds_format: OddsFormat = "DECIMAL",
|
|
96
|
+
since: int | None = None,
|
|
97
|
+
is_live: bool | None = None,
|
|
98
|
+
event_ids: list[int] | None = None,
|
|
99
|
+
) -> OddsResponse:
|
|
100
|
+
return _get_default_client().get_odds(
|
|
101
|
+
sport_id=sport_id,
|
|
102
|
+
is_special=is_special,
|
|
103
|
+
league_ids=league_ids,
|
|
104
|
+
odds_format=odds_format,
|
|
105
|
+
since=since,
|
|
106
|
+
is_live=is_live,
|
|
107
|
+
event_ids=event_ids,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
112
|
+
def get_special_fixtures(
|
|
113
|
+
sport_id: int | None = None,
|
|
114
|
+
league_ids: list[int] | None = None,
|
|
115
|
+
event_id: int | None = None,
|
|
116
|
+
) -> Any:
|
|
117
|
+
return _get_default_client().get_special_fixtures(
|
|
118
|
+
sport_id=sport_id, league_ids=league_ids, event_id=event_id
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
123
|
+
def get_line(
|
|
124
|
+
league_id: int,
|
|
125
|
+
event_id: int,
|
|
126
|
+
period_number: int,
|
|
127
|
+
bet_type: Literal["SPREAD", "MONEYLINE", "TOTAL_POINTS", "TEAM_TOTAL_POINTS"],
|
|
128
|
+
handicap: float,
|
|
129
|
+
team: Literal["Team1", "Team2", "Draw"] | None = None,
|
|
130
|
+
side: Literal["OVER", "UNDER"] | None = None,
|
|
131
|
+
sport_id: int | None = None,
|
|
132
|
+
odds_format: str = "Decimal",
|
|
133
|
+
) -> LineResponse:
|
|
134
|
+
return _get_default_client().get_line(
|
|
135
|
+
league_id=league_id,
|
|
136
|
+
event_id=event_id,
|
|
137
|
+
period_number=period_number,
|
|
138
|
+
bet_type=bet_type,
|
|
139
|
+
handicap=handicap,
|
|
140
|
+
team=team,
|
|
141
|
+
side=side,
|
|
142
|
+
sport_id=sport_id,
|
|
143
|
+
odds_format=odds_format,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
148
|
+
def place_straigh_bet(
|
|
149
|
+
*,
|
|
150
|
+
stake: float,
|
|
151
|
+
event_id: int,
|
|
152
|
+
bet_type: BetType,
|
|
153
|
+
line_id: int | None,
|
|
154
|
+
period_number: int = 0,
|
|
155
|
+
sport_id: int = SOCCER_SPORT_ID,
|
|
156
|
+
alt_line_id: int | None = None,
|
|
157
|
+
unique_request_id: str | None = None,
|
|
158
|
+
odds_format: OddsFormat = "DECIMAL",
|
|
159
|
+
fill_type: FillType = "NORMAL",
|
|
160
|
+
accept_better_line: bool = True,
|
|
161
|
+
win_risk_stake: Literal["WIN", "RISK"] = "RISK",
|
|
162
|
+
pitcher1_must_start: bool = True,
|
|
163
|
+
pitcher2_must_start: bool = True,
|
|
164
|
+
team: Team | None = None,
|
|
165
|
+
side: Side | None = None,
|
|
166
|
+
handicap: float | None = None,
|
|
167
|
+
) -> PlaceStraightBetResponse:
|
|
168
|
+
return _get_default_client().place_straight_bet(
|
|
169
|
+
stake=stake,
|
|
170
|
+
event_id=event_id,
|
|
171
|
+
bet_type=bet_type,
|
|
172
|
+
line_id=line_id,
|
|
173
|
+
period_number=period_number,
|
|
174
|
+
sport_id=sport_id,
|
|
175
|
+
alt_line_id=alt_line_id,
|
|
176
|
+
unique_request_id=unique_request_id,
|
|
177
|
+
odds_format=odds_format,
|
|
178
|
+
fill_type=fill_type,
|
|
179
|
+
accept_better_line=accept_better_line,
|
|
180
|
+
win_risk_stake=win_risk_stake,
|
|
181
|
+
pitcher1_must_start=pitcher1_must_start,
|
|
182
|
+
pitcher2_must_start=pitcher2_must_start,
|
|
183
|
+
team=team,
|
|
184
|
+
side=side,
|
|
185
|
+
handicap=handicap,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@deprecated("Use `ps3838api.client.PinnacleClient` base methods")
|
|
190
|
+
def get_betting_status() -> BettingStatusResponse:
|
|
191
|
+
return _get_default_client().get_betting_status()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def export_my_bets(
|
|
195
|
+
*,
|
|
196
|
+
from_datetime: datetime,
|
|
197
|
+
to_datetime: datetime,
|
|
198
|
+
d: int = -1,
|
|
199
|
+
status: Literal["UNSETTLED", "SETTLED"] = "SETTLED",
|
|
200
|
+
sd: bool = False,
|
|
201
|
+
bet_type: str = "WAGER",
|
|
202
|
+
product: str = "SB,PP,BG",
|
|
203
|
+
locale: str = "en_US",
|
|
204
|
+
timezone: str = "GMT-4",
|
|
205
|
+
) -> bytes:
|
|
206
|
+
return _get_default_client().export_my_bets(
|
|
207
|
+
from_datetime=from_datetime,
|
|
208
|
+
to_datetime=to_datetime,
|
|
209
|
+
d=d,
|
|
210
|
+
status=status,
|
|
211
|
+
sd=sd,
|
|
212
|
+
bet_type=bet_type,
|
|
213
|
+
product=product,
|
|
214
|
+
locale=locale,
|
|
215
|
+
timezone=timezone,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
__all__ = [
|
|
220
|
+
"get_client_balance",
|
|
221
|
+
"get_periods",
|
|
222
|
+
"get_sports",
|
|
223
|
+
"get_leagues",
|
|
224
|
+
"get_fixtures",
|
|
225
|
+
"get_odds",
|
|
226
|
+
"get_special_fixtures",
|
|
227
|
+
"get_line",
|
|
228
|
+
"place_straigh_bet",
|
|
229
|
+
"get_betting_status",
|
|
230
|
+
"export_my_bets",
|
|
231
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
2
|
+
|
|
3
|
+
from ps3838api.models.bets import OddsFormat
|
|
4
|
+
from ps3838api.models.odds import OddsResponseV4
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
# to avoid ciruclar imports
|
|
8
|
+
from ps3838api.api.client import PinnacleClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class V4PinnacleClient:
|
|
12
|
+
"""Subclient for V4 API endpoints.
|
|
13
|
+
|
|
14
|
+
V4 endpoints provide enhanced responses, particularly for team totals
|
|
15
|
+
which return arrays of alternative lines instead of single objects.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: "PinnacleClient"):
|
|
19
|
+
self._client = client
|
|
20
|
+
|
|
21
|
+
def get_odds(
|
|
22
|
+
self,
|
|
23
|
+
sport_id: int | None = None,
|
|
24
|
+
league_ids: list[int] | None = None,
|
|
25
|
+
odds_format: OddsFormat = "DECIMAL",
|
|
26
|
+
since: int | None = None,
|
|
27
|
+
is_live: bool | None = None,
|
|
28
|
+
event_ids: list[int] | None = None,
|
|
29
|
+
to_currency_code: str | None = None,
|
|
30
|
+
) -> OddsResponseV4:
|
|
31
|
+
"""Get straight odds for non-settled events using V4 endpoint.
|
|
32
|
+
|
|
33
|
+
V4 returns enhanced team total data with arrays of alternative lines.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
sport_id: Sport ID. Uses client's default_sport if not provided.
|
|
37
|
+
league_ids: List of league IDs to filter.
|
|
38
|
+
odds_format: Format for odds (DECIMAL, AMERICAN, etc.).
|
|
39
|
+
since: Used for incremental updates. Use 'last' from previous response.
|
|
40
|
+
is_live: True for live events only, False for prematch only.
|
|
41
|
+
event_ids: List of event IDs to filter.
|
|
42
|
+
to_currency_code: Convert limits to specified currency.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
OddsResponseV4 containing odds data with V4 enhanced structure.
|
|
46
|
+
"""
|
|
47
|
+
endpoint = "/v4/odds"
|
|
48
|
+
|
|
49
|
+
resolved_sport_id = sport_id if sport_id is not None else self._client.default_sport
|
|
50
|
+
|
|
51
|
+
params: dict[str, Any] = {
|
|
52
|
+
"sportId": resolved_sport_id,
|
|
53
|
+
"oddsFormat": odds_format,
|
|
54
|
+
}
|
|
55
|
+
if league_ids:
|
|
56
|
+
params["leagueIds"] = ",".join(map(str, league_ids))
|
|
57
|
+
if since is not None:
|
|
58
|
+
params["since"] = since
|
|
59
|
+
if is_live is not None:
|
|
60
|
+
params["isLive"] = int(is_live)
|
|
61
|
+
if event_ids:
|
|
62
|
+
params["eventIds"] = ",".join(map(str, event_ids))
|
|
63
|
+
if to_currency_code is not None:
|
|
64
|
+
params["toCurrencyCode"] = to_currency_code
|
|
65
|
+
|
|
66
|
+
return cast(OddsResponseV4, self._client._get(endpoint, params)) # pyright: ignore[reportPrivateUsage]
|
|
67
|
+
|
|
68
|
+
def get_parlay_odds(
|
|
69
|
+
self,
|
|
70
|
+
sport_id: int | None = None,
|
|
71
|
+
league_ids: list[int] | None = None,
|
|
72
|
+
odds_format: OddsFormat = "DECIMAL",
|
|
73
|
+
since: int | None = None,
|
|
74
|
+
is_live: bool | None = None,
|
|
75
|
+
event_ids: list[int] | None = None,
|
|
76
|
+
) -> OddsResponseV4:
|
|
77
|
+
"""Get parlay odds for non-settled events using V4 endpoint.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
sport_id: Sport ID. Uses client's default_sport if not provided.
|
|
81
|
+
league_ids: List of league IDs to filter.
|
|
82
|
+
odds_format: Format for odds (DECIMAL, AMERICAN, etc.).
|
|
83
|
+
since: Used for incremental updates. Use 'last' from previous response.
|
|
84
|
+
is_live: True for live events only, False for prematch only.
|
|
85
|
+
event_ids: List of event IDs to filter.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
OddsResponseV4 containing parlay odds data.
|
|
89
|
+
"""
|
|
90
|
+
endpoint = "/v4/odds/parlay"
|
|
91
|
+
|
|
92
|
+
resolved_sport_id = sport_id if sport_id is not None else self._client.default_sport
|
|
93
|
+
|
|
94
|
+
params: dict[str, Any] = {
|
|
95
|
+
"sportId": resolved_sport_id,
|
|
96
|
+
"oddsFormat": odds_format,
|
|
97
|
+
}
|
|
98
|
+
if league_ids:
|
|
99
|
+
params["leagueIds"] = ",".join(map(str, league_ids))
|
|
100
|
+
if since is not None:
|
|
101
|
+
params["since"] = since
|
|
102
|
+
if is_live is not None:
|
|
103
|
+
params["isLive"] = int(is_live)
|
|
104
|
+
if event_ids:
|
|
105
|
+
params["eventIds"] = ",".join(map(str, event_ids))
|
|
106
|
+
|
|
107
|
+
return cast(OddsResponseV4, self._client._get(endpoint, params)) # pyright: ignore[reportPrivateUsage]
|
ps3838api/matching.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Final, Literal
|
|
4
|
+
|
|
5
|
+
from rapidfuzz import fuzz
|
|
6
|
+
|
|
7
|
+
import ps3838api.api as ps
|
|
8
|
+
from ps3838api import ROOT_MODULE_DIR
|
|
9
|
+
from ps3838api.models.event import (
|
|
10
|
+
Failure,
|
|
11
|
+
MatchedLeague,
|
|
12
|
+
NoSuchEvent,
|
|
13
|
+
NoSuchLeague,
|
|
14
|
+
NoSuchLeagueFixtures,
|
|
15
|
+
NoSuchLeagueMatching,
|
|
16
|
+
WrongLeague,
|
|
17
|
+
)
|
|
18
|
+
from ps3838api.models.fixtures import FixturesLeagueV3, FixturesResponse
|
|
19
|
+
from ps3838api.models.tank import EventInfo
|
|
20
|
+
from ps3838api.utils.ops import normalize_to_set
|
|
21
|
+
|
|
22
|
+
warnings.warn(
|
|
23
|
+
f"{__name__} is experimental and its interface is not stable yet.",
|
|
24
|
+
FutureWarning,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with open(ROOT_MODULE_DIR / "out/matched_leagues.json") as file:
|
|
28
|
+
MATCHED_LEAGUES: Final[list[MatchedLeague]] = json.load(file)
|
|
29
|
+
|
|
30
|
+
with open(ROOT_MODULE_DIR / "out/ps3838_leagues.json") as file:
|
|
31
|
+
ALL_LEAGUES: Final[list[ps.LeagueV3]] = json.load(file)["leagues"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def match_league(
|
|
35
|
+
*,
|
|
36
|
+
league_betsapi: str,
|
|
37
|
+
leagues_mapping: list[MatchedLeague] = MATCHED_LEAGUES,
|
|
38
|
+
) -> MatchedLeague | NoSuchLeagueMatching | WrongLeague:
|
|
39
|
+
for league in leagues_mapping:
|
|
40
|
+
if league["betsapi_league"] == league_betsapi:
|
|
41
|
+
if league["ps3838_id"]:
|
|
42
|
+
return league
|
|
43
|
+
else:
|
|
44
|
+
return NoSuchLeagueMatching(league_betsapi)
|
|
45
|
+
return WrongLeague(league_betsapi)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def find_league_by_name(league: str, leagues: list[ps.LeagueV3] = ALL_LEAGUES) -> ps.LeagueV3 | NoSuchLeague:
|
|
49
|
+
normalized = normalize_to_set(league)
|
|
50
|
+
for leagueV3 in leagues:
|
|
51
|
+
if normalize_to_set(leagueV3["name"]) == normalized:
|
|
52
|
+
return leagueV3
|
|
53
|
+
return NoSuchLeagueMatching(league)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def find_event_in_league(
|
|
57
|
+
league_data: FixturesLeagueV3,
|
|
58
|
+
league: str,
|
|
59
|
+
home: str,
|
|
60
|
+
away: str,
|
|
61
|
+
live_status: Literal["PREMATCH", "LIVE"] | None = "PREMATCH",
|
|
62
|
+
) -> EventInfo | NoSuchEvent:
|
|
63
|
+
"""
|
|
64
|
+
If live_status is "LIVE", search only for events with a parentId.
|
|
65
|
+
This does not necessarily mean the event is live — it could also be a corners subevent.
|
|
66
|
+
|
|
67
|
+
If live_status is "PREMATCH", search only for events without a parentId.
|
|
68
|
+
Some prematch events (e.g. corners leagues) will be skipped.
|
|
69
|
+
|
|
70
|
+
Scans `league_data["events"]` for the best fuzzy match to `home` and `away`.
|
|
71
|
+
Returns the matching event with the highest sum of match scores, as long as
|
|
72
|
+
that sum >= 75 (which is 37.5% of the max possible 200).
|
|
73
|
+
Otherwise, returns NoSuchEvent.
|
|
74
|
+
"""
|
|
75
|
+
best_event = None
|
|
76
|
+
best_sum_score = 0
|
|
77
|
+
for event in league_data["events"]:
|
|
78
|
+
match (live_status, "parentId" in event):
|
|
79
|
+
case None, _:
|
|
80
|
+
pass
|
|
81
|
+
case "PREMATCH", False:
|
|
82
|
+
pass
|
|
83
|
+
case "LIVE", True:
|
|
84
|
+
pass
|
|
85
|
+
case _:
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
# Compare the user-provided home and away vs. the fixture's home and away.
|
|
89
|
+
# Using token_set_ratio (see below for comparison vs token_sort_ratio).
|
|
90
|
+
score_home = fuzz.token_set_ratio(home, event.get("home", ""))
|
|
91
|
+
score_away = fuzz.token_set_ratio(away, event.get("away", ""))
|
|
92
|
+
total_score = score_home + score_away
|
|
93
|
+
if total_score > best_sum_score:
|
|
94
|
+
best_sum_score = total_score
|
|
95
|
+
best_event = event
|
|
96
|
+
# If the best event's combined fuzzy match is < 37.5% of the total possible 200,
|
|
97
|
+
# treat it as no match:
|
|
98
|
+
if best_event is None or best_sum_score < 75:
|
|
99
|
+
return NoSuchEvent(league, home, away)
|
|
100
|
+
return {"eventId": best_event["id"], "leagueId": league_data["id"]}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def magic_find_event(
|
|
104
|
+
fixtures: FixturesResponse,
|
|
105
|
+
league: str,
|
|
106
|
+
home: str,
|
|
107
|
+
away: str,
|
|
108
|
+
live_status: Literal["PREMATCH", "LIVE"] | None = "PREMATCH",
|
|
109
|
+
) -> EventInfo | Failure:
|
|
110
|
+
"""
|
|
111
|
+
1. Tries to find league by normalizng names;
|
|
112
|
+
2. If don't, search for a league matching
|
|
113
|
+
3. Then `find_event_in_league`
|
|
114
|
+
|
|
115
|
+
If live_status is "LIVE", search only for events with a parentId.
|
|
116
|
+
This does not necessarily mean the event is live — it could also be a corners subevent.
|
|
117
|
+
|
|
118
|
+
If live_status is "PREMATCH", search only for events without a parentId.
|
|
119
|
+
Some prematch events (e.g. corners leagues) will be skipped.
|
|
120
|
+
|
|
121
|
+
If live_status is None, search for any.
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
leagueV3 = find_league_by_name(league)
|
|
126
|
+
if isinstance(leagueV3, NoSuchLeague):
|
|
127
|
+
match match_league(league_betsapi=league):
|
|
128
|
+
case {"ps3838_id": int()} as value:
|
|
129
|
+
league_id: int = value["ps3838_id"] # type: ignore
|
|
130
|
+
case _:
|
|
131
|
+
return NoSuchLeagueMatching(league)
|
|
132
|
+
else:
|
|
133
|
+
league_id = leagueV3["id"]
|
|
134
|
+
|
|
135
|
+
for leagueV3 in fixtures["league"]:
|
|
136
|
+
if leagueV3["id"] == league_id:
|
|
137
|
+
break
|
|
138
|
+
else:
|
|
139
|
+
return NoSuchLeagueFixtures(league)
|
|
140
|
+
|
|
141
|
+
return find_event_in_league(leagueV3, league, home, away, live_status)
|
|
File without changes
|