213 lines
4.7 KiB
Go
213 lines
4.7 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"html/template"
|
||
|
"io"
|
||
|
"io/fs"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type Status struct {
|
||
|
NFTNum int
|
||
|
TotalUSDValue float64
|
||
|
}
|
||
|
|
||
|
const NFTS_DIR = "./nfts"
|
||
|
|
||
|
func getDownloadedOpenSeaAsset(id string) (downloadedOpenSeaAsset, error) {
|
||
|
asset := downloadedOpenSeaAsset{}
|
||
|
file, err := os.Open(filepath.Join(NFTS_DIR, id+".json"))
|
||
|
if err != nil {
|
||
|
return asset, err
|
||
|
}
|
||
|
err = json.NewDecoder(file).Decode(&asset)
|
||
|
if err != nil {
|
||
|
return asset, err
|
||
|
}
|
||
|
return asset, nil
|
||
|
}
|
||
|
|
||
|
func getStatus() (Status, error) {
|
||
|
status := Status{}
|
||
|
entries, err := os.ReadDir(NFTS_DIR)
|
||
|
if err != nil {
|
||
|
return status, err
|
||
|
}
|
||
|
|
||
|
for _, val := range entries {
|
||
|
info, err := val.Info()
|
||
|
if err != nil {
|
||
|
return status, err
|
||
|
}
|
||
|
if filepath.Ext(info.Name()) != ".json" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
asset, err := getDownloadedOpenSeaAsset(strings.Split(info.Name(), ".")[0])
|
||
|
if err != nil {
|
||
|
return status, err
|
||
|
}
|
||
|
|
||
|
status.NFTNum += 1
|
||
|
status.TotalUSDValue += asset.GetUSDPrice()
|
||
|
}
|
||
|
return status, nil
|
||
|
}
|
||
|
|
||
|
func getOpenSeaId(id string) string {
|
||
|
return "opensea:asset:" + id
|
||
|
}
|
||
|
|
||
|
func getRandomOpenSeaAsset() (Asset, error) {
|
||
|
assets, err := GetAssets()
|
||
|
if err != nil {
|
||
|
log.Println("Error hablandole a OpenSea:", err)
|
||
|
return Asset{}, nil
|
||
|
}
|
||
|
for _, a := range assets {
|
||
|
if len(a.ImageUrl) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
id := getOpenSeaId(a.TokenId)
|
||
|
file, err := os.Open(filepath.Join(NFTS_DIR, id))
|
||
|
defer file.Close()
|
||
|
if errors.Is(err, fs.ErrNotExist) {
|
||
|
return a, nil
|
||
|
}
|
||
|
}
|
||
|
// TODO: paginar y cachear paginación
|
||
|
return Asset{}, errors.New("No conseguí ningún asset")
|
||
|
}
|
||
|
|
||
|
type downloadedOpenSeaAsset struct {
|
||
|
Asset `json:"Asset"`
|
||
|
Transaction `json:"Transaction"`
|
||
|
}
|
||
|
|
||
|
func (d downloadedOpenSeaAsset) GetUSDPrice() float64 {
|
||
|
return d.Transaction.Value * d.Asset.LastSale.TokenUSDPrice
|
||
|
}
|
||
|
|
||
|
func downloadOpenSeaAsset(asset Asset, transaction Transaction) (string, error) {
|
||
|
id := getOpenSeaId(asset.TokenId)
|
||
|
jsonFile, err := os.Create(filepath.Join(NFTS_DIR, id+".json"))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
err = json.NewEncoder(jsonFile).Encode(downloadedOpenSeaAsset{
|
||
|
Asset: asset,
|
||
|
Transaction: transaction,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
imageRes, err := http.DefaultClient.Get(asset.ImageUrl)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
file, err := os.Create(filepath.Join(NFTS_DIR, id))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
_, err = io.Copy(file, imageRes.Body)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return id, nil
|
||
|
}
|
||
|
|
||
|
func must(err error) {
|
||
|
if err != nil {
|
||
|
log.Panicln(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
indexTmpl, err := template.ParseFiles("./index.tmpl")
|
||
|
must(err)
|
||
|
nftsTmpl, err := template.ParseFiles("./nft.tmpl")
|
||
|
must(err)
|
||
|
|
||
|
fs := http.FileServer(http.Dir(NFTS_DIR))
|
||
|
http.Handle("/static/nfts/", http.StripPrefix("/static/nfts/", fs))
|
||
|
|
||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
|
status, err := getStatus()
|
||
|
if err != nil {
|
||
|
log.Println("Error en Status:", err)
|
||
|
internalError(w, "Error consiguiendo el contador de NFTs.")
|
||
|
return
|
||
|
}
|
||
|
w.Header().Add("Content-Type", "text/html")
|
||
|
err = indexTmpl.Execute(w, status)
|
||
|
if err != nil {
|
||
|
log.Panicln("No pude escribir index", err)
|
||
|
}
|
||
|
})
|
||
|
http.HandleFunc("/copiar", func(w http.ResponseWriter, r *http.Request) {
|
||
|
asset, err := getRandomOpenSeaAsset()
|
||
|
if err != nil {
|
||
|
log.Println("Error hablandole a OpenSea:", err)
|
||
|
internalError(w, "Error hablandole a OpenSea.")
|
||
|
return
|
||
|
}
|
||
|
log.Println(asset)
|
||
|
|
||
|
transaction, err := GetTransaction(asset.LastSale.TransactionHash)
|
||
|
if err != nil {
|
||
|
log.Println("Error consiguiendo transacción de Etherscan", err)
|
||
|
internalError(w, "Error hablandole a Etherscan.")
|
||
|
return
|
||
|
}
|
||
|
log.Println(transaction)
|
||
|
|
||
|
id, err := downloadOpenSeaAsset(asset, transaction)
|
||
|
if err != nil {
|
||
|
log.Println("Error descargando OpenSea asset", err)
|
||
|
internalError(w, "Error descargando NFT.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
w.Header().Add("Location", "/nfts/"+id)
|
||
|
w.WriteHeader(http.StatusTemporaryRedirect)
|
||
|
w.Write([]byte("Redirijiendo al NFT..."))
|
||
|
})
|
||
|
http.HandleFunc("/nfts/", func(w http.ResponseWriter, r *http.Request) {
|
||
|
_, id := path.Split(r.URL.Path)
|
||
|
nft, err := getDownloadedOpenSeaAsset(id)
|
||
|
if err != nil {
|
||
|
log.Panicln("No pude conseguir NFT", err)
|
||
|
}
|
||
|
w.Header().Add("Content-Type", "text/html")
|
||
|
err = nftsTmpl.Execute(w, struct {
|
||
|
Id string
|
||
|
NFT downloadedOpenSeaAsset
|
||
|
}{
|
||
|
Id: id,
|
||
|
NFT: nft,
|
||
|
})
|
||
|
if err != nil {
|
||
|
log.Panicln("No pude escribir nft", err)
|
||
|
}
|
||
|
})
|
||
|
log.Println("Hola")
|
||
|
http.ListenAndServe(":5050", nil)
|
||
|
}
|
||
|
|
||
|
func internalError(w http.ResponseWriter, err_str string) {
|
||
|
w.WriteHeader(http.StatusInternalServerError)
|
||
|
_, err := w.Write([]byte(err_str))
|
||
|
if err != nil {
|
||
|
log.Panicln("No pude escribir el error xD")
|
||
|
}
|
||
|
}
|