OVH VPS-x系列对别的商家绝对是降维打击。然后新加坡这机器总是卖断货,放货一天然后断货一周甚至更久。
上周本来入了一个想自用,结果被人高价给收去了。我是贪财之人,只要有溢价,我就忍不住把机器卖了。。。
好吧,现在就只能继续蹲了。让AI写了个小工具来监控一下。我唯一做的有价值的事,只是告诉AI需要监视的API链接。
代码是GO的,运行方法就是 ./checkstock sgp 这样了。只是提供思路了,如果需要,你可以让AI把它改成任何语言以及形式等。

package main

import (
        "encoding/json"
        "fmt"
        "io"
        "net/http"
        "net/url"
        "os"
        "strings"
        "time"
)

const (
        telegramToken  = "Telegram机器人token"
        telegramChatID = "Telegram发送通知的会话ID"
        apiURL         = "https://api.ovh.com/1.0/vps/order/rule/datacenter?ovhSubsidiary=IE&os=Ubuntu+25.04&planCode=vps-2025-model1"
)

type Data struct {
        Datacenters []struct {
                Datacenter    string `json:"datacenter"`
                Status        string `json:"status"`
                LinuxStatus   string `json:"linuxStatus"`
                WindowsStatus string `json:"windowsStatus"`
        } `json:"datacenters"`
}

func isAvailable(s string) bool {
        return s == "available" || s == "out-of-stock-preorder-allowed"
}

func sendTelegram(msg string) {
        endpoint := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", telegramToken)
        data := url.Values{}
        data.Set("chat_id", telegramChatID)
        data.Set("text", msg)
        http.PostForm(endpoint, data)
}

func check(target string) (bool, error) {
        resp, err := http.Get(apiURL)
        if err != nil {
                return false, err
        }
        defer resp.Body.Close()

        body, err := io.ReadAll(resp.Body)
        if err != nil {
                return false, err
        }

        var data Data
        if err := json.Unmarshal(body, &data); err != nil {
                return false, err
        }

        fmt.Printf("[%s] 当前各数据中心状态:\n", time.Now().Format("2006-01-02 15:04:05"))
        for _, d := range data.Datacenters {
                fmt.Printf("  %s: status=%s, linux=%s, windows=%s\n",
                        d.Datacenter, d.Status, d.LinuxStatus, d.WindowsStatus)
        }

        for _, d := range data.Datacenters {
                if strings.EqualFold(d.Datacenter, target) {
                        return isAvailable(d.Status) && isAvailable(d.LinuxStatus), nil
                }
        }
        fmt.Printf("%s 不存在\n", target)
        return false, nil
}

func main() {
        if len(os.Args) < 2 {
                fmt.Println("Usage: checkstock <datacenter_code>")
                os.Exit(1)
        }
        target := strings.ToUpper(strings.TrimSpace(os.Args[1]))

        startMsg := fmt.Sprintf("监控启动:正在监控 %s 库存状态", target)
        fmt.Println(startMsg)
        sendTelegram(startMsg)

        for {
                ok, err := check(target)
                if err != nil {
                        fmt.Println("Error:", err)
                } else if ok {
                        msg := fmt.Sprintf("🚀 %s 有货(含预购)!", target)
                        fmt.Println(msg)
                        sendTelegram(msg)
                        os.Exit(0)
                }
                fmt.Printf("[%s] 无货,等待60秒后重试...\n----\n", time.Now().Format("2006-01-02 15:04:05"))
                time.Sleep(60 * time.Second)
        }
}

运行示例:

[2025-10-09 01:54:20] 当前各数据中心状态:
  GRA: status=available, linux=available, windows=available
  DE: status=available, linux=available, windows=available
  BHS: status=available, linux=available, windows=available
  SBG: status=out-of-stock-preorder-allowed, linux=out-of-stock-preorder-allowed, windows=out-of-stock-preorder-allowed
  WAW: status=out-of-stock-preorder-allowed, linux=out-of-stock-preorder-allowed, windows=available
  SGP: status=out-of-stock, linux=out-of-stock, windows=out-of-stock
  UK: status=out-of-stock, linux=out-of-stock, windows=out-of-stock
[2025-10-09 01:54:20] 无货,等待60秒后重试...

标签: none

已有 2 条评论

  1. admin
    历时7天,运行正常,今天成功抢到了机器。现在ALL IN BOOM在上面了。 SGP对中国直连不好对CF也不好的,不过,我因为需要架一条隧道,新加坡-香港,所以需要用到它,相比龟壳等,线路感觉更稳定。
  2. admin
    有朋友需要,重发一下ovh抢购代码。太长了,发这里方便。 ~~~ package main import ( "bytes" "encoding/json" "flag" "fmt" "log" "math/rand" "net/http" "os" "strconv" "time" "github.com/ovh/go-ovh/ovh" "gopkg.in/yaml.v2" ) /* 获得全部可用产品 curl 'https://ca.api.ovh.com/1.0/order/catalog/public/eco?ovhSubsidiary=ASIA' 搜索返回值,查找 "planCode": "25skleb01",获得options */ type Config struct { App struct { Key string `yaml:"key"` Secret string `yaml:"secret"` ConsumerKey string `yaml:"consumer_key"` Region string `yaml:"region"` Interval int `yaml:"interval"` Times int `yaml:"times"` } `yaml:"app"` Telegram struct { Token string `yaml:"token"` ChatID string `yaml:"chat_id"` } `yaml:"telegram"` Server struct { IAM string `yaml:"iam"` Zone string `yaml:"zone"` RequiredPlanCode string `yaml:"required_plan_code"` RequiredDisk string `yaml:"required_disk"` RequiredMemory string `yaml:"required_memory"` RequiredDatacenter string `yaml:"required_datacenter"` PlanName string `yaml:"plan_name"` AutoPay bool `yaml:"autopay"` Options []string `yaml:"options"` Coupon string `yaml:"coupon"` } `yaml:"server"` } var config Config var timescount int func loadConfig(configPath string) error { configFile, err := os.ReadFile(configPath) if err != nil { return fmt.Errorf("error reading config file: %v", err) } err = yaml.Unmarshal(configFile, &config) if err != nil { return fmt.Errorf("error parsing config file: %v", err) } return nil } func runTask() { client, err := ovh.NewClient( config.App.Region, config.App.Key, config.App.Secret, config.App.ConsumerKey, ) if err != nil { log.Printf("Failed to create OVH client: %v\n", err) return } // 获取数据中心可用性 var result []map[string]interface{} url := "https://eu.api.ovh.com/1.0/dedicated/server/datacenter/availabilities?planCode=" + config.Server.RequiredPlanCode if config.Server.RequiredDisk != "" { url = url + "&storage=" + config.Server.RequiredDisk } if config.Server.RequiredMemory != "" { url = url + "&memory=" + config.Server.RequiredMemory } if config.Server.RequiredDatacenter != "" { url = url + "&datacenters=" + config.Server.RequiredDatacenter } resp, err := http.Get(url) if err != nil { log.Printf("Failed to get datacenter availabilities: %v\n", err) return } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { log.Printf("Failed to decode datacenter availabilities response: %v\n", err) return } foundAvailable := false var fqn, planCode, datacenter string for _, item := range result { if item["planCode"] == config.Server.RequiredPlanCode { fqn = item["fqn"].(string) planCode = item["planCode"].(string) datacenters := item["datacenters"].([]interface{}) var availableDCs []map[string]interface{} for _, dcInfo := range datacenters { dc := dcInfo.(map[string]interface{}) availability := dc["availability"].(string) datacenter = dc["datacenter"].(string) if datacenter == "sgp" || datacenter == "syd" { //正常是不会到这里的,但为了防止成大冤种,自保一下 continue } log.Printf("FQN: %s\n", fqn) log.Printf("Availability: %s\n", availability) log.Printf("Datacenter: %s\n", datacenter) log.Println("------------------------") if availability != "unavailable" && availability!="comingSoon" { availableDCs = append(availableDCs, dc) } } if len(availableDCs) > 0 { popi := rand.Intn(len(availableDCs)) selectedDC := availableDCs[popi] datacenter = selectedDC["datacenter"].(string) availability := selectedDC["availability"].(string) foundAvailable = true log.Printf("RECROD FOUND,SELECT:\n") log.Printf("FQN: %s\n", fqn) log.Printf("Availability: %s\n", availability) log.Printf("Datacenter: %s\n", datacenter) break } } } if !foundAvailable { log.Printf("all out of stock %s\n", planCode) if timescount > 0 { log.Printf("no remain stock to buy,quit") os.Exit(0) } return } msg := fmt.Sprintf("%s: found %s available at %s", config.Server.IAM, config.Server.PlanName, datacenter) sendTelegramMsg(config.Telegram.Token, config.Telegram.ChatID, msg) // 创建购物车 log.Println("Create cart") var cartResult map[string]interface{} err = client.Post("/order/cart", map[string]interface{}{ "ovhSubsidiary": config.Server.Zone, }, &cartResult) if err != nil { log.Printf("Failed to create cart: %v\n", err) return } cartID := cartResult["cartId"].(string) log.Printf("Cart ID: %s\n", cartID) // 分配购物车 log.Println("Assign cart") err = client.Post("/order/cart/"+cartID+"/assign", nil, nil) if err != nil { log.Printf("Failed to assign cart: %v\n", err) return } // 将商品放入购物车 log.Println("Put item into cart") var itemResult map[string]interface{} err = client.Post("/order/cart/"+cartID+"/eco", map[string]interface{}{ "planCode": planCode, "pricingMode": "default", "duration": "P1M", "quantity": 1, }, &itemResult) if err != nil { log.Printf("Failed to add item to cart: %v\n", err) return } var itemID string if v, ok := itemResult["itemId"].(json.Number); ok { itemID = v.String() } else if v, ok := itemResult["itemId"].(string); ok { itemID = v } else { log.Printf("Unexpected type for itemId, expected json.Number or string, got %T\n", itemResult["itemId"]) return } log.Printf("Item ID: %s\n", itemID) // 检查所需配置 log.Println("Checking required configuration") var requiredConfig []map[string]interface{} err = client.Get("/order/cart/"+cartID+"/item/"+itemID+"/requiredConfiguration", &requiredConfig) if err != nil { log.Printf("Failed to get required configuration: %v\n", err) return } dedicatedOs := "none_64.en" var regionValue string for _, config := range requiredConfig { if config["label"] == "region" { if allowedValues, ok := config["allowedValues"].([]interface{}); ok && len(allowedValues) > 0 { regionValue = allowedValues[0].(string) } } } // 配置数据中心、操作系统和区域 configurations := []map[string]interface{}{ {"label": "dedicated_datacenter", "value": datacenter}, {"label": "dedicated_os", "value": dedicatedOs}, {"label": "region", "value": regionValue}, } for _, conf := range configurations { log.Printf("Configure %s\n", conf["label"]) err = client.Post("/order/cart/"+cartID+"/item/"+itemID+"/configuration", map[string]interface{}{ "label": conf["label"], "value": conf["value"], }, nil) if err != nil { log.Printf("Failed to configure %s: %v\n", conf["label"], err) return } } // 添加选项 log.Println("Add options") itemIDInt, _ := strconv.Atoi(itemID) for _, option := range config.Server.Options { err = client.Post("/order/cart/"+cartID+"/eco/options", map[string]interface{}{ "duration": "P1M", "itemId": itemIDInt, "planCode": option, "pricingMode": "default", "quantity": 1, }, nil) if err != nil { log.Printf("Failed to add option %s: %v\n", option, err) return } } //有优惠码 if config.Server.Coupon != "" { log.Println("Apply coupon") client.Post("/order/cart/"+cartID+"/coupon", map[string]interface{}{ "coupon": config.Server.Coupon, }, nil) //不处理返回,不管是否成功均需要正常结帐 } // 结账 log.Println("Checkout") var checkoutResult map[string]interface{} err = client.Get("/order/cart/"+cartID+"/checkout", &checkoutResult) if err != nil { log.Printf("Failed to get checkout: %v\n", err) return } err = client.Post("/order/cart/"+cartID+"/checkout", map[string]interface{}{ "autoPayWithPreferredPaymentMethod": config.Server.AutoPay, "waiveRetractationPeriod": true, }, nil) if err != nil { log.Printf("Failed to checkout: %v\n", err) return } log.Println("Bingo!") timescount++ if timescount >= config.App.Times { os.Exit(0) } } func sendTelegramMsg(botToken, chatID, message string) error { url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken) payload := map[string]string{ "chat_id": chatID, "text": message, } jsonData, err := json.Marshal(payload) if err != nil { return fmt.Errorf("error encoding JSON: %v", err) } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { return fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("received non-OK response status: %v", resp.Status) } return nil } func main() { var configPath string flag.StringVar(&configPath, "config", "", "path to config file (required)") flag.Parse() if configPath == "" { flag.Usage() log.Fatal("config file path is required") } err := loadConfig(configPath) if err != nil { log.Fatalf("Failed to load config: %v", err) } if !config.Server.AutoPay { log.Println("WARNNING: NO AUTOPAY") } if config.App.Interval == 0 { config.App.Interval = 10 } for { runTask() if timescount > 0 { time.Sleep(1 * time.Second) } else { time.Sleep(time.Duration(config.App.Interval) * time.Second) } } } ~~~

添加新评论