diff --git a/cmd/fake-server/main.go b/cmd/fake-server/main.go index 601ac0f..684f6cc 100644 --- a/cmd/fake-server/main.go +++ b/cmd/fake-server/main.go @@ -14,12 +14,13 @@ var weiboTests = [4]string{ } var weiboIdx = 0 -var akAnnoTests = [5]string{ +var akAnnoTests = [6]string{ "tests/akanno/00-init.json", "tests/akanno/01-new-gacha.json", "tests/akanno/02-activity-end.json", "tests/akanno/03-placehold.json", "tests/akanno/04-dev-news.json", + "tests/akanno/05-null.json", } var akAnnoIdx = 0 @@ -33,7 +34,7 @@ func weiboHandler(w http.ResponseWriter, r *http.Request) { func akAnnoHandler(w http.ResponseWriter, r *http.Request) { data, _ := ioutil.ReadFile(akAnnoTests[akAnnoIdx]) log.Printf("Deliverd %v\n", akAnnoTests[akAnnoIdx]) - akAnnoIdx = (akAnnoIdx + 1) % 5 + akAnnoIdx = (akAnnoIdx + 1) % 6 w.Write(data) } diff --git a/config.go b/config.go index 524d6cb..33bada6 100644 --- a/config.go +++ b/config.go @@ -31,11 +31,11 @@ func LoadConfig(path string) (YamlConfig, error) { } if config.Version == "1.0" { - return config, errors.New("Please upgrade the config file") + return config, errors.New("please upgrade the config file") } if config.Version != "1.1" { - return config, errors.New("Invalid config version") + return config, errors.New("invalid config version") } return config, nil diff --git a/config_test.go b/config_test.go index b6158db..a029ff9 100644 --- a/config_test.go +++ b/config_test.go @@ -34,7 +34,7 @@ func TestParseNotifiers(t *testing.T) { } func TestParseWatchers(t *testing.T) { - watchers, err := watcher.ParseWatchers(config.Watchers, false) + watchers, err := watcher.ParseWatchers(config.Watchers, "", false) if err != nil { t.Error(err) diff --git a/dr-feeder.go b/dr-feeder.go index cad2ade..584fb53 100644 --- a/dr-feeder.go +++ b/dr-feeder.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "math/rand" + "path" "time" "github.com/hguandl/dr-feeder/v2/common" @@ -13,7 +14,7 @@ import ( ) // Version is current `git describe --tags` infomation. -var Version string = "v2.1.1" +var Version string = "v2.2.0" func consume(ch chan common.NotifyPayload, notifiers []notifier.Notifier) { for { @@ -37,7 +38,7 @@ func watch(watcher watcher.Watcher, ch chan common.NotifyPayload) { func main() { printVersion := flag.Bool("V", false, "Print current version") debugMode := flag.Bool("d", false, "Debug with fake server") - pathPtr := flag.String("c", "config.yaml", "Configuration file") + pathPtr := flag.String("c", ".", "Configuration and data directory") flag.Parse() if *printVersion { @@ -49,7 +50,7 @@ func main() { println("Running on debug mode...") } - config, err := LoadConfig(*pathPtr) + config, err := LoadConfig(path.Join(*pathPtr, "config.yaml")) if err != nil { log.Fatal(err) } @@ -59,7 +60,7 @@ func main() { log.Fatal(err) } - watchers, err := watcher.ParseWatchers(config.Watchers, *debugMode) + watchers, err := watcher.ParseWatchers(config.Watchers, *pathPtr, *debugMode) if err != nil { log.Fatal(err) } diff --git a/go.mod b/go.mod index f03630e..ce4623b 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( github.com/antchfx/htmlquery v1.2.3 github.com/gocolly/colly/v2 v2.1.0 github.com/mitchellh/mapstructure v1.4.1 + go.etcd.io/bbolt v1.3.5 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 20edea5..a241be0 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA= github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -85,6 +87,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ 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-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= diff --git a/tests/akanno/05-null.json b/tests/akanno/05-null.json new file mode 100644 index 0000000..33d96d5 --- /dev/null +++ b/tests/akanno/05-null.json @@ -0,0 +1,99 @@ +{ + "focusAnnounceId": null, + "announceList": [ + { + "announceId": "601", + "title": "【多维合作】\n模式问卷调查", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/601.html", + "day": 19, + "month": 3, + "group": "ACTIVITY" + }, + { + "announceId": "600", + "title": "特定干员\n限时出率上升", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/600.html", + "day": 18, + "month": 3, + "group": "ACTIVITY" + }, + { + "announceId": "594", + "title": "【源石尘行动】\n限时活动开启", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/594.html", + "day": 9, + "month": 3, + "group": "ACTIVITY" + }, + { + "announceId": "593", + "title": "罗德岛防御协议\n限时开启", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/593.html", + "day": 9, + "month": 3, + "group": "ACTIVITY" + }, + { + "announceId": "585", + "title": "「制作组通讯」\n#9期", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/585.html", + "day": 23, + "month": 2, + "group": "SYSTEM" + }, + { + "announceId": "97", + "title": "新人寻访特惠\n必得六星干员", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/97.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + }, + { + "announceId": "95", + "title": "通关特定关卡\n赠送专属时装", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/95.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + }, + { + "announceId": "192", + "title": "《明日方舟》\n公测开启说明", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/192.html", + "day": 30, + "month": 4, + "group": "SYSTEM" + }, + { + "announceId": "98", + "title": "《明日方舟》\n公平运营申明", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/98.html", + "day": 30, + "month": 4, + "group": "SYSTEM" + }, + { + "announceId": "94", + "title": "常驻活动介绍", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/94.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + } + ], + "extra": { + "enable": false, + "name": "额外活动" + } +} diff --git a/watcher/akanno.go b/watcher/akanno.go index c6b6dda..c4fc64e 100644 --- a/watcher/akanno.go +++ b/watcher/akanno.go @@ -9,6 +9,8 @@ import ( "github.com/gocolly/colly/v2" "github.com/hguandl/dr-feeder/v2/common" + + bolt "go.etcd.io/bbolt" ) const iOSClientUA = "arknights/385" + @@ -17,18 +19,25 @@ const iOSClientUA = "arknights/385" + type akAnnounceWatcher struct { name string - focusID string latestAnno announce - existedID []string debugURL string + db *bolt.DB } // NewAkAnnounceWatcher creates a Watcher of Arknights game annoucements. -func NewAkAnnounceWatcher(debugURL string) (Watcher, error) { +func NewAkAnnounceWatcher(dbPath string, debugURL string) (Watcher, error) { + var err error = nil + watcher := new(akAnnounceWatcher) watcher.name = "明日方舟客户端公告" watcher.debugURL = debugURL - err := watcher.setup() + + watcher.db, err = bolt.Open(dbPath, 0666, nil) + if err != nil { + return watcher, err + } + + err = watcher.setup() return watcher, err } @@ -71,19 +80,21 @@ func (watcher *akAnnounceWatcher) setup() error { return err } - watcher.focusID = data.FocusAnnounceID - watcher.existedID = flushIDList(data.AnnounceList) + watcher.storeAnnos(data.AnnounceList) return nil } -func flushIDList(announceList []announce) []string { - ret := make([]string, len(announceList)) - for i, anno := range announceList { - ret[i] = anno.AnnounceID - } +func (watcher *akAnnounceWatcher) storeAnnos(announceList []announce) error { + err := watcher.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte("AkAnno")) + for _, anno := range announceList { + err = b.Put([]byte(anno.AnnounceID), []byte(anno.Title)) + } + return err + }) - return ret + return err } func (watcher *akAnnounceWatcher) update() bool { @@ -93,43 +104,29 @@ func (watcher *akAnnounceWatcher) update() bool { return false } - if watcher.focusID != data.FocusAnnounceID { - watcher.focusID = data.FocusAnnounceID - existed := false + ret := false + err = watcher.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte("AkAnno")) for _, anno := range data.AnnounceList { - if anno.AnnounceID == data.FocusAnnounceID { - existed = true + v := b.Get([]byte(anno.AnnounceID)) + if v == nil { + if strings.Contains(anno.Title, "制作组通讯") { + watcher.latestAnno = anno + ret = true + } + err = b.Put([]byte(anno.AnnounceID), []byte(anno.Title)) break } } - if existed == false { - watcher.latestAnno = announce{ - Title: "出现公告弹窗,可能会有新饼", - WebURL: "https://ak.hypergryph.com/news.html", - } - return true - } - } + return err + }) - for _, anno := range data.AnnounceList { - newID := anno.AnnounceID - existed := false - for _, oldID := range watcher.existedID { - if newID == oldID { - existed = true - break - } - } - if existed == false { - watcher.existedID = flushIDList(data.AnnounceList) - if strings.Contains(anno.Title, "制作组通讯") { - watcher.latestAnno = anno - return true - } - } + if err != nil { + log.Println(err) + return false } - return false + return ret } func (watcher akAnnounceWatcher) parseContent() common.NotifyPayload { diff --git a/watcher/watcher.go b/watcher/watcher.go index 2dc79b3..7b0047f 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -3,6 +3,7 @@ package watcher import ( "errors" "fmt" + "path" "github.com/hguandl/dr-feeder/v2/common" "github.com/mitchellh/mapstructure" @@ -36,31 +37,36 @@ func wrapDebug(debugURL string, debugMode bool) string { } // ParseWatchers decodes the config returns a list of Watchers. -func ParseWatchers(configs []map[string]interface{}, debugMode bool) ([]Watcher, error) { +func ParseWatchers(configs []map[string]interface{}, dataPath string, debugMode bool) ([]Watcher, error) { var err error = nil ret := make([]Watcher, len(configs)) for idx, config := range configs { watcherType, ok := config["type"].(string) if !ok { - err = errors.New("Invalid watcher config") + err = errors.New("invalid watcher config") break } switch watcherType { case "weibo": var wbConfig weiboConfig - err = mapstructure.Decode(config, &wbConfig) + if err = mapstructure.Decode(config, &wbConfig); err != nil { + break + } ret[idx], err = NewWeiboWatcher(wbConfig.UID, wrapDebug(wbConfig.DebugURL, debugMode)) case "akanno": var akConfig akAnnoConfig - err = mapstructure.Decode(config, &akConfig) + if err = mapstructure.Decode(config, &akConfig); err != nil { + break + } if akConfig.Channel != "IOS" { - err = fmt.Errorf("Unsupported channel \"%v\"", akConfig.Channel) + err = fmt.Errorf("unsupported channel \"%v\"", akConfig.Channel) + break } - ret[idx], err = NewAkAnnounceWatcher(wrapDebug(akConfig.DebugURL, debugMode)) + ret[idx], err = NewAkAnnounceWatcher(path.Join(dataPath, "akanno.db"), wrapDebug(akConfig.DebugURL, debugMode)) default: - err = fmt.Errorf("Unknown watcher #%d with type \"%s\"", idx, watcherType) + err = fmt.Errorf("unknown watcher #%d with type \"%s\"", idx, watcherType) } if err != nil {