...
 
Commits (2)
...@@ -25,7 +25,7 @@ RUN apk add --no-cache \ ...@@ -25,7 +25,7 @@ RUN apk add --no-cache \
COPY . /opt/mautrix-twilio COPY . /opt/mautrix-twilio
WORKDIR /opt/mautrix-twilio WORKDIR /opt/mautrix-twilio
RUN pip3 install . RUN pip3 install .[phonenumbers]
VOLUME /data VOLUME /data
......
...@@ -50,15 +50,18 @@ appservice: ...@@ -50,15 +50,18 @@ appservice:
# Bridge config # Bridge config
bridge: bridge:
# Localpart template of MXIDs for remote users. # Localpart template of MXIDs for remote users.
# {userid} is replaced with the phone number of the user (international format without +). # {userid} is replaced with the phone number of the user (plain/E.164 international format).
username_template: "twilio_whatsapp_{userid}" username_template: "twilio_whatsapp_{userid}"
# Displayname template for remote users. # Displayname template for remote users.
# {displayname} is replaced with the phone number of the user (international format without +). # {displayname} is replaced with the phone number of the user (human-readable international format).
displayname_template: "+{displayname} (WhatsApp)" displayname_template: "{displayname} (WhatsApp)"
# The prefix for commands. Only required in non-management rooms. # The prefix for commands. Only required in non-management rooms.
command_prefix: "!tw" command_prefix: "!tw"
# List of users to always invite to newly created portal rooms.
invite_users: []
# Whether or not created rooms should have federation enabled. # Whether or not created rooms should have federation enabled.
# If false, created portal rooms will never be federated. # If false, created portal rooms will never be federated.
federate_rooms: true federate_rooms: true
......
...@@ -32,6 +32,8 @@ class Config(BaseBridgeConfig): ...@@ -32,6 +32,8 @@ class Config(BaseBridgeConfig):
copy("bridge.username_template") copy("bridge.username_template")
copy("bridge.command_prefix") copy("bridge.command_prefix")
copy("bridge.invite_users")
copy("bridge.federate_rooms") copy("bridge.federate_rooms")
copy("bridge.initial_state") copy("bridge.initial_state")
......
...@@ -118,7 +118,7 @@ class Portal(BasePortal): ...@@ -118,7 +118,7 @@ class Portal(BasePortal):
initial_state[EventType.ROOM_POWER_LEVELS].content.users[self.az.bot_mxid] = 100 initial_state[EventType.ROOM_POWER_LEVELS].content.users[self.az.bot_mxid] = 100
self.mxid = await self.az.intent.create_room(name=puppet.displayname, self.mxid = await self.az.intent.create_room(name=puppet.displayname,
invitees=[self.main_intent.mxid, invitees=[self.main_intent.mxid,
"@tulir:pc.mau.dev"], *config["bridge.invite_users"]],
creation_content=creation_content, creation_content=creation_content,
initial_state=list(initial_state.values())) initial_state=list(initial_state.values()))
if not self.mxid: if not self.mxid:
...@@ -130,7 +130,8 @@ class Portal(BasePortal): ...@@ -130,7 +130,8 @@ class Portal(BasePortal):
return self.mxid return self.mxid
async def handle_twilio_message(self, message: TwilioMessageEvent) -> None: async def handle_twilio_message(self, message: TwilioMessageEvent) -> None:
await self.create_matrix_room() if not await self.create_matrix_room():
return
mxid = None mxid = None
if message.media: if message.media:
......
...@@ -26,6 +26,11 @@ from .twilio import TwilioUserID ...@@ -26,6 +26,11 @@ from .twilio import TwilioUserID
if TYPE_CHECKING: if TYPE_CHECKING:
from .context import Context from .context import Context
try:
import phonenumbers
except ImportError:
phonenumbers = None
config: Config config: Config
...@@ -38,6 +43,7 @@ class Puppet(BasePuppet): ...@@ -38,6 +43,7 @@ class Puppet(BasePuppet):
by_twid: Dict[TwilioUserID, 'Puppet'] = {} by_twid: Dict[TwilioUserID, 'Puppet'] = {}
twid: TwilioUserID twid: TwilioUserID
_formatted_number: Optional[str]
_db_instance: Optional[DBPuppet] _db_instance: Optional[DBPuppet]
...@@ -46,6 +52,7 @@ class Puppet(BasePuppet): ...@@ -46,6 +52,7 @@ class Puppet(BasePuppet):
super().__init__() super().__init__()
self.twid = twid self.twid = twid
self.is_registered = is_registered self.is_registered = is_registered
self._formatted_number = None
self._db_instance = db_instance self._db_instance = db_instance
self.intent = self.az.intent.user(self.mxid) self.intent = self.az.intent.user(self.mxid)
self.log = self.log.getChild(self.twid) self.log = self.log.getChild(self.twid)
...@@ -55,13 +62,21 @@ class Puppet(BasePuppet): ...@@ -55,13 +62,21 @@ class Puppet(BasePuppet):
def phone_number(self) -> int: def phone_number(self) -> int:
return self.twid_template.parse(self.twid) return self.twid_template.parse(self.twid)
@property
def formatted_phone_number(self) -> str:
if not self._formatted_number:
parsed = phonenumbers.parse(f"+{self.phone_number}")
fmt = phonenumbers.PhoneNumberFormat.INTERNATIONAL
self._formatted_number = phonenumbers.format_number(parsed, fmt)
return self._formatted_number
@property @property
def mxid(self) -> UserID: def mxid(self) -> UserID:
return UserID(self.mxid_template.format_full(str(self.phone_number))) return UserID(self.mxid_template.format_full(str(self.phone_number)))
@property @property
def displayname(self) -> str: def displayname(self) -> str:
return self.displayname_template.format_full(str(self.phone_number)) return self.displayname_template.format_full(self.formatted_phone_number)
@property @property
def db_instance(self) -> DBPuppet: def db_instance(self) -> DBPuppet:
......
...@@ -30,6 +30,9 @@ setuptools.setup( ...@@ -30,6 +30,9 @@ setuptools.setup(
"SQLAlchemy>=1.2,<2", "SQLAlchemy>=1.2,<2",
"alembic>=1,<2", "alembic>=1,<2",
], ],
extras_require={
"phonenumbers": ["phonenumbers>=8,<9"],
},
classifiers=[ classifiers=[
"Development Status :: 2 - Pre-Alpha", "Development Status :: 2 - Pre-Alpha",
......