Преглед изворни кода

voice messages: bridge to native Matrix messages

Sumner Evans пре 3 година
родитељ
комит
5f956ed5f5
1 измењених фајлова са 39 додато и 20 уклоњено
  1. 39 20
      mautrix_instagram/portal.py

+ 39 - 20
mautrix_instagram/portal.py

@@ -14,7 +14,7 @@
 # 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 typing import (Dict, Tuple, Optional, List, Deque, Set, Any, Union, AsyncGenerator,
-                    Awaitable, NamedTuple, Callable, TYPE_CHECKING, cast)
+                    Awaitable, Callable, TYPE_CHECKING, cast)
 from collections import deque
 from io import BytesIO
 import mimetypes
@@ -22,20 +22,22 @@ import asyncio
 
 import asyncpg
 import magic
+from mautrix.types.event.message import Audio, ExtensibleFile, Voice
 from mautrix.util.message_send_checkpoint import MessageSendCheckpointStatus
 
 from mauigpapi.types import (Thread, ThreadUser, ThreadItem, RegularMediaItem, MediaType,
                              ReactionStatus, Reaction, AnimatedMediaItem, ThreadItemType,
                              VoiceMediaItem, ExpiredMediaItem, MessageSyncMessage, ReelShareType,
-                             TypingStatus, ThreadUserLastSeenAt, MediaShareItem)
+                             TypingStatus, ThreadUserLastSeenAt, MediaShareItem, ReelMediaShareItem)
 from mautrix.appservice import AppService, IntentAPI
 from mautrix.bridge import BasePortal, NotificationDisabler, async_getter_lock
 from mautrix.types import (EventID, MessageEventContent, RoomID, EventType, MessageType, ImageInfo,
                            VideoInfo, MediaMessageEventContent, TextMessageEventContent, AudioInfo,
-                           ContentURI, EncryptedFile, LocationMessageEventContent, Format, UserID)
+                           ContentURI, LocationMessageEventContent, Format, UserID)
 from mautrix.errors import MatrixError, MForbidden, MNotFound, SessionNotFound
 from mautrix.util.simple_lock import SimpleLock
 
+
 from .db import Portal as DBPortal, Message as DBMessage, Reaction as DBReaction
 from .config import Config
 from . import user as u, puppet as p, matrix as m
@@ -56,11 +58,15 @@ except ImportError:
 StateBridge = EventType.find("m.bridge", EventType.Class.STATE)
 StateHalfShotBridge = EventType.find("uk.half-shot.bridge", EventType.Class.STATE)
 FileInfo = Union[AudioInfo, ImageInfo, VideoInfo]
-ReuploadedMediaInfo = NamedTuple('ReuploadedMediaInfo', mxc=Optional[ContentURI], url=str,
-                                 decryption_info=Optional[EncryptedFile], msgtype=MessageType,
-                                 file_name=str, info=FileInfo)
-MediaData = Union[RegularMediaItem, ExpiredMediaItem]
-MediaUploadFunc = Callable[['u.User', MediaData, IntentAPI], Awaitable[ReuploadedMediaInfo]]
+MediaData = Union[
+    AnimatedMediaItem,
+    ExpiredMediaItem,
+    MediaShareItem,
+    ReelMediaShareItem,
+    RegularMediaItem,
+    VoiceMediaItem,
+]
+MediaUploadFunc = Callable[['u.User', MediaData, IntentAPI], Awaitable[MediaMessageEventContent]]
 
 
 class Portal(DBPortal, BasePortal):
@@ -432,7 +438,7 @@ class Portal(DBPortal, BasePortal):
     # region Instagram event handling
 
     async def _reupload_instagram_media(self, source: 'u.User', media: RegularMediaItem,
-                                        intent: IntentAPI) -> ReuploadedMediaInfo:
+                                        intent: IntentAPI) -> MediaMessageEventContent:
         if media.media_type == MediaType.IMAGE:
             image = media.best_image
             if not image:
@@ -452,21 +458,31 @@ class Portal(DBPortal, BasePortal):
         return await self._reupload_instagram_file(source, url, msgtype, info, intent)
 
     async def _reupload_instagram_animated(self, source: 'u.User', media: AnimatedMediaItem,
-                                           intent: IntentAPI) -> ReuploadedMediaInfo:
+                                           intent: IntentAPI) -> MediaMessageEventContent:
         url = media.images.fixed_height.webp
         info = ImageInfo(height=int(media.images.fixed_height.height),
                          width=int(media.images.fixed_height.width))
         return await self._reupload_instagram_file(source, url, MessageType.IMAGE, info, intent)
 
     async def _reupload_instagram_voice(self, source: 'u.User', media: VoiceMediaItem,
-                                        intent: IntentAPI) -> ReuploadedMediaInfo:
+                                        intent: IntentAPI) -> MediaMessageEventContent:
         url = media.media.audio.audio_src
         info = AudioInfo(duration=media.media.audio.duration)
-        return await self._reupload_instagram_file(source, url, MessageType.AUDIO, info, intent)
+        waveform = [int(p * 1000) for p in media.media.audio.waveform_data]
+        content = await self._reupload_instagram_file(source, url, MessageType.AUDIO, info, intent)
+        content.extensible_file = ExtensibleFile(
+            file=content.file,
+            name=content.body,
+            mimetype=content.info.mimetype,
+            size=content.info.size,
+        )
+        content.audio = Audio(duration=media.media.audio.duration, waveform=waveform)
+        content.voice = Voice()
+        return content
 
     async def _reupload_instagram_file(self, source: 'u.User', url: str, msgtype: MessageType,
                                        info: FileInfo, intent: IntentAPI
-                                       ) -> ReuploadedMediaInfo:
+                                       ) -> MediaMessageEventContent:
         async with await source.client.raw_http_get(url) as resp:
             try:
                 length = int(resp.headers["Content-Length"])
@@ -506,8 +522,14 @@ class Portal(DBPortal, BasePortal):
             decryption_info.url = mxc
             mxc = None
 
-        return ReuploadedMediaInfo(mxc=mxc, url=url, decryption_info=decryption_info,
-                                   file_name=file_name, msgtype=msgtype, info=info)
+        return MediaMessageEventContent(
+            body=file_name,
+            external_url=url,
+            url=mxc,
+            file=decryption_info,
+            info=info,
+            msgtype=msgtype,
+        )
 
     def _get_instagram_media_info(self, item: ThreadItem) -> Tuple[MediaUploadFunc, MediaData]:
         # TODO maybe use a dict and item.item_type instead of a ton of ifs
@@ -543,17 +565,14 @@ class Portal(DBPortal, BasePortal):
                                       ) -> Optional[EventID]:
         try:
             reupload_func, media_data = self._get_instagram_media_info(item)
-            reuploaded = await reupload_func(source, media_data, intent)
+            content = await reupload_func(source, media_data, intent)
         except ValueError as e:
             content = TextMessageEventContent(body=str(e), msgtype=MessageType.NOTICE)
         except Exception:
             self.log.warning("Failed to upload media", exc_info=True)
             content = TextMessageEventContent(body="Attachment not available: failed to copy file",
                                               msgtype=MessageType.NOTICE)
-        else:
-            content = MediaMessageEventContent(
-                body=reuploaded.file_name, external_url=reuploaded.url, url=reuploaded.mxc,
-                file=reuploaded.decryption_info, info=reuploaded.info, msgtype=reuploaded.msgtype)
+
         await self._add_instagram_reply(content, item.replied_to_message)
         return await self._send_message(intent, content, timestamp=item.timestamp // 1000)