123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- // This file is subject to a 1-clause BSD license.
- // Its contents can be found in the enclosed LICENSE file.
- // Package knmi provides commands to do current weather lookups.
- package knmi
- import (
- "encoding/xml"
- "log"
- "os/exec"
- "path/filepath"
- "sync"
- "notabug.org/mouz/bot/app/util"
- "notabug.org/mouz/bot/irc"
- "notabug.org/mouz/bot/irc/cmd"
- "notabug.org/mouz/bot/irc/proto"
- "notabug.org/mouz/bot/plugins"
- )
- func init() { plugins.Register(&plugin{}) }
- type plugin struct {
- m sync.Mutex
- cmd *cmd.Set
- config struct {
- ForecastURL string
- }
- }
- // Load initializes the module and loads any internal resources
- // which may be required.
- func (p *plugin) Load(prof irc.Profile) error {
- p.cmd = cmd.New(prof.CommandPrefix(), nil)
- p.cmd.Bind(TextForecastName, false, p.cmdForecast)
- 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.ForecastURL = ""
- 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.ForecastURL) == 0 {
- log.Println("[knmi] ForecastURL not in weather.cfg")
- _ = proto.PrivMsg(w, r.Target, TextNoForecast)
- return
- }
- p.cmd.Dispatch(w, r)
- }
- // cmdForecast yields the weather forecast.
- func (p *plugin) cmdForecast(w irc.ResponseWriter, r *irc.Request, params cmd.ParamList) {
- p.m.Lock()
- defer p.m.Unlock()
- data, err := p.fetch(p.config.ForecastURL)
- if err != nil {
- log.Println("[knmi]", err)
- return
- }
- err = p.sendResult(w, r, data)
- if err != nil {
- log.Println("[knmi]", err)
- return
- }
- }
- // fetch fetches the given URL using curl.
- // It returns curl's standard output or an error.
- func (p *plugin) fetch(URL string) ([]byte, error) {
- // using dirty hack so ftp URL does not need its own full module
- cmd := exec.Command("curl", URL)
- stdout, err := cmd.Output()
- if err != nil {
- return nil, err
- }
- return stdout, nil
- }
- // sendResult formats the result end sends it back to the user.
- func (p *plugin) sendResult(w irc.ResponseWriter, r *irc.Request, data []byte) error {
- forecast := knmiForecast{}
- err := xml.Unmarshal(data, &forecast)
- if err != nil {
- return err
- }
- report := forecast.Data.Location.Block[1].FieldContent
- return proto.PrivMsg(w, r.Target, report)
- }
- // knmiForecast represents the response when the request for the forecast was
- // succesful.
- //
- // The struct is generated using https://github.com/miku/zek via
- // https://www.onlinetool.io/xmltogo/ using as input a copy of
- // ftp://ftp.knmi.nl/pub_weerberichten/basisverwachting.xml
- type knmiForecast struct {
- XMLName xml.Name `xml:"report"`
- Text string `xml:",chardata"`
- Metadata struct {
- Text string `xml:",chardata"`
- ReportInfo struct {
- Text string `xml:",chardata"`
- ReportID string `xml:"report_id"`
- ReportLanguage string `xml:"report_language"`
- ReportTimeCoordinate string `xml:"report_time_coordinate"`
- ReportDtgIssued string `xml:"report_dtg_issued"`
- ReportStartValidTime string `xml:"report_start_valid_time"`
- ReportEndValidTime string `xml:"report_end_valid_time"`
- ReportIssuedBy string `xml:"report_issued_by"`
- ReportForecasterID string `xml:"report_forecaster_id"`
- ReportAdditionalInformation string `xml:"report_additional_information"`
- ReportDisclaimer string `xml:"report_disclaimer"`
- ReportCredit string `xml:"report_credit"`
- ReportCreditLogo string `xml:"report_credit_logo"`
- } `xml:"report_info"`
- ReportLocations struct {
- Text string `xml:",chardata"`
- ReportLocation struct {
- Text string `xml:",chardata"`
- LocationID string `xml:"location_id"`
- LocationDescr string `xml:"location_descr"`
- } `xml:"report_location"`
- } `xml:"report_locations"`
- ReportValidPeriods struct {
- Text string `xml:",chardata"`
- ReportValidPeriod []struct {
- Text string `xml:",chardata"`
- ValidID string `xml:"valid_id"`
- ValidStart string `xml:"valid_start"`
- ValidEnd string `xml:"valid_end"`
- ValidDescr string `xml:"valid_descr"`
- } `xml:"report_valid_period"`
- } `xml:"report_valid_periods"`
- ReportFields struct {
- Text string `xml:",chardata"`
- ReportField []struct {
- Text string `xml:",chardata"`
- FieldID string `xml:"field_id"`
- FieldDescr string `xml:"field_descr"`
- } `xml:"report_field"`
- } `xml:"report_fields"`
- } `xml:"metadata"`
- Data struct {
- Text string `xml:",chardata"`
- Location struct {
- Text string `xml:",chardata"`
- LocationID string `xml:"location_id"`
- Block []struct {
- Text string `xml:",chardata"`
- FieldID string `xml:"field_id"`
- ValidID string `xml:"valid_id"`
- FieldContent string `xml:"field_content"`
- } `xml:"block"`
- } `xml:"location"`
- } `xml:"data"`
- }
|