next #2
							
								
								
									
										105
									
								
								bot/bot.go
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								bot/bot.go
									
									
									
									
									
								
							| @ -1,10 +1,10 @@ | |||||||
| package bot | package bot | ||||||
| 
 | 
 | ||||||
| //import "github.com/kr/pretty"
 | import "github.com/kr/pretty" | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/mattermost/mattermost-server/v5/model" | 	"github.com/mattermost/mattermost-server/v5/model" | ||||||
| 	"net/http" | 	"github.com/rs/zerolog/log" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| @ -43,6 +43,14 @@ func New(options ...func(s *Bot)) *Bot { | |||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (b *Bot) identify() { | ||||||
|  | 	log.Info(). | ||||||
|  | 		Str("version", b.Version). | ||||||
|  | 		Str("buildarch", b.Buildarch). | ||||||
|  | 		Str("commit", b.Commit). | ||||||
|  | 		Msg("starting") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (b *Bot) Main() int { | func (b *Bot) Main() int { | ||||||
| 	println(b.BotName) | 	println(b.BotName) | ||||||
| 
 | 
 | ||||||
| @ -50,6 +58,8 @@ func (b *Bot) Main() int { | |||||||
| 
 | 
 | ||||||
| 	b.SetupGracefulShutdown() | 	b.SetupGracefulShutdown() | ||||||
| 
 | 
 | ||||||
|  | 	b.setupLogging() | ||||||
|  | 
 | ||||||
| 	b.client = model.NewAPIv4Client(b.APIURL) | 	b.client = model.NewAPIv4Client(b.APIURL) | ||||||
| 
 | 
 | ||||||
| 	// Lets test to see if the mattermost server is up and running
 | 	// Lets test to see if the mattermost server is up and running
 | ||||||
| @ -72,7 +82,7 @@ func (b *Bot) Main() int { | |||||||
| 
 | 
 | ||||||
| 	// Lets create a bot channel for logging debug messages into
 | 	// Lets create a bot channel for logging debug messages into
 | ||||||
| 	b.CreateBotDebuggingChannelIfNeeded() | 	b.CreateBotDebuggingChannelIfNeeded() | ||||||
| 	msg := fmt.Sprintf("_**%s** (version `%s`) is now starting up", b.BotName, b.Version) | 	msg := fmt.Sprintf("_**%s** (version `%s`) is now starting up_", b.BotName, b.Version) | ||||||
| 	b.SendMsgToDebuggingChannel(msg, "") | 	b.SendMsgToDebuggingChannel(msg, "") | ||||||
| 
 | 
 | ||||||
| 	// Lets start listening to some channels via the websocket!
 | 	// Lets start listening to some channels via the websocket!
 | ||||||
| @ -230,38 +240,6 @@ func (b *Bot) Shutdown() { | |||||||
| 	syscall.Kill(syscall.Getpid(), syscall.SIGINT) | 	syscall.Kill(syscall.Getpid(), syscall.SIGINT) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (b *Bot) HandleWeatherRequest(channelid string, postid string, message string) { |  | ||||||
| 	msg := fmt.Sprintf("weather request received: `%s`", message) |  | ||||||
| 	b.SendMsgToChannel(msg, postid, channelid) |  | ||||||
| 
 |  | ||||||
| 	r := regexp.MustCompile(`metar\s+([A-Za-z]{4})`) |  | ||||||
| 	loc := r.FindString(message) |  | ||||||
| 	if loc == "" { |  | ||||||
| 		b.SendMsgToChannel("error, sorry", postid, channelid) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	token := os.Getenv("METAR_API_TOKEN") |  | ||||||
| 	url := fmt.Sprintf("https://avwx.rest/api/metar/%s?options=&airport=true&reporting=true&format=json&onfail=cache", loc) |  | ||||||
| 
 |  | ||||||
| 	msg = fmt.Sprintf("calculated url: `%s`", url) |  | ||||||
| 	b.SendMsgToChannel(msg, postid, channelid) |  | ||||||
| 
 |  | ||||||
| 	client := http.Client{ |  | ||||||
| 		Timeout: 5 * time.Second, |  | ||||||
| 	} |  | ||||||
| 	req, err := http.NewRequest("GET", url, nil) |  | ||||||
| 	req.Header.Add("Authorization", `Token `+token) |  | ||||||
| 	resp, err := client.Do(req) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		b.SendMsgToChannel(fmt.Sprintf("weather fetch error: %s", err), postid, channelid) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b.SendMsgToChannel(fmt.Sprintf("weather %s: %s", loc, resp), postid, channelid) |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (b *Bot) HandleMsgFromChannel(event *model.WebSocketEvent) { | func (b *Bot) HandleMsgFromChannel(event *model.WebSocketEvent) { | ||||||
| 
 | 
 | ||||||
| 	post := model.PostFromJson(strings.NewReader(event.Data["post"].(string))) | 	post := model.PostFromJson(strings.NewReader(event.Data["post"].(string))) | ||||||
| @ -269,7 +247,6 @@ func (b *Bot) HandleMsgFromChannel(event *model.WebSocketEvent) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//pretty.Print(post)
 |  | ||||||
| 	if matched, _ := regexp.MatchString(`(?:^|\W)metar(?:$|\W)`, post.Message); matched { | 	if matched, _ := regexp.MatchString(`(?:^|\W)metar(?:$|\W)`, post.Message); matched { | ||||||
| 		b.HandleWeatherRequest(post.ChannelId, post.Id, post.Message) | 		b.HandleWeatherRequest(post.ChannelId, post.Id, post.Message) | ||||||
| 		return | 		return | ||||||
| @ -304,37 +281,41 @@ func (b *Bot) HandleMsgFromDebuggingChannel(event *model.WebSocketEvent) { | |||||||
| 	println("responding to debugging channel msg") | 	println("responding to debugging channel msg") | ||||||
| 
 | 
 | ||||||
| 	post := model.PostFromJson(strings.NewReader(event.Data["post"].(string))) | 	post := model.PostFromJson(strings.NewReader(event.Data["post"].(string))) | ||||||
| 	if post != nil { | 	if post == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 		// ignore my events
 | 	// FIXME check and see if the message from mm is a bot message, if so,
 | ||||||
| 		if matched, _ := regexp.MatchString(`(?:^|\W)shutdown(?:$|\W)`, post.Message); matched { | 	// ignore it
 | ||||||
| 			b.Shutdown() | 	pretty.Print(post) | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		// if you see any word matching 'alive' then respond
 | 	if matched, _ := regexp.MatchString(`(?:^|\W)shutdown(?:$|\W)`, post.Message); matched { | ||||||
| 		if matched, _ := regexp.MatchString(`(?:^|\W)alive(?:$|\W)`, post.Message); matched { | 		b.Shutdown() | ||||||
| 			b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | 		return | ||||||
| 			return | 	} | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		// if you see any word matching 'up' then respond
 | 	// if you see any word matching 'alive' then respond
 | ||||||
| 		if matched, _ := regexp.MatchString(`(?:^|\W)up(?:$|\W)`, post.Message); matched { | 	if matched, _ := regexp.MatchString(`(?:^|\W)alive(?:$|\W)`, post.Message); matched { | ||||||
| 			b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | 		b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | ||||||
| 			return | 		return | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		// if you see any word matching 'running' then respond
 | 	// if you see any word matching 'up' then respond
 | ||||||
| 		if matched, _ := regexp.MatchString(`(?:^|\W)running(?:$|\W)`, post.Message); matched { | 	if matched, _ := regexp.MatchString(`(?:^|\W)up(?:$|\W)`, post.Message); matched { | ||||||
| 			b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | 		b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | ||||||
| 			return | 		return | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		// if you see any word matching 'hello' then respond
 | 	// if you see any word matching 'running' then respond
 | ||||||
| 		if matched, _ := regexp.MatchString(`(?:^|\W)hello(?:$|\W)`, post.Message); matched { | 	if matched, _ := regexp.MatchString(`(?:^|\W)running(?:$|\W)`, post.Message); matched { | ||||||
| 			b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | 		b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | ||||||
| 			return | 		return | ||||||
| 		} | 	} | ||||||
|  | 
 | ||||||
|  | 	// if you see any word matching 'hello' then respond
 | ||||||
|  | 	if matched, _ := regexp.MatchString(`(?:^|\W)hello(?:$|\W)`, post.Message); matched { | ||||||
|  | 		b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	b.SendMsgToChannel("I did not understand your command, sorry", post.Id, post.ChannelId) | 	b.SendMsgToChannel("I did not understand your command, sorry", post.Id, post.ChannelId) | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								bot/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								bot/logger.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | package bot | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/mattn/go-isatty" | ||||||
|  | 	"github.com/rs/zerolog" | ||||||
|  | 	"github.com/rs/zerolog/log" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (b *Bot) setupLogging() { | ||||||
|  | 
 | ||||||
|  | 	log.Logger = log.With().Caller().Logger() | ||||||
|  | 
 | ||||||
|  | 	tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) | ||||||
|  | 
 | ||||||
|  | 	if tty { | ||||||
|  | 		out := zerolog.NewConsoleWriter( | ||||||
|  | 			func(w *zerolog.ConsoleWriter) { | ||||||
|  | 				// Customize time format
 | ||||||
|  | 				w.TimeFormat = time.RFC3339 | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 		log.Logger = log.Output(out) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// always log in UTC
 | ||||||
|  | 	zerolog.TimestampFunc = func() time.Time { | ||||||
|  | 		return time.Now().UTC() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	zerolog.SetGlobalLevel(zerolog.DebugLevel) | ||||||
|  | 	//zerolog.SetGlobalLevel(zerolog.InfoLevel)
 | ||||||
|  | 	//if viper.GetBool("debug") {
 | ||||||
|  | 	//}
 | ||||||
|  | 
 | ||||||
|  | 	b.identify() | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								bot/metar.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								bot/metar.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | |||||||
|  | package bot | ||||||
|  | 
 | ||||||
|  | //import "github.com/kr/pretty"
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/rs/zerolog/log" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"regexp" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (b *Bot) HandleWeatherRequest(channelid string, postid string, message string) { | ||||||
|  | 
 | ||||||
|  | 	// we are using a very bare image with no CA cert bundle
 | ||||||
|  | 	// actually if you docker bind mount the ca cert bundle in the right
 | ||||||
|  | 	// place, golang will find it and use it.
 | ||||||
|  | 	//http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
 | ||||||
|  | 
 | ||||||
|  | 	log.Info().Msgf("weather request received: `%s`", message) | ||||||
|  | 
 | ||||||
|  | 	r := regexp.MustCompile(`metar\s+([A-Za-z]{4})`) | ||||||
|  | 	matches := r.FindStringSubmatch(message) | ||||||
|  | 	if len(matches) < 2 { | ||||||
|  | 		b.SendMsgToChannel("error, sorry", postid, channelid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	loc := matches[1] | ||||||
|  | 
 | ||||||
|  | 	token := os.Getenv("METAR_API_TOKEN") | ||||||
|  | 	url := fmt.Sprintf("https://avwx.rest/api/metar/%s?options=&airport=true&reporting=true&format=json&onfail=cache", loc) | ||||||
|  | 
 | ||||||
|  | 	log.Info().Msgf("calculated url: `%s`", url) | ||||||
|  | 
 | ||||||
|  | 	client := http.Client{ | ||||||
|  | 		Timeout: 5 * time.Second, | ||||||
|  | 	} | ||||||
|  | 	req, err := http.NewRequest("GET", url, nil) | ||||||
|  | 	req.Header.Add("Authorization", `Token `+token) | ||||||
|  | 	resp, err := client.Do(req) | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.SendMsgToChannel(fmt.Sprintf("weather fetch error: %s", err), postid, channelid) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if resp.StatusCode != http.StatusOK { | ||||||
|  | 		b.SendMsgToChannel(fmt.Sprintf("weather fetch error: http status %d", resp.StatusCode), postid, channelid) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, _ := ioutil.ReadAll(resp.Body) | ||||||
|  | 
 | ||||||
|  | 	log.Info().Msgf("weather %s: %s", loc, data) | ||||||
|  | 	b.SendMsgToChannel(fmt.Sprintf("weather %s: %s", loc, data), postid, channelid) | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,4 +5,6 @@ go 1.15 | |||||||
| require ( | require ( | ||||||
| 	github.com/kr/pretty v0.1.0 | 	github.com/kr/pretty v0.1.0 | ||||||
| 	github.com/mattermost/mattermost-server/v5 v5.26.2 | 	github.com/mattermost/mattermost-server/v5 v5.26.2 | ||||||
|  | 	github.com/mattn/go-isatty v0.0.12 | ||||||
|  | 	github.com/rs/zerolog v1.19.0 | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
								
							| @ -332,6 +332,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd | |||||||
| github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= | ||||||
| github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= | ||||||
| github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | ||||||
|  | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||||||
| github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||||||
| github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||||||
| github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||||||
| @ -443,6 +444,9 @@ github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qq | |||||||
| github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||||
| github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= | ||||||
|  | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | ||||||
|  | github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg= | ||||||
|  | github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= | ||||||
| github.com/rudderlabs/analytics-go v3.2.1+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30= | github.com/rudderlabs/analytics-go v3.2.1+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30= | ||||||
| github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM= | github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM= | ||||||
| github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||||||
| @ -674,6 +678,7 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= | ||||||
| golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| @ -698,6 +703,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 | |||||||
| golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||||
| golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||||
| golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||||
|  | golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user