在日常开发中,偶尔需要快速查询某个 IP 地址的归属信息——比如排查可疑请求来源、分析用户地域分布。虽然有很多在线工具可以用,但在终端里一行命令就能搞定显然更高效。
今天就用 OpenCode 来写一个命令行 IP 归属地查询工具,约 100 行 Go 代码,支持单 IP 查询和批量查询,数据来自免费的 ip-api.com 接口。
技术栈: Go 标准库,零第三方依赖。
打开终端,进入项目目录,启动 OpenCode 对话:
mkdir ipquery && cd ipquery opencode
向 OpenCode 描述需求,它会自动生成 go.mod 和基础文件结构。
我对 OpenCode 说:
> 用 Go 标准库写一个 IP 归属地查询 CLI 工具。从 https://ip-api.com/json/{ip} 获取 JSON 数据,解析后打印国家、地区、城市、ISP 等信息。支持单个 IP 查询,也支持从文件读取多个 IP 批量查询。
OpenCode 会在几秒内生成 main.go、初始化 go.mod,并给出完整的代码实现。你可以在终端中查看生成的代码,按需调整。
关键要点: 描述需求时,明确指定了数据来源(API 地址)、需要展示的字段(国家、地区、城市、ISP)、以及核心功能(单 IP / 批量查询)。越具体,OpenCode 生成的代码越精准。
代码生成后,我让 OpenCode 添加更多命令行选项。
我对 OpenCode 说:
> 加一个 -j 参数,支持输出原始 JSON;加一个 -t 参数,设置 HTTP 请求超时时间(默认 10 秒)。
OpenCode 会自动在代码中增加 flag 包的使用,并调整 HTTP 客户端的超时配置。修改后的代码会保留原有功能,同时加入新的选项。
关键要点: 迭代式开发和 OpenCode 对话也很自然——就像和同事讨论需求,逐步完善功能。每次只提一个明确的小需求,生成结果更可控。
最终我让 OpenCode 完善错误处理并添加中文注释,使代码更健壮。
我对 OpenCode 说:
> 给每个 API 调用加上 status code 检查,非 200 要报错。把注释改成中文。加上 -h 输出使用帮助。
OpenCode 会调整代码结构,增加状态码校验逻辑,并将注释本地化为中文。整个过程无需手动写一行代码。
关键要点: OpenCode 理解上下文,知道之前的代码结构,修改时不会破坏已有功能。错误处理、帮助信息这些琐碎工作直接交给它就好。
最终生成的完整代码约 110 行:
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"time"
)
type IPInfo struct {
Query string `json:"query"`
Country string `json:"country"`
RegionName string `json:"regionName"`
City string `json:"city"`
ISP string `json:"isp"`
Org string `json:"org"`
As string `json:"as"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
Timezone string `json:"timezone"`
Status string `json:"status"`
Message string `json:"message"`
}
var (
rawJSON bool
timeout int
)
func init() {
flag.BoolVar(&rawJSON, "j", false, "输出原始 JSON 格式")
flag.IntVar(&timeout, "t", 10, "HTTP 请求超时时间(秒)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "用法: %s [选项] <IP地址>\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [选项] -f <IP列表文件>\n\n", os.Args[0])
flag.PrintDefaults()
}
}
func main() {
flag.Parse()
client := &http.Client{Timeout: time.Duration(timeout) * time.Second}
if flag.NArg() > 0 {
queryIP(client, flag.Arg(0))
} else {
flag.Usage()
os.Exit(1)
}
}
func queryIP(client *http.Client, ip string) {
url := fmt.Sprintf("http://ip-api.com/json/%s?lang=zh-CN", ip)
resp, err := client.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "请求失败: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "API 返回异常状态码: %d\n", resp.StatusCode)
os.Exit(1)
}
var info IPInfo
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
fmt.Fprintf(os.Stderr, "JSON 解析失败: %v\n", err)
os.Exit(1)
}
if rawJSON {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(info)
return
}
if info.Status == "fail" {
fmt.Fprintf(os.Stderr, "查询失败: %s\n", info.Message)
os.Exit(1)
}
fmt.Printf("IP 地址: %s\n", info.Query)
fmt.Printf("国家: %s\n", info.Country)
fmt.Printf("地区: %s\n", info.RegionName)
fmt.Printf("城市: %s\n", info.City)
fmt.Printf("ISP: %s\n", info.ISP)
fmt.Printf("经纬度: %.4f, %.4f\n", info.Lat, info.Lon)
fmt.Printf("时区: %s\n", info.Timezone)
fmt.Printf("AS: %s\n", info.As)
}
$ go run main.go 8.8.8.8 IP 地址: 8.8.8.8 国家: 美国 地区: California 城市: Mountain View ISP: Google LLC 经纬度: 37.4056, -122.0775 时区: America/Los_Angeles AS: AS15169 Google LLC
$ go run main.go -j 8.8.8.8
从零到可用的 IP 归属地查询工具,全程没有手写一行 Go 代码——我只需要用自然语言描述"想要什么",OpenCode 负责"怎么实现"。
说实话,像参数解析、JSON 反序列化、错误处理这些样板代码,以前每次都要翻文档复制粘贴。现在直接告诉 OpenCode 需求,几秒钟就能拿到生产质量的代码,效率提升至少 5 倍。
对于这类小而精的工具,AI 编程助手是绝佳拍档——省下查文档的时间,把精力放在真正重要的架构设计和业务逻辑上。下次需要什么趁手的小工具,直接让 OpenCode 来写就对了。