Implement a cancel command for VoteBot
[cacert-votebot.git] / src / main / java / org / cacert / votebot / audit / CAcertVoteAuditor.java
1 /*
2 * Copyright (c) 2015 Felix Doerre
3 * Copyright (c) 2015 Benny Baumann
4 * Copyright (c) 2016, 2018 Jan Dittberner
5 *
6 * This file is part of CAcert VoteBot.
7 *
8 * CAcert VoteBot is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation, either version 3 of the License, or (at your option)
11 * any later version.
12 *
13 * CAcert VoteBot is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * CAcert VoteBot. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 package org.cacert.votebot.audit;
23
24 import org.apache.commons.cli.ParseException;
25 import org.cacert.votebot.shared.CAcertVoteMechanics;
26 import org.cacert.votebot.shared.IRCBot;
27 import org.cacert.votebot.shared.IRCClient;
28 import org.cacert.votebot.shared.VoteType;
29 import org.cacert.votebot.shared.exceptions.IRCClientException;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.springframework.beans.factory.annotation.Autowired;
33 import org.springframework.beans.factory.annotation.Value;
34 import org.springframework.boot.CommandLineRunner;
35 import org.springframework.boot.SpringApplication;
36 import org.springframework.boot.autoconfigure.SpringBootApplication;
37 import org.springframework.stereotype.Component;
38
39 import java.io.IOException;
40 import java.util.Arrays;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43
44 /**
45 * Auditor bot for votes.
46 *
47 * @author Felix Doerre
48 * @author Jan Dittberner
49 */
50 @SpringBootApplication(scanBasePackageClasses = {CAcertVoteAuditor.class, IRCClient.class})
51 @Component
52 public class CAcertVoteAuditor extends IRCBot implements CommandLineRunner {
53 private static final Logger LOGGER = LoggerFactory.getLogger(
54 CAcertVoteAuditor.class);
55 private static final String NEW_VOTE_REGEX =
56 "New Vote: (.*) has started a vote on \"(.*)\"";
57
58 @Value("${auditor.target.nick}")
59 private String toAudit;
60
61 @Value("${auditor.target.voteChn}")
62 private String voteAuxChn;
63
64 private final IRCClient ircClient;
65
66 private final CAcertVoteMechanics voteMechanics;
67
68 private final String[] capturedResults = new String[VoteType.values().length];
69
70 private int counter = -1;
71
72 @Autowired
73 public CAcertVoteAuditor(IRCClient ircClient) {
74 this.ircClient = ircClient;
75 this.voteMechanics = new CAcertVoteMechanics();
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 @Override
82 protected final IRCClient getIrcClient() {
83 return ircClient;
84 }
85
86 /**
87 * {@inheritDoc}
88 */
89 @Override
90 public final synchronized void publicMessage(final String from, final String channel, final String message) {
91 if (channel.equals(voteAuxChn)) {
92 if (from.equals(toAudit)) {
93 if (counter >= 0) {
94 capturedResults[counter++] = message;
95
96 if (counter == capturedResults.length) {
97 final String[] reals = voteMechanics.closeVote();
98
99 if (Arrays.equals(reals, capturedResults)) {
100 LOGGER.info("Audit for vote was successful.");
101 } else {
102 LOGGER.warn("Audit failed! Vote Bot (or Auditor) is probably broken.");
103 }
104
105 counter = -1;
106 }
107
108 return;
109 }
110 if (message.startsWith("New Vote: ")) {
111 LOGGER.info("detected vote-start");
112
113 final Pattern pattern = Pattern.compile(NEW_VOTE_REGEX);
114 final Matcher matcher = pattern.matcher(message);
115
116 if (!matcher.matches()) {
117 LOGGER.warn("error: vote-start malformed");
118 return;
119 }
120
121 voteMechanics.callVote(matcher.group(2), 0, 0);
122 } else if (message.startsWith("Results: ")) {
123 LOGGER.info("detected vote-end. Reading results");
124
125 counter = 0;
126 }
127 } else {
128 if (counter != -1) {
129 LOGGER.info("Vote after end.");
130 return;
131 }
132
133 LOGGER.info("detected vote");
134 voteMechanics.evaluateVote(from, message);
135 final String currentResult = voteMechanics.getCurrentResult();
136 LOGGER.info("Current state: {}", currentResult);
137 }
138 }
139 }
140
141 /**
142 * Do nothing for private messages.
143 *
144 * @param from source nick for the message
145 * @param message message text
146 */
147 @Override
148 public synchronized void privateMessage(final String from, final String message) {
149 }
150
151 /**
152 * Do nothing on join messages.
153 *
154 * @param referent joining nick
155 * @param channel channel name
156 */
157 @Override
158 public synchronized void join(final String referent, final String channel) {
159 }
160
161 /**
162 * Do nothing on part messages.
163 *
164 * @param referent leaving nick
165 * @param channel channel name
166 */
167 @Override
168 public synchronized void part(final String referent, final String channel) {
169 }
170
171 /**
172 * Run the audit bot.
173 * {@inheritDoc}
174 *
175 * @param args command line arguments
176 */
177 @Override
178 public final void run(final String... args) {
179 try {
180 getIrcClient().initializeFromArgs(args).assignBot(this);
181
182 getIrcClient().join(voteAuxChn);
183 } catch (IOException | InterruptedException | ParseException | IRCClientException e) {
184 LOGGER.error("error running votebot {}", e.getMessage());
185 }
186 }
187 /**
188 * Entry point for the audit bot.
189 *
190 * @param args command line arguments
191 */
192 public static void main(final String... args) {
193 SpringApplication.run(CAcertVoteAuditor.class, args);
194 }
195 }