@@ -5,6 +5,8 @@ package main
55import (
66 "bytes"
77 "context"
8+ _ "embed"
9+ "encoding/json"
810 "flag"
911 "fmt"
1012 "image"
@@ -14,37 +16,72 @@ import (
1416 "log"
1517 "net/url"
1618 "os"
19+ "sort"
1720 "strings"
1821 "time"
1922
2023 "github.com/chromedp/cdproto/cdp"
2124 "github.com/chromedp/cdproto/dom"
2225 "github.com/chromedp/chromedp"
2326 "github.com/kenshaw/rasterm"
27+ "golang.org/x/exp/maps"
2428)
2529
2630const dataSel = `div[data-ve-view]`
2731
32+ const svgSel = `#wob_d svg path`
33+
2834func main () {
2935 verbose := flag .Bool ("v" , false , "verbose" )
3036 timeout := flag .Duration ("timeout" , 1 * time .Minute , "timeout" )
31- query := flag .String ("q" , "" , "query" )
32- lang := flag .String ("hl" , "en " , "language" )
37+ query := flag .String ("q" , "" , "weather query" )
38+ lang := flag .String ("hl" , "" , "language (see hl.json) " )
3339 unit := flag .String ("unit" , "" , "temperature unit (C, F, or blank)" )
40+ typ := flag .String ("type" , "" , "selection type (temp, rain, wind)" )
41+ day := flag .Int ("day" , 0 , "day (0-7)" )
3442 scale := flag .Float64 ("scale" , 1.5 , "scale" )
3543 padding := flag .Int ("padding" , 20 , "padding" )
3644 out := flag .String ("out" , "" , "out file" )
3745 flag .Parse ()
38- if err := run (context .Background (), * verbose , * timeout , * query , * lang , * unit , * scale , * padding , * out ); err != nil {
46+ if err := run (context .Background (), * verbose , * timeout , * query , * lang , * unit , * typ , * day , * scale , * padding , * out ); err != nil {
3947 fmt .Fprintf (os .Stderr , "error: %v\n " , err )
48+ if strings .HasPrefix (err .Error (), "invalid lang " ) {
49+ fmt .Fprint (os .Stderr , "\n valid languages:\n " )
50+ keys := maps .Keys (langs )
51+ sort .Strings (keys )
52+ for _ , key := range keys {
53+ fmt .Fprintf (os .Stderr , " %s:\t %s\n " , key , langs [key ])
54+ }
55+ }
4056 os .Exit (1 )
4157 }
4258}
4359
44- func run (ctx context.Context , verbose bool , timeout time.Duration , query , lang , unit string , scale float64 , padding int , out string ) error {
60+ func run (ctx context.Context , verbose bool , timeout time.Duration , query , lang , unit , typ string , day int , scale float64 , padding int , out string ) error {
61+ // check
62+ lang = strings .ToLower (lang )
63+ if _ , ok := langs [lang ]; ! ok && lang != "" {
64+ return fmt .Errorf ("invalid lang %q" , lang )
65+ }
4566 if unit = strings .ToUpper (unit ); unit != "F" && unit != "C" && unit != "" {
4667 return fmt .Errorf ("invalid unit %q" , unit )
4768 }
69+ switch typ = strings .ToLower (typ ); typ {
70+ case "temp" :
71+ typ = ""
72+ case "" , "rain" , "wind" :
73+ default :
74+ return fmt .Errorf ("invalid type %q" , typ )
75+ }
76+ if day < 0 || day > 7 {
77+ return fmt .Errorf ("invalid day %d" , day )
78+ }
79+ if scale <= 0 {
80+ return fmt .Errorf ("invalid scale %f" , scale )
81+ }
82+ if padding < 0 {
83+ return fmt .Errorf ("invalid padding %d" , padding )
84+ }
4885
4986 query = "weather " + query
5087
@@ -72,22 +109,32 @@ func run(ctx context.Context, verbose bool, timeout time.Duration, query, lang,
72109 if err := chromedp .Run (ctx ,
73110 chromedp .Navigate ("https://www.google.com/search?" + v .Encode ()),
74111 chromedp .WaitVisible (dataSel , chromedp .ByQuery ),
112+ chromedp .WaitVisible (svgSel , chromedp .ByQuery ),
75113 chromedp .Nodes (dataSel , & nodes , chromedp .ByQuery , chromedp .NodeVisible ),
76114 chromedp .ActionFunc (func (ctx context.Context ) error {
77115 return dom .RequestChildNodes (nodes [0 ].NodeID ).WithDepth (- 1 ).Do (ctx )
78116 }),
79- chromedp .Sleep (50 * time .Millisecond ),
80117 ); err != nil {
81118 return err
82119 }
83120
121+ // click on unit
84122 if unit != "" {
85- // click on unit button if present
86123 if node := findNode (`°` + unit , nodes ); node != nil {
87124 _ = chromedp .Run (ctx , chromedp .MouseClickNode (node ))
88125 }
89126 }
90127
128+ // click on type
129+ if typ != "" {
130+ _ = chromedp .Run (ctx , chromedp .Click ("wob_" + typ , chromedp .ByID ))
131+ }
132+
133+ // click on day
134+ if day != 0 {
135+ _ = chromedp .Run (ctx , chromedp .Click (fmt .Sprintf (`//*[@data-wob-di=%d]` , day )))
136+ }
137+
91138 // capture screenshot
92139 var buf []byte
93140 if err := chromedp .Run (ctx , chromedp .ScreenshotScale (dataSel , scale , & buf , chromedp .ByQuery )); err != nil {
@@ -140,3 +187,14 @@ func findNode(val string, nodes []*cdp.Node) *cdp.Node {
140187 }
141188 return nil
142189}
190+
191+ var langs map [string ]string
192+
193+ func init () {
194+ if err := json .Unmarshal (hlJSON , & langs ); err != nil {
195+ panic (err )
196+ }
197+ }
198+
199+ //go:embed hl.json
200+ var hlJSON []byte
0 commit comments