123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- // This file is subject to a 1-clause BSD license.
- // Its contents can be found in the enclosed LICENSE file.
- // Package owm provides commands to do current weather lookups.
- package owm
- import (
- "fmt"
- "io/ioutil"
- "net/http"
- "path/filepath"
- "sync"
- "time"
- "notabug.org/mouz/bot/app/util"
- "notabug.org/mouz/bot/irc"
- "notabug.org/mouz/bot/irc/cmd"
- "notabug.org/mouz/bot/plugins"
- )
- func init() { plugins.Register(&plugin{}) }
- // CacheTimeout defines the time after which a cache entry is
- // considered stale and it must be re-fetched.
- const CacheTimeout = time.Minute * 5
- // LookupTimeout defines the timeout after which a service request
- // is considered failed.
- const LookupTimeout = time.Second * 5
- // MaxFetchFrequency defines the maximum number of GET requests per
- // minute to the weather server
- const MaxFetchFrequency = 60
- type plugin struct {
- m sync.Mutex
- cmd *cmd.Set
- currentWeatherCache map[string]*owmCurrent
- fetchTimeCache map[time.Time]bool
- config struct {
- OwmAPIKey string
- }
- }
- // Load initializes the module and loads any internal resources
- // which may be required.
- func (p *plugin) Load(prof irc.Profile) error {
- p.currentWeatherCache = make(map[string]*owmCurrent)
- p.fetchTimeCache = make(map[time.Time]bool)
- p.cmd = cmd.New(prof.CommandPrefix(), nil)
- p.cmd.Bind(TextCurrentWeatherName, false, p.cmdCurrentWeather).
- Add(TextLocation, true, cmd.RegAny)
- file := filepath.Join(prof.Root(), "weather.cfg")
- return util.ReadFile(file, &p.config, false)
- }
- // Unload cleans the module up and unloads any internal resources.
- func (p *plugin) Unload(prof irc.Profile) error {
- p.config.OwmAPIKey = ""
- return nil
- }
- // Dispatch sends the given, incoming IRC message to the plugin for
- // processing as it sees fit.
- func (p *plugin) Dispatch(w irc.ResponseWriter, r *irc.Request) {
- if len(p.config.OwmAPIKey) > 0 {
- p.cmd.Dispatch(w, r)
- }
- }
- // fetch fetches the given URL. It returns the content of the
- // response, or an error if there were too many fetches in the last
- // minute.
- func (p *plugin) fetch(serviceURL, query string) ([]byte, error) {
- if p.fetchOverload() {
- return nil, fmt.Errorf("Number of API requests exceeds %d per minute", MaxFetchFrequency)
- }
- p.fetchTimeCache[time.Now()] = true
- url := fmt.Sprintf(serviceURL, TextUnits, TextLanguage, p.config.OwmAPIKey, query)
- resp, err := http.Get(url)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- // fetchOverload returns true iff too many fetches are done recently
- // (in the last minute).
- func (p *plugin) fetchOverload() bool {
- // remove old entries from time cache
- isold := make(map[time.Time]bool)
- for t := range p.fetchTimeCache {
- if t.Add(time.Minute).Before(time.Now()) {
- isold[t] = true
- }
- }
- for t := range isold {
- delete(p.fetchTimeCache, t)
- }
- // check remaining length of time cache
- if len(p.fetchTimeCache) >= MaxFetchFrequency {
- return true
- }
- return false
- }
|