One_Piece_Bounty_Bot / src /model /enums /Notification.py
github-actions[bot]
Automated sync from GitHub
71e71e8
Raw
History Blame Contribute Delete
61 kB
from datetime import datetime
from enum import IntEnum
import resources.Environment as Env
from resources import phrases
from src.utils.LazyPhraseDict import LazyPhraseDict
import src.model.enums.Location as Location
from src.model.BountyLoan import BountyLoan
from src.model.Crew import Crew
from src.model.CrewAbility import CrewAbility
from src.model.DavyBackFight import DavyBackFight
from src.model.DavyBackFightParticipant import DavyBackFightParticipant
from src.model.DevilFruit import DevilFruit
from src.model.DevilFruitTrade import DevilFruitTrade
from src.model.Fight import Fight
from src.model.Game import Game
from src.model.ImpelDownLog import ImpelDownLog
from src.model.Plunder import Plunder
from src.model.Prediction import Prediction
from src.model.PredictionOption import PredictionOption
from src.model.PredictionOptionUser import PredictionOptionUser
from src.model.User import User
from src.model.Warlord import Warlord
from src.model.LegendaryPirate import LegendaryPirate
from src.model.enums.Emoji import Emoji
from src.model.enums.GameStatus import GameStatus
from src.model.enums.Log import Log
from src.model.enums.LogType import LogType
from src.model.enums.ReservedKeyboardKeys import ReservedKeyboardKeys, LogTypeReservedKeys
from src.model.enums.Screen import Screen
from src.model.enums.impel_down.ImpelDownBountyAction import ImpelDownBountyAction
from src.model.enums.impel_down.ImpelDownSentenceType import ImpelDownSentenceType
from src.model.game.GameType import GameType
from src.model.pojo.Keyboard import Keyboard
from src.service.date_service import (
default_datetime_format,
convert_days_to_duration,
get_remaining_time_in_days,
)
from src.service.date_service import get_remaining_duration
from src.service.message_service import (
get_image_preview,
escape_valid_markdown_chars,
mention_markdown_user,
get_message_url,
)
from src.utils.math_utils import format_percentage_value
from src.utils.string_utils import get_belly_formatted
class NotificationCategory(IntEnum):
"""Enum for different categories of notifications"""
CREW = 1
LOCATION = 2
GAME = 3
IMPEL_DOWN = 4
PREDICTION = 5
DELETED_MESSAGE = 6
BOUNTY_GIFT = 7
DEVIL_FRUIT = 8
BOUNTY_LOAN = 9
WARLORD = 10
DAVY_BACK_FIGHT = 11
FIGHT = 12
PLUNDER = 13
LEGENDARY_PIRATE = 14
NOTIFICATION_CATEGORY_DESCRIPTIONS = LazyPhraseDict(
{
NotificationCategory.CREW: "NOTIFICATION_CATEGORY_CREW",
NotificationCategory.LOCATION: "NOTIFICATION_CATEGORY_LOCATION",
NotificationCategory.GAME: "NOTIFICATION_CATEGORY_GAME",
NotificationCategory.IMPEL_DOWN: "NOTIFICATION_CATEGORY_IMPEL_DOWN",
NotificationCategory.PREDICTION: "NOTIFICATION_CATEGORY_PREDICTION",
NotificationCategory.DELETED_MESSAGE: "NOTIFICATION_CATEGORY_DELETED_MESSAGE",
NotificationCategory.BOUNTY_GIFT: "NOTIFICATION_CATEGORY_BOUNTY_GIFT",
NotificationCategory.DEVIL_FRUIT: "NOTIFICATION_CATEGORY_DEVIL_FRUIT",
NotificationCategory.BOUNTY_LOAN: "NOTIFICATION_CATEGORY_BOUNTY_LOAN",
NotificationCategory.WARLORD: "NOTIFICATION_CATEGORY_WARLORD",
NotificationCategory.DAVY_BACK_FIGHT: "NOTIFICATION_CATEGORY_DAVY_BACK_FIGHT",
NotificationCategory.FIGHT: "NOTIFICATION_CATEGORY_FIGHT",
NotificationCategory.PLUNDER: "NOTIFICATION_CATEGORY_PLUNDER",
NotificationCategory.LEGENDARY_PIRATE: "NOTIFICATION_CATEGORY_LEGENDARY_PIRATE",
}
)
class NotificationType(IntEnum):
"""Enum for the different types of notifications."""
CREW_LEAVE = 1
LOCATION_UPDATE = 2
CREW_DISBAND = 3
CREW_DISBAND_WARNING = 4
GAME_TURN = 5
CREW_MEMBER_REMOVE = 6
IMPEL_DOWN_RESTRICTION_PLACED = 7
IMPEL_DOWN_RESTRICTION_REMOVED = 8
PREDICTION_RESULT = 9
PREDICTION_BET_INVALID = 10
DELETED_MESSAGE_ARREST = 11
DELETED_MESSAGE_MUTE = 12
DELETED_MESSAGE_LOCATION = 13
BOUNTY_GIFT_RECEIVED = 14
DEVIL_FRUIT_AWARDED = 15
DEVIL_FRUIT_EXPIRED = 16
DEVIL_FRUIT_REVOKE_WARNING = 17
DEVIL_FRUIT_REVOKE = 18
BOUNTY_LOAN_PAYMENT = 19
BOUNTY_LOAN_FORGIVEN = 20
BOUNTY_LOAN_EXPIRED = 21
WARLORD_APPOINTMENT = 22
WARLORD_REVOCATION = 23
CREW_ABILITY_ACTIVATED = 24
CREW_FIRST_MATE_PROMOTION = 25
CREW_FIRST_MATE_DEMOTION = 26
CREW_JOIN_REQUEST_ACCEPTED = 27
CREW_JOIN_REQUEST_REJECTED = 28
IMPEL_DOWN_BAIL_POSTED = 29
DAVY_BACK_FIGHT_REQUEST_ACCEPTED = 30
DAVY_BACK_FIGHT_REQUEST_REJECTED = 31
DAVY_BACK_FIGHT_START = 32
DAVY_BACK_FIGHT_END = 33
CREW_CONSCRIPTION_START = 34
CREW_CONSCRIPTION_START_CAPTAIN = 35
CREW_CONSCRIPTION_END = 36
DEVIL_FRUIT_SOLD = 37
CREW_CAPTAIN_PROMOTION = 38
FIGHT_ATTACK = 39
PLUNDER_ATTACK = 40
GAME_OUTCOME = 41
LEGENDARY_PIRATE_APPOINTMENT = 42
LEGENDARY_PIRATE_REVOCATION = 43
class Notification:
"""Class for notifications."""
def __init__(
self,
category: NotificationCategory,
notification_type: NotificationType,
text: str,
description: str,
button_text: str,
disable_web_page_preview: bool = True,
disable_notification: bool = True,
item_screen: Screen = None,
item_info: dict = None,
go_to_item_button_text: str = None,
item_previous_screens: list[Screen] = None,
item_button_is_deeplink: bool = False,
keyboard: list[list[Keyboard]] = None,
):
"""
Constructor
:param category: Category of the notification
:param notification_type: The type of notification
:param text: Base text that will be sent
:param description: Description of the notification to be provided in settings
:param button_text: Text for the button to change the notification settings
:param disable_web_page_preview: True if the web page preview should be disabled
:param disable_notification: True if telegram should not notify of the message
:param item_screen: The screen of the item that is related to this notification
:param item_info: The info of the item that is related to this notification
:param go_to_item_button_text: The text for the button to go to the item
:param item_previous_screens: The previous screens of the item
:param item_button_is_deeplink: True if the item button is a deeplink
:param keyboard: The keyboard
"""
self.category = category
self.type = notification_type
self._text_key = text
self._description_key = description
self._button_text_key = button_text
self.disable_web_page_preview = disable_web_page_preview
self.disable_notification = disable_notification
self.item_screen = item_screen
self.item_info = item_info
self._go_to_item_button_text_key = go_to_item_button_text
self.item_previous_screens = item_previous_screens if item_previous_screens else []
self.item_button_is_deeplink = item_button_is_deeplink
self.keyboard = keyboard if keyboard else []
@staticmethod
def _resolve(value: str) -> str:
"""
Resolves a phrases.py constant name to its value in the currently active language.
If the value isn't a known constant name (e.g. it's already resolved text, or an
empty string), it's returned unchanged.
"""
if isinstance(value, str) and hasattr(phrases, value):
return getattr(phrases, value)
return value
@property
def text(self) -> str:
"""Base text that will be sent, resolved in the currently active language"""
return self._resolve(self._text_key)
@text.setter
def text(self, value: str) -> None:
self._text_key = value
@property
def description(self) -> str:
"""Description of the notification, resolved in the currently active language"""
return self._resolve(self._description_key)
@property
def button_text(self) -> str:
"""Text for the button to change the notification settings"""
return self._resolve(self._button_text_key)
@property
def go_to_item_button_text(self) -> str | None:
"""Text for the button to go to the item, resolved in the currently active language"""
if self._go_to_item_button_text_key is None:
return None
return self._resolve(self._go_to_item_button_text_key)
def build(self):
"""Builds the notification."""
return self.text
def get_go_to_item_keyboard(self) -> list[Keyboard]:
"""Gets the go to item keyboard."""
if self.item_screen is None or self.go_to_item_button_text is None:
return []
return [
Keyboard(
self.go_to_item_button_text,
screen=self.item_screen,
info=self.item_info,
previous_screen_list=self.item_previous_screens,
from_deeplink=self.item_button_is_deeplink,
)
]
def get_keyboard(self) -> list[list[Keyboard]]:
"""Gets the keyboard."""
return self.keyboard + [self.get_go_to_item_keyboard()]
class CrewLeaveNotification(Notification):
"""Class for crew leave notifications."""
def __init__(self, crew_member: User = None):
"""
:param crew_member: Member of the crew that left
"""
self.crew_member = crew_member
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_LEAVE,
"CREW_LEAVE_NOTIFICATION",
"CREW_LEAVE_NOTIFICATION_DESCRIPTION",
"CREW_LEAVE_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification."""
return self.text.format(mention_markdown_user(self.crew_member))
class LocationUpdateNotification(Notification):
"""Class for location update notifications."""
def __init__(self, user: User = None, location: Location = None):
"""
:param user: User
:param location: The new location
"""
self.user = user
self.location = location
super().__init__(
NotificationCategory.LOCATION,
NotificationType.LOCATION_UPDATE,
"LOCATION_UPDATE_NOTIFICATION",
"LOCATION_UPDATE_NOTIFICATION_DESCRIPTION",
"LOCATION_UPDATE_NOTIFICATION_KEY",
disable_web_page_preview=False,
)
def build(self) -> str:
"""Builds the notification."""
from src.utils.string_utils import get_belly_formatted
location: Location = self.location
# Determine preposition to use for the location
if "island" in location.name.lower() or "archipelago" in location.name.lower():
preposition = "on"
if location.name.lower().startswith("island"):
preposition += " the"
else:
preposition = "in"
# Determine text suffix
try:
next_location = Location.get_by_level(location.level + 1)
text_suffix = phrases.LOCATION_NEXT_LEVEL_REQUIREMENT.format(
get_belly_formatted(next_location.required_bounty)
)
except ValueError:
text_suffix = phrases.LOCATION_CURRENT_LEVEL_MAX
return self.text.format(
get_image_preview(location.image_url),
mention_markdown_user(self.user),
preposition,
escape_valid_markdown_chars(location.name),
text_suffix,
)
class CrewDisbandNotification(Notification):
"""Class for crew disband notifications."""
def __init__(self):
"""Constructor"""
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_DISBAND,
"CREW_DISBAND_NOTIFICATION",
"CREW_DISBAND_NOTIFICATION_DESCRIPTION",
"CREW_DISBAND_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification."""
return self.text
class CrewDisbandWarningNotification(Notification):
"""Class for crew disband warning notifications."""
def __init__(self):
"""Constructor"""
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_DISBAND_WARNING,
"CREW_DISBAND_WARNING_NOTIFICATION",
"CREW_DISBAND_WARNING_NOTIFICATION_DESCRIPTION",
"CREW_DISBAND_WARNING_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification."""
from src.service.bounty_service import get_duration_to_next_bounty_reset
return self.text.format(get_duration_to_next_bounty_reset())
class GameTurnNotification(Notification):
"""Class for game turn notifications."""
def __init__(self, game: Game = None, opponent: User = None):
"""Constructor
:param game: The game
:param opponent: The opponent whose turn it's not
"""
self.game = game
self.opponent = opponent
super().__init__(
NotificationCategory.GAME,
NotificationType.GAME_TURN,
"GAME_TURN_NOTIFICATION",
"GAME_TURN_NOTIFICATION_DESCRIPTION",
"GAME_TURN_NOTIFICATION_KEY",
disable_notification=False,
)
def build(self) -> str:
"""Builds the notification."""
from src.service.game_service import get_auto_move_warning
return self.text.format(
GameType(self.game.type).get_name(),
mention_markdown_user(self.opponent),
get_message_url(self.game.message_id, self.game.group_chat),
get_auto_move_warning(),
)
class CrewMemberRemoveNotification(Notification):
"""Class for crew member remove notifications."""
def __init__(self, crew_member: User = None):
"""Constructor
:param crew_member: The crew member that was removed
"""
self.crew_member = crew_member
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_MEMBER_REMOVE,
"CREW_MEMBER_REMOVE_NOTIFICATION",
"CREW_MEMBER_REMOVE_NOTIFICATION_DESCRIPTION",
"CREW_MEMBER_REMOVE_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification"""
return self.text.format(mention_markdown_user(self.crew_member))
class ImpelDownNotificationRestrictionPlaced(Notification):
"""Class for impel down restriction place notifications."""
def __init__(
self,
sentence_type: ImpelDownSentenceType = None,
release_date_time: datetime = None,
bounty_action: ImpelDownBountyAction = None,
reason: str = None,
impel_down_log: ImpelDownLog = None,
):
"""
Constructor
:param sentence_type: The sentence type
:param release_date_time: The release date time
:param bounty_action: The bounty action
:param reason: The reason
:param impel_down_log: The impel down log
"""
from src.service.impel_down_service import get_post_bail_deeplink_button
self.sentence_type = sentence_type
self.release_date_time = release_date_time
self.bounty_action = bounty_action
self.reason = reason
self.impel_down_log = impel_down_log
post_bail_button = get_post_bail_deeplink_button(impel_down_log)
keyboard = [[post_bail_button]] if post_bail_button is not None else []
super().__init__(
NotificationCategory.IMPEL_DOWN,
NotificationType.IMPEL_DOWN_RESTRICTION_PLACED,
"IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION",
"IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_DESCRIPTION",
"IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_KEY",
keyboard=keyboard,
)
def build(self) -> str:
"""Builds the notification"""
restriction_text = ""
duration_text = ""
bail_text = ""
# Bounty action
if self.bounty_action is ImpelDownBountyAction.HALVE:
restriction_text += phrases.IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_BOUNTY_HALVED
elif self.bounty_action is ImpelDownBountyAction.ERASE:
restriction_text += phrases.IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_BOUNTY_ERASED
# Sentence type
if self.sentence_type is not ImpelDownSentenceType.NONE:
restriction_text += phrases.IMPEL_DOWN_RESTRICTIONS
duration_text = phrases.IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_DURATION.format(
get_remaining_duration(self.release_date_time)
if self.sentence_type is ImpelDownSentenceType.TEMPORARY
else phrases.IMPEL_DOWN_RESTRICTION_PLACED_NOTIFICATION_DURATION_PERMANENT
)
if self.sentence_type is ImpelDownSentenceType.TEMPORARY:
bail_text = phrases.IMPEL_DOWN_RESTRICTION_BAIL_GUIDE
return self.text.format(
escape_valid_markdown_chars(self.reason), restriction_text, duration_text, bail_text
)
class ImpelDownNotificationRestrictionRemoved(Notification):
"""Class for impel down restriction remove notifications."""
def __init__(self):
"""Constructor"""
super().__init__(
NotificationCategory.IMPEL_DOWN,
NotificationType.IMPEL_DOWN_RESTRICTION_REMOVED,
"IMPEL_DOWN_RESTRICTION_REMOVED_NOTIFICATION",
"IMPEL_DOWN_RESTRICTION_REMOVED_NOTIFICATION_DESCRIPTION",
"IMPEL_DOWN_RESTRICTION_REMOVED_NOTIFICATION_KEY",
)
class PredictionResultNotification(Notification):
"""Class for prediction result notifications."""
def __init__(
self,
prediction: Prediction = None,
prediction_options: list[PredictionOption] = None,
correct_prediction_options: list[PredictionOption] = None,
total_win: int = None,
user: User = None,
):
"""
Constructor
:param prediction: The prediction
:param prediction_options: The prediction options that were chosen by the user
:param correct_prediction_options: The correct prediction options
:param total_win: The total win
:param user: The user
"""
self.prediction = prediction
self.prediction_options = prediction_options
self.correct_prediction_options = correct_prediction_options
self.total_win = total_win
self.user = user
super().__init__(
NotificationCategory.PREDICTION,
NotificationType.PREDICTION_RESULT,
"PREDICTION_RESULT_NOTIFICATION",
"PREDICTION_RESULT_NOTIFICATION_DESCRIPTION",
"PREDICTION_RESULT_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification"""
from src.utils.string_utils import get_belly_formatted
from src.service.prediction_service import get_max_wager_refund
# Result text
result_text = phrases.TEXT_WON if self.total_win >= 0 else phrases.TEXT_LOST
# User prediction options
user_prediction_options_list = ""
for option in self.prediction_options:
option_emoji = Emoji.LOG_POSITIVE if option.is_correct else Emoji.LOG_NEGATIVE
user_prediction_options_list += phrases.PREDICTION_RESULT_NOTIFICATION_OPTION.format(
option_emoji, escape_valid_markdown_chars(option.option)
)
phrase_to_use = (
phrases.PREDICTION_RESULT_NOTIFICATION_YOUR_OPTION
if len(self.prediction_options) == 1
else phrases.PREDICTION_RESULT_NOTIFICATION_YOUR_OPTIONS
)
user_prediction_options_text = phrase_to_use.format(user_prediction_options_list)
prediction_has_correct_options = len(self.correct_prediction_options) > 0
# Correct prediction options
prediction_has_multiple_options = (
len(self.correct_prediction_options) > 0 or len(self.prediction_options) > 0
)
user_got_the_only_correct_option = (
len(self.correct_prediction_options) == 1
and len([option for option in self.prediction_options if option.is_correct]) == 1
)
# Show correct options list if:
# - Prediction has at least one correct option
# - Prediction has multiple options or didn't get the only correct option
correct_prediction_options_text = ""
if prediction_has_correct_options and (
prediction_has_multiple_options and not user_got_the_only_correct_option
):
correct_prediction_options_list = ""
for option in self.correct_prediction_options:
correct_prediction_options_list += (
phrases.PREDICTION_RESULT_NOTIFICATION_OPTION_NO_EMOJI.format(
escape_valid_markdown_chars(option.option)
)
)
correct_prediction_options_text = (
phrases.PREDICTION_RESULT_NOTIFICATION_CORRECT_OPTIONS.format(
correct_prediction_options_list
)
)
# Wager refunded notice
wager_refunded_text = ""
if not prediction_has_correct_options:
wager_refunded_text = (
phrases.PREDICTION_RESULT_NOTIFICATION_WAGER_REFUNDED_NO_CORRECT_OPTIONS
)
elif self.prediction.refund_wager:
max_refund_wager = get_max_wager_refund(prediction=self.prediction, user=self.user)
# User lost more than the refundable amount
if self.total_win < 0 and abs(self.total_win) > max_refund_wager: #
wager_refunded_text = (
phrases.PREDICTION_RESULT_NOTIFICATION_WAGER_REFUNDED_PARTIAL.format(
get_belly_formatted(max_refund_wager)
)
)
else:
wager_refunded_text = phrases.PREDICTION_RESULT_NOTIFICATION_WAGER_REFUNDED
return self.text.format(
result_text,
get_belly_formatted(abs(self.total_win)),
escape_valid_markdown_chars(self.prediction.question),
user_prediction_options_text,
correct_prediction_options_text,
wager_refunded_text,
)
class PredictionBetInvalidNotification(Notification):
"""Class for invalid prediction bet notifications."""
def __init__(
self,
prediction: Prediction = None,
prediction_options_user: list[PredictionOptionUser] = None,
total_refund: int = None,
):
"""
Constructor
:param prediction: The prediction
:param prediction_options_user: The invalid prediction options that were chosen by the user
:param total_refund: The total refund
"""
self.prediction = prediction
self.prediction_options_user = prediction_options_user
self.total_refund = total_refund
super().__init__(
NotificationCategory.PREDICTION,
NotificationType.PREDICTION_BET_INVALID,
"PREDICTION_BET_INVALID_NOTIFICATION",
"PREDICTION_BET_INVALID_NOTIFICATION_DESCRIPTION",
"PREDICTION_BET_INVALID_NOTIFICATION_KEY",
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
if len(self.prediction_options_user) == 1:
bet_has = phrases.PREDICTION_BET_INVALID_BET_HAS
it_was = phrases.TEXT_IT_WAS
else:
bet_has = phrases.PREDICTION_BET_INVALID_BETS_HAVE
it_was = phrases.TEXT_THEY_WERE
invalid_prediction_options_list = ""
for prediction_option_user in self.prediction_options_user:
prediction_option: PredictionOption = prediction_option_user.prediction_option
invalid_prediction_options_list += (
phrases.PREDICTION_BET_INVALID_NOTIFICATION_OPTION.format(
escape_valid_markdown_chars(prediction_option.option),
default_datetime_format(prediction_option_user.date),
)
)
return self.text.format(
bet_has,
escape_valid_markdown_chars(self.prediction.question),
it_was,
default_datetime_format(self.prediction.cut_off_date),
invalid_prediction_options_list,
get_belly_formatted(self.total_refund),
)
class DeletedMessageArrestNotification(Notification):
"""Class for deleted messages because user is arrested notifications."""
def __init__(self):
"""Constructor"""
super().__init__(
NotificationCategory.DELETED_MESSAGE,
NotificationType.DELETED_MESSAGE_ARREST,
"DELETED_MESSAGE_ARREST_NOTIFICATION",
"DELETED_MESSAGE_ARREST_NOTIFICATION_DESCRIPTION",
"DELETED_MESSAGE_ARREST_NOTIFICATION_KEY",
disable_notification=False,
)
class DeletedMessageMuteNotification(Notification):
"""Class for deleted messages because user is muted notifications."""
def __init__(self):
"""Constructor"""
super().__init__(
NotificationCategory.DELETED_MESSAGE,
NotificationType.DELETED_MESSAGE_MUTE,
"DELETED_MESSAGE_MUTE_NOTIFICATION",
"DELETED_MESSAGE_MUTE_NOTIFICATION_DESCRIPTION",
"DELETED_MESSAGE_MUTE_NOTIFICATION_KEY",
disable_notification=False,
)
class DeletedMessageLocationNotification(Notification):
"""
Class for deleted messages because user has not reached the required location notifications.
"""
def __init__(self, user: User = None, required_location_level: int = None):
"""
Constructor
:param user: The user
:param required_location_level: The required location level
"""
self.user = user
self.required_location_level = required_location_level
super().__init__(
NotificationCategory.DELETED_MESSAGE,
NotificationType.DELETED_MESSAGE_LOCATION,
"DELETED_MESSAGE_LOCATION_NOTIFICATION",
"DELETED_MESSAGE_LOCATION_NOTIFICATION_DESCRIPTION",
"DELETED_MESSAGE_LOCATION_NOTIFICATION_KEY",
disable_notification=False,
)
def build(self) -> str:
current_location: Location = Location.get_by_level(self.user.location_level)
required_location: Location = Location.get_by_level(self.required_location_level)
return self.text.format(
escape_valid_markdown_chars(current_location.name),
escape_valid_markdown_chars(required_location.name),
)
class BountyGiftReceivedNotification(Notification):
"""Class for bounty gift received notifications."""
def __init__(self, sender: User = None, amount: int = None):
"""
Constructor
:param sender: The sender
:param amount: The amount
"""
self.sender = sender
self.amount = amount
super().__init__(
NotificationCategory.BOUNTY_GIFT,
NotificationType.BOUNTY_GIFT_RECEIVED,
"BOUNTY_GIFT_RECEIVED_NOTIFICATION",
"BOUNTY_GIFT_RECEIVED_NOTIFICATION_DESCRIPTION",
"BOUNTY_GIFT_RECEIVED_NOTIFICATION_KEY",
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
return self.text.format(
get_belly_formatted(self.amount), self.sender.get_markdown_mention()
)
class DevilFruitAwardedNotification(Notification):
"""Class for Devil Fruit awarded notifications."""
def __init__(self, devil_fruit: DevilFruit = None, reason: str = None):
"""
Constructor
:param devil_fruit: The Devil Fruit
:param reason: The reason
"""
self.devil_fruit = devil_fruit
self.reason = reason
item_id = devil_fruit.id if devil_fruit is not None else None
super().__init__(
NotificationCategory.DEVIL_FRUIT,
NotificationType.DEVIL_FRUIT_AWARDED,
"DEVIL_FRUIT_AWARDED_NOTIFICATION",
"DEVIL_FRUIT_AWARDED_NOTIFICATION_DESCRIPTION",
"DEVIL_FRUIT_AWARDED_NOTIFICATION_KEY",
item_screen=Screen.PVT_DEVIL_FRUIT_DETAIL,
item_info={ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id},
go_to_item_button_text="KEY_MANAGE_DEVIL_FRUIT",
)
def build(self) -> str:
return self.text.format(
escape_valid_markdown_chars(self.devil_fruit.get_full_name()),
escape_valid_markdown_chars(self.reason),
)
class DevilFruitExpiredNotification(Notification):
"""Class for Devil Fruit expired notifications."""
def __init__(self, devil_fruit: DevilFruit = None):
"""
Constructor
:param devil_fruit: The Devil Fruit
"""
self.devil_fruit = devil_fruit
super().__init__(
NotificationCategory.DEVIL_FRUIT,
NotificationType.DEVIL_FRUIT_EXPIRED,
"DEVIL_FRUIT_EXPIRED_NOTIFICATION",
"DEVIL_FRUIT_EXPIRED_NOTIFICATION_DESCRIPTION",
"DEVIL_FRUIT_EXPIRED_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(escape_valid_markdown_chars(self.devil_fruit.get_full_name()))
class DevilFruitRevokeNotification(Notification):
"""Class for Devil Fruit revoke notifications."""
def __init__(self, devil_fruit: DevilFruit = None):
"""
Constructor
:param devil_fruit: The Devil Fruit
"""
self.devil_fruit = devil_fruit
super().__init__(
NotificationCategory.DEVIL_FRUIT,
NotificationType.DEVIL_FRUIT_REVOKE,
"DEVIL_FRUIT_REVOKE_NOTIFICATION",
"DEVIL_FRUIT_REVOKE_NOTIFICATION_DESCRIPTION",
"DEVIL_FRUIT_REVOKE_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification."""
return self.text.format(escape_valid_markdown_chars(self.devil_fruit.get_full_name()))
class DevilFruitRevokeWarningNotification(Notification):
"""Class for crew disband warning notifications."""
def __init__(self, devil_fruit: DevilFruit = None):
"""
Constructor
:param devil_fruit: The Devil Fruit
"""
self.devil_fruit = devil_fruit
super().__init__(
NotificationCategory.DEVIL_FRUIT,
NotificationType.DEVIL_FRUIT_REVOKE_WARNING,
"DEVIL_FRUIT_REVOKE_WARNING_NOTIFICATION",
"DEVIL_FRUIT_REVOKE_WARNING_NOTIFICATION_DESCRIPTION",
"DEVIL_FRUIT_REVOKE_WARNING_NOTIFICATION_KEY",
)
def build(self) -> str:
"""Builds the notification."""
from src.model.Leaderboard import Leaderboard
from src.service.message_service import get_message_url
leaderboard_url = ""
latest_global_leaderboards = Leaderboard.get_latest_n(1, group=None)
if latest_global_leaderboards:
leaderboard_url = get_message_url(
message_id=latest_global_leaderboards[0].message_id,
chat_id=Env.UPDATES_CHAT_ID.get(),
chat_username=Env.UPDATES_CHAT_USERNAME.get_or_none(),
)
return self.text.format(
leaderboard_url, escape_valid_markdown_chars(self.devil_fruit.get_full_name())
)
class BountyLoanPaymentNotification(Notification):
"""Class for bounty loan payment notifications."""
def __init__(self, loan: BountyLoan = None, amount: int = None):
"""
Constructor
:param loan: The loan
:param amount: The amount
"""
self.loan = loan
self.amount = amount
item_id = loan.id if loan is not None else None
super().__init__(
NotificationCategory.BOUNTY_LOAN,
NotificationType.BOUNTY_LOAN_PAYMENT,
"BOUNTY_LOAN_PAYMENT_NOTIFICATION",
"BOUNTY_LOAN_PAYMENT_NOTIFICATION_DESCRIPTION",
"BOUNTY_LOAN_PAYMENT_NOTIFICATION_KEY",
item_screen=Screen.PVT_BOUNTY_LOAN_DETAIL,
item_info={ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id},
go_to_item_button_text=(
"BOUNTY_LOAN_NOTIFICATION_GO_TO_ITEM_BUTTON_TEXT"
),
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
borrower: User = self.loan.borrower
return self.text.format(get_belly_formatted(self.amount), borrower.get_markdown_mention())
class BountyLoanForgivenNotification(Notification):
"""Class for bounty loan forgiven notifications."""
def __init__(self, loan: BountyLoan = None):
"""
Constructor
:param loan: The loan
"""
self.loan = loan
item_id = loan.id if loan is not None else None
super().__init__(
NotificationCategory.BOUNTY_LOAN,
NotificationType.BOUNTY_LOAN_FORGIVEN,
"BOUNTY_LOAN_FORGIVEN_NOTIFICATION",
"BOUNTY_LOAN_FORGIVEN_NOTIFICATION_DESCRIPTION",
"BOUNTY_LOAN_FORGIVEN_NOTIFICATION_KEY",
item_screen=Screen.PVT_BOUNTY_LOAN_DETAIL,
item_info={ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id},
go_to_item_button_text=(
"BOUNTY_LOAN_NOTIFICATION_GO_TO_ITEM_BUTTON_TEXT"
),
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
loaner: User = self.loan.loaner
return self.text.format(
get_belly_formatted(self.loan.amount), loaner.get_markdown_mention()
)
class BountyLoanExpiredNotification(Notification):
"""Class for bounty loan expired notifications."""
def __init__(self, loan: BountyLoan = None):
"""
Constructor
:param loan: The loan
"""
self.loan = loan
item_id = loan.id if loan is not None else None
super().__init__(
NotificationCategory.BOUNTY_LOAN,
NotificationType.BOUNTY_LOAN_EXPIRED,
"BOUNTY_LOAN_EXPIRED_NOTIFICATION",
"BOUNTY_LOAN_EXPIRED_NOTIFICATION_DESCRIPTION",
"BOUNTY_LOAN_EXPIRED_NOTIFICATION_KEY",
item_screen=Screen.PVT_BOUNTY_LOAN_DETAIL,
item_info={ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id},
go_to_item_button_text=(
"BOUNTY_LOAN_NOTIFICATION_GO_TO_ITEM_BUTTON_TEXT"
),
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
loaner: User = self.loan.loaner
return self.text.format(
get_belly_formatted(self.loan.amount),
loaner.get_markdown_mention(),
Env.BOUNTY_LOAN_GARNISH_PERCENTAGE.get(),
loaner.get_markdown_mention(),
)
class WarlordAppointmentNotification(Notification):
"""Class for warlord appointment notifications."""
def __init__(self, warlord: Warlord = None, days: int = None):
"""
Constructor
:param warlord: The warlord
"""
self.warlord = warlord
self.days = days
super().__init__(
NotificationCategory.WARLORD,
NotificationType.WARLORD_APPOINTMENT,
"WARLORD_APPOINTMENT_NOTIFICATION",
"WARLORD_APPOINTMENT_NOTIFICATION_DESCRIPTION",
"WARLORD_APPOINTMENT_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(
escape_valid_markdown_chars(self.warlord.epithet),
convert_days_to_duration(self.days),
escape_valid_markdown_chars(self.warlord.reason),
)
class WarlordRevocationNotification(Notification):
"""Class for warlord revocation notifications."""
def __init__(self, warlord: Warlord = None):
"""
Constructor
:param warlord: The warlord
"""
self.warlord = warlord
super().__init__(
NotificationCategory.WARLORD,
NotificationType.WARLORD_REVOCATION,
"WARLORD_REVOCATION_NOTIFICATION",
"WARLORD_REVOCATION_NOTIFICATION_DESCRIPTION",
"WARLORD_REVOCATION_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(escape_valid_markdown_chars(self.warlord.revoke_reason))
class LegendaryPirateAppointmentNotification(Notification):
"""Class for legendary pirate appointment notifications."""
def __init__(
self,
legendary_pirate: "LegendaryPirate" = None,
days: int = None,
is_permanent: bool = False,
):
"""
Constructor
:param legendary_pirate: The legendary pirate
:param days: The appointment duration in days
:param is_permanent: Whether the appointment is permanent
"""
self.legendary_pirate = legendary_pirate
self.days = days
self.is_permanent = is_permanent or (
legendary_pirate is not None and legendary_pirate.is_permanent
)
super().__init__(
NotificationCategory.LEGENDARY_PIRATE,
NotificationType.LEGENDARY_PIRATE_APPOINTMENT,
"LEGENDARY_PIRATE_APPOINTMENT_NOTIFICATION",
"LEGENDARY_PIRATE_APPOINTMENT_NOTIFICATION_DESCRIPTION",
"LEGENDARY_PIRATE_APPOINTMENT_NOTIFICATION_KEY",
)
def build(self) -> str:
if self.is_permanent or self.legendary_pirate.end_date is None:
duration_text = "Permanent"
else:
days = self.days
if days is None:
days = get_remaining_time_in_days(
self.legendary_pirate.end_date, self.legendary_pirate.date
)
duration_text = convert_days_to_duration(days)
text = self.text.format(
escape_valid_markdown_chars(self.legendary_pirate.epithet),
duration_text,
escape_valid_markdown_chars(self.legendary_pirate.reason),
)
if self.is_permanent or self.legendary_pirate.end_date is None:
text += phrases.LEGENDARY_PIRATE_APPOINTMENT_NOTIFICATION_PERMANENT_FOOTER
return text
class LegendaryPirateRevocationNotification(Notification):
"""Class for legendary pirate revocation notifications."""
def __init__(self, legendary_pirate: "LegendaryPirate" = None):
"""
Constructor
:param legendary_pirate: The legendary pirate
"""
self.legendary_pirate = legendary_pirate
super().__init__(
NotificationCategory.LEGENDARY_PIRATE,
NotificationType.LEGENDARY_PIRATE_REVOCATION,
"LEGENDARY_PIRATE_REVOCATION_NOTIFICATION",
"LEGENDARY_PIRATE_REVOCATION_NOTIFICATION_DESCRIPTION",
"LEGENDARY_PIRATE_REVOCATION_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(escape_valid_markdown_chars(self.legendary_pirate.revoke_reason))
class CrewAbilityActivatedNotification(Notification):
"""Class for crew ability activated notifications."""
def __init__(self, crew_ability: CrewAbility = None):
"""
Constructor
:param crew_ability: The crew ability
"""
self.crew_ability = crew_ability
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_ABILITY_ACTIVATED,
"CREW_ABILITY_ACTIVATED_NOTIFICATION",
"CREW_ABILITY_ACTIVATED_NOTIFICATION_DESCRIPTION",
"CREW_ABILITY_ACTIVATED_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(
escape_valid_markdown_chars(self.crew_ability.get_description()),
self.crew_ability.get_value_with_sign(),
get_remaining_duration(self.crew_ability.expiration_date, self.crew_ability.date),
)
class CrewFirstMatePromotionNotification(Notification):
"""Class for crew first mate promotion notifications."""
def __init__(self):
"""
Constructor
"""
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_FIRST_MATE_PROMOTION,
"CREW_FIRST_MATE_PROMOTION_NOTIFICATION",
"CREW_FIRST_MATE_PROMOTION_NOTIFICATION_DESCRIPTION",
"CREW_FIRST_MATE_PROMOTION_NOTIFICATION_KEY",
)
class CrewFirstMateDemotionNotification(Notification):
"""Class for crew first mate demotion notifications."""
def __init__(self):
"""
Constructor
"""
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_FIRST_MATE_DEMOTION,
"CREW_FIRST_MATE_DEMOTION_NOTIFICATION",
"CREW_FIRST_MATE_DEMOTION_NOTIFICATION_DESCRIPTION",
"CREW_FIRST_MATE_DEMOTION_NOTIFICATION_KEY",
)
class CrewJoinRequestAcceptedNotification(Notification):
"""Class for crew join request accepted notifications."""
def __init__(self, crew: Crew = None):
"""
Constructor
"""
self.crew: Crew = crew
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_JOIN_REQUEST_ACCEPTED,
"CREW_JOIN_REQUEST_ACCEPTED_NOTIFICATION",
"CREW_JOIN_REQUEST_ACCEPTED_NOTIFICATION_DESCRIPTION",
"CREW_JOIN_REQUEST_ACCEPTED_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW,
go_to_item_button_text="KEY_VIEW",
)
def build(self) -> str:
return self.text.format(self.crew.get_name_with_deeplink())
class CrewJoinRequestRejectedNotification(Notification):
"""Class for crew join request rejected notifications."""
def __init__(self, crew: Crew = None):
"""
Constructor
"""
self.crew: Crew = crew
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_JOIN_REQUEST_REJECTED,
"CREW_JOIN_REQUEST_REJECTED_NOTIFICATION",
"CREW_JOIN_REQUEST_REJECTED_NOTIFICATION_DESCRIPTION",
"CREW_JOIN_REQUEST_REJECTED_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(self.crew.get_name_with_deeplink())
class CrewConscriptionStartNotification(Notification):
"""Class for crew conscription start notifications."""
def __init__(self, user: User = None):
"""
Constructor
"""
self.user: User = user
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_CONSCRIPTION_START,
"CREW_CONSCRIPTION_START_NOTIFICATION",
"CREW_CONSCRIPTION_START_NOTIFICATION_DESCRIPTION",
"CREW_CONSCRIPTION_START_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW,
go_to_item_button_text="KEY_VIEW",
)
def build(self) -> str:
return self.text.format(
self.user.crew.get_name_with_deeplink(), self.user.get_conscription_remaining_time()
)
class CrewConscriptionStartCaptainNotification(Notification):
"""Class for crew conscription start captain notifications."""
def __init__(self, user: User = None):
"""
Constructor
"""
self.user: User = user
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_CONSCRIPTION_START_CAPTAIN,
"CREW_CONSCRIPTION_START_CAPTAIN_NOTIFICATION",
"CREW_CONSCRIPTION_START_CAPTAIN_NOTIFICATION_DESCRIPTION",
"CREW_CONSCRIPTION_START_CAPTAIN_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(
self.user.get_markdown_mention(),
self.user.crew.get_name_with_deeplink(),
self.user.get_conscription_remaining_time(),
)
class CrewConscriptionEndNotification(Notification):
"""Class for crew conscription end notifications."""
def __init__(self, user: User = None):
"""
Constructor
"""
self.user: User = user
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_CONSCRIPTION_END,
"CREW_CONSCRIPTION_END_NOTIFICATION",
"CREW_CONSCRIPTION_END_NOTIFICATION_DESCRIPTION",
"CREW_CONSCRIPTION_END_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW,
go_to_item_button_text="KEY_VIEW",
)
def build(self) -> str:
return self.text.format(self.user.crew.get_name_with_deeplink())
class DavyBackFightRequestAcceptedNotification(Notification):
"""Class for davy back fight request accepted notifications."""
def __init__(self, crew: Crew = None, davy_back_fight: DavyBackFight = None):
"""
Constructor
"""
self.crew: Crew = crew
item_id = davy_back_fight.id if davy_back_fight is not None else None
super().__init__(
NotificationCategory.DAVY_BACK_FIGHT,
NotificationType.DAVY_BACK_FIGHT_REQUEST_ACCEPTED,
"DAVY_BACK_FIGHT_REQUEST_ACCEPTED_NOTIFICATION",
"DAVY_BACK_FIGHT_REQUEST_ACCEPTED_NOTIFICATION_DESCRIPTION",
"DAVY_BACK_FIGHT_REQUEST_ACCEPTED_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW_DAVY_BACK_FIGHT_DETAIL,
item_info={
ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id,
ReservedKeyboardKeys.DIRECT_ITEM: False,
},
go_to_item_button_text="KEY_MANAGE",
item_previous_screens=[Screen.PVT_CREW],
)
def build(self) -> str:
return self.text.format(self.crew.get_name_with_deeplink())
class DavyBackFightRequestRejectedNotification(Notification):
"""Class for davy back fight request rejected notifications."""
def __init__(self, crew: Crew = None):
"""
Constructor
"""
self.crew: Crew = crew
super().__init__(
NotificationCategory.DAVY_BACK_FIGHT,
NotificationType.DAVY_BACK_FIGHT_REQUEST_REJECTED,
"CREW_DAVY_BACK_FIGHT_REQUEST_REJECTED_NOTIFICATION",
"DAVY_BACK_FIGHT_REQUEST_REJECTED_NOTIFICATION_DESCRIPTION",
"DAVY_BACK_FIGHT_REQUEST_REJECTED_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(self.crew.get_name_with_deeplink())
class DavyBackFightStartNotification(Notification):
"""Class for davy back fight start notifications."""
def __init__(self, opponent_crew: Crew = None, davy_back_fight: DavyBackFight = None):
"""
Constructor
"""
self.opponent_crew: Crew = opponent_crew
self.davy_back_fight: DavyBackFight = davy_back_fight
item_id = davy_back_fight.id if davy_back_fight is not None else None
super().__init__(
NotificationCategory.DAVY_BACK_FIGHT,
NotificationType.DAVY_BACK_FIGHT_START,
"DAVY_BACK_FIGHT_START_NOTIFICATION",
"DAVY_BACK_FIGHT_START_NOTIFICATION_DESCRIPTION",
"DAVY_BACK_FIGHT_START_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW_DAVY_BACK_FIGHT_DETAIL,
item_info={
ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id,
ReservedKeyboardKeys.DIRECT_ITEM: False,
},
go_to_item_button_text="KEY_VIEW",
item_previous_screens=[Screen.PVT_CREW],
)
def build(self) -> str:
return self.text.format(
self.opponent_crew.get_name_with_deeplink(),
self.davy_back_fight.get_remaining_time(),
)
class DavyBackFightEndNotification(Notification):
"""Class for davy back fight end notifications."""
def __init__(self, opponent_crew: Crew = None, participant: DavyBackFightParticipant = None):
"""
Constructor
"""
self.opponent_crew: Crew = opponent_crew
self.participant: DavyBackFightParticipant = participant
self.davy_back_fight: DavyBackFight = participant.davy_back_fight if participant else None
item_id = self.davy_back_fight.id if self.davy_back_fight is not None else None
super().__init__(
NotificationCategory.DAVY_BACK_FIGHT,
NotificationType.DAVY_BACK_FIGHT_END,
"DAVY_BACK_FIGHT_END_NOTIFICATION",
"DAVY_BACK_FIGHT_END_NOTIFICATION_DESCRIPTION",
"DAVY_BACK_FIGHT_END_NOTIFICATION_KEY",
item_screen=Screen.PVT_CREW_DAVY_BACK_FIGHT_DETAIL,
item_info={
ReservedKeyboardKeys.DEFAULT_PRIMARY_KEY: item_id,
ReservedKeyboardKeys.DIRECT_ITEM: False,
},
go_to_item_button_text="KEY_VIEW",
item_previous_screens=[Screen.PVT_CREW],
)
def build(self) -> str:
if self.participant.in_winner_crew():
return phrases.DAVY_BACK_FIGHT_END_NOTIFICATION_WON.format(
self.opponent_crew.get_name_with_deeplink(),
get_belly_formatted(self.participant.win_amount),
format_percentage_value(self.participant.get_contribution_percentage()),
get_remaining_duration(self.davy_back_fight.penalty_end_date),
)
return phrases.DAVY_BACK_FIGHT_END_NOTIFICATION_LOST.format(
self.opponent_crew.get_name_with_deeplink(),
get_remaining_duration(self.davy_back_fight.penalty_end_date),
)
class ImpelDownBailPostedNotification(Notification):
"""Class for impel down bail posted notifications."""
def __init__(self, impel_down_log: ImpelDownLog = None):
"""
Constructor
:param impel_down_log: The impel down log
"""
self.impel_down_log: ImpelDownLog = impel_down_log
super().__init__(
NotificationCategory.IMPEL_DOWN,
NotificationType.IMPEL_DOWN_BAIL_POSTED,
"IMPEL_DOWN_BAIL_POSTED_NOTIFICATION",
"IMPEL_DOWN_BAIL_POSTED_NOTIFICATION_DESCRIPTION",
"IMPEL_DOWN_BAIL_POSTED_NOTIFICATION_KEY",
)
def build(self) -> str:
from src.utils.string_utils import get_belly_formatted
return self.text.format(
self.impel_down_log.bail_payer.get_markdown_mention(),
get_belly_formatted(self.impel_down_log.bail_amount),
get_remaining_duration(self.impel_down_log.release_date_time),
)
class DevilFruitSoldNotification(Notification):
"""Class for devil fruit sold notifications."""
def __init__(self, devil_fruit_trade: DevilFruitTrade = None):
"""
Constructor
:param devil_fruit_trade: The trade
"""
self.trade: DevilFruitTrade = devil_fruit_trade
super().__init__(
NotificationCategory.DEVIL_FRUIT,
NotificationType.DEVIL_FRUIT_SOLD,
"DEVIL_FRUIT_SOLD_NOTIFICATION",
"DEVIL_FRUIT_SOLD_NOTIFICATION_DESCRIPTION",
"DEVIL_FRUIT_SOLD_NOTIFICATION_KEY",
)
def build(self) -> str:
return self.text.format(
escape_valid_markdown_chars(self.trade.devil_fruit.get_full_name()),
get_belly_formatted(self.trade.price),
self.trade.receiver.get_markdown_mention(),
)
class CrewCaptainPromotionNotification(Notification):
"""Class for crew captain promotion notifications."""
def __init__(self):
"""
Constructor
"""
super().__init__(
NotificationCategory.CREW,
NotificationType.CREW_CAPTAIN_PROMOTION,
"CREW_CAPTAIN_PROMOTION_NOTIFICATION",
"CREW_CAPTAIN_PROMOTION_NOTIFICATION_DESCRIPTION",
"CREW_CAPTAIN_PROMOTION_NOTIFICATION_KEY",
)
class FightAttackNotification(Notification):
"""Class for fight attack notifications."""
def __init__(self, fight: Fight = None):
"""
Constructor
:param fight: The fight
"""
self.fight = fight
super().__init__(
NotificationCategory.FIGHT,
NotificationType.FIGHT_ATTACK,
"FIGHT_ATTACK_NOTIFICATION",
"FIGHT_ATTACK_NOTIFICATION_DESCRIPTION",
"FIGHT_ATTACK_NOTIFICATION_KEY",
item_screen=Screen.PVT_LOGS_TYPE_DETAIL,
item_info={
LogTypeReservedKeys.TYPE: LogType.FIGHT,
LogTypeReservedKeys.ITEM_ID: fight.id if fight is not None else None,
},
go_to_item_button_text="KEY_VIEW_LOG",
)
def build(self) -> str:
if self.fight.get_status() is GameStatus.LOST:
won_lost_text = phrases.TEXT_WON
won_lost_emoji = Emoji.CONFETTI
else:
won_lost_text = phrases.TEXT_LOST
won_lost_emoji = Emoji.LOSER
ot_text = self.text.format(
won_lost_text,
self.fight.challenger.get_markdown_mention(),
won_lost_emoji,
won_lost_text,
get_belly_formatted(self.fight.belly),
)
# Fight can be revenged
if self.fight.can_revenge():
ot_text += phrases.FIGHT_ATTACK_CAN_REVENGE.format(
self.fight.get_revenge_remaining_duration()
)
else: # Only occasion a fight can't be revenged is if it was in response to another fight
ot_text += phrases.FIGHT_ATTACK_CANNOT_REVENGE.format(
Log.get_deeplink_by_type(LogType.FIGHT, self.fight.in_revenge_to_fight.id)
)
return ot_text
class PlunderAttackNotification(Notification):
"""Class for plunder attack notifications."""
def __init__(self, plunder: Plunder = None):
"""
Constructor
:param plunder: The plunder
"""
self.plunder = plunder
super().__init__(
NotificationCategory.PLUNDER,
NotificationType.PLUNDER_ATTACK,
"PLUNDER_ATTACK_NOTIFICATION",
"PLUNDER_ATTACK_NOTIFICATION_DESCRIPTION",
"PLUNDER_ATTACK_NOTIFICATION_KEY",
item_screen=Screen.PVT_LOGS_TYPE_DETAIL,
item_info={
LogTypeReservedKeys.TYPE: LogType.PLUNDER,
LogTypeReservedKeys.ITEM_ID: plunder.id if plunder is not None else None,
},
go_to_item_button_text="KEY_VIEW_LOG",
)
def build(self) -> str:
if self.plunder.get_status() is GameStatus.LOST:
text = phrases.PLUNDER_ATTACK_NOTIFICATION_WON
else:
text = phrases.PLUNDER_ATTACK_NOTIFICATION_LOST
ot_text = text.format(
self.plunder.challenger.get_markdown_mention(), get_belly_formatted(self.plunder.belly)
)
# Plunder can be revenged
if self.plunder.can_revenge():
ot_text += phrases.PLUNDER_ATTACK_CAN_REVENGE.format(
self.plunder.get_revenge_remaining_duration()
)
else: # Only occasion a plunder can't be revenged is if it was in response to another plunder
ot_text += phrases.PLUNDER_ATTACK_CANNOT_REVENGE.format(
Log.get_deeplink_by_type(LogType.PLUNDER, self.plunder.in_revenge_to_plunder.id)
)
return ot_text
class GameOutcomeNotification(Notification):
"""Class for game outcome notifications."""
def __init__(self, game: Game = None, user: User = None, footer_text: str = ""):
"""Constructor
:param game: The game
:param user: The user to send the outcome to
:param footer_text: Text to add as footer
"""
self.game = game
self.user = user
self.footer_text = footer_text
super().__init__(
NotificationCategory.GAME,
NotificationType.GAME_OUTCOME,
"",
"GAME_OUTCOME_NOTIFICATION_DESCRIPTION",
"GAME_OUTCOME_NOTIFICATION_KEY",
disable_notification=False,
item_screen=Screen.PVT_LOGS_TYPE_DETAIL,
item_info={
LogTypeReservedKeys.TYPE: LogType.GAME,
LogTypeReservedKeys.ITEM_ID: game.id if game is not None else None,
},
go_to_item_button_text="KEY_VIEW_LOG",
)
def build(self) -> str:
"""Builds the notification."""
# Not supported if result is not win/lost/draw
if self.game.get_status() not in [GameStatus.WON, GameStatus.LOST, GameStatus.DRAW]:
raise ValueError(
"send_outcome_to_user is not supported for this game outcome: "
+ str(self.game.get_status())
)
if self.game.get_status() is not GameStatus.DRAW:
won = self.user == self.game.get_winner()
if won:
emoji = Emoji.LOG_POSITIVE
text_won_lost = phrases.TEXT_WON
else:
emoji = Emoji.LOG_NEGATIVE
text_won_lost = phrases.TEXT_LOST
return (
phrases.GAME_OUTCOME_NOTIFICATION.format(
emoji,
text_won_lost,
self.game.get_half_wager_formatted(),
GameType(self.game.type).get_name(),
mention_markdown_user(self.game.get_other_player(self.user)),
)
+ self.footer_text
)
return (
phrases.GAME_OUTCOME_NOTIFICATION_DRAW.format(
GameType(self.game.type).get_name(),
mention_markdown_user(self.game.get_other_player(self.user)),
self.game.get_half_wager_formatted(),
)
+ self.footer_text
)
NOTIFICATIONS = [
CrewLeaveNotification(),
LocationUpdateNotification(),
CrewDisbandNotification(),
CrewDisbandWarningNotification(),
GameTurnNotification(),
CrewMemberRemoveNotification(),
ImpelDownNotificationRestrictionPlaced(),
ImpelDownNotificationRestrictionRemoved(),
PredictionResultNotification(),
PredictionBetInvalidNotification(),
DeletedMessageArrestNotification(),
DeletedMessageMuteNotification(),
DeletedMessageLocationNotification(),
BountyGiftReceivedNotification(),
DevilFruitAwardedNotification(),
DevilFruitExpiredNotification(),
DevilFruitRevokeNotification(),
DevilFruitRevokeWarningNotification(),
BountyLoanPaymentNotification(),
BountyLoanForgivenNotification(),
BountyLoanExpiredNotification(),
WarlordAppointmentNotification(),
WarlordRevocationNotification(),
LegendaryPirateAppointmentNotification(),
LegendaryPirateRevocationNotification(),
CrewAbilityActivatedNotification(),
CrewFirstMatePromotionNotification(),
CrewFirstMateDemotionNotification(),
CrewJoinRequestAcceptedNotification(),
CrewJoinRequestRejectedNotification(),
CrewConscriptionStartNotification(),
CrewConscriptionStartCaptainNotification(),
CrewConscriptionEndNotification(),
ImpelDownBailPostedNotification(),
DavyBackFightRequestAcceptedNotification(),
DavyBackFightRequestRejectedNotification(),
DavyBackFightStartNotification(),
DavyBackFightEndNotification(),
DevilFruitSoldNotification(),
CrewCaptainPromotionNotification(),
FightAttackNotification(),
PlunderAttackNotification(),
GameOutcomeNotification(),
]
def get_notifications_by_category(
notification_category: NotificationCategory,
) -> list[Notification]:
"""
Get all notifications by category
:param notification_category: The category
:return: List of notifications
"""
return [
notification
for notification in NOTIFICATIONS
if notification.category is notification_category
]
def get_notification_by_type(notification_type: NotificationType) -> Notification:
"""
Get a notification by type
:param notification_type: The type
:return: The notification
"""
notification = next(
notification for notification in NOTIFICATIONS if notification.type is notification_type
)
# Need to re-initialize the class since it would be initialized only once on program start, and it would keep in
# memory items
# noinspection PyArgumentList
notification.__init__()
return notification