ForcePilot/backend/package/yuxi/channel/extensions/bilibili/streaming.py
Kris 415d66d1c6 feat(bilibili): 新增B站渠道插件,支持直播弹幕和私信交互
该提交实现了完整的B站渠道插件,包含以下核心功能:
1.  支持B站直播弹幕监听与处理,包含弹幕、SC、礼物等多种直播间事件
2.  支持B站私信的轮询接收与发送
3.  内置WBI签名算法,适配B站API鉴权要求
4.  提供账号配对、黑白名单等弹幕私信权限控制
5.  集成速率限制与防风险机制,降低账号封禁风险
6.  完善的配置管理与状态监控能力
2026-05-21 10:40:04 +08:00

75 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import asyncio
import time
class BilibiliBlockChunker:
def __init__(
self,
outbound,
target_id: str,
anti_risk,
chunk_min_chars: int = 200,
chunk_max_chars: int = 1200,
coalesce_min_chars: int = 80,
coalesce_max_chars: int = 400,
coalesce_idle_ms: int = 500,
):
self._outbound = outbound
self._target_id = target_id
self._anti_risk = anti_risk
self._chunk_min = chunk_min_chars
self._chunk_max = chunk_max_chars
self._coalesce_min = coalesce_min_chars
self._coalesce_max = coalesce_max_chars
self._coalesce_idle = coalesce_idle_ms / 1000.0
self._buffer = ""
self._last_flush = time.time()
async def append(self, text: str) -> None:
self._buffer += text
should_flush = len(self._buffer) >= self._chunk_max or (
len(self._buffer) >= self._chunk_min and time.time() - self._last_flush > self._coalesce_idle
)
if should_flush:
await self._flush()
async def finalize(self) -> None:
if self._buffer:
await self._flush()
async def _flush(self) -> None:
chunks = self._split_chunks(self._buffer, self._chunk_max)
for chunk in chunks:
await self._outbound.send_text(to=self._target_id, text=chunk)
await asyncio.sleep(0.5)
self._buffer = ""
self._last_flush = time.time()
def _split_chunks(self, text: str, limit: int) -> list[str]:
if len(text) <= limit:
return [text] if text else []
chunks = []
remaining = text
while len(remaining) > limit:
window = remaining[:limit]
last_para = window.rfind("\n\n")
if last_para > limit * 0.5:
chunks.append(window[:last_para].strip())
remaining = remaining[last_para + 2 :]
continue
for sep in ["\n", "", "", "", "\n", " "]:
idx = window.rfind(sep)
if idx > limit * 0.3:
chunks.append(window[: idx + len(sep)].strip())
remaining = remaining[idx + len(sep) :]
break
else:
chunks.append(window.strip())
remaining = remaining[limit:]
if remaining.strip():
chunks.append(remaining.strip())
return chunks