fix(channels): stdlib logging placeholders in slack and whatsapp

The same pre-existing bug class the QQ increment fixed in qq.py: both
adapters format self.logger calls (a stdlib logger from BaseChannel)
with loguru-style "{}" placeholders. With args present, stdlib's
msg % args raises TypeError inside handler formatting - the message is
lost and a logging-error traceback prints instead. Runtime-proven on
the converted whatsapp deprecated-bridge warning: it now renders
("Ignoring deprecated WhatsApp bridge config fields: bridge_url.
...") where the same call lost the message before.

- slack.py: all 14 sites converted (incl. the {:.0f} handshake-timeout
  spec -> %.0f); AST scan shows zero "{}" logger calls left.
- whatsapp.py: all 5 sites converted (incl. the multi-line unauthorized-
  sender pairing notice); zero leftover per the same scan.
- feishu.py: drop the stale "logging_bridge not needed" comment - the
  last reference to the ghost module the matrix fix removed.

The scan found the same class in the remaining 12 adapters (~321
sites, feishu/dingtalk/telegram largest); that sweep is out of this
increment's scope and tracked in #1531.

Part of #1519.

Signed-off-by: shadowinlife <shadowinlife@gmail.com>
This commit is contained in:
shadowinlife committed 2026-09-21 15:53:05 +08:00
1 parent c45d3c95e8
commit d8092e24ed
3 files changed
+20 -21

No files matched your search

-1
View File
@@ -28,7 +28,6 @@ from src.channels.base import BaseChannel
from src.channels.utils import get_media_dir
from pydantic import BaseModel
from src.channels.utils import safe_filename
# logging_bridge not needed (using stdlib logging)
if TYPE_CHECKING:
from lark_oapi.api.im.v1.model import MentionEvent, P2ImMessageReceiveV1
+14 -14
View File
@@ -95,7 +95,7 @@ class SlackChannel(BaseChannel):
self.logger.error("bot/app token not configured")
return
if self.config.mode != "socket":
self.logger.error("Unsupported mode: {}", self.config.mode)
self.logger.error("Unsupported mode: %s", self.config.mode)
return
self._running = True
@@ -112,9 +112,9 @@ class SlackChannel(BaseChannel):
try:
auth = await self._web_client.auth_test()
self._bot_user_id = auth.get("user_id")
self.logger.info("bot connected as {}", self._bot_user_id)
self.logger.info("bot connected as %s", self._bot_user_id)
except Exception as e:
self.logger.warning("auth_test failed: {}", e)
self.logger.warning("auth_test failed: %s", e)
self.logger.info("Starting Socket Mode client...")
try:
@@ -124,7 +124,7 @@ class SlackChannel(BaseChannel):
)
except asyncio.TimeoutError:
self.logger.error(
"Slack Socket Mode WebSocket handshake timed out after {:.0f}s. "
"Slack Socket Mode WebSocket handshake timed out after %.0fs. "
"auth_test uses HTTPS and may still succeed while WSS is blocked. "
"Check outbound access to Slack WebSockets; slack-sdk Socket Mode "
"does not apply HTTP(S)_PROXY to websockets.connect.",
@@ -145,7 +145,7 @@ class SlackChannel(BaseChannel):
try:
await self._socket_client.close()
except Exception as e:
self.logger.warning("socket close failed: {}", e)
self.logger.warning("socket close failed: %s", e)
self._socket_client = None
async def send(self, msg: OutboundMessage) -> None:
@@ -187,7 +187,7 @@ class SlackChannel(BaseChannel):
thread_ts=thread_ts_param,
)
except Exception:
self.logger.exception("Failed to upload file {}", media_path)
self.logger.exception("Failed to upload file %s", media_path)
# Update reaction emoji when the final (non-progress) response is sent
if not (msg.metadata or {}).get("_progress"):
@@ -353,7 +353,7 @@ class SlackChannel(BaseChannel):
# Debug: log basic event shape
self.logger.debug(
"event: type={} subtype={} user={} channel={} channel_type={} text={}",
"event: type=%s subtype=%s user=%s channel=%s channel_type=%s text=%s",
event_type,
subtype,
sender_id,
@@ -402,7 +402,7 @@ class SlackChannel(BaseChannel):
timestamp=event.get("ts"),
)
except Exception as e:
self.logger.debug("reactions_add failed: {}", e)
self.logger.debug("reactions_add failed: %s", e)
# Thread-scoped session key whenever the user is in a real thread
# (raw_thread_ts is set). DM threads get their own session, separate
@@ -451,7 +451,7 @@ class SlackChannel(BaseChannel):
session_key=session_key,
)
except Exception:
self.logger.exception("Error handling message from {}", sender_id)
self.logger.exception("Error handling message from %s", sender_id)
async def _download_slack_file(self, file_info: dict[str, Any]) -> tuple[str | None, str]:
"""Download a Slack private file to the local media directory."""
@@ -484,7 +484,7 @@ class SlackChannel(BaseChannel):
path.write_bytes(response.content)
return str(path), marker
except Exception as e:
self.logger.warning("Failed to download file {}: {}", file_id, e)
self.logger.warning("Failed to download file %s: %s", file_id, e)
return None, self._download_failure_marker(marker_type, name, "download failed")
@staticmethod
@@ -531,7 +531,7 @@ class SlackChannel(BaseChannel):
session_key=session_key,
)
except Exception:
self.logger.exception("Error handling button click from {}", sender_id)
self.logger.exception("Error handling button click from %s", sender_id)
async def _with_thread_context(
self,
@@ -568,7 +568,7 @@ class SlackChannel(BaseChannel):
limit=max(1, self.config.thread_context_limit),
)
except Exception as e:
self.logger.warning("thread context unavailable for {}: {}", key, e)
self.logger.warning("thread context unavailable for %s: %s", key, e)
return text
lines = self._format_thread_context(
@@ -628,7 +628,7 @@ class SlackChannel(BaseChannel):
timestamp=ts,
)
except Exception as e:
self.logger.debug("reactions_remove failed: {}", e)
self.logger.debug("reactions_remove failed: %s", e)
if self.config.done_emoji:
try:
await self._web_client.reactions_add(
@@ -637,7 +637,7 @@ class SlackChannel(BaseChannel):
timestamp=ts,
)
except Exception as e:
self.logger.debug("done reaction failed: {}", e)
self.logger.debug("done reaction failed: %s", e)
def _is_allowed(self, sender_id: str, chat_id: str, channel_type: str) -> bool:
if channel_type == "im":
+6 -6
View File
@@ -283,7 +283,7 @@ class WhatsAppChannel(BaseChannel):
super().__init__(config, bus)
if legacy_bridge_fields:
self.logger.warning(
"Ignoring deprecated WhatsApp bridge config fields: {}. "
"Ignoring deprecated WhatsApp bridge config fields: %s. "
"Run 'vibe-trading channels login whatsapp' to create a neonize session.",
", ".join(legacy_bridge_fields),
)
@@ -329,7 +329,7 @@ class WhatsAppChannel(BaseChannel):
self.logger.info("WhatsApp login complete")
return True
except Exception as exc:
self.logger.error("WhatsApp login failed: {}", exc)
self.logger.error("WhatsApp login failed: %s", exc)
return False
finally:
with suppress(Exception):
@@ -465,7 +465,7 @@ class WhatsAppChannel(BaseChannel):
login_result.set_exception(
RuntimeError(f"WhatsApp disconnected before login completed: {event}")
)
self.logger.warning("WhatsApp disconnected: {}", event)
self.logger.warning("WhatsApp disconnected: %s", event)
@client.event(api.PairStatusEv)
async def _on_pair_status(_: Any, event: Any) -> None:
@@ -475,7 +475,7 @@ class WhatsAppChannel(BaseChannel):
if login_result is not None and not login_result.done():
login_result.set_exception(exc)
raise exc
self.logger.info("WhatsApp pair status: {}", event)
self.logger.info("WhatsApp pair status: %s", event)
if not handle_messages:
return
@@ -558,8 +558,8 @@ class WhatsAppChannel(BaseChannel):
}
if not self.is_allowed(sender_id):
self.logger.info(
"Passing unauthorized WhatsApp sender {} to pairing flow "
"(phone={}, lid={}, chat={})",
"Passing unauthorized WhatsApp sender %s to pairing flow "
"(phone=%s, lid=%s, chat=%s)",
sender_id,
phone_id or "",
lid_id or "",