feat(bot_cli): add monitor-once subcommand

This commit is contained in:
yiekheng 2026-05-02 16:59:55 +08:00
parent e2eb32dacb
commit f472a94916
2 changed files with 73 additions and 0 deletions

View File

@ -67,6 +67,22 @@ def cmd_transfer(args):
)) ))
def cmd_monitor_once(args):
bot = CM_BOT_HAL()
available = bot.get_all_available_acc()
print(f"Available accounts: {len(available)} (target: {args.target})")
if len(available) >= args.target:
print("Already at target; nothing to do.")
return
for _ in range(len(available), args.target):
try:
user = bot.create_new_acc()
print(f"Created: {user['username']}")
except Exception as exc:
print(f"ERROR creating account: {exc}", file=sys.stderr)
sys.exit(1)
def build_parser() -> argparse.ArgumentParser: def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser( p = argparse.ArgumentParser(
prog="bot_cli", prog="bot_cli",
@ -98,6 +114,10 @@ def build_parser() -> argparse.ArgumentParser:
sp.add_argument("t_password") sp.add_argument("t_password")
sp.set_defaults(func=cmd_transfer) sp.set_defaults(func=cmd_transfer)
sp = sub.add_parser("monitor-once", aliases=["monitor"], help="One iteration of the auto-create monitor.")
sp.add_argument("--target", type=int, default=20)
sp.set_defaults(func=cmd_monitor_once)
return p return p

View File

@ -188,5 +188,58 @@ class CmdTransferTests(unittest.TestCase):
self.assertIs(args.func, bot_cli.cmd_transfer) self.assertIs(args.func, bot_cli.cmd_transfer)
class CmdMonitorOnceTests(unittest.TestCase):
@mock.patch.object(bot_cli, "CM_BOT_HAL")
def test_does_nothing_when_already_at_target(self, mock_hal_class):
mock_hal = mock_hal_class.return_value
mock_hal.get_all_available_acc.return_value = [{"username": f"u{i}"} for i in range(20)]
out = io.StringIO()
with contextlib.redirect_stdout(out):
bot_cli.cmd_monitor_once(argparse.Namespace(target=20))
text = out.getvalue()
self.assertIn("Available accounts: 20", text)
self.assertIn("Already at target", text)
mock_hal.create_new_acc.assert_not_called()
@mock.patch.object(bot_cli, "CM_BOT_HAL")
def test_creates_accounts_until_target(self, mock_hal_class):
mock_hal = mock_hal_class.return_value
mock_hal.get_all_available_acc.return_value = [{"username": "u1"}, {"username": "u2"}]
mock_hal.create_new_acc.side_effect = [
{"username": "u3", "password": "p3", "link": "l3"},
{"username": "u4", "password": "p4", "link": "l4"},
{"username": "u5", "password": "p5", "link": "l5"},
]
out = io.StringIO()
with contextlib.redirect_stdout(out):
bot_cli.cmd_monitor_once(argparse.Namespace(target=5))
text = out.getvalue()
self.assertEqual(mock_hal.create_new_acc.call_count, 3)
self.assertIn("Created: u3", text)
self.assertIn("Created: u4", text)
self.assertIn("Created: u5", text)
@mock.patch.object(bot_cli, "CM_BOT_HAL")
def test_create_failure_exits_1(self, mock_hal_class):
mock_hal = mock_hal_class.return_value
mock_hal.get_all_available_acc.return_value = []
mock_hal.create_new_acc.side_effect = RuntimeError("fail login")
with self.assertRaises(SystemExit) as cm:
bot_cli.cmd_monitor_once(argparse.Namespace(target=1))
self.assertEqual(cm.exception.code, 1)
def test_monitor_once_subparser_dispatches(self):
parser = bot_cli.build_parser()
args = parser.parse_args(["monitor-once", "--target", "7"])
self.assertIs(args.func, bot_cli.cmd_monitor_once)
self.assertEqual(args.target, 7)
def test_monitor_alias_dispatches(self):
parser = bot_cli.build_parser()
args = parser.parse_args(["monitor"])
self.assertIs(args.func, bot_cli.cmd_monitor_once)
self.assertEqual(args.target, 20)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()