|
9 | 9 | import os |
10 | 10 | import json |
11 | 11 | import uuid |
| 12 | +import time |
12 | 13 | from bisect import bisect |
13 | 14 | from collections.abc import Mapping |
| 15 | +from collections import defaultdict |
14 | 16 | from itertools import chain |
15 | 17 | from textwrap import dedent |
16 | 18 | from typing import List, Union, Dict, Any # pylint: disable=unused-import |
@@ -363,6 +365,197 @@ def main(self): |
363 | 365 | print(res) |
364 | 366 |
|
365 | 367 |
|
| 368 | +@HabiticaCli.subcommand('pets') |
| 369 | +class Pets(ApplicationWithApi): |
| 370 | + """Core inheritable class for dealing with actions on pets.""" |
| 371 | + DESCRIPTION = _('List pets and their status') |
| 372 | + pet_specifier = cli.SwitchAttr( |
| 373 | + ['-P', '--pet'], |
| 374 | + help=_('Only show information about a particular pet')) # noqa: Q000 |
| 375 | + color_specifier = cli.SwitchAttr( |
| 376 | + ['-C', '--color'], |
| 377 | + help=_('Only show information about a particular color')) # noqa: Q000 |
| 378 | + |
| 379 | + def get_full_percent(self, amount: int): |
| 380 | + """Return the percentage of "fullness" for a pet.""" |
| 381 | + if amount == -1: |
| 382 | + amount = 100 |
| 383 | + else: |
| 384 | + amount *= 2 |
| 385 | + return str(amount) |
| 386 | + |
| 387 | + def get_food_needed(self, pet_fullness: int, amount_per_food: int = 5) -> int: |
| 388 | + """Return the amount of food needed to feed a pet till full.""" |
| 389 | + if pet_fullness == -1: |
| 390 | + return 0 |
| 391 | + return int((50 - int(pet_fullness)) / amount_per_food) |
| 392 | + |
| 393 | + def is_hatchable(self, user: dict, pet: str, color: str) -> bool: |
| 394 | + """Return true when a pat of a particular type and color can be hatched.""" |
| 395 | + combined = pet + '-' + color |
| 396 | + |
| 397 | + # check if pet exists or name is wrong |
| 398 | + if user['items']['pets'].get(combined, 100) != -1: |
| 399 | + return False |
| 400 | + |
| 401 | + if color not in user['items']['hatchingPotions'] or pet not in user['items']['eggs']: |
| 402 | + return False |
| 403 | + if user['items']['hatchingPotions'][color] > 0 and user['items']['eggs'][pet] > 0: |
| 404 | + return True |
| 405 | + return False |
| 406 | + |
| 407 | + |
| 408 | +@Pets.subcommand('list') |
| 409 | +class ListPets(Pets): |
| 410 | + """Lists all pets from the inventory.""" |
| 411 | + def main(self): # pylint: disable=too-many-branches |
| 412 | + super().main() |
| 413 | + user = self.api.user.get() |
| 414 | + print(_('Pets:')) |
| 415 | + |
| 416 | + color_specifier = self.color_specifier |
| 417 | + if color_specifier: |
| 418 | + color_specifier = color_specifier[0].capitalize() + color_specifier[1:] |
| 419 | + pet_specifier = self.pet_specifier |
| 420 | + if pet_specifier: |
| 421 | + pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:] |
| 422 | + |
| 423 | + # split pets into type and color |
| 424 | + pet_summaries = defaultdict(dict) |
| 425 | + for pet in user['items']['pets']: |
| 426 | + (pettype, color) = pet.split('-') |
| 427 | + pet_summaries[pettype][color] = user['items']['pets'][pet] |
| 428 | + |
| 429 | + for pet in pet_summaries: |
| 430 | + if pet_specifier and pet != pet_specifier: |
| 431 | + continue |
| 432 | + pet_printed = False |
| 433 | + for color in pet_summaries[pet]: |
| 434 | + if color_specifier and color != color_specifier: |
| 435 | + continue |
| 436 | + |
| 437 | + if not pet_printed: |
| 438 | + print(f' {pet}:') |
| 439 | + pet_printed = True |
| 440 | + |
| 441 | + pet_full_level = pet_summaries[pet][color] |
| 442 | + if pet_full_level == -1: |
| 443 | + full_percentage = colors.red | _('No Pet') |
| 444 | + if self.is_hatchable(user, pet, color): |
| 445 | + full_percentage += ' ' + (colors.green | _('(hatchable)')) |
| 446 | + elif pet + '-' + color in user['items']['mounts']: |
| 447 | + full_percentage = colors.green | '100%' |
| 448 | + else: |
| 449 | + full_percentage = self.get_full_percent(pet_full_level) + '%' |
| 450 | + if full_percentage == '100%': |
| 451 | + full_percentage = colors.green | full_percentage |
| 452 | + else: |
| 453 | + full_percentage = colors.yellow | full_percentage |
| 454 | + print(f' {color:<30} {full_percentage}') |
| 455 | + |
| 456 | + |
| 457 | +@Pets.subcommand('feed') |
| 458 | +class FeedPet(Pets): |
| 459 | + """Feeds a pet or pets with specified food.""" |
| 460 | + sleep_time = cli.SwitchAttr( |
| 461 | + ['-S', '--sleep-time'], argtype=int, default=1, |
| 462 | + help=_('Time to wait between feeding each pet to avoid overloading the server')) # pylint: disable=line-too-long |
| 463 | + maximum_food = cli.SwitchAttr( |
| 464 | + ['-M', '--maxmimum-food'], argtype=int, default=10, |
| 465 | + help=_('Maximum amount of food to feed a pet') |
| 466 | + ) |
| 467 | + |
| 468 | + def main(self, *food): |
| 469 | + super().main() |
| 470 | + if len(food) != 1: |
| 471 | + self.log.error(_('error: must specify one food to feed.')) # noqa: Q000 |
| 472 | + return |
| 473 | + |
| 474 | + food = food[0] |
| 475 | + user = self.api.user.get() |
| 476 | + pets = user['items']['pets'] |
| 477 | + mounts = user['items']['mounts'] |
| 478 | + |
| 479 | + color_specifier = self.color_specifier |
| 480 | + if color_specifier: |
| 481 | + color_specifier = color_specifier[0].capitalize() + color_specifier[1:] |
| 482 | + pet_specifier = self.pet_specifier |
| 483 | + if pet_specifier: |
| 484 | + pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:] |
| 485 | + |
| 486 | + for pet in pets: |
| 487 | + (pettype, color) = pet.split('-') |
| 488 | + if pet_specifier and pettype != pet_specifier: |
| 489 | + continue |
| 490 | + if color_specifier and color != color_specifier: |
| 491 | + continue |
| 492 | + |
| 493 | + food_needed = self.get_food_needed(pets[pet]) |
| 494 | + if food_needed > 0 and pet not in mounts: |
| 495 | + food_amount = min(food_needed, self.maximum_food) |
| 496 | + print(_(f'feeding {food_amount} {food} to {color} {pettype}')) |
| 497 | + response = self.api.user.feed[pet][food].post(uri_params={ |
| 498 | + 'amount': food_amount, |
| 499 | + }) |
| 500 | + print(_(f' new fullness: {self.get_full_percent(response)}%')) |
| 501 | + time.sleep(self.sleep_time) |
| 502 | + else: |
| 503 | + print(_(f'NOT feeding {color} {pettype}')) |
| 504 | + |
| 505 | + |
| 506 | +@Pets.subcommand('hatch') |
| 507 | +class HatchPet(Pets): |
| 508 | + """Hatches pets with eggs when possible.""" |
| 509 | + sleep_time = cli.SwitchAttr( |
| 510 | + ['-S', '--sleep-time'], argtype=int, default=1, |
| 511 | + help=_('Time to wait between feeding each pet to avoid overloading the server')) # pylint: disable=line-too-long |
| 512 | + maximum_food = cli.SwitchAttr( |
| 513 | + ['-M', '--maxmimum-food'], argtype=int, default=10, |
| 514 | + help=_('Maximum amount of food to feed a pet') |
| 515 | + ) |
| 516 | + |
| 517 | + def main(self): |
| 518 | + super().main() |
| 519 | + user = self.api.user.get() |
| 520 | + pets = user['items']['pets'] |
| 521 | + |
| 522 | + color_specifier = self.color_specifier |
| 523 | + if color_specifier: |
| 524 | + color_specifier = color_specifier[0].capitalize() + color_specifier[1:] |
| 525 | + pet_specifier = self.pet_specifier |
| 526 | + if pet_specifier: |
| 527 | + pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:] |
| 528 | + |
| 529 | + for pet in pets: |
| 530 | + (pettype, color) = pet.split('-') |
| 531 | + |
| 532 | + if pet_specifier and pettype != pet_specifier: |
| 533 | + continue |
| 534 | + if color_specifier and color != color_specifier: |
| 535 | + continue |
| 536 | + |
| 537 | + if self.is_hatchable(user, pettype, color): |
| 538 | + print(_(f'hatching {color} {pettype}')) |
| 539 | + self.api.user.hatch[pettype][color].post() |
| 540 | + time.sleep(self.sleep_time) |
| 541 | + else: |
| 542 | + print(_(f'NOT hatching {color} {pettype}')) |
| 543 | + |
| 544 | + |
| 545 | +@HabiticaCli.subcommand('food') |
| 546 | +class Food(ApplicationWithApi): |
| 547 | + """Lists food from the inventory.""" |
| 548 | + DESCRIPTION = _('List inventory food and their quantities available') |
| 549 | + |
| 550 | + def main(self): |
| 551 | + super().main() |
| 552 | + user = self.api.user.get() |
| 553 | + food_list = user['items']['food'] |
| 554 | + food_list_keys = sorted(food_list, key=lambda x: food_list[x]) |
| 555 | + for food in food_list_keys: |
| 556 | + print(f'{food:<30}: {food_list[food]}') |
| 557 | + |
| 558 | + |
366 | 559 | @HabiticaCli.subcommand('habits') |
367 | 560 | class Habits(TasksPrint): # pylint: disable=missing-class-docstring |
368 | 561 | DESCRIPTION = _("List, up and down habit tasks") # noqa: Q000 |
|
0 commit comments