diff --git a/config.gen.ini b/config.gen.ini
index 703126f..ab4a603 100644
--- a/config.gen.ini
+++ b/config.gen.ini
@@ -17,6 +17,8 @@ ipv6 = False
admin = 0
lofter_channel = 0
lofter_channel_username = username
+splash_channel = 0
+splash_channel_username = username
[api]
amap_key = ABCD
diff --git a/defs/glover.py b/defs/glover.py
index 377a45b..43e4d64 100644
--- a/defs/glover.py
+++ b/defs/glover.py
@@ -11,6 +11,8 @@ ipv6: Union[bool, str] = "False"
admin: int = 0
lofter_channel: int = 0
lofter_channel_username: str = ""
+splash_channel: int = 0
+splash_channel_username: str = ""
# [api]
amap_key: str = ""
bili_cookie: str = ""
@@ -24,6 +26,10 @@ lofter_channel = config.getint("post", "lofter_channel", fallback=lofter_channel
lofter_channel_username = config.get(
"post", "lofter_channel_username", fallback=lofter_channel_username
)
+splash_channel = config.getint("post", "splash_channel", fallback=splash_channel)
+splash_channel_username = config.get(
+ "post", "splash_channel_username", fallback=splash_channel_username
+)
amap_key = config.get("api", "amap_key", fallback=amap_key)
bili_cookie = config.get("api", "bili_cookie", fallback=bili_cookie)
try:
diff --git a/defs/splash.py b/defs/splash.py
new file mode 100644
index 0000000..c3be63f
--- /dev/null
+++ b/defs/splash.py
@@ -0,0 +1,115 @@
+import asyncio
+import traceback
+from typing import List
+
+from pyrogram.enums import ParseMode
+from pyrogram.errors import FloodWait
+
+from defs.glover import splash_channel
+from init import bot, request, logger
+from models.models.splash import Splash as SplashModel
+from models.apis.splash import Splash as SplashApi
+from models.splash import SplashService
+
+
+async def get_splash() -> List[SplashApi]:
+ data = await request.get("https://bbs-api.miyoushe.com/apihub/api/getAppSplash")
+ splash_list = []
+ if data.is_success:
+ data = data.json()
+ for i in data["data"]["splashes"]:
+ splash_list.append(SplashApi(**i))
+ return splash_list
+
+
+async def check_splash(splash: SplashApi) -> bool:
+ if data := await SplashService.get_by_splash_id(splash.id):
+ if splash.splash_image:
+ data.splash_image = splash.splash_image
+ if splash.online_ts:
+ data.online_ts = splash.online_ts
+ if splash.offline_ts:
+ data.offline_ts = splash.offline_ts
+ if splash.article_url:
+ data.article_url = splash.article_url
+ await SplashService.update_splash(data)
+ return False
+ return True
+
+
+def gen_splash(splash: SplashApi) -> SplashModel:
+ return SplashModel(
+ id=splash.id,
+ splash_image=splash.splash_image,
+ online_ts=splash.online_ts,
+ offline_ts=splash.offline_ts,
+ file_id=splash.file_id,
+ article_url=splash.article_url,
+ )
+
+
+def retry(func):
+ async def wrapper(*args, **kwargs):
+ try:
+ return await func(*args, **kwargs)
+ except FloodWait as e:
+ logger.warning(f"Sleeping for {e.value}s")
+ await asyncio.sleep(e.value + 1)
+ return await func(*args, **kwargs)
+ return wrapper
+
+
+@retry
+async def send_splash_text(api: SplashApi):
+ await bot.send_message(
+ splash_channel,
+ api.text,
+ parse_mode=ParseMode.HTML,
+ disable_web_page_preview=True,
+ )
+
+
+@retry
+async def send_splash_photo(model: SplashModel):
+ photo = await bot.send_photo(
+ splash_channel,
+ model.splash_image,
+ )
+ model.file_id = photo.photo.file_id
+
+
+@retry
+async def send_splash_document(model: SplashModel):
+ await bot.send_document(
+ splash_channel,
+ model.splash_image,
+ force_document=True,
+ )
+
+
+async def send_splash(api: SplashApi, model: SplashModel):
+ await send_splash_text(api)
+ await send_splash_photo(model)
+ await send_splash_document(model)
+
+
+async def update_splash():
+ logger.info("Updating splash ...")
+ try:
+ data = await get_splash()
+ except Exception:
+ traceback.print_exc()
+ return
+ for i in data:
+ if not await check_splash(i):
+ continue
+ model = gen_splash(i)
+ if not model.splash_image:
+ continue
+ try:
+ await send_splash(i, model)
+ except Exception:
+ traceback.print_exc()
+ continue
+ await SplashService.add_splash(model)
+ logger.info("Splash updated.")
diff --git a/models/apis/splash.py b/models/apis/splash.py
new file mode 100644
index 0000000..363363e
--- /dev/null
+++ b/models/apis/splash.py
@@ -0,0 +1,66 @@
+import re
+from datetime import datetime
+from typing import Optional
+
+from pydantic import BaseModel
+
+GAME_ID_MAP = {1: "bh3", 2: "ys", 3: "bh2", 4: "wd", 5: "dby", 6: "sr", 8: "zzz"}
+
+
+class Splash(BaseModel):
+ id: int
+ splash_image: str
+ app_path: str
+ online_ts: int
+ offline_ts: int
+ game_id: Optional[int] = 0
+ file_id: Optional[str] = ""
+
+ @property
+ def online_time(self) -> datetime:
+ return datetime.fromtimestamp(int(self.online_ts))
+
+ @property
+ def online_time_str(self) -> str:
+ return self.online_time.strftime("%Y-%m-%d %H:%M:%S")
+
+ @property
+ def offline_time(self) -> datetime:
+ return datetime.fromtimestamp(int(self.offline_ts))
+
+ @property
+ def offline_time_str(self) -> str:
+ return self.offline_time.strftime("%Y-%m-%d %H:%M:%S")
+
+ @property
+ def article_id(self) -> int:
+ try:
+ return int(re.search(r"article/(\d+)", self.app_path).group(1))
+ except AttributeError:
+ return 0
+
+ @property
+ def game_short_name(self) -> str:
+ if not self.game_id:
+ return ""
+ return GAME_ID_MAP.get(self.game_id)
+
+ @property
+ def article_url(self) -> str:
+ if self.app_path.startswith("http"):
+ return self.app_path
+ if not self.article_id:
+ return ""
+ if not self.game_short_name:
+ return ""
+ return f"https://www.miyoushe.com/{self.game_short_name}/article/{self.article_id}"
+
+ @property
+ def text(self) -> str:
+ return f"#id{self.id} \n" \
+ f"ID:{self.id}
\n" \
+ f"所属分区:{self.game_id} - {self.game_short_name}
\n" \
+ f"开始时间:{self.online_time_str}
\n" \
+ f"结束时间:{self.offline_time_str}
\n" \
+ f"链接: {self.splash_image}\n" \
+ f"文章链接: {self.article_url}"
diff --git a/models/models/splash.py b/models/models/splash.py
new file mode 100644
index 0000000..925afd1
--- /dev/null
+++ b/models/models/splash.py
@@ -0,0 +1,12 @@
+from sqlmodel import SQLModel, Field
+
+
+class Splash(SQLModel, table=True):
+ __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci")
+
+ id: int = Field(primary_key=True)
+ splash_image: str = Field()
+ online_ts: int = Field(default=0)
+ offline_ts: int = Field(default=0)
+ file_id: str = Field()
+ article_url: str = Field(default="")
diff --git a/models/splash.py b/models/splash.py
new file mode 100644
index 0000000..e62c0ae
--- /dev/null
+++ b/models/splash.py
@@ -0,0 +1,41 @@
+from typing import cast, Optional, List
+
+from sqlalchemy import select
+from sqlmodel.ext.asyncio.session import AsyncSession
+
+from init import sqlite
+from models.models.splash import Splash
+
+
+class SplashService:
+ @staticmethod
+ async def get_by_splash_id(splash_id: int) -> Optional[Splash]:
+ async with sqlite.Session() as session:
+ session = cast(AsyncSession, session)
+ check = Splash.id == splash_id
+ statement = select(Splash).where(check)
+ results = await session.exec(statement)
+ return post[0] if (post := results.first()) else None
+
+ @staticmethod
+ async def get_all_splashes() -> List[Optional[Splash]]:
+ async with sqlite.Session() as session:
+ session = cast(AsyncSession, session)
+ statement = select(Splash)
+ results = await session.exec(statement)
+ return [item[0] for item in results.all()]
+
+ @staticmethod
+ async def add_splash(splash: Splash):
+ async with sqlite.Session() as session:
+ session = cast(AsyncSession, session)
+ session.add(splash)
+ await session.commit()
+
+ @staticmethod
+ async def update_splash(splash: Splash):
+ async with sqlite.Session() as session:
+ session = cast(AsyncSession, session)
+ session.add(splash)
+ await session.commit()
+ await session.refresh(splash)
diff --git a/modules/splash.py b/modules/splash.py
new file mode 100644
index 0000000..ba91661
--- /dev/null
+++ b/modules/splash.py
@@ -0,0 +1,21 @@
+from pyrogram import Client, filters
+from pyrogram.types import Message
+
+from defs.splash import update_splash
+from scheduler import scheduler
+
+
+@Client.on_message(
+ filters.incoming & filters.command(["splash_update"])
+)
+async def splash_update(_: Client, message: Message):
+ """
+ 更新 splash
+ """
+ await update_splash()
+ await message.reply("更新成功", quote=True)
+
+
+@scheduler.scheduled_job("interval", minutes=30, id="splash_update")
+async def splash_update_job() -> None:
+ await update_splash()