最后再更新一版,因为原始版有个别地方存在缺陷,我怕谁受伤了来砍我。

这一版,主要是解决了原始代码中的一些问题和不足,以前算毛胚,现在算简装吧。 我希望大家不要直接使用这代码而只是借鉴,所以,相应的yaml我就不发了。具体改了些什么以及原因我也不说明了。算是给开发者的福利。

没有版权,随便使用。但尽量请提供原始出处。我这blog还是需要多点外链。

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"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"`
	} `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

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
	}
	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" {
					if config.Server.RequiredDatacenter == "" || contains(strings.Split(config.Server.RequiredDatacenter, ","), datacenter) {
						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)
		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!")
	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 contains(slice []string, item string) bool {
	normalizedItem := strings.ToLower(strings.TrimSpace(item))
	for _, v := range slice {
		if strings.ToLower(strings.TrimSpace(v)) == normalizedItem {
			return true
		}
	}
	return false
}

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()
		time.Sleep(time.Duration(config.App.Interval) * time.Second)
	}
}

本来不想发,不过因为let上一个鬼佬发了流程,那就这样吧
大家都科学,最终结果是大家都得不到了

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/ovh/go-ovh/ovh"
)

const (
	appKey      = "ovh appkey"                 
	appSecret   = "ovh appsecret" 
	consumerKey = "ovh consumerkey" 
	region      = "ovh-eu"                          
	tgtoken     = "telegrambot token"
	tgchatid    = "telegram chatid"
	iam         = "go-ovh-ie"
	zone        = "IE"
)

func runTask() {

	client, err := ovh.NewClient(region, appKey, appSecret, consumerKey)
	if err != nil {
		log.Printf("Failed to create OVH client: %v\n", err)
		return
	}

	var result []map[string]interface{}
	err = client.Get("/dedicated/server/datacenter/availabilities", &result)
	if err != nil {
		log.Printf("Failed to get datacenter availabilities: %v\n", err)
		return
	}

	foundAvailable := false
	var fqn, planCode, datacenter string

	for _, item := range result {
		if item["planCode"] == "25skleb01" {
			fqn = item["fqn"].(string)
			planCode = item["planCode"].(string)
			datacenters := item["datacenters"].([]interface{})

			for _, dcInfo := range datacenters {
				dc := dcInfo.(map[string]interface{})
				availability := dc["availability"].(string)
				datacenter = dc["datacenter"].(string)

				fmt.Printf("FQN: %s\n", fqn)
				fmt.Printf("Availability: %s\n", availability)
				fmt.Printf("Datacenter: %s\n", datacenter)
				fmt.Println("------------------------")

				if availability != "unavailable" {
					foundAvailable = true
					break
				}
			}

			if foundAvailable {
				fmt.Printf("Proceeding to next step with FQN: %s Datacenter: %s\n", fqn, datacenter)
				break
			}
		}
	}

	if !foundAvailable {
		log.Println("No record to buy")
		return
	}

	msg := fmt.Sprintf("%s: found ks-le-b available at %s", iam, datacenter)
	sendTelegramMsg(tgtoken, tgchatid, msg)

	fmt.Println("Create cart")
	var cartResult map[string]interface{}
	err = client.Post("/order/cart", map[string]interface{}{
		"ovhSubsidiary": zone,
	}, &cartResult)
	if err != nil {
		log.Printf("Failed to create cart: %v\n", err)
		return
	}

	cartID := cartResult["cartId"].(string)
	fmt.Printf("Cart ID: %s\n", cartID)

	fmt.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
	}

	fmt.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
	}

	fmt.Printf("Item ID: %s\n", itemID)

	fmt.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 _, config := range configurations {
		fmt.Printf("Configure %s\n", config["label"])
		err = client.Post("/order/cart/"+cartID+"/item/"+itemID+"/configuration", map[string]interface{}{
			"label": config["label"],
			"value": config["value"],
		}, nil)
		if err != nil {
			log.Printf("Failed to configure %s: %v\n", config["label"], err)
			return
		}
	}

	fmt.Println("Add options")
	options := []string{
		"bandwidth-300-25skle",
		"ram-32g-ecc-2400-25skle",
		"softraid-2x450nvme-25skle",
	}

	itemIDInt, _ := strconv.Atoi(itemID)
	for _, option := range 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
		}
	}

	fmt.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": true,
		"waiveRetractationPeriod":           true,
	}, nil)
	if err != nil {
		log.Printf("Failed to checkout: %v\n", err)
		return
	}
	log.Println("Bingo!")
	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() {
	for {
		runTask()
		time.Sleep(20 * time.Second)
	}
}

心心念念的KS-A抢到了,当然,必须是要用全自动的脚本,人手是没可能性的。
现在就等开机了。
如果能开机,我慢慢来公布脚本。不开机那我得继续自己抢先。

p1.jpg
p2.jpg

补货监控脚本挂了几个月一直没动静,今天下午脚本突然在tg上吼有货了,我当时正在烧饭。
犹豫片刻,关了火,上楼,打开电脑,果然。
下一步下一步然后check out,可恶,没货的红色错误蹦出来了。
就差这1秒钟,拍断大腿啊。
后悔了,我以前的监控是一分钟检查一次,要是频率快一点今天就成功了。
哎,改成了15秒,继续蹲吧,不知道下一次是什么时候了。大概黑五有点希望。

这个blog最初就是拿来试手做的, 以前的贴文我说过这个,就是想看看正经做一个站而不是采集垃圾站会怎么样。中间有过几次波折,比如域名选取不当而被迫中途改域名。现在时间够久了,可以回顾性发言了。

首先blog的内容,从搜索引擎角度来说应该都是优质内容,因为是全部原创的,而且文字多,关键字集中,内容角度应该算一个非常优质的网站。不过外链比较少,毕竟这年头想不花钱不大会有人给你外链。 近大半年下来,蜘蛛仍然只保持非常低的爬取频率,说明一件事,外链的重要性还是超过了内容本身的。
另外一个影响因素是,页面过少。页面过少的两大问题,一是搜索引擎觉得你的站点内容少价值不大,另外就是,就算给高权重也不会有多大流量。

以上两点决定了,这个站注定是失败的。

下面贴一下google/bing的统计图。
先说结论:
一、google的流量相对bing少了很多。这点符合预期,因为墙的原因,google价值远低于Bing。
二、bing上有展示数和点击数,可以看出,展示与点击的比率是非常高的,表明内容确实是优质受欢迎的。在webmaster工具里也看以看到,好几个关键词都是排第一,大多数关键词都是排前3,然而如此少的点击和展示表明两件事,一是关键词本身就没人有兴趣,因为这个站主要是主机内容偏多过于小众,另外就是,页面实在太少。

问题已经是很清楚明确的,不过也是无法解决的,要想优质就不可能有大量内容,想要大量内容就只能是垃圾采集。
一句话,做站已死,有事烧纸。

google.jpg

bing.jpg