Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 55 additions & 9 deletions gateway/platforms/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,11 @@ async def edit_message(
"""
return SendResult(success=False, error="Not supported")

async def send_typing(self, chat_id: str) -> None:
async def send_typing(
self,
chat_id: str,
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""
Send a typing indicator.

Expand All @@ -427,6 +431,7 @@ async def send_image(
image_url: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send an image natively via the platform API.
Expand All @@ -437,14 +442,20 @@ async def send_image(
"""
# Fallback: send URL as text (subclasses override for native images)
text = f"{caption}\n{image_url}" if caption else image_url
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

async def send_animation(
self,
chat_id: str,
animation_url: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send an animated GIF natively via the platform API.
Expand All @@ -453,7 +464,13 @@ async def send_animation(
(e.g., Telegram send_animation) so they auto-play inline.
Default falls back to send_image.
"""
return await self.send_image(chat_id=chat_id, image_url=animation_url, caption=caption, reply_to=reply_to)
return await self.send_image(
chat_id=chat_id,
image_url=animation_url,
caption=caption,
reply_to=reply_to,
metadata=metadata,
)

@staticmethod
def _is_animation_url(url: str) -> bool:
Expand Down Expand Up @@ -515,6 +532,7 @@ async def send_voice(
audio_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send an audio file as a native voice message via the platform API.
Expand All @@ -526,14 +544,20 @@ async def send_voice(
text = f"🔊 Audio: {audio_path}"
if caption:
text = f"{caption}\n{text}"
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

async def send_video(
self,
chat_id: str,
video_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send a video natively via the platform API.
Expand All @@ -544,7 +568,12 @@ async def send_video(
text = f"🎬 Video: {video_path}"
if caption:
text = f"{caption}\n{text}"
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

async def send_document(
self,
Expand All @@ -553,6 +582,7 @@ async def send_document(
caption: Optional[str] = None,
file_name: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send a document/file natively via the platform API.
Expand All @@ -563,14 +593,20 @@ async def send_document(
text = f"📎 File: {file_path}"
if caption:
text = f"{caption}\n{text}"
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

async def send_image_file(
self,
chat_id: str,
image_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""
Send a local image file natively via the platform API.
Expand All @@ -582,7 +618,12 @@ async def send_image_file(
text = f"🖼️ Image: {image_path}"
if caption:
text = f"{caption}\n{text}"
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

@staticmethod
def extract_media(content: str) -> Tuple[List[Tuple[str, bool]], str]:
Expand Down Expand Up @@ -620,7 +661,12 @@ def extract_media(content: str) -> Tuple[List[Tuple[str, bool]], str]:

return media, cleaned

async def _keep_typing(self, chat_id: str, interval: float = 2.0) -> None:
async def _keep_typing(
self,
chat_id: str,
interval: float = 2.0,
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""
Continuously send typing indicator until cancelled.

Expand All @@ -629,7 +675,7 @@ async def _keep_typing(self, chat_id: str, interval: float = 2.0) -> None:
"""
try:
while True:
await self.send_typing(chat_id)
await self.send_typing(chat_id, metadata=metadata)
await asyncio.sleep(interval)
except asyncio.CancelledError:
pass # Normal cancellation when handler completes
Expand Down
35 changes: 31 additions & 4 deletions gateway/platforms/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ async def send_voice(
audio_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send audio as a Discord file attachment."""
if not self._client:
Expand Down Expand Up @@ -265,14 +266,21 @@ async def send_voice(

except Exception as e:
print(f"[{self.name}] Failed to send audio: {e}")
return await super().send_voice(chat_id, audio_path, caption, reply_to)
return await super().send_voice(
chat_id,
audio_path,
caption,
reply_to,
metadata=metadata,
)

async def send_image_file(
self,
chat_id: str,
image_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send a local image file natively as a Discord file attachment."""
if not self._client:
Expand Down Expand Up @@ -302,14 +310,21 @@ async def send_image_file(

except Exception as e:
print(f"[{self.name}] Failed to send local image: {e}")
return await super().send_image_file(chat_id, image_path, caption, reply_to)
return await super().send_image_file(
chat_id,
image_path,
caption,
reply_to,
metadata=metadata,
)

async def send_image(
self,
chat_id: str,
image_url: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send an image natively as a Discord file attachment."""
if not self._client:
Expand Down Expand Up @@ -354,10 +369,22 @@ async def send_image(

except ImportError:
print(f"[{self.name}] aiohttp not installed, falling back to URL. Run: pip install aiohttp")
return await super().send_image(chat_id, image_url, caption, reply_to)
return await super().send_image(
chat_id,
image_url,
caption,
reply_to,
metadata=metadata,
)
except Exception as e:
print(f"[{self.name}] Failed to send image attachment, falling back to URL: {e}")
return await super().send_image(chat_id, image_url, caption, reply_to)
return await super().send_image(
chat_id,
image_url,
caption,
reply_to,
metadata=metadata,
)

async def send_typing(self, chat_id: str, metadata=None) -> None:
"""Send typing indicator."""
Expand Down
8 changes: 7 additions & 1 deletion gateway/platforms/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,11 @@ async def send(
return SendResult(success=True)
return SendResult(success=False, error="RPC send failed")

async def send_typing(self, chat_id: str) -> None:
async def send_typing(
self,
chat_id: str,
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""Send a typing indicator."""
params: Dict[str, Any] = {
"account": self.account,
Expand All @@ -587,6 +591,7 @@ async def send_image(
chat_id: str,
image_url: str,
caption: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
**kwargs,
) -> SendResult:
"""Send an image. Supports http(s):// and file:// URLs."""
Expand Down Expand Up @@ -633,6 +638,7 @@ async def send_document(
file_path: str,
caption: Optional[str] = None,
filename: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
**kwargs,
) -> SendResult:
"""Send a document/file attachment."""
Expand Down
37 changes: 33 additions & 4 deletions gateway/platforms/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ async def send_image_file(
image_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send a local image file to Slack by uploading it."""
if not self._app:
Expand All @@ -216,14 +217,21 @@ async def send_image_file(

except Exception as e:
print(f"[{self.name}] Failed to send local image: {e}")
return await super().send_image_file(chat_id, image_path, caption, reply_to)
return await super().send_image_file(
chat_id,
image_path,
caption,
reply_to,
metadata=metadata,
)

async def send_image(
self,
chat_id: str,
image_url: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send an image to Slack by uploading the URL as a file."""
if not self._app:
Expand All @@ -250,14 +258,20 @@ async def send_image(
except Exception as e:
# Fall back to sending the URL as text
text = f"{caption}\n{image_url}" if caption else image_url
return await self.send(chat_id=chat_id, content=text, reply_to=reply_to)
return await self.send(
chat_id=chat_id,
content=text,
reply_to=reply_to,
metadata=metadata,
)

async def send_voice(
self,
chat_id: str,
audio_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send an audio file to Slack."""
if not self._app:
Expand All @@ -282,6 +296,7 @@ async def send_video(
video_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send a video file to Slack."""
if not self._app:
Expand All @@ -302,7 +317,13 @@ async def send_video(

except Exception as e:
print(f"[{self.name}] Failed to send video: {e}")
return await super().send_video(chat_id, video_path, caption, reply_to)
return await super().send_video(
chat_id,
video_path,
caption,
reply_to,
metadata=metadata,
)

async def send_document(
self,
Expand All @@ -311,6 +332,7 @@ async def send_document(
caption: Optional[str] = None,
file_name: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send a document/file attachment to Slack."""
if not self._app:
Expand All @@ -333,7 +355,14 @@ async def send_document(

except Exception as e:
print(f"[{self.name}] Failed to send document: {e}")
return await super().send_document(chat_id, file_path, caption, file_name, reply_to)
return await super().send_document(
chat_id,
file_path,
caption,
file_name,
reply_to,
metadata=metadata,
)

async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
"""Get information about a Slack channel."""
Expand Down
Loading
Loading