diff --git a/attack_event/fw.go b/attack_event/fw.go
new file mode 100644
index 0000000..25c95f7
--- /dev/null
+++ b/attack_event/fw.go
@@ -0,0 +1,40 @@
+package attackevent
+
+import (
+ "bytes"
+ "dt_automate/conn"
+ "dt_automate/tool"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/url"
+ "time"
+)
+
+// var cookieStr string
+
+func Fw_event() {
+ //当前时间
+ EndTime := time.Unix(tool.Timestamp("second"), 0).Format("2006-01-02T15:04:05")
+ //今天0点
+ StartTime := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()).Format("2006-01-02T15:04:05")
+ // 构建 x-www-form-urlencoded 格式的请求体
+ values := url.Values{}
+ values.Add("xml", "1"+StartTime+""+EndTime+"1200{"SrcZoneName":"Untrust","DestZoneName":"Trust"}")
+ values.Add("req_menu", "M_Monitor/M_AtkLog/M_ThreatLog")
+ header := map[string]string{
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+ "referer": "https://11.2.68.146/wnm/frame/index.php",
+ "cookie": cookieStr,
+ }
+ datas := conn.DT_POST("https://11.2.68.146/wnm/get.j", header, bytes.NewBufferString(values.Encode()))["NTOP"].(map[string]interface{})["LogPaging"]
+ var a map[string]interface{}
+ for v, k := range datas.([]interface{}) {
+ b := k.(map[string]interface{})["OutputJSON"].(string)
+ if err := json.Unmarshal([]byte(b), &a); err != nil {
+ log.Fatalf("Failed to unmarshal JSON: %v", err)
+ }
+ fmt.Println(v, a)
+ // fmt.Printf("序号:%d,攻击时间:[%s],安全域:%s-%s##攻击源IP:%s#目的源IP:%s:%s,攻击类型:%s,应用协议:%s,请求域名:%s\n", v, a["Time"], a["SrcZoneName"], a["DestZoneName"], a["SrcIPAddr"], a["DestIPAddr"], strconv.FormatFloat(a["DestPort"].(float64), 'f', 0, 64), a["ThreatName"], a["Application"], a["HttpHost"])
+ }
+}
diff --git a/attack_event/job.go b/attack_event/job.go
new file mode 100644
index 0000000..18c2efb
--- /dev/null
+++ b/attack_event/job.go
@@ -0,0 +1,109 @@
+package attackevent
+
+import (
+ "dt_automate/conn"
+ "dt_automate/tool"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/robfig/cron/v3"
+)
+
+var cookieStr = "vindex==39=16=0AB00=0R; supportLang=cn%2Cen; lang=cn; sessionid=200001dc6ccf3da4c61484899a31aaf741de; loginid=975d5428764bcee5451c191f464cb5ad; 200001dc6ccf3da4c61484899a31aaf741de=true; abcd1234=true; login=false"
+
+func FW_Job() {
+ job := cron.New(cron.WithSeconds())
+
+ j := &myJob{
+ t: time.Now(),
+ }
+ j1 := &myJob1{
+ t: time.Now(),
+ }
+ job.Schedule(cron.Every(3*60*time.Second), j)
+ job.Schedule(cron.Every(3*time.Second), j1)
+ job.Start()
+ select {}
+}
+
+type myJob struct {
+ i int
+ t time.Time
+}
+
+func (j *myJob) Run() { //心跳//三分钟触发一次
+ //当前时间
+ // EndTime := time.Unix(tool.Timestamp("second"), 0).Format("2006-01-02T15:04:05")
+ // //今天0点
+ // StartTime := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()).Format("2006-01-02T15:04:05")
+ // 构建 x-www-form-urlencoded 格式的请求体
+ // values := url.Values{}
+ // values.Add("xml", "1"+StartTime+""+EndTime+"1200{"SrcZoneName":"Untrust","DestZoneName":"Trust"}")
+ // values.Add("req_menu", "M_Monitor/M_AtkLog/M_ThreatLog")
+ header := map[string]string{
+ // "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+ "referer": "https://11.2.68.146/wnm/frame/index.php",
+ "cookie": cookieStr,
+ }
+ datas := conn.DT_POST("https://11.2.68.146/wnm/keepalive.j", header, nil)
+ // var a map[string]interface{}
+ // for v, k := range datas.([]interface{}) {
+ // b := k.(map[string]interface{})["OutputJSON"].(string)
+ // if err := json.Unmarshal([]byte(b), &a); err != nil {
+ // log.Fatalf("Failed to unmarshal JSON: %v", err)
+ // }
+ // fmt.Println(v, a)
+ // // fmt.Printf("序号:%d,攻击时间:[%s],安全域:%s-%s##攻击源IP:%s#目的源IP:%s:%s,攻击类型:%s,应用协议:%s,请求域名:%s\n", v, a["Time"], a["SrcZoneName"], a["DestZoneName"], a["SrcIPAddr"], a["DestIPAddr"], strconv.FormatFloat(a["DestPort"].(float64), 'f', 0, 64), a["ThreatName"], a["Application"], a["HttpHost"])
+ // }
+ log.Println(datas)
+ //#################################
+ j.i++
+ now := time.Now()
+ sub := now.Sub(j.t)
+ j.t = now
+ fmt.Printf("心跳: %d, duraction : %d ms %f \n", j.i, sub.Milliseconds(), sub.Minutes())
+}
+
+type myJob1 struct {
+ i int
+ t time.Time
+}
+
+func (j1 *myJob1) Run() { //定时任务1小时执行一次,获取威胁数据
+ now := time.Now()
+ // 当前时间
+ EndTime := time.Unix(tool.Timestamp("second"), 0).Format("2006-01-02T15:04:05")
+ h, _ := time.ParseDuration("-1h")
+ h1 := now.Add(1 * h).Format("2006-01-02T15:04:05")
+ log.Println(EndTime)
+ fmt.Println(h1)
+ // //今天0点
+ // StartTime := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()).Format("2006-01-02T15:04:05")
+ // // 构建 x-www-form-urlencoded 格式的请求体
+ // values := url.Values{}
+ // values.Add("xml", "1"+StartTime+""+EndTime+"1200{"SrcZoneName":"Untrust","DestZoneName":"Trust"}")
+ // values.Add("req_menu", "M_Monitor/M_AtkLog/M_ThreatLog")
+ // header := map[string]string{
+ // "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+ // "referer": "https://11.2.68.146/wnm/frame/index.php",
+ // "cookie": cookieStr,
+ // }
+ // datas := conn.DT_POST("https://11.2.68.146/wnm/get.j", header, bytes.NewBufferString(values.Encode()))["NTOP"].(map[string]interface{})["LogPaging"]
+ // var a map[string]interface{}
+ // for v, k := range datas.([]interface{}) {
+ // b := k.(map[string]interface{})["OutputJSON"].(string)
+ // if err := json.Unmarshal([]byte(b), &a); err != nil {
+ // log.Fatalf("Failed to unmarshal JSON: %v", err)
+ // }
+ // fmt.Println(v, a)
+ // fmt.Printf("序号:%d,攻击时间:[%s],安全域:%s-%s##攻击源IP:%s#目的源IP:%s:%s,攻击类型:%s,应用协议:%s,请求域名:%s\n", v, a["Time"], a["SrcZoneName"], a["DestZoneName"], a["SrcIPAddr"], a["DestIPAddr"], strconv.FormatFloat(a["DestPort"].(float64), 'f', 0, 64), a["ThreatName"], a["Application"], a["HttpHost"])
+ // }
+ // ###############################################
+ j1.i++
+ // now := time.Now()
+ sub := now.Sub(j1.t)
+ j1.t = now
+ fmt.Printf("获取数据: %d, duraction : %d ms %f\n", j1.i, sub.Milliseconds(), sub.Hours())
+
+}
diff --git a/go.mod b/go.mod
index 5358da9..56bcc24 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
github.com/go-sql-driver/mysql v1.9.0
github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c
github.com/playwright-community/playwright-go v0.5001.0
+ github.com/robfig/cron/v3 v3.0.1
github.com/super-l/machine-code v0.0.0-20241121142923-4cb40646deba
github.com/unidoc/unioffice v1.39.0
github.com/unidoc/unioffice/v2 v2.0.0
diff --git a/go.sum b/go.sum
index 32727a0..b3120f9 100644
--- a/go.sum
+++ b/go.sum
@@ -42,6 +42,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
diff --git a/img/afc2000_1.png b/img/afc2000_1.png
index 9faa51e..57631b1 100644
Binary files a/img/afc2000_1.png and b/img/afc2000_1.png differ
diff --git a/img/afc2000_2.png b/img/afc2000_2.png
index e634ef7..d9f9440 100644
Binary files a/img/afc2000_2.png and b/img/afc2000_2.png differ
diff --git a/img/capt.png b/img/capt.png
index 76aa882..fac3fb0 100644
Binary files a/img/capt.png and b/img/capt.png differ
diff --git a/img/hw_dtcloud_01.png b/img/hw_dtcloud_01.png
new file mode 100644
index 0000000..f7b06e3
Binary files /dev/null and b/img/hw_dtcloud_01.png differ
diff --git a/img/hw_dtcloud_02.png b/img/hw_dtcloud_02.png
new file mode 100644
index 0000000..c2e1498
Binary files /dev/null and b/img/hw_dtcloud_02.png differ
diff --git a/img/hw_dtcloud_03.png b/img/hw_dtcloud_03.png
new file mode 100644
index 0000000..6f3e033
Binary files /dev/null and b/img/hw_dtcloud_03.png differ
diff --git a/img/hw_dtcloud_04.png b/img/hw_dtcloud_04.png
new file mode 100644
index 0000000..2e2f066
Binary files /dev/null and b/img/hw_dtcloud_04.png differ
diff --git a/img/hw_dtcloud_05.png b/img/hw_dtcloud_05.png
new file mode 100644
index 0000000..a49e149
Binary files /dev/null and b/img/hw_dtcloud_05.png differ
diff --git a/img/hw_dtcloud_06.png b/img/hw_dtcloud_06.png
new file mode 100644
index 0000000..71ff4fb
Binary files /dev/null and b/img/hw_dtcloud_06.png differ
diff --git a/img/hw_dtcloud_07.png b/img/hw_dtcloud_07.png
new file mode 100644
index 0000000..0d75377
Binary files /dev/null and b/img/hw_dtcloud_07.png differ
diff --git a/img/hw_dtcloud_08.png b/img/hw_dtcloud_08.png
new file mode 100644
index 0000000..8ba5c71
Binary files /dev/null and b/img/hw_dtcloud_08.png differ
diff --git a/img/hw_dtcloud_man.png b/img/hw_dtcloud_man.png
new file mode 100644
index 0000000..e4f0d6c
Binary files /dev/null and b/img/hw_dtcloud_man.png differ
diff --git a/img/hw_fw1.png b/img/hw_fw1.png
index d298976..2bc5eab 100644
Binary files a/img/hw_fw1.png and b/img/hw_fw1.png differ
diff --git a/img/hw_tsgz.png b/img/hw_tsgz.png
index e1d6be1..1eae9bc 100644
Binary files a/img/hw_tsgz.png and b/img/hw_tsgz.png differ
diff --git a/img/zww_dtcloud_01.png b/img/zww_dtcloud_01.png
new file mode 100644
index 0000000..620aced
Binary files /dev/null and b/img/zww_dtcloud_01.png differ
diff --git a/img/zww_dtcloud_02.png b/img/zww_dtcloud_02.png
new file mode 100644
index 0000000..d1656c3
Binary files /dev/null and b/img/zww_dtcloud_02.png differ
diff --git a/img/zww_dtcloud_03.png b/img/zww_dtcloud_03.png
new file mode 100644
index 0000000..5ad9d95
Binary files /dev/null and b/img/zww_dtcloud_03.png differ
diff --git a/img/zww_dtcloud_04.png b/img/zww_dtcloud_04.png
new file mode 100644
index 0000000..c3751e2
Binary files /dev/null and b/img/zww_dtcloud_04.png differ
diff --git a/img/zww_dtcloud_05.png b/img/zww_dtcloud_05.png
new file mode 100644
index 0000000..5d96852
Binary files /dev/null and b/img/zww_dtcloud_05.png differ
diff --git a/img/zww_dtcloud_06.png b/img/zww_dtcloud_06.png
new file mode 100644
index 0000000..e0cfcae
Binary files /dev/null and b/img/zww_dtcloud_06.png differ
diff --git a/img/zww_dtcloud_07.png b/img/zww_dtcloud_07.png
new file mode 100644
index 0000000..192af08
Binary files /dev/null and b/img/zww_dtcloud_07.png differ
diff --git a/img/zww_dtcloud_08.png b/img/zww_dtcloud_08.png
new file mode 100644
index 0000000..20bae84
Binary files /dev/null and b/img/zww_dtcloud_08.png differ
diff --git a/img/zww_dtcloud_man.png b/img/zww_dtcloud_man.png
new file mode 100644
index 0000000..5b48247
Binary files /dev/null and b/img/zww_dtcloud_man.png differ
diff --git a/img/zww_fw1.png b/img/zww_fw1.png
index 6b3c00e..c051faa 100644
Binary files a/img/zww_fw1.png and b/img/zww_fw1.png differ
diff --git a/img/zww_tsgz.png b/img/zww_tsgz.png
index 5a030dc..5e8f386 100644
Binary files a/img/zww_tsgz.png and b/img/zww_tsgz.png differ
diff --git a/main.go b/main.go
index 09a7125..aa457c1 100644
--- a/main.go
+++ b/main.go
@@ -1,8 +1,8 @@
package main
import (
+ "dt_automate/method"
"dt_automate/tool"
- "dt_automate/wps"
"flag"
"fmt"
"log"
@@ -93,11 +93,14 @@ func main() {
// method.HW_FAC1() //截取流量清洗的图片
// method.HW_FW1() //截取防火墙的图片
// method.HW_TSGZ() //截取态势感知的图片
+ // method.HW_DTCLOUD() //截取运维中心的图片
// //政务网区
// method.ZWW_FW1() //截取防火墙的图片
// method.ZWW_TSGZ() //截取态势感知的图片
- wps.Word()
+ // wps.Word()
// wps.Damo()
+ method.ZWW_DTCLOUD() //截取运维中心的图片
+ // attackevent.FW_Job()
}
} else {
log.Println("没有授权")
diff --git a/method/hw_dtcloud.go b/method/hw_dtcloud.go
new file mode 100644
index 0000000..449afb2
--- /dev/null
+++ b/method/hw_dtcloud.go
@@ -0,0 +1,152 @@
+package method
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/playwright-community/playwright-go"
+)
+
+func HW_DTCLOUD() {
+ // 启动 Playwright
+ pw, err := playwright.Run()
+ if err != nil {
+ log.Fatalf("could not start Playwright: %v", err)
+ }
+ defer pw.Stop()
+
+ // 启动浏览器(Chromium)
+ browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
+ Headless: playwright.Bool(false), // 设置为 false 以显示浏览器界面
+ Args: []string{
+ "--ignore-certificate-errors", // 忽略证书错误[^28^][^29^]
+ },
+ })
+ if err != nil {
+ log.Fatalf("could not launch browser: %v", err)
+ }
+ defer browser.Close()
+
+ // 创建一个新页面
+ page, err := browser.NewPage()
+ if err != nil {
+ log.Fatalf("could not create page: %v", err)
+ }
+ // 设置浏览器窗口大小
+ err = page.SetViewportSize(1920, 1080) // 宽度为 1200px,高度为 800px
+ if err != nil {
+ log.Fatalf("无法设置浏览器窗口大小: %v", err)
+ }
+
+ // 导航到指定网址
+ _, err = page.Goto("https://ops.hlw.dtcloud.com/login", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded})
+ if err != nil {
+ log.Fatalf("could not go to the page: %v", err)
+ }
+
+ // page.WaitForTimeout(4000)
+ //输入用户名
+ page.Locator(`.el-input__inner`).Nth(0).Fill("opsadmin")
+ //输入密码
+ page.Locator(`.el-input__inner`).Nth(1).Fill(`DTXCY@2024#`)
+ var name string
+ fmt.Print("请输入验证码:")
+ fmt.Scanf("%s", &name)
+ //输入验证码
+ page.Locator(`.el-input__inner`).Nth(2).Fill(name)
+ log.Println("当前验证码为:", name)
+ page.WaitForTimeout(2000)
+ //登录
+ if err := page.Locator(".el-button.login-form-item.login-btn.el-button--primary").Click(); err != nil {
+ log.Fatalf("could not click button: %v", err)
+ }
+ page.WaitForTimeout(7000)
+ //运维中心主页
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_man.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_man.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/console/om/capacity/overview?regionId=-1") //跳转到运维中心概览
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_01.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_01.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/console/om/capacity/resource?regionId=-1") //运维-资源容量
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_02.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_02.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/region/dtcloud1/console/om/monitor/overview") //监控-概览
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_03.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_03.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/region/dtcloud1/console/om/monitor/resource") //监控-资源监控
+ page.WaitForTimeout(4000)
+ page.Locator(`.arco-tree.arco-tree-show-line`).Locator(`div`).Nth(4).Locator(`span`).Nth(1).Locator(`span[role="button"]`).Click()
+ page.Locator(`div[data-test-id="PhysicalServer"]`).Click()
+ page.WaitForTimeout(1000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_04.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_04.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/region/dtcloud1/console/om/alert/view") //监控-告警-告警查看
+ page.WaitForTimeout(4000)
+ page.Locator(`.flex-between mb20`).Locator(`.arco-material-last-days`).Locator(`.arco-space.arco-space-horizontal.arco-space-align-center`).Locator(`div`).Nth(0).Locator(`button`).Click()
+ page.WaitForTimeout(2000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_05.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_05.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/region/dtcloud1/console/om/alert/history") //监控-告警历史
+ page.WaitForTimeout(4000)
+ page.Locator(`.arco-btn.arco-btn-outline.arco-btn-size-default.arco-btn-shape-square`).Nth(0).Click()
+ page.WaitForTimeout(500)
+ page.Locator(`.arco-input-tag-input.arco-input-tag-input-size-default.arco-input-tag-input-autowidth`).Click()
+ page.Locator(`.arco-select-option-wrapper`).Nth(1).Locator(`.arco-checkbox-mask`).Click()
+ page.Locator(`.arco-btn.arco-btn-primary.arco-btn-size-mini.arco-btn-shape-square`).Click()
+ page.WaitForTimeout(1000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_06.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_06.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/region/dtcloud1/console/om/health-inspection/outputs?page=1&size=10&order=createTime%3Adesc&searchResult=%7B%7D") //运维-监控巡检-巡检结果
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_07.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_07.png")
+
+ page.Goto("https://ops.hlw.dtcloud.com/console/om/capacity/service?regionId=-1") //运维-容量-云服务容量
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/hw_dtcloud_08.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // 获取页面标题
+ title, err := page.Title()
+ if err != nil {
+ log.Fatalf("could not get title: %v", err)
+ }
+ log.Printf("Page title is: %s\n", title)
+
+ page.WaitForTimeout(5000)
+}
diff --git a/method/hw_fw1.go b/method/hw_fw1.go
index 07578f1..f82912d 100644
--- a/method/hw_fw1.go
+++ b/method/hw_fw1.go
@@ -2,6 +2,7 @@ package method
import (
"dt_automate/tool"
+ "fmt"
"log"
"github.com/playwright-community/playwright-go"
@@ -44,7 +45,7 @@ func HW_FW1() {
log.Fatalf("could not go to the page: %v", err)
}
- page.WaitForTimeout(2000)
+ page.WaitForTimeout(3000)
//输入
if err := page.Locator(`#user_name`).Fill("xcy_user"); err != nil {
log.Fatalf("could not fill input: %v", err)
@@ -103,6 +104,16 @@ func HW_FW1() {
log.Fatalf("could not get title: %v", err)
}
log.Printf("Page title is: %s\n", title)
+ cookies, err := page.Context().Cookies("https://11.2.68.146/wnm/get.j")
+ if err != nil {
+ log.Println(err)
+ }
+ var cookieStr string
+ for i, cookie := range cookies {
+ cookieStr += fmt.Sprintf("%s=%s;", cookie.Name, cookie.Value)
+ log.Printf("Cookie %d: %+v", i, cookie)
+ }
+ log.Println(cookieStr)
// StartBlocker()
page.WaitForTimeout(5000)
diff --git a/method/zww_dtcloud.go b/method/zww_dtcloud.go
new file mode 100644
index 0000000..8963cc8
--- /dev/null
+++ b/method/zww_dtcloud.go
@@ -0,0 +1,152 @@
+package method
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/playwright-community/playwright-go"
+)
+
+func ZWW_DTCLOUD() {
+ // 启动 Playwright
+ pw, err := playwright.Run()
+ if err != nil {
+ log.Fatalf("could not start Playwright: %v", err)
+ }
+ defer pw.Stop()
+
+ // 启动浏览器(Chromium)
+ browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
+ Headless: playwright.Bool(false), // 设置为 false 以显示浏览器界面
+ Args: []string{
+ "--ignore-certificate-errors", // 忽略证书错误[^28^][^29^]
+ },
+ })
+ if err != nil {
+ log.Fatalf("could not launch browser: %v", err)
+ }
+ defer browser.Close()
+
+ // 创建一个新页面
+ page, err := browser.NewPage()
+ if err != nil {
+ log.Fatalf("could not create page: %v", err)
+ }
+ // 设置浏览器窗口大小
+ err = page.SetViewportSize(1920, 1080) // 宽度为 1200px,高度为 800px
+ if err != nil {
+ log.Fatalf("无法设置浏览器窗口大小: %v", err)
+ }
+
+ // 导航到指定网址
+ _, err = page.Goto("https://ops.zww.dtcloud.com/login", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded})
+ if err != nil {
+ log.Fatalf("could not go to the page: %v", err)
+ }
+
+ // page.WaitForTimeout(4000)
+ //输入用户名
+ page.Locator(`.el-input__inner`).Nth(0).Fill("opsadmin")
+ //输入密码
+ page.Locator(`.el-input__inner`).Nth(1).Fill(`DTXCY@2024#`)
+ var name string
+ fmt.Print("请输入验证码:")
+ fmt.Scanf("%s", &name)
+ //输入验证码
+ page.Locator(`.el-input__inner`).Nth(2).Fill(name)
+ log.Println("当前验证码为:", name)
+ page.WaitForTimeout(2000)
+ //登录
+ if err := page.Locator(".el-button.login-form-item.login-btn.el-button--primary").Click(); err != nil {
+ log.Fatalf("could not click button: %v", err)
+ }
+ page.WaitForTimeout(7000)
+ //运维中心主页
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_man.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_man.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/console/om/capacity/overview?regionId=-1", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //跳转到运维中心概览
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_01.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_01.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/console/om/capacity/resource?regionId=-1", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //运维-资源容量
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_02.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_02.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/region/dtcloud2/console/om/monitor/overview", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //监控-概览
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_03.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_03.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/region/dtcloud2/console/om/monitor/resource", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //监控-资源监控-物理服务器
+ page.WaitForTimeout(4000)
+ page.Locator(`.arco-tree.arco-tree-show-line`).Locator(`div`).Nth(4).Locator(`span`).Nth(1).Locator(`span[role="button"]`).Click()
+ page.Locator(`div[data-test-id="PhysicalServer"]`).Click()
+ page.WaitForTimeout(1000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_04.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_04.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/region/dtcloud2/console/om/alert/view", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //监控-告警-告警查看
+ page.WaitForTimeout(4000)
+ page.Locator(`.flex-between mb20`).Locator(`.arco-material-last-days`).Locator(`.arco-space.arco-space-horizontal.arco-space-align-center`).Locator(`div`).Nth(0).Locator(`button`).Click()
+ page.WaitForTimeout(2000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_05.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_05.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/region/dtcloud2/console/om/alert/history", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //监控-告警历史
+ page.WaitForTimeout(4000)
+ page.Locator(`.arco-btn.arco-btn-outline.arco-btn-size-default.arco-btn-shape-square`).Nth(0).Click()
+ page.WaitForTimeout(500)
+ page.Locator(`.arco-input-tag-input.arco-input-tag-input-size-default.arco-input-tag-input-autowidth`).Click()
+ page.Locator(`.arco-select-option-wrapper`).Nth(1).Locator(`.arco-checkbox-mask`).Click()
+ page.Locator(`.arco-btn.arco-btn-primary.arco-btn-size-mini.arco-btn-shape-square`).Click()
+ page.WaitForTimeout(1000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_06.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_06.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/region/dtcloud2/console/om/health-inspection/outputs?page=1&size=10&order=createTime%3Adesc&searchResult=%7B%7D", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //运维-监控巡检-巡检结果
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_07.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // tool.Jietu("img/hw_dtcloud_07.png")
+
+ page.Goto("https://ops.zww.dtcloud.com/console/om/capacity/service?regionId=-1", playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded}) //运维-容量-云服务容量
+ page.WaitForTimeout(4000)
+ page.Screenshot(playwright.PageScreenshotOptions{
+ Path: playwright.String("img/zww_dtcloud_08.png"),
+ FullPage: playwright.Bool(false),
+ })
+ // 获取页面标题
+ title, err := page.Title()
+ if err != nil {
+ log.Fatalf("could not get title: %v", err)
+ }
+ log.Printf("Page title is: %s\n", title)
+
+ page.WaitForTimeout(5000)
+}
diff --git a/vendor/github.com/robfig/cron/v3/.gitignore b/vendor/github.com/robfig/cron/v3/.gitignore
new file mode 100644
index 0000000..0026861
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/.gitignore
@@ -0,0 +1,22 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
diff --git a/vendor/github.com/robfig/cron/v3/.travis.yml b/vendor/github.com/robfig/cron/v3/.travis.yml
new file mode 100644
index 0000000..4f2ee4d
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/.travis.yml
@@ -0,0 +1 @@
+language: go
diff --git a/vendor/github.com/robfig/cron/v3/LICENSE b/vendor/github.com/robfig/cron/v3/LICENSE
new file mode 100644
index 0000000..3a0f627
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/LICENSE
@@ -0,0 +1,21 @@
+Copyright (C) 2012 Rob Figueiredo
+All Rights Reserved.
+
+MIT LICENSE
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/robfig/cron/v3/README.md b/vendor/github.com/robfig/cron/v3/README.md
new file mode 100644
index 0000000..984c537
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/README.md
@@ -0,0 +1,125 @@
+[](http://godoc.org/github.com/robfig/cron)
+[](https://travis-ci.org/robfig/cron)
+
+# cron
+
+Cron V3 has been released!
+
+To download the specific tagged release, run:
+
+ go get github.com/robfig/cron/v3@v3.0.0
+
+Import it in your program as:
+
+ import "github.com/robfig/cron/v3"
+
+It requires Go 1.11 or later due to usage of Go Modules.
+
+Refer to the documentation here:
+http://godoc.org/github.com/robfig/cron
+
+The rest of this document describes the the advances in v3 and a list of
+breaking changes for users that wish to upgrade from an earlier version.
+
+## Upgrading to v3 (June 2019)
+
+cron v3 is a major upgrade to the library that addresses all outstanding bugs,
+feature requests, and rough edges. It is based on a merge of master which
+contains various fixes to issues found over the years and the v2 branch which
+contains some backwards-incompatible features like the ability to remove cron
+jobs. In addition, v3 adds support for Go Modules, cleans up rough edges like
+the timezone support, and fixes a number of bugs.
+
+New features:
+
+- Support for Go modules. Callers must now import this library as
+ `github.com/robfig/cron/v3`, instead of `gopkg.in/...`
+
+- Fixed bugs:
+ - 0f01e6b parser: fix combining of Dow and Dom (#70)
+ - dbf3220 adjust times when rolling the clock forward to handle non-existent midnight (#157)
+ - eeecf15 spec_test.go: ensure an error is returned on 0 increment (#144)
+ - 70971dc cron.Entries(): update request for snapshot to include a reply channel (#97)
+ - 1cba5e6 cron: fix: removing a job causes the next scheduled job to run too late (#206)
+
+- Standard cron spec parsing by default (first field is "minute"), with an easy
+ way to opt into the seconds field (quartz-compatible). Although, note that the
+ year field (optional in Quartz) is not supported.
+
+- Extensible, key/value logging via an interface that complies with
+ the https://github.com/go-logr/logr project.
+
+- The new Chain & JobWrapper types allow you to install "interceptors" to add
+ cross-cutting behavior like the following:
+ - Recover any panics from jobs
+ - Delay a job's execution if the previous run hasn't completed yet
+ - Skip a job's execution if the previous run hasn't completed yet
+ - Log each job's invocations
+ - Notification when jobs are completed
+
+It is backwards incompatible with both v1 and v2. These updates are required:
+
+- The v1 branch accepted an optional seconds field at the beginning of the cron
+ spec. This is non-standard and has led to a lot of confusion. The new default
+ parser conforms to the standard as described by [the Cron wikipedia page].
+
+ UPDATING: To retain the old behavior, construct your Cron with a custom
+ parser:
+
+ // Seconds field, required
+ cron.New(cron.WithSeconds())
+
+ // Seconds field, optional
+ cron.New(
+ cron.WithParser(
+ cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))
+
+- The Cron type now accepts functional options on construction rather than the
+ previous ad-hoc behavior modification mechanisms (setting a field, calling a setter).
+
+ UPDATING: Code that sets Cron.ErrorLogger or calls Cron.SetLocation must be
+ updated to provide those values on construction.
+
+- CRON_TZ is now the recommended way to specify the timezone of a single
+ schedule, which is sanctioned by the specification. The legacy "TZ=" prefix
+ will continue to be supported since it is unambiguous and easy to do so.
+
+ UPDATING: No update is required.
+
+- By default, cron will no longer recover panics in jobs that it runs.
+ Recovering can be surprising (see issue #192) and seems to be at odds with
+ typical behavior of libraries. Relatedly, the `cron.WithPanicLogger` option
+ has been removed to accommodate the more general JobWrapper type.
+
+ UPDATING: To opt into panic recovery and configure the panic logger:
+
+ cron.New(cron.WithChain(
+ cron.Recover(logger), // or use cron.DefaultLogger
+ ))
+
+- In adding support for https://github.com/go-logr/logr, `cron.WithVerboseLogger` was
+ removed, since it is duplicative with the leveled logging.
+
+ UPDATING: Callers should use `WithLogger` and specify a logger that does not
+ discard `Info` logs. For convenience, one is provided that wraps `*log.Logger`:
+
+ cron.New(
+ cron.WithLogger(cron.VerbosePrintfLogger(logger)))
+
+
+### Background - Cron spec format
+
+There are two cron spec formats in common usage:
+
+- The "standard" cron format, described on [the Cron wikipedia page] and used by
+ the cron Linux system utility.
+
+- The cron format used by [the Quartz Scheduler], commonly used for scheduled
+ jobs in Java software
+
+[the Cron wikipedia page]: https://en.wikipedia.org/wiki/Cron
+[the Quartz Scheduler]: http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-06.html
+
+The original version of this package included an optional "seconds" field, which
+made it incompatible with both of these formats. Now, the "standard" format is
+the default format accepted, and the Quartz format is opt-in.
diff --git a/vendor/github.com/robfig/cron/v3/chain.go b/vendor/github.com/robfig/cron/v3/chain.go
new file mode 100644
index 0000000..9565b41
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/chain.go
@@ -0,0 +1,92 @@
+package cron
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "time"
+)
+
+// JobWrapper decorates the given Job with some behavior.
+type JobWrapper func(Job) Job
+
+// Chain is a sequence of JobWrappers that decorates submitted jobs with
+// cross-cutting behaviors like logging or synchronization.
+type Chain struct {
+ wrappers []JobWrapper
+}
+
+// NewChain returns a Chain consisting of the given JobWrappers.
+func NewChain(c ...JobWrapper) Chain {
+ return Chain{c}
+}
+
+// Then decorates the given job with all JobWrappers in the chain.
+//
+// This:
+// NewChain(m1, m2, m3).Then(job)
+// is equivalent to:
+// m1(m2(m3(job)))
+func (c Chain) Then(j Job) Job {
+ for i := range c.wrappers {
+ j = c.wrappers[len(c.wrappers)-i-1](j)
+ }
+ return j
+}
+
+// Recover panics in wrapped jobs and log them with the provided logger.
+func Recover(logger Logger) JobWrapper {
+ return func(j Job) Job {
+ return FuncJob(func() {
+ defer func() {
+ if r := recover(); r != nil {
+ const size = 64 << 10
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ err, ok := r.(error)
+ if !ok {
+ err = fmt.Errorf("%v", r)
+ }
+ logger.Error(err, "panic", "stack", "...\n"+string(buf))
+ }
+ }()
+ j.Run()
+ })
+ }
+}
+
+// DelayIfStillRunning serializes jobs, delaying subsequent runs until the
+// previous one is complete. Jobs running after a delay of more than a minute
+// have the delay logged at Info.
+func DelayIfStillRunning(logger Logger) JobWrapper {
+ return func(j Job) Job {
+ var mu sync.Mutex
+ return FuncJob(func() {
+ start := time.Now()
+ mu.Lock()
+ defer mu.Unlock()
+ if dur := time.Since(start); dur > time.Minute {
+ logger.Info("delay", "duration", dur)
+ }
+ j.Run()
+ })
+ }
+}
+
+// SkipIfStillRunning skips an invocation of the Job if a previous invocation is
+// still running. It logs skips to the given logger at Info level.
+func SkipIfStillRunning(logger Logger) JobWrapper {
+ return func(j Job) Job {
+ var ch = make(chan struct{}, 1)
+ ch <- struct{}{}
+ return FuncJob(func() {
+ select {
+ case v := <-ch:
+ j.Run()
+ ch <- v
+ default:
+ logger.Info("skip")
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/robfig/cron/v3/constantdelay.go b/vendor/github.com/robfig/cron/v3/constantdelay.go
new file mode 100644
index 0000000..cd6e7b1
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/constantdelay.go
@@ -0,0 +1,27 @@
+package cron
+
+import "time"
+
+// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
+// It does not support jobs more frequent than once a second.
+type ConstantDelaySchedule struct {
+ Delay time.Duration
+}
+
+// Every returns a crontab Schedule that activates once every duration.
+// Delays of less than a second are not supported (will round up to 1 second).
+// Any fields less than a Second are truncated.
+func Every(duration time.Duration) ConstantDelaySchedule {
+ if duration < time.Second {
+ duration = time.Second
+ }
+ return ConstantDelaySchedule{
+ Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
+ }
+}
+
+// Next returns the next time this should be run.
+// This rounds so that the next activation time will be on the second.
+func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
+ return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
+}
diff --git a/vendor/github.com/robfig/cron/v3/cron.go b/vendor/github.com/robfig/cron/v3/cron.go
new file mode 100644
index 0000000..c7e9176
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/cron.go
@@ -0,0 +1,355 @@
+package cron
+
+import (
+ "context"
+ "sort"
+ "sync"
+ "time"
+)
+
+// Cron keeps track of any number of entries, invoking the associated func as
+// specified by the schedule. It may be started, stopped, and the entries may
+// be inspected while running.
+type Cron struct {
+ entries []*Entry
+ chain Chain
+ stop chan struct{}
+ add chan *Entry
+ remove chan EntryID
+ snapshot chan chan []Entry
+ running bool
+ logger Logger
+ runningMu sync.Mutex
+ location *time.Location
+ parser ScheduleParser
+ nextID EntryID
+ jobWaiter sync.WaitGroup
+}
+
+// ScheduleParser is an interface for schedule spec parsers that return a Schedule
+type ScheduleParser interface {
+ Parse(spec string) (Schedule, error)
+}
+
+// Job is an interface for submitted cron jobs.
+type Job interface {
+ Run()
+}
+
+// Schedule describes a job's duty cycle.
+type Schedule interface {
+ // Next returns the next activation time, later than the given time.
+ // Next is invoked initially, and then each time the job is run.
+ Next(time.Time) time.Time
+}
+
+// EntryID identifies an entry within a Cron instance
+type EntryID int
+
+// Entry consists of a schedule and the func to execute on that schedule.
+type Entry struct {
+ // ID is the cron-assigned ID of this entry, which may be used to look up a
+ // snapshot or remove it.
+ ID EntryID
+
+ // Schedule on which this job should be run.
+ Schedule Schedule
+
+ // Next time the job will run, or the zero time if Cron has not been
+ // started or this entry's schedule is unsatisfiable
+ Next time.Time
+
+ // Prev is the last time this job was run, or the zero time if never.
+ Prev time.Time
+
+ // WrappedJob is the thing to run when the Schedule is activated.
+ WrappedJob Job
+
+ // Job is the thing that was submitted to cron.
+ // It is kept around so that user code that needs to get at the job later,
+ // e.g. via Entries() can do so.
+ Job Job
+}
+
+// Valid returns true if this is not the zero entry.
+func (e Entry) Valid() bool { return e.ID != 0 }
+
+// byTime is a wrapper for sorting the entry array by time
+// (with zero time at the end).
+type byTime []*Entry
+
+func (s byTime) Len() int { return len(s) }
+func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byTime) Less(i, j int) bool {
+ // Two zero times should return false.
+ // Otherwise, zero is "greater" than any other time.
+ // (To sort it at the end of the list.)
+ if s[i].Next.IsZero() {
+ return false
+ }
+ if s[j].Next.IsZero() {
+ return true
+ }
+ return s[i].Next.Before(s[j].Next)
+}
+
+// New returns a new Cron job runner, modified by the given options.
+//
+// Available Settings
+//
+// Time Zone
+// Description: The time zone in which schedules are interpreted
+// Default: time.Local
+//
+// Parser
+// Description: Parser converts cron spec strings into cron.Schedules.
+// Default: Accepts this spec: https://en.wikipedia.org/wiki/Cron
+//
+// Chain
+// Description: Wrap submitted jobs to customize behavior.
+// Default: A chain that recovers panics and logs them to stderr.
+//
+// See "cron.With*" to modify the default behavior.
+func New(opts ...Option) *Cron {
+ c := &Cron{
+ entries: nil,
+ chain: NewChain(),
+ add: make(chan *Entry),
+ stop: make(chan struct{}),
+ snapshot: make(chan chan []Entry),
+ remove: make(chan EntryID),
+ running: false,
+ runningMu: sync.Mutex{},
+ logger: DefaultLogger,
+ location: time.Local,
+ parser: standardParser,
+ }
+ for _, opt := range opts {
+ opt(c)
+ }
+ return c
+}
+
+// FuncJob is a wrapper that turns a func() into a cron.Job
+type FuncJob func()
+
+func (f FuncJob) Run() { f() }
+
+// AddFunc adds a func to the Cron to be run on the given schedule.
+// The spec is parsed using the time zone of this Cron instance as the default.
+// An opaque ID is returned that can be used to later remove it.
+func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
+ return c.AddJob(spec, FuncJob(cmd))
+}
+
+// AddJob adds a Job to the Cron to be run on the given schedule.
+// The spec is parsed using the time zone of this Cron instance as the default.
+// An opaque ID is returned that can be used to later remove it.
+func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error) {
+ schedule, err := c.parser.Parse(spec)
+ if err != nil {
+ return 0, err
+ }
+ return c.Schedule(schedule, cmd), nil
+}
+
+// Schedule adds a Job to the Cron to be run on the given schedule.
+// The job is wrapped with the configured Chain.
+func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
+ c.runningMu.Lock()
+ defer c.runningMu.Unlock()
+ c.nextID++
+ entry := &Entry{
+ ID: c.nextID,
+ Schedule: schedule,
+ WrappedJob: c.chain.Then(cmd),
+ Job: cmd,
+ }
+ if !c.running {
+ c.entries = append(c.entries, entry)
+ } else {
+ c.add <- entry
+ }
+ return entry.ID
+}
+
+// Entries returns a snapshot of the cron entries.
+func (c *Cron) Entries() []Entry {
+ c.runningMu.Lock()
+ defer c.runningMu.Unlock()
+ if c.running {
+ replyChan := make(chan []Entry, 1)
+ c.snapshot <- replyChan
+ return <-replyChan
+ }
+ return c.entrySnapshot()
+}
+
+// Location gets the time zone location
+func (c *Cron) Location() *time.Location {
+ return c.location
+}
+
+// Entry returns a snapshot of the given entry, or nil if it couldn't be found.
+func (c *Cron) Entry(id EntryID) Entry {
+ for _, entry := range c.Entries() {
+ if id == entry.ID {
+ return entry
+ }
+ }
+ return Entry{}
+}
+
+// Remove an entry from being run in the future.
+func (c *Cron) Remove(id EntryID) {
+ c.runningMu.Lock()
+ defer c.runningMu.Unlock()
+ if c.running {
+ c.remove <- id
+ } else {
+ c.removeEntry(id)
+ }
+}
+
+// Start the cron scheduler in its own goroutine, or no-op if already started.
+func (c *Cron) Start() {
+ c.runningMu.Lock()
+ defer c.runningMu.Unlock()
+ if c.running {
+ return
+ }
+ c.running = true
+ go c.run()
+}
+
+// Run the cron scheduler, or no-op if already running.
+func (c *Cron) Run() {
+ c.runningMu.Lock()
+ if c.running {
+ c.runningMu.Unlock()
+ return
+ }
+ c.running = true
+ c.runningMu.Unlock()
+ c.run()
+}
+
+// run the scheduler.. this is private just due to the need to synchronize
+// access to the 'running' state variable.
+func (c *Cron) run() {
+ c.logger.Info("start")
+
+ // Figure out the next activation times for each entry.
+ now := c.now()
+ for _, entry := range c.entries {
+ entry.Next = entry.Schedule.Next(now)
+ c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
+ }
+
+ for {
+ // Determine the next entry to run.
+ sort.Sort(byTime(c.entries))
+
+ var timer *time.Timer
+ if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
+ // If there are no entries yet, just sleep - it still handles new entries
+ // and stop requests.
+ timer = time.NewTimer(100000 * time.Hour)
+ } else {
+ timer = time.NewTimer(c.entries[0].Next.Sub(now))
+ }
+
+ for {
+ select {
+ case now = <-timer.C:
+ now = now.In(c.location)
+ c.logger.Info("wake", "now", now)
+
+ // Run every entry whose next time was less than now
+ for _, e := range c.entries {
+ if e.Next.After(now) || e.Next.IsZero() {
+ break
+ }
+ c.startJob(e.WrappedJob)
+ e.Prev = e.Next
+ e.Next = e.Schedule.Next(now)
+ c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
+ }
+
+ case newEntry := <-c.add:
+ timer.Stop()
+ now = c.now()
+ newEntry.Next = newEntry.Schedule.Next(now)
+ c.entries = append(c.entries, newEntry)
+ c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
+
+ case replyChan := <-c.snapshot:
+ replyChan <- c.entrySnapshot()
+ continue
+
+ case <-c.stop:
+ timer.Stop()
+ c.logger.Info("stop")
+ return
+
+ case id := <-c.remove:
+ timer.Stop()
+ now = c.now()
+ c.removeEntry(id)
+ c.logger.Info("removed", "entry", id)
+ }
+
+ break
+ }
+ }
+}
+
+// startJob runs the given job in a new goroutine.
+func (c *Cron) startJob(j Job) {
+ c.jobWaiter.Add(1)
+ go func() {
+ defer c.jobWaiter.Done()
+ j.Run()
+ }()
+}
+
+// now returns current time in c location
+func (c *Cron) now() time.Time {
+ return time.Now().In(c.location)
+}
+
+// Stop stops the cron scheduler if it is running; otherwise it does nothing.
+// A context is returned so the caller can wait for running jobs to complete.
+func (c *Cron) Stop() context.Context {
+ c.runningMu.Lock()
+ defer c.runningMu.Unlock()
+ if c.running {
+ c.stop <- struct{}{}
+ c.running = false
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ c.jobWaiter.Wait()
+ cancel()
+ }()
+ return ctx
+}
+
+// entrySnapshot returns a copy of the current cron entry list.
+func (c *Cron) entrySnapshot() []Entry {
+ var entries = make([]Entry, len(c.entries))
+ for i, e := range c.entries {
+ entries[i] = *e
+ }
+ return entries
+}
+
+func (c *Cron) removeEntry(id EntryID) {
+ var entries []*Entry
+ for _, e := range c.entries {
+ if e.ID != id {
+ entries = append(entries, e)
+ }
+ }
+ c.entries = entries
+}
diff --git a/vendor/github.com/robfig/cron/v3/doc.go b/vendor/github.com/robfig/cron/v3/doc.go
new file mode 100644
index 0000000..fa5d08b
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/doc.go
@@ -0,0 +1,231 @@
+/*
+Package cron implements a cron spec parser and job runner.
+
+Installation
+
+To download the specific tagged release, run:
+
+ go get github.com/robfig/cron/v3@v3.0.0
+
+Import it in your program as:
+
+ import "github.com/robfig/cron/v3"
+
+It requires Go 1.11 or later due to usage of Go Modules.
+
+Usage
+
+Callers may register Funcs to be invoked on a given schedule. Cron will run
+them in their own goroutines.
+
+ c := cron.New()
+ c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
+ c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
+ c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
+ c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
+ c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
+ c.Start()
+ ..
+ // Funcs are invoked in their own goroutine, asynchronously.
+ ...
+ // Funcs may also be added to a running Cron
+ c.AddFunc("@daily", func() { fmt.Println("Every day") })
+ ..
+ // Inspect the cron job entries' next and previous run times.
+ inspect(c.Entries())
+ ..
+ c.Stop() // Stop the scheduler (does not stop any jobs already running).
+
+CRON Expression Format
+
+A cron expression represents a set of times, using 5 space-separated fields.
+
+ Field name | Mandatory? | Allowed values | Allowed special characters
+ ---------- | ---------- | -------------- | --------------------------
+ Minutes | Yes | 0-59 | * / , -
+ Hours | Yes | 0-23 | * / , -
+ Day of month | Yes | 1-31 | * / , - ?
+ Month | Yes | 1-12 or JAN-DEC | * / , -
+ Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
+
+Month and Day-of-week field values are case insensitive. "SUN", "Sun", and
+"sun" are equally accepted.
+
+The specific interpretation of the format is based on the Cron Wikipedia page:
+https://en.wikipedia.org/wiki/Cron
+
+Alternative Formats
+
+Alternative Cron expression formats support other fields like seconds. You can
+implement that by creating a custom Parser as follows.
+
+ cron.New(
+ cron.WithParser(
+ cron.NewParser(
+ cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)))
+
+Since adding Seconds is the most common modification to the standard cron spec,
+cron provides a builtin function to do that, which is equivalent to the custom
+parser you saw earlier, except that its seconds field is REQUIRED:
+
+ cron.New(cron.WithSeconds())
+
+That emulates Quartz, the most popular alternative Cron schedule format:
+http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html
+
+Special Characters
+
+Asterisk ( * )
+
+The asterisk indicates that the cron expression will match for all values of the
+field; e.g., using an asterisk in the 5th field (month) would indicate every
+month.
+
+Slash ( / )
+
+Slashes are used to describe increments of ranges. For example 3-59/15 in the
+1st field (minutes) would indicate the 3rd minute of the hour and every 15
+minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...",
+that is, an increment over the largest possible range of the field. The form
+"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the
+increment until the end of that specific range. It does not wrap around.
+
+Comma ( , )
+
+Commas are used to separate items of a list. For example, using "MON,WED,FRI" in
+the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.
+
+Hyphen ( - )
+
+Hyphens are used to define ranges. For example, 9-17 would indicate every
+hour between 9am and 5pm inclusive.
+
+Question mark ( ? )
+
+Question mark may be used instead of '*' for leaving either day-of-month or
+day-of-week blank.
+
+Predefined schedules
+
+You may use one of several pre-defined schedules in place of a cron expression.
+
+ Entry | Description | Equivalent To
+ ----- | ----------- | -------------
+ @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 1 1 *
+ @monthly | Run once a month, midnight, first of month | 0 0 1 * *
+ @weekly | Run once a week, midnight between Sat/Sun | 0 0 * * 0
+ @daily (or @midnight) | Run once a day, midnight | 0 0 * * *
+ @hourly | Run once an hour, beginning of hour | 0 * * * *
+
+Intervals
+
+You may also schedule a job to execute at fixed intervals, starting at the time it's added
+or cron is run. This is supported by formatting the cron spec like this:
+
+ @every
+
+where "duration" is a string accepted by time.ParseDuration
+(http://golang.org/pkg/time/#ParseDuration).
+
+For example, "@every 1h30m10s" would indicate a schedule that activates after
+1 hour, 30 minutes, 10 seconds, and then every interval after that.
+
+Note: The interval does not take the job runtime into account. For example,
+if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,
+it will have only 2 minutes of idle time between each run.
+
+Time zones
+
+By default, all interpretation and scheduling is done in the machine's local
+time zone (time.Local). You can specify a different time zone on construction:
+
+ cron.New(
+ cron.WithLocation(time.UTC))
+
+Individual cron schedules may also override the time zone they are to be
+interpreted in by providing an additional space-separated field at the beginning
+of the cron spec, of the form "CRON_TZ=Asia/Tokyo".
+
+For example:
+
+ # Runs at 6am in time.Local
+ cron.New().AddFunc("0 6 * * ?", ...)
+
+ # Runs at 6am in America/New_York
+ nyc, _ := time.LoadLocation("America/New_York")
+ c := cron.New(cron.WithLocation(nyc))
+ c.AddFunc("0 6 * * ?", ...)
+
+ # Runs at 6am in Asia/Tokyo
+ cron.New().AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
+
+ # Runs at 6am in Asia/Tokyo
+ c := cron.New(cron.WithLocation(nyc))
+ c.SetLocation("America/New_York")
+ c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
+
+The prefix "TZ=(TIME ZONE)" is also supported for legacy compatibility.
+
+Be aware that jobs scheduled during daylight-savings leap-ahead transitions will
+not be run!
+
+Job Wrappers
+
+A Cron runner may be configured with a chain of job wrappers to add
+cross-cutting functionality to all submitted jobs. For example, they may be used
+to achieve the following effects:
+
+ - Recover any panics from jobs (activated by default)
+ - Delay a job's execution if the previous run hasn't completed yet
+ - Skip a job's execution if the previous run hasn't completed yet
+ - Log each job's invocations
+
+Install wrappers for all jobs added to a cron using the `cron.WithChain` option:
+
+ cron.New(cron.WithChain(
+ cron.SkipIfStillRunning(logger),
+ ))
+
+Install wrappers for individual jobs by explicitly wrapping them:
+
+ job = cron.NewChain(
+ cron.SkipIfStillRunning(logger),
+ ).Then(job)
+
+Thread safety
+
+Since the Cron service runs concurrently with the calling code, some amount of
+care must be taken to ensure proper synchronization.
+
+All cron methods are designed to be correctly synchronized as long as the caller
+ensures that invocations have a clear happens-before ordering between them.
+
+Logging
+
+Cron defines a Logger interface that is a subset of the one defined in
+github.com/go-logr/logr. It has two logging levels (Info and Error), and
+parameters are key/value pairs. This makes it possible for cron logging to plug
+into structured logging systems. An adapter, [Verbose]PrintfLogger, is provided
+to wrap the standard library *log.Logger.
+
+For additional insight into Cron operations, verbose logging may be activated
+which will record job runs, scheduling decisions, and added or removed jobs.
+Activate it with a one-off logger as follows:
+
+ cron.New(
+ cron.WithLogger(
+ cron.VerbosePrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))))
+
+
+Implementation
+
+Cron entries are stored in an array, sorted by their next activation time. Cron
+sleeps until the next job is due to be run.
+
+Upon waking:
+ - it runs each entry that is active on that second
+ - it calculates the next run times for the jobs that were run
+ - it re-sorts the array of entries by next activation time.
+ - it goes to sleep until the soonest job.
+*/
+package cron
diff --git a/vendor/github.com/robfig/cron/v3/logger.go b/vendor/github.com/robfig/cron/v3/logger.go
new file mode 100644
index 0000000..b4efcc0
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/logger.go
@@ -0,0 +1,86 @@
+package cron
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+ "time"
+)
+
+// DefaultLogger is used by Cron if none is specified.
+var DefaultLogger Logger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))
+
+// DiscardLogger can be used by callers to discard all log messages.
+var DiscardLogger Logger = PrintfLogger(log.New(ioutil.Discard, "", 0))
+
+// Logger is the interface used in this package for logging, so that any backend
+// can be plugged in. It is a subset of the github.com/go-logr/logr interface.
+type Logger interface {
+ // Info logs routine messages about cron's operation.
+ Info(msg string, keysAndValues ...interface{})
+ // Error logs an error condition.
+ Error(err error, msg string, keysAndValues ...interface{})
+}
+
+// PrintfLogger wraps a Printf-based logger (such as the standard library "log")
+// into an implementation of the Logger interface which logs errors only.
+func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
+ return printfLogger{l, false}
+}
+
+// VerbosePrintfLogger wraps a Printf-based logger (such as the standard library
+// "log") into an implementation of the Logger interface which logs everything.
+func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
+ return printfLogger{l, true}
+}
+
+type printfLogger struct {
+ logger interface{ Printf(string, ...interface{}) }
+ logInfo bool
+}
+
+func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) {
+ if pl.logInfo {
+ keysAndValues = formatTimes(keysAndValues)
+ pl.logger.Printf(
+ formatString(len(keysAndValues)),
+ append([]interface{}{msg}, keysAndValues...)...)
+ }
+}
+
+func (pl printfLogger) Error(err error, msg string, keysAndValues ...interface{}) {
+ keysAndValues = formatTimes(keysAndValues)
+ pl.logger.Printf(
+ formatString(len(keysAndValues)+2),
+ append([]interface{}{msg, "error", err}, keysAndValues...)...)
+}
+
+// formatString returns a logfmt-like format string for the number of
+// key/values.
+func formatString(numKeysAndValues int) string {
+ var sb strings.Builder
+ sb.WriteString("%s")
+ if numKeysAndValues > 0 {
+ sb.WriteString(", ")
+ }
+ for i := 0; i < numKeysAndValues/2; i++ {
+ if i > 0 {
+ sb.WriteString(", ")
+ }
+ sb.WriteString("%v=%v")
+ }
+ return sb.String()
+}
+
+// formatTimes formats any time.Time values as RFC3339.
+func formatTimes(keysAndValues []interface{}) []interface{} {
+ var formattedArgs []interface{}
+ for _, arg := range keysAndValues {
+ if t, ok := arg.(time.Time); ok {
+ arg = t.Format(time.RFC3339)
+ }
+ formattedArgs = append(formattedArgs, arg)
+ }
+ return formattedArgs
+}
diff --git a/vendor/github.com/robfig/cron/v3/option.go b/vendor/github.com/robfig/cron/v3/option.go
new file mode 100644
index 0000000..09e4278
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/option.go
@@ -0,0 +1,45 @@
+package cron
+
+import (
+ "time"
+)
+
+// Option represents a modification to the default behavior of a Cron.
+type Option func(*Cron)
+
+// WithLocation overrides the timezone of the cron instance.
+func WithLocation(loc *time.Location) Option {
+ return func(c *Cron) {
+ c.location = loc
+ }
+}
+
+// WithSeconds overrides the parser used for interpreting job schedules to
+// include a seconds field as the first one.
+func WithSeconds() Option {
+ return WithParser(NewParser(
+ Second | Minute | Hour | Dom | Month | Dow | Descriptor,
+ ))
+}
+
+// WithParser overrides the parser used for interpreting job schedules.
+func WithParser(p ScheduleParser) Option {
+ return func(c *Cron) {
+ c.parser = p
+ }
+}
+
+// WithChain specifies Job wrappers to apply to all jobs added to this cron.
+// Refer to the Chain* functions in this package for provided wrappers.
+func WithChain(wrappers ...JobWrapper) Option {
+ return func(c *Cron) {
+ c.chain = NewChain(wrappers...)
+ }
+}
+
+// WithLogger uses the provided logger.
+func WithLogger(logger Logger) Option {
+ return func(c *Cron) {
+ c.logger = logger
+ }
+}
diff --git a/vendor/github.com/robfig/cron/v3/parser.go b/vendor/github.com/robfig/cron/v3/parser.go
new file mode 100644
index 0000000..3cf8879
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/parser.go
@@ -0,0 +1,434 @@
+package cron
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Configuration options for creating a parser. Most options specify which
+// fields should be included, while others enable features. If a field is not
+// included the parser will assume a default value. These options do not change
+// the order fields are parse in.
+type ParseOption int
+
+const (
+ Second ParseOption = 1 << iota // Seconds field, default 0
+ SecondOptional // Optional seconds field, default 0
+ Minute // Minutes field, default 0
+ Hour // Hours field, default 0
+ Dom // Day of month field, default *
+ Month // Month field, default *
+ Dow // Day of week field, default *
+ DowOptional // Optional day of week field, default *
+ Descriptor // Allow descriptors such as @monthly, @weekly, etc.
+)
+
+var places = []ParseOption{
+ Second,
+ Minute,
+ Hour,
+ Dom,
+ Month,
+ Dow,
+}
+
+var defaults = []string{
+ "0",
+ "0",
+ "0",
+ "*",
+ "*",
+ "*",
+}
+
+// A custom Parser that can be configured.
+type Parser struct {
+ options ParseOption
+}
+
+// NewParser creates a Parser with custom options.
+//
+// It panics if more than one Optional is given, since it would be impossible to
+// correctly infer which optional is provided or missing in general.
+//
+// Examples
+//
+// // Standard parser without descriptors
+// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
+// sched, err := specParser.Parse("0 0 15 */3 *")
+//
+// // Same as above, just excludes time fields
+// subsParser := NewParser(Dom | Month | Dow)
+// sched, err := specParser.Parse("15 */3 *")
+//
+// // Same as above, just makes Dow optional
+// subsParser := NewParser(Dom | Month | DowOptional)
+// sched, err := specParser.Parse("15 */3")
+//
+func NewParser(options ParseOption) Parser {
+ optionals := 0
+ if options&DowOptional > 0 {
+ optionals++
+ }
+ if options&SecondOptional > 0 {
+ optionals++
+ }
+ if optionals > 1 {
+ panic("multiple optionals may not be configured")
+ }
+ return Parser{options}
+}
+
+// Parse returns a new crontab schedule representing the given spec.
+// It returns a descriptive error if the spec is not valid.
+// It accepts crontab specs and features configured by NewParser.
+func (p Parser) Parse(spec string) (Schedule, error) {
+ if len(spec) == 0 {
+ return nil, fmt.Errorf("empty spec string")
+ }
+
+ // Extract timezone if present
+ var loc = time.Local
+ if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
+ var err error
+ i := strings.Index(spec, " ")
+ eq := strings.Index(spec, "=")
+ if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil {
+ return nil, fmt.Errorf("provided bad location %s: %v", spec[eq+1:i], err)
+ }
+ spec = strings.TrimSpace(spec[i:])
+ }
+
+ // Handle named schedules (descriptors), if configured
+ if strings.HasPrefix(spec, "@") {
+ if p.options&Descriptor == 0 {
+ return nil, fmt.Errorf("parser does not accept descriptors: %v", spec)
+ }
+ return parseDescriptor(spec, loc)
+ }
+
+ // Split on whitespace.
+ fields := strings.Fields(spec)
+
+ // Validate & fill in any omitted or optional fields
+ var err error
+ fields, err = normalizeFields(fields, p.options)
+ if err != nil {
+ return nil, err
+ }
+
+ field := func(field string, r bounds) uint64 {
+ if err != nil {
+ return 0
+ }
+ var bits uint64
+ bits, err = getField(field, r)
+ return bits
+ }
+
+ var (
+ second = field(fields[0], seconds)
+ minute = field(fields[1], minutes)
+ hour = field(fields[2], hours)
+ dayofmonth = field(fields[3], dom)
+ month = field(fields[4], months)
+ dayofweek = field(fields[5], dow)
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return &SpecSchedule{
+ Second: second,
+ Minute: minute,
+ Hour: hour,
+ Dom: dayofmonth,
+ Month: month,
+ Dow: dayofweek,
+ Location: loc,
+ }, nil
+}
+
+// normalizeFields takes a subset set of the time fields and returns the full set
+// with defaults (zeroes) populated for unset fields.
+//
+// As part of performing this function, it also validates that the provided
+// fields are compatible with the configured options.
+func normalizeFields(fields []string, options ParseOption) ([]string, error) {
+ // Validate optionals & add their field to options
+ optionals := 0
+ if options&SecondOptional > 0 {
+ options |= Second
+ optionals++
+ }
+ if options&DowOptional > 0 {
+ options |= Dow
+ optionals++
+ }
+ if optionals > 1 {
+ return nil, fmt.Errorf("multiple optionals may not be configured")
+ }
+
+ // Figure out how many fields we need
+ max := 0
+ for _, place := range places {
+ if options&place > 0 {
+ max++
+ }
+ }
+ min := max - optionals
+
+ // Validate number of fields
+ if count := len(fields); count < min || count > max {
+ if min == max {
+ return nil, fmt.Errorf("expected exactly %d fields, found %d: %s", min, count, fields)
+ }
+ return nil, fmt.Errorf("expected %d to %d fields, found %d: %s", min, max, count, fields)
+ }
+
+ // Populate the optional field if not provided
+ if min < max && len(fields) == min {
+ switch {
+ case options&DowOptional > 0:
+ fields = append(fields, defaults[5]) // TODO: improve access to default
+ case options&SecondOptional > 0:
+ fields = append([]string{defaults[0]}, fields...)
+ default:
+ return nil, fmt.Errorf("unknown optional field")
+ }
+ }
+
+ // Populate all fields not part of options with their defaults
+ n := 0
+ expandedFields := make([]string, len(places))
+ copy(expandedFields, defaults)
+ for i, place := range places {
+ if options&place > 0 {
+ expandedFields[i] = fields[n]
+ n++
+ }
+ }
+ return expandedFields, nil
+}
+
+var standardParser = NewParser(
+ Minute | Hour | Dom | Month | Dow | Descriptor,
+)
+
+// ParseStandard returns a new crontab schedule representing the given
+// standardSpec (https://en.wikipedia.org/wiki/Cron). It requires 5 entries
+// representing: minute, hour, day of month, month and day of week, in that
+// order. It returns a descriptive error if the spec is not valid.
+//
+// It accepts
+// - Standard crontab specs, e.g. "* * * * ?"
+// - Descriptors, e.g. "@midnight", "@every 1h30m"
+func ParseStandard(standardSpec string) (Schedule, error) {
+ return standardParser.Parse(standardSpec)
+}
+
+// getField returns an Int with the bits set representing all of the times that
+// the field represents or error parsing field value. A "field" is a comma-separated
+// list of "ranges".
+func getField(field string, r bounds) (uint64, error) {
+ var bits uint64
+ ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
+ for _, expr := range ranges {
+ bit, err := getRange(expr, r)
+ if err != nil {
+ return bits, err
+ }
+ bits |= bit
+ }
+ return bits, nil
+}
+
+// getRange returns the bits indicated by the given expression:
+// number | number "-" number [ "/" number ]
+// or error parsing range.
+func getRange(expr string, r bounds) (uint64, error) {
+ var (
+ start, end, step uint
+ rangeAndStep = strings.Split(expr, "/")
+ lowAndHigh = strings.Split(rangeAndStep[0], "-")
+ singleDigit = len(lowAndHigh) == 1
+ err error
+ )
+
+ var extra uint64
+ if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
+ start = r.min
+ end = r.max
+ extra = starBit
+ } else {
+ start, err = parseIntOrName(lowAndHigh[0], r.names)
+ if err != nil {
+ return 0, err
+ }
+ switch len(lowAndHigh) {
+ case 1:
+ end = start
+ case 2:
+ end, err = parseIntOrName(lowAndHigh[1], r.names)
+ if err != nil {
+ return 0, err
+ }
+ default:
+ return 0, fmt.Errorf("too many hyphens: %s", expr)
+ }
+ }
+
+ switch len(rangeAndStep) {
+ case 1:
+ step = 1
+ case 2:
+ step, err = mustParseInt(rangeAndStep[1])
+ if err != nil {
+ return 0, err
+ }
+
+ // Special handling: "N/step" means "N-max/step".
+ if singleDigit {
+ end = r.max
+ }
+ if step > 1 {
+ extra = 0
+ }
+ default:
+ return 0, fmt.Errorf("too many slashes: %s", expr)
+ }
+
+ if start < r.min {
+ return 0, fmt.Errorf("beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
+ }
+ if end > r.max {
+ return 0, fmt.Errorf("end of range (%d) above maximum (%d): %s", end, r.max, expr)
+ }
+ if start > end {
+ return 0, fmt.Errorf("beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
+ }
+ if step == 0 {
+ return 0, fmt.Errorf("step of range should be a positive number: %s", expr)
+ }
+
+ return getBits(start, end, step) | extra, nil
+}
+
+// parseIntOrName returns the (possibly-named) integer contained in expr.
+func parseIntOrName(expr string, names map[string]uint) (uint, error) {
+ if names != nil {
+ if namedInt, ok := names[strings.ToLower(expr)]; ok {
+ return namedInt, nil
+ }
+ }
+ return mustParseInt(expr)
+}
+
+// mustParseInt parses the given expression as an int or returns an error.
+func mustParseInt(expr string) (uint, error) {
+ num, err := strconv.Atoi(expr)
+ if err != nil {
+ return 0, fmt.Errorf("failed to parse int from %s: %s", expr, err)
+ }
+ if num < 0 {
+ return 0, fmt.Errorf("negative number (%d) not allowed: %s", num, expr)
+ }
+
+ return uint(num), nil
+}
+
+// getBits sets all bits in the range [min, max], modulo the given step size.
+func getBits(min, max, step uint) uint64 {
+ var bits uint64
+
+ // If step is 1, use shifts.
+ if step == 1 {
+ return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
+ }
+
+ // Else, use a simple loop.
+ for i := min; i <= max; i += step {
+ bits |= 1 << i
+ }
+ return bits
+}
+
+// all returns all bits within the given bounds. (plus the star bit)
+func all(r bounds) uint64 {
+ return getBits(r.min, r.max, 1) | starBit
+}
+
+// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
+func parseDescriptor(descriptor string, loc *time.Location) (Schedule, error) {
+ switch descriptor {
+ case "@yearly", "@annually":
+ return &SpecSchedule{
+ Second: 1 << seconds.min,
+ Minute: 1 << minutes.min,
+ Hour: 1 << hours.min,
+ Dom: 1 << dom.min,
+ Month: 1 << months.min,
+ Dow: all(dow),
+ Location: loc,
+ }, nil
+
+ case "@monthly":
+ return &SpecSchedule{
+ Second: 1 << seconds.min,
+ Minute: 1 << minutes.min,
+ Hour: 1 << hours.min,
+ Dom: 1 << dom.min,
+ Month: all(months),
+ Dow: all(dow),
+ Location: loc,
+ }, nil
+
+ case "@weekly":
+ return &SpecSchedule{
+ Second: 1 << seconds.min,
+ Minute: 1 << minutes.min,
+ Hour: 1 << hours.min,
+ Dom: all(dom),
+ Month: all(months),
+ Dow: 1 << dow.min,
+ Location: loc,
+ }, nil
+
+ case "@daily", "@midnight":
+ return &SpecSchedule{
+ Second: 1 << seconds.min,
+ Minute: 1 << minutes.min,
+ Hour: 1 << hours.min,
+ Dom: all(dom),
+ Month: all(months),
+ Dow: all(dow),
+ Location: loc,
+ }, nil
+
+ case "@hourly":
+ return &SpecSchedule{
+ Second: 1 << seconds.min,
+ Minute: 1 << minutes.min,
+ Hour: all(hours),
+ Dom: all(dom),
+ Month: all(months),
+ Dow: all(dow),
+ Location: loc,
+ }, nil
+
+ }
+
+ const every = "@every "
+ if strings.HasPrefix(descriptor, every) {
+ duration, err := time.ParseDuration(descriptor[len(every):])
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse duration %s: %s", descriptor, err)
+ }
+ return Every(duration), nil
+ }
+
+ return nil, fmt.Errorf("unrecognized descriptor: %s", descriptor)
+}
diff --git a/vendor/github.com/robfig/cron/v3/spec.go b/vendor/github.com/robfig/cron/v3/spec.go
new file mode 100644
index 0000000..fa1e241
--- /dev/null
+++ b/vendor/github.com/robfig/cron/v3/spec.go
@@ -0,0 +1,188 @@
+package cron
+
+import "time"
+
+// SpecSchedule specifies a duty cycle (to the second granularity), based on a
+// traditional crontab specification. It is computed initially and stored as bit sets.
+type SpecSchedule struct {
+ Second, Minute, Hour, Dom, Month, Dow uint64
+
+ // Override location for this schedule.
+ Location *time.Location
+}
+
+// bounds provides a range of acceptable values (plus a map of name to value).
+type bounds struct {
+ min, max uint
+ names map[string]uint
+}
+
+// The bounds for each field.
+var (
+ seconds = bounds{0, 59, nil}
+ minutes = bounds{0, 59, nil}
+ hours = bounds{0, 23, nil}
+ dom = bounds{1, 31, nil}
+ months = bounds{1, 12, map[string]uint{
+ "jan": 1,
+ "feb": 2,
+ "mar": 3,
+ "apr": 4,
+ "may": 5,
+ "jun": 6,
+ "jul": 7,
+ "aug": 8,
+ "sep": 9,
+ "oct": 10,
+ "nov": 11,
+ "dec": 12,
+ }}
+ dow = bounds{0, 6, map[string]uint{
+ "sun": 0,
+ "mon": 1,
+ "tue": 2,
+ "wed": 3,
+ "thu": 4,
+ "fri": 5,
+ "sat": 6,
+ }}
+)
+
+const (
+ // Set the top bit if a star was included in the expression.
+ starBit = 1 << 63
+)
+
+// Next returns the next time this schedule is activated, greater than the given
+// time. If no time can be found to satisfy the schedule, return the zero time.
+func (s *SpecSchedule) Next(t time.Time) time.Time {
+ // General approach
+ //
+ // For Month, Day, Hour, Minute, Second:
+ // Check if the time value matches. If yes, continue to the next field.
+ // If the field doesn't match the schedule, then increment the field until it matches.
+ // While incrementing the field, a wrap-around brings it back to the beginning
+ // of the field list (since it is necessary to re-verify previous field
+ // values)
+
+ // Convert the given time into the schedule's timezone, if one is specified.
+ // Save the original timezone so we can convert back after we find a time.
+ // Note that schedules without a time zone specified (time.Local) are treated
+ // as local to the time provided.
+ origLocation := t.Location()
+ loc := s.Location
+ if loc == time.Local {
+ loc = t.Location()
+ }
+ if s.Location != time.Local {
+ t = t.In(s.Location)
+ }
+
+ // Start at the earliest possible time (the upcoming second).
+ t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
+
+ // This flag indicates whether a field has been incremented.
+ added := false
+
+ // If no time is found within five years, return zero.
+ yearLimit := t.Year() + 5
+
+WRAP:
+ if t.Year() > yearLimit {
+ return time.Time{}
+ }
+
+ // Find the first applicable month.
+ // If it's this month, then do nothing.
+ for 1< 12 {
+ t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
+ } else {
+ t = t.Add(time.Duration(-t.Hour()) * time.Hour)
+ }
+ }
+
+ if t.Day() == 1 {
+ goto WRAP
+ }
+ }
+
+ for 1< 0
+ dowMatch bool = 1< 0
+ )
+ if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
+ return domMatch && dowMatch
+ }
+ return domMatch || dowMatch
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 9c54090..fd43ab4 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -54,6 +54,9 @@ github.com/pmezard/go-difflib/difflib
# github.com/richardlehane/msoleps v1.0.3
## explicit
github.com/richardlehane/msoleps/types
+# github.com/robfig/cron/v3 v3.0.1
+## explicit; go 1.12
+github.com/robfig/cron/v3
# github.com/sirupsen/logrus v1.9.3
## explicit; go 1.13
github.com/sirupsen/logrus
diff --git a/wps/word.go b/wps/word.go
index 4ead381..2b6373a 100644
--- a/wps/word.go
+++ b/wps/word.go
@@ -1,6 +1,7 @@
package wps
import (
+ "dt_automate/tool"
"log"
"strings"
"time"
@@ -45,7 +46,7 @@ func Word() {
for _, p := range paragraphs {
for _, r := range p.Runs() {
if strings.Contains(r.Text(), "{{DATE_A}}") {
- Replacetext(r, "{{DATE_A}}", time.Date(2025, time.February, 25, 0, 0, 0, 0, time.Local).Format("2006年01月02日"))
+ Replacetext(r, "{{DATE_A}}", time.Unix(tool.Timestamp("second"), 0).Format("2006年01月02日"))
} else if strings.Contains(r.Text(), "{{HLW_FW_1}}") { //插入互联网防火墙图片
Replaceimg(r, doc, "img/hw_fw1.png")
} else if strings.Contains(r.Text(), "{{HLW_AFC2000_1}}") { //插入互联网流量清洗图片
@@ -91,14 +92,14 @@ func Word() {
}
cell.Properties().SetVerticalAlignment(wml.ST_VerticalJcCenter)
// log.Println(time.Date(2025, time.February, 25, 0, 0, 0, 0, time.Local).Format("2006年01月02日"))
- cell.Paragraphs()[k].AddRun().AddText(time.Date(2025, time.February, 25, 0, 0, 0, 0, time.Local).Format("2006年01月02日"))
+ cell.Paragraphs()[k].AddRun().AddText(time.Unix(tool.Timestamp("second"), 0).Format("2006年01月02日"))
}
// log.Println(b)
b = ""
}
}
}
- doc.SaveToFile("大同信创云平台安全运维日报" + time.Date(2025, time.February, 25, 0, 0, 0, 0, time.Local).Format("20060102") + ".docx")
+ doc.SaveToFile("大同信创云平台安全运维日报" + time.Unix(tool.Timestamp("second"), 0).Format("20060102") + ".docx")
}
// 文字替换
@@ -113,7 +114,7 @@ func Replacetext(r document.Run, src, ends string) {
// 插入图片
func Replaceimg(r document.Run, doc *document.Document, path_img string) {
r.ClearContent()
- img1, err := common.ImageFromFile("img/hw_fw1.png")
+ img1, err := common.ImageFromFile(path_img)
if err != nil {
log.Fatalf("unable to create image: %s", err)
}
diff --git a/大同信创云5.2.0 互联网区-2025年02月26日巡检报告.docx b/大同信创云5.2.0 互联网区-2025年02月26日巡检报告.docx
new file mode 100644
index 0000000..0fd6f49
Binary files /dev/null and b/大同信创云5.2.0 互联网区-2025年02月26日巡检报告.docx differ
diff --git a/大同信创云5.2.0 政务外网区-2025年02月26日巡检报告.docx b/大同信创云5.2.0 政务外网区-2025年02月26日巡检报告.docx
new file mode 100644
index 0000000..6c96170
Binary files /dev/null and b/大同信创云5.2.0 政务外网区-2025年02月26日巡检报告.docx differ
diff --git a/大同信创云巡检日报-2025-02-26.xlsx b/大同信创云巡检日报-2025-02-26.xlsx
new file mode 100644
index 0000000..243939f
Binary files /dev/null and b/大同信创云巡检日报-2025-02-26.xlsx differ
diff --git a/大同信创云平台安全运维日报20250225.docx b/大同信创云平台安全运维日报20250225.docx
deleted file mode 100644
index 2190431..0000000
Binary files a/大同信创云平台安全运维日报20250225.docx and /dev/null differ
diff --git a/大同信创云平台安全运维日报20250226.docx b/大同信创云平台安全运维日报20250226.docx
new file mode 100644
index 0000000..c1238a1
Binary files /dev/null and b/大同信创云平台安全运维日报20250226.docx differ