Skip to content

Commit 2ff2273

Browse files
authored
Merge pull request #47 from pythonitalia/feature/targeted-announce
feat: targeted announce
2 parents d2e53ab + 7556f67 commit 2ff2273

12 files changed

Lines changed: 418 additions & 38 deletions

File tree

schema.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ CREATE TABLE IF NOT EXISTS globally_verified_users (
5555

5656
CREATE TABLE IF NOT EXISTS bot_chats (
5757
chat_id BIGINT PRIMARY KEY,
58+
title TEXT,
5859
added_at TIMESTAMPTZ DEFAULT NOW()
5960
);
6061

@@ -87,3 +88,7 @@ CREATE TABLE IF NOT EXISTS welcomed_users (
8788
-- Add welcome_delay_minutes column to group_settings (idempotent).
8889
ALTER TABLE group_settings
8990
ADD COLUMN IF NOT EXISTS welcome_delay_minutes INTEGER;
91+
92+
-- Add title column to bot_chats for group name lookup (idempotent).
93+
ALTER TABLE bot_chats
94+
ADD COLUMN IF NOT EXISTS title TEXT;

src/python_italy_bot/db/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .base import AsyncRepository, Repository
44
from .in_memory import InMemoryRepository
5-
from .models import Ban, Mute, Report
5+
from .models import Ban, Chat, Mute, Report
66
from .postgres import PostgresRepository
77

88
__all__ = [
@@ -11,6 +11,7 @@
1111
"InMemoryRepository",
1212
"PostgresRepository",
1313
"Ban",
14+
"Chat",
1415
"Mute",
1516
"Report",
1617
"create_repository",

src/python_italy_bot/db/base.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from abc import ABC, abstractmethod
44

5-
from .models import KnownUser
5+
from .models import Chat, KnownUser
66

77

88
class Repository(ABC):
@@ -109,7 +109,7 @@ def mark_globally_verified(self, user_id: int) -> None:
109109
...
110110

111111
@abstractmethod
112-
def register_chat(self, chat_id: int) -> None:
112+
def register_chat(self, chat_id: int, title: str | None = None) -> None:
113113
"""Track a chat where the bot is active."""
114114
...
115115

@@ -118,6 +118,16 @@ def get_all_chats(self) -> list[int]:
118118
"""Get all tracked chat IDs."""
119119
...
120120

121+
@abstractmethod
122+
def get_all_chats_with_titles(self) -> list[Chat]:
123+
"""Get all tracked chats with their titles."""
124+
...
125+
126+
@abstractmethod
127+
def find_chats_by_title(self, query: str) -> list[Chat]:
128+
"""Find tracked chats whose title contains the query (case-insensitive)."""
129+
...
130+
121131
@abstractmethod
122132
def add_global_ban(
123133
self,
@@ -290,7 +300,7 @@ async def mark_globally_verified(self, user_id: int) -> None:
290300
...
291301

292302
@abstractmethod
293-
async def register_chat(self, chat_id: int) -> None:
303+
async def register_chat(self, chat_id: int, title: str | None = None) -> None:
294304
"""Track a chat where the bot is active."""
295305
...
296306

@@ -299,6 +309,16 @@ async def get_all_chats(self) -> list[int]:
299309
"""Get all tracked chat IDs."""
300310
...
301311

312+
@abstractmethod
313+
async def get_all_chats_with_titles(self) -> list[Chat]:
314+
"""Get all tracked chats with their titles."""
315+
...
316+
317+
@abstractmethod
318+
async def find_chats_by_title(self, query: str) -> list[Chat]:
319+
"""Find tracked chats whose title contains the query (case-insensitive)."""
320+
...
321+
302322
@abstractmethod
303323
async def add_global_ban(
304324
self,

src/python_italy_bot/db/in_memory.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime, timezone
44

55
from .base import AsyncRepository
6-
from .models import Ban, KnownUser, Mute, Report
6+
from .models import Ban, Chat, KnownUser, Mute, Report
77

88

99
class InMemoryRepository(AsyncRepository):
@@ -17,7 +17,7 @@ def __init__(self) -> None:
1717
self._reports: list[Report] = []
1818
self._welcome_messages: dict[int, str] = {}
1919
self._globally_verified: set[int] = set()
20-
self._bot_chats: set[int] = set()
20+
self._bot_chats: dict[int, str | None] = {}
2121
self._global_bans: dict[int, tuple[int, str | None]] = {}
2222
self._known_users: dict[int, KnownUser] = {}
2323
self._username_to_user_id: dict[str, int] = {}
@@ -141,11 +141,22 @@ async def is_globally_verified(self, user_id: int) -> bool:
141141
async def mark_globally_verified(self, user_id: int) -> None:
142142
self._globally_verified.add(user_id)
143143

144-
async def register_chat(self, chat_id: int) -> None:
145-
self._bot_chats.add(chat_id)
144+
async def register_chat(self, chat_id: int, title: str | None = None) -> None:
145+
self._bot_chats[chat_id] = title
146146

147147
async def get_all_chats(self) -> list[int]:
148-
return list(self._bot_chats)
148+
return list(self._bot_chats.keys())
149+
150+
async def get_all_chats_with_titles(self) -> list[Chat]:
151+
return [Chat(chat_id=cid, title=t) for cid, t in self._bot_chats.items()]
152+
153+
async def find_chats_by_title(self, query: str) -> list[Chat]:
154+
query_lower = query.lower()
155+
return [
156+
Chat(chat_id=cid, title=t)
157+
for cid, t in self._bot_chats.items()
158+
if t and query_lower in t.lower()
159+
]
149160

150161
async def add_global_ban(
151162
self,

src/python_italy_bot/db/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,11 @@ class Report:
4848
message_id: int | None
4949
reason: str | None
5050
created_at: datetime
51+
52+
53+
@dataclass
54+
class Chat:
55+
"""A tracked chat where the bot is active."""
56+
57+
chat_id: int
58+
title: str | None

src/python_italy_bot/db/postgres.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from psycopg_pool import AsyncConnectionPool
44

55
from .base import AsyncRepository
6-
from .models import KnownUser
6+
from .models import Chat, KnownUser
77

88

99
class PostgresRepository(AsyncRepository):
@@ -208,15 +208,15 @@ async def mark_globally_verified(self, user_id: int) -> None:
208208
(user_id,),
209209
)
210210

211-
async def register_chat(self, chat_id: int) -> None:
211+
async def register_chat(self, chat_id: int, title: str | None = None) -> None:
212212
async with self._pool.connection() as conn:
213213
await conn.execute(
214214
"""
215-
INSERT INTO bot_chats (chat_id)
216-
VALUES (%s)
217-
ON CONFLICT (chat_id) DO NOTHING
215+
INSERT INTO bot_chats (chat_id, title)
216+
VALUES (%s, %s)
217+
ON CONFLICT (chat_id) DO UPDATE SET title = EXCLUDED.title
218218
""",
219-
(chat_id,),
219+
(chat_id, title),
220220
)
221221

222222
async def get_all_chats(self) -> list[int]:
@@ -226,6 +226,25 @@ async def get_all_chats(self) -> list[int]:
226226
rows = await cur.fetchall()
227227
return [row[0] for row in rows]
228228

229+
async def get_all_chats_with_titles(self) -> list[Chat]:
230+
async with self._pool.connection() as conn:
231+
async with conn.cursor() as cur:
232+
await cur.execute(
233+
"SELECT chat_id, title FROM bot_chats ORDER BY chat_id"
234+
)
235+
rows = await cur.fetchall()
236+
return [Chat(chat_id=row[0], title=row[1]) for row in rows]
237+
238+
async def find_chats_by_title(self, query: str) -> list[Chat]:
239+
async with self._pool.connection() as conn:
240+
async with conn.cursor() as cur:
241+
await cur.execute(
242+
"SELECT chat_id, title FROM bot_chats WHERE title ILIKE %s",
243+
(f"%{query}%",),
244+
)
245+
rows = await cur.fetchall()
246+
return [Chat(chat_id=row[0], title=row[1]) for row in rows]
247+
229248
async def add_global_ban(
230249
self,
231250
user_id: int,

0 commit comments

Comments
 (0)