Better standard compliance
authorJan Dittberner <jandd@cacert.org>
Thu, 5 Apr 2018 17:56:01 +0000 (19:56 +0200)
committerJan Dittberner <jandd@cacert.org>
Thu, 5 Apr 2018 17:56:01 +0000 (19:56 +0200)
- fix topic output
- use a CRLFPrintWriter
- support multiline messages
- wait a few seconds before joining channels

build.gradle
src/main/java/org/cacert/votebot/shared/CRLFPrintWriter.java [new file with mode: 0644]
src/main/java/org/cacert/votebot/shared/IRCClient.java
src/main/java/org/cacert/votebot/vote/CAcertVoteBot.java
src/main/resources/messages.properties
src/test/java/org/cacert/votebot/shared/IRCClientTest.java

index 3967197..54c086d 100644 (file)
@@ -47,7 +47,7 @@ dependencies {
 }
 
 group = 'org.cacert'
-version = '0.1.0-SNAPSHOT'
+version = '0.2.0-SNAPSHOT'
 
 bootJar {
     mainClassName = 'org.cacert.votebot.vote.CAcertVoteBot'
diff --git a/src/main/java/org/cacert/votebot/shared/CRLFPrintWriter.java b/src/main/java/org/cacert/votebot/shared/CRLFPrintWriter.java
new file mode 100644 (file)
index 0000000..47392fd
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This file is part of CAcert VoteBot.
+ *
+ * CAcert VoteBot is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * CAcert VoteBot is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * CAcert VoteBot.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.cacert.votebot.shared;
+
+import java.io.*;
+
+/**
+ * Based on http://www.java2s.com/Tutorial/Java/0180__File/APrintWriterthatendslineswithacarriagereturnlinefeedCRLF.htm
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class CRLFPrintWriter extends PrintWriter {
+    private boolean autoFlush;
+
+    /**
+     * @param outputStream wrapped output stream
+     * @param autoFlush    whether to autoFlush output immediately
+     */
+    CRLFPrintWriter(OutputStream outputStream, boolean autoFlush) {
+        super(outputStream, autoFlush);
+        this.autoFlush = autoFlush;
+    }
+
+    private void ensureOpen() throws IOException {
+        if (out == null) throw new IOException("Stream closed");
+    }
+
+    @Override
+    public void println() {
+        try {
+            //noinspection SynchronizeOnNonFinalField
+            synchronized (lock) {
+                ensureOpen();
+
+                out.write("\r\n");
+
+                if (autoFlush) {
+                    out.flush();
+                }
+            }
+        } catch (InterruptedIOException e) {
+            Thread.currentThread().interrupt();
+        } catch (IOException ioe) {
+            setError();
+        }
+    }
+}
index 3f17366..3aaa480 100644 (file)
@@ -131,7 +131,7 @@ public class IRCClient {
             socket = new Socket(server, port); // default-plain = 6667
         }
 
-        out = new PrintWriter(socket.getOutputStream(), true);
+        out = new CRLFPrintWriter(socket.getOutputStream(), true);
         final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 
         new ServerReader(in);
@@ -227,6 +227,9 @@ public class IRCClient {
         checkChannelPreconditions(channel);
 
         for (String line : msg.split("\n")) {
+            if (line.length() == 0) {
+                line = " ";
+            }
             out.println(String.format("PRIVMSG #%s :%s", channel, line));
         }
     }
@@ -242,6 +245,9 @@ public class IRCClient {
         checkPrivateMessagePreconditions(to);
 
         for (String line : msg.split("\n")) {
+            if (line.length() == 0) {
+                line = " ";
+            }
             out.println(String.format("PRIVMSG %s :%s", to, line));
         }
     }
index a3d9bd5..f5887ba 100644 (file)
@@ -99,12 +99,14 @@ public class CAcertVoteBot extends IRCBot implements Runnable, CommandLineRunner
         try {
             getIrcClient().initializeFromArgs(args).assignBot(this);
 
+            Thread.sleep(Duration.ofSeconds(3).toMillis());
             getIrcClient().join(meetingChannel);
+            Thread.sleep(Duration.ofSeconds(1).toMillis());
             getIrcClient().join(voteChannel);
 
             new Thread(this).start();
         } catch (IOException | InterruptedException | ParseException | IRCClientException e) {
-            LOGGER.error(String.format("error running votebot %s", e.getMessage()));
+            LOGGER.error(MessageFormat.format(messages.getString("error_running_votebot"), e.getMessage()));
         }
     }
 
@@ -142,7 +144,7 @@ public class CAcertVoteBot extends IRCBot implements Runnable, CommandLineRunner
     }
 
     private void sendUnknownCommand(String from, String command) throws IRCClientException {
-        sendPrivateMessage(from, String.format(messages.getString("unknown_command"), command));
+        sendPrivateMessage(from, MessageFormat.format(messages.getString("unknown_command"), command));
     }
 
     private void giveHelp(String from) throws IRCClientException {
@@ -180,11 +182,15 @@ public class CAcertVoteBot extends IRCBot implements Runnable, CommandLineRunner
                 }
 
                 Thread.sleep(warn * MILLIS_ONE_SECOND);
-                announce("Voting on " + voteMechanics.getTopic() + " will end in " + (timeout - warn) + " seconds.");
+                String topic = voteMechanics.getTopic();
+                announce(MessageFormat.format(
+                        messages.getString("voting_will_end_in_n_seconds"),
+                        topic, timeout - warn));
                 Thread.sleep((timeout - warn) * MILLIS_ONE_SECOND);
-                announce("Voting on " + voteMechanics.getTopic() + " has closed.");
+                announce(MessageFormat.format(
+                        messages.getString("voting_has_closed"), topic));
                 final String[] res = voteMechanics.closeVote();
-                announce("Results: for " + voteMechanics.getTopic() + ":");
+                announce(MessageFormat.format(messages.getString("results_for_vote"), topic));
 
                 for (final String re : res) {
                     announce(re);
index 8921d4c..17e7663 100644 (file)
@@ -30,10 +30,14 @@ new_vote=New Vote: {0} has started a vote on "{1}"
 cast_vote_in_vote_channel=Please cast your vote in #{0}
 cast_vote_in_next_seconds=Please cast your vote in the next {0} seconds.
 
-help_message=Help for VoteBot\
-  \
-  VoteBot understands the following commands:\
-  \
-  HELP         - this help\
+help_message=Help for VoteBot\n\
+  \n\
+  VoteBot understands the following commands:\n\
+  \n\
+  HELP         - this help\n\
   VOTE <topic> - start a vote on <topic> if no other vote is running
-unknown_command=I do not understand what you mean with %s
+unknown_command=I do not understand what you mean with {0}
+error_running_votebot=error running votebot {0}
+voting_will_end_in_n_seconds=Voting on {0} will end in {1} seconds.
+voting_has_closed=Voting on {0} has closed.
+results_for_vote=Results: for {0}:
index 1a55068..53a7ce2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018  Jan Dittberner
+ * Copyright (c) 2018  Jan Dittberner
  *
  * This file is part of CAcert VoteBot.
  *
@@ -252,7 +252,7 @@ public class IRCClientTest {
     }
 
     @Test
-    public void testFailPrivateMessageToSelf() throws Exception {
+    public void testFailPrivateMessageToSelf() {
         try {
             client.sendPrivate("Test message", "test/nick");
             fail("Expected IRC client exception for private message to invalid nick not thrown.");