marvin

A simple and modular IRC bot

git clone https://git.8pit.net/marvin.git

  1// This program is free software: you can redistribute it and/or modify
  2// it under the terms of the GNU Affero General Public License as
  3// published by the Free Software Foundation, either version 3 of the
  4// License, or (at your option) any later version.
  5//
  6// This program is distributed in the hope that it will be useful, but
  7// WITHOUT ANY WARRANTY; without even the implied warranty of
  8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  9// Affero General Public License for more details.
 10//
 11// You should have received a copy of the GNU Affero General Public
 12// License along with this program. If not, see <http://www.gnu.org/licenses/>.
 13
 14package spacestatus
 15
 16import (
 17	"encoding/json"
 18	"errors"
 19	"github.com/nmeum/marvin/irc"
 20	"github.com/nmeum/marvin/modules"
 21	"io/ioutil"
 22	"net/http"
 23	"time"
 24)
 25
 26// Supported SpaceAPI version
 27const apiVersion = "0.13"
 28
 29type spaceapi struct {
 30	API   string `json:"api"`
 31	Space string `json:"space"`
 32	State struct {
 33		Open bool `json:"open"`
 34	} `json:"state"`
 35}
 36
 37type Module struct {
 38	api      *spaceapi
 39	URL      string `json:"url"`
 40	Notify   bool   `json:"notify"`
 41	Interval string `json:"interval"`
 42}
 43
 44func Init(moduleSet *modules.ModuleSet) {
 45	moduleSet.Register(new(Module))
 46}
 47
 48func (m *Module) Name() string {
 49	return "spacestatus"
 50}
 51
 52func (m *Module) Help() string {
 53	return "USAGE: !spacestatus"
 54}
 55
 56func (m *Module) Defaults() {
 57	m.Notify = true
 58	m.Interval = "0h15m"
 59}
 60
 61func (m *Module) Load(client *irc.Client) error {
 62	if len(m.URL) <= 0 {
 63		return nil
 64	}
 65
 66	duration, err := time.ParseDuration(m.Interval)
 67	if err != nil {
 68		return err
 69	}
 70
 71	if err := m.updateHandler(client); err != nil {
 72		return err
 73	}
 74
 75	if m.api.API != apiVersion {
 76		return errors.New("unsupported spaceapi version")
 77	}
 78
 79	go func(c *irc.Client) {
 80		for {
 81			time.Sleep(duration)
 82			m.updateHandler(c)
 83		}
 84	}(client)
 85
 86	client.CmdHook("privmsg", m.statusCmd)
 87	return nil
 88}
 89
 90func (m *Module) updateHandler(client *irc.Client) error {
 91	var oldState bool
 92	if m.api == nil {
 93		oldState = false
 94	} else {
 95		oldState = m.api.State.Open
 96	}
 97
 98	firstPoll := m.api == nil
 99	if err := m.pollStatus(); err != nil {
100		return err
101	}
102
103	newState := m.api.State.Open
104	if newState != oldState && m.Notify && !firstPoll {
105		m.notify(client, newState)
106	}
107
108	return nil
109}
110
111func (m *Module) pollStatus() error {
112	resp, err := http.Get(m.URL)
113	if err != nil {
114		return err
115	}
116	defer resp.Body.Close()
117
118	data, err := ioutil.ReadAll(resp.Body)
119	if err != nil {
120		return err
121	}
122
123	if err := json.Unmarshal(data, &m.api); err != nil {
124		return err
125	}
126
127	return nil
128}
129
130func (m *Module) statusCmd(client *irc.Client, msg irc.Message) error {
131	if msg.Data != "!spacestatus" {
132		return nil
133	} else if m.api == nil {
134		return client.Write("NOTICE %s :Status currently unknown.",
135			msg.Receiver)
136	}
137
138	var state string
139	if m.api.State.Open {
140		state = "open"
141	} else {
142		state = "closed"
143	}
144
145	return client.Write("NOTICE %s :%s is currently %s",
146		msg.Receiver, m.api.Space, state)
147}
148
149func (m *Module) notify(client *irc.Client, open bool) {
150	var oldState, newState string
151	if open {
152		oldState = "closed"
153		newState = "open"
154	} else {
155		oldState = "open"
156		newState = "closed"
157	}
158
159	for _, ch := range client.Channels {
160		client.Write("NOTICE %s :%s changed door status from %s to %s",
161			ch, m.api.Space, oldState, newState)
162	}
163}