config.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. # coding=utf-8
  2. """
  3. 配置工具模块 - 多账号配置解析和验证
  4. 提供多账号推送配置的解析、验证和限制功能
  5. """
  6. from typing import Dict, List, Optional, Tuple
  7. def parse_multi_account_config(config_value: str, separator: str = ";") -> List[str]:
  8. """
  9. 解析多账号配置,返回账号列表
  10. Args:
  11. config_value: 配置值字符串,多个账号用分隔符分隔
  12. separator: 分隔符,默认为 ;
  13. Returns:
  14. 账号列表,空字符串会被保留(用于占位)
  15. Examples:
  16. >>> parse_multi_account_config("url1;url2;url3")
  17. ['url1', 'url2', 'url3']
  18. >>> parse_multi_account_config(";token2") # 第一个账号无token
  19. ['', 'token2']
  20. >>> parse_multi_account_config("")
  21. []
  22. """
  23. if not config_value:
  24. return []
  25. # 保留空字符串用于占位(如 ";token2" 表示第一个账号无token)
  26. accounts = [acc.strip() for acc in config_value.split(separator)]
  27. # 过滤掉全部为空的情况
  28. if all(not acc for acc in accounts):
  29. return []
  30. return accounts
  31. def validate_paired_configs(
  32. configs: Dict[str, List[str]],
  33. channel_name: str,
  34. required_keys: Optional[List[str]] = None
  35. ) -> Tuple[bool, int]:
  36. """
  37. 验证配对配置的数量是否一致
  38. 对于需要多个配置项配对的渠道(如 Telegram 的 token 和 chat_id),
  39. 验证所有配置项的账号数量是否一致。
  40. Args:
  41. configs: 配置字典,key 为配置名,value 为账号列表
  42. channel_name: 渠道名称,用于日志输出
  43. required_keys: 必须有值的配置项列表
  44. Returns:
  45. (是否验证通过, 账号数量)
  46. Examples:
  47. >>> validate_paired_configs({
  48. ... "token": ["t1", "t2"],
  49. ... "chat_id": ["c1", "c2"]
  50. ... }, "Telegram", ["token", "chat_id"])
  51. (True, 2)
  52. >>> validate_paired_configs({
  53. ... "token": ["t1", "t2"],
  54. ... "chat_id": ["c1"] # 数量不匹配
  55. ... }, "Telegram", ["token", "chat_id"])
  56. (False, 0)
  57. """
  58. # 过滤掉空列表
  59. non_empty_configs = {k: v for k, v in configs.items() if v}
  60. if not non_empty_configs:
  61. return True, 0
  62. # 检查必须项
  63. if required_keys:
  64. for key in required_keys:
  65. if key not in non_empty_configs or not non_empty_configs[key]:
  66. return True, 0 # 必须项为空,视为未配置
  67. # 获取所有非空配置的长度
  68. lengths = {k: len(v) for k, v in non_empty_configs.items()}
  69. unique_lengths = set(lengths.values())
  70. if len(unique_lengths) > 1:
  71. print(f"❌ {channel_name} 配置错误:配对配置数量不一致,将跳过该渠道推送")
  72. for key, length in lengths.items():
  73. print(f" - {key}: {length} 个")
  74. return False, 0
  75. return True, list(unique_lengths)[0] if unique_lengths else 0
  76. def limit_accounts(
  77. accounts: List[str],
  78. max_count: int,
  79. channel_name: str
  80. ) -> List[str]:
  81. """
  82. 限制账号数量
  83. 当配置的账号数量超过最大限制时,只使用前 N 个账号,
  84. 并输出警告信息。
  85. Args:
  86. accounts: 账号列表
  87. max_count: 最大账号数量
  88. channel_name: 渠道名称,用于日志输出
  89. Returns:
  90. 限制后的账号列表
  91. Examples:
  92. >>> limit_accounts(["a1", "a2", "a3"], 2, "飞书")
  93. ⚠️ 飞书 配置了 3 个账号,超过最大限制 2,只使用前 2 个
  94. ['a1', 'a2']
  95. """
  96. if len(accounts) > max_count:
  97. print(f"⚠️ {channel_name} 配置了 {len(accounts)} 个账号,超过最大限制 {max_count},只使用前 {max_count} 个")
  98. print(f" ⚠️ 警告:如果您是 fork 用户,过多账号可能导致 GitHub Actions 运行时间过长,存在账号风险")
  99. return accounts[:max_count]
  100. return accounts
  101. def get_account_at_index(accounts: List[str], index: int, default: str = "") -> str:
  102. """
  103. 安全获取指定索引的账号值
  104. 当索引超出范围或账号值为空时,返回默认值。
  105. Args:
  106. accounts: 账号列表
  107. index: 索引
  108. default: 默认值
  109. Returns:
  110. 账号值或默认值
  111. Examples:
  112. >>> get_account_at_index(["a", "b", "c"], 1)
  113. 'b'
  114. >>> get_account_at_index(["a", "", "c"], 1, "default")
  115. 'default'
  116. >>> get_account_at_index(["a"], 5, "default")
  117. 'default'
  118. """
  119. if index < len(accounts):
  120. return accounts[index] if accounts[index] else default
  121. return default