Go Ws Module Skill logo

Go Ws Module Skill

Community
jiushiwon
go-ws-module-skill

Go Gin WebSocket 通信模块一键叠加技能。面向已使用 go-gin-init-skill 生成的项目,标准化落地 WebSocket 长连接(JWT 鉴权)、单聊消息(含文本/图片/语音)、好友关系校验、消息幂等、离线推送、未读数、会话管理、多端登录。触发词:"Go WebSocket","WS 聊天","即时通信","IM 模块","聊天模块","ws 模块","添加 WebSocket","帮我加聊天功能","go-ws-module"。

Overview

Publisherjiushiwon
Repositorywg-skills
Skill namego-ws-module-skill
Stars
123
Forks
38
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by jiushiwon on GitHub. Read the source before you install it.

Installation

Install the Go Ws Module Skill AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/jiushiwon/wg-skills/tree/main/vibeCoding/backend/go/go-ws-module-skill
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/jiushiwon/wg-skills.git /tmp/wg-skills
mkdir -p .claude/skills
cp -r /tmp/wg-skills/vibeCoding/backend/go/go-ws-module-skill .claude/skills/go-ws-module-skill
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Go Ws Module Skill in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Go Ws Module Skill on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Go Ws Module Skill is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Go Gin WebSocket Module Skill

为 Go Gin 项目叠加一套 WebSocket 即时通信能力,不是重新生成新项目。

定位

  • 目标:在已有 go-gin-init-skill 骨架上,添加可运行的 WebSocket 通信模块。
  • 不替代:不重复生成 go-gin-init-skill 已经提供的 JWT、统一响应、GORM 等基础设施。
  • 不做腾讯 IM:自建轻量方案,适合小中规模,不依赖第三方 IM 服务。
  • 输出:连接管理器、消息路由、消息存储、离线推送、会话管理、好友校验、接口契约。

骨架依赖(子模块)

本模块是 go-gin-init-skill 的子模块,必须在骨架基础上使用。

使用前必须满足以下条件:

  1. ✅ 已安装 go-gin-init-skill(项目骨架)
  2. ✅ 骨架包含:JWT、统一响应、GORM、分页、目录结构
  3. ✅ 遵循骨架的表前缀、字段命名、软删除规范

检测逻辑:

  1. 读取用户项目根目录,检查是否包含 go-gin-init-skill 的标志性文件(main.go、config/config.go、middleware/auth.go)
  2. 检查是否已有 getCurrentUser 依赖
  3. 如未检测到骨架,提示:"本模块需要先安装 go-gin-init-skill 骨架"
  4. 如用户拒绝安装骨架,则终止并提示无法使用

用户问题(最多 3 个)

1. 现有项目的包名是什么?(默认从 go-gin-init-skill 推断,如 main)
2. 表前缀是什么?(默认 wg)
3. 是否需要群聊?(默认不需要,先做单聊)

核心能力清单

#能力说明
1WebSocket 长连接WS /api/ws?token=<jwt>,query 参数鉴权,多端登录
2单聊消息文本/图片/语音实时投递,在线直推,离线入库
3好友校验仅允许已通过好友互发消息
4消息幂等客户端传 client_msg_id,网络抖动重发不重复入库
5离线消息用户上线时自动推送未送达消息(最多 100 条)
6未读数单聊未读计数,支持清零 + 实时通知对方
7会话列表最近会话排序,最后一条消息预览
8聊天记录基于 cursor 翻页的历史消息查询
9消息状态未读 → 已送达 → 已读 三态流转
10接口契约生成 api-contract-ws.md,与前端对齐

生成流程

  1. 确认已存在 Go Gin 骨架(含 JWT、统一响应、GORM)。
  2. 询问用户包名、表前缀(默认 wg)、是否需要群聊。
  3. 按 references/skeleton.go 生成 ws/ 下全部源码:
    • 将所有 {prefix} 占位符替换为用户指定的表前缀(如 wg_)
    • 将 ws 替换为用户的包名
  4. 生成 api-contract-ws.md 与 docs/ws-module-guide.md。
  5. 在 main.go 注册 WebSocket 路由。
  6. 提示用户:重启服务后可通过 WebSocket 客户端测试连接。

模块结构

{{PROJECT_NAME}}/
├── main.go                         # 注册 WebSocket 路由
├── config/
│   └── config.go                   # 配置
├── ws/
│   ├── manager.go                  # WebSocket 连接管理器(多端登录)
│   ├── handler.go                  # 消息路由与处理
│   ├── hub.go                      # 消息分发中心
│   ├── models/
│   │   ├── message.go              # 消息模型
│   │   ├── conversation.go         # 会话模型
│   │   └── friend.go               # 好友模型
│   ├── services/
│   │   ├── message_service.go      # 消息存储与查询
│   │   └── conversation_service.go # 会话管理
│   ├── routers/
│   │   ├── ws.go                   # WebSocket 端点
│   │   ├── chat.go                 # REST 接口
│   │   └── friend.go               # 好友接口
│   └── utils/
│       ├── validators.go           # 消息校验
│       └── helpers.go              # 工具函数
├── migrations/
│   └── ws_module.sql              # 数据库迁移
├── api-contract-ws.md              # 接口契约
└── docs/
    └── ws-module-guide.md          # 接入指南

接口清单

WebSocket 端点

协议路径说明鉴权
WS/api/ws?token=<jwt>WebSocket 长连接URL 参数 token

REST 接口

方法路径说明鉴权
GET/api/chat/history聊天记录(cursor 翻页)Bearer
GET/api/chat/conversations会话列表Bearer
GET/api/chat/unread未读消息总数Bearer
POST/api/chat/read标记已读Bearer
POST/api/chat/messagesREST 发消息Bearer
POST/api/friend/request发起好友申请Bearer
POST/api/friend/review通过/拒绝好友申请Bearer
GET/api/friend/list好友列表Bearer
DELETE/api/friend/:friend_id删除好友Bearer

WebSocket 消息协议

客户端 → 服务端:

json
{
  "type": "chat",
  "to": 123,
  "msg_type": 1,
  "content": "你好",
  "client_msg_id": "cli-uuid-8-64-char"
}
字段类型必填说明
typestring是消息类型:chat / ping / ack / read
toint64是(chat)接收方用户 ID
client_msg_idstring是(chat)客户端幂等键,8-64 字符
msg_typeint否1 文本(默认),2 图片,3 语音
contentstring是(msg_type=1)文本内容
image_urlstring是(msg_type=2)图片 URL
voice_urlstring是(msg_type=3)语音 URL
voice_durationint是(msg_type=3)语音时长(秒)

服务端 → 客户端:

json
{
  "type": "chat",
  "from": 456,
  "msg_id": 1001,
  "msg_type": 1,
  "content": "你好",
  "client_msg_id": "cli-uuid-8-64-char",
  "timestamp": "2026-09-09T10:00:00Z"
}

表结构

{prefix}_chat_message — 聊天消息

sql
CREATE TABLE {prefix}_chat_message (
  id              BIGSERIAL PRIMARY KEY,
  sender_id       BIGINT NOT NULL,
  receiver_id     BIGINT NOT NULL,
  group_id        BIGINT NOT NULL DEFAULT 0,
  msg_type        SMALLINT NOT NULL DEFAULT 1,
  content         TEXT NOT NULL DEFAULT '',
  image_url       VARCHAR(500) NOT NULL DEFAULT '',
  voice_url       VARCHAR(500) NOT NULL DEFAULT '',
  voice_duration  INT NOT NULL DEFAULT 0,
  client_msg_id   VARCHAR(64) NOT NULL,
  status          SMALLINT NOT NULL DEFAULT 0,
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE (client_msg_id)
);

CREATE INDEX idx_msg_conversation ON {prefix}_chat_message(sender_id, receiver_id, created_at);
CREATE INDEX idx_msg_receiver_status ON {prefix}_chat_message(receiver_id, status);

{prefix}_chat_conversation — 会话

sql
CREATE TABLE {prefix}_chat_conversation (
  id            BIGSERIAL PRIMARY KEY,
  user_id       BIGINT NOT NULL,
  peer_id       BIGINT NOT NULL,
  peer_type     SMALLINT NOT NULL DEFAULT 1,
  last_msg_id   BIGINT,
  last_msg_at   TIMESTAMPTZ,
  last_message  VARCHAR(200) NOT NULL DEFAULT '',
  last_msg_type SMALLINT NOT NULL DEFAULT 0,
  unread_count  INT NOT NULL DEFAULT 0,
  updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE(user_id, peer_id, peer_type)
);

CREATE INDEX idx_conv_user ON {prefix}_chat_conversation(user_id, last_msg_at DESC NULLS LAST);

{prefix}_user_friend — 好友关系

sql
CREATE TABLE {prefix}_user_friend (
  id          BIGSERIAL PRIMARY KEY,
  uid         BIGINT NOT NULL,
  friend_id   BIGINT NOT NULL,
  status      SMALLINT NOT NULL DEFAULT 0,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE (uid, friend_id)
);

CREATE INDEX idx_friend_uid ON {prefix}_user_friend(uid, status);

核心代码实现

WebSocket 管理器

go
// ws/hub.go
type Hub struct {
    clients    map[int64]map[*Client]bool  // userID -> clients
    register   chan *Client
    unregister chan *Client
    broadcast  chan *Message
    mutex      sync.RWMutex
}

func NewHub() *Hub {
    return &Hub{
        clients:    make(map[int64]map[*Client]bool),
        register:   make(chan *Client),
        unregister: make(chan *Client),
        broadcast:  make(chan *Message, 256),
    }
}

func (h *Hub) Register(client *Client) {
    h.mutex.Lock()
    defer h.mutex.Unlock()
    if h.clients[client.UserID] == nil {
        h.clients[client.UserID] = make(map[*Client]bool)
    }
    h.clients[client.UserID][client] = true
}

func (h *Hub) Unregister(client *Client) {
    h.mutex.Lock()
    defer h.mutex.Unlock()
    if clients, ok := h.clients[client.UserID]; ok {
        if _, ok := clients[client]; ok {
            delete(clients, client)
            close(client.Send)
            if len(clients) == 0 {
                delete(h.clients, client.UserID)
            }
        }
    }
}

func (h *Hub) SendToUser(userID int64, msg []byte) {
    h.mutex.RLock()
    defer h.mutex.RUnlock()
    if clients, ok := h.clients[userID]; ok {
        for client := range clients {
            select {
            case client.Send <- msg:
            default:
                close(client.Send)
                delete(clients, client)
            }
        }
    }
}

WebSocket 处理器

go
// ws/handler.go
type Handler struct {
    hub      *Hub
    db       *gorm.DB
    jwtKey   string
}

type Client struct {
    Hub    *Hub
    Conn   *websocket.Conn
    Send   chan []byte
    UserID int64
}

func (h *Handler) ServeWS(c *gin.Context) {
    token := c.Query("token")
    if token == "" {
        c.JSON(400, gin.H{"code": -1002, "message": "token required"})
        return
    }

    // 解析 JWT
    claims, err := utils.ParseToken(token, h.jwtKey)
    if err != nil {
        c.JSON(400, gin.H{"code": -1002, "message": "invalid token"})
        return
    }
    userID := claims.UserID

    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        log.Printf("upgrade error: %v", err)
        return
    }

    client := &Client{
        Hub:    h.hub,
        Conn:   conn,
        Send:   make(chan []byte, 256),
        UserID: userID,
    }
    h.hub.Register(client)

    go client.writePump()
    go client.readPump()
}

func (c *Client) readPump() {
    defer func() {
        c.Hub.Unregister(c)
        c.Conn.Close()
    }()

    c.Conn.SetReadLimit(512 * 1024)
    c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
    c.Conn.SetPongHandler(func(string) error {
        c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
        return nil
    })

    for {
        _, message, err := c.Conn.ReadMessage()
        if err != nil {
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
                log.Printf("error: %v", err)
            }
            break
        }
        h.handleMessage(c, message)
    }
}

模块红线

  1. WebSocket 连接必须鉴权:query 参数中的 token 必须验证,无效/过期立即关闭(code 4001)。
  2. 消息必须落库:无论对方是否在线,消息先入库再投递,保证不丢失。
  3. 好友关系是消息的前置条件:仅 status=1 的好友可以互发消息。
  4. 消息必须带 client_msg_id:8-64 字符,全局唯一;服务端按此去重。
  5. 心跳保活:客户端每 30s 发 ping,服务端回 pong;超 60s 无消息断开。
  6. 离线消息上限:单次最多推送 100 条离线消息。
  7. 消息有序:按 id 自增保证顺序。
  8. 支持多端登录:同一用户允许多个 WebSocket 连接。
  9. 契约即事实:接口字段/枚举/错误码以 api-contract-ws.md 为准。

错误码

code含义触发场景
-1001参数错误消息格式不对、必填字段缺失
-1002未授权token 无效或过期
-1003禁止访问对方不是已通过好友、给自己发消息
-2000系统错误服务端内部异常

引用索引

文件内容
references/skeleton.go完整模块代码模板
references/ws-protocol.mdWebSocket 消息协议详细规范
references/heartbeat-guide.md心跳保活与断线重连方案
references/friend-guide.md好友关系约束与集成方式

强制交付物

文档位置说明
接口契约api-contract-ws.mdWebSocket 协议 + REST 接口全量文档
接入指南docs/ws-module-guide.md表结构、连接流程、与 go-gin-init-skill 集成步骤

不做

  • 不重复生成 Go Gin 基础骨架。
  • 不实现群聊(v1 单聊闭环)。
  • 不依赖第三方 IM 服务。
  • 不做音视频通话。
  • 不在 SKILL.md 锁定版本号。
  • 不替用户提交 git。

【考拉搞AI】,带你全面进入 VibeCoding 的世界~

触发关键词

Go WebSocket、WS 聊天、即时通信、IM 模块、聊天模块、ws 模块、
添加 WebSocket、帮我加聊天功能、go-ws-module、Gin WebSocket

Frequently asked questions

What does the Go Ws Module Skill AI skill do?

Go Gin WebSocket 通信模块一键叠加技能。面向已使用 go-gin-init-skill 生成的项目,标准化落地 WebSocket 长连接(JWT 鉴权)、单聊消息(含文本/图片/语音)、好友关系校验、消息幂等、离线推送、未读数、会话管理、多端登录。触发词:"Go WebSocket","WS 聊天","即时通信","IM 模块","聊天模块","ws 模块","添加 WebSocket","帮我加聊天功能","go-ws-module"。

Why use Go Ws Module Skill on TypingMind?

Because you install it once and use it with any model. Go Ws Module Skill is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Go Ws Module Skill in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/jiushiwon/wg-skills/tree/main/vibeCoding/backend/go/go-ws-module-skill. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Go Ws Module Skill?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Go Ws Module Skill?

As many as you like. As long as a model supports skills, you can use Go Ws Module Skill with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Go Ws Module Skill AI skill free?

Yes. It is published on GitHub by jiushiwon under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇