Add support for new reaction stuff
* Custom emojis in reactions * Premium users can react 3 times to a single message * Reactions to recent messages are now polled on read receipt
This commit is contained in:
@@ -152,6 +152,17 @@ class Message:
|
||||
rows = await cls.db.fetch(q, mx_room, tg_space, *mxids)
|
||||
return [cls._from_row(row) for row in rows]
|
||||
|
||||
@classmethod
|
||||
async def find_recent(
|
||||
cls, mx_room: RoomID, not_sender: TelegramID, limit: int = 20
|
||||
) -> list[Message]:
|
||||
q = f"""
|
||||
SELECT {cls.columns} FROM message
|
||||
WHERE mx_room=$1 AND sender<>$2
|
||||
ORDER BY tgid DESC LIMIT $3
|
||||
"""
|
||||
return [cls._from_row(row) for row in await cls.db.fetch(q, mx_room, not_sender, limit)]
|
||||
|
||||
@classmethod
|
||||
async def replace_temp_mxid(cls, temp_mxid: str, mx_room: RoomID, real_mxid: EventID) -> None:
|
||||
q = "UPDATE message SET mxid=$1 WHERE mxid=$2 AND mx_room=$3"
|
||||
|
||||
@@ -50,6 +50,7 @@ class Puppet:
|
||||
avatar_set: bool
|
||||
is_bot: bool | None
|
||||
is_channel: bool
|
||||
is_premium: bool
|
||||
|
||||
custom_mxid: UserID | None
|
||||
access_token: str | None
|
||||
@@ -67,7 +68,8 @@ class Puppet:
|
||||
columns: ClassVar[str] = (
|
||||
"id, is_registered, displayname, displayname_source, displayname_contact, "
|
||||
"displayname_quality, disable_updates, username, phone, photo_id, avatar_url, "
|
||||
"name_set, avatar_set, is_bot, is_channel, custom_mxid, access_token, next_batch, base_url"
|
||||
"name_set, avatar_set, is_bot, is_channel, is_premium, "
|
||||
"custom_mxid, access_token, next_batch, base_url"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -108,6 +110,7 @@ class Puppet:
|
||||
self.avatar_set,
|
||||
self.is_bot,
|
||||
self.is_channel,
|
||||
self.is_premium,
|
||||
self.custom_mxid,
|
||||
self.access_token,
|
||||
self.next_batch,
|
||||
@@ -120,7 +123,7 @@ class Puppet:
|
||||
SET is_registered=$2, displayname=$3, displayname_source=$4, displayname_contact=$5,
|
||||
displayname_quality=$6, disable_updates=$7, username=$8, phone=$9, photo_id=$10,
|
||||
avatar_url=$11, name_set=$12, avatar_set=$13, is_bot=$14, is_channel=$15,
|
||||
custom_mxid=$16, access_token=$17, next_batch=$18, base_url=$19
|
||||
is_premium=$16, custom_mxid=$17, access_token=$18, next_batch=$19, base_url=$20
|
||||
WHERE id=$1
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
@@ -130,8 +133,9 @@ class Puppet:
|
||||
INSERT INTO puppet (
|
||||
id, is_registered, displayname, displayname_source, displayname_contact,
|
||||
displayname_quality, disable_updates, username, phone, photo_id, avatar_url, name_set,
|
||||
avatar_set, is_bot, is_channel, custom_mxid, access_token, next_batch, base_url
|
||||
avatar_set, is_bot, is_channel, is_premium, custom_mxid, access_token, next_batch,
|
||||
base_url
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,
|
||||
$19)
|
||||
$19, $20)
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
||||
@@ -19,6 +19,7 @@ from typing import TYPE_CHECKING, ClassVar
|
||||
|
||||
from asyncpg import Record
|
||||
from attr import dataclass
|
||||
from telethon.tl.types import ReactionCustomEmoji, ReactionEmoji, TypeReaction
|
||||
|
||||
from mautrix.types import EventID, RoomID
|
||||
from mautrix.util.async_db import Database
|
||||
@@ -58,9 +59,10 @@ class Reaction:
|
||||
@classmethod
|
||||
async def get_by_sender(
|
||||
cls, mxid: EventID, mx_room: RoomID, tg_sender: TelegramID
|
||||
) -> Reaction | None:
|
||||
) -> list[Reaction]:
|
||||
q = f"SELECT {cls.columns} FROM reaction WHERE msg_mxid=$1 AND mx_room=$2 AND tg_sender=$3"
|
||||
return cls._from_row(await cls.db.fetchrow(q, mxid, mx_room, tg_sender))
|
||||
rows = await cls.db.fetch(q, mxid, mx_room, tg_sender)
|
||||
return [cls._from_row(row) for row in rows]
|
||||
|
||||
@classmethod
|
||||
async def get_all_by_message(cls, mxid: EventID, mx_room: RoomID) -> list[Reaction]:
|
||||
@@ -68,6 +70,13 @@ class Reaction:
|
||||
rows = await cls.db.fetch(q, mxid, mx_room)
|
||||
return [cls._from_row(row) for row in rows]
|
||||
|
||||
@property
|
||||
def telegram(self) -> TypeReaction:
|
||||
if self.reaction.isdecimal():
|
||||
return ReactionCustomEmoji(document_id=int(self.reaction))
|
||||
else:
|
||||
return ReactionEmoji(emoticon=self.reaction)
|
||||
|
||||
@property
|
||||
def _values(self):
|
||||
return (
|
||||
@@ -81,11 +90,11 @@ class Reaction:
|
||||
async def save(self) -> None:
|
||||
q = """
|
||||
INSERT INTO reaction (mxid, mx_room, msg_mxid, tg_sender, reaction)
|
||||
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (msg_mxid, mx_room, tg_sender)
|
||||
DO UPDATE SET mxid=$1, reaction=$5
|
||||
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (msg_mxid, mx_room, tg_sender, reaction)
|
||||
DO UPDATE SET mxid=excluded.mxid
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
||||
async def delete(self) -> None:
|
||||
q = "DELETE FROM reaction WHERE msg_mxid=$1 AND mx_room=$2 AND tg_sender=$3"
|
||||
await self.db.execute(q, self.msg_mxid, self.mx_room, self.tg_sender)
|
||||
q = "DELETE FROM reaction WHERE msg_mxid=$1 AND mx_room=$2 AND tg_sender=$3 AND reaction=$4"
|
||||
await self.db.execute(q, self.msg_mxid, self.mx_room, self.tg_sender, self.reaction)
|
||||
|
||||
@@ -82,6 +82,11 @@ class TelegramFile:
|
||||
file.thumbnail = await cls.get(thumbnail_id, _thumbnail=True)
|
||||
return file
|
||||
|
||||
@classmethod
|
||||
async def find_by_mxc(cls, mxc: ContentURI) -> TelegramFile | None:
|
||||
q = f"SELECT {cls.columns} FROM telegram_file WHERE mxc=$1"
|
||||
return cls._from_row(await cls.db.fetchrow(q, mxc))
|
||||
|
||||
async def insert(self) -> None:
|
||||
q = (
|
||||
"INSERT INTO telegram_file (id, mxc, mime_type, was_converted, size, width, height, "
|
||||
|
||||
@@ -15,4 +15,5 @@ from . import (
|
||||
v10_more_backfill_fields,
|
||||
v11_backfill_queue,
|
||||
v12_message_sender,
|
||||
v13_multiple_reactions,
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ async def create_latest_tables(conn: Connection) -> int:
|
||||
tg_username TEXT,
|
||||
tg_phone TEXT,
|
||||
is_bot BOOLEAN NOT NULL DEFAULT false,
|
||||
is_premium BOOLEAN NOT NULL DEFAULT false,
|
||||
saved_contacts INTEGER NOT NULL DEFAULT 0
|
||||
)"""
|
||||
)
|
||||
@@ -78,7 +79,7 @@ async def create_latest_tables(conn: Connection) -> int:
|
||||
tg_sender BIGINT,
|
||||
reaction TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (msg_mxid, mx_room, tg_sender),
|
||||
PRIMARY KEY (msg_mxid, mx_room, tg_sender, reaction),
|
||||
UNIQUE (mxid, mx_room)
|
||||
)"""
|
||||
)
|
||||
@@ -111,6 +112,7 @@ async def create_latest_tables(conn: Connection) -> int:
|
||||
avatar_set BOOLEAN NOT NULL DEFAULT false,
|
||||
is_bot BOOLEAN,
|
||||
is_channel BOOLEAN NOT NULL DEFAULT false,
|
||||
is_premium BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
access_token TEXT,
|
||||
custom_mxid TEXT,
|
||||
@@ -135,6 +137,7 @@ async def create_latest_tables(conn: Connection) -> int:
|
||||
ON UPDATE CASCADE ON DELETE SET NULL
|
||||
)"""
|
||||
)
|
||||
await conn.execute("CREATE INDEX telegram_file_mxc_idx ON telegram_file(mxc)")
|
||||
await conn.execute(
|
||||
"""CREATE TABLE bot_chat (
|
||||
id BIGINT PRIMARY KEY,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# mautrix-telegram - A Matrix-Telegram puppeting bridge
|
||||
# Copyright (C) 2022 Tulir Asokan
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from mautrix.util.async_db import Connection, Scheme
|
||||
|
||||
from . import upgrade_table
|
||||
|
||||
|
||||
@upgrade_table.register(description="Allow multiple reactions from the same user")
|
||||
async def upgrade_v13(conn: Connection, scheme: Scheme) -> None:
|
||||
await conn.execute("CREATE INDEX telegram_file_mxc_idx ON telegram_file(mxc)")
|
||||
await conn.execute('ALTER TABLE "user" ADD COLUMN is_premium BOOLEAN NOT NULL DEFAULT false')
|
||||
await conn.execute("ALTER TABLE puppet ADD COLUMN is_premium BOOLEAN NOT NULL DEFAULT false")
|
||||
if scheme == Scheme.POSTGRES:
|
||||
await conn.execute(
|
||||
"""
|
||||
ALTER TABLE reaction
|
||||
DROP CONSTRAINT reaction_pkey,
|
||||
ADD CONSTRAINT reaction_pkey PRIMARY KEY (msg_mxid, mx_room, tg_sender, reaction)
|
||||
"""
|
||||
)
|
||||
else:
|
||||
await conn.execute(
|
||||
"""CREATE TABLE new_reaction (
|
||||
mxid TEXT NOT NULL,
|
||||
mx_room TEXT NOT NULL,
|
||||
msg_mxid TEXT NOT NULL,
|
||||
tg_sender BIGINT,
|
||||
reaction TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (msg_mxid, mx_room, tg_sender, reaction),
|
||||
UNIQUE (mxid, mx_room)
|
||||
)"""
|
||||
)
|
||||
await conn.execute(
|
||||
"""
|
||||
INSERT INTO new_reaction (mxid, mx_room, msg_mxid, tg_sender, reaction)
|
||||
SELECT mxid, mx_room, msg_mxid, tg_sender, reaction FROM reaction
|
||||
"""
|
||||
)
|
||||
await conn.execute("DROP TABLE reaction")
|
||||
await conn.execute("ALTER TABLE new_reaction RENAME TO reaction")
|
||||
@@ -37,6 +37,7 @@ class User:
|
||||
tg_username: str | None
|
||||
tg_phone: str | None
|
||||
is_bot: bool
|
||||
is_premium: bool
|
||||
saved_contacts: int
|
||||
|
||||
@classmethod
|
||||
@@ -45,7 +46,9 @@ class User:
|
||||
return None
|
||||
return cls(**row)
|
||||
|
||||
columns: ClassVar[str] = "mxid, tgid, tg_username, tg_phone, is_bot, saved_contacts"
|
||||
columns: ClassVar[str] = ", ".join(
|
||||
("mxid", "tgid", "tg_username", "tg_phone", "is_bot", "is_premium", "saved_contacts")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_by_tgid(cls, tgid: TelegramID) -> User | None:
|
||||
@@ -78,21 +81,23 @@ class User:
|
||||
self.tg_username,
|
||||
self.tg_phone,
|
||||
self.is_bot,
|
||||
self.is_premium,
|
||||
self.saved_contacts,
|
||||
)
|
||||
|
||||
async def save(self) -> None:
|
||||
q = (
|
||||
'UPDATE "user" SET tgid=$2, tg_username=$3, tg_phone=$4, is_bot=$5, saved_contacts=$6 '
|
||||
"WHERE mxid=$1"
|
||||
)
|
||||
q = """
|
||||
UPDATE "user" SET tgid=$2, tg_username=$3, tg_phone=$4, is_bot=$5, is_premium=$6,
|
||||
saved_contacts=$7
|
||||
WHERE mxid=$1
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
||||
async def insert(self) -> None:
|
||||
q = (
|
||||
'INSERT INTO "user" (mxid, tgid, tg_username, tg_phone, is_bot, saved_contacts) '
|
||||
"VALUES ($1, $2, $3, $4, $5, $6)"
|
||||
)
|
||||
q = """
|
||||
INSERT INTO "user" (mxid, tgid, tg_username, tg_phone, is_bot, is_premium, saved_contacts)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
||||
async def get_contacts(self) -> list[TelegramID]:
|
||||
|
||||
Reference in New Issue
Block a user