Add support for contact messages
This commit is contained in:
+19
-62
@@ -63,6 +63,7 @@ from telethon.tl.types import (
|
|||||||
DocumentAttributeImageSize, GeoPoint, InputChatUploadedPhoto, MessageActionChatEditPhoto,
|
DocumentAttributeImageSize, GeoPoint, InputChatUploadedPhoto, MessageActionChatEditPhoto,
|
||||||
MessageMediaGeo, SendMessageCancelAction, SendMessageTypingAction, TypeInputPeer,
|
MessageMediaGeo, SendMessageCancelAction, SendMessageTypingAction, TypeInputPeer,
|
||||||
UpdateNewMessage, InputMediaUploadedDocument, InputMediaUploadedPhoto, TypeMessage,
|
UpdateNewMessage, InputMediaUploadedDocument, InputMediaUploadedPhoto, TypeMessage,
|
||||||
|
MessageMediaContact,
|
||||||
)
|
)
|
||||||
|
|
||||||
from mautrix.errors import MatrixRequestError, IntentError, MForbidden
|
from mautrix.errors import MatrixRequestError, IntentError, MForbidden
|
||||||
@@ -1607,7 +1608,7 @@ class Portal(DBPortal, BasePortal):
|
|||||||
async def handle_matrix_power_levels(
|
async def handle_matrix_power_levels(
|
||||||
self, sender: u.User, new_users: dict[UserID, int],
|
self, sender: u.User, new_users: dict[UserID, int],
|
||||||
old_users: dict[UserID, int], event_id: EventID | None
|
old_users: dict[UserID, int], event_id: EventID | None
|
||||||
) -> None:
|
) -> None:
|
||||||
# TODO handle all power level changes and bridge exact admin rights to supergroups/channels
|
# TODO handle all power level changes and bridge exact admin rights to supergroups/channels
|
||||||
for user, level in new_users.items():
|
for user, level in new_users.items():
|
||||||
if not user or user == self.main_intent.mxid or user == sender.mxid:
|
if not user or user == self.main_intent.mxid or user == sender.mxid:
|
||||||
@@ -2025,68 +2026,12 @@ class Portal(DBPortal, BasePortal):
|
|||||||
await intent.set_typing(self.mxid, is_typing=False)
|
await intent.set_typing(self.mxid, is_typing=False)
|
||||||
return await self._send_message(intent, content, timestamp=evt.date)
|
return await self._send_message(intent, content, timestamp=evt.date)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _format_dice(roll: MessageMediaDice) -> str:
|
|
||||||
if roll.emoticon == "\U0001F3B0":
|
|
||||||
emojis = {
|
|
||||||
0: "\U0001F36B", # "🍫",
|
|
||||||
1: "\U0001F352", # "🍒",
|
|
||||||
2: "\U0001F34B", # "🍋",
|
|
||||||
3: "7\ufe0f\u20e3" # "7️⃣",
|
|
||||||
}
|
|
||||||
res = roll.value - 1
|
|
||||||
slot1, slot2, slot3 = emojis[res % 4], emojis[res // 4 % 4], emojis[res // 16]
|
|
||||||
return f"{slot1} {slot2} {slot3} ({roll.value})"
|
|
||||||
elif roll.emoticon == "\u26BD":
|
|
||||||
results = {
|
|
||||||
1: "miss",
|
|
||||||
2: "hit the woodwork",
|
|
||||||
3: "goal", # seems to go in through the center
|
|
||||||
4: "goal",
|
|
||||||
5: "goal 🎉", # seems to go in through the top right corner, includes confetti
|
|
||||||
}
|
|
||||||
elif roll.emoticon == "\U0001F3B3":
|
|
||||||
results = {
|
|
||||||
1: "miss",
|
|
||||||
2: "1 pin down",
|
|
||||||
3: "3 pins down, split",
|
|
||||||
4: "4 pins down, split",
|
|
||||||
5: "5 pins down",
|
|
||||||
6: "strike 🎉",
|
|
||||||
}
|
|
||||||
# elif roll.emoticon == "\U0001F3C0":
|
|
||||||
# results = {
|
|
||||||
# 2: "rolled off",
|
|
||||||
# 3: "stuck",
|
|
||||||
# }
|
|
||||||
# elif roll.emoticon == "\U0001F3AF":
|
|
||||||
# results = {
|
|
||||||
# 1: "bounced off",
|
|
||||||
# 2: "outer rim",
|
|
||||||
#
|
|
||||||
# 6: "bullseye",
|
|
||||||
# }
|
|
||||||
else:
|
|
||||||
return str(roll.value)
|
|
||||||
return f"{results[roll.value]} ({roll.value})"
|
|
||||||
|
|
||||||
async def _handle_telegram_dice(
|
async def _handle_telegram_dice(
|
||||||
self, _: au.AbstractUser, intent: IntentAPI, evt: Message, relates_to: RelatesTo
|
self, _: au.AbstractUser, intent: IntentAPI, evt: Message, relates_to: RelatesTo
|
||||||
) -> EventID:
|
) -> EventID:
|
||||||
emoji_text = {
|
content = util.make_dice_event_content(evt.media)
|
||||||
"\U0001F3AF": " Dart throw",
|
content.relates_to = relates_to
|
||||||
"\U0001F3B2": " Dice roll",
|
content.external_url = self._get_external_url(evt)
|
||||||
"\U0001F3C0": " Basketball throw",
|
|
||||||
"\U0001F3B0": " Slot machine",
|
|
||||||
"\U0001F3B3": " Bowling",
|
|
||||||
"\u26BD": " Football kick"
|
|
||||||
}
|
|
||||||
roll: MessageMediaDice = evt.media
|
|
||||||
text = f"{roll.emoticon}{emoji_text.get(roll.emoticon, '')} result: {self._format_dice(roll)}"
|
|
||||||
content = TextMessageEventContent(msgtype=MessageType.TEXT, format=Format.HTML, body=text,
|
|
||||||
formatted_body=f"<h4>{text}</h4>", relates_to=relates_to,
|
|
||||||
external_url=self._get_external_url(evt))
|
|
||||||
content["net.maunium.telegram.dice"] = {"emoticon": roll.emoticon, "value": roll.value}
|
|
||||||
await intent.set_typing(self.mxid, is_typing=False)
|
await intent.set_typing(self.mxid, is_typing=False)
|
||||||
return await self._send_message(intent, content, timestamp=evt.date)
|
return await self._send_message(intent, content, timestamp=evt.date)
|
||||||
|
|
||||||
@@ -2128,14 +2073,25 @@ class Portal(DBPortal, BasePortal):
|
|||||||
override_text=override_text, override_entities=override_entities)
|
override_text=override_text, override_entities=override_entities)
|
||||||
content.msgtype = MessageType.NOTICE
|
content.msgtype = MessageType.NOTICE
|
||||||
content.external_url = self._get_external_url(evt)
|
content.external_url = self._get_external_url(evt)
|
||||||
|
content.relates_to = relates_to
|
||||||
content["net.maunium.telegram.game"] = play_id
|
content["net.maunium.telegram.game"] = play_id
|
||||||
|
|
||||||
await intent.set_typing(self.mxid, is_typing=False)
|
await intent.set_typing(self.mxid, is_typing=False)
|
||||||
return await self._send_message(intent, content, timestamp=evt.date)
|
return await self._send_message(intent, content, timestamp=evt.date)
|
||||||
|
|
||||||
|
async def _handle_telegram_contact(
|
||||||
|
self, source: au.AbstractUser, intent: IntentAPI, evt: Message, relates_to: RelatesTo
|
||||||
|
) -> EventID:
|
||||||
|
content = await util.make_contact_event_content(source, evt.media)
|
||||||
|
content.relates_to = relates_to
|
||||||
|
content.external_url = self._get_external_url(evt)
|
||||||
|
|
||||||
|
await intent.set_typing(self.mxid, is_typing=False)
|
||||||
|
return await self._send_message(intent, content, timestamp=evt.date)
|
||||||
|
|
||||||
async def handle_telegram_edit(
|
async def handle_telegram_edit(
|
||||||
self, source: au.AbstractUser, sender: p.Puppet, evt: Message
|
self, source: au.AbstractUser, sender: p.Puppet, evt: Message
|
||||||
) -> None:
|
) -> None:
|
||||||
if not self.mxid:
|
if not self.mxid:
|
||||||
self.log.trace("Ignoring edit to %d as chat has no Matrix room", evt.id)
|
self.log.trace("Ignoring edit to %d as chat has no Matrix room", evt.id)
|
||||||
return
|
return
|
||||||
@@ -2352,7 +2308,7 @@ class Portal(DBPortal, BasePortal):
|
|||||||
f" updating with data {entity!s}")
|
f" updating with data {entity!s}")
|
||||||
|
|
||||||
allowed_media = (MessageMediaPhoto, MessageMediaDocument, MessageMediaGeo,
|
allowed_media = (MessageMediaPhoto, MessageMediaDocument, MessageMediaGeo,
|
||||||
MessageMediaGame, MessageMediaDice, MessageMediaPoll,
|
MessageMediaGame, MessageMediaDice, MessageMediaPoll, MessageMediaContact,
|
||||||
MessageMediaUnsupported)
|
MessageMediaUnsupported)
|
||||||
if sender:
|
if sender:
|
||||||
intent = sender.intent_for(self)
|
intent = sender.intent_for(self)
|
||||||
@@ -2371,6 +2327,7 @@ class Portal(DBPortal, BasePortal):
|
|||||||
MessageMediaDice: self._handle_telegram_dice,
|
MessageMediaDice: self._handle_telegram_dice,
|
||||||
MessageMediaUnsupported: self._handle_telegram_unsupported,
|
MessageMediaUnsupported: self._handle_telegram_unsupported,
|
||||||
MessageMediaGame: self._handle_telegram_game,
|
MessageMediaGame: self._handle_telegram_game,
|
||||||
|
MessageMediaContact: self._handle_telegram_contact,
|
||||||
}[type(evt.media)]
|
}[type(evt.media)]
|
||||||
relates_to = await formatter.telegram_reply_to_matrix(evt, source)
|
relates_to = await formatter.telegram_reply_to_matrix(evt, source)
|
||||||
event_id = await handler(source, intent, evt, relates_to)
|
event_id = await handler(source, intent, evt, relates_to)
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ from .recursive_dict import recursive_del, recursive_set, recursive_get
|
|||||||
from .color_log import ColorFormatter
|
from .color_log import ColorFormatter
|
||||||
from .send_lock import PortalSendLock
|
from .send_lock import PortalSendLock
|
||||||
from .deduplication import PortalDedup
|
from .deduplication import PortalDedup
|
||||||
|
from .media_fallback import make_dice_event_content, make_contact_event_content
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# mautrix-telegram - A Matrix-Telegram puppeting bridge
|
||||||
|
# Copyright (C) 2021 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 __future__ import annotations
|
||||||
|
|
||||||
|
import html
|
||||||
|
|
||||||
|
from telethon.tl.types import MessageMediaDice, MessageMediaContact, PeerUser
|
||||||
|
|
||||||
|
from mautrix.types import TextMessageEventContent, MessageType, Format
|
||||||
|
|
||||||
|
from .. import puppet as pu, abstract_user as au
|
||||||
|
from ..types import TelegramID
|
||||||
|
|
||||||
|
try:
|
||||||
|
import phonenumbers
|
||||||
|
except ImportError:
|
||||||
|
phonenumbers = None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_dice(roll: MessageMediaDice) -> str:
|
||||||
|
if roll.emoticon == "\U0001F3B0":
|
||||||
|
emojis = {
|
||||||
|
0: "\U0001F36B", # "🍫",
|
||||||
|
1: "\U0001F352", # "🍒",
|
||||||
|
2: "\U0001F34B", # "🍋",
|
||||||
|
3: "7\ufe0f\u20e3" # "7️⃣",
|
||||||
|
}
|
||||||
|
res = roll.value - 1
|
||||||
|
slot1, slot2, slot3 = emojis[res % 4], emojis[res // 4 % 4], emojis[res // 16]
|
||||||
|
return f"{slot1} {slot2} {slot3} ({roll.value})"
|
||||||
|
elif roll.emoticon == "\u26BD":
|
||||||
|
results = {
|
||||||
|
1: "miss",
|
||||||
|
2: "hit the woodwork",
|
||||||
|
3: "goal", # seems to go in through the center
|
||||||
|
4: "goal",
|
||||||
|
5: "goal 🎉", # seems to go in through the top right corner, includes confetti
|
||||||
|
}
|
||||||
|
elif roll.emoticon == "\U0001F3B3":
|
||||||
|
results = {
|
||||||
|
1: "miss",
|
||||||
|
2: "1 pin down",
|
||||||
|
3: "3 pins down, split",
|
||||||
|
4: "4 pins down, split",
|
||||||
|
5: "5 pins down",
|
||||||
|
6: "strike 🎉",
|
||||||
|
}
|
||||||
|
# elif roll.emoticon == "\U0001F3C0":
|
||||||
|
# results = {
|
||||||
|
# 2: "rolled off",
|
||||||
|
# 3: "stuck",
|
||||||
|
# }
|
||||||
|
# elif roll.emoticon == "\U0001F3AF":
|
||||||
|
# results = {
|
||||||
|
# 1: "bounced off",
|
||||||
|
# 2: "outer rim",
|
||||||
|
#
|
||||||
|
# 6: "bullseye",
|
||||||
|
# }
|
||||||
|
else:
|
||||||
|
return str(roll.value)
|
||||||
|
return f"{results[roll.value]} ({roll.value})"
|
||||||
|
|
||||||
|
|
||||||
|
def make_dice_event_content(roll: MessageMediaDice) -> TextMessageEventContent:
|
||||||
|
emoji_text = {
|
||||||
|
"\U0001F3AF": " Dart throw",
|
||||||
|
"\U0001F3B2": " Dice roll",
|
||||||
|
"\U0001F3C0": " Basketball throw",
|
||||||
|
"\U0001F3B0": " Slot machine",
|
||||||
|
"\U0001F3B3": " Bowling",
|
||||||
|
"\u26BD": " Football kick"
|
||||||
|
}
|
||||||
|
text = f"{roll.emoticon}{emoji_text.get(roll.emoticon, '')} result: {_format_dice(roll)}"
|
||||||
|
content = TextMessageEventContent(msgtype=MessageType.TEXT, format=Format.HTML, body=text,
|
||||||
|
formatted_body=f"<h4>{text}</h4>")
|
||||||
|
content["net.maunium.telegram.dice"] = {"emoticon": roll.emoticon, "value": roll.value}
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
async def make_contact_event_content(
|
||||||
|
source: au.AbstractUser, contact: MessageMediaContact
|
||||||
|
) -> TextMessageEventContent:
|
||||||
|
name = " ".join(x for x in [contact.first_name, contact.last_name] if x)
|
||||||
|
formatted_phone = f"+{contact.phone_number}"
|
||||||
|
if phonenumbers is not None:
|
||||||
|
parsed = phonenumbers.parse(formatted_phone)
|
||||||
|
fmt = phonenumbers.PhoneNumberFormat.INTERNATIONAL
|
||||||
|
formatted_phone = phonenumbers.format_number(parsed, fmt)
|
||||||
|
content = TextMessageEventContent(
|
||||||
|
msgtype=MessageType.TEXT,
|
||||||
|
body=f"Shared contact info for {name}: {formatted_phone}",
|
||||||
|
)
|
||||||
|
content["net.maunium.telegram.contact"] = {
|
||||||
|
"user_id": contact.user_id,
|
||||||
|
"first_name": contact.first_name,
|
||||||
|
"last_name": contact.last_name,
|
||||||
|
"phone_number": contact.phone_number,
|
||||||
|
"vcard": contact.vcard,
|
||||||
|
}
|
||||||
|
|
||||||
|
puppet = await pu.Puppet.get_by_tgid(TelegramID(contact.user_id))
|
||||||
|
if not puppet.displayname:
|
||||||
|
try:
|
||||||
|
entity = await source.client.get_entity(PeerUser(contact.user_id))
|
||||||
|
await puppet.update_info(source, entity)
|
||||||
|
except Exception as e:
|
||||||
|
source.log.warning(f"Failed to sync puppet info of received contact: {e}")
|
||||||
|
else:
|
||||||
|
content.format = Format.HTML
|
||||||
|
content.formatted_body = (
|
||||||
|
f"Shared contact info for "
|
||||||
|
f"<a href='https://matrix.to/#/{puppet.mxid}'>{html.escape(name)}</a>: "
|
||||||
|
f"{html.escape(formatted_phone)}"
|
||||||
|
)
|
||||||
|
return content
|
||||||
@@ -14,6 +14,9 @@ qrcode>=6,<7
|
|||||||
#/hq_thumbnails
|
#/hq_thumbnails
|
||||||
moviepy>=1,<2
|
moviepy>=1,<2
|
||||||
|
|
||||||
|
#/formatted_phonenumbers
|
||||||
|
phonenumbers>=8,<9
|
||||||
|
|
||||||
#/metrics
|
#/metrics
|
||||||
prometheus_client>=0.6,<0.13
|
prometheus_client>=0.6,<0.13
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user