From 976c3f9d3e4302ef0b442412ada369582d8efc99 Mon Sep 17 00:00:00 2001 From: Richie Cahill Date: Wed, 18 Mar 2026 08:35:45 -0400 Subject: [PATCH] move signal bot to its own DB --- ...rating_signal_bot_database_6b275323f435.py | 171 ++++++++++++++++++ ...rating_signal_bot_database_6eaf696e07a5.py | 100 ++++++++++ python/database_cli.py | 7 + python/orm/__init__.py | 2 + python/orm/richie/__init__.py | 7 - python/orm/richie/dead_letter_message.py | 26 --- python/orm/signal_bot/__init__.py | 16 ++ python/orm/signal_bot/base.py | 52 ++++++ .../signal_device.py => signal_bot/models.py} | 28 ++- python/signal_bot/commands/location.py | 16 +- python/signal_bot/device_registry.py | 2 +- python/signal_bot/llm_client.py | 12 +- python/signal_bot/main.py | 8 +- systems/jeeves/services/postgress.nix | 4 +- systems/jeeves/services/signal_bot.nix | 3 +- 15 files changed, 402 insertions(+), 52 deletions(-) create mode 100644 python/alembic/richie/versions/2026_03_18-seprating_signal_bot_database_6b275323f435.py create mode 100644 python/alembic/signal_bot/versions/2026_03_17-seprating_signal_bot_database_6eaf696e07a5.py delete mode 100644 python/orm/richie/dead_letter_message.py create mode 100644 python/orm/signal_bot/__init__.py create mode 100644 python/orm/signal_bot/base.py rename python/orm/{richie/signal_device.py => signal_bot/models.py} (60%) diff --git a/python/alembic/richie/versions/2026_03_18-seprating_signal_bot_database_6b275323f435.py b/python/alembic/richie/versions/2026_03_18-seprating_signal_bot_database_6b275323f435.py new file mode 100644 index 0000000..22d4621 --- /dev/null +++ b/python/alembic/richie/versions/2026_03_18-seprating_signal_bot_database_6b275323f435.py @@ -0,0 +1,171 @@ +"""seprating signal_bot database. + +Revision ID: 6b275323f435 +Revises: 2ef7ba690159 +Create Date: 2026-03-18 08:34:28.785885 + +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +from python.orm import RichieBase + +if TYPE_CHECKING: + from collections.abc import Sequence + +# revision identifiers, used by Alembic. +revision: str = "6b275323f435" +down_revision: str | None = "2ef7ba690159" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + +schema = RichieBase.schema_name + + +def upgrade() -> None: + """Upgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("device_role", schema=schema) + op.drop_table("signal_device", schema=schema) + op.drop_table("role", schema=schema) + op.drop_table("dead_letter_message", schema=schema) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "dead_letter_message", + sa.Column("source", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("message", sa.TEXT(), autoincrement=False, nullable=False), + sa.Column("received_at", postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.Column( + "status", + postgresql.ENUM("UNPROCESSED", "PROCESSED", name="message_status", schema=schema), + autoincrement=False, + nullable=False, + ), + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "created", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "updated", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_dead_letter_message")), + schema=schema, + ) + op.create_table( + "role", + sa.Column("name", sa.VARCHAR(length=50), autoincrement=False, nullable=False), + sa.Column( + "id", + sa.SMALLINT(), + server_default=sa.text("nextval(f'{schema}.role_id_seq'::regclass)"), + autoincrement=True, + nullable=False, + ), + sa.Column( + "created", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "updated", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_role")), + sa.UniqueConstraint( + "name", name=op.f("uq_role_name"), postgresql_include=[], postgresql_nulls_not_distinct=False + ), + schema=schema, + ) + op.create_table( + "signal_device", + sa.Column("phone_number", sa.VARCHAR(length=50), autoincrement=False, nullable=False), + sa.Column("safety_number", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column( + "trust_level", + postgresql.ENUM("VERIFIED", "UNVERIFIED", "BLOCKED", name="trust_level", schema=schema), + autoincrement=False, + nullable=False, + ), + sa.Column("last_seen", postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "created", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "updated", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_signal_device")), + sa.UniqueConstraint( + "phone_number", + name=op.f("uq_signal_device_phone_number"), + postgresql_include=[], + postgresql_nulls_not_distinct=False, + ), + schema=schema, + ) + op.create_table( + "device_role", + sa.Column("device_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("role_id", sa.SMALLINT(), autoincrement=False, nullable=False), + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "created", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "updated", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.ForeignKeyConstraint( + ["device_id"], [f"{schema}.signal_device.id"], name=op.f("fk_device_role_device_id_signal_device") + ), + sa.ForeignKeyConstraint(["role_id"], [f"{schema}.role.id"], name=op.f("fk_device_role_role_id_role")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_device_role")), + sa.UniqueConstraint( + "device_id", + "role_id", + name=op.f("uq_device_role_device_role"), + postgresql_include=[], + postgresql_nulls_not_distinct=False, + ), + schema=schema, + ) + # ### end Alembic commands ### diff --git a/python/alembic/signal_bot/versions/2026_03_17-seprating_signal_bot_database_6eaf696e07a5.py b/python/alembic/signal_bot/versions/2026_03_17-seprating_signal_bot_database_6eaf696e07a5.py new file mode 100644 index 0000000..d51cb3e --- /dev/null +++ b/python/alembic/signal_bot/versions/2026_03_17-seprating_signal_bot_database_6eaf696e07a5.py @@ -0,0 +1,100 @@ +"""seprating signal_bot database. + +Revision ID: 6eaf696e07a5 +Revises: +Create Date: 2026-03-17 21:35:37.612672 + +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +from python.orm import SignalBotBase + +if TYPE_CHECKING: + from collections.abc import Sequence + +# revision identifiers, used by Alembic. +revision: str = "6eaf696e07a5" +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + +schema = SignalBotBase.schema_name + + +def upgrade() -> None: + """Upgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "dead_letter_message", + sa.Column("source", sa.String(), nullable=False), + sa.Column("message", sa.Text(), nullable=False), + sa.Column("received_at", sa.DateTime(timezone=True), nullable=False), + sa.Column( + "status", postgresql.ENUM("UNPROCESSED", "PROCESSED", name="message_status", schema=schema), nullable=False + ), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("created", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.Column("updated", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_dead_letter_message")), + schema=schema, + ) + op.create_table( + "role", + sa.Column("name", sa.String(length=50), nullable=False), + sa.Column("id", sa.SmallInteger(), nullable=False), + sa.Column("created", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.Column("updated", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_role")), + sa.UniqueConstraint("name", name=op.f("uq_role_name")), + schema=schema, + ) + op.create_table( + "signal_device", + sa.Column("phone_number", sa.String(length=50), nullable=False), + sa.Column("safety_number", sa.String(), nullable=True), + sa.Column( + "trust_level", + postgresql.ENUM("VERIFIED", "UNVERIFIED", "BLOCKED", name="trust_level", schema=schema), + nullable=False, + ), + sa.Column("last_seen", sa.DateTime(timezone=True), nullable=False), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("created", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.Column("updated", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_signal_device")), + sa.UniqueConstraint("phone_number", name=op.f("uq_signal_device_phone_number")), + schema=schema, + ) + op.create_table( + "device_role", + sa.Column("device_id", sa.Integer(), nullable=False), + sa.Column("role_id", sa.SmallInteger(), nullable=False), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("created", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.Column("updated", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), + sa.ForeignKeyConstraint( + ["device_id"], [f"{schema}.signal_device.id"], name=op.f("fk_device_role_device_id_signal_device") + ), + sa.ForeignKeyConstraint(["role_id"], [f"{schema}.role.id"], name=op.f("fk_device_role_role_id_role")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_device_role")), + sa.UniqueConstraint("device_id", "role_id", name="uq_device_role_device_role"), + schema=schema, + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("device_role", schema=schema) + op.drop_table("signal_device", schema=schema) + op.drop_table("role", schema=schema) + op.drop_table("dead_letter_message", schema=schema) + # ### end Alembic commands ### diff --git a/python/database_cli.py b/python/database_cli.py index e28c19b..8366e6c 100644 --- a/python/database_cli.py +++ b/python/database_cli.py @@ -83,6 +83,13 @@ DATABASES: dict[str, DatabaseConfig] = { base_class_name="VanInventoryBase", models_module="python.orm.van_inventory.models", ), + "signal_bot": DatabaseConfig( + env_prefix="SIGNALBOT", + version_location="python/alembic/signal_bot/versions", + base_module="python.orm.signal_bot.base", + base_class_name="SignalBotBase", + models_module="python.orm.signal_bot.models", + ), } diff --git a/python/orm/__init__.py b/python/orm/__init__.py index d76ba95..e5482f9 100644 --- a/python/orm/__init__.py +++ b/python/orm/__init__.py @@ -1,9 +1,11 @@ """ORM package exports.""" from python.orm.richie.base import RichieBase +from python.orm.signal_bot.base import SignalBotBase from python.orm.van_inventory.base import VanInventoryBase __all__ = [ "RichieBase", + "SignalBotBase", "VanInventoryBase", ] diff --git a/python/orm/richie/__init__.py b/python/orm/richie/__init__.py index 85806c7..0d11aef 100644 --- a/python/orm/richie/__init__.py +++ b/python/orm/richie/__init__.py @@ -11,22 +11,15 @@ from python.orm.richie.contact import ( Need, RelationshipType, ) -from python.orm.richie.dead_letter_message import DeadLetterMessage -from python.orm.richie.signal_device import DeviceRole, RoleRecord, SignalDevice - __all__ = [ "Bill", "Contact", "ContactNeed", "ContactRelationship", - "DeadLetterMessage", - "DeviceRole", - "RoleRecord", "Legislator", "Need", "RelationshipType", "RichieBase", - "SignalDevice", "TableBase", "TableBaseBig", "TableBaseSmall", diff --git a/python/orm/richie/dead_letter_message.py b/python/orm/richie/dead_letter_message.py deleted file mode 100644 index 2dee0aa..0000000 --- a/python/orm/richie/dead_letter_message.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Dead letter queue for Signal bot messages that fail processing.""" - -from __future__ import annotations - -from datetime import datetime - -from sqlalchemy import DateTime, Text -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.orm import Mapped, mapped_column - -from python.orm.richie.base import TableBase -from python.signal_bot.models import MessageStatus - - -class DeadLetterMessage(TableBase): - """A Signal message that failed processing and was sent to the dead letter queue.""" - - __tablename__ = "dead_letter_message" - - source: Mapped[str] - message: Mapped[str] = mapped_column(Text) - received_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) - status: Mapped[MessageStatus] = mapped_column( - ENUM(MessageStatus, name="message_status", create_type=True, schema="main"), - default=MessageStatus.UNPROCESSED, - ) diff --git a/python/orm/signal_bot/__init__.py b/python/orm/signal_bot/__init__.py new file mode 100644 index 0000000..1c92a4a --- /dev/null +++ b/python/orm/signal_bot/__init__.py @@ -0,0 +1,16 @@ +"""Signal bot database ORM exports.""" + +from __future__ import annotations + +from python.orm.signal_bot.base import SignalBotBase, SignalBotTableBase, SignalBotTableBaseSmall +from python.orm.signal_bot.models import DeadLetterMessage, DeviceRole, RoleRecord, SignalDevice + +__all__ = [ + "DeadLetterMessage", + "DeviceRole", + "RoleRecord", + "SignalBotBase", + "SignalBotTableBase", + "SignalBotTableBaseSmall", + "SignalDevice", +] diff --git a/python/orm/signal_bot/base.py b/python/orm/signal_bot/base.py new file mode 100644 index 0000000..12d8a34 --- /dev/null +++ b/python/orm/signal_bot/base.py @@ -0,0 +1,52 @@ +"""Signal bot database ORM base.""" + +from __future__ import annotations + +from datetime import datetime + +from sqlalchemy import DateTime, MetaData, SmallInteger, func +from sqlalchemy.ext.declarative import AbstractConcreteBase +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + +from python.orm.common import NAMING_CONVENTION + + +class SignalBotBase(DeclarativeBase): + """Base class for signal_bot database ORM models.""" + + schema_name = "main" + + metadata = MetaData( + schema=schema_name, + naming_convention=NAMING_CONVENTION, + ) + + +class _TableMixin: + """Shared timestamp columns for all table bases.""" + + created: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + ) + updated: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now(), + ) + + +class SignalBotTableBaseSmall(_TableMixin, AbstractConcreteBase, SignalBotBase): + """Table with SmallInteger primary key.""" + + __abstract__ = True + + id: Mapped[int] = mapped_column(SmallInteger, primary_key=True) + + +class SignalBotTableBase(_TableMixin, AbstractConcreteBase, SignalBotBase): + """Table with Integer primary key.""" + + __abstract__ = True + + id: Mapped[int] = mapped_column(primary_key=True) diff --git a/python/orm/richie/signal_device.py b/python/orm/signal_bot/models.py similarity index 60% rename from python/orm/richie/signal_device.py rename to python/orm/signal_bot/models.py index ea62bc4..4dc6e45 100644 --- a/python/orm/richie/signal_device.py +++ b/python/orm/signal_bot/models.py @@ -1,18 +1,18 @@ -"""Signal bot device and role ORM models.""" +"""Signal bot device, role, and dead letter ORM models.""" from __future__ import annotations from datetime import datetime -from sqlalchemy import DateTime, ForeignKey, SmallInteger, String, UniqueConstraint +from sqlalchemy import DateTime, ForeignKey, SmallInteger, String, Text, UniqueConstraint from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import Mapped, mapped_column, relationship -from python.orm.richie.base import TableBase, TableBaseSmall -from python.signal_bot.models import TrustLevel +from python.orm.signal_bot.base import SignalBotTableBase, SignalBotTableBaseSmall +from python.signal_bot.models import MessageStatus, TrustLevel -class RoleRecord(TableBaseSmall): +class RoleRecord(SignalBotTableBaseSmall): """Lookup table for RBAC roles, keyed by smallint.""" __tablename__ = "role" @@ -20,7 +20,7 @@ class RoleRecord(TableBaseSmall): name: Mapped[str] = mapped_column(String(50), unique=True) -class DeviceRole(TableBase): +class DeviceRole(SignalBotTableBase): """Association between a device and a role.""" __tablename__ = "device_role" @@ -33,7 +33,7 @@ class DeviceRole(TableBase): role_id: Mapped[int] = mapped_column(SmallInteger, ForeignKey("main.role.id")) -class SignalDevice(TableBase): +class SignalDevice(SignalBotTableBase): """A Signal device tracked by phone number and safety number.""" __tablename__ = "signal_device" @@ -47,3 +47,17 @@ class SignalDevice(TableBase): last_seen: Mapped[datetime] = mapped_column(DateTime(timezone=True)) roles: Mapped[list[RoleRecord]] = relationship(secondary=DeviceRole.__table__) + + +class DeadLetterMessage(SignalBotTableBase): + """A Signal message that failed processing and was sent to the dead letter queue.""" + + __tablename__ = "dead_letter_message" + + source: Mapped[str] + message: Mapped[str] = mapped_column(Text) + received_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) + status: Mapped[MessageStatus] = mapped_column( + ENUM(MessageStatus, name="message_status", create_type=True, schema="main"), + default=MessageStatus.UNPROCESSED, + ) diff --git a/python/signal_bot/commands/location.py b/python/signal_bot/commands/location.py index c213cc5..be9e132 100644 --- a/python/signal_bot/commands/location.py +++ b/python/signal_bot/commands/location.py @@ -2,6 +2,7 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Any import requests @@ -10,11 +11,15 @@ if TYPE_CHECKING: from python.signal_bot.models import SignalMessage from python.signal_bot.signal_client import SignalClient +logger = logging.getLogger(__name__) + def _get_entity_state(ha_url: str, ha_token: str, entity_id: str) -> dict[str, Any]: """Fetch an entity's state from Home Assistant.""" + entity_url = f"{ha_url}/api/states/{entity_id}" + logger.debug(f"Fetching {entity_url=}") response = requests.get( - f"{ha_url}/api/states/{entity_id}", + entity_url, headers={"Authorization": f"Bearer {ha_token}"}, timeout=30, ) @@ -24,10 +29,7 @@ def _get_entity_state(ha_url: str, ha_token: str, entity_id: str) -> dict[str, A def _format_location(latitude: str, longitude: str) -> str: """Render a friendly location response.""" - return ( - f"Van location: {latitude}, {longitude}\n" - f"https://maps.google.com/?q={latitude},{longitude}" - ) + return f"Van location: {latitude}, {longitude}\nhttps://maps.google.com/?q={latitude},{longitude}" def handle_location_request( @@ -41,10 +43,14 @@ def handle_location_request( signal.reply(message, "Location command is not configured (missing HA_URL or HA_TOKEN).") return + lat_payload = None + lon_payload = None try: lat_payload = _get_entity_state(ha_url, ha_token, "sensor.van_last_known_latitude") lon_payload = _get_entity_state(ha_url, ha_token, "sensor.van_last_known_longitude") except requests.RequestException: + logger.exception("Couldn't fetch van location from Home Assistant right now.") + logger.debug(f"{ha_url=} {lat_payload=} {lon_payload=}") signal.reply(message, "Couldn't fetch van location from Home Assistant right now.") return diff --git a/python/signal_bot/device_registry.py b/python/signal_bot/device_registry.py index 55970c4..5d0737a 100644 --- a/python/signal_bot/device_registry.py +++ b/python/signal_bot/device_registry.py @@ -10,7 +10,7 @@ from sqlalchemy import delete, select from sqlalchemy.orm import Session from python.common import utcnow -from python.orm.richie.signal_device import RoleRecord, SignalDevice +from python.orm.signal_bot.models import RoleRecord, SignalDevice from python.signal_bot.models import Role, TrustLevel if TYPE_CHECKING: diff --git a/python/signal_bot/llm_client.py b/python/signal_bot/llm_client.py index 0ea3e05..a6c9a5f 100644 --- a/python/signal_bot/llm_client.py +++ b/python/signal_bot/llm_client.py @@ -21,10 +21,18 @@ class LLMClient: temperature: Sampling temperature. """ - def __init__(self, model: str, host: str, port: int = 11434, *, temperature: float = 0.1) -> None: + def __init__( + self, + *, + model: str, + host: str, + port: int = 11434, + temperature: float = 0.1, + timeout: int = 300, + ) -> None: self.model = model self.temperature = temperature - self._client = httpx.Client(base_url=f"http://{host}:{port}", timeout=120) + self._client = httpx.Client(base_url=f"http://{host}:{port}", timeout=timeout) def chat(self, prompt: str, image_data: bytes | None = None, system: str | None = None) -> str: """Send a text prompt and return the response.""" diff --git a/python/signal_bot/main.py b/python/signal_bot/main.py index 9bdf289..5432765 100644 --- a/python/signal_bot/main.py +++ b/python/signal_bot/main.py @@ -11,12 +11,14 @@ if TYPE_CHECKING: from collections.abc import Callable import typer +from alembic.command import upgrade from sqlalchemy.orm import Session from tenacity import before_sleep_log, retry, stop_after_attempt, wait_exponential from python.common import configure_logger, utcnow +from python.database_cli import DATABASES from python.orm.common import get_postgres_engine -from python.orm.richie.dead_letter_message import DeadLetterMessage +from python.orm.signal_bot.models import DeadLetterMessage from python.signal_bot.commands.inventory import handle_inventory_update from python.signal_bot.commands.location import handle_location_request from python.signal_bot.device_registry import DeviceRegistry, sync_roles @@ -181,7 +183,7 @@ class Bot: def main( - log_level: Annotated[str, typer.Option()] = "INFO", + log_level: Annotated[str, typer.Option()] = "DEBUG", llm_timeout: Annotated[int, typer.Option()] = 600, ) -> None: """Run the Signal command and control bot.""" @@ -200,6 +202,8 @@ def main( error = "INVENTORY_API_URL environment variable not set" raise ValueError(error) + signal_bot_config = DATABASES["signal_bot"].alembic_config() + upgrade(signal_bot_config, "head") engine = get_postgres_engine(name="SIGNALBOT") sync_roles(engine) config = BotConfig( diff --git a/systems/jeeves/services/postgress.nix b/systems/jeeves/services/postgress.nix index 4f76549..ab961a0 100644 --- a/systems/jeeves/services/postgress.nix +++ b/systems/jeeves/services/postgress.nix @@ -31,7 +31,7 @@ in local gitea gitea trust # signalbot - local richie signalbot trust + local signalbot signalbot trust # math local postgres math trust @@ -103,6 +103,7 @@ in } { name = "signalbot"; + ensureDBOwnership = true; ensureClauses = { login = true; }; @@ -114,6 +115,7 @@ in "math" "n8n" "richie" + "signalbot" ]; # Thank you NotAShelf # https://github.com/NotAShelf/nyx/blob/d407b4d6e5ab7f60350af61a3d73a62a5e9ac660/modules/core/roles/server/system/services/databases/postgresql.nix#L74 diff --git a/systems/jeeves/services/signal_bot.nix b/systems/jeeves/services/signal_bot.nix index 7b16055..a74ca6f 100644 --- a/systems/jeeves/services/signal_bot.nix +++ b/systems/jeeves/services/signal_bot.nix @@ -26,7 +26,7 @@ in environment = { PYTHONPATH = "${inputs.self}"; - SIGNALBOT_DB = "richie"; + SIGNALBOT_DB = "signalbot"; SIGNALBOT_USER = "signalbot"; SIGNALBOT_HOST = "/run/postgresql"; SIGNALBOT_PORT = "5432"; @@ -34,6 +34,7 @@ in serviceConfig = { Type = "simple"; + WorkingDirectory = "${inputs.self}"; User = "signalbot"; Group = "signalbot"; EnvironmentFile = "${vars.secrets}/services/signal-bot";