Switched to Alpha Avantage API, created test.
This commit is contained in:
parent
4b0ef573bc
commit
df6567ad1c
2 changed files with 172 additions and 50 deletions
|
@ -31,13 +31,20 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules;
|
package net.thauvin.erik.mobibot.modules;
|
||||||
|
|
||||||
import com.Ostermiller.util.CSVParser;
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot;
|
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.PrivateMessage;
|
||||||
|
import net.thauvin.erik.mobibot.msg.PublicMessage;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The StockQuote module.
|
* The StockQuote module.
|
||||||
|
@ -47,19 +54,86 @@ import java.io.IOException;
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public final class StockQuote extends AbstractModule {
|
public final class StockQuote extends AbstractModule {
|
||||||
|
|
||||||
|
static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key";
|
||||||
|
|
||||||
|
// The Alpha Advantage URL.
|
||||||
|
private static final String ALAPHADVANTAGE_URL = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The quote command.
|
* The quote command.
|
||||||
*/
|
*/
|
||||||
public static final String STOCK_CMD = "stock";
|
private static final String STOCK_CMD = "stock";
|
||||||
|
|
||||||
// The Yahoo! stock quote URL.
|
|
||||||
private static final String YAHOO_URL = "http://finance.yahoo.com/d/quotes.csv?&f=snl1d1t1c1oghv&e=.csv&s=";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link StockQuote} instance.
|
* Creates a new {@link StockQuote} instance.
|
||||||
*/
|
*/
|
||||||
public StockQuote() {
|
public StockQuote() {
|
||||||
commands.add(STOCK_CMD);
|
commands.add(STOCK_CMD);
|
||||||
|
properties.put(ALPHAVANTAGE_API_KEY_PROP, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a stock quote.
|
||||||
|
*
|
||||||
|
* @param symbol The stock symbol.
|
||||||
|
* @return The stock quote.
|
||||||
|
* @throws ModuleException If an errors occurs.
|
||||||
|
*/
|
||||||
|
static ArrayList<Message> getQuote(String symbol, String apiKey) throws ModuleException {
|
||||||
|
final String debugMessage = "getQuote(" + symbol + ')';
|
||||||
|
final ArrayList<Message> messages = new ArrayList<>();
|
||||||
|
final OkHttpClient client = new OkHttpClient();
|
||||||
|
final Request request =
|
||||||
|
new Request.Builder().url(ALAPHADVANTAGE_URL + "&symbol=" + symbol + "&apikey=" + apiKey).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Response response = client.newCall(request).execute();
|
||||||
|
final JSONObject json = new JSONObject(response.body().string());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String info = json.getString("Information");
|
||||||
|
if (!info.isEmpty()) {
|
||||||
|
throw new ModuleException(debugMessage, Utils.unescapeXml(info));
|
||||||
|
}
|
||||||
|
} catch (JSONException ignore) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String error = json.getString("Error Message");
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
throw new ModuleException(debugMessage, Utils.unescapeXml(error));
|
||||||
|
}
|
||||||
|
} catch (JSONException ignore) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
final JSONObject quote = json.getJSONObject("Global Quote");
|
||||||
|
|
||||||
|
if (quote.isEmpty()) {
|
||||||
|
messages.add(new ErrorMessage("Invalid symbol."));
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.add(
|
||||||
|
new PublicMessage("Symbol: " + Utils.unescapeXml(quote.getString("01. symbol"))));
|
||||||
|
messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price"))));
|
||||||
|
messages.add(new PublicMessage(" Previous: "
|
||||||
|
+ Utils.unescapeXml(quote.getString("08. previous close"))));
|
||||||
|
messages.add(new PrivateMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open"))));
|
||||||
|
messages.add(new PrivateMessage(" High: " + Utils.unescapeXml(quote.getString("03. high"))));
|
||||||
|
messages.add(new PrivateMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low"))));
|
||||||
|
messages.add(new PrivateMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume"))));
|
||||||
|
messages.add(new PrivateMessage(" Latest: "
|
||||||
|
+ Utils.unescapeXml(quote.getString("07. latest trading day"))));
|
||||||
|
messages.add(new PrivateMessage(" Change: " + Utils.unescapeXml(quote.getString("09. change"))
|
||||||
|
+ " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']'));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e);
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,60 +154,27 @@ public final class StockQuote extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||||
bot.send(sender, "To retrieve a stock quote:");
|
bot.send(sender, "To retrieve a stock quote:");
|
||||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol[.country code]>"));
|
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the specified stock quote from Yahoo!
|
* Returns the specified stock quote from Alpha Advantage.
|
||||||
*/
|
*/
|
||||||
private void run(final Mobibot bot, final String sender, final String symbol) {
|
private void run(final Mobibot bot, final String sender, final String symbol) {
|
||||||
try {
|
try {
|
||||||
final OkHttpClient client = new OkHttpClient();
|
final ArrayList<Message> messages =
|
||||||
final Request request = new Request.Builder().url(YAHOO_URL + symbol.toUpperCase()).build();
|
getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP));
|
||||||
final Response response = client.newCall(request).execute();
|
for (Message msg : messages) {
|
||||||
|
if (msg.isPrivate() || msg.isError()) {
|
||||||
final String[][] lines = CSVParser.parse(response.body().string());
|
bot.send(sender, msg.getMessage());
|
||||||
|
|
||||||
if (lines.length > 0) {
|
|
||||||
final String[] quote = lines[0];
|
|
||||||
|
|
||||||
if (quote.length > 0) {
|
|
||||||
if ((quote.length > 3) && (!"N/A".equalsIgnoreCase(quote[3]))) {
|
|
||||||
bot.send(bot.getChannel(), "Symbol: " + quote[0] + " [" + quote[1] + ']');
|
|
||||||
|
|
||||||
if (quote.length > 5) {
|
|
||||||
bot.send(bot.getChannel(), "Last Trade: " + quote[2] + " (" + quote[5] + ')');
|
|
||||||
} else {
|
|
||||||
bot.send(bot.getChannel(), "Last Trade: " + quote[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote.length > 4) {
|
|
||||||
bot.send(sender, "Time: " + quote[3] + ' ' + quote[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote.length > 6 && !"N/A".equalsIgnoreCase(quote[6])) {
|
|
||||||
bot.send(sender, "Open: " + quote[6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote.length > 7 && !"N/A".equalsIgnoreCase(quote[7]) && !"N/A".equalsIgnoreCase(quote[8])) {
|
|
||||||
bot.send(sender, "Day's Range: " + quote[7] + " - " + quote[8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote.length > 9 && !"0".equalsIgnoreCase(quote[9])) {
|
|
||||||
bot.send(sender, "Volume: " + quote[9]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bot.send(sender, "Invalid ticker symbol.");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "No values returned.");
|
bot.send(bot.getChannel(), msg.getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
bot.send(sender, "No data returned.");
|
|
||||||
}
|
}
|
||||||
} catch (NullPointerException | IOException e) {
|
|
||||||
bot.getLogger().debug("Unable to retrieve stock quote for: " + symbol, e);
|
} catch (ModuleException e) {
|
||||||
bot.send(sender, "An error has occurred retrieving a stock quote: " + e.getMessage());
|
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||||
|
bot.send(sender, "An error has occurred retrieving a stock quote.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* StockQuoteTest.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.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>StockQuoteTest</code> class.
|
||||||
|
*
|
||||||
|
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||||
|
* @created 2019-04-07
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class StockQuoteTest {
|
||||||
|
@Test
|
||||||
|
public void testGetQuote() throws ModuleException, IOException {
|
||||||
|
String apiKey = System.getenv("ALPHA_ADVANTAGE");
|
||||||
|
if (apiKey == null) {
|
||||||
|
final Path localProps = Paths.get("local.properties");
|
||||||
|
if (Files.exists(localProps)) {
|
||||||
|
try (final InputStream stream = Files.newInputStream(localProps)) {
|
||||||
|
final Properties p = new Properties();
|
||||||
|
p.load(stream);
|
||||||
|
apiKey = p.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Message> messages = StockQuote.getQuote("AAPL", apiKey);
|
||||||
|
assertThat(messages).as("response not empty").isNotEmpty();
|
||||||
|
assertThat(messages.get(0).getMessage()).as("same stock symbol").contains("AAPL");
|
||||||
|
|
||||||
|
messages = StockQuote.getQuote("012", apiKey);
|
||||||
|
assertThat(messages.get(0).isError()).as("invalid symbol error").isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStockQuoteImpl() {
|
||||||
|
AbstractModuleTest.testAbstractModule(new StockQuote());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue