gameflip-api 1.0.1__tar.gz
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.
- gameflip_api-1.0.1/PKG-INFO +82 -0
- gameflip_api-1.0.1/README.md +70 -0
- gameflip_api-1.0.1/pyproject.toml +15 -0
- gameflip_api-1.0.1/setup.cfg +4 -0
- gameflip_api-1.0.1/src/gameflip_api/__init__.py +0 -0
- gameflip_api-1.0.1/src/gameflip_api/api.py +136 -0
- gameflip_api-1.0.1/src/gameflip_api/api.pyi +332 -0
- gameflip_api-1.0.1/src/gameflip_api/enums.py +214 -0
- gameflip_api-1.0.1/src/gameflip_api/params.py +134 -0
- gameflip_api-1.0.1/src/gameflip_api/types.py +60 -0
- gameflip_api-1.0.1/src/gameflip_api.egg-info/PKG-INFO +82 -0
- gameflip_api-1.0.1/src/gameflip_api.egg-info/SOURCES.txt +13 -0
- gameflip_api-1.0.1/src/gameflip_api.egg-info/dependency_links.txt +1 -0
- gameflip_api-1.0.1/src/gameflip_api.egg-info/requires.txt +4 -0
- gameflip_api-1.0.1/src/gameflip_api.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gameflip-api
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Uma API wrapper em Python para utilização com a plataforma Gameflip.
|
|
5
|
+
Author-email: Gustavo Pedroso Bernardes <gpedrosobernardes@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: validators
|
|
9
|
+
Requires-Dist: pydantic
|
|
10
|
+
Requires-Dist: pyotp
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
|
|
13
|
+
# Gameflip API
|
|
14
|
+
|
|
15
|
+
Uma **API wrapper em Python** para utilização com a plataforma Gameflip.
|
|
16
|
+
Facilita a integração via script, para automatizar operações, consultar dados e interagir com a Gameflip de forma programática.
|
|
17
|
+
|
|
18
|
+
## 🧰 Funcionalidades
|
|
19
|
+
|
|
20
|
+
* Conexão autenticada com a Gameflip via chave e segredo (`API_KEY`, `API_SECRET`)
|
|
21
|
+
* Métodos para executar operações comuns (por exemplo: listagem de produtos, criação de ofertas, consulta de histórico)
|
|
22
|
+
* Interface simples em Python para agilizar automações
|
|
23
|
+
* Código open-source, fácil de estender para necessidades específicas
|
|
24
|
+
|
|
25
|
+
## 🚀 Começando
|
|
26
|
+
|
|
27
|
+
### Pré-requisitos
|
|
28
|
+
|
|
29
|
+
* Python (versão compatível — idealmente 3.8+)
|
|
30
|
+
* Conta na Gameflip e acesso à API (chave + segredo)
|
|
31
|
+
* Variáveis de ambiente configuradas:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export GFAPI_KEY=<sua_chave_aqui>
|
|
35
|
+
export GFAPI_SECRET=<seu_segredo_aqui>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
(No Windows: `set GFAPI_KEY=<…>`, `set GFAPI_SECRET=<…>`)
|
|
39
|
+
|
|
40
|
+
### Instalação
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install gameflip-api
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Uso básico
|
|
47
|
+
|
|
48
|
+
Exemplo no arquivo `example.py` (ajuste conforme a sua necessidade):
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import os
|
|
52
|
+
from pprint import pprint
|
|
53
|
+
from dotenv import load_dotenv
|
|
54
|
+
from gameflip_api.api import GameflipAPI
|
|
55
|
+
|
|
56
|
+
load_dotenv()
|
|
57
|
+
|
|
58
|
+
print("Listing results:")
|
|
59
|
+
|
|
60
|
+
listing_response = GameflipAPI.listing_search(digital=True, limit=1)
|
|
61
|
+
pprint(listing_response.json())
|
|
62
|
+
|
|
63
|
+
gameflip_api = GameflipAPI(os.getenv('GFAPI_KEY'), os.getenv('GFAPI_SECRET'))
|
|
64
|
+
|
|
65
|
+
print("My profile info:")
|
|
66
|
+
|
|
67
|
+
pprint(gameflip_api.profile().json())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 📦 Estrutura do Projeto
|
|
71
|
+
|
|
72
|
+
* `src/gameflip_api/` — código-fonte da biblioteca
|
|
73
|
+
* `example.py` — script de demonstração de uso
|
|
74
|
+
* `test.py` — arquivo para testes rápidos
|
|
75
|
+
* `requirements.txt` — dependências do Python
|
|
76
|
+
|
|
77
|
+
## 🤝 Contato
|
|
78
|
+
|
|
79
|
+
Se tiver dúvidas, sugestões ou quiser colaborar:
|
|
80
|
+
|
|
81
|
+
* Crie uma *issue* no próprio repositório
|
|
82
|
+
* Envie um pull request com descrições claras das alterações
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Gameflip API
|
|
2
|
+
|
|
3
|
+
Uma **API wrapper em Python** para utilização com a plataforma Gameflip.
|
|
4
|
+
Facilita a integração via script, para automatizar operações, consultar dados e interagir com a Gameflip de forma programática.
|
|
5
|
+
|
|
6
|
+
## 🧰 Funcionalidades
|
|
7
|
+
|
|
8
|
+
* Conexão autenticada com a Gameflip via chave e segredo (`API_KEY`, `API_SECRET`)
|
|
9
|
+
* Métodos para executar operações comuns (por exemplo: listagem de produtos, criação de ofertas, consulta de histórico)
|
|
10
|
+
* Interface simples em Python para agilizar automações
|
|
11
|
+
* Código open-source, fácil de estender para necessidades específicas
|
|
12
|
+
|
|
13
|
+
## 🚀 Começando
|
|
14
|
+
|
|
15
|
+
### Pré-requisitos
|
|
16
|
+
|
|
17
|
+
* Python (versão compatível — idealmente 3.8+)
|
|
18
|
+
* Conta na Gameflip e acesso à API (chave + segredo)
|
|
19
|
+
* Variáveis de ambiente configuradas:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
export GFAPI_KEY=<sua_chave_aqui>
|
|
23
|
+
export GFAPI_SECRET=<seu_segredo_aqui>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
(No Windows: `set GFAPI_KEY=<…>`, `set GFAPI_SECRET=<…>`)
|
|
27
|
+
|
|
28
|
+
### Instalação
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install gameflip-api
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Uso básico
|
|
35
|
+
|
|
36
|
+
Exemplo no arquivo `example.py` (ajuste conforme a sua necessidade):
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import os
|
|
40
|
+
from pprint import pprint
|
|
41
|
+
from dotenv import load_dotenv
|
|
42
|
+
from gameflip_api.api import GameflipAPI
|
|
43
|
+
|
|
44
|
+
load_dotenv()
|
|
45
|
+
|
|
46
|
+
print("Listing results:")
|
|
47
|
+
|
|
48
|
+
listing_response = GameflipAPI.listing_search(digital=True, limit=1)
|
|
49
|
+
pprint(listing_response.json())
|
|
50
|
+
|
|
51
|
+
gameflip_api = GameflipAPI(os.getenv('GFAPI_KEY'), os.getenv('GFAPI_SECRET'))
|
|
52
|
+
|
|
53
|
+
print("My profile info:")
|
|
54
|
+
|
|
55
|
+
pprint(gameflip_api.profile().json())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 📦 Estrutura do Projeto
|
|
59
|
+
|
|
60
|
+
* `src/gameflip_api/` — código-fonte da biblioteca
|
|
61
|
+
* `example.py` — script de demonstração de uso
|
|
62
|
+
* `test.py` — arquivo para testes rápidos
|
|
63
|
+
* `requirements.txt` — dependências do Python
|
|
64
|
+
|
|
65
|
+
## 🤝 Contato
|
|
66
|
+
|
|
67
|
+
Se tiver dúvidas, sugestões ou quiser colaborar:
|
|
68
|
+
|
|
69
|
+
* Crie uma *issue* no próprio repositório
|
|
70
|
+
* Envie um pull request com descrições claras das alterações
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gameflip-api"
|
|
3
|
+
version = "1.0.1"
|
|
4
|
+
description = "Uma API wrapper em Python para utilização com a plataforma Gameflip."
|
|
5
|
+
authors = [{ name="Gustavo Pedroso Bernardes", email="gpedrosobernardes@gmail.com" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
dependencies = ["validators", "pydantic", "pyotp", "requests"]
|
|
9
|
+
|
|
10
|
+
[build-system]
|
|
11
|
+
requires = ["setuptools", "wheel"]
|
|
12
|
+
build-backend = "setuptools.build_meta"
|
|
13
|
+
|
|
14
|
+
[tool.pydantic-pycharm-plugin.parsable-types]
|
|
15
|
+
"src.gameflip_api.types.AwsCognitoUUID" = ["str"]
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import pyotp
|
|
5
|
+
|
|
6
|
+
from gameflip_api.enums import ListingOps, ListingPhotoStatus
|
|
7
|
+
from gameflip_api.params import GameflipAPIParams, UUIDParam, ListingPostParams, Op, ExchangeParams, ExchangePostParams, \
|
|
8
|
+
OwnerParam, ListingSearchParams
|
|
9
|
+
import validators
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GameflipAPI:
|
|
13
|
+
__api = 'https://production-gameflip.fingershock.com/api/v1'
|
|
14
|
+
|
|
15
|
+
def __init__(self, api_key, secret):
|
|
16
|
+
GameflipAPIParams(api_key=api_key, secret=secret)
|
|
17
|
+
self.__api_key = api_key
|
|
18
|
+
self.__secret = secret
|
|
19
|
+
self.__totp = pyotp.TOTP(secret)
|
|
20
|
+
|
|
21
|
+
def __get_auth_header(self):
|
|
22
|
+
return {"Authorization": f"GFAPI {self.__api_key}:{self.__totp.now()}"}
|
|
23
|
+
|
|
24
|
+
def profile(self, owner = 'me'):
|
|
25
|
+
if owner != 'me':
|
|
26
|
+
# noinspection PyTypeChecker
|
|
27
|
+
OwnerParam(owner=owner)
|
|
28
|
+
return requests.get(f"{self.__api}/account/{owner}/profile", headers=self.__get_auth_header())
|
|
29
|
+
|
|
30
|
+
def wallet_history(self):
|
|
31
|
+
return requests.get(f"{self.__api}/account/me/wallet_history", headers=self.__get_auth_header())
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def get_rldata_items():
|
|
35
|
+
return requests.get("https://gameflip.com/api/gameitem/inventory/812872018935")
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def listing_search(cls, params = None, **kwargs):
|
|
39
|
+
if params is None:
|
|
40
|
+
params = ListingSearchParams(**kwargs)
|
|
41
|
+
elif not isinstance(params, ListingSearchParams):
|
|
42
|
+
raise TypeError("params must be of type ListingPostParams.")
|
|
43
|
+
return requests.get(f"{cls.__api}/listing", params.model_dump(exclude_none=True))
|
|
44
|
+
|
|
45
|
+
def my_listing_search(self, params = None, **kwargs):
|
|
46
|
+
if params is None:
|
|
47
|
+
params = ListingSearchParams(**kwargs)
|
|
48
|
+
elif not isinstance(params, ListingSearchParams):
|
|
49
|
+
raise TypeError("params must be of type ListingPostParams.")
|
|
50
|
+
return requests.get(f"{self.__api}/listing", params.model_dump(exclude_none=True),
|
|
51
|
+
headers=self.__get_auth_header())
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def listing_of(cls, uuid):
|
|
55
|
+
UUIDParam(uuid=uuid)
|
|
56
|
+
return requests.get(f"{cls.__api}/listing/{uuid}")
|
|
57
|
+
|
|
58
|
+
def my_listing_of(self, uuid):
|
|
59
|
+
UUIDParam(uuid=uuid)
|
|
60
|
+
return requests.get(f"{self.__api}/listing/{uuid}", headers=self.__get_auth_header())
|
|
61
|
+
|
|
62
|
+
def listing_post(self, params = None, **kwargs):
|
|
63
|
+
if params is None:
|
|
64
|
+
params = ListingPostParams(**kwargs)
|
|
65
|
+
elif not isinstance(params, ListingPostParams):
|
|
66
|
+
raise TypeError("params must be of type ListingPostParams.")
|
|
67
|
+
return requests.post(
|
|
68
|
+
f"{self.__api}/listing",
|
|
69
|
+
params.model_dump(exclude_none=True),
|
|
70
|
+
headers=self.__get_auth_header()
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def listing_delete(self, uuid):
|
|
74
|
+
UUIDParam(uuid=uuid)
|
|
75
|
+
return requests.delete(f"{self.__api}/listing/{uuid}", headers=self.__get_auth_header())
|
|
76
|
+
|
|
77
|
+
def listing_patch(self, uuid, ops):
|
|
78
|
+
UUIDParam(uuid=uuid)
|
|
79
|
+
ops_objects = map(lambda op: op if isinstance(op, Op) else Op(**op), ops)
|
|
80
|
+
ops_json = list(map(lambda op: op.model_dump(mode="json"), ops_objects))
|
|
81
|
+
headers = {"Content-Type": "application/json-patch+json"}
|
|
82
|
+
headers.update(self.__get_auth_header())
|
|
83
|
+
return requests.patch(f"{self.__api}/listing/{uuid}", json=ops_json, headers=headers)
|
|
84
|
+
|
|
85
|
+
def post_photo(self, listing_uuid, photo, display_order):
|
|
86
|
+
if not (isinstance(photo, Path) or isinstance(photo, str)):
|
|
87
|
+
raise TypeError("Photo must be of type Path or str.")
|
|
88
|
+
|
|
89
|
+
if isinstance(photo, str) and validators.url(photo):
|
|
90
|
+
photo_request = requests.get(photo)
|
|
91
|
+
photo_request.raise_for_status()
|
|
92
|
+
photo_data = photo_request.content
|
|
93
|
+
mime_type = photo_request.headers.get("Content-Type", "application/octet-stream")
|
|
94
|
+
else:
|
|
95
|
+
if isinstance(photo, Path):
|
|
96
|
+
photo_path = photo
|
|
97
|
+
else:
|
|
98
|
+
photo_path = Path(photo)
|
|
99
|
+
if not photo_path.exists() or not photo_path.is_file():
|
|
100
|
+
raise FileNotFoundError(f"File not found: {photo}")
|
|
101
|
+
photo_data = open(photo_path, "rb")
|
|
102
|
+
mime_type = photo_path.suffix[1:]
|
|
103
|
+
|
|
104
|
+
post_photo_request = requests.post(f"{self.__api}/listing/{listing_uuid}/photo", headers=self.__get_auth_header())
|
|
105
|
+
post_photo_request.raise_for_status()
|
|
106
|
+
post_photo_data = post_photo_request.json()['data']
|
|
107
|
+
upload_photo_url = post_photo_data['upload_url']
|
|
108
|
+
photo_id = post_photo_data['id']
|
|
109
|
+
|
|
110
|
+
put_photo_headers = {'Content-Type': f'image/{mime_type}'}
|
|
111
|
+
put_photo_request = requests.put(upload_photo_url, headers=put_photo_headers, data=photo_data)
|
|
112
|
+
put_photo_request.raise_for_status()
|
|
113
|
+
|
|
114
|
+
ops = [Op(op=ListingOps.REPLACE, path=f'/photo/{photo_id}/status', value=ListingPhotoStatus.ACTIVE)]
|
|
115
|
+
|
|
116
|
+
if display_order >= 0:
|
|
117
|
+
ops.append(Op(op=ListingOps.REPLACE, path=f'/photo/{photo_id}/display_order', value=display_order))
|
|
118
|
+
else:
|
|
119
|
+
ops.append(Op(op=ListingOps.REPLACE, path=f'/cover_photo', value=photo_id))
|
|
120
|
+
|
|
121
|
+
listing_patch_request = self.listing_patch(listing_uuid, ops)
|
|
122
|
+
listing_patch_request.raise_for_status()
|
|
123
|
+
|
|
124
|
+
def exchange_search(self, params = None, **kwargs):
|
|
125
|
+
if params is None:
|
|
126
|
+
params = ExchangeParams(**kwargs)
|
|
127
|
+
elif not isinstance(params, ExchangeParams):
|
|
128
|
+
raise TypeError("params must be of type ExchangeParams.")
|
|
129
|
+
return requests.get(f"{self.__api}/exchange", params.model_dump(mode='json'), headers=self.__get_auth_header())
|
|
130
|
+
|
|
131
|
+
def exchange_post(self, params = None, **kwargs):
|
|
132
|
+
if params is None:
|
|
133
|
+
params = ExchangePostParams(**kwargs)
|
|
134
|
+
elif not isinstance(params, ExchangePostParams):
|
|
135
|
+
raise TypeError("params must be of type ExchangePostParams.")
|
|
136
|
+
return requests.post(f"{self.__api}/exchange", json=params.model_dump(mode='json'), headers=self.__get_auth_header())
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
import pyotp
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from gameflip_api.enums import ShippingPaidBy, Category, Platform, UPC, ListingStatus, AcceptCurrency, Visibility
|
|
10
|
+
from gameflip_api.params import PriceRange, DatetimeRange, ListingSearchParams, ListingPostParams, Op, ExchangeParams, \
|
|
11
|
+
IntRange, ExchangePostParams
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GameflipAPI:
|
|
15
|
+
__api: str
|
|
16
|
+
__api_key: str
|
|
17
|
+
__secret: str
|
|
18
|
+
__totp: pyotp.totp.TOTP
|
|
19
|
+
|
|
20
|
+
def __init__(self, api_key: str, secret: str) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Generates the totp to get authorization for requests.
|
|
23
|
+
:param api_key: API Key is located at developer tab on your profile.
|
|
24
|
+
:param secret: The secret is generated on API Key creation.
|
|
25
|
+
"""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def __get_auth_header(self) -> dict: ...
|
|
29
|
+
|
|
30
|
+
def profile(self, uuid: typing.Union[str, UUID] = 'me') -> requests.Response:
|
|
31
|
+
"""
|
|
32
|
+
Makes a request to the gameflip API to fetch user profile information, which you can get your own information.
|
|
33
|
+
:param uuid: The id is on the url of any profile, it usually starts with 'us-east' and ends with a bunt of numbers.
|
|
34
|
+
To fetch data from your own profile you can set the id with 'me'.
|
|
35
|
+
:raise pydantic.error_wrappers.ValidationError: If id_ is empty or not a string.
|
|
36
|
+
:return: requests.Response
|
|
37
|
+
"""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def wallet_history(self) -> requests.Response:
|
|
41
|
+
"""
|
|
42
|
+
Makes a request to the gameflip API to fetch your wallet history.
|
|
43
|
+
:return: requests.Response
|
|
44
|
+
"""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def get_rldata_items() -> requests.Response:
|
|
49
|
+
"""
|
|
50
|
+
Makes a request to the gameflip API to fetch Rocket League items data.
|
|
51
|
+
:return: requests.Response
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
@typing.overload
|
|
57
|
+
def listing_search(cls, params: ListingSearchParams) -> requests.Response: ...
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
@typing.overload
|
|
61
|
+
def listing_search(
|
|
62
|
+
cls,
|
|
63
|
+
*,
|
|
64
|
+
term: typing.Optional[str] = None,
|
|
65
|
+
category: typing.Optional[Category] = None,
|
|
66
|
+
platform: typing.Optional[Platform] = None,
|
|
67
|
+
genre: typing.Optional[str] = None,
|
|
68
|
+
upc: typing.Optional[UPC] = None,
|
|
69
|
+
shipping_paid_by: typing.Optional[ShippingPaidBy] = None,
|
|
70
|
+
digital: typing.Optional[bool] = None,
|
|
71
|
+
status: typing.Optional[ListingStatus] = None,
|
|
72
|
+
owner: typing.Optional[str] = None,
|
|
73
|
+
condition: typing.Optional[str] = None,
|
|
74
|
+
condition_min: typing.Optional[str] = None,
|
|
75
|
+
price: typing.Optional[PriceRange] = None,
|
|
76
|
+
created: typing.Optional[DatetimeRange] = None,
|
|
77
|
+
updated: typing.Optional[DatetimeRange] = None,
|
|
78
|
+
expiration: typing.Optional[DatetimeRange] = None,
|
|
79
|
+
seller_online_until: typing.Optional[datetime.datetime] = None,
|
|
80
|
+
tags: typing.Optional[str] = None,
|
|
81
|
+
start: typing.Optional[int] = None,
|
|
82
|
+
limit: typing.Optional[int] = None
|
|
83
|
+
) -> requests.Response:
|
|
84
|
+
"""
|
|
85
|
+
Makes a get to /api/v1/listing
|
|
86
|
+
::param term: Searches listing name and description and generates relevance score
|
|
87
|
+
::param category: Filters category
|
|
88
|
+
::param platform: Filters platform
|
|
89
|
+
::param genre: Filters genre
|
|
90
|
+
::param upc: Filters UPC (Universal Product Code), games in this case
|
|
91
|
+
::param shipping_paid_by: Filters shipping paid by
|
|
92
|
+
::param digital: Filters if it's a digital listing
|
|
93
|
+
::param status: Filters status
|
|
94
|
+
::param owner: Filters owner
|
|
95
|
+
::param condition: Filters condition
|
|
96
|
+
::param condition_min: Filters condition (any one value defined for condition)
|
|
97
|
+
::param price: Filters price
|
|
98
|
+
::param created: Filters in a range when it has been created
|
|
99
|
+
::param updated: Filters in a range when it has been updated
|
|
100
|
+
::param expiration: Filters in a range for its expiration
|
|
101
|
+
::param seller_online_until: Filters sellers online
|
|
102
|
+
::param tags: A special term filter which also allows logical AND using the carrot ^ operator in addition
|
|
103
|
+
to logical OR with comma ,
|
|
104
|
+
Precedence is given to OR operations over AND so that a filter like tags=a,b^c^d,e will match all
|
|
105
|
+
listings that have (a OR b) AND (c) AND (d OR e).
|
|
106
|
+
::param start: Gets listing by page number
|
|
107
|
+
::param limit: Limit the number of listings returned
|
|
108
|
+
::raise pydantic.error_wrappers.ValidationError: If some parameter is not correct type
|
|
109
|
+
::raise TypeError: params is not ListingPostParams neither None
|
|
110
|
+
::return requests.Response: Returns a response object
|
|
111
|
+
"""
|
|
112
|
+
...
|
|
113
|
+
|
|
114
|
+
@typing.overload
|
|
115
|
+
def my_listing_search(self, params: ListingSearchParams) -> requests.Response: ...
|
|
116
|
+
|
|
117
|
+
@typing.overload
|
|
118
|
+
def my_listing_search(
|
|
119
|
+
self,
|
|
120
|
+
*,
|
|
121
|
+
term: typing.Optional[str] = None,
|
|
122
|
+
category: typing.Optional[Category] = None,
|
|
123
|
+
platform: typing.Optional[Platform] = None,
|
|
124
|
+
genre: typing.Optional[str] = None,
|
|
125
|
+
upc: typing.Optional[UPC] = None,
|
|
126
|
+
shipping_paid_by: typing.Optional[ShippingPaidBy] = None,
|
|
127
|
+
digital: typing.Optional[bool] = None,
|
|
128
|
+
status: typing.Optional[ListingStatus] = None,
|
|
129
|
+
owner: typing.Optional[str] = None,
|
|
130
|
+
condition: typing.Optional[str] = None,
|
|
131
|
+
condition_min: typing.Optional[str] = None,
|
|
132
|
+
price: typing.Optional[PriceRange] = None,
|
|
133
|
+
created: typing.Optional[DatetimeRange] = None,
|
|
134
|
+
updated: typing.Optional[DatetimeRange] = None,
|
|
135
|
+
expiration: typing.Optional[DatetimeRange] = None,
|
|
136
|
+
seller_online_until: typing.Optional[datetime.datetime] = None,
|
|
137
|
+
tags: typing.Optional[str] = None,
|
|
138
|
+
start: typing.Optional[int] = None,
|
|
139
|
+
limit: typing.Optional[int] = None
|
|
140
|
+
) -> requests.Response:
|
|
141
|
+
"""
|
|
142
|
+
Makes a get to /api/v1/listing
|
|
143
|
+
::param term: Searches listing name and description and generates relevance score
|
|
144
|
+
::param category: Filters category
|
|
145
|
+
::param platform: Filters platform
|
|
146
|
+
::param genre: Filters genre
|
|
147
|
+
::param upc: Filters UPC (Universal Product Code), games in this case
|
|
148
|
+
::param shipping_paid_by: Filters shipping paid by
|
|
149
|
+
::param digital: Filters if it's a digital listing
|
|
150
|
+
::param status: Filters status
|
|
151
|
+
::param owner: Filters owner
|
|
152
|
+
::param condition: Filters condition
|
|
153
|
+
::param condition_min: Filters condition (any one value defined for condition)
|
|
154
|
+
::param price: Filters price
|
|
155
|
+
::param created: Filters in a range when it has been created
|
|
156
|
+
::param updated: Filters in a range when it has been updated
|
|
157
|
+
::param expiration: Filters in a range for its expiration
|
|
158
|
+
::param seller_online_until: Filters sellers online
|
|
159
|
+
::param tags: A special term filter which also allows logical AND using the carrot ^ operator in addition
|
|
160
|
+
to logical OR with comma ,
|
|
161
|
+
Precedence is given to OR operations over AND so that a filter like tags=a,b^c^d,e will match all
|
|
162
|
+
listings that have (a OR b) AND (c) AND (d OR e).
|
|
163
|
+
::param start: Gets listing by page number
|
|
164
|
+
::param limit: Limit the number of listings returned
|
|
165
|
+
::raise pydantic.error_wrappers.ValidationError: If some parameter is not correct type
|
|
166
|
+
::raise TypeError: params is not ListingPostParams neither None
|
|
167
|
+
::return requests.Response: Returns a response object
|
|
168
|
+
"""
|
|
169
|
+
...
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def listing_of(cls, uuid: typing.Union[str, UUID]) -> requests.Response:
|
|
173
|
+
"""
|
|
174
|
+
Makes a request to the gameflip API to fetch listing information.
|
|
175
|
+
:param uuid: The id is on the url of any listing, it usually made with a bunt of numbers.
|
|
176
|
+
:raise pydantic.error_wrappers.ValidationError: If id_ is empty or not a string.
|
|
177
|
+
:return: requests.Response
|
|
178
|
+
"""
|
|
179
|
+
...
|
|
180
|
+
|
|
181
|
+
def my_listing_of(self, uuid: typing.Union[str, UUID]) -> requests.Response:
|
|
182
|
+
"""
|
|
183
|
+
Makes a request to the gameflip API to fetch listing information. Can fetch your own private listing.
|
|
184
|
+
:param uuid: The id is on the url of any listing, it usually made with a bunt of numbers.
|
|
185
|
+
:raise pydantic.error_wrappers.ValidationError: If id_ is empty or not a string.
|
|
186
|
+
:return: requests.Response
|
|
187
|
+
"""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
@typing.overload
|
|
191
|
+
def listing_post(self, params: ListingPostParams) -> requests.Response: ...
|
|
192
|
+
|
|
193
|
+
@typing.overload
|
|
194
|
+
def listing_post(
|
|
195
|
+
self,
|
|
196
|
+
*,
|
|
197
|
+
name: typing.Optional[str] = None,
|
|
198
|
+
description: typing.Optional[str] = None,
|
|
199
|
+
price: typing.Optional[int] = None,
|
|
200
|
+
category: typing.Optional[Category] = None,
|
|
201
|
+
upc: typing.Optional[UPC] = None,
|
|
202
|
+
accept_currency: AcceptCurrency = AcceptCurrency.USD,
|
|
203
|
+
digital: bool = False,
|
|
204
|
+
visibility: Visibility = Visibility.PUBLIC
|
|
205
|
+
) -> requests.Response:
|
|
206
|
+
"""
|
|
207
|
+
Makes a post to /api/v1/listing to create a new listing
|
|
208
|
+
:param name: Name of listing
|
|
209
|
+
:param description: Description of listing
|
|
210
|
+
:param price: Price of listing
|
|
211
|
+
:param category: Category of listing
|
|
212
|
+
:param upc: UPC of listing
|
|
213
|
+
:param accept_currency: Accept currency of the listing
|
|
214
|
+
:param digital: Specifies if it's a digital listing
|
|
215
|
+
:param visibility: Visibility of listing
|
|
216
|
+
:raise pydantic.error_wrappers.ValidationError: If some parameter is not correct type or price is invalid
|
|
217
|
+
:return: requests.Response
|
|
218
|
+
"""
|
|
219
|
+
...
|
|
220
|
+
|
|
221
|
+
def listing_delete(self, uuid: typing.Union[str, UUID]) -> requests.Response:
|
|
222
|
+
"""
|
|
223
|
+
Makes a request to the gameflip API to delete a listing
|
|
224
|
+
:param uuid: The id of the listing
|
|
225
|
+
:return: requests.Response
|
|
226
|
+
"""
|
|
227
|
+
...
|
|
228
|
+
|
|
229
|
+
def listing_patch(self, uuid: typing.Union[str, UUID], ops: typing.List[typing.Union[Op, typing.Dict]]) -> requests.Response:
|
|
230
|
+
"""
|
|
231
|
+
Makes a request to the gameflip API to update a listing
|
|
232
|
+
:param uuid: The id of the listing
|
|
233
|
+
:param ops: Operations to apply
|
|
234
|
+
:return: requests.Response
|
|
235
|
+
"""
|
|
236
|
+
...
|
|
237
|
+
|
|
238
|
+
def post_photo(self, listing_uuid: typing.Union[str, UUID], photo: typing.Union[str, Path], display_order: int):
|
|
239
|
+
"""
|
|
240
|
+
Makes a serie of requests to update a listing photo
|
|
241
|
+
:param listing_uuid: The id of the listing
|
|
242
|
+
:param photo: Url or Path for the photo
|
|
243
|
+
:param display_order: Display order of the photo
|
|
244
|
+
:raise TypeError: photo is not a string or a Path
|
|
245
|
+
:raise HTTPError: some request goes wrong
|
|
246
|
+
:raise FileNotFoundError: photo path not found or don't exist
|
|
247
|
+
:return: None
|
|
248
|
+
"""
|
|
249
|
+
...
|
|
250
|
+
|
|
251
|
+
@typing.overload
|
|
252
|
+
def exchange_search(self, params: ExchangeParams) -> requests.Response: ...
|
|
253
|
+
|
|
254
|
+
@typing.overload
|
|
255
|
+
def exchange_search(
|
|
256
|
+
self,
|
|
257
|
+
*,
|
|
258
|
+
role: typing.Optional[ShippingPaidBy] = None,
|
|
259
|
+
id: typing.Optional[str, UUID] = None,
|
|
260
|
+
name: typing.Optional[str] = None,
|
|
261
|
+
buyer: typing.Optional[str] = None,
|
|
262
|
+
seller: typing.Optional[str] = None,
|
|
263
|
+
price: typing.Optional[PriceRange] = None,
|
|
264
|
+
risk: typing.Optional[IntRange] = None,
|
|
265
|
+
status: typing.Optional[ShippingPaidBy] = None,
|
|
266
|
+
start: typing.Optional[int] = None,
|
|
267
|
+
buyer_rated: typing.Optional[bool] = None,
|
|
268
|
+
seller_rated: typing.Optional[bool] = None,
|
|
269
|
+
category: typing.Optional[Category] = None,
|
|
270
|
+
upc: typing.Optional[UPC] = None,
|
|
271
|
+
digital: typing.Optional[bool] = None,
|
|
272
|
+
escrow: typing.Optional[bool] = None,
|
|
273
|
+
settled: typing.Optional[DatetimeRange] = None,
|
|
274
|
+
shipped: typing.Optional[DatetimeRange] = None,
|
|
275
|
+
received: typing.Optional[DatetimeRange] = None,
|
|
276
|
+
auto_rate_after: typing.Optional[DatetimeRange] = None,
|
|
277
|
+
created: typing.Optional[DatetimeRange] = None,
|
|
278
|
+
updated: typing.Optional[DatetimeRange] = None,
|
|
279
|
+
version: typing.Optional[int] = None,
|
|
280
|
+
limit: typing.Optional[int] = None,
|
|
281
|
+
) -> requests.Response:
|
|
282
|
+
"""
|
|
283
|
+
Makes a request to the gameflip API to fetch exchange
|
|
284
|
+
::param role: buyer or seller
|
|
285
|
+
::param id: uuid of the exchange
|
|
286
|
+
::param name: Name of listing
|
|
287
|
+
::param buyer: uuid of the buyer
|
|
288
|
+
::param seller: uuid of the seller
|
|
289
|
+
::param price: price range of the exchange
|
|
290
|
+
::param risk: risk range of the exchange
|
|
291
|
+
::param status: status of the exchange
|
|
292
|
+
::param start: start page of the fetch
|
|
293
|
+
::param buyer_rated: buyer rated (True/False)
|
|
294
|
+
::param seller_rated: seller rated (True/False)
|
|
295
|
+
::param category: category of the listing
|
|
296
|
+
::param upc: UPC of the listing
|
|
297
|
+
::param digital: specifies if it's a digital listing
|
|
298
|
+
::param escrow: specifies if it's exchange with escrow
|
|
299
|
+
::param settled: settled time range of the listing
|
|
300
|
+
::param shipped: shipped time range of the listing
|
|
301
|
+
::param received: received time range of the listing
|
|
302
|
+
::param auto_rate_after: time range
|
|
303
|
+
::param created: time range of the listing
|
|
304
|
+
::param updated: time range of the listing
|
|
305
|
+
::param version: version of the exchange
|
|
306
|
+
::param limit: limit the fetch
|
|
307
|
+
::raise pydantic.error_wrappers.ValidationError: If some parameter is not correct type
|
|
308
|
+
::raise TypeError: params is not ExchangeParams neither None
|
|
309
|
+
:return: requests.Response
|
|
310
|
+
"""
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
@typing.overload
|
|
314
|
+
def exchange_post(self, params: ExchangePostParams) -> requests.Response: ...
|
|
315
|
+
|
|
316
|
+
@typing.overload
|
|
317
|
+
def exchange_post(
|
|
318
|
+
self,
|
|
319
|
+
*,
|
|
320
|
+
listing_id: typing.Union[str, UUID],
|
|
321
|
+
source_id: typing.Union[str, UUID],
|
|
322
|
+
address_id: typing.Union[str, UUID],
|
|
323
|
+
):
|
|
324
|
+
"""
|
|
325
|
+
Makes a request to the gameflip API to buy a listing
|
|
326
|
+
:param listing_id: listing id
|
|
327
|
+
:param source_id: source id
|
|
328
|
+
:param address_id: address id
|
|
329
|
+
::raise pydantic.error_wrappers.ValidationError: If some parameter is not correct type
|
|
330
|
+
::raise TypeError: params is not ExchangePostParams neither None
|
|
331
|
+
:return: requests.Response
|
|
332
|
+
"""
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class ShippingPaidBy(str, Enum):
|
|
4
|
+
BUYER = "buyer"
|
|
5
|
+
SELLER = "seller"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Category(str, Enum):
|
|
9
|
+
GAMES = 'CONSOLE_VIDEO_GAMES' # Video games, digital or physical
|
|
10
|
+
INGAME = 'DIGITAL_INGAME' # In-game items, digital only
|
|
11
|
+
GIFTCARD = 'GIFTCARD' # Gift cards, digital or physical
|
|
12
|
+
CONSOLE = 'VIDEO_GAME_HARDWARE' # Console game hardware, physical listing only
|
|
13
|
+
ACCESSORIES = 'VIDEO_GAME_ACCESSORIES' # Console game accessories, physical listing only
|
|
14
|
+
TOYS = 'TOYS_AND_GAMES' # Collectibles, physical listing only
|
|
15
|
+
VIDEO = 'VIDEO_DVD' # Movies, physical or digital
|
|
16
|
+
OTHER = 'UNKNOWN' # Unsupported category
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Kind(str, Enum):
|
|
20
|
+
ITEM = 'item' # Item selling
|
|
21
|
+
GIG = 'gig' # Gig selling
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UPC(str, Enum):
|
|
25
|
+
ANIMAL_CROSSING_NH = "045496596439"
|
|
26
|
+
BORDERLANDS3_PC = "GFPCBDLANDS3"
|
|
27
|
+
BORDERLANDS3_PS4 = "GFPSBDLANDS3"
|
|
28
|
+
BORDERLANDS3_XONE = "GFXOBDLANDS3"
|
|
29
|
+
BRAWLHALLA = "045496663285"
|
|
30
|
+
BRAWL_STARS = "GFBRAWLSTARS"
|
|
31
|
+
CLASH_OF_CLANS = "GFCLASHOCLAN"
|
|
32
|
+
CLASH_ROYALE = "GFCLASHROYAL"
|
|
33
|
+
COD_BLOPS6 = "7340f6b6-3a5a-4737-9033-237b9fb522ca"
|
|
34
|
+
COD_MWII = "GF0000CODMW2"
|
|
35
|
+
COD_MWIII = "GF0000CODMW3"
|
|
36
|
+
COD_WZ = "GF00000CODWZ"
|
|
37
|
+
CS2 = "094922417596"
|
|
38
|
+
DARK_AND_DARKER = "GF00DADARKER"
|
|
39
|
+
DARK_SOULS_3 = "GFDARKSOULS3"
|
|
40
|
+
DCU_ONLINE = "GF000000DCUO"
|
|
41
|
+
DEAD_BY_DAYLIGHT = "GFDBDAYLIGHT"
|
|
42
|
+
DIABLO_4 = "GF000DIABLO4"
|
|
43
|
+
DOTA2 = "111111111111"
|
|
44
|
+
DRAGONS_DOGMA_2 = "GFDRAGONSDM2"
|
|
45
|
+
DREAMLIGHT_VALLEY = "GFDREAMLIGHT"
|
|
46
|
+
DYING_LIGHT_2_PS4 = "662248923314"
|
|
47
|
+
DYING_LIGHT_2_PS5 = "662248924915"
|
|
48
|
+
DYING_LIGHT_2_XONE = "662248923369"
|
|
49
|
+
DYING_LIGHT_2_XSERIES = "662248924823"
|
|
50
|
+
ELDEN_RING_PC = "GF000ERINGPC"
|
|
51
|
+
ELDEN_RING_PS5 = "GF000ERINGPS"
|
|
52
|
+
ELDEN_RING_XSERIES = "GF000ERINGXS"
|
|
53
|
+
ENSHROUDED = "GFENSHROUDED"
|
|
54
|
+
ESO_PC = "GF00000PCESO"
|
|
55
|
+
ESO_PS5 = "GF00000PSESO"
|
|
56
|
+
ESO_XSERIES = "GF00000XSESO"
|
|
57
|
+
EVE_ONLINE = "GF0000000EVE"
|
|
58
|
+
FALLOUT76_PC = "GFPCFLLOUT76"
|
|
59
|
+
FALLOUT76_PS4 = "GFPSFLLOUT76"
|
|
60
|
+
FALLOUT76_XONE = "GFXOFLLOUT76"
|
|
61
|
+
FIFA = "GF000000FIFA"
|
|
62
|
+
FORTNITE = "GFFORTNITE"
|
|
63
|
+
GROWTOPIA = "GF0GROWTOPIA"
|
|
64
|
+
GTA5_PC = "710425414534"
|
|
65
|
+
GTA5_PS4 = "710425474521"
|
|
66
|
+
GTA5_PS5 = "GF000GTA5PS5"
|
|
67
|
+
GTA5_XONE = "710425494512"
|
|
68
|
+
GTA5_XSERIES = "GF000GTA5XSX"
|
|
69
|
+
HALO_INFINITE = "GF000HALOINF"
|
|
70
|
+
HAY_DAY = "GF0000HAYDAY"
|
|
71
|
+
LEAGUE_OF_LEGENDS = "GF0000000LOL"
|
|
72
|
+
LORDS_OF_THE_FALLEN = "GFLORDFALLEN"
|
|
73
|
+
MADDEN = "GF0000MADDEN"
|
|
74
|
+
MONOPOLY_GO = "GFMONOPOLYGO"
|
|
75
|
+
NO_MANS_SKY = "GF0NOMANSSKY"
|
|
76
|
+
PATH_OF_EXILE = "GF000POEXILE"
|
|
77
|
+
PATH_OF_EXILE_2 = "6ce9c6d3-c32d-4037-9554-3f1f9de866bd"
|
|
78
|
+
POKEMON_DIAMOND_PEARL = "045496597894"
|
|
79
|
+
POKEMON_LEGENDS_ARCEUS = "045496598044"
|
|
80
|
+
POKEMON_SCARLET_VIOLET = "045496599058"
|
|
81
|
+
POKEMON_SWORD_SHIELD = "045496596972"
|
|
82
|
+
PUBG = "000000578080"
|
|
83
|
+
PUBG_MOBILE = "GF00PUBGMOBI"
|
|
84
|
+
RUNESCAPE = "GF0RUNESCAPE"
|
|
85
|
+
RUST = "000000252490"
|
|
86
|
+
RUST_CONSOLE = "GFRUSTCONSOL"
|
|
87
|
+
SEA_OF_THIEVES = "GFSEATHIEVES"
|
|
88
|
+
SKULL_BONES = "GFSKULLBONES"
|
|
89
|
+
STATE_OF_DECAY_2 = "GF00SODECAY2"
|
|
90
|
+
SWTOR = "GF00000SWTOR"
|
|
91
|
+
TF2 = "014633098693"
|
|
92
|
+
TINY_TINAS_WONDERLANDS = "GF00TTWONDER"
|
|
93
|
+
WARFRAME_PC = "GFSTWARFRAME"
|
|
94
|
+
WARFRAME_PS = "GFPSWARFRAME"
|
|
95
|
+
WARFRAME_SWITCH = "GFSWWARFRAME"
|
|
96
|
+
WARFRAME_XBOX = "GFXOWARFRAME"
|
|
97
|
+
WORLD_OF_WARCRAFT = "GF000WOW0000"
|
|
98
|
+
XDEFIANT = "GF00XDEFIANT"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Platform(str, Enum):
|
|
102
|
+
XBOX = 'xbox'
|
|
103
|
+
X360 = 'xbox_360'
|
|
104
|
+
XONE = 'xbox_one'
|
|
105
|
+
PS1 = 'playstation'
|
|
106
|
+
PS2 = 'playstation_2'
|
|
107
|
+
PS3 = 'playstation_3'
|
|
108
|
+
PS4 = 'playstation_4'
|
|
109
|
+
PSP = 'playstation_portable'
|
|
110
|
+
PSVITA = 'playstation_vita'
|
|
111
|
+
N64 = 'nintendo_64'
|
|
112
|
+
NGAMECUBE = 'nintendo_gamecube'
|
|
113
|
+
NWII = 'nintendo_wii'
|
|
114
|
+
NWIIU = 'nintendo_wiiu'
|
|
115
|
+
NSWITCH = 'nintendo_switch'
|
|
116
|
+
NDS = 'nintendo_ds'
|
|
117
|
+
NDSI = 'nintendo_dsi'
|
|
118
|
+
N3DS = 'nintendo_3ds'
|
|
119
|
+
STEAM = 'steam'
|
|
120
|
+
ORIGIN = 'origin'
|
|
121
|
+
UPLAY = 'uplay'
|
|
122
|
+
GOG = 'gog'
|
|
123
|
+
MOBILE = 'mobile'
|
|
124
|
+
BATTLENET = 'battlenet'
|
|
125
|
+
XLIVE = 'xbox_live'
|
|
126
|
+
PSN = 'playstation_network'
|
|
127
|
+
UNKNOWN = 'unknown' # For PC platform, use UNKNOWN
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ShippingWithinDays(int, Enum):
|
|
131
|
+
AUTO = 0
|
|
132
|
+
ONE = 1
|
|
133
|
+
TWO = 2
|
|
134
|
+
THREE = 3
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ExpireInDays(int, Enum):
|
|
138
|
+
SEVEN = 7
|
|
139
|
+
FOURTEEN = 14
|
|
140
|
+
THIRTY = 30
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class EscrowStatus(str, Enum):
|
|
144
|
+
START = 'start' # Initial condition: seller has Steam item(s)
|
|
145
|
+
RECEIVE_PENDING = 'receive_pending' # Offer made to seller to get Steam item(s)
|
|
146
|
+
RECEIVED = 'received' # Gameflip has Steam item(s)
|
|
147
|
+
LISTED = 'listed' # Gameflip has created listings for Steam item(s)
|
|
148
|
+
STEAM_ESCROW = 'steam_escrow' # Steam item(s) is held by Steam in escrow
|
|
149
|
+
TRADE_HOLD = 'trade_hold' # Gameflip has item(s) but there is a trade hold on them (e.g., Just Survive)
|
|
150
|
+
DELIVER_PENDING = "deliver_pending" # Escrow status: Offer made to buyer, but not accepted yet
|
|
151
|
+
DELIVERED = "delivered" # Escrow status: Buyer has item (terminal state)
|
|
152
|
+
RETURN_PENDING = "return_pending" # Escrow status: Trade offer made to seller to return item, but not accepted yet
|
|
153
|
+
RETURNED = "returned" # Escrow status: Seller has accepted return of item
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ListingStatus(str, Enum):
|
|
157
|
+
DRAFT = "draft" # Listing is draft/editing mode. You cannot list it when it's in this mode
|
|
158
|
+
READY = "ready" # Listing is ready to be listed, required fields have been filled
|
|
159
|
+
ONSALE = "onsale" # Listing is published to the public
|
|
160
|
+
SALE_PENDING = "sale_pending" # A buyer just bought the listing, and payment is being processed
|
|
161
|
+
SOLD = "sold" # A buyer had bought the listing
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class ListingPhotoStatus(str, Enum):
|
|
165
|
+
PENDING = "pending"
|
|
166
|
+
ACTIVE = "active"
|
|
167
|
+
DELETED = "deleted"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ListingOps(str, Enum):
|
|
171
|
+
TEST = "test"
|
|
172
|
+
ADD = "add"
|
|
173
|
+
REPLACE = "replace"
|
|
174
|
+
REMOVE = "remove"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ExchangeStatus(str, Enum):
|
|
178
|
+
PENDING = "pending" # Exchange is being created
|
|
179
|
+
PENDING_CANCEL = "pending_cancel" # Exchange is being canceled
|
|
180
|
+
PENDING_RESCINDING = "pending_rescinding" # Exchange is being refunded
|
|
181
|
+
SETTLED = "settled" # Exchange has payment settled (verified and approved)
|
|
182
|
+
RECEIVED = "received" # Buyer has received the item
|
|
183
|
+
PENDING_COMPLETION = "pending_completion" # Exchange is being completed, happens after seller rates
|
|
184
|
+
COMPLETE = "complete" # Exchange completes, both buyer and seller have rated each other
|
|
185
|
+
CANCELLED = "cancelled" # Exchange has been cancelled, and payment authorization (if any) is also cancelled
|
|
186
|
+
RESCINDED = "rescinded" # Exchange has been cancelled with refund completed
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class AcceptCurrency(str, Enum):
|
|
190
|
+
USD = "USD"
|
|
191
|
+
FLP = "FLP"
|
|
192
|
+
BOTH = "BOTH"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Steam APP IDs and Context IDs can be defined in a similar manner if needed.
|
|
196
|
+
class SteamAppID(str, Enum):
|
|
197
|
+
CSGO = '730' # CS:GO
|
|
198
|
+
TF2 = '440' # Team Fortress 2
|
|
199
|
+
DOTA2 = '570' # DOTA 2
|
|
200
|
+
RUST = '252490' # Rust
|
|
201
|
+
PUBG = '578080' # PlayerUnknown's Battlegrounds
|
|
202
|
+
H1Z1_KOK = '433850' # H1Z1: King of the Kill
|
|
203
|
+
JUST_SURVIVE = '295110' # H1Z1: Just Survive
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class SteamContextID(str, Enum):
|
|
207
|
+
CONTEXT_ID_433850 = '1' # H1Z1: King of the Kill
|
|
208
|
+
CONTEXT_ID_295110 = '1' # Just Survive
|
|
209
|
+
CONTEXT_ID_DEFAULT = '2' # Default if not specified above
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class Visibility(str, Enum):
|
|
213
|
+
UNLISTED = 'unlisted'
|
|
214
|
+
PUBLIC = 'public'
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator
|
|
6
|
+
from pydantic_core.core_schema import ValidationInfo
|
|
7
|
+
|
|
8
|
+
from gameflip_api.enums import Category, Platform, UPC, ShippingPaidBy, ListingStatus, AcceptCurrency, Visibility, \
|
|
9
|
+
ListingOps
|
|
10
|
+
from gameflip_api.types import AwsCognitoUUID, Price
|
|
11
|
+
|
|
12
|
+
T = typing.TypeVar("T")
|
|
13
|
+
|
|
14
|
+
class Range(BaseModel, typing.Generic[T]):
|
|
15
|
+
start: T
|
|
16
|
+
end: T
|
|
17
|
+
|
|
18
|
+
@field_validator("end", mode="after")
|
|
19
|
+
@classmethod
|
|
20
|
+
def check_end_after_start(cls, value: int, info: ValidationInfo) -> int:
|
|
21
|
+
if "start" in info.data and value < info.data["start"]:
|
|
22
|
+
raise ValueError("start must be greater or equal to end.")
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
def __str__(self):
|
|
26
|
+
return f"{self.start},{self.end}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class IntRange(Range[int]):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PriceRange(Range[Price]):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DatetimeRange(Range[datetime.datetime]):
|
|
38
|
+
def __str__(self):
|
|
39
|
+
return f"{self.__format_datetime(self.start)},{self.__format_datetime(self.end)}"
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def __format_datetime(datetime_: datetime.datetime):
|
|
43
|
+
return datetime_.isoformat(timespec='milliseconds') + "Z"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ListingSearchParams(BaseModel):
|
|
47
|
+
term: typing.Optional[str] = None
|
|
48
|
+
category: typing.Optional[Category] = None
|
|
49
|
+
platform: typing.Optional[Platform] = None
|
|
50
|
+
genre: typing.Optional[str] = None
|
|
51
|
+
upc: typing.Optional[UPC] = None
|
|
52
|
+
shipping_paid_by: typing.Optional[ShippingPaidBy] = None
|
|
53
|
+
digital: typing.Optional[bool] = None
|
|
54
|
+
status: typing.Optional[ListingStatus] = None
|
|
55
|
+
owner: typing.Optional[str] = None
|
|
56
|
+
condition: typing.Optional[str] = None
|
|
57
|
+
condition_min: typing.Optional[str] = None
|
|
58
|
+
price: typing.Optional[PriceRange] = None
|
|
59
|
+
created: typing.Optional[DatetimeRange] = None
|
|
60
|
+
updated: typing.Optional[DatetimeRange] = None
|
|
61
|
+
expiration: typing.Optional[DatetimeRange] = None
|
|
62
|
+
seller_online_until: typing.Optional[DatetimeRange] = None
|
|
63
|
+
tags: typing.Optional[str] = None
|
|
64
|
+
start: typing.Optional[int] = None
|
|
65
|
+
limit: typing.Optional[int] = None
|
|
66
|
+
|
|
67
|
+
def model_dump(self, *args, **kwargs):
|
|
68
|
+
d = super().model_dump(*args, **kwargs)
|
|
69
|
+
for attr in ["price", "created", "updated", "expiration", "seller_online_until"]:
|
|
70
|
+
if d.get(attr) is not None:
|
|
71
|
+
d[attr] = str(getattr(self, attr))
|
|
72
|
+
return d
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ListingPostParams(BaseModel):
|
|
76
|
+
name: typing.Optional[str] = None
|
|
77
|
+
description: typing.Optional[str] = None
|
|
78
|
+
price: typing.Optional[Price] = None
|
|
79
|
+
category: typing.Optional[Category] = None
|
|
80
|
+
upc: typing.Optional[UPC] = None
|
|
81
|
+
accept_currency: AcceptCurrency = AcceptCurrency.USD
|
|
82
|
+
digital: bool = False
|
|
83
|
+
visibility: Visibility = Visibility.PUBLIC
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class GameflipAPIParams(BaseModel):
|
|
87
|
+
api_key: str = Field(..., min_length=1, description="API key is required.")
|
|
88
|
+
secret: str = Field(..., min_length=1, description="Secret is required.")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class UUIDParam(BaseModel):
|
|
92
|
+
uuid: UUID
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class OwnerParam(BaseModel):
|
|
96
|
+
owner: AwsCognitoUUID
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Op(BaseModel):
|
|
100
|
+
op: ListingOps
|
|
101
|
+
path: str
|
|
102
|
+
value: typing.Any
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ExchangeParams(BaseModel):
|
|
106
|
+
role: typing.Optional[ShippingPaidBy] = None
|
|
107
|
+
id: typing.Optional[UUID] = None
|
|
108
|
+
name: typing.Optional[str] = None
|
|
109
|
+
buyer: typing.Optional[str] = None
|
|
110
|
+
seller: typing.Optional[str] = None
|
|
111
|
+
price: typing.Optional[PriceRange] = None
|
|
112
|
+
risk: typing.Optional[IntRange] = None
|
|
113
|
+
status: typing.Optional[ListingStatus] = None
|
|
114
|
+
buyer_rated : typing.Optional[bool] = None
|
|
115
|
+
seller_rated : typing.Optional[bool] = None
|
|
116
|
+
start: typing.Optional[int] = None
|
|
117
|
+
category: typing.Optional[Category] = None
|
|
118
|
+
upc: typing.Optional[UPC] = None
|
|
119
|
+
digital: typing.Optional[bool] = None
|
|
120
|
+
escrow: typing.Optional[bool] = None
|
|
121
|
+
settled: typing.Optional[DatetimeRange] = None
|
|
122
|
+
shipped: typing.Optional[DatetimeRange] = None
|
|
123
|
+
received: typing.Optional[DatetimeRange] = None
|
|
124
|
+
auto_rate_after: typing.Optional[DatetimeRange] = None
|
|
125
|
+
created: typing.Optional[DatetimeRange] = None
|
|
126
|
+
updated: typing.Optional[DatetimeRange] = None
|
|
127
|
+
version: typing.Optional[int] = None
|
|
128
|
+
limit: typing.Optional[int] = None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ExchangePostParams(BaseModel):
|
|
132
|
+
listing_id: UUID
|
|
133
|
+
source_id: UUID
|
|
134
|
+
address_id: UUID
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from pydantic import GetCoreSchemaHandler
|
|
5
|
+
from pydantic_core import core_schema
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AwsCognitoUUID(str):
|
|
9
|
+
"""
|
|
10
|
+
Tipo customizado que valida strings no formato:
|
|
11
|
+
<region>:<uuid>
|
|
12
|
+
Exemplo: us-east-1:dc501f75-302c-4419-8ece-57974d688e6f
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
pattern = re.compile(
|
|
16
|
+
r"^[a-z]{2}-[a-z]+-\d+:[0-9a-fA-F-]{36}$"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def __get_pydantic_core_schema__(cls, _source_type, _handler: GetCoreSchemaHandler):
|
|
21
|
+
return core_schema.no_info_after_validator_function(
|
|
22
|
+
cls.validate,
|
|
23
|
+
core_schema.str_schema()
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def validate(cls, value):
|
|
28
|
+
if not isinstance(value, str):
|
|
29
|
+
raise TypeError("valor should be string.")
|
|
30
|
+
|
|
31
|
+
if not cls.pattern.match(value):
|
|
32
|
+
raise ValueError("invalid format (expected '<region>:<uuid>')")
|
|
33
|
+
|
|
34
|
+
# Valida se a parte após ':' é um UUID válido
|
|
35
|
+
region, uuid_part = value.split(":", 1)
|
|
36
|
+
try:
|
|
37
|
+
UUID(uuid_part)
|
|
38
|
+
except ValueError:
|
|
39
|
+
raise ValueError("UUID invalid after ':'")
|
|
40
|
+
|
|
41
|
+
return cls(value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Price(int):
|
|
45
|
+
@classmethod
|
|
46
|
+
def __get_pydantic_core_schema__(cls, _source_type, _handler: GetCoreSchemaHandler):
|
|
47
|
+
return core_schema.no_info_after_validator_function(
|
|
48
|
+
cls.validate,
|
|
49
|
+
core_schema.int_schema()
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def validate(cls, value):
|
|
54
|
+
if not isinstance(value, int):
|
|
55
|
+
raise TypeError("value should be int.")
|
|
56
|
+
|
|
57
|
+
if value < 75:
|
|
58
|
+
raise ValueError("value should be higher than 75.")
|
|
59
|
+
|
|
60
|
+
return cls(value)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gameflip-api
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Uma API wrapper em Python para utilização com a plataforma Gameflip.
|
|
5
|
+
Author-email: Gustavo Pedroso Bernardes <gpedrosobernardes@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: validators
|
|
9
|
+
Requires-Dist: pydantic
|
|
10
|
+
Requires-Dist: pyotp
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
|
|
13
|
+
# Gameflip API
|
|
14
|
+
|
|
15
|
+
Uma **API wrapper em Python** para utilização com a plataforma Gameflip.
|
|
16
|
+
Facilita a integração via script, para automatizar operações, consultar dados e interagir com a Gameflip de forma programática.
|
|
17
|
+
|
|
18
|
+
## 🧰 Funcionalidades
|
|
19
|
+
|
|
20
|
+
* Conexão autenticada com a Gameflip via chave e segredo (`API_KEY`, `API_SECRET`)
|
|
21
|
+
* Métodos para executar operações comuns (por exemplo: listagem de produtos, criação de ofertas, consulta de histórico)
|
|
22
|
+
* Interface simples em Python para agilizar automações
|
|
23
|
+
* Código open-source, fácil de estender para necessidades específicas
|
|
24
|
+
|
|
25
|
+
## 🚀 Começando
|
|
26
|
+
|
|
27
|
+
### Pré-requisitos
|
|
28
|
+
|
|
29
|
+
* Python (versão compatível — idealmente 3.8+)
|
|
30
|
+
* Conta na Gameflip e acesso à API (chave + segredo)
|
|
31
|
+
* Variáveis de ambiente configuradas:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export GFAPI_KEY=<sua_chave_aqui>
|
|
35
|
+
export GFAPI_SECRET=<seu_segredo_aqui>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
(No Windows: `set GFAPI_KEY=<…>`, `set GFAPI_SECRET=<…>`)
|
|
39
|
+
|
|
40
|
+
### Instalação
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install gameflip-api
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Uso básico
|
|
47
|
+
|
|
48
|
+
Exemplo no arquivo `example.py` (ajuste conforme a sua necessidade):
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import os
|
|
52
|
+
from pprint import pprint
|
|
53
|
+
from dotenv import load_dotenv
|
|
54
|
+
from gameflip_api.api import GameflipAPI
|
|
55
|
+
|
|
56
|
+
load_dotenv()
|
|
57
|
+
|
|
58
|
+
print("Listing results:")
|
|
59
|
+
|
|
60
|
+
listing_response = GameflipAPI.listing_search(digital=True, limit=1)
|
|
61
|
+
pprint(listing_response.json())
|
|
62
|
+
|
|
63
|
+
gameflip_api = GameflipAPI(os.getenv('GFAPI_KEY'), os.getenv('GFAPI_SECRET'))
|
|
64
|
+
|
|
65
|
+
print("My profile info:")
|
|
66
|
+
|
|
67
|
+
pprint(gameflip_api.profile().json())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 📦 Estrutura do Projeto
|
|
71
|
+
|
|
72
|
+
* `src/gameflip_api/` — código-fonte da biblioteca
|
|
73
|
+
* `example.py` — script de demonstração de uso
|
|
74
|
+
* `test.py` — arquivo para testes rápidos
|
|
75
|
+
* `requirements.txt` — dependências do Python
|
|
76
|
+
|
|
77
|
+
## 🤝 Contato
|
|
78
|
+
|
|
79
|
+
Se tiver dúvidas, sugestões ou quiser colaborar:
|
|
80
|
+
|
|
81
|
+
* Crie uma *issue* no próprio repositório
|
|
82
|
+
* Envie um pull request com descrições claras das alterações
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/gameflip_api/__init__.py
|
|
4
|
+
src/gameflip_api/api.py
|
|
5
|
+
src/gameflip_api/api.pyi
|
|
6
|
+
src/gameflip_api/enums.py
|
|
7
|
+
src/gameflip_api/params.py
|
|
8
|
+
src/gameflip_api/types.py
|
|
9
|
+
src/gameflip_api.egg-info/PKG-INFO
|
|
10
|
+
src/gameflip_api.egg-info/SOURCES.txt
|
|
11
|
+
src/gameflip_api.egg-info/dependency_links.txt
|
|
12
|
+
src/gameflip_api.egg-info/requires.txt
|
|
13
|
+
src/gameflip_api.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gameflip_api
|