aiosofascore 0.0.4__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.
@@ -0,0 +1,3 @@
1
+ from .api.soccer.base import BaseSoccerApi
2
+
3
+ __all__ = ["BaseSoccerApi"]
File without changes
@@ -0,0 +1,30 @@
1
+ from aiohttp import ClientSession, ClientResponse
2
+
3
+ from ..exeptions import ResponseParseContentError
4
+
5
+
6
+ class ClientSessionManagerMixin:
7
+ async def __aenter__(self):
8
+ headers = {
9
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 YaBrowser/24.12.0.0 Safari/537.36"
10
+ }
11
+ self.session = ClientSession(headers=headers)
12
+
13
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
14
+ await self.session.close()
15
+
16
+ async def _get(self, path: str, params: dict = None) -> ClientResponse:
17
+ """
18
+ Executes an HTTP GET request to the given API path.
19
+
20
+ Args:
21
+ path (str): The API path to request.
22
+ params (dict, optional): Query parameters for the request.
23
+
24
+ Returns:
25
+ ClientResponse: The response from the API.
26
+ """
27
+ result = await self.session.get(self.BASE_URL + path, params=params)
28
+ if result.ok:
29
+ return await result.json()
30
+ raise ResponseParseContentError(result, path)
File without changes
@@ -0,0 +1,17 @@
1
+ from aiosofascore.api.mixins import ClientSessionManagerMixin
2
+ from aiosofascore.api.soccer.categories import SoccerCategoriesApi
3
+ from aiosofascore.api.soccer.event import SoccerEventApi
4
+ from aiosofascore.api.soccer.tournaments import SoccerTournamentApi
5
+
6
+ __all__ = ["BaseSoccerApi"]
7
+
8
+
9
+ class BaseSoccerApi(
10
+ ClientSessionManagerMixin, SoccerCategoriesApi, SoccerTournamentApi, SoccerEventApi
11
+ ):
12
+ """
13
+ Base API client for interacting with SofaScore's soccer API.
14
+ Provides methods for fetching soccer categories and tournaments.
15
+ """
16
+
17
+ BASE_URL = "https://api.sofascore.com/"
@@ -0,0 +1,21 @@
1
+ from typing import Union
2
+
3
+ from aiosofascore.api.soccer.schemas.base import CategoryList, Category
4
+
5
+ __all__ = ["SoccerCategoriesApi"]
6
+
7
+
8
+ class SoccerCategoriesApi:
9
+ async def get_categories(self) -> Union[CategoryList, list]:
10
+ """
11
+ Fetches a list of soccer categories.
12
+
13
+ Returns:
14
+ CategoryList: A list of soccer categories encapsulated in a CategoryList object.
15
+ Returns an empty list if an error occurs during the request or JSON parsing.
16
+ """
17
+ async with self:
18
+ content = await self._get("api/v1/sport/football/categories/")
19
+ return CategoryList(
20
+ categories=[Category(**category) for category in content["categories"]]
21
+ )
@@ -0,0 +1,19 @@
1
+ from aiosofascore.api.soccer.schemas.base import PregameTeamForm, H2H, EventManagers
2
+
3
+
4
+ class SoccerEventApi:
5
+
6
+ async def get_pregame_form(self, event_id: str) -> PregameTeamForm:
7
+ async with self:
8
+ result = self._get(f"api/v1/event/{event_id}/pregame-form")
9
+ return PregameTeamForm(**result)
10
+
11
+ async def get_h2h(self, event_id: str) -> H2H:
12
+ async with self:
13
+ result = self._get(f"api/v1/event/{event_id}/h2h")
14
+ return H2H(**result)
15
+
16
+ async def get_managers(self, event_id: str) -> EventManagers:
17
+ async with self:
18
+ result = self._get(f"api/v1/event/{event_id}/managers")
19
+ return EventManagers(**result)
@@ -0,0 +1 @@
1
+ from .base import *
@@ -0,0 +1,289 @@
1
+ from typing import Optional, Iterator
2
+ from pydantic import BaseModel, fields, Field
3
+
4
+
5
+ class Sport(BaseModel):
6
+ """
7
+ Represents a sport entity with essential attributes.
8
+
9
+ Attributes:
10
+ name (str): Name of the sport.
11
+ slug (str): URL-friendly identifier for the sport.
12
+ id (int): Unique identifier for the sport.
13
+ """
14
+
15
+ name: str
16
+ slug: str
17
+ id: int
18
+
19
+
20
+ class Category(BaseModel):
21
+ """
22
+ Represents a category associated with a sport.
23
+
24
+ Attributes:
25
+ name (str): Name of the category.
26
+ slug (str): URL-friendly identifier for the category.
27
+ sport (Sport): The sport associated with this category.
28
+ priority (Optional[int]): Priority level for the category (if available).
29
+ id (int): Unique identifier for the category.
30
+ flag (str): Country flag or symbol associated with the category.
31
+ """
32
+
33
+ name: str
34
+ slug: str
35
+ sport: Sport
36
+ priority: Optional[int] = None
37
+ id: int
38
+ flag: str
39
+
40
+
41
+ class CategoryList(BaseModel):
42
+ """
43
+ Represents a list of Category objects with search functionality.
44
+
45
+ Attributes:
46
+ categories (list[Category]): A list containing Category objects.
47
+
48
+ Methods:
49
+ find_all_by_name(name: str, search_amateur: bool = False) -> list[Category]:
50
+ Finds categories by name with an optional search for amateur categories.
51
+ """
52
+
53
+ categories: list[Category]
54
+
55
+ def find_all_by_name(self, name: str, search_amateur=False) -> list[Category]:
56
+ """
57
+ Finds all categories whose names contain the given substring.
58
+
59
+ Args:
60
+ name (str): The substring to search for in category names.
61
+ search_amateur (bool, optional): Whether to include only amateur categories. Defaults to False.
62
+
63
+ Returns:
64
+ list[Category]: A list of categories matching the criteria.
65
+ """
66
+ return [
67
+ c
68
+ for c in self.categories
69
+ if name.lower() in c.name.lower()
70
+ and ("amateur" in c.name.lower()) == search_amateur
71
+ ]
72
+
73
+ def __iter__(self) -> Iterator[Category]:
74
+ return iter(self.categories)
75
+
76
+ def __getitem__(self, index: int) -> Category:
77
+ return self.categories[index]
78
+
79
+
80
+ class UniqueTournament(BaseModel):
81
+ """
82
+ Represents a unique soccer tournament.
83
+
84
+ Attributes:
85
+ name (str): Name of the tournament.
86
+ slug (str): URL-friendly identifier for the tournament.
87
+ primaryColorHex (Optional[str]): Primary color in hexadecimal format.
88
+ secondaryColorHex (Optional[str]): Secondary color in hexadecimal format.
89
+ category (Category): The category associated with the tournament.
90
+ userCount (int): Number of users following the tournament.
91
+ id (int): Unique identifier for the tournament.
92
+ displayInverseHomeAwayTeams (bool): Flag indicating if home/away teams should be inversed in display.
93
+ """
94
+
95
+ name: str
96
+ slug: str
97
+ primaryColorHex: Optional[str] = None
98
+ secondaryColorHex: Optional[str] = None
99
+ category: Category
100
+ userCount: int
101
+ id: int
102
+ displayInverseHomeAwayTeams: bool
103
+
104
+
105
+ class UniqueTournamentsList(BaseModel):
106
+ unique_tournaments: list[UniqueTournament]
107
+
108
+ def __iter__(self) -> Iterator[UniqueTournament]:
109
+ return iter(self.unique_tournaments)
110
+
111
+ def __getitem__(self, index: int) -> UniqueTournament:
112
+ return self.unique_tournaments[index]
113
+
114
+
115
+ class Tournament(BaseModel):
116
+ category: Category
117
+ id: int
118
+ is_group: bool = Field(..., alias="isGroup")
119
+ is_live: bool = Field(..., alias="isLive")
120
+ name: str
121
+ priority: int
122
+ slug: str
123
+ uniqueTournament: UniqueTournament
124
+
125
+
126
+ class Promotion(BaseModel):
127
+ id: int
128
+ text: str
129
+
130
+
131
+ class Team(BaseModel):
132
+ disabled: bool
133
+ entityType: str
134
+ gender: str
135
+ id: int
136
+ name: str
137
+ name_code: str = Field(..., alias="nameCode")
138
+ national: bool
139
+ short_name: str = Field(..., alias="shortName")
140
+ slug: str
141
+
142
+
143
+ class StandingsRow(BaseModel):
144
+ descriptions: list[str]
145
+ draws: int
146
+ id: int
147
+ losses: int
148
+ matches: int
149
+ points: int
150
+ position: int
151
+ promotion: Promotion = None
152
+ score_diff_formatted: str = Field(..., alias="scoreDiffFormatted")
153
+ scores_against: int = Field(..., alias="scoresAgainst")
154
+ scores_for: int = Field(..., alias="scoresFor")
155
+ team: Team
156
+ wins: int
157
+
158
+
159
+ class Standings(BaseModel):
160
+ descriptions: list[str]
161
+ id: int
162
+ name: str
163
+ rows: list[StandingsRow]
164
+ tournament: Tournament
165
+
166
+
167
+ class Season(BaseModel):
168
+ name: str
169
+ year: str
170
+ editor: bool
171
+ id: int
172
+
173
+
174
+ class SeasonList(BaseModel):
175
+ seasons: list[Season]
176
+
177
+ def get_season_by_year(self, year: str) -> Season:
178
+ return next((season for season in self.seasons if season.year == year), None)
179
+
180
+ def get_current_season(self) -> Season:
181
+ return self.seasons[0]
182
+
183
+ def __iter__(self) -> Iterator[Season]:
184
+ return iter(self.seasons)
185
+
186
+ def __getitem__(self, index: int) -> Season:
187
+ return self.seasons[index]
188
+
189
+
190
+ class TeamForm(BaseModel):
191
+ avg_rating: str = Field(alias="avgRating")
192
+ form: list[str]
193
+ position: int
194
+ value: str
195
+
196
+
197
+ class PregameTeamForm(BaseModel):
198
+ away_team: TeamForm = Field(alias="awayTeam")
199
+ home_team: TeamForm = Field(alias="homeTeam")
200
+
201
+
202
+ class Duel(BaseModel):
203
+ away_wins: int = Field(alias="awayWins")
204
+ draws: int
205
+ home_wins: int = Field(alias="homeWins")
206
+
207
+
208
+ class H2H(BaseModel):
209
+ manager_duel: Duel = Field(alias="managerDuel")
210
+ team_duel: Duel = Field(alias="teamDuel")
211
+
212
+
213
+ class Manager(BaseModel):
214
+ name: str
215
+ slug: str
216
+ short_name: str = Field(alias="shortName")
217
+ id: str
218
+
219
+
220
+ class EventManagers(BaseModel):
221
+ home_manager: Manager = Field(alias="homeManager")
222
+ away_manager: Manager = Field(alias="awayManager")
223
+
224
+
225
+ class RoundInfo(BaseModel):
226
+ round: int
227
+
228
+
229
+ class City(BaseModel):
230
+ name: str
231
+
232
+
233
+ class VenueCoordinates(BaseModel):
234
+ latitude: str
235
+ longtitude: str
236
+
237
+
238
+ class Country(BaseModel):
239
+ alpha2: str
240
+ alpha3: str
241
+ name: str
242
+ slug: str
243
+
244
+
245
+ class Stadium(BaseModel):
246
+ name: str
247
+ capacity: int
248
+
249
+
250
+ class Venue(BaseModel):
251
+ city: City
252
+ venue_coordinates: VenueCoordinates = Field(alias="venueCoordinates")
253
+ hidden: bool
254
+ slug: str
255
+ name: str
256
+ capacity: int
257
+ id: int
258
+ country: Country
259
+ stadium: Stadium
260
+
261
+
262
+ class Referee(BaseModel):
263
+ name: str
264
+ slug: str
265
+ yellow_cards: int = Field(alias="yellowCards")
266
+ red_cards: int = Field(alias="redCards")
267
+ yellow_red_cards: int = Field(alias="yellowRedCards")
268
+ games: int
269
+ sport: Sport
270
+ id: int
271
+ country: Country
272
+
273
+
274
+ class EventTeam(Team):
275
+ sport: Sport
276
+ manager: Manager
277
+ venue: Venue
278
+ country: Country
279
+
280
+
281
+ # TODO: Create Prematch,live,fulltime Event
282
+ class Event(BaseModel):
283
+ tournament: Tournament
284
+ season: Season
285
+ round_info: RoundInfo = Field(alias="roundInfo")
286
+ custom_id: str = Field(alias="customId")
287
+ venue: Venue
288
+ home_team: EventTeam = Field(alias="homeTeam")
289
+ away_team: EventTeam = Field(alias="awayTeam")
@@ -0,0 +1,60 @@
1
+ from aiosofascore.api.soccer.schemas.base import (
2
+ UniqueTournament,
3
+ Category,
4
+ SeasonList,
5
+ Standings,
6
+ UniqueTournamentsList,
7
+ )
8
+
9
+ __all__ = ["SoccerTournamentApi"]
10
+
11
+
12
+ class SoccerTournamentApi:
13
+ async def get_tournaments_by_category(
14
+ self, category: Category
15
+ ) -> UniqueTournamentsList:
16
+ """
17
+ Fetches a list of unique tournaments for a given soccer category.
18
+
19
+ Args:
20
+ category (Category): The soccer category for which to retrieve tournaments.
21
+
22
+ Returns:
23
+ list[UniqueTournament]: A list of unique tournaments for the given category.
24
+ """
25
+ async with self:
26
+ content = await self._get(
27
+ f"api/v1/category/{category.id}/unique-tournaments"
28
+ )
29
+ return UniqueTournamentsList(
30
+ unique_tournaments=content["groups"][0]["uniqueTournaments"]
31
+ )
32
+
33
+ async def get_tournament_seasons(
34
+ self, unique_tournament: UniqueTournament
35
+ ) -> SeasonList:
36
+ async with self:
37
+ response = await self._get(
38
+ f"api/v1/unique-tournament/{unique_tournament.id}/seasons"
39
+ )
40
+ seasons = response["seasons"]
41
+ return SeasonList(seasons=seasons)
42
+
43
+ async def get_tournament_standings(
44
+ self, unique_tournament: UniqueTournament, season_year: str = None
45
+ ):
46
+ if season_year:
47
+ season = await (
48
+ await self.get_tournament_seasons(unique_tournament)
49
+ ).get_season_by_year(season_year)
50
+ else:
51
+ season = await (
52
+ await self.get_tournament_seasons(unique_tournament)
53
+ ).get_current_season()
54
+
55
+ async with self:
56
+ response = await self._get(
57
+ f"api/v1/unique-tournament/{unique_tournament.id}/season/{season.id}/standings/home"
58
+ )
59
+ standings = response["standings"][0]
60
+ return Standings(**standings)
@@ -0,0 +1,19 @@
1
+ from aiohttp import ClientResponse
2
+
3
+
4
+ class ResponseParseContentError(Exception):
5
+ def __init__(self, response: ClientResponse, path: str):
6
+ self._response = response
7
+ self._path = path
8
+
9
+ @property
10
+ def response(self):
11
+ return self._response
12
+
13
+ def __str__(self):
14
+ return (
15
+ "Response processing error:\n"
16
+ f"api call: {self._path}"
17
+ f"response status:{self._response.status}"
18
+ f"response content:{self._response.content}"
19
+ )
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.2
2
+ Name: aiosofascore
3
+ Version: 0.0.4
4
+ Summary: API client for SofaScore soccer data
5
+ Home-page: https://github.com/Rooney27/aio_sofascore
6
+ Author: Philip
7
+ Author-email: vasilewskij.fil@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: aiohttp
15
+ Requires-Dist: pydantic
16
+ Dynamic: author
17
+ Dynamic: author-email
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: home-page
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ ![PyPI Version](https://img.shields.io/pypi/v/aiosofascore)
27
+ ![LICENSE](https://img.shields.io/badge/License-MIT-blue.svg)
28
+
29
+ # Aiosofascore
30
+
31
+ **Aiosofascore** is an API client for SofaScore's soccer data, designed to provide easy access to soccer categories, tournaments, events, and much more. It is built with `aiohttp` for asynchronous HTTP requests and can be integrated into any Python project that needs soccer-related data.
32
+
33
+ ## Features
34
+
35
+ - Fetch soccer categories and tournaments
36
+ - Get detailed tournament standings and seasons
37
+ - Retrieve pregame forms, head-to-head stats, and event managers
38
+ - Built using Python's asynchronous capabilities with `aiohttp`
39
+
40
+ ## Installation
41
+
42
+ To install **aiosofascore** from PyPI, run the following command:
43
+
44
+ ```bash
45
+ pip install aiosofascore
46
+
47
+ ```
48
+ ## Usage Example
49
+
50
+ Here's how you can use aiosofascore to get data about football tournaments
51
+
52
+ ```python
53
+ import asyncio
54
+ from aiosofascore import BaseSoccerApi
55
+
56
+
57
+ async def main():
58
+ # Create a client
59
+ client = BaseSoccerApi()
60
+
61
+ # Fetch categories
62
+ categories = await client.get_categories()
63
+ for category in categories:
64
+ print(f"Category: {category.name}")
65
+
66
+ # Fetch tournaments by category
67
+ tournaments = await client.get_tournaments_by_category(categories[0])
68
+ for tournament in tournaments:
69
+ print(f"Tournament: {tournament.name}")
70
+
71
+
72
+ if __name__ == "__main__":
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ ## License
77
+ This project is licensed under the MIT License — see the LICENSE file for details.
@@ -0,0 +1,16 @@
1
+ aiosofascore/__init__.py,sha256=7arRme7PkLjECpXc3uVx1e3ym1OAsIwKrHOHgq3SEEY,72
2
+ aiosofascore/exeptions.py,sha256=g038BodDoqW_HegQX8XGx0QAMnWlHwLJWYrknGx6si8,515
3
+ aiosofascore/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ aiosofascore/api/mixins.py,sha256=FvCyIY4x_Z3G3TOhqAvykQwSK6ZlRdRaFJja3XoNHC4,1067
5
+ aiosofascore/api/soccer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ aiosofascore/api/soccer/base.py,sha256=jPHewJQ7sgibEDG52zk3uuM-DcZWMYLyFG6htocWnz8,592
7
+ aiosofascore/api/soccer/categories.py,sha256=G02dSCJ9HQFsT_US5IWB9ktegUNINhPUNLNcXV8JUs4,731
8
+ aiosofascore/api/soccer/event.py,sha256=zL0GczfnmwPGnhAPBBD11vByu0fvWXBJnzZj4gociwk,700
9
+ aiosofascore/api/soccer/tournaments.py,sha256=zK-OF7vFVD6d4MQT-pF7nAGdd94RcscrirqkuxyRKmE,1969
10
+ aiosofascore/api/soccer/schemas/__init__.py,sha256=ERmmOxz_9mUkIuccNbzUa5Y6gVLLVDdyc4cCxbCCUbY,20
11
+ aiosofascore/api/soccer/schemas/base.py,sha256=WHWLcwcHGT0TTvXqQTtDR-lPpslMYWmY7nn_gocqmb4,7055
12
+ aiosofascore-0.0.4.dist-info/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
13
+ aiosofascore-0.0.4.dist-info/METADATA,sha256=_ZFBttObMPr29YeeoNBs5N72sQKao1t2VvsFHVDH9uA,2195
14
+ aiosofascore-0.0.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
15
+ aiosofascore-0.0.4.dist-info/top_level.txt,sha256=ClavTMe9yHQjALo6Pa4E1h-HW1HU8Hf5Gx9-j6sRpSQ,13
16
+ aiosofascore-0.0.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ aiosofascore