nftmashin/main.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")
}
}