You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
5.1 KiB
Go
224 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"html/template"
|
|
"io"
|
|
"io/fs"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Status struct {
|
|
NFTNum int
|
|
TotalUSDValue float64
|
|
NFTs []downloadedOpenSeaAsset
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// https://yourbasic.org/golang/shuffle-slice-array/
|
|
rand.Seed(time.Now().UnixNano())
|
|
rand.Shuffle(len(entries), func(i, j int) { entries[i], entries[j] = entries[j], entries[i] })
|
|
|
|
for _, val := range entries {
|
|
info, err := val.Info()
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
if filepath.Ext(info.Name()) != ".json" {
|
|
continue
|
|
}
|
|
|
|
id := strings.Split(info.Name(), ".")[0]
|
|
asset, err := getDownloadedOpenSeaAsset(id)
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
|
|
if len(status.NFTs) <= 20 {
|
|
status.NFTs = append(status.NFTs, asset)
|
|
}
|
|
|
|
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{}, err
|
|
}
|
|
for _, a := range assets {
|
|
if len(a.ImageUrl) == 0 {
|
|
continue
|
|
}
|
|
id := getOpenSeaId(a.Id)
|
|
file, err := os.Open(filepath.Join(NFTS_DIR, id+".json"))
|
|
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 {
|
|
Id string `json:"Id"`
|
|
Asset `json:"Asset"`
|
|
FileName string `json:"FileName"`
|
|
}
|
|
|
|
func (d downloadedOpenSeaAsset) GetUSDPrice() float64 {
|
|
return d.Asset.LastSale.TotalPrice * d.Asset.LastSale.PaymentToken.USDPrice
|
|
}
|
|
func (d downloadedOpenSeaAsset) IsVideo() bool {
|
|
return path.Ext(d.FileName) == ".mp4"
|
|
}
|
|
|
|
func downloadOpenSeaAsset(asset Asset) (string, error) {
|
|
id := getOpenSeaId(asset.Id)
|
|
|
|
u, err := url.Parse(asset.ImageUrl)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ext := path.Ext(u.Path)
|
|
filename := id + ext
|
|
|
|
imageRes, err := http.DefaultClient.Get(asset.ImageUrl)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
file, err := os.Create(filepath.Join(NFTS_DIR, filename))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = io.Copy(file, imageRes.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
jsonFile, err := os.Create(filepath.Join(NFTS_DIR, id+".json"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = json.NewEncoder(jsonFile).Encode(downloadedOpenSeaAsset{
|
|
Id: id,
|
|
Asset: asset,
|
|
FileName: filename,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func must(err error) {
|
|
if err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
indexTmpl, err := template.ParseFiles("./templates/base.tmpl", "./templates/embed-nft.tmpl", "./templates/index.tmpl")
|
|
must(err)
|
|
nftsTmpl, err := template.ParseFiles("./templates/base.tmpl", "./templates/embed-nft.tmpl", "./templates/nft.tmpl")
|
|
must(err)
|
|
|
|
http.Handle("/static/nfts/", http.StripPrefix("/static/nfts/", http.FileServer(http.Dir(NFTS_DIR))))
|
|
http.Handle("/static/assets/", http.StripPrefix("/static/assets/", http.FileServer(http.Dir("./assets"))))
|
|
|
|
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 {
|
|
internalError(w, "Error hablandole a OpenSea.")
|
|
return
|
|
}
|
|
log.Println(asset)
|
|
|
|
id, err := downloadOpenSeaAsset(asset)
|
|
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, 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")
|
|
}
|
|
}
|