import requests import typing from typing import Optional from datetime import datetime, timezone, timedelta import discord from discord.ext import commands from discord import app_commands from PIL import Image, ImageDraw, ImageFont, ImageColor, ImageFilter, ImageSequence import json from web3 import Web3 from eth_utils import to_checksum_address # PolygonScan API 키 api_key = "4H655NPT5229MKFE2NNHIS2GNZ3U4UTGT2" with open("token.txt", "r", encoding='utf-8') as f: token = f.read() def first_time(timestamp): timestamp = int(timestamp) dt_object = datetime.fromtimestamp(timestamp, timezone.utc) # Convert UTC to Korea Standard Time (KST) kst = timezone(timedelta(hours=9)) # KST is UTC+9 dt_kst = dt_object.astimezone(kst) t = dt_kst.strftime('%Y-%m-%d') return str(t) def web3_find(token_id): # Web3 프로바이더 설정 (여기서는 Infura 사용, 자신의 프로젝트 ID로 대체) INFURA_URL = "https://polygon-mainnet.infura.io/v3/16185d569adf4d00a04f2d2a200cf231" web3 = Web3(Web3.HTTPProvider(INFURA_URL)) # 토큰 컨트랙트 주소 및 ABI (최소한 tokenURI 함수를 포함해야 함) token_contract_address = "0xA4B37bE40F7b231Ee9574c4b16b7DDb7EAcDC99B" token_id # 컨트랙트 ABI의 일부 예시 (실제 ABI로 대체 필요) contract_abi = [ { "constant": True, "inputs": [{"name": "_tokenId", "type": "uint256"}], "name": "tokenURI", "outputs": [{"name": "", "type": "string"}], "payable": False, "stateMutability": "view", "type": "function" }, ] # 컨트랙트 인스턴스 생성 contract = web3.eth.contract(address=token_contract_address, abi=contract_abi) # tokenURI 호출하여 메타데이터 URI 가져오기 token_uri = contract.functions.tokenURI(token_id).call() # 메타데이터 URI에서 JSON 메타데이터 다운로드 metadata_response = requests.get(token_uri) metadata = metadata_response.json() # 메타데이터에서 NFT 이름 추출 nft_name = metadata.get("name", "Name not found") print(f"NFT Name: {nft_name}") return nft_name def obj_srch(account_address): # PolygonScan API URL 구성 contract_address = "0xA4B37bE40F7b231Ee9574c4b16b7DDb7EAcDC99B" url = f"https://api.polygonscan.com/api?module=account&action=tokenbalance&contractaddress={contract_address}&address={account_address}&tag=latest&apikey={api_key}" # API 요청 response = requests.get(url) # 응답 확인 및 처리 if response.status_code == 200: data = response.json() balance = data.get('result') if balance is not None: # ERC-721은 보통 1 NFT당 1을 가지므로, balance를 직접 반환 real_balance = int(balance) print(f"계정 주소 {account_address}는(은) 토큰 컨트랙트 {contract_address}의 NFT를 {real_balance}개 가지고 있습니다.") return real_balance else: print("NFT 잔액 정보를 가져올 수 없습니다.") else: print("API 요청에 실패했습니다.") def como_srch(account_address): tok = "0x58AeABfE2D9780c1bFcB713Bf5598261b15dB6e5" url = f"https://api.polygonscan.com/api?module=account&action=tokenbalance&contractaddress={tok}&address={account_address}&tag=latest&apikey={api_key}" response = requests.get(url) # 응답 확인 if response.status_code == 200: data = response.json() # 토큰 수량 출력 balance = data.get('result') if balance is not None: real_balance = int(balance) / 10 ** 18 print(f"계정 주소 {account_address}는(은) 토큰 주소 {tok}의 토큰을 {int(real_balance)}개 가지고 있습니다.") return int(real_balance) else: print("토큰 수량 정보를 가져올 수 없습니다.") else: print("API 요청에 실패했습니다.") def ad2id(input_str): input_str = str(input_str) input_str = to_checksum_address(input_str) print(input_str) base_url = "https://cache.nova.gd/user/v1" users = [] error_message = None try: if input_str.startswith("0x"): url = f"{base_url}/by-address/{input_str}" response = requests.get(url) if response.status_code == 200: users = response.json() # 'by-address' 가 리스트를 직접 반환한다고 가정 else: error_message = f"Request failed with status code {response.status_code}" except Exception as e: error_message = str(e) if error_message: return "Error" if not users: return "Mint" formatted_result = "".join(f"{user['nickname']}" for i, user in enumerate(users)) return formatted_result def ad_srch(input_str): if len(input_str) < 4: return "4글자 이상으로 작성해주세요." base_url = "https://cache.nova.gd/user/v1" url = f"{base_url}/search?query={input_str}" response = requests.get(url) data = response.json() # 'address' 값 추출 address = data['results'][0]['address'] print(address) return address def como_txt(text): image_size = (180, 50) image = Image.new('RGBA', image_size, (255, 255, 255, 0)) draw = ImageDraw.Draw(image) font = ImageFont.truetype("Helvetica_Neue_LT_Std_75_Bold.otf", 50) text_color = (200, 200, 200) # 텍스트의 경계 상자 계산 text_bbox = draw.textbbox((0, 0), text, font=font) text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] # 텍스트를 이미지 중앙에 정렬 text_x = (image_size[0] - text_width) / 2 text_y = (image_size[1] - text_height) / 2 draw.text((text_x, text_y), text, fill=text_color, font=font) return image def acom(input_str): if len(input_str) < 4: return ["4글자 이상으로 작성해주세요."] base_url = "https://cache.nova.gd/user/v1" url = f"{base_url}/search?query={input_str}" response = requests.get(url) data = response.json() users = data.get("results", []) # 'search' 가 {"results": [...]} 형식을 반환한다고 가정 if response.status_code != 200: raise Exception(f"Request failed with status code {response.status_code}") if not users: return ["검색 결과 없음"] # 결과를 리스트 형태로 반환하기 전에 최대 25개의 결과만 포함되도록 조정 result_list = [user['nickname'] for user in users[:25]] return result_list def names(text, img, x = 124, y = 26): text = str(text) image = img draw = ImageDraw.Draw(image) font = ImageFont.truetype("Helvetica_Neue_LT_Std_75_Bold.otf", 42) text_color = (208, 211, 214) draw.text((x, y), text, fill=text_color, font=font) return image async def name_auto(interaction: discord.Interaction, current: str) -> typing.List[app_commands.Choice[str]]: acs = acom(current) matching_files = acs return [app_commands.Choice(name=num, value=num) for num in matching_files] def find_trans(account_address, number): API_ENDPOINT = "https://api.polygonscan.com/api" API_KEY = "4H655NPT5229MKFE2NNHIS2GNZ3U4UTGT2" # 검색할 계좌 주소 및 토큰 컨트랙트 주소 token_contract_address = "0xa4b37be40f7b231ee9574c4b16b7ddb7eacdc99b" # Transfer 이벤트를 조회하기 위한 파라미터 params = { "module": "account", "action": "tokennfttx", "contractaddress": token_contract_address, "address": account_address, "page": 1, "offset": number, # 요청한 이벤트 개수만큼 조회 "sort": "desc", # 최신순으로 정렬 "apikey": API_KEY } response = requests.get(API_ENDPOINT, params=params) data = response.json() print(data) # 결과에서 요청한 개수의 NFT 정보 추출 if data["status"] == "1" and data["result"]: nft_list = data["result"][:number] # 요청한 개수만큼의 결과만 사용 token_ids = [nft["tokenID"] for nft in nft_list] # 토큰 ID 리스트 생성 token_from = [nft["from"] for nft in nft_list] # from 값의 리스트 생성 return token_ids, token_from # 토큰 ID 리스트와 from 리스트를 반환 else: print("No recent NFT transfers found.") return [], [] # 두 빈 리스트를 반환 def find_first(account_address, number = 1): API_ENDPOINT = "https://api.polygonscan.com/api" API_KEY = "4H655NPT5229MKFE2NNHIS2GNZ3U4UTGT2" # 검색할 계좌 주소 및 토큰 컨트랙트 주소 token_contract_address = "0xa4b37be40f7b231ee9574c4b16b7ddb7eacdc99b" # Transfer 이벤트를 조회하기 위한 파라미터 params = { "module": "account", "action": "tokennfttx", "contractaddress": token_contract_address, "address": account_address, "page": 1, "offset": number, # 요청한 이벤트 개수만큼 조회 "sort": "asc", # 최신순으로 정렬 "apikey": API_KEY } response = requests.get(API_ENDPOINT, params=params) data = response.json() print(data) # 결과에서 요청한 개수의 NFT 정보 추출 if data["status"] == "1" and data["result"]: nft_list = data["result"][:number] # 요청한 개수만큼의 결과만 사용 timeStamp = [nft["timeStamp"] for nft in nft_list] # 토큰 ID 리스트 생성 return timeStamp # 토큰 ID 리스트와 from 리스트를 반환 else: print("No recent NFT transfers found.") return [] # 두 빈 리스트를 반환 def run(): intents = discord.Intents.all() bot = commands.Bot(command_prefix="!", intents=intents) @bot.event async def on_ready(): bot.tree.copy_global_to(guild=discord.Object(id=1192532796967751790)) await bot.tree.sync(guild=discord.Object(id=1192532796967751790)) print("ready") @app_commands.autocomplete(name=name_auto) @bot.tree.command() async def me(interaction: discord.Interaction, name: str): await interaction.response.send_message(f"https://cdn.discordapp.com/emojis/455204724786855938.gif?size=96&quality=lossless", ephemeral=False) did = interaction.user.id nid = ad_srch(name) nid = str(nid) print(nid) como = como_srch(nid) como_img = como_txt(str(como)) base_img = Image.open("base_img.png") name_paste_position = (219, 295) base_img.paste(como_img, name_paste_position, como_img) obj = obj_srch(nid) como_img = como_txt(str(obj)) name_paste_position = (219, 205) base_img.paste(como_img, name_paste_position, como_img) base_img = names(name, base_img) token_ids, token_from = find_trans(nid, 3) token_name = [None] * len(token_ids) # token_ids의 길이만큼 token_name 배열 초기화 for i in range(len(token_ids)): token_name[i] = web3_find(int(token_ids[i])) token_from[0] = ad2id(token_from[0]) token_from[1] = ad2id(token_from[1]) token_from[2] = ad2id(token_from[2]) print(token_from[0]) print(token_from[1]) print(token_from[2]) base_img = names(token_from[0], base_img, 114, 459) base_img = names(token_from[1], base_img, 114, 519) base_img = names(token_from[2], base_img, 114, 580) base_img = names(token_name[0], base_img, 361, 459) base_img = names(token_name[1], base_img, 361, 519) base_img = names(token_name[2], base_img, 361, 580) timeStamp = find_first(nid) timeStamp = first_time(timeStamp[0]) print(timeStamp) base_img = names(timeStamp, base_img, 640, 66) base_img.save("img.png") #await interaction.response.send_message(f"", ephemeral=False, file=discord.File("img.png")) await interaction.edit_original_response(content="Done!") await interaction.followup.send(f"<@!{did}>", ephemeral=False, file=discord.File("img.png")) bot.run(token) if __name__ == "__main__": run()