Initial import of Shirobana bot backend

Include utility modules, shared channels, prompts, read-only skills, wardrobe memory and regression tests. Externalize legacy credentials and exclude private runtime state.
This commit is contained in:
RRRosmontis
2026-11-11 00:25:30 +08:00
commit 34089d0042
106 changed files with 28028 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
"""Adversarial tests for command authorization and Telegram sticker delivery."""
import asyncio
import importlib.util
import sys
import tempfile
import types
import unittest
from pathlib import Path
from unittest import mock
BASE = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(BASE / "bot-utility"), str(BASE)]
def load_dispatch():
"""Import dispatch without optional network dependencies of unused modules."""
stubs = {}
for name in (
"setu", "jm", "pixiv", "bilibili", "mail", "live_monitor", "ginkgostu",
"identity", "identity_cmd", "for_admin_komari_agent", "for_admin_catch_log",
"mode_commands",
):
stubs[name] = types.ModuleType(name)
help_ui = types.ModuleType("help_ui")
help_ui.command_index = lambda: "help"
stubs["help_ui"] = help_ui
deepseek = types.ModuleType("deepseek")
deepseek.__path__ = []
deepseek.deepseek_balance = types.ModuleType("deepseek.deepseek_balance")
stubs["deepseek"] = deepseek
stubs["deepseek.deepseek_balance"] = deepseek.deepseek_balance
spec = importlib.util.spec_from_file_location(
"dispatch_security_test", BASE / "bot-utility" / "dispatch.py",
)
module = importlib.util.module_from_spec(spec)
with mock.patch.dict(sys.modules, stubs):
spec.loader.exec_module(module)
return module
class ChatAdminTests(unittest.IsolatedAsyncioTestCase):
async def test_chat_on_off_requires_admin(self):
dispatch = load_dispatch()
channel = types.SimpleNamespace(is_admin=mock.Mock(return_value=False))
engine = types.SimpleNamespace(set_chat_enabled=mock.Mock())
chat = types.ModuleType("chat")
chat.engine = engine
dispatch.send_message = mock.AsyncMock()
with mock.patch.dict(sys.modules, {"chat": chat}):
handled = await dispatch._handle_chat_legacy(channel, "group", 20, 10, ".chat on")
self.assertTrue(handled)
engine.set_chat_enabled.assert_not_called()
dispatch.send_message.assert_awaited_once_with(channel, "group", 20, "该指令仅管理员可用")
async def test_admin_can_toggle_chat(self):
dispatch = load_dispatch()
channel = types.SimpleNamespace(is_admin=mock.Mock(return_value=True))
engine = types.SimpleNamespace(set_chat_enabled=mock.Mock())
chat = types.ModuleType("chat")
chat.engine = engine
dispatch.send_message = mock.AsyncMock()
with mock.patch.dict(sys.modules, {"chat": chat}):
for arg, enabled in (("on", True), ("off", False)):
with self.subTest(arg=arg):
handled = await dispatch._handle_chat_legacy(
channel, "private", 10, 10, ".chat " + arg,
)
self.assertTrue(handled)
engine.set_chat_enabled.assert_called_with(enabled)
self.assertEqual(engine.set_chat_enabled.call_count, 2)
try:
from aiogram.types import FSInputFile
except ModuleNotFoundError:
aiogram = types.ModuleType("aiogram")
aiogram.__path__ = []
aiogram_types = types.ModuleType("aiogram.types")
class FSInputFile:
def __init__(self, path):
self.path = path
aiogram_types.FSInputFile = FSInputFile
aiogram.types = aiogram_types
sys.modules["aiogram"] = aiogram
sys.modules["aiogram.types"] = aiogram_types
from tg_channel import TGChannel
class TelegramStickerTests(unittest.IsolatedAsyncioTestCase):
async def test_agent_sticker_uses_sticker_api_not_photo(self):
with tempfile.TemporaryDirectory() as directory:
image = Path(directory) / "sticker.webp"
image.write_bytes(b"sticker")
bot = types.SimpleNamespace(
send_sticker=mock.AsyncMock(return_value=object()),
send_photo=mock.AsyncMock(return_value=object()),
send_document=mock.AsyncMock(return_value=object()),
)
channel = TGChannel(bot, admin_ids=[])
sent = await channel.send("private", 123, [{
"type": "image", "data": {"file": image.as_uri(), "sub_type": 1},
}])
self.assertTrue(sent)
bot.send_sticker.assert_awaited_once()
self.assertEqual(bot.send_sticker.await_args.kwargs["chat_id"], 123)
bot.send_photo.assert_not_awaited()
bot.send_document.assert_not_awaited()
async def test_regular_image_still_uses_photo_api(self):
with tempfile.TemporaryDirectory() as directory:
image = Path(directory) / "image.jpg"
image.write_bytes(b"image")
bot = types.SimpleNamespace(
send_sticker=mock.AsyncMock(return_value=object()),
send_photo=mock.AsyncMock(return_value=object()),
send_document=mock.AsyncMock(return_value=object()),
)
channel = TGChannel(bot, admin_ids=[])
sent = await channel.send("private", 123, [{
"type": "image", "data": {"file": image.as_uri()},
}])
self.assertTrue(sent)
bot.send_photo.assert_awaited_once()
bot.send_sticker.assert_not_awaited()
if __name__ == "__main__":
unittest.main()