liberapay.com/liberapay/exceptions.py
2018-11-27 09:42:10 +01:00

465 lines
14 KiB
Python

from __future__ import print_function, unicode_literals
from dependency_injection import resolve_dependencies
from pando import Response
from .constants import PASSWORD_MIN_SIZE, PASSWORD_MAX_SIZE
class Redirect(Exception):
def __init__(self, url):
self.url = url
class LazyResponse(Response):
def __init__(self, code, lazy_body, **kw):
Response.__init__(self, code, '', **kw)
self.lazy_body = lazy_body
def render_body(self, state):
f = self.lazy_body
self.body = f(*resolve_dependencies(f, state).as_args)
def render_in_english(self):
f = self.lazy_body
fake_state = {}
from liberapay.i18n.base import LOCALE_EN, add_helpers_to_context
add_helpers_to_context(fake_state, LOCALE_EN)
return f(*resolve_dependencies(f, fake_state).as_args)
class AuthRequired(LazyResponse):
def __init__(self):
Response.__init__(self, 403, '')
self.html_template = 'templates/auth-required.html'
def lazy_body(self, _):
return _("You need to sign in first")
class LoginRequired(LazyResponse):
def __init__(self):
Response.__init__(self, 403, '')
self.html_template = 'templates/log-in-required.html'
def lazy_body(self, _):
return _("You need to log in")
class NeedDatabase(LazyResponse):
def __init__(self):
Response.__init__(self, 503, '')
self.html_template = 'templates/no-db.html'
def lazy_body(self, _):
return _("We're unable to process your request right now, sorry.")
class LazyResponseXXX(LazyResponse):
def __init__(self, *args, **kw):
Response.__init__(self, self.code, '', **kw)
self.lazy_body = self.msg
self.args = args
__str__ = Exception.__str__
class LazyResponse400(LazyResponseXXX):
code = 400
class ProblemChangingUsername(LazyResponse400): pass
class UsernameIsEmpty(ProblemChangingUsername):
def msg(self, _):
return _("You need to provide a username!")
class UsernameTooLong(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' is too long.", *self.args)
class UsernameContainsInvalidCharacters(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' contains invalid characters.", *self.args)
class UsernameIsRestricted(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' is restricted.", *self.args)
class UsernameAlreadyTaken(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' is already taken.", *self.args)
class UsernameBeginsWithRestrictedCharacter(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' begins with a restricted character.", *self.args)
class UsernameEndsWithForbiddenSuffix(ProblemChangingUsername):
def msg(self, _):
return _("The username '{0}' ends with the forbidden suffix '{1}'.", *self.args)
class TooManyUsernameChanges(ProblemChangingUsername):
code = 429
def msg(self, _):
return _(
"You've already changed your username many times recently, please "
"retry later (e.g. in a week) or contact support@liberapay.com."
)
class ValueTooLong(LazyResponse400):
def msg(self, _):
return _("The value '{0}' is too long.", self.args[0])
class ValueContainsForbiddenCharacters(LazyResponse400):
def msg(self, _, locale):
return _(
"The value '{0}' contains the following forbidden characters: {1}.",
self.args[0], locale.format_list(["'%s'" % c for c in self.args[1]])
)
class ProblemChangingEmail(LazyResponse400): pass
class EmailAlreadyTaken(ProblemChangingEmail):
code = 409
def msg(self, _):
return _("{0} is already connected to a different Liberapay account.", *self.args)
class CannotRemovePrimaryEmail(ProblemChangingEmail):
code = 403
def msg(self, _):
return _("You cannot remove your primary email address.")
class EmailNotVerified(ProblemChangingEmail):
def msg(self, _):
return _("The email address '{0}' is not verified.", *self.args)
class TooManyEmailAddresses(ProblemChangingEmail):
code = 403
def msg(self, _):
return _("You've reached the maximum number of email addresses we allow.")
class BadEmailAddress(ProblemChangingEmail):
def msg(self, _):
return _("'{0}' is not a valid email address.", *self.args)
class BadEmailDomain(ProblemChangingEmail):
def msg(self, _):
return _("'{domain_name}' is not a valid email domain.", domain_name=self.args[0])
class EmailAddressIsBlacklisted(ProblemChangingEmail):
def msg(self, _, to_age):
address, reason, ts = self.args
if reason == 'bounce':
return _(
"The email address {email_address} is blacklisted because an "
"attempt to send a message to it failed {timedelta} ago. Please "
"send an email from that address to support@liberapay.com if "
"you want us to remove it from the blacklist.",
email_address=address, timedelta=to_age(ts)
)
else:
return _(
"The email address {email_address} is blacklisted because of a "
"complaint received {timedelta} ago. Please send an email "
"from that address to support@liberapay.com if you want us to "
"remove it from the blacklist.",
email_address=address, timedelta=to_age(ts)
)
class EmailAlreadyAttachedToSelf(ProblemChangingEmail):
code = 409
def msg(self, _):
return _("The email address {0} is already connected to your account.", *self.args)
class VerificationEmailAlreadySent(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"A verification email has already been sent to {email_address} recently.",
email_address=self.args[0]
)
class TooManyEmailVerifications(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"You are not allowed to add another email address right now, please "
"try again in a few days."
)
class TooManyLogInAttempts(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"There have been too many attempts to log in from your IP address or country "
"recently. Please try again in an hour and email support@liberapay.com if "
"the problem persists."
)
class TooManyLoginEmails(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"You have consumed your quota of email logins, please try again tomorrow, "
"or contact support@liberapay.com."
)
class TooManyPasswordLogins(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"There have been too many attempts to log in to this account recently, "
"please try again in a few hours or log in via email."
)
class TooManySignUps(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"Too many accounts have been created recently. This either means that "
"a lot of people are trying to join Liberapay today, or that an attacker "
"is trying to overload our system. As a result we have to ask you to come "
"back later (e.g. in a few hours), or send an email to support@liberapay.com. "
"We apologize for the inconvenience."
)
class TooManyTeamsCreated(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"You've already created several teams recently, please come back in a week."
)
class BadPasswordSize(LazyResponse400):
def msg(self, _):
return _("The password must be at least {0} and at most {1} characters long.",
PASSWORD_MIN_SIZE, PASSWORD_MAX_SIZE)
class NoSelfTipping(LazyResponse400):
def msg(self, _):
return _("You can't donate to yourself.")
class NoTippee(LazyResponse400):
def msg(self, _):
return _("There is no user named {0}.", *self.args)
_ = lambda a: a
BAD_AMOUNT_MESSAGES = {
'weekly': _("'{0}' is not a valid weekly donation amount (min={1}, max={2})"),
'monthly': _("'{0}' is not a valid monthly donation amount (min={1}, max={2})"),
'yearly': _("'{0}' is not a valid yearly donation amount (min={1}, max={2})"),
}
del _
class BadAmount(LazyResponse400):
def msg(self, _):
amount, period, limits = self.args
return _(BAD_AMOUNT_MESSAGES[period], amount, *limits)
class UserDoesntAcceptTips(LazyResponseXXX):
code = 403
def msg(self, _):
return _("The user {0} doesn't accept donations.", *self.args)
class BadDonationCurrency(LazyResponseXXX):
code = 403
def msg(self, _):
tippee, rejected_currency = self.args
return _(
"Donations to {username} must be in {main_currency}, not {rejected_currency}.",
username=tippee.username, main_currency=tippee.main_currency,
rejected_currency=rejected_currency,
)
class NonexistingElsewhere(LazyResponse400):
def msg(self, _):
return _("It seems you're trying to delete something that doesn't exist.")
class NegativeBalance(LazyResponse400):
def msg(self, _):
return _("There isn't enough money in your wallet.")
class NotEnoughWithdrawableMoney(LazyResponse400):
def msg(self, _):
return _("You can't withdraw more than {0} at this time.", *self.args)
class FeeExceedsAmount(LazyResponse400):
def msg(self, _):
return _("The transaction's fee would exceed its amount.")
class TransactionFeeTooHigh(Exception): pass
class PaydayIsRunning(LazyResponseXXX):
code = 503
def msg(self, _):
return _(
"Sorry, we're running payday right now, and we're not set up to do "
"payouts while payday is running. Please check back in a few hours."
)
class InvalidNumber(LazyResponse400):
def msg(self, _):
return _('"{0}" is not a valid number.', *self.args)
class AmbiguousNumber(LazyResponse400):
def __init__(self, ambiguous_string, suggestions):
Response.__init__(self, 400, '')
self.ambiguous_string = ambiguous_string
self.suggestions = suggestions
def lazy_body(self, _, locale):
if self.suggestions:
return _(
'"{0}" is not a properly formatted number. Perhaps you meant {list_of_suggestions}?',
self.ambiguous_string,
list_of_suggestions=locale.format_list(self.suggestions, 'or')
)
else:
return _('"{0}" is not a properly formatted number.', self.ambiguous_string)
class CommunityAlreadyExists(LazyResponse400):
def msg(self, _):
return _('The "{0}" community already exists.', *self.args)
class InvalidCommunityName(LazyResponse400):
def msg(self, _):
return _('"{0}" is not a valid community name.', *self.args)
class TransferError(LazyResponseXXX):
code = 500
def msg(self, _):
return _(
"Transferring the money failed, sorry. Please contact support@liberapay.com "
"if the problem persists. Error message: {0}", *self.args
)
class AccountSuspended(LazyResponseXXX):
code = 403
def msg(self, _):
return _(
"You are not allowed to do this because your account is currently "
"suspended."
)
class RecipientAccountSuspended(LazyResponseXXX):
code = 403
def msg(self, _):
return _(
"This payment cannot be processed because the account of the "
"recipient is currently suspended."
)
class MissingPaymentAccount(LazyResponseXXX):
code = 400
def msg(self, _):
return _(
"Your donation to {recipient} cannot be processed right now because the "
"account of the beneficiary isn't ready to receive money.",
recipient=self.args[0].username
)
class TooManyCurrencyChanges(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"You've already changed your main currency recently, please retry "
"later (e.g. in a week) or contact support@liberapay.com."
)
class TooManyAttempts(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"There have been too many attempts to perform this action recently, please "
"retry later (e.g. in a week) or contact support@liberapay.com if you "
"require assistance."
)
class TooManyRequests(LazyResponseXXX):
code = 429
def msg(self, _):
return _(
"You're sending requests at an unusually fast pace. Please retry in a few "
"seconds, and contact support@liberapay.com if the problem persists."
)
class UnableToDistributeBalance(LazyResponse400):
def msg(self, _):
return _(
"The attempt to distribute all the money in your wallet failed: "
"{money_amount} remains.", money_amount=self.args[0]
)
class UnableToSendEmail(LazyResponseXXX):
code = 500
def msg(self, _):
return _(
"The attempt to send an email to {email_address} failed. Please "
"check that the address is valid and retry. If the problem persists "
"please contact support@liberapay.com.", email_address=self.args[0]
)
class PayinMethodIsUnavailable(LazyResponseXXX):
code = 403
def msg(self, _):
return _("This payment method is currently unavailable. We apologize for the inconvenience.")
class PayinsAreDisabled(LazyResponse):
def __init__(self):
Response.__init__(self, 403, '')
self.html_template = 'templates/no-payins.html'
def lazy_body(self, _):
return _(
"We're very sorry but incoming payments are currently disabled. "
"You can read {link_start}this blog post{link_end} to learn why.",
link_start='[',
link_end='](https://medium.com/liberapay-blog/liberapay-is-in-trouble-b58b40714d82)'
)
class PaymentError(LazyResponseXXX):
code = 500
def msg(self, _):
return _(
"The payment processor {name} returned an error. Please try again "
"and contact support@liberapay.com if the problem persists.",
name=self.args[0]
)