Refactor Votebot to use spring-boot
[cacert-votebot.git] / src / main / java / org / cacert / votebot / vote / CAcertVoteBot.java
1 /*
2 * Copyright (c) 2016. Jan Dittberner
3 *
4 * This file is part of CAcert votebot.
5 *
6 * CAcert votebot is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
10 *
11 * CAcert votebot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * CAcert votebot. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.cacert.votebot.vote;
20
21 import org.apache.commons.cli.ParseException;
22 import org.cacert.votebot.shared.CAcertVoteMechanics;
23 import org.cacert.votebot.shared.CAcertVoteMechanics.State;
24 import org.cacert.votebot.shared.IRCBot;
25 import org.cacert.votebot.shared.IRCClient;
26 import org.cacert.votebot.shared.exceptions.IRCClientException;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.beans.factory.annotation.Value;
31 import org.springframework.boot.CommandLineRunner;
32 import org.springframework.boot.SpringApplication;
33 import org.springframework.boot.autoconfigure.SpringBootApplication;
34 import org.springframework.stereotype.Component;
35
36 import java.io.IOException;
37
38
39 /**
40 * Vote bot.
41 *
42 * @author Felix Doerre
43 * @author Jan Dittberner
44 */
45 @SpringBootApplication(scanBasePackageClasses = {IRCClient.class, CAcertVoteBot.class})
46 @Component
47 public class CAcertVoteBot extends IRCBot implements Runnable, CommandLineRunner {
48 private static final Logger LOGGER = LoggerFactory.getLogger(CAcertVoteBot.class);
49 private static final int MILLIS_ONE_SECOND = 1000;
50
51 /**
52 * Meeting channel where votes and results are published.
53 */
54 @Value("${voteBot.meetingChn}")
55 private String meetingChannel;
56
57 /**
58 * Channel name where voting is performed.
59 */
60 @Value("${voteBot.voteChn}")
61 private String voteChannel;
62
63 /**
64 * Seconds to warn before a vote ends.
65 */
66 @Value("${voteBot.warnSecs}")
67 private long warn;
68
69 /**
70 * Seconds before a vote times out.
71 */
72 @Value("${voteBot.timeoutSecs}")
73 private long timeout;
74
75 @Autowired
76 private CAcertVoteMechanics voteMechanics;
77
78 @Autowired
79 private IRCClient ircClient;
80
81 /**
82 * {@inheritDoc}
83 *
84 * @param args command line arguments
85 */
86 @Override
87 public final void run(final String... args) {
88 try {
89 getIrcClient().initializeFromArgs(args).assignBot(this);
90
91 getIrcClient().join(meetingChannel);
92 getIrcClient().join(voteChannel);
93
94 new Thread(this).start();
95 } catch (IOException | InterruptedException | ParseException | IRCClientException e) {
96 LOGGER.error(String.format("error running votebot %s", e.getMessage()));
97 }
98 }
99
100 @Override
101 protected final IRCClient getIrcClient() {
102 return ircClient;
103 }
104
105 @Override
106 public final synchronized void publicMessage(final String from, final String channel, final String message) throws
107 IRCClientException {
108 if (channel.equals(voteChannel)) {
109 sendPublicMessage(voteChannel, voteMechanics.evaluateVote(from, message));
110 }
111 }
112
113 @Override
114 public final synchronized void privateMessage(final String from, final String message) throws IRCClientException {
115 if (message.startsWith("vote ")) {
116 final String response = voteMechanics.callVote(message.substring(5));
117 sendPrivateMessage(from, response);
118
119 if (response.startsWith("Sorry,")) {
120 return;
121 }
122
123 announce("New Vote: " + from + " has started a vote on \"" + voteMechanics.getTopic() + "\"");
124 sendPublicMessage(meetingChannel, "Please cast your vote in #vote");
125 sendPublicMessage(voteChannel, "Please cast your vote in the next " + timeout + " seconds.");
126 }
127 }
128
129 private synchronized void announce(final String msg) throws IRCClientException {
130 sendPublicMessage(meetingChannel, msg);
131 sendPublicMessage(voteChannel, msg);
132 }
133
134 @Override
135 public final void run() {
136 try {
137 //noinspection InfiniteLoopStatement
138 while (true) {
139 while (voteMechanics.getState() == State.IDLE) {
140 Thread.sleep(MILLIS_ONE_SECOND);
141 }
142
143 Thread.sleep(warn * MILLIS_ONE_SECOND);
144 announce("Voting on " + voteMechanics.getTopic() + " will end in " + (timeout - warn) + " seconds.");
145 Thread.sleep((timeout - warn) * MILLIS_ONE_SECOND);
146 announce("Voting on " + voteMechanics.getTopic() + " has closed.");
147 final String[] res = voteMechanics.closeVote();
148 announce("Results: for " + voteMechanics.getTopic() + ":");
149
150 for (final String re : res) {
151 announce(re);
152 }
153 }
154 } catch (final InterruptedException | IRCClientException e) {
155 LOGGER.error(e.getMessage(), e);
156 }
157 }
158
159 @Override
160 public synchronized void join(final String referent, final String chn) {
161
162 }
163
164 @Override
165 public synchronized void part(final String referent, final String channel) {
166
167 }
168
169 /**
170 * Entry point for the vote bot.
171 *
172 * @param args command line arguments
173 */
174 public static void main(final String... args) {
175 SpringApplication.run(CAcertVoteBot.class, args);
176 }
177 }