From 8b5ad8c9e63170682ce03dc99861de4843c44357 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Wed, 27 Apr 2022 16:08:33 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Support=20ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + ci.py | 30 ++++++++++++++++++++++---- config.ini.example | 1 + defs/ci.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++ plugins/update.py | 26 +++++++++++++++++++++++ requirements.txt | 4 +++- 6 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 defs/ci.py create mode 100644 plugins/update.py diff --git a/.gitignore b/.gitignore index 008934d..899a9e2 100644 --- a/.gitignore +++ b/.gitignore @@ -155,3 +155,4 @@ cython_debug/ # data config.ini *.session +data/ diff --git a/ci.py b/ci.py index d3f1a5a..4c32437 100644 --- a/ci.py +++ b/ci.py @@ -1,11 +1,29 @@ from configparser import RawConfigParser -from httpx import get -from pyrogram import Client +from apscheduler.schedulers.asyncio import AsyncIOScheduler +from httpx import get +from os.path import exists +from os import mkdir, sep +from pyrogram import Client +from sqlitedict import SqliteDict + +if not exists("data"): + mkdir("data") +sqlite = SqliteDict(f"data{sep}sqlite.db", autocommit=True) +# 结构如下 +# { +# "artifact_id": 0, +# } config = RawConfigParser() config.read("config.ini") bot_token: str = "" -bot_token = config.get("basic", "bot_token", fallback=bot_token) +api_id: int = 0 +api_hash: str = "" +channel_id: int = 0 +bot_token = config.get("pyrogram", "bot_token", fallback=bot_token) +api_id = config.get("pyrogram", "api_id", fallback=api_id) +api_hash = config.get("basic", "api_hash", fallback=api_hash) +channel_id = config.get("basic", "channel_id", fallback=channel_id) # 自定义类型 @@ -18,4 +36,8 @@ class Bot: me = Bot(get(f"https://api.telegram.org/bot{bot_token}/getme").json()["result"]) # 初始化客户端 -app = Client("bot", bot_token=bot_token) +scheduler = AsyncIOScheduler() +if not scheduler.running: + scheduler.configure(timezone="Asia/ShangHai") + scheduler.start() +app = Client("bot", api_id=api_id, api_hash=api_hash, bot_token=bot_token, plugins=dict(root="plugins")) diff --git a/config.ini.example b/config.ini.example index 62b8e3f..4d2c33d 100644 --- a/config.ini.example +++ b/config.ini.example @@ -4,6 +4,7 @@ api_hash = 0123456789abc0123456789abc [basic] bot_token = 111:abc +channel_id = 0 [plugins] root = plugins diff --git a/defs/ci.py b/defs/ci.py new file mode 100644 index 0000000..08b322a --- /dev/null +++ b/defs/ci.py @@ -0,0 +1,53 @@ +import datetime +from os.path import exists +from os import remove +from typing import Optional + +from httpx import get, stream + + +class Artifact: + def __init__(self, data: dict): + self.id: int = data.get("id", 0) + self.name: str = "Grasscutter.zip" + self.url: str = data.get("archive_download_url", "") + size: int = data.get("archive_size", 0) + self.size: str = f"{size / 1024 / 1024:.2f} MB" + self.expired: bool = data.get("expired", False) + created_at: str = data.get("created_at", "") + # 2022-04-27T06:31:46Z + try: + self.created_at: datetime.datetime = datetime.datetime.strptime( + created_at, "%Y-%m-%dT%H:%M:%SZ" + ) + datetime.timedelta(hours=8) + except ValueError: + self.created_at: datetime.datetime = datetime.datetime.utcnow() + datetime.timedelta(hours=8) + self.created_time: str = self.created_at.strftime("%Y-%m-%d %H:%M:%S") + self.hash: str = get_hash() + + async def download(self, path: str = "Grasscutter.zip"): + if exists("Grasscutter.zip"): + remove("Grasscutter.zip") + async with stream("GET", self.url, allow_redirects=True) as response: + for chunk in response.iter_bytes(): + with open(path, "ab") as f: + f.write(chunk) + + +async def get_artifact() -> Optional[Artifact]: + req = get("https://api.github.com/repos/Grasscutters/Grasscutter/actions/artifacts?per_page=1") + if req.status_code == 200: + data = req.json() + if data.get("artifacts", []): + return Artifact(data["artifacts"][0]) + return None + + +def get_hash() -> str: + req = get("https://api.github.com/repos/Grasscutters/Grasscutter/actions/runs?per_page=1&status=success") + if req.status_code == 200: + data = req.json() + if data.get("workflow_runs", []): + return data["workflow_runs"][0]["head_commit"]["id"][:6] + " " + \ + data["workflow_runs"][0]["head_commit"]["message"][:50] + return "" diff --git a/plugins/update.py b/plugins/update.py new file mode 100644 index 0000000..1481aa8 --- /dev/null +++ b/plugins/update.py @@ -0,0 +1,26 @@ +from pyrogram import Client, filters +from pyrogram.types import Message +from ci import app, channel_id, sqlite, scheduler +from defs.ci import get_artifact + + +@scheduler.scheduled_job("cron", minute="*/30", id="0") +async def run_every_30_minute(): + old_artifact_id = sqlite.get("artifact_id", 0) + new_artifact = await get_artifact() + if old_artifact_id == new_artifact.id: + return + if not channel_id: + return + sqlite.set("artifact_id", new_artifact.id) + await new_artifact.download() + await app.send_document(channel_id, "Grasscutter.zip", + caption=new_artifact.hash, + force_document=True) + + +@Client.on_message(filters.incoming & filters.private & + filters.command(["force_update", ])) +async def force_update(_: Client, message: Message): + await run_every_30_minute() + await message.reply("Updated!", quote=True) diff --git a/requirements.txt b/requirements.txt index 2d32277..27f8cb6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ -pyrogram==1.4.16 +pyrogram==2.0.13 tgcrypto httpx +sqlitedict +apscheduler>=3.8.1