Inbound file messages

This commit is contained in:
Andrew Ferrazzutti 2022-04-28 01:36:15 -04:00
parent c9961d5078
commit 2602e58734
5 changed files with 90 additions and 22 deletions

View File

@ -58,6 +58,7 @@ from ..types.request import (
deserialize_result,
ResultType,
ResultListType,
ResultRawType,
RootCommandResult,
CommandResultDoneValue
)
@ -327,6 +328,18 @@ class Client:
await self._rpc_client.request("get_memo_ids", mxid=self.user.mxid)
)
def download_file(
self,
channel_props: ChannelProps,
key: str,
) -> Awaitable[bytes]:
return self._api_user_request_result(
ResultRawType(bytes),
"download_file",
channel_props=channel_props.serialize(),
key=key,
)
def send_chat(
self,
channel_props: ChannelProps,

View File

@ -13,7 +13,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 Generic, Type, TypeVar, Union, Iterable
from typing import Generic, Type, TypeVar, Union
from attr import dataclass
from enum import IntEnum
@ -103,6 +103,17 @@ def ResultListType(result_type: Type[ResultType]):
return _ResultListType
def ResultRawType(result_type: Type):
class _ResultRawType(result_type, Serializable):
def serialize(self) -> result_type:
return self
@classmethod
def deserialize(cls, data: JSON) -> "_ResultRawType":
return result_type(data)
return _ResultRawType
@dataclass
class CommandResultDoneValue(RootCommandResult, Generic[ResultType]):

View File

@ -81,7 +81,7 @@ from .kt.types.chat import Chatlog, ChatType, KnownChatType
from .kt.types.chat.attachment import (
Attachment,
AudioAttachment,
#FileAttachment,
FileAttachment,
MediaAttachment,
MultiPhotoAttachment,
PhotoAttachment,
@ -223,7 +223,7 @@ class Portal(DBPortal, BasePortal):
KnownChatType.MULTIPHOTO: cls._handle_kakaotalk_multiphoto,
KnownChatType.VIDEO: cls._handle_kakaotalk_video,
KnownChatType.AUDIO: cls._handle_kakaotalk_audio,
#KnownChatType.FILE: cls._handle_kakaotalk_file,
KnownChatType.FILE: cls._handle_kakaotalk_file,
16385: cls._handle_kakaotalk_deleted,
}
@ -436,7 +436,7 @@ class Portal(DBPortal, BasePortal):
power_levels.set_user_level(demoter_id, orig_power_levels[demoter_id])
@classmethod
async def _reupload_kakaotalk_file(
async def _reupload_kakaotalk_file_from_url(
cls,
url: str,
source: u.User,
@ -457,6 +457,28 @@ class Portal(DBPortal, BasePortal):
if length > cls.matrix.media_config.upload_size:
raise ValueError("File not available: too large")
data = await resp.read()
return await cls._reupload_kakaotalk_file_from_bytes(
data,
intent,
filename=filename,
mimetype=mimetype,
encrypt=encrypt,
find_size=find_size,
convert_audio=convert_audio,
)
@classmethod
async def _reupload_kakaotalk_file_from_bytes(
cls,
data: bytes,
intent: IntentAPI,
*,
filename: str | None = None,
mimetype: str | None = None,
encrypt: bool = False,
find_size: bool = False,
convert_audio: bool = False,
) -> tuple[ContentURI, FileInfo | VideoInfo | AudioInfo | ImageInfo, EncryptedFile | None]:
if not mimetype:
mimetype = magic.mimetype(data)
if convert_audio and mimetype != "audio/ogg":
@ -1610,22 +1632,6 @@ class Portal(DBPortal, BasePortal):
**kwargs
))
""" TODO Find what auth is required for reading file contents
def _handle_kakaotalk_file(
self,
attachment: FileAttachment,
**kwargs
) -> Awaitable[list[EventID]]:
return asyncio.gather(self._handle_kakaotalk_media(
attachment,
FileInfo(
size=attachment.size,
),
MessageType.FILE,
**kwargs
))
"""
async def _handle_kakaotalk_media(
self,
attachment: MediaAttachment,
@ -1638,7 +1644,7 @@ class Portal(DBPortal, BasePortal):
chat_text: str | None,
**_
) -> EventID:
mxc, additional_info, decryption_info = await self._reupload_kakaotalk_file(
mxc, additional_info, decryption_info = await self._reupload_kakaotalk_file_from_url(
attachment.url,
source,
intent,
@ -1653,6 +1659,27 @@ class Portal(DBPortal, BasePortal):
)
return await self._send_message(intent, content, timestamp=timestamp)
async def _handle_kakaotalk_file(
self,
source: u.User,
intent: IntentAPI,
attachment: FileAttachment,
timestamp: int,
chat_text: str | None,
**_
) -> list[EventID]:
data = await source.client.download_file(self.channel_props, attachment.k)
mxc, info, decryption_info = await self._reupload_kakaotalk_file_from_bytes(
data,
intent,
filename=attachment.name,
encrypt=self.encrypted,
)
content = MediaMessageEventContent(
url=mxc, file=decryption_info, msgtype=MessageType.FILE, body=chat_text or "", info=info
)
return [await self._send_message(intent, content, timestamp=timestamp)]
async def handle_kakaotalk_chat_delete(
self,
sender: p.Puppet,

View File

@ -281,7 +281,7 @@ class RPCClient:
break
except asyncio.LimitOverrunError as e:
self.log.warning(f"Buffer overrun: {e}")
line += await self._reader.read(self._reader._limit)
line += await self._reader.read(e.consumed)
except asyncio.CancelledError:
raise
if not line:

View File

@ -23,6 +23,7 @@ import {
KnownAuthStatusCode,
util,
} from "node-kakao"
import { ReadStreamUtil } from "node-kakao/stream"
/** @typedef {import("node-kakao").OAuthCredential} OAuthCredential */
/** @typedef {import("node-kakao").ChannelType} ChannelType */
/** @typedef {import("node-kakao").ReplyAttachment} ReplyAttachment */
@ -824,6 +825,21 @@ export default class PeerClient {
return channelIds
}
/**
* @param {Object} req
* @param {string} req.mxid
* @param {ChannelProps} req.channel_props
* @param {string} req.key
*/
downloadFile = async (req) => {
const talkChannel = await this.#getUserChannel(req.mxid, req.channel_props)
const res = await talkChannel.downloadMedia({ key: req.key }, KnownChatType.FILE)
if (!res.success) return res
const data = await ReadStreamUtil.all(res.result)
return makeCommandResult(Array.from(data))
}
/**
* @param {Object} req
* @param {string} req.mxid
@ -1048,6 +1064,7 @@ export default class PeerClient {
list_friends: this.listFriends,
get_friend_dm_id: req => this.getFriendProperty(req, "directChatId"),
get_memo_ids: this.getMemoIds,
download_file: this.downloadFile,
send_chat: this.sendChat,
send_media: this.sendMedia,
delete_chat: this.deleteChat,