feat: support mult misskey instance

This commit is contained in:
omg-xtao 2023-07-20 22:21:37 +08:00 committed by GitHub
parent 871d3f4a01
commit 6dd58cb15d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 286 additions and 235 deletions

View File

@ -5,28 +5,27 @@ from mipac.models.lite import LiteUser
from pyrogram.errors import MediaEmpty
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from glover import misskey_host
from init import bot, request
from models.services.scheduler import add_delete_file_job, delete_file
def get_user_link(user: LiteUser) -> str:
def get_user_link(host: str, user: LiteUser) -> str:
if user.host:
return f"https://{user.host}/@{user.username}"
return f"{misskey_host}/@{user.username}"
return f"https://{host}/@{user.username}@{user.host}"
return f"https://{host}/@{user.username}"
def get_source_link(message: ChatMessage) -> str:
def get_source_link(host: str, message: ChatMessage) -> str:
return (
f"{misskey_host}/my/messaging/{message.user.username}?cid={message.user.id}"
f"https://{host}/my/messaging/{message.user.username}?cid={message.user.id}"
if not message.group and message.user
else f"{misskey_host}/my/messaging/group/{message.group.id}"
else f"https://{host}/my/messaging/group/{message.group.id}"
)
def gen_button(message: ChatMessage):
author = get_user_link(message.user)
source = get_source_link(message)
def gen_button(host: str, message: ChatMessage):
author = get_user_link(host, message.user)
source = get_source_link(host, message)
first_line = [
InlineKeyboardButton(text="Chat", url=source),
InlineKeyboardButton(text="Author", url=author),
@ -34,24 +33,26 @@ def gen_button(message: ChatMessage):
return InlineKeyboardMarkup([first_line])
def get_content(message: ChatMessage) -> str:
def get_content(host: str, message: ChatMessage) -> str:
content = message.text or ""
content = content[:768]
user = f'<a href="{get_user_link(message.user)}">{message.user.nickname}</a>'
user = f'<a href="{get_user_link(host, message.user)}">{message.user.nickname}</a>'
if message.group:
group = f'<a href="{get_source_link(message)}">{message.group.name}</a>'
group = f'<a href="{get_source_link(host, message)}">{message.group.name}</a>'
user += f" ( {group} )"
return f"""<b>Misskey Message</b>
{user} <code>{content}</code>"""
async def send_text(cid: int, message: ChatMessage, reply_to_message_id: int):
async def send_text(
host: str, cid: int, message: ChatMessage, reply_to_message_id: int
):
await bot.send_message(
cid,
get_content(message),
get_content(host, message),
reply_to_message_id=reply_to_message_id,
reply_markup=gen_button(message),
reply_markup=gen_button(host, message),
disable_web_page_preview=True,
)
@ -61,53 +62,53 @@ def deprecated_to_text(func):
try:
return await func(*args, **kwargs)
except MediaEmpty:
return await send_text(args[0], args[2], args[3])
return await send_text(args[0], args[1], args[3], args[4])
return wrapper
@deprecated_to_text
async def send_photo(
cid: int, url: str, message: ChatMessage, reply_to_message_id: int
host: str, cid: int, url: str, message: ChatMessage, reply_to_message_id: int
):
if not url:
return await send_text(cid, message, reply_to_message_id)
return await send_text(host, cid, message, reply_to_message_id)
await bot.send_photo(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(message),
reply_markup=gen_button(message),
caption=get_content(host, message),
reply_markup=gen_button(host, message),
)
@deprecated_to_text
async def send_video(
cid: int, url: str, message: ChatMessage, reply_to_message_id: int
host: str, cid: int, url: str, message: ChatMessage, reply_to_message_id: int
):
if not url:
return await send_text(cid, message, reply_to_message_id)
return await send_text(host, cid, message, reply_to_message_id)
await bot.send_video(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(message),
reply_markup=gen_button(message),
caption=get_content(host, message),
reply_markup=gen_button(host, message),
)
@deprecated_to_text
async def send_audio(
cid: int, url: str, message: ChatMessage, reply_to_message_id: int
host: str, cid: int, url: str, message: ChatMessage, reply_to_message_id: int
):
if not url:
return await send_text(cid, message, reply_to_message_id)
return await send_text(host, cid, message, reply_to_message_id)
await bot.send_audio(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(message),
reply_markup=gen_button(message),
caption=get_content(host, message),
reply_markup=gen_button(host, message),
)
@ -129,31 +130,31 @@ async def fetch_document(file: File) -> Optional[str]:
@deprecated_to_text
async def send_document(
cid: int, file: File, message: ChatMessage, reply_to_message_id: int
host: str, cid: int, file: File, message: ChatMessage, reply_to_message_id: int
):
file = await fetch_document(file)
if not file:
return await send_text(cid, message, reply_to_message_id)
return await send_text(host, cid, message, reply_to_message_id)
await bot.send_document(
cid,
file,
reply_to_message_id=reply_to_message_id,
caption=get_content(message),
reply_markup=gen_button(message),
caption=get_content(host, message),
reply_markup=gen_button(host, message),
)
await delete_file(file)
async def send_chat_message(cid: int, message: ChatMessage, topic_id: int):
async def send_chat_message(host: str, cid: int, message: ChatMessage, topic_id: int):
if not message.file:
return await send_text(cid, message, topic_id)
return await send_text(host, cid, message, topic_id)
file_url = message.file.url
file_type = message.file.type
if file_type.startswith("image"):
await send_photo(cid, file_url, message, topic_id)
await send_photo(host, cid, file_url, message, topic_id)
elif file_type.startswith("video"):
await send_video(cid, file_url, message, topic_id)
await send_video(host, cid, file_url, message, topic_id)
elif file_type.startswith("audio"):
await send_audio(cid, file_url, message, topic_id)
await send_audio(host, cid, file_url, message, topic_id)
else:
await send_document(cid, message.file, message, topic_id)
await send_document(host, cid, message.file, message, topic_id)

30
defs/check_node.py Normal file
View File

@ -0,0 +1,30 @@
from httpx import URL, InvalidURL
from init import request
def get_host(url: str) -> str:
try:
url = URL(url)
except InvalidURL:
return ""
return url.host
async def check_host(host: str) -> bool:
if not host:
return False
try:
req = await request.get(f"https://{host}/.well-known/nodeinfo")
req.raise_for_status()
node_url = req.json()["links"][0]["href"]
req = await request.get(node_url)
req.raise_for_status()
data = req.json()
if data["software"]["name"] != "misskey":
raise ValueError
if not data["software"]["version"].startswith("13."):
raise ValueError
return True
except Exception:
return False

View File

@ -6,7 +6,6 @@ from mipac.models.lite import LiteUser
from mipac.types import IDriveFile
from pyrogram.enums import ParseMode
from pyrogram.errors import MediaEmpty
from pyrogram.types import (
InlineKeyboardMarkup,
InlineKeyboardButton,
@ -16,18 +15,17 @@ from pyrogram.types import (
InputMediaAudio,
)
from glover import misskey_host
from init import bot, request
from models.services.scheduler import add_delete_file_job, delete_file
def get_note_url(note: Note) -> str:
return f"{misskey_host}/notes/{note.id}"
def get_note_url(host: str, note: Note) -> str:
return f"https://{host}/notes/{note.id}"
def gen_button(note: Note, author: str):
source = get_note_url(note)
reply_source = get_note_url(note.reply) if note.reply else None
def gen_button(host: str, note: Note, author: str):
source = get_note_url(host, note)
reply_source = get_note_url(host, note.reply) if note.reply else None
renote_id = note.renote_id if note.reply else note.id
if reply_source:
first_line = [
@ -48,14 +46,16 @@ def gen_button(note: Note, author: str):
return InlineKeyboardMarkup([first_line, second_line])
def get_user_link(user: LiteUser) -> str:
def get_user_link(host: str, user: LiteUser) -> str:
if user.host:
return f"https://{user.host}/@{user.username}"
return f"{misskey_host}/@{user.username}"
return f"https://{host}/@{user.username}@{user.host}"
return f"https://{host}/@{user.username}"
def get_user_alink(user: LiteUser) -> str:
return "<a href=\"{}\">{}</a>".format(get_user_link(user), user.nickname or f"@{user.username}")
def get_user_alink(host: str, user: LiteUser) -> str:
return '<a href="{}">{}</a>'.format(
get_user_link(host, user), user.nickname or f"@{user.username}"
)
def get_post_time(date: datetime) -> str:
@ -66,7 +66,7 @@ def get_post_time(date: datetime) -> str:
return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
def get_content(note: Note) -> str:
def get_content(host: str, note: Note) -> str:
content = note.content or ""
action = "发表"
origin = ""
@ -76,7 +76,7 @@ def get_content(note: Note) -> str:
action = "转推"
content = note.renote.content or content
origin = (
f'\n{get_user_alink(note.renote.author)} '
f"\n{get_user_alink(host, note.renote.author)} "
f"发表于 {get_post_time(note.renote.created_at)}"
)
content = content[:768]
@ -84,16 +84,16 @@ def get_content(note: Note) -> str:
<code>{content}</code>
{get_user_alink(note.author)} {action} {get_post_time(note.created_at)}{origin}
{get_user_alink(host, note.author)} {action} {get_post_time(note.created_at)}{origin}
点赞: {sum(show_note.reactions.values())} | 回复: {show_note.replies_count} | 转发: {show_note.renote_count}"""
async def send_text(cid: int, note: Note, reply_to_message_id: int):
async def send_text(host: str, cid: int, note: Note, reply_to_message_id: int):
await bot.send_message(
cid,
get_content(note),
get_content(host, note),
reply_to_message_id=reply_to_message_id,
reply_markup=gen_button(note, get_user_link(note.author)),
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
disable_web_page_preview=True,
)
@ -103,47 +103,53 @@ def deprecated_to_text(func):
try:
return await func(*args, **kwargs)
except MediaEmpty:
return await send_text(args[0], args[2], args[3])
return await send_text(args[0], args[1], args[3], args[4])
return wrapper
@deprecated_to_text
async def send_photo(cid: int, url: str, note: Note, reply_to_message_id: int):
async def send_photo(
host: str, cid: int, url: str, note: Note, reply_to_message_id: int
):
if not url:
return await send_text(cid, note, reply_to_message_id)
return await send_text(host, cid, note, reply_to_message_id)
await bot.send_photo(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(note),
reply_markup=gen_button(note, get_user_link(note.author)),
caption=get_content(host, note),
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
)
@deprecated_to_text
async def send_video(cid: int, url: str, note: Note, reply_to_message_id: int):
async def send_video(
host: str, cid: int, url: str, note: Note, reply_to_message_id: int
):
if not url:
return await send_text(cid, note, reply_to_message_id)
return await send_text(host, cid, note, reply_to_message_id)
await bot.send_video(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(note),
reply_markup=gen_button(note, get_user_link(note.author)),
caption=get_content(host, note),
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
)
@deprecated_to_text
async def send_audio(cid: int, url: str, note: Note, reply_to_message_id: int):
async def send_audio(
host: str, cid: int, url: str, note: Note, reply_to_message_id: int
):
if not url:
return await send_text(cid, note, reply_to_message_id)
return await send_text(host, cid, note, reply_to_message_id)
await bot.send_audio(
cid,
url,
reply_to_message_id=reply_to_message_id,
caption=get_content(note),
reply_markup=gen_button(note, get_user_link(note.author)),
caption=get_content(host, note),
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
)
@ -165,17 +171,17 @@ async def fetch_document(file: IDriveFile) -> Optional[str]:
@deprecated_to_text
async def send_document(
cid: int, file: IDriveFile, note: Note, reply_to_message_id: int
host: str, cid: int, file: IDriveFile, note: Note, reply_to_message_id: int
):
file = await fetch_document(file)
if not file:
return await send_text(cid, note, reply_to_message_id)
return await send_text(host, cid, note, reply_to_message_id)
await bot.send_document(
cid,
file,
reply_to_message_id=reply_to_message_id,
caption=get_content(note),
reply_markup=gen_button(note, get_user_link(note.author)),
caption=get_content(host, note),
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
)
await delete_file(file)
@ -219,11 +225,11 @@ async def get_media_group(files: list[IDriveFile]) -> list:
async def send_group(
cid: int, files: list[IDriveFile], note: Note, reply_to_message_id: int
host: str, cid: int, files: list[IDriveFile], note: Note, reply_to_message_id: int
):
groups = await get_media_group(files)
if len(groups) == 0:
return await send_text(cid, note, reply_to_message_id)
return await send_text(host, cid, note, reply_to_message_id)
photo, video, audio, document, msg = [], [], [], [], None
for i in groups:
if isinstance(i, InputMediaPhoto):
@ -278,10 +284,10 @@ async def send_group(
)
if msg and isinstance(msg, list):
msg = msg[0]
await send_text(cid, note, msg.id if msg else None)
await send_text(host, cid, note, msg.id if msg else None)
async def send_update(cid: int, note: Note, topic_id: int):
async def send_update(host: str, cid: int, note: Note, topic_id: int):
files = list(note.files)
if note.reply:
files.extend(iter(note.reply.files))
@ -290,18 +296,18 @@ async def send_update(cid: int, note: Note, topic_id: int):
files = list({f.get("id"): f for f in files}.values())
match len(files):
case 0:
await send_text(cid, note, topic_id)
await send_text(host, cid, note, topic_id)
case 1:
file = files[0]
file_url = file.get("url", None)
file_type = file.get("type", "")
if file_type.startswith("image"):
await send_photo(cid, file_url, note, topic_id)
await send_photo(host, cid, file_url, note, topic_id)
elif file_type.startswith("video"):
await send_video(cid, file_url, note, topic_id)
await send_video(host, cid, file_url, note, topic_id)
elif file_type.startswith("audio"):
await send_audio(cid, file_url, note, topic_id)
await send_audio(host, cid, file_url, note, topic_id)
else:
await send_document(cid, file, note, topic_id)
await send_document(host, cid, file, note, topic_id)
case _:
await send_group(cid, files, note, topic_id)
await send_group(host, cid, files, note, topic_id)

View File

@ -1,10 +1,11 @@
from json import load
from mipac.models.lite.user import LiteUser
from mipac.models.notification import (
NotificationFollow,
NotificationFollowRequest,
NotificationAchievement,
)
from mipac.models.lite.user import LiteUser
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from init import bot

View File

@ -1,6 +1,6 @@
from datetime import datetime, timedelta
from typing import Optional
from datetime import datetime, timedelta
from mipac import UserDetailed
from mipac.errors import FailedToResolveRemoteUserError
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup

View File

@ -2,157 +2,157 @@
"notes1": [
"初来乍到",
"第一次发帖",
"祝您在Misskey玩的愉快"
"祝您在 Misskey 玩的愉快~"
],
"notes10": [
"一些帖子",
"发布了10篇帖子",
"发布了 10 篇帖子",
""
],
"notes100": [
"很多帖子",
"发布了100篇帖子",
"发布了 100 篇帖子",
""
],
"notes500": [
"满是帖子",
"发布了500篇帖子",
"发布了 500 篇帖子",
""
],
"notes1000": [
"积帖成山",
"发布了1,000篇帖子",
"发布了 1,000 篇帖子",
""
],
"notes5000": [
"帖如泉涌",
"发布了5,000篇帖子",
"发布了 5,000 篇帖子",
""
],
"notes10000": [
"超级帖",
"发布了10,000篇帖子",
"发布了 10,000 篇帖子",
""
],
"notes20000": [
"还想要更多帖子",
"发布了20,000篇帖子",
"发布了 20,000 篇帖子",
""
],
"notes30000": [
"帖子帖子帖子",
"发布了30,000篇帖子",
"发布了 30,000 篇帖子",
""
],
"notes40000": [
"帖子工厂",
"发布了40,000篇帖子",
"发布了 40,000 篇帖子",
""
],
"notes50000": [
"帖子星球",
"发布了50,000篇帖子",
"发布了 50,000 篇帖子",
""
],
"notes60000": [
"帖子类星体",
"发布了60,000篇帖子",
"发布了 60,000 篇帖子",
""
],
"notes70000": [
"帖子黑洞",
"发布了70,000篇帖子",
"发布了 70,000 篇帖子",
""
],
"notes80000": [
"帖子星系",
"发布了80,000篇帖子",
"发布了 80,000 篇帖子",
""
],
"notes90000": [
"帖子起源",
"发布了90,000篇帖子",
"发布了 90,000 篇帖子",
""
],
"notes100000": [
"ALL YOUR NOTE ARE BELONG TO US",
"发布了100,000篇帖子",
"发布了 100,000 篇帖子",
"真的有那么多可以写的东西吗?"
],
"login3": [
"初学者 I",
"连续登录3天",
"今天开始我就是Misskist"
"累计登录 3 天",
"今天开始我就是 Misskist"
],
"login7": [
"初学者 II",
"连续登录7天",
"累计登录 7 天",
"您开始习惯了吗?"
],
"login15": [
"初学者 III",
"连续登录15天",
"累计登录 15 天",
""
],
"login30": [
"Misskist ",
"连续登录30天",
"累计登录 30 天",
""
],
"login60": [
"Misskist Ⅱ",
"连续登录60天",
"累计登录 60 天",
""
],
"login100": [
"Misskist Ⅲ",
"总登入100天",
"那个用户,是Misskist喔"
"累计登入 100 天",
"那个用户,是 Misskist 喔"
],
"login200": [
"定期联系Ⅰ",
"总登录天数200天",
"累计登录 200 天",
""
],
"login300": [
"定期联系Ⅱ",
"总登录天数300天",
"累计登录 300 天",
""
],
"login400": [
"定期联系Ⅲ",
"总登录天数400天",
"累计登录 400 天",
""
],
"login500": [
"老熟人Ⅰ",
"总登录天数500天",
"累计登录 500 天",
"诸君,我喜欢贴文"
],
"login600": [
"老熟人Ⅱ",
"总登录天数600天",
"累计登录 600 天",
""
],
"login700": [
"老熟人Ⅲ",
"总登录天数700天",
"累计登录 700 天",
""
],
"login800": [
"帖子大师",
"总登录天数800天",
"帖子大师 ",
"累计登录 800 天",
""
],
"login900": [
"帖子大师Ⅱ",
"总登录天数900天",
"帖子大师 Ⅱ",
"累计登录 900 天",
""
],
"login1000": [
"帖子大师Ⅲ",
"总登录天数1000天",
"感谢您使用Misskey"
"帖子大师 Ⅲ",
"累计登录 1000 天",
"感谢您使用 Misskey"
],
"noteClipped1": [
"忍不住要收藏到便签",
@ -186,22 +186,22 @@
],
"following10": [
"关注,跟随",
"关注超过10人",
"关注超过 10 人",
""
],
"following50": [
"我的朋友很多",
"关注超过50人",
"关注超过 50 人",
""
],
"following100": [
"我的朋友很多",
"关注超过100人",
"胜友如云",
"关注超过 100 人",
""
],
"following300": [
"朋友成群",
"关注数超过300",
"关注数超过 300",
""
],
"followers1": [
@ -211,37 +211,37 @@
],
"followers10": [
"关注我吧!",
"拥有超过10名关注者",
"拥有超过 10 名关注者",
""
],
"followers50": [
"三五成群",
"拥有超过50名关注者",
"拥有超过 50 名关注者",
""
],
"followers100": [
"胜友如云",
"拥有超过100名关注者",
"拥有超过 100 名关注者",
""
],
"followers300": [
"排列成行",
"拥有超过300名关注者",
"拥有超过 300 名关注者",
""
],
"followers500": [
"信号塔",
"拥有超过500名关注者",
"拥有超过 500 名关注者",
""
],
"followers1000": [
"大影响家",
"拥有超过1000名关注者",
"拥有超过 1000 名关注者",
""
],
"collectAchievements30": [
"成就收藏家",
"获得超过30个成就",
"获得超过 30 个成就",
""
],
"viewAchievements3min": [
@ -251,7 +251,7 @@
],
"iLoveMisskey": [
"I Love Misskey",
"发布\"I ❤ #Misskey\"帖子",
"发布 \"I ❤ #Misskey\" 帖子",
"感谢您使用 Misskey by 开发团队"
],
"foundTreasure": [
@ -261,12 +261,12 @@
],
"client30min": [
"休息一下!",
"启动客户端超过30分钟",
"启动客户端超过 30 分钟",
""
],
"client60min": [
"Misskey重度依赖",
"启动客户端超过60分钟",
"Misskey 重度依赖",
"启动客户端超过 60 分钟",
""
],
"noteDeletedWithin1min": [
@ -281,7 +281,7 @@
],
"postedAt0min0sec": [
"报时",
"在0点发布一篇帖子",
"在 0 点发布一篇帖子",
"嘣 嘣 嘣 Biu——"
],
"selfQuote": [
@ -291,7 +291,7 @@
],
"htl20npm": [
"流动的时间线",
"在首页时间线的流速超过20npm",
"在首页时间线的流速超过 20npm",
""
],
"viewInstanceChart": [
@ -301,7 +301,7 @@
],
"outputHelloWorldOnScratchpad": [
"Hello, world!",
"在AiScript控制台中输出 hello world",
"在 AiScript 控制台中输出 hello world",
""
],
"open3windows": [
@ -316,7 +316,7 @@
],
"reactWithoutRead": [
"有好好读过吗?",
"在含有100字以上的帖子被发出三秒内做出回应",
"在含有 100 字以上的帖子被发出三秒内做出回应",
""
],
"clickedClickHere": [
@ -326,27 +326,27 @@
],
"justPlainLucky": [
"超高校级的幸运",
"每10秒有0.01的概率自动获得",
"每 10 秒有 0.01 的概率自动获得",
""
],
"setNameToSyuilo": [
"像神一样呐",
"将名称设定为syuilo",
"将名称设定为 syuilo",
""
],
"passedSinceAccountCreated1": [
"一周年",
"账户创建时间超过1年",
"账户创建时间超过 1 年",
""
],
"passedSinceAccountCreated2": [
"二周年",
"账户创建时间超过2年",
"账户创建时间超过 2 年",
""
],
"passedSinceAccountCreated3": [
"三周年",
"账户创建时间超过3年",
"账户创建时间超过 3 年",
""
],
"loggedInOnBirthday": [
@ -366,7 +366,7 @@
],
"brainDiver": [
"Brain Diver",
"发布了包含Brain Diver链接的帖子",
"发布了包含 Brain Diver 链接的帖子",
"Misskey-Misskey La-Tu-Ma"
]
}

View File

@ -1,7 +1,8 @@
import yaml
from pathlib import Path
from httpx import get
from json import dump
from pathlib import Path
import yaml
from httpx import get
json_path = Path(__file__).parent / "achievement.json"

View File

@ -1,4 +1,3 @@
import re
from configparser import RawConfigParser
from typing import Union
from distutils.util import strtobool
@ -9,9 +8,6 @@ api_hash: str = ""
# [Basic]
ipv6: Union[bool, str] = "False"
# [misskey]
misskey_url: str = ""
misskey_host: str = ""
misskey_domain: str = ""
web_domain: str = ""
admin: int = 0
@ -20,20 +16,9 @@ config.read("config.ini")
api_id = config.getint("pyrogram", "api_id", fallback=api_id)
api_hash = config.get("pyrogram", "api_hash", fallback=api_hash)
ipv6 = config.get("basic", "ipv6", fallback=ipv6)
misskey_url = config.get("misskey", "url", fallback=misskey_url)
if origin_url := re.search(r"wss?://(.*)/streaming", misskey_url):
misskey_host = (
origin_url[0]
.replace("wss", "https")
.replace("ws", "http")
.replace("/streaming", "")
)
else:
misskey_host = misskey_url
misskey_domain = re.search(r"https?://(.*)", misskey_host)[1]
web_domain = config.get("misskey", "web_domain", fallback=web_domain)
admin = config.getint("misskey", "admin", fallback=admin)
try:
ipv6 = strtobool(ipv6)
ipv6 = bool(strtobool(ipv6))
except ValueError:
ipv6 = False

View File

@ -23,7 +23,6 @@ from defs.notice import (
send_follow_request_accept,
send_achievement_earned,
)
from glover import misskey_url, misskey_host
from models.models.user import User, TokenStatusEnum
from models.services.user import UserAction
@ -45,41 +44,37 @@ class MisskeyBot(commands.Bot):
await Router(ws).connect_channel(["main", "home"])
async def on_note(self, note: Note):
if self.tg_user:
await send_update(self.tg_user.chat_id, note, self.tg_user.timeline_topic)
await send_update(
self.tg_user.host, self.tg_user.chat_id, note, self.tg_user.timeline_topic
)
async def on_user_followed(self, notice: NotificationFollow):
if self.tg_user:
await send_user_followed(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
await send_user_followed(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
async def on_follow_request(self, notice: NotificationFollowRequest):
if self.tg_user:
await send_follow_request(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
await send_follow_request(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
async def on_follow_request_accept(self, notice: NotificationFollowRequest):
if self.tg_user:
await send_follow_request_accept(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
await send_follow_request_accept(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
async def on_chat(self, message: ChatMessage):
if self.tg_user:
await send_chat_message(
self.tg_user.chat_id, message, self.tg_user.notice_topic
)
await send_chat_message(
self.tg_user.host, self.tg_user.chat_id, message, self.tg_user.notice_topic
)
async def on_chat_unread_message(self, message: ChatMessage):
await message.api.read()
async def on_achievement_earned(self, notice: NotificationAchievement):
if self.tg_user:
await send_achievement_earned(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
await send_achievement_earned(
self.tg_user.chat_id, notice, self.tg_user.notice_topic
)
misskey_bot_map: dict[int, MisskeyBot] = {}
@ -99,16 +94,16 @@ async def run(user: User):
misskey = await create_or_get_misskey_bot(user)
try:
logs.info(f"尝试启动 Misskey Bot WS 任务 {user.user_id}")
await misskey.start(misskey_url, user.token)
await misskey.start(f"wss://{user.host}", user.token)
except ClientConnectorError:
await sleep(3)
await run(user)
async def test_token(token: str) -> bool:
async def test_token(host: str, token: str) -> bool:
try:
logs.info(f"验证 Token {token}")
client = MisskeyClient(misskey_host, token)
logs.info(f"验证 Token {host} {token}")
client = MisskeyClient(f"https://{host}", token)
await client.http.login()
await client.http.close_session()
return True
@ -124,7 +119,7 @@ async def rerun_misskey_bot(user_id: int) -> bool:
user = await UserAction.get_user_if_ok(user_id)
if not user:
return False
if not await test_token(user.token):
if not await test_token(user.host, user.token):
await UserAction.set_user_status(user_id, TokenStatusEnum.INVALID_TOKEN)
return False
bot.loop.create_task(run(user))
@ -135,7 +130,7 @@ async def init_misskey_bot():
await sqlite.create_db_and_tables()
count = 0
for user in await UserAction.get_all_token_ok_users():
if not await test_token(user.token):
if not await test_token(user.host, user.token):
user.status = TokenStatusEnum.INVALID_TOKEN
await UserAction.update_user(user)
continue

View File

@ -12,8 +12,9 @@ class User(SQLModel, table=True):
__table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci")
user_id: int = Field(primary_key=True)
host: str = Field(default="")
token: str = Field(default="")
status: TokenStatusEnum = Field(sa_column=Column(Enum(TokenStatusEnum)))
chat_id: int = Field(default=0)
chat_id: int = Field(default=0, primary_key=True)
timeline_topic: int = Field(default=0)
notice_topic: int = Field(default=0)

View File

@ -35,6 +35,7 @@ class UserAction:
.where(User.timeline_topic != 0)
.where(User.notice_topic != 0)
.where(User.token != "")
.where(User.host != "")
)
results = await session.exec(statement)
return user[0] if (user := results.first()) else None
@ -80,12 +81,22 @@ class UserAction:
return True
@staticmethod
async def change_user_token(user_id: int, token: str) -> bool:
async def change_user_host(user_id: int, host: str) -> bool:
user = await UserAction.get_user_by_id(user_id)
if not user:
user = User(
user_id=user_id, token=token, status=TokenStatusEnum.STATUS_SUCCESS
user_id=user_id, host=host, status=TokenStatusEnum.INVALID_TOKEN
)
user.host = host
user.status = TokenStatusEnum.INVALID_TOKEN
await UserAction.update_user(user)
return True
@staticmethod
async def change_user_token(user_id: int, token: str) -> bool:
user = await UserAction.get_user_by_id(user_id)
if not user:
return False
user.token = token
user.status = TokenStatusEnum.STATUS_SUCCESS
await UserAction.update_user(user)

View File

@ -1,7 +1,8 @@
from pathlib import Path
from sqlmodel import SQLModel
from models.models.user import User
from pathlib import Path
__all__ = ["User", "Sqlite"]

View File

@ -1,7 +1,6 @@
from models.services.scheduler import scheduler
from defs.announcement import get_unread_announcements
from misskey_init import misskey_bot_map
from models.services.scheduler import scheduler
@scheduler.scheduled_job("interval", minutes=15, id="check_announcement")

View File

@ -1,9 +1,8 @@
from pyrogram import Client, filters
from pyrogram.types import Message
from models.services.user import UserAction
from misskey_init import rerun_misskey_bot
from models.services.user import UserAction
async def pre_check(message: Message):

View File

@ -1,4 +1,5 @@
import contextlib
from mipac.errors import (
InternalErrorError,
AlreadyFollowingError,

View File

@ -1,22 +1,25 @@
from pyrogram import Client, filters
from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from glover import misskey_host, web_domain, misskey_domain
from defs.check_node import get_host, check_host
from glover import web_domain
from init import bot
from misskey_init import test_token, rerun_misskey_bot
from models.services.user import UserAction
des = f"""欢迎使用 {bot.me.first_name},这是一个用于在 Telegram 上使用 Misskey 的机器人。按下方教程开始使用:
1. 点击下方按钮绑定 Misskey 账号
1. 使用 `/start https://[misskey_domain]` 设置账号所在 Misskey 实例地址仅支持 https 链接
2. 在论坛群组中使用 /bind_timeline 绑定 Timeline 话题接收时间线更新
2. 点击 start 之后回复你的按钮绑定所在 Misskey 实例的账号
3. 在论坛群组中使用 /bind_notice 绑定 Notice 话题接收通知
3. 在论坛群组中使用 /bind_timeline 绑定 Timeline 话题接收时间线更新
4. 在论坛群组中使用 /bind_notice 绑定 Notice 话题接收通知
至此你便可以在 Telegram 接收 Misskey 消息同时你可以私聊我使用 /status 查看 Bot 运行状态
Bot 仅支持绑定 {misskey_host} Misskey 账号"""
Bot 仅支持 Misskey V13 实例的账号"""
async def finish_check(message: Message):
@ -26,8 +29,40 @@ async def finish_check(message: Message):
await message.reply("Token 设置完成,请绑定群组。", quote=True)
def gen_url():
return f"https://{web_domain}/gen?host={misskey_domain}&back_host={web_domain}&username={bot.me.username}"
def gen_url(domain: str):
return f"https://{web_domain}/gen?host={domain}&back_host={web_domain}&username={bot.me.username}"
async def change_host(message: Message, token_or_host: str):
host = get_host(token_or_host)
if await check_host(host):
await UserAction.change_user_host(message.from_user.id, host)
await message.reply(
"Host 验证成功,请点击下方按钮绑定账号。",
quote=True,
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(text="绑定 Misskey 账号", url=gen_url(host)),
]
]
),
)
else:
await message.reply("Host 验证失败,请检查 Host 是否正在运行 Misskey V13", quote=True)
async def change_token(message: Message, token_or_host: str):
if user := await UserAction.get_user_by_id(message.from_user.id):
if user.host:
if await test_token(user.host, token_or_host):
await UserAction.change_user_token(message.from_user.id, token_or_host)
await message.reply(
"Token 验证成功,绑定账号完成。\n当你撤销此登录时,你可以重新点击按钮授权。", quote=True
)
await finish_check(message)
else:
await message.reply("Token 验证失败,请检查 Token 是否正确", quote=True)
@Client.on_message(filters.incoming & filters.private & filters.command(["start"]))
@ -36,27 +71,13 @@ async def start_command(_: Client, message: Message):
回应 start
"""
if len(message.command) == 2:
token = message.command[1]
if not token:
token_or_host = message.command[1]
if not token_or_host:
await message.reply(des, quote=True)
return
if await test_token(token):
await UserAction.change_user_token(message.from_user.id, token)
await message.reply(
"Token 验证成功,绑定账号完成。\n" "当你撤销此登录时,你可以重新点击按钮授权。", quote=True
)
await finish_check(message)
else:
await message.reply("Token 验证失败,请检查 Token 是否正确", quote=True)
if token_or_host.startswith("https://"):
await change_host(message, token_or_host)
return
await change_token(message, token_or_host)
return
await message.reply(
des,
quote=True,
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(text="绑定 Misskey 账号", url=gen_url()),
]
]
),
)
await message.reply(des, quote=True)

View File

@ -2,7 +2,6 @@ from pyrogram import filters, Client
from pyrogram.types import Message
from glover import admin
from misskey_init import get_misskey_bot, rerun_misskey_bot, misskey_bot_map
from models.models.user import TokenStatusEnum
from models.services.user import UserAction

View File

@ -5,4 +5,4 @@ apscheduler==3.10.1
sqlalchemy==1.4.41
sqlmodel==0.0.8
aiosqlite==0.19.0
PyYAML==6.0
PyYAML==6.0.1