Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,5 +275,18 @@ async def m012_chat_schedules_seen_and_notification_jobs(db):
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
updated_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
);
"""
)


async def m013_categories_nostr_dm_type(db):
"""
Add the Nostr direct-message type to category notifications.
"""

await db.execute(
"""
ALTER TABLE chat.categories
ADD COLUMN notify_nostr_dm_type TEXT NOT NULL DEFAULT 'nip04';
"""
)
9 changes: 9 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
from datetime import datetime, timezone
from enum import Enum

from lnbits.db import FilterModel
from pydantic import BaseModel, Field

DEFAULT_PUBLIC_NOTE = "we aim to reply as soon as possible but it may take up to 24hrs for a reply"


class NostrDmType(str, Enum):
nip04 = "nip04"
nip17 = "nip17"
nip17b = "nip17b"


class CreateCategories(BaseModel):
name: str
wallet: str | None = None
Expand All @@ -20,6 +27,7 @@ class CreateCategories(BaseModel):
public_note: str | None = DEFAULT_PUBLIC_NOTE
notify_telegram: str | None = None
notify_nostr: str | None = None
notify_nostr_dm_type: NostrDmType = NostrDmType.nip04
notify_email: str | None = None
schedule_enabled: bool | None = False
schedule_timezone: str | None = "Europe/London"
Expand Down Expand Up @@ -48,6 +56,7 @@ class Categories(BaseModel):
public_note: str | None = DEFAULT_PUBLIC_NOTE
notify_telegram: str | None = None
notify_nostr: str | None = None
notify_nostr_dm_type: NostrDmType = NostrDmType.nip04
notify_email: str | None = None
schedule_enabled: bool | None = False
schedule_timezone: str | None = "Europe/London"
Expand Down
5 changes: 5 additions & 0 deletions services.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import math
from datetime import datetime, time, timedelta, timezone
from typing import TYPE_CHECKING, cast
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

from lnbits.core.crud.users import get_user
Expand Down Expand Up @@ -43,6 +44,9 @@
CreateChatMessage,
)

if TYPE_CHECKING:
from lnbits.core.services.notifications import NostrDmType as CoreNostrDmType

MAX_PARTICIPANTS = 10
USER_EMAIL_DELAY_MINUTES = 5
TELEGRAM_REMINDER_DELAY_MINUTES = 2
Expand Down Expand Up @@ -288,6 +292,7 @@ async def _notify_new_chat(
_parse_notify_emails(category.notify_email),
message,
"chat.new",
nostr_dm_types=[cast("CoreNostrDmType", category.notify_nostr_dm_type.value)],
)


Expand Down
8 changes: 8 additions & 0 deletions static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ window.PageChat = {
data: function () {
return {
currencyOptions: ['sat'],
nostrDmTypeOptions: [
{label: 'NIP-04', value: 'nip04'},
{label: 'NIP-17', value: 'nip17'},
{label: 'NIP-17B', value: 'nip17b'}
],
categoriesFormDialog: {
show: false,
data: {
Expand All @@ -21,6 +26,7 @@ window.PageChat = {
'we aim to reply as soon as possible but it may take up to 24hrs for a reply',
notify_telegram: null,
notify_nostr: null,
notify_nostr_dm_type: 'nip04',
notify_email: null,
schedule_enabled: false,
schedule_timezone: 'Europe/London',
Expand Down Expand Up @@ -266,6 +272,7 @@ window.PageChat = {
'we aim to reply as soon as possible but it may take up to 24hrs for a reply',
notify_telegram: null,
notify_nostr: null,
notify_nostr_dm_type: 'nip04',
notify_email: null,
schedule_enabled: false,
schedule_timezone: 'Europe/London',
Expand All @@ -284,6 +291,7 @@ window.PageChat = {
async showEditCategoriesForm(data) {
this.categoriesFormDialog.data = {
...data,
notify_nostr_dm_type: data.notify_nostr_dm_type || 'nip04',
schedule_enabled:
data.schedule_enabled === true || data.schedule_enabled === 'true',
schedule_days: (data.schedule_days || '0,1,2,3,4')
Expand Down
11 changes: 11 additions & 0 deletions static/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,17 @@
hint="Optional notification target"
></q-input>

<q-select
class="q-mb-md"
filled
dense
emit-value
map-options
v-model="categoriesFormDialog.data.notify_nostr_dm_type"
:options="nostrDmTypeOptions"
label="Nostr direct message type"
></q-select>

<q-input
filled
dense
Expand Down
2 changes: 2 additions & 0 deletions tests/test_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async def test_create_and_get_categories():
chars=39,
price_chars=78.00288769676463,
denomination="sat",
notify_nostr_dm_type="nip17b",
)
categories_one = await create_categories(user_id, data)
assert categories_one.id is not None
Expand All @@ -42,6 +43,7 @@ async def test_create_and_get_categories():
assert categories_one.chars == data.chars
assert categories_one.price_chars == data.price_chars
assert categories_one.denomination == data.denomination
assert categories_one.notify_nostr_dm_type == "nip17b"

data = CreateCategories(
name="name_AwqF6ginw2mCnHfLCbEk7D",
Expand Down
29 changes: 29 additions & 0 deletions tests/test_schedule_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
get_notification_job,
)
from chat.models import ( # type: ignore[import]
Categories,
ChatNotificationJob,
ChatSession,
CreateCategories,
)
from chat.services import ( # type: ignore[import]
CHAT_TEMPLATE_DEFAULTS,
_notify_new_chat,
_render_template,
get_category_schedule_metadata,
is_chat_available_from_config,
Expand Down Expand Up @@ -78,6 +80,33 @@ def test_template_rendering_keeps_unknown_placeholders():
assert rendered == "Hello Alice, keep {unknown}"


@pytest.mark.asyncio
async def test_new_chat_uses_selected_nostr_dm_type(monkeypatch):
sent = []

async def fake_send_notification(*args, **kwargs):
sent.append((args, kwargs))

monkeypatch.setattr("chat.services.send_notification", fake_send_notification)
category = Categories(
id=urlsafe_short_hash(),
user_id=uuid4().hex,
name="support",
notify_nostr="support@example.com",
notify_nostr_dm_type="nip17",
)
chat = ChatSession(
id=urlsafe_short_hash(),
categories_id=category.id,
)

await _notify_new_chat(category, chat, first_message="Hello")

assert len(sent) == 1
assert sent[0][0][1] == ["support@example.com"]
assert sent[0][1] == {"nostr_dm_types": ["nip17"]}


async def _create_chat_with_admin_message(seen: bool) -> tuple[str, str, str]:
category = await create_categories(
uuid4().hex,
Expand Down
Loading