code.pablomurad.com

prami-pet

Community virtual pet that lives as a Mastodon account

prami/admin.py raw
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from . import memory
from . import repository as repo
from . import stats
from .actions.base import Outcome

VISIBILITIES = ("public", "unlisted", "private", "direct")
ADMIN_VISIBILITY = "direct"


def run(ctx):
    config, user = ctx.config, ctx.user
    if not config.is_admin(user.acct):
        if config.admin_refuse_nonadmin:
            return Outcome(
                "That's an admin-only command.",
                accepted=False, record=False, address=False, visibility=ADMIN_VISIBILITY,
            )
        return None

    parts = ctx.arg.split()
    sub = parts[0].lower() if parts else "help"
    rest = " ".join(parts[1:]).strip()

    if sub == "stats":
        text = stats.text_report(ctx.session, ctx.config, ctx.settings, ctx.now)
    else:
        text = _dispatch(ctx.session, ctx.pet, ctx.settings, sub, rest)
    repo.log_event(ctx.session, ctx.pet.id, "admin", f"{user.acct}: {ctx.arg}".strip())
    return Outcome(text, record=False, address=False, visibility=ADMIN_VISIBILITY)


def _dispatch(session, pet, settings, sub, rest):
    if sub == "status":
        return _status(session, settings)
    if sub == "pause":
        settings.paused = True
        return "Prami is paused — it will stop reacting until you resume it."
    if sub == "resume":
        settings.paused = False
        return "Prami is back. It missed you, probably."
    if sub == "set-visibility":
        if rest in VISIBILITIES:
            settings.default_visibility = rest
            return f"Default reply visibility set to {rest}."
        return f"Visibility must be one of: {', '.join(VISIBILITIES)}."
    if sub == "set-autopost":
        settings.autopost_enabled = _on(rest)
        return f"Autoposts {_state(settings.autopost_enabled)}."
    if sub == "set-favourites":
        settings.favourites_enabled = _on(rest)
        return f"Favourites {_state(settings.favourites_enabled)}."
    if sub == "set-boosts":
        settings.boosts_enabled = _on(rest)
        return f"Boosts {_state(settings.boosts_enabled)}."
    if sub == "blocked-users":
        return _blocked(session)
    if sub == "block" and rest:
        repo.block_user(session, rest)
        return f"Blocked {rest.lstrip('@')}."
    if sub == "unblock" and rest:
        target = repo.get_user(session, rest)
        if target:
            target.blocked = False
        return f"Unblocked {rest.lstrip('@')}."
    if sub == "memories" or (sub == "memory-list" and rest in ("", "pending")):
        return _memory_list(session, "pending")
    if sub == "memory-list":
        return _memory_list(session, rest)
    if sub == "approve-memory" and rest:
        return _review(session, rest, approve=True)
    if sub == "reject-memory" and rest:
        return _review(session, rest, approve=False)
    return _help()


def _on(value):
    return value.strip().lower() in ("on", "true", "yes", "1", "enable", "enabled")


def _state(enabled):
    return "enabled" if enabled else "disabled"


def _status(session, settings):
    pending = len(memory.by_status(session, "pending"))
    approved = len(memory.by_status(session, "approved"))
    return (
        f"paused={settings.paused} · autopost={_state(settings.autopost_enabled)} · "
        f"favourites={_state(settings.favourites_enabled)} · boosts={_state(settings.boosts_enabled)} · "
        f"visibility={settings.default_visibility} · memories: {pending} pending, {approved} approved"
    )


def _blocked(session):
    from .models import User
    from sqlalchemy import select

    blocked = list(session.scalars(select(User).where(User.blocked.is_(True))))
    if not blocked:
        return "No blocked users."
    return "Blocked: " + ", ".join(u.acct for u in blocked)


def _memory_list(session, status):
    status = status if status in ("pending", "approved", "rejected") else "pending"
    items = memory.by_status(session, status)
    if not items:
        return f"No {status} memories."
    lines = [f"#{m.id} [{m.category}] {m.content}" for m in items[:10]]
    return f"{status.capitalize()} memories:\n" + "\n".join(lines)


def _review(session, rest, approve):
    parts = rest.split(None, 1)
    try:
        suggestion_id = int(parts[0])
    except ValueError:
        return "Usage: approve-memory <id> | reject-memory <id> [reason]"

    if approve:
        result = memory.approve(session, suggestion_id, "admin")
        return f"Approved memory #{suggestion_id}." if result else f"No pending memory #{suggestion_id}."

    reason = parts[1] if len(parts) > 1 else ""
    result = memory.reject(session, suggestion_id, "admin", reason)
    return f"Rejected memory #{suggestion_id}." if result else f"No pending memory #{suggestion_id}."


def _help():
    return (
        "Admin commands: status · stats · pause · resume · set-visibility <v> · "
        "set-autopost on|off · set-favourites on|off · set-boosts on|off · "
        "blocked-users · block <user> · unblock <user> · memories · "
        "memory-list pending|approved · approve-memory <id> · reject-memory <id> [reason]"
    )