mirror of
https://github.com/Xtao-Labs/misskey2telegram.git
synced 2024-11-25 22:59:19 +00:00
feat: support mult misskey instance
This commit is contained in:
parent
871d3f4a01
commit
6dd58cb15d
79
defs/chat.py
79
defs/chat.py
@ -5,28 +5,27 @@ from mipac.models.lite import LiteUser
|
|||||||
from pyrogram.errors import MediaEmpty
|
from pyrogram.errors import MediaEmpty
|
||||||
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
|
||||||
from glover import misskey_host
|
|
||||||
from init import bot, request
|
from init import bot, request
|
||||||
from models.services.scheduler import add_delete_file_job, delete_file
|
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:
|
if user.host:
|
||||||
return f"https://{user.host}/@{user.username}"
|
return f"https://{host}/@{user.username}@{user.host}"
|
||||||
return f"{misskey_host}/@{user.username}"
|
return f"https://{host}/@{user.username}"
|
||||||
|
|
||||||
|
|
||||||
def get_source_link(message: ChatMessage) -> str:
|
def get_source_link(host: str, message: ChatMessage) -> str:
|
||||||
return (
|
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
|
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):
|
def gen_button(host: str, message: ChatMessage):
|
||||||
author = get_user_link(message.user)
|
author = get_user_link(host, message.user)
|
||||||
source = get_source_link(message)
|
source = get_source_link(host, message)
|
||||||
first_line = [
|
first_line = [
|
||||||
InlineKeyboardButton(text="Chat", url=source),
|
InlineKeyboardButton(text="Chat", url=source),
|
||||||
InlineKeyboardButton(text="Author", url=author),
|
InlineKeyboardButton(text="Author", url=author),
|
||||||
@ -34,24 +33,26 @@ def gen_button(message: ChatMessage):
|
|||||||
return InlineKeyboardMarkup([first_line])
|
return InlineKeyboardMarkup([first_line])
|
||||||
|
|
||||||
|
|
||||||
def get_content(message: ChatMessage) -> str:
|
def get_content(host: str, message: ChatMessage) -> str:
|
||||||
content = message.text or ""
|
content = message.text or ""
|
||||||
content = content[:768]
|
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:
|
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} )"
|
user += f" ( {group} )"
|
||||||
return f"""<b>Misskey Message</b>
|
return f"""<b>Misskey Message</b>
|
||||||
|
|
||||||
{user}: <code>{content}</code>"""
|
{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(
|
await bot.send_message(
|
||||||
cid,
|
cid,
|
||||||
get_content(message),
|
get_content(host, message),
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
reply_markup=gen_button(message),
|
reply_markup=gen_button(host, message),
|
||||||
disable_web_page_preview=True,
|
disable_web_page_preview=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,53 +62,53 @@ def deprecated_to_text(func):
|
|||||||
try:
|
try:
|
||||||
return await func(*args, **kwargs)
|
return await func(*args, **kwargs)
|
||||||
except MediaEmpty:
|
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
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@deprecated_to_text
|
||||||
async def send_photo(
|
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:
|
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(
|
await bot.send_photo(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(message),
|
caption=get_content(host, message),
|
||||||
reply_markup=gen_button(message),
|
reply_markup=gen_button(host, message),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@deprecated_to_text
|
||||||
async def send_video(
|
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:
|
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(
|
await bot.send_video(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(message),
|
caption=get_content(host, message),
|
||||||
reply_markup=gen_button(message),
|
reply_markup=gen_button(host, message),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@deprecated_to_text
|
||||||
async def send_audio(
|
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:
|
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(
|
await bot.send_audio(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(message),
|
caption=get_content(host, message),
|
||||||
reply_markup=gen_button(message),
|
reply_markup=gen_button(host, message),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -129,31 +130,31 @@ async def fetch_document(file: File) -> Optional[str]:
|
|||||||
|
|
||||||
@deprecated_to_text
|
@deprecated_to_text
|
||||||
async def send_document(
|
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)
|
file = await fetch_document(file)
|
||||||
if not 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(
|
await bot.send_document(
|
||||||
cid,
|
cid,
|
||||||
file,
|
file,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(message),
|
caption=get_content(host, message),
|
||||||
reply_markup=gen_button(message),
|
reply_markup=gen_button(host, message),
|
||||||
)
|
)
|
||||||
await delete_file(file)
|
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:
|
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_url = message.file.url
|
||||||
file_type = message.file.type
|
file_type = message.file.type
|
||||||
if file_type.startswith("image"):
|
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"):
|
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"):
|
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:
|
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
30
defs/check_node.py
Normal 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
|
@ -6,7 +6,6 @@ from mipac.models.lite import LiteUser
|
|||||||
from mipac.types import IDriveFile
|
from mipac.types import IDriveFile
|
||||||
from pyrogram.enums import ParseMode
|
from pyrogram.enums import ParseMode
|
||||||
from pyrogram.errors import MediaEmpty
|
from pyrogram.errors import MediaEmpty
|
||||||
|
|
||||||
from pyrogram.types import (
|
from pyrogram.types import (
|
||||||
InlineKeyboardMarkup,
|
InlineKeyboardMarkup,
|
||||||
InlineKeyboardButton,
|
InlineKeyboardButton,
|
||||||
@ -16,18 +15,17 @@ from pyrogram.types import (
|
|||||||
InputMediaAudio,
|
InputMediaAudio,
|
||||||
)
|
)
|
||||||
|
|
||||||
from glover import misskey_host
|
|
||||||
from init import bot, request
|
from init import bot, request
|
||||||
from models.services.scheduler import add_delete_file_job, delete_file
|
from models.services.scheduler import add_delete_file_job, delete_file
|
||||||
|
|
||||||
|
|
||||||
def get_note_url(note: Note) -> str:
|
def get_note_url(host: str, note: Note) -> str:
|
||||||
return f"{misskey_host}/notes/{note.id}"
|
return f"https://{host}/notes/{note.id}"
|
||||||
|
|
||||||
|
|
||||||
def gen_button(note: Note, author: str):
|
def gen_button(host: str, note: Note, author: str):
|
||||||
source = get_note_url(note)
|
source = get_note_url(host, note)
|
||||||
reply_source = get_note_url(note.reply) if note.reply else None
|
reply_source = get_note_url(host, note.reply) if note.reply else None
|
||||||
renote_id = note.renote_id if note.reply else note.id
|
renote_id = note.renote_id if note.reply else note.id
|
||||||
if reply_source:
|
if reply_source:
|
||||||
first_line = [
|
first_line = [
|
||||||
@ -48,14 +46,16 @@ def gen_button(note: Note, author: str):
|
|||||||
return InlineKeyboardMarkup([first_line, second_line])
|
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:
|
if user.host:
|
||||||
return f"https://{user.host}/@{user.username}"
|
return f"https://{host}/@{user.username}@{user.host}"
|
||||||
return f"{misskey_host}/@{user.username}"
|
return f"https://{host}/@{user.username}"
|
||||||
|
|
||||||
|
|
||||||
def get_user_alink(user: LiteUser) -> str:
|
def get_user_alink(host: str, user: LiteUser) -> str:
|
||||||
return "<a href=\"{}\">{}</a>".format(get_user_link(user), user.nickname or f"@{user.username}")
|
return '<a href="{}">{}</a>'.format(
|
||||||
|
get_user_link(host, user), user.nickname or f"@{user.username}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_post_time(date: datetime) -> str:
|
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")
|
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 ""
|
content = note.content or ""
|
||||||
action = "发表"
|
action = "发表"
|
||||||
origin = ""
|
origin = ""
|
||||||
@ -76,7 +76,7 @@ def get_content(note: Note) -> str:
|
|||||||
action = "转推"
|
action = "转推"
|
||||||
content = note.renote.content or content
|
content = note.renote.content or content
|
||||||
origin = (
|
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)}"
|
f"发表于 {get_post_time(note.renote.created_at)}"
|
||||||
)
|
)
|
||||||
content = content[:768]
|
content = content[:768]
|
||||||
@ -84,16 +84,16 @@ def get_content(note: Note) -> str:
|
|||||||
|
|
||||||
<code>{content}</code>
|
<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}"""
|
点赞: {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(
|
await bot.send_message(
|
||||||
cid,
|
cid,
|
||||||
get_content(note),
|
get_content(host, note),
|
||||||
reply_to_message_id=reply_to_message_id,
|
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,
|
disable_web_page_preview=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -103,47 +103,53 @@ def deprecated_to_text(func):
|
|||||||
try:
|
try:
|
||||||
return await func(*args, **kwargs)
|
return await func(*args, **kwargs)
|
||||||
except MediaEmpty:
|
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
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@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:
|
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(
|
await bot.send_photo(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(note),
|
caption=get_content(host, note),
|
||||||
reply_markup=gen_button(note, get_user_link(note.author)),
|
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@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:
|
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(
|
await bot.send_video(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(note),
|
caption=get_content(host, note),
|
||||||
reply_markup=gen_button(note, get_user_link(note.author)),
|
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@deprecated_to_text
|
@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:
|
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(
|
await bot.send_audio(
|
||||||
cid,
|
cid,
|
||||||
url,
|
url,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(note),
|
caption=get_content(host, note),
|
||||||
reply_markup=gen_button(note, get_user_link(note.author)),
|
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
|
@deprecated_to_text
|
||||||
async def send_document(
|
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)
|
file = await fetch_document(file)
|
||||||
if not 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(
|
await bot.send_document(
|
||||||
cid,
|
cid,
|
||||||
file,
|
file,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
caption=get_content(note),
|
caption=get_content(host, note),
|
||||||
reply_markup=gen_button(note, get_user_link(note.author)),
|
reply_markup=gen_button(host, note, get_user_link(host, note.author)),
|
||||||
)
|
)
|
||||||
await delete_file(file)
|
await delete_file(file)
|
||||||
|
|
||||||
@ -219,11 +225,11 @@ async def get_media_group(files: list[IDriveFile]) -> list:
|
|||||||
|
|
||||||
|
|
||||||
async def send_group(
|
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)
|
groups = await get_media_group(files)
|
||||||
if len(groups) == 0:
|
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
|
photo, video, audio, document, msg = [], [], [], [], None
|
||||||
for i in groups:
|
for i in groups:
|
||||||
if isinstance(i, InputMediaPhoto):
|
if isinstance(i, InputMediaPhoto):
|
||||||
@ -278,10 +284,10 @@ async def send_group(
|
|||||||
)
|
)
|
||||||
if msg and isinstance(msg, list):
|
if msg and isinstance(msg, list):
|
||||||
msg = msg[0]
|
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)
|
files = list(note.files)
|
||||||
if note.reply:
|
if note.reply:
|
||||||
files.extend(iter(note.reply.files))
|
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())
|
files = list({f.get("id"): f for f in files}.values())
|
||||||
match len(files):
|
match len(files):
|
||||||
case 0:
|
case 0:
|
||||||
await send_text(cid, note, topic_id)
|
await send_text(host, cid, note, topic_id)
|
||||||
case 1:
|
case 1:
|
||||||
file = files[0]
|
file = files[0]
|
||||||
file_url = file.get("url", None)
|
file_url = file.get("url", None)
|
||||||
file_type = file.get("type", "")
|
file_type = file.get("type", "")
|
||||||
if file_type.startswith("image"):
|
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"):
|
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"):
|
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:
|
else:
|
||||||
await send_document(cid, file, note, topic_id)
|
await send_document(host, cid, file, note, topic_id)
|
||||||
case _:
|
case _:
|
||||||
await send_group(cid, files, note, topic_id)
|
await send_group(host, cid, files, note, topic_id)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from json import load
|
from json import load
|
||||||
|
|
||||||
|
from mipac.models.lite.user import LiteUser
|
||||||
from mipac.models.notification import (
|
from mipac.models.notification import (
|
||||||
NotificationFollow,
|
NotificationFollow,
|
||||||
NotificationFollowRequest,
|
NotificationFollowRequest,
|
||||||
NotificationAchievement,
|
NotificationAchievement,
|
||||||
)
|
)
|
||||||
from mipac.models.lite.user import LiteUser
|
|
||||||
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
|
||||||
from init import bot
|
from init import bot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from mipac import UserDetailed
|
from mipac import UserDetailed
|
||||||
from mipac.errors import FailedToResolveRemoteUserError
|
from mipac.errors import FailedToResolveRemoteUserError
|
||||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
@ -2,157 +2,157 @@
|
|||||||
"notes1": [
|
"notes1": [
|
||||||
"初来乍到",
|
"初来乍到",
|
||||||
"第一次发帖",
|
"第一次发帖",
|
||||||
"祝您在Misskey玩的愉快~"
|
"祝您在 Misskey 玩的愉快~"
|
||||||
],
|
],
|
||||||
"notes10": [
|
"notes10": [
|
||||||
"一些帖子",
|
"一些帖子",
|
||||||
"发布了10篇帖子",
|
"发布了 10 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes100": [
|
"notes100": [
|
||||||
"很多帖子",
|
"很多帖子",
|
||||||
"发布了100篇帖子",
|
"发布了 100 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes500": [
|
"notes500": [
|
||||||
"满是帖子",
|
"满是帖子",
|
||||||
"发布了500篇帖子",
|
"发布了 500 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes1000": [
|
"notes1000": [
|
||||||
"积帖成山",
|
"积帖成山",
|
||||||
"发布了1,000篇帖子",
|
"发布了 1,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes5000": [
|
"notes5000": [
|
||||||
"帖如泉涌",
|
"帖如泉涌",
|
||||||
"发布了5,000篇帖子",
|
"发布了 5,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes10000": [
|
"notes10000": [
|
||||||
"超级帖",
|
"超级帖",
|
||||||
"发布了10,000篇帖子",
|
"发布了 10,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes20000": [
|
"notes20000": [
|
||||||
"还想要更多帖子",
|
"还想要更多帖子",
|
||||||
"发布了20,000篇帖子",
|
"发布了 20,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes30000": [
|
"notes30000": [
|
||||||
"帖子帖子帖子",
|
"帖子帖子帖子",
|
||||||
"发布了30,000篇帖子",
|
"发布了 30,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes40000": [
|
"notes40000": [
|
||||||
"帖子工厂",
|
"帖子工厂",
|
||||||
"发布了40,000篇帖子",
|
"发布了 40,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes50000": [
|
"notes50000": [
|
||||||
"帖子星球",
|
"帖子星球",
|
||||||
"发布了50,000篇帖子",
|
"发布了 50,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes60000": [
|
"notes60000": [
|
||||||
"帖子类星体",
|
"帖子类星体",
|
||||||
"发布了60,000篇帖子",
|
"发布了 60,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes70000": [
|
"notes70000": [
|
||||||
"帖子黑洞",
|
"帖子黑洞",
|
||||||
"发布了70,000篇帖子",
|
"发布了 70,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes80000": [
|
"notes80000": [
|
||||||
"帖子星系",
|
"帖子星系",
|
||||||
"发布了80,000篇帖子",
|
"发布了 80,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes90000": [
|
"notes90000": [
|
||||||
"帖子起源",
|
"帖子起源",
|
||||||
"发布了90,000篇帖子",
|
"发布了 90,000 篇帖子",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"notes100000": [
|
"notes100000": [
|
||||||
"ALL YOUR NOTE ARE BELONG TO US",
|
"ALL YOUR NOTE ARE BELONG TO US",
|
||||||
"发布了100,000篇帖子",
|
"发布了 100,000 篇帖子",
|
||||||
"真的有那么多可以写的东西吗?"
|
"真的有那么多可以写的东西吗?"
|
||||||
],
|
],
|
||||||
"login3": [
|
"login3": [
|
||||||
"初学者 I",
|
"初学者 I",
|
||||||
"连续登录3天",
|
"累计登录 3 天",
|
||||||
"今天开始我就是Misskist!"
|
"今天开始我就是 Misskist!"
|
||||||
],
|
],
|
||||||
"login7": [
|
"login7": [
|
||||||
"初学者 II",
|
"初学者 II",
|
||||||
"连续登录7天",
|
"累计登录 7 天",
|
||||||
"您开始习惯了吗?"
|
"您开始习惯了吗?"
|
||||||
],
|
],
|
||||||
"login15": [
|
"login15": [
|
||||||
"初学者 III",
|
"初学者 III",
|
||||||
"连续登录15天",
|
"累计登录 15 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login30": [
|
"login30": [
|
||||||
"Misskist Ⅰ",
|
"Misskist Ⅰ",
|
||||||
"连续登录30天",
|
"累计登录 30 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login60": [
|
"login60": [
|
||||||
"Misskist Ⅱ",
|
"Misskist Ⅱ",
|
||||||
"连续登录60天",
|
"累计登录 60 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login100": [
|
"login100": [
|
||||||
"Misskist Ⅲ",
|
"Misskist Ⅲ",
|
||||||
"总登入100天",
|
"累计登入 100 天",
|
||||||
"那个用户,是Misskist喔"
|
"那个用户,是 Misskist 喔"
|
||||||
],
|
],
|
||||||
"login200": [
|
"login200": [
|
||||||
"定期联系Ⅰ",
|
"定期联系Ⅰ",
|
||||||
"总登录天数200天",
|
"累计登录 200 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login300": [
|
"login300": [
|
||||||
"定期联系Ⅱ",
|
"定期联系Ⅱ",
|
||||||
"总登录天数300天",
|
"累计登录 300 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login400": [
|
"login400": [
|
||||||
"定期联系Ⅲ",
|
"定期联系Ⅲ",
|
||||||
"总登录天数400天",
|
"累计登录 400 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login500": [
|
"login500": [
|
||||||
"老熟人Ⅰ",
|
"老熟人Ⅰ",
|
||||||
"总登录天数500天",
|
"累计登录 500 天",
|
||||||
"诸君,我喜欢贴文"
|
"诸君,我喜欢贴文"
|
||||||
],
|
],
|
||||||
"login600": [
|
"login600": [
|
||||||
"老熟人Ⅱ",
|
"老熟人Ⅱ",
|
||||||
"总登录天数600天",
|
"累计登录 600 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login700": [
|
"login700": [
|
||||||
"老熟人Ⅲ",
|
"老熟人Ⅲ",
|
||||||
"总登录天数700天",
|
"累计登录 700 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login800": [
|
"login800": [
|
||||||
"帖子大师Ⅰ",
|
"帖子大师 Ⅰ",
|
||||||
"总登录天数800天",
|
"累计登录 800 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login900": [
|
"login900": [
|
||||||
"帖子大师Ⅱ",
|
"帖子大师 Ⅱ",
|
||||||
"总登录天数900天",
|
"累计登录 900 天",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"login1000": [
|
"login1000": [
|
||||||
"帖子大师Ⅲ",
|
"帖子大师 Ⅲ",
|
||||||
"总登录天数1000天",
|
"累计登录 1000 天",
|
||||||
"感谢您使用Misskey!"
|
"感谢您使用 Misskey!"
|
||||||
],
|
],
|
||||||
"noteClipped1": [
|
"noteClipped1": [
|
||||||
"忍不住要收藏到便签",
|
"忍不住要收藏到便签",
|
||||||
@ -186,22 +186,22 @@
|
|||||||
],
|
],
|
||||||
"following10": [
|
"following10": [
|
||||||
"关注,跟随",
|
"关注,跟随",
|
||||||
"关注超过10人",
|
"关注超过 10 人",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"following50": [
|
"following50": [
|
||||||
"我的朋友很多",
|
"我的朋友很多",
|
||||||
"关注超过50人",
|
"关注超过 50 人",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"following100": [
|
"following100": [
|
||||||
"我的朋友很多",
|
"胜友如云",
|
||||||
"关注超过100人",
|
"关注超过 100 人",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"following300": [
|
"following300": [
|
||||||
"朋友成群",
|
"朋友成群",
|
||||||
"关注数超过300",
|
"关注数超过 300",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers1": [
|
"followers1": [
|
||||||
@ -211,37 +211,37 @@
|
|||||||
],
|
],
|
||||||
"followers10": [
|
"followers10": [
|
||||||
"关注我吧!",
|
"关注我吧!",
|
||||||
"拥有超过10名关注者",
|
"拥有超过 10 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers50": [
|
"followers50": [
|
||||||
"三五成群",
|
"三五成群",
|
||||||
"拥有超过50名关注者",
|
"拥有超过 50 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers100": [
|
"followers100": [
|
||||||
"胜友如云",
|
"胜友如云",
|
||||||
"拥有超过100名关注者",
|
"拥有超过 100 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers300": [
|
"followers300": [
|
||||||
"排列成行",
|
"排列成行",
|
||||||
"拥有超过300名关注者",
|
"拥有超过 300 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers500": [
|
"followers500": [
|
||||||
"信号塔",
|
"信号塔",
|
||||||
"拥有超过500名关注者",
|
"拥有超过 500 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"followers1000": [
|
"followers1000": [
|
||||||
"大影响家",
|
"大影响家",
|
||||||
"拥有超过1000名关注者",
|
"拥有超过 1000 名关注者",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"collectAchievements30": [
|
"collectAchievements30": [
|
||||||
"成就收藏家",
|
"成就收藏家",
|
||||||
"获得超过30个成就",
|
"获得超过 30 个成就",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"viewAchievements3min": [
|
"viewAchievements3min": [
|
||||||
@ -251,7 +251,7 @@
|
|||||||
],
|
],
|
||||||
"iLoveMisskey": [
|
"iLoveMisskey": [
|
||||||
"I Love Misskey",
|
"I Love Misskey",
|
||||||
"发布\"I ❤ #Misskey\"帖子",
|
"发布 \"I ❤ #Misskey\" 帖子",
|
||||||
"感谢您使用 Misskey ! by 开发团队"
|
"感谢您使用 Misskey ! by 开发团队"
|
||||||
],
|
],
|
||||||
"foundTreasure": [
|
"foundTreasure": [
|
||||||
@ -261,12 +261,12 @@
|
|||||||
],
|
],
|
||||||
"client30min": [
|
"client30min": [
|
||||||
"休息一下!",
|
"休息一下!",
|
||||||
"启动客户端超过30分钟",
|
"启动客户端超过 30 分钟",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"client60min": [
|
"client60min": [
|
||||||
"Misskey重度依赖",
|
"Misskey 重度依赖",
|
||||||
"启动客户端超过60分钟",
|
"启动客户端超过 60 分钟",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"noteDeletedWithin1min": [
|
"noteDeletedWithin1min": [
|
||||||
@ -281,7 +281,7 @@
|
|||||||
],
|
],
|
||||||
"postedAt0min0sec": [
|
"postedAt0min0sec": [
|
||||||
"报时",
|
"报时",
|
||||||
"在0点发布一篇帖子",
|
"在 0 点发布一篇帖子",
|
||||||
"嘣 嘣 嘣 Biu——!"
|
"嘣 嘣 嘣 Biu——!"
|
||||||
],
|
],
|
||||||
"selfQuote": [
|
"selfQuote": [
|
||||||
@ -291,7 +291,7 @@
|
|||||||
],
|
],
|
||||||
"htl20npm": [
|
"htl20npm": [
|
||||||
"流动的时间线",
|
"流动的时间线",
|
||||||
"在首页时间线的流速超过20npm",
|
"在首页时间线的流速超过 20npm",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"viewInstanceChart": [
|
"viewInstanceChart": [
|
||||||
@ -301,7 +301,7 @@
|
|||||||
],
|
],
|
||||||
"outputHelloWorldOnScratchpad": [
|
"outputHelloWorldOnScratchpad": [
|
||||||
"Hello, world!",
|
"Hello, world!",
|
||||||
"在AiScript控制台中输出 hello world",
|
"在 AiScript 控制台中输出 hello world",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"open3windows": [
|
"open3windows": [
|
||||||
@ -316,7 +316,7 @@
|
|||||||
],
|
],
|
||||||
"reactWithoutRead": [
|
"reactWithoutRead": [
|
||||||
"有好好读过吗?",
|
"有好好读过吗?",
|
||||||
"在含有100字以上的帖子被发出三秒内做出回应",
|
"在含有 100 字以上的帖子被发出三秒内做出回应",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"clickedClickHere": [
|
"clickedClickHere": [
|
||||||
@ -326,27 +326,27 @@
|
|||||||
],
|
],
|
||||||
"justPlainLucky": [
|
"justPlainLucky": [
|
||||||
"超高校级的幸运",
|
"超高校级的幸运",
|
||||||
"每10秒有0.01的概率自动获得",
|
"每 10 秒有 0.01 的概率自动获得",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"setNameToSyuilo": [
|
"setNameToSyuilo": [
|
||||||
"像神一样呐",
|
"像神一样呐",
|
||||||
"将名称设定为syuilo",
|
"将名称设定为 syuilo",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"passedSinceAccountCreated1": [
|
"passedSinceAccountCreated1": [
|
||||||
"一周年",
|
"一周年",
|
||||||
"账户创建时间超过1年",
|
"账户创建时间超过 1 年",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"passedSinceAccountCreated2": [
|
"passedSinceAccountCreated2": [
|
||||||
"二周年",
|
"二周年",
|
||||||
"账户创建时间超过2年",
|
"账户创建时间超过 2 年",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"passedSinceAccountCreated3": [
|
"passedSinceAccountCreated3": [
|
||||||
"三周年",
|
"三周年",
|
||||||
"账户创建时间超过3年",
|
"账户创建时间超过 3 年",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"loggedInOnBirthday": [
|
"loggedInOnBirthday": [
|
||||||
@ -366,7 +366,7 @@
|
|||||||
],
|
],
|
||||||
"brainDiver": [
|
"brainDiver": [
|
||||||
"Brain Diver",
|
"Brain Diver",
|
||||||
"发布了包含Brain Diver链接的帖子",
|
"发布了包含 Brain Diver 链接的帖子",
|
||||||
"Misskey-Misskey La-Tu-Ma"
|
"Misskey-Misskey La-Tu-Ma"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import yaml
|
|
||||||
from pathlib import Path
|
|
||||||
from httpx import get
|
|
||||||
from json import dump
|
from json import dump
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from httpx import get
|
||||||
|
|
||||||
json_path = Path(__file__).parent / "achievement.json"
|
json_path = Path(__file__).parent / "achievement.json"
|
||||||
|
|
||||||
|
17
glover.py
17
glover.py
@ -1,4 +1,3 @@
|
|||||||
import re
|
|
||||||
from configparser import RawConfigParser
|
from configparser import RawConfigParser
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
@ -9,9 +8,6 @@ api_hash: str = ""
|
|||||||
# [Basic]
|
# [Basic]
|
||||||
ipv6: Union[bool, str] = "False"
|
ipv6: Union[bool, str] = "False"
|
||||||
# [misskey]
|
# [misskey]
|
||||||
misskey_url: str = ""
|
|
||||||
misskey_host: str = ""
|
|
||||||
misskey_domain: str = ""
|
|
||||||
web_domain: str = ""
|
web_domain: str = ""
|
||||||
admin: int = 0
|
admin: int = 0
|
||||||
|
|
||||||
@ -20,20 +16,9 @@ config.read("config.ini")
|
|||||||
api_id = config.getint("pyrogram", "api_id", fallback=api_id)
|
api_id = config.getint("pyrogram", "api_id", fallback=api_id)
|
||||||
api_hash = config.get("pyrogram", "api_hash", fallback=api_hash)
|
api_hash = config.get("pyrogram", "api_hash", fallback=api_hash)
|
||||||
ipv6 = config.get("basic", "ipv6", fallback=ipv6)
|
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)
|
web_domain = config.get("misskey", "web_domain", fallback=web_domain)
|
||||||
admin = config.getint("misskey", "admin", fallback=admin)
|
admin = config.getint("misskey", "admin", fallback=admin)
|
||||||
try:
|
try:
|
||||||
ipv6 = strtobool(ipv6)
|
ipv6 = bool(strtobool(ipv6))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
ipv6 = False
|
ipv6 = False
|
||||||
|
@ -23,7 +23,6 @@ from defs.notice import (
|
|||||||
send_follow_request_accept,
|
send_follow_request_accept,
|
||||||
send_achievement_earned,
|
send_achievement_earned,
|
||||||
)
|
)
|
||||||
from glover import misskey_url, misskey_host
|
|
||||||
|
|
||||||
from models.models.user import User, TokenStatusEnum
|
from models.models.user import User, TokenStatusEnum
|
||||||
from models.services.user import UserAction
|
from models.services.user import UserAction
|
||||||
@ -45,41 +44,37 @@ class MisskeyBot(commands.Bot):
|
|||||||
await Router(ws).connect_channel(["main", "home"])
|
await Router(ws).connect_channel(["main", "home"])
|
||||||
|
|
||||||
async def on_note(self, note: Note):
|
async def on_note(self, note: Note):
|
||||||
if self.tg_user:
|
await send_update(
|
||||||
await send_update(self.tg_user.chat_id, note, self.tg_user.timeline_topic)
|
self.tg_user.host, self.tg_user.chat_id, note, self.tg_user.timeline_topic
|
||||||
|
)
|
||||||
|
|
||||||
async def on_user_followed(self, notice: NotificationFollow):
|
async def on_user_followed(self, notice: NotificationFollow):
|
||||||
if self.tg_user:
|
await send_user_followed(
|
||||||
await send_user_followed(
|
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
||||||
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
)
|
||||||
)
|
|
||||||
|
|
||||||
async def on_follow_request(self, notice: NotificationFollowRequest):
|
async def on_follow_request(self, notice: NotificationFollowRequest):
|
||||||
if self.tg_user:
|
await send_follow_request(
|
||||||
await send_follow_request(
|
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
||||||
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
)
|
||||||
)
|
|
||||||
|
|
||||||
async def on_follow_request_accept(self, notice: NotificationFollowRequest):
|
async def on_follow_request_accept(self, notice: NotificationFollowRequest):
|
||||||
if self.tg_user:
|
await send_follow_request_accept(
|
||||||
await send_follow_request_accept(
|
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
||||||
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
)
|
||||||
)
|
|
||||||
|
|
||||||
async def on_chat(self, message: ChatMessage):
|
async def on_chat(self, message: ChatMessage):
|
||||||
if self.tg_user:
|
await send_chat_message(
|
||||||
await send_chat_message(
|
self.tg_user.host, self.tg_user.chat_id, message, self.tg_user.notice_topic
|
||||||
self.tg_user.chat_id, message, self.tg_user.notice_topic
|
)
|
||||||
)
|
|
||||||
|
|
||||||
async def on_chat_unread_message(self, message: ChatMessage):
|
async def on_chat_unread_message(self, message: ChatMessage):
|
||||||
await message.api.read()
|
await message.api.read()
|
||||||
|
|
||||||
async def on_achievement_earned(self, notice: NotificationAchievement):
|
async def on_achievement_earned(self, notice: NotificationAchievement):
|
||||||
if self.tg_user:
|
await send_achievement_earned(
|
||||||
await send_achievement_earned(
|
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
||||||
self.tg_user.chat_id, notice, self.tg_user.notice_topic
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
misskey_bot_map: dict[int, MisskeyBot] = {}
|
misskey_bot_map: dict[int, MisskeyBot] = {}
|
||||||
@ -99,16 +94,16 @@ async def run(user: User):
|
|||||||
misskey = await create_or_get_misskey_bot(user)
|
misskey = await create_or_get_misskey_bot(user)
|
||||||
try:
|
try:
|
||||||
logs.info(f"尝试启动 Misskey Bot WS 任务 {user.user_id}")
|
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:
|
except ClientConnectorError:
|
||||||
await sleep(3)
|
await sleep(3)
|
||||||
await run(user)
|
await run(user)
|
||||||
|
|
||||||
|
|
||||||
async def test_token(token: str) -> bool:
|
async def test_token(host: str, token: str) -> bool:
|
||||||
try:
|
try:
|
||||||
logs.info(f"验证 Token {token}")
|
logs.info(f"验证 Token {host} {token}")
|
||||||
client = MisskeyClient(misskey_host, token)
|
client = MisskeyClient(f"https://{host}", token)
|
||||||
await client.http.login()
|
await client.http.login()
|
||||||
await client.http.close_session()
|
await client.http.close_session()
|
||||||
return True
|
return True
|
||||||
@ -124,7 +119,7 @@ async def rerun_misskey_bot(user_id: int) -> bool:
|
|||||||
user = await UserAction.get_user_if_ok(user_id)
|
user = await UserAction.get_user_if_ok(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
return False
|
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)
|
await UserAction.set_user_status(user_id, TokenStatusEnum.INVALID_TOKEN)
|
||||||
return False
|
return False
|
||||||
bot.loop.create_task(run(user))
|
bot.loop.create_task(run(user))
|
||||||
@ -135,7 +130,7 @@ async def init_misskey_bot():
|
|||||||
await sqlite.create_db_and_tables()
|
await sqlite.create_db_and_tables()
|
||||||
count = 0
|
count = 0
|
||||||
for user in await UserAction.get_all_token_ok_users():
|
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
|
user.status = TokenStatusEnum.INVALID_TOKEN
|
||||||
await UserAction.update_user(user)
|
await UserAction.update_user(user)
|
||||||
continue
|
continue
|
||||||
|
@ -12,8 +12,9 @@ class User(SQLModel, table=True):
|
|||||||
__table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci")
|
__table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci")
|
||||||
|
|
||||||
user_id: int = Field(primary_key=True)
|
user_id: int = Field(primary_key=True)
|
||||||
|
host: str = Field(default="")
|
||||||
token: str = Field(default="")
|
token: str = Field(default="")
|
||||||
status: TokenStatusEnum = Field(sa_column=Column(Enum(TokenStatusEnum)))
|
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)
|
timeline_topic: int = Field(default=0)
|
||||||
notice_topic: int = Field(default=0)
|
notice_topic: int = Field(default=0)
|
||||||
|
@ -35,6 +35,7 @@ class UserAction:
|
|||||||
.where(User.timeline_topic != 0)
|
.where(User.timeline_topic != 0)
|
||||||
.where(User.notice_topic != 0)
|
.where(User.notice_topic != 0)
|
||||||
.where(User.token != "")
|
.where(User.token != "")
|
||||||
|
.where(User.host != "")
|
||||||
)
|
)
|
||||||
results = await session.exec(statement)
|
results = await session.exec(statement)
|
||||||
return user[0] if (user := results.first()) else None
|
return user[0] if (user := results.first()) else None
|
||||||
@ -80,12 +81,22 @@ class UserAction:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@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)
|
user = await UserAction.get_user_by_id(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
user = 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.token = token
|
||||||
user.status = TokenStatusEnum.STATUS_SUCCESS
|
user.status = TokenStatusEnum.STATUS_SUCCESS
|
||||||
await UserAction.update_user(user)
|
await UserAction.update_user(user)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
from models.models.user import User
|
from models.models.user import User
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
__all__ = ["User", "Sqlite"]
|
__all__ = ["User", "Sqlite"]
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from models.services.scheduler import scheduler
|
|
||||||
from defs.announcement import get_unread_announcements
|
from defs.announcement import get_unread_announcements
|
||||||
|
|
||||||
from misskey_init import misskey_bot_map
|
from misskey_init import misskey_bot_map
|
||||||
|
from models.services.scheduler import scheduler
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job("interval", minutes=15, id="check_announcement")
|
@scheduler.scheduled_job("interval", minutes=15, id="check_announcement")
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
from pyrogram import Client, filters
|
from pyrogram import Client, filters
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from models.services.user import UserAction
|
|
||||||
|
|
||||||
from misskey_init import rerun_misskey_bot
|
from misskey_init import rerun_misskey_bot
|
||||||
|
from models.services.user import UserAction
|
||||||
|
|
||||||
|
|
||||||
async def pre_check(message: Message):
|
async def pre_check(message: Message):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
from mipac.errors import (
|
from mipac.errors import (
|
||||||
InternalErrorError,
|
InternalErrorError,
|
||||||
AlreadyFollowingError,
|
AlreadyFollowingError,
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
from pyrogram import Client, filters
|
from pyrogram import Client, filters
|
||||||
from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
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 init import bot
|
||||||
from misskey_init import test_token, rerun_misskey_bot
|
from misskey_init import test_token, rerun_misskey_bot
|
||||||
from models.services.user import UserAction
|
from models.services.user import UserAction
|
||||||
|
|
||||||
des = f"""欢迎使用 {bot.me.first_name},这是一个用于在 Telegram 上使用 Misskey 的机器人。按下方教程开始使用:
|
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 运行状态
|
至此,你便可以在 Telegram 接收 Misskey 消息,同时你可以私聊我使用 /status 查看 Bot 运行状态
|
||||||
|
|
||||||
此 Bot 仅支持绑定 {misskey_host} 的 Misskey 账号!"""
|
此 Bot 仅支持 Misskey V13 实例的账号!"""
|
||||||
|
|
||||||
|
|
||||||
async def finish_check(message: Message):
|
async def finish_check(message: Message):
|
||||||
@ -26,8 +29,40 @@ async def finish_check(message: Message):
|
|||||||
await message.reply("Token 设置完成,请绑定群组。", quote=True)
|
await message.reply("Token 设置完成,请绑定群组。", quote=True)
|
||||||
|
|
||||||
|
|
||||||
def gen_url():
|
def gen_url(domain: str):
|
||||||
return f"https://{web_domain}/gen?host={misskey_domain}&back_host={web_domain}&username={bot.me.username}"
|
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"]))
|
@Client.on_message(filters.incoming & filters.private & filters.command(["start"]))
|
||||||
@ -36,27 +71,13 @@ async def start_command(_: Client, message: Message):
|
|||||||
回应 start
|
回应 start
|
||||||
"""
|
"""
|
||||||
if len(message.command) == 2:
|
if len(message.command) == 2:
|
||||||
token = message.command[1]
|
token_or_host = message.command[1]
|
||||||
if not token:
|
if not token_or_host:
|
||||||
await message.reply(des, quote=True)
|
await message.reply(des, quote=True)
|
||||||
return
|
return
|
||||||
if await test_token(token):
|
if token_or_host.startswith("https://"):
|
||||||
await UserAction.change_user_token(message.from_user.id, token)
|
await change_host(message, token_or_host)
|
||||||
await message.reply(
|
return
|
||||||
"Token 验证成功,绑定账号完成。\n" "当你撤销此登录时,你可以重新点击按钮授权。", quote=True
|
await change_token(message, token_or_host)
|
||||||
)
|
|
||||||
await finish_check(message)
|
|
||||||
else:
|
|
||||||
await message.reply("Token 验证失败,请检查 Token 是否正确", quote=True)
|
|
||||||
return
|
return
|
||||||
await message.reply(
|
await message.reply(des, quote=True)
|
||||||
des,
|
|
||||||
quote=True,
|
|
||||||
reply_markup=InlineKeyboardMarkup(
|
|
||||||
[
|
|
||||||
[
|
|
||||||
InlineKeyboardButton(text="绑定 Misskey 账号", url=gen_url()),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
@ -2,7 +2,6 @@ from pyrogram import filters, Client
|
|||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from glover import admin
|
from glover import admin
|
||||||
|
|
||||||
from misskey_init import get_misskey_bot, rerun_misskey_bot, misskey_bot_map
|
from misskey_init import get_misskey_bot, rerun_misskey_bot, misskey_bot_map
|
||||||
from models.models.user import TokenStatusEnum
|
from models.models.user import TokenStatusEnum
|
||||||
from models.services.user import UserAction
|
from models.services.user import UserAction
|
||||||
|
@ -5,4 +5,4 @@ apscheduler==3.10.1
|
|||||||
sqlalchemy==1.4.41
|
sqlalchemy==1.4.41
|
||||||
sqlmodel==0.0.8
|
sqlmodel==0.0.8
|
||||||
aiosqlite==0.19.0
|
aiosqlite==0.19.0
|
||||||
PyYAML==6.0
|
PyYAML==6.0.1
|
||||||
|
Loading…
Reference in New Issue
Block a user