1、修复邮件插入失败
2、插件支持设置页面
3、修复pop3邮箱拉取权限判断
This commit is contained in:
jinnrry 2024-07-27 20:40:21 +08:00
parent 729eb9658a
commit 4a111788a6
28 changed files with 432 additions and 224 deletions

View File

@ -14,7 +14,6 @@ COPY --from=febuild /work/dist /work/server/http_server/dist
RUN apk update && apk add git
RUN cd /work/server && go build -ldflags "-s -w -X 'main.version=${VERSION}' -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 main.go
RUN cd /work/server/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
RUN cd /work/server/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
RUN cd /work/server/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
RUN cd /work/server/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
@ -32,7 +31,6 @@ RUN apk add --no-cache tzdata \
COPY --from=serverbuild /work/server/pmail .
COPY --from=serverbuild /work/server/hooks/telegram_push/output/* ./plugins/
COPY --from=serverbuild /work/server/hooks/web_push/output/* ./plugins/
COPY --from=serverbuild /work/server/hooks/wechat_push/output/* ./plugins/
COPY --from=serverbuild /work/server/hooks/spam_block/output/* ./plugins/

View File

@ -8,7 +8,6 @@ COPY server .
RUN apk update && apk add git
RUN go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(go version)' -X 'main.gitHash=${GITHASH}' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail main.go
RUN cd /work/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
RUN cd /work/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
RUN cd /work/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
RUN cd /work/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
@ -26,7 +25,6 @@ RUN apk add --no-cache tzdata \
COPY --from=serverbuild /work/pmail .
COPY --from=serverbuild /work/hooks/telegram_push/output/* ./plugins/
COPY --from=serverbuild /work/hooks/web_push/output/* ./plugins/
COPY --from=serverbuild /work/hooks/wechat_push/output/* ./plugins/
COPY --from=serverbuild /work/hooks/spam_block/output/* ./plugins/

View File

@ -21,11 +21,6 @@ telegram_push:
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
@ -41,7 +36,7 @@ spam_block:
plugin: telegram_push wechat_push web_push
plugin: telegram_push wechat_push
package: clean
@ -53,7 +48,6 @@ package: clean
cp -r server/config/ssl output/config/
cp -r server/config/config.json output/config/
mv server/hooks/telegram_push/output/* output/plugins
mv server/hooks/web_push/output/* output/plugins
mv server/hooks/wechat_push/output/* output/plugins
cp README.md output/

View File

@ -25,6 +25,11 @@
<el-tab-pane v-if="$userInfos.is_admin" :label="lang.user_management">
<UserManagement />
</el-tab-pane>
<el-tab-pane :label="lang.plugin_settings">
<PluginSettings />
</el-tab-pane>
</el-tabs>
</el-drawer>
@ -41,6 +46,7 @@ import GroupSettings from './GroupSettings.vue';
import RuleSettings from './RuleSettings.vue';
import UserManagement from './UserManagement.vue';
import { getCurrentInstance } from 'vue'
import PluginSettings from './PluginSettings.vue';
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const $isLogin = app.appContext.config.globalProperties.$isLogin

View File

@ -0,0 +1,35 @@
<template>
<div id="main">
<el-tabs>
<el-tab-pane v-for="(src, name) in pluginList" :label="name">
<iframe :src="src"></iframe>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { reactive, ref, getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const pluginList = reactive({})
$http.get('/api/plugin/list').then(res => {
if (res.data != null && res.data.length > 0) {
for (let i = 0; i < res.data.length; i++) {
let name = res.data[i];
pluginList[name] = "/api/plugin/settings/"+ name +"/index.html";
}
}
})
</script>
<style scoped>
iframe{
width: 100%;
border: 0;
}
</style>

View File

@ -7,6 +7,7 @@ var lang = {
"editUser": "Edit Account",
"user_name": "User Name",
"user_management": "user management",
"plugin_settings": "Plugin Settings",
"lang": "en",
"submit": "submit",
"compose": "compose",
@ -117,6 +118,7 @@ var zhCN = {
"newUser": "新增用户",
"editUser": "编辑用户",
"user_management": "用户管理",
"plugin_settings": "插件设置",
"lang": "zhCn",
"submit": "提交",
"compose": "发件",

View File

@ -9,7 +9,7 @@
"SSLPublicKeyPath": "./config/ssl/public.crt",
"dbDSN": "./config/pmail_temp.db",
"dbType": "sqlite",
"httpsEnabled": 1,
"httpsEnabled": 2,
"spamFilterLevel": 0,
"httpPort": 80,
"httpsPort": 443,

View File

@ -0,0 +1,46 @@
package controllers
import (
"github.com/Jinnrry/pmail/dto/response"
"github.com/Jinnrry/pmail/hooks"
"github.com/Jinnrry/pmail/utils/context"
"io"
"net/http"
"strings"
)
func GetPluginList(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
ret := []string{}
for s, _ := range hooks.HookList {
ret = append(ret, s)
}
response.NewSuccessResponse(ret).FPrint(w)
}
func SettingsHtml(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
args := strings.Split(req.RequestURI, "/")
if len(args) < 4 {
response.NewErrorResponse(response.ParamsError, "404", "").FPrint(w)
return
}
pluginName := args[4]
if plugin, ok := hooks.HookList[pluginName]; ok {
dt, err := io.ReadAll(req.Body)
if err != nil {
response.NewErrorResponse(response.ParamsError, err.Error(), "").FPrint(w)
return
}
html := plugin.SettingsHtml(ctx,
strings.Join(args[4:], "/"),
string(dt),
)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write([]byte(html))
return
}
response.NewErrorResponse(response.ParamsError, "404", "")
}

View File

@ -54,15 +54,17 @@ type Email struct {
Date string
Status int // 0未发送1已发送2发送失败3删除
MessageId int64
Size int
}
func NewEmailFromReader(to []string, r io.Reader) *Email {
func NewEmailFromReader(to []string, r io.Reader, size int) *Email {
ret := &Email{}
m, err := message.Read(r)
if err != nil {
log.Errorf("email解析错误 Error %+v", err)
}
ret.Size = size
ret.From = buildUser(m.Header.Get("From"))
if len(to) > 0 {

View File

@ -137,6 +137,47 @@ func (h *HookSender) ReceiveParseAfter(ctx *context.Context, email *parsemail.Em
}
// GetName 获取插件名称
func (h *HookSender) GetName(ctx *context.Context) string {
dto := framework.HookDTO{
Ctx: ctx,
}
body, _ := json.Marshal(dto)
ret, errL := h.httpc.Post("http://plugin/GetName", "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)
return string(body)
}
// SettingsHtml 插件页面
func (h *HookSender) SettingsHtml(ctx *context.Context, url string, requestData string) string {
dto := framework.SettingsHtmlRequest{
Ctx: ctx,
URL: url,
RequestData: requestData,
}
body, _ := json.Marshal(dto)
ret, errL := h.httpc.Post("http://plugin/SettingsHtml", "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)
return string(body)
}
func NewHookSender(socketPath string, name string, serverVersion string) *HookSender {
httpc := http.Client{
Timeout: time.Second * 10,
@ -214,8 +255,10 @@ func Init(serverVersion string) {
}
}
if loadSucc {
HookList[info.Name()] = NewHookSender(socketPath, info.Name(), serverVersion)
log.Infof("[%s] Plugin Load Success!", info.Name())
hk := NewHookSender(socketPath, info.Name(), serverVersion)
hkName := hk.GetName(&context.Context{})
HookList[hkName] = hk
log.Infof("[%s] Plugin Load Success!", hkName)
}
}

View File

@ -13,6 +13,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
@ -27,6 +28,10 @@ type EmailHook interface {
ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
// ReceiveSaveAfter 邮件落库以后执行(收信规则后执行) 异步执行
ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail)
// GetName 获取插件名称
GetName(ctx *context.Context) string
// SettingsHtml 插件页面
SettingsHtml(ctx *context.Context, url string, requestData string) string
}
// HookDTO PMail 主程序和插件通信的结构体
@ -39,6 +44,12 @@ type HookDTO struct {
UserEmail []*models.UserEmail
}
type SettingsHtmlRequest struct {
Ctx *context.Context // 上下文
URL string
RequestData string
}
type Plugin struct {
name string
hook EmailHook
@ -160,6 +171,32 @@ func (p *Plugin) Run() {
writer.Write(body)
log.Debugf("[%s] ReceiveSaveAfter End", p.name)
})
mux.HandleFunc("/GetName", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] GetName Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
name := strings.ReplaceAll(p.hook.GetName(hookDTO.Ctx), " ", "")
writer.Write([]byte(name))
log.Debugf("[%s] GetName End", p.name)
})
mux.HandleFunc("/SettingsHtml", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] SettingsHtml Start", p.name)
var hookDTO SettingsHtmlRequest
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
html := p.hook.SettingsHtml(hookDTO.Ctx, hookDTO.URL, hookDTO.RequestData)
writer.Write([]byte(html))
log.Debugf("[%s] SettingsHtml End", p.name)
})
server := http.Server{
ReadTimeout: 5 * time.Second,

View File

@ -52,16 +52,11 @@ curl -X POST http://localhost:8501/v1/models/emotion_model:predict -d '{
4、将spam_block插件移动到pmail插件目录
5、在插件位置新建配置文件`spam_block_config.json`内容类似
5、设置插件
```json
{
"apiURL": "http://localhost:8501/v1/models/emotion_model:predict",
"apiTimeout": 3000
}
```
PMail后台->右上角设置按钮->插件设置->SpamBlock
apiURL表示模型api访问地址如果你是使用Docker部署PMail和tensorflow/serving容器需要设置为相同网络才能通信并且需要把localhost替换为tensorflow/serving的容器名称
接口地址表示模型api访问地址如果你是使用Docker部署PMail和tensorflow/serving容器需要设置为相同网络才能通信并且需要把localhost替换为tensorflow/serving的容器名称
# 模型效果

View File

@ -1,4 +1,6 @@
datasets==2.20.0
numpy==1.20.3
retvec==1.0.1
tensorflow==2.8.4
tensorflow==2.12.1
beautifulsoup4
lxml

View File

@ -1,6 +1,7 @@
package main
import (
_ "embed"
"encoding/json"
"fmt"
"github.com/Jinnrry/pmail/dto/parsemail"
@ -9,6 +10,7 @@ import (
"github.com/Jinnrry/pmail/models"
"github.com/Jinnrry/pmail/utils/context"
log "github.com/sirupsen/logrus"
"github.com/spf13/cast"
"io"
"net/http"
"os"
@ -21,15 +23,64 @@ type SpamBlock struct {
hc *http.Client
}
func (s SpamBlock) SendBefore(ctx *context.Context, email *parsemail.Email) {
func (s *SpamBlock) SendBefore(ctx *context.Context, email *parsemail.Email) {
}
func (s SpamBlock) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
func (s *SpamBlock) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
}
func (s SpamBlock) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
func (s *SpamBlock) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
}
// GetName 获取插件名称
func (s *SpamBlock) GetName(ctx *context.Context) string {
return "SpamBlock"
}
//go:embed static/index.html
var index string
//go:embed static/jquery.js
var jquery string
// SettingsHtml 插件页面
func (s *SpamBlock) SettingsHtml(ctx *context.Context, url string, requestData string) string {
if strings.Contains(url, "jquery.js") {
return jquery
}
if strings.Contains(url, "index.html") {
if !ctx.IsAdmin {
return fmt.Sprintf(`
<div>
Please contact the administrator for configuration.
</div>
`)
}
return fmt.Sprintf(index, s.cfg.ApiURL, s.cfg.ApiTimeout, s.cfg.Threshold)
}
var cfg SpamBlockConfig
var tempCfg map[string]string
err := json.Unmarshal([]byte(requestData), &tempCfg)
if err != nil {
return err.Error()
}
cfg.ApiURL = tempCfg["url"]
cfg.Threshold = cast.ToFloat64(tempCfg["threshold"])
cfg.ApiTimeout = cast.ToInt(tempCfg["timeout"])
err = saveConfig(cfg)
if err != nil {
return err.Error()
}
s.cfg = cfg
return "success"
}
@ -45,7 +96,11 @@ type InstanceItem struct {
Token []string `json:"token"`
}
func (s SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
func (s *SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
if s.cfg.ApiURL == "" {
return
}
reqData := ApiRequest{
Instances: []InstanceItem{
@ -94,25 +149,26 @@ func (s SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Emai
switch maxClass {
case 0:
log.WithContext(ctx).Infof("[Spam Check Result: Normal] %s", email.Subject)
log.WithContext(ctx).Infof("[Spam Check Result: %f Normal] %s", maxScore, email.Subject)
case 1:
log.WithContext(ctx).Infof("[Spam Check Result: Spam ] %s", email.Subject)
log.WithContext(ctx).Infof("[Spam Check Result: %f Spam ] %s", maxScore, email.Subject)
case 2:
log.WithContext(ctx).Infof("[Spam Check Result: Blackmail ] %s", email.Subject)
log.WithContext(ctx).Infof("[Spam Check Result: %f Blackmail ] %s", maxScore, email.Subject)
}
if maxClass != 0 {
if maxClass != 0 && maxScore > s.cfg.Threshold/100 {
email.Status = 3
}
}
func (s SpamBlock) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
func (s *SpamBlock) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
}
type SpamBlockConfig struct {
ApiURL string `json:"apiURL"`
ApiTimeout int `json:"apiTimeout"` // 单位毫秒
ApiURL string `json:"apiURL"`
ApiTimeout int `json:"apiTimeout"` // 单位毫秒
Threshold float64 `json:"threshold"`
}
func NewSpamBlockHook() *SpamBlock {
@ -123,20 +179,18 @@ func NewSpamBlockHook() *SpamBlock {
if err == nil {
json.Unmarshal(cfgData, &pluginConfig)
}
} else {
log.Infof("No Config file found")
return nil
}
log.Infof("Config: %+v", pluginConfig)
if pluginConfig.ApiURL == "" {
pluginConfig.ApiURL = "http://localhost:8501/v1/models/emotion_model:predict"
}
if pluginConfig.ApiTimeout == 0 {
pluginConfig.ApiTimeout = 3000
}
if pluginConfig.Threshold == 0 {
pluginConfig.Threshold = 80
}
hc := &http.Client{
Timeout: time.Duration(pluginConfig.ApiTimeout) * time.Millisecond,
}
@ -147,6 +201,12 @@ func NewSpamBlockHook() *SpamBlock {
}
}
func saveConfig(cfg SpamBlockConfig) error {
data, _ := json.Marshal(cfg)
err := os.WriteFile("./plugins/spam_block_config.json", data, 0777)
return err
}
func main() {
log.Infof("SpamBlockPlug Star Success")
instance := NewSpamBlockHook()

View File

@ -0,0 +1,53 @@
<html>
<header>
<script src="/api/plugin/settings/SpamBlock/jquery.js"></script>
<script>
$(function () {
$("#submit").click(function () {
let data = {};
let value = $('#form').serializeArray();
$.each(value, function (index, item) {
data[item.name] = item.value;
});
let jsonData = JSON.stringify(data);
console.log(jsonData);
$.post("/api/plugin/settings/SpamBlock/save", jsonData, function (data) {
alert(data)
})
})
})
</script>
</header>
<body>
<div>
<form id="form" action="/api/plugin/settings/SpamBlock/save" method="post">
<div>
<label for="url"> 模型API接口地址 </label>
<input id="url" style="width: 600px;" name="url" value="%s"
placeholder="http://localhost:8501/v1/models/emotion_model:predict">
</div>
<div>
<label for="timeout"> 超时时间: </label>
<input id="timeout" style="width: 600px;" name="timeout" value="%d" placeholder="单位毫秒">
</div>
<div>
<label for="threshold"> 识别阈值: </label>
<input id="threshold" name="threshold" type="number" value="%f" style="width: 600px;"
step=".01" max="100" min="0" placeholder="识别阈值,值越低过滤越严格,越容易误判">
</div>
<input id="submit" type="button" value="Submit"/>
</form>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -24,6 +24,8 @@ def getData(folder_path):
# 读取csv文件内容
with open(file_path, 'r', errors='ignore') as csv_file:
for line in csv_file:
if line[0] == '' or line[0]==' ':
continue
labels.append([int(str.strip(line[0]))])
msgs.append(line[3:])
return np.array(msgs), np.array(labels)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,57 @@
import os
from email.parser import Parser
from email.policy import default
from bs4 import BeautifulSoup
# 该脚本用于整理trec06c数据集可以生成训练集和测试集数据格式
def getData(path):
f = open(path, 'r', errors='ignore')
data = f.read()
headers = Parser(policy=default).parsestr(data)
body = ""
if headers.is_multipart():
for part in headers.iter_parts():
tbody = part.get_payload()
if isinstance(tbody, list):
for item in tbody:
txt = item.get_payload()
if isinstance(tbody, list):
return "", ""
bsObj = BeautifulSoup(txt, 'lxml')
body += bsObj.get_text()
else:
bsObj = BeautifulSoup(tbody, 'lxml')
body += bsObj.get_text()
else:
tbody = headers.get_payload()
bsObj = BeautifulSoup(tbody, 'lxml')
body += bsObj.get_text()
return headers["subject"], body.replace("\n", "")
num = 0
# getData("../data/000/000")
with open("index", "r") as f:
with open("trec07p_train.csv", "w") as w:
with open("trec07p_test.csv", "w") as wt:
while True:
line = f.readline()
if not line:
break
infos = line.split(" ")
subject, body = getData(infos[1].strip())
if subject == "":
continue
tp = 0
if infos[0].lower() == "spam":
tp = 1
data = "{} \t{} {}\n".format(tp, subject, body)
if num < 55000:
w.write(data)
else:
wt.write(data)
num += 1
print(num)

View File

@ -35,6 +35,20 @@ func (w *TelegramPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsema
}
// GetName 获取插件名称
func (w *TelegramPushHook) GetName(ctx *context.Context) string {
return "TgPush"
}
// SettingsHtml 插件页面
func (w *TelegramPushHook) SettingsHtml(ctx *context.Context, url string, requestData string) string {
return fmt.Sprintf(`
<div>
TG push No Settings Page
</div>
`)
}
func (w *TelegramPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
}

View File

@ -1,14 +0,0 @@
## How To Ues
Copy plugin binary file to `/plugins`
add config.json to `/plugins/config.com` like this:
```json
{
"webPushUrl": "", // webhook push URL
"webPushToken": "", // webhook push token
}
```

View File

@ -1,136 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"github.com/Jinnrry/pmail/config"
"github.com/Jinnrry/pmail/dto/parsemail"
"github.com/Jinnrry/pmail/hooks/framework"
"github.com/Jinnrry/pmail/models"
"github.com/Jinnrry/pmail/utils/context"
log "github.com/sirupsen/logrus"
"net/http"
"os"
)
type WebPushHook struct {
url string
token string
}
func (w *WebPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
if w.url == "" {
return
}
content := string(email.Text)
if content == "" {
content = email.Subject
}
webhookURL := w.url // 替换为您的 Webhook URL
to := make([]string, len(email.To))
for i, user := range email.To {
to[i] = user.EmailAddress
}
data := EmailData{
From: email.From.EmailAddress,
To: to,
Subject: email.Subject,
Body: content,
Token: w.token,
}
jsonData, err := json.Marshal(data)
if err != nil {
log.WithContext(ctx).Errorf("web push error %+v", err)
}
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
log.WithContext(ctx).Errorf("web push error %+v", err)
}
defer resp.Body.Close()
}
// EmailData 用于存储解析后的邮件数据
type EmailData struct {
From string `json:"from"`
To []string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
Token string `json:"token"`
}
func (w *WebPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
}
func (w *WebPushHook) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
}
func (w *WebPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
}
func (w *WebPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
}
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("./plugins/web_push_config.json"); err == nil {
cfgData, err = os.ReadFile("./plugins/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: pushURL,
token: token,
}
return ret
}
func main() {
framework.CreatePlugin("web_push", NewWebPushHook()).Run()
}

View File

@ -32,6 +32,19 @@ type WeChatPushHook struct {
mainConfig *config.Config
}
func (w *WeChatPushHook) GetName(ctx *context.Context) string {
return "WeChatPushHook"
}
// SettingsHtml 插件页面
func (w *WeChatPushHook) SettingsHtml(ctx *context.Context, url string, requestData string) string {
return fmt.Sprintf(`
<div>
TG push No Settings Page
</div>
`)
}
func (w *WeChatPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
if w.appId == "" || w.secret == "" || w.pushUser == "" {
return

View File

@ -54,6 +54,8 @@ func router(mux *http.ServeMux) {
mux.HandleFunc("/api/user/edit", contextIterceptor(controllers.EditUser))
mux.HandleFunc("/api/user/info", contextIterceptor(controllers.Info))
mux.HandleFunc("/api/user/list", contextIterceptor(controllers.UserList))
mux.HandleFunc("/api/plugin/settings/", contextIterceptor(controllers.SettingsHtml))
mux.HandleFunc("/api/plugin/list", contextIterceptor(controllers.GetPluginList))
}
func HttpStart() {

View File

@ -185,15 +185,12 @@ func (a action) Uidl(session *gopop.Session, msg string) ([]gopop.UidlItem, erro
var res []listItem
var err error
var ssql string
err = db.Instance.Where("type=0 and status=0").Select("id").Table("email").Find(&res)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
log.WithContext(session.Ctx.(*context.Context)).Errorf("SQL:%s Error: %+v", ssql, err)
err = nil
return []gopop.UidlItem{}, nil
emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
for _, info := range emailList {
res = append(res, listItem{
Id: cast.ToInt64(info.Id),
Size: cast.ToInt64(info.Size),
})
}
ret := []gopop.UidlItem{}
for _, re := range res {

View File

@ -20,14 +20,14 @@ func HasAuth(ctx *context.Context, email *models.Email) bool {
if ctx.IsAdmin {
return true
}
var ue *models.UserEmail
err := db.Instance.Where("email_id = ?", email.Id).Find(&ue)
var ue []models.UserEmail
err := db.Instance.Table(&models.UserEmail{}).Where("email_id = ? and user_id = ?", email.Id, ctx.UserID).Find(&ue)
if err != nil {
log.Errorf("Error while checking user: %v", err)
return false
}
return ue != nil
return len(ue) != 0
}
func DkimGen() string {

View File

@ -46,7 +46,7 @@ func (s *Session) Data(r io.Reader) error {
}
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End")
email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData))
email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData), len(emailData))
if s.From != "" {
from := parsemail.BuilderUser(s.From)
@ -209,24 +209,26 @@ func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserI
}
modelEmail := models.Email{
Type: cast.ToInt8(emailType),
Subject: email.Subject,
ReplyTo: json2string(email.ReplyTo),
FromName: email.From.Name,
FromAddress: email.From.EmailAddress,
To: json2string(email.To),
Bcc: json2string(email.Bcc),
Cc: json2string(email.Cc),
Text: sql.NullString{String: string(email.Text), Valid: true},
Html: sql.NullString{String: string(email.HTML), Valid: true},
Sender: json2string(email.Sender),
Attachments: json2string(email.Attachments),
SPFCheck: spfV,
DKIMCheck: dkimV,
SendUserID: sendUserID,
SendDate: time.Now(),
Status: cast.ToInt8(email.Status),
CreateTime: time.Now(),
Type: cast.ToInt8(emailType),
Subject: email.Subject,
ReplyTo: json2string(email.ReplyTo),
FromName: email.From.Name,
FromAddress: email.From.EmailAddress,
To: json2string(email.To),
Bcc: json2string(email.Bcc),
Cc: json2string(email.Cc),
Text: sql.NullString{String: string(email.Text), Valid: true},
Html: sql.NullString{String: string(email.HTML), Valid: true},
Sender: json2string(email.Sender),
Attachments: json2string(email.Attachments),
Size: email.Size,
SPFCheck: spfV,
DKIMCheck: dkimV,
SendUserID: sendUserID,
SendDate: time.Now(),
Status: cast.ToInt8(email.Status),
CreateTime: time.Now(),
CronSendTime: time.Now(),
}
_, err := db.Instance.Insert(&modelEmail)