Skip to content

Commit 723a68a

Browse files
authored
Merge pull request #3 from PrettyNeet/FeatAPIroute1H
Merging new API (/1h) to price checks
2 parents a2416c0 + 7393e93 commit 723a68a

File tree

7 files changed

+105
-41
lines changed

7 files changed

+105
-41
lines changed

assets/images/DiscoBot_transp.png

448 KB
Loading

assets/images/discord bot.png

158 KB
Loading

bot/commands/fish_profit.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import discord
22
from discord.ext import commands
33
from discord import app_commands
4-
from bot.utils.api import fetch_latest_prices
4+
from bot.utils.api import fetch_latest_prices, fetch_1h_prices
55
from data.items import fish
66
import pandas as pd
77

@@ -51,7 +51,7 @@ async def select_callback(self, interaction: discord.Interaction, select: discor
5151
f"**Cooked Price:** {row['Cooked Price']}\n"
5252
f"**Profit:** {int(row['Profit'])}\n"
5353
f"**XP/hr:** {row['XP/hr']}\n"
54-
f"**GP/hr:** {row['XP/hr']}\n"
54+
f"**GP/hr:** {row['GP/hr']}\n"
5555
),
5656
inline=False
5757
)
@@ -63,17 +63,32 @@ def __init__(self, bot):
6363
self.bot = bot
6464

6565
@app_commands.command(name="fish_profit", description="Calculate the potential profit from cooking fish.")
66-
async def fish_profit(self, interaction: discord.Interaction):
67-
prices = fetch_latest_prices()
66+
@app_commands.choices(
67+
price_type=[
68+
app_commands.Choice(name="Latest", value="latest"),
69+
app_commands.Choice(name="1-hour average", value="1h"),
70+
]
71+
)
72+
async def fish_profit(self, interaction: discord.Interaction, price_type: app_commands.Choice[str]):
73+
if price_type.value == "latest":
74+
prices = fetch_latest_prices()
75+
price_key = "high"
76+
elif price_type.value == "1h":
77+
prices = fetch_1h_prices()
78+
price_key = "avgHighPrice"
79+
else:
80+
await self.interaction.followup.send("error in price type selection")
81+
return
82+
6883
cooking_rate = 1435
6984

7085
profit_results = []
7186
for fish_name, info in fish.items():
7287
raw_id_str = str(info["raw_id"])
7388
cooked_id_str = str(info["cooked_id"])
7489
fish_xp_each = info["xp_each"]
75-
raw_price = prices[raw_id_str]["high"]
76-
cooked_price = prices[cooked_id_str]["high"]
90+
raw_price = prices[raw_id_str][price_key]
91+
cooked_price = prices[cooked_id_str][price_key]
7792

7893
if raw_price and cooked_price:
7994
profit_fish = cooked_price - raw_price

bot/commands/herb_profit.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
from discord.ext import commands
33
from discord import app_commands
44
import pandas as pd
5-
from bot.utils.api import fetch_latest_prices
6-
from bot.utils.calculations import calculate_custom_profit
5+
from bot.utils.api import fetch_latest_prices, fetch_1h_prices
6+
from bot.utils.calculations import calculate_custom_profit, DEBUG
77
from data.items import herbs
88

99

1010
# Setting the VIEW class to handle user format selection, interactive within discord channel message
1111
class FormatSelectView(discord.ui.View):
12-
def __init__(self, bot, interaction, farming_level, patches, weiss, trollheim, hosidius, fortis, kandarin_diary, kourend, magic_secateurs, farming_cape, bottomless_bucket, attas, compost):
12+
def __init__(self, bot, interaction, farming_level, patches, weiss, trollheim, hosidius, fortis, kandarin_diary, kourend, magic_secateurs, farming_cape, bottomless_bucket, attas, compost, prices, price_key, price_type):
1313
super().__init__()
1414
self.bot = bot
1515
self.interaction = interaction
@@ -26,6 +26,9 @@ def __init__(self, bot, interaction, farming_level, patches, weiss, trollheim, h
2626
self.bottomless_bucket = bottomless_bucket
2727
self.attas = attas
2828
self.compost = compost
29+
self.prices = prices
30+
self.price_key = price_key
31+
self.price_type = price_type
2932

3033
# Select menu for format, set's the option for the bot to later format the reply
3134
@discord.ui.select(
@@ -40,13 +43,17 @@ async def select_callback(self, interaction: discord.Interaction, select):
4043
# Sets the format choice
4144
format_choice = interaction.data["values"][0]
4245
await interaction.response.defer()
46+
47+
if DEBUG:
48+
# Debugging: Print the prices and price_key
49+
print("Prices fetched:", self.prices)
50+
print("Price key:", self.price_key)
4351

44-
# Fetch the latest prices and calculate profits
45-
latest_prices = fetch_latest_prices()
52+
# calculate profits
4653
profit_results = calculate_custom_profit(
47-
latest_prices, herbs, self.farming_level, self.patches, self.weiss,
54+
self.prices, herbs, self.farming_level, self.patches, self.weiss,
4855
self.trollheim, self.hosidius, self.fortis, self.compost, self.kandarin_diary,
49-
self.kourend, self.magic_secateurs, self.farming_cape, self.bottomless_bucket, self.attas
56+
self.kourend, self.magic_secateurs, self.farming_cape, self.bottomless_bucket, self.attas, self.price_key
5057
)
5158

5259
# Error handling if API or calc is empty
@@ -59,16 +66,17 @@ async def select_callback(self, interaction: discord.Interaction, select):
5966
df_sorted = df.sort_values(by="Profit per Run", ascending=False)
6067

6168
# Format and send response based on user choice
69+
title = f"results using {self.price_type} prices"
6270
if format_choice == "markdown":
6371
table_header = f"{'Herb':<12} {'Seed Price':<12} {'Herb Price':<12} {'Profit per Run':<15}\n{'-'*12} {'-'*12} {'-'*12} {'-'*15}\n"
6472
table_rows = ""
6573
for index, row in df_sorted.iterrows():
6674
table_rows += f"{row['Herb']:<12} {row['Seed Price']:<12} {row['Grimy Herb Price']:<12} {int(row['Profit per Run']):<15}\n"
67-
table = f"```{table_header}{table_rows}```"
75+
table = f"```{title}\n{table_header}{table_rows}```"
6876
await self.interaction.followup.send(content=f"{self.interaction.user.mention} Here are the results:\n{table}")
6977

7078
elif format_choice == "embed":
71-
embed = discord.Embed(title="Herb Profit per Run", color=discord.Color.green())
79+
embed = discord.Embed(title=title, color=discord.Color.green())
7280
embed.set_author(name=self.interaction.user.display_name, icon_url=self.interaction.user.display_avatar.url)
7381
for index, row in df_sorted.iterrows():
7482
embed.add_field(
@@ -109,6 +117,10 @@ def __init__(self, bot):
109117
app_commands.Choice(name="Compost", value="Compost"),
110118
app_commands.Choice(name="Supercompost", value="Supercompost"),
111119
app_commands.Choice(name="Ultracompost", value="Ultracompost"),
120+
],
121+
price_type=[
122+
app_commands.Choice(name="Latest", value="latest"),
123+
app_commands.Choice(name="1-hour average", value="1h"),
112124
]
113125
)
114126
async def herb_profit(
@@ -127,14 +139,29 @@ async def herb_profit(
127139
bottomless_bucket: bool,
128140
attas: bool,
129141
compost: app_commands.Choice[str],
130-
142+
price_type: app_commands.Choice[str]
131143
):
144+
if price_type.value == "latest":
145+
prices = fetch_latest_prices()
146+
price_key = "high"
147+
elif price_type.value == "1h":
148+
prices = fetch_1h_prices()
149+
price_key = "avgHighPrice"
150+
else:
151+
await self.interaction.followup.send("error is checking price type value")
152+
return
153+
154+
if DEBUG:
155+
# Debugging: Print the prices and price_key
156+
print("Prices fetched:", prices)
157+
print("Price key:", price_key)
158+
132159
# Create and send a view select
133160
view = FormatSelectView(
134161
bot=self.bot, interaction=interaction, farming_level=farming_level, patches=patches,
135162
weiss=weiss, trollheim=trollheim, hosidius=hosidius, fortis=fortis, kandarin_diary=kandarin_diary,
136163
kourend=kourend, magic_secateurs=magic_secateurs, farming_cape=farming_cape, bottomless_bucket=bottomless_bucket, attas=attas,
137-
compost=compost.value
164+
compost=compost.value, prices=prices, price_key=price_key, price_type=price_type.value
138165
)
139166
await interaction.response.send_message("Choose the format for the reply:", view=view)
140167

bot/utils/api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ def fetch_latest_prices():
88
response = requests.get(url, headers=HEADERS) # OSRS wiki demands custom user-agent headers, defined in config.yaml. python requests are blocked by default
99
data = response.json()
1010

11+
if "data" not in data:
12+
raise ValueError("Error fetching data from API")
13+
return data["data"]
14+
15+
16+
def fetch_1h_prices():
17+
url = "https://prices.runescape.wiki/api/v1/osrs/1h"
18+
response = requests.get(url, headers=HEADERS)
19+
data = response.json()
1120
if "data" not in data:
1221
raise ValueError("Error fetching data from API")
1322
return data["data"]

bot/utils/calculations.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
# Calculate profit based on user inputs and real-time prices
6-
def calculate_custom_profit(prices, herbs, farming_level, patches, weiss, trollheim, hosidius, fortis, compost, kandarin_diary, kourend, magic_secateurs, farming_cape, bottomless_bucket, attas):
6+
def calculate_custom_profit(prices, herbs, farming_level, patches, weiss, trollheim, hosidius, fortis, compost, kandarin_diary, kourend, magic_secateurs, farming_cape, bottomless_bucket, attas, price_key):
77
# Constants and multipliers
88
compost_life_value = {'None': 0, 'Compost': 1, 'Supercompost': 2, 'Ultracompost': 3}
99
item_bonus = 0.1 if magic_secateurs else 0
@@ -45,8 +45,15 @@ def calculate_custom_profit(prices, herbs, farming_level, patches, weiss, trollh
4545
if seed_id_str not in prices or herb_id_str not in prices:
4646
continue
4747

48-
seed_price = prices[seed_id_str]["high"]
49-
herb_price = prices[herb_id_str]["high"]
48+
seed_price = prices[seed_id_str][price_key]
49+
herb_price = prices[herb_id_str][price_key]
50+
51+
if DEBUG:
52+
print(f"price type: {price_key}\n")
53+
print(f"Herb: {herb}, Seed Price: {seed_price}, Herb Price: {herb_price}")
54+
55+
if seed_price is None or herb_price is None:
56+
continue
5057

5158
# Calculate expected yield using detailed mechanics
5259
expected_yield_unprotected = generate_estimated_yield(

readme.md

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
11
# OSRS Utilities
22

3-
This is a Discord bot that fetches the latest prices from the Old School Runescape Grand Exchange and calculates the potential profit from herb farming runs. The reason for a different utility than the osrs wiki farm herb calculator is due to the method that it fetches GE prices for items, which does not accurately represent the current price.
3+
![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54) ![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white) ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)
44

5-
## Commands
6-
7-
### Herb Profit
5+
![GitHub last commit](https://img.shields.io/github/last-commit/PrettyNeet/OSRS_Utilities) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/PrettyNeet/OSRS_Utilities)
86

9-
Calculate the potential profit from herb farming runs.
7+
This Discord bot fetches the latest prices from the Old School Runescape Grand Exchange and calculates potential profits from herb farming runs and cooking fish. Unlike the OSRS Wiki herb calculator, this bot uses a different method to fetch Grand Exchange prices for items, providing more accurate current prices.
108

11-
Usage:
12-
```/herb_profit farming_level patches weiss trollheim hosidius fortis kandarin_diary kourend magic_secateurs farming_cape bottomless_bucket compost```
9+
## Commands
1310

14-
### Fish Profit
11+
The bot commands are configured in a way that the parameters for the command will be prompted and autofilled by the bot.
1512

16-
Calculate the potential profit from cooking fish.
13+
```/herb_profit```
1714

18-
Usage:
1915
```/fish_profit```
2016

21-
## Roadmap / TODO 📋✨
17+
## Roadmap 📋✨
2218

23-
Please feel free to submit ideas (as issues) or pull requests for requested/nice to have features
19+
Feel free to submit ideas (as issues) or pull requests for requested or nice-to-have features. See the projects board for accurate feature/issue tracking.
2420

2521
### New User Options for Commands 🛠️
2622

27-
- [ ] Allow users to set if they want the latest price or 1h average
23+
- [x] Allow users to set if they want the latest price or 1h average
2824

2925
### New Commands 🎣🔥
3026

@@ -41,6 +37,8 @@ Please feel free to submit ideas (as issues) or pull requests for requested/nice
4137

4238
```bash
4339
osrs_utilities/
40+
├── assets/
41+
│ ├── images/
4442
├── bot/
4543
│ ├── __init__.py
4644
│ ├── bot.py
@@ -101,20 +99,27 @@ osrs_utilities/
10199

102100
3. **Set up environment variables:**
103101

104-
Create a `.env` file in the root directory and add your Discord bot token:
102+
Create a ```.env``` file in the root directory and add your Discord bot token:
105103

106104
```env
107105
DISCORD_BOT_TOKEN=your_discord_bot_token
108106
```
109107

110108
4. **Configure settings:**
111109

112-
Update `config.yaml` with any additional settings you need.
110+
Update ```config.yaml``` with any additional settings you need.
111+
112+
```yaml
113+
bot_prefix:
114+
intents:
115+
headers:
116+
debug:
117+
```
113118

114119
5. **Run the bot:**
115120

116121
```bash
117-
python run.py
122+
python .\run.py
118123
```
119124

120125
## Installation (Docker)
@@ -143,7 +148,7 @@ osrs_utilities/
143148
## Usage
144149

145150
- Add your bot to your Discord server using the OAuth2 URL generated in the Discord Developer Portal.
146-
- Use the `/herb_profit` command in your Discord server to see the profit data.
151+
- refer to the command section or bot.py for all commands
147152

148153
## Debugging
149154

@@ -159,10 +164,10 @@ When debugging is enabled, additional information will be printed to the console
159164

160165
To add new commands and features, follow these steps:
161166

162-
1. **Create a new command file (optional):**
167+
1. **Create a new command file:**
163168

164169
- If you have multiple commands related to a feature, you might want to group them in a separate file.
165-
- For example, create a file bot/new_feature_commands.py.
170+
- create the new command file in /commands/ folder
166171

167172
2. **Define the command in the command file:**
168173

@@ -171,9 +176,10 @@ To add new commands and features, follow these steps:
171176

172177
3. **Add utility functions (if needed):**
173178

174-
- If the command requires helper functions, add them to bot/utils.py.
179+
- re-use any of the helper utilities found in the /utils/ folder
180+
- if a new utility is identified, create one here to be re-used later in new commands
175181

176-
4. **Load the new command in run.py:**
182+
4. **Load the new command in run.py and/or bot.py:**
177183

178184
- Ensure the new command file is imported and the setup function is called.
179185

0 commit comments

Comments
 (0)