Page MenuHomedesp's stash

CityInfo.java
No OneTemporary

CityInfo.java

package me.despawningbone.discordbot.command.info;
import java.awt.Color;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import me.despawningbone.discordbot.command.Command;
import me.despawningbone.discordbot.command.CommandResult;
import me.despawningbone.discordbot.command.CommandResult.CommandResultType;
import me.despawningbone.discordbot.utils.MiscUtils;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
public class CityInfo extends Command {
public CityInfo() {
this.alias = Arrays.asList("ci", "weather");
this.desc = "Search for info about a city!"; //"Search for info about the city the address is in!";
this.usage = "<address/search words>";
this.examples = Arrays.asList("hong kong", "tokyo"); //"HK", "akihabara");
String[] countryCodes = Locale.getISOCountries();
for (String cc : countryCodes) {
countries.put(new Locale("", cc).getDisplayCountry(), cc.toUpperCase());
}
}
private HashMap<String, String> countries = new HashMap<String, String>();
NumberFormat formatter = new DecimalFormat("#0.00");
@Override
public CommandResult execute(TextChannel channel, User author, Message msg, String[] args) {
if(args.length < 1) {
return new CommandResult(CommandResultType.INVALIDARGS, "Please input a city name."); //or a address.");
} else {
channel.sendTyping().queue();
String sword = String.join(" ", args);
try {
//www.yahoo.com changed its endpoint - there's no longer a AJAX API for weather info, and the search autocomplete is basically just a prefix search which is way inferior; so we use ca.news.yahoo.com instead
URLConnection sCon = new URL("https://ca.news.yahoo.com/tdv2_fp/api/resource/WeatherLocationService.autocomplete?text=" + URLEncoder.encode(sword, "UTF-8")).openConnection();
sCon.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0");
JSONArray res = new JSONArray(new JSONTokener(sCon.getInputStream()));
if(res.length() < 1) return new CommandResult(CommandResultType.NORESULT);
JSONObject wsearch = res.getJSONObject(0);
String woeid = String.valueOf(wsearch.getInt("woeid"));
String lat = String.valueOf(wsearch.getDouble("lat"));
String lng = String.valueOf(wsearch.getDouble("lon"));
String wQualifiedName = wsearch.getString("qualifiedName");
String countryShort = wsearch.getString("country");
String region = wsearch.getString("state").isEmpty() ? countryShort : wsearch.getString("state");
//original API no longer returns results, seems like all of the current info is generated server side so do some parsing instead
Document mainCon = Jsoup.connect("https://ca.news.yahoo.com/weather/" + wsearch.getString("country") + "/" + (wsearch.getString("state").isEmpty() ? wsearch.getString("country") : wsearch.getString("state")) + "/" + wsearch.getString("city") + "-" + woeid).get();
int timeOffset = (int) Math.round(Duration.between(Instant.now(), Instant.from(DateTimeFormatter.ofPattern("yyyy M-d, h:m a X").parse(OffsetDateTime.now().getYear() + " " + mainCon.selectFirst("#module-location-heading time").text().replace(".", "").toUpperCase() + " Z"))).getSeconds() / 60.0 / 60.0);
String tempNow = mainCon.selectFirst(".temperature-forecast .celsius").text();
//not sure why ~= doesnt work in jsoup but works in normal browsers
String tempHigh = mainCon.selectFirst("#module-location-heading .arrowUp ~ span[class*=\"celsius_D(b)\"]").text();
String tempLow = mainCon.selectFirst("#module-location-heading .arrowDown ~ span[class*=\"celsius_D(b)\"]").text();
String humidity = mainCon.selectFirst("#module-weather-details dt:contains(Humidity)").nextElementSibling().text();
String precipitationProb = mainCon.select(".hourlyForecast .precipitation dt").get(1).text();
String visibility = mainCon.selectFirst("#module-weather-details dt:contains(Visibility) ~ dd[class*=\"kilometers_D(b)\"]").text();
String conditionDesc = mainCon.selectFirst("#module-location-heading p").text();
String feelsLike = mainCon.selectFirst("#module-weather-details dt:contains(RealFeel®) ~ dd[class*=\"celsius_D(b)\"]").text();
String uv = mainCon.selectFirst("#module-weather-details dt:contains(UV Index)").nextElementSibling().text();
String pressure = mainCon.selectFirst("#module-weather-wind-pressure dt:contains(Barometer) ~ dd[class*=\"celsius_D(b)\"]").text();
String[] wind = mainCon.selectFirst("#module-weather-wind-pressure dt:contains(Wind) ~ dd[class*=\"kilometers_D(b)\"]").text().split(" ");
String windSpeed = wind[0] + " " + wind[1];
String windDir = wind[2];
Elements sun = mainCon.select("#module-weather-sun-moon time");
String sunrise = sun.get(0).text();
String sunset = sun.get(1).text();
String imgUrl = mainCon.selectFirst("picture img").attr("data-wf-src");
EmbedBuilder embedmsg = new EmbedBuilder();
embedmsg.setAuthor("Info for " + wQualifiedName, null, null);
embedmsg.setColor(new Color(100, 0, 255));
embedmsg.addField("Country", countries.containsKey(countryShort) ? MiscUtils.countryNameToUnicode(countries.get(countryShort)) : countryShort, true);
embedmsg.addField("Region", region, true);
embedmsg.addField("Current time", OffsetDateTime.now(ZoneOffset.ofHours(timeOffset)).format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ssa").withLocale(Locale.ENGLISH)).trim() + " (UTC" + (Math.signum(timeOffset) == 1 || Math.signum(timeOffset) == 0 ? "+" + timeOffset : timeOffset) + ")", true);
embedmsg.addField("Temperature", tempNow + "°C (↑" + tempHigh + "C | ↓" + tempLow + "C)", true);
embedmsg.addField("Feels Like", feelsLike + "C", true);
embedmsg.addField("Humidity", humidity + " (Chance of rain: " + precipitationProb + ")", true);
embedmsg.addField("Atmospheric pressure", pressure, true);
embedmsg.addField("Wind speed", windSpeed, true);
embedmsg.addField("Wind direction", windDir, true);
embedmsg.addField("UV index", uv, true);
embedmsg.addField("Sunrise", sunrise, true);
embedmsg.addField("Sunset", sunset, true);
embedmsg.addField("Visibility", visibility + " (" + conditionDesc + ")", true);
embedmsg.setThumbnail(imgUrl);
embedmsg.addField("Latitude", lat, true);
embedmsg.addField("Longitude", lng, true);
//no weather info update time anymore, just credit accuweather ig
embedmsg.setFooter("Powered by Accuweather / Yahoo", null);
try {
MessageEmbed fmsg = embedmsg.build();
channel.sendMessageEmbeds(fmsg).queue();
} catch(InsufficientPermissionException e2) {
return new CommandResult(CommandResultType.FAILURE, "Unfortunately, the bot is missing the permission `MESSAGE_EMBED_LINKS` which is required for this command to work.");
}
return new CommandResult(CommandResultType.SUCCESS);
} catch (Exception e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
}
}
}

File Metadata

Mime Type
text/x-java
Expires
Sun, Aug 16, 8:16 PM (19 h, 8 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
94/37/a55562bc975cbc17397ce777770b

Event Timeline