diff --git a/config.gen.ini b/config.gen.ini
index 671217a..1922c1c 100644
--- a/config.gen.ini
+++ b/config.gen.ini
@@ -18,3 +18,7 @@ consumer_key = ABCD
consumer_secret = ABCD
access_token_key = ABCD
access_token_secret = ABCD
+
+[post]
+admin = 0
+lofter_channel = 0
diff --git a/defs/glover.py b/defs/glover.py
index 2217367..b412715 100644
--- a/defs/glover.py
+++ b/defs/glover.py
@@ -12,6 +12,9 @@ consumer_key: str = ""
consumer_secret: str = ""
access_token_key: str = ""
access_token_secret: str = ""
+# [post]
+admin: int = 0
+lofter_channel: int = 0
config = RawConfigParser()
config.read("config.ini")
@@ -22,6 +25,8 @@ consumer_key = config.get("twitter", "consumer_key", fallback=consumer_key)
consumer_secret = config.get("twitter", "consumer_secret", fallback=consumer_secret)
access_token_key = config.get("twitter", "access_token_key", fallback=access_token_key)
access_token_secret = config.get("twitter", "access_token_secret", fallback=access_token_secret)
+admin = config.getint("post", "admin", fallback=admin)
+lofter_channel = config.getint("post", "lofter_channel", fallback=lofter_channel)
try:
ipv6 = strtobool(ipv6)
except ValueError:
diff --git a/defs/post.py b/defs/post.py
new file mode 100644
index 0000000..9851a12
--- /dev/null
+++ b/defs/post.py
@@ -0,0 +1,144 @@
+import asyncio
+import contextlib
+import os
+import re
+from asyncio import sleep
+from io import BytesIO
+
+from pyrogram.errors import FloodWait
+from pyrogram.types import Message
+
+from defs.glover import lofter_channel
+from defs.lofter import lofter_link
+from init import request, bot
+
+pattern = re.compile(r'<[^>]+>',re.S)
+
+
+class LofterPost:
+ def __init__(self, url: str, offset: str):
+ try:
+ self.grainId = int(url)
+ except ValueError:
+ self.grainId = int(url[url.find('grainId=') + 8:].split("&")[0])
+ try:
+ self.offset = int(offset)
+ except ValueError:
+ self.offset = 0
+ self.url = f"https://api.lofter.com/api-grain/grain/getH5Detail.json?grainId={self.grainId}&offset="
+
+ async def get_data(self):
+ if self.offset == -1:
+ return None
+ res = await request.get(self.url + str(self.offset))
+ assert res.status_code == 200
+ return res.json()
+
+ class Item:
+ def __init__(self, url, origin_url, title, username, name, tags, comment, static):
+ self.url = url.split('?')[0]
+ self.origin_url = origin_url
+ self.username = username
+ self.static = static
+ title = pattern.sub('\n', title).strip()[:500]
+ self.text = f"Lofter Status Info\n\n" \
+ f"{title}
\n\n" \
+ f"✍️ {name}\n" \
+ f"{tags}\n" \
+ f"{comment}"
+
+ async def init(self):
+ file = await request.get(self.url, timeout=30)
+ file = BytesIO(file.content)
+ file.name = os.path.basename(self.url)
+ return file
+
+ async def upload(self, file):
+ try:
+ if self.static:
+ await bot.send_document(lofter_channel, file, caption=self.text, disable_notification=True,
+ reply_markup=lofter_link(self.url, self.origin_url, self.username))
+ else:
+ await bot.send_photo(lofter_channel, file, caption=self.text, disable_notification=True,
+ reply_markup=lofter_link(self.url, self.origin_url, self.username))
+ except FloodWait as e:
+ await asyncio.sleep(e.value + 0.5)
+ await self.upload(file)
+
+ @staticmethod
+ def parse_data(data: list[dict]) -> list[Item]:
+ datas = []
+ for i in data:
+ if post_data := i.get("postData"):
+ username, name, comment = "", "", ""
+ if blog_info := post_data.get("blogInfo"):
+ username = blog_info.get("blogName", "")
+ name = blog_info.get("blogNickName", "")
+ if post_count_view := post_data.get("postCountView"):
+ a = post_count_view.get("responseCount", 0)
+ b = post_count_view.get("hotCount", 0)
+ c = post_count_view.get("favoriteCount", 0)
+ comment = f"评论({a}) 热度({b}) 喜欢({c})"
+ if post_view := post_data.get("postView"):
+ title = post_view.get("digest", "")
+ permalink = post_view.get("permalink", "")
+ origin_url = f"https://{username}.lofter.com/post/{permalink}"
+ tags = "".join(f"#{i} " for i in post_view.get("tagList", []))
+ if photo_post_view := post_view.get("photoPostView"):
+ if photo_links := photo_post_view.get("photoLinks"):
+ for photo in photo_links:
+ if url := photo.get("orign"):
+ width = photo.get("ow", 0)
+ height = photo.get("oh", 0)
+ static = abs(height - width) > 1300
+ datas.append(LofterPost.Item(
+ url,
+ origin_url,
+ title,
+ username,
+ name,
+ tags,
+ comment,
+ static
+ ))
+ return datas
+
+ async def get_items(self) -> list[Item]:
+ datas: list[LofterPost.Item] = []
+ while True:
+ data = await self.get_data()
+ if not data:
+ break
+ data = data.get("data", {})
+ if not data:
+ break
+ self.offset = data.get("offset", 0)
+ posts = data.get("posts", [])
+ if not posts:
+ break
+ datas.extend(self.parse_data(posts))
+ if self.offset == -1:
+ break
+ return datas
+
+ async def upload(self, message: Message):
+ msg = await message.reply_text("正在获取数据...")
+ try:
+ items = await self.get_items()
+ except Exception as e:
+ await msg.edit_text(f"获取数据失败: {e}")
+ return
+ await msg.edit(f"获取到{len(items)}条数据,正在上传...")
+ success, error, final = 0, 0, len(items)
+ for item in items:
+ try:
+ file = await item.init()
+ await item.upload(file)
+ success += 1
+ await sleep(1)
+ except Exception:
+ error += 1
+ if (success + error) % 10 == 0:
+ with contextlib.suppress(Exception):
+ await msg.edit(f"已成功上传{success}条,失败{error}条,剩余{final - success - error}条")
+ await msg.edit(f"上传完成,成功{success}条,失败{error}条")
diff --git a/modules/post.py b/modules/post.py
new file mode 100644
index 0000000..0941dd6
--- /dev/null
+++ b/modules/post.py
@@ -0,0 +1,25 @@
+from pyrogram import Client, filters
+from defs.glover import admin
+from pyrogram.types import Message
+
+from defs.post import LofterPost
+
+
+@Client.on_message(filters.incoming & filters.private & filters.user(admin) &
+ filters.command(["lofter_post"]))
+async def lofter_post_command(client: Client, message: Message):
+ """
+ 抓取 lofter 粮单
+ """
+ data = message.text.split(" ")
+ offset = 0
+ if len(data) < 2:
+ await message.reply("参数错误")
+ return
+ elif len(data) == 2:
+ url = data[1]
+ else:
+ url = data[1]
+ offset = data[2]
+ data = LofterPost(url, offset)
+ client.loop.create_task(data.upload(message))