Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Reserve multiple bags at once #567

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
18 changes: 15 additions & 3 deletions tgtg_scanner/models/reservations.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,19 @@ def __init__(self, client: TgtgClient) -> None:
self.active_orders: Dict[str, Order] = {}

def reserve(self, item_id: str, display_name: str, amount: int = 1) -> None:
"""Create a new reservation
"""Create a new reservation or increase the amount in the existing reservation

Args:
item_id (str): Item ID
display_name (str): Item display name
amount (int, optional): Amount. Defaults to 1.
"""
self.reservation_query.append(Reservation(item_id, amount, display_name))
for reservation in self.reservation_query:
if reservation.item_id == item_id:
reservation.amount += amount
break
else:
self.reservation_query.append(Reservation(item_id, amount, display_name))

def make_orders(self, state: Dict[str, Item], callback: Callable[[Reservation], None]) -> None:
"""Create orders for reservations
Expand All @@ -50,10 +55,17 @@ def make_orders(self, state: Dict[str, Item], callback: Callable[[Reservation],
item = state.get(reservation.item_id)
if item and item.items_available > 0:
try:
remaining_amount = reservation.amount
reservation.amount = min(reservation.amount, item.items_available)
remaining_amount -= reservation.amount
self._create_order(reservation)
self.reservation_query.remove(reservation)
if remaining_amount > 0:
reservation.amount = remaining_amount
else:
self.reservation_query.remove(reservation)
callback(reservation)
except Exception as exc:
reservation.amount += remaining_amount
log.warning("Order failed: %s", exc)

def update_active_orders(self) -> None:
Expand Down
49 changes: 44 additions & 5 deletions tgtg_scanner/notifiers/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class Telegram(Notifier):
"""Notifier for Telegram"""

MAX_RETRIES = 10
MAX_BUTTON_TEXT_LENGTH = 50

def __init__(self, config: Config, reservations: Reservations, favorites: Favorites):
super().__init__(config, reservations, favorites)
Expand Down Expand Up @@ -222,7 +223,14 @@ async def _send(self, item: Union[Item, Reservation]) -> None: # type: ignore[o
if self.image:
image = self._unmask_image(self.image, item)
elif isinstance(item, Reservation):
message = escape_markdown(f"{item.display_name} is reserved for 5 minutes", version=2)
message = escape_markdown(
(
f"{item.display_name} ({item.amount} bags) are reserved for 5 minutes"
if item.amount > 1
else f"{item.display_name} is reserved for 5 minutes"
),
version=2,
)
else:
return
await self._send_message(message, image)
Expand Down Expand Up @@ -284,15 +292,29 @@ async def _unmute(self, update: Update, _) -> None:
async def _reserve_item_menu(self, update: Update, _) -> None:
favorites = self.favorites.get_favorites()
buttons = [
[InlineKeyboardButton(f"{item.display_name}: {item.items_available}", callback_data=item)] for item in favorites
[
InlineKeyboardButton(
Telegram._shorten_with_ellipsis(f"{item.display_name}: {item.items_available}"), callback_data=item
)
]
for item in favorites
]
reply_markup = InlineKeyboardMarkup(buttons)
await update.message.reply_text("Select a Bag to reserve", reply_markup=reply_markup)

@_private
async def _cancel_reservations_menu(self, update: Update, _) -> None:
buttons = [
[InlineKeyboardButton(reservation.display_name, callback_data=reservation)]
[
InlineKeyboardButton(
Telegram._shorten_with_ellipsis(
f"{reservation.display_name} ({reservation.amount} bags)"
if reservation.amount > 1
else reservation.display_name
),
callback_data=reservation,
)
]
for reservation in self.reservations.reservation_query
]
if len(buttons) == 0:
Expand All @@ -305,7 +327,15 @@ async def _cancel_reservations_menu(self, update: Update, _) -> None:
async def _cancel_orders_menu(self, update: Update, _) -> None:
self.reservations.update_active_orders()
buttons = [
[InlineKeyboardButton(order.display_name, callback_data=order)] for order in self.reservations.active_orders.values()
[
InlineKeyboardButton(
Telegram._shorten_with_ellipsis(
f"{order.display_name} ({order.amount} bags)" if order.amount > 1 else order.display_name
),
callback_data=order,
)
]
for order in self.reservations.active_orders.values()
]
if len(buttons) == 0:
await update.message.reply_text("No active Orders")
Expand Down Expand Up @@ -439,7 +469,7 @@ async def _callback_query_handler(self, update: Update, _) -> None:
log.debug('Added "%s" to reservation queue', data.display_name)
if isinstance(data, Reservation):
self.reservations.reservation_query.remove(data)
await update.callback_query.answer(f"Removed {data.display_name} form reservation queue")
await update.callback_query.answer(f"Removed {data.display_name} from reservation queue")
log.debug('Removed "%s" from reservation queue', data.display_name)
if isinstance(data, Order):
self.reservations.cancel_order(data.id)
Expand Down Expand Up @@ -500,3 +530,12 @@ async def _get_chat_id(self) -> None:

def __repr__(self) -> str:
return f"Telegram: {self.chat_ids}"

@staticmethod
def _shorten_with_ellipsis(text: str, length: int = MAX_BUTTON_TEXT_LENGTH) -> str:
"""Shorten text to length and add ellipsis in the middle"""
if len(text) <= length:
return text
else:
slice_size = (length - 3) // 2
return text[:slice_size] + "..." + text[-slice_size:]
18 changes: 9 additions & 9 deletions tgtg_scanner/tgtg/tgtg_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
BASE_URL = "https://apptoogoodtogo.com/api/"
API_ITEM_ENDPOINT = "item/v8/"
FAVORITE_ITEM_ENDPOINT = "user/favorite/v1/{}/update"
AUTH_BY_EMAIL_ENDPOINT = "auth/v4/authByEmail"
AUTH_POLLING_ENDPOINT = "auth/v4/authByRequestPollingId"
SIGNUP_BY_EMAIL_ENDPOINT = "auth/v4/signUpByEmail"
REFRESH_ENDPOINT = "auth/v4/token/refresh"
ACTIVE_ORDER_ENDPOINT = "order/v7/active"
INACTIVE_ORDER_ENDPOINT = "order/v7/inactive"
CREATE_ORDER_ENDPOINT = "order/v7/create/"
ABORT_ORDER_ENDPOINT = "order/v7/{}/abort"
ORDER_STATUS_ENDPOINT = "order/v7/{}/status"
AUTH_BY_EMAIL_ENDPOINT = "auth/v5/authByEmail"
AUTH_POLLING_ENDPOINT = "auth/v5/authByRequestPollingId"
SIGNUP_BY_EMAIL_ENDPOINT = "auth/v5/signUpByEmail"
REFRESH_ENDPOINT = "auth/v5/token/refresh"
ACTIVE_ORDER_ENDPOINT = "order/v8/active"
INACTIVE_ORDER_ENDPOINT = "order/v8/inactive"
CREATE_ORDER_ENDPOINT = "order/v8/create/"
ABORT_ORDER_ENDPOINT = "order/v8/{}/abort"
ORDER_STATUS_ENDPOINT = "order/v8/{}/status"
MANUFACTURERITEM_ENDPOINT = "manufactureritem/v2/"
USER_AGENTS = [
"TGTG/{} Dalvik/2.1.0 (Linux; U; Android 9; Nexus 5 Build/M4B30Z)",
Expand Down