From 31cac6fb5e75667d272bf0daae094578add09a1f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 26 Jan 2022 18:02:22 +0200 Subject: [PATCH] Remove personal filtering community stuff --- mautrix_facebook/config.py | 1 - mautrix_facebook/db/__init__.py | 4 +- mautrix_facebook/db/upgrade/__init__.py | 8 +- .../db/upgrade/v05_remove_communities.py | 45 ++++++++++ mautrix_facebook/db/user_contact.py | 75 ---------------- mautrix_facebook/db/user_portal.py | 30 ++----- mautrix_facebook/example-config.yaml | 7 -- mautrix_facebook/portal.py | 21 ++--- mautrix_facebook/user.py | 85 +------------------ 9 files changed, 69 insertions(+), 207 deletions(-) create mode 100644 mautrix_facebook/db/upgrade/v05_remove_communities.py delete mode 100644 mautrix_facebook/db/user_contact.py diff --git a/mautrix_facebook/config.py b/mautrix_facebook/config.py index 9bcb07c6..231b9796 100644 --- a/mautrix_facebook/config.py +++ b/mautrix_facebook/config.py @@ -69,7 +69,6 @@ def do_update(self, helper: ConfigUpdateHelper) -> None: copy("bridge.username_template") copy("bridge.displayname_template") copy("bridge.displayname_preference") - copy("bridge.community_template") copy("bridge.command_prefix") copy("bridge.initial_chat_sync") diff --git a/mautrix_facebook/db/__init__.py b/mautrix_facebook/db/__init__.py index a67ce08d..76c8bcd0 100644 --- a/mautrix_facebook/db/__init__.py +++ b/mautrix_facebook/db/__init__.py @@ -6,12 +6,11 @@ from .reaction import Reaction from .upgrade import upgrade_table from .user import User -from .user_contact import UserContact from .user_portal import UserPortal def init(db: Database) -> None: - for table in (Portal, Message, Reaction, User, Puppet, UserPortal, UserContact): + for table in (Portal, Message, Reaction, User, Puppet, UserPortal): table.db = db @@ -25,5 +24,4 @@ def init(db: Database) -> None: "Puppet", "User", "UserPortal", - "UserContact", ] diff --git a/mautrix_facebook/db/upgrade/__init__.py b/mautrix_facebook/db/upgrade/__init__.py index c20c3605..2c975f37 100644 --- a/mautrix_facebook/db/upgrade/__init__.py +++ b/mautrix_facebook/db/upgrade/__init__.py @@ -2,4 +2,10 @@ upgrade_table = UpgradeTable() -from . import v01_initial_revision, v02_message_oti, v03_portal_meta_set, v04_relay_mode +from . import ( + v01_initial_revision, + v02_message_oti, + v03_portal_meta_set, + v04_relay_mode, + v05_remove_communities, +) diff --git a/mautrix_facebook/db/upgrade/v05_remove_communities.py b/mautrix_facebook/db/upgrade/v05_remove_communities.py new file mode 100644 index 00000000..cf44a25a --- /dev/null +++ b/mautrix_facebook/db/upgrade/v05_remove_communities.py @@ -0,0 +1,45 @@ +# mautrix-facebook - A Matrix-Facebook Messenger 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 . +from asyncpg import Connection + +from . import upgrade_table + + +@upgrade_table.register(description="Remove community-related fields") +async def upgrade_v5(conn: Connection, scheme: str) -> None: + await conn.execute("DROP TABLE user_contact") + if scheme == "postgres": + await conn.execute("ALTER TABLE user_portal DROP COLUMN in_community") + else: + await conn.execute( + """CREATE TABLE user_portal_v5 ( + "user" BIGINT, + portal BIGINT, + portal_receiver BIGINT, + FOREIGN KEY (portal, portal_receiver) REFERENCES portal(fbid, fb_receiver) + ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY ("user") REFERENCES "user"(fbid) ON UPDATE CASCADE ON DELETE CASCADE, + PRIMARY KEY ("user", portal, portal_receiver) + )""" + ) + await conn.execute( + """ + INSERT INTO user_portal_v5 ("user", portal, portal_receiver) + SELECT "user", portal, portal_receiver FROM user_portal + """ + ) + await conn.execute("DROP TABLE user_portal") + await conn.execute("ALTER TABLE user_portal_v5 RENAME TO user_portal") diff --git a/mautrix_facebook/db/user_contact.py b/mautrix_facebook/db/user_contact.py deleted file mode 100644 index a10da10b..00000000 --- a/mautrix_facebook/db/user_contact.py +++ /dev/null @@ -1,75 +0,0 @@ -# mautrix-facebook - A Matrix-Facebook Messenger 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 . -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar - -from asyncpg import Record -from attr import dataclass - -from mautrix.util.async_db import Database - -fake_db = Database.create("") if TYPE_CHECKING else None - - -@dataclass -class UserContact: - db: ClassVar[Database] = fake_db - - user: int - contact: int - in_community: bool - - @classmethod - def _from_row(cls, row: Record | None) -> UserContact | None: - if row is None: - return None - return cls(**row) - - @classmethod - async def all(cls, user: int) -> dict[int, UserContact]: - q = 'SELECT "user", contact, in_community FROM user_contact WHERE "user"=$1' - rows = await cls.db.fetch(q, user) - return {up.contact: up for up in (cls._from_row(row) for row in rows)} - - @classmethod - async def get(cls, user: int, contact: int) -> UserContact | None: - q = 'SELECT "user", contact, in_community FROM user_contact WHERE "user"=$1 AND contact=$2' - row = await cls.db.fetchrow(q, user, contact) - return cls._from_row(row) - - async def insert(self) -> None: - q = 'INSERT INTO user_contact ("user", contact, in_community) VALUES ($1, $2, $3)' - await self.db.execute(q, self.user, self.contact, self.in_community) - - async def upsert(self) -> None: - q = ( - 'INSERT INTO user_contact ("user", contact, in_community) VALUES ($1, $2, $3) ' - 'ON CONFLICT ("user", contact) DO UPDATE SET in_community=$3' - ) - await self.db.execute(q, self.user, self.contact, self.in_community) - - async def delete(self) -> None: - q = 'DELETE FROM user_contact WHERE "user"=$1 AND contact=$2' - await self.db.execute(q, self.user, self.contact) - - async def save(self) -> None: - q = 'UPDATE user_contact SET in_community=$1 WHERE "user"=$2 AND contact=$3' - await self.db.execute(q, self.in_community, self.user, self.contact) - - @classmethod - async def delete_all(cls, user: int) -> None: - await cls.db.execute('DELETE FROM user_contact WHERE "user"=$1', user) diff --git a/mautrix_facebook/db/user_portal.py b/mautrix_facebook/db/user_portal.py index 4dad8999..56858665 100644 --- a/mautrix_facebook/db/user_portal.py +++ b/mautrix_facebook/db/user_portal.py @@ -32,7 +32,6 @@ class UserPortal: user: int portal: int portal_receiver: int - in_community: bool @classmethod def _from_row(cls, row: Record | None) -> UserPortal | None: @@ -42,48 +41,35 @@ def _from_row(cls, row: Record | None) -> UserPortal | None: @classmethod async def all(cls, user: int) -> dict[int, UserPortal]: - q = ( - 'SELECT "user", portal, portal_receiver, in_community FROM user_portal ' - 'WHERE "user"=$1' - ) + q = 'SELECT "user", portal, portal_receiver FROM user_portal WHERE "user"=$1' rows = await cls.db.fetch(q, user) return {up.portal: up for up in (cls._from_row(row) for row in rows)} @classmethod async def get(cls, user: int, portal: int, portal_receiver: int) -> UserPortal | None: q = ( - 'SELECT "user", portal, portal_receiver, in_community FROM user_portal ' + 'SELECT "user", portal, portal_receiver FROM user_portal ' 'WHERE "user"=$1 AND portal=$2 AND portal_receiver=$3' ) row = await cls.db.fetchrow(q, user, portal, portal_receiver) return cls._from_row(row) async def insert(self) -> None: - q = ( - 'INSERT INTO user_portal ("user", portal, portal_receiver, in_community) ' - "VALUES ($1, $2, $3, $4)" - ) - await self.db.execute(q, self.user, self.portal, self.portal_receiver, self.in_community) + q = 'INSERT INTO user_portal ("user", portal, portal_receiver) ' "VALUES ($1, $2, $3)" + await self.db.execute(q, self.user, self.portal, self.portal_receiver) async def upsert(self) -> None: q = ( - 'INSERT INTO user_portal ("user", portal, portal_receiver, in_community) ' - "VALUES ($1, $2, $3, $4) " - 'ON CONFLICT ("user", portal, portal_receiver) DO UPDATE SET in_community=$4' + 'INSERT INTO user_portal ("user", portal, portal_receiver) ' + "VALUES ($1, $2, $3) " + 'ON CONFLICT ("user", portal, portal_receiver) DO NOTHING' ) - await self.db.execute(q, self.user, self.portal, self.portal_receiver, self.in_community) + await self.db.execute(q, self.user, self.portal, self.portal_receiver) async def delete(self) -> None: q = 'DELETE FROM user_portal WHERE "user"=$1 AND portal=$2 AND portal_receiver=$3' await self.db.execute(q, self.user) - async def save(self) -> None: - q = ( - "UPDATE user_portal SET in_community=$1 " - 'WHERE "user"=$2 AND portal=$3 AND portal_receiver=$4' - ) - await self.db.execute(q, self.in_community, self.user, self.portal, self.portal_receiver) - @classmethod async def delete_all(cls, user: int) -> None: await cls.db.execute('DELETE FROM user_portal WHERE "user"=$1', user) diff --git a/mautrix_facebook/example-config.yaml b/mautrix_facebook/example-config.yaml index ebcdb029..e6e312d7 100644 --- a/mautrix_facebook/example-config.yaml +++ b/mautrix_facebook/example-config.yaml @@ -104,13 +104,6 @@ bridge: # Localpart template of MXIDs for Facebook users. # {userid} is replaced with the user ID of the Facebook user. username_template: "facebook_{userid}" - # Localpart template for per-user room grouping community IDs. - # The bridge will create these communities and add all of the specific user's portals to the community. - # {localpart} is the MXID localpart and {server} is the MXID server part of the user. - # (Note that, by default, non-admins might not have your homeserver's permission to create - # communities. You should set `enable_group_creation: true` in homeserver.yaml to fix this.) - # `facebook_{localpart}={server}` is a good value. - community_template: null # Displayname template for Facebook users. # {displayname} is replaced with the display name of the Facebook user # as defined below in displayname_preference. diff --git a/mautrix_facebook/portal.py b/mautrix_facebook/portal.py index 4788da9e..9f2b40b8 100644 --- a/mautrix_facebook/portal.py +++ b/mautrix_facebook/portal.py @@ -448,20 +448,11 @@ async def _update_matrix_room( self.log.warning("Canceling _update_matrix_room as update_info didn't return info") return - up = await UserPortal.get(source.fbid, self.fbid, self.fb_receiver) - if not up: - in_community = await source._community_helper.add_room(source._community_id, self.mxid) - await UserPortal( - user=source.fbid, - portal=self.fbid, - portal_receiver=self.fb_receiver, - in_community=in_community, - ).insert() - elif not up.in_community: - up.in_community = await source._community_helper.add_room( - source._community_id, self.mxid - ) - await up.save() + await UserPortal( + user=source.fbid, + portal=self.fbid, + portal_receiver=self.fb_receiver, + ).upsert() await self._sync_read_receipts(info.read_receipts.nodes) async def _sync_read_receipts(self, receipts: list[graphql.ReadReceipt]) -> None: @@ -628,12 +619,10 @@ async def _create_matrix_room( if not self.is_direct: await self._update_participants(source, info) - in_community = await source._community_helper.add_room(source._community_id, self.mxid) await UserPortal( user=source.fbid, portal=self.fbid, portal_receiver=self.fb_receiver, - in_community=in_community, ).upsert() try: diff --git a/mautrix_facebook/user.py b/mautrix_facebook/user.py index 5daac115..fa87697e 100644 --- a/mautrix_facebook/user.py +++ b/mautrix_facebook/user.py @@ -26,8 +26,6 @@ from maufbapi.mqtt import Connect, Disconnect, MQTTNotConnected, MQTTNotLoggedIn from maufbapi.types import graphql, mqtt as mqtt_t from mautrix.bridge import BaseUser, async_getter_lock -from mautrix.bridge._community import CommunityHelper, CommunityID -from mautrix.client import Client as MxClient from mautrix.errors import MNotFound from mautrix.types import ( EventID, @@ -46,7 +44,7 @@ from . import portal as po, puppet as pu from .commands import enter_2fa_code from .config import Config -from .db import User as DBUser, UserContact, UserPortal +from .db import User as DBUser, UserPortal from .util.interval import get_interval METRIC_SYNC_THREADS = Summary("bridge_sync_threads", "calls to sync_threads") @@ -120,9 +118,6 @@ class User(DBUser, BaseUser): _logged_in_info: graphql.LoggedInUser | None _logged_in_info_time: float - _community_helper: CommunityHelper - _community_id: CommunityID | None - def __init__( self, mxid: UserID, @@ -147,7 +142,6 @@ def __init__( self._connection_time = time.monotonic() self._prev_thread_sync = -10 self._prev_reconnect_fail_refresh = time.monotonic() - self._community_id = None self._sync_lock = SimpleLock( "Waiting for thread sync to finish before handling %s", log=self.log ) @@ -168,7 +162,6 @@ def init_cls(cls, bridge: "MessengerBridge") -> AsyncIterable[Awaitable[bool]]: cls.config = bridge.config cls.az = bridge.az cls.loop = bridge.loop - cls._community_helper = CommunityHelper(cls.az) cls.temp_disconnect_notices = bridge.config["bridge.temporary_disconnect_notices"] return (user.load_session() async for user in cls.all_logged_in()) @@ -418,7 +411,6 @@ async def logout(self, remove_fbid: bool = True) -> bool: self.mqtt = None if self.fbid and remove_fbid: - await UserContact.delete_all(self.fbid) await UserPortal.delete_all(self.fbid) del self.by_fbid[self.fbid] self.fbid = None @@ -439,63 +431,9 @@ async def post_login(self) -> None: except Exception: self.log.exception("Failed to automatically enable custom puppet") - await self._create_community() if await self.sync_threads(): self.start_listen() - async def _create_community(self) -> None: - template = self.config["bridge.community_template"] - if not template: - return - localpart, server = MxClient.parse_user_id(self.mxid) - community_localpart = template.format(localpart=localpart, server=server) - self.log.debug(f"Creating personal filtering community {community_localpart}...") - self._community_id, created = await self._community_helper.create(community_localpart) - if created: - await self._community_helper.update( - self._community_id, - name="Facebook Messenger", - avatar_url=self.config["appservice.bot_avatar"], - short_desc="Your Facebook bridged chats", - ) - await self._community_helper.invite(self._community_id, self.mxid) - - async def _add_community( - self, - up: UserPortal | None, - contact: UserContact | None, - portal: po.Portal, - puppet: pu.Puppet | None, - ) -> None: - if portal.mxid: - if not up or not up.in_community: - ic = await self._community_helper.add_room(self._community_id, portal.mxid) - if up and ic: - up.in_community = True - await up.save() - elif not up: - await UserPortal( - user=self.fbid, - in_community=ic, - portal=portal.fbid, - portal_receiver=portal.fb_receiver, - ).insert() - if puppet: - await self._add_community_puppet(contact, puppet) - - async def _add_community_puppet( - self, contact: UserContact | None, puppet: "pu.Puppet" - ) -> None: - if not contact or not contact.in_community: - await puppet.default_mxid_intent.ensure_registered() - ic = await self._community_helper.join(self._community_id, puppet.default_mxid_intent) - if contact and ic: - contact.in_community = True - await contact.save() - elif not contact: - # This uses upsert instead of insert as a hacky fix for potential conflicts - await UserContact(user=self.fbid, contact=puppet.fbid, in_community=ic).upsert() - async def get_direct_chats(self) -> dict[UserID, list[RoomID]]: return { pu.Puppet.get_mxid_from_id(portal.fbid): [portal.mxid] @@ -533,8 +471,6 @@ async def sync_threads(self) -> bool: async def _sync_threads(self) -> None: sync_count = self.config["bridge.initial_chat_sync"] self.log.debug("Fetching threads...") - ups = await UserPortal.all(self.fbid) - contacts = await UserContact.all(self.fbid) # TODO paginate with 20 threads per request resp = await self.client.fetch_thread_list(thread_count=sync_count) self.seq_id = int(resp.sync_sequence_id) @@ -545,31 +481,16 @@ async def _sync_threads(self) -> None: await self.push_bridge_state(BridgeStateEvent.BACKFILLING) for thread in resp.nodes: try: - await self._sync_thread(thread, ups, contacts) + await self._sync_thread(thread) except Exception: self.log.exception("Failed to sync thread %s", thread.id) await self.update_direct_chats() - async def _sync_thread( - self, - thread: graphql.Thread, - ups: dict[int, UserPortal], - contacts: dict[int, UserContact], - ) -> None: + async def _sync_thread(self, thread: graphql.Thread) -> None: self.log.debug(f"Syncing thread {thread.thread_key.id}") is_direct = bool(thread.thread_key.other_user_id) portal = await po.Portal.get_by_thread(thread.thread_key, self.fbid) - puppet = ( - await pu.Puppet.get_by_fbid(thread.thread_key.other_user_id) if is_direct else None - ) - - await self._add_community( - ups.get(portal.fbid, None), - contacts.get(puppet.fbid, None) if puppet else None, - portal, - puppet, - ) was_created = False if not portal.mxid: