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}