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