1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 |
from datetime import timedelta
from . import milestones
from . import relationships
from . import repository as repo
from . import responses
from .actions.base import ActionContext
from .actions.registry import REGISTRY
from .models import utcnow
from .result import Result
def handle(session, pet, user, command, config, status_id=None, now=None, arg=""):
now = now or utcnow()
settings = repo.get_settings(session, config)
action = REGISTRY.resolve(command or "unknown")
ctx = ActionContext(session, pet, user, arg or "", config, settings, now, status_id)
if action.admin_only:
return _finalize(ctx, action, action.run(ctx))
if user.blocked or settings.paused:
return None
if repo.count_global_interactions_last_hour(session, now) >= config.max_global_hour:
repo.log_event(session, pet.id, "rate_block_global", f"{user.acct} hit the global cap")
return None
if repo.count_user_interactions_last_hour(session, user.id, now) >= config.max_per_user_hour:
return Result(responses.too_much(pet), settings.default_visibility, False)
if not action.enabled(config):
return _finalize(ctx, action, action.on_disabled(ctx))
if pet.asleep and not action.runs_while_asleep:
return Result(responses.too_sleepy(action.name, pet), settings.default_visibility, False)
early = action.precheck(ctx)
if early is not None:
return _finalize(ctx, action, early)
reason = _cooldown_reason(session, pet, user, action, now)
if reason:
return Result(responses.cooldown(action.name, reason, pet), settings.default_visibility, False)
return _finalize(ctx, action, action.run(ctx))
def _finalize(ctx, action, outcome):
if outcome is None:
return None
session, pet, user = ctx.session, ctx.pet, ctx.user
achieved = []
if outcome.accepted:
_touch_cooldowns(session, pet, user, action, ctx.now)
if outcome.record:
repo.record_interaction(session, pet.id, user.id, action.name, ctx.status_id, True)
relationships.record(user, action.name, pet, outcome.was_critical)
user.last_seen = utcnow()
achieved = milestones.check(session, pet, user, action.name, outcome.was_critical)
text = responses.maybe_address(outcome.text, user) if outcome.address else outcome.text
visibility = outcome.visibility or ctx.settings.default_visibility
return Result(text, visibility, outcome.accepted, outcome.favourite, outcome.boost, achieved)
def _cooldown_reason(session, pet, user, action, now):
if action.global_cooldown_minutes:
cd = repo.get_cooldown(session, pet.id, None, action.name)
if cd and now - cd.last_used_at < timedelta(minutes=action.global_cooldown_minutes):
return "global"
if action.user_cooldown_minutes:
cd = repo.get_cooldown(session, pet.id, user.id, action.name)
if cd and now - cd.last_used_at < timedelta(minutes=action.user_cooldown_minutes):
return "user"
if action.per_hour_limit is not None:
if repo.count_command_last_hour(session, action.name, now) >= action.per_hour_limit:
return "busy"
return None
def _touch_cooldowns(session, pet, user, action, now):
if action.user_cooldown_minutes:
repo.touch_cooldown(session, pet.id, user.id, action.name, now)
if action.global_cooldown_minutes:
repo.touch_cooldown(session, pet.id, None, action.name, now)
|