diff --git a/.github/workflows/check-update-widget-index.yaml b/.github/workflows/check-update-widget-index.yaml index d75cd8b..497c933 100644 --- a/.github/workflows/check-update-widget-index.yaml +++ b/.github/workflows/check-update-widget-index.yaml @@ -1,16 +1,13 @@ name: Check and update widget index on: - workflow_run: - workflows: [Check widget translation] - types: [completed] + push: branches: [master, dev] jobs: main: name: Check and update widget index runs-on: ${{ matrix.os }} - if: ${{ github.event.workflow_run.conclusion == 'success' }} timeout-minutes: 15 strategy: fail-fast: false diff --git a/chat/display_widgets.py b/chat/display_widgets.py index 06fa7dd..67fdc5f 100644 --- a/chat/display_widgets.py +++ b/chat/display_widgets.py @@ -171,6 +171,12 @@ def _widgetize_inner(command: str, params: str, depth: int = 0) -> str: elif command == 'yield-protocol-borrow-close': items = params.split(",") lines.append(f"yield protocol borrow close action: {items[0]}") + elif command == 'hop-protocol-bridge': + items = params.split(",") + lines.append(f"hop protocol bridge action for amount: {items[0]}, token symbol: {items[1]}, from chain: {items[2]}, to chain: {items[3]}.") + elif command == 'tx-replay': + items = params.split(",") + lines.append(f"replay transaction with tx hash: {items[0]}") else: # assert 0, f'unrecognized command: {command}({params})' lines.append(f"An unrecognized command: {command}({params})") diff --git a/integrations/__init__.py b/integrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integrations/center.py b/integrations/center.py index 027a7be..a5e9e2e 100644 --- a/integrations/center.py +++ b/integrations/center.py @@ -7,8 +7,11 @@ import requests import web3 +from fastapi.responses import Response + import env import utils +import auth from utils import ETH_MAINNET_CHAIN_ID, nft, FetchError, ExecError import utils.timing as timing @@ -26,8 +29,7 @@ #"polygon-mainnet", ] -API_URL = "https://api.center.dev/v1" -API_V2_URL = "https://api.center.dev/v2" +API_URL = "https://api.center.dev" MAX_RESULTS = 12 PAGE_LIMIT = 12 @@ -126,6 +128,7 @@ class NFTAsset(ContainerMixin): preview_image_url: str description: str = '' price: Optional[str] = None + attributes: Optional[ List ] = None def container_name(self) -> str: return 'display-nft-asset-container' @@ -214,7 +217,7 @@ def fetch_nft_search(search_str: str) -> Generator[Union[NFTCollection, NFTAsset )) count = 0 for network in NETWORKS: - url = f"{API_V2_URL}/{network}/search?{q}" + url = f"{API_URL}/v2/{network}/search?{q}" timing.log('search_begin') response = requests.get(url, headers=HEADERS) try: @@ -295,7 +298,7 @@ def fetch_nft_search_collection_by_trait(network: str, address: str, trait_name: limit=limit, offset=offset, )) - url = f"{API_URL}/{network}/{address}/assets/searchByTraits?{q}" + url = f"{API_URL}/v1/{network}/{address}/assets/searchByTraits?{q}" response = requests.post(url, headers=headers, json=payload) try: response.raise_for_status() @@ -337,7 +340,7 @@ def fetch_nft_search_collection_by_trait(network: str, address: str, trait_name: def fetch_nft_collection(network: str, address: str) -> NFTCollection: - url = f"{API_V2_URL}/{network}/{address}/nft/metadata" + url = f"{API_URL}/v2/{network}/{address}/nft/metadata" response = requests.get(url, headers=HEADERS) response.raise_for_status() obj = response.json() @@ -345,7 +348,7 @@ def fetch_nft_collection(network: str, address: str) -> NFTCollection: if num_assets == 0: # seems to return 0 incorrectly # use the asset endpoint with dummy token token_id = 1 - url = f"{API_V2_URL}/{network}/{address}/nft/{token_id}/metadata" + url = f"{API_URL}/v2/{network}/{address}/nft/{token_id}/metadata" response = requests.get(url, headers=HEADERS) if response.status_code == 200: token_obj = response.json() @@ -375,7 +378,7 @@ def fetch_nft_collection_assets(network: str, address: str) -> NFTCollectionAsse {"Address": address, "TokenID": token_id} for token_id in token_ids[offset: offset + limit] ]} - url = f"{API_URL}/{network}/assets" + url = f"{API_URL}/v1/{network}/assets" response = requests.post(url, headers=HEADERS, json=payload) try: response.raise_for_status() @@ -400,6 +403,7 @@ def fetch_nft_collection_assets(network: str, address: str) -> NFTCollectionAsse name=item['name'], preview_image_url=item['mediumPreviewImageUrl'], price=price, + ) if not _is_valid_asset(asset): continue @@ -434,7 +438,7 @@ def fetch_nft_collection_assets_for_sale(network: str, address: str) -> Generato {"Address": address, "TokenID": token_id} for token_id in token_ids[offset: offset + limit] ]} - url = f"{API_URL}/{network}/assets" + url = f"{API_URL}/v1/{network}/assets" response = requests.post(url, headers=HEADERS, json=payload) try: response.raise_for_status() @@ -482,7 +486,7 @@ def fetch_nft_collection_traits(network: str, address: str) -> NFTCollectionTrai limit=limit, offset=offset, )) - url = f"{API_URL}/{network}/{address}/traits?{q}" + url = f"{API_URL}/v1/{network}/{address}/traits?{q}" response = requests.get(url, headers=HEADERS) try: response.raise_for_status() @@ -525,7 +529,7 @@ def fetch_nft_collection_trait_values(network: str, address: str, trait: str) -> limit=limit, offset=offset, )) - url = f"{API_URL}/{network}/{address}/traits/{trait}?{q}" + url = f"{API_URL}/v1/{network}/{address}/traits/{trait}?{q}" response = requests.get(url, headers=HEADERS) try: response.raise_for_status() @@ -557,7 +561,7 @@ def fetch_nft_collection_trait_values(network: str, address: str, trait: str) -> def fetch_nft_asset(network: str, address: str, token_id: str) -> NFTAsset: - url = f"{API_URL}/{network}/{address}/{token_id}" + url = f"{API_URL}/v2/{network}/{address}/nft/{token_id}/metadata" response = requests.get(url, headers=HEADERS) response.raise_for_status() obj = response.json() @@ -565,16 +569,18 @@ def fetch_nft_asset(network: str, address: str, token_id: str) -> NFTAsset: network=network, address=address, token_id=token_id, - collection_name=obj['collectionName'], - name=obj['name'], - preview_image_url=obj['mediumPreviewImageUrl'], + collection_name=obj['collection']['name'], + name=obj['metadata']['name'], + preview_image_url=f"{API_URL}{obj['media']['small']}", + # image_url=f"{API_URL}{obj['media']['medium']}", description=obj['metadata']['description'], + attributes=obj['metadata']['attributes'], ) def fetch_nft_asset_traits(network: str, address: str, token_id: str) -> NFTAssetTraits: price = _fetch_nft_asset_price_str(network, address, token_id) - url = f"{API_URL}/{network}/{address}/{token_id}" + url = f"{API_URL}/v1/{network}/{address}/{token_id}" response = requests.get(url, headers=HEADERS) response.raise_for_status() obj = response.json() @@ -622,7 +628,7 @@ def fetch_nfts_owned_by_address_or_domain(network: str, address_or_domain: str) limit=limit, offset=offset, )) - url = f"{API_V2_URL}/{normalized_network}/{address_or_domain}/nfts-owned?{q}" + url = f"{API_URL}/v2/{normalized_network}/{address_or_domain}/nfts-owned?{q}" try: response = requests.get(url, headers=HEADERS) @@ -691,3 +697,19 @@ def _fetch_nft_asset_price_str(network: str, address: str, token_id: str) -> Opt else: price = None return price + +@auth.authenticate_user_id() +def fetch_center_image(response, network: str, address: str, token_id: str, size: str, user_id:str=None): + + url = f"{API_URL}/v2/{network}/{address}/nft/{token_id}/render/{size}" + resp = requests.get(url, headers=HEADERS) + + # Check if user is authenticated + if not user_id: return None + + # return response.content + if resp.status_code != 200: + # Handle error appropriately, return a message, or another status code + return Response(content="Could not retrieve image", status_code=resp.status_code) + + return Response(content=resp.content) \ No newline at end of file diff --git a/integrations/opensea.py b/integrations/opensea.py index a1c65ca..214f1ef 100644 --- a/integrations/opensea.py +++ b/integrations/opensea.py @@ -31,8 +31,6 @@ class NFTContract: chain: str address: str slug: str - name: str - image_url: str # this is a listing of an NFT asset @@ -56,7 +54,7 @@ class NFTFulfillmentData: def fetch_contract(address: str) -> NFTContract: """Fetch data about a contract (collection).""" - url = f"{API_V1_URL}/asset_contract/{address}" + url = f"{API_V2_URL}/chain/ethereum/contract/{address}/nfts" def _exec_request(): response = requests.get(url, headers=HEADERS) @@ -67,16 +65,12 @@ def _exec_request(): _exec_request, [ErrorToRetry(requests.exceptions.HTTPError, _should_retry_exception)], ) - collection = obj["collection"] return NFTContract( - chain=obj["chain_identifier"], + chain='ethereum', address=address, - slug=collection["slug"], - name=collection["name"], - image_url=collection["image_url"], + slug=obj["nfts"][0]["collection"], ) - def fetch_listings(address: str, token_id: str) -> List[NFTListing]: """Fetch cheapest listing for an asset.""" chain = "ethereum" diff --git a/knowledge_base/widgets.yaml b/knowledge_base/widgets.yaml index 5bf628f..afeb6c9 100644 --- a/knowledge_base/widgets.yaml +++ b/knowledge_base/widgets.yaml @@ -698,3 +698,37 @@ - borrowToken type: object return_value_description: "" +- _name_: display_hop_protocol_bridge + description: use the hop protocol to bridge/deposit tokens from a supported chain to a different supported chain + parameters: + properties: + amount: + description: token amount + type: string + tokenSymbol: + description: token symbol + type: string + fromChain: + description: chain to bridge from + type: string + toChain: + description: chain to bridge to + type: string + required: + - amount + - tokenSymbol + - fromChain + - toChain + type: object + return_value_description: "" +- _name_: display_tx_replay + description: replay a transaction with the same parameters + parameters: + properties: + txHash: + description: the transaction with tx hash to replay + type: string + required: + - txHash + type: object + return_value_description: "" \ No newline at end of file diff --git a/main.py b/main.py index 16a4eb9..faeb9ee 100644 --- a/main.py +++ b/main.py @@ -3,9 +3,10 @@ from dataclasses import dataclass from typing import Any, Dict, Optional, Set -from fastapi import FastAPI, Request, Response, Body, WebSocket, WebSocketDisconnect +from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from starlette.middleware.sessions import SessionMiddleware +from fastapi.responses import StreamingResponse import server import chat @@ -15,6 +16,7 @@ from app import chat as app_chat from app import share as app_share +from integrations import center app = FastAPI() @@ -80,6 +82,9 @@ class ClientState: async def api_nonce(request: Request): return auth.api_nonce(request) +@app.get("/center_image/{network}/{address}/{token_id}/{size}") +async def fetch_nft_image(request: Request, network: str, address: str, token_id: str, size: str): + return center.fetch_center_image(request, network, address, token_id, size) @app.post("/login") async def api_login(request: Request, data: auth.AcceptJSON): diff --git a/server.py b/server.py index e977cbe..950917b 100644 --- a/server.py +++ b/server.py @@ -49,7 +49,7 @@ def _register_system(system_config_id, system_config_json): def _get_default_system_config_id(): # we query the db but return the identifier to store, so that we can still # reference the id even after the session has been closed. - default_system_config = SystemConfig.query.filter_by(json=config.default_config).one_or_none() + default_system_config = SystemConfig.query.filter_by(json=config.default_config).first() if not default_system_config: default_system_config = SystemConfig(json=config.default_config) db_session.add(default_system_config) diff --git a/utils/constants.py b/utils/constants.py index cd74a88..20bfb48 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -53,7 +53,7 @@ WIDGET_INFO_TOKEN_LIMIT = 4000 # Widget Index -WIDGET_INDEX_NAME = "WidgetV20" +WIDGET_INDEX_NAME = "WidgetV22" def get_widget_index_name(): if env.is_local():