Added editcounter management command for setting and adjusting named counters.
This commit is contained in:
+1
-1
Submodule docs updated: df285f62d9...7882423a22
@@ -31,6 +31,7 @@ from .management_commands import (
|
|||||||
commandmodonly,
|
commandmodonly,
|
||||||
deletecommand,
|
deletecommand,
|
||||||
editcommand,
|
editcommand,
|
||||||
|
editcounter,
|
||||||
listcommands,
|
listcommands,
|
||||||
removealias,
|
removealias,
|
||||||
resetcommand,
|
resetcommand,
|
||||||
@@ -45,6 +46,7 @@ __all__ = [
|
|||||||
"commandmodonly",
|
"commandmodonly",
|
||||||
"deletecommand",
|
"deletecommand",
|
||||||
"editcommand",
|
"editcommand",
|
||||||
|
"editcounter",
|
||||||
"listcommands",
|
"listcommands",
|
||||||
"removealias",
|
"removealias",
|
||||||
"resetcommand",
|
"resetcommand",
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
"""Management commands for custom commands (add, edit, delete, etc.)."""
|
"""Management commands for custom commands (add, edit, delete, etc.)."""
|
||||||
|
|
||||||
import re
|
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@@ -27,6 +26,7 @@ from .handler import (
|
|||||||
resolve_command_name,
|
resolve_command_name,
|
||||||
update_and_reregister,
|
update_and_reregister,
|
||||||
)
|
)
|
||||||
|
from .placeholder_handlers import NAME_RE
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
@@ -83,7 +83,7 @@ async def addcommand(ctx: CommandContext) -> None:
|
|||||||
|
|
||||||
name = _clean_raw_name(args[0], prefix)
|
name = _clean_raw_name(args[0], prefix)
|
||||||
|
|
||||||
if not re.match(r"^[a-z0-9_]+$", name):
|
if not NAME_RE.match(name):
|
||||||
await ctx.owncast_client.send_message(
|
await ctx.owncast_client.send_message(
|
||||||
"Invalid command name. Only letters, numbers, and underscores are allowed."
|
"Invalid command name. Only letters, numbers, and underscores are allowed."
|
||||||
)
|
)
|
||||||
@@ -284,6 +284,69 @@ async def resetcommand(ctx: CommandContext) -> None:
|
|||||||
await ctx.owncast_client.send_message(f"Command {prefix}{name} counter reset.")
|
await ctx.owncast_client.send_message(f"Command {prefix}{name} counter reset.")
|
||||||
|
|
||||||
|
|
||||||
|
@on_command("editcounter", aliases=["editcount"], requires_moderator=True)
|
||||||
|
async def editcounter(ctx: CommandContext) -> None:
|
||||||
|
"""
|
||||||
|
Set, increment, or decrement a named counter.
|
||||||
|
|
||||||
|
Usage: !editcounter <name> <value>
|
||||||
|
|
||||||
|
The value can be an absolute number (e.g. ``15``), or a relative
|
||||||
|
modifier prefixed with ``+`` or ``-`` (e.g. ``+1``, ``-3``).
|
||||||
|
If the counter does not exist it is created on the fly.
|
||||||
|
|
||||||
|
:param ctx: The command context.
|
||||||
|
"""
|
||||||
|
prefix = ctx.commands.prefix
|
||||||
|
args = ctx.args_list
|
||||||
|
|
||||||
|
if len(args) != 2:
|
||||||
|
await ctx.owncast_client.send_message(
|
||||||
|
f"Usage: {prefix}editcounter name <value>"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
counter_name = args[0].lower()
|
||||||
|
|
||||||
|
if not NAME_RE.match(counter_name):
|
||||||
|
await ctx.owncast_client.send_message(
|
||||||
|
"Invalid counter name. Only letters, numbers, and underscores are allowed."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
value_str = args[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
value = int(value_str)
|
||||||
|
except ValueError:
|
||||||
|
await ctx.owncast_client.send_message(
|
||||||
|
"Invalid value. Must be an integer (e.g. 15, +1, -3)."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Fetch old value (default 0 if counter doesn't exist yet).
|
||||||
|
row = await ctx.storage.fetch_one(
|
||||||
|
"SELECT value FROM counters WHERE name = ?", (counter_name,)
|
||||||
|
)
|
||||||
|
old_value = row["value"] if row else 0
|
||||||
|
|
||||||
|
# Leading +/- means relative delta; otherwise absolute.
|
||||||
|
new_value = old_value + value if value_str[0] in ("+", "-") else value
|
||||||
|
|
||||||
|
await ctx.storage.execute(
|
||||||
|
"INSERT INTO counters (name, value) VALUES (?, ?) "
|
||||||
|
"ON CONFLICT(name) DO UPDATE SET value = ?",
|
||||||
|
(counter_name, new_value, new_value),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.logger.info(
|
||||||
|
f"Counter '{counter_name}' changed from {old_value} to {new_value}."
|
||||||
|
)
|
||||||
|
await ctx.owncast_client.send_message(
|
||||||
|
f"Changed the {counter_name} counter from {old_value} to {new_value}."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@on_command("commandcooldown", aliases=["cmdcooldown"], requires_moderator=True)
|
@on_command("commandcooldown", aliases=["cmdcooldown"], requires_moderator=True)
|
||||||
async def commandcooldown(ctx: CommandContext) -> None:
|
async def commandcooldown(ctx: CommandContext) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -357,7 +420,7 @@ async def addalias(ctx: CommandContext) -> None:
|
|||||||
|
|
||||||
alias = _clean_raw_name(args[1], prefix)
|
alias = _clean_raw_name(args[1], prefix)
|
||||||
|
|
||||||
if not re.match(r"^[a-z0-9_]+$", alias):
|
if not NAME_RE.match(alias):
|
||||||
await ctx.owncast_client.send_message(
|
await ctx.owncast_client.send_message(
|
||||||
"Invalid alias name. Only letters, numbers, and underscores are allowed."
|
"Invalid alias name. Only letters, numbers, and underscores are allowed."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -248,8 +248,8 @@ TIMEZONE_OFFSETS: dict[str, float] = {
|
|||||||
"YEKT": 5,
|
"YEKT": 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pattern for validating counter names.
|
# Pattern for validating identifiers (command names, counter names, aliases).
|
||||||
_COUNTER_NAME_RE = re.compile(r"^[a-z0-9_]+$")
|
NAME_RE = re.compile(r"^[a-z0-9_]+$")
|
||||||
|
|
||||||
|
|
||||||
def _parse_placeholder_date(date_str: str) -> datetime | None:
|
def _parse_placeholder_date(date_str: str) -> datetime | None:
|
||||||
@@ -341,7 +341,7 @@ async def _evaluate_count(
|
|||||||
|
|
||||||
# Named counter.
|
# Named counter.
|
||||||
counter_name = args[0].lower()
|
counter_name = args[0].lower()
|
||||||
if not _COUNTER_NAME_RE.match(counter_name):
|
if not NAME_RE.match(counter_name):
|
||||||
raise PlaceholderError(
|
raise PlaceholderError(
|
||||||
"Invalid $(count): counter name may only contain "
|
"Invalid $(count): counter name may only contain "
|
||||||
"letters, numbers, and underscores"
|
"letters, numbers, and underscores"
|
||||||
@@ -395,7 +395,7 @@ async def _evaluate_getcount(
|
|||||||
"Invalid $(getcount): too many arguments, expected $(getcount name)"
|
"Invalid $(getcount): too many arguments, expected $(getcount name)"
|
||||||
)
|
)
|
||||||
counter_name = args[0].lower()
|
counter_name = args[0].lower()
|
||||||
if not _COUNTER_NAME_RE.match(counter_name):
|
if not NAME_RE.match(counter_name):
|
||||||
raise PlaceholderError(
|
raise PlaceholderError(
|
||||||
"Invalid $(getcount): counter name may only contain "
|
"Invalid $(getcount): counter name may only contain "
|
||||||
"letters, numbers, and underscores"
|
"letters, numbers, and underscores"
|
||||||
|
|||||||
Reference in New Issue
Block a user