subscription.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. from datetime import datetime
  2. from typing import Dict, List, Optional
  3. from sqlmodel import SQLModel, Field,Session,select,Relationship
  4. from sqlalchemy.engine import Engine
  5. from sqlalchemy.dialects.postgresql import JSON
  6. from pydantic import BaseModel
  7. import yaml
  8. from database.engine import get_session,create_engine,engine
  9. from config.logu import logger
  10. class SubscriptFile(SQLModel, table=True):
  11. id: Optional[int] = Field(default=None, primary_key=True)
  12. name: str = Field()
  13. url: str = Field(index=True)
  14. file_path: str = Field()
  15. updated_at: datetime = Field(default_factory=datetime.now)
  16. error: int = Field(default=0)
  17. detail: dict = Field(default={}, sa_type=JSON)
  18. mihomo_meta: List["MihomoMeta"] = Relationship(back_populates="subscript_file")
  19. class MihomoMeta(SQLModel, table=True):
  20. id: Optional[int] = Field(default=None, primary_key=True)
  21. provider_name: str
  22. proxy_name: str
  23. mixed_port: Optional[int]
  24. external_controller: Optional[str]
  25. temp_file_path: Optional[str]
  26. pid: Optional[int]
  27. running: Optional[bool] = False
  28. updated_at: datetime = Field(default_factory=datetime.now)
  29. subscript_file_id: Optional[int] = Field(default=None, foreign_key="subscriptfile.id")
  30. subscript_file: SubscriptFile | None = Relationship(back_populates="mihomo_meta")
  31. class SubscriptionManager:
  32. def __init__(self, db:Engine=None):
  33. self.engine:Engine = db or engine
  34. def add_subscription_meta(self, sub_model: SubscriptFile, proxies, overwrite:bool=False):
  35. with Session(self.engine) as session:
  36. exist_sub = session.exec(select(SubscriptFile).where(SubscriptFile.url == sub_model.url)).first()
  37. if exist_sub and not overwrite:
  38. logger.info(f"{sub_model.url} already exist, skip add")
  39. return exist_sub
  40. logger.info(f"exist_sub {exist_sub} overwrite {overwrite} proxies {proxies}")
  41. if exist_sub and overwrite:
  42. # 删除与 exist_sub 相关的 MihomoMeta 记录
  43. session.exec(select(MihomoMeta).where(MihomoMeta.subscript_file_id == exist_sub.id)).all()
  44. for proxy in session.exec(select(MihomoMeta).where(MihomoMeta.subscript_file_id == exist_sub.id)).all():
  45. session.delete(proxy)
  46. # 删除旧的 SubscriptFile 记录
  47. session.delete(exist_sub)
  48. session.commit()
  49. # 添加新的 SubscriptFile
  50. session.add(sub_model)
  51. session.commit()
  52. session.refresh(sub_model)
  53. # 添加 MihomoMeta 记录
  54. for proxy in proxies:
  55. miho = MihomoMeta(
  56. provider_name=sub_model.name,
  57. proxy_name=proxy["name"],
  58. subscript_file_id=sub_model.id # 使用 sub_model.id
  59. )
  60. logger.info(f"miho {miho}")
  61. session.add(miho)
  62. session.commit()
  63. session.refresh(sub_model)
  64. return sub_model
  65. def get_subscription_meta(self) -> List[SubscriptFile]:
  66. with Session(self.engine) as session:
  67. return session.exec(select(SubscriptFile)).all()
  68. def get_proxies(self) -> List[MihomoMeta]:
  69. with Session(self.engine) as session:
  70. return session.exec(select(MihomoMeta)).all()
  71. def get_proxies_by_provider(self) -> Dict[str, List[MihomoMeta]]:
  72. """
  73. 返回一个字典,键是 provider_name,值是该 provider_name 对应的所有 MihomoMeta 记录列表。
  74. """
  75. with Session(self.engine) as session:
  76. all_proxies = session.exec(select(MihomoMeta)).all()
  77. # 使用字典来组织数据
  78. proxies_by_provider = {}
  79. for proxy in all_proxies:
  80. if proxy.provider_name not in proxies_by_provider:
  81. proxies_by_provider[proxy.provider_name] = []
  82. proxies_by_provider[proxy.provider_name].append(proxy)
  83. return proxies_by_provider
  84. def check_valid(self, sub_model:SubscriptFile):
  85. with open(sub_model.file_path, "r",encoding='utf-8') as f:
  86. sub_yaml = yaml.safe_load(f)
  87. groups = sub_yaml.get("proxy-groups", [])
  88. if not groups:
  89. raise ValueError("subscription file is not valid")
  90. name = groups[0].get("name", "")
  91. if not name:
  92. raise ValueError("subscription file is not valid")
  93. proxies = sub_yaml.get("proxies", [])
  94. if not proxies:
  95. raise ValueError("subscription file is not valid")
  96. fileter_proxies = []
  97. fileter_proxies = []
  98. keywords = ['流量', '套餐', '剩余', '测试']
  99. for proxy in proxies:
  100. if not any(keyword in proxy.get("name", "") for keyword in keywords):
  101. fileter_proxies.append(proxy)
  102. return name, groups,proxies
  103. # ret.append(
  104. # SubscriptionResponse(
  105. # file_name=Path(sub.file_path).name,
  106. # provider_name=name,
  107. # updated_at=sub.updated_at,
  108. # proxies=sub_yaml.get("proxies", []),
  109. # )
  110. # )
  111. # return ret