diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java index 1b87363..282210e 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java @@ -39,7 +39,13 @@ import net.aksingh.owmjapis.model.param.Weather; import net.aksingh.owmjapis.model.param.Wind; import net.thauvin.erik.mobibot.Mobibot; import net.thauvin.erik.mobibot.Utils; +import net.thauvin.erik.mobibot.msg.ErrorMessage; +import net.thauvin.erik.mobibot.msg.Message; +import net.thauvin.erik.mobibot.msg.NoticeMessage; +import net.thauvin.erik.mobibot.msg.PublicMessage; +import org.jibble.pircbot.Colors; +import java.util.ArrayList; import java.util.List; /** @@ -49,9 +55,12 @@ import java.util.List; * @created 2017-04-02 * @since 1.0 */ -public class Weather2 extends AbstractModule { - // The OpenWeatherMap API Key property. - private static final String OWM_API_KEY_PROP = "owm-api-key"; +public class Weather2 extends ThreadedModule { + /** + * The OpenWeatherMap API Key property. + */ + public static final String OWM_API_KEY_PROP = "owm-api-key"; + // The weather command. private static final String WEATHER_CMD = "weather"; @@ -63,51 +72,33 @@ public class Weather2 extends AbstractModule { properties.put(OWM_API_KEY_PROP, ""); } - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - new Thread(() -> run(bot, sender, args.toUpperCase(), isPrivate)).start(); - } - - private String fAndC(final Double d) { + private static String fAndC(final Double d) { final double c = (d - 32) * 5 / 9; return Math.round(d) + " \u00B0F, " + Math.round(c) + " \u00B0C"; } - private OWM.Country getCountry(String countryCode) { - for (OWM.Country c : OWM.Country.values()) { + private static OWM.Country getCountry(final String countryCode) { + for (final OWM.Country c : OWM.Country.values()) { if (c.name().equalsIgnoreCase(countryCode)) { return c; } - } return OWM.Country.UNITED_STATES; } - /** - * {@inheritDoc} - */ - @Override - public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - bot.send(sender, "To display weather information:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " [, ]")); - bot.send(sender, "For example:"); - bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); - bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") - + ". Zip codes are supported in most countries."); - } + static ArrayList getWeather(final String query, final String apiKey) throws ModuleException { + if (!Utils.isValidString(apiKey)) { + throw new ModuleException(Utils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing."); + } + + final OWM owm = new OWM(apiKey); + final ArrayList messages = new ArrayList<>(); - /** - * Fetches the weather data from a specific city. - */ - private void run(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { - final OWM owm = new OWM(properties.get(OWM_API_KEY_PROP)); owm.setUnit(OWM.Unit.IMPERIAL); - if (Utils.isValidString(args)) { - final String[] argv = args.split(","); + + if (Utils.isValidString(query)) { + final String[] argv = query.split(","); if (argv.length >= 1 && argv.length <= 2) { final String country; @@ -126,23 +117,23 @@ public class Weather2 extends AbstractModule { cwd = owm.currentWeatherByCityName(city, getCountry(country)); } if (cwd.hasCityName()) { - bot.send(sender, "City: " + cwd.getCityName() + " [" + country + "]", isPrivate); + messages.add(new NoticeMessage("City: " + cwd.getCityName() + " [" + country + "]")); final Main main = cwd.getMainData(); if (main != null) { if (main.hasTemp()) { - bot.send(sender, "Temperature: " + fAndC(main.getTemp()), isPrivate); + messages.add(new NoticeMessage("Temperature: " + fAndC(main.getTemp()))); } if (main.hasHumidity() && (main.getHumidity() != null)) { - bot.send(sender, "Humidity: " + Math.round(main.getHumidity()) + "%", isPrivate); + messages.add(new PublicMessage("Humidity: " + Math.round(main.getHumidity()) + "%")); } } if (cwd.hasWindData()) { final Wind w = cwd.getWindData(); if (w != null && w.hasSpeed()) { - bot.send(sender, "Wind: " + wind(w.getSpeed()), isPrivate); + messages.add(new PublicMessage("Wind: " + wind(w.getSpeed()))); } } @@ -150,37 +141,71 @@ public class Weather2 extends AbstractModule { final StringBuilder condition = new StringBuilder("Condition: "); final List list = cwd.getWeatherList(); if (list != null) { - for (Weather w : list) { + for (final Weather w : list) { if (condition.indexOf(",") == -1) { condition.append(Utils.capitalize(w.getDescription())); } else { condition.append(", ").append(w.getDescription()); } } - bot.send(sender, condition.toString(), isPrivate); + messages.add(new PublicMessage(condition.toString())); } } - - bot.send(sender, Utils.green("https://openweathermap.org/city/" + cwd.getCityId()), isPrivate); - - return; + messages.add(new NoticeMessage("https://openweathermap.org/city/" + + cwd.getCityId(), Colors.GREEN)); } } catch (APIException | NullPointerException e) { - if (bot.getLogger().isDebugEnabled()) { - bot.getLogger().debug("Unable to perform weather lookup: " + args, e); - } - - bot.send(bot.getChannel(), "Unable to perform weather lookup: " + e.getMessage()); + throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e); } } } - helpResponse(bot, sender, args, isPrivate); + if (messages.isEmpty()) { + messages.add(new ErrorMessage("Invalid syntax.")); + } + + return messages; } - private String wind(final Double w) { + private static String wind(final Double w) { final double kmh = w * 1.60934; return Math.round(w) + " mph, " + Math.round(kmh) + " km/h"; } + + /** + * {@inheritDoc} + */ + @Override + public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) { + bot.send(sender, "To display weather information:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " [, ]")); + bot.send(sender, "For example:"); + bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr")); + bot.send(sender, "The default ISO 3166 country code is " + Utils.bold("US") + + ". Zip codes are supported in most countries."); + } + + /** + * Fetches the weather data from a specific city. + */ + void run(final Mobibot bot, final String sender, final String args) { + if (Utils.isValidString(args)) { + try { + final ArrayList messages = getWeather(args, properties.get(OWM_API_KEY_PROP)); + if (messages.get(0).isError()) { + helpResponse(bot, sender, args, true); + } else { + for (final Message msg : messages) { + bot.send(msg.isNotice() ? bot.getChannel() : sender, msg.getMessage(), msg.getColor()); + } + } + } catch (ModuleException e) { + bot.getLogger().debug(e.getDebugMessage(), e); + bot.send(bot.getChannel(), e.getMessage()); + } + } else { + helpResponse(bot, sender, args, true); + } + } } diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java new file mode 100644 index 0000000..bcd4a66 --- /dev/null +++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java @@ -0,0 +1,76 @@ +/* + * Weather2Test.java + * + * Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.thauvin.erik.mobibot.modules; + +import net.thauvin.erik.mobibot.msg.Message; +import org.testng.annotations.Test; + +import java.util.ArrayList; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The Weather2Test class. + * + * @author Erik C. Thauvin + * @created 2019-04-09 + * @since 1.0 + */ +public class Weather2Test extends LocalProperties { + @Test + public void testWeather() throws ModuleException { + ArrayList messages = Weather2.getWeather("98204", + LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + assertThat(messages.get(0).getMessage()).as("is Everett").contains("Everett"); + messages = Weather2.getWeather("London, UK", LocalProperties.getProperty(Weather2.OWM_API_KEY_PROP)); + assertThat(messages.get(0).getMessage()).as("is UK").contains("UK"); + + try { + Weather2.getWeather("test", ""); + } catch (Exception e) { + assertThat(e).as("no API key").isInstanceOf(ModuleException.class); + assertThat(e).as("no API key exception has no cause").hasNoCause(); + } + + try { + Weather2.getWeather("", "apikey"); + } catch (Exception e) { + assertThat(e).as("no query").isInstanceOf(ModuleException.class); + assertThat(e).as("no query exception has no cause").hasNoCause(); + } + } + + @Test + public void testWeather2Impl() { + AbstractModuleTest.testAbstractModule(new Weather2()); + } +}