Ver Fonte

插件架构改造

jinnrry há 2 anos atrás
pai
commit
2968f9cbaa

+ 3 - 1
.gitignore

@@ -2,4 +2,6 @@
 .DS_Store
 dist
 output
-pmail.db
+pmail.db
+server/plugins
+config

+ 30 - 2
Makefile

@@ -1,7 +1,10 @@
-build: build_fe build_server package
+build: build_fe build_server telegram_push web_push wechat_push package
 
 clean:
 	rm -rf output
+	rm -rf server/hooks/telegram_push/output
+	rm -rf server/hooks/web_push/output
+	rm -rf server/hooks/wechat_push/output
 
 build_fe:
 	cd fe && yarn && yarn build
@@ -13,11 +16,36 @@ build_server:
 	cd server && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X 'main.goVersion=$(go version)' -X 'main.gitHash=$(git show -s --format=%H)' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail_mac_amd64  main.go
 	cd server && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -X 'main.goVersion=$(go version)' -X 'main.gitHash=$(git show -s --format=%H)' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail_mac_arm64  main.go
 
+telegram_push:
+	cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/telegram_push_linux_amd64  telegram_push.go
+	cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o output/telegram_push_windows_amd64.exe  telegram_push.go
+	cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/telegram_push_mac_amd64  telegram_push.go
+	cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/telegram_push_mac_arm64  telegram_push.go
+
+web_push:
+	cd server/hooks/web_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_linux_amd64  web_push.go
+	cd server/hooks/web_push && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_windows_amd64.exe  web_push.go
+	cd server/hooks/web_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_mac_amd64  web_push.go
+	cd server/hooks/web_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/web_push_mac_arm64  web_push.go
+
+wechat_push:
+	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_linux_amd64  wechat_push.go
+	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_windows_amd64.exe  wechat_push.go
+	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_mac_amd64  wechat_push.go
+	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/wechat_push_mac_arm64  wechat_push.go
+
+plugin: telegram_push wechat_push web_push
+
+
 package: clean
 	mkdir output
 	mv server/pmail* output/
-	mkdir config
+	mkdir output/config
+	mkdir output/plugin
 	cp -r server/config/dkim output/config/
 	cp -r server/config/ssl output/config/
 	cp -r server/config/config.json output/config/
+	mv server/hooks/telegram_push/output/* output/plugin
+	mv server/hooks/web_push/output/* output/plugin
+	mv server/hooks/wechat_push/output/* output/plugin
 	cp README.md output/

+ 1 - 1
server/config/config.go

@@ -41,7 +41,7 @@ type Config struct {
 //go:embed tables/*
 var tableConfig embed.FS
 
-const Version = "2.3.8"
+const Version = "2.4.0"
 
 const DBTypeMySQL = "mysql"
 const DBTypeSQLite = "sqlite"

+ 0 - 8
server/config/config.json

@@ -11,14 +11,6 @@
   "spamFilterLevel": 2,
   "httpPort": 80,
   "httpsPort": 443,
-  "weChatPushAppId": "",
-  "weChatPushSecret": "",
-  "weChatPushTemplateId": "",
-  "weChatPushUserId": "",
-  "tgChatId": "",
-  "tgBotToken": "",
-  "webPushUrl": "",
-  "webPushToken": "",
   "isInit": true,
   "httpsEnabled": 1
 }

+ 3 - 2
server/controllers/email/send.go

@@ -11,6 +11,7 @@ import (
 	"pmail/dto/parsemail"
 	"pmail/dto/response"
 	"pmail/hooks"
+	"pmail/hooks/framework"
 	"pmail/i18n"
 	"pmail/utils/async"
 	"pmail/utils/context"
@@ -134,7 +135,7 @@ func Send(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
 			continue
 		}
 		as.WaitProcess(func(hk any) {
-			hk.(hooks.EmailHook).SendBefore(ctx, e)
+			hk.(framework.EmailHook).SendBefore(ctx, e)
 		}, hook)
 	}
 	as.Wait()
@@ -180,7 +181,7 @@ func Send(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
 				continue
 			}
 			as2.WaitProcess(func(hk any) {
-				hk.(hooks.EmailHook).SendAfter(ctx, e, sendErr)
+				hk.(framework.EmailHook).SendAfter(ctx, e, sendErr)
 			}, hook)
 		}
 		as2.Wait()

+ 172 - 19
server/hooks/base.go

@@ -1,32 +1,185 @@
 package hooks
 
 import (
+	oContext "context"
+	"encoding/json"
+	"fmt"
+	log "github.com/sirupsen/logrus"
+	"io"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
 	"pmail/dto/parsemail"
-	"pmail/hooks/telegram_push"
-	"pmail/hooks/web_push"
-	"pmail/hooks/wechat_push"
+	"pmail/hooks/framework"
 	"pmail/utils/context"
+	"strings"
+	"time"
 )
 
-type EmailHook interface {
-	// SendBefore 邮件发送前的数据
-	SendBefore(ctx *context.Context, email *parsemail.Email)
-	// SendAfter 邮件发送后的数据,err是每个收信服务器的错误信息
-	SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error)
-	// ReceiveParseBefore 接收到邮件,解析之前的原始数据
-	ReceiveParseBefore(email []byte)
-	// ReceiveParseAfter 接收到邮件,解析之后的结构化数据
-	ReceiveParseAfter(email *parsemail.Email)
+// HookList
+var HookList []framework.EmailHook
+
+type HookSender struct {
+	httpc  http.Client
+	name   string
+	socket string
 }
 
-// HookList
-var HookList []EmailHook
+func (h *HookSender) SendBefore(ctx *context.Context, email *parsemail.Email) {
+
+	dto := framework.HookDTO{
+		Ctx:   ctx,
+		Email: email,
+	}
+	body, _ := json.Marshal(dto)
+
+	ret, err := h.httpc.Post("http://plugin/SendBefore", "application/json", strings.NewReader(string(body)))
+	if err != nil {
+		log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, err)
+		return
+	}
+
+	body, _ = io.ReadAll(ret.Body)
+	json.Unmarshal(body, &dto)
+
+	ctx = dto.Ctx
+	email = dto.Email
+}
+
+func (h *HookSender) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
+	dto := framework.HookDTO{
+		Ctx:    ctx,
+		Email:  email,
+		ErrMap: err,
+	}
+	body, _ := json.Marshal(dto)
+
+	ret, errL := h.httpc.Post("http://plugin/SendAfter", "application/json", strings.NewReader(string(body)))
+	if errL != nil {
+		log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, errL)
+		return
+	}
+
+	body, _ = io.ReadAll(ret.Body)
+	json.Unmarshal(body, &dto)
+
+	ctx = dto.Ctx
+	email = dto.Email
+	err = dto.ErrMap
+}
+
+func (h *HookSender) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
+	dto := framework.HookDTO{
+		Ctx:       ctx,
+		EmailByte: email,
+	}
+	body, _ := json.Marshal(dto)
+
+	ret, errL := h.httpc.Post("http://plugin/ReceiveParseBefore", "application/json", strings.NewReader(string(body)))
+	if errL != nil {
+		log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, errL)
+		return
+	}
+
+	body, _ = io.ReadAll(ret.Body)
+	json.Unmarshal(body, &dto)
+
+	ctx = dto.Ctx
+	email = dto.EmailByte
+}
 
-// Init 这里注册hook对象
+func (h *HookSender) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
+	dto := framework.HookDTO{
+		Ctx:   ctx,
+		Email: email,
+	}
+	body, _ := json.Marshal(dto)
+
+	ret, errL := h.httpc.Post("http://plugin/ReceiveParseAfter", "application/json", strings.NewReader(string(body)))
+	if errL != nil {
+		log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, errL)
+		return
+	}
+
+	body, _ = io.ReadAll(ret.Body)
+	json.Unmarshal(body, &dto)
+
+	ctx = dto.Ctx
+	email = dto.Email
+}
+
+func NewHookSender(socketPath string, name string) *HookSender {
+	httpc := http.Client{
+		Timeout: time.Second * 10,
+		Transport: &http.Transport{
+			DialContext: func(ctx oContext.Context, network, addr string) (net.Conn, error) {
+				return net.Dial("unix", socketPath)
+			},
+		},
+	}
+	return &HookSender{
+		httpc:  httpc,
+		socket: socketPath,
+		name:   name,
+	}
+}
+
+// Init 注册hook对象
 func Init() {
-	HookList = []EmailHook{
-		wechat_push.NewWechatPushHook(),
-		telegram_push.NewTelegramPushHook(),
-		web_push.NewWebPushHook(),
+
+	env := os.Environ()
+	procAttr := &os.ProcAttr{
+		Env: env,
+		Files: []*os.File{
+			os.Stdin,
+			os.Stdout,
+			os.Stderr,
+		},
 	}
+
+	root := "./plugins"
+
+	pluginNo := 1
+	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+		if info != nil && !info.IsDir() && !strings.Contains(info.Name(), ".") {
+
+			socketPath := fmt.Sprintf("%s/%d.socket", root, pluginNo)
+
+			os.Remove(socketPath)
+
+			log.Infof("[%s] Plugin Load", info.Name())
+			p, err := os.StartProcess(path, []string{
+				info.Name(),
+				fmt.Sprintf("%d.socket", pluginNo),
+			}, procAttr)
+			if err != nil {
+				panic(err)
+			}
+			fmt.Printf("[%s] Plugin Start! PID:%d", info.Name(), p.Pid)
+
+			pluginNo++
+
+			HookList = append(HookList, NewHookSender(socketPath, info.Name()))
+
+			go func() {
+				stat, err := p.Wait()
+				log.Errorf("[%s] Plugin Stop. Error:%v Stat:%v", info.Name(), err, stat.String())
+			}()
+
+			for i := 0; i < 5; i++ {
+				time.Sleep(1 * time.Second)
+				if _, err := os.Stat(socketPath); err == nil {
+					break
+				}
+				if i == 4 {
+					panic(fmt.Sprintf("[%s] Start Fail!", info.Name()))
+				}
+			}
+
+		}
+
+		return nil
+	})
+
 }

+ 122 - 0
server/hooks/framework/framework.go

@@ -0,0 +1,122 @@
+package framework
+
+import (
+	"encoding/json"
+	log "github.com/sirupsen/logrus"
+	"io"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
+	"pmail/dto/parsemail"
+	"pmail/utils/context"
+)
+
+type EmailHook interface {
+	// SendBefore 邮件发送前的数据
+	SendBefore(ctx *context.Context, email *parsemail.Email)
+	// SendAfter 邮件发送后的数据,err是每个收信服务器的错误信息
+	SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error)
+	// ReceiveParseBefore 接收到邮件,解析之前的原始数据
+	ReceiveParseBefore(ctx *context.Context, email *[]byte)
+	// ReceiveParseAfter 接收到邮件,解析之后的结构化数据
+	ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
+}
+
+type HookDTO struct {
+	Ctx       *context.Context
+	Email     *parsemail.Email
+	EmailByte *[]byte
+	ErrMap    map[string]error
+}
+
+type Plugin struct {
+	hook EmailHook
+}
+
+func CreatePlugin(hook EmailHook) *Plugin {
+	return &Plugin{
+		hook: hook,
+	}
+}
+
+func (p *Plugin) Run() {
+	if len(os.Args) < 2 {
+		panic("Command Params Error!")
+	}
+	mux := http.NewServeMux()
+
+	mux.HandleFunc("/SendBefore", func(writer http.ResponseWriter, request *http.Request) {
+		var hookDTO HookDTO
+		body, _ := io.ReadAll(request.Body)
+		err := json.Unmarshal(body, &hookDTO)
+		if err != nil {
+			log.Errorf("params error %+v", err)
+			return
+		}
+		p.hook.SendBefore(hookDTO.Ctx, hookDTO.Email)
+		body, _ = json.Marshal(hookDTO)
+		writer.Write(body)
+	})
+	mux.HandleFunc("/SendAfter", func(writer http.ResponseWriter, request *http.Request) {
+
+		var hookDTO HookDTO
+		body, _ := io.ReadAll(request.Body)
+		err := json.Unmarshal(body, &hookDTO)
+		if err != nil {
+			log.Errorf("params error %+v", err)
+			return
+		}
+		p.hook.SendAfter(hookDTO.Ctx, hookDTO.Email, hookDTO.ErrMap)
+		body, _ = json.Marshal(hookDTO)
+		writer.Write(body)
+	})
+	mux.HandleFunc("/ReceiveParseBefore", func(writer http.ResponseWriter, request *http.Request) {
+
+		var hookDTO HookDTO
+		body, _ := io.ReadAll(request.Body)
+		err := json.Unmarshal(body, &hookDTO)
+		if err != nil {
+			log.Errorf("params error %+v", err)
+			return
+		}
+		p.hook.ReceiveParseBefore(hookDTO.Ctx, hookDTO.EmailByte)
+		body, _ = json.Marshal(hookDTO)
+		writer.Write(body)
+	})
+	mux.HandleFunc("/ReceiveParseAfter", func(writer http.ResponseWriter, request *http.Request) {
+
+		var hookDTO HookDTO
+		body, _ := io.ReadAll(request.Body)
+		err := json.Unmarshal(body, &hookDTO)
+		if err != nil {
+			log.Errorf("params error %+v", err)
+			return
+		}
+		p.hook.ReceiveParseAfter(hookDTO.Ctx, hookDTO.Email)
+		body, _ = json.Marshal(hookDTO)
+		writer.Write(body)
+	})
+
+	server := http.Server{
+		Handler: mux,
+	}
+
+	unixListener, err := net.Listen("unix", getExePath()+"/"+os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	err = server.Serve(unixListener)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func getExePath() string {
+	ex, err := os.Executable()
+	if err != nil {
+		panic(err)
+	}
+	exePath := filepath.Dir(ex)
+	return exePath
+}

+ 55 - 7
server/hooks/telegram_push/telegram_push.go

@@ -1,11 +1,13 @@
-package telegram_push
+package main
 
 import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"os"
 	"pmail/config"
 	"pmail/dto/parsemail"
+	"pmail/hooks/framework"
 	"pmail/utils/context"
 	"strings"
 
@@ -27,11 +29,11 @@ func (w *TelegramPushHook) SendAfter(ctx *context.Context, email *parsemail.Emai
 
 }
 
-func (w *TelegramPushHook) ReceiveParseBefore(email []byte) {
+func (w *TelegramPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
 
 }
 
-func (w *TelegramPushHook) ReceiveParseAfter(email *parsemail.Email) {
+func (w *TelegramPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
 	if w.chatId == "" || w.botToken == "" {
 		return
 	}
@@ -84,13 +86,59 @@ func (w *TelegramPushHook) sendUserMsg(ctx *context.Context, email *parsemail.Em
 	}
 
 }
+
+type Config struct {
+	TgBotToken string `json:"tgBotToken"`
+	TgChatId   string `json:"tgChatId"`
+}
+
 func NewTelegramPushHook() *TelegramPushHook {
+	var cfgData []byte
+	var err error
+
+	cfgData, err = os.ReadFile("../config/config.json")
+	if err != nil {
+		panic(err)
+	}
+	var mainConfig *config.Config
+	err = json.Unmarshal(cfgData, &mainConfig)
+	if err != nil {
+		panic(err)
+	}
+
+	var pluginConfig *Config
+	if _, err := os.Stat("./telegram_push_config.json"); err == nil {
+		cfgData, err = os.ReadFile("./telegram_push_config.json")
+		if err != nil {
+			panic(err)
+		}
+		err = json.Unmarshal(cfgData, &pluginConfig)
+		if err != nil {
+			panic(err)
+		}
+
+	}
+
+	token := ""
+	chatID := ""
+	if pluginConfig != nil {
+		token = pluginConfig.TgBotToken
+		chatID = pluginConfig.TgChatId
+	} else {
+		token = mainConfig.TgBotToken
+		chatID = mainConfig.TgChatId
+	}
+
 	ret := &TelegramPushHook{
-		botToken:     config.Instance.TgBotToken,
-		chatId:       config.Instance.TgChatId,
-		webDomain:    config.Instance.WebDomain,
-		httpsEnabled: config.Instance.HttpsEnabled,
+		botToken:     token,
+		chatId:       chatID,
+		webDomain:    mainConfig.WebDomain,
+		httpsEnabled: mainConfig.HttpsEnabled,
 	}
 	return ret
 
 }
+
+func main() {
+	framework.CreatePlugin(NewTelegramPushHook()).Run()
+}

+ 1 - 1
server/hooks/telegram_push/telegram_push_test.go

@@ -1,4 +1,4 @@
-package telegram_push
+package main
 
 import (
 	"pmail/config"

+ 51 - 6
server/hooks/web_push/web_push.go

@@ -1,11 +1,13 @@
-package web_push
+package main
 
 import (
 	"bytes"
 	"encoding/json"
 	"net/http"
+	"os"
 	"pmail/config"
 	"pmail/dto/parsemail"
+	"pmail/hooks/framework"
 	"pmail/utils/context"
 
 	log "github.com/sirupsen/logrus"
@@ -33,11 +35,11 @@ func (w *WebPushHook) SendAfter(ctx *context.Context, email *parsemail.Email, er
 
 }
 
-func (w *WebPushHook) ReceiveParseBefore(email []byte) {
+func (w *WebPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
 
 }
 
-func (w *WebPushHook) ReceiveParseAfter(email *parsemail.Email) {
+func (w *WebPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
 	if w.url == "" {
 		return
 	}
@@ -63,7 +65,6 @@ func (w *WebPushHook) ReceiveParseAfter(email *parsemail.Email) {
 		Token:   w.token,
 	}
 
-	var ctx *context.Context = nil
 	jsonData, err := json.Marshal(data)
 
 	if err != nil {
@@ -77,12 +78,56 @@ func (w *WebPushHook) ReceiveParseAfter(email *parsemail.Email) {
 	defer resp.Body.Close()
 }
 
+type Config struct {
+	WebPushUrl   string `json:"webPushUrl"`
+	WebPushToken string `json:"webPushToken"`
+}
+
 func NewWebPushHook() *WebPushHook {
+	var cfgData []byte
+	var err error
+
+	cfgData, err = os.ReadFile("../config/config.json")
+	if err != nil {
+		panic(err)
+	}
+	var mainConfig *config.Config
+	err = json.Unmarshal(cfgData, &mainConfig)
+	if err != nil {
+		panic(err)
+	}
+
+	var pluginConfig *Config
+	if _, err := os.Stat("./web_push_config.json"); err == nil {
+		cfgData, err = os.ReadFile("./web_push_config.json")
+		if err != nil {
+			panic(err)
+		}
+		err = json.Unmarshal(cfgData, &pluginConfig)
+		if err != nil {
+			panic(err)
+		}
+
+	}
+
+	token := ""
+	pushURL := ""
+	if pluginConfig != nil {
+		pushURL = pluginConfig.WebPushUrl
+		token = pluginConfig.WebPushToken
+	} else {
+		pushURL = mainConfig.WebPushUrl
+		token = mainConfig.WebPushToken
+	}
 
 	ret := &WebPushHook{
-		url:   config.Instance.WebPushUrl,
-		token: config.Instance.WebPushToken,
+		url:   pushURL,
+		token: token,
 	}
 	return ret
 
 }
+
+func main() {
+	framework.CreatePlugin(NewWebPushHook()).Run()
+}

+ 2 - 2
server/hooks/web_push/wechat_push_test.go

@@ -1,4 +1,4 @@
-package web_push
+package main
 
 import (
 	"pmail/config"
@@ -15,5 +15,5 @@ func TestWebPushHook_ReceiveParseAfter(t *testing.T) {
 	testInit()
 
 	w := NewWebPushHook()
-	w.ReceiveParseAfter(&parsemail.Email{Subject: "标题", Text: []byte("文本内容")})
+	w.ReceiveParseAfter(nil, &parsemail.Email{Subject: "标题", Text: []byte("文本内容")})
 }

+ 67 - 9
server/hooks/wechat_push/wechat_push.go

@@ -1,4 +1,4 @@
-package wechat_push
+package main
 
 import (
 	"encoding/json"
@@ -7,8 +7,10 @@ import (
 	"github.com/spf13/cast"
 	"io"
 	"net/http"
+	"os"
 	"pmail/config"
 	"pmail/dto/parsemail"
+	"pmail/hooks/framework"
 	"pmail/utils/context"
 	"strings"
 	"time"
@@ -26,6 +28,7 @@ type WeChatPushHook struct {
 	tokenExpires int64
 	templateId   string
 	pushUser     string
+	mainConfig   *config.Config
 }
 
 func (w *WeChatPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
@@ -36,11 +39,11 @@ func (w *WeChatPushHook) SendAfter(ctx *context.Context, email *parsemail.Email,
 
 }
 
-func (w *WeChatPushHook) ReceiveParseBefore(email []byte) {
+func (w *WeChatPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
 
 }
 
-func (w *WeChatPushHook) ReceiveParseAfter(email *parsemail.Email) {
+func (w *WeChatPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
 	if w.appId == "" || w.secret == "" || w.pushUser == "" {
 		return
 	}
@@ -88,8 +91,8 @@ type DataItem struct {
 
 func (w *WeChatPushHook) sendUserMsg(ctx *context.Context, userId string, content string) {
 
-	url := config.Instance.WebDomain
-	if config.Instance.HttpsEnabled > 1 {
+	url := w.mainConfig.WebDomain
+	if w.mainConfig.HttpsEnabled > 1 {
 		url = "http://" + url
 	} else {
 		url = "https://" + url
@@ -108,14 +111,69 @@ func (w *WeChatPushHook) sendUserMsg(ctx *context.Context, userId string, conten
 	}
 
 }
+
+type Config struct {
+	WeChatPushAppId      string `json:"weChatPushAppId"`
+	WeChatPushSecret     string `json:"weChatPushSecret"`
+	WeChatPushTemplateId string `json:"weChatPushTemplateId"`
+	WeChatPushUserId     string `json:"weChatPushUserId"`
+}
+
 func NewWechatPushHook() *WeChatPushHook {
 
+	var cfgData []byte
+	var err error
+
+	cfgData, err = os.ReadFile("../config/config.json")
+	if err != nil {
+		panic(err)
+	}
+	var mainConfig *config.Config
+	err = json.Unmarshal(cfgData, &mainConfig)
+	if err != nil {
+		panic(err)
+	}
+
+	var pluginConfig *Config
+	if _, err := os.Stat("./wechat_push_config.json"); err == nil {
+		cfgData, err = os.ReadFile("./wechat_push_config.json")
+		if err != nil {
+			panic(err)
+		}
+		err = json.Unmarshal(cfgData, &pluginConfig)
+		if err != nil {
+			panic(err)
+		}
+
+	}
+
+	appid := ""
+	secret := ""
+	templateId := ""
+	userId := ""
+	if pluginConfig != nil {
+		appid = pluginConfig.WeChatPushAppId
+		secret = pluginConfig.WeChatPushSecret
+		templateId = pluginConfig.WeChatPushTemplateId
+		userId = pluginConfig.WeChatPushUserId
+	} else {
+		appid = mainConfig.WeChatPushAppId
+		secret = mainConfig.WeChatPushSecret
+		templateId = mainConfig.WeChatPushTemplateId
+		userId = mainConfig.WeChatPushUserId
+	}
+
 	ret := &WeChatPushHook{
-		appId:      config.Instance.WeChatPushAppId,
-		secret:     config.Instance.WeChatPushSecret,
-		templateId: config.Instance.WeChatPushTemplateId,
-		pushUser:   config.Instance.WeChatPushUserId,
+		appId:      appid,
+		secret:     secret,
+		templateId: templateId,
+		pushUser:   userId,
+		mainConfig: mainConfig,
 	}
 	return ret
 
 }
+
+func main() {
+	framework.CreatePlugin(NewWechatPushHook()).Run()
+}

+ 2 - 2
server/hooks/wechat_push/wechat_push_test.go

@@ -1,4 +1,4 @@
-package wechat_push
+package main
 
 import (
 	"pmail/config"
@@ -15,5 +15,5 @@ func TestWeChatPushHook_ReceiveParseAfter(t *testing.T) {
 	testInit()
 
 	w := NewWechatPushHook()
-	w.ReceiveParseAfter(&parsemail.Email{Subject: "标题", Text: []byte("文本内容")})
+	w.ReceiveParseAfter(nil, &parsemail.Email{Subject: "标题", Text: []byte("文本内容")})
 }

+ 3 - 2
server/smtp_server/read_content.go

@@ -12,6 +12,7 @@ import (
 	"pmail/db"
 	"pmail/dto/parsemail"
 	"pmail/hooks"
+	"pmail/hooks/framework"
 	"pmail/services/rule"
 	"pmail/utils/async"
 	"pmail/utils/context"
@@ -38,7 +39,7 @@ func (s *Session) Data(r io.Reader) error {
 			continue
 		}
 		as1.WaitProcess(func(hk any) {
-			hk.(hooks.EmailHook).ReceiveParseBefore(emailData)
+			hk.(framework.EmailHook).ReceiveParseBefore(ctx, &emailData)
 		}, hook)
 	}
 	as1.Wait()
@@ -94,7 +95,7 @@ func (s *Session) Data(r io.Reader) error {
 				continue
 			}
 			as2.WaitProcess(func(hk any) {
-				hk.(hooks.EmailHook).ReceiveParseAfter(email)
+				hk.(framework.EmailHook).ReceiveParseAfter(ctx, email)
 			}, hook)
 		}
 		as2.Wait()

+ 5 - 4
server/smtp_server/read_content_test.go

@@ -11,6 +11,7 @@ import (
 	"pmail/config"
 	"pmail/db"
 	parsemail2 "pmail/dto/parsemail"
+	"pmail/hooks"
 	"pmail/session"
 	"pmail/utils/context"
 	"testing"
@@ -42,7 +43,7 @@ func testInit() {
 	parsemail2.Init()
 	db.Init()
 	session.Init()
-
+	hooks.Init()
 }
 
 func TestNuisanace(t *testing.T) {
@@ -129,9 +130,9 @@ Content-Type: text/html
 	s := Session{
 		RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)),
 		Ctx: &context.Context{
-			UserID:      1,
-			UserName:    "a",
-			UserAccount: "a",
+			UserID:      0,
+			UserName:    "",
+			UserAccount: "",
 		},
 	}
 

+ 6 - 6
server/utils/context/context.go

@@ -13,21 +13,21 @@ type Context struct {
 	UserID      int
 	UserAccount string
 	UserName    string
-	values      map[string]any
+	Values      map[string]any
 	Lang        string
 }
 
 func (c *Context) SetValue(key string, value any) {
-	if c.values == nil {
-		c.values = map[string]any{}
+	if c.Values == nil {
+		c.Values = map[string]any{}
 	}
-	c.values[key] = value
+	c.Values[key] = value
 
 }
 
 func (c *Context) GetValue(key string) any {
-	if c.values == nil {
+	if c.Values == nil {
 		return nil
 	}
-	return c.values[key]
+	return c.Values[key]
 }