├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.md
└── workflows
│ ├── docker_build.yml
│ ├── docker_build_pre.yml
│ ├── release.yml
│ └── unitTest.yml
├── .gitignore
├── Dockerfile
├── DockerfileGithubAction
├── LICENSE
├── Makefile
├── README.md
├── README_CN.md
├── docs
├── cn.gif
├── en.gif
├── nuisance
│ └── demo.txt
└── settings.jpg
├── fe
├── .eslintrc.cjs
├── .gitignore
├── .vscode
│ └── extensions.json
├── README.md
├── index.html
├── package.json
├── public
│ └── favicon.ico
├── src
│ ├── App.vue
│ ├── assets
│ │ ├── base.css
│ │ ├── logo.svg
│ │ └── main.css
│ ├── components
│ │ ├── GroupSettings.vue
│ │ ├── HomeAside.vue
│ │ ├── HomeHeader.vue
│ │ ├── PluginSettings.vue
│ │ ├── RuleSettings.vue
│ │ ├── SecuritySettings.vue
│ │ └── UserManagement.vue
│ ├── i18n
│ │ └── i18n.js
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── stores
│ │ ├── group.js
│ │ └── useGlobalStatusStore.js
│ ├── utils
│ │ └── axios.js
│ └── views
│ │ ├── EditerView.vue
│ │ ├── EmailDetailView.vue
│ │ ├── ListView.vue
│ │ ├── LoginView.vue
│ │ └── SetupView.vue
├── vite.config.js
└── yarn.lock
└── server
├── config
├── config.dev.json
├── config.go
├── config.json
├── config_mysql.json
├── dkim
│ ├── README.md
│ ├── dkim.priv
│ └── dkim.public
└── ssl
│ ├── README.md
│ ├── private.key
│ ├── public.crt
│ └── server.csr
├── consts
└── consts.go
├── controllers
├── attachments.go
├── base.go
├── email
│ ├── delete.go
│ ├── detail.go
│ ├── list.go
│ ├── move.go
│ ├── read.go
│ └── send.go
├── group.go
├── interceptor.go
├── login.go
├── ping.go
├── plugin.go
├── rule.go
├── settings.go
├── setup.go
└── user.go
├── db
└── init.go
├── dto
├── parsemail
│ ├── dkim.go
│ ├── dkim_test.go
│ ├── email.go
│ ├── email_test.go
│ └── encodedword.go
├── response
│ ├── email.go
│ └── response.go
├── rule.go
└── tag.go
├── go.mod
├── go.sum
├── hooks
├── base.go
├── debug
│ └── debug.go
├── framework
│ └── framework.go
├── spam_block
│ ├── README.md
│ ├── export
│ │ ├── Makefile
│ │ └── export.go
│ ├── requirements.txt
│ ├── spam_block.go
│ ├── static
│ │ ├── index.html
│ │ └── jquery.js
│ ├── test.py
│ ├── testData
│ │ └── data.csv
│ ├── tools
│ │ └── tools.go
│ ├── train.py
│ ├── trainData
│ │ └── data.csv
│ ├── trec06c_format.py
│ └── trec07p_format.py
├── telegram_push
│ ├── README.md
│ └── telegram_push.go
└── wechat_push
│ ├── README.md
│ └── wechat_push.go
├── i18n
└── i18n.go
├── listen
├── cron_server
│ └── ssl_update.go
├── http_server
│ ├── http_server.go
│ ├── https_server.go
│ └── setup_server.go
├── imap_server
│ ├── imap_server.go
│ ├── imap_server_test.go
│ ├── server.go
│ ├── session_copy.go
│ ├── session_create.go
│ ├── session_delete.go
│ ├── session_expunge.go
│ ├── session_fetch.go
│ ├── session_idle.go
│ ├── session_list.go
│ ├── session_login.go
│ ├── session_move.go
│ ├── session_namespace.go
│ ├── session_poll.go
│ ├── session_rename.go
│ ├── session_search.go
│ ├── session_select.go
│ ├── session_status.go
│ └── session_store.go
├── pop3_server
│ ├── action.go
│ ├── action_test.go
│ └── pop3server.go
└── smtp_server
│ ├── action.go
│ ├── login.go
│ ├── read_content.go
│ ├── read_content_test.go
│ ├── smtp.go
│ └── smtp_test
│ └── sendEmailTest.py
├── main.go
├── main_test.go
├── models
├── User.go
├── email.go
├── group.go
├── rule.go
├── session.go
├── user_email.go
└── version.go
├── res_init
└── init.go
├── services
├── attachments
│ └── attachments.go
├── auth
│ └── auth.go
├── del_email
│ └── del_email.go
├── detail
│ └── detail.go
├── group
│ ├── group.go
│ └── group_test.go
├── list
│ ├── list.go
│ └── list_test.go
├── rule
│ ├── match
│ │ ├── base.go
│ │ ├── contains_match.go
│ │ ├── equal_match.go
│ │ ├── regex_match.go
│ │ └── regex_match_test.go
│ └── rule.go
└── setup
│ ├── db.go
│ ├── dns.go
│ ├── domain.go
│ ├── finish.go
│ └── ssl
│ ├── challenge.go
│ ├── dnsProvide.go
│ ├── ssl.go
│ └── ssl_test.go
├── session
└── init.go
├── signal
└── signal.go
└── utils
├── address
├── address.go
└── address_test.go
├── array
└── array.go
├── async
└── async.go
├── consts
└── consts.go
├── context
└── context.go
├── errors
└── error.go
├── file
└── file.go
├── id
└── logid.go
├── ip
└── ip.go
├── password
├── encode.go
└── encode_test.go
├── send
└── send.go
├── smtp
└── smtp.go
├── utf7
├── LICENSE
├── README.md
├── decoder.go
├── decoder_test.go
├── encoder.go
├── encoder_test.go
└── utf7.go
└── version
├── version.go
└── version_test.go
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: bug反馈 / bug report
2 | description: "提交 bug反馈/ bug report"
3 | body:
4 | - type: checkboxes
5 | attributes:
6 | label: 完整性要求 / Integrity requirements
7 | description: |-
8 | 请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。
9 | options:
10 | - label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
11 | required: true
12 | - label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。
13 | required: true
14 | - label: 我搜索了issues,没有发现已提出的类似问题。
15 | required: true
16 | - label: 我已经阅读了项目[Readme](https://github.com/Jinnrry/PMail/blob/master/README_CN.md)和[常见问题](https://github.com/Jinnrry/PMail/discussions/170)
17 | required: true
18 | - type: input
19 | attributes:
20 | label: 版本
21 | description: 使用的PMail版本
22 | validations:
23 | required: true
24 | - type: markdown
25 | attributes:
26 | value: |-
27 | ## 配置与日志部分
28 |
29 | ### 对于配置文件
30 | 请提供可以重现问题的配置文件。
31 |
32 | ### 对于日志
33 | 请先将日志等级设置为 debug.
34 | 重启 PMail ,再按复现方式操作,尽量减少日志中的无关部分。
35 | 记得删除有关个人信息的部分。
36 | 提供 PMail 的日志,而不是面板或者别的东西输出的日志。
37 |
38 | ### 最后
39 | 在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。
40 | 把内容放在文本框预置的 `````` 和 ```
47 |
48 |
56 |
57 |
This is an HTML part.
") 52 | w1.Close() 53 | 54 | var h2 message.Header 55 | h2.SetContentType("text/plain", nil) 56 | w2, err := w.CreatePart(h2) 57 | if err != nil { 58 | } 59 | io.WriteString(w2, "Hello World!\n\nThis is a text part.") 60 | w2.Close() 61 | 62 | w.Close() 63 | 64 | fmt.Println(b.String()) 65 | } 66 | 67 | func TestEmail_builder(t *testing.T) { 68 | e := Email{ 69 | From: buildUser("i@test.com"), 70 | To: buildUsers([]string{"to@test.com"}), 71 | Subject: "Title中文", 72 | HTML: []byte("Html"), 73 | Text: []byte("Text"), 74 | Attachments: []*Attachment{ 75 | { 76 | Filename: "a.png", 77 | ContentType: "image/jpeg", 78 | Content: []byte("aaa"), 79 | ContentID: "1", 80 | }, 81 | }, 82 | } 83 | 84 | rest := e.BuildBytes(nil, false) 85 | fmt.Println(string(rest)) 86 | } 87 | -------------------------------------------------------------------------------- /server/dto/response/email.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/Jinnrry/pmail/models" 4 | 5 | type EmailResponseData struct { 6 | models.Email `xorm:"extends"` 7 | IsRead int8 `json:"is_read"` 8 | SerialNumber int `json:"serial_number"` 9 | UeId int `json:"ue_id"` 10 | } 11 | 12 | type UserEmailUIDData struct { 13 | models.UserEmail `xorm:"extends"` 14 | SerialNumber int `json:"serial_number"` 15 | } 16 | -------------------------------------------------------------------------------- /server/dto/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | ) 7 | 8 | const ( 9 | NeedSetup = 402 10 | NeedLogin = 403 11 | NoAccessPrivileges = 405 12 | ParamsError = 100 13 | ServerError = 500 14 | ) 15 | 16 | type Response struct { 17 | ErrorNo int `json:"errorNo"` 18 | ErrorMsg string `json:"errorMsg"` 19 | Data any `json:"data"` 20 | } 21 | 22 | func (p *Response) FPrint(w http.ResponseWriter) { 23 | bytesData, _ := json.Marshal(p) 24 | w.Write(bytesData) 25 | } 26 | 27 | func NewSuccessResponse(data any) *Response { 28 | return &Response{ 29 | Data: data, 30 | } 31 | } 32 | 33 | func NewErrorResponse(errorNo int, errorMsg string, data any) *Response { 34 | return &Response{ 35 | ErrorNo: errorNo, 36 | ErrorMsg: errorMsg, 37 | Data: data, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/dto/rule.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/Jinnrry/pmail/models" 6 | ) 7 | 8 | type RuleType int 9 | 10 | // 1已读,2转发,3删除 11 | var ( 12 | READ RuleType = 1 13 | FORWARD RuleType = 2 14 | DELETE RuleType = 3 15 | MOVE RuleType = 4 16 | ) 17 | 18 | type Rule struct { 19 | Id int `json:"id"` 20 | UserId int `json:"user_id"` 21 | Name string `json:"name"` 22 | Rules []*Value `json:"rules"` 23 | Action RuleType `json:"action"` 24 | Params string `json:"params"` 25 | Sort int `json:"sort"` 26 | } 27 | 28 | type Value struct { 29 | Field string `json:"field"` 30 | Type string `json:"type"` 31 | Rule string `json:"rule"` 32 | } 33 | 34 | func (p *Rule) Decode(data *models.Rule) *Rule { 35 | json.Unmarshal([]byte(data.Value), &p.Rules) 36 | p.Id = data.Id 37 | p.Name = data.Name 38 | p.Action = RuleType(data.Action) 39 | p.Sort = data.Sort 40 | p.Params = data.Params 41 | p.UserId = data.UserId 42 | return p 43 | } 44 | 45 | func (p *Rule) Encode() *models.Rule { 46 | v, _ := json.Marshal(p.Rules) 47 | ret := &models.Rule{ 48 | Id: p.Id, 49 | Name: p.Name, 50 | Value: string(v), 51 | Action: int(p.Action), 52 | Sort: p.Sort, 53 | Params: p.Params, 54 | } 55 | return ret 56 | } 57 | -------------------------------------------------------------------------------- /server/dto/tag.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "encoding/json" 4 | 5 | type SearchTag struct { 6 | Type int8 `json:"type"` // -1 不限 7 | Status int8 `json:"status"` // -1 不限 8 | GroupId int `json:"group_id"` // -1 不限 9 | } 10 | 11 | func (t SearchTag) ToString() string { 12 | data, _ := json.Marshal(t) 13 | return string(data) 14 | } 15 | -------------------------------------------------------------------------------- /server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Jinnrry/pmail 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | github.com/Jinnrry/gopop v0.0.0-20231113115125-fbdf52ae39ea 7 | github.com/alexedwards/scs/mysqlstore v0.0.0-20250417082927-ab20b3feb5e9 8 | github.com/alexedwards/scs/postgresstore v0.0.0-20250417082927-ab20b3feb5e9 9 | github.com/alexedwards/scs/sqlite3store v0.0.0-20250417082927-ab20b3feb5e9 10 | github.com/alexedwards/scs/v2 v2.8.0 11 | github.com/dlclark/regexp2 v1.11.5 12 | github.com/emersion/go-imap/v2 v2.0.0-beta.5 13 | github.com/emersion/go-message v0.18.2 14 | github.com/emersion/go-msgauth v0.6.8 15 | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 16 | github.com/emersion/go-smtp v0.21.3 17 | github.com/go-acme/lego/v4 v4.23.1 18 | github.com/go-sql-driver/mysql v1.9.2 19 | github.com/lib/pq v1.10.9 20 | github.com/mileusna/spf v0.9.5 21 | github.com/sirupsen/logrus v1.9.3 22 | github.com/spf13/cast v1.7.1 23 | golang.org/x/crypto v0.37.0 24 | golang.org/x/text v0.24.0 25 | modernc.org/sqlite v1.37.0 26 | xorm.io/builder v0.3.13 27 | xorm.io/xorm v1.3.9 28 | ) 29 | 30 | require ( 31 | filippo.io/edwards25519 v1.1.0 // indirect 32 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 33 | github.com/dustin/go-humanize v1.0.1 // indirect 34 | github.com/go-jose/go-jose/v4 v4.1.0 // indirect 35 | github.com/goccy/go-json v0.10.5 // indirect 36 | github.com/golang/snappy v1.0.0 // indirect 37 | github.com/google/uuid v1.6.0 // indirect 38 | github.com/json-iterator/go v1.1.12 // indirect 39 | github.com/mattn/go-isatty v0.0.20 // indirect 40 | github.com/miekg/dns v1.1.65 // indirect 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 42 | github.com/modern-go/reflect2 v1.0.2 // indirect 43 | github.com/ncruces/go-strftime v0.1.9 // indirect 44 | github.com/nxadm/tail v1.4.11 // indirect 45 | github.com/onsi/ginkgo v1.16.4 // indirect 46 | github.com/onsi/gomega v1.31.1 // indirect 47 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 48 | github.com/rogpeppe/go-internal v1.12.0 // indirect 49 | github.com/syndtr/goleveldb v1.0.0 // indirect 50 | golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect 51 | golang.org/x/mod v0.24.0 // indirect 52 | golang.org/x/net v0.39.0 // indirect 53 | golang.org/x/sync v0.13.0 // indirect 54 | golang.org/x/sys v0.32.0 // indirect 55 | golang.org/x/tools v0.32.0 // indirect 56 | modernc.org/libc v1.63.0 // indirect 57 | modernc.org/mathutil v1.7.1 // indirect 58 | modernc.org/memory v1.10.0 // indirect 59 | ) 60 | -------------------------------------------------------------------------------- /server/hooks/debug/debug.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Jinnrry/pmail/dto/parsemail" 6 | "github.com/Jinnrry/pmail/hooks/framework" 7 | "github.com/Jinnrry/pmail/models" 8 | "github.com/Jinnrry/pmail/utils/context" 9 | ) 10 | 11 | type Debug struct { 12 | } 13 | 14 | func NewDebug() *Debug { 15 | return &Debug{} 16 | } 17 | 18 | func (d Debug) SendBefore(ctx *context.Context, email *parsemail.Email) { 19 | fmt.Printf("[debug SendBefore] %+v ", email) 20 | 21 | } 22 | 23 | func (d Debug) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) { 24 | fmt.Printf("[debug SendAfter] %+v ", email) 25 | 26 | } 27 | 28 | func (d Debug) ReceiveParseBefore(ctx *context.Context, email *[]byte) { 29 | fmt.Printf("[debug ReceiveParseBefore] %s ", *email) 30 | 31 | } 32 | 33 | func (d Debug) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) { 34 | fmt.Printf("[debug ReceiveParseAfter] %+v ", email) 35 | email.Status = 5 36 | } 37 | 38 | func (d Debug) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) { 39 | fmt.Printf("[debug ReceiveSaveAfter] %+v %+v ", email, ue) 40 | } 41 | 42 | func (d Debug) GetName(ctx *context.Context) string { 43 | return "debug" 44 | } 45 | 46 | func (d Debug) SettingsHtml(ctx *context.Context, url string, requestData string) string { 47 | return "" 48 | } 49 | 50 | func main() { 51 | framework.CreatePlugin("debug_plugin", NewDebug()).Run() 52 | } 53 | -------------------------------------------------------------------------------- /server/hooks/spam_block/README.md: -------------------------------------------------------------------------------- 1 | # 插件介绍 2 | 3 | 使用机器学习的方式识别垃圾邮件。模型使用的是RETVec。模型参数约 200k,在我1核1G的服务器上,单次推理耗时约3秒,Mac M1上可达到毫秒级耗时。耗时上,其实可以将模型进行裁剪,转换为Tensorflow Lite模型,转换后模型的资源消耗应该更小。但是Lite模型部署比较繁琐,涉及大量C库的编译安装,过程过于复杂。另外 4 | 我觉得,这个模型在我这垃圾服务器上面都能勉强使用,其他所有人的服务器上面应该都能顺利运行了,没必要继续裁剪模型了。 5 | 6 | # Help 7 | 8 | 目前Google GMail使用的垃圾邮件识别算法也是RETVec,理论上识别效果能够达到GMail同等级别。但是,我并没有Google那样大量的训练集。欢迎大家以Pull 9 | Request的形式提交机器学习的各类样本数据。 10 | 11 | 你可以给testData和trainData这两个文件夹下面的csv文件提交PR,CSV文件每行的第一个数字表示数据类型,0表示正常邮件,1表示广告邮件,2表示诈骗邮件。 12 | 13 | 你可以使用export.go这个脚本或者从Release中下载[email_export工具](https://github.com/Jinnrry/PMail/releases/tag/v2.6.1)导出你全部的邮件数据,过滤掉隐私内容并且标记好分类后提交上来。 14 | 15 | # 如何运行 16 | 17 | 1、下载[emotion_model.zip](https://github.com/Jinnrry/PMail/releases/tag/v2.8.3)或者自己训练模型 18 | 19 | 2、使用docker运行tensorflow模型 20 | `docker run -d -p 127.0.0.1:8501:8501 \ 21 | -v "{模型文件位置}:/models/emotion_model" \ 22 | -e MODEL_NAME=emotion_model tensorflow/serving &` 23 | 24 | 3、CURL测试模型部署是否成功 25 | 26 | > 详细部署说明请参考[tensorflow官方](https://www.tensorflow.org/tfx/guide/serving?hl=zh-cn) 27 | 28 | ```bash 29 | curl -X POST http://localhost:8501/v1/models/emotion_model:predict -d '{ 30 | "instances": [ 31 | {"token":["各位同事请注意 这里是110,请大家立刻把银行卡账号密码回复发给我!"]} 32 | ] 33 | }' 34 | ``` 35 | 36 | 将得到类似输出: 37 | 38 | ```jsonc 39 | { 40 | "predictions": [ 41 | [ 42 | 0.394376636, 43 | // 正常邮件的得分 44 | 0.0055413493, 45 | // 广告邮件的得分 46 | 0.633584619 47 | // 诈骗邮件的得分,这里诈骗邮件得分最高,因此最可能为诈骗邮件 48 | ] 49 | ] 50 | } 51 | ``` 52 | 53 | 4、将spam_block插件移动到pmail插件目录 54 | 55 | 5、设置插件 56 | 57 | PMail后台->右上角设置按钮->插件设置->SpamBlock 58 | 59 | 接口地址表示模型api访问地址,如果你是使用Docker部署,PMail和tensorflow/serving容器需要设置为相同网络才能通信,并且需要把localhost替换为tensorflow/serving的容器名称 60 | 61 | # 模型效果 62 | 63 | trec06c数据集: 64 | 65 | loss: 0.0187 - acc: 0.9948 - val_loss: 0.0047 - val_acc: 0.9993 66 | 67 | 实际使用效果: 68 | 69 | 我最近一周的使用效果来看,实际使用效果远低于模型理论效果。猜测原因如下: 70 | 71 | trec06c数据集已经公开十多年了,目前应该市面上所有反垃圾系统都使用这个数据集训练过。这个训练集训练出来的特征可能具有普遍性,而对于发垃圾邮件的人来说,这十多年他们也大致摸透了哪些特征会被识别为垃圾邮件,因此他们会针对性的避开很多关键字以免被封 72 | 73 | 解决方案只能是加入更多更优质的训练数据,但是trec06c之后就没这样优质的训练数据了,因此如果大家愿意,欢迎贡献模型训练数据。另外,针对模型本身,也欢迎提出优化方案。 74 | 75 | # 训练模型 76 | 77 | `python train.py` 78 | 79 | # 测试模型 80 | 81 | `python test.py` 82 | 83 | # trec06c 数据集 84 | 85 | [trec06c_format.py](trec06c_format.py) 86 | 脚本用于整理trec06c数据集,将其转化为训练所需的数据格式。由于数据集版权限制,如有需要请前往[这里](https://plg.uwaterloo.ca/~gvcormac/treccorpus06/about.html) 87 | 自行下载,本项目中不直接引入数据集内容。 88 | 89 | # 致谢 90 | 91 | Tanks For [google-research/retvec](https://github.com/google-research/retvec) 92 | 93 | -------------------------------------------------------------------------------- /server/hooks/spam_block/export/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o export_linux_amd64 export.go 3 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o export_windows_amd64.exe export.go 4 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o export_mac_amd64 export.go 5 | CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o export_mac_arm64 export.go 6 | -------------------------------------------------------------------------------- /server/hooks/spam_block/export/export.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Jinnrry/pmail/config" 6 | "github.com/Jinnrry/pmail/db" 7 | "github.com/Jinnrry/pmail/hooks/spam_block/tools" 8 | "github.com/Jinnrry/pmail/models" 9 | "github.com/spf13/cast" 10 | "os" 11 | ) 12 | 13 | func getType(emailId int) int { 14 | var ue models.UserEmail 15 | _, err := db.Instance.Table(&ue).Where("email_id = ?", emailId).Limit(1).Get(&ue) 16 | if err != nil { 17 | fmt.Println(err) 18 | } 19 | if ue.Status == 3 { 20 | return 2 21 | } 22 | 23 | if ue.Status == 5 { 24 | return 1 25 | } 26 | 27 | return 0 28 | } 29 | 30 | func main() { 31 | args := os.Args 32 | 33 | var id int 34 | if len(args) >= 2 { 35 | id = cast.ToInt(args[1]) 36 | } 37 | 38 | config.Init() 39 | err := db.Init("test") 40 | if err != nil { 41 | panic(err) 42 | } 43 | fmt.Println(config.Instance.DbDSN) 44 | 45 | fmt.Println("文件第一列是分类,0表示正常邮件,1表示垃圾邮件,2表示诈骗邮件") 46 | 47 | var start int 48 | file, err := os.OpenFile("data.csv", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0777) 49 | if err != nil { 50 | fmt.Println(err) 51 | } 52 | defer file.Close() 53 | for { 54 | var emails []models.Email 55 | if id > 0 { 56 | db.Instance.Table(&models.Email{}).Where("id = ?", id).OrderBy("id").Find(&emails) 57 | } else { 58 | db.Instance.Table(&models.Email{}).Where("id > ?", start).OrderBy("id").Find(&emails) 59 | } 60 | if len(emails) == 0 { 61 | break 62 | } 63 | for _, email := range emails { 64 | start = email.Id 65 | content := tools.Trim(tools.TrimHtml(email.Html.String)) 66 | if content == "" { 67 | content = tools.Trim(email.Text.String) 68 | } 69 | _, err = file.WriteString(fmt.Sprintf("%d \t%s %s\n", getType(email.Id), email.Subject, content)) 70 | if err != nil { 71 | fmt.Println(err) 72 | } 73 | //fmt.Printf("0 \t%s %s\n", email.Subject, trim(trimHtml(email.Html.String))) 74 | } 75 | if id > 0 { 76 | break 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /server/hooks/spam_block/requirements.txt: -------------------------------------------------------------------------------- 1 | datasets==2.20.0 2 | numpy==1.20.3 3 | retvec==1.0.1 4 | tensorflow==2.12.1 5 | beautifulsoup4 6 | lxml -------------------------------------------------------------------------------- /server/hooks/spam_block/static/index.html: -------------------------------------------------------------------------------- 1 | 2 |