完善多个通道
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
package aliyun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
dm20151123 "github.com/alibabacloud-go/dm-20151123/v2/client"
|
||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
type Aliyun struct {
|
||||
interfaces.DefaultEmail
|
||||
client *dm20151123.Client
|
||||
params *interfaces.EmialConfigDataAliyun
|
||||
logger loggerx.LoggerInterface
|
||||
}
|
||||
|
||||
func (l *Aliyun) InitEmail(ctx context.Context, params interfaces.EmailConfigData, logger loggerx.LoggerInterface) (interfaces.Email, error) {
|
||||
l.logger.Infof(ctx, "params:%+v", params)
|
||||
if params.Aliyun == nil {
|
||||
return nil, errors.New("not aliyun")
|
||||
}
|
||||
l.logger.Infof(ctx, "params:%+v", params.Aliyun)
|
||||
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
|
||||
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html。
|
||||
config := &openapi.Config{
|
||||
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
|
||||
AccessKeyId: tea.String(params.Aliyun.AccessId),
|
||||
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
|
||||
AccessKeySecret: tea.String(params.Aliyun.AccessKey),
|
||||
}
|
||||
if params.Aliyun.Endpoint == "" {
|
||||
params.Aliyun.Endpoint = "dm.aliyuncs.com"
|
||||
}
|
||||
|
||||
// Endpoint 请参考 https://api.aliyun.com/product/Dm
|
||||
config.Endpoint = tea.String(params.Aliyun.Endpoint)
|
||||
|
||||
result, err := dm20151123.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Aliyun{
|
||||
client: result,
|
||||
params: params.Aliyun,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *Aliyun) Send(ctx context.Context, params interfaces.Message) error {
|
||||
if l.client == nil {
|
||||
return errors.New("client no init")
|
||||
}
|
||||
if len(params.To) > 100 {
|
||||
return errors.New("最多 100 个地址")
|
||||
}
|
||||
if l.params.AccountName == "" {
|
||||
return errors.New("AccountName 必填")
|
||||
}
|
||||
|
||||
toAddress := strings.Join(params.To, ",")
|
||||
|
||||
singleSendMailRequest := &dm20151123.SingleSendMailRequest{}
|
||||
|
||||
singleSendMailRequest.AccountName = tea.String(l.params.AccountName)
|
||||
singleSendMailRequest.ToAddress = tea.String(toAddress) // 目标地址,多个 email 地址可以用逗号分隔,最多 100 个地址(支持邮件组)。
|
||||
singleSendMailRequest.Subject = tea.String(params.Subject)
|
||||
singleSendMailRequest.HtmlBody = tea.String(params.Body)
|
||||
singleSendMailRequest.AddressType = tea.Int32(0) // 地址类型。取值:0:为随机账号1:为发信地址
|
||||
|
||||
if params.ReplyTo != "" {
|
||||
singleSendMailRequest.ReplyToAddress = tea.Bool(false)
|
||||
singleSendMailRequest.ReplyAddress = tea.String(params.ReplyTo)
|
||||
} else {
|
||||
singleSendMailRequest.ReplyToAddress = tea.Bool(true)
|
||||
}
|
||||
|
||||
runtime := &util.RuntimeOptions{}
|
||||
tryErr := func() (_e error) {
|
||||
defer func() {
|
||||
if r := tea.Recover(recover()); r != nil {
|
||||
_e = r
|
||||
}
|
||||
}()
|
||||
// 复制代码运行请自行打印 API 的返回值
|
||||
resp, err := l.client.SingleSendMailWithOptions(singleSendMailRequest, runtime)
|
||||
by, _ := json.Marshal(resp)
|
||||
fmt.Printf("resp:%+v err:%+v", string(by), err)
|
||||
l.logger.Infof(ctx, "resp:%+v err:%+v", resp, err)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if tryErr != nil {
|
||||
l.logger.Errorf(ctx, "err:%+v", tryErr)
|
||||
return tryErr
|
||||
|
||||
// var error = &tea.SDKError{}
|
||||
// if _t, ok := tryErr.(*tea.SDKError); ok {
|
||||
// error = _t
|
||||
// } else {
|
||||
// error.Message = tea.String(tryErr.Error())
|
||||
// }
|
||||
// // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// // 错误 message
|
||||
// fmt.Println(tea.StringValue(error.Message))
|
||||
// // 诊断地址
|
||||
// var data interface{}
|
||||
// d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
|
||||
// d.Decode(&data)
|
||||
// if m, ok := data.(map[string]interface{}); ok {
|
||||
// recommend, _ := m["Recommend"]
|
||||
// fmt.Println("recommend", recommend)
|
||||
// }
|
||||
// _, _err := util.AssertAsString(error.Message)
|
||||
// if _err != nil {
|
||||
// return _err
|
||||
// }
|
||||
}
|
||||
|
||||
return nil // 实现具体的 Aliyun 发送方法
|
||||
// 如:return aliyunSDK.SendMail(params)
|
||||
}
|
||||
|
||||
// 同步状态
|
||||
func (l *Aliyun) SyncStatus(ctx context.Context) (resp []interfaces.EmailSendRecord, err error) {
|
||||
|
||||
start := ""
|
||||
|
||||
// 一次同步一天的数据
|
||||
for {
|
||||
list, next, err := l.getSendStatus(ctx, start)
|
||||
l.logger.Infof(ctx, "list:%+v next:%+v err:%+v", list, next, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, val := range list {
|
||||
|
||||
t, _ := time.ParseInLocation("2006-01-02T15:04Z", tea.StringValue(val.LastUpdateTime), time.Local)
|
||||
|
||||
// 0:成功 2:无效地址 3:垃圾邮件 4:失败
|
||||
record := interfaces.EmailSendRecord{
|
||||
AccountName: tea.StringValue(val.AccountName),
|
||||
UpdateTime: t.UnixMilli(),
|
||||
ToUser: tea.StringValue(val.ToAddress),
|
||||
Subject: tea.StringValue(val.Subject),
|
||||
ErrorMessage: tea.StringValue(val.Message),
|
||||
}
|
||||
|
||||
switch tea.Int32Value(val.Status) {
|
||||
case 0:
|
||||
record.Status = interfaces.EmailSendStatusSuccess
|
||||
case 2:
|
||||
record.Status = interfaces.EmailSendStatusInvalidAddress
|
||||
case 3:
|
||||
record.Status = interfaces.EmailSendStatusSpam
|
||||
case 4:
|
||||
record.Status = interfaces.EmailSendStatusFailed
|
||||
default:
|
||||
record.Status = interfaces.EmailSendStatusUnknown
|
||||
}
|
||||
|
||||
resp = append(resp, record)
|
||||
}
|
||||
if next == nil || len(*next) == 0 {
|
||||
break
|
||||
}
|
||||
start = *next
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
func (l *Aliyun) getSendStatus(ctx context.Context, start string) (list []*dm20151123.SenderStatisticsDetailByParamResponseBodyDataMailDetail, nextStart *string, err error) {
|
||||
now := time.Now().Local()
|
||||
senderStatisticsDetailByParamRequest := &dm20151123.SenderStatisticsDetailByParamRequest{
|
||||
StartTime: tea.String(now.AddDate(0, 0, -1).Format("2006-01-02 15:04")),
|
||||
EndTime: tea.String(now.Format("2006-01-02 15:04")),
|
||||
Length: tea.Int32(100),
|
||||
}
|
||||
if start != "" {
|
||||
senderStatisticsDetailByParamRequest.NextStart = tea.String(start)
|
||||
}
|
||||
runtime := &util.RuntimeOptions{}
|
||||
tryErr := func() (_e error) {
|
||||
defer func() {
|
||||
if r := tea.Recover(recover()); r != nil {
|
||||
_e = r
|
||||
}
|
||||
}()
|
||||
// 复制代码运行请自行打印 API 的返回值
|
||||
resp, _err := l.client.SenderStatisticsDetailByParamWithOptions(senderStatisticsDetailByParamRequest, runtime)
|
||||
if _err != nil {
|
||||
l.logger.Errorf(ctx, "resp:%+v err:%+v", resp, _err)
|
||||
return _err
|
||||
}
|
||||
|
||||
if resp == nil || resp.Body == nil || resp.Body.Data == nil {
|
||||
return errors.New("resp.Body.Data is nil")
|
||||
}
|
||||
|
||||
list = resp.Body.Data.MailDetail
|
||||
nextStart = resp.Body.NextStart
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if tryErr != nil {
|
||||
l.logger.Errorf(ctx, "err:%+v", tryErr)
|
||||
return nil, nil, tryErr
|
||||
|
||||
// var error = &tea.SDKError{}
|
||||
// if _t, ok := tryErr.(*tea.SDKError); ok {
|
||||
// error = _t
|
||||
// } else {
|
||||
// error.Message = tea.String(tryErr.Error())
|
||||
// }
|
||||
// // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// // 错误 message
|
||||
// fmt.Println(tea.StringValue(error.Message))
|
||||
// // 诊断地址
|
||||
// var data interface{}
|
||||
// d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
|
||||
// d.Decode(&data)
|
||||
// if m, ok := data.(map[string]interface{}); ok {
|
||||
// recommend, _ := m["Recommend"]
|
||||
// fmt.Println("recommend:", recommend)
|
||||
// }
|
||||
// _, _err := util.AssertAsString(error.Message)
|
||||
// if _err != nil {
|
||||
// l.logger.Errorf(ctx, "resp:%+v err:%+v", error.Message, _err)
|
||||
// return nil, nil, _err
|
||||
// }
|
||||
}
|
||||
return list, nextStart, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package aliyun_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx/aliyun"
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
aliyun := &aliyun.Aliyun{}
|
||||
ctx := context.Background()
|
||||
|
||||
logger := loggerx.NewLogger(ctx)
|
||||
|
||||
ali, err := aliyun.InitEmail(ctx, interfaces.EmailConfigData{
|
||||
Aliyun: &interfaces.EmialConfigDataAliyun{
|
||||
AccessId: "LTAI5tEQ8L8fmDir8udD3CFr",
|
||||
AccessKey: "llg9M1U56s2SW5PuerlKPvTB1xYhn0",
|
||||
Endpoint: "dm.aliyuncs.com",
|
||||
AccountName: "test@email.aisz.org", //"test@email.aisz.org",
|
||||
ReplyAddress: "287852692@qq.com",
|
||||
},
|
||||
}, logger)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
by, err := os.ReadFile("../../../static/email/msg/zh_Hant.html")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(by))
|
||||
|
||||
err = ali.Send(ctx, interfaces.Message{
|
||||
To: []string{"huangxinyun@dreaminglife.cn"},
|
||||
Subject: "测试主题",
|
||||
Body: string(by),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("resp err", err)
|
||||
}
|
||||
|
||||
t.Log("send success")
|
||||
}
|
||||
|
||||
// func TestSyncStatus(t *testing.T) {
|
||||
// aliyun := &aliyun.Aliyun{}
|
||||
// ctx := context.Background()
|
||||
|
||||
// global.Logger = loggerx.NewLogger(ctx)
|
||||
|
||||
// ali, err := aliyun.InitEmail(ctx, interfaces.EmailConfigData{
|
||||
// Aliyun: &interfaces.EmialConfigDataAliyun{
|
||||
// AccessId: "LTAI5tEQ8L8fmDir8udD3CFr",
|
||||
// AccessKey: "llg9M1U56s2SW5PuerlKPvTB1xYhn0",
|
||||
// Endpoint: "dm.aliyuncs.com",
|
||||
// AccountName: "test@email.aisz.org",
|
||||
// ReplyAddress: "287852692@qq.com",
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// list, err := ali.SyncStatus(ctx)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// _ = list
|
||||
|
||||
// // global.Logger.Infof(ctx, "status: %v", list)
|
||||
|
||||
// t.Log("status:", list)
|
||||
|
||||
// }
|
||||
@@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<!-- <link rel="stylesheet" href="./assets/css/index.css" /> -->
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 96px;
|
||||
background-color: #1b1b22;
|
||||
}
|
||||
|
||||
.header .logo {
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
.code-main {
|
||||
padding: 22px 24px;
|
||||
}
|
||||
|
||||
.code-tips {
|
||||
color: #5b5b65;
|
||||
font-size: 12px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.code-content {
|
||||
color: #1b1b22;
|
||||
font-weight: bold;
|
||||
font-size: 28px;
|
||||
margin-bottom: 61px;
|
||||
}
|
||||
|
||||
.team-tips {
|
||||
font-size: 11px;
|
||||
color: #5b5b65;
|
||||
margin-bottom: 59px;
|
||||
}
|
||||
|
||||
/* .team-tips::marker {
|
||||
margin-right: 2px !important;
|
||||
} */
|
||||
|
||||
.contact-us {
|
||||
color: #d1d1d1;
|
||||
padding-top: 24px;
|
||||
border-top: solid 0.5px #d1d1d1;
|
||||
}
|
||||
|
||||
.contact-us-info {
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #838384;
|
||||
padding: 0 40px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.contact-tel {
|
||||
color: #838384;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.contact-tel-number {
|
||||
color: #1b1b22;
|
||||
}
|
||||
|
||||
.contact-created-version {
|
||||
color: #838384;
|
||||
text-align: center;
|
||||
margin-top: 7px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
width: 64px;
|
||||
margin: 9px auto 15px;
|
||||
}
|
||||
|
||||
/* 异常邮件 */
|
||||
|
||||
.exception-title {
|
||||
color: #1b1b22;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.exception-content {
|
||||
line-height: 24px;
|
||||
color: #5b5b65;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.exception-ignore {
|
||||
margin-top: 45px;
|
||||
color: #5b5b65;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.exception-team-tips {
|
||||
margin-top: 85px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<header class="header flex-center">
|
||||
<img src="https://abpay-pub.s3.ap-northeast-1.amazonaws.com/1077_1727166836625.png" alt="" class="logo" />
|
||||
</header>
|
||||
|
||||
<main class="code-main">
|
||||
<div class="code-tips">
|
||||
以下是你的 ABPAY 验证码。请注意,该验证码将在 {{.shijian}} 分钟后过期, 请尽快完成验证。
|
||||
</div>
|
||||
|
||||
<div class="code-content">{{.verify_code}}</div>
|
||||
|
||||
<li class="team-tips">ABpay开发者团队服务</li>
|
||||
|
||||
<section class="contact-us">
|
||||
<div class="contact-us-info">
|
||||
本邮件由系统自动发出请勿回复,如需要了解更多服务,欢迎访问ABpay官方网
|
||||
</div>
|
||||
<div class="contact-us-info">还可以通以下方式联系我们</div>
|
||||
|
||||
<img src="https://abpay-pub.s3.ap-northeast-1.amazonaws.com/1077_1727166836625.png" alt="" class="qr-code" />
|
||||
<div class="contact-tel">
|
||||
客服电话:<span class="contact-tel-number">400-278-2890</span>
|
||||
</div>
|
||||
<div class="contact-created-version">2024 ABpay.com.cn . All rightes reserved</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
[info]{"time":"2024-09-25 14:15:35.045218","file":"/aliyun.go:94","func":"func1","gid":"57","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 06:15:34 GMT\",\n \"etag\": \"8f9dfw6sRErJKrt+hoTBWhQ1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"DC2492CF-2A5B-56CB-A605-AEEB8A39E3B4\",\n \"x-acs-trace-id\": \"e616ffedeb4d2a0be8ef74bf36aa7d03\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109262317450\",\n \"RequestId\": \"DC2492CF-2A5B-56CB-A605-AEEB8A39E3B4\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 14:16:59.676574","file":"/aliyun.go:94","func":"func1","gid":"53","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 06:16:59 GMT\",\n \"etag\": \"8XWguuFK0kv64XgUSTRDjQQ1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"396FAEEA-EF53-50D9-86F6-4229E0081279\",\n \"x-acs-trace-id\": \"a70ec22bb9fd7218476ef4363242e166\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109175592530\",\n \"RequestId\": \"396FAEEA-EF53-50D9-86F6-4229E0081279\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 14:17:35.907625","file":"/aliyun.go:94","func":"func1","gid":"52","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 06:17:35 GMT\",\n \"etag\": \"8eICx6U2H3qOv6K8RGRRP7A1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"DC8695D8-D326-52A3-B73B-F916F386B2CF\",\n \"x-acs-trace-id\": \"c826dc3f513763620ba59f048d9d8e71\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109435113590\",\n \"RequestId\": \"DC8695D8-D326-52A3-B73B-F916F386B2CF\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 14:22:38.398287","file":"/aliyun.go:94","func":"func1","gid":"50","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 06:22:38 GMT\",\n \"etag\": \"89EbQ3zPb8RIEJbK53hyAgQ1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"801C56E0-1498-5B09-AF52-DE958E9DB834\",\n \"x-acs-trace-id\": \"1a3d50fec251c83edb55bf48dc431eff\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109262322078\",\n \"RequestId\": \"801C56E0-1498-5B09-AF52-DE958E9DB834\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:20:19.656969","file":"/aliyun.go:94","func":"func1","gid":"53","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:20:19 GMT\",\n \"etag\": \"8+qybIN7HNz96KPKbfNwipw1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"A23D5487-3B08-5F91-8890-C98EDD8A0902\",\n \"x-acs-trace-id\": \"1c20039869fc8fc0a71da2c288a059d5\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109348886893\",\n \"RequestId\": \"A23D5487-3B08-5F91-8890-C98EDD8A0902\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:20:54.628471","file":"/aliyun.go:94","func":"func1","gid":"50","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:20:54 GMT\",\n \"etag\": \"8QixS/p2+Z7xHHIZ/VpSwtQ1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"61555477-0B6F-5E19-B3C1-CAB53CC31AFE\",\n \"x-acs-trace-id\": \"eabe03d20719d8258e78779c4bd37c88\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109175763220\",\n \"RequestId\": \"61555477-0B6F-5E19-B3C1-CAB53CC31AFE\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:21:22.460201","file":"/aliyun.go:94","func":"func1","gid":"16","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:21:22 GMT\",\n \"etag\": \"87/gA8cEkLTFvPqJfgP9K1A1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"76FDF640-FBB0-5B0C-827E-70B92FF62C82\",\n \"x-acs-trace-id\": \"4ad06f6435167d8ccb7e36072cd6acb9\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109320087021\",\n \"RequestId\": \"76FDF640-FBB0-5B0C-827E-70B92FF62C82\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:21:47.550484","file":"/aliyun.go:94","func":"func1","gid":"9","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:21:47 GMT\",\n \"etag\": \"8biiNi4vpUJzYstObqFPfxQ1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"0097D9F9-5143-5B06-B211-50C6F0299471\",\n \"x-acs-trace-id\": \"e1d69216717b0323e90275f6c460c320\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109377685846\",\n \"RequestId\": \"0097D9F9-5143-5B06-B211-50C6F0299471\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:22:23.999040","file":"/aliyun.go:94","func":"func1","gid":"11","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:22:23 GMT\",\n \"etag\": \"8nQth23LiOl8DONamRuHNvg1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"51D36408-D2DB-5D49-AB15-C515C1483B2C\",\n \"x-acs-trace-id\": \"8c3710e125841396fd31b65c69fdd265\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109377686445\",\n \"RequestId\": \"51D36408-D2DB-5D49-AB15-C515C1483B2C\"\n }\n} err:\u003cnil\u003e"]}
|
||||
[info]{"time":"2024-09-25 17:22:45.785559","file":"/aliyun.go:94","func":"func1","gid":"50","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Wed, 25 Sep 2024 09:22:45 GMT\",\n \"etag\": \"8ntJrjXOJMSTVmROovNQlSw1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"AB250371-0BF3-5627-92FB-4924E7127958\",\n \"x-acs-trace-id\": \"a8f4491d14db90fa47134b7f428bcb7e\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109262490484\",\n \"RequestId\": \"AB250371-0BF3-5627-92FB-4924E7127958\"\n }\n} err:\u003cnil\u003e"]}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
|
||||
[info]{"time":"2024-09-30 11:31:02.535008","file":"/aliyun.go:26","func":"InitEmail","gid":"53","content":["params:{Smtp:\u003cnil\u003e Aws:\u003cnil\u003e Aliyun:0xc0000a3310 Mailgun:\u003cnil\u003e}"]}
|
||||
[info]{"time":"2024-09-30 11:31:02.541008","file":"/aliyun.go:30","func":"InitEmail","gid":"53","content":["params:\u0026{AccessId:LTAI5tEQ8L8fmDir8udD3CFr AccessKey:llg9M1U56s2SW5PuerlKPvTB1xYhn0 Endpoint:dm.aliyuncs.com AccountName:test@email.aisz.org ReplyAddress:287852692@qq.com}"]}
|
||||
[info]{"time":"2024-09-30 11:31:03.017155","file":"/aliyun.go:96","func":"func1","gid":"53","content":["resp:{\n \"headers\": {\n \"access-control-allow-origin\": \"*\",\n \"access-control-expose-headers\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"81\",\n \"content-type\": \"application/json;charset=utf-8\",\n \"date\": \"Mon, 30 Sep 2024 03:31:02 GMT\",\n \"etag\": \"8f4BKIO+S82BtzQYSJDv3Sg1\",\n \"keep-alive\": \"timeout=25\",\n \"x-acs-request-id\": \"48DE3F8A-AB82-5A9A-9B73-FD422B247391\",\n \"x-acs-trace-id\": \"80bfb0b978c2b82ac64dd59459485ab2\"\n },\n \"statusCode\": 200,\n \"body\": {\n \"EnvId\": \"600000109324064279\",\n \"RequestId\": \"48DE3F8A-AB82-5A9A-9B73-FD422B247391\"\n }\n} err:\u003cnil\u003e"]}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
// 不支持变更发信人(必须配置好)
|
||||
|
||||
type Aws struct {
|
||||
interfaces.DefaultEmail
|
||||
params *interfaces.EmailConfigDataAws
|
||||
logger loggerx.LoggerInterface
|
||||
}
|
||||
|
||||
func (l *Aws) InitEmail(ctx context.Context, params interfaces.EmailConfigData, logger loggerx.LoggerInterface) (interfaces.Email, error) {
|
||||
l.logger.Infof(ctx, "params:%+v", params)
|
||||
if params.Aws == nil {
|
||||
return nil, errors.New("not aws")
|
||||
}
|
||||
l.logger.Infof(ctx, "params:%+v", params.Aws)
|
||||
if params.Aws.Region == "" {
|
||||
params.Aws.Region = "ap-northeast-1"
|
||||
}
|
||||
|
||||
return &Aws{
|
||||
params: params.Aws,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *Aws) Send(ctx context.Context, params interfaces.Message) error {
|
||||
if l.params == nil {
|
||||
return errors.New("not init")
|
||||
}
|
||||
// 配置AWS认证信息
|
||||
config := aws.Config{
|
||||
Region: aws.String(l.params.Region), // 设置你的AWS区域
|
||||
Credentials: credentials.NewStaticCredentials(l.params.AccessId, l.params.AccessSecret, ""),
|
||||
}
|
||||
|
||||
// 创建AWS会话
|
||||
sess := session.Must(session.NewSession(&config))
|
||||
|
||||
// 创建SES客户端
|
||||
svc := ses.New(sess)
|
||||
|
||||
toAddress := []*string{}
|
||||
for _, val := range params.To {
|
||||
toAddress = append(toAddress, aws.String(val))
|
||||
}
|
||||
|
||||
// 使用SES服务发送邮件
|
||||
_, err := svc.SendEmail(&ses.SendEmailInput{
|
||||
Destination: &ses.Destination{
|
||||
ToAddresses: toAddress,
|
||||
},
|
||||
Message: &ses.Message{
|
||||
Body: &ses.Body{
|
||||
Html: &ses.Content{
|
||||
Data: aws.String(params.Body),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Subject: &ses.Content{
|
||||
Data: aws.String(params.Subject),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Source: aws.String(l.params.Sender),
|
||||
})
|
||||
|
||||
// svc.SendRawEmail()
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx/aws"
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
// https://ap-northeast-1.console.aws.amazon.com/ses/home?region=ap-northeast-1#/identities
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
// email:
|
||||
// #区域
|
||||
// AwsRegion: "ap-northeast-1"
|
||||
// #秘钥ID
|
||||
// AwsAccessKeyId: "AKIAU6GD3MNRHKR4RZG5"
|
||||
// #秘钥
|
||||
// AwsSecretAccessKey: "GSdGuFbZlcpVHMODlqeIKr07R/BdTBGeurq0s+4l"
|
||||
// #发件人
|
||||
// Source: "chenlihan@dreaminglife.cn"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
logger := loggerx.NewLogger(ctx)
|
||||
|
||||
mail := aws.Aws{}
|
||||
ini, err := mail.InitEmail(ctx, interfaces.EmailConfigData{
|
||||
Aws: &interfaces.EmailConfigDataAws{
|
||||
AccessId: "AKIAU6GD3MNRHKR4RZG5",
|
||||
AccessSecret: "GSdGuFbZlcpVHMODlqeIKr07R/BdTBGeurq0s+4l",
|
||||
Region: "ap-northeast-1",
|
||||
Sender: "chenlihan@dreaminglife.cn",
|
||||
},
|
||||
}, logger)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ini.Send(ctx, interfaces.Message{
|
||||
Form: "chenlihan@dreaminglife.cn",
|
||||
To: []string{"huangxinyun@dreaminglife.cn"},
|
||||
Body: "Hello",
|
||||
Subject: "主题",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("send success")
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
[info]{"time":"2024-09-27 18:32:19.629859","file":"/aws.go:23","func":"InitEmail","gid":"33","content":["params:{Smtp:\u003cnil\u003e Aws:0xc000698080 Aliyun:\u003cnil\u003e Mailgun:\u003cnil\u003e}"]}
|
||||
[info]{"time":"2024-09-27 18:32:30.207798","file":"/aws.go:23","func":"InitEmail","gid":"28","content":["params:{Smtp:\u003cnil\u003e Aws:0xc000565400 Aliyun:\u003cnil\u003e Mailgun:\u003cnil\u003e}"]}
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
[info]{"time":"2024-09-30 11:31:01.195981","file":"/aws.go:23","func":"InitEmail","gid":"44","content":["params:{Smtp:\u003cnil\u003e Aws:0xc00078c080 Aliyun:\u003cnil\u003e Mailgun:\u003cnil\u003e}"]}
|
||||
[info]{"time":"2024-09-30 11:31:01.201642","file":"/aws.go:27","func":"InitEmail","gid":"44","content":["params:\u0026{AccessId:AKIAU6GD3MNRHKR4RZG5 AccessSecret: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 Region:ap-northeast-1 Sender:chenlihan@dreaminglife.cn}"]}
|
||||
@@ -1,10 +1,59 @@
|
||||
module code.yun.ink/pkg/mailx
|
||||
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
require github.com/PuerkitoBio/goquery v1.9.2
|
||||
toolchain go1.22.6
|
||||
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.9.2
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
|
||||
github.com/alibabacloud-go/dm-20151123/v2 v2.2.3
|
||||
github.com/alibabacloud-go/tea v1.2.2
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
||||
github.com/aws/aws-sdk-go v1.55.5
|
||||
github.com/mailgun/mailgun-go/v4 v4.18.5
|
||||
github.com/yuninks/loggerx v1.0.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
||||
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
||||
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||
github.com/aliyun/credentials-go v1.3.10 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mailgun/errors v0.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,40 +1,326 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
|
||||
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
||||
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
||||
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
||||
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
||||
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
|
||||
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
|
||||
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
|
||||
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
|
||||
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
|
||||
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
|
||||
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
|
||||
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
|
||||
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
|
||||
github.com/alibabacloud-go/dm-20151123/v2 v2.2.3 h1:nEq/9OGxV2yiB3pP100TSn1Av4k4QJvRTu1pGkyhUS4=
|
||||
github.com/alibabacloud-go/dm-20151123/v2 v2.2.3/go.mod h1:82WXJL+riPgj7BxYHwaLmVIMdLaMW0X8Azh+GZGjESQ=
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
|
||||
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
|
||||
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
|
||||
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
|
||||
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
|
||||
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||
github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA=
|
||||
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
|
||||
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
||||
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
|
||||
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
||||
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
|
||||
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
|
||||
github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA=
|
||||
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mailgun/errors v0.3.0 h1:g8R8lodkwqk5WIVMAClyUqt0PSd5JTVgobB+H7C2sLs=
|
||||
github.com/mailgun/errors v0.3.0/go.mod h1:+ltknP+jhv3gZ1StKY6ugoQECcPxDCaSdmYesqTZcLQ=
|
||||
github.com/mailgun/mailgun-go/v4 v4.18.5 h1:wZnTutW/fzxNyJVBMa6O4LLGW/hO+IGfKpuqBT4nbVs=
|
||||
github.com/mailgun/mailgun-go/v4 v4.18.5/go.mod h1:+d4FCswFAukgYc1XtKK2IxOYaVxjVm8AN2z/5TBiT8M=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuninks/loggerx v1.0.12 h1:5XxhSo5bQZEjLRdJ9FPmj+uS018s1meo/9OhG9y2hUg=
|
||||
github.com/yuninks/loggerx v1.0.12/go.mod h1:+QFoywQ1ICh4v40zj6OHM8GBZHEqV0yvRbkZjZUe2o4=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// 解析HTML资源,响应资源链接
|
||||
// html路径替换
|
||||
func ParseHtmlResource(html string) ([]string, error) {
|
||||
resp := []string{}
|
||||
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@ package mailx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx"
|
||||
"wallet-pay-api/pkg/mailx"
|
||||
)
|
||||
|
||||
func TestParseHtmlResource(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
type Email interface {
|
||||
InitEmail(ctx context.Context, params EmailConfigData,logger loggerx.LoggerInterface) (Email, error)
|
||||
// Send 发送邮件
|
||||
Send(ctx context.Context, params Message) error
|
||||
}
|
||||
|
||||
type EmailConfigData struct {
|
||||
Smtp *EmailConfigDataSmtp `json:"smtp,omitempty"` // smtp
|
||||
Aws *EmailConfigDataAws `json:"aws,omitempty"` // 亚马逊
|
||||
Aliyun *EmialConfigDataAliyun `json:"aliyun,omitempty"` // 阿里云
|
||||
Mailgun *EmialConfigDataMailgun `json:"mailgun,omitempty"` // mailgun
|
||||
}
|
||||
|
||||
type EmialConfigDataMailgun struct {
|
||||
ApiKey string `json:"api_key"` // mailgun api key
|
||||
Domain string `json:"domain"` // mailgun domain
|
||||
Sender string `json:"sender"` // 发件人
|
||||
}
|
||||
|
||||
type EmailConfigDataSmtp struct {
|
||||
Username string `json:"username"` // 邮箱账号
|
||||
Password string `json:"password"` // 授权码
|
||||
Host string `json:"host"` // SMTP 服务器【默认smtpdm.aliyun.com】
|
||||
Port string `json:"port"` // 发信端口
|
||||
ReplyTo string `json:"reply_to"` // 【选填】回复地址
|
||||
// From string `json:"from"` // 【选填】阿里云邮箱发件人
|
||||
}
|
||||
|
||||
type EmailConfigDataAws struct {
|
||||
AccessId string `json:"access_id"` // 亚马逊AccessId
|
||||
AccessSecret string `json:"access_secret"` // 亚马逊AccessSecret
|
||||
Region string `json:"region"` // 亚马逊Region
|
||||
Sender string `json:"sender"` // 亚马逊发件人
|
||||
}
|
||||
|
||||
type EmialConfigDataAliyun struct {
|
||||
AccessId string `json:"access_id"` // 阿里云AccessId
|
||||
AccessKey string `json:"access_key"` // 阿里云AccessKey
|
||||
Endpoint string `json:"endpoint"` // 区域 默认dm.aliyuncs.com
|
||||
AccountName string `json:"account_name"` // 账号名
|
||||
ReplyAddress string `json:"reply_address"` // 邮件回复地址
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Form string
|
||||
To []string
|
||||
Cc []string
|
||||
Bcc []string
|
||||
Subject string
|
||||
Body string
|
||||
ReplyTo string
|
||||
Attachment []MessageAttachment // 附件
|
||||
}
|
||||
|
||||
type MessageAttachment struct {
|
||||
Content string
|
||||
ContentType string
|
||||
}
|
||||
|
||||
type EmailSendRecord struct {
|
||||
AccountName string // 发件人
|
||||
UpdateTime int64 // 毫秒时间戳
|
||||
Status EmailSendStatus // 状态
|
||||
ToUser string // 收件人
|
||||
Subject string // 邮件主题
|
||||
ErrorMessage string // 错误信息
|
||||
}
|
||||
|
||||
type EmailSendStatus int
|
||||
|
||||
const (
|
||||
EmailSendStatusUnknown EmailSendStatus = 0
|
||||
EmailSendStatusSuccess EmailSendStatus = 1
|
||||
EmailSendStatusInvalidAddress EmailSendStatus = 2
|
||||
EmailSendStatusSpam EmailSendStatus = 3
|
||||
EmailSendStatusFailed EmailSendStatus = 4
|
||||
)
|
||||
|
||||
type DefaultEmail struct{}
|
||||
|
||||
func (l *DefaultEmail) InitEmail(ctx context.Context, params EmailConfigData,logger loggerx.LoggerInterface) (Email, error) {
|
||||
return &DefaultEmail{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (l *DefaultEmail) Send(ctx context.Context, params Message) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
[error]{"time":"2024-09-30 11:31:04.522647","file":"/mailgun.go:40","func":"Send","gid":"47","content":["Could not send email: UnexpectedResponseError URL=https://api.mailgun.net/v3/sandboxd045b2448880433785c34f72a7fd0d45.mailgun.org/messages ExpectedOneOf=[]int{200, 202, 204} Got=403 Error: {\"message\":\"Domain sandboxd045b2448880433785c34f72a7fd0d45.mailgun.org is not allowed to send: Please activate your Mailgun account. Check your inbox or log in to your control panel to resend the activation email.\"}\n, resp message: , id: "]}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
[info]{"time":"2024-09-30 11:31:03.950305","file":"/mailgun.go:23","func":"InitEmail","gid":"47","content":["params:\u0026{ApiKey:b87ece56257aa282dd3c809f683c60e5-32a0fef1-6327df27 Domain:sandboxd045b2448880433785c34f72a7fd0d45.mailgun.org Sender:zhaoyang@dreaminglife.cn}"]}
|
||||
@@ -0,0 +1,46 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
type MailGun struct {
|
||||
interfaces.DefaultEmail
|
||||
params *interfaces.EmialConfigDataMailgun
|
||||
mg *mailgun.MailgunImpl
|
||||
logger loggerx.LoggerInterface
|
||||
}
|
||||
|
||||
func (l *MailGun) InitEmail(ctx context.Context, params interfaces.EmailConfigData, logger loggerx.LoggerInterface) (interfaces.Email, error) {
|
||||
|
||||
if params.Mailgun == nil {
|
||||
return nil, errors.New("not mailgun")
|
||||
}
|
||||
l.logger.Infof(ctx, "params:%+v", params.Mailgun)
|
||||
mg := mailgun.NewMailgun(params.Mailgun.Domain, params.Mailgun.ApiKey)
|
||||
return &MailGun{
|
||||
params: params.Mailgun,
|
||||
mg: mg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *MailGun) Send(ctx context.Context, params interfaces.Message) error {
|
||||
if l.params == nil {
|
||||
return errors.New("not init")
|
||||
}
|
||||
|
||||
message := l.mg.NewMessage(l.params.Sender, params.Subject, params.Body, params.To...)
|
||||
|
||||
resp, id, err := l.mg.Send(ctx, message)
|
||||
if err != nil {
|
||||
l.logger.Errorf(ctx, "Could not send email: %v, resp message: %s, id: %s", err, resp, id)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package mailgun_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"code.yun.ink/pkg/mailx/mailgun"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
var (
|
||||
apikey = "b87ece56257aa282dd3c809f683c60e5-32a0fef1-6327df27"
|
||||
domain = "sandboxd045b2448880433785c34f72a7fd0d45.mailgun.org"
|
||||
sender = "zhaoyang@dreaminglife.cn"
|
||||
)
|
||||
|
||||
func TestSendEmail(t *testing.T) {
|
||||
gun := &mailgun.MailGun{}
|
||||
ctx := context.Background()
|
||||
|
||||
logger := loggerx.NewLogger(ctx)
|
||||
|
||||
ini, err := gun.InitEmail(ctx, interfaces.EmailConfigData{
|
||||
Mailgun: &interfaces.EmialConfigDataMailgun{
|
||||
ApiKey: apikey,
|
||||
Domain: domain,
|
||||
Sender: sender,
|
||||
},
|
||||
}, logger)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ini.Send(ctx, interfaces.Message{
|
||||
To: []string{"995116474@qq.com"},
|
||||
Subject: "test mail",
|
||||
Body: "Hello",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("send success")
|
||||
|
||||
}
|
||||
@@ -1,182 +1,41 @@
|
||||
package mailx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.yun.ink/pkg/mailx/aliyun"
|
||||
"code.yun.ink/pkg/mailx/aws"
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"code.yun.ink/pkg/mailx/mailgun"
|
||||
"code.yun.ink/pkg/mailx/smtp"
|
||||
)
|
||||
|
||||
// 邮件发送的封装
|
||||
// 1. 支持文本
|
||||
// 2. 支持文件
|
||||
var platform map[consts.Platform3rdType]interfaces.Email
|
||||
|
||||
// 注册
|
||||
func init() {
|
||||
platform = make(map[consts.Platform3rdType]interfaces.Email)
|
||||
|
||||
// 阿里
|
||||
platform[consts.Platform3rdTypeAliyun] = &aliyun.Aliyun{}
|
||||
|
||||
// AWS
|
||||
platform[consts.Platform3rdTypeAws] = &aws.Aws{}
|
||||
|
||||
// Smtp
|
||||
platform[consts.Platform3rdTypeSmtp] = &smtp.Smtp{}
|
||||
|
||||
// mailgun
|
||||
platform[consts.Platform3rdTypeMailgun] = &mailgun.MailGun{}
|
||||
|
||||
type Mailx struct {
|
||||
user string
|
||||
password string
|
||||
host string
|
||||
port string
|
||||
auth smtp.Auth
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
Name string
|
||||
ContentType string
|
||||
WithFile bool
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Form string
|
||||
To []string
|
||||
Cc []string
|
||||
Bcc []string
|
||||
Subject string
|
||||
Body string
|
||||
// ContentType string
|
||||
ReplyTo string
|
||||
Attachment []Attachment
|
||||
}
|
||||
|
||||
func NewMailx(user, password, host, port string) *Mailx {
|
||||
m := &Mailx{
|
||||
user: user,
|
||||
password: password,
|
||||
host: host,
|
||||
port: port,
|
||||
}
|
||||
m.auth = smtp.PlainAuth("", user, password, host)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Mailx) Send(message Message) error {
|
||||
// .Auth()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
boundary := "YunBoundaryYun"
|
||||
|
||||
Header := make(map[string]string)
|
||||
// Header["From"] = "BOP<" + message.Form + ">"
|
||||
Header["From"] = m.user
|
||||
|
||||
if len(message.To) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.To {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["To"] = strings.Trim(str, ",")
|
||||
// Header["To"] = strings.Join(message.To, ",")
|
||||
}
|
||||
if len(message.Cc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Cc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Cc"] = strings.Trim(str, ",")
|
||||
// Header["Cc"] = strings.Join(message.Cc, ",")
|
||||
}
|
||||
if len(message.Bcc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Bcc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Bcc"] = strings.Trim(str, ",")
|
||||
// Header["Bcc"] = strings.Join(message.Bcc, ",")
|
||||
}
|
||||
|
||||
Header["Subject"] = message.Subject
|
||||
Header["Content-Type"] = "multipart/mixed; charset=UTF-8; boundary=" + boundary
|
||||
Header["Date"] = time.Now().String()
|
||||
Header["Reply-To"] = message.ReplyTo
|
||||
|
||||
Header["X-Priority"] = "3"
|
||||
m.writeHeader(buffer, Header)
|
||||
|
||||
body := "--" + boundary + "\r\n"
|
||||
// body += "Content-Type: text/plain; charset=UTF-8 \r\n"
|
||||
body += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
body += "Content-Transfer-Encoding:quoted-printable\r\n\r\n"
|
||||
// body += "<html><body><h1>huang</h1><h2>xin</h2></body></html>\r\n"
|
||||
body += "<html><body>" + message.Body + "</body></html>\r\n"
|
||||
// body += "--" + boundary + "--\r\n\r\n"
|
||||
buffer.WriteString(body)
|
||||
|
||||
for _, value := range message.Attachment {
|
||||
newBuf := bytes.NewBuffer(nil)
|
||||
err := m.writeFile(newBuf, value.Name)
|
||||
if err != nil {
|
||||
fmt.Println("file err:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
f_name := path.Base(value.Name)
|
||||
attachment := "--" + boundary + "\r\n"
|
||||
attachment += "Content-Transfer-Encoding:base64\r\n"
|
||||
attachment += "Content-Disposition:attachment;filename=" + f_name + "\r\n"
|
||||
attachment += "Content-Type: application/octet-stream;charset=utf-8;name=" + f_name + "\r\n"
|
||||
// attachment += "Contment-Type:" + message.attachment.contentType + ";name=\"" + message.attachment.name + "\"\r\n"
|
||||
buffer.WriteString(attachment)
|
||||
|
||||
buffer.WriteString(newBuf.String())
|
||||
}
|
||||
|
||||
if message.Form == "" {
|
||||
message.Form = m.user
|
||||
}
|
||||
|
||||
buffer.WriteString("\r\n--" + boundary + "--\r\n")
|
||||
b := buffer.Bytes()
|
||||
err := smtp.SendMail(m.host+":"+m.port, m.auth, message.Form, message.To, b)
|
||||
fmt.Println("发送结束:", err)
|
||||
fmt.Println(string(b))
|
||||
return err
|
||||
}
|
||||
|
||||
// 格式化header
|
||||
func (m *Mailx) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
|
||||
header := ""
|
||||
// header := "Content-Type: multipart/mixed;charset=UTF-8;boundary=\"YunBoundaryYun\" \r\n"
|
||||
for key, value := range Header {
|
||||
if value != "" {
|
||||
header += key + ": " + value + "\r\n"
|
||||
}
|
||||
}
|
||||
header += "\r\n"
|
||||
buffer.WriteString(header)
|
||||
return header
|
||||
}
|
||||
|
||||
// 格式化文件
|
||||
func (m *Mailx) writeFile(buffer *bytes.Buffer, fileName string) error {
|
||||
file, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := make([]byte, base64.StdEncoding.EncodedLen(len(file)))
|
||||
base64.StdEncoding.Encode(payload, file)
|
||||
buffer.WriteString("\r\n")
|
||||
for index, line := 0, len(payload); index < line; index++ {
|
||||
buffer.WriteByte(payload[index])
|
||||
if (index+1)%76 == 0 {
|
||||
buffer.WriteString("\r\n")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
func GetPlatform(ctx context.Context, plat consts.Platform3rdType) (interfaces.Email, error) {
|
||||
iplat, ok := platform[plat]
|
||||
if ok {
|
||||
return iplat, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
hhhhhhhhhhh
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
+77
@@ -0,0 +1,77 @@
|
||||
package mailx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// 解析HTML资源,响应资源链接
|
||||
func ParseHtmlResource(html string) ([]string, error) {
|
||||
resp := []string{}
|
||||
|
||||
b := bytes.NewBufferString(html)
|
||||
doc, err := goquery.NewDocumentFromReader(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 找到所有的 css 标签,并且打印它们的 href 属性
|
||||
doc.Find("link").Each(func(i int, s *goquery.Selection) {
|
||||
// 忽略dns预请求
|
||||
r, ok := s.Attr("rel")
|
||||
if ok && r == "dns-prefetch" {
|
||||
return
|
||||
}
|
||||
|
||||
href, ok := s.Attr("href")
|
||||
if ok && href != "" {
|
||||
resp = append(resp, href)
|
||||
}
|
||||
})
|
||||
|
||||
// 找到所有的 script 标签,并且打印它们的 src 属性
|
||||
doc.Find("script").Each(func(i int, s *goquery.Selection) {
|
||||
src, ok := s.Attr("src")
|
||||
if ok && src != "" {
|
||||
resp = append(resp, src)
|
||||
}
|
||||
})
|
||||
|
||||
// 找到所有的 img 标签,并且打印它们的 src 属性
|
||||
doc.Find("img").Each(func(i int, s *goquery.Selection) {
|
||||
src, ok := s.Attr("src")
|
||||
if ok && src != "" {
|
||||
resp = append(resp, src)
|
||||
}
|
||||
data_src, ok := s.Attr("data-src")
|
||||
if ok && data_src != "" {
|
||||
resp = append(resp, data_src)
|
||||
}
|
||||
})
|
||||
|
||||
// 找到所有的 video 标签,并且打印它们的 src 属性
|
||||
doc.Find("video").Each(func(i int, s *goquery.Selection) {
|
||||
src, ok := s.Attr("src")
|
||||
if ok && src != "" {
|
||||
resp = append(resp, src)
|
||||
}
|
||||
|
||||
data_src, ok := s.Attr("data-src")
|
||||
if ok && data_src != "" {
|
||||
resp = append(resp, data_src)
|
||||
}
|
||||
})
|
||||
|
||||
// 找到所有的 audio 标签,并且打印它们的 src 属性
|
||||
doc.Find("audio").Each(func(i int, s *goquery.Selection) {
|
||||
src, ok := s.Attr("src")
|
||||
|
||||
if ok && src != "" {
|
||||
resp = append(resp, src)
|
||||
}
|
||||
})
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package mailx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx"
|
||||
)
|
||||
|
||||
func TestParseHtmlResource(t *testing.T) {
|
||||
html := `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="./assets/css/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="header flex-center">
|
||||
<img src="./assets/img/ab-pay-logo.png" alt="" class="logo" />
|
||||
</header>
|
||||
|
||||
<main class="code-main">
|
||||
<div class="exception-title">您的收款方式审核失败</div>
|
||||
|
||||
<div class="exception-content">
|
||||
尊敬的ABpay用户,您好!
|
||||
您的收款方式审核失败,失败原因人脸识别失败,请检查您的面部是否被遮挡或处于模糊状态,并再次尝试。
|
||||
如非本人操作,请立即修改密码或联系客服。
|
||||
</div>
|
||||
|
||||
<div class="exception-ignore">这是一条自动发送的消息,请勿回复</div>
|
||||
|
||||
<li class="exception-team-tips">ABpay开发者团队服务</li>
|
||||
|
||||
<section class="contact-us">
|
||||
<div class="contact-us-info">
|
||||
本邮件由系统自动发出请勿回复,如需要了解更多服务,欢迎访问ABpay官方网
|
||||
还可以通以下方式联系我们
|
||||
</div>
|
||||
|
||||
<img src="./assets/img/contact-us-qrcode.png" alt="" class="qr-code" />
|
||||
<div class="contact-tel">
|
||||
客服电话:<span class="contact-tel-number">400-278-2890</span>
|
||||
</div>
|
||||
<div class="contact-created-version">2024 ABpay.com.cn . All rightes reserved</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
res, err := mailx.ParseHtmlResource(html)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(res)
|
||||
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
package mailx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 邮件发送的封装
|
||||
// 1. 支持文本
|
||||
// 2. 支持文件
|
||||
|
||||
type Mailx struct {
|
||||
user string
|
||||
password string
|
||||
host string
|
||||
port string
|
||||
auth smtp.Auth
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
Name string
|
||||
ContentType string
|
||||
WithFile bool
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Form string
|
||||
To []string
|
||||
Cc []string
|
||||
Bcc []string
|
||||
Subject string
|
||||
Body string
|
||||
// ContentType string
|
||||
ReplyTo string
|
||||
Attachment []Attachment
|
||||
}
|
||||
|
||||
func NewMailx(user, password, host, port string) *Mailx {
|
||||
m := &Mailx{
|
||||
user: user,
|
||||
password: password,
|
||||
host: host,
|
||||
port: port,
|
||||
}
|
||||
m.auth = smtp.PlainAuth("", user, password, host)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Mailx) Send(message Message) error {
|
||||
// .Auth()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
boundary := "YunBoundaryYun"
|
||||
|
||||
Header := make(map[string]string)
|
||||
// Header["From"] = "BOP<" + message.Form + ">"
|
||||
Header["From"] = m.user
|
||||
|
||||
if len(message.To) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.To {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["To"] = strings.Trim(str, ",")
|
||||
// Header["To"] = strings.Join(message.To, ",")
|
||||
}
|
||||
if len(message.Cc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Cc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Cc"] = strings.Trim(str, ",")
|
||||
// Header["Cc"] = strings.Join(message.Cc, ",")
|
||||
}
|
||||
if len(message.Bcc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Bcc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Bcc"] = strings.Trim(str, ",")
|
||||
// Header["Bcc"] = strings.Join(message.Bcc, ",")
|
||||
}
|
||||
|
||||
Header["Subject"] = message.Subject
|
||||
Header["Content-Type"] = "multipart/mixed; charset=UTF-8; boundary=" + boundary
|
||||
Header["Date"] = time.Now().String()
|
||||
Header["Reply-To"] = message.ReplyTo
|
||||
|
||||
Header["X-Priority"] = "3"
|
||||
m.writeHeader(buffer, Header)
|
||||
|
||||
body := "--" + boundary + "\r\n"
|
||||
// body += "Content-Type: text/plain; charset=UTF-8 \r\n"
|
||||
body += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
body += "Content-Transfer-Encoding:quoted-printable\r\n\r\n"
|
||||
// body += "<html><body><h1>huang</h1><h2>xin</h2></body></html>\r\n"
|
||||
body += "<html><body>" + message.Body + "</body></html>\r\n"
|
||||
// body += "--" + boundary + "--\r\n\r\n"
|
||||
buffer.WriteString(body)
|
||||
|
||||
for _, value := range message.Attachment {
|
||||
newBuf := bytes.NewBuffer(nil)
|
||||
err := m.writeFile(newBuf, value.Name)
|
||||
if err != nil {
|
||||
fmt.Println("file err:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
f_name := path.Base(value.Name)
|
||||
attachment := "--" + boundary + "\r\n"
|
||||
attachment += "Content-Transfer-Encoding:base64\r\n"
|
||||
attachment += "Content-Disposition:attachment;filename=" + f_name + "\r\n"
|
||||
attachment += "Content-Type: application/octet-stream;charset=utf-8;name=" + f_name + "\r\n"
|
||||
// attachment += "Contment-Type:" + message.attachment.contentType + ";name=\"" + message.attachment.name + "\"\r\n"
|
||||
buffer.WriteString(attachment)
|
||||
|
||||
buffer.WriteString(newBuf.String())
|
||||
|
||||
buffer.WriteString("\r\n")
|
||||
}
|
||||
|
||||
if message.Form == "" {
|
||||
message.Form = m.user
|
||||
}
|
||||
|
||||
imgBuf := bytes.NewBuffer(nil)
|
||||
err := m.writeFile(imgBuf, "./asset/余额宝.png")
|
||||
if err != nil {
|
||||
return fmt.Errorf("file err: %w", err)
|
||||
}
|
||||
f_name:= "f_name"
|
||||
attachment := "--" + boundary + "\r\n"
|
||||
attachment += "Content-Transfer-Encoding:base64\r\n"
|
||||
attachment += "Content-ID:myimage \r\n"
|
||||
attachment += "Content-Disposition:inline;filename=" + f_name + ".png \r\n"
|
||||
attachment += "Content-Type:image/png \r\n"
|
||||
buffer.WriteString(attachment)
|
||||
|
||||
buffer.WriteString(imgBuf.String())
|
||||
|
||||
buffer.WriteString("\r\n--" + boundary + "--\r\n")
|
||||
|
||||
b := buffer.Bytes()
|
||||
err = smtp.SendMail(m.host+":"+m.port, m.auth, message.Form, message.To, b)
|
||||
fmt.Println("发送结束:", err)
|
||||
fmt.Println(string(b))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 格式化header
|
||||
func (m *Mailx) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
|
||||
header := ""
|
||||
// header := "Content-Type: multipart/mixed;charset=UTF-8;boundary=\"YunBoundaryYun\" \r\n"
|
||||
for key, value := range Header {
|
||||
if value != "" {
|
||||
header += key + ": " + value + "\r\n"
|
||||
}
|
||||
}
|
||||
header += "\r\n"
|
||||
buffer.WriteString(header)
|
||||
return header
|
||||
}
|
||||
|
||||
// 格式化文件
|
||||
func (m *Mailx) writeFile(buffer *bytes.Buffer, fileName string) error {
|
||||
file, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := make([]byte, base64.StdEncoding.EncodedLen(len(file)))
|
||||
base64.StdEncoding.Encode(payload, file)
|
||||
buffer.WriteString("\r\n")
|
||||
for index, line := 0, len(payload); index < line; index++ {
|
||||
buffer.WriteByte(payload[index])
|
||||
if (index+1)%76 == 0 {
|
||||
buffer.WriteString("\r\n")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx"
|
||||
mailx "code.yun.ink/pkg/mailx/old"
|
||||
)
|
||||
|
||||
func TestMail(t *testing.T) {
|
||||
@@ -12,17 +12,17 @@ func TestMail(t *testing.T) {
|
||||
|
||||
msg := mailx.Message{
|
||||
// Form: "support@email.blueoceanpay.com",
|
||||
To: []string{"yun@blueoceanpay.com", "995116474@qq.com"},
|
||||
Cc: []string{"287852692@qq.com"},
|
||||
Bcc: []string{"1362716835@qq.com"},
|
||||
Subject: "test mail",
|
||||
Body: "测试文件",
|
||||
To: []string{"huangxinyun520@gmail.com", "995116474@qq.com"},
|
||||
// Cc: []string{"287852692@qq.com"},
|
||||
// Bcc: []string{"1362716835@qq.com"},
|
||||
Subject: "test mail",
|
||||
Body: "<img src=\"cid:myimage\">dasdsadsasda<br>dasdsadsadsa",
|
||||
Attachment: []mailx.Attachment{
|
||||
// {
|
||||
// Name: "/code/statistic/out.xlsx",
|
||||
// ContentType: "",
|
||||
// WithFile: true,
|
||||
// },
|
||||
{
|
||||
Name: "asset/hhh.txt",
|
||||
ContentType: "",
|
||||
WithFile: true,
|
||||
},
|
||||
// {
|
||||
// Name: "/code/statistic/origin.xlsx",
|
||||
// ContentType: "",
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
[info]{"time":"2024-09-27 18:32:44.687712","file":"/smtp.go:29","func":"InitEmail","gid":"70","content":["params:{Smtp:0xc00013a910 Aws:\u003cnil\u003e Aliyun:\u003cnil\u003e Mailgun:\u003cnil\u003e}"]}
|
||||
[info]{"time":"2024-09-27 18:54:34.654603","file":"/smtp.go:29","func":"InitEmail","gid":"43","content":["params:{Smtp:0xc0004780f0 Aws:\u003cnil\u003e Aliyun:\u003cnil\u003e Mailgun:\u003cnil\u003e}"]}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
[info]{"time":"2024-09-30 11:31:05.402488","file":"/smtp.go:33","func":"InitEmail","gid":"54","content":["params:\u0026{Username:test@email.aisz.org Password:FDuSoJKiC9tNSDIG6DFh Host:smtpdm.aliyun.com Port:80 ReplyTo:}"]}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
// 邮件发送的封装
|
||||
// 1. 支持文本
|
||||
// 2. 支持文件
|
||||
|
||||
type Smtp struct {
|
||||
interfaces.DefaultEmail
|
||||
params *interfaces.EmailConfigDataSmtp
|
||||
auth smtp.Auth
|
||||
logger loggerx.LoggerInterface
|
||||
}
|
||||
|
||||
func (l *Smtp) InitEmail(ctx context.Context, params interfaces.EmailConfigData, logger loggerx.LoggerInterface) (interfaces.Email, error) {
|
||||
|
||||
if params.Smtp == nil {
|
||||
return nil, errors.New("not smtp")
|
||||
}
|
||||
l.logger.Infof(ctx, "params:%+v", params.Smtp)
|
||||
|
||||
m := &Smtp{
|
||||
params: params.Smtp,
|
||||
}
|
||||
m.auth = smtp.PlainAuth("", params.Smtp.Username, params.Smtp.Password, params.Smtp.Host)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
|
||||
if l.params == nil {
|
||||
return errors.New("not init")
|
||||
}
|
||||
// .Auth()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
boundary := "YunBoundaryYun"
|
||||
|
||||
Header := make(map[string]string)
|
||||
// Header["From"] = "BOP<" + message.Form + ">"
|
||||
|
||||
if message.Form != "" {
|
||||
Header["From"] = message.Form
|
||||
} else {
|
||||
Header["From"] = l.params.Username
|
||||
}
|
||||
|
||||
if len(message.To) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.To {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["To"] = strings.Trim(str, ",")
|
||||
// Header["To"] = strings.Join(message.To, ",")
|
||||
}
|
||||
if len(message.Cc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Cc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Cc"] = strings.Trim(str, ",")
|
||||
// Header["Cc"] = strings.Join(message.Cc, ",")
|
||||
}
|
||||
if len(message.Bcc) > 0 {
|
||||
str := ""
|
||||
for _, val := range message.Bcc {
|
||||
name := ""
|
||||
s := strings.Split(val, "@")
|
||||
if len(s) > 0 {
|
||||
name = s[0]
|
||||
}
|
||||
str = str + "," + name + "<" + val + ">"
|
||||
}
|
||||
Header["Bcc"] = strings.Trim(str, ",")
|
||||
// Header["Bcc"] = strings.Join(message.Bcc, ",")
|
||||
}
|
||||
|
||||
Header["Subject"] = message.Subject
|
||||
Header["Content-Type"] = "multipart/mixed; charset=UTF-8; boundary=" + boundary
|
||||
Header["Date"] = time.Now().String()
|
||||
Header["Reply-To"] = message.ReplyTo
|
||||
|
||||
Header["X-Priority"] = "3"
|
||||
l.writeHeader(buffer, Header)
|
||||
|
||||
body := "--" + boundary + "\r\n"
|
||||
// body += "Content-Type: text/plain; charset=UTF-8 \r\n"
|
||||
body += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
body += "Content-Transfer-Encoding:quoted-printable\r\n\r\n"
|
||||
// body += "<html><body><h1>huang</h1><h2>xin</h2></body></html>\r\n"
|
||||
|
||||
// body += "<html><body>" + message.Body + "</body></html>\r\n"
|
||||
|
||||
body += message.Body + "\r\n"
|
||||
|
||||
// body += "--" + boundary + "--\r\n\r\n"
|
||||
buffer.WriteString(body)
|
||||
|
||||
for _, value := range message.Attachment {
|
||||
newBuf := bytes.NewBuffer(nil)
|
||||
err := l.writeFile(newBuf, value.Content)
|
||||
if err != nil {
|
||||
fmt.Println("file err:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
f_name := path.Base(value.Content)
|
||||
attachment := "--" + boundary + "\r\n"
|
||||
attachment += "Content-Transfer-Encoding:base64\r\n"
|
||||
attachment += "Content-Disposition:attachment;filename=" + f_name + "\r\n"
|
||||
attachment += "Content-Type: application/octet-stream;charset=utf-8;name=" + f_name + "\r\n"
|
||||
// attachment += "Contment-Type:" + message.attachment.contentType + ";name=\"" + message.attachment.name + "\"\r\n"
|
||||
buffer.WriteString(attachment)
|
||||
|
||||
buffer.WriteString(newBuf.String())
|
||||
}
|
||||
|
||||
buffer.WriteString("\r\n--" + boundary + "--\r\n")
|
||||
b := buffer.Bytes()
|
||||
err := smtp.SendMail(l.params.Host+":"+l.params.Port, l.auth, l.params.Username, message.To, b)
|
||||
return err
|
||||
}
|
||||
|
||||
// 格式化header
|
||||
func (l *Smtp) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
|
||||
header := ""
|
||||
// header := "Content-Type: multipart/mixed;charset=UTF-8;boundary=\"YunBoundaryYun\" \r\n"
|
||||
for key, value := range Header {
|
||||
if value != "" {
|
||||
header += key + ": " + value + "\r\n"
|
||||
}
|
||||
}
|
||||
header += "\r\n"
|
||||
buffer.WriteString(header)
|
||||
return header
|
||||
}
|
||||
|
||||
// 格式化文件
|
||||
func (l *Smtp) writeFile(buffer *bytes.Buffer, fileName string) error {
|
||||
file, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := make([]byte, base64.StdEncoding.EncodedLen(len(file)))
|
||||
base64.StdEncoding.Encode(payload, file)
|
||||
buffer.WriteString("\r\n")
|
||||
for index, line := 0, len(payload); index < line; index++ {
|
||||
buffer.WriteByte(payload[index])
|
||||
if (index+1)%76 == 0 {
|
||||
buffer.WriteString("\r\n")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package smtp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"code.yun.ink/pkg/mailx/smtp"
|
||||
"github.com/yuninks/loggerx"
|
||||
)
|
||||
|
||||
func TestMail(t *testing.T) {
|
||||
sm := smtp.Smtp{}
|
||||
ctx := context.Background()
|
||||
|
||||
logger := loggerx.NewLogger(ctx)
|
||||
|
||||
ini, err := sm.InitEmail(ctx, interfaces.EmailConfigData{
|
||||
Smtp: &interfaces.EmailConfigDataSmtp{
|
||||
Username: "test@email.aisz.org",
|
||||
Password: "FDuSoJKiC9tNSDIG6DFh",
|
||||
ReplyTo: "",
|
||||
Host: "smtpdm.aliyun.com",
|
||||
Port: "80",
|
||||
},
|
||||
}, logger)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := http.Get("https://baidu.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer req.Body.Close()
|
||||
|
||||
by, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := interfaces.Message{
|
||||
To: []string{"995116474@qq.com"},
|
||||
Cc: []string{"287852692@qq.com"},
|
||||
Bcc: []string{"1362716835@qq.com"},
|
||||
ReplyTo: "huangxinyun@dreaminglife.cn",
|
||||
Subject: "test mail",
|
||||
Body: string(by),
|
||||
Attachment: []interfaces.MessageAttachment{
|
||||
// {
|
||||
// Name: "/code/statistic/out.xlsx",
|
||||
// ContentType: "",
|
||||
// WithFile: true,
|
||||
// },
|
||||
// {
|
||||
// Name: "/code/statistic/origin.xlsx",
|
||||
// ContentType: "",
|
||||
// WithFile: true,
|
||||
// },
|
||||
// {
|
||||
// Name: "/code/statistic/out2.xlsx",
|
||||
// ContentType: "",
|
||||
// WithFile: true,
|
||||
// },
|
||||
},
|
||||
}
|
||||
|
||||
err = ini.Send(ctx, msg)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// func TestQQ(t *testing.T) {
|
||||
|
||||
// // 发件人邮箱
|
||||
// from := "995116474@qq.com"
|
||||
// // 授权码,而非密码
|
||||
// authCode := "xxxxxxxxxxxxxxxxxxxxxx"
|
||||
// // 收件人邮箱,可以是多个收件人
|
||||
// to := []string{"yun@yun.ink"}
|
||||
// // 邮件服务器信息
|
||||
// smtpHost := "smtp.qq.com"
|
||||
// smtpPort := "587" // 或使用465,根据你的SMTP服务器要求设置
|
||||
|
||||
// mail := mailx.NewMailx(from, authCode, smtpHost, smtpPort)
|
||||
|
||||
// msg := mailx.Message{
|
||||
// To: to,
|
||||
// Subject: "test mail",
|
||||
// Body: "测试",
|
||||
// }
|
||||
// err := mail.Send(msg)
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
Reference in New Issue
Block a user