diff --git a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java b/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java new file mode 100644 index 0000000000..d80a739d29 --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.api.osbuddy; + +import com.google.gson.JsonParseException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +public class GrandExchangeClient +{ + public GrandExchangeResult lookupItem(int itemId) throws IOException + { + final HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("osb") + .addPathSegment("ge") + .addQueryParameter("itemId", Integer.toString(itemId)) + .build(); + + log.debug("Built URI: {}", url); + + final Request request = new Request.Builder() + .url(url) + .build(); + + try (final Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + throw new IOException("Error looking up item id: " + response.message()); + } + + final InputStream in = response.body().byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), GrandExchangeResult.class); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } +} diff --git a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java b/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java new file mode 100644 index 0000000000..993ee0d567 --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.api.osbuddy; + +import java.time.Instant; +import lombok.Data; + +@Data +public class GrandExchangeResult +{ + private int item_id; + private int buy_average; + private int sell_average; + private int overall_average; + private Instant last_update; +} diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java new file mode 100644 index 0000000000..92ba9093e7 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.service.osbuddy; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.CacheControl; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/osb/ge") +public class GrandExchangeController +{ + private final GrandExchangeService grandExchangeService; + + @Autowired + public GrandExchangeController(GrandExchangeService grandExchangeService) + { + this.grandExchangeService = grandExchangeService; + } + + @RequestMapping + public ResponseEntity get(@RequestParam("itemId") int itemId) throws ExecutionException + { + GrandExchangeEntry grandExchangeEntry = grandExchangeService.get(itemId); + + return ResponseEntity.ok() + .cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic()) + .body(grandExchangeEntry); + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeEntry.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeEntry.java new file mode 100644 index 0000000000..8ea34c10c9 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeEntry.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.service.osbuddy; + +import java.time.Instant; +import lombok.Data; + +@Data +class GrandExchangeEntry +{ + private int item_id; + private int buy_average; + private int sell_average; + private int overall_average; + private Instant last_update; +} diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java new file mode 100644 index 0000000000..f7addee899 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.service.osbuddy; + +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.service.osbuddy.beans.OsbuddySummaryItem; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.sql2o.Connection; +import org.sql2o.Query; +import org.sql2o.Sql2o; + +@Service +@Slf4j +public class GrandExchangeService +{ + private static final String CREATE_GRAND_EXCHANGE_PRICES = "CREATE TABLE IF NOT EXISTS `osb_ge` (\n" + + " `item_id` int(11) NOT NULL,\n" + + " `buy_average` int(11) NOT NULL,\n" + + " `sell_average` int(11) NOT NULL,\n" + + " `overall_average` int(11) NOT NULL,\n" + + " `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n" + + " PRIMARY KEY (`item_id`)\n" + + ") ENGINE=InnoDB"; + + private static final OsbuddyClient CLIENT = new OsbuddyClient(); + + private final Sql2o sql2o; + + @Autowired + public GrandExchangeService(@Qualifier("Runelite SQL2O") Sql2o sql2o) + { + this.sql2o = sql2o; + + try (Connection con = sql2o.open()) + { + con.createQuery(CREATE_GRAND_EXCHANGE_PRICES).executeUpdate(); + } + } + + public GrandExchangeEntry get(int itemId) + { + try (Connection con = sql2o.open()) + { + return con.createQuery("SELECT item_id, buy_average, sell_average, overall_average, last_update" + + " FROM osb_ge WHERE item_id = :itemId") + .addParameter("itemId", itemId) + .executeAndFetchFirst(GrandExchangeEntry.class); + } + } + + @Scheduled(initialDelay = 1000 * 5, fixedDelay = 1000 * 60 * 30) + private void updateDatabase() + { + try + { + Map summary = CLIENT.getSummary(); + + try (Connection con = sql2o.beginTransaction()) + { + Instant updateTime = Instant.now(); + + Query query = con.createQuery("INSERT INTO osb_ge (item_id, buy_average, sell_average, overall_average," + + " last_update) VALUES (:itemId, :buyAverage, :sellAverage, :overallAverage, :lastUpdate)" + + " ON DUPLICATE KEY UPDATE buy_average = VALUES(buy_average), sell_average = VALUES(sell_average)," + + " overall_average = VALUES(overall_average), last_update = VALUES(last_update)"); + + for (Map.Entry entry : summary.entrySet()) + { + Integer itemId = entry.getKey(); + OsbuddySummaryItem item = entry.getValue(); + + query + .addParameter("itemId", itemId) + .addParameter("buyAverage", item.getBuy_average()) + .addParameter("sellAverage", item.getSell_average()) + .addParameter("overallAverage", item.getOverall_average()) + .addParameter("lastUpdate", Timestamp.from(updateTime)) + .addToBatch(); + } + + query.executeBatch(); + con.commit(false); + } + } + catch (IOException e) + { + log.warn("Error while updating the osb grand exchange table", e); + } + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/OsbuddyClient.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/OsbuddyClient.java new file mode 100644 index 0000000000..dadb635a4d --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/OsbuddyClient.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.service.osbuddy; + +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; +import net.runelite.http.api.RuneLiteAPI; +import net.runelite.http.service.osbuddy.beans.OsbuddySummaryItem; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; + +public class OsbuddyClient +{ + private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"; + + public Map getSummary() throws IOException + { + HttpUrl httpUrl = new HttpUrl.Builder() + .scheme("https") + .host("rsbuddy.com") + .addPathSegment("exchange") + .addPathSegment("summary.json") + .build(); + + Request request = new Request.Builder() + .url(httpUrl) + .header("User-Agent", USER_AGENT) + .build(); + + try (Response responseOk = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!responseOk.isSuccessful()) + { + throw new IOException("Error retrieving summary from OSBuddy: " + responseOk.message()); + } + + Type type = new TypeToken>() + { + }.getType(); + + return RuneLiteAPI.GSON.fromJson(responseOk.body().string(), type); + } + catch (JsonSyntaxException ex) + { + throw new IOException(ex); + } + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/beans/OsbuddySummaryItem.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/beans/OsbuddySummaryItem.java new file mode 100644 index 0000000000..2f969d9636 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/beans/OsbuddySummaryItem.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, AeonLucid + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 OWNER 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.runelite.http.service.osbuddy.beans; + +import lombok.Data; + +@Data +public class OsbuddySummaryItem +{ + private int id; + private String name; + private boolean members; + private int sp; + private int buy_average; + private int sell_average; + private int overall_average; +} diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 04fac9f777..396629fe1b 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -42,6 +42,7 @@ public class WidgetID public static final int BANK_GROUP_ID = 12; public static final int BANK_INVENTORY_GROUP_ID = 15; public static final int GRAND_EXCHANGE_INVENTORY_GROUP_ID = 467; + public static final int GRAND_EXCHANGE_GROUP_ID = 465; public static final int DEPOSIT_BOX_GROUP_ID = 192; public static final int INVENTORY_GROUP_ID = 149; public static final int FRIENDS_LIST_GROUP_ID = 429; @@ -158,6 +159,18 @@ public class WidgetID } static class GrandExchange + { + static final int WINDOW_CONTAINER = 0; + static final int WINDOW_BORDERS = 2; + static final int HISTORY_BUTTON = 3; + static final int BACK_BUTTON = 4; + static final int OFFER_CONTAINER = 24; + static final int OFFER_DESCRIPTION = 25; + static final int OFFER_PRICE = 26; + static final int OFFER_CONFIRM_BUTTON = 27; + } + + static class GrandExchangeInventory { static final int INVENTORY_ITEM_CONTAINER = 0; } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 62f03cb83c..8caa62a3e7 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -114,7 +114,12 @@ public enum WidgetInfo BANK_TITLE_BAR(WidgetID.BANK_GROUP_ID, WidgetID.Bank.BANK_TITLE_BAR), BANK_ITEM_COUNT(WidgetID.BANK_GROUP_ID, WidgetID.Bank.BANK_ITEM_COUNT), - GRAND_EXCHANGE_INVENTORY_ITEMS_CONTAINER(WidgetID.GRAND_EXCHANGE_INVENTORY_GROUP_ID, WidgetID.GrandExchange.INVENTORY_ITEM_CONTAINER), + GRAND_EXCHANGE_WINDOW_CONTAINER(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.WINDOW_CONTAINER), + GRAND_EXCHANGE_OFFER_CONTAINER(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER_CONTAINER), + GRAND_EXCHANGE_OFFER_TEXT(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER_DESCRIPTION), + GRAND_EXCHANGE_OFFER_PRICE(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER_PRICE), + + GRAND_EXCHANGE_INVENTORY_ITEMS_CONTAINER(WidgetID.GRAND_EXCHANGE_INVENTORY_GROUP_ID, WidgetID.GrandExchangeInventory.INVENTORY_ITEM_CONTAINER), DEPOSIT_BOX_INVENTORY_ITEMS_CONTAINER(WidgetID.DEPOSIT_BOX_GROUP_ID, WidgetID.DepositBox.INVENTORY_ITEM_CONTAINER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java index 2dc8eed80f..4e5215bae5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java @@ -56,4 +56,15 @@ public interface GrandExchangeConfig extends Config { return true; } + + @ConfigItem( + position = 3, + keyName = "enableOsbPrices", + name = "Enable OSB actively traded prices", + description = "Shows the OSBuddy actively traded price at the GE" + ) + default boolean enableOsbPrices() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 8cbf90ad9b..182e8e81a7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -31,6 +31,7 @@ import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.concurrent.ScheduledExecutorService; import javax.imageio.ImageIO; import javax.inject.Inject; import javax.swing.SwingUtilities; @@ -48,8 +49,11 @@ import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.FocusChanged; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; import net.runelite.api.events.GrandExchangeOfferChanged; import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.Notifier; @@ -61,7 +65,10 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.PluginToolbar; +import net.runelite.client.util.StackFormatter; import net.runelite.client.util.Text; +import net.runelite.http.api.osbuddy.GrandExchangeClient; +import net.runelite.http.api.osbuddy.GrandExchangeResult; @PluginDescriptor( name = "Grand Exchange" @@ -69,6 +76,10 @@ import net.runelite.client.util.Text; @Slf4j public class GrandExchangePlugin extends Plugin { + private static final int OFFER_CONTAINER_ITEM = 21; + private static final int OFFER_DEFAULT_ITEM_ID = 6512; + private static final GrandExchangeClient CLIENT = new GrandExchangeClient(); + @Getter(AccessLevel.PACKAGE) private NavigationButton button; @@ -103,6 +114,14 @@ public class GrandExchangePlugin extends Plugin @Inject private Notifier notifier; + @Inject + private ScheduledExecutorService executorService; + + private Widget grandExchangeText; + private Widget grandExchangeItem; + + private int lastItem = -1; + @Provides GrandExchangeConfig provideConfig(ConfigManager configManager) { @@ -239,4 +258,72 @@ public class GrandExchangePlugin extends Plugin setHotKeyPressed(false); } } + + @Subscribe + public void onWidgetLoaded(WidgetLoaded event) + { + switch (event.getGroupId()) + { + // Grand exchange was opened. + case WidgetID.GRAND_EXCHANGE_GROUP_ID: + if (!config.enableOsbPrices()) + { + return; + } + + Widget grandExchangeOffer = client.getWidget(WidgetInfo.GRAND_EXCHANGE_OFFER_CONTAINER); + grandExchangeText = client.getWidget(WidgetInfo.GRAND_EXCHANGE_OFFER_TEXT); + grandExchangeItem = grandExchangeOffer.getDynamicChildren()[OFFER_CONTAINER_ITEM]; + lastItem = -1; + break; + + // Grand exchange was closed (if it was open before). + case WidgetID.INVENTORY_GROUP_ID: + grandExchangeText = null; + grandExchangeItem = null; + lastItem = -1; + break; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (grandExchangeText == null || grandExchangeItem == null) + { + return; + } + + int itemId = grandExchangeItem.getItemId(); + if (itemId == OFFER_DEFAULT_ITEM_ID + || itemId == -1 + || lastItem == itemId) + { + return; + } + + lastItem = itemId; + final Widget geText = grandExchangeText; + + executorService.submit(() -> + { + try + { + final GrandExchangeResult result = CLIENT.lookupItem(itemId); + + if (result.getItem_id() != lastItem) + { + // something else has since been looked up? + return; + } + + final String text = geText.getText() + "
OSBuddy Actively traded price: " + StackFormatter.formatNumber(result.getOverall_average()); + geText.setText(text); + } + catch (IOException e) + { + log.debug("Error getting price of item {}", itemId, e); + } + }); + } }