真不知道老马怎么想的,从乌克兰到现在的xAI,总是弄些很奇怪的言行和举动。
没做到承诺的永久也就算了,但一点提前量都没有,5月中旬来宣布6/1停止使用,这真的过份了。
我们一介草尼能怎么样呢?删号走人!

delete1.jpg
delete.jpg

一直觉得Gemini非常垃圾,前些天Google放水,这不骗了一个Advance来用嘛,以为会比免费版更好点,结果用下来还是一团屎。
新闻/软文是天天看到秒天秒地秒空气,但每次用,都是各个AI里面最差的,而且是差得离谱那种。在AI时代大潮中,Google真的败了。
今天要解决docker和iptables冲突的问题,所以,问了一堆的AI怎么解决这问题。很遗憾,没有一个能解决,这点我认了。。这问题确实有难度,stack overflow上我看到鬼佬也是弄了很久最终放弃。
但是,别的AI,包括GPT,包括Claude,包括Grok,包括DeepSeek都努力想帮我解决问题,至少态度很好。唯独Gemini,不知道是触动了它的哪个高潮点,它在回答了一半之后,直接退行清除掉先前回答的内容,然后生硬的拒绝回答。 用过DeepSeek的人都知道,问它zz敏感问题就是这样。
问题是,我只是问iptables规则啊?想啥呢?

gemini.jpg

用过很多年GCP了,熟知他的永久免费机计费规则:
特定的三个区域,俄勒冈啥的
E2-MICRO
30G标准永久磁盘
标准网络层级
以前也用得好好的,一直每月0费用,直到,上个月底删除了机器然后再新开了机器。当时发现开机界面变了,我很小心的还是按照上面的规则来开机,咱老司机了,小事对吧? 很不幸,翻车了!哇哦,一个星期居然欠了4分钱。
几分钱不多不是付不起,咱不是非洲难民,但这给人挖坑让人不爽啊!
帐单看上去,应该是开机器的时候自动默认给开了一个多区域的备份啥吧,懒得研究了,我不用了,惹不起躲得起。
另外,我就欠了4分钱,一没扣到,至于就那么气势汹汹的就直接关闭帐号么?谁家不是都会通知一下给几天宽限期?gcp这么猴急干嘛。

后续,忍不住,还是研究了一下,应该是开机时操作需要多一个选择点:
数据安全->无快照

gcp3.jpg
gcp4.jpg

人活得这么风光不容易也不虚一生了。

Rivers do not drink their own waters; trees do not eat their own fruit; the sun does not shine on itself and flowers do not spread their fragrance for themselves. Living for others is a rule of nature. We are all born to help each other. No matter how difficult it is...Life is good when you are happy; but much better when others are happy because of you!

-- Pope Francis

gg.jpg

昨天突然发现了dht爬虫可以用bitmagnet自部署,欣喜若狂啊,以前有什么手撕包菜什么的,不过早就不行了。
为什么自部署?因为数据库在自己这里啊,想怎么查询怎么查询,能精准的找到自己的需要。
连夜部署好bitmagnet,然后再手搓好api和页面,不错不错。。。 因为过于那个,demo不敢放了,只放一下代码吧。
api的功能是在数据库中随机查找包含中文(不允许日文平片假名),然后包含的压缩文件大小比例不超过50%的。
后面两个要求我不解释,我只解释一下随机。。知识爆炸文件爆炸,爆炸到连关键词都想不出来了,所以,随机随机,也许有喜欢的。 🙂🙂

package main

import (
        "database/sql"
        "log"
        "net/http"
        "strings"

        "github.com/gin-gonic/gin"
        _ "github.com/lib/pq"
)

type Torrent struct {
        InfoHash string  `json:"info_hash"`
        SizeGB   float64 `json:"size_gb"`
        Name     string  `json:"name"`
}

func main() {
        r := gin.Default()

        r.GET("/api/rantorrents", handleGetTorrents)

        if err := r.Run(":8000"); err != nil {
                log.Fatal(err)
        }
}

func handleGetTorrents(c *gin.Context) {
        db, err := sql.Open("postgres", "host=127.0.0.1 port=5432 dbname=bitmagnet user=postgres password=postgres sslmode=disable")
        if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{"error": "db error"})
                return
        }
        defer db.Close()

        const query = `
                SELECT encode(info_hash, 'hex') AS info_hash, size, name
                FROM torrents
                WHERE name ~ '[\u4e00-\u9fff]'
                AND name !~ '[\u3040-\u309f]'
                AND name !~ '[\u30a0-\u30ff]'
                ORDER BY RANDOM()
                LIMIT 100;
        `

        rows, err := db.Query(query)
        if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{"error": "query fail"})
                return
        }
        defer rows.Close()

        var results []Torrent
        compressedExts := []string{".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"}

        for rows.Next() {
                var infoHash, name string
                var size int64
                if err := rows.Scan(&infoHash, &size, &name); err != nil {
                        continue
                }

                var totalFileSize int64
                var compressedSize int64

                filesQuery := `
                        SELECT path, size
                        FROM torrent_files
                        WHERE info_hash = decode($1, 'hex')
                `
                fileRows, err := db.Query(filesQuery, infoHash)
                if err != nil {
                        continue
                }

                for fileRows.Next() {
                        var path string
                        var fileSize int64
                        if err := fileRows.Scan(&path, &fileSize); err != nil {
                                continue
                        }
                        totalFileSize += fileSize
                        ext := strings.ToLower(path)
                        for _, suffix := range compressedExts {
                                if strings.HasSuffix(ext, suffix) {
                                        compressedSize += fileSize
                                        break
                                }
                        }
                }
                fileRows.Close()

                if totalFileSize == 0 || float64(compressedSize)/float64(totalFileSize) > 0.5 {
                        continue                 }

                t := Torrent{
                        InfoHash: infoHash,
                        SizeGB:   float64(size) / (1024 * 1024 * 1024),
                        Name:     name,
                }
                results = append(results, t)

                if len(results) >= 20 {
                        break
                }
        }

        c.JSON(http.StatusOK, results)
}
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>随机磁链</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      margin: 0;
      font-family: sans-serif;
      background: #f5f5f5;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    .button {
      margin: 10px;
      padding: 8px 16px;
      background: #3498db;
      color: #fff;
      border: none;
      border-radius: 6px;
      cursor: pointer;
    }

    .list {
      width: 100%;
      max-width: 800px;
      padding: 10px;
      box-sizing: border-box;
    }

    .torrent {
      background: #fff;
      margin: 6px 0;
      padding: 10px;
      border-radius: 6px;
      box-shadow: 0 1px 4px rgba(0,0,0,0.1);
    }

    .name {
      font-size: 15px;
      cursor: pointer;
      word-break: break-word;
    }

    .copied {
      font-size: 12px;
      color: green;
      margin-left: 10px;
      display: none;
    }

    @media (max-width: 600px) {
      .name {
        font-size: 14px;
      }
    }
  </style>
</head>
<body>

  <button class="button" onclick="loadTorrents(false)">🔄 刷新</button>

  <div class="list" id="list"></div>

  <script>
    let loading = false;

    async function loadTorrents(append = true) {
      if (loading) return;
      loading = true;

      try {
        const res = await fetch('/api/rantorrents');
        const data = await res.json();

        console.log('收到的数据:', data);  // 调试:输出数据检查

        const list = document.getElementById("list");
        if (!append) list.innerHTML = "";

        if (data.length === 0) {
          console.log('没有数据!');
          return;
        }

        data.forEach(item => {
          const div = document.createElement("div");
          div.className = "torrent";

          const name = document.createElement("div");
          name.className = "name";

          // 修复size字段,确保是有效的数字,并且以GB为单位显示
          const sizeInGB = item.size_gb ? item.size_gb.toFixed(2) : "0.00";
          name.textContent = `[${sizeInGB} GB] ${item.name}`;

          const copied = document.createElement("span");
          copied.className = "copied";
          copied.textContent = "✔ 已复制";

          const magnet = `magnet:?xt=urn:btih:${item.info_hash}`;

          name.onclick = async () => {
            try {
              await navigator.clipboard.writeText(magnet);
              copied.style.display = "inline";
              setTimeout(() => (copied.style.display = "none"), 1500);
            } catch (err) {
              alert("复制失败:" + err);
            }
          };

          div.appendChild(name);
          div.appendChild(copied);
          list.appendChild(div);
        });

        // 限制最多保留 100 条
        while (list.children.length > 100) {
          list.removeChild(list.firstChild);
        }

      } catch (err) {
        console.error("加载失败:", err);
      }

      loading = false;
    }

    // 初始加载
    loadTorrents();

    // 无限滚动:接近底部时加载更多
    window.addEventListener("scroll", () => {
      if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 200)) {
        loadTorrents(true);
      }
    });
  </script>
</body>
</html>

bt.jpg