commit
3b2b97f860
@ -0,0 +1,14 @@
|
|||||||
|
.idea
|
||||||
|
go.sum
|
||||||
|
main.exe
|
||||||
|
server.exe
|
||||||
|
log.txt
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
uploads
|
||||||
|
gin.log
|
||||||
|
cert
|
||||||
|
challenge
|
||||||
|
.env
|
||||||
|
application.yaml
|
||||||
|
nogit_test.go
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
################################################################################
|
||||||
|
## MAIN STAGE ##
|
||||||
|
################################################################################
|
||||||
|
# Copy the manager into the distroless image.
|
||||||
|
#FROM scratch
|
||||||
|
#FROM hub.sre.ink/base/distroless-static:nonroot-20210710
|
||||||
|
#FROM mirror.ccs.tencentyun.com/library/alpine:3.13
|
||||||
|
FROM alpine:3.15.0
|
||||||
|
#FROM hub.sre.ink/base/alpine:3.15.0
|
||||||
|
#FROM centos:7.9.2009
|
||||||
|
LABEL Description="sre wechat reciver gateway"
|
||||||
|
MAINTAINER sre <sre@yangqiao.org>
|
||||||
|
#RUN echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/main' > /etc/apk/repositories \
|
||||||
|
# && echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/community' >>/etc/apk/repositories \
|
||||||
|
# && apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||||
|
# && echo "Asia/Shanghai" > /etc/timezone
|
||||||
|
RUN apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||||
|
&& echo "Asia/Shanghai" > /etc/timezone
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY wechat.bin /app/wechat.bin
|
||||||
|
COPY application.yaml /app/application.yaml
|
||||||
|
ADD kubectl /usr/bin/kubectl
|
||||||
|
RUN mkdir -p /root/.kube
|
||||||
|
ADD config /root/.kube/config
|
||||||
|
RUN chmod -R 777 /app
|
||||||
|
|
||||||
|
#USER nonroot:nonroot
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["/app/wechat.bin"]
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
wechat:
|
||||||
|
CorpId: ww24112334343434
|
||||||
|
AppToken: xPh343434343434343434
|
||||||
|
EncodingAesKey: SJmT3434343434343434343434
|
||||||
|
SendSecret: Jk343434444444444444444343434
|
||||||
|
SendAid: 1000002
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
var CMDList = []string{
|
||||||
|
"kubectl ",
|
||||||
|
"df -h",
|
||||||
|
"free -m",
|
||||||
|
"history",
|
||||||
|
"last",
|
||||||
|
}
|
||||||
@ -0,0 +1,224 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
var EmojMap = map[string]string{
|
||||||
|
"/::-|": "汗",
|
||||||
|
"/:pig": "猪头",
|
||||||
|
"/:cake": "蛋糕",
|
||||||
|
"/::!": "恐慌",
|
||||||
|
"[大兵]": "大兵",
|
||||||
|
"/:showlove": "吻",
|
||||||
|
"/::Z": "睡",
|
||||||
|
"/::Q": "抓狂",
|
||||||
|
"/:--b": "流汗",
|
||||||
|
"/:B-)": "坏笑",
|
||||||
|
"/:X-)": "奸笑",
|
||||||
|
"/:fade": "花谢了",
|
||||||
|
"[爆竹]": "爆竹",
|
||||||
|
"/:sun": "太阳",
|
||||||
|
"/::P": "调皮",
|
||||||
|
"[Sigh]": "叹气",
|
||||||
|
"/::@": "发怒",
|
||||||
|
"[Awesome]": "666",
|
||||||
|
"/::T": "呕吐",
|
||||||
|
"/:P-(": "委屈",
|
||||||
|
"/:8*": "可怜",
|
||||||
|
"[Hey]": "打招呼",
|
||||||
|
"[Concerned]": "关心",
|
||||||
|
"[Flushed]": "圆眼睛",
|
||||||
|
"[Facepalm]": "绷不住",
|
||||||
|
"[Smart]": "机灵",
|
||||||
|
"/:heart": "爱心",
|
||||||
|
"/:>-|": "鄙视",
|
||||||
|
"/:cake/:gift": "生日礼物",
|
||||||
|
"/::-S": "嚷嚷",
|
||||||
|
"/::d": "目瞪口呆",
|
||||||
|
"/:,@o": "咦",
|
||||||
|
"/:rose": "玫瑰",
|
||||||
|
"/::(": "难过",
|
||||||
|
"[Onlooker]": "吃瓜",
|
||||||
|
"/:hug": "抱抱",
|
||||||
|
"/:dig": "抠鼻",
|
||||||
|
"/:,@!": "衰",
|
||||||
|
"/::O": "惊讶",
|
||||||
|
"[Hurt]": "受伤",
|
||||||
|
"/:?": "疑惑",
|
||||||
|
"/:!!!": "骷髅",
|
||||||
|
"[Sick]": "口罩",
|
||||||
|
"[Blessing]": "福气",
|
||||||
|
"/:coffee": "咖啡",
|
||||||
|
"/::|": "呆住",
|
||||||
|
"/:handclap": "故障",
|
||||||
|
"/::'|": "快哭了",
|
||||||
|
"[LetMeSee]": "我看看",
|
||||||
|
"[微笑]": "微笑",
|
||||||
|
"[Yeah!]": "茄子",
|
||||||
|
"/:@>": "右哼哼",
|
||||||
|
"/:@@": "拳头",
|
||||||
|
"[Party]": "祝贺",
|
||||||
|
"/:share": "握手",
|
||||||
|
"/:wipe": "擦汗",
|
||||||
|
"/:,@P": "抿嘴笑",
|
||||||
|
"/:|-)": "熊猫眼",
|
||||||
|
"[Doge]": "狗头",
|
||||||
|
"/::<": "泪奔",
|
||||||
|
"[Broken]": "裂开",
|
||||||
|
"[Fireworks]": "烟花",
|
||||||
|
"[NoProb]": "妥妥的",
|
||||||
|
"/:strong/:beer": "牛皮",
|
||||||
|
"[Duh]": "没精神",
|
||||||
|
"[Emm]": "嗯啊",
|
||||||
|
"/:jj": "勾引",
|
||||||
|
"/:moon": "月亮",
|
||||||
|
"[Lol]": "喜极而泣",
|
||||||
|
"/:jump": "跳一跳",
|
||||||
|
"/::~": "撇嘴",
|
||||||
|
"/:weak": "弱鸡",
|
||||||
|
"/:,@@": "困",
|
||||||
|
"/::>": "憨笑",
|
||||||
|
"[Happy]": "眯眼笑",
|
||||||
|
"/:shit": "便便",
|
||||||
|
"[Packet]": "红包",
|
||||||
|
"[MyBad]": "掌掴",
|
||||||
|
"[Boring]": "无聊",
|
||||||
|
"/::D": "裂嘴笑",
|
||||||
|
"[Respect]": "佩服",
|
||||||
|
"/:pd": "菜刀",
|
||||||
|
"/:xx": "敲打",
|
||||||
|
"/:@)": "承让",
|
||||||
|
"/::B": "色",
|
||||||
|
"/:8-)": "酷",
|
||||||
|
"/::$": "害羞",
|
||||||
|
"[Wow]": "哇偶",
|
||||||
|
"/:break": "心碎",
|
||||||
|
"/:v": "胜利",
|
||||||
|
"/::'(": "大哭",
|
||||||
|
"/:circle": "转圈圈",
|
||||||
|
"[Smirk]": "斜眼笑",
|
||||||
|
"/:shake": "瑟瑟发抖",
|
||||||
|
"[OMG]": "老天爷",
|
||||||
|
"/:ok": "ok",
|
||||||
|
"[Rich]": "发财",
|
||||||
|
"/:strong": "赞",
|
||||||
|
"[Sweats]": "汗水",
|
||||||
|
"/:,@x": "嘘",
|
||||||
|
"/:bye": "再见",
|
||||||
|
"[GoForIt]": "加油啊",
|
||||||
|
"/:gift": "礼物",
|
||||||
|
"/:,@-D": "害羞笑",
|
||||||
|
"[Worship]": "祈祷",
|
||||||
|
"/::X": "住口",
|
||||||
|
"/::*": "亲亲",
|
||||||
|
"[LetDown]": "无精打采",
|
||||||
|
}
|
||||||
|
var EmojMapReverse = map[string]string{
|
||||||
|
"抠鼻": "/:dig",
|
||||||
|
"斜眼笑": "[Smirk]",
|
||||||
|
"衰": "/:,@!",
|
||||||
|
"鄙视": "/:>-|",
|
||||||
|
"狗头": "[Doge]",
|
||||||
|
"猪头": "/:pig",
|
||||||
|
"便便": "/:shit",
|
||||||
|
"裂嘴笑": "/::D",
|
||||||
|
"裂开": "[Broken]",
|
||||||
|
"我看看": "[LetMeSee]",
|
||||||
|
"胜利": "/:v",
|
||||||
|
"红包": "[Packet]",
|
||||||
|
"太阳": "/:sun",
|
||||||
|
"月亮": "/:moon",
|
||||||
|
"扇耳光": "[MyBad]",
|
||||||
|
"握手": "/:share",
|
||||||
|
"赞": "/:strong",
|
||||||
|
"牛皮": "/:strong/:beer",
|
||||||
|
"喜极而泣": "[Lol]",
|
||||||
|
"困": "/:,@@",
|
||||||
|
"大哭": "/::'(",
|
||||||
|
"睡": "/::Z",
|
||||||
|
"呕吐": "/::T",
|
||||||
|
"茄子": "[Yeah!]",
|
||||||
|
"承让": "/:@)",
|
||||||
|
"蛋糕": "/:cake",
|
||||||
|
"咖啡": "/:coffee",
|
||||||
|
"菜刀": "/:pd",
|
||||||
|
"礼物": "/:gift",
|
||||||
|
"生日礼物": "/:cake",
|
||||||
|
"微笑": "[微笑]",
|
||||||
|
"撇嘴": "/::~",
|
||||||
|
"色": "/::B",
|
||||||
|
"呆住": "/::|",
|
||||||
|
"酷": "/:8-)",
|
||||||
|
"泪奔": "/::<",
|
||||||
|
"害羞": "/::$",
|
||||||
|
"住口": "/::X",
|
||||||
|
"汗": "/::-|",
|
||||||
|
"发怒": "/::@",
|
||||||
|
"调皮": "/::P",
|
||||||
|
"惊讶": "/::O",
|
||||||
|
"难过": "/::(",
|
||||||
|
"流汗": "/:--b",
|
||||||
|
"抓狂": "/::Q",
|
||||||
|
"抿嘴笑": "/:,@P",
|
||||||
|
"害羞笑": "/:,@-D",
|
||||||
|
"目瞪口呆": "/::d",
|
||||||
|
"咦": "/:,@o",
|
||||||
|
"熊猫眼": "/:|-)",
|
||||||
|
"恐慌": "/::!",
|
||||||
|
"憨笑": "/::>",
|
||||||
|
"大兵": "[大兵]",
|
||||||
|
"嚷嚷": "/::-S",
|
||||||
|
"疑惑": "/:?",
|
||||||
|
"嘘": "/:,@x",
|
||||||
|
"目眩": "/:,@@",
|
||||||
|
"骷髅": "/:!!!",
|
||||||
|
"敲打": "/:xx",
|
||||||
|
"再见": "/:bye",
|
||||||
|
"擦汗": "/:wipe",
|
||||||
|
"故障": "/:handclap",
|
||||||
|
"坏笑": "/:B-)",
|
||||||
|
"右哼哼": "/:@>",
|
||||||
|
"委屈": "/:P-(",
|
||||||
|
"快哭了": "/::'|",
|
||||||
|
"奸笑": "/:X-)",
|
||||||
|
"亲亲": "/::*",
|
||||||
|
"可怜": "/:8*",
|
||||||
|
"眯眼笑": "[Happy]",
|
||||||
|
"口罩": "[Sick]",
|
||||||
|
"圆眼睛": "[Flushed]",
|
||||||
|
"无精打采": "[LetDown]",
|
||||||
|
"没精神": "[Duh]",
|
||||||
|
"打招呼": "[Hey]",
|
||||||
|
"绷不住": "[Facepalm]",
|
||||||
|
"机灵": "[Smart]",
|
||||||
|
"关心": "[Concerned]",
|
||||||
|
"吃瓜": "[Onlooker]",
|
||||||
|
"加油啊": "[GoForIt]",
|
||||||
|
"汗水": "[Sweats]",
|
||||||
|
"老天爷": "[OMG]",
|
||||||
|
"嗯啊": "[Emm]",
|
||||||
|
"佩服": "[Respect]",
|
||||||
|
"妥妥的": "[NoProb]",
|
||||||
|
"掌掴": "[MyBad]",
|
||||||
|
"哇偶": "[Wow]",
|
||||||
|
"无聊": "[Boring]",
|
||||||
|
"666": "[Awesome]",
|
||||||
|
"叹气": "[Sigh]",
|
||||||
|
"受伤": "[Hurt]",
|
||||||
|
"吻": "/:showlove",
|
||||||
|
"爱心": "/:heart",
|
||||||
|
"心碎": "/:break",
|
||||||
|
"抱抱": "/:hug",
|
||||||
|
"弱鸡": "/:weak",
|
||||||
|
"勾引": "/:jj",
|
||||||
|
"拳头": "/:@@",
|
||||||
|
"ok": "/:ok",
|
||||||
|
"祈祷": "[Worship]",
|
||||||
|
"玫瑰": "/:rose",
|
||||||
|
"花谢了": "/:fade",
|
||||||
|
"祝贺": "[Party]",
|
||||||
|
"发财": "[Rich]",
|
||||||
|
"福气": "[Blessing]",
|
||||||
|
"烟花": "[Fireworks]",
|
||||||
|
"爆竹": "[爆竹]",
|
||||||
|
"跳一跳": "/:jump",
|
||||||
|
"瑟瑟发抖": "/:shake",
|
||||||
|
"转圈圈": "/:circle",
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmoj(tt *testing.T) {
|
||||||
|
for k, v := range EmojMap {
|
||||||
|
fmt.Println(fmt.Sprintf("%s:%s", v, k))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CTX = context.Background()
|
||||||
|
RedisDb *redis.Client
|
||||||
|
HistoryCmds []string
|
||||||
|
EnableReply = true
|
||||||
|
EnableAiReply = true
|
||||||
|
EnableCmdExec = true
|
||||||
|
)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
var (
|
||||||
|
WxCrypt *WXBizMsgCrypt
|
||||||
|
WechatCorpId string
|
||||||
|
WechatToken string //微信回调的token
|
||||||
|
WechatEncodingAesKey string
|
||||||
|
WechatSendSecret string
|
||||||
|
WechatSendAid string
|
||||||
|
WechatAccessToken string //主动发消息的token
|
||||||
|
)
|
||||||
@ -0,0 +1,315 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ValidateSignatureError int = -40001
|
||||||
|
ParseXmlError int = -40002
|
||||||
|
ComputeSignatureError int = -40003
|
||||||
|
IllegalAesKey int = -40004
|
||||||
|
ValidateCorpidError int = -40005
|
||||||
|
EncryptAESError int = -40006
|
||||||
|
DecryptAESError int = -40007
|
||||||
|
IllegalBuffer int = -40008
|
||||||
|
EncodeBase64Error int = -40009
|
||||||
|
DecodeBase64Error int = -40010
|
||||||
|
GenXmlError int = -40010
|
||||||
|
ParseJsonError int = -40012
|
||||||
|
GenJsonError int = -40013
|
||||||
|
IllegalProtocolType int = -40014
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProtocolType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
XmlType ProtocolType = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type CryptError struct {
|
||||||
|
ErrCode int
|
||||||
|
ErrMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCryptError(err_code int, err_msg string) *CryptError {
|
||||||
|
return &CryptError{ErrCode: err_code, ErrMsg: err_msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXBizMsg4Recv struct {
|
||||||
|
Tousername string `xml:"ToUserName"`
|
||||||
|
Encrypt string `xml:"Encrypt"`
|
||||||
|
Agentid string `xml:"AgentID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CDATA struct {
|
||||||
|
Value string `xml:",cdata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXBizMsg4Send struct {
|
||||||
|
XMLName xml.Name `xml:"xml"`
|
||||||
|
Encrypt CDATA `xml:"Encrypt"`
|
||||||
|
Signature CDATA `xml:"MsgSignature"`
|
||||||
|
Timestamp string `xml:"TimeStamp"`
|
||||||
|
Nonce CDATA `xml:"Nonce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWXBizMsg4Send(encrypt, signature, timestamp, nonce string) *WXBizMsg4Send {
|
||||||
|
return &WXBizMsg4Send{Encrypt: CDATA{Value: encrypt}, Signature: CDATA{Value: signature}, Timestamp: timestamp, Nonce: CDATA{Value: nonce}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProtocolProcessor interface {
|
||||||
|
parse(src_data []byte) (*WXBizMsg4Recv, *CryptError)
|
||||||
|
serialize(msg_send *WXBizMsg4Send) ([]byte, *CryptError)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXBizMsgCrypt struct {
|
||||||
|
token string
|
||||||
|
encoding_aeskey string
|
||||||
|
receiver_id string
|
||||||
|
protocol_processor ProtocolProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
type XmlProcessor struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *XmlProcessor) parse(src_data []byte) (*WXBizMsg4Recv, *CryptError) {
|
||||||
|
var msg4_recv WXBizMsg4Recv
|
||||||
|
err := xml.Unmarshal(src_data, &msg4_recv)
|
||||||
|
if nil != err {
|
||||||
|
return nil, NewCryptError(ParseXmlError, "xml to msg fail")
|
||||||
|
}
|
||||||
|
return &msg4_recv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *XmlProcessor) serialize(msg4_send *WXBizMsg4Send) ([]byte, *CryptError) {
|
||||||
|
xml_msg, err := xml.Marshal(msg4_send)
|
||||||
|
if nil != err {
|
||||||
|
return nil, NewCryptError(GenXmlError, err.Error())
|
||||||
|
}
|
||||||
|
return xml_msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWXBizMsgCrypt(token, encoding_aeskey, receiver_id string, protocol_type ProtocolType) *WXBizMsgCrypt {
|
||||||
|
var protocol_processor ProtocolProcessor
|
||||||
|
if protocol_type != XmlType {
|
||||||
|
panic("unsupport protocal")
|
||||||
|
} else {
|
||||||
|
protocol_processor = new(XmlProcessor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &WXBizMsgCrypt{token: token, encoding_aeskey: (encoding_aeskey + "="), receiver_id: receiver_id, protocol_processor: protocol_processor}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) randString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) pKCS7Padding(plaintext string, block_size int) []byte {
|
||||||
|
padding := block_size - (len(plaintext) % block_size)
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(plaintext)
|
||||||
|
buffer.Write(padtext)
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) pKCS7Unpadding(plaintext []byte, block_size int) ([]byte, *CryptError) {
|
||||||
|
plaintext_len := len(plaintext)
|
||||||
|
if nil == plaintext || plaintext_len == 0 {
|
||||||
|
return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding error nil or zero")
|
||||||
|
}
|
||||||
|
if plaintext_len%block_size != 0 {
|
||||||
|
return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding text not a multiple of the block size")
|
||||||
|
}
|
||||||
|
padding_len := int(plaintext[plaintext_len-1])
|
||||||
|
return plaintext[:plaintext_len-padding_len], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) cbcEncrypter(plaintext string) ([]byte, *CryptError) {
|
||||||
|
aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
|
||||||
|
if nil != err {
|
||||||
|
return nil, NewCryptError(DecodeBase64Error, err.Error())
|
||||||
|
}
|
||||||
|
const block_size = 32
|
||||||
|
pad_msg := self.pKCS7Padding(plaintext, block_size)
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(aeskey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewCryptError(EncryptAESError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := make([]byte, len(pad_msg))
|
||||||
|
iv := aeskey[:aes.BlockSize]
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
|
||||||
|
mode.CryptBlocks(ciphertext, pad_msg)
|
||||||
|
base64_msg := make([]byte, base64.StdEncoding.EncodedLen(len(ciphertext)))
|
||||||
|
base64.StdEncoding.Encode(base64_msg, ciphertext)
|
||||||
|
|
||||||
|
return base64_msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) cbcDecrypter(base64_encrypt_msg string) ([]byte, *CryptError) {
|
||||||
|
aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
|
||||||
|
if nil != err {
|
||||||
|
return nil, NewCryptError(DecodeBase64Error, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt_msg, err := base64.StdEncoding.DecodeString(base64_encrypt_msg)
|
||||||
|
if nil != err {
|
||||||
|
return nil, NewCryptError(DecodeBase64Error, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(aeskey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewCryptError(DecryptAESError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(encrypt_msg) < aes.BlockSize {
|
||||||
|
return nil, NewCryptError(DecryptAESError, "encrypt_msg size is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := aeskey[:aes.BlockSize]
|
||||||
|
|
||||||
|
if len(encrypt_msg)%aes.BlockSize != 0 {
|
||||||
|
return nil, NewCryptError(DecryptAESError, "encrypt_msg not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
|
||||||
|
mode.CryptBlocks(encrypt_msg, encrypt_msg)
|
||||||
|
|
||||||
|
return encrypt_msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) calSignature(timestamp, nonce, data string) string {
|
||||||
|
sort_arr := []string{self.token, timestamp, nonce, data}
|
||||||
|
sort.Strings(sort_arr)
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for _, value := range sort_arr {
|
||||||
|
buffer.WriteString(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
sha := sha1.New()
|
||||||
|
sha.Write(buffer.Bytes())
|
||||||
|
signature := fmt.Sprintf("%x", sha.Sum(nil))
|
||||||
|
return string(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) ParsePlainText(plaintext []byte) ([]byte, uint32, []byte, []byte, *CryptError) {
|
||||||
|
const block_size = 32
|
||||||
|
plaintext, err := self.pKCS7Unpadding(plaintext, block_size)
|
||||||
|
if nil != err {
|
||||||
|
return nil, 0, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
text_len := uint32(len(plaintext))
|
||||||
|
if text_len < 20 {
|
||||||
|
return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 1")
|
||||||
|
}
|
||||||
|
random := plaintext[:16]
|
||||||
|
msg_len := binary.BigEndian.Uint32(plaintext[16:20])
|
||||||
|
if text_len < (20 + msg_len) {
|
||||||
|
return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := plaintext[20 : 20+msg_len]
|
||||||
|
receiver_id := plaintext[20+msg_len:]
|
||||||
|
|
||||||
|
return random, msg_len, msg, receiver_id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) VerifyURL(msg_signature, timestamp, nonce, echostr string) ([]byte, *CryptError) {
|
||||||
|
signature := self.calSignature(timestamp, nonce, echostr)
|
||||||
|
|
||||||
|
if strings.Compare(signature, msg_signature) != 0 {
|
||||||
|
return nil, NewCryptError(ValidateSignatureError, "signature not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext, err := self.cbcDecrypter(echostr)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, msg, receiver_id, err := self.ParsePlainText(plaintext)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
|
||||||
|
fmt.Println(string(receiver_id), self.receiver_id, len(receiver_id), len(self.receiver_id))
|
||||||
|
return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) EncryptMsg(reply_msg, timestamp, nonce string) ([]byte, *CryptError) {
|
||||||
|
rand_str := self.randString(16)
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(rand_str)
|
||||||
|
|
||||||
|
msg_len_buf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(msg_len_buf, uint32(len(reply_msg)))
|
||||||
|
buffer.Write(msg_len_buf)
|
||||||
|
buffer.WriteString(reply_msg)
|
||||||
|
buffer.WriteString(self.receiver_id)
|
||||||
|
|
||||||
|
tmp_ciphertext, err := self.cbcEncrypter(buffer.String())
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ciphertext := string(tmp_ciphertext)
|
||||||
|
|
||||||
|
signature := self.calSignature(timestamp, nonce, ciphertext)
|
||||||
|
|
||||||
|
msg4_send := NewWXBizMsg4Send(ciphertext, signature, timestamp, nonce)
|
||||||
|
return self.protocol_processor.serialize(msg4_send)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WXBizMsgCrypt) DecryptMsg(msg_signature, timestamp, nonce string, post_data []byte) ([]byte, *CryptError) {
|
||||||
|
msg4_recv, crypt_err := self.protocol_processor.parse(post_data)
|
||||||
|
if nil != crypt_err {
|
||||||
|
return nil, crypt_err
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := self.calSignature(timestamp, nonce, msg4_recv.Encrypt)
|
||||||
|
|
||||||
|
if strings.Compare(signature, msg_signature) != 0 {
|
||||||
|
return nil, NewCryptError(ValidateSignatureError, "signature not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext, crypt_err := self.cbcDecrypter(msg4_recv.Encrypt)
|
||||||
|
if nil != crypt_err {
|
||||||
|
return nil, crypt_err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, msg, receiver_id, crypt_err := self.ParsePlainText(plaintext)
|
||||||
|
if nil != crypt_err {
|
||||||
|
return nil, crypt_err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
|
||||||
|
return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
module WechatGateWay
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.6 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||||
|
github.com/spf13/afero v1.8.2 // indirect
|
||||||
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.12.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.3.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||||
|
)
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleTencent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.RequestURI() == "/favicon.ico" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch r.Method {
|
||||||
|
case "GET":
|
||||||
|
// 微信企业号验证
|
||||||
|
log.Println("接收到验证请求")
|
||||||
|
handleVerify(w, r)
|
||||||
|
case "POST":
|
||||||
|
// 微信企业号接收到消息
|
||||||
|
log.Println("接收到消息")
|
||||||
|
handleMessage(w, r)
|
||||||
|
default:
|
||||||
|
log.Println("接收到非法请求")
|
||||||
|
fmt.Fprintln(w, "接收到非法请求")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/global"
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleMessage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
msgSignature := r.URL.Query().Get("msg_signature") //消息签名
|
||||||
|
timestamp := r.URL.Query().Get("timestamp")
|
||||||
|
nonce := r.URL.Query().Get("nonce") //随机数,由企业自行生成
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("读取Body错误", err.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
msg, err := global.WxCrypt.DecryptMsg(msgSignature, timestamp, nonce, body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("解密消息Body错误", err.ErrMsg)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
var msgContent MsgContent
|
||||||
|
err := xml.Unmarshal(msg, &msgContent)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("反序列化错误")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Println("消息验证成功:", msgContent)
|
||||||
|
if global.EnableReply {
|
||||||
|
Reply(msgContent, timestamp, nonce, w)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reply(msgContent MsgContent, timestamp, nonce string, w http.ResponseWriter) {
|
||||||
|
switch msgContent.MsgType {
|
||||||
|
case "text":
|
||||||
|
log.Println("接收到文本消息")
|
||||||
|
replyText(msgContent, timestamp, nonce, w)
|
||||||
|
case "image":
|
||||||
|
log.Println("收到图片消息")
|
||||||
|
replyImage(msgContent, timestamp, nonce, w)
|
||||||
|
return
|
||||||
|
case "voice":
|
||||||
|
log.Println("收到语音消息")
|
||||||
|
return
|
||||||
|
case "event":
|
||||||
|
log.Println("收到事件消息")
|
||||||
|
return
|
||||||
|
case "location":
|
||||||
|
log.Println("收到地理位置消息")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
package handle
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/global"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleVerify(w http.ResponseWriter, r *http.Request) {
|
||||||
|
msgSignature := r.URL.Query().Get("msg_signature")
|
||||||
|
timestamp := r.URL.Query().Get("timestamp")
|
||||||
|
nonce := r.URL.Query().Get("nonce")
|
||||||
|
echoStr := r.URL.Query().Get("echostr")
|
||||||
|
// 合法性验证
|
||||||
|
echoStrBytes, err := global.WxCrypt.VerifyURL(msgSignature, timestamp, nonce, echoStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("验证失败", err.ErrMsg)
|
||||||
|
} else {
|
||||||
|
log.Println("验证成功", string(echoStrBytes))
|
||||||
|
// 需要返回才能通过验证
|
||||||
|
_, err := w.Write(echoStrBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("返回验证结果失败", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/third_part"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func replyImage(msgContent MsgContent, timestamp, nonce string, w http.ResponseWriter) {
|
||||||
|
fmt.Println(msgContent.ToUsername)
|
||||||
|
fmt.Println(msgContent.FromUsername)
|
||||||
|
fmt.Println(msgContent.CreateTime)
|
||||||
|
fmt.Println(msgContent.MsgType)
|
||||||
|
fmt.Println(msgContent.Content)
|
||||||
|
fmt.Println(msgContent.Msgid)
|
||||||
|
fmt.Println(msgContent.Agentid)
|
||||||
|
|
||||||
|
third_part.SendPicFile(msgContent.FromUsername, "/app/data/ygg.jpg")
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
# 接收消息与事件
|
||||||
|
https://developer.work.weixin.qq.com/document/10514
|
||||||
|
|
||||||
|
微信消息加解密库
|
||||||
|
https://github.com/go-laoji/wxbizmsgcrypt
|
||||||
|
|
||||||
|
https://github.com/easychen/wecomchan/blob/main/go-wecomchan/wecomchan.go
|
||||||
|
|
||||||
|
## build arm64 on oracle k8s
|
||||||
|
```bash
|
||||||
|
cd /root
|
||||||
|
rm -rf WechatGateWay
|
||||||
|
git clone https://git.sre.ink/go/WechatGateWay.git
|
||||||
|
cd WechatGateWay
|
||||||
|
/bin/cp -arf /root/wx.yaml application.yaml
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go mod tidy
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -v -a -o wechat.bin main.go
|
||||||
|
/bin/cp -arf /usr/bin/kubectl .
|
||||||
|
/bin/cp -arf /root/.kube/config .
|
||||||
|
docker build -t sre/wechatgateway:arm64 .
|
||||||
|
kubectl rollout restart deployment -n sre wehcat-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
## ingress
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: ingress-ip2region
|
||||||
|
namespace: sre
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "nginx" # 自动签发开关
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod-http01" # 自动签发开关
|
||||||
|
spec:
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- test.com
|
||||||
|
secretName: ingress-tls-test-com # 需要修改
|
||||||
|
rules:
|
||||||
|
- host: test.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ipregion
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package third_part
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/utils"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type qingyunkeRes struct {
|
||||||
|
Result int `json:"result"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func AiChat(msg string) string {
|
||||||
|
log.Println("分析关键词:", msg)
|
||||||
|
url := "http://api.qingyunke.com/api.php?key=free&appid=0&msg=" + msg
|
||||||
|
ansByte := utils.HTTPGet(url)
|
||||||
|
if ansByte == nil {
|
||||||
|
return "没听懂"
|
||||||
|
}
|
||||||
|
var ansRes qingyunkeRes
|
||||||
|
json.Unmarshal(ansByte, &ansRes)
|
||||||
|
//{br}替换成换行
|
||||||
|
return strings.Replace(ansRes.Content, "{br}", "\n", -1)
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package third_part
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAiChat(t *testing.T) {
|
||||||
|
t.Log(AiChat("你好"))
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CMDBool(name string, arg []string) bool {
|
||||||
|
cmdExec := exec.Command(name, arg...)
|
||||||
|
log.Println(cmdExec.String())
|
||||||
|
out, err := cmdExec.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(string(out))
|
||||||
|
log.Printf("cmd.Run() failed with %s", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fmt.Printf("combined out:%s", string(out))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMDString(name string, arg []string) (string, error) {
|
||||||
|
cmdExec := exec.Command(name, arg...)
|
||||||
|
out, err := cmdExec.CombinedOutput()
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMDShellTrick(cmd string) (string, error) {
|
||||||
|
out, err := exec.Command("sh", "-c", cmd).Output()
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCMDString(t *testing.T) {
|
||||||
|
t.Log(CMDString("ping", []string{"www.baidu.com"}))
|
||||||
|
}
|
||||||
|
func TestCMDTrick(t *testing.T) {
|
||||||
|
t.Log(CMDShellTrick("ping www.baidu.com"))
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HTTPGet(url string) []byte {
|
||||||
|
method := "GET"
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest(method, url, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPGet(t *testing.T) {
|
||||||
|
fmt.Println(HTTPGet("http://www.baidu.com"))
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseJson 将json字符串解析为map
|
||||||
|
func ParseJson(jsonStr string) map[string]any {
|
||||||
|
var wecomResponse map[string]any
|
||||||
|
if string(jsonStr) != "" {
|
||||||
|
err := json.Unmarshal([]byte(string(jsonStr)), &wecomResponse)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("生成json字符串错误")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wecomResponse
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "math/rand"
|
||||||
|
|
||||||
|
func RandMapKey(m map[string]string) string {
|
||||||
|
mapKeys := make([]string, 0, len(m)) // pre-allocate exact size
|
||||||
|
for key := range m {
|
||||||
|
mapKeys = append(mapKeys, key)
|
||||||
|
}
|
||||||
|
return mapKeys[rand.Intn(len(mapKeys))]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapKey 知道值 拿第一个key
|
||||||
|
func MapKey(m map[string]string, value string) (string, bool) {
|
||||||
|
for k, v := range m {
|
||||||
|
if v == value {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/global"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRandMap(tt *testing.T) {
|
||||||
|
fmt.Println(RandMapKey(global.EmojMap))
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SliceContain(cmds []string, cmd string) bool {
|
||||||
|
for _, v := range cmds {
|
||||||
|
if strings.Contains(cmd, v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func SlicePrint(cmds []string) string {
|
||||||
|
res := ""
|
||||||
|
for _, v := range cmds {
|
||||||
|
res = res + v + "\n"
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"WechatGateWay/global"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSLiceContain(t *testing.T) {
|
||||||
|
fmt.Println(SliceContain(global.CMDList, "history"))
|
||||||
|
fmt.Println(SliceContain(global.CMDList, "history1"))
|
||||||
|
fmt.Println(SliceContain(global.CMDList, "kubectl "))
|
||||||
|
fmt.Println(SliceContain(global.CMDList, "kubectlssss "))
|
||||||
|
fmt.Println(SliceContain(global.CMDList, "kubectl get ns "))
|
||||||
|
}
|
||||||
|
func TestSlicePrint(t *testing.T) {
|
||||||
|
fmt.Println(SlicePrint(global.CMDList))
|
||||||
|
}
|
||||||
Loading…
Reference in new issue