Inbound file messages
This commit is contained in:
parent
c9961d5078
commit
2602e58734
|
@ -58,6 +58,7 @@ from ..types.request import (
|
||||||
deserialize_result,
|
deserialize_result,
|
||||||
ResultType,
|
ResultType,
|
||||||
ResultListType,
|
ResultListType,
|
||||||
|
ResultRawType,
|
||||||
RootCommandResult,
|
RootCommandResult,
|
||||||
CommandResultDoneValue
|
CommandResultDoneValue
|
||||||
)
|
)
|
||||||
|
@ -327,6 +328,18 @@ class Client:
|
||||||
await self._rpc_client.request("get_memo_ids", mxid=self.user.mxid)
|
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(
|
def send_chat(
|
||||||
self,
|
self,
|
||||||
channel_props: ChannelProps,
|
channel_props: ChannelProps,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# 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 attr import dataclass
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
@ -103,6 +103,17 @@ def ResultListType(result_type: Type[ResultType]):
|
||||||
|
|
||||||
return _ResultListType
|
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
|
@dataclass
|
||||||
class CommandResultDoneValue(RootCommandResult, Generic[ResultType]):
|
class CommandResultDoneValue(RootCommandResult, Generic[ResultType]):
|
||||||
|
|
|
@ -81,7 +81,7 @@ from .kt.types.chat import Chatlog, ChatType, KnownChatType
|
||||||
from .kt.types.chat.attachment import (
|
from .kt.types.chat.attachment import (
|
||||||
Attachment,
|
Attachment,
|
||||||
AudioAttachment,
|
AudioAttachment,
|
||||||
#FileAttachment,
|
FileAttachment,
|
||||||
MediaAttachment,
|
MediaAttachment,
|
||||||
MultiPhotoAttachment,
|
MultiPhotoAttachment,
|
||||||
PhotoAttachment,
|
PhotoAttachment,
|
||||||
|
@ -223,7 +223,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
KnownChatType.MULTIPHOTO: cls._handle_kakaotalk_multiphoto,
|
KnownChatType.MULTIPHOTO: cls._handle_kakaotalk_multiphoto,
|
||||||
KnownChatType.VIDEO: cls._handle_kakaotalk_video,
|
KnownChatType.VIDEO: cls._handle_kakaotalk_video,
|
||||||
KnownChatType.AUDIO: cls._handle_kakaotalk_audio,
|
KnownChatType.AUDIO: cls._handle_kakaotalk_audio,
|
||||||
#KnownChatType.FILE: cls._handle_kakaotalk_file,
|
KnownChatType.FILE: cls._handle_kakaotalk_file,
|
||||||
16385: cls._handle_kakaotalk_deleted,
|
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])
|
power_levels.set_user_level(demoter_id, orig_power_levels[demoter_id])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _reupload_kakaotalk_file(
|
async def _reupload_kakaotalk_file_from_url(
|
||||||
cls,
|
cls,
|
||||||
url: str,
|
url: str,
|
||||||
source: u.User,
|
source: u.User,
|
||||||
|
@ -457,6 +457,28 @@ class Portal(DBPortal, BasePortal):
|
||||||
if length > cls.matrix.media_config.upload_size:
|
if length > cls.matrix.media_config.upload_size:
|
||||||
raise ValueError("File not available: too large")
|
raise ValueError("File not available: too large")
|
||||||
data = await resp.read()
|
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:
|
if not mimetype:
|
||||||
mimetype = magic.mimetype(data)
|
mimetype = magic.mimetype(data)
|
||||||
if convert_audio and mimetype != "audio/ogg":
|
if convert_audio and mimetype != "audio/ogg":
|
||||||
|
@ -1610,22 +1632,6 @@ class Portal(DBPortal, BasePortal):
|
||||||
**kwargs
|
**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(
|
async def _handle_kakaotalk_media(
|
||||||
self,
|
self,
|
||||||
attachment: MediaAttachment,
|
attachment: MediaAttachment,
|
||||||
|
@ -1638,7 +1644,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
chat_text: str | None,
|
chat_text: str | None,
|
||||||
**_
|
**_
|
||||||
) -> EventID:
|
) -> 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,
|
attachment.url,
|
||||||
source,
|
source,
|
||||||
intent,
|
intent,
|
||||||
|
@ -1653,6 +1659,27 @@ class Portal(DBPortal, BasePortal):
|
||||||
)
|
)
|
||||||
return await self._send_message(intent, content, timestamp=timestamp)
|
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(
|
async def handle_kakaotalk_chat_delete(
|
||||||
self,
|
self,
|
||||||
sender: p.Puppet,
|
sender: p.Puppet,
|
||||||
|
|
|
@ -281,7 +281,7 @@ class RPCClient:
|
||||||
break
|
break
|
||||||
except asyncio.LimitOverrunError as e:
|
except asyncio.LimitOverrunError as e:
|
||||||
self.log.warning(f"Buffer overrun: {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:
|
except asyncio.CancelledError:
|
||||||
raise
|
raise
|
||||||
if not line:
|
if not line:
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
KnownAuthStatusCode,
|
KnownAuthStatusCode,
|
||||||
util,
|
util,
|
||||||
} from "node-kakao"
|
} from "node-kakao"
|
||||||
|
import { ReadStreamUtil } from "node-kakao/stream"
|
||||||
/** @typedef {import("node-kakao").OAuthCredential} OAuthCredential */
|
/** @typedef {import("node-kakao").OAuthCredential} OAuthCredential */
|
||||||
/** @typedef {import("node-kakao").ChannelType} ChannelType */
|
/** @typedef {import("node-kakao").ChannelType} ChannelType */
|
||||||
/** @typedef {import("node-kakao").ReplyAttachment} ReplyAttachment */
|
/** @typedef {import("node-kakao").ReplyAttachment} ReplyAttachment */
|
||||||
|
@ -824,6 +825,21 @@ export default class PeerClient {
|
||||||
return channelIds
|
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 {Object} req
|
||||||
* @param {string} req.mxid
|
* @param {string} req.mxid
|
||||||
|
@ -1048,6 +1064,7 @@ export default class PeerClient {
|
||||||
list_friends: this.listFriends,
|
list_friends: this.listFriends,
|
||||||
get_friend_dm_id: req => this.getFriendProperty(req, "directChatId"),
|
get_friend_dm_id: req => this.getFriendProperty(req, "directChatId"),
|
||||||
get_memo_ids: this.getMemoIds,
|
get_memo_ids: this.getMemoIds,
|
||||||
|
download_file: this.downloadFile,
|
||||||
send_chat: this.sendChat,
|
send_chat: this.sendChat,
|
||||||
send_media: this.sendMedia,
|
send_media: this.sendMedia,
|
||||||
delete_chat: this.deleteChat,
|
delete_chat: this.deleteChat,
|
||||||
|
|
Loading…
Reference in New Issue