| 1 | import java.io.IOException; |
|---|
| 2 | import java.net.URLEncoder; |
|---|
| 3 | import java.util.ArrayList; |
|---|
| 4 | import java.util.HashMap; |
|---|
| 5 | import java.util.List; |
|---|
| 6 | import java.util.Map; |
|---|
| 7 | import java.util.Arrays; |
|---|
| 8 | |
|---|
| 9 | import javax.xml.bind.JAXBContext; |
|---|
| 10 | import javax.xml.bind.JAXBException; |
|---|
| 11 | import javax.xml.bind.Unmarshaller; |
|---|
| 12 | import javax.xml.bind.annotation.XmlAccessType; |
|---|
| 13 | import javax.xml.bind.annotation.XmlAccessorType; |
|---|
| 14 | import javax.xml.bind.annotation.XmlAttribute; |
|---|
| 15 | import javax.xml.bind.annotation.XmlElementRef; |
|---|
| 16 | import javax.xml.bind.annotation.XmlElementRefs; |
|---|
| 17 | import javax.xml.bind.annotation.XmlRootElement; |
|---|
| 18 | import javax.xml.namespace.QName; |
|---|
| 19 | import javax.xml.transform.Source; |
|---|
| 20 | import javax.xml.ws.Dispatch; |
|---|
| 21 | import javax.xml.ws.Service; |
|---|
| 22 | import javax.xml.ws.handler.MessageContext; |
|---|
| 23 | import javax.xml.ws.http.HTTPBinding; |
|---|
| 24 | |
|---|
| 25 | import uk.co.uwcs.choob.modules.Modules; |
|---|
| 26 | import uk.co.uwcs.choob.support.ChoobNoSuchCallException; |
|---|
| 27 | import uk.co.uwcs.choob.support.IRCInterface; |
|---|
| 28 | import uk.co.uwcs.choob.support.events.Message; |
|---|
| 29 | |
|---|
| 30 | /** |
|---|
| 31 | * Plugin for Querying weather information from google. |
|---|
| 32 | * @author benji |
|---|
| 33 | */ |
|---|
| 34 | public class GoogleWeather |
|---|
| 35 | { |
|---|
| 36 | private static final String baseURL = "http://www.google.com/ig/api?weather="; |
|---|
| 37 | private static final Class[] DATA_TYPES = {GoogleMapsResponse.class,Weather.class,CurrentConditions.class,ForecastConditions.class,DataItem.class}; |
|---|
| 38 | |
|---|
| 39 | private GoogleWeather() |
|---|
| 40 | { |
|---|
| 41 | |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | public String[] info() |
|---|
| 45 | { |
|---|
| 46 | return new String[] { |
|---|
| 47 | "Plugin to get weather forecasts from http://www.google.com/ig", |
|---|
| 48 | "The Choob Team", |
|---|
| 49 | "choob@uwcs.co.uk", |
|---|
| 50 | "0.2" |
|---|
| 51 | }; |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | private static final HashMap<String,String> symbols = new HashMap<String,String>() |
|---|
| 55 | { |
|---|
| 56 | private static final long serialVersionUID = 997592107556024573L; |
|---|
| 57 | |
|---|
| 58 | { |
|---|
| 59 | put("Cloudy","â"); |
|---|
| 60 | put("Rain","â"); |
|---|
| 61 | put("Light Rain","â"); |
|---|
| 62 | }}; |
|---|
| 63 | |
|---|
| 64 | public <T> T getXmlFromHTTP(final String url, final Class... dataTypes) throws IOException, JAXBException |
|---|
| 65 | { |
|---|
| 66 | final QName qname = new QName("", ""); |
|---|
| 67 | final Service service = Service.create(qname); |
|---|
| 68 | service.addPort(qname, HTTPBinding.HTTP_BINDING, baseURL); |
|---|
| 69 | final Dispatch<Source> dispatcher = service.createDispatch(qname, Source.class, Service.Mode.PAYLOAD); |
|---|
| 70 | final Map<String, Object> requestContext = dispatcher.getRequestContext(); |
|---|
| 71 | requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "GET"); |
|---|
| 72 | requestContext.put(MessageContext.HTTP_REQUEST_HEADERS,new HashMap<String,List<String>>(){{put("User-Agent",Arrays.asList("Opera/8.51 (X11; Linux x86_64; U; en)"));}}); |
|---|
| 73 | requestContext.put(Dispatch.ENDPOINT_ADDRESS_PROPERTY, url); |
|---|
| 74 | final Source got = dispatcher.invoke(null); |
|---|
| 75 | final JAXBContext context = JAXBContext.newInstance(dataTypes); |
|---|
| 76 | final Unmarshaller u = context.createUnmarshaller(); |
|---|
| 77 | return (T)u.unmarshal(got); |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | private String getWeather(final String weatherLocation) throws IOException, JAXBException, LocationNotFoundException |
|---|
| 81 | { |
|---|
| 82 | final GoogleMapsResponse response = getXmlFromHTTP(baseURL + URLEncoder.encode(weatherLocation, "UTF-8"), DATA_TYPES); |
|---|
| 83 | if (response == null || response.getWeather() == null || response.getWeather().getCurrentConditions() == null) |
|---|
| 84 | throw new LocationNotFoundException(); |
|---|
| 85 | final String conditions = response.getWeather().getCurrentConditions().getCondition().getData(); |
|---|
| 86 | final String temp = response.getWeather().getCurrentConditions().getTemp_c().getData(); |
|---|
| 87 | final ForecastInformation inf = response.getWeather().getForecastInformation(); |
|---|
| 88 | final StringBuilder forecast = new StringBuilder(); |
|---|
| 89 | for (final ForecastConditions fCons : response.getWeather().getForeCastConditions()) |
|---|
| 90 | forecast |
|---|
| 91 | .append(fCons.getDay_of_week().getData()) |
|---|
| 92 | .append(" ") |
|---|
| 93 | .append(fCons.getCondition().getData()) |
|---|
| 94 | .append("; "); |
|---|
| 95 | |
|---|
| 96 | return "Weather in " + |
|---|
| 97 | (inf != null ? inf.getCity() : weatherLocation ) + |
|---|
| 98 | " is " + |
|---|
| 99 | (symbols.containsKey(conditions) ? symbols.get(conditions) + " " : "") + |
|---|
| 100 | conditions + |
|---|
| 101 | ". Current temperature is " + |
|---|
| 102 | temp + |
|---|
| 103 | "°C. " + |
|---|
| 104 | (forecast.length() > 0 ? "Forecast is - " + forecast.toString() : ""); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | private Modules mods; |
|---|
| 108 | private IRCInterface irc; |
|---|
| 109 | |
|---|
| 110 | public GoogleWeather(final Modules mods, final IRCInterface irc) |
|---|
| 111 | { |
|---|
| 112 | this.mods = mods; |
|---|
| 113 | this.irc = irc; |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | public String[] optionsUser = { "Location" }; |
|---|
| 117 | public String[] optionsUserDefaults = { "coventry" }; |
|---|
| 118 | |
|---|
| 119 | public String[] helpOptionGoogleWeatherLocation = |
|---|
| 120 | { |
|---|
| 121 | "Set your home location for the weather command with no parameters." |
|---|
| 122 | }; |
|---|
| 123 | |
|---|
| 124 | public boolean optionCheckUserWeather(final String value, final String nick) |
|---|
| 125 | { |
|---|
| 126 | return true; //hmm |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | private String checkOption(final String userNick) |
|---|
| 130 | { |
|---|
| 131 | try |
|---|
| 132 | { |
|---|
| 133 | return (String)mods.plugin.callAPI("Options", "GetUserOption", userNick,"Location", optionsUserDefaults[0]); |
|---|
| 134 | } catch (final ChoobNoSuchCallException e) |
|---|
| 135 | { |
|---|
| 136 | return optionsUserDefaults[0]; |
|---|
| 137 | } |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | public String[] helpCommandWeather = |
|---|
| 141 | { |
|---|
| 142 | "Return weather data for a location", |
|---|
| 143 | "<LOCATION>", |
|---|
| 144 | "<LOCATION> can be any of a text location eg: 'london' or a US zip code, or any other location google weather accepts" |
|---|
| 145 | }; |
|---|
| 146 | public void commandWeather( final Message mes ) |
|---|
| 147 | { |
|---|
| 148 | final List<String> params = mods.util.getParams(mes,2); |
|---|
| 149 | String location = ""; |
|---|
| 150 | if (params.size()<2) |
|---|
| 151 | { |
|---|
| 152 | location = checkOption(mes.getNick()); |
|---|
| 153 | } |
|---|
| 154 | if (location == null || location.equals("")) |
|---|
| 155 | location = mods.util.getParams(mes, 1).get(1); |
|---|
| 156 | |
|---|
| 157 | if (location.equals("")) |
|---|
| 158 | { |
|---|
| 159 | irc.sendContextReply(mes, "Please give me a location to lookup."); |
|---|
| 160 | return; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | try |
|---|
| 164 | { |
|---|
| 165 | irc.sendContextReply(mes,getWeather(location)); |
|---|
| 166 | } catch (final IOException e) |
|---|
| 167 | { |
|---|
| 168 | System.err.println(e.getMessage()); |
|---|
| 169 | e.printStackTrace(); |
|---|
| 170 | irc.sendContextReply(mes,"Could not contact the site to obtain weather information"); |
|---|
| 171 | } catch (final JAXBException e) |
|---|
| 172 | { |
|---|
| 173 | System.err.println(e.getMessage()); |
|---|
| 174 | e.printStackTrace(); |
|---|
| 175 | irc.sendContextReply(mes,"Could not understand the weather information"); |
|---|
| 176 | } catch (final LocationNotFoundException e) |
|---|
| 177 | { |
|---|
| 178 | irc.sendContextReply(mes,"Could not retrieve any weather information for that location"); |
|---|
| 179 | } catch (final Throwable e) |
|---|
| 180 | { |
|---|
| 181 | Throwable ex = e; |
|---|
| 182 | while (ex != null) |
|---|
| 183 | { |
|---|
| 184 | System.err.println(ex.getMessage()); |
|---|
| 185 | ex.printStackTrace(); |
|---|
| 186 | System.out.println("Cause is " + ex.getCause()); |
|---|
| 187 | ex = ex.getCause(); |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | public static void main(final String[] args) throws IOException, JAXBException, LocationNotFoundException |
|---|
| 193 | { |
|---|
| 194 | if (args.length != 1) |
|---|
| 195 | { |
|---|
| 196 | System.err.println("Usage: <Location>"); |
|---|
| 197 | System.exit(-1); |
|---|
| 198 | } |
|---|
| 199 | final GoogleWeather m = new GoogleWeather(); |
|---|
| 200 | System.out.println(m.getWeather(args[0])); |
|---|
| 201 | } |
|---|
| 202 | } |
|---|
| 203 | |
|---|
| 204 | class LocationNotFoundException extends Exception { |
|---|
| 205 | |
|---|
| 206 | /** |
|---|
| 207 | * |
|---|
| 208 | */ |
|---|
| 209 | private static final long serialVersionUID = 3732524579011476793L;} |
|---|
| 210 | |
|---|
| 211 | /** |
|---|
| 212 | * Represents the response google maps will send us |
|---|
| 213 | * @author benji |
|---|
| 214 | */ |
|---|
| 215 | @XmlRootElement(name = "xml_api_reply") |
|---|
| 216 | class GoogleMapsResponse |
|---|
| 217 | { |
|---|
| 218 | |
|---|
| 219 | private Weather weather; |
|---|
| 220 | |
|---|
| 221 | public void setWeather(final Weather weather) |
|---|
| 222 | { |
|---|
| 223 | this.weather = weather; |
|---|
| 224 | } |
|---|
| 225 | |
|---|
| 226 | public Weather getWeather() |
|---|
| 227 | { |
|---|
| 228 | return weather; |
|---|
| 229 | } |
|---|
| 230 | } |
|---|
| 231 | |
|---|
| 232 | |
|---|
| 233 | class Weather |
|---|
| 234 | { |
|---|
| 235 | |
|---|
| 236 | public CurrentConditions getCurrentConditions() |
|---|
| 237 | { |
|---|
| 238 | for (final Object o : getForeCastInformation()) |
|---|
| 239 | { |
|---|
| 240 | if (o instanceof CurrentConditions) |
|---|
| 241 | { |
|---|
| 242 | return (CurrentConditions) o; |
|---|
| 243 | } |
|---|
| 244 | } |
|---|
| 245 | return null; |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | public List<ForecastConditions> getForeCastConditions() |
|---|
| 249 | { |
|---|
| 250 | final ArrayList<ForecastConditions> forecastConditions = new ArrayList<ForecastConditions>(); |
|---|
| 251 | for (final Object o : getForeCastInformation()) |
|---|
| 252 | { |
|---|
| 253 | if (o instanceof ForecastConditions) |
|---|
| 254 | { |
|---|
| 255 | forecastConditions.add((ForecastConditions) o); |
|---|
| 256 | } |
|---|
| 257 | } |
|---|
| 258 | return forecastConditions; |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | public ForecastInformation getForecastInformation() |
|---|
| 262 | { |
|---|
| 263 | for (final Object o : getForeCastInformation()) |
|---|
| 264 | { |
|---|
| 265 | if (o instanceof ForecastInformation) |
|---|
| 266 | { |
|---|
| 267 | return (ForecastInformation)o; |
|---|
| 268 | } |
|---|
| 269 | } |
|---|
| 270 | return null; |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | @XmlElementRefs( |
|---|
| 274 | { |
|---|
| 275 | @XmlElementRef(name = "forecast_information", type = ForecastInformation.class), |
|---|
| 276 | @XmlElementRef(name = "current_conditions", type = CurrentConditions.class), |
|---|
| 277 | @XmlElementRef(name = "forecast_conditions", type = ForecastConditions.class) |
|---|
| 278 | }) |
|---|
| 279 | protected List<Object> foreCastInformation; |
|---|
| 280 | |
|---|
| 281 | public List<Object> getForeCastInformation() |
|---|
| 282 | { |
|---|
| 283 | if (foreCastInformation == null) |
|---|
| 284 | { |
|---|
| 285 | foreCastInformation = new ArrayList<Object>(); |
|---|
| 286 | } |
|---|
| 287 | return this.foreCastInformation; |
|---|
| 288 | } |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | @XmlRootElement(name="forecast_conditions") |
|---|
| 292 | class ForecastConditions |
|---|
| 293 | { |
|---|
| 294 | |
|---|
| 295 | private DataItem day_of_week; |
|---|
| 296 | private DataItem low; |
|---|
| 297 | private DataItem high; |
|---|
| 298 | private DataItem icon; |
|---|
| 299 | private DataItem condition; |
|---|
| 300 | |
|---|
| 301 | public DataItem getDay_of_week() |
|---|
| 302 | { |
|---|
| 303 | return day_of_week; |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | public void setDay_of_week(final DataItem day_of_week) |
|---|
| 307 | { |
|---|
| 308 | this.day_of_week = day_of_week; |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | public DataItem getLow() |
|---|
| 312 | { |
|---|
| 313 | return low; |
|---|
| 314 | } |
|---|
| 315 | |
|---|
| 316 | public void setLow(final DataItem low) |
|---|
| 317 | { |
|---|
| 318 | this.low = low; |
|---|
| 319 | } |
|---|
| 320 | |
|---|
| 321 | public DataItem getHigh() |
|---|
| 322 | { |
|---|
| 323 | return high; |
|---|
| 324 | } |
|---|
| 325 | |
|---|
| 326 | public void setHigh(final DataItem high) |
|---|
| 327 | { |
|---|
| 328 | this.high = high; |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | public DataItem getIcon() |
|---|
| 332 | { |
|---|
| 333 | return icon; |
|---|
| 334 | } |
|---|
| 335 | |
|---|
| 336 | public void setIcon(final DataItem icon) |
|---|
| 337 | { |
|---|
| 338 | this.icon = icon; |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | public DataItem getCondition() |
|---|
| 342 | { |
|---|
| 343 | return condition; |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | public void setCondition(final DataItem condition) |
|---|
| 347 | { |
|---|
| 348 | this.condition = condition; |
|---|
| 349 | } |
|---|
| 350 | } |
|---|
| 351 | |
|---|
| 352 | |
|---|
| 353 | @XmlRootElement(name="current_conditions") |
|---|
| 354 | class CurrentConditions |
|---|
| 355 | { |
|---|
| 356 | |
|---|
| 357 | private DataItem condition; |
|---|
| 358 | private DataItem temp_f; |
|---|
| 359 | private DataItem temp_c; |
|---|
| 360 | private DataItem humidity; |
|---|
| 361 | private DataItem icon; |
|---|
| 362 | private DataItem wind_direction; |
|---|
| 363 | |
|---|
| 364 | public DataItem getCondition() |
|---|
| 365 | { |
|---|
| 366 | return condition; |
|---|
| 367 | } |
|---|
| 368 | |
|---|
| 369 | public void setCondition(final DataItem condition) |
|---|
| 370 | { |
|---|
| 371 | this.condition = condition; |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | public DataItem getTemp_f() |
|---|
| 375 | { |
|---|
| 376 | return temp_f; |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | public void setTemp_f(final DataItem temp_f) |
|---|
| 380 | { |
|---|
| 381 | this.temp_f = temp_f; |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | public DataItem getTemp_c() |
|---|
| 385 | { |
|---|
| 386 | return temp_c; |
|---|
| 387 | } |
|---|
| 388 | |
|---|
| 389 | public void setTemp_c(final DataItem temp_c) |
|---|
| 390 | { |
|---|
| 391 | this.temp_c = temp_c; |
|---|
| 392 | } |
|---|
| 393 | |
|---|
| 394 | public DataItem getHumidity() |
|---|
| 395 | { |
|---|
| 396 | return humidity; |
|---|
| 397 | } |
|---|
| 398 | |
|---|
| 399 | public void setHumidity(final DataItem humidity) |
|---|
| 400 | { |
|---|
| 401 | this.humidity = humidity; |
|---|
| 402 | } |
|---|
| 403 | |
|---|
| 404 | public DataItem getIcon() |
|---|
| 405 | { |
|---|
| 406 | return icon; |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | public void setIcon(final DataItem icon) |
|---|
| 410 | { |
|---|
| 411 | this.icon = icon; |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| 414 | public DataItem getWind_direction() |
|---|
| 415 | { |
|---|
| 416 | return wind_direction; |
|---|
| 417 | } |
|---|
| 418 | |
|---|
| 419 | public void setWind_direction(final DataItem wind_direction) |
|---|
| 420 | { |
|---|
| 421 | this.wind_direction = wind_direction; |
|---|
| 422 | } |
|---|
| 423 | } |
|---|
| 424 | |
|---|
| 425 | @XmlAccessorType(XmlAccessType.FIELD) |
|---|
| 426 | class DataItem |
|---|
| 427 | { |
|---|
| 428 | |
|---|
| 429 | @XmlAttribute(required = true) |
|---|
| 430 | protected String data; |
|---|
| 431 | |
|---|
| 432 | public String getData() |
|---|
| 433 | { |
|---|
| 434 | return data; |
|---|
| 435 | } |
|---|
| 436 | |
|---|
| 437 | public void setData(final String value) |
|---|
| 438 | { |
|---|
| 439 | this.data = value; |
|---|
| 440 | } |
|---|
| 441 | |
|---|
| 442 | @Override |
|---|
| 443 | public String toString() |
|---|
| 444 | { |
|---|
| 445 | return data; |
|---|
| 446 | } |
|---|
| 447 | } |
|---|
| 448 | |
|---|
| 449 | @XmlRootElement(name="forecast_information") |
|---|
| 450 | class ForecastInformation |
|---|
| 451 | { |
|---|
| 452 | private DataItem city; |
|---|
| 453 | |
|---|
| 454 | public DataItem getCity() |
|---|
| 455 | { |
|---|
| 456 | return city; |
|---|
| 457 | } |
|---|
| 458 | |
|---|
| 459 | public void setCity(final DataItem city) |
|---|
| 460 | { |
|---|
| 461 | this.city = city; |
|---|
| 462 | } |
|---|
| 463 | } |
|---|