Merge branch 'Teahouse-Studios:master' into master
This commit is contained in:
commit
f315a97c9a
45 changed files with 595 additions and 213 deletions
194
LOCALES.md
Normal file
194
LOCALES.md
Normal file
|
@ -0,0 +1,194 @@
|
|||
# 目录
|
||||
- [简介](#简介)
|
||||
- [文件位置](#文件位置)
|
||||
- [语言文件格式](#语言文件格式)
|
||||
- [排版](#排版)
|
||||
- [空格](#空格)
|
||||
- [中英文之间需要增加空格](#中英文之间需要增加空格)
|
||||
- [中文与数字之间需要增加空格](#中文与数字之间需要增加空格)
|
||||
- [数字与单位之间不加空格](#数字与单位之间不加空格)
|
||||
- [全角标点与其他字符之间不加空格](#全角标点与其他字符之间不加空格)
|
||||
- [变量与中文之间需要增加空格](#变量与中文之间需要增加空格)
|
||||
- [标点符号](#标点符号)
|
||||
- [不要重复使用标点符号](#不要重复使用标点符号)
|
||||
- [简体中文不要使用直角引号](#简体中文不要使用直角引号)
|
||||
- [英文不要使用弯引号](#英文不要使用弯引号)
|
||||
- [英文省略号使用三个点](#英文省略号使用三个点)
|
||||
- [全角和半角](#全角和半角)
|
||||
- [使用全角中文标点](#使用全角中文标点)
|
||||
- [数字和英文使用半角字符](#数字和英文使用半角字符)
|
||||
- [遇到完整的英文成句内容中使用半角标点](#遇到完整的英文成句内容中使用半角标点)
|
||||
- [名词](#名词)
|
||||
- [专有名词使用正确的大小写](#专有名词使用正确的大小写)
|
||||
- [不要使用非正式的缩写](#不要使用非正式的缩写)
|
||||
- [不同地区的中文使用对应的地区词](#不同地区的中文使用对应的地区词)
|
||||
- [报告问题](#报告问题)
|
||||
|
||||
# 简介
|
||||
本规范文件旨在确保项目中的多语言文本一致性和格式规范。请遵循以下内容以保持代码库中的多语言资源的统一性。
|
||||
|
||||
# 文件位置
|
||||
请将语言文件放置在 `/locales` 目录中,并以语言代码命名为 JSON 文件。
|
||||
|
||||
根路径下的目录用于放置全局字符串,模块下的目录用于放置对应专用的模块字符串。
|
||||
|
||||
原则上,模块字符串和对应模块须对应,若模块之间存在关联则可以例外,否则请考虑转为全局字符串。
|
||||
|
||||
# 语言文件格式
|
||||
请确保键名与字符串一一对应,不得嵌套。
|
||||
|
||||
**全局字符串的命名方式**:`字符串类别.用途`
|
||||
|
||||
**模块字符串的命名方式**:`模块名称.字符串类别.命令名称.用途`
|
||||
|
||||
使用 `${变量名}` 表示变量,变量名必须使用英文,不能使用空格和特殊符号,如需分隔则使用下划线代替。
|
||||
|
||||
# 排版
|
||||
> 本文部分参照[中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines),内容可能有出入。
|
||||
|
||||
## 空格
|
||||
### 中英文之间需要增加空格
|
||||
正确:
|
||||
> 在 IBM 的研究中,他们利用 AI 技术开发了一种先进的语音识别系统。
|
||||
|
||||
错误:
|
||||
> 在IBM的研究中,他们利用AI技术开发了一种先进的语音识别系统。
|
||||
|
||||
例外:专有名词、商品名等词语,按照约定俗成的格式书写。
|
||||
|
||||
### 中文与数字之间需要增加空格
|
||||
正确:
|
||||
> 今年的全球汽车销售量达到了 8000 万辆。
|
||||
|
||||
错误:
|
||||
> 今年的全球汽车销售量达到了8000万辆。
|
||||
|
||||
### 数字与单位之间不加空格
|
||||
正确:
|
||||
> 我家的光纤入户宽带有 10Gbps,SSD 一共有 10TB。
|
||||
|
||||
> 这个城市每年平均降雨量为 1200mm。
|
||||
>
|
||||
> 角度为 90° 的角,就是直角。
|
||||
|
||||
错误:
|
||||
>我家的光纤入户宽带有 10 Gbps,SSD 一共有 20 TB。
|
||||
|
||||
> 这个城市每年平均降雨量为 1200 mm。
|
||||
>
|
||||
> 角度为 90 ° 的角,就是直角。
|
||||
|
||||
### 全角标点与其他字符之间不加空格
|
||||
正确:
|
||||
> 刚刚买了一部 iPhone,好开心!
|
||||
|
||||
错误:
|
||||
> 刚刚买了一部 iPhone ,好开心!
|
||||
|
||||
### 变量与中文之间需要增加空格
|
||||
一般情况下,变量的输入为英文或数字,故变量与中文之间加入空格。
|
||||
|
||||
正确:
|
||||
> 你扔了一块石头,漂了 ${count} 下。
|
||||
|
||||
错误:
|
||||
> 你扔了一块石头,漂了${count}下。
|
||||
|
||||
例外:如果变量的输入一定为中文,则不加空格。
|
||||
|
||||
## 标点符号
|
||||
### 不要重复使用标点符号
|
||||
虽然重复使用标点符号是被允许的行为,但是请别这样做。
|
||||
|
||||
正确:
|
||||
> 德国队竟然战胜了巴西队!
|
||||
|
||||
错误:
|
||||
> 德国队竟然战胜了巴西队!!!
|
||||
|
||||
### 简体中文不要使用直角引号
|
||||
直角引号并不符合简体中文使用者的使用习惯。
|
||||
|
||||
正确:
|
||||
> “老师,‘有条不紊’的‘紊’是什么意思?”
|
||||
|
||||
错误:
|
||||
> 「老师,『有条不紊』的『紊』是什么意思?」
|
||||
|
||||
### 英文不要使用弯引号
|
||||
中文弯引号和英文弯引号属于同一个字符,如果使用弯引号反而会造成阅读问题,请使用直引号 `"`。
|
||||
|
||||
正确:
|
||||
> "Success is not final, failure is not fatal: It is the courage to continue that counts."
|
||||
|
||||
错误:
|
||||
> “Success is not final, failure is not fatal: It is the courage to continue that counts.”
|
||||
|
||||
### 英文省略号使用三个点
|
||||
同上,请使用三个点 `...`。
|
||||
|
||||
正确:
|
||||
> In the serene moonlit night, whispers of ancient tales lingered, echoing through the stillness of time...
|
||||
|
||||
错误:
|
||||
> In the serene moonlit night, whispers of ancient tales lingered, echoing through the stillness of time…
|
||||
|
||||
## 全角和半角
|
||||
### 使用全角中文标点
|
||||
正确:
|
||||
> 嗨!你知道嘛?今天前台的小妹跟我说“喵”了哎!
|
||||
|
||||
错误:
|
||||
> 嗨! 你知道嘛? 今天前台的小妹跟我说 "喵" 了哎!
|
||||
>
|
||||
> 嗨!你知道嘛?今天前台的小妹跟我说"喵"了哎!
|
||||
|
||||
### 数字和英文使用半角字符
|
||||
正确:
|
||||
> 这件蛋糕只卖 200 元。
|
||||
|
||||
错误:
|
||||
> 这件蛋糕只卖 200 元。
|
||||
|
||||
### 遇到完整的英文成句内容中使用半角标点
|
||||
正确:
|
||||
> 乔布斯那句话是怎么说的?“Stay hungry, stay foolish.”
|
||||
|
||||
错误:
|
||||
> 乔布斯那句话是怎么说的?“Stay hungry, stay foolish。”
|
||||
|
||||
## 名词
|
||||
### 专有名词使用正确的大小写
|
||||
正确:
|
||||
> 使用 GitHub 登录
|
||||
|
||||
错误:
|
||||
> 使用 Github 登录
|
||||
>
|
||||
> 使用 gitHub 登录
|
||||
>
|
||||
> 使用 github 登录
|
||||
>
|
||||
> 使用 GITHUB 登录
|
||||
|
||||
### 不要使用非正式的缩写
|
||||
正确:
|
||||
> 我们需要一位熟悉 JavaScript、HTML5,至少理解一种框架(如 Backbone.js、AngularJS、React 等)的前端开发者。
|
||||
|
||||
错误:
|
||||
> 我们需要一位熟悉 Js、h5,至少理解一种框架(如 backbone、angular、RJS 等)的 FED。
|
||||
|
||||
|
||||
### 不同地区的中文使用对应的地区词
|
||||
不要在繁体中文中使用“视频”等错误的地区词,认真的。
|
||||
|
||||
正确:
|
||||
> 輸入影片編號取得對應資訊。
|
||||
|
||||
错误:
|
||||
> 輸入視頻編號獲得相應信息。
|
||||
|
||||
# 报告问题
|
||||
与简体中文翻译有关的问题可直接使用 [Issue](https://github.com/Teahouse-Studios/akari-bot/issues/new) 报告。
|
||||
|
||||
简体中文以外的所有语言由机器人 @Hldbot 维护,相关问题请移步 [Crowdin](https://crowdin.com/project/akari-bot) 报告。
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
#### QQ
|
||||
|
||||
~~目前 QQ 上稳定运行的小可实例为小可三号机。由于腾讯风控问题,请[在此提交](https://github.com/Teahouse-Studios/bot/issues/new?assignees=OasisAkari&labels=New&template=add_new_group.yaml&title=%5BNEW%5D%3A+)入群申请。~~
|
||||
~~目前 QQ 上稳定运行的小可实例为小可三号机。由于腾讯风控问题,请[在此提交](https://github.com/Teahouse-Studios/akari-bot/issues/new?assignees=OasisAkari&labels=New&template=add_new_group.yaml&title=%5BNEW%5D%3A+)入群申请。~~
|
||||
|
||||
~~QQ 频道的测试版支持也请在上方链接申请。~~ 我们正在进行 QQ 频道官方机器人入驻,未来可能会成为官方认证的机器人。
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
|||
|
||||
你可以[参考这里](./DEPLOY.md)来进行尝试搭建。
|
||||
|
||||
若遇到问题,可以通过 [Issue](https://github.com/Teahouse-Studios/bot/issues/new) 或其他方式咨询开发者。
|
||||
若遇到问题,可以通过 [Issue](https://github.com/Teahouse-Studios/akari-bot/issues/new) 或其他方式咨询开发者。
|
||||
|
||||
### 多语言
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ from aiocqhttp import MessageSegment
|
|||
from bots.aiocqhttp.client import bot
|
||||
from bots.aiocqhttp.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, base_superuser_list, command_prefix, ErrorMessage, Image, Plain, Temp, Voice
|
||||
from core.builtins import Bot, base_superuser_list, command_prefix, ErrorMessage, Image, Plain, Temp, Voice, MessageTaskManager
|
||||
from core.builtins.message import MessageSession as MessageSessionT
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.exceptions import SendMessageFailed
|
||||
|
@ -88,26 +88,30 @@ class MessageSession(MessageSessionT):
|
|||
quote = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True,
|
||||
callback=None) -> FinishedSession:
|
||||
|
||||
message_chain = MessageChain(message_chain)
|
||||
message_chain_assendable = message_chain.as_sendable(self, embed=False)
|
||||
|
||||
if (self.target.target_from == 'QQ|Group' and Temp.data.get('lagrange_status', False) and not
|
||||
self.tmp.get('enforce_send_by_master_client', False)):
|
||||
self.tmp.get('enforce_send_by_master_client', False) and not callback):
|
||||
lagrange_available_groups = Temp.data.get('lagrange_available_groups', [])
|
||||
if int(self.session.target) in lagrange_available_groups:
|
||||
choose = random.randint(0, 1)
|
||||
Logger.debug(f'choose: {choose}')
|
||||
if choose:
|
||||
can_sends = []
|
||||
cant_sends = []
|
||||
for x in message_chain_assendable:
|
||||
if isinstance(x, (Plain, Image)):
|
||||
can_sends.append(x)
|
||||
if isinstance(x, Voice):
|
||||
cant_sends.append(x)
|
||||
message_chain_assendable.remove(x)
|
||||
if can_sends:
|
||||
if message_chain_assendable:
|
||||
await JobQueue.send_message('Lagrange', self.target.target_id,
|
||||
MessageChain(can_sends).to_list())
|
||||
if not message_chain_assendable:
|
||||
MessageChain(message_chain_assendable).to_list())
|
||||
if cant_sends:
|
||||
self.tmp['enforce_send_by_master_client'] = True
|
||||
await self.send_message(MessageChain(cant_sends))
|
||||
return FinishedSession(self, 0, [{}])
|
||||
else:
|
||||
Logger.debug(f'Do not use lagrange since some conditions are not met.\n{self.target.target_from} '
|
||||
|
@ -170,6 +174,8 @@ class MessageSession(MessageSessionT):
|
|||
return FinishedSession(self, 0, [{}])
|
||||
else:
|
||||
raise e
|
||||
if callback:
|
||||
MessageTaskManager.add_callback(send['message_id'], callback)
|
||||
return FinishedSession(self, send['message_id'], [send])
|
||||
|
||||
async def check_native_permission(self):
|
||||
|
|
|
@ -5,7 +5,7 @@ from typing import List, Union
|
|||
from bots.aiogram.client import dp, bot, token
|
||||
from bots.aiogram.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage, MessageTaskManager
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.logger import Logger
|
||||
from core.types import FetchTarget as FetchTargetT, \
|
||||
|
@ -39,7 +39,7 @@ class MessageSession(MessageSessionT):
|
|||
wait = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True, callback=None) -> FinishedSession:
|
||||
message_chain = MessageChain(message_chain)
|
||||
if not message_chain.is_safe and not disable_secret_check:
|
||||
return await self.send_message(Plain(ErrorMessage(self.locale.t("error.message.chain.unsafe"))))
|
||||
|
@ -89,6 +89,8 @@ class MessageSession(MessageSessionT):
|
|||
msg_ids = []
|
||||
for x in send:
|
||||
msg_ids.append(x.message_id)
|
||||
if callback:
|
||||
MessageTaskManager.add_callback(x.message_id, callback)
|
||||
return FinishedSession(self, msg_ids, send)
|
||||
|
||||
async def check_native_permission(self):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import discord
|
||||
|
||||
from config import Config
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
client = discord.Bot(intents=intents)
|
||||
client = discord.Bot(intents=intents, proxy=Config('proxy'))
|
||||
|
|
|
@ -9,7 +9,7 @@ import filetype
|
|||
from bots.discord.client import client
|
||||
from bots.discord.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, Plain, Image, MessageSession as MessageSessionT
|
||||
from core.builtins import Bot, Plain, Image, MessageSession as MessageSessionT, MessageTaskManager
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.builtins.message.internal import Embed, ErrorMessage, Voice
|
||||
from core.logger import Logger
|
||||
|
@ -69,7 +69,8 @@ class MessageSession(MessageSessionT):
|
|||
quote = True
|
||||
wait = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False, allow_split_image=True
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False, allow_split_image=True,
|
||||
callback=None
|
||||
) -> FinishedSession:
|
||||
message_chain = MessageChain(message_chain)
|
||||
if not message_chain.is_safe and not disable_secret_check:
|
||||
|
@ -102,13 +103,15 @@ class MessageSession(MessageSessionT):
|
|||
files=files)
|
||||
Logger.info(f'[Bot] -> [{self.target.target_id}]: Embed: {str(x.__dict__)}')
|
||||
else:
|
||||
send_ = False
|
||||
send_ = None
|
||||
if send_:
|
||||
send.append(send_)
|
||||
count += 1
|
||||
msg_ids = []
|
||||
for x in send:
|
||||
msg_ids.append(x.id)
|
||||
if callback:
|
||||
MessageTaskManager.add_callback(x.id, callback)
|
||||
|
||||
return FinishedSession(self, msg_ids, send)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from khl import MessageTypes, Message
|
|||
from bots.kook.client import bot
|
||||
from bots.kook.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage, MessageTaskManager
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.logger import Logger
|
||||
from core.types import FetchTarget as FetchTargetT, \
|
||||
|
@ -68,7 +68,7 @@ class MessageSession(MessageSessionT):
|
|||
wait = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True, callback=None) -> FinishedSession:
|
||||
self.session.message: Message
|
||||
message_chain = MessageChain(message_chain)
|
||||
if not message_chain.is_safe and not disable_secret_check:
|
||||
|
@ -102,6 +102,8 @@ class MessageSession(MessageSessionT):
|
|||
msg_ids = []
|
||||
for x in send:
|
||||
msg_ids.append(x['msg_id'])
|
||||
if callback:
|
||||
MessageTaskManager.add_callback(x['msg_id'], callback)
|
||||
return FinishedSession(self, msg_ids, {self.session.message.channel_type.name: send})
|
||||
|
||||
async def check_native_permission(self):
|
||||
|
|
|
@ -14,7 +14,8 @@ from aiocqhttp import MessageSegment
|
|||
from bots.lagrange.client import bot
|
||||
from bots.lagrange.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, base_superuser_list, command_prefix, ErrorMessage, Image, Plain, Temp, Voice
|
||||
from core.builtins import Bot, base_superuser_list, command_prefix, ErrorMessage, Image, Plain, Temp, Voice, \
|
||||
MessageTaskManager
|
||||
from core.builtins.message import MessageSession as MessageSessionT
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.exceptions import SendMessageFailed
|
||||
|
@ -86,7 +87,8 @@ class MessageSession(MessageSessionT):
|
|||
quote = False
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True,
|
||||
callback=None) -> FinishedSession:
|
||||
msg = []
|
||||
"""
|
||||
if quote and self.target.target_from == 'QQ|Group' and self.session.message:
|
||||
|
@ -102,7 +104,7 @@ class MessageSession(MessageSessionT):
|
|||
msg.append({
|
||||
"type": "text",
|
||||
"data": {
|
||||
"text": x.text
|
||||
"text": ('\n' if count != 0 else '') + x.text
|
||||
}
|
||||
})
|
||||
elif isinstance(x, Image):
|
||||
|
@ -122,7 +124,7 @@ class MessageSession(MessageSessionT):
|
|||
self.locale.t("error.message.timeout")))
|
||||
except aiocqhttp.exceptions.ActionFailed:
|
||||
message_chain.insert(0, Plain(self.locale.t("error.message.limited.msg2img")))
|
||||
msg2img = MessageSegment.image(Path(await msgchain2image(self, message_chain)).as_uri())
|
||||
msg2img = MessageSegment.image(Path(await msgchain2image(message_chain, self)).as_uri())
|
||||
try:
|
||||
send = await bot.send_group_msg(group_id=int(self.session.target), message=msg2img)
|
||||
except aiocqhttp.exceptions.ActionFailed as e:
|
||||
|
@ -143,6 +145,8 @@ class MessageSession(MessageSessionT):
|
|||
return FinishedSession(self, 0, [{}])
|
||||
else:
|
||||
raise e
|
||||
if callback:
|
||||
MessageTaskManager.add_callback(send['message_id'], callback)
|
||||
return FinishedSession(self, send['message_id'], [send])
|
||||
|
||||
async def check_native_permission(self):
|
||||
|
|
|
@ -9,7 +9,7 @@ import nio
|
|||
from bots.matrix.client import bot, homeserver_host
|
||||
from bots.matrix.info import client_name
|
||||
from config import Config
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage
|
||||
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MessageSessionT, ErrorMessage, MessageTaskManager
|
||||
from core.builtins.message.chain import MessageChain
|
||||
from core.logger import Logger
|
||||
from core.types import FetchTarget as FetchedTargetT, \
|
||||
|
@ -43,7 +43,8 @@ class MessageSession(MessageSessionT):
|
|||
wait = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True,
|
||||
callback=None) -> FinishedSession:
|
||||
message_chain = MessageChain(message_chain)
|
||||
if not message_chain.is_safe and not disable_secret_check:
|
||||
return await self.send_message(Plain(ErrorMessage(self.locale.t("error.message.chain.unsafe"))))
|
||||
|
@ -154,7 +155,9 @@ class MessageSession(MessageSessionT):
|
|||
Logger.error(f"Error in sending message: {str(resp)}")
|
||||
else:
|
||||
send.append(resp)
|
||||
|
||||
if callback:
|
||||
for x in send:
|
||||
MessageTaskManager.add_callback(x.event_id, callback)
|
||||
return FinishedSession(self, [resp.event_id for resp in send], self.session.target)
|
||||
|
||||
async def check_native_permission(self):
|
||||
|
|
|
@ -144,10 +144,13 @@ class MessageSession(MessageSessionT):
|
|||
checkPermission = check_permission
|
||||
checkSuperUser = check_super_user
|
||||
|
||||
def ts2strftime(self, timestamp: float, date=True, time=True, seconds=True, timezone=True):
|
||||
def ts2strftime(self, timestamp: float, date=True, iso=False, time=True, seconds=True, timezone=True):
|
||||
ftime_template = []
|
||||
if date:
|
||||
ftime_template.append(self.locale.t("time.date.format"))
|
||||
if iso:
|
||||
ftime_template.append(self.locale.t("time.date.iso.format"))
|
||||
else:
|
||||
ftime_template.append(self.locale.t("time.date.format"))
|
||||
if time:
|
||||
if seconds:
|
||||
ftime_template.append(self.locale.t("time.time.format"))
|
||||
|
@ -155,7 +158,7 @@ class MessageSession(MessageSessionT):
|
|||
ftime_template.append(self.locale.t("time.time.nosec.format"))
|
||||
if timezone:
|
||||
if self._tz_offset == "+0":
|
||||
ftime_template.append(f"(UTC)")
|
||||
ftime_template.append("(UTC)")
|
||||
else:
|
||||
ftime_template.append(f"(UTC{self._tz_offset})")
|
||||
return (datetime.utcfromtimestamp(timestamp) + self.timezone_offset).strftime(' '.join(ftime_template))
|
||||
|
|
|
@ -5,7 +5,7 @@ from urllib.parse import urlparse
|
|||
|
||||
import ujson as json
|
||||
|
||||
from core.builtins.message.internal import Plain, Image, Voice, Embed, Url, ErrorMessage, FormattedTime
|
||||
from core.builtins.message.internal import Plain, Image, Voice, Embed, Url, ErrorMessage, FormattedTime, I18NText
|
||||
from core.builtins.utils import Secret
|
||||
from core.logger import Logger
|
||||
from core.types.message import MessageChain as MessageChainT, MessageSession
|
||||
|
@ -51,6 +51,13 @@ class MessageChain(MessageChainT):
|
|||
e['data']['timestamp'],
|
||||
e['data']['color'], Image(e['data']['image']), Image(e['data']['thumbnail']),
|
||||
e['data']['author'], e['data']['footer'], e['data']['fields']))
|
||||
elif e['type'] == 'url':
|
||||
self.value.append(Url(e['data']['url']))
|
||||
elif e['type'] == 'formatted_time':
|
||||
self.value.append(FormattedTime(e['data']['time'], e['data']['date'], e['data']['seconds'],
|
||||
e['data']['timezone']))
|
||||
elif e['type'] == 'i18n':
|
||||
self.value.append(I18NText(e['data']['key'], **e['data']['kwargs']))
|
||||
elif isinstance(e, str):
|
||||
if e != '':
|
||||
self.value += match_kecode(e)
|
||||
|
@ -124,6 +131,8 @@ class MessageChain(MessageChainT):
|
|||
value.append(Plain(ErrorMessage('{error.message.chain.plain.empty}', locale=locale)))
|
||||
elif isinstance(x, FormattedTime):
|
||||
value.append(Plain(x.to_str(msg=msg)))
|
||||
elif isinstance(x, I18NText):
|
||||
value.append(Plain(msg.locale.t(x.key, **x.kwargs)))
|
||||
else:
|
||||
value.append(x)
|
||||
if not value:
|
||||
|
|
|
@ -93,7 +93,23 @@ class FormattedTime:
|
|||
return f'FormattedTime(time={self.timestamp})'
|
||||
|
||||
def to_dict(self):
|
||||
return {'type': 'time', 'data': {'time': self.timestamp}}
|
||||
return {'type': 'formatted_time', 'data': {'time': self.timestamp, 'date': self.date, 'seconds': self.seconds,
|
||||
'timezone': self.timezone}}
|
||||
|
||||
|
||||
class I18NText:
|
||||
def __init__(self, key, **kwargs):
|
||||
self.key = key
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
return self.key
|
||||
|
||||
def __repr__(self):
|
||||
return f'I18NText(key="{self.key}", kwargs={self.kwargs})'
|
||||
|
||||
def to_dict(self):
|
||||
return {'type': 'i18n', 'data': {'key': self.key, 'kwargs': self.kwargs}}
|
||||
|
||||
|
||||
class ErrorMessage(EMsg):
|
||||
|
@ -269,4 +285,4 @@ class Embed(EmbedT):
|
|||
'fields': self.fields}}
|
||||
|
||||
|
||||
__all__ = ["Plain", "Image", "Voice", "Embed", "EmbedField", "Url", "ErrorMessage", "FormattedTime"]
|
||||
__all__ = ["Plain", "Image", "Voice", "Embed", "EmbedField", "Url", "ErrorMessage", "FormattedTime", "I18NText"]
|
||||
|
|
|
@ -6,6 +6,7 @@ from core.types import MessageSession
|
|||
|
||||
class MessageTaskManager:
|
||||
_list = {}
|
||||
_callback_list = {}
|
||||
|
||||
@classmethod
|
||||
def add_task(cls, session: MessageSession, flag, all_=False, reply=None):
|
||||
|
@ -22,6 +23,10 @@ class MessageTaskManager:
|
|||
'flag': flag, 'active': True, 'type': task_type, 'reply': reply, 'ts': datetime.now().timestamp()}
|
||||
Logger.debug(cls._list)
|
||||
|
||||
@classmethod
|
||||
def add_callback(cls, message_id, callback):
|
||||
cls._callback_list[message_id] = {'callback': callback, 'ts': datetime.now().timestamp()}
|
||||
|
||||
@classmethod
|
||||
def get_result(cls, session: MessageSession):
|
||||
if 'result' in cls._list[session.target.target_id][session.target.sender_id][session]:
|
||||
|
@ -42,6 +47,9 @@ class MessageTaskManager:
|
|||
if datetime.now().timestamp() - cls._list[target][sender][session]['ts'] > 3600:
|
||||
cls._list[target][sender][session]['active'] = False
|
||||
cls._list[target][sender][session]['flag'].set() # no result = cancel
|
||||
for message_id in cls._callback_list:
|
||||
if datetime.now().timestamp() - cls._callback_list[message_id]['ts'] > 3600:
|
||||
del cls._callback_list[message_id]
|
||||
|
||||
@classmethod
|
||||
async def check(cls, session: MessageSession):
|
||||
|
@ -72,6 +80,8 @@ class MessageTaskManager:
|
|||
get_['result'] = session
|
||||
get_['active'] = False
|
||||
get_['flag'].set()
|
||||
if session.target.reply_id in cls._callback_list:
|
||||
await cls._callback_list[session.target.reply_id]['callback'](session)
|
||||
|
||||
|
||||
__all__ = ['MessageTaskManager']
|
||||
|
|
|
@ -30,7 +30,7 @@ class MessageSession(MessageSessionT):
|
|||
wait = True
|
||||
|
||||
async def send_message(self, message_chain, quote=True, disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True, callback=None) -> FinishedSession:
|
||||
message_chain = MessageChain(message_chain)
|
||||
self.sent.append(message_chain)
|
||||
msg_list = []
|
||||
|
@ -44,7 +44,7 @@ class MessageSession(MessageSessionT):
|
|||
img = Image.open(image_path)
|
||||
img.show()
|
||||
Logger.info(f'[Bot] -> [{self.target.target_id}]: Image: {image_path}')
|
||||
return FinishedSession(self, [0], ['There should be a callable here... hmm...'])
|
||||
return FinishedSession(self, [0], ['Should be a callable here... hmm...'])
|
||||
|
||||
async def wait_confirm(self, message_chain=None, quote=True, delete=True):
|
||||
send = None
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import asyncio
|
||||
from typing import List, Union, Dict
|
||||
from typing import List, Union, Dict, Coroutine
|
||||
|
||||
from core.exceptions import FinishedException
|
||||
from .chain import MessageChain
|
||||
|
@ -89,13 +89,15 @@ class MessageSession:
|
|||
message_chain,
|
||||
quote=True,
|
||||
disable_secret_check=False,
|
||||
allow_split_image=True) -> FinishedSession:
|
||||
allow_split_image=True,
|
||||
callback: Coroutine = None) -> FinishedSession:
|
||||
"""
|
||||
用于向消息发送者回复消息。
|
||||
:param message_chain: 消息链,若传入str则自动创建一条带有Plain元素的消息链
|
||||
:param quote: 是否引用传入dict中的消息(默认为True)
|
||||
:param disable_secret_check: 是否禁用消息检查(默认为False)
|
||||
:param allow_split_image: 是否允许拆分图片发送(此参数作接口兼容用,仅telegram平台使用了切割)
|
||||
:param callback: 回调函数,用于在消息发送完成后回复本消息执行的函数
|
||||
:return: 被发送的消息链
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
@ -104,32 +106,36 @@ class MessageSession:
|
|||
message_chain=None,
|
||||
quote=True,
|
||||
disable_secret_check=False,
|
||||
allow_split_image=True):
|
||||
allow_split_image=True,
|
||||
callback: Coroutine = None):
|
||||
"""
|
||||
用于向消息发送者回复消息并终结会话(模块后续代码不再执行)。
|
||||
:param message_chain: 消息链,若传入str则自动创建一条带有Plain元素的消息链
|
||||
:param quote: 是否引用传入dict中的消息(默认为True)
|
||||
:param disable_secret_check: 是否禁用消息检查(默认为False)
|
||||
:param allow_split_image: 是否允许拆分图片发送(此参数作接口兼容用,仅telegram平台使用了切割)
|
||||
:param callback: 回调函数,用于在消息发送完成后回复本消息执行的函数
|
||||
:return: 被发送的消息链
|
||||
"""
|
||||
...
|
||||
f = None
|
||||
if message_chain is not None:
|
||||
f = await self.send_message(message_chain, disable_secret_check=disable_secret_check, quote=quote,
|
||||
allow_split_image=allow_split_image)
|
||||
allow_split_image=allow_split_image, callback=callback)
|
||||
raise FinishedException(f)
|
||||
|
||||
async def send_direct_message(self, message_chain, disable_secret_check=False, allow_split_image=True):
|
||||
async def send_direct_message(self, message_chain, disable_secret_check=False, allow_split_image=True,
|
||||
callback: Coroutine = None):
|
||||
"""
|
||||
用于向消息发送者直接发送消息。
|
||||
:param message_chain: 消息链,若传入str则自动创建一条带有Plain元素的消息链
|
||||
:param disable_secret_check: 是否禁用消息检查(默认为False)
|
||||
:param allow_split_image: 是否允许拆分图片发送(此参数作接口兼容用,仅Telegram平台使用了切割)
|
||||
:param callback: 回调函数,用于在消息发送完成后回复本消息执行的函数
|
||||
:return: 被发送的消息链
|
||||
"""
|
||||
await self.send_message(message_chain, disable_secret_check=disable_secret_check, quote=False,
|
||||
allow_split_image=allow_split_image)
|
||||
allow_split_image=allow_split_image, callback=callback)
|
||||
|
||||
async def wait_confirm(self, message_chain=None, quote=True, delete=True, timeout=120, append_instruction=True):
|
||||
"""
|
||||
|
@ -142,7 +148,8 @@ class MessageSession:
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def wait_next_message(self, message_chain=None, quote=True, delete=False, timeout=120, append_instruction=True):
|
||||
async def wait_next_message(self, message_chain=None, quote=True, delete=False, timeout=120,
|
||||
append_instruction=True):
|
||||
"""
|
||||
一次性模板,用于等待对象的下一条消息。
|
||||
:param message_chain: 需要发送的确认消息,可不填
|
||||
|
@ -153,7 +160,8 @@ class MessageSession:
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def wait_reply(self, message_chain, quote=True, delete=False, timeout=120, all_=False, append_instruction=True):
|
||||
async def wait_reply(self, message_chain, quote=True, delete=False, timeout=120, all_=False,
|
||||
append_instruction=True):
|
||||
"""
|
||||
一次性模板,用于等待触发对象回复消息。
|
||||
:param message_chain: 需要发送的确认消息,可不填
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"game.message.stop.none": "This game is not in progress.",
|
||||
"i18n.prompt.fallback.failed": "(If you're seeing this strange string, it means something we've messed up!\nPlease send the issue back to\n${url}\nso that we can fix it in time.)",
|
||||
"language": "English",
|
||||
"message.brackets": " (${msg})",
|
||||
"message.collapse": "...show only the first ${amount} items.",
|
||||
"message.cooldown": "${time} second(s) have passed since the last execution. The cooldown time of this command are ${cd_time} second(s).",
|
||||
"message.delimiter": ", ",
|
||||
|
@ -60,6 +61,7 @@
|
|||
"petal.message.lost.success": "You lost ${amount} petal(s).",
|
||||
"success": "Success.",
|
||||
"time.date.format": "%B %d, %Y",
|
||||
"time.date.iso.format": "%Y-%m-%d",
|
||||
"time.time.format": "%H:%M:%S",
|
||||
"time.time.nosec.format": "%H:%M",
|
||||
"tos.reason": "Reason: ",
|
||||
|
|
|
@ -35,13 +35,14 @@
|
|||
"game.message.stop.none": "当前此游戏未在进行。",
|
||||
"i18n.prompt.fallback.failed": "(如果你看到了这条奇怪的字符串,说明我们又搞错了什么东西!\n请将问题反馈至\n${url}\n以便我们快速解决此问题。)",
|
||||
"language": "简体中文",
|
||||
"message.brackets": "(${msg})",
|
||||
"message.collapse": "……仅显示前 ${amount} 条内容。",
|
||||
"message.cooldown": "距离上次执行已过去 ${time} 秒,本命令的冷却时间为 ${cd_time} 秒。",
|
||||
"message.delimiter": "、",
|
||||
"message.end": "。",
|
||||
"message.reply.prompt": "(请使用指定的词语回复本条消息)",
|
||||
"message.wait.confirm.prompt.type1": "(发送“是”或符合确认条件的词语来确认)",
|
||||
"message.wait.confirm.prompt.type2": "(发送符合条件的词语来确认)",
|
||||
"message.collapse": "……仅显示前 ${amount} 条内容。",
|
||||
"no": "否",
|
||||
"none": "无",
|
||||
"parser.admin.module.permission.denied": "“${module}”模块下的命令仅能被该群组的管理员所使用,请联系管理员执行此命令。",
|
||||
|
@ -60,6 +61,7 @@
|
|||
"petal.message.lost.success": "失去了 ${amount} 片花瓣。",
|
||||
"success": "成功。",
|
||||
"time.date.format": "%Y 年 %m 月 %d 日",
|
||||
"time.date.iso.format": "%Y-%m-%d",
|
||||
"time.time.format": "%H:%M:%S",
|
||||
"time.time.nosec.format": "%H:%M",
|
||||
"tos.reason": "具体原因:",
|
||||
|
@ -74,4 +76,4 @@
|
|||
"tos.warning.last": "这是最后一次警告。",
|
||||
"unknown": "未知",
|
||||
"yes": "是"
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
"game.message.stop.none": "目前此遊戲沒有在進行。",
|
||||
"i18n.prompt.fallback.failed": "(如果你看到了這條奇怪的字串,說明我們又搞錯了什麼!\n請將問題回報至\n${url}\n以便我們快速解決此問題。)",
|
||||
"language": "繁體中文",
|
||||
"message.brackets": "(${msg})",
|
||||
"message.collapse": "……僅顯示前 ${amount} 條內容。",
|
||||
"message.cooldown": "距上次執行已過去 ${time} 秒,此指令的冷卻時間為 ${cd_time} 秒。",
|
||||
"message.delimiter": "、",
|
||||
|
@ -59,7 +60,8 @@
|
|||
"petal.message.lost.limit": "(本日失去花瓣已達到每日上限。)",
|
||||
"petal.message.lost.success": "失去了 ${amount} 片花瓣。",
|
||||
"success": "成功。",
|
||||
"time.date.format": "%Y年%m月%d日",
|
||||
"time.date.format": "%Y 年 %m 月 %d 日",
|
||||
"time.date.iso.format": "%Y-%m-%d",
|
||||
"time.time.format": "%H:%M:%S",
|
||||
"time.time.nosec.format": "%H:%M",
|
||||
"tos.reason": "原因:",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from datetime import datetime
|
||||
|
||||
from core.builtins import Bot, Image, Plain
|
||||
from core.builtins import Bot, Image, Plain, Url
|
||||
from core.utils.http import get_url
|
||||
|
||||
|
||||
|
@ -16,16 +16,13 @@ async def get_info(msg: Bot.MessageSession, url, get_detail):
|
|||
stat = view['stat']
|
||||
|
||||
pic = view['pic']
|
||||
video_url = f"https://www.bilibili.com/video/{view['bvid']}\n"
|
||||
video_url = f"https://www.bilibili.com/video/{view['bvid']}"
|
||||
title = view['title']
|
||||
tname = view['tname']
|
||||
|
||||
timestamp = view['ctime']
|
||||
timeobject = datetime.fromtimestamp(timestamp)
|
||||
time = timeobject.strftime('%Y-%m-%d %H:%M:%S')
|
||||
time = msg.ts2strftime(view['ctime'], iso=True, timezone=False)
|
||||
|
||||
if len(view['pages']) > 1:
|
||||
pages = f" ({len(view['pages'])}P)"
|
||||
pages = msg.locale.t("message.brackets", msg=f"{len(view['pages'])}P")
|
||||
else:
|
||||
pages = ''
|
||||
|
||||
|
@ -41,15 +38,15 @@ async def get_info(msg: Bot.MessageSession, url, get_detail):
|
|||
fans = format_num(res['data']['Card']['card']['fans'])
|
||||
|
||||
if not get_detail:
|
||||
output = video_url + msg.locale.t("bilibili.message", title=title, tname=tname, owner=owner, time=time)
|
||||
output = msg.locale.t("bilibili.message", title=title, tname=tname, owner=owner, time=time)
|
||||
else:
|
||||
output = video_url + msg.locale.t("bilibili.message.detail", title=title, pages=pages, tname=tname,
|
||||
output = msg.locale.t("bilibili.message.detail", title=title, pages=pages, tname=tname,
|
||||
owner=owner, fans=fans, view=stat_view, danmaku=stat_danmaku,
|
||||
reply=stat_reply,
|
||||
like=stat_like, coin=stat_coin, favorite=stat_favorite, share=stat_share,
|
||||
time=time)
|
||||
|
||||
await msg.finish([Image(pic), Plain(output)])
|
||||
await msg.finish([Image(pic), Url(video_url), Plain(output)])
|
||||
|
||||
|
||||
def format_num(number):
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
"bilibili.help.regex.bv": "发送 BV 号获取视频信息。",
|
||||
"bilibili.help.regex.shorturl": "发送 b23.tv 短连接获取视频信息。",
|
||||
"bilibili.help.regex.url": "发送 URL 获取视频信息。",
|
||||
"bilibili.message": "标题:${title}\n类型:${tname}\nUP 主:${owner}\n日期:${time}",
|
||||
"bilibili.message.detail": "标题:${title}${pages} | 类型:${tname}\nUP 主:${owner} | 粉丝:${fans}\n观看:${view} | 弹幕:${danmaku} | 评论:${reply}\n喜欢:${like} | 投币:${coin} | 收藏:${favorite} | 分享:${share}\n日期:${time}",
|
||||
"bilibili.message": "标题:${title}\n类型:${tname}\nUP主:${owner}\n日期:${time}",
|
||||
"bilibili.message.detail": "标题:${title}${pages} | 类型:${tname}\nUP主:${owner} | 粉丝:${fans}\n观看:${view} | 弹幕:${danmaku} | 评论:${reply}\n喜欢:${like} | 投币:${coin} | 收藏:${favorite} | 分享:${share}\n日期:${time}",
|
||||
"bilibili.message.error.invalid": "发生错误:视频编号无效,请检查输入。",
|
||||
"bilibili.message.not_found": "未找到对应的视频。"
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"bilibili.help.regex.bv": "傳送 BV 號取得影片資訊。",
|
||||
"bilibili.help.regex.shorturl": "傳送 b23.tv 短網址取得影片資訊。",
|
||||
"bilibili.help.regex.url": "傳送 URL 取得影片資訊。",
|
||||
"bilibili.message": "標題:${title}\n類型:${tname}\nUP 主:${owner}\n日期:${time}",
|
||||
"bilibili.message": "標題:${title}\n類型:${tname}\nUP主:${owner}\n日期:${time}",
|
||||
"bilibili.message.detail": "標題:${title}${pages} | 類型:${tname}\nUP 主:${owner} | 追隨者:${fans}\n觀看:${view} | 彈幕:${danmaku} | 留言:${reply}\n喜歡:${like} | 投幣:${coin} | 收藏:${favorite} | 分享:${share}\n日期:${time}",
|
||||
"bilibili.message.error.invalid": "發生錯誤:影片編號無效,請校對輸入。",
|
||||
"bilibili.message.not_found": "未找到對應的影片。"
|
||||
|
|
|
@ -26,6 +26,6 @@
|
|||
"calc.help.option.str": "转换为字符串:str(1) -> \"1\"",
|
||||
"calc.help.option.xor": "Bitwise xor: 1 ^ 1 -> 0",
|
||||
"calc.message.invalid": "Invalid expression: ${expr}",
|
||||
"calc.message.running_time": "Calculating time: ${time} ms / 10000 ms. ",
|
||||
"calc.message.running_time": "Calculating time: ${time}ms / 10000ms. ",
|
||||
"calc.message.time_out": "Calculation timeout."
|
||||
}
|
|
@ -26,6 +26,6 @@
|
|||
"calc.help.option.str": "转换为字符串:str(1) -> \"1\"",
|
||||
"calc.help.option.xor": "按位异或:1 ^ 1 -> 0",
|
||||
"calc.message.invalid": "表达式无效:${expr}",
|
||||
"calc.message.running_time": "计算花费时间:${time} ms / 10000 ms。",
|
||||
"calc.message.running_time": "计算花费时间:${time}ms / 10000ms。",
|
||||
"calc.message.time_out": "计算超时。"
|
||||
}
|
|
@ -26,6 +26,6 @@
|
|||
"calc.help.option.str": "轉換為字串:str(1) -> \"1\"",
|
||||
"calc.help.option.xor": "按位元互斥或:1 ^ 1 -> 0",
|
||||
"calc.message.invalid": "運算式無效:${expr}",
|
||||
"calc.message.running_time": "計算耗費時間:${time} ms / 10000 ms。",
|
||||
"calc.message.running_time": "計算耗費時間:${time}ms / 10000ms。",
|
||||
"calc.message.time_out": "計算逾時。"
|
||||
}
|
|
@ -24,7 +24,7 @@ async def _(msg: Bot.MessageSession):
|
|||
elif 'r30' in msg.parsed_msg:
|
||||
query = 'r30'
|
||||
else:
|
||||
raise
|
||||
return
|
||||
pat = msg.parsed_msg.get('<UserID>', False)
|
||||
if pat:
|
||||
query_id = pat
|
||||
|
@ -39,7 +39,7 @@ async def _(msg: Bot.MessageSession):
|
|||
qc = CoolDown('cytoid_rank', msg)
|
||||
c = qc.check(150)
|
||||
if c == 0:
|
||||
img = await get_rating(query_id, query, msg)
|
||||
img = await get_rating(msg, query_id, query)
|
||||
if 'path' in img:
|
||||
await msg.send_message([Image(path=img['path'])], allow_split_image=False)
|
||||
if 'text' in img:
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"cytoid.help.r30": "查询 Cytoid 用户的 R30 列表。",
|
||||
"cytoid.help.unbind": "解绑用户。",
|
||||
"cytoid.message.b30.cooldown": "(据官方人员所述,此 API 的调用十分昂贵,故手动做出此限制,请谅解。)",
|
||||
"cytoid.message.b30.level": "等级",
|
||||
"cytoid.message.bind.failed": "绑定失败,请检查输入。",
|
||||
"cytoid.message.bind.success": "绑定成功:",
|
||||
"cytoid.message.unbind.success": "解绑成功。",
|
||||
|
|
|
@ -20,7 +20,7 @@ from core.utils.http import get_url
|
|||
from core.utils.html2text import html2text
|
||||
|
||||
|
||||
async def get_rating(uid, query_type, msg: Bot.MessageSession):
|
||||
async def get_rating(msg: Bot.MessageSession, uid, query_type):
|
||||
try:
|
||||
if query_type == 'b30':
|
||||
query_type = 'bestRecords'
|
||||
|
@ -88,7 +88,7 @@ async def get_rating(uid, query_type, msg: Bot.MessageSession):
|
|||
resources = []
|
||||
songcards = []
|
||||
|
||||
async def mkresources(x, rank):
|
||||
async def mkresources(msg: Bot.MessageSession, x, rank):
|
||||
thumbpath = await download_cover_thumb(x['chart']['level']['uid'])
|
||||
chart_type = x['chart']['type']
|
||||
difficulty = x['chart']['difficulty']
|
||||
|
@ -98,7 +98,7 @@ async def get_rating(uid, query_type, msg: Bot.MessageSession):
|
|||
rt = x['rating']
|
||||
details = x['details']
|
||||
_date = datetime.strptime(x['date'], "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
local_time = _date + timedelta(hours=int(Config("timezone_offset", 8)))
|
||||
local_time = _date + timedelta(hours=int(msg.options.get('timezone_offset', Config('timezone_offset', '+8'))))
|
||||
playtime = local_time.timestamp()
|
||||
nowtime = time.time()
|
||||
playtime = playtime - nowtime
|
||||
|
@ -125,7 +125,7 @@ async def get_rating(uid, query_type, msg: Bot.MessageSession):
|
|||
|
||||
for x in best_records:
|
||||
rank += 1
|
||||
resources.append(mkresources(x, rank))
|
||||
resources.append(mkresources(msg, x, rank))
|
||||
|
||||
await asyncio.gather(*resources)
|
||||
cards_ = await asyncio.gather(*songcards)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"langconv.help": "简繁转换。",
|
||||
"langconv.message.unsupported_language": "不支持此语言。支持的语言:zh_cn、zh_tw、zh_hk",
|
||||
"langconv.message.running_time": "花费时间:${time} ms。"
|
||||
"langconv.message.running_time": "花费时间:${time}ms。"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"langconv.help": "简繁转换。",
|
||||
"langconv.message.unsupported_language": "不支持此语言。支持的语言:zh_cn、zh_tw、zh_hk",
|
||||
"langconv.message.running_time": "花费时间:${time} ms。"
|
||||
"langconv.message.running_time": "花费时间:${time}ms。"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"langconv.help": "繁簡轉換。",
|
||||
"langconv.message.unsupported_language": "不支援此語言。已支援的語言:zh_cn、zh_tw、zh_hk",
|
||||
"langconv.message.running_time": "花費時間:${time} ms。"
|
||||
"langconv.message.running_time": "花費時間:${time}ms。"
|
||||
}
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
"mcv.message.mclgv": "The latest version: ${version}\n(The data from Mojira may be earlier than the official release. Information is for reference purposes only).",
|
||||
"mcv.message.mcv": "The latest version in the launcher is: \n${launcher_ver}\nThe latest version recorded on Mojira is: \n${jira_ver}\n(The latest version in the launcher shall prevail, Mojira is only for previewing the version)",
|
||||
"mcv.message.mcv.jira.failed": "Failed to get Mojira information.",
|
||||
"mcv.message.mcv.launcher": "The latest release: ${release}, the latest snapshot: ${snapshot},",
|
||||
"mcv.message.mcv.launcher": "The latest release: ${release}\nRelease time: ${release_time}\nThe latest snapshot: ${snapshot}\nRelease time: ${snapshot_time}",
|
||||
"mcv.message.mcv.launcher.failed": "Failed to get manifest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
"mcv.message.mclgv": "最新版:${version}\n(資訊來源於 Mojira,可能會比官方發布要早一段時間。資訊僅供參考。)",
|
||||
"mcv.message.mcv": "目前啟動器內最新版本為:\n${launcher_ver}\nMojira 上所記錄最新版本為:\n${jira_ver}\n(以啟動器內最新版本為準,Mojira 僅作版本號預覽用)",
|
||||
"mcv.message.mcv.jira.failed": "取得 Mojira 資訊失敗。",
|
||||
"mcv.message.mcv.launcher": "最新版:${release},最新快照:${snapshot},",
|
||||
"mcv.message.mcv.launcher": "最新版:${release}\n發佈於:${release_time}\n最新快照:${snapshot}\n發佈於:${snapshot_time}\n",
|
||||
"mcv.message.mcv.launcher.failed": "取得 manifest.json 失敗。"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@
|
|||
"mcv_rss.message.mcv_jira_rss.future": "Java Edition ${version} is added to Mojira.\n(“Future Version” only indicates that a new version is planned; you will have to wait for Mojang to release it in the Launcher.)",
|
||||
"mcv_rss.message.mcv_rss.release": "${version} is now released in Minecraft Launcher.",
|
||||
"mcv_rss.message.mcv_rss.snapshot": "Snapshot ${version} is now released in Minecraft Launcher."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
"mcv_rss.message.mclgv_jira_rss": "Mojira 已更新 Minecraft Legends ${version}。\n(Mojira 上的信息仅作版本号预览用,不代表启动器/商店已更新此版本)",
|
||||
"mcv_rss.message.mcv_jira_rss": "Mojira 已更新 Java 版 ${version}。\n(Mojira 上的信息仅作版本号预览用,不代表启动器已更新此版本)",
|
||||
"mcv_rss.message.mcv_jira_rss.future": "Mojira 已新增 Java 版 ${version}。\n(Future Version 仅代表与此相关的版本正在规划中,不代表启动器已更新此版本)",
|
||||
"mcv_rss.message.mcv_rss.release": "启动器已更新 ${version} 正式版。",
|
||||
"mcv_rss.message.mcv_rss.snapshot": "启动器已更新 ${version} 快照。"
|
||||
}
|
||||
"mcv_rss.message.mcv_rss.release": "启动器已更新 ${version} 正式版。\n更新时间:",
|
||||
"mcv_rss.message.mcv_rss.snapshot": "启动器已更新 ${version} 快照。\n更新时间:"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"mcv_rss.help.mcbv_jira_rss.desc": "啟用後 Mojira 更新基岩版時會自動推播訊息。",
|
||||
"mcv_rss.help.mcbv_rss.desc": "啟用後商店更新 Minecraft 基岩版時會自動推播訊息。",
|
||||
"mcv_rss.help.mcdv_jira_rss.desc": "啟用後 Mojira 更新 Minecraft Dungeons 時會自動推播訊息。",
|
||||
"mcv_rss.help.mclgv_jira_rss.desc": "开启后 Mojira 更新 Minecraft Legends 时会自动推送消息。",
|
||||
"mcv_rss.help.mclgv_jira_rss.desc": "啟用後 Mojira 更新 Minecraft Legends 時會自動推播訊息。",
|
||||
"mcv_rss.help.mcv_jira_rss.desc": "啟用後 Mojira 更新 Java 版時會自動推播訊息。",
|
||||
"mcv_rss.help.mcv_rss.desc": "啟用後 Minecraft 啟動器更新 Minecraft:Java 版時會自動推播訊息。",
|
||||
"mcv_rss.message.mcbv_jira_rss": "Mojira 已更新基岩版 ${version}。 \n(Mojira 上的資訊僅作版本號預覽用,不代表啟動器已更新此版本)",
|
||||
|
@ -11,6 +11,6 @@
|
|||
"mcv_rss.message.mclgv_jira_rss": "Mojira 已更新 Minecraft Legends ${version}。 \n(Mojira 上的資訊僅作版本號預覽用,不代表啟動器/商店已更新此版本)",
|
||||
"mcv_rss.message.mcv_jira_rss": "Mojira 已更新 Java 版 ${version}。 \n(Mojira 上的資訊僅作版本號預覽用,不代表啟動器已更新此版本)",
|
||||
"mcv_rss.message.mcv_jira_rss.future": "Mojira 已新增 Java 版 ${version}。 \n(Future Version 僅代表與此相關的版本正在規劃中,不代表啟動器已更新此版本)",
|
||||
"mcv_rss.message.mcv_rss.release": "啟動器已更新 ${version} 正式版。",
|
||||
"mcv_rss.message.mcv_rss.snapshot": "啟動器已更新 ${version} 快照。"
|
||||
}
|
||||
"mcv_rss.message.mcv_rss.release": "啟動器已更新 ${version} 正式版。\n更新時間:",
|
||||
"mcv_rss.message.mcv_rss.snapshot": "啟動器已更新 ${version} 快照。\n更新時間:"
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
{
|
||||
"ncmusic.help.desc": "网易云音乐相关工具。",
|
||||
"ncmusic.help.info": "获取音乐详细信息。",
|
||||
"ncmusic.help.search": "搜索网易云音乐。",
|
||||
"ncmusic.help.search.legacy": "搜索网易云音乐。(旧版)",
|
||||
"ncmusic.help.info": "获取音乐详细信息。",
|
||||
"ncmusic.message.info": "歌名:${name}(${id})\n歌手:${artists}\n专辑名:${album}(${album_id})",
|
||||
"ncmusic.message.info": "歌名:${name}(${id})\n专辑名:${album}(${album_id})\n歌手:${artists}",
|
||||
"ncmusic.message.info.not_found": "找不到对应的音乐。",
|
||||
"ncmusic.message.search.confirm": "是否查看音乐详细信息?",
|
||||
"ncmusic.message.search.invalid.non_digital": "无效的编号,必须为数字。",
|
||||
"ncmusic.message.search.invalid.out_of_range": "编号超出范围。",
|
||||
"ncmusic.message.search.not_found": "未找到结果。",
|
||||
"ncmusic.message.search.prompt": "输入对应的序号以查看歌曲信息。",
|
||||
"ncmusic.message.search.result": "搜索结果:",
|
||||
"ncmusic.message.search.table.header.album": "专辑名",
|
||||
"ncmusic.message.search.table.header.artists": "歌手",
|
||||
"ncmusic.message.search.table.header.id": "序号",
|
||||
"ncmusic.message.search.table.header.name": "歌名",
|
||||
"ncmusic.message.search.result": "搜索结果:"
|
||||
}
|
||||
"ncmusic.message.search.table.header.name": "歌名"
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
{
|
||||
"ncmusic.help.desc": "网易云音乐相关工具。",
|
||||
"ncmusic.help.info": "取得音樂詳細資訊。",
|
||||
"ncmusic.help.search": "搜尋網易雲音樂。",
|
||||
"ncmusic.help.search.legacy": "搜尋網易雲音樂。(舊版)",
|
||||
"ncmusic.message.info": "歌名:${name}(${id})\n歌手:${artists}\n專輯名:${album}(${album_id})",
|
||||
"ncmusic.message.info.not_found": "找不到对应的音乐。",
|
||||
"ncmusic.message.search.confirm": "是否檢視音樂詳細資訊?",
|
||||
"ncmusic.message.search.invalid.non_digital": "無效的編號,必須為數字。",
|
||||
"ncmusic.message.search.invalid.out_of_range": "編號超出範圍。",
|
||||
|
|
|
@ -8,109 +8,112 @@ from modules.wiki.utils.dbutils import Audit
|
|||
from modules.wiki.utils.wikilib import WikiLib
|
||||
|
||||
|
||||
if Config('enable_urlmanager'):
|
||||
aud = module('wiki_audit', required_superuser=True,
|
||||
alias='wau')
|
||||
aud = module('wiki_audit', required_superuser=True,
|
||||
alias='wau')
|
||||
|
||||
@aud.command(['trust <apiLink>', 'block <apiLink>'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
date = msg.ts2strftime(datetime.now().timestamp(), timezone=False)
|
||||
api = msg.parsed_msg['<apiLink>']
|
||||
check = await WikiLib(api).check_wiki_available()
|
||||
if check.available:
|
||||
api = check.value.api
|
||||
if msg.parsed_msg.get('trust', False):
|
||||
res = Audit(api).add_to_AllowList(date)
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.allowlist')
|
||||
else:
|
||||
res = Audit(api).add_to_BlockList(date)
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.blocklist')
|
||||
if not res:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.add.failed', list_name=list_name) + api)
|
||||
else:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.add.success', list_name=list_name) + api)
|
||||
else:
|
||||
result = msg.locale.t('wiki.message.error.add') + \
|
||||
('\n' + msg.locale.t('wiki.message.error.info') + check.message if check.message != '' else '')
|
||||
await msg.finish(result)
|
||||
|
||||
@aud.command(['distrust <apiLink>', 'unblock <apiLink>'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
api = msg.parsed_msg['<apiLink>'] # 已关闭的站点无法验证有效性
|
||||
if msg.parsed_msg.get('distrust', False):
|
||||
res = Audit(api).remove_from_AllowList()
|
||||
if res is None:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.failed.other') + api)
|
||||
@aud.command(['trust <apiLink>', 'block <apiLink>'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
date = msg.ts2strftime(datetime.now().timestamp(), timezone=False)
|
||||
api = msg.parsed_msg['<apiLink>']
|
||||
check = await WikiLib(api).check_wiki_available()
|
||||
if check.available:
|
||||
api = check.value.api
|
||||
if msg.parsed_msg.get('trust', False):
|
||||
res = Audit(api).add_to_AllowList(date)
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.allowlist')
|
||||
else:
|
||||
res = Audit(api).remove_from_BlockList()
|
||||
res = Audit(api).add_to_BlockList(date)
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.blocklist')
|
||||
if not res:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.failed', list_name=list_name) + api)
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.add.failed', list_name=list_name) + api)
|
||||
else:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.success', list_name=list_name) + api)
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.add.success', list_name=list_name) + api)
|
||||
else:
|
||||
result = msg.locale.t('wiki.message.error.add') + \
|
||||
('\n' + msg.locale.t('wiki.message.error.info') + check.message if check.message != '' else '')
|
||||
await msg.finish(result)
|
||||
|
||||
@aud.command('query <apiLink>')
|
||||
async def _(msg: Bot.MessageSession):
|
||||
api = msg.parsed_msg['<apiLink>']
|
||||
check = await WikiLib(api).check_wiki_available()
|
||||
if check.available:
|
||||
api = check.value.api
|
||||
audit = Audit(api)
|
||||
allow = audit.inAllowList
|
||||
block = audit.inBlockList
|
||||
msg_list = []
|
||||
if allow:
|
||||
msg_list.append(api + msg.locale.t('wiki.message.wiki_audit.query.allowlist'))
|
||||
if block:
|
||||
msg_list.append(api + msg.locale.t('wiki.message.wiki_audit.query.blocklist'))
|
||||
if msg_list:
|
||||
msg_list.append(msg.locale.t('wiki.message.wiki_audit.query.conflict'))
|
||||
await msg.finish('\n'.join(msg_list))
|
||||
else:
|
||||
await msg.finish(api + msg.locale.t('wiki.message.wiki_audit.query.none'))
|
||||
|
||||
@aud.command(['distrust <apiLink>', 'unblock <apiLink>'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
api = msg.parsed_msg['<apiLink>'] # 已关闭的站点无法验证有效性
|
||||
if msg.parsed_msg.get('distrust', False):
|
||||
res = Audit(api).remove_from_AllowList()
|
||||
if res is None:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.failed.other') + api)
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.allowlist')
|
||||
else:
|
||||
res = Audit(api).remove_from_BlockList()
|
||||
list_name = msg.locale.t('wiki.message.wiki_audit.list_name.blocklist')
|
||||
if not res:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.failed', list_name=list_name) + api)
|
||||
else:
|
||||
await msg.finish(msg.locale.t('wiki.message.wiki_audit.remove.success', list_name=list_name) + api)
|
||||
|
||||
|
||||
@aud.command('query <apiLink>')
|
||||
async def _(msg: Bot.MessageSession):
|
||||
api = msg.parsed_msg['<apiLink>']
|
||||
check = await WikiLib(api).check_wiki_available()
|
||||
if check.available:
|
||||
api = check.value.api
|
||||
audit = Audit(api)
|
||||
allow = audit.inAllowList
|
||||
block = audit.inBlockList
|
||||
msg_list = []
|
||||
if allow:
|
||||
msg_list.append(api + msg.locale.t('wiki.message.wiki_audit.query.allowlist'))
|
||||
if block:
|
||||
msg_list.append(api + msg.locale.t('wiki.message.wiki_audit.query.blocklist'))
|
||||
if msg_list:
|
||||
msg_list.append(msg.locale.t('wiki.message.wiki_audit.query.conflict'))
|
||||
await msg.finish('\n'.join(msg_list))
|
||||
else:
|
||||
result = msg.locale.t('wiki.message.error.query') + \
|
||||
('\n' + msg.locale.t('wiki.message.error.info') + check.message if check.message != '' else '')
|
||||
await msg.finish(result)
|
||||
await msg.finish(api + msg.locale.t('wiki.message.wiki_audit.query.none'))
|
||||
else:
|
||||
result = msg.locale.t('wiki.message.error.query') + \
|
||||
('\n' + msg.locale.t('wiki.message.error.info') + check.message if check.message != '' else '')
|
||||
await msg.finish(result)
|
||||
|
||||
@aud.command(['list', 'list legacy'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
allow_list = Audit.get_allow_list()
|
||||
block_list = Audit.get_block_list()
|
||||
legacy = True
|
||||
if not msg.parsed_msg.get('legacy', False) and msg.Feature.image:
|
||||
send_msgs = []
|
||||
allow_columns = [[x[0], x[1]] for x in allow_list]
|
||||
if allow_columns:
|
||||
allow_table = ImageTable(data=allow_columns, headers=[
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.apilink'),
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.date')
|
||||
])
|
||||
if allow_table:
|
||||
allow_image = await image_table_render(allow_table)
|
||||
if allow_image:
|
||||
send_msgs.append(Plain(msg.locale.t('wiki.message.wiki_audit.list.allowlist')))
|
||||
send_msgs.append(Image(allow_image))
|
||||
block_columns = [[x[0], x[1]] for x in block_list]
|
||||
if block_columns:
|
||||
block_table = ImageTable(data=block_columns, headers=[
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.apilink'),
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.date')
|
||||
])
|
||||
if block_table:
|
||||
block_image = await image_table_render(block_table)
|
||||
if block_image:
|
||||
send_msgs.append(Plain(msg.locale.t('wiki.message.wiki_audit.list.blocklist')))
|
||||
send_msgs.append(Image(block_image))
|
||||
if send_msgs:
|
||||
await msg.finish(send_msgs)
|
||||
legacy = False
|
||||
if legacy:
|
||||
wikis = [msg.locale.t('wiki.message.wiki_audit.list.allowlist')]
|
||||
for al in allow_list:
|
||||
wikis.append(f'{al[0]} ({al[1]})')
|
||||
wikis.append(msg.locale.t('wiki.message.wiki_audit.list.blocklist'))
|
||||
for bl in block_list:
|
||||
wikis.append(f'{bl[0]} ({bl[1]})')
|
||||
await msg.finish('\n'.join(wikis))
|
||||
|
||||
@aud.command(['list', 'list legacy'])
|
||||
async def _(msg: Bot.MessageSession):
|
||||
allow_list = Audit.get_allow_list()
|
||||
block_list = Audit.get_block_list()
|
||||
legacy = True
|
||||
if not msg.parsed_msg.get('legacy', False) and msg.Feature.image:
|
||||
send_msgs = []
|
||||
allow_columns = [[x[0], x[1]] for x in allow_list]
|
||||
if allow_columns:
|
||||
allow_table = ImageTable(data=allow_columns, headers=[
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.apilink'),
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.date')
|
||||
])
|
||||
if allow_table:
|
||||
allow_image = await image_table_render(allow_table)
|
||||
if allow_image:
|
||||
send_msgs.append(Plain(msg.locale.t('wiki.message.wiki_audit.list.allowlist')))
|
||||
send_msgs.append(Image(allow_image))
|
||||
block_columns = [[x[0], x[1]] for x in block_list]
|
||||
if block_columns:
|
||||
block_table = ImageTable(data=block_columns, headers=[
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.apilink'),
|
||||
msg.locale.t('wiki.message.wiki_audit.list.table.header.date')
|
||||
])
|
||||
if block_table:
|
||||
block_image = await image_table_render(block_table)
|
||||
if block_image:
|
||||
send_msgs.append(Plain(msg.locale.t('wiki.message.wiki_audit.list.blocklist')))
|
||||
send_msgs.append(Image(block_image))
|
||||
if send_msgs:
|
||||
await msg.finish(send_msgs)
|
||||
legacy = False
|
||||
if legacy:
|
||||
wikis = [msg.locale.t('wiki.message.wiki_audit.list.allowlist')]
|
||||
for al in allow_list:
|
||||
wikis.append(f'{al[0]} ({al[1]})')
|
||||
wikis.append(msg.locale.t('wiki.message.wiki_audit.list.blocklist'))
|
||||
for bl in block_list:
|
||||
wikis.append(f'{bl[0]} ({bl[1]})')
|
||||
await msg.finish('\n'.join(wikis))
|
||||
|
|
|
@ -9,6 +9,7 @@ from core.component import module
|
|||
from core.dirty_check import check
|
||||
from core.logger import Logger
|
||||
from core.utils.http import download_to_cache
|
||||
from core.utils.image_table import image_table_render, ImageTable
|
||||
from modules.wiki.utils.dbutils import WikiTargetInfo
|
||||
from modules.wiki.utils.screenshot_image import generate_screenshot_v1, generate_screenshot_v2
|
||||
from modules.wiki.utils.wikilib import WikiLib
|
||||
|
@ -20,7 +21,7 @@ wiki_inline = module('wiki_inline',
|
|||
|
||||
|
||||
@wiki_inline.regex(re.compile(r'\[\[(.*?)]]', flags=re.I), mode='A',
|
||||
desc="{wiki.help.wiki_inline.page}")
|
||||
desc="{wiki.help.wiki_inline.page}")
|
||||
async def _(msg: Bot.MessageSession):
|
||||
query_list = []
|
||||
for x in msg.matched_msg:
|
||||
|
@ -31,7 +32,7 @@ async def _(msg: Bot.MessageSession):
|
|||
|
||||
|
||||
@wiki_inline.regex(re.compile(r'\{\{(.*?)}}', flags=re.I), mode='A',
|
||||
desc='{wiki.help.wiki_inline.template}')
|
||||
desc='{wiki.help.wiki_inline.template}')
|
||||
async def _(msg: Bot.MessageSession):
|
||||
query_list = []
|
||||
for x in msg.matched_msg:
|
||||
|
@ -42,7 +43,7 @@ async def _(msg: Bot.MessageSession):
|
|||
|
||||
|
||||
@wiki_inline.regex(re.compile(r'≺(.*?)≻|⧼(.*?)⧽', flags=re.I), mode='A', show_typing=False,
|
||||
desc='{wiki.help.wiki_inline.mediawiki}')
|
||||
desc='{wiki.help.wiki_inline.mediawiki}')
|
||||
async def _(msg: Bot.MessageSession):
|
||||
query_list = []
|
||||
for x in msg.matched_msg:
|
||||
|
@ -130,6 +131,27 @@ async def _(msg: Bot.MessageSession):
|
|||
get_infobox = await generate_screenshot_v1(q[qq].realurl, qq, headers)
|
||||
if get_infobox:
|
||||
await msg.send_message(Image(get_infobox), quote=False)
|
||||
if get_page.invalid_section and wiki_.wiki_info.in_allowlist:
|
||||
i_msg_lst = []
|
||||
session_data = [[str(i + 1), get_page.sections[i]] for i in
|
||||
range(len(get_page.sections))]
|
||||
i_msg_lst.append(Plain(msg.locale.t('wiki.message.invalid_section')))
|
||||
i_msg_lst.append(Image(await
|
||||
image_table_render(
|
||||
ImageTable(session_data,
|
||||
['ID',
|
||||
msg.locale.t('wiki.message.section')]))))
|
||||
|
||||
async def _callback(msg: Bot.MessageSession):
|
||||
display = msg.as_display(text_only=True)
|
||||
if display.isdigit():
|
||||
display = int(display)
|
||||
if display <= len(get_page.sections):
|
||||
get_page.selected_section = display - 1
|
||||
await query_pages(msg, title=get_page.title + '#' +
|
||||
get_page.sections[display - 1])
|
||||
|
||||
await msg.send_message(i_msg_lst, callback=_callback)
|
||||
if len(query_list) == 1 and img_send:
|
||||
return
|
||||
if msg.Feature.image:
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
"wiki.help.wiki_inline.page": "Enter a title within [[ ]] to automatically query the page.",
|
||||
"wiki.help.wiki_inline.template": "Enter a title within {{ }} to automatically query the template.",
|
||||
"wiki.help.wiki_inline.url": "Send the URL of the wiki page to generate the Infobox image.",
|
||||
"wiki.message.section": "章节",
|
||||
"wiki.message.section.rendering": "(章节渲染中)",
|
||||
"wiki.message.ab.qq.title": "滥用过滤器日志",
|
||||
"wiki.message.ab.qq.title.address": "滥用过滤器日志地址",
|
||||
"wiki.message.ab.slice": "•${title} - ${user} at ${time}\n Filter description: ${filter_name}\n Actions taken: ${result}",
|
||||
|
@ -37,6 +39,7 @@
|
|||
"wiki.message.error.info": "Detail: ",
|
||||
"wiki.message.error.query": "An error occurred: Unable to query this wiki.",
|
||||
"wiki.message.error.set": "An error occurred: Unable to set up this wiki.",
|
||||
"wiki.message.error.unable_to_render_section": "(章节渲染失败,请联系开发者检查原因)",
|
||||
"wiki.message.fandom.disable": "Disabled to use Fandom global Interwiki queries.",
|
||||
"wiki.message.fandom.enable": "Enabled to use Fandom global Interwiki queries.",
|
||||
"wiki.message.flies": "This page includes the following files:",
|
||||
|
@ -47,6 +50,8 @@
|
|||
"wiki.message.id.error": "An error occurred: Page ID must be numeric.",
|
||||
"wiki.message.id.not_found": "Page with page ID ${id} not found.",
|
||||
"wiki.message.invalid_namespace": "There is no namespace named ${namespace} on this wiki. Please check your input.",
|
||||
"wiki.message.invalid_section": "此章节不存在,以下是可能的章节(回复对应序号以选中):",
|
||||
"wiki.message.invalid_section.prompt": "(此章节不存在)",
|
||||
"wiki.message.iw.add.success": "Custom Interwiki added:\n ${iw} -> ${name}",
|
||||
"wiki.message.iw.get.not_found": "Interwiki not found: ${iw}",
|
||||
"wiki.message.iw.list": "Use \"${prefix}wiki iw get <Interwiki>\" to get the corresponding link to Interwiki.",
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
"wiki.help.wiki_inline.page": "在 [[ ]] 内输入标题以自动查询页面。",
|
||||
"wiki.help.wiki_inline.template": "在 {{ }} 内输入标题以自动查询模板。",
|
||||
"wiki.help.wiki_inline.url": "发送 Wiki 页面的 URL 以生成 Infobox 图片。",
|
||||
"wiki.message.section": "章节",
|
||||
"wiki.message.section.rendering": "(章节渲染中)",
|
||||
"wiki.message.ab.qq.title": "滥用过滤器日志",
|
||||
"wiki.message.ab.qq.title.address": "滥用过滤器日志地址",
|
||||
"wiki.message.ab.slice": "•${title} - ${user} 于 ${time}\n 过滤器描述:${filter_name}\n 采取的行动:${result}",
|
||||
|
@ -37,6 +39,7 @@
|
|||
"wiki.message.error.info": "详细信息:",
|
||||
"wiki.message.error.query": "发生错误:无法查询此 Wiki。",
|
||||
"wiki.message.error.set": "发生错误:无法设置此 Wiki。",
|
||||
"wiki.message.error.unable_to_render_section": "(章节渲染失败,请联系开发者检查原因)",
|
||||
"wiki.message.fandom.disable": "已关闭 Fandom 全局 Interwiki 查询。",
|
||||
"wiki.message.fandom.enable": "已开启 Fandom 全局 Interwiki 查询。",
|
||||
"wiki.message.flies": "此页面包括以下文件:\n",
|
||||
|
@ -47,6 +50,8 @@
|
|||
"wiki.message.id.error": "发生错误:页面 ID 必须为数字。",
|
||||
"wiki.message.id.not_found": "未找到页面 ID 为 ${id} 的页面。",
|
||||
"wiki.message.invalid_namespace": "此 Wiki 上没有名为 ${namespace} 的命名空间,请检查输入。",
|
||||
"wiki.message.invalid_section": "此章节不存在,以下是可能的章节(回复对应序号以选中):",
|
||||
"wiki.message.invalid_section.prompt": "(此章节不存在)",
|
||||
"wiki.message.iw.add.success": "已添加自定义 Interwiki:\n${iw} -> ${name}",
|
||||
"wiki.message.iw.get.not_found": "未找到 Interwiki:${iw}",
|
||||
"wiki.message.iw.list": "使用“${prefix}wiki iw get <Interwiki>”可以获取 Interwiki 对应的链接。",
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
"wiki.help.wiki_inline.page": "在 [[ ]] 內輸入標題以自動查詢頁面。",
|
||||
"wiki.help.wiki_inline.template": "在 {{ }} 內輸入標題以自動查詢模板。",
|
||||
"wiki.help.wiki_inline.url": "傳送 Wiki 頁面的 URL 以生成 Infobox 圖片。",
|
||||
"wiki.message.section": "章節",
|
||||
"wiki.message.section.rendering": "(章節成像中)",
|
||||
"wiki.message.ab.qq.title": "防濫用過濾器日誌",
|
||||
"wiki.message.ab.qq.title.address": "防濫用過濾器位址",
|
||||
"wiki.message.ab.slice": "•${title} - ${user} 於 ${time}\n 過濾器描述:${filter_name}\n 採取的動作:${result}",
|
||||
|
@ -37,6 +39,7 @@
|
|||
"wiki.message.error.info": "詳細訊息:",
|
||||
"wiki.message.error.query": "發生錯誤:無法查詢此 Wiki。",
|
||||
"wiki.message.error.set": "發生錯誤:無法設定此 Wiki。",
|
||||
"wiki.message.error.unable_to_render_section": "(章節成像失敗,請聯絡開發人員檢查原因)",
|
||||
"wiki.message.fandom.disable": "已停用 Fandom 全域 Interwiki 查詢。",
|
||||
"wiki.message.fandom.enable": "已啟用 Fandom 全域 Interwiki 查詢。",
|
||||
"wiki.message.flies": "此頁面含有以下檔案:",
|
||||
|
@ -47,6 +50,8 @@
|
|||
"wiki.message.id.error": "發生錯誤:頁面 ID 必須為數字。",
|
||||
"wiki.message.id.not_found": "未找到頁面 ID 為 ${id} 的頁面。",
|
||||
"wiki.message.invalid_namespace": "此 Wiki 上沒有名為 ${namespace} 的命名空間,请校對輸入。",
|
||||
"wiki.message.invalid_section": "此章節不存在,以下是可能的章節(回覆對應序號以選中):",
|
||||
"wiki.message.invalid_section.prompt": "(此章節不存在)",
|
||||
"wiki.message.iw.add.success": "已新增自訂 Interwiki:\n${iw} -> ${name}",
|
||||
"wiki.message.iw.get.not_found": "未找到 Interwiki:${iw}",
|
||||
"wiki.message.iw.list": "使用 「${prefix}wiki iw get <Interwiki>」可以取得 Interwiki 對應的連結。",
|
||||
|
|
|
@ -114,7 +114,8 @@ class PageInfo:
|
|||
file: str = None,
|
||||
desc: str = None,
|
||||
args: str = None,
|
||||
section: str = None,
|
||||
selected_section: str = None,
|
||||
sections: List[str] = None,
|
||||
interwiki_prefix: str = '',
|
||||
status: bool = True,
|
||||
templates: List[str] = None,
|
||||
|
@ -122,7 +123,7 @@ class PageInfo:
|
|||
page_property: str = 'page',
|
||||
has_template_doc: bool = False,
|
||||
invalid_namespace: Union[str, bool] = False,
|
||||
possible_research_title: List[str] = None
|
||||
possible_research_title: List[str] = None,
|
||||
):
|
||||
self.info = info
|
||||
self.id = id
|
||||
|
@ -133,7 +134,8 @@ class PageInfo:
|
|||
self.file = file
|
||||
self.desc = desc
|
||||
self.args = args
|
||||
self.section = section
|
||||
self.selected_section = selected_section
|
||||
self.sections = sections
|
||||
self.interwiki_prefix = interwiki_prefix
|
||||
self.templates = templates
|
||||
self.status = status
|
||||
|
@ -142,6 +144,7 @@ class PageInfo:
|
|||
self.has_template_doc = has_template_doc
|
||||
self.invalid_namespace = invalid_namespace
|
||||
self.possible_research_title = possible_research_title
|
||||
self.invalid_section = False
|
||||
|
||||
|
||||
class WikiLib:
|
||||
|
@ -437,7 +440,7 @@ class WikiLib:
|
|||
if _tried > 5:
|
||||
if Config('enable_tos'):
|
||||
raise WhatAreUDoingError
|
||||
section = None
|
||||
selected_section = None
|
||||
if title is not None:
|
||||
if title == '':
|
||||
return PageInfo(title='', link=self.wiki_info.articlepath.replace("$1", ""), info=self.wiki_info,
|
||||
|
@ -450,11 +453,13 @@ class WikiLib:
|
|||
arg_list = []
|
||||
_arg_list = []
|
||||
section_list = []
|
||||
used_quote = False
|
||||
quote_code = False
|
||||
for a in split_name[1:]:
|
||||
if len(a) > 0:
|
||||
if a[0] == '#':
|
||||
quote_code = True
|
||||
used_quote = True
|
||||
if a[0] == '?':
|
||||
quote_code = False
|
||||
if quote_code:
|
||||
|
@ -471,9 +476,11 @@ class WikiLib:
|
|||
else:
|
||||
title += _arg
|
||||
if len(section_list) > 1:
|
||||
section = ''.join(section_list)[1:]
|
||||
selected_section = ''.join(section_list)[1:]
|
||||
page_info = PageInfo(info=self.wiki_info, title=title, args=''.join(arg_list), interwiki_prefix=_prefix)
|
||||
page_info.section = section
|
||||
page_info.selected_section = selected_section
|
||||
if not selected_section and used_quote:
|
||||
page_info.invalid_section = True
|
||||
query_string = {'action': 'query', 'prop': 'info|imageinfo|langlinks|templates', 'llprop': 'url',
|
||||
'inprop': 'url', 'iiprop': 'url',
|
||||
'redirects': 'True', 'titles': title}
|
||||
|
@ -484,7 +491,7 @@ class WikiLib:
|
|||
else:
|
||||
raise ValueError('title and pageid cannot be both None')
|
||||
use_textextracts = True if 'TextExtracts' in self.wiki_info.extensions else False
|
||||
if use_textextracts and section is None:
|
||||
if use_textextracts and selected_section is None:
|
||||
query_string.update({'prop': 'info|imageinfo|langlinks|templates|extracts|pageprops',
|
||||
'ppprop': 'description|displaytitle|disambiguation|infoboxes', 'explaintext': 'true',
|
||||
'exsectionformat': 'plain', 'exchars': '200'})
|
||||
|
@ -493,6 +500,7 @@ class WikiLib:
|
|||
if query is None:
|
||||
return PageInfo(title=title, link=None, desc=self.locale.t("wiki.message.utils.wikilib.error.empty"),
|
||||
info=self.wiki_info)
|
||||
|
||||
redirects_: List[Dict[str, str]] = query.get('redirects')
|
||||
if redirects_ is not None:
|
||||
for r in redirects_:
|
||||
|
@ -598,6 +606,17 @@ class WikiLib:
|
|||
else:
|
||||
page_info.status = True
|
||||
templates = page_info.templates = [t['title'] for t in page_raw.get('templates', [])]
|
||||
if selected_section or page_info.invalid_section:
|
||||
parse_section_string = {'action': 'parse', 'page': title, 'prop': 'sections'}
|
||||
parse_section = await self.get_json(**parse_section_string)
|
||||
section_list = []
|
||||
sections = parse_section['parse']['sections']
|
||||
for s in sections:
|
||||
section_list.append(s['anchor'])
|
||||
page_info.sections = section_list
|
||||
if selected_section:
|
||||
if urllib.parse.unquote(selected_section) not in section_list:
|
||||
page_info.invalid_section = True
|
||||
if 'special' in page_raw:
|
||||
full_url = re.sub(r'\$1',
|
||||
urllib.parse.quote(title.encode('UTF-8')),
|
||||
|
@ -675,12 +694,12 @@ class WikiLib:
|
|||
page_info.has_template_doc = True
|
||||
page_info.before_page_property = page_info.page_property = 'template'
|
||||
if get_desc:
|
||||
if use_textextracts and section is None:
|
||||
if use_textextracts and (selected_section is None or page_info.invalid_section):
|
||||
raw_desc = page_raw.get('extract')
|
||||
if raw_desc is not None:
|
||||
page_desc = self.parse_text(raw_desc)
|
||||
else:
|
||||
page_desc = self.parse_text(await self.get_html_to_text(title, section))
|
||||
page_desc = self.parse_text(await self.get_html_to_text(title, selected_section))
|
||||
full_url = page_raw['fullurl'] + page_info.args
|
||||
file = None
|
||||
if 'imageinfo' in page_raw:
|
||||
|
@ -736,8 +755,8 @@ class WikiLib:
|
|||
for possible_title in
|
||||
page_info.possible_research_title]
|
||||
|
||||
if before_page_info.section is not None:
|
||||
page_info.section = before_page_info.section
|
||||
if before_page_info.selected_section is not None:
|
||||
page_info.selected_section = before_page_info.selected_section
|
||||
if not self.wiki_info.in_allowlist:
|
||||
checklist = []
|
||||
if page_info.title is not None:
|
||||
|
|
|
@ -5,6 +5,7 @@ from typing import Union
|
|||
import filetype
|
||||
|
||||
from core.builtins import Bot, Plain, Image, Voice, Url, confirm_command
|
||||
from core.utils.image_table import image_table_render, ImageTable
|
||||
from core.component import module
|
||||
from core.exceptions import AbuseWarning
|
||||
from core.logger import Logger
|
||||
|
@ -25,7 +26,7 @@ wiki = module('wiki',
|
|||
|
||||
|
||||
@wiki.command('<PageName> [-l <lang>] {{wiki.help}}',
|
||||
options_desc={'-l': '{wiki.help.option.l}'})
|
||||
options_desc={'-l': '{wiki.help.option.l}'})
|
||||
async def _(msg: Bot.MessageSession):
|
||||
get_lang = msg.parsed_msg.get('-l', False)
|
||||
if get_lang:
|
||||
|
@ -201,8 +202,16 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio
|
|||
else:
|
||||
plain_slice.append(session.locale.t('wiki.message.redirect', title=display_before_title,
|
||||
redirected_title=display_title))
|
||||
if r.desc is not None and r.desc != '':
|
||||
plain_slice.append(r.desc)
|
||||
if (r.link is not None and r.selected_section is not None and r.info.in_allowlist and
|
||||
not r.invalid_section):
|
||||
render_section_list.append(
|
||||
{r.link: {'url': r.info.realurl, 'section': r.selected_section,
|
||||
'in_allowlist': r.info.in_allowlist}})
|
||||
plain_slice.append(session.locale.t("wiki.message.section.rendering"))
|
||||
else:
|
||||
if r.desc is not None and r.desc != '':
|
||||
plain_slice.append(r.desc)
|
||||
|
||||
if r.link is not None:
|
||||
plain_slice.append(
|
||||
str(Url(r.link, use_mm=not r.info.in_allowlist)))
|
||||
|
@ -211,19 +220,39 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio
|
|||
dl_list.append(r.file)
|
||||
plain_slice.append(session.locale.t('wiki.message.flies') + r.file)
|
||||
else:
|
||||
if r.link is not None and r.section is None:
|
||||
if r.link is not None and r.selected_section is None:
|
||||
render_infobox_list.append(
|
||||
{r.link: {'url': r.info.realurl, 'in_allowlist': r.info.in_allowlist,
|
||||
'content_mode': r.has_template_doc or r.title.split(':')[0] in ['User'] or
|
||||
(r.templates is not None and
|
||||
('Template:Disambiguation' in r.templates or
|
||||
'Template:Version disambiguation' in r.templates))}})
|
||||
elif r.link is not None and r.section is not None and r.info.in_allowlist:
|
||||
render_section_list.append(
|
||||
{r.link: {'url': r.info.realurl, 'section': r.section,
|
||||
'in_allowlist': r.info.in_allowlist}})
|
||||
if plain_slice:
|
||||
msg_list.append(Plain('\n'.join(plain_slice)))
|
||||
if r.invalid_section and r.info.in_allowlist:
|
||||
if isinstance(session, Bot.MessageSession):
|
||||
|
||||
if session.Feature.image:
|
||||
i_msg_lst = []
|
||||
session_data = [[str(i + 1), r.sections[i]] for i in range(len(r.sections))]
|
||||
i_msg_lst.append(Plain(session.locale.t('wiki.message.invalid_section')))
|
||||
i_msg_lst.append(Image(await
|
||||
image_table_render(
|
||||
ImageTable(session_data,
|
||||
['ID',
|
||||
session.locale.t('wiki.message.section')]))))
|
||||
|
||||
async def _callback(msg: Bot.MessageSession):
|
||||
display = msg.as_display(text_only=True)
|
||||
if display.isdigit():
|
||||
display = int(display)
|
||||
if display <= len(r.sections):
|
||||
r.selected_section = display - 1
|
||||
await query_pages(session, title=r.title + '#' + r.sections[display - 1])
|
||||
|
||||
await session.send_message(i_msg_lst, callback=_callback)
|
||||
else:
|
||||
msg_list.append(Plain(session.locale.t('wiki.message.invalid_section.prompt')))
|
||||
else:
|
||||
plain_slice = []
|
||||
wait_plain_slice = []
|
||||
|
@ -323,11 +352,17 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio
|
|||
get_section = await generate_screenshot_v2(ii, section=i[ii]['section'])
|
||||
if get_section:
|
||||
section_msg_list.append(Image(get_section))
|
||||
else:
|
||||
section_msg_list.append(Plain(
|
||||
session.locale.t("wiki.message.error.unable_to_render_section")))
|
||||
else:
|
||||
get_section = await generate_screenshot_v1(i[ii]['url'], ii, headers,
|
||||
section=i[ii]['section'])
|
||||
if get_section:
|
||||
section_msg_list.append(Image(get_section))
|
||||
else:
|
||||
section_msg_list.append(Plain(
|
||||
session.locale.t("wiki.message.error.unable_to_render_section")))
|
||||
if section_msg_list:
|
||||
await session.send_message(section_msg_list, quote=False)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import datetime
|
||||
import re
|
||||
import traceback
|
||||
from urllib.parse import quote
|
||||
|
@ -7,6 +8,7 @@ from bs4 import BeautifulSoup
|
|||
from google_play_scraper import app as google_play_scraper
|
||||
|
||||
from config import CFG, Config
|
||||
from core.builtins import I18NText, FormattedTime
|
||||
from core.logger import Logger
|
||||
from core.queue import JobQueue
|
||||
from core.scheduler import Scheduler, IntervalTrigger
|
||||
|
@ -74,32 +76,47 @@ async def mcv_rss():
|
|||
file = json.loads(await get_url(url, attempt=1))
|
||||
release = file['latest']['release']
|
||||
snapshot = file['latest']['snapshot']
|
||||
time_release = 0
|
||||
time_snapshot = 0
|
||||
for v in file['versions']:
|
||||
if v['id'] == release:
|
||||
time_release = datetime.datetime.fromisoformat(v['releaseTime']).timestamp()
|
||||
if v['id'] == snapshot:
|
||||
time_snapshot = datetime.datetime.fromisoformat(v['releaseTime']).timestamp()
|
||||
|
||||
if release not in verlist:
|
||||
Logger.info(f'huh, we find {release}.')
|
||||
await JobQueue.trigger_hook_all('mcv_rss', message='mcv_rss.message.mcv_rss.release',
|
||||
i18n=True, version=file['latest']['release'])
|
||||
|
||||
await JobQueue.trigger_hook_all('mcv_rss',
|
||||
message=[I18NText('mcv_rss.message.mcv_rss.release',
|
||||
version=release).to_dict(),
|
||||
FormattedTime(time_release).to_dict()
|
||||
])
|
||||
verlist.append(release)
|
||||
update_stored_list('scheduler', 'mcv_rss', verlist)
|
||||
article = await get_article(release)
|
||||
if article[0] != '':
|
||||
get_stored_news_title = get_stored_list('scheduler', 'mcnews')
|
||||
if article[1] not in get_stored_news_title:
|
||||
await JobQueue.trigger_hook_all('minecraft_news', message='minecraft_news.message.update_log',
|
||||
i18n=True, version=release, article=article[0])
|
||||
await JobQueue.trigger_hook_all('minecraft_news',
|
||||
message=[I18NText('minecraft_news.message.update_log',
|
||||
version=release, article=article[0]).to_dict()])
|
||||
get_stored_news_title.append(article[1])
|
||||
update_stored_list('scheduler', 'mcnews', get_stored_news_title)
|
||||
if snapshot not in verlist:
|
||||
Logger.info(f'huh, we find {snapshot}.')
|
||||
await JobQueue.trigger_hook_all('mcv_rss', message='mcv_rss.message.mcv_rss.snapshot', i18n=True,
|
||||
version=file['latest']['snapshot'])
|
||||
await JobQueue.trigger_hook_all('mcv_rss', message=[I18NText('mcv_rss.message.mcv_rss.snapshot',
|
||||
version=file['latest']['snapshot']).to_dict(),
|
||||
FormattedTime(time_snapshot).to_dict()])
|
||||
verlist.append(snapshot)
|
||||
update_stored_list('scheduler', 'mcv_rss', verlist)
|
||||
article = await get_article(snapshot)
|
||||
if article[0] != '':
|
||||
get_stored_news_title = get_stored_list('scheduler', 'mcnews')
|
||||
if article[1] not in get_stored_news_title:
|
||||
await JobQueue.trigger_hook_all('minecraft_news', message='minecraft_news.message.update_log',
|
||||
i18n=True, version=snapshot, article=article[0])
|
||||
await JobQueue.trigger_hook_all('minecraft_news',
|
||||
message=[I18NText('minecraft_news.message.update_log',
|
||||
version=snapshot, article=article[0]).to_dict()])
|
||||
get_stored_news_title.append(article[1])
|
||||
update_stored_list('scheduler', 'mcnews', get_stored_news_title)
|
||||
except Exception:
|
||||
|
|
Reference in a new issue