Merge branch 'main' into pr-694
This commit is contained in:
@@ -84,7 +84,7 @@ class SlackChannel(BaseChannel):
|
||||
use_thread = thread_ts and channel_type != "im"
|
||||
await self._web_client.chat_postMessage(
|
||||
channel=msg.chat_id,
|
||||
text=msg.content or "",
|
||||
text=self._convert_markdown(msg.content) or "",
|
||||
thread_ts=thread_ts if use_thread else None,
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -203,3 +203,47 @@ class SlackChannel(BaseChannel):
|
||||
if not text or not self._bot_user_id:
|
||||
return text
|
||||
return re.sub(rf"<@{re.escape(self._bot_user_id)}>\s*", "", text).strip()
|
||||
|
||||
# Markdown → Slack mrkdwn formatting rules (order matters: longest markers first)
|
||||
_MD_TO_SLACK = (
|
||||
(r'(?m)(^|[^\*])\*\*\*(.+?)\*\*\*([^\*]|$)', r'\1*_\2_*\3'), # ***bold italic***
|
||||
(r'(?m)(^|[^_])___(.+?)___([^_]|$)', r'\1*_\2_*\3'), # ___bold italic___
|
||||
(r'(?m)(^|[^\*])\*\*(.+?)\*\*([^\*]|$)', r'\1*\2*\3'), # **bold**
|
||||
(r'(?m)(^|[^_])__(.+?)__([^_]|$)', r'\1*\2*\3'), # __bold__
|
||||
(r'(?m)(^|[^\*])\*(.+?)\*([^\*]|$)', r'\1_\2_\3'), # *italic*
|
||||
(r'(?m)(^|[^~])~~(.+?)~~([^~]|$)', r'\1~\2~\3'), # ~~strike~~
|
||||
(r'(?m)(^|[^!])\[(.+?)\]\((http.+?)\)', r'\1<\3|\2>'), # [text](url)
|
||||
(r'!\[.+?\]\((http.+?)(?:\s".*?")?\)', r'<\1>'), # 
|
||||
)
|
||||
_TABLE_RE = re.compile(r'(?m)^\|.*?\|$(?:\n(?:\|\:?-{3,}\:?)*?\|$)(?:\n\|.*?\|$)*')
|
||||
|
||||
def _convert_markdown(self, text: str) -> str:
|
||||
"""Convert standard Markdown to Slack mrkdwn format."""
|
||||
if not text:
|
||||
return text
|
||||
for pattern, repl in self._MD_TO_SLACK:
|
||||
text = re.sub(pattern, repl, text)
|
||||
return self._TABLE_RE.sub(self._convert_table, text)
|
||||
|
||||
@staticmethod
|
||||
def _convert_table(match: re.Match) -> str:
|
||||
"""Convert Markdown table to Slack quote + bullet format."""
|
||||
lines = [l.strip() for l in match.group(0).strip().split('\n') if l.strip()]
|
||||
if len(lines) < 2:
|
||||
return match.group(0)
|
||||
|
||||
headers = [h.strip() for h in lines[0].strip('|').split('|')]
|
||||
start = 2 if not re.search(r'[^|\-\s:]', lines[1]) else 1
|
||||
|
||||
result: list[str] = []
|
||||
for line in lines[start:]:
|
||||
cells = [c.strip() for c in line.strip('|').split('|')]
|
||||
cells = (cells + [''] * len(headers))[:len(headers)]
|
||||
if not any(cells):
|
||||
continue
|
||||
result.append(f"> *{headers[0]}*: {cells[0] or '--'}")
|
||||
for i, cell in enumerate(cells[1:], 1):
|
||||
if cell and i < len(headers):
|
||||
result.append(f" \u2022 *{headers[i]}*: {cell}")
|
||||
result.append("")
|
||||
return '\n'.join(result).rstrip()
|
||||
|
||||
@@ -231,12 +231,18 @@ class TelegramChannel(BaseChannel):
|
||||
"Type /help to see available commands."
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _sender_id(user) -> str:
|
||||
"""Build sender_id with username for allowlist matching."""
|
||||
sid = str(user.id)
|
||||
return f"{sid}|{user.username}" if user.username else sid
|
||||
|
||||
async def _forward_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Forward slash commands to the bus for unified handling in AgentLoop."""
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
await self._handle_message(
|
||||
sender_id=str(update.effective_user.id),
|
||||
sender_id=self._sender_id(update.effective_user),
|
||||
chat_id=str(update.message.chat_id),
|
||||
content=update.message.text,
|
||||
)
|
||||
@@ -249,11 +255,7 @@ class TelegramChannel(BaseChannel):
|
||||
message = update.message
|
||||
user = update.effective_user
|
||||
chat_id = message.chat_id
|
||||
|
||||
# Use stable numeric ID, but keep username for allowlist compatibility
|
||||
sender_id = str(user.id)
|
||||
if user.username:
|
||||
sender_id = f"{sender_id}|{user.username}"
|
||||
sender_id = self._sender_id(user)
|
||||
|
||||
# Store chat_id for replies
|
||||
self._chat_ids[sender_id] = chat_id
|
||||
|
||||
Reference in New Issue
Block a user