diff --git a/LOCALES.md b/LOCALES.md new file mode 100644 index 0000000000..c4e21514c5 --- /dev/null +++ b/LOCALES.md @@ -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) 报告。 diff --git a/README.md b/README.md index 0bcef1897a..7b05e9192b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,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 频道官方机器人入驻,未来可能会成为官方认证的机器人。 @@ -51,7 +51,7 @@ 你可以[参考这里](./DEPLOY.md)来进行尝试搭建。 -若遇到问题,可以通过 [Issue](https://github.com/Teahouse-Studios/bot/issues/new) 或其他方式咨询开发者。 +若遇到问题,可以通过 [Issue](https://github.com/Teahouse-Studios/akari-bot/issues/new) 或其他方式咨询开发者。 ### 多语言 diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_01.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_01.png index 5a9c90da5b..f271502019 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_01.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_01.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_02.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_02.png index fb630dd705..692228b99c 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_02.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_02.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_03.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_03.png index 59fbe4178c..9c284dd3f7 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_03.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_03.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_04.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_04.png index 9aed976297..6849ba4f87 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_04.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_04.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_05.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_05.png index 2eefb2c548..3c4060daa9 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_05.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_05.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_06.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_06.png index de4fd344be..5c9b9ade00 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_06.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_06.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_07.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_07.png index 688b65ae0a..eb5b8f2e0d 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_07.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_07.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_08.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_08.png index 18d8fc1e6d..87e3609769 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_08.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_08.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_09.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_09.png index ceb8056ae4..8d231ff1f5 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_09.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_09.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_10.png b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_10.png index 3b6e600bef..0b926783ac 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_10.png and b/assets/maimai/static/mai/default_pic/UI_CMN_DXRating_S_10.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_MiniDialog_01.png b/assets/maimai/static/mai/default_pic/UI_CMN_MiniDialog_01.png index 729476dd1c..75f686ecbc 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_MiniDialog_01.png and b/assets/maimai/static/mai/default_pic/UI_CMN_MiniDialog_01.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_Name_DX.png b/assets/maimai/static/mai/default_pic/UI_CMN_Name_DX.png index 2c44b7db74..2f4aac5d33 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_Name_DX.png and b/assets/maimai/static/mai/default_pic/UI_CMN_Name_DX.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_Shougou_Rainbow.png b/assets/maimai/static/mai/default_pic/UI_CMN_Shougou_Rainbow.png index ecbc0bdc1a..d2aafe89e4 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_Shougou_Rainbow.png and b/assets/maimai/static/mai/default_pic/UI_CMN_Shougou_Rainbow.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_CMN_TabTitle_MaimaiTitle_Ver214.png b/assets/maimai/static/mai/default_pic/UI_CMN_TabTitle_MaimaiTitle_Ver214.png index 1f07453f13..650b2e9f45 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_CMN_TabTitle_MaimaiTitle_Ver214.png and b/assets/maimai/static/mai/default_pic/UI_CMN_TabTitle_MaimaiTitle_Ver214.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_A.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_A.png index 6e0788043d..e5764d3535 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_A.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_A.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AA.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AA.png index c5a6eea25b..83a201e278 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AA.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AA.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AAA.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AAA.png index 8da350216e..dd6d17e95c 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AAA.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_AAA.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_B.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_B.png index efa112f040..809a785af9 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_B.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_B.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BB.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BB.png index ae1980817f..4329f90ebf 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BB.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BB.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BBB.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BBB.png index c9a18b5b97..d0056c8746 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BBB.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_BBB.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_C.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_C.png index 3647e7ac50..6492d4b000 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_C.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_C.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_D.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_D.png index 94aafc26e5..b062c2a85e 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_D.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_D.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_S.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_S.png index 5947c4ce59..f5bad4b457 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_S.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_S.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SS.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SS.png index c8f027f511..21ec570a5d 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SS.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SS.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSS.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSS.png index 0897375db5..aab58bdb5d 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSS.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSS.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSSp.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSSp.png index d798fd85d5..6b72fb2af1 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSSp.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSSp.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSp.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSp.png index df680fe70d..d15957b031 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSp.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_SSp.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_Sp.png b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_Sp.png index 4d36f49d04..fc35aa7113 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_GAM_Rank_Sp.png and b/assets/maimai/static/mai/default_pic/UI_GAM_Rank_Sp.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_AP_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_AP_S.png index fbb6af4405..3aff7591fa 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_AP_S.png and b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_AP_S.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_APp_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_APp_S.png index b19804b783..4fe1dda99a 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_APp_S.png and b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_APp_S.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FC_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FC_S.png index ff37604cfc..1dd6abbe07 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FC_S.png and b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FC_S.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FCp_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FCp_S.png index bc832181c2..9f6052f364 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FCp_S.png and b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FCp_S.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSD_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSD_S.png deleted file mode 100644 index bd617dabf9..0000000000 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSD_S.png and /dev/null differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSDp_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSDp_S.png deleted file mode 100644 index 7df2fe9f64..0000000000 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSDp_S.png and /dev/null differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FS_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FS_S.png deleted file mode 100644 index fab64accff..0000000000 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FS_S.png and /dev/null differ diff --git a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSp_S.png b/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSp_S.png deleted file mode 100644 index a2e2b852eb..0000000000 Binary files a/assets/maimai/static/mai/default_pic/UI_MSS_MBase_Icon_FSp_S.png and /dev/null differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_0.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_0.png index 0d1cf1cc88..73a8fb739a 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_0.png and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_0.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_1.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_1.png index f69f8700f1..25a06656c7 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_1.png and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_1.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_2.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_2.png index 3942ef5931..4a486b0c6e 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_2.png and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_2.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_3.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_3.png index 7c91b782bf..9f1c6af36f 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_3.png and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_3.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_4.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_4.png index 78d845e3f5..b98b6f6b4c 100644 Binary files a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_4.png and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_4.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_5.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_5.png new file mode 100644 index 0000000000..862441b891 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_5.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_6.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_6.png new file mode 100644 index 0000000000..c2d8fd8efa Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_6.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_7.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_7.png new file mode 100644 index 0000000000..3bb7855c3b Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_7.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_8.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_8.png new file mode 100644 index 0000000000..ba6ec5ae36 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_8.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_NUM_Drating_9.png b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_9.png new file mode 100644 index 0000000000..40a3e2413d Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_NUM_Drating_9.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_01.png b/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_01.png new file mode 100644 index 0000000000..0c023e27e7 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_01.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_02.png b/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_02.png new file mode 100644 index 0000000000..d95e25fad5 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_RSL_MBase_Parts_02.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_TST_PlateMask.png b/assets/maimai/static/mai/default_pic/UI_TST_PlateMask.png new file mode 100644 index 0000000000..7bb6747072 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_TST_PlateMask.png differ diff --git a/assets/maimai/static/mai/default_pic/UI_TTR_BG_Base_Plus.png b/assets/maimai/static/mai/default_pic/UI_TTR_BG_Base_Plus.png new file mode 100644 index 0000000000..7e6be84e01 Binary files /dev/null and b/assets/maimai/static/mai/default_pic/UI_TTR_BG_Base_Plus.png differ diff --git a/bot.py b/bot.py index 8a07abc6eb..9c58158b2b 100644 --- a/bot.py +++ b/bot.py @@ -45,7 +45,7 @@ def enqueue_output(out, queue): def init_bot(): base_superuser = Config('base_superuser') - if base_superuser is not None: + if base_superuser: if isinstance(base_superuser, str): base_superuser = [base_superuser] for bu in base_superuser: @@ -143,7 +143,7 @@ def run_bot(): raise RestartBot # break when all processes are done. - if all(p.poll() is not None for p in runlst): + if all(p.poll() for p in runlst): break sleep(0.0001) @@ -154,7 +154,7 @@ def run_bot(): logger.remove() logger.add(sys.stderr, format='{message}', level="INFO") query_dbver = session.query(DBVersion).first() - if query_dbver is None: + if not query_dbver: session.add_all([DBVersion(value=str(BotDBUtil.database_version))]) session.commit() query_dbver = session.query(DBVersion).first() diff --git a/bots/aiocqhttp/bot.py b/bots/aiocqhttp/bot.py index 35d4527657..f05b6f58f6 100644 --- a/bots/aiocqhttp/bot.py +++ b/bots/aiocqhttp/bot.py @@ -107,7 +107,7 @@ class GuildAccountInfo: @bot.on_message('guild') async def _(event): - if GuildAccountInfo.tiny_id is None: + if not GuildAccountInfo.tiny_id: profile = await bot.call_action('get_guild_service_profile') GuildAccountInfo.tiny_id = profile['tiny_id'] tiny_id = event.user_id @@ -144,7 +144,7 @@ async def _(event: Event): if not Config('allow_bot_auto_approve_group_invite'): await bot.send_private_msg(user_id=event.user_id, message='你好!本机器人暂时不主动同意入群请求。\n' - f'请至{Config("qq_join_group_application_link")}申请入群。') + f'请至https://github.com/Teahouse-Studios/bot/issues/new?assignees=OasisAkari&labels=New&template=add_new_group.yaml&title=%5BNEW%5D%3A+申请入群。') else: return {'approve': True} diff --git a/bots/aiocqhttp/message.py b/bots/aiocqhttp/message.py index c790cf598b..2e543b2691 100644 --- a/bots/aiocqhttp/message.py +++ b/bots/aiocqhttp/message.py @@ -14,7 +14,7 @@ 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 Feature: 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} ' @@ -124,12 +128,12 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal if isinstance(x, Plain): msg = msg + MessageSegment.text(('\n' if count != 0 else '') + x.text) elif isinstance(x, Image): - msg = msg + MessageSegment.image(Path(await x.get()).as_uri()) + msg = msg + MessageSegment.image('base64://' + await x.get_base64()) elif isinstance(x, Voice): if self.target.target_from != 'QQ|Guild': msg = msg + MessageSegment.record(file=Path(x.path).as_uri()) count += 1 - Logger.info(f'[Bot] -> [{self.target.target_id}]: {msg}') + Logger.info(f'[Bot] -> [{self.target.target_id}]: {message_chain_assendable}') if self.target.target_from == 'QQ|Group': try: send = await bot.send_group_msg(group_id=self.session.target, message=msg) @@ -170,6 +174,8 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): @@ -343,11 +349,14 @@ async def post_(fetch_: Bot.FetchedSession): Temp.data['waiting_for_send_group_message'].append({'fetch': fetch_, 'message': message, 'i18n': i18n, 'kwargs': kwargs}) else: - if i18n: - await fetch_.send_direct_message(fetch_.parent.locale.t(message, **kwargs)) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(fetch_.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) - else: - await fetch_.send_direct_message(message) + await fetch_.send_direct_message(msgchain) if _tsk: _tsk = [] if enable_analytics: @@ -376,7 +385,7 @@ async def post_(fetch_: Bot.FetchedSession): except Exception: Logger.error(traceback.format_exc()) - if user_list is not None: + if user_list: for x in user_list: await post_(x) else: diff --git a/bots/aiogram/bot.py b/bots/aiogram/bot.py index a16a236d4f..3d60b0a670 100644 --- a/bots/aiogram/bot.py +++ b/bots/aiogram/bot.py @@ -21,7 +21,7 @@ async def msg_handler(message: types.Message): target_id = f'Telegram|{message.chat.type}|{message.chat.id}' reply_id = None - if message.reply_to_message is not None: + if message.reply_to_message: reply_id = message.reply_to_message.message_id msg = MessageSession(MsgInfo(target_id=target_id, sender_id=f'Telegram|User|{message.from_user.id}', diff --git a/bots/aiogram/message.py b/bots/aiogram/message.py index 84c55a5dd8..b5aa60f097 100644 --- a/bots/aiogram/message.py +++ b/bots/aiogram/message.py @@ -5,7 +5,7 @@ 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 Feature: 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 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): @@ -174,14 +176,16 @@ async def fetch_target_list(target_list: list) -> List[Bot.FetchedSession]: @staticmethod async def post_message(module_name, message, user_list: List[Bot.FetchedSession] = None, i18n=False, **kwargs): - if user_list is not None: + if user_list: for x in user_list: try: - if i18n: - await x.send_direct_message(x.parent.locale.t(message, **kwargs)) - - else: - await x.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(x.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await x.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(x).add('', module_name, 'schedule') except Exception: @@ -192,11 +196,13 @@ async def post_message(module_name, message, user_list: List[Bot.FetchedSession] fetch = await FetchTarget.fetch_target(x.targetId) if fetch: try: - if i18n: - await fetch.send_direct_message(fetch.parent.locale.t(message, **kwargs)) - - else: - await fetch.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(fetch.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await fetch.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(fetch).add('', module_name, 'schedule') except Exception: diff --git a/bots/api/bot.py b/bots/api/bot.py index c507fc63ec..1b9d73b35a 100644 --- a/bots/api/bot.py +++ b/bots/api/bot.py @@ -54,7 +54,7 @@ async def auth(token: str): @app.get('/target/{target_id}') async def get_target(target_id: str): target = BotDBUtil.TargetInfo(target_id) - if target.query is None: + if not target.query: return JSONResponse(status_code=404, content={ 'target_id': target_id, 'notFound': True, diff --git a/bots/discord/bot.py b/bots/discord/bot.py index 41ca3beb33..6fe956b9cb 100644 --- a/bots/discord/bot.py +++ b/bots/discord/bot.py @@ -51,7 +51,7 @@ def load_slashcommands(): elif os.path.isfile(file_path): if file_name[0] != '_' and file_name.endswith('.py'): fun_file = file_name[:-3] - if fun_file is not None: + if fun_file: Logger.info(f'Loading slash.{fun_file}...') modules = 'bots.discord.slash.' + fun_file importlib.import_module(modules) @@ -75,7 +75,7 @@ async def on_message(message): target = "Discord|DM|Channel" target_id = f"{target}|{message.channel.id}" reply_id = None - if message.reference is not None: + if message.reference: reply_id = message.reference.message_id prefix = None if match_at := re.match(r'^<@(.*?)>', message.content): diff --git a/bots/discord/client.py b/bots/discord/client.py index e1ae5130aa..ededf6d3e5 100644 --- a/bots/discord/client.py +++ b/bots/discord/client.py @@ -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')) diff --git a/bots/discord/message.py b/bots/discord/message.py index 9caa03a70f..5886929046 100644 --- a/bots/discord/message.py +++ b/bots/discord/message.py @@ -9,7 +9,7 @@ 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 @@ -23,25 +23,25 @@ async def convert_embed(embed: Embed): if isinstance(embed, Embed): files = [] - embeds = discord.Embed(title=embed.title if embed.title is not None else discord.Embed.Empty, - description=embed.description if embed.description is not None else discord.Embed.Empty, - color=embed.color if embed.color is not None else discord.Embed.Empty, - url=embed.url if embed.url is not None else discord.Embed.Empty, + embeds = discord.Embed(title=embed.title if embed.title else discord.Embed.Empty, + description=embed.description if embed.description else discord.Embed.Empty, + color=embed.color if embed.color else discord.Embed.Empty, + url=embed.url if embed.url else discord.Embed.Empty, timestamp=datetime.datetime.fromtimestamp( - embed.timestamp) if embed.timestamp is not None else discord.Embed.Empty, ) - if embed.image is not None: + embed.timestamp) if embed.timestamp else discord.Embed.Empty, ) + if embed.image: upload = discord.File(await embed.image.get(), filename="image.png") files.append(upload) embeds.set_image(url="attachment://image.png") - if embed.thumbnail is not None: + if embed.thumbnail: upload = discord.File(await embed.thumbnail.get(), filename="thumbnail.png") files.append(upload) embeds.set_thumbnail(url="attachment://thumbnail.png") - if embed.author is not None: + if embed.author: embeds.set_author(name=embed.author) - if embed.footer is not None: + if embed.footer: embeds.set_footer(text=embed.footer) - if embed.fields is not None: + if embed.fields: for field in embed.fields: embeds.add_field(name=field.name, value=field.value, inline=field.inline) return embeds, files @@ -69,7 +69,8 @@ class Feature: 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 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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) @@ -209,14 +212,16 @@ async def fetch_target_list(target_list: list) -> List[Bot.FetchedSession]: @staticmethod async def post_message(module_name, message, user_list: List[Bot.FetchedSession] = None, i18n=False, **kwargs): - if user_list is not None: + if user_list: for x in user_list: try: - if i18n: - await x.send_direct_message(x.parent.locale.t(message, **kwargs)) - - else: - await x.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(x.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await x.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(x).add('', module_name, 'schedule') except Exception: @@ -227,11 +232,13 @@ async def post_message(module_name, message, user_list: List[Bot.FetchedSession] fetch = await FetchTarget.fetch_target(x.targetId) if fetch: try: - if i18n: - await fetch.send_direct_message(fetch.parent.locale.t(message, **kwargs)) - - else: - await fetch.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(fetch.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await fetch.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(fetch).add('', module_name, 'schedule') except Exception: diff --git a/bots/discord/slash/arcaea.py b/bots/discord/slash/arcaea.py index 1eca2d4eca..14d1cf8954 100644 --- a/bots/discord/slash/arcaea.py +++ b/bots/discord/slash/arcaea.py @@ -3,48 +3,27 @@ from bots.discord.client import client from bots.discord.slash_parser import slash_parser -arcaea = client.create_group("arcaea", "查询arcaea的相关信息") +arcaea = client.create_group("arcaea", "Queries about Arcaea.") -@arcaea.command(description="查询best30列表") -async def b30(ctx: discord.ApplicationContext): - await slash_parser(ctx, "b30") - - -@arcaea.command(description="查询最近游玩记录") -async def info(ctx: discord.ApplicationContext): - await slash_parser(ctx, "info") - - -@arcaea.command(description="绑定账户") -@discord.option(name="friendcode", description="好友代码") -async def bind(ctx: discord.ApplicationContext, friendcode: str): - await slash_parser(ctx, f"bind {friendcode}") - - -@arcaea.command(description="取消绑定账户") -async def unbind(ctx: discord.ApplicationContext): - await slash_parser(ctx, "unbind") - - -@arcaea.command(description="获取最新版本的arcaea安卓版链接") +@arcaea.command(name="download", description="Get the latest version of game apk.") async def download(ctx: discord.ApplicationContext): await slash_parser(ctx, "download") -@arcaea.command(description="随机一首歌曲") +@arcaea.command(name="random", description="Random a song.") async def random(ctx: discord.ApplicationContext): await slash_parser(ctx, "random") -rank = arcaea.create_subgroup("rank", "查询arcaea日排行榜的相关信息") +rank = arcaea.create_subgroup("rank", "View the current daily rank of Arcaea songs.") -@rank.command(description="查询arcaea免费包当前日排行榜") +@rank.command(name="free", description="View the current rank of the free packs.") async def free(ctx: discord.ApplicationContext): await slash_parser(ctx, "rank free") -@rank.command(description="查询arcaea收费包当前日排行榜") +@rank.command(name="paid", description="View the current rank of the paid packs.") async def paid(ctx: discord.ApplicationContext): await slash_parser(ctx, "rank paid") diff --git a/bots/discord/slash/ask.py b/bots/discord/slash/ask.py new file mode 100644 index 0000000000..143c3e5efe --- /dev/null +++ b/bots/discord/slash/ask.py @@ -0,0 +1,10 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +@client.slash_command(name="ask", description="Answer your question via ChatGPT.") +@discord.option(name="question", description="Ask ChatGPT.") +async def ask(ctx: discord.ApplicationContext, question: str): + await slash_parser(ctx, question) \ No newline at end of file diff --git a/bots/discord/slash/bug.py b/bots/discord/slash/bug.py deleted file mode 100644 index a44b9162ef..0000000000 --- a/bots/discord/slash/bug.py +++ /dev/null @@ -1,15 +0,0 @@ -import discord - -from bots.discord.client import client -from bots.discord.slash_parser import slash_parser - - -async def auto_search(ctx: discord.AutocompleteContext): - if ctx.options["mojiraid"] == '': - return ['MC-', 'MCPE-', 'MCD-', 'MCL-', 'REALMS-', 'WEB-', 'MCCE-'] - - -@client.command(description="查询一个已记录在Mojira上的bug信息") -@discord.option(name="mojiraid", autocomplete=auto_search) -async def bug(ctx: discord.ApplicationContext, mojiraid: str): - await slash_parser(ctx, mojiraid) diff --git a/bots/discord/slash/bugtracker.py b/bots/discord/slash/bugtracker.py new file mode 100644 index 0000000000..10db5cdf8e --- /dev/null +++ b/bots/discord/slash/bugtracker.py @@ -0,0 +1,15 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +async def auto_search(ctx: discord.AutocompleteContext): + if not ctx.options["mojiraid"]: + return ['BDS-', 'MCPE-', 'MCD-', 'MCL-', 'MCLG-', 'REALMS-', 'MC-', 'WEB-'] + + +@client.command(name="bugtracker", description="Query the corresponding ticket on Mojira.") +@discord.option(name="mojiraid", autocomplete=auto_search) +async def bugtracker(ctx: discord.ApplicationContext, mojiraid: str): + await slash_parser(ctx, mojiraid) diff --git a/bots/discord/slash/color.py b/bots/discord/slash/color.py new file mode 100644 index 0000000000..d46ac39d95 --- /dev/null +++ b/bots/discord/slash/color.py @@ -0,0 +1,16 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +async def auto_complete(ctx: discord.AutocompleteContext): + if not ctx.options["color"]: + return ['#123456', 'rgb(12,34,56)', 'hsl(123,45%,67%)'] + + +@client.slash_command(name="color", description="Get color information.") +@discord.option(name="color", default="", autocomplete=auto_complete, + description="Color information. Support for Hex, RGB, HSL color code, or name in CSS and Material Design.") +async def color(ctx: discord.ApplicationContext, color: str): + await slash_parser(ctx, color) diff --git a/bots/discord/slash/core.py b/bots/discord/slash/core.py new file mode 100644 index 0000000000..2b508febc0 --- /dev/null +++ b/bots/discord/slash/core.py @@ -0,0 +1,190 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser +from core.loader import ModulesManager +from core.utils.i18n import get_available_locales + +async def auto_get_module_list(ctx: discord.AutocompleteContext): + module_list = ModulesManager.return_modules_list() + module_ = [] + for x in module_list: + if x[0] == '_': + continue + if module_list[x].required_superuser or module_list[x].required_base_superuser: + continue + module_.append(module_list[x]) + return module_ + + +async def auto_get_lang(ctx: discord.AutocompleteContext): + if not ctx.options["lang"]: + return get_available_locales() + + +@client.slash_command(name="locale", description="Set the bot running languages.") +@discord.option(name="lang", default="", description="Supported language codes.", autocomplete=auto_get_lang) +async def locale(ctx: discord.ApplicationContext, lang: str): + await slash_parser(ctx, lang) + + +@client.slash_command(name="mute", description="Make the bot stop sending message.") +async def mute(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +@client.slash_command(name="ping", description="Get bot status.") +async def ping(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +@client.slash_command(name="petal", description="Get the number of petals in the current channel.") +async def petal(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +@client.slash_command(name="version", description="View bot version.") +async def version(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +@client.slash_command(name="whoami", description="Get the ID of the user account that sent the command inside the bot.") +async def whoami(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +admin = client.create_group("admin", "Commands available to bot administrators.") + + +@admin.command(name="add", description="Set members as bot administrators.") +@discord.option(name="userid", description="The user ID.") +async def add(ctx: discord.ApplicationContext, userid: str): + await slash_parser(ctx, f"add {userid}") + + +@admin.command(name="remove", description="Remove bot administrator from member.") +@discord.option(name="userid", description="The user ID.") +async def remove(ctx: discord.ApplicationContext, userid: str): + await slash_parser(ctx, f"remove {userid}") + + +@admin.command(name="list", description="View all bot administrators.") +async def lst(ctx: discord.ApplicationContext): + await slash_parser(ctx, "list") + + +@admin.command(name="ban", description="Limit someone to use bot in the channel.") +@discord.option(name="userid", description="The user ID.") +async def ban(ctx: discord.ApplicationContext, userid: str): + await slash_parser(ctx, f"ban {userid}") + + +@admin.command(name="unban", description="Remove limit someone to use bot in the channel.") +@discord.option(name="userid", description="The user ID.") +async def unban(ctx: discord.ApplicationContext, userid: str): + await slash_parser(ctx, f"unban {userid}") + + +ali = client.create_group("alias", "Set custom command alias.") + + +@ali.command(name="add", description="Add custom command alias.") +@discord.option(name="alias", description="The custom alias.") +@discord.option(name="command", description="The command you want to refer to.") +async def add(ctx: discord.ApplicationContext, alias: str, command: str): + await slash_parser(ctx, f"add {alias} {command}") + + +@ali.command(name="remove", description="Remove custom command alias.") +@discord.option(name="alias", description="The custom alias.") +async def remove(ctx: discord.ApplicationContext, alias: str): + await slash_parser(ctx, f"remove {alias}") + + +@ali.command(name="list", description="View custom command alias.") +@discord.option(name="legacy", choices=['false', 'true'], description="Whether to use legacy mode.") +async def lst(ctx: discord.ApplicationContext, legacy: str): + legacy = "legacy" if legacy == "true" else "" + await slash_parser(ctx, f"list {legacy}") + + +@ali.command(name="reset", description="Reset custom command alias.") +async def reset(ctx: discord.ApplicationContext): + await slash_parser(ctx, "reset") + + +hlp = client.create_group("help", "Get bot help.") + + +@hlp.command(name="list", description="View help list.") +@discord.option(name="legacy", choices=['false', 'true'], description="Whether to use legacy mode.") +async def lst(ctx: discord.ApplicationContext, legacy: str): + legacy = "legacy" if legacy == "true" else "" + await slash_parser(ctx, legacy) + + +@hlp.command(name="detail", description="View details of a module.") +@discord.option(name="module", description="The module you want to know about.", autocomplete=auto_get_module_list) +async def detail(ctx: discord.ApplicationContext, module: str): + await slash_parser(ctx, module) + + +m = client.create_group("module", "Set about modules.") + + +@m.command(name="enable", description="Enable module(s).") +@discord.option(name="module", description="The modules you want to enable.", autocomplete=auto_get_module_list) +async def add(ctx: discord.ApplicationContext, module: str): + await slash_parser(ctx, f"enable {module}") + + +@m.command(name="disable", description="Disable module(s).") +@discord.option(name="module", description="The modules you want to disable.", autocomplete=auto_get_module_list) +async def add(ctx: discord.ApplicationContext, module: str): + await slash_parser(ctx, f"disable {module}") + + +@m.command(name="list", description="View all available modules.") +@discord.option(name="legacy", choices=['false', 'true'], description="Whether to use legacy mode.") +async def lst(ctx: discord.ApplicationContext, legacy: str): + legacy = "legacy" if legacy == "true" else "" + await slash_parser(ctx, f"list {legacy}") + + +p = client.create_group("prefix", "Set custom command prefix.") + + +@p.command(name="add", description="Add custom command prefix.") +@discord.option(name="prefix", description="The custom prefix.") +async def add(ctx: discord.ApplicationContext, prefix: str): + await slash_parser(ctx, f"add {prefix}") + + +@p.command(name="remove", description="Remove custom command prefix.") +@discord.option(name="prefix", description="The custom prefix.") +async def remove(ctx: discord.ApplicationContext, prefix: str): + await slash_parser(ctx, f"remove {prefix}") + + +@p.command(name="list", description="View custom command prefix.") +async def lst(ctx: discord.ApplicationContext): + await slash_parser(ctx, "list") + + +@p.command(name="reset", description="Reset custom command prefix.") +async def reset(ctx: discord.ApplicationContext): + await slash_parser(ctx, "reset") + + +setup = client.create_group("setup", "Set up bot actions.") + + +@setup.command(name="typing", description="Set up whether to display input prompts.") +async def typing(ctx: discord.ApplicationContext): + await slash_parser(ctx, "typing") + + +@setup.command(name="timeoffset", description="Set the time offset.") +@discord.option(name="offset", description="The timezone offset.") +async def offset(ctx: discord.ApplicationContext, offset: str): + await slash_parser(ctx, f"timeoffset {offset}") diff --git a/bots/discord/slash/cytoid.py b/bots/discord/slash/cytoid.py index 40545a528f..df5f276caa 100644 --- a/bots/discord/slash/cytoid.py +++ b/bots/discord/slash/cytoid.py @@ -3,30 +3,30 @@ from bots.discord.client import client from bots.discord.slash_parser import slash_parser -cytoid = client.create_group("cytoid", "查询Cytoid的相关信息") +cytoid = client.create_group("cytoid", "Query about Cytoid.") -@cytoid.command(description="查询Best30列表") +@cytoid.command(name="b30", description="Query the Best 30 list.") async def b30(ctx: discord.ApplicationContext): await slash_parser(ctx, "b30") -@cytoid.command(description="查询Recent30列表") +@cytoid.command(name="r30", description="Query the Recent 30 list.") async def r30(ctx: discord.ApplicationContext): await slash_parser(ctx, "r30") -@cytoid.command(description="查询个人信息") +@cytoid.command(name="profile", description="Query user profile.") async def profile(ctx: discord.ApplicationContext): await slash_parser(ctx, "profile") -@cytoid.command(description="绑定用户") -@discord.option(name="username", description="用户名") +@cytoid.command(name="bind", description="Bind user.") +@discord.option(name="username", description="Your Cytoid username.") async def bind(ctx: discord.ApplicationContext, username: str): await slash_parser(ctx, f"bind {username}") -@cytoid.command(description="取消绑定用户") +@cytoid.command(name="unbind", description="Unbind user.") async def unbind(ctx: discord.ApplicationContext): await slash_parser(ctx, "unbind") diff --git a/bots/discord/slash/dice.py b/bots/discord/slash/dice.py new file mode 100644 index 0000000000..dc6961f9a8 --- /dev/null +++ b/bots/discord/slash/dice.py @@ -0,0 +1,24 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +async def auto_complete(ctx: discord.AutocompleteContext): + if not ctx.options["dices"]: + return ['d4', 'd6', 'd8', 'd12', 'd20'] + + +dice = client.create_group("dice", "Random dice.") + + +@dice.command(name="roll", description="Roll the specified dice.") +@discord.option(name="dices", autocomplete=auto_complete, description="Dice expression.") +@discord.option(name="dc", default="", description="Difficulty class.") +async def roll(ctx: discord.ApplicationContext, dices: str, dc: str): + await slash_parser(ctx, f'{dices} {dc}') + + +@dice.command(name="rule", description="Modify the checking rule of dc.") +async def rule(ctx: discord.ApplicationContext): + await slash_parser(ctx, 'rule') diff --git a/bots/discord/slash/dictionary.py b/bots/discord/slash/dictionary.py deleted file mode 100644 index b9e10f7b97..0000000000 --- a/bots/discord/slash/dictionary.py +++ /dev/null @@ -1,10 +0,0 @@ -import discord - -from bots.discord.client import client -from bots.discord.slash_parser import slash_parser - - -@client.slash_command(description="查询柯林斯词典", name='dict') -@discord.option(name="word", description="词汇") -async def _(ctx: discord.ApplicationContext, word: str): - await slash_parser(ctx, word) diff --git a/bots/discord/slash/exchange_rate.py b/bots/discord/slash/exchange_rate.py new file mode 100644 index 0000000000..061e5228f1 --- /dev/null +++ b/bots/discord/slash/exchange_rate.py @@ -0,0 +1,11 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + +@client.slash_command(name="exchange_rate", description="Convert currency prices according to the exchange rate of the day.") +@discord.option(name="amount", description="The amount of base currency.") +@discord.option(name="base", description="The base currency unit.") +@discord.option(name="target", description="The target currency unit.") +async def excr(ctx: discord.ApplicationContext, amount: float, base: str, target: str): + await slash_parser(ctx, f"{amount}{base} {target}") diff --git a/bots/discord/slash/github.py b/bots/discord/slash/github.py index 86c8b441e2..27fd349bcb 100644 --- a/bots/discord/slash/github.py +++ b/bots/discord/slash/github.py @@ -3,16 +3,29 @@ from bots.discord.client import client from bots.discord.slash_parser import slash_parser -github = client.create_group("github", "查询Github的相关信息") +github = client.create_group("github", "Github query tool.") -@github.command() -@discord.option(name="username_or_repo", description="用户名或仓库名") -async def get(ctx: discord.ApplicationContext, username_or_repo: str): - await slash_parser(ctx, username_or_repo) +@github.command(name="get", description="Trying to automatically identifying and distinguishing repo/user.") +@discord.option(name="name", description="GitHub user or repository name.") +async def get(ctx: discord.ApplicationContext, name: str): + await slash_parser(ctx, name) -@github.command() -@discord.option(name="keyword", description="搜索关键词") -async def search(ctx: discord.ApplicationContext, keyword: str): + +@github.command(name="repo", description="Get GitHub repository information.") +@discord.option(name="name", description="GitHub repository name.") +async def repo(ctx: discord.ApplicationContext, name: str): + await slash_parser(ctx, f'repo {name}') + + +@github.command(name="user", description="Get GitHub user or organization information.") +@discord.option(name="name", description="GitHub user name.") +async def user(ctx: discord.ApplicationContext, name: str): + await slash_parser(ctx, f'user {name}') + + +@github.command(name="search", description="Search repositories on GitHub.") +@discord.option(name="query", description="Search keywords.") +async def search(ctx: discord.ApplicationContext, query: str): await slash_parser(ctx, f'search {keyword}') diff --git a/bots/discord/slash/idlist.py b/bots/discord/slash/idlist.py deleted file mode 100644 index acb5fecc2f..0000000000 --- a/bots/discord/slash/idlist.py +++ /dev/null @@ -1,28 +0,0 @@ -import urllib.parse - -import discord - -from bots.discord.client import client -from bots.discord.slash_parser import slash_parser -from core.utils.http import get_url - -api = 'https://ca.projectxero.top/idlist/search' - - -async def auto_search(ctx: discord.AutocompleteContext): - title = ctx.options["keywords"] - query_options = {'q': title, 'limit': '5'} - query_url = api + '?' + urllib.parse.urlencode(query_options) - resp = await get_url(query_url, 200, fmt='json') - result_ = resp['data']['result'] - results = [title] - if result_: - for x in result_: - results.append(f'{x["enumName"]} {x["key"]}') - return results - - -@client.slash_command(description="查询MCBEID表") -@discord.option(name="keywords", description="关键词", autocomplete=auto_search) -async def idlist(ctx: discord.ApplicationContext, keywords: str): - await slash_parser(ctx, keywords) diff --git a/bots/discord/slash/ip.py b/bots/discord/slash/ip.py new file mode 100644 index 0000000000..a6ccce477f --- /dev/null +++ b/bots/discord/slash/ip.py @@ -0,0 +1,10 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +@client.slash_command(name="ip", description="Query the information of IP.") +@discord.option(name="ip_address", description="The IP address.") +async def ip(ctx: discord.ApplicationContext, ip_address: str): + await slash_parser(ctx, ip_address) diff --git a/bots/discord/slash/mcplayer.py b/bots/discord/slash/mcplayer.py index 7be0d94850..665f5a1823 100644 --- a/bots/discord/slash/mcplayer.py +++ b/bots/discord/slash/mcplayer.py @@ -4,7 +4,7 @@ from bots.discord.slash_parser import slash_parser -@client.slash_command(description="获取一个Minecraft玩家的信息") -@discord.option(name="player", description="玩家名") -async def mcplayer(ctx: discord.ApplicationContext, player: str): - await slash_parser(ctx, player) +@client.slash_command(name="mcplayer", description="Get Minecraft player information.") +@discord.option(name="username_or_uuid", description="The name or UUID of Minecraft player.") +async def mcplayer(ctx: discord.ApplicationContext, username_or_uuid: str): + await slash_parser(ctx, username_or_uuid) diff --git a/bots/discord/slash/mcv.py b/bots/discord/slash/mcv.py index f1a0fbbf7e..bd00978845 100644 --- a/bots/discord/slash/mcv.py +++ b/bots/discord/slash/mcv.py @@ -4,21 +4,26 @@ from bots.discord.slash_parser import slash_parser -@client.slash_command(description="获取Minecraft Java版的最新版本信息") +@client.slash_command(name="mcv", description="Get the latest version of Minecraft: Java Edition in the launcher.") async def mcv(ctx: discord.ApplicationContext): await slash_parser(ctx, '') -@client.slash_command(description="获取Minecraft基岩版的最新版本信息") +@client.slash_command(name="mcbv", description="Get the latest version of Minecraft: Bedrock Edition on Mojira.") async def mcbv(ctx: discord.ApplicationContext): await slash_parser(ctx, '') -@client.slash_command(description="获取Minecraft Dungeons的最新版本信息") +@client.slash_command(name="mcdv", description="Get the latest version of Minecraft Dungeons on Mojira.") async def mcdv(ctx: discord.ApplicationContext): await slash_parser(ctx, '') -@client.slash_command(description="获取Minecraft教育版的最新版本信息") +@client.slash_command(name="mcev", description="Get the latest version of Minecraft: Education Edition in Windows Edition.") async def mcev(ctx: discord.ApplicationContext): await slash_parser(ctx, '') + + +@client.slash_command(name="mclgv", description="Get the latest version of Minecraft Legends on Mojira.") +async def mclgv(ctx: discord.ApplicationContext): + await slash_parser(ctx, '') \ No newline at end of file diff --git a/bots/discord/slash/server.py b/bots/discord/slash/server.py index b3dbb12b04..8846884d73 100644 --- a/bots/discord/slash/server.py +++ b/bots/discord/slash/server.py @@ -10,7 +10,7 @@ async def auto_search(ctx: discord.AutocompleteContext): return [ctx.options["address"]] -@client.slash_command(description="获取一个Minecraft服务器的信息") -@discord.option(name="address", description="服务器地址", autocomplete=auto_search) +@client.slash_command(name="server", description="Get Minecraft: Java/Bedrock Edition server motd.") +@discord.option(name="address", description="The server address.", autocomplete=auto_search) async def server(ctx: discord.ApplicationContext, address: str): await slash_parser(ctx, address) diff --git a/bots/discord/slash/tweet.py b/bots/discord/slash/tweet.py index e6ce7841c6..ff037e8845 100644 --- a/bots/discord/slash/tweet.py +++ b/bots/discord/slash/tweet.py @@ -3,10 +3,7 @@ from bots.discord.client import client from bots.discord.slash_parser import slash_parser -tweet = client.create_group("tweet", "获取推文摘要") - - -@tweet.command() -@discord.option(name="tweetid", description="推文ID") -async def get(ctx: discord.ApplicationContext, tweetid: str): - await slash_parser(ctx, tweetid) +@client.slash_command(name="tweet", description="Get tweet image from tweet ID or link.") +@discord.option(name="tweetid", description="The tweet ID or tweet link.") +async def tweet(ctx: discord.ApplicationContext, tweetid: str): + await slash_parser(ctx, tweet) diff --git a/bots/discord/slash/whois.py b/bots/discord/slash/whois.py new file mode 100644 index 0000000000..f58af5533d --- /dev/null +++ b/bots/discord/slash/whois.py @@ -0,0 +1,10 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +@client.slash_command(name="whois", description="Query the information of WHOIS.") +@discord.option(name="domain", description="The domain.") +async def whois(ctx: discord.ApplicationContext, domain: str): + await slash_parser(ctx, domain) diff --git a/bots/discord/slash/wiki.py b/bots/discord/slash/wiki.py index 780f73b1bf..d2df9cf9ab 100644 --- a/bots/discord/slash/wiki.py +++ b/bots/discord/slash/wiki.py @@ -6,7 +6,23 @@ from bots.discord.slash_parser import slash_parser, ctx_to_session from modules.wiki import WikiLib, WikiTargetInfo -wiki = client.create_group("wiki", "查询Mediawiki的相关信息") + +@client.slash_command(description="Get recent abuse logs for the default wiki.") +async def ab(ctx: discord.ApplicationContext): + await slash_parser(ctx, "legacy") + + +@client.slash_command(description="Get recent newbie logs for the default wiki.") +async def newbie(ctx: discord.ApplicationContext): + await slash_parser(ctx, "") + + +@client.slash_command(description="Get recent changes for the default wiki.") +async def rc(ctx: discord.ApplicationContext): + await slash_parser(ctx, "legacy") + + +wiki = client.create_group("wiki", "Query information from Mediawiki-based websites.") async def auto_search(ctx: discord.AutocompleteContext): @@ -20,7 +36,7 @@ async def auto_search(ctx: discord.AutocompleteContext): query_wiki = iws[match_iw.group(1)] iw = match_iw.group(1) + ':' title = match_iw.group(2) - if query_wiki is None: + if not query_wiki: return [] wiki = WikiLib(query_wiki) if title != "": @@ -39,87 +55,107 @@ async def auto_get_custom_iw_list(ctx: discord.AutocompleteContext): async def default_wiki(ctx: discord.AutocompleteContext): - if ctx.options["link"] == '': - return ['https://minecraft.fandom.com/zh/'] + if not ctx.options["link"]: + return ['https://zh.minecraft.wiki/'] -@wiki.command(description="根据页面名称查询一个wiki页面") -@discord.option(name="title", description="页面名称", autocomplete=auto_search) -async def query(ctx: discord.ApplicationContext, title: str): - await slash_parser(ctx, title) +@wiki.command(name="query", description="Query a wiki page.") +@discord.option(name="pagename", description="The title of wiki page.", autocomplete=auto_search) +async def query(ctx: discord.ApplicationContext, pagename: str): + await slash_parser(ctx, pagename) -@wiki.command(description="根据页面名称搜索一个wiki页面") -@discord.option(name="title", description="页面名称") -async def search(ctx: discord.ApplicationContext, title: str): - await slash_parser(ctx, f'search {title}') +@wiki.command(name="id", description="Query a Wiki page based on page ID.") +@discord.option(name="pageid", description="The wiki page ID.") +async def byid(ctx: discord.ApplicationContext, pageid: str): + await slash_parser(ctx, f'id {pageid}') -@wiki.command(name="id", description="根据页面ID查询一个wiki页面") -@discord.option(name="pid", description="页面ID") -async def page_id(ctx: discord.ApplicationContext, pid: str): - await slash_parser(ctx, f'-p {pid}') +@wiki.command(name="search", description="Search a wiki page.") +@discord.option(name="pagename", description="The title of wiki page.", autocomplete=auto_search) +async def search(ctx: discord.ApplicationContext, pagename: str): + await slash_parser(ctx, f'search {pagename}') -@wiki.command(name="set", description="设置起始查询wiki") -@discord.option(name="link", description="页面链接", autocomplete=default_wiki) -async def set_base(ctx: discord.ApplicationContext, link: str): - await slash_parser(ctx, f'set {link}') +@wiki.command(name="set", description="Set up start wiki.") +@discord.option(name="wikiurl", description="The URL of wiki.", autocomplete=default_wiki) +async def set_base(ctx: discord.ApplicationContext, wikiurl: str): + await slash_parser(ctx, f'set {wikiurl}') -iw = wiki.create_subgroup("iw", "设置有关自定义Interwiki的命令") +iw = wiki.create_subgroup("iw", "Set up commands for custom Interwiki.") -@iw.command(description="添加自定义Interwiki") -@discord.option(name="interwiki", description="自定义iw名") -@discord.option(name="link", description="页面链接") -async def add(ctx: discord.ApplicationContext, iw: str, link: str): - await slash_parser(ctx, f'iw add {iw} {link}') +@iw.command(name="add", description="Add custom Interwiki.") +@discord.option(name="interwiki", description="The custom Interwiki.") +@discord.option(name="wikiurl", description="The URL of wiki.") +async def add(ctx: discord.ApplicationContext, interwiki: str, wikiurl: str): + await slash_parser(ctx, f'iw add {interwiki} {wikiurl}') -@iw.command(name='remove', description="删除自定义Interwiki") -@discord.option(name="iw", description="自定义iw名", autocomplete=auto_get_custom_iw_list) -async def iw_remove(ctx: discord.ApplicationContext, iw: str): - await slash_parser(ctx, f'iw rm {iw}') +@iw.command(name="remove", description="Remove custom Interwiki.") +@discord.option(name="interwiki", description="The custom Interwiki.", autocomplete=auto_get_custom_iw_list) +async def iwremove(ctx: discord.ApplicationContext, interwiki: str): + await slash_parser(ctx, f'iw remove {interwiki}') -@iw.command(name="list", description="查看所有已自定义的Interwiki") -async def iw_list(ctx: discord.ApplicationContext): - await slash_parser(ctx, 'iw list') +@iw.command(name="list", description="Lists the currently configured Interwiki.") +@discord.option(name="legacy", choices=['false', 'true'], description="Whether to use legacy mode.") +async def iw_list(ctx: discord.ApplicationContext, legacy: str): + legacy = "legacy" if legacy == "true" else "" + await slash_parser(ctx, f'iw list {legacy}') -@iw.command(description="获取自定义Interwiki的链接") -@discord.option(name="iw", description="自定义interwiki名", autocomplete=auto_get_custom_iw_list) -async def get(ctx: discord.ApplicationContext, iw: str): - await slash_parser(ctx, f'iw get {iw}') +@iw.command(name="get", description="Get the API address corresponding to the set Interwiki.") +@discord.option(name="interwiki", description="The custom Interwiki.", autocomplete=auto_get_custom_iw_list) +async def get(ctx: discord.ApplicationContext, interwiki: str): + await slash_parser(ctx, f'iw get {interwiki}') -headers = wiki.create_subgroup("headers", "设置有关自定义header的命令") +headers = wiki.create_subgroup("headers", "Set up commands for custom response headers.") -@headers.command(name="set", description="添加自定义headers") -@discord.option(name="headers_json", description="自定义headers") -async def set_headers(ctx: discord.ApplicationContext, headers_json: str): - await slash_parser(ctx, f'headers set {headers_json}') +@headers.command(name="add", description="Add custom request headers.") +@discord.option(name="headers", description="The json of custom request headers.") +async def add_headers(ctx: discord.ApplicationContext, headers: str): + await slash_parser(ctx, f'headers set {headers}') -@headers.command(description="删除一个自定义header") -@discord.option(name="header_key") -async def delete(ctx: discord.ApplicationContext, header_key: str): - await slash_parser(ctx, f'headers del {header_key}') +@headers.command(name="remove", description="Remove custom request headers.") +@discord.option(name="headerkey", description="The key of custom request headers json.") +async def set_headers(ctx: discord.ApplicationContext, headerkey: str): + await slash_parser(ctx, f'headers remove {headerkey}') -@headers.command(name='show', description="查看所有自定义的headers") -async def headers_show(ctx: discord.ApplicationContext): +@headers.command(name='show', description="View the currently set request headers.") +async def show_headers(ctx: discord.ApplicationContext): await slash_parser(ctx, 'headers show') -@headers.command(name='reset', description="重置所有自定义的headers") -async def headers_reset(ctx: discord.ApplicationContext): +@headers.command(name='reset', description="Reset custom request headers.") +async def reset_headers(ctx: discord.ApplicationContext): await slash_parser(ctx, 'headers reset') -@wiki.command(description="是否启用Fandom全局Interwiki查询") -@discord.option(name="_", choices=['enable', 'disable']) -async def fandom(ctx: discord.ApplicationContext, _: str): - await slash_parser(ctx, f'fandom {_}') +p = wiki.create_subgroup("prefix", "Set up commands for custom wiki prefix.") + + +@p.command(name="set", description="Set custom wiki prefix.") +@discord.option(name="prefix", description="The custom wiki prefix.") +async def set_prefix(ctx: discord.ApplicationContext, prefix: str): + await slash_parser(ctx, f'prefix set {prefix}') + + +@p.command(name="reset", description="Reset custom wiki prefix.") +async def reset_prefix(ctx: discord.ApplicationContext): + await slash_parser(ctx, 'prefix reset') + + +@wiki.command(name="fandom", description="Toggle whether to use Fandom global Interwiki queries.") +async def fandom(ctx: discord.ApplicationContext): + await slash_parser(ctx, 'fandom') + + +@wiki.command(name="redlink", description="Toggle whether to return the edit link when the page does not exist.") +async def redlink(ctx: discord.ApplicationContext): + await slash_parser(ctx, 'redlink') diff --git a/bots/discord/slash/wolframalpha.py b/bots/discord/slash/wolframalpha.py new file mode 100644 index 0000000000..768cf2da26 --- /dev/null +++ b/bots/discord/slash/wolframalpha.py @@ -0,0 +1,18 @@ +import discord + +from bots.discord.client import client +from bots.discord.slash_parser import slash_parser + + +wolframalpha = client.create_group("wolframalpha", "Use WolframAlpha.") + +@wolframalpha.command(name="query", description="Input a question or formula to search for WolframAlpha.") +@discord.option(name="query", description="Enter what you want to calculate.") +async def query(ctx: discord.ApplicationContext, query: str): + await slash_parser(ctx, query) + + +@wolframalpha.command(name="ask", description="Answer the question via WolframAlpha.") +@discord.option(name="question", description="Ask WolframAlpha.") +async def ask(ctx: discord.ApplicationContext, question: str): + await slash_parser(ctx, f"ask {question}") \ No newline at end of file diff --git a/bots/discord/slash_message.py b/bots/discord/slash_message.py index f850234a06..49bebb2ac0 100644 --- a/bots/discord/slash_message.py +++ b/bots/discord/slash_message.py @@ -4,7 +4,7 @@ from bots.discord.message import convert_embed from config import Config -from core.builtins import Plain, Image, MessageSession as MessageSessionT +from core.builtins import Plain, Image, MessageSession as MessageSessionT, MessageTaskManager from core.builtins.message.chain import MessageChain from core.builtins.message.internal import Embed, ErrorMessage from core.logger import Logger @@ -37,7 +37,8 @@ class Feature: quote = False 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: @@ -67,7 +68,7 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal send_ = await self.session.message.send(embed=embeds) Logger.info(f'[Bot] -> [{self.target.target_id}]: Embed: {str(x.__dict__)}') else: - send_ = False + send_ = None if send_: send.append(send_) count += 1 @@ -75,6 +76,8 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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) diff --git a/bots/kook/message.py b/bots/kook/message.py index 2f3f8e580f..7febb27d91 100644 --- a/bots/kook/message.py +++ b/bots/kook/message.py @@ -9,7 +9,7 @@ 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 Feature: 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 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): @@ -184,7 +186,7 @@ async def send_direct_message(self, message_chain, disable_secret_check=False, a message_chain = MessageChain(message_chain) - for x in message_chain.as_sendable(self, embed=False): + for x in message_chain.as_sendable(self.parent, embed=False): if isinstance(x, Plain): await get_channel.send(x.text) @@ -232,14 +234,16 @@ async def fetch_target_list(target_list: list) -> List[Bot.FetchedSession]: @staticmethod async def post_message(module_name, message, user_list: List[Bot.FetchedSession] = None, i18n=False, **kwargs): - if user_list is not None: + if user_list: for x in user_list: try: - if i18n: - await x.send_direct_message(x.parent.locale.t(message, **kwargs)) - - else: - await x.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(x.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await x.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(x).add('', module_name, 'schedule') except Exception: @@ -250,11 +254,13 @@ async def post_message(module_name, message, user_list: List[Bot.FetchedSession] fetch = await FetchTarget.fetch_target(x.targetId) if fetch: try: - if i18n: - await fetch.send_direct_message(fetch.parent.locale.t(message, **kwargs)) - - else: - await fetch.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(fetch.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await fetch.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(fetch).add('', module_name, 'schedule') except Exception: diff --git a/bots/lagrange/message.py b/bots/lagrange/message.py index 63fdca39ea..0fd62f7ef1 100644 --- a/bots/lagrange/message.py +++ b/bots/lagrange/message.py @@ -14,7 +14,8 @@ 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 Feature: 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 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal msg.append({ "type": "text", "data": { - "text": x.text + "text": ('\n' if count != 0 else '') + x.text } }) elif isinstance(x, Image): @@ -122,7 +124,7 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): @@ -214,7 +218,7 @@ async def to_message_chain(self): spl = re.split(r'(\[CQ:.*?])', m) lst = [] for s in spl: - if s == '': + if not s: continue if s.startswith('[CQ:'): if s.startswith('[CQ:image'): @@ -350,7 +354,7 @@ async def post_(fetch_: Bot.FetchedSession): except Exception: Logger.error(traceback.format_exc()) - if user_list is not None: + if user_list: for x in user_list: await post_(x) else: diff --git a/bots/matrix/message.py b/bots/matrix/message.py index 1f974c4daa..e5657fe22d 100644 --- a/bots/matrix/message.py +++ b/bots/matrix/message.py @@ -9,7 +9,7 @@ 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 Feature: 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")))) @@ -82,7 +83,7 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal filename = os.path.basename(path) filesize = os.path.getsize(path) (content_type, content_encoding) = mimetypes.guess_type(path) - if content_type is None or content_encoding is None: + if not content_type or not content_encoding: content_type = 'image' content_encoding = 'png' mimetype = f"{content_type}/{content_encoding}" @@ -111,7 +112,7 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal filename = os.path.basename(path) filesize = os.path.getsize(path) (content_type, content_encoding) = mimetypes.guess_type(path) - if content_type is None or content_encoding is None: + if not content_type or not content_encoding: content_type = 'audio' content_encoding = 'ogg' mimetype = f"{content_type}/{content_encoding}" @@ -154,7 +155,9 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): @@ -163,7 +166,7 @@ async def check_native_permission(self): # https://spec.matrix.org/v1.7/client-server-api/#permissions power_levels = (await bot.room_get_state_event(self.session.target, 'm.room.power_levels')).content level = power_levels['users'][self.session.sender] if self.session.sender in power_levels['users'] else power_levels['users_default'] - if level is not None and int(level) >= 50: + if level and int(level) >= 50: return True return False @@ -181,7 +184,7 @@ async def to_message_chain(self): msgtype = 'm.text' if msgtype == 'm.text': # compatible with py38 text = str(content['body']) - if self.target.reply_id is not None: + if self.target.reply_id: # redact the fallback line for rich reply # https://spec.matrix.org/v1.7/client-server-api/#fallbacks-for-rich-replies while text.startswith('> '): @@ -314,14 +317,16 @@ async def fetch_target_list(target_list: list) -> List[FetchedSession]: @staticmethod async def post_message(module_name, message, user_list: List[FetchedSession] = None, i18n=False, **kwargs): - if user_list is not None: + if user_list: for x in user_list: try: - if i18n: - await x.send_direct_message(x.parent.locale.t(message, **kwargs)) - - else: - await x.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(x.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await x.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(x).add('', module_name, 'schedule') except Exception: @@ -332,10 +337,13 @@ async def post_message(module_name, message, user_list: List[FetchedSession] = N fetch = await FetchTarget.fetch_target(x.targetId) if fetch: try: - if i18n: - await fetch.send_direct_message(fetch.parent.locale.t(message, **kwargs)) - else: - await fetch.send_direct_message(message) + msgchain = message + if isinstance(message, str): + if i18n: + msgchain = MessageChain([Plain(fetch.parent.locale.t(message, **kwargs))]) + else: + msgchain = MessageChain([Plain(message)]) + await fetch.send_direct_message(msgchain) if enable_analytics: BotDBUtil.Analytics(fetch).add('', module_name, 'schedule') except Exception: diff --git a/config/config.toml.example b/config/config.toml.example index fe3c2a7cfb..a6dc0d10b2 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -48,7 +48,6 @@ qq_host = "127.0.0.1:11451" qq_invite_join_group_notice = true qq_disable_temp_session = false qq_enable_listening_self_message = false -qq_join_group_application_link = "https://github.com/Teahouse-Studios/bot/issues/new?assignees=OasisAkari&labels=New&template=add_new_group.yaml&title=%5BNEW%5D%3A+" allow_request_private_ip = false allow_bot_auto_approve_group_invite = false slower_schedule = false diff --git a/console.py b/console.py index 8deb5096e5..7f8cf6776b 100644 --- a/console.py +++ b/console.py @@ -3,7 +3,7 @@ from config import Config -if Config('db_path') is None: +if not Config('db_path'): raise AttributeError('Wait! You need to fill a valid database address into the config.cfg "db_path" field\n' 'Example: \ndb_path = sqlite:///database/save.db\n' '(Also you can fill in the above example directly,' @@ -26,7 +26,7 @@ from core.logger import Logger query_dbver = session.query(DBVersion).first() -if query_dbver is None: +if not query_dbver: session.add_all([DBVersion(value=str(BotDBUtil.database_version))]) session.commit() query_dbver = session.query(DBVersion).first() diff --git a/core/builtins/__init__.py b/core/builtins/__init__.py index feab461b74..0490d135ec 100644 --- a/core/builtins/__init__.py +++ b/core/builtins/__init__.py @@ -56,7 +56,7 @@ async def trigger(module_or_hook_name: str, args): if '.' in module_or_hook_name: hook_mode = True if not hook_mode: - if module_or_hook_name is not None: + if module_or_hook_name: modules = ModulesManager.modules if module_or_hook_name in modules: for hook in modules[module_or_hook_name].hooks_list.set: @@ -65,7 +65,7 @@ async def trigger(module_or_hook_name: str, args): raise ValueError("Invalid module name") else: - if module_or_hook_name is not None: + if module_or_hook_name: if module_or_hook_name in ModulesManager.modules_hooks: await asyncio.create_task(ModulesManager.modules_hooks[module_or_hook_name](Bot.FetchTarget, ModuleHookContext( @@ -76,9 +76,9 @@ async def trigger(module_or_hook_name: str, args): class FetchedSession(FetchedSessionT): def __init__(self, target_from, target_id, sender_from=None, sender_id=None): - if sender_from is None: + if not sender_from: sender_from = target_from - if sender_id is None: + if not sender_id: sender_id = target_id self.target = MsgInfo(target_id=f'{target_from}|{target_id}', sender_id=f'{sender_from}|{sender_id}', @@ -90,7 +90,7 @@ def __init__(self, target_from, target_id, sender_from=None, sender_id=None): reply_id=None) self.session = Session(message=False, target=target_id, sender=sender_id) self.parent = Bot.MessageSession(self.target, self.session) - if sender_id is not None: + if sender_id: self.parent.target.sender_info = BotDBUtil.SenderInfo(f'{sender_from}|{sender_id}') diff --git a/core/builtins/message/__init__.py b/core/builtins/message/__init__.py index d3aeca3c99..a93792e418 100644 --- a/core/builtins/message/__init__.py +++ b/core/builtins/message/__init__.py @@ -1,6 +1,6 @@ import asyncio from config import Config -from datetime import datetime, timedelta +from datetime import datetime from typing import List from core.builtins.message.chain import * @@ -35,20 +35,24 @@ def __init__(self, 'timezone_offset', Config('timezone_offset', '+8')) self.timezone_offset = parse_time_string(self._tz_offset) - async def wait_confirm(self, message_chain=None, quote=True, delete=True, append_instruction=True) -> bool: + async def wait_confirm(self, message_chain=None, quote=True, delete=True, timeout=120, append_instruction=True) \ + -> bool: send = None ExecutionLockList.remove(self) - if message_chain is not None: + if message_chain: message_chain = MessageChain(message_chain) if append_instruction: message_chain.append(Plain(self.locale.t("message.wait.confirm.prompt.type1"))) send = await self.send_message(message_chain, quote) flag = asyncio.Event() - MessageTaskManager.add_task(self, flag) - await flag.wait() + MessageTaskManager.add_task(self, flag, timeout=timeout) + try: + await asyncio.wait_for(flag.wait(), timeout=timeout) + except asyncio.TimeoutError: + raise WaitCancelException result = MessageTaskManager.get_result(self) - if result is not None: - if message_chain is not None and delete: + if result: + if message_chain and delete: await send.delete() if result.as_display(text_only=True) in confirm_command: return True @@ -58,54 +62,67 @@ async def wait_confirm(self, message_chain=None, quote=True, delete=True, append else: raise WaitCancelException - async def wait_next_message(self, message_chain=None, quote=True, delete=False, + async def wait_next_message(self, message_chain=None, quote=True, delete=False, timeout=120, append_instruction=True) -> (MessageSessionT, FinishedSession): sent = None ExecutionLockList.remove(self) - if message_chain is not None: + if message_chain: message_chain = MessageChain(message_chain) if append_instruction: message_chain.append(Plain(self.locale.t("message.wait.confirm.prompt.type2"))) sent = await self.send_message(message_chain, quote) flag = asyncio.Event() - MessageTaskManager.add_task(self, flag) - await flag.wait() + MessageTaskManager.add_task(self, flag, timeout=timeout) + try: + await asyncio.wait_for(flag.wait(), timeout=timeout) + except asyncio.TimeoutError: + raise WaitCancelException result = MessageTaskManager.get_result(self) - if delete and sent is not None: + if delete and sent: await sent.delete() - if result is not None: + if result: return (result, sent) else: raise WaitCancelException - async def wait_reply(self, message_chain, quote=True, all_=False, append_instruction=True) -> MessageSessionT: + async def wait_reply(self, message_chain, quote=True, delete=False, timeout=120, + all_=False, append_instruction=True) -> MessageSessionT: self.tmp['enforce_send_by_master_client'] = True + send = None ExecutionLockList.remove(self) message_chain = MessageChain(message_chain) if append_instruction: message_chain.append(Plain(self.locale.t("message.reply.prompt"))) send = await self.send_message(message_chain, quote) flag = asyncio.Event() - MessageTaskManager.add_task(self, flag, reply=send.message_id, all_=all_) - await flag.wait() + MessageTaskManager.add_task(self, flag, reply=send.message_id, all_=all_, timeout=timeout) + try: + await asyncio.wait_for(flag.wait(), timeout=timeout) + except asyncio.TimeoutError: + raise WaitCancelException result = MessageTaskManager.get_result(self) - if result is not None: + if delete and send: + await send.delete() + if result: return result else: raise WaitCancelException - async def wait_anyone(self, message_chain=None, delete=False) -> MessageSessionT: + async def wait_anyone(self, message_chain=None, quote=False, delete=False, timeout=120) -> MessageSessionT: send = None ExecutionLockList.remove(self) - if message_chain is not None: + if message_chain: message_chain = MessageChain(message_chain) - send = await self.send_message(message_chain, quote=False) + send = await self.send_message(message_chain, quote) flag = asyncio.Event() - MessageTaskManager.add_task(self, flag, all_=True) - await flag.wait() + MessageTaskManager.add_task(self, flag, all_=True, timeout=timeout) + try: + await asyncio.wait_for(flag.wait(), timeout=timeout) + except asyncio.TimeoutError: + raise WaitCancelException result = MessageTaskManager.get()[self.target.target_id]['all'][self] if 'result' in result: - if send is not None and delete: + if send and delete: await send.delete() return MessageTaskManager.get()[self.target.target_id]['all'][self]['result'] else: @@ -130,17 +147,21 @@ async def check_permission(self): checkPermission = check_permission checkSuperUser = check_super_user - def ts2strftime(self, timestamp: float, date=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 seconds: - ftime_template.append(self.locale.t("time.time.format")) - else: - ftime_template.append(self.locale.t("time.time.nosec.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")) + else: + 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)) diff --git a/core/builtins/message/chain.py b/core/builtins/message/chain.py index f685ff580b..6d24a24341 100644 --- a/core/builtins/message/chain.py +++ b/core/builtins/message/chain.py @@ -5,22 +5,22 @@ 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, I18NContext from core.builtins.utils import Secret from core.logger import Logger from core.types.message import MessageChain as MessageChainT, MessageSession class MessageChain(MessageChainT): - def __init__(self, elements: Union[str, List[Union[Plain, Image, Voice, Embed, Url, FormattedTime]], - Tuple[Union[Plain, Image, Voice, Embed, Url, FormattedTime]], - Plain, Image, Voice, Embed, Url, FormattedTime] = None): + def __init__(self, elements: Union[str, List[Union[Plain, Image, Voice, Embed, Url, FormattedTime, I18NContext]], + Tuple[Union[Plain, Image, Voice, Embed, Url, FormattedTime, I18NContext]], + Plain, Image, Voice, Embed, Url, FormattedTime, I18NContext] = None): self.value = [] if isinstance(elements, ErrorMessage): elements = str(elements) if isinstance(elements, str): elements = Plain(elements) - if isinstance(elements, (Plain, Image, Voice, Embed, Url, FormattedTime)): + if isinstance(elements, (Plain, Image, Voice, Embed, Url, FormattedTime, I18NContext)): if isinstance(elements, Plain): if elements.text != '': elements = match_kecode(elements.text) @@ -51,6 +51,13 @@ def __init__(self, elements: Union[str, List[Union[Plain, Image, Voice, Embed, U 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(I18NContext(e['data']['key'], **e['data']['kwargs'])) elif isinstance(e, str): if e != '': self.value += match_kecode(e) @@ -58,7 +65,7 @@ def __init__(self, elements: Union[str, List[Union[Plain, Image, Voice, Embed, U Logger.error(f'Unexpected message type: {elements}') elif isinstance(elements, MessageChain): self.value = elements.value - elif elements is None: + elif not elements: pass else: Logger.error(f'Unexpected message type: {elements}') @@ -80,23 +87,23 @@ def unsafeprompt(name, secret, text): for secret in Secret.list: if secret in ["", None, True, False]: continue - if v.title is not None: + if v.title: if v.title.upper().find(secret.upper()) != -1: Logger.warn(unsafeprompt('Embed.title', secret, v.title)) return False - if v.description is not None: + if v.description: if v.description.upper().find(secret.upper()) != -1: Logger.warn(unsafeprompt('Embed.description', secret, v.description)) return False - if v.footer is not None: + if v.footer: if v.footer.upper().find(secret.upper()) != -1: Logger.warn(unsafeprompt('Embed.footer', secret, v.footer)) return False - if v.author is not None: + if v.author: if v.author.upper().find(secret.upper()) != -1: Logger.warn(unsafeprompt('Embed.author', secret, v.author)) return False - if v.url is not None: + if v.url: if v.url.upper().find(secret.upper()) != -1: Logger.warn(unsafeprompt('Embed.url', secret, v.url)) return False @@ -124,6 +131,12 @@ def as_sendable(self, msg: MessageSession = None, embed=True): 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, I18NContext): + t_value = msg.locale.t(x.key, **x.kwargs) + if isinstance(t_value, str): + value.append(Plain(t_value)) + elif isinstance(t_value, list): + value += MessageChain(t_value).as_sendable(msg) else: value.append(x) if not value: @@ -171,7 +184,7 @@ def __repr__(self): def match_kecode(text: str) -> List[Union[Plain, Image, Voice, Embed]]: split_all = re.split(r'(\[Ke:.*?])', text) for x in split_all: - if x == '': + if not x: split_all.remove('') elements = [] for e in split_all: @@ -183,7 +196,7 @@ def match_kecode(text: str) -> List[Union[Plain, Image, Voice, Embed]]: element_type = match.group(1).lower() args = re.split(r',|,.\s', match.group(2)) for x in args: - if x == '': + if not x: args.remove('') if element_type == 'plain': for a in args: @@ -206,7 +219,7 @@ def match_kecode(text: str) -> List[Union[Plain, Image, Voice, Embed]]: img = Image(path=ma.group(2)) if ma.group(1) == 'headers': img.headers = json.loads(str(base64.b64decode(ma.group(2)), "UTF-8")) - if img is not None: + if img: elements.append(img) else: elements.append(Image(a)) diff --git a/core/builtins/message/internal.py b/core/builtins/message/internal.py index f0f4abee2e..83f99084ec 100644 --- a/core/builtins/message/internal.py +++ b/core/builtins/message/internal.py @@ -93,7 +93,23 @@ def __repr__(self): 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 I18NContext: + def __init__(self, key, **kwargs): + self.key = key + self.kwargs = kwargs + + def __str__(self): + return str(self.to_dict()) + + def __repr__(self): + return f'I18NContext(key="{self.key}", kwargs={self.kwargs})' + + def to_dict(self): + return {'type': 'i18n', 'data': {'key': self.key, 'kwargs': self.kwargs}} class ErrorMessage(EMsg): @@ -221,26 +237,26 @@ def __init__(self, def to_message_chain(self): text_lst = [] - if self.title is not None: + if self.title: text_lst.append(self.title) - if self.description is not None: + if self.description: text_lst.append(self.description) - if self.url is not None: + if self.url: text_lst.append(self.url) - if self.fields is not None: + if self.fields: for f in self.fields: if f.inline: text_lst.append(f"{f.name}: {f.value}") else: text_lst.append(f"{f.name}:\n{f.value}") - if self.author is not None: + if self.author: text_lst.append("作者:" + self.author) - if self.footer is not None: + if self.footer: text_lst.append(self.footer) message_chain = [] if text_lst: message_chain.append(Plain('\n'.join(text_lst))) - if self.image is not None: + if self.image: message_chain.append(self.image) return message_chain @@ -269,4 +285,4 @@ def to_dict(self): 'fields': self.fields}} -__all__ = ["Plain", "Image", "Voice", "Embed", "EmbedField", "Url", "ErrorMessage", "FormattedTime"] +__all__ = ["Plain", "Image", "Voice", "Embed", "EmbedField", "Url", "ErrorMessage", "FormattedTime", "I18NContext"] diff --git a/core/builtins/tasks.py b/core/builtins/tasks.py index 2a2a12683e..f3ccddae61 100644 --- a/core/builtins/tasks.py +++ b/core/builtins/tasks.py @@ -6,11 +6,12 @@ class MessageTaskManager: _list = {} + _callback_list = {} @classmethod - def add_task(cls, session: MessageSession, flag, all_=False, reply=None): + def add_task(cls, session: MessageSession, flag, all_=False, reply=None, timeout=120): sender = session.target.sender_id - task_type = 'reply' if reply is not None else 'wait' + task_type = 'reply' if reply else 'wait' if all_: sender = 'all' @@ -19,9 +20,14 @@ def add_task(cls, session: MessageSession, flag, all_=False, reply=None): if sender not in cls._list[session.target.target_id]: cls._list[session.target.target_id][sender] = {} cls._list[session.target.target_id][sender][session] = { - 'flag': flag, 'active': True, 'type': task_type, 'reply': reply, 'ts': datetime.now().timestamp()} + 'flag': flag, 'active': True, 'type': task_type, 'reply': reply, 'ts': datetime.now().timestamp(), + 'timeout': timeout} 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) -> MessageSession: if 'result' in cls._list[session.target.target_id][session.target.sender_id][session]: @@ -39,9 +45,13 @@ async def bg_check(cls): for sender in cls._list[target]: for session in cls._list[target][sender]: if cls._list[target][sender][session]['active']: - if datetime.now().timestamp() - cls._list[target][sender][session]['ts'] > 3600: + if (datetime.now().timestamp() - cls._list[target][sender][session]['ts'] > + cls._list[target][sender][session].get('timeout', 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): @@ -51,7 +61,7 @@ async def check(cls, session: MessageSession): senders.append(session.target.sender_id) if 'all' in cls._list[session.target.target_id]: senders.append('all') - if senders is not None: + if senders: for sender in senders: for s in cls._list[session.target.target_id][sender]: get_ = cls._list[session.target.target_id][sender][s] @@ -72,6 +82,8 @@ async def check(cls, session: MessageSession): 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'] diff --git a/core/component.py b/core/component.py index f33c302be4..1a36959a21 100644 --- a/core/component.py +++ b/core/component.py @@ -34,7 +34,7 @@ def decorator(function): help_doc = [help_doc] if help_docs: help_doc += help_docs - if help_doc is None: + if not help_doc: help_doc = [] ModulesManager.bind_to_module( diff --git a/core/console/message.py b/core/console/message.py index 2781ba4071..1bbfc4fa6f 100644 --- a/core/console/message.py +++ b/core/console/message.py @@ -30,7 +30,7 @@ class Feature: 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,35 +44,36 @@ async def send_message(self, message_chain, quote=True, disable_secret_check=Fal 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): + async def wait_confirm(self, message_chain=None, quote=True, delete=True, timeout=120, append_instruction=True): send = None - if message_chain is not None: + if message_chain: send = await self.send_message(message_chain) print(self.locale.t("message.wait.confirm.prompt.type1")) c = await aioconsole.ainput('Confirm: ') print(c) - if message_chain is not None and delete: + if message_chain and delete: await send.delete() if c in confirm_command: return True return False - async def wait_anyone(self, message_chain=None, quote=True, delete=True): + async def wait_anyone(self, message_chain=None, quote=True, delete=True, timeout=120): send = None - if message_chain is not None: + if message_chain: send = await self.send_message(message_chain) c = await aioconsole.ainput('Confirm: ') print(c) - if message_chain is not None and delete: + if message_chain and delete: await send.delete() self.session.message = c return self - async def wait_reply(self, message_chain, quote=True, all_=False, append_instruction=True): + async def wait_reply(self, message_chain, quote=True, delete=False, timeout=120, + all_=False, append_instruction=True): message_chain = MessageChain(message_chain) if append_instruction: message_chain.append(Plain(self.locale.t("message.reply.prompt"))) diff --git a/core/exceptions.py b/core/exceptions.py index 13dd7fa3da..538f9d0a14 100644 --- a/core/exceptions.py +++ b/core/exceptions.py @@ -32,3 +32,6 @@ class InvalidTemplatePattern(Exception): class NoReportException(Exception): pass + +class TestException(Exception): + pass diff --git a/core/extra/scheduler.py b/core/extra/scheduler.py index 30cf1e9e3f..7b2258845a 100644 --- a/core/extra/scheduler.py +++ b/core/extra/scheduler.py @@ -30,7 +30,7 @@ async def clear_queue(): elif os.path.isfile(file_path): if file_name[0] != '_' and file_name.endswith('.py'): fun_file = file_name[:-3] - if fun_file is not None: + if fun_file: Logger.debug(f'Loading schedulers.{fun_file}...') modules = 'schedulers.' + fun_file importlib.import_module(modules) diff --git a/core/loader/__init__.py b/core/loader/__init__.py index 6308e25a6d..26bbc7ec43 100644 --- a/core/loader/__init__.py +++ b/core/loader/__init__.py @@ -40,7 +40,7 @@ def load_modules(): elif os.path.isfile(file_path): if file_name[0] != '_' and file_name.endswith('.py'): fun_file = file_name[:-3] - if fun_file is not None: + if fun_file: Logger.debug(f'Loading modules.{fun_file}...') all_modules.append(fun_file) if fun_file in unloaded_modules: @@ -155,7 +155,7 @@ def bind_to_module(cls, bind_prefix: str, meta: Union[CommandMeta, RegexMeta, Sc @classmethod def return_modules_list(cls, target_from: str = None) -> \ Dict[str, Module]: - if target_from is not None: + if target_from: if target_from in cls._return_cache: return cls._return_cache[target_from] returns = {} diff --git a/core/parser/args.py b/core/parser/args.py index 671721488a..c77c06587c 100644 --- a/core/parser/args.py +++ b/core/parser/args.py @@ -130,7 +130,7 @@ def parse_template(argv: List[str]) -> List[Template]: patterns = filter(None, re.split(r'(\[.*?])|(<.*?>)|(\{.*})| ', a)) for p in patterns: strip_pattern = p.strip() - if strip_pattern == '': + if not strip_pattern: continue if strip_pattern.startswith('['): if not strip_pattern.endswith(']'): @@ -171,10 +171,10 @@ def templates_to_str(templates: List[Template], with_desc=False, simplify=True) sub_arg_text.append(arg.name) elif isinstance(arg, OptionalPattern): t = '[' - if arg.flag is not None: + if arg.flag: t += arg.flag if arg.args: - if arg.flag is not None: + if arg.flag: t += ' ' t += ' '.join(templates_to_str(arg.args, simplify=False)) t += ']' @@ -210,7 +210,7 @@ def parse_argv(argv: List[str], templates: List[Template]) -> MatchedResult: continue for a in args: # optional first if isinstance(a, OptionalPattern): - if a.flag is None: + if not a.flag: afters.append(a.args[0]) continue parsed_argv[a.flag] = Optional({}, flagged=False) diff --git a/core/parser/command.py b/core/parser/command.py index 0458b7a513..c949346b6c 100644 --- a/core/parser/command.py +++ b/core/parser/command.py @@ -21,17 +21,17 @@ def __init__(self, args: Module, command_prefixes: list, self.origin_template = args self.msg: Union[MessageSession, None] = msg self.options_desc = [] - self.lang = self.msg.locale if self.msg is not None else Locale(default_locale) + self.lang = self.msg.locale if self.msg else Locale(default_locale) help_docs = {} for match in ( - args.command_list.set if self.msg is None else args.command_list.get( + args.command_list.set if not self.msg else args.command_list.get( self.msg.target.target_from)): if match.help_doc: for m in match.help_doc: help_docs[m] = {'priority': match.priority, 'meta': match} else: help_docs[''] = {'priority': match.priority, 'meta': match} - if match.options_desc is not None: + if match.options_desc: for m in match.options_desc: desc = match.options_desc[m] if locale_str := re.findall(r'\{(.*)}', desc): @@ -64,7 +64,7 @@ def return_formatted_help_doc(self) -> str: return args def parse(self, command): - if self.args is None: + if not self.args: return None command = re.sub(r'[“”]', '"', command) try: diff --git a/core/parser/message.py b/core/parser/message.py index 59f8621317..41686ccf9f 100644 --- a/core/parser/message.py +++ b/core/parser/message.py @@ -31,13 +31,13 @@ async def remove_temp_ban(msg: Bot.MessageSession): is_temp_banned = temp_ban_counter.get(msg.target.sender_id) - if is_temp_banned is not None: + if is_temp_banned: del temp_ban_counter[msg.target.sender_id] async def tos_msg_counter(msg: Bot.MessageSession, command: str): same = counter_same.get(msg.target.sender_id) - if same is None or datetime.now().timestamp() - same['ts'] > 300 or same['command'] != command: + if not same or datetime.now().timestamp() - same['ts'] > 300 or same['command'] != command: # 检查是否滥用(5分钟内重复使用同一命令10条) counter_same[msg.target.sender_id] = {'command': command, 'count': 1, 'ts': datetime.now().timestamp()} @@ -46,7 +46,7 @@ async def tos_msg_counter(msg: Bot.MessageSession, command: str): if same['count'] > 10: raise AbuseWarning(msg.locale.t("tos.reason.cooldown")) all_ = counter_all.get(msg.target.sender_id) - if all_ is None or datetime.now().timestamp() - all_['ts'] > 300: # 检查是否滥用(5分钟内使用20条命令) + if not all_ or datetime.now().timestamp() - all_['ts'] > 300: # 检查是否滥用(5分钟内使用20条命令) counter_all[msg.target.sender_id] = {'count': 1, 'ts': datetime.now().timestamp()} else: @@ -58,7 +58,7 @@ async def tos_msg_counter(msg: Bot.MessageSession, command: str): async def temp_ban_check(msg: Bot.MessageSession): is_temp_banned = temp_ban_counter.get(msg.target.sender_id) is_superuser = msg.check_super_user() - if is_temp_banned is not None and not is_superuser: + if is_temp_banned and not is_superuser: ban_time = datetime.now().timestamp() - is_temp_banned['ts'] if ban_time < TOS_TEMPBAN_TIME: if is_temp_banned['count'] < 2: @@ -96,16 +96,16 @@ async def parser(msg: Bot.MessageSession, require_enable_modules: bool = True, p return msg.prefixes = command_prefix.copy() # 复制一份作为基础命令前缀 get_custom_alias = msg.options.get('command_alias') - if get_custom_alias is not None: + if get_custom_alias: get_display_alias = get_custom_alias.get(msg.trigger_msg) - if get_display_alias is not None: + if get_display_alias: msg.trigger_msg = get_display_alias get_custom_prefix = msg.options.get('command_prefix') # 获取自定义命令前缀 - if get_custom_prefix is not None: + if get_custom_prefix: msg.prefixes = get_custom_prefix + msg.prefixes # 混合 disable_prefix = False - if prefix is not None: # 如果上游指定了命令前缀,则使用指定的命令前缀 + if prefix: # 如果上游指定了命令前缀,则使用指定的命令前缀 if '' in prefix: disable_prefix = True msg.prefixes.clear() @@ -186,7 +186,7 @@ async def parser(msg: Bot.MessageSession, require_enable_modules: bool = True, p module: Module = modules[command_first_word] if not module.command_list.set: # 如果没有可用的命令,则展示模块简介 - if module.desc is not None: + if module.desc: desc = msg.locale.t("parser.module.desc", desc=msg.locale.tl_str(module.desc)) if command_first_word not in msg.enabled_modules: @@ -254,7 +254,7 @@ async def execute_submodule(msg: Bot.MessageSession, command_first_word, command kwargs = {} func_params = inspect.signature(submodule.function).parameters - if len(func_params) > 1 and msg.parsed_msg is not None: + if len(func_params) > 1 and msg.parsed_msg: parsed_msg_ = msg.parsed_msg.copy() for param_name, param_obj in func_params.items(): if param_obj.annotation == Bot.MessageSession: @@ -405,11 +405,11 @@ async def execute_submodule(msg: Bot.MessageSession, command_first_word, command matched = False if rfunc.mode.upper() in ['M', 'MATCH']: msg.matched_msg = re.match(rfunc.pattern, msg.trigger_msg, flags=rfunc.flags) - if msg.matched_msg is not None: + if msg.matched_msg: matched = True elif rfunc.mode.upper() in ['A', 'FINDALL']: msg.matched_msg = re.findall(rfunc.pattern, msg.trigger_msg, flags=rfunc.flags) - if msg.matched_msg and msg.matched_msg is not None: + if msg.matched_msg: matched = True if matched and not (msg.target.target_from in regex_module.exclude_from or diff --git a/core/petal.py b/core/petal.py index e351262f1d..abacfe44bd 100644 --- a/core/petal.py +++ b/core/petal.py @@ -1,6 +1,6 @@ import os import json -from datetime import datetime, timedelta +from datetime import datetime from decimal import Decimal from config import Config diff --git a/core/tos.py b/core/tos.py index f0c7a00de0..57ebc14f75 100644 --- a/core/tos.py +++ b/core/tos.py @@ -10,10 +10,14 @@ async def warn_target(msg: Bot.MessageSession, reason=None): current_warns = int(msg.target.sender_info.query.warns) + 1 msg.target.sender_info.edit('warns', current_warns) warn_template = [msg.locale.t("tos.warning")] - if reason is not None: + if reason: warn_template.append(msg.locale.t("tos.reason") + reason) if current_warns < WARNING_COUNTS: - warn_template.append(msg.locale.t('tos.warning.count', current_warns=current_warns, warn_counts=WARNING_COUNTS)) + warn_template.append( + msg.locale.t( + 'tos.warning.count', + current_warns=current_warns, + warn_counts=WARNING_COUNTS)) if current_warns <= 2: warn_template.append(msg.locale.t('tos.warning.appeal', issue_url=Config('issue_url'))) if current_warns == WARNING_COUNTS: diff --git a/core/types/message/__init__.py b/core/types/message/__init__.py index 37cb929b48..608cb0309e 100644 --- a/core/types/message/__init__.py +++ b/core/types/message/__init__.py @@ -1,5 +1,5 @@ import asyncio -from typing import List, Self, Union, Dict +from typing import List, Union, Dict, Coroutine, Self from core.exceptions import FinishedException from .chain import MessageChain @@ -89,13 +89,15 @@ async def send_message(self, 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,68 +106,80 @@ async def finish(self, 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: + if message_chain: 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 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): + async def wait_confirm(self, message_chain=None, quote=True, delete=True, timeout=120, append_instruction=True): """ 一次性模板,用于等待触发对象确认。 :param message_chain: 需要发送的确认消息,可不填 :param quote: 是否引用传入dict中的消息(默认为True) - :param delete: 是否在触发后删除消息 + :param delete: 是否在触发后删除消息(默认为True) + :param timeout: 超时时间 :return: 若对象发送confirm_command中的其一文本时返回True,反之则返回False """ raise NotImplementedError - async def wait_next_message(self, message_chain=None, quote=True, delete=False, append_instruction=True) -> (Self, FinishedSession): + async def wait_next_message(self, message_chain=None, quote=True, delete=False, timeout=120, + append_instruction=True) -> (Self, FinishedSession): """ 一次性模板,用于等待对象的下一条消息。 :param message_chain: 需要发送的确认消息,可不填 :param quote: 是否引用传入dict中的消息(默认为True) - :param delete: 是否在触发后删除消息 + :param delete: 是否在触发后删除消息(默认为False) + :param timeout: 超时时间 :return: 下一条消息的MessageChain对象和发出的提示消息 """ raise NotImplementedError - async def wait_reply(self, message_chain, quote=True, 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: 需要发送的确认消息,可不填 :param quote: 是否引用传入dict中的消息(默认为True) + :param delete: 是否在触发后删除消息(默认为False) + :param timeout: 超时时间 :param all_: 是否设置触发对象为对象内的所有人(默认为False) :return: 回复消息的MessageChain对象 """ raise NotImplementedError - async def wait_anyone(self, message_chain=None, delete=False): + async def wait_anyone(self, message_chain=None, quote=False, delete=False, timeout=120): """ 一次性模板,用于等待触发发送者所属对象内所有成员确认。 :param message_chain: 需要发送的确认消息,可不填 - :param delete: 是否在触发后删除消息 + :param quote: 是否引用传入dict中的消息(默认为False) + :param delete: 是否在触发后删除消息(默认为False) + :param timeout: 超时时间 :return: 任意人的MessageChain对象 """ raise NotImplementedError @@ -220,11 +234,13 @@ async def get_text_channel_list(self): """ raise NotImplementedError - def ts2strftime(self, timestamp: float, date=True, seconds=True, timezone=True): + def ts2strftime(self, timestamp: float, date=True, iso=False, time=True, seconds=True, timezone=True): """ 用于将时间戳转换为可读的时间格式。 :param timestamp: 时间戳 :param date: 是否显示日期 + :param iso: 是否以ISO格式显示 + :param time: 是否显示时间 :param seconds: 是否显示秒 :param timezone: 是否显示时区 """ @@ -285,9 +301,9 @@ def __str__(self): class FetchedSession: def __init__(self, target_from, target_id, sender_from=None, sender_id=None): - if sender_from is None: + if not sender_from: sender_from = target_from - if sender_id is None: + if not sender_id: sender_id = target_id self.target = MsgInfo(target_id=f'{target_from}|{target_id}', sender_id=f'{target_from}|{sender_id}', diff --git a/core/utils/bot.py b/core/utils/bot.py index e9e3830b42..b124954bc4 100644 --- a/core/utils/bot.py +++ b/core/utils/bot.py @@ -44,7 +44,7 @@ async def load_secret(): for x in CFG.value: if x == 'secret': for y in CFG().value[x]: - if CFG().value[x][y] is not None: + if CFG().value[x][y]: Secret.add(str(CFG().value[x][y]).upper()) diff --git a/core/utils/http.py b/core/utils/http.py index cba813e081..d171af5a0d 100644 --- a/core/utils/http.py +++ b/core/utils/http.py @@ -74,7 +74,7 @@ async def get_(): Logger.error(await req.read()) raise ValueError( f'{str(req.status)}[Ke:Image,path=https://http.cat/{str(req.status)}.jpg]') - if fmt is not None: + if fmt: if hasattr(req, fmt): return await getattr(req, fmt)() else: @@ -125,7 +125,7 @@ async def _post(): Logger.error(await req.read()) raise ValueError( f'{str(req.status)}[Ke:Image,path=https://http.cat/{str(req.status)}.jpg]') - if fmt is not None: + if fmt: if hasattr(req, fmt): return await getattr(req, fmt)() else: @@ -177,8 +177,8 @@ async def download_(): timeout=timeout, attempt=1, request_private_ip=request_private_ip, logging_err_resp=logging_err_resp) - if data is not None: - if filename is None: + if data: + if not filename: ftt = ft.match(data).extension path = f'{random_cache_path()}.{ftt}' else: diff --git a/core/utils/i18n.py b/core/utils/i18n.py index e64ba4cd8c..aed8a87c60 100644 --- a/core/utils/i18n.py +++ b/core/utils/i18n.py @@ -107,7 +107,7 @@ def load_locale_file(): class Locale: def __init__(self, locale: str, fallback_lng=None): """创建一个本地化对象""" - if fallback_lng is None: + if not fallback_lng: fallback_lng = ['zh_cn', 'zh_tw', 'en_us'] self.locale = locale self.data: LocaleNode = locale_root.query_node(locale) @@ -122,7 +122,7 @@ def __contains__(self, key: str): def t(self, key: Union[str, dict], fallback_failed_prompt=True, *args, **kwargs) -> str: """获取本地化字符串""" if isinstance(key, dict): - if (ft := key.get(self.locale)) is not None: + if ft := key.get(self.locale): return ft elif 'fallback' in key: return key['fallback'] @@ -138,14 +138,14 @@ def get_locale_node(self, path: str): def get_string_with_fallback(self, key: str, fallback_failed_prompt) -> str: node = self.data.query_node(key) - if node is not None: + if node: return node.value # 1. 如果本地化字符串存在,直接返回 fallback_lng = list(self.fallback_lng) fallback_lng.insert(0, self.locale) for lng in fallback_lng: if lng in locale_root.children: node = locale_root.query_node(lng).query_node(key) - if node is not None: + if node: return node.value # 2. 如果在 fallback 语言中本地化字符串存在,直接返回 if fallback_failed_prompt: return f'{{{key}}}' + self.t("i18n.prompt.fallback.failed", url=Config('bug_report_url'), diff --git a/core/utils/message.py b/core/utils/message.py index 6ba1fa24d1..56f4bf8b15 100644 --- a/core/utils/message.py +++ b/core/utils/message.py @@ -18,15 +18,15 @@ def remove_ineffective_text(prefix: str, lst: list) -> list: for y in lst: split_list = y.split(x) for _ in split_list: - if split_list[0] == '': + if not split_list[0]: del split_list[0] if len(split_list) > 0: - if split_list[-1] == '': + if not split_list[-1]: del split_list[-1] for _ in split_list: if len(split_list) > 0: spl0 = split_list[0] - if spl0.startswith(prefix) and spl0 != '': + if spl0.startswith(prefix) and spl0: split_list[0] = re.sub(r'^' + prefix, '', split_list[0]) list_cache.append(x.join(split_list)) lst = list_cache diff --git a/core/utils/storedata.py b/core/utils/storedata.py index 6fd0a4b506..7f9ed5c8b1 100644 --- a/core/utils/storedata.py +++ b/core/utils/storedata.py @@ -8,7 +8,7 @@ def get_stored_list(bot: Union[FetchTarget, str], name: str) -> list: get = BotDBUtil.Data(bot).get(name=name) - if get is None: + if not get: return [] else: return json.loads(get.value) diff --git a/database/__init__.py b/database/__init__.py index 6e5df437f4..67112b5fe8 100644 --- a/database/__init__.py +++ b/database/__init__.py @@ -41,7 +41,7 @@ def query_data(self): @retry(stop=stop_after_attempt(3)) @auto_rollback_error def init(self): - if self.query is None: + if not self.query: session.add_all([TargetInfo(targetId=self.target_id)]) session.commit() return self.query_data @@ -50,7 +50,7 @@ def init(self): @property def enabled_modules(self) -> list: - if self.query is None: + if not self.query: return [] return json.loads(self.query.enabledModules) @@ -60,7 +60,7 @@ def check_target_enabled_module(self, module_name) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def enable(self, module_name) -> bool: - if self.query is None: + if not self.query: self.query = self.init() enabled_modules = self.enabled_modules.copy() if isinstance(module_name, str): @@ -78,7 +78,7 @@ def enable(self, module_name) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def disable(self, module_name) -> bool: - if self.query is not None: + if self.query: enabled_modules = self.enabled_modules.copy() if isinstance(module_name, str): if module_name in enabled_modules: @@ -94,14 +94,14 @@ def disable(self, module_name) -> bool: @property def is_muted(self): - if self.query is None: + if not self.query: return False return self.query.muted @retry(stop=stop_after_attempt(3)) @auto_rollback_error def switch_mute(self) -> bool: - if self.query is None: + if not self.query: self.query = self.init() self.query.muted = not self.query.muted session.commit() @@ -109,16 +109,16 @@ def switch_mute(self) -> bool: @property def options(self): - if self.query is None: + if not self.query: return {} return json.loads(self.query.options) def get_option(self, k=None): - if self.query is None and k is None: + if not self.query and not k: return {} - elif self.query is None and k is not None: + elif not self.query and k: return None - if k is None: + if not k: return self.options else: return self.options.get(k) @@ -126,7 +126,7 @@ def get_option(self, k=None): @retry(stop=stop_after_attempt(3)) @auto_rollback_error def edit_option(self, k, v) -> bool: - if self.query is None: + if not self.query: self.query = self.init() self.query.options = json.dumps({**json.loads(self.query.options), k: v}) session.commit() @@ -135,7 +135,7 @@ def edit_option(self, k, v) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def remove_option(self, k) -> bool: - if self.query is not None: + if self.query: options = self.options.copy() if k in options: options.pop(k) @@ -146,7 +146,7 @@ def remove_option(self, k) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def edit(self, column: str, value): - if self.query is None: + if not self.query: self.query = self.init() query = self.query setattr(query, column, value) @@ -156,7 +156,7 @@ def edit(self, column: str, value): @property def custom_admins(self): - if self.query is None: + if not self.query: return [] return json.loads(self.query.custom_admins) @@ -166,7 +166,7 @@ def check_custom_target_admin(self, sender_id) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def add_custom_admin(self, sender_id) -> bool: - if self.query is None: + if not self.query: self.query = self.init() custom_admins = self.custom_admins.copy() if sender_id not in custom_admins: @@ -178,7 +178,7 @@ def add_custom_admin(self, sender_id) -> bool: @retry(stop=stop_after_attempt(3)) @auto_rollback_error def remove_custom_admin(self, sender_id) -> bool: - if self.query is not None: + if self.query: custom_admins = self.custom_admins.copy() if sender_id in custom_admins: custom_admins.remove(sender_id) @@ -187,27 +187,27 @@ def remove_custom_admin(self, sender_id) -> bool: @property def locale(self): - if self.query is None: + if not self.query: self.query = self.init() return self.query.locale @staticmethod def get_enabled_this(module_name, id_prefix=None) -> List[TargetInfo]: filter_ = [TargetInfo.enabledModules.like(f'%"{module_name}"%')] - if id_prefix is not None: + if id_prefix: filter_.append(TargetInfo.targetId.like(f'{id_prefix}%')) return session.query(TargetInfo).filter(*filter_).all() @property def petal(self): - if self.query is None: + if not self.query: return 0 return self.query.petal @retry(stop=stop_after_attempt(3)) @auto_rollback_error def modify_petal(self, amount: int) -> bool: - if self.query is None: + if not self.query: self.query = self.init() petal = self.petal new_petal = petal + amount @@ -221,7 +221,7 @@ class SenderInfo: def __init__(self, sender_id): self.sender_id = sender_id self.query = self.query_SenderInfo - if self.query is None: + if not self.query: session.add_all([SenderInfo(id=sender_id)]) session.commit() self.query = session.query(SenderInfo).filter_by(id=sender_id).first() @@ -249,7 +249,7 @@ def __init__(self, msg: MessageSession, name): self.name = name self.query = session.query(CommandTriggerTime).filter_by(targetId=str(msg.target.sender_id), commandName=name).first() - self.need_insert = True if self.query is None else False + self.need_insert = True if not self.query else False def check(self, delay): if not self.need_insert: @@ -275,7 +275,7 @@ def reset(self): def isGroupInAllowList(target_id): session.expire_all() query = session.query(GroupAllowList).filter_by(targetId=target_id).first() - if query is not None: + if query: return True return False @@ -303,7 +303,7 @@ def get(self, name): @auto_rollback_error def update(self, name, value: str): exists = self.get(name) - if exists is None: + if not exists: self.add(name=name, value=value) else: exists.value = value @@ -334,14 +334,14 @@ def get_first(): @staticmethod def get_data_by_times(new, old, module_name=None): filter_ = [AnalyticsData.timestamp <= new, AnalyticsData.timestamp >= old] - if module_name is not None: + if module_name: filter_.append(AnalyticsData.moduleName == module_name) return session.query(AnalyticsData).filter(*filter_).all() @staticmethod def get_count_by_times(new, old, module_name=None): filter_ = [AnalyticsData.timestamp < new, AnalyticsData.timestamp > old] - if module_name is not None: + if module_name: filter_.append(AnalyticsData.moduleName == module_name) return session.query(AnalyticsData).filter(*filter_).count() diff --git a/database/local.py b/database/local.py index 32efdeae47..458787ec72 100644 --- a/database/local.py +++ b/database/local.py @@ -51,9 +51,9 @@ def __init__(self, query_word): self.query_word = query_word self.query = session.query(DirtyFilterTable).filter_by(desc=self.query_word).first() self.need_insert = False - if self.query is None: + if not self.query: self.need_insert = True - if self.query is not None and datetime.datetime.now().timestamp() - self.query.timestamp.timestamp() > 86400: + if self.query and datetime.datetime.now().timestamp() - self.query.timestamp.timestamp() > 86400: session.delete(self.query) session.commit() self.need_insert = True diff --git a/locales/en_us.json b/locales/en_us.json index 915b672c60..05ceee6ef3 100644 --- a/locales/en_us.json +++ b/locales/en_us.json @@ -35,6 +35,8 @@ "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": ", ", "message.end": ".", @@ -58,7 +60,8 @@ "petal.message.lost.limit": "(You can't lose more petals today.)", "petal.message.lost.success": "You lost ${amount} petal(s).", "success": "Success.", - "time.date.format": "%B %d, %Y,", + "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: ", @@ -73,4 +76,4 @@ "tos.warning.last": "This is the last warning.", "unknown": "Unknown", "yes": "Yes" -} +} \ No newline at end of file diff --git a/locales/zh_cn.json b/locales/zh_cn.json index 95b7d737bd..97e83da2f0 100644 --- a/locales/zh_cn.json +++ b/locales/zh_cn.json @@ -35,6 +35,8 @@ "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": "。", @@ -58,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": "具体原因:", @@ -73,4 +76,4 @@ "tos.warning.last": "这是最后一次警告。", "unknown": "未知", "yes": "是" -} +} \ No newline at end of file diff --git a/locales/zh_tw.json b/locales/zh_tw.json index 6ace44983a..e9ab542940 100644 --- a/locales/zh_tw.json +++ b/locales/zh_tw.json @@ -35,6 +35,8 @@ "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": "。", @@ -58,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": "原因:", @@ -73,4 +76,4 @@ "tos.warning.last": "這是最後警告。", "unknown": "未知", "yes": "是" -} +} \ No newline at end of file diff --git a/modules/arcaea/__init__.py b/modules/arcaea/__init__.py index 8458d98245..36fb63bdb7 100644 --- a/modules/arcaea/__init__.py +++ b/modules/arcaea/__init__.py @@ -95,16 +95,15 @@ async def _(msg: Bot.MessageSession, use_local=True): await msg.finish(msg.locale.t("arcaea.message.get_failed")) -@arc.command('calc ') +@arc.command('calc {{arcaea.help.calc}}') async def _(msg: Bot.MessageSession, score: int, rating: float): - ptt = 0 if score >= 10000000: - ptt += 2 + ptt = rating + 2 elif score >= 9800000: - ptt += 1 + (score - 9800000) / 200000 + ptt = rating + 1 + (score - 9800000) / 200000 else: - ptt += (score - 9500000) / 300000 - await msg.finish([Plain(rating + ptt)]) + ptt = rating + (score - 9500000) / 300000 + await msg.finish([Plain(round(max(0, ptt), 2))]) p = module('ptt', developers=['OasisAkari']) diff --git a/modules/arcaea/locales/en_us.json b/modules/arcaea/locales/en_us.json index 81e3fe40b5..5927004cd0 100644 --- a/modules/arcaea/locales/en_us.json +++ b/modules/arcaea/locales/en_us.json @@ -1,7 +1,8 @@ { + "arcaea.help.calc": "Calculate the single Potential based on scores and rating.", "arcaea.help.desc": "Queries about Arcaea.", "arcaea.help.download": "Get the latest version of game apk.", - "arcaea.help.random": "Random a track.", + "arcaea.help.random": "Random a song.", "arcaea.help.rank.free": "View the current rank of the free packs.", "arcaea.help.rank.paid": "View the current rank of the paid packs.", "arcaea.message.download": "Current latest version is ${version}.\nDownload: ${url}", diff --git a/modules/arcaea/locales/zh_cn.json b/modules/arcaea/locales/zh_cn.json index 3d882e6ee3..071ce383bc 100644 --- a/modules/arcaea/locales/zh_cn.json +++ b/modules/arcaea/locales/zh_cn.json @@ -1,4 +1,5 @@ { + "arcaea.help.calc": "根据分数与歌曲定数计算对应的单曲潜力值。", "arcaea.help.desc": "查询 Arcaea 相关内容。", "arcaea.help.download": "获取最新版本的游戏 Apk。", "arcaea.help.random": "随机一首歌曲。", diff --git a/modules/arcaea/locales/zh_tw.json b/modules/arcaea/locales/zh_tw.json index 880c8782c1..fa4256e181 100644 --- a/modules/arcaea/locales/zh_tw.json +++ b/modules/arcaea/locales/zh_tw.json @@ -1,4 +1,5 @@ { + "arcaea.help.calc": "依據分數與歌曲定數計算對應的單曲潛力值。", "arcaea.help.desc": "查詢 Arcaea 相關內容。", "arcaea.help.download": "取得最新版本的遊戲 Apk。", "arcaea.help.random": "隨機一首歌曲。", diff --git a/modules/ask/tools/utils.py b/modules/ask/tools/utils.py index f210c63cb8..a9df64db14 100644 --- a/modules/ask/tools/utils.py +++ b/modules/ask/tools/utils.py @@ -24,7 +24,7 @@ async def wrapper(*args, **kwargs): def with_args(func: Callable, *args, **kwargs): async def wrapper(*a, **k): # if a is tuple with empty string - if len(a) == 1 and a[0] == '': + if len(a) == 1 and not a[0]: return await func(*args, **kwargs, **k) return await func(*args, *a, **kwargs, **k) diff --git a/modules/bilibili/__init__.py b/modules/bilibili/__init__.py index db46db1183..212718e5b3 100644 --- a/modules/bilibili/__init__.py +++ b/modules/bilibili/__init__.py @@ -54,12 +54,11 @@ async def _(msg: Bot.MessageSession): await get_info(msg, url, get_detail=False) -""" + @bili.handle(re.compile(r"https?://b23\.tv/(av\d+|BV[A-Za-z0-9]{10}|[A-Za-z0-9]{7})(?:/.*?|)$"), mode="M", desc="{bilibili.help.regex.shorturl}") async def _(msg: Bot.MessageSession): - res = msg.matched_msg - video = res.groups()[0] + video = msg.matched_msg.groups()[0] if video[:2] == "BV": url = f"{api_url}?bvid={video}" elif video[:2] == "av": @@ -70,13 +69,11 @@ async def _(msg: Bot.MessageSession): await get_info(msg, url, get_detail=False) - async def parse_shorturl(shorturl): async with aiohttp.ClientSession() as session: - async with session.get(shorturl, allow_redirects=True) as response: - target_url = str(response.url) - parsed_url = urlparse(target_url) - video = parsed_url.path.split("/")[-2] - url = f"{api_url}?bvid={video}" - return url -""" + async with session.get(shorturl, allow_redirects=False) as response: + target_url = response.headers.get('Location') + + video = re.search(r'/video/([^/?]+)', target_url) + url = f"{api_url}?bvid={video.group(1)}" + return url diff --git a/modules/bilibili/bilibili.py b/modules/bilibili/bilibili.py index 92f72d8808..c03d42e95a 100644 --- a/modules/bilibili/bilibili.py +++ b/modules/bilibili/bilibili.py @@ -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): diff --git a/modules/bilibili/locales/zh_cn.json b/modules/bilibili/locales/zh_cn.json index f1c33a093c..5e92eb17c2 100644 --- a/modules/bilibili/locales/zh_cn.json +++ b/modules/bilibili/locales/zh_cn.json @@ -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": "未找到对应的视频。" } diff --git a/modules/bilibili/locales/zh_tw.json b/modules/bilibili/locales/zh_tw.json index a7d562aa27..1ffab802d5 100644 --- a/modules/bilibili/locales/zh_tw.json +++ b/modules/bilibili/locales/zh_tw.json @@ -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": "未找到對應的影片。" diff --git a/modules/bugtracker/__init__.py b/modules/bugtracker/__init__.py index dc9a5a349e..4d75e3b65e 100644 --- a/modules/bugtracker/__init__.py +++ b/modules/bugtracker/__init__.py @@ -15,7 +15,7 @@ async def bugtracker(msg: Bot.MessageSession): if q: result = await bugtracker_get(msg, q.group(1)) await msg.send_message(result[0]) - if result[1] is not None: + if result[1]: screenshot = await make_screenshot(result[1]) if screenshot: await msg.send_message(Image(screenshot)) @@ -28,7 +28,7 @@ async def regex_bugtracker(msg: Bot.MessageSession): if len(matched_msg.group(1)) < 10: result = await bugtracker_get(msg, matched_msg.group(1) + '-' + matched_msg.group(2)) await msg.send_message(result[0]) - if result[1] is not None: + if result[1]: screenshot = await make_screenshot(result[1]) if screenshot: await msg.send_message(Image(screenshot)) @@ -44,7 +44,7 @@ async def bgtask(msg: Bot.MessageSession): if t != '': get_ = await bugtracker_get(msg, t.split('?')[0], nolink=True) await msg.send_message(get_[0]) - if get_[1] is not None: + if get_[1]: screenshot = await make_screenshot(get_[1]) if screenshot: await msg.send_message(Image(screenshot)) diff --git a/modules/bugtracker/bugtracker.py b/modules/bugtracker/bugtracker.py index ec75e1cc41..18f3cf967e 100644 --- a/modules/bugtracker/bugtracker.py +++ b/modules/bugtracker/bugtracker.py @@ -91,7 +91,7 @@ async def bugtracker_get(session, mojira_id: str, nolink=False): data["project"] = fields['project']['name'] if 'resolution' in fields: data["resolution"] = fields['resolution']['name'] if fields[ - 'resolution'] is not None else 'Unresolved' + 'resolution'] else 'Unresolved' if 'versions' in load_json['fields']: versions = fields['versions'] verlist = [] diff --git a/modules/calc/locales/en_us.json b/modules/calc/locales/en_us.json index cc69ad11c2..d66eb01de4 100644 --- a/modules/calc/locales/en_us.json +++ b/modules/calc/locales/en_us.json @@ -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." } \ No newline at end of file diff --git a/modules/calc/locales/zh_cn.json b/modules/calc/locales/zh_cn.json index 577849a835..523707bf97 100644 --- a/modules/calc/locales/zh_cn.json +++ b/modules/calc/locales/zh_cn.json @@ -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": "计算超时。" } \ No newline at end of file diff --git a/modules/calc/locales/zh_tw.json b/modules/calc/locales/zh_tw.json index 9eeec64fd5..11571818e9 100644 --- a/modules/calc/locales/zh_tw.json +++ b/modules/calc/locales/zh_tw.json @@ -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": "計算逾時。" } \ No newline at end of file diff --git a/modules/chemical_code/__init__.py b/modules/chemical_code/__init__.py index 68107cd3ab..04a4780cd5 100644 --- a/modules/chemical_code/__init__.py +++ b/modules/chemical_code/__init__.py @@ -17,9 +17,11 @@ from core.utils.http import get_url, download_to_cache from core.utils.text import remove_prefix +CSID_RANGE_MAX = 200000000 # 数据库增长速度很快,可手动在此修改 ID 区间 + csr_link = 'https://www.chemspider.com' -special_id_path = os.path.abspath(f'./assets/chemical_code/special_id') # 去掉文件扩展名并存储在special_id列表中 +special_id_path = os.path.abspath(f'./assets/chemical_code/special_id') # 去掉文件扩展名并存储在 special_id 列表中 special_id = [os.path.splitext(filename)[0] for filename in os.listdir( special_id_path)] # 可能会导致识别问题的物质(如部分单质)ID,这些 ID 的图片将会在本地调用 @@ -41,7 +43,7 @@ def parse_elements(formula: str) -> dict: elements = {} while True: - if formula == '': + if not formula: break for element in element_lists: if formula.startswith(element): @@ -59,12 +61,12 @@ def parse_elements(formula: str) -> dict: @retry(stop=stop_after_attempt(3), reraise=True) async def search_csr(id=None): - if id is not None: + if id: answer_id = id else: - answer_id = random.randint(1, 200000000) # 数据库增长速度很快,可手动在此修改ID区间 + answer_id = random.randint(1, CSID_RANGE_MAX) answer_id = str(answer_id) - Logger.info("ChemSpider ID: " + answer_id) + Logger.info(f'ChemSpider ID: {answer_id}') get = await get_url(csr_link + '/Search.aspx?q=' + answer_id, 200, fmt='text') # Logger.info(get) soup = BeautifulSoup(get, 'html.parser') @@ -146,11 +148,9 @@ async def chemical_code(msg: Bot.MessageSession, id=None, random_mode=True, capt # print(csr) play_state[msg.target.target_id]['answer'] = csr['name'] Logger.info(f'Answer: {csr["name"]}') - Logger.info(f'Image: {csr["image"]}') download = False if csr["id"] in special_id: # 如果正确答案在 special_id 中 file_path = os.path.abspath(f'./assets/chemicalcode/special_id/{csr["id"]}.png') - Logger.info(f'File path: {file_path}') exists_file = os.path.exists(file_path) if exists_file: download = file_path @@ -169,7 +169,7 @@ async def chemical_code(msg: Bot.MessageSession, id=None, random_mode=True, capt set_timeout = 2 async def ans(msg: Bot.MessageSession, answer, random_mode): - wait = await msg.wait_anyone() + wait = await msg.wait_anyone(timeout=3600) if play_state[msg.target.target_id]['active']: if (wait_text := wait.as_display(text_only=True)) != answer: if re.match(r'^[A-Za-z0-9]+$', wait_text): @@ -244,8 +244,8 @@ async def timer(start): await asyncio.gather(ans(msg, csr['name'], random_mode), timer(time_start)) else: result, _ = await msg.wait_next_message([Plain(msg.locale.t('chemical_code.message.showid', id=csr["id"])), - Image(newpath), Plain(msg.locale.t('chemical_code.message.captcha', - times=set_timeout))], append_instruction=False) + Image(newpath), Plain(msg.locale.t('chemical_code.message.captcha', + times=set_timeout))], timeout=3600, append_instruction=False) if play_state[msg.target.target_id]['active']: if result.as_display(text_only=True) == csr['name']: send_ = msg.locale.t('chemical_code.message.correct') diff --git a/modules/color/__init__.py b/modules/color/__init__.py index d45066af81..fbf52f125d 100644 --- a/modules/color/__init__.py +++ b/modules/color/__init__.py @@ -27,11 +27,11 @@ @c.command('[] {{color.help}}') @c.command() async def _(msg: Bot.MessageSession, color: str = None): - if color is None: + if not color: color = webcolors.HTML5SimpleColor(*(np.random.randint(0, 256, 3))) - elif css_names_to_hex.get(color) is not None: + elif css_names_to_hex.get(color): color = webcolors.html5_parse_simple_color(css_names_to_hex[color]) - elif material_colors_names_to_hex.get(color) is not None: + elif material_colors_names_to_hex.get(color): color = webcolors.html5_parse_simple_color(material_colors_names_to_hex[color]) elif re.match(r'^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$', color): # add hash if missing @@ -79,7 +79,7 @@ async def _(msg: Bot.MessageSession, color: str = None): css_color_name = f'\n{msg.locale.t("color.message.css")}{css_color_name_raw[0]}' if css_color_name_raw[0] != 'black' and css_color_name_raw[0] != 'white': css_color_name_short = f'{css_color_name_raw[0]}\n' - elif css_color_name_raw[0] is not None: + elif css_color_name_raw[0]: css_color_name = f'\n{msg.locale.t("color.message.css.approximate")}{css_color_name_raw[0]}' material_color_name_raw = get_color_name(color, material_colors_hex_to_names) @@ -88,7 +88,7 @@ async def _(msg: Bot.MessageSession, color: str = None): if material_color_name_raw[1]: material_color_name = f'\n{msg.locale.t("color.message.md")}{material_color_name_raw[0]}' material_color_name_short = f'{material_color_name_raw[0]}\n' - elif material_color_name_raw[0] is not None: + elif material_color_name_raw[0]: material_color_name = f'\n{msg.locale.t("color.message.md.approximate")}{material_color_name_raw[0]}' draw.multiline_text( diff --git a/modules/convert/__init__.py b/modules/convert/__init__.py deleted file mode 100644 index c9c1e9676e..0000000000 --- a/modules/convert/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from decimal import Decimal - -from pint import UnitRegistry - -from core.builtins import Bot -from core.component import module - -# ureg = UnitRegistry(os.path.dirname(os.path.abspath(__file__)) + -# '/default_bi_zh-cn_en.txt', non_int_type=Decimal) -ureg = UnitRegistry(non_int_type=Decimal) -i = module('convert', alias=['conv', 'unit'], desc='{convert.help.desc}', - developers=['Dianliang233'], support_languages=['en_us']) - - -@i.command(' {{convert.help}}') -async def _(msg: Bot.MessageSession, from_val: str, to_unit: str): - try: - ori = ureg.parse_expression(from_val) - res = ureg.parse_expression(from_val).to(to_unit) - except BaseException: - await msg.finish(msg.locale.t("convert.message.error")) - - await msg.finish(f"{ori:~Pg} = {res:~Pg}") diff --git a/modules/convert/constants.txt b/modules/convert/constants.txt deleted file mode 100644 index 9babc8fa2e..0000000000 --- a/modules/convert/constants.txt +++ /dev/null @@ -1,74 +0,0 @@ -# Default Pint constants definition file -# Based on the International System of Units -# Language: english -# Source: https://physics.nist.gov/cuu/Constants/ -# https://physics.nist.gov/PhysRefData/XrayTrans/Html/search.html -# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details. - -#### MATHEMATICAL CONSTANTS #### -# As computed by Maxima with fpprec:50 - -pi = 3.1415926535897932384626433832795028841971693993751 = π # pi -tansec = 4.8481368111333441675396429478852851658848753880815e-6 # tangent of 1 arc-second ~ arc_second/radian -ln10 = 2.3025850929940456840179914546843642076011014886288 # natural logarithm of 10 -wien_x = 4.9651142317442763036987591313228939440555849867973 # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5 -wien_u = 2.8214393721220788934031913302944851953458817440731 # solution to (u-3)*exp(u)+3 = 0 => u = W(3/exp(3))+3 -eulers_number = 2.71828182845904523536028747135266249775724709369995 - -#### DEFINED EXACT CONSTANTS #### - -speed_of_light = 299792458 m/s = c = c_0 # since 1983 -planck_constant = 6.62607015e-34 J s = ℎ # since May 2019 -elementary_charge = 1.602176634e-19 C = e # since May 2019 -avogadro_number = 6.02214076e23 # since May 2019 -boltzmann_constant = 1.380649e-23 J K^-1 = k = k_B # since May 2019 -standard_gravity = 9.80665 m/s^2 = g_0 = g0 = g_n = gravity # since 1901 -standard_atmosphere = 1.01325e5 Pa = atm = atmosphere # since 1954 -conventional_josephson_constant = 4.835979e14 Hz / V = K_J90 # since Jan 1990 -conventional_von_klitzing_constant = 2.5812807e4 ohm = R_K90 # since Jan 1990 - -#### DERIVED EXACT CONSTANTS #### -# Floating-point conversion may introduce inaccuracies - -zeta = c / (cm/s) = ζ -dirac_constant = ℎ / (2 * π) = ħ = hbar = atomic_unit_of_action = a_u_action -avogadro_constant = avogadro_number * mol^-1 = N_A -molar_gas_constant = k * N_A = R -faraday_constant = e * N_A -conductance_quantum = 2 * e ** 2 / ℎ = G_0 -magnetic_flux_quantum = ℎ / (2 * e) = Φ_0 = Phi_0 -josephson_constant = 2 * e / ℎ = K_J -von_klitzing_constant = ℎ / e ** 2 = R_K -stefan_boltzmann_constant = 2 / 15 * π ** 5 * k ** 4 / (ℎ ** 3 * c ** 2) = σ = sigma -first_radiation_constant = 2 * π * ℎ * c ** 2 = c_1 -second_radiation_constant = ℎ * c / k = c_2 -wien_wavelength_displacement_law_constant = ℎ * c / (k * wien_x) -wien_frequency_displacement_law_constant = wien_u * k / ℎ - -#### MEASURED CONSTANTS #### -# Recommended CODATA-2018 values -# To some extent, what is measured and what is derived is a bit arbitrary. -# The choice of measured constants is based on convenience and on available uncertainty. -# The uncertainty in the last significant digits is given in parentheses as a comment. - -newtonian_constant_of_gravitation = 6.67430e-11 m^3/(kg s^2) = _ = gravitational_constant # (15) -rydberg_constant = 1.0973731568160e7 * m^-1 = R_∞ = R_inf # (21) -electron_g_factor = -2.00231930436256 = g_e # (35) -atomic_mass_constant = 1.66053906660e-27 kg = m_u # (50) -electron_mass = 9.1093837015e-31 kg = m_e = atomic_unit_of_mass = a_u_mass # (28) -proton_mass = 1.67262192369e-27 kg = m_p # (51) -neutron_mass = 1.67492749804e-27 kg = m_n # (95) -lattice_spacing_of_Si = 1.920155716e-10 m = d_220 # (32) -K_alpha_Cu_d_220 = 0.80232719 # (22) -K_alpha_Mo_d_220 = 0.36940604 # (19) -K_alpha_W_d_220 = 0.108852175 # (98) - -#### DERIVED CONSTANTS #### - -fine_structure_constant = (2 * ℎ * R_inf / (m_e * c)) ** 0.5 = α = alpha -vacuum_permeability = 2 * α * ℎ / (e ** 2 * c) = µ_0 = mu_0 = mu0 = magnetic_constant -vacuum_permittivity = e ** 2 / (2 * α * ℎ * c) = ε_0 = epsilon_0 = eps_0 = eps0 = electric_constant -impedance_of_free_space = 2 * α * ℎ / e ** 2 = Z_0 = characteristic_impedance_of_vacuum -coulomb_constant = α * hbar * c / e ** 2 = k_C -classical_electron_radius = α * hbar / (m_e * c) = r_e -thomson_cross_section = 8 / 3 * π * r_e ** 2 = σ_e = sigma_e diff --git a/modules/convert/default_bi_zh-cn_en.txt b/modules/convert/default_bi_zh-cn_en.txt deleted file mode 100644 index b45b5cc9a6..0000000000 --- a/modules/convert/default_bi_zh-cn_en.txt +++ /dev/null @@ -1,901 +0,0 @@ - -# Default Pint units definition file -# Based on the International System of Units -# Language: english -# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details. - -# Syntax -# ====== -# Units -# ----- -# = [= ] [= ] [ = ] [...] -# -# The canonical name and aliases should be expressed in singular form. -# Pint automatically deals with plurals built by adding 's' to the singular form; plural -# forms that don't follow this rule should be instead explicitly listed as aliases. -# -# If a unit has no symbol and one wants to define aliases, then the symbol should be -# conventionally set to _. -# -# Example: -# millennium = 1e3 * year = _ = millennia -# -# -# Prefixes -# -------- -# - = [= ] [= ] [ = ] [...] -# -# Example: -# deca- = 1e+1 = da- = deka- -# -# -# Derived dimensions -# ------------------ -# [dimension name] = -# -# Example: -[mass] = # [density] / [volume] -# -# Note that primary dimensions don't need to be declared; they can be -# defined for the first time in a unit definition. -# E.g. see below `meter = [length]` -# -# -# Additional aliases -# ------------------ -# @alias = [ = ] [...] -# -# Used to add aliases to already existing unit definitions. -# Particularly useful when one wants to enrich definitions -# from defaults_en.txt with custom aliases. -# -# Example: -# @alias meter = my_meter - -# See also: https://pint.readthedocs.io/en/latest/defining.html - -@defaults - group = international - system = mks -@end - - -#### PREFIXES #### - -# decimal prefixes -quecto- = 1e-30 = q- = 亏可托- = 亏- -ronto- = 1e-27 = r- = 柔托- = 柔- -yocto- = 1e-24 = y- = 幺科托- = 幺- = 攸- -zepto- = 1e-21 = z- = 仄普托- = 仄- = 介- -atto- = 1e-18 = a- = 阿托- = 阿- -femto- = 1e-15 = f- = 飞母托- = 飞- = 飛- -pico- = 1e-12 = p- = 皮可- = 皮- -nano- = 1e-9 = n- = 纳诺- = 纳- = 奈- -# The micro (U+00B5) and Greek mu (U+03BC) are both valid prefixes, -# and they often use the same glyph. -micro- = 1e-6 = µ- = μ- = u- = 微- -milli- = 1e-3 = m- = 毫- -centi- = 1e-2 = c- = 厘- = 釐- -deci- = 1e-1 = d- = 分- -deca- = 1e+1 = da- = deka- = 十- -hecto- = 1e2 = h- = 百- -kilo- = 1e3 = k- = 千- -mega- = 1e6 = M- = 兆- = 百萬- -giga- = 1e9 = G- = 吉咖- = 吉- -tera- = 1e12 = T- = 太拉- = 太- -peta- = 1e15 = P- = 拍它- = 拍- -exa- = 1e18 = E- = 艾可萨- = 艾- -zetta- = 1e21 = Z- = 泽它- = 泽- = 皆= -yotta- = 1e24 = Y- = 尧它- = 尧- = 佑- -ronna- = 1e27 = R- = 容那- = 容- -quetta- = 1e30 = Q- = 昆它- = 昆- - -# binary_prefixes -kibi- = 2**10 = Ki- -mebi- = 2**20 = Mi- -gibi- = 2**30 = Gi- -tebi- = 2**40 = Ti- -pebi- = 2**50 = Pi- -exbi- = 2**60 = Ei- -zebi- = 2**70 = Zi- -yobi- = 2**80 = Yi- - -# extra_prefixes -semi- = 0.5 = _ = demi- = 半- -sesqui- = 1.5 - - -#### BASE UNITS #### - -meter = [length] = m = metre = 米 = 公尺 -second = [time] = s = sec = 秒 -ampere = [current] = A = amp = 安培 = 安 -candela = [luminosity] = cd = candle = 坎德拉 = 坎 -gram = [mass] = g = 克 = 公克 -mole = [substance] = mol = 摩尔 = 摩 -kelvin = [temperature]; offset: 0 = K = degK = °K = degree_Kelvin = degreeK = 开尔文 = 开 # older names supported for compatibility -radian = [] = rad = 弧度 -bit = [] = 比特 -count = [] = 个 - - -#### CONSTANTS #### - -@import constants.txt - -#### UNITS #### -# Common and less common, grouped by quantity. -# Conversion factors are exact (except when noted), -# although floating-point conversion may introduce inaccuracies - -# Angle -turn = 2 * π * radian = _ = revolution = cycle = circle = 转 -degree = π / 180 * radian = deg = arcdeg = arcdegree = 度 = 角度 -arcminute = degree / 60 = arcmin = arc_minute = angular_minute = 弧分 = 角分 -arcsecond = arcminute / 60 = arcsec = arc_second = angular_second = 弧秒 = 角秒 -milliarcsecond = 1e-3 * arcsecond = mas = 毫弧秒 = 毫角秒 -grade = π / 200 * radian = grad = gon = 百分度 = 梯度 -milliradian = π / 32000 * radian = mil = 毫弧度 = 密位 - -# Solid angle -steradian = radian ** 2 = sr = 球面度 -square_degree = (π / 180) ** 2 * sr = sq_deg = sqdeg = 平方度 - -# Information -baud = bit / second = Bd = bps = 比特每秒 = 波特 - -byte = 8 * bit = B = octet = 字节 -字节 = 8 * bit = _ = octet = # byte -## NOTE: B (byte) symbol can conflict with Bell - -# Ratios -percent = 0.01 = % = 百分 -ppm = 1e-6 = 分比 - -# Length -angstrom = 1e-10 * meter = Å = ångström = Å = 埃格斯特朗 = 埃 -centimeter = cm = 厘米 = 釐米 = 公分 -millimeter = mm = 毫米 = 公厘 = 公釐 -decimeter = dm = 分米 = 公寸 -dekameter = dam = 十米 = 公丈 -hectometer = hm = 百米 = 公引 -kilometer = km = 千米 = 公里 -micron = micrometer = µm = μ = 微米 -fermi = femtometer = fm = 费米 -light_year = speed_of_light * julian_year = ly = lightyear = 光年 -astronomical_unit = 149597870700 * meter = au = 天文单位 = 天文單位# since Aug 2012 -parsec = 1 / tansec * astronomical_unit = pc = 秒差距 -nautical_mile = 1852 * meter = nmi = 海里 = 浬 -bohr = hbar / (alpha * m_e * c) = a_0 = a0 = bohr_radius = atomic_unit_of_length = a_u_length = 玻尔半径 = 原子单位长度 = 波耳半徑 -x_unit_Cu = K_alpha_Cu_d_220 * d_220 / 1537.4 = Xu_Cu = 铜的X单位 = 铜的X射线波长 -x_unit_Mo = K_alpha_Mo_d_220 * d_220 / 707.831 = Xu_Mo = 钼的X单位 = 钼的X射线波长 -angstrom_star = K_alpha_W_d_220 * d_220 / 0.2090100 = Å_star = 埃星 -planck_length = (hbar * gravitational_constant / c ** 3) ** 0.5 = 普朗克长度 = 普朗克長度 - - -# Mass -metric_ton = 1e3 * kilogram = t = tonne = 吨 = 公吨 = 噸 = 公噸 -centigram = cg = 厘克 = 公毫 -milligram = mg = 毫克 = 公絲 -decigram = dg = 分克 = 公銖 -dekagram = dag = 十克 = 公錢 -hectogram = hg = 百克 = 公兩 -kilogram = kg = 千克 = 公斤 -unified_atomic_mass_unit = atomic_mass_constant = u = amu = 原子质量单位 = 原子質量單位 -dalton = atomic_mass_constant = Da = 道尔顿 -grain = 64.79891 * milligram = gr = 格令 = 格林 -gamma_mass = microgram = 微克 = 伽玛质量 -carat = 200 * milligram = ct = karat = 克拉 -planck_mass = (hbar * c / gravitational_constant) ** 0.5 = 普朗克质量 = 普朗克質量 - -# Time -minute = 60 * second = min = 分钟 = 分 -hour = 60 * minute = h = hr = 小时 -day = 24 * hour = d = 日 = 天 -week = 7 * day = 星期 = 周 = 礼拜 -fortnight = 2 * week = 两星期 -year = 365.25 * day = a = yr = julian_year = 年 = 儒略年 -month = year / 12 = 月 - -# decade = 10 * year -## NOTE: decade [time] can conflict with decade [dimensionless] - -century = 100 * year = _ = centuries = 世纪 -millennium = 1e3 * year = _ = millennia = 千年纪 = 千纪 = 千年 -eon = 1e9 * year = 宙 -shake = 1e-8 * second = 抖 -svedberg = 1e-13 * second = 斯韦德贝里 = 斯维德伯格 -atomic_unit_of_time = hbar / E_h = a_u_time = 原子单位时间 -gregorian_year = 365.2425 * day = 公历年 -sidereal_year = 365.256363004 * day = 恒星年 -tropical_year = 365.242190402 * day = 回归年 -common_year = 365 * day = 平年 -leap_year = 366 * day = 闰年 -sidereal_day = day / 1.00273790935079524 = 恒星日 -sidereal_month = 27.32166155 * day = 恒星月 -tropical_month = 27.321582 * day = 回归月 -synodic_month = 29.530589 * day = _ = lunar_month = 同步月 = 朔望月 -planck_time = (hbar * gravitational_constant / c ** 5) ** 0.5 = 普朗克时间 - -# Temperature -degree_Celsius = kelvin; offset: 273.15 = °C = celsius = degC = degreeC = 摄氏度 -degree_Rankine = 5 / 9 * kelvin; offset: 0 = °R = °Ra = rankine = degR = degreeR = 兰氏度 -degree_Fahrenheit = 5 / 9 * kelvin; offset: 233.15 + 200 / 9 = °F = fahrenheit = degF = degreeF = 华氏度 -réaumur = 4 / 5 * kelvin; offset: 273.15 = °Re = reaumur = degRe = degreeRe = degree_Réaumur = 列氏度 -atomic_unit_of_temperature = E_h / k = a_u_temp = 原子单位温度 -planck_temperature = (hbar * c ** 5 / gravitational_constant / k ** 2) ** 0.5 = 普朗克温度 - -# Area -[area] = [length] ** 2 -are = 100 * meter ** 2 = 公亩 -barn = 1e-28 * meter ** 2 = b = 靶恩 -darcy = centipoise * centimeter ** 2 / (second * atmosphere) = 达西 -hectare = 100 * are = ha = 公顷 - -# Volume -[volume] = [length] ** 3 -liter = decimeter ** 3 = l = L = litre = 升 -cubic_centimeter = centimeter ** 3 = cc = 立方厘米 -lambda = microliter = λ -stere = meter ** 3 - -# Frequency -[frequency] = 1 / [time] -hertz = 1 / second = Hz = 赫兹 = 赫 -revolutions_per_minute = revolution / minute = rpm = 转每分 -revolutions_per_second = revolution / second = rps = 转每秒 -counts_per_second = count / second = cps = 次每秒 - -# Wavenumber -[wavenumber] = 1 / [length] -reciprocal_centimeter = 1 / cm = cm_1 = kayser = 倒数厘米 - -# Velocity -[velocity] = [length] / [time] -[speed] = [velocity] -knot = nautical_mile / hour = kt = knot_international = international_knot = 节 -mile_per_hour = mile / hour = mph = MPH = 英里每小时 = 迈 -kilometer_per_hour = kilometer / hour = kph = KPH = 千米每小时 -kilometer_per_second = kilometer / second = kps = 千米每秒 -meter_per_second = meter / second = mps = 米每秒 -foot_per_second = foot / second = fps = 英尺每秒 - -# Volumetric Flow Rate -[volumetric_flow_rate] = [volume] / [time] -sverdrup = 1e6 * meter ** 3 / second = sv = 斯维德鲁普 - -# Acceleration -[acceleration] = [velocity] / [time] -galileo = centimeter / second ** 2 = Gal = 伽利略 - -# Force -[force] = [mass] * [acceleration] -newton = kilogram * meter / second ** 2 = N = 牛顿 = 牛 -dyne = gram * centimeter / second ** 2 = dyn = 达因 -force_kilogram = g_0 * kilogram = kgf = kilogram_force = pond = 千克力 -force_gram = g_0 * gram = gf = gram_force = 克力 -force_metric_ton = g_0 * metric_ton = tf = metric_ton_force = force_t = t_force = 公吨力 = 吨力 -atomic_unit_of_force = E_h / a_0 = a_u_force = 原子单位力 - -# Energy -[energy] = [force] * [length] -joule = newton * meter = J = 焦耳 -erg = dyne * centimeter = 尔格 -watt_hour = watt * hour = Wh = watthour = 瓦时 -electron_volt = e * volt = eV = 电子伏特 -rydberg = ℎ * c * R_inf = Ry = 里德伯格 -hartree = 2 * rydberg = E_h = Eh = hartree_energy = atomic_unit_of_energy = a_u_energy = 哈特里 -calorie = 4.184 * joule = cal = thermochemical_calorie = cal_th = 卡路里 -international_calorie = 4.1868 * joule = cal_it = international_steam_table_calorie = 国际卡路里 -fifteen_degree_calorie = 4.1855 * joule = cal_15 = 卡路里15度 -british_thermal_unit = 1055.056 * joule = Btu = BTU = Btu_iso = 英热单位 -international_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * international_calorie = Btu_it = 国际英热单位 -thermochemical_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * calorie = Btu_th = 热化学英热单位 -quadrillion_Btu = 1e15 * Btu = quad = 兆英热单位 -therm = 1e5 * Btu = thm = EC_therm = 热量 -US_therm = 1.054804e8 * joule = 美国热量 -ton_TNT = 1e9 * calorie = tTNT = TNT当量 -tonne_of_oil_equivalent = 1e10 * international_calorie = toe = 吨油当量 -atmosphere_liter = atmosphere * liter = atm_l = 气体升 - -# Power -[power] = [energy] / [time] -watt = joule / second = W = 瓦特 -volt_ampere = volt * ampere = VA = 伏特安培 -horsepower = 550 * foot * force_pound / second = hp = UK_horsepower = hydraulic_horsepower = 马力 -boiler_horsepower = 33475 * Btu / hour = 锅炉马力 -metric_horsepower = 75 * force_kilogram * meter / second = 公制马力 -electrical_horsepower = 746 * watt = 电马力 -refrigeration_ton = 12e3 * Btu / hour = _ = ton_of_refrigeration = 制冷吨 -cooling_tower_ton = 1.25 * refrigeration_ton = 冷却塔吨 -standard_liter_per_minute = atmosphere * liter / minute = slpm = slm = 标准升每分钟 -conventional_watt_90 = K_J90 ** 2 * R_K90 / (K_J ** 2 * R_K) * watt = W_90 = 传统瓦特90 - -# Momentum -[momentum] = [length] * [mass] / [time] - -# Density (as auxiliary for pressure) -[density] = [mass] / [volume] -mercury = 13.5951 * kilogram / liter = Hg = Hg_0C = Hg_32F = conventional_mercury = 水银 -water = 1.0 * kilogram / liter = H2O = conventional_water = 水 -mercury_60F = 13.5568 * kilogram / liter = Hg_60F = 水银60华氏度 -water_39F = 0.999972 * kilogram / liter = water_4C = 水4摄氏度 -water_60F = 0.999001 * kilogram / liter = 水60华氏度 - -# Pressure -[pressure] = [force] / [area] -pascal = newton / meter ** 2 = Pa = 帕斯卡 -barye = dyne / centimeter ** 2 = Ba = barie = barad = barrie = baryd = 微巴 -bar = 1e5 * pascal = 巴 -technical_atmosphere = kilogram * g_0 / centimeter ** 2 = at = 工程大气压 -torr = atm / 760 = 托 -pound_force_per_square_inch = force_pound / inch ** 2 = psi = 磅力每平方英寸 -kip_per_square_inch = kip / inch ** 2 = ksi = 千磅力每平方英寸 -millimeter_Hg_0C = millimeter * Hg * g_0 = mmHg = mm_Hg = 毫米汞柱 -centimeter_Hg_0C = centimeter * Hg * g_0 = cmHg = cm_Hg = 厘米汞柱 -inch_Hg_32F = inch * Hg * g_0 = inHg = in_Hg = 英寸汞柱 -inch_Hg_60F = inch * Hg_60F * g_0 = 60华氏度英寸汞柱 -inch_H2O_39F = inch * water_39F * g_0 = 39华氏度英寸水柱 -inch_H2O_60F = inch * water_60F * g_0 = 60华氏度英寸水柱 -feet_H2O = foot * water * g_0 = ftH2O = 英尺水柱 -centimeter_H2O = cm_H2O = centimeter * water * g_0 = cmH2O = 厘米水柱 -sound_pressure_level = 20e-6 * pascal = SPL = 声压级 - -# Torque -[torque] = [force] * [length] -foot_pound = foot * force_pound = ft_lb = footpound = 英尺磅 - -# Viscosity -[viscosity] = [pressure] * [time] -poise = 0.1 * Pa * second = P = 泊伊斯 -reyn = psi * second = 雷恩 - -# Kinematic viscosity -[kinematic_viscosity] = [area] / [time] -stokes = centimeter ** 2 / second = St = 斯托克斯 - -# Fluidity -[fluidity] = 1 / [viscosity] -rhe = 1 / poise = 莱 - -# Amount of substance -particle = 1 / N_A = _ = molec = molecule = 粒子 - -# Concentration -[concentration] = [substance] / [volume] -molar = mole / liter = M = 体积摩尔 - -# Catalytic activity -[activity] = [substance] / [time] -katal = mole / second = kat = 开特 -enzyme_unit = micromole / minute = U = enzymeunit = 酶单位 - -# Entropy -[entropy] = [energy] / [temperature] -clausius = calorie / kelvin = Cl = 克劳西斯 - -# Molar entropy -[molar_entropy] = [entropy] / [substance] -entropy_unit = calorie / kelvin / mole = eu = 熵单位 - -# Radiation -becquerel = counts_per_second = Bq = 贝克勒尔 = 贝克 -curie = 3.7e10 * becquerel = Ci = 居里 -rutherford = 1e6 * becquerel = Rd = 卢瑟福 -gray = joule / kilogram = Gy = 戈瑞 -sievert = joule / kilogram = Sv = 希沃特 -rads = 0.01 * gray = 拉德斯 -rem = 0.01 * sievert = 雷质 -roentgen = 2.58e-4 * coulomb / kilogram = _ = röntgen = 伦琴 - -# Heat transimission -[heat_transmission] = [energy] / [area] -peak_sun_hour = 1e3 * watt_hour / meter ** 2 = PSH = 峰值太阳小时 -langley = thermochemical_calorie / centimeter ** 2 = Ly = 兰利 - -# Luminance -[luminance] = [luminosity] / [area] -nit = candela / meter ** 2 = 尼特 -stilb = candela / centimeter ** 2 = 斯蒂尔布 -lambert = 1 / π * candela / centimeter ** 2 = 兰伯特 - -# Luminous flux -[luminous_flux] = [luminosity] -lumen = candela * steradian = lm = 流明 - -# Illuminance -[illuminance] = [luminous_flux] / [area] -lux = lumen / meter ** 2 = lx = 勒克斯 - -# Intensity -[intensity] = [power] / [area] -atomic_unit_of_intensity = 0.5 * ε_0 * c * atomic_unit_of_electric_field ** 2 = a_u_intensity = 原子单位光强 - -# Current -biot = 10 * ampere = Bi = 毕奥 -abampere = biot = abA = 绝对安培 -atomic_unit_of_current = e / atomic_unit_of_time = a_u_current = 原子单位电流 -mean_international_ampere = mean_international_volt / mean_international_ohm = A_it = 平均国际安培 -US_international_ampere = US_international_volt / US_international_ohm = A_US = 美国国际安培 -conventional_ampere_90 = K_J90 * R_K90 / (K_J * R_K) * ampere = A_90 = 传统安培90 -planck_current = (c ** 6 / gravitational_constant / k_C) ** 0.5 = 普朗克电流 - -# Charge -[charge] = [current] * [time] -coulomb = ampere * second = C = 库仑 -abcoulomb = 10 * C = abC = 绝对库伦 -faraday = e * N_A * mole = 法拉第 -conventional_coulomb_90 = K_J90 * R_K90 / (K_J * R_K) * coulomb = C_90 = 传统库伦90 -ampere_hour = ampere * hour = Ah = 安培时 - -# Electric potential -[electric_potential] = [energy] / [charge] -volt = joule / coulomb = V = 伏特 = 伏 -abvolt = 1e-8 * volt = abV = 绝对伏特 -mean_international_volt = 1.00034 * volt = V_it = 平均国际伏特 # approximate -US_international_volt = 1.00033 * volt = V_US = 美国国际伏特 # approximate -conventional_volt_90 = K_J90 / K_J * volt = V_90 = 传统伏特90 - -# Electric field -[electric_field] = [electric_potential] / [length] -atomic_unit_of_electric_field = e * k_C / a_0 ** 2 = a_u_electric_field = 原子单位电场 - -# Electric displacement field -[electric_displacement_field] = [charge] / [area] - -# Resistance -[resistance] = [electric_potential] / [current] -ohm = volt / ampere = Ω = 欧姆 = 欧 -abohm = 1e-9 * ohm = abΩ = 绝对欧姆 -mean_international_ohm = 1.00049 * ohm = Ω_it = ohm_it = 平均国际欧姆 # approximate -US_international_ohm = 1.000495 * ohm = Ω_US = ohm_US = 美国国际欧姆 # approximate -conventional_ohm_90 = R_K / R_K90 * ohm = Ω_90 = ohm_90 = 传统欧姆90 - -# Resistivity -[resistivity] = [resistance] * [length] - -# Conductance -[conductance] = [current] / [electric_potential] -siemens = ampere / volt = S = mho = 西门子 = 西门 -absiemens = 1e9 * siemens = abS = abmho = 绝对西门子 - -# Capacitance -[capacitance] = [charge] / [electric_potential] -farad = coulomb / volt = F = 法拉 -abfarad = 1e9 * farad = abF = 绝对法拉 -conventional_farad_90 = R_K90 / R_K * farad = F_90 = 常用法拉_90 - -# Magnetic flux -[magnetic_flux] = [electric_potential] * [time] -weber = volt * second = Wb = 韦伯 -unit_pole = µ_0 * biot * centimeter = 单位极 - -# Inductance -[inductance] = [magnetic_flux] / [current] -henry = weber / ampere = H = 亨利 -abhenry = 1e-9 * henry = abH = 绝对亨利 -conventional_henry_90 = R_K / R_K90 * henry = H_90 = 常用亨利_90 - -# Magnetic field -[magnetic_field] = [magnetic_flux] / [area] -tesla = weber / meter ** 2 = T = 特斯拉 -gamma = 1e-9 * tesla = γ = 伽玛 - -# Magnetomotive force -[magnetomotive_force] = [current] -ampere_turn = ampere = At = 安培匝 -biot_turn = biot = 比奥特匝 -gilbert = 1 / (4 * π) * biot_turn = Gb = 吉尔伯特 - -# Magnetic field strength -[magnetic_field_strength] = [current] / [length] - -# Electric dipole moment -[electric_dipole] = [charge] * [length] -debye = 1e-9 / ζ * coulomb * angstrom = D = 德拜 # formally 1 D = 1e-10 Fr*Å, but we generally want to use it outside the Gaussian context - -# Electric quadrupole moment -[electric_quadrupole] = [charge] * [area] -buckingham = debye * angstrom = 巴金汉姆 - -# Magnetic dipole moment -[magnetic_dipole] = [current] * [area] -bohr_magneton = e * hbar / (2 * m_e) = µ_B = mu_B = 波尔磁子 -nuclear_magneton = e * hbar / (2 * m_p) = µ_N = mu_N = 核磁子 - -# Logaritmic Unit Definition -# Unit = scale; logbase; logfactor -# x_dB = [logfactor] * log( x_lin / [scale] ) / log( [logbase] ) - -# Logaritmic Units of dimensionless quantity: [ https://en.wikipedia.org/wiki/Level_(logarithmic_quantity) ] - -decibelmilliwatt = 1e-3 watt; logbase: 10; logfactor: 10 = dBm -decibelmicrowatt = 1e-6 watt; logbase: 10; logfactor: 10 = dBu - -decibel = 1 ; logbase: 10; logfactor: 10 = dB -# bell = 1 ; logbase: 10; logfactor: = B -## NOTE: B (Bell) symbol conflicts with byte - -decade = 1 ; logbase: 10; logfactor: 1 -## NOTE: decade [time] can conflict with decade [dimensionless] - -octave = 1 ; logbase: 2; logfactor: 1 = oct - -neper = 1 ; logbase: 2.71828182845904523536028747135266249775724709369995; logfactor: 0.5 = Np -# neper = 1 ; logbase: eulers_number; logfactor: 0.5 = Np - -#### UNIT GROUPS #### -# Mostly for length, area, volume, mass, force -# (customary or specialized units) - -@group USCSLengthInternational - thou = 1e-3 * inch = th = mil_length - inch = yard / 36 = in = international_inch = inches = international_inches - hand = 4 * inch - foot = yard / 3 = ft = international_foot = feet = international_feet - yard = 0.9144 * meter = yd = international_yard # since Jul 1959 - mile = 1760 * yard = mi = international_mile - - circular_mil = π / 4 * mil_length ** 2 = cmil - square_inch = inch ** 2 = sq_in = square_inches - square_foot = foot ** 2 = sq_ft = square_feet - square_yard = yard ** 2 = sq_yd - square_mile = mile ** 2 = sq_mi - - cubic_inch = in ** 3 = cu_in - cubic_foot = ft ** 3 = cu_ft = cubic_feet - cubic_yard = yd ** 3 = cu_yd -@end - -@group USCSLengthSurvey - link = 1e-2 * chain = li = survey_link - survey_foot = 1200 / 3937 * meter = sft - fathom = 6 * survey_foot - rod = 16.5 * survey_foot = rd = pole = perch - chain = 4 * rod - furlong = 40 * rod = fur - cables_length = 120 * fathom - survey_mile = 5280 * survey_foot = smi = us_statute_mile - league = 3 * survey_mile - - square_rod = rod ** 2 = sq_rod = sq_pole = sq_perch - acre = 10 * chain ** 2 - square_survey_mile = survey_mile ** 2 = _ = section - square_league = league ** 2 - - acre_foot = acre * survey_foot = _ = acre_feet -@end - -@group USCSDryVolume - dry_pint = bushel / 64 = dpi = US_dry_pint - dry_quart = bushel / 32 = dqt = US_dry_quart - dry_gallon = bushel / 8 = dgal = US_dry_gallon - peck = bushel / 4 = pk - bushel = 2150.42 cubic_inch = bu - dry_barrel = 7056 cubic_inch = _ = US_dry_barrel - board_foot = ft * ft * in = FBM = board_feet = BF = BDFT = super_foot = superficial_foot = super_feet = superficial_feet -@end - -@group USCSLiquidVolume - minim = pint / 7680 - fluid_dram = pint / 128 = fldr = fluidram = US_fluid_dram = US_liquid_dram - fluid_ounce = pint / 16 = floz = US_fluid_ounce = US_liquid_ounce - gill = pint / 4 = gi = liquid_gill = US_liquid_gill - pint = quart / 2 = pt = liquid_pint = US_pint - fifth = gallon / 5 = _ = US_liquid_fifth - quart = gallon / 4 = qt = liquid_quart = US_liquid_quart - gallon = 231 * cubic_inch = gal = liquid_gallon = US_liquid_gallon -@end - -@group USCSVolumeOther - teaspoon = fluid_ounce / 6 = tsp - tablespoon = fluid_ounce / 2 = tbsp - shot = 3 * tablespoon = jig = US_shot - cup = pint / 2 = cp = liquid_cup = US_liquid_cup - barrel = 31.5 * gallon = bbl - oil_barrel = 42 * gallon = oil_bbl - beer_barrel = 31 * gallon = beer_bbl - hogshead = 63 * gallon -@end - -@group Avoirdupois - dram = pound / 256 = dr = avoirdupois_dram = avdp_dram = drachm - ounce = pound / 16 = oz = avoirdupois_ounce = avdp_ounce - pound = 7e3 * grain = lb = avoirdupois_pound = avdp_pound - stone = 14 * pound - quarter = 28 * stone - bag = 94 * pound - hundredweight = 100 * pound = cwt = short_hundredweight - long_hundredweight = 112 * pound - ton = 2e3 * pound = _ = short_ton - long_ton = 2240 * pound - slug = g_0 * pound * second ** 2 / foot - slinch = g_0 * pound * second ** 2 / inch = blob = slugette - - force_ounce = g_0 * ounce = ozf = ounce_force - force_pound = g_0 * pound = lbf = pound_force - force_ton = g_0 * ton = _ = ton_force = force_short_ton = short_ton_force - force_long_ton = g_0 * long_ton = _ = long_ton_force - kip = 1e3 * force_pound - poundal = pound * foot / second ** 2 = pdl -@end - -@group AvoirdupoisUK using Avoirdupois - UK_hundredweight = long_hundredweight = UK_cwt - UK_ton = long_ton - UK_force_ton = force_long_ton = _ = UK_ton_force -@end - -@group AvoirdupoisUS using Avoirdupois - US_hundredweight = hundredweight = US_cwt - US_ton = ton - US_force_ton = force_ton = _ = US_ton_force -@end - -@group Troy - pennyweight = 24 * grain = dwt - troy_ounce = 480 * grain = toz = ozt - troy_pound = 12 * troy_ounce = tlb = lbt -@end - -@group Apothecary - scruple = 20 * grain - apothecary_dram = 3 * scruple = ap_dr - apothecary_ounce = 8 * apothecary_dram = ap_oz - apothecary_pound = 12 * apothecary_ounce = ap_lb -@end - -@group ImperialVolume - imperial_minim = imperial_fluid_ounce / 480 - imperial_fluid_scruple = imperial_fluid_ounce / 24 - imperial_fluid_drachm = imperial_fluid_ounce / 8 = imperial_fldr = imperial_fluid_dram - imperial_fluid_ounce = imperial_pint / 20 = imperial_floz = UK_fluid_ounce - imperial_gill = imperial_pint / 4 = imperial_gi = UK_gill - imperial_cup = imperial_pint / 2 = imperial_cp = UK_cup - imperial_pint = imperial_gallon / 8 = imperial_pt = UK_pint - imperial_quart = imperial_gallon / 4 = imperial_qt = UK_quart - imperial_gallon = 4.54609 * liter = imperial_gal = UK_gallon - imperial_peck = 2 * imperial_gallon = imperial_pk = UK_pk - imperial_bushel = 8 * imperial_gallon = imperial_bu = UK_bushel - imperial_barrel = 36 * imperial_gallon = imperial_bbl = UK_bbl -@end - -@group Printer - pica = inch / 6 = _ = printers_pica - point = pica / 12 = pp = printers_point = big_point = bp - didot = 1 / 2660 * m - cicero = 12 * didot - tex_point = inch / 72.27 - tex_pica = 12 * tex_point - tex_didot = 1238 / 1157 * tex_point - tex_cicero = 12 * tex_didot - scaled_point = tex_point / 65536 - css_pixel = inch / 96 = px - - pixel = [printing_unit] = _ = dot = pel = picture_element - pixels_per_centimeter = pixel / cm = PPCM - pixels_per_inch = pixel / inch = dots_per_inch = PPI = ppi = DPI = printers_dpi - bits_per_pixel = bit / pixel = bpp -@end - -@group Textile - tex = gram / kilometer = Tt - dtex = decitex - denier = gram / (9 * kilometer) = den = Td - jute = pound / (14400 * yard) = Tj - aberdeen = jute = Ta - RKM = gf / tex - - number_english = 840 * yard / pound = Ne = NeC = ECC - number_meter = kilometer / kilogram = Nm -@end - - -#### CGS ELECTROMAGNETIC UNITS #### - -# === Gaussian system of units === -@group Gaussian - franklin = erg ** 0.5 * centimeter ** 0.5 = Fr = statcoulomb = statC = esu - statvolt = erg / franklin = statV - statampere = franklin / second = statA - gauss = dyne / franklin = G - maxwell = gauss * centimeter ** 2 = Mx - oersted = dyne / maxwell = Oe = ørsted - statohm = statvolt / statampere = statΩ - statfarad = franklin / statvolt = statF - statmho = statampere / statvolt -@end -# Note this system is not commensurate with SI, as ε_0 and µ_0 disappear; -# some quantities with different dimensions in SI have the same -# dimensions in the Gaussian system (e.g. [Mx] = [Fr], but [Wb] != [C]), -# and therefore the conversion factors depend on the context (not in pint sense) -[gaussian_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time] -[gaussian_current] = [gaussian_charge] / [time] -[gaussian_electric_potential] = [gaussian_charge] / [length] -[gaussian_electric_field] = [gaussian_electric_potential] / [length] -[gaussian_electric_displacement_field] = [gaussian_charge] / [area] -[gaussian_electric_flux] = [gaussian_charge] -[gaussian_electric_dipole] = [gaussian_charge] * [length] -[gaussian_electric_quadrupole] = [gaussian_charge] * [area] -[gaussian_magnetic_field] = [force] / [gaussian_charge] -[gaussian_magnetic_field_strength] = [gaussian_magnetic_field] -[gaussian_magnetic_flux] = [gaussian_magnetic_field] * [area] -[gaussian_magnetic_dipole] = [energy] / [gaussian_magnetic_field] -[gaussian_resistance] = [gaussian_electric_potential] / [gaussian_current] -[gaussian_resistivity] = [gaussian_resistance] * [length] -[gaussian_capacitance] = [gaussian_charge] / [gaussian_electric_potential] -[gaussian_inductance] = [gaussian_electric_potential] * [time] / [gaussian_current] -[gaussian_conductance] = [gaussian_current] / [gaussian_electric_potential] -@context Gaussian = Gau - [gaussian_charge] -> [charge]: value / k_C ** 0.5 - [charge] -> [gaussian_charge]: value * k_C ** 0.5 - [gaussian_current] -> [current]: value / k_C ** 0.5 - [current] -> [gaussian_current]: value * k_C ** 0.5 - [gaussian_electric_potential] -> [electric_potential]: value * k_C ** 0.5 - [electric_potential] -> [gaussian_electric_potential]: value / k_C ** 0.5 - [gaussian_electric_field] -> [electric_field]: value * k_C ** 0.5 - [electric_field] -> [gaussian_electric_field]: value / k_C ** 0.5 - [gaussian_electric_displacement_field] -> [electric_displacement_field]: value / (4 * π / ε_0) ** 0.5 - [electric_displacement_field] -> [gaussian_electric_displacement_field]: value * (4 * π / ε_0) ** 0.5 - [gaussian_electric_dipole] -> [electric_dipole]: value / k_C ** 0.5 - [electric_dipole] -> [gaussian_electric_dipole]: value * k_C ** 0.5 - [gaussian_electric_quadrupole] -> [electric_quadrupole]: value / k_C ** 0.5 - [electric_quadrupole] -> [gaussian_electric_quadrupole]: value * k_C ** 0.5 - [gaussian_magnetic_field] -> [magnetic_field]: value / (4 * π / µ_0) ** 0.5 - [magnetic_field] -> [gaussian_magnetic_field]: value * (4 * π / µ_0) ** 0.5 - [gaussian_magnetic_flux] -> [magnetic_flux]: value / (4 * π / µ_0) ** 0.5 - [magnetic_flux] -> [gaussian_magnetic_flux]: value * (4 * π / µ_0) ** 0.5 - [gaussian_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π * µ_0) ** 0.5 - [magnetic_field_strength] -> [gaussian_magnetic_field_strength]: value * (4 * π * µ_0) ** 0.5 - [gaussian_magnetic_dipole] -> [magnetic_dipole]: value * (4 * π / µ_0) ** 0.5 - [magnetic_dipole] -> [gaussian_magnetic_dipole]: value / (4 * π / µ_0) ** 0.5 - [gaussian_resistance] -> [resistance]: value * k_C - [resistance] -> [gaussian_resistance]: value / k_C - [gaussian_resistivity] -> [resistivity]: value * k_C - [resistivity] -> [gaussian_resistivity]: value / k_C - [gaussian_capacitance] -> [capacitance]: value / k_C - [capacitance] -> [gaussian_capacitance]: value * k_C - [gaussian_inductance] -> [inductance]: value * k_C - [inductance] -> [gaussian_inductance]: value / k_C - [gaussian_conductance] -> [conductance]: value / k_C - [conductance] -> [gaussian_conductance]: value * k_C -@end - -# === ESU system of units === -# (where different from Gaussian) -# See note for Gaussian system too -@group ESU using Gaussian - statweber = statvolt * second = statWb - stattesla = statweber / centimeter ** 2 = statT - stathenry = statweber / statampere = statH -@end -[esu_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time] -[esu_current] = [esu_charge] / [time] -[esu_electric_potential] = [esu_charge] / [length] -[esu_magnetic_flux] = [esu_electric_potential] * [time] -[esu_magnetic_field] = [esu_magnetic_flux] / [area] -[esu_magnetic_field_strength] = [esu_current] / [length] -[esu_magnetic_dipole] = [esu_current] * [area] -@context ESU = esu - [esu_magnetic_field] -> [magnetic_field]: value * k_C ** 0.5 - [magnetic_field] -> [esu_magnetic_field]: value / k_C ** 0.5 - [esu_magnetic_flux] -> [magnetic_flux]: value * k_C ** 0.5 - [magnetic_flux] -> [esu_magnetic_flux]: value / k_C ** 0.5 - [esu_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π / ε_0) ** 0.5 - [magnetic_field_strength] -> [esu_magnetic_field_strength]: value * (4 * π / ε_0) ** 0.5 - [esu_magnetic_dipole] -> [magnetic_dipole]: value / k_C ** 0.5 - [magnetic_dipole] -> [esu_magnetic_dipole]: value * k_C ** 0.5 -@end - - -#### CONVERSION CONTEXTS #### - -@context(n=1) spectroscopy = sp - # n index of refraction of the medium. - [length] <-> [frequency]: speed_of_light / n / value - [frequency] -> [energy]: planck_constant * value - [energy] -> [frequency]: value / planck_constant - # allow wavenumber / kayser - [wavenumber] <-> [length]: 1 / value -@end - -@context boltzmann - [temperature] -> [energy]: boltzmann_constant * value - [energy] -> [temperature]: value / boltzmann_constant -@end - -@context energy - [energy] -> [energy] / [substance]: value * N_A - [energy] / [substance] -> [energy]: value / N_A - [energy] -> [mass]: value / c ** 2 - [mass] -> [energy]: value * c ** 2 -@end - -@context(mw=0,volume=0,solvent_mass=0) chemistry = chem - # mw is the molecular weight of the species - # volume is the volume of the solution - # solvent_mass is the mass of solvent in the solution - - # moles -> mass require the molecular weight - [substance] -> [mass]: value * mw - [mass] -> [substance]: value / mw - - # moles/volume -> mass/volume and moles/mass -> mass/mass - # require the molecular weight - [substance] / [volume] -> [mass] / [volume]: value * mw - [mass] / [volume] -> [substance] / [volume]: value / mw - [substance] / [mass] -> [mass] / [mass]: value * mw - [mass] / [mass] -> [substance] / [mass]: value / mw - - # moles/volume -> moles requires the solution volume - [substance] / [volume] -> [substance]: value * volume - [substance] -> [substance] / [volume]: value / volume - - # moles/mass -> moles requires the solvent (usually water) mass - [substance] / [mass] -> [substance]: value * solvent_mass - [substance] -> [substance] / [mass]: value / solvent_mass - - # moles/mass -> moles/volume require the solvent mass and the volume - [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume - [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume - -@end - -@context textile - # Allow switching between Direct count system (i.e. tex) and - # Indirect count system (i.e. Ne, Nm) - [mass] / [length] <-> [length] / [mass]: 1 / value -@end - - -#### SYSTEMS OF UNITS #### - -@system SI - second - meter - kilogram - ampere - kelvin - mole - candela -@end - -@system mks using international - meter - kilogram - second -@end - -@system cgs using international, Gaussian, ESU - centimeter - gram - second -@end - -@system atomic using international - # based on unit m_e, e, hbar, k_C, k - bohr: meter - electron_mass: gram - atomic_unit_of_time: second - atomic_unit_of_current: ampere - atomic_unit_of_temperature: kelvin -@end - -@system Planck using international - # based on unit c, gravitational_constant, hbar, k_C, k - planck_length: meter - planck_mass: gram - planck_time: second - planck_current: ampere - planck_temperature: kelvin -@end - -@system imperial using ImperialVolume, USCSLengthInternational, AvoirdupoisUK - yard - pound -@end - -@system US using USCSLiquidVolume, USCSDryVolume, USCSVolumeOther, USCSLengthInternational, USCSLengthSurvey, AvoirdupoisUS - yard - pound -@end diff --git a/modules/convert/locales/en_us.json b/modules/convert/locales/en_us.json deleted file mode 100644 index 091ac396ac..0000000000 --- a/modules/convert/locales/en_us.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "convert.help": "单位转换,大小写敏感。", - "convert.help.desc": "全能单位转换。", - "convert.message.error": "发生错误:无法转换单位,请检查输入。" -} \ No newline at end of file diff --git a/modules/convert/locales/zh_cn.json b/modules/convert/locales/zh_cn.json deleted file mode 100644 index 8cf2c430fe..0000000000 --- a/modules/convert/locales/zh_cn.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "convert.help": "单位转换,大小写敏感。", - "convert.help.desc": "全能单位转换。", - "convert.message.error": "发生错误:无法转换单位,请检查输入。" -} \ No newline at end of file diff --git a/modules/convert/locales/zh_tw.json b/modules/convert/locales/zh_tw.json deleted file mode 100644 index 999d3f6b11..0000000000 --- a/modules/convert/locales/zh_tw.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "convert.help": "單位轉換,區分大小寫。", - "convert.help.desc": "全能單位轉換。", - "convert.message.error": "發生錯誤:無法轉換單位,請校對輸入。" -} \ No newline at end of file diff --git a/modules/convert/old/constants_bi_zh-cn_en_old.txt b/modules/convert/old/constants_bi_zh-cn_en_old.txt deleted file mode 100644 index 52105b283a..0000000000 --- a/modules/convert/old/constants_bi_zh-cn_en_old.txt +++ /dev/null @@ -1,75 +0,0 @@ - -# Default Pint constants definition file -# Based on the International System of Units -# Language: english -# Source: https://physics.nist.gov/cuu/Constants/ -# https://physics.nist.gov/PhysRefData/XrayTrans/Html/search.html -# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details. - -#### MATHEMATICAL CONSTANTS #### -# As computed by Maxima with fpprec:50 - -pi = 3.1415926535897932384626433832795028841971693993751 = π = 派 = 圆周率 # pi -tansec = 4.8481368111333441675396429478852851658848753880815e-6 # tangent of 1 arc-second ~ arc_second/radian -ln10 = 2.3025850929940456840179914546843642076011014886288 # natural logarithm of 10 -wien_x = 4.9651142317442763036987591313228939440555849867973 # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5 -wien_u = 2.8214393721220788934031913302944851953458817440731 # solution to (u-3)*exp(u)+3 = 0 => u = W(3/exp(3))+3 -eulers_number = 2.71828182845904523536028747135266249775724709369995 = 欧拉数 - -#### DEFINED EXACT CONSTANTS #### - -speed_of_light = 299792458 m/s = c = c_0 = 光速 # since 1983 -planck_constant = 6.62607015e-34 J s = ℎ = 普朗克常数 = 普朗克常量 # since May 2019 -elementary_charge = 1.602176634e-19 C = e = 基本电荷 = 基本电量 = 元电荷 # since May 2019 -avogadro_number = 6.02214076e23 = 阿伏伽德罗数 # since May 2019 -boltzmann_constant = 1.380649e-23 J K^-1 = k = k_B = 玻尔兹曼常数 = 玻尔兹曼常量 # since May 2019 -standard_gravity = 9.80665 m/s^2 = g_0 = g0 = g_n = gravity = 标准重力 = 重力 = 重力加速度 # since 1901 -standard_atmosphere = 1.01325e5 Pa = atm = atmosphere = 标准大气压 = 大气压 # since 1954 -conventional_josephson_constant = 4.835979e14 Hz / V = K_J90 = 常规约瑟夫逊常数 = 常规约瑟夫逊常量 # since Jan 1990 -conventional_von_klitzing_constant = 2.5812807e4 ohm = R_K90 = 常规冯克利青常数 = 常规冯克利青常量 # since Jan 1990 - -#### DERIVED EXACT CONSTANTS #### -# Floating-point conversion may introduce inaccuracies - -zeta = c / (cm/s) = ζ -dirac_constant = ℎ / (2 * π) = ħ = hbar = atomic_unit_of_action = a_u_action = 约化普朗克常数 = 约化普朗克常量 = 狄拉克常数 = 狄拉克常量 -avogadro_constant = avogadro_number * mol^-1 = N_A = 阿伏伽德罗常数 = 阿伏伽德罗常量 -molar_gas_constant = k * N_A = R = 摩尔气体常数 = 通用气体常数 = 理想气体常数 = 普适气体常数 = 摩尔气体常量 = 通用气体常量 = 理想气体常量 = 普适气体常量 -faraday_constant = e * N_A = 法拉第常数 = 法拉第常量 -conductance_quantum = 2 * e ** 2 / ℎ = G_0 = 电导量子 -magnetic_flux_quantum = ℎ / (2 * e) = Φ_0 = Phi_0 = 磁通量量子 -josephson_constant = 2 * e / ℎ = K_J= 约瑟夫逊常数 = 约瑟夫逊常量 -von_klitzing_constant = ℎ / e ** 2 = R_K = 冯克利青常数 = 冯克利青常量 -stefan_boltzmann_constant = 2 / 15 * π ** 5 * k ** 4 / (ℎ ** 3 * c ** 2) = σ = sigma = 斯特藩-玻尔兹曼常数 = 斯特藩常数 = 斯特藩常量 = 斯特藩-玻尔兹曼常量 -first_radiation_constant = 2 * π * ℎ * c ** 2 = c_1 = 第一辐射常数 = 第一辐射常量 -second_radiation_constant = ℎ * c / k = c_2 = 第二辐射常数 = 第二辐射常量 -wien_wavelength_displacement_law_constant = ℎ * c / (k * wien_x) = 维恩波长位移常数 = 维恩波长位移常量 -wien_frequency_displacement_law_constant = wien_u * k / ℎ = 维恩频率位移常数 = 维恩频率位移常量 - -#### MEASURED CONSTANTS #### -# Recommended CODATA-2018 values -# To some extent, what is measured and what is derived is a bit arbitrary. -# The choice of measured constants is based on convenience and on available uncertainty. -# The uncertainty in the last significant digits is given in parentheses as a comment. - -newtonian_constant_of_gravitation = 6.67430e-11 m^3/(kg s^2) = _ = gravitational_constant = 牛顿重力常数 = 万有引力常数 = 重力常数 = 牛顿常数 = 牛顿重力常量 = 万有引力常量 = 重力常量 = 牛顿常量 # (15) -rydberg_constant = 1.0973731568160e7 * m^-1 = R_∞ = R_inf = 里德伯常量 = 里德伯常数 # (21) -electron_g_factor = -2.00231930436256 = g_e = 电子g因子 # (35) -atomic_mass_constant = 1.66053906660e-27 kg = m_u = 原子质量常数 = 原子质量常量 # (50) -electron_mass = 9.1093837015e-31 kg = m_e = atomic_unit_of_mass = a_u_mass = 电子质量 # (28) -proton_mass = 1.67262192369e-27 kg = m_p = 质子质量 # (51) -neutron_mass = 1.67492749804e-27 kg = m_n = 中子质量 # (95) -lattice_spacing_of_Si = 1.920155716e-10 m = d_220 = 硅的晶格常数 # (32) -K_alpha_Cu_d_220 = 0.80232719 # (22) -K_alpha_Mo_d_220 = 0.36940604 # (19) -K_alpha_W_d_220 = 0.108852175 # (98) - -#### DERIVED CONSTANTS #### - -fine_structure_constant = (2 * ℎ * R_inf / (m_e * c)) ** 0.5 = α = alpha = 精细结构常数 = 精细结构常量 -vacuum_permeability = 2 * α * ℎ / (e ** 2 * c) = µ_0 = mu_0 = mu0 = magnetic_constant = 真空磁导率 = 自由空间磁导率 = 磁常数 = 磁场常数 = 磁常量 = 磁场常量 -vacuum_permittivity = e ** 2 / (2 * α * ℎ * c) = ε_0 = epsilon_0 = eps_0 = eps0 = electric_constant = 真空电容率 = 真空介电系数 = 电常数 = 电常量 -impedance_of_free_space = 2 * α * ℎ / e ** 2 = Z_0 = characteristic_impedance_of_vacuum = 自由空间阻抗 -coulomb_constant = α * hbar * c / e ** 2 = k_C = 库仑常数 = 库仑常量 -classical_electron_radius = α * hbar / (m_e * c) = r_e = 经典电子半径 -thomson_cross_section = 8 / 3 * π * r_e ** 2 = σ_e = sigma_e = 汤姆逊散射截面 diff --git a/modules/convert/old/default_bi_zh-cn_en_old.txt b/modules/convert/old/default_bi_zh-cn_en_old.txt deleted file mode 100644 index 8ce1a0d6ca..0000000000 --- a/modules/convert/old/default_bi_zh-cn_en_old.txt +++ /dev/null @@ -1,889 +0,0 @@ -# Default Pint units definition file -# Based on the International System of Units -# Language: chinese -# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details. - -# Syntax -# ====== -# Units -# ----- -# = [= ] [= ] [ = ] [...] -# -# The canonical name and aliases should be expressed in singular form. -# Pint automatically deals with plurals built by adding 's' to the singular form; plural -# forms that don't follow this rule should be instead explicitly listed as aliases. -# -# If a unit has no symbol and one wants to define aliases, then the symbol should be -# conventionally set to _. -# -# Example: -# millennium = 1e3 * year = _ = millennia -# -# -# Prefixes -# -------- -# - = [= ] [= ] [ = ] [...] -# -# Example: -# deca- = 1e+1 = da- = deka- -# -# -# Derived dimensions -# ------------------ -# [dimension name] = -# -# Example: -# [density] = [mass] / [volume] -# -# Note that primary dimensions don't need to be declared; they can be -# defined for the first time in a unit definition. -# E.g. see below `meter = [length]` -# -# -# Additional aliases -# ------------------ -# @alias = [ = ] [...] -# -# Used to add aliases to already existing unit definitions. -# Particularly useful when one wants to enrich definitions -# from defaults_en.txt with custom aliases. -# -# Example: -# @alias meter = my_meter - -# See also: https://pint.readthedocs.io/en/latest/defining.html - -@defaults - group = international - system = mks -@end - - -#### PREFIXES #### - -# decimal prefixes -quecto- = 1e-30 = q- # zh-CN SI name not decided yet -ronto- = 1e-27 = r- # zh-CN SI name not decided yet -yocto- = 1e-24 = y- = 幺科托- = 幺- -zepto- = 1e-21 = z- = 仄普托- = 仄- -atto- = 1e-18 = a- = 阿托- = 阿- -femto- = 1e-15 = f- = 飞母托- = 飞- -pico- = 1e-12 = p- = 皮可- = 皮- -nano- = 1e-9 = n- = 纳诺- = 纳- -# The micro (U+00B5) and Greek mu (U+03BC) are both valid prefixes, -# and they often use the same glyph. -micro- = 1e-6 = µ- = μ- = u- = 微- -milli- = 1e-3 = m- = 毫- -centi- = 1e-2 = c- = 厘- -deci- = 1e-1 = d- = 分- -deca- = 1e+1 = da- = deka- = 十- -hecto- = 1e2 = h- = 百- -kilo- = 1e3 = k- = 千- -mega- = 1e6 = M- = 兆- -giga- = 1e9 = G- = 吉咖- = 吉- -tera- = 1e12 = T- = 太拉- = 太- -peta- = 1e15 = P- = 拍它- = 拍- -exa- = 1e18 = E- = 艾可萨- = 艾- -zetta- = 1e21 = Z- = 泽它- = 泽- -yotta- = 1e24 = Y- = 尧它- = 尧- -ronna- = 1e27 = R- # zh-CN SI name not decided yet -quetta- = 1e30 = Q- # zh-CN SI name not decided yet - -# binary_prefixes -kibi- = 2**10 = Ki- -mebi- = 2**20 = Mi- -gibi- = 2**30 = Gi- -tebi- = 2**40 = Ti- -pebi- = 2**50 = Pi- -exbi- = 2**60 = Ei- -zebi- = 2**70 = Zi- -yobi- = 2**80 = Yi- - -# extra_prefixes -semi- = 0.5 = _ = demi- = 半- -sesqui- = 1.5 - - -#### BASE UNITS #### - -米 = [length] = m = meter = metre -秒 = [time] = s = sec = second -安 = [current] = A = amp = 安培 = ampere -坎 = [luminosity] = cd = candle = 坎德拉 = candela -克 = [mass] = g = gram -摩 = [substance] = mol = 摩尔 = mole -开 = [temperature]; offset: 0 = K = degK = °K = degree_Kelvin = degreeK = 开尔文 = kelvin # older names supported for compatibility -弧度 = [] = rad = radian -比特 = [] = bit -个 = [] = count - - -#### CONSTANTS #### - -@import constants_bi_zh-cn_en.txt - - -#### UNITS #### -# Common and less common, grouped by quantity. -# Conversion factors are exact (except when noted), -# although floating-point conversion may introduce inaccuracies - -# Angle -转 = 2 * π * radian = _ = revolution = cycle = circle = turn -度 = degree = π / 180 * radian = deg = arcdeg = arcdegree = angular_degree = 角度 -角分 = degree / 60 = arcmin = arc_minute = angular_minute = 弧分 = arcminute -角秒 = arcsecond = arcminute / 60 = arcsec = arc_second = angular_second = 角秒 = arcsecond -毫角秒 = 1e-3 * arcsecond = mas = 毫弧秒 = milliarcsecond -梯度 = π / 200 * radian = grad = gon = 百分度 = grade -密位 = π / 32000 * radian = 毫弧度 = mil - -# Solid angle -球面度 = radian ** 2 = sr = steradian -平方度 = (π / 180) ** 2 * sr = sq_deg = sqdeg = square_degree - -# Information -波特 = bit / second = Bd = bps = 比特每秒 = baud - -字节 = 8 * bit = B = octet = byte -# byte = 8 * bit = _ = octet = 字节 -## NOTE: B (byte) symbol can conflict with Bell - -# Ratios -百分 = 0.01 = % = percent -分比 = 1e-6 = ppm - -# Length -埃 = 1e-10 * meter = Å = ångström = Å = 埃格斯特朗 = angstrom -微米 = micrometer = µ = μ = micron -费米 = femtometer = fm = fermi -光年 = speed_of_light * julian_year = ly = lightyear = light_year -天文单位 = 149597870700 * meter = au = astronomical_unit # since Aug 2012 -秒差距 = 1 / tansec * astronomical_unit = pc = parsec -海里 = 1852 * meter = nmi = nautical_mile -波尔 = hbar / (alpha * m_e * c) = a_0 = a0 = bohr_radius = atomic_unit_of_length = a_u_length = bohr -铜的X射线波长 = K_alpha_Cu_d_220 * d_220 / 1537.4 = Xu_Cu = 铜的X单位 = x_unit_Cu -钼的X射线波长 = K_alpha_Mo_d_220 * d_220 / 707.831 = Xu_Mo = 钼的X单位 = x_unit_Mo -埃星 = K_alpha_W_d_220 * d_220 / 0.2090100 = Å_star = angstrom_star -普朗克长度 = (hbar * gravitational_constant / c ** 3) ** 0.5 = planck_length - - -# Mass -公吨 = 1e3 * kilogram = t = tonne = metric_ton -原子质量单位 = atomic_mass_constant = u = amu = unified_atomic_mass_unit -道尔顿 = atomic_mass_constant = Da = dalton -格令 = 64.79891 * milligram = gr = grain -伽玛质量 = microgram = gamma_mass -克拉 = 200 * milligram = ct = karat = carat -普朗克质量 = (hbar * c / gravitational_constant) ** 0.5 = planck_mass - -# Time -分钟 = 60 * second = min = minute -小时 = 60 * minute = h = hr = hour -天 = 24 * hour = d = day -星期 = 7 * day = week -两星期 = 2 * week = fortnight -年 = 365.25 * day = a = yr = julian_year = year -月 = year / 12 = month - -# decade = 10 * year -## NOTE: decade [time] can conflict with decade [dimensionless] - -世纪 = 100 * year = _ = centuries = century -千年 = 1e3 * year = _ = millennia = millennium -宙 = 1e9 * year = eon -抖 = 1e-8 * second = shake -斯维德伯格 = 1e-13 * second = svedberg -原子单位时间 = hbar / E_h = a_u_time = atomic_unit_of_time -公历年 = 365.2425 * day = gregorian_year -恒星年 = 365.256363004 * day = sidereal_year -回归年 = 365.242190402 * day = tropical_year -平年 = 365 * day = common_year -闰年 = 366 * day = leap_year -恒星日 = day / 1.00273790935079524 = sidereal_day -恒星月 = 27.32166155 * day = sidereal_month -回归月 = 27.321582 * day = tropical_month -同步月 = 29.530589 * day = _ = lunar_month = synodic_month -普朗克时间 = (hbar * gravitational_constant / c ** 5) ** 0.5 = planck_time - -# Temperature -摄氏度 = kelvin; offset: 273.15 = °C = celsius = degC = degreeC = degree_Celsius -兰氏度 = 5 / 9 * kelvin; offset: 0 = °R = rankine = degR = degreeR = degree_Rankine -华氏度 = 5 / 9 * kelvin; offset: 233.15 + 200 / 9 = °F = fahrenheit = degF = degreeF = degree_Fahrenheit -列氏度 = 4 / 5 * kelvin; offset: 273.15 = °Re = reaumur = degRe = degreeRe = degree_Réaumur = réaumur -原子单位温度 = E_h / k = a_u_temp = atomic_unit_of_temperature -普朗克温度 = (hbar * c ** 5 / gravitational_constant / k ** 2) ** 0.5 = planck_temperature - -# Area -[area] = [length] ** 2 -are = 100 * meter ** 2 -barn = 1e-28 * meter ** 2 = b -darcy = centipoise * centimeter ** 2 / (second * atmosphere) -hectare = 100 * are = ha - -# Volume -[volume] = [length] ** 3 -公亩 = 100 * meter ** 2 = are -贝纳 = 1e-28 * meter ** 2 = b = barn -达西 = centipoise * centimeter ** 2 / (second * atmosphere) = darcy -公顷 = 100 * are = ha = hectare - -# Frequency -[frequency] = 1 / [time] -赫兹 = 1 / second = Hz = hertz -转每分 = revolution / minute = rpm = revolutions_per_minute -转每秒 = revolution / second = rps = revolutions_per_second -次每秒 = count / second = cps = counts_per_second - -# Wavenumber -[wavenumber] = 1 / [length] -倒数厘米 = 1 / cm = cm_1 = kayser = reciprocal_centimeter - -# Velocity -[velocity] = [length] / [time] -[speed] = [velocity] -节 = nautical_mile / hour = kt = knot_international = international_knot = knot -英里每小时 = mile / hour = mph = MPH = mile_per_hour -千米每小时 = kilometer / hour = kph = KPH = kilometer_per_hour -千米每秒 = kilometer / second = kps = kilometer_per_second -米每秒 = meter / second = mps = meter_per_second -英尺每秒 = foot / second = fps = foot_per_second - -# Volumetric Flow Rate -[volumetric_flow_rate] = [volume] / [time] -斯维德鲁普 = 1e6 * meter ** 3 / second = sv = sverdrup - -# Acceleration -[acceleration] = [velocity] / [time] -伽利略 = centimeter / second ** 2 = Gal = galileo - -# Force -[force] = [mass] * [acceleration] -牛顿 = kilogram * meter / second ** 2 = N = newton -丁 = gram * centimeter / second ** 2 = dyn = dyne -千克力 = g_0 * kilogram = kgf = kilogram_force = pond = force_kilogram -克力 = g_0 * gram = gf = gram_force = force_gram -公吨力 = g_0 * metric_ton = tf = metric_ton_force = force_t = t_force = force_metric_ton -原子单位力 = E_h / a_0 = a_u_force = atomic_unit_of_force - -# Energy -[energy] = [force] * [length] -焦耳 = newton * meter = J = joule -尔格 = dyne * centimeter = erg -瓦时 = watt * hour = Wh = watthour = watt_hour -电子伏特 = e * volt = eV = electron_volt -里德伯格 = ℎ * c * R_inf = Ry = rydberg -哈特里 = 2 * rydberg = E_h = Eh = hartree_energy = atomic_unit_of_energy = a_u_energy = hartree -卡路里 = 4.184 * joule = cal = thermochemical_calorie = cal_th = calorie -国际卡路里 = 4.1868 * joule = cal_it = international_steam_table_calorie = international_calorie -卡路里15度 = 4.1855 * joule = cal_15 = fifteen_degree_calorie -英热单位 = 1055.056 * joule = Btu = BTU = Btu_iso = british_thermal_unit -国际英热单位 = 1e3 * pound / kilogram * degR / kelvin * international_calorie = Btu_it = international_british_thermal_unit -热化学英热单位 = 1e3 * pound / kilogram * degR / kelvin * calorie = Btu_th = thermochemical_british_thermal_unit -兆英热单位 = 1e15 * Btu = quad = quadrillion_Btu -热量 = 1e5 * Btu = thm = EC_therm = therm -美国热量 = 1.054804e8 * joule = US_therm -吨TNT = 1e9 * calorie = tTNT = ton_TNT -吨油当量 = 1e10 * international_calorie = toe = tonne_of_oil_equivalent -气体升 = atmosphere * liter = atm_l = atmosphere_liter - -# Power -[power] = [energy] / [time] -瓦特 = joule / second = W = watt -伏特安培 = volt * ampere = VA = volt_ampere -马力 = 550 * foot * force_pound / second = hp = UK_horsepower = hydraulic_horsepower = horsepower -锅炉马力 = 33475 * Btu / hour = boiler_horsepower -公制马力 = 75 * force_kilogram * meter / second = metric_horsepower -电马力 = 746 * watt = electrical_horsepower -制冷吨 = 12e3 * Btu / hour = _ = ton_of_refrigeration = refrigeration_ton -冷却塔吨 = 1.25 * refrigeration_ton = cooling_tower_ton -标准升每分钟 = atmosphere * liter / minute = slpm = slm = standard_liter_per_minute -传统瓦特90 = K_J90 ** 2 * R_K90 / (K_J ** 2 * R_K) * watt = W_90 = conventional_watt_90 - -# Momentum -[momentum] = [length] * [mass] / [time] - -# Density (as auxiliary for pressure) -[density] = [mass] / [volume] -水银 = 13.5951 * kilogram / liter = Hg = Hg_0C = Hg_32F = conventional_mercury = mercury -水 = 1.0 * kilogram / liter = H2O = conventional_water = water -水银60华氏度 = 13.5568 * kilogram / liter = Hg_60F = mercury_60F -水4摄氏度 = 0.999972 * kilogram / liter = water_4C = water_39F -水60华氏度 = 0.999001 * kilogram / liter = water_60F - -# Pressure -[pressure] = [force] / [area] -帕斯卡 = newton / meter ** 2 = Pa = pascal -微巴 = dyne / centimeter ** 2 = Ba = barie = barad = barrie = baryd = barye -巴 = 1e5 * pascal = bar -技术大气 = kilogram * g_0 / centimeter ** 2 = at = technical_atmosphere -托尔 = atm / 760 = torr -每平方英寸磅力 = force_pound / inch ** 2 = psi = pound_force_per_square_inch -每平方英寸千磅力 = kip / inch ** 2 = ksi = kip_per_square_inch -毫米水银 = millimeter * Hg * g_0 = mmHg = mm_Hg = millimeter_Hg_0C -厘米水银 = centimeter * Hg * g_0 = cmHg = cm_Hg = centimeter_Hg_0C -英寸水银 = inch * Hg * g_0 = inHg = in_Hg = inch_Hg_32F -英寸水银60华氏度 = inch * Hg_60F * g_0 = inch_Hg_60F -英寸水39华氏度 = inch * water_39F * g_0 = inch_H2O_39F -英寸水60华氏度 = inch * water_60F * g_0 = inch_H2O_60F -英尺水 = foot * water * g_0 = ftH2O = feet_H2O -厘米水 = centimeter * water * g_0 = cmH2O = cm_H2O -声压级 = 20e-6 * pascal = SPL = sound_pressure_level - -# Torque -[torque] = [force] * [length] -英尺磅 = foot * force_pound = ft_lb = footpound = foot_pound - -# Viscosity -[viscosity] = [pressure] * [time] -泊伊斯 = 0.1 * Pa * second = P = poise -雷恩 = psi * second = reyn - -# Kinematic viscosity -[kinematic_viscosity] = [area] / [time] -斯托克斯 = centimeter ** 2 / second = St = stokes - -# Fluidity -[fluidity] = 1 / [viscosity] -莱 = 1 / poise = rhe - -# Amount of substance -粒子 = 1 / N_A = _ = molec = molecule = particle - -# Concentration -[concentration] = [substance] / [volume] -体积摩尔 = mole / liter = M = molar - -# Catalytic activity -[activity] = [substance] / [time] -卡塔尔 = mole / second = kat = katal -酶单位 = micromole / minute = U = enzymeunit = enzyme_unit - -# Entropy -[entropy] = [energy] / [temperature] -克劳西斯 = calorie / kelvin = Cl = clausius - -# Molar entropy -[molar_entropy] = [entropy] / [substance] -熵单位 = calorie / kelvin / mole = eu = entropy_unit - -# Radiation -贝可勒尔 = counts_per_second = Bq = becquerel -居里 = 3.7e10 * becquerel = Ci = curie -卢瑟福 = 1e6 * becquerel = Rd = rutherford -格雷 = joule / kilogram = Gy = gray -西弗 = joule / kilogram = Sv = sievert -拉德斯 = 0.01 * gray = rads -雷质 = 0.01 * sievert = rem -伦琴 = 2.58e-4 * coulomb / kilogram = _ = röntgen = roentgen - -# Heat transimission -[heat_transmission] = [energy] / [area] -峰值太阳小时 = 1e3 * watt_hour / meter ** 2 = PSH = peak_sun_hour -兰利 = thermochemical_calorie / centimeter ** 2 = Ly = langley - -# Luminance -[luminance] = [luminosity] / [area] -尼特 = candela / meter ** 2 = nit -斯蒂尔布 = candela / centimeter ** 2 = stilb -兰伯特 = 1 / π * candela / centimeter ** 2 = lambert - -# Luminous flux -[luminous_flux] = [luminosity] -流明 = candela * steradian = lm = lumen - -# Illuminance -[illuminance] = [luminous_flux] / [area] -勒克斯 = lumen / meter ** 2 = lx = lux - -# Intensity -[intensity] = [power] / [area] -原子单位光强 = 0.5 * ε_0 * c * atomic_unit_of_electric_field ** 2 = a_u_intensity = atomic_unit_of_intensity - -# Current -比奥 = 10 * ampere = Bi = biot -绝对安培 = biot = abA = abampere -原子单位电流 = e / atomic_unit_of_time = a_u_current = atomic_unit_of_current -平均国际安培 = mean_international_volt / mean_international_ohm = A_it = mean_international_ampere -美国国际安培 = US_international_volt / US_international_ohm = A_US = US_international_ampere -传统安培90 = K_J90 * R_K90 / (K_J * R_K) * ampere = A_90 = conventional_ampere_90 -普朗克电流 = (c ** 6 / gravitational_constant / k_C) ** 0.5 = planck_current - -# Charge -[charge] = [current] * [time] -库仑 = ampere * second = C = coulomb -绝对库伦 = 10 * C = abC = abcoulomb -法拉第 = e * N_A * mole = faraday -传统库伦90 = K_J90 * R_K90 / (K_J * R_K) * coulomb = C_90 = conventional_coulomb_90 -安培时 = ampere * hour = Ah = ampere_hour - -# Electric potential -[electric_potential] = [energy] / [charge] -伏特 = joule / coulomb = V = volt -绝对伏特 = 1e-8 * volt = abV = abvolt -平均国际伏特 = 1.00034 * volt = V_it = mean_international_volt # approximate -美国国际伏特 = 1.00033 * volt = V_US = US_international_volt # approximate -传统伏特90 = K_J90 / K_J * volt = V_90 = conventional_volt_90 - -# Electric field -[electric_field] = [electric_potential] / [length] -原子单位电场 = e * k_C / a_0 ** 2 = a_u_electric_field = atomic_unit_of_electric_field - -# Electric displacement field -[electric_displacement_field] = [charge] / [area] - -# Resistance -[resistance] = [electric_potential] / [current] -欧姆 = volt / ampere = Ω = ohm -绝对欧姆 = 1e-9 * ohm = abΩ = abohm -平均国际欧姆 = 1.00049 * ohm = Ω_it = ohm_it = mean_international_ohm # approximate -美国国际欧姆 = 1.000495 * ohm = Ω_US = ohm_US = US_international_ohm # approximate -传统欧姆90 = R_K / R_K90 * ohm = Ω_90 = ohm_90 = conventional_ohm_90 - -# Resistivity -[resistivity] = [resistance] * [length] - -# Conductance -[conductance] = [current] / [electric_potential] -西门子 = ampere / volt = S = mho = siemens -绝对西门子 = 1e9 * siemens = abS = abmho = absiemens - -# Capacitance -[capacitance] = [charge] / [electric_potential] -法拉 = coulomb / volt = F = farad -绝对法拉 = 1e9 * farad = abF = abfarad -常用法拉_90 = R_K90 / R_K * farad = F_90 = conventional_farad_90 - -# Magnetic flux -[magnetic_flux] = [electric_potential] * [time] -韦伯 = volt * second = Wb = weber -单位极 = µ_0 * biot * centimeter = unit_pole - -# Inductance -[inductance] = [magnetic_flux] / [current] -亨利 = weber / ampere = H = henry -绝对亨利 = 1e-9 * henry = abH = abhenry -常用亨利_90 = R_K / R_K90 * henry = H_90 = conventional_henry_90 - -# Magnetic field -[magnetic_field] = [magnetic_flux] / [area] -特斯拉 = weber / meter ** 2 = T = tesla -伽玛 = 1e-9 * tesla = γ = gamma - -# Magnetomotive force -[magnetomotive_force] = [current] -安培匝 = ampere = At = ampere_turn -比奥特匝 = biot = biot_turn -吉尔伯特 = 1 / (4 * π) * biot_turn = Gb = gilbert - -# Magnetic field strength -[magnetic_field_strength] = [current] / [length] - -# Electric dipole moment -[electric_dipole] = [charge] * [length] -德拜 = 1e-9 / ζ * coulomb * angstrom = D = debye # formally 1 D = 1e-10 Fr*Å, but we generally want to use it outside the Gaussian context - -# Electric quadrupole moment -[electric_quadrupole] = [charge] * [area] -巴金汉姆 = debye * angstrom = buckingham - -# Magnetic dipole moment -[magnetic_dipole] = [current] * [area] -波尔磁子 = e * hbar / (2 * m_e) = µ_B = mu_B = bohr_magneton -核磁子 = e * hbar / (2 * m_p) = µ_N = mu_N = nuclear_magneton - -# Logaritmic Unit Definition -# Unit = scale; logbase; logfactor -# x_dB = [logfactor] * log( x_lin / [scale] ) / log( [logbase] ) - -# Logaritmic Units of dimensionless quantity: [ https://en.wikipedia.org/wiki/Level_(logarithmic_quantity) ] - -分贝毫瓦 = 1e-3 watt; logbase: 10; logfactor: 10 = dBm = decibelmilliwatt -分贝微瓦 = 1e-6 watt; logbase: 10; logfactor: 10 = dBu = decibelmicrowatt - -decibel = 1 ; logbase: 10; logfactor: 10 = dB -# bell = 1 ; logbase: 10; logfactor: = B -## NOTE: B (Bell) symbol conflicts with byte - -decade = 1 ; logbase: 10; logfactor: 1 -## NOTE: decade [time] can conflict with decade [dimensionless] - -octave = 1 ; logbase: 2; logfactor: 1 = oct - -neper = 1 ; logbase: 2.71828182845904523536028747135266249775724709369995; logfactor: 0.5 = Np -# neper = 1 ; logbase: eulers_number; logfactor: 0.5 = Np - -#### UNIT GROUPS #### -# Mostly for length, area, volume, mass, force -# (customary or specialized units) - -@group USCSLengthInternational - thou = 1e-3 * inch = th = mil_length - inch = yard / 36 = in = international_inch = inches = international_inches - hand = 4 * inch - foot = yard / 3 = ft = international_foot = feet = international_feet - yard = 0.9144 * meter = yd = international_yard # since Jul 1959 - mile = 1760 * yard = mi = international_mile - - circular_mil = π / 4 * mil_length ** 2 = cmil - square_inch = inch ** 2 = sq_in = square_inches - square_foot = foot ** 2 = sq_ft = square_feet - square_yard = yard ** 2 = sq_yd - square_mile = mile ** 2 = sq_mi - - cubic_inch = in ** 3 = cu_in - cubic_foot = ft ** 3 = cu_ft = cubic_feet - cubic_yard = yd ** 3 = cu_yd -@end - -@group USCSLengthSurvey - link = 1e-2 * chain = li = survey_link - survey_foot = 1200 / 3937 * meter = sft - fathom = 6 * survey_foot - rod = 16.5 * survey_foot = rd = pole = perch - chain = 4 * rod - furlong = 40 * rod = fur - cables_length = 120 * fathom - survey_mile = 5280 * survey_foot = smi = us_statute_mile - league = 3 * survey_mile - - square_rod = rod ** 2 = sq_rod = sq_pole = sq_perch - acre = 10 * chain ** 2 - square_survey_mile = survey_mile ** 2 = _ = section - square_league = league ** 2 - - acre_foot = acre * survey_foot = _ = acre_feet -@end - -@group USCSDryVolume - dry_pint = bushel / 64 = dpi = US_dry_pint - dry_quart = bushel / 32 = dqt = US_dry_quart - dry_gallon = bushel / 8 = dgal = US_dry_gallon - peck = bushel / 4 = pk - bushel = 2150.42 cubic_inch = bu - dry_barrel = 7056 cubic_inch = _ = US_dry_barrel - board_foot = ft * ft * in = FBM = board_feet = BF = BDFT = super_foot = superficial_foot = super_feet = superficial_feet -@end - -@group USCSLiquidVolume - minim = pint / 7680 - fluid_dram = pint / 128 = fldr = fluidram = US_fluid_dram = US_liquid_dram - fluid_ounce = pint / 16 = floz = US_fluid_ounce = US_liquid_ounce - gill = pint / 4 = gi = liquid_gill = US_liquid_gill - pint = quart / 2 = pt = liquid_pint = US_pint - fifth = gallon / 5 = _ = US_liquid_fifth - quart = gallon / 4 = qt = liquid_quart = US_liquid_quart - gallon = 231 * cubic_inch = gal = liquid_gallon = US_liquid_gallon -@end - -@group USCSVolumeOther - teaspoon = fluid_ounce / 6 = tsp - tablespoon = fluid_ounce / 2 = tbsp - shot = 3 * tablespoon = jig = US_shot - cup = pint / 2 = cp = liquid_cup = US_liquid_cup - barrel = 31.5 * gallon = bbl - oil_barrel = 42 * gallon = oil_bbl - beer_barrel = 31 * gallon = beer_bbl - hogshead = 63 * gallon -@end - -@group Avoirdupois - dram = pound / 256 = dr = avoirdupois_dram = avdp_dram = drachm - ounce = pound / 16 = oz = avoirdupois_ounce = avdp_ounce - pound = 7e3 * grain = lb = avoirdupois_pound = avdp_pound - stone = 14 * pound - quarter = 28 * stone - bag = 94 * pound - hundredweight = 100 * pound = cwt = short_hundredweight - long_hundredweight = 112 * pound - ton = 2e3 * pound = _ = short_ton - long_ton = 2240 * pound - slug = g_0 * pound * second ** 2 / foot - slinch = g_0 * pound * second ** 2 / inch = blob = slugette - - force_ounce = g_0 * ounce = ozf = ounce_force - force_pound = g_0 * pound = lbf = pound_force - force_ton = g_0 * ton = _ = ton_force = force_short_ton = short_ton_force - force_long_ton = g_0 * long_ton = _ = long_ton_force - kip = 1e3 * force_pound - poundal = pound * foot / second ** 2 = pdl -@end - -@group AvoirdupoisUK using Avoirdupois - UK_hundredweight = long_hundredweight = UK_cwt - UK_ton = long_ton - UK_force_ton = force_long_ton = _ = UK_ton_force -@end - -@group AvoirdupoisUS using Avoirdupois - US_hundredweight = hundredweight = US_cwt - US_ton = ton - US_force_ton = force_ton = _ = US_ton_force -@end - -@group Troy - pennyweight = 24 * grain = dwt - troy_ounce = 480 * grain = toz = ozt - troy_pound = 12 * troy_ounce = tlb = lbt -@end - -@group Apothecary - scruple = 20 * grain - apothecary_dram = 3 * scruple = ap_dr - apothecary_ounce = 8 * apothecary_dram = ap_oz - apothecary_pound = 12 * apothecary_ounce = ap_lb -@end - -@group ImperialVolume - imperial_minim = imperial_fluid_ounce / 480 - imperial_fluid_scruple = imperial_fluid_ounce / 24 - imperial_fluid_drachm = imperial_fluid_ounce / 8 = imperial_fldr = imperial_fluid_dram - imperial_fluid_ounce = imperial_pint / 20 = imperial_floz = UK_fluid_ounce - imperial_gill = imperial_pint / 4 = imperial_gi = UK_gill - imperial_cup = imperial_pint / 2 = imperial_cp = UK_cup - imperial_pint = imperial_gallon / 8 = imperial_pt = UK_pint - imperial_quart = imperial_gallon / 4 = imperial_qt = UK_quart - imperial_gallon = 4.54609 * liter = imperial_gal = UK_gallon - imperial_peck = 2 * imperial_gallon = imperial_pk = UK_pk - imperial_bushel = 8 * imperial_gallon = imperial_bu = UK_bushel - imperial_barrel = 36 * imperial_gallon = imperial_bbl = UK_bbl -@end - -@group Printer - pica = inch / 6 = _ = printers_pica - point = pica / 12 = pp = printers_point = big_point = bp - didot = 1 / 2660 * m - cicero = 12 * didot - tex_point = inch / 72.27 - tex_pica = 12 * tex_point - tex_didot = 1238 / 1157 * tex_point - tex_cicero = 12 * tex_didot - scaled_point = tex_point / 65536 - css_pixel = inch / 96 = px - - pixel = [printing_unit] = _ = dot = pel = picture_element - pixels_per_centimeter = pixel / cm = PPCM - pixels_per_inch = pixel / inch = dots_per_inch = PPI = ppi = DPI = printers_dpi - bits_per_pixel = bit / pixel = bpp -@end - -@group Textile - tex = gram / kilometer = Tt - dtex = decitex - denier = gram / (9 * kilometer) = den = Td - jute = pound / (14400 * yard) = Tj - aberdeen = jute = Ta - RKM = gf / tex - - number_english = 840 * yard / pound = Ne = NeC = ECC - number_meter = kilometer / kilogram = Nm -@end - - -#### CGS ELECTROMAGNETIC UNITS #### - -# === Gaussian system of units === -@group Gaussian - franklin = erg ** 0.5 * centimeter ** 0.5 = Fr = statcoulomb = statC = esu - statvolt = erg / franklin = statV - statampere = franklin / second = statA - gauss = dyne / franklin = G - maxwell = gauss * centimeter ** 2 = Mx - oersted = dyne / maxwell = Oe = ørsted - statohm = statvolt / statampere = statΩ - statfarad = franklin / statvolt = statF - statmho = statampere / statvolt -@end -# Note this system is not commensurate with SI, as ε_0 and µ_0 disappear; -# some quantities with different dimensions in SI have the same -# dimensions in the Gaussian system (e.g. [Mx] = [Fr], but [Wb] != [C]), -# and therefore the conversion factors depend on the context (not in pint sense) -[gaussian_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time] -[gaussian_current] = [gaussian_charge] / [time] -[gaussian_electric_potential] = [gaussian_charge] / [length] -[gaussian_electric_field] = [gaussian_electric_potential] / [length] -[gaussian_electric_displacement_field] = [gaussian_charge] / [area] -[gaussian_electric_flux] = [gaussian_charge] -[gaussian_electric_dipole] = [gaussian_charge] * [length] -[gaussian_electric_quadrupole] = [gaussian_charge] * [area] -[gaussian_magnetic_field] = [force] / [gaussian_charge] -[gaussian_magnetic_field_strength] = [gaussian_magnetic_field] -[gaussian_magnetic_flux] = [gaussian_magnetic_field] * [area] -[gaussian_magnetic_dipole] = [energy] / [gaussian_magnetic_field] -[gaussian_resistance] = [gaussian_electric_potential] / [gaussian_current] -[gaussian_resistivity] = [gaussian_resistance] * [length] -[gaussian_capacitance] = [gaussian_charge] / [gaussian_electric_potential] -[gaussian_inductance] = [gaussian_electric_potential] * [time] / [gaussian_current] -[gaussian_conductance] = [gaussian_current] / [gaussian_electric_potential] -@context Gaussian = Gau - [gaussian_charge] -> [charge]: value / k_C ** 0.5 - [charge] -> [gaussian_charge]: value * k_C ** 0.5 - [gaussian_current] -> [current]: value / k_C ** 0.5 - [current] -> [gaussian_current]: value * k_C ** 0.5 - [gaussian_electric_potential] -> [electric_potential]: value * k_C ** 0.5 - [electric_potential] -> [gaussian_electric_potential]: value / k_C ** 0.5 - [gaussian_electric_field] -> [electric_field]: value * k_C ** 0.5 - [electric_field] -> [gaussian_electric_field]: value / k_C ** 0.5 - [gaussian_electric_displacement_field] -> [electric_displacement_field]: value / (4 * π / ε_0) ** 0.5 - [electric_displacement_field] -> [gaussian_electric_displacement_field]: value * (4 * π / ε_0) ** 0.5 - [gaussian_electric_dipole] -> [electric_dipole]: value / k_C ** 0.5 - [electric_dipole] -> [gaussian_electric_dipole]: value * k_C ** 0.5 - [gaussian_electric_quadrupole] -> [electric_quadrupole]: value / k_C ** 0.5 - [electric_quadrupole] -> [gaussian_electric_quadrupole]: value * k_C ** 0.5 - [gaussian_magnetic_field] -> [magnetic_field]: value / (4 * π / µ_0) ** 0.5 - [magnetic_field] -> [gaussian_magnetic_field]: value * (4 * π / µ_0) ** 0.5 - [gaussian_magnetic_flux] -> [magnetic_flux]: value / (4 * π / µ_0) ** 0.5 - [magnetic_flux] -> [gaussian_magnetic_flux]: value * (4 * π / µ_0) ** 0.5 - [gaussian_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π * µ_0) ** 0.5 - [magnetic_field_strength] -> [gaussian_magnetic_field_strength]: value * (4 * π * µ_0) ** 0.5 - [gaussian_magnetic_dipole] -> [magnetic_dipole]: value * (4 * π / µ_0) ** 0.5 - [magnetic_dipole] -> [gaussian_magnetic_dipole]: value / (4 * π / µ_0) ** 0.5 - [gaussian_resistance] -> [resistance]: value * k_C - [resistance] -> [gaussian_resistance]: value / k_C - [gaussian_resistivity] -> [resistivity]: value * k_C - [resistivity] -> [gaussian_resistivity]: value / k_C - [gaussian_capacitance] -> [capacitance]: value / k_C - [capacitance] -> [gaussian_capacitance]: value * k_C - [gaussian_inductance] -> [inductance]: value * k_C - [inductance] -> [gaussian_inductance]: value / k_C - [gaussian_conductance] -> [conductance]: value / k_C - [conductance] -> [gaussian_conductance]: value * k_C -@end - -# === ESU system of units === -# (where different from Gaussian) -# See note for Gaussian system too -@group ESU using Gaussian - statweber = statvolt * second = statWb - stattesla = statweber / centimeter ** 2 = statT - stathenry = statweber / statampere = statH -@end -[esu_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time] -[esu_current] = [esu_charge] / [time] -[esu_electric_potential] = [esu_charge] / [length] -[esu_magnetic_flux] = [esu_electric_potential] * [time] -[esu_magnetic_field] = [esu_magnetic_flux] / [area] -[esu_magnetic_field_strength] = [esu_current] / [length] -[esu_magnetic_dipole] = [esu_current] * [area] -@context ESU = esu - [esu_magnetic_field] -> [magnetic_field]: value * k_C ** 0.5 - [magnetic_field] -> [esu_magnetic_field]: value / k_C ** 0.5 - [esu_magnetic_flux] -> [magnetic_flux]: value * k_C ** 0.5 - [magnetic_flux] -> [esu_magnetic_flux]: value / k_C ** 0.5 - [esu_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π / ε_0) ** 0.5 - [magnetic_field_strength] -> [esu_magnetic_field_strength]: value * (4 * π / ε_0) ** 0.5 - [esu_magnetic_dipole] -> [magnetic_dipole]: value / k_C ** 0.5 - [magnetic_dipole] -> [esu_magnetic_dipole]: value * k_C ** 0.5 -@end - - -#### CONVERSION CONTEXTS #### - -@context(n=1) spectroscopy = sp - # n index of refraction of the medium. - [length] <-> [frequency]: speed_of_light / n / value - [frequency] -> [energy]: planck_constant * value - [energy] -> [frequency]: value / planck_constant - # allow wavenumber / kayser - [wavenumber] <-> [length]: 1 / value -@end - -@context boltzmann - [temperature] -> [energy]: boltzmann_constant * value - [energy] -> [temperature]: value / boltzmann_constant -@end - -@context energy - [energy] -> [energy] / [substance]: value * N_A - [energy] / [substance] -> [energy]: value / N_A - [energy] -> [mass]: value / c ** 2 - [mass] -> [energy]: value * c ** 2 -@end - -@context(mw=0,volume=0,solvent_mass=0) chemistry = chem - # mw is the molecular weight of the species - # volume is the volume of the solution - # solvent_mass is the mass of solvent in the solution - - # moles -> mass require the molecular weight - [substance] -> [mass]: value * mw - [mass] -> [substance]: value / mw - - # moles/volume -> mass/volume and moles/mass -> mass/mass - # require the molecular weight - [substance] / [volume] -> [mass] / [volume]: value * mw - [mass] / [volume] -> [substance] / [volume]: value / mw - [substance] / [mass] -> [mass] / [mass]: value * mw - [mass] / [mass] -> [substance] / [mass]: value / mw - - # moles/volume -> moles requires the solution volume - [substance] / [volume] -> [substance]: value * volume - [substance] -> [substance] / [volume]: value / volume - - # moles/mass -> moles requires the solvent (usually water) mass - [substance] / [mass] -> [substance]: value * solvent_mass - [substance] -> [substance] / [mass]: value / solvent_mass - - # moles/mass -> moles/volume require the solvent mass and the volume - [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume - [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume - -@end - -@context textile - # Allow switching between Direct count system (i.e. tex) and - # Indirect count system (i.e. Ne, Nm) - [mass] / [length] <-> [length] / [mass]: 1 / value -@end - - -#### SYSTEMS OF UNITS #### - -@system SI - second - meter - kilogram - ampere - kelvin - mole - candela -@end - -@system mks using international - meter - kilogram - second -@end - -@system cgs using international, Gaussian, ESU - centimeter - gram - second -@end - -@system atomic using international - # based on unit m_e, e, hbar, k_C, k - bohr: meter - electron_mass: gram - atomic_unit_of_time: second - atomic_unit_of_current: ampere - atomic_unit_of_temperature: kelvin -@end - -@system Planck using international - # based on unit c, gravitational_constant, hbar, k_C, k - planck_length: meter - planck_mass: gram - planck_time: second - planck_current: ampere - planck_temperature: kelvin -@end - -@system imperial using ImperialVolume, USCSLengthInternational, AvoirdupoisUK - yard - pound -@end - -@system US using USCSLiquidVolume, USCSDryVolume, USCSVolumeOther, USCSLengthInternational, USCSLengthSurvey, AvoirdupoisUS - yard - pound -@end diff --git a/modules/core/__init__.py b/modules/core/__init__.py index 614eddc69e..ba5c9d3a84 100644 --- a/modules/core/__init__.py +++ b/modules/core/__init__.py @@ -2,5 +2,4 @@ from .modules import * from .prefix import * from .su_utils import * -from .update import * from .utils import * diff --git a/modules/core/alias.py b/modules/core/alias.py index ea5d87d783..40300724fe 100644 --- a/modules/core/alias.py +++ b/modules/core/alias.py @@ -1,4 +1,4 @@ -from core.builtins import Bot, Image, command_prefix +from core.builtins import Bot, Image from core.component import module from core.utils.image_table import image_table_render, ImageTable @@ -14,7 +14,7 @@ async def set_alias(msg: Bot.MessageSession): aliases = msg.options.get('command_alias') alias = msg.parsed_msg.get('', False) command = msg.parsed_msg.get('', False) - if aliases is None: + if not aliases: aliases = {} if 'add' in msg.parsed_msg: if alias not in aliases: @@ -25,7 +25,7 @@ async def set_alias(msg: Bot.MessageSession): break if not has_prefix: await msg.finish(msg.locale.t("core.message.alias.add.invalid_prefix")) - command = command_prefix[0] + command[1:] + command = msg.prefixes[0] + command[1:] aliases[alias] = command msg.data.edit_option('command_alias', aliases) await msg.finish(msg.locale.t("core.message.alias.add.success", alias=alias, command=command)) @@ -55,7 +55,7 @@ async def set_alias(msg: Bot.MessageSession): await msg.finish([msg.locale.t("core.message.alias.list"), Image(img)]) else: pass - + if legacy: - await msg.finish(f'{msg.locale.t("core.message.alias.list")}\n' - + '\n'.join([f'{k} -> {aliases[k]}' for k in aliases])) + await msg.finish(f'{msg.locale.t("core.message.alias.list")}\n' + + '\n'.join([f'{k} -> {aliases[k]}' for k in aliases])) diff --git a/modules/core/locales/en_us.json b/modules/core/locales/en_us.json index 40e838d354..d0848c14d8 100644 --- a/modules/core/locales/en_us.json +++ b/modules/core/locales/en_us.json @@ -1,7 +1,7 @@ { - "core.help.admin": "Commands available to group administrators.", "core.help.admin.add": "Set members as bot administrators, implement the function of managing the robot without setting members as group administrators. It's no longer needed to set up it when you are already a group administrator.", "core.help.admin.ban": "Limit someone to use bot in the group.", + "core.help.admin.desc": "Commands available to group administrators.", "core.help.admin.list": "View all bot administrators.", "core.help.admin.remove": "Remove bot administrator from member.", "core.help.admin.unban": "Remove limit someone to use bot in the group.", @@ -10,29 +10,30 @@ "core.help.alias.list.legacy": "View custom command alias. (Legacy)", "core.help.alias.remove": "Remove custom command alias.", "core.help.alias.reset": "Reset custom command alias.", + "core.help.help": "View help list.", + "core.help.help.detail": "View details of a module.", + "core.help.help.legacy": "View help list. (Legacy)", "core.help.leave": "Let the bot leave the group.", - "core.help.locale": "View bot running language.", - "core.help.locale.set": "Set bot running language.", + "core.help.locale.desc": "Control bot language.", + "core.help.locale.set": "Set the bot running languages.", "core.help.module.disable": "Disable single/multiple module(s).", "core.help.module.disable_all": "Disable all modules.", "core.help.module.enable": "Enable single/multiple module(s).", "core.help.module.enable_all": "Enable all modules.", - "core.help.module.help": "View help list.", - "core.help.module.help.detail": "View details of a module.", - "core.help.module.help.legacy": "View help list. (Legacy)", "core.help.module.list": "View all available modules.", "core.help.module.list.legacy": "View all available modules. (Legacy)", "core.help.mute": "Make the bot stop sending message.", "core.help.option.module.g": "Perform global operations of the channels.", "core.help.petal": "Get the number of petals in the current group.", "core.help.ping": "Get bot status.", - "core.help.prefix.add": "Set custom command prefix.", + "core.help.prefix.add": "Add custom command prefix.", "core.help.prefix.list": "View custom command prefixes.", "core.help.prefix.remove": "Remove custom command prefix.", "core.help.prefix.reset": "Reset custom command prefix.", - "core.help.toggle.check": "Toggles whether to display command check prompts.", - "core.help.toggle.typing": "Toggle whether to display input prompts.", - "core.help.toggle.timeoffset": "Set the time offset.", + "core.help.setup.check": "Set up whether to display command check prompts.", + "core.help.setup.desc": "Set up bot actions.", + "core.help.setup.timeoffset": "Set the time offset.", + "core.help.setup.typing": "Set up whether to display input prompts.", "core.help.version": "View bot version.", "core.help.whoami": "Get the ID of the user account that sent the command inside the bot.", "core.message.abuse.ban.success": "Successfully banned ${user}.", @@ -71,6 +72,22 @@ "core.message.forward_msg.disable": "Forward message disabled.", "core.message.forward_msg.disabled": "Forward message disabled.", "core.message.forward_msg.enable": "Forward message enabled.", + "core.message.help.author.type1": "Module author: ", + "core.message.help.author.type2": "Module Author", + "core.message.help.helpdoc.address": "Online module document: ${help_url}", + "core.message.help.legacy.availables": "Currently available module(s):", + "core.message.help.legacy.base": "Base Command: ", + "core.message.help.legacy.external": "Extension Command: ", + "core.message.help.legacy.more_information": "Use \"${prefix}help \" to view details.\nUse \"${prefix}module list\" to view all available modules.\nYou can also consult the web documentation for help:\n${help_url}", + "core.message.help.more_information": "Here are the help documents shown only show the enabled module(s). If you need to view the help documents of all modules, please use \"${prefix}module list\".\nYou can also consult the web documentation for help:\n${help_url}\nIf you have sufficient funds, welcome to sponsor us: \n${donate_url}", + "core.message.help.not_found": "This module may not exist. Please check your input.", + "core.message.help.regex.detail": "(${msg})", + "core.message.help.regex.no_information": "No description", + "core.message.help.support_regex": "This module supports regular expressions, the message will match with the following: ", + "core.message.help.table.header.alias": "Command Alias", + "core.message.help.table.header.base": "Base Module List", + "core.message.help.table.header.external": "Extended Module List", + "core.message.help.table.header.help": "Help", "core.message.leave.success": "Goodbye.", "core.message.locale": "Current language: ", "core.message.locale.reload.failed": "Failed to reload the following string: ${detail}.", @@ -86,22 +103,6 @@ "core.message.module.enable.not_found": "Failed: Module \"${module}\" does not exist.", "core.message.module.enable.qqchannel_global.success": "Success: Enabled module \"${module}\" for all text channels.", "core.message.module.enable.success": "Success: Enabled module \"${module}\".", - "core.message.module.help.author.type1": "Module author: ", - "core.message.module.help.author.type2": "Module Author", - "core.message.module.help.helpdoc.address": "Online module document: ${help_url}", - "core.message.module.help.legacy.availables": "Currently available module(s):", - "core.message.module.help.legacy.base": "Base Command: ", - "core.message.module.help.legacy.external": "Extension Command: ", - "core.message.module.help.legacy.more_information": "Use \"${prefix}help \" to view details.\nUse \"${prefix}module list\" to view all available modules.\nYou can also consult the web documentation for help:\n${help_url}", - "core.message.module.help.more_information": "Here are the help documents shown only show the enabled module(s). If you need to view the help documents of all modules, please use \"${prefix}module list\".\nYou can also consult the web documentation for help:\n${help_url}\nIf you have sufficient funds, welcome to sponsor us: \n${donate_url}", - "core.message.module.help.not_found": "This module may not exist. Please check your input.", - "core.message.module.help.regex.detail": "(${msg})", - "core.message.module.help.regex.no_information": "No description", - "core.message.module.help.support_regex": "This module supports regular expressions, the message will match with the following: ", - "core.message.module.help.table.header.alias": "Command Alias", - "core.message.module.help.table.header.base": "Base Module List", - "core.message.module.help.table.header.external": "Extended Module List", - "core.message.module.help.table.header.help": "Help", "core.message.module.load.error": "An error occurred: This module has been loaded or does not exist.", "core.message.module.load.failed": "Failed to load module.", "core.message.module.load.success": "Successfully loaded module(s): ${module}", @@ -153,13 +154,13 @@ "core.message.set.help.option.success": "The following parameters were set for the object: ${k} -> ${v}", "core.message.set.invalid": "Invalid ID format.", "core.message.set.module.success": "The following modules were successfully configured for the object: ", + "core.message.setup.check.disable": "Check prompt disabled.", + "core.message.setup.check.enable": "Check prompt enabled.", + "core.message.setup.timeoffset.invalid": "Invalid time offset.", + "core.message.setup.timeoffset.success": "The time offset has been set to UTC${offset}.", + "core.message.setup.typing.disable": "Input prompt disabled.", + "core.message.setup.typing.enable": "Input prompt enabled.", "core.message.superuser.invalid": "Invalid ID format. The format should be \"${target}|\".", - "core.message.toggle.check.disable": "Check prompt disabled.", - "core.message.toggle.check.enable": "Check prompt enabled.", - "core.message.toggle.timeoffset.invalid": "Invalid time offset.", - "core.message.toggle.timeoffset.success": "The time offset has been set to UTC${offset}.", - "core.message.toggle.typing.disable": "Input prompt disabled.", - "core.message.toggle.typing.enable": "Input prompt enabled.", "core.message.update.failed": "Failed to fetch update from GitHub. Please try again later.", "core.message.version": "Current bot version: ${commit}", "core.message.version.unknown": "Unable to get version. This instance may not be deployed using Git.", diff --git a/modules/core/locales/zh_cn.json b/modules/core/locales/zh_cn.json index df8dbe1238..13b4b70aff 100644 --- a/modules/core/locales/zh_cn.json +++ b/modules/core/locales/zh_cn.json @@ -1,7 +1,7 @@ { - "core.help.admin": "一些群组管理员可使用的命令。", "core.help.admin.add": "设置成员为机器人管理员。以实现不设置成员为群组管理员的状況下管理机器人的效果。已是群组管理员无需设置此项目。", "core.help.admin.ban": "限制某人在本群使用机器人。", + "core.help.admin.desc": "一些群组管理员可使用的命令。", "core.help.admin.list": "查看所有机器人管理员。", "core.help.admin.remove": "取消成员的机器人管理员。", "core.help.admin.unban": "解除对某人在本群使用机器人的限制。", @@ -10,29 +10,30 @@ "core.help.alias.list.legacy": "查看自定义命令别名列表。(旧版)", "core.help.alias.remove": "移除自定义命令别名。", "core.help.alias.reset": "重置自定义命令别名。", + "core.help.help": "查看帮助列表。", + "core.help.help.detail": "查看一个模块的详细信息。", + "core.help.help.legacy": "查看帮助列表。(旧版)", "core.help.leave": "使机器人离开群组。", - "core.help.locale": "查看机器人运行语言。", + "core.help.locale.desc": "控制机器人语言。", "core.help.locale.set": "设置机器人运行语言。", "core.help.module.disable": "关闭一个/多个模块。", "core.help.module.disable_all": "关闭所有模块。", "core.help.module.enable": "开启一个/多个模块。", "core.help.module.enable_all": "开启所有模块。", - "core.help.module.help": "查看帮助列表。", - "core.help.module.help.detail": "查看一个模块的详细信息。", - "core.help.module.help.legacy": "查看帮助列表。(旧版)", "core.help.module.list": "查看所有可用模块。", "core.help.module.list.legacy": "查看所有可用模块。(旧版)", "core.help.mute": "使机器人停止发言。", "core.help.option.module.g": "对频道进行全局操作。", "core.help.petal": "获取当前群组的花瓣数。", "core.help.ping": "获取机器人状态。", - "core.help.prefix.add": "设置自定义命令前缀。", + "core.help.prefix.add": "添加自定义命令前缀。", "core.help.prefix.list": "查看自定义命令前缀列表。", "core.help.prefix.remove": "移除自定义命令前缀。", "core.help.prefix.reset": "重置自定义命令前缀。", - "core.help.toggle.check": "切换是否展示命令错字检查提示。", - "core.help.toggle.typing": "切换是否展示输入提示。", - "core.help.toggle.timeoffset": "设置时间偏移量。", + "core.help.setup.check": "切换是否展示命令错字检查提示。", + "core.help.setup.desc": "设置机器人行为。", + "core.help.setup.timeoffset": "设置时间偏移量。", + "core.help.setup.typing": "切换是否展示输入提示。", "core.help.version": "查看机器人的版本号。", "core.help.whoami": "获取发送命令的账号在机器人内部的 ID。", "core.message.abuse.ban.success": "成功封禁 ${user}。", @@ -71,6 +72,22 @@ "core.message.forward_msg.disable": "已关闭转发消息。", "core.message.forward_msg.disabled": "转发消息已关闭。", "core.message.forward_msg.enable": "已开启转发消息。", + "core.message.help.author.type1": "模块作者:", + "core.message.help.author.type2": "模块作者", + "core.message.help.helpdoc.address": "在线模块文档:${help_url}", + "core.message.help.legacy.availables": "当前可用的模块有:", + "core.message.help.legacy.base": "基础命令:", + "core.message.help.legacy.external": "模块扩展命令:", + "core.message.help.legacy.more_information": "使用“${prefix}help <对应模块名>”查看详细信息。\n使用“${prefix}module list”查看所有的可用模块。\n您也可以通过查阅在线文档获取帮助:\n${help_url}", + "core.message.help.more_information": "此处展示的帮助文档仅展示已开启的模块,若需要查看全部模块的帮助文档,请使用“${prefix}module list”。\n您也可以通过查阅在线文档获取帮助:\n${help_url}\n若您有经济实力,欢迎给孩子们在爱发电上打钱:\n${donate_url}", + "core.message.help.not_found": "此模块可能不存在,请检查输入。", + "core.message.help.regex.detail": "(${msg})", + "core.message.help.regex.no_information": "无描述", + "core.message.help.support_regex": "此模块支持正则表达式,消息发送时将会匹配以下内容:", + "core.message.help.table.header.alias": "命令别名", + "core.message.help.table.header.base": "基础模块列表", + "core.message.help.table.header.external": "扩展模块列表", + "core.message.help.table.header.help": "帮助信息", "core.message.leave.success": "已执行,再见。", "core.message.locale": "当前使用的语言:", "core.message.locale.reload.failed": "以下字符串重载失败:${detail}。", @@ -86,22 +103,6 @@ "core.message.module.enable.not_found": "失败:“${module}”模块不存在。", "core.message.module.enable.qqchannel_global.success": "成功:为所有文字频道开启“${module}”模块。", "core.message.module.enable.success": "成功:开启模块“${module}”。", - "core.message.module.help.author.type1": "模块作者:", - "core.message.module.help.author.type2": "模块作者", - "core.message.module.help.helpdoc.address": "在线模块文档:${help_url}", - "core.message.module.help.legacy.availables": "当前可用的模块有:", - "core.message.module.help.legacy.base": "基础命令:", - "core.message.module.help.legacy.external": "模块扩展命令:", - "core.message.module.help.legacy.more_information": "使用“${prefix}help <对应模块名>”查看详细信息。\n使用“${prefix}module list”查看所有的可用模块。\n您也可以通过查阅在线文档获取帮助:\n${help_url}", - "core.message.module.help.more_information": "此处展示的帮助文档仅展示已开启的模块,若需要查看全部模块的帮助文档,请使用“${prefix}module list”。\n您也可以通过查阅在线文档获取帮助:\n${help_url}\n若您有经济实力,欢迎给孩子们在爱发电上打钱:\n${donate_url}", - "core.message.module.help.not_found": "此模块可能不存在,请检查输入。", - "core.message.module.help.regex.detail": "(${msg})", - "core.message.module.help.regex.no_information": "无描述", - "core.message.module.help.support_regex": "此模块支持正则表达式,消息发送时将会匹配以下内容:", - "core.message.module.help.table.header.alias": "命令别名", - "core.message.module.help.table.header.base": "基础模块列表", - "core.message.module.help.table.header.external": "扩展模块列表", - "core.message.module.help.table.header.help": "帮助信息", "core.message.module.load.error": "发生错误:该模块已加载或不存在。", "core.message.module.load.failed": "加载模块失败。", "core.message.module.load.success": "成功加载模块:${module}", @@ -153,13 +154,13 @@ "core.message.set.help.option.success": "成功为对象设置了以下参数:${k} -> ${v}", "core.message.set.invalid": "ID 格式错误。", "core.message.set.module.success": "成功为对象配置了以下模块:", + "core.message.setup.check.disable": "已关闭错字检查提示。", + "core.message.setup.check.enable": "已开启错字检查提示。", + "core.message.setup.timeoffset.invalid": "无效的时间偏移量。", + "core.message.setup.timeoffset.success": "已将时间偏移量设定为 UTC${offset}。", + "core.message.setup.typing.disable": "已关闭输入提示。", + "core.message.setup.typing.enable": "已开启输入提示。", "core.message.superuser.invalid": "ID 格式错误,格式应为“${target}|<用户 ID>”。", - "core.message.toggle.check.disable": "已关闭错字检查提示。", - "core.message.toggle.check.enable": "已开启错字检查提示。", - "core.message.toggle.timeoffset.invalid": "无效的时间偏移量。", - "core.message.toggle.timeoffset.success": "已将时间偏移量设定为 UTC${offset}。", - "core.message.toggle.typing.disable": "已关闭输入提示。", - "core.message.toggle.typing.enable": "已开启输入提示。", "core.message.update.failed": "尝试从 GitHub 获取更新失败,请稍后重试。", "core.message.version": "当前机器人版本号:${commit}", "core.message.version.unknown": "无法获取版本号。此实例可能没有使用 Git 进行部署。", diff --git a/modules/core/locales/zh_tw.json b/modules/core/locales/zh_tw.json index d8cdda6611..9fe17d2472 100644 --- a/modules/core/locales/zh_tw.json +++ b/modules/core/locales/zh_tw.json @@ -1,7 +1,7 @@ { - "core.help.admin": "一些群組管理員可使用的指令。", "core.help.admin.add": "設定成員為機器人管理員,以實現不設定成員為群組管理員的狀況下管理機器人的效果。已是群組管理員無須設定此指令。", "core.help.admin.ban": "限制某人在此群組使用機器人。", + "core.help.admin.desc": "一些群組管理員可使用的指令。", "core.help.admin.list": "檢視所有機器人管理員。", "core.help.admin.remove": "取消成員的機器人管理員。", "core.help.admin.unban": "解除對某人在此群組使用機器人的限制。", @@ -10,29 +10,30 @@ "core.help.alias.list.legacy": "檢視自訂指令別名列表。(舊版)", "core.help.alias.remove": "移除自訂指令別名。", "core.help.alias.reset": "重設自訂指令別名。", + "core.help.help": "檢視說明列表。", + "core.help.help.detail": "檢視一個模組的詳細資訊。", + "core.help.help.legacy": "檢視說明列表。(舊版)", "core.help.leave": "使機器人離開群組。", - "core.help.locale": "檢視機器人使用的語言。", + "core.help.locale.desc": "控制機器人語言。", "core.help.locale.set": "設定機器人使用的語言。", "core.help.module.disable": "停用一個/多個模組。", "core.help.module.disable_all": "停用所有模組。", "core.help.module.enable": "啟用一個/多個模組。", "core.help.module.enable_all": "啟用所有模組。", - "core.help.module.help": "檢視說明列表。", - "core.help.module.help.detail": "檢視一個模組的詳細資訊。", - "core.help.module.help.legacy": "檢視說明列表。(舊版)", "core.help.module.list": "檢視所有可用模組。", "core.help.module.list.legacy": "檢視所有可用模組。(舊版)", "core.help.mute": "使機器人停止發言。", "core.help.option.module.g": "對頻道進行全域操作。", "core.help.petal": "取得目前群組的花瓣數。", "core.help.ping": "取得機器人狀態。", - "core.help.prefix.add": "設定自訂指令前綴。", + "core.help.prefix.add": "新增自訂指令前綴。", "core.help.prefix.list": "檢視自訂指令前綴列表。", "core.help.prefix.remove": "移除自訂指令前綴。", "core.help.prefix.reset": "重設自訂指令前綴。", - "core.help.toggle.check": "切換是否顯示指令錯誤檢查提醒。", - "core.help.toggle.typing": "切換是否顯示輸入提醒。", - "core.help.toggle.timeoffset": "設定時間偏移量。", + "core.help.setup.check": "切換是否顯示指令錯誤檢查提醒。", + "core.help.setup.desc": "設定機器人行動", + "core.help.setup.timeoffset": "設定時間偏移量。", + "core.help.setup.typing": "切換是否顯示輸入提醒。", "core.help.version": "檢視機器人的版本號。", "core.help.whoami": "取得傳送指令的帳戶在機器人內部的 ID。", "core.message.abuse.ban.success": "成功封鎖 ${user}。", @@ -71,6 +72,22 @@ "core.message.forward_msg.disable": "已停用轉發訊息。", "core.message.forward_msg.disabled": "轉發訊息已停用。", "core.message.forward_msg.enable": "已啟用轉發訊息。", + "core.message.help.author.type1": "模組作者:", + "core.message.help.author.type2": "模組作者", + "core.message.help.helpdoc.address": "線上模組說明文件:${help_url}", + "core.message.help.legacy.availables": "目前可用的模組有:", + "core.message.help.legacy.base": "基礎指令:", + "core.message.help.legacy.external": "模組擴充指令:", + "core.message.help.legacy.more_information": "使用「${prefix}help <對應模組名>」檢視詳細資訊。\n使用「${prefix}module list」檢視所有的可用模組。\n您也可以透過查閱線上文件取得說明:\n${help_url}", + "core.message.help.more_information": "此處展示的說明文件僅展示已啟用的模組,若需要檢視全部模組的說明文件,請使用「${prefix}module list」。\n您也可以透過查閱線上文件取得說明:\n${help_url}\n若您有經濟實力,歡迎在愛發電提供贊助:\n${donate_url}", + "core.message.help.not_found": "此模組可能不存在,請校對輸入。", + "core.message.help.regex.detail": "(${msg})", + "core.message.help.regex.no_information": "無描述", + "core.message.help.support_regex": "此模組支援正規表示式,訊息傳送時將會尋找相符的以下內容:", + "core.message.help.table.header.alias": "指令別名", + "core.message.help.table.header.base": "基礎模組列表", + "core.message.help.table.header.external": "擴充模組列表", + "core.message.help.table.header.help": "說明資訊", "core.message.leave.success": "已執行,再見。", "core.message.locale": "目前使用的語言:", "core.message.locale.reload.failed": "以下字串重新加載失敗:${detail}。", @@ -86,22 +103,6 @@ "core.message.module.enable.not_found": "失敗:「${module}」模組不存在。", "core.message.module.enable.qqchannel_global.success": "成功:為所有文字頻道啟用「${module}」模組。", "core.message.module.enable.success": "成功:啟用模組「${module}」。", - "core.message.module.help.author.type1": "模組作者:", - "core.message.module.help.author.type2": "模組作者", - "core.message.module.help.helpdoc.address": "線上模組說明文件:${help_url}", - "core.message.module.help.legacy.availables": "目前可用的模組有:", - "core.message.module.help.legacy.base": "基礎指令:", - "core.message.module.help.legacy.external": "模組擴充指令:", - "core.message.module.help.legacy.more_information": "使用「${prefix}help <對應模組名>」檢視詳細資訊。\n使用「${prefix}module list」檢視所有的可用模組。\n您也可以透過查閱線上文件取得說明:\n${help_url}", - "core.message.module.help.more_information": "此處展示的說明文件僅展示已啟用的模組,若需要檢視全部模組的說明文件,請使用「${prefix}module list」。\n您也可以透過查閱線上文件取得說明:\n${help_url}\n若您有經濟實力,歡迎在愛發電提供贊助:\n${donate_url}", - "core.message.module.help.not_found": "此模組可能不存在,請校對輸入。", - "core.message.module.help.regex.detail": "(${msg})", - "core.message.module.help.regex.no_information": "無描述", - "core.message.module.help.support_regex": "此模組支援正規表示式,訊息傳送時將會尋找相符的以下內容:", - "core.message.module.help.table.header.alias": "指令別名", - "core.message.module.help.table.header.base": "基礎模組列表", - "core.message.module.help.table.header.external": "擴充模組列表", - "core.message.module.help.table.header.help": "說明資訊", "core.message.module.load.error": "發生錯誤:此模組已載入或不存在。", "core.message.module.load.failed": "載入模組失敗。", "core.message.module.load.success": "成功載入模組:${module}", @@ -153,13 +154,13 @@ "core.message.set.help.option.success": "成功為物件設定了以下參數:${k} -> ${v}", "core.message.set.invalid": "ID 格式錯誤。", "core.message.set.module.success": "成功為物件配置了以下模組:", + "core.message.setup.check.disable": "已停用指令錯誤檢查提醒。", + "core.message.setup.check.enable": "已啟用指令錯誤檢查提醒。", + "core.message.setup.timeoffset.invalid": "無效的時間偏移量。", + "core.message.setup.timeoffset.success": "已將時間偏移量設定為 UTC${offset}。", + "core.message.setup.typing.disable": "已停用輸入提醒。", + "core.message.setup.typing.enable": "已啟用輸入提醒。", "core.message.superuser.invalid": "ID 格式錯誤,格式應為「${target}|<使用者 ID>」。", - "core.message.toggle.check.disable": "已停用指令錯誤檢查提醒。", - "core.message.toggle.check.enable": "已啟用指令錯誤檢查提醒。", - "core.message.toggle.timeoffset.invalid": "無效的時間偏移量。", - "core.message.toggle.timeoffset.success": "已將時間偏移量設定為 UTC${offset}。", - "core.message.toggle.typing.disable": "已停用輸入提醒。", - "core.message.toggle.typing.enable": "已啟用輸入提醒。", "core.message.update.failed": "嘗試從 GitHub 取得更新失敗,請稍後重試。", "core.message.version": "目前機器人版本號:${commit}", "core.message.version.unknown": "無法取得版本號。此實例可能沒有使用 Git 進行部署。", diff --git a/modules/core/modules.py b/modules/core/modules.py index 968578ab54..222382ba18 100644 --- a/modules/core/modules.py +++ b/modules/core/modules.py @@ -49,7 +49,8 @@ async def _(msg: Bot.MessageSession): 'load ...', 'unload ...', 'list {{core.help.module.list}}', - 'list legacy {{core.help.module.list.legacy}}'], options_desc={'-g': '{core.help.option.module.g}'}, + 'list legacy {{core.help.module.list.legacy}}'], + options_desc={'-g': '{core.help.option.module.g}'}, available_for=['QQ|Guild']) async def _(msg: Bot.MessageSession): if msg.parsed_msg.get('list', False): @@ -98,7 +99,7 @@ async def config_modules(msg: Bot.MessageSession): else: enable_list.append(module_) recommend = modules_[module_].recommend_modules - if recommend is not None: + if recommend: for r in recommend: if r not in enable_list and r not in enabled_modules_list: recommend_modules_list.append(r) @@ -117,7 +118,7 @@ async def config_modules(msg: Bot.MessageSession): else: msglist.append(msg.locale.t("core.message.module.enable.success", module=x)) support_lang = modules_[x].support_languages - if support_lang is not None: + if support_lang: if msg.locale.locale not in support_lang: msglist.append(msg.locale.t("core.message.module.unsupported_language", module=x)) @@ -127,11 +128,11 @@ async def config_modules(msg: Bot.MessageSession): recommend_modules_help_doc_list.append(msg.locale.t("core.message.module.module.help", module=m )) - if modules_[m].desc is not None: + if modules_[m].desc: recommend_modules_help_doc_list.append(msg.locale.tl_str(modules_[m].desc)) hdoc = CommandParser(modules_[m], msg=msg, bind_prefix=modules_[m].bind_prefix, command_prefixes=msg.prefixes).return_formatted_help_doc() - if hdoc == '': + if not hdoc: hdoc = msg.locale.t('core.help.none') recommend_modules_help_doc_list.append(hdoc) except InvalidHelpDocTypeError: @@ -176,14 +177,14 @@ def module_reload(module, extra_modules, base_mode=False): reload_count = ModulesManager.reload_module(module) if reload_count > 1: return msg.locale.t('core.message.module.reload.success', module=module) + \ - ('\n' if len(extra_modules) != 0 else '') + \ - '\n'.join(extra_modules) + \ - '\n' + msg.locale.t('core.message.module.reload.with', reloadCnt=reload_count - 1) + ('\n' if len(extra_modules) != 0 else '') + \ + '\n'.join(extra_modules) + \ + '\n' + msg.locale.t('core.message.module.reload.with', reloadCnt=reload_count - 1) elif reload_count == 1: return msg.locale.t('core.message.module.reload.success', module=module) + \ - ('\n' if len(extra_modules) != 0 else '') + \ - '\n'.join(extra_modules) + \ - '\n' + msg.locale.t('core.message.module.reload.no_more') + ('\n' if len(extra_modules) != 0 else '') + \ + '\n'.join(extra_modules) + \ + '\n' + msg.locale.t('core.message.module.reload.no_more') else: return msg.locale.t("core.message.module.reload.failed") @@ -266,7 +267,7 @@ def module_reload(module, extra_modules, base_mode=False): else: msglist.append(msg.locale.t("parser.superuser.permission.denied")) - if msglist is not None: + if msglist: if not recommend_modules_help_doc_list: await msg.finish('\n'.join(msglist)) else: @@ -288,19 +289,19 @@ def module_reload(module, extra_modules, base_mode=False): hlp = module('help', base=True) -@hlp.command(' {{core.help.module.help.detail}}') +@hlp.command(' {{core.help.help.detail}}') async def bot_help(msg: Bot.MessageSession): module_list = ModulesManager.return_modules_list( target_from=msg.target.target_from) alias = ModulesManager.modules_aliases - if msg.parsed_msg is not None: + if msg.parsed_msg: msgs = [] help_name = msg.parsed_msg[''] if help_name in alias: help_name = alias[help_name] if help_name in module_list: module_ = module_list[help_name] - if module_.desc is not None: + if module_.desc: desc = module_.desc if locale_str := re.match(r'\{(.*)}', desc): if locale_str: @@ -313,7 +314,7 @@ async def bot_help(msg: Bot.MessageSession): doc = '\n'.join(msgs) if module_.regex_list.set: - doc += '\n' + msg.locale.t("core.message.module.help.support_regex") + doc += '\n' + msg.locale.t("core.message.help.support_regex") for regex in module_.regex_list.set: pattern = None if isinstance(regex.pattern, str): @@ -323,28 +324,28 @@ async def bot_help(msg: Bot.MessageSession): if pattern: desc = regex.desc if desc: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.detail", + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.detail", msg=msg.locale.tl_str(desc)) else: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.no_information") + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.no_information") module_alias = module_.alias malias = [] if module_alias: for a in module_alias: malias.append(f'{a} -> {module_alias[a]}') - if module_.developers is not None: + if module_.developers: devs = msg.locale.t('message.delimiter').join(module_.developers) - devs_msg = '\n' + msg.locale.t("core.message.module.help.author.type1") + devs + devs_msg = '\n' + msg.locale.t("core.message.help.author.type1") + devs else: devs_msg = '' - wiki_msg = '\n' + msg.locale.t("core.message.module.help.helpdoc.address", + wiki_msg = '\n' + msg.locale.t("core.message.help.helpdoc.address", help_url=Config('help_url')) + '/' + help_name if len(doc) > 500 and msg.Feature.image: try: tables = [ImageTable([[doc, '\n'.join(malias), devs]], - [msg.locale.t("core.message.module.help.table.header.help"), - msg.locale.t("core.message.module.help.table.header.alias"), - msg.locale.t("core.message.module.help.author.type2")])] + [msg.locale.t("core.message.help.table.header.help"), + msg.locale.t("core.message.help.table.header.alias"), + msg.locale.t("core.message.help.author.type2")])] render = await image_table_render(tables) if render: await msg.finish([Image(render), @@ -355,11 +356,11 @@ async def bot_help(msg: Bot.MessageSession): doc += f'\n{msg.locale.t("core.help.alias")}\n' + '\n'.join(malias) await msg.finish((doc + devs_msg + wiki_msg).lstrip()) else: - await msg.finish(msg.locale.t("core.message.module.help.not_found")) + await msg.finish(msg.locale.t("core.message.help.not_found")) -@hlp.command(['{{core.help.module.help}}', - 'legacy {{core.help.module.help.legacy}}']) +@hlp.command(['{{core.help.help}}', + 'legacy {{core.help.help.legacy}}']) async def _(msg: Bot.MessageSession): module_list = ModulesManager.return_modules_list( target_from=msg.target.target_from) @@ -377,13 +378,13 @@ async def _(msg: Bot.MessageSession): help_ = CommandParser(module_, msg=msg, bind_prefix=module_.bind_prefix, command_prefixes=msg.prefixes) - if module_.desc is not None: + if module_.desc: doc_.append(msg.locale.tl_str(module_.desc)) if help_.args: doc_.append(help_.return_formatted_help_doc()) doc = '\n'.join(doc_) if module_.regex_list.set: - doc += '\n' + msg.locale.t("core.message.module.help.support_regex") + doc += '\n' + msg.locale.t("core.message.help.support_regex") for regex in module_.regex_list.set: pattern = None if isinstance(regex.pattern, str): @@ -393,10 +394,10 @@ async def _(msg: Bot.MessageSession): if pattern: desc = regex.desc if desc: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.detail", + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.detail", msg=msg.locale.tl_str(desc)) else: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.no_information") + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.no_information") appends.append(doc) module_alias = module_.alias malias = [] @@ -412,34 +413,34 @@ async def _(msg: Bot.MessageSession): m.append(appends) if essential: tables.append(ImageTable( - essential, [msg.locale.t("core.message.module.help.table.header.base"), - msg.locale.t("core.message.module.help.table.header.help"), - msg.locale.t("core.message.module.help.table.header.alias"), - msg.locale.t("core.message.module.help.author.type2")])) + essential, [msg.locale.t("core.message.help.table.header.base"), + msg.locale.t("core.message.help.table.header.help"), + msg.locale.t("core.message.help.table.header.alias"), + msg.locale.t("core.message.help.author.type2")])) if m: - tables.append(ImageTable(m, [msg.locale.t("core.message.module.help.table.header.external"), - msg.locale.t("core.message.module.help.table.header.help"), - msg.locale.t("core.message.module.help.table.header.alias"), - msg.locale.t("core.message.module.help.author.type2")])) + tables.append(ImageTable(m, [msg.locale.t("core.message.help.table.header.external"), + msg.locale.t("core.message.help.table.header.help"), + msg.locale.t("core.message.help.table.header.alias"), + msg.locale.t("core.message.help.author.type2")])) if tables: render = await image_table_render(tables) if render: legacy_help = False await msg.finish([Image(render), - Plain(msg.locale.t("core.message.module.help.more_information", + Plain(msg.locale.t("core.message.help.more_information", prefix=msg.prefixes[0], help_url=Config('help_url'), donate_url=Config('donate_url')))]) except Exception: traceback.print_exc() if legacy_help: - help_msg = [msg.locale.t("core.message.module.help.legacy.base")] + help_msg = [msg.locale.t("core.message.help.legacy.base")] essential = [] for x in module_list: if module_list[x].base and not ( module_list[x].required_superuser or module_list[x].required_base_superuser): essential.append(module_list[x].bind_prefix) help_msg.append(' | '.join(essential)) - help_msg.append(msg.locale.t("core.message.module.help.legacy.external")) + help_msg.append(msg.locale.t("core.message.help.legacy.external")) module_ = [] for x in module_list: if x in target_enabled_list and not ( @@ -448,7 +449,7 @@ async def _(msg: Bot.MessageSession): help_msg.append(' | '.join(module_)) help_msg.append( msg.locale.t( - "core.message.module.help.legacy.more_information", + "core.message.help.legacy.more_information", prefix=msg.prefixes[0], help_url=Config('help_url'))) await msg.finish('\n'.join(help_msg)) @@ -472,13 +473,13 @@ async def modules_help(msg: Bot.MessageSession, legacy): doc_ = [] help_ = CommandParser( module_, bind_prefix=module_.bind_prefix, command_prefixes=msg.prefixes, msg=msg) - if module_.desc is not None: + if module_.desc: doc_.append(msg.locale.tl_str(module_.desc)) if help_.args: doc_.append(help_.return_formatted_help_doc()) doc = '\n'.join(doc_) if module_.regex_list.set: - doc += '\n' + msg.locale.t("core.message.module.help.support_regex") + doc += '\n' + msg.locale.t("core.message.help.support_regex") for regex in module_.regex_list.set: pattern = None if isinstance(regex.pattern, str): @@ -488,10 +489,10 @@ async def modules_help(msg: Bot.MessageSession, legacy): if pattern: desc = regex.desc if desc: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.detail", + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.detail", msg=msg.locale.tl_str(desc)) else: - doc += f'\n{pattern} ' + msg.locale.t("core.message.module.help.regex.no_information") + doc += f'\n{pattern} ' + msg.locale.t("core.message.help.regex.no_information") appends.append(doc) module_alias = module_.alias malias = [] @@ -503,10 +504,10 @@ async def modules_help(msg: Bot.MessageSession, legacy): appends.append(msg.locale.t('message.delimiter').join(module_.developers)) m.append(appends) if m: - tables.append(ImageTable(m, [msg.locale.t("core.message.module.help.table.header.external"), - msg.locale.t("core.message.module.help.table.header.help"), - msg.locale.t("core.message.module.help.table.header.alias"), - msg.locale.t("core.message.module.help.author.type2")])) + tables.append(ImageTable(m, [msg.locale.t("core.message.help.table.header.external"), + msg.locale.t("core.message.help.table.header.help"), + msg.locale.t("core.message.help.table.header.alias"), + msg.locale.t("core.message.help.author.type2")])) if tables: render = await image_table_render(tables) if render: @@ -515,7 +516,7 @@ async def modules_help(msg: Bot.MessageSession, legacy): except Exception: traceback.print_exc() if legacy_help: - help_msg = [msg.locale.t("core.message.module.help.legacy.availables")] + help_msg = [msg.locale.t("core.message.help.legacy.availables")] module_ = [] for x in module_list: if x[0] == '_': @@ -526,7 +527,7 @@ async def modules_help(msg: Bot.MessageSession, legacy): help_msg.append(' | '.join(module_)) help_msg.append( msg.locale.t( - "core.message.module.help.legacy.more_information", + "core.message.help.legacy.more_information", prefix=msg.prefixes[0], help_url=Config('help_url'))) await msg.finish('\n'.join(help_msg)) diff --git a/modules/core/prefix.py b/modules/core/prefix.py index 10b69ecd67..f857d5b75c 100644 --- a/modules/core/prefix.py +++ b/modules/core/prefix.py @@ -11,7 +11,7 @@ async def set_prefix(msg: Bot.MessageSession): prefixes = msg.options.get('command_prefix') prefix = msg.parsed_msg.get('', False) - if prefixes is None: + if not prefixes: prefixes = [] if 'add' in msg.parsed_msg: if prefix: diff --git a/modules/core/su_utils.py b/modules/core/su_utils.py index aea693e58a..c15cbdab9c 100644 --- a/modules/core/su_utils.py +++ b/modules/core/su_utils.py @@ -11,18 +11,19 @@ from dateutil.relativedelta import relativedelta from config import Config, CFG -from core.builtins import Bot, Image, Plain, Temp +from core.builtins import Bot, PrivateAssets, Image, Plain, ExecutionLockList, Temp, MessageTaskManager from core.component import module +from core.exceptions import TestException from core.loader import ModulesManager from core.logger import Logger from core.parser.message import remove_temp_ban -from core.scheduler import CronTrigger from core.tos import pardon_user, warn_user from core.utils.cache import random_cache_path from core.utils.info import Info from core.utils.storedata import get_stored_list, update_stored_list from database import BotDBUtil + su = module('superuser', alias='su', required_superuser=True, base=True) @@ -76,7 +77,7 @@ async def _(msg: Bot.MessageSession): module_ = None if '' in msg.parsed_msg: module_ = msg.parsed_msg[''] - if module_ is None: + if not module_: result = msg.locale.t("core.message.analytics.days.total", first_record=first_record.timestamp) else: result = msg.locale.t("core.message.analytics.days", module=module_, @@ -114,7 +115,7 @@ async def _(msg: Bot.MessageSession): module_ = None if '' in msg.parsed_msg: module_ = msg.parsed_msg[''] - if module_ is None: + if not module_: result = msg.locale.t("core.message.analytics.year.total", first_record=first_record.timestamp) else: result = msg.locale.t("core.message.analytics.year", module=module_, @@ -165,15 +166,6 @@ async def _(msg: Bot.MessageSession): await msg.finish(msg.locale.t("core.message.purge.empty")) -@purge.schedule(CronTrigger.from_crontab('0 0 * * *')) -async def _(): - cache_path = os.path.abspath(Config('cache_path')) - Logger.info('Start purging cache...') - if os.path.exists(cache_path): - shutil.rmtree(cache_path) - os.mkdir(cache_path) - - set_ = module('set', required_superuser=True, base=True) @@ -183,7 +175,7 @@ async def _(msg: Bot.MessageSession): if not target.startswith(f'{msg.target.target_from}|'): await msg.finish(msg.locale.t("core.message.set.invalid")) target_data = BotDBUtil.TargetInfo(target) - if target_data.query is None: + if not target_data.query: confirm = await msg.wait_confirm(msg.locale.t("core.message.set.confirm.init"), append_instruction=False) if not confirm: return @@ -201,7 +193,7 @@ async def _(msg: Bot.MessageSession): if not target.startswith(f'{msg.target.target_from}|'): await msg.finish(msg.locale.t("core.message.set.invalid")) target_data = BotDBUtil.TargetInfo(target) - if target_data.query is None: + if not target_data.query: confirm = await msg.wait_confirm(msg.locale.t("core.message.set.confirm.init"), append_instruction=False) if not confirm: return @@ -283,6 +275,97 @@ async def _(msg: Bot.MessageSession): await msg.finish(msg.locale.t("core.message.abuse.unban.success", user=user)) +upd = module('update', required_superuser=True, base=True) + + +def pull_repo(): + return os.popen('git pull', 'r').read()[:-1] + + +def update_dependencies(): + poetry_install = os.popen('poetry install').read()[:-1] + if poetry_install != '': + return poetry_install + pip_install = os.popen('pip install -r requirements.txt').read()[:-1] + if len(pip_install) > 500: + return '...' + pip_install[-500:] + return pip_install + + +@upd.command() +async def update_bot(msg: Bot.MessageSession): + confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) + if confirm: + pull_repo_result = pull_repo() + if pull_repo_result != '': + await msg.send_message(pull_repo_result) + else: + await msg.send_message(msg.locale.t("core.message.update.failed")) + await msg.send_message(update_dependencies()) + +if Info.subprocess: + rst = module('restart', required_superuser=True, base=True) + + def restart(): + sys.exit(233) + + def write_version_cache(msg: Bot.MessageSession): + update = os.path.abspath(PrivateAssets.path + '/cache_restart_author') + write_version = open(update, 'w') + write_version.write(json.dumps({'From': msg.target.target_from, 'ID': msg.target.target_id})) + write_version.close() + + restart_time = [] + + async def wait_for_restart(msg: Bot.MessageSession): + get = ExecutionLockList.get() + if datetime.now().timestamp() - restart_time[0] < 60: + if len(get) != 0: + await msg.send_message(msg.locale.t("core.message.restart.wait", count=len(get))) + await asyncio.sleep(10) + return await wait_for_restart(msg) + else: + await msg.send_message(msg.locale.t("core.message.restart.restarting")) + get_wait_list = MessageTaskManager.get() + for x in get_wait_list: + for y in get_wait_list[x]: + for z in get_wait_list[x][y]: + if get_wait_list[x][y][z]['active']: + await z.send_message(z.locale.t("core.message.restart.prompt")) + + else: + await msg.send_message(msg.locale.t("core.message.restart.timeout")) + + @rst.command() + async def restart_bot(msg: Bot.MessageSession): + confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) + if confirm: + restart_time.append(datetime.now().timestamp()) + await wait_for_restart(msg) + write_version_cache(msg) + restart() + + +if Info.subprocess: + upds = module('update&restart', required_superuser=True, alias='u&r', base=True) + + @upds.command() + async def update_and_restart_bot(msg: Bot.MessageSession): + confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) + if confirm: + restart_time.append(datetime.now().timestamp()) + await wait_for_restart(msg) + write_version_cache(msg) + pull_repo_result = pull_repo() + if pull_repo_result != '': + await msg.send_message(pull_repo_result) + await msg.send_message(update_dependencies()) + else: + Logger.warn(f'Failed to get Git repository result.') + await msg.send_message(msg.locale.t("core.message.update.failed")) + restart() + + if Bot.FetchTarget.name == 'QQ': resume = module('resume', required_base_superuser=True) @@ -342,6 +425,7 @@ async def _(msg: Bot.MessageSession): else: await msg.finish(msg.locale.t('core.message.forward_msg.disable')) + echo = module('echo', required_superuser=True, base=True) @@ -357,13 +441,14 @@ async def _(msg: Bot.MessageSession): async def _(msg: Bot.MessageSession): await msg.finish(msg.parsed_msg[''], quote=False) + rse = module('raise', required_superuser=True, base=True) @rse.command() async def _(msg: Bot.MessageSession): e = msg.locale.t("core.message.raise") - raise Exception(e) + raise TestException(e) if Config('enable_eval'): diff --git a/modules/core/update.py b/modules/core/update.py deleted file mode 100644 index f568c3aa58..0000000000 --- a/modules/core/update.py +++ /dev/null @@ -1,101 +0,0 @@ -import asyncio -import os -import sys -from datetime import datetime - -import ujson as json - -from core.builtins import Bot, PrivateAssets, ExecutionLockList, MessageTaskManager -from core.component import module -from core.logger import Logger -from core.utils.info import Info - -upd = module('update', required_superuser=True, base=True) - - -def pull_repo(): - return os.popen('git pull', 'r').read()[:-1] - - -def update_dependencies(): - poetry_install = os.popen('poetry install').read()[:-1] - if poetry_install != '': - return poetry_install - pip_install = os.popen('pip install -r requirements.txt').read()[:-1] - if len(pip_install) > 500: - return '...' + pip_install[-500:] - return - - -@upd.command() -async def update_bot(msg: Bot.MessageSession): - confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) - if confirm: - pull_repo_result = pull_repo() - if pull_repo_result != '': - await msg.send_message(pull_repo_result) - else: - await msg.send_message(msg.locale.t("core.message.update.failed")) - await msg.send_message(update_dependencies()) - -if Info.subprocess: - rst = module('restart', required_superuser=True, base=True) - - def restart(): - sys.exit(233) - - def write_version_cache(msg: Bot.MessageSession): - update = os.path.abspath(PrivateAssets.path + '/cache_restart_author') - write_version = open(update, 'w') - write_version.write(json.dumps({'From': msg.target.target_from, 'ID': msg.target.target_id})) - write_version.close() - - restart_time = [] - - async def wait_for_restart(msg: Bot.MessageSession): - get = ExecutionLockList.get() - if datetime.now().timestamp() - restart_time[0] < 60: - if len(get) != 0: - await msg.send_message(msg.locale.t("core.message.restart.wait", count=len(get))) - await asyncio.sleep(10) - return await wait_for_restart(msg) - else: - await msg.send_message(msg.locale.t("core.message.restart.restarting")) - get_wait_list = MessageTaskManager.get() - for x in get_wait_list: - for y in get_wait_list[x]: - for z in get_wait_list[x][y]: - if get_wait_list[x][y][z]['active']: - await z.send_message(z.locale.t("core.message.restart.prompt")) - - else: - await msg.send_message(msg.locale.t("core.message.restart.timeout")) - - @rst.command() - async def restart_bot(msg: Bot.MessageSession): - confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) - if confirm: - restart_time.append(datetime.now().timestamp()) - await wait_for_restart(msg) - write_version_cache(msg) - restart() - - -if Info.subprocess: - upds = module('update&restart', required_superuser=True, alias='u&r', base=True) - - @upds.command() - async def update_and_restart_bot(msg: Bot.MessageSession): - confirm = await msg.wait_confirm(msg.locale.t("core.message.confirm"), append_instruction=False) - if confirm: - restart_time.append(datetime.now().timestamp()) - await wait_for_restart(msg) - write_version_cache(msg) - pull_repo_result = pull_repo() - if pull_repo_result != '': - await msg.send_message(pull_repo_result) - await msg.send_message(update_dependencies()) - else: - Logger.warn(f'Failed to get Git repository result.') - await msg.send_message(msg.locale.t("core.message.update.failed")) - restart() diff --git a/modules/core/utils.py b/modules/core/utils.py index 7e9b37d379..bd2b286aa1 100644 --- a/modules/core/utils.py +++ b/modules/core/utils.py @@ -15,10 +15,10 @@ jwt_secret = Config('jwt_secret') -ver = module('version', base=True, desc='{core.help.version}') +ver = module('version', base=True) -@ver.command() +@ver.command('{{core.help.version}}') async def bot_version(msg: Bot.MessageSession): if Info.version: await msg.finish(msg.locale.t('core.message.version', commit=Info.version[0:6])) @@ -26,12 +26,12 @@ async def bot_version(msg: Bot.MessageSession): await msg.finish(msg.locale.t('core.message.version.unknown')) -ping = module('ping', base=True, desc='{core.help.ping}') +ping = module('ping', base=True) started_time = datetime.now() -@ping.command() +@ping.command('{{core.help.ping}}') async def _(msg: Bot.MessageSession): checkpermisson = msg.check_super_user() result = "Pong!" @@ -70,7 +70,7 @@ async def _(msg: Bot.MessageSession): await msg.finish(result) -admin = module('admin', base=True, required_admin=True, desc='{core.help.admin}') +admin = module('admin', base=True, required_admin=True, desc='{core.help.admin.desc}') @admin.command([ @@ -125,14 +125,14 @@ async def config_ban(msg: Bot.MessageSession): await msg.finish(msg.locale.t("core.message.admin.ban.not_yet")) -locale = module('locale', base=True) +locale = module('locale', base=True, desc='{core.help.locale.desc}') -@locale.command('{{core.help.locale}}') +@locale.command() async def _(msg: Bot.MessageSession): avaliable_lang = msg.locale.t("message.delimiter").join(get_available_locales()) await msg.finish( - f"{msg.locale.t('core.message.locale')}{msg.locale.t('language')}\n{msg.locale.t('core.message.locale.set.prompt', langlist=avaliable_lang, prefix=command_prefix[0])}") + f"{msg.locale.t('core.message.locale')}{msg.locale.t('language')}\n{msg.locale.t('core.message.locale.set.prompt', langlist=avaliable_lang, prefix=msg.prefixes[0])}") @locale.command(' {{core.help.locale.set}}', required_admin=True) @@ -159,50 +159,50 @@ async def reload_locale(msg: Bot.MessageSession): @whoami.command('{{core.help.whoami}}') async def _(msg: Bot.MessageSession): - rights = '' + perm = '' if await msg.check_native_permission(): - rights += '\n' + msg.locale.t("core.message.whoami.admin") + perm += '\n' + msg.locale.t("core.message.whoami.admin") elif await msg.check_permission(): - rights += '\n' + msg.locale.t("core.message.whoami.botadmin") + perm += '\n' + msg.locale.t("core.message.whoami.botadmin") if msg.check_super_user(): - rights += '\n' + msg.locale.t("core.message.whoami.superuser") + perm += '\n' + msg.locale.t("core.message.whoami.superuser") await msg.finish( - msg.locale.t('core.message.whoami', senderid=msg.target.sender_id, targetid=msg.target.target_id) + rights, + msg.locale.t('core.message.whoami', senderid=msg.target.sender_id, targetid=msg.target.target_id) + perm, disable_secret_check=True) -tog = module('toggle', base=True, required_admin=True) +setup = module('setup', base=True, required_admin=True, desc='{core.help.setup.desc}') -@tog.command('typing {{core.help.toggle.typing}}') +@setup.command('typing {{core.help.setup.typing}}') async def _(msg: Bot.MessageSession): target = BotDBUtil.SenderInfo(msg.target.sender_id) state = target.query.disable_typing if not state: target.edit('disable_typing', True) - await msg.finish(msg.locale.t('core.message.toggle.typing.disable')) + await msg.finish(msg.locale.t('core.message.setup.typing.disable')) else: target.edit('disable_typing', False) - await msg.finish(msg.locale.t('core.message.toggle.typing.enable')) + await msg.finish(msg.locale.t('core.message.setup.typing.enable')) -@tog.command('check {{core.help.toggle.check}}') +@setup.command('check {{core.help.setup.check}}') async def _(msg: Bot.MessageSession): state = msg.options.get('typo_check') if state: msg.data.edit_option('typo_check', False) - await msg.finish(msg.locale.t('core.message.toggle.check.enable')) + await msg.finish(msg.locale.t('core.message.setup.check.enable')) else: msg.data.edit_option('typo_check', True) - await msg.finish(msg.locale.t('core.message.toggle.check.disable')) + await msg.finish(msg.locale.t('core.message.setup.check.disable')) -@tog.command('timeoffset {{core.help.toggle.timeoffset}}') +@setup.command('timeoffset {{core.help.setup.timeoffset}}') async def _(msg: Bot.MessageSession, offset: str): try: - tstr_split = [part for part in offset.split(':')] - hour = int(tstr_split[0]) - minute = int(tstr_split[1]) if len(tstr_split) > 1 else 0 + tstr_split = [int(part) for part in offset.split(':')] + hour = tstr_split[0] + minute = tstr_split[1] if len(tstr_split) > 1 else 0 if minute == 0: offset = f"{'+' if hour >= 0 else '-'}{abs(hour)}" else: @@ -211,15 +211,15 @@ async def _(msg: Bot.MessageSession, offset: str): if hour > 12 or minute >= 60: raise ValueError except ValueError: - await msg.finish(msg.locale.t('core.message.toggle.timeoffset.invalid')) + await msg.finish(msg.locale.t('core.message.setup.timeoffset.invalid')) msg.data.edit_option('timezone_offset', offset) - await msg.finish(msg.locale.t('core.message.toggle.timeoffset.success', offset=offset)) + await msg.finish(msg.locale.t('core.message.setup.timeoffset.success', offset=offset)) -mute = module('mute', base=True, required_admin=True, desc='{core.help.mute}') +mute = module('mute', base=True, required_admin=True) -@mute.command() +@mute.command('{{core.help.mute}}') async def _(msg: Bot.MessageSession): state = msg.data.switch_mute() if state: @@ -228,16 +228,10 @@ async def _(msg: Bot.MessageSession): await msg.finish(msg.locale.t('core.message.mute.disable')) -leave = module( - 'leave', - base=True, - required_admin=True, - available_for='QQ|Group', - alias='dismiss', - desc='{core.help.leave}') +leave = module('leave', base=True, required_admin=True, available_for='QQ|Group', alias='dismiss') -@leave.command() +@leave.command('{{core.help.leave}}') async def _(msg: Bot.MessageSession): confirm = await msg.wait_confirm(msg.locale.t('core.message.confirm')) if confirm: @@ -245,10 +239,10 @@ async def _(msg: Bot.MessageSession): await msg.call_api('set_group_leave', group_id=msg.session.target) -token = module('token', base=True, desc='{core.help.token}') +token = module('token', base=True) -@token.command('') +@token.command(' {{core.help.token}}') async def _(msg: Bot.MessageSession): await msg.finish(jwt.encode({ 'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24 * 7), # 7 days diff --git a/modules/cytoid/__init__.py b/modules/cytoid/__init__.py index fce4a541f6..aeb02a381b 100644 --- a/modules/cytoid/__init__.py +++ b/modules/cytoid/__init__.py @@ -7,7 +7,7 @@ from .utils import get_profile_name ctd = module('cytoid', desc='{cytoid.help.desc}', - developers=['OasisAkari'], alias='ctd') + developers=['OasisAkari'], alias='ctd') @ctd.handle('profile [] {{cytoid.help.profile}}') @@ -24,13 +24,13 @@ async def _(msg: Bot.MessageSession): elif 'r30' in msg.parsed_msg: query = 'r30' else: - raise + return pat = msg.parsed_msg.get('', False) if pat: query_id = pat else: query_id = CytoidBindInfoManager(msg).get_bind_username() - if query_id is None: + if not query_id: await msg.finish(msg.locale.t('cytoid.message.user_unbound', prefix=msg.prefixes[0])) if query: if msg.target.target_from == 'TEST|Console': @@ -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: @@ -48,7 +48,8 @@ async def _(msg: Bot.MessageSession): if img['status']: qc.reset() else: - res = msg.locale.t('message.cooldown', time=int(c), cd_time='150') + msg.locale.t('cytoid.message.b30.cooldown') + res = msg.locale.t('message.cooldown', time=int(c), cd_time='150') + \ + msg.locale.t('cytoid.message.b30.cooldown') await msg.finish(res) diff --git a/modules/cytoid/dbutils.py b/modules/cytoid/dbutils.py index dd69d2c58d..bd7d8ecaa6 100644 --- a/modules/cytoid/dbutils.py +++ b/modules/cytoid/dbutils.py @@ -13,7 +13,7 @@ class CytoidBindInfoManager: def __init__(self, msg: Bot.MessageSession): self.target_id = msg.target.sender_id self.query = session.query(CytoidBindInfo).filter_by(targetId=self.target_id).first() - if self.query is None: + if not self.query: session.add_all([CytoidBindInfo(targetId=self.target_id, username='')]) session.commit() self.query = session.query(CytoidBindInfo).filter_by(targetId=self.target_id).first() diff --git a/modules/cytoid/locales/en_us.json b/modules/cytoid/locales/en_us.json index 94c1b91028..19b67f9ae9 100644 --- a/modules/cytoid/locales/en_us.json +++ b/modules/cytoid/locales/en_us.json @@ -1,14 +1,15 @@ { - "cytoid.help.b30": "查询 Cytoid 用户的 B30 列表。", - "cytoid.help.bind": "绑定 Cytoid 用户。", - "cytoid.help.desc": "查询 Cytoid 相关内容。", - "cytoid.help.profile": "查询一个用户的基本信息。", - "cytoid.help.r30": "查询 Cytoid 用户的 R30 列表。", - "cytoid.help.unbind": "解绑用户。", + "cytoid.help.b30": "Query the Best 30 list of Cytoid user. ", + "cytoid.help.bind": "Bind Cytoid user.", + "cytoid.help.desc": "Query about Cytoid.", + "cytoid.help.profile": "Query user profile.", + "cytoid.help.r30": "Query the Recent 30 list of Cytoid user. ", + "cytoid.help.unbind": "Unbind user.", "cytoid.message.b30.cooldown": "(据官方人员所述,此 API 的调用十分昂贵,故手动做出此限制,请谅解。)", - "cytoid.message.bind.failed": "绑定失败,请检查输入。", - "cytoid.message.bind.success": "绑定成功:", - "cytoid.message.unbind.success": "解绑成功。", - "cytoid.message.user_not_found": "用户不存在。", - "cytoid.message.user_unbound": "未绑定用户,请使用“${prefix}cytoid bind”绑定一个用户。" + "cytoid.message.b30.level": "Level", + "cytoid.message.bind.failed": "Binding failed. Please check input.", + "cytoid.message.bind.success": "Binding successful: ", + "cytoid.message.unbind.success": "Unbinding successful.", + "cytoid.message.user_not_found": "User not found.", + "cytoid.message.user_unbound": "The user is not bound. Please bind user by \"${prefix}cytoid bind\"." } \ No newline at end of file diff --git a/modules/cytoid/locales/zh_cn.json b/modules/cytoid/locales/zh_cn.json index 94c1b91028..8b7f1d506d 100644 --- a/modules/cytoid/locales/zh_cn.json +++ b/modules/cytoid/locales/zh_cn.json @@ -6,9 +6,10 @@ "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": "解绑成功。", - "cytoid.message.user_not_found": "用户不存在。", + "cytoid.message.user_not_found": "未找到用户。", "cytoid.message.user_unbound": "未绑定用户,请使用“${prefix}cytoid bind”绑定一个用户。" } \ No newline at end of file diff --git a/modules/cytoid/locales/zh_tw.json b/modules/cytoid/locales/zh_tw.json index 8195120328..b7384cad1e 100644 --- a/modules/cytoid/locales/zh_tw.json +++ b/modules/cytoid/locales/zh_tw.json @@ -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": "解除綁定成功。", diff --git a/modules/cytoid/profile.py b/modules/cytoid/profile.py index 2464ca24cb..ee6cdfa39f 100644 --- a/modules/cytoid/profile.py +++ b/modules/cytoid/profile.py @@ -11,7 +11,7 @@ async def cytoid_profile(msg: Bot.MessageSession): query_id = pat.lower() else: query_id = CytoidBindInfoManager(msg).get_bind_username() - if query_id is None: + if not query_id: await msg.finish(msg.locale.t('cytoid.message.user_unbound', prefix=msg.prefixes[0])) profile_url = 'http://services.cytoid.io/profile/' + query_id try: @@ -22,8 +22,6 @@ async def cytoid_profile(msg: Bot.MessageSession): raise e uid = profile['user']['uid'] nick = profile['user']['name'] - if nick is None: - nick = False avatar = profile['user']['avatar']['large'] basic_exp = profile['exp']['basicExp'] level_exp = profile['exp']['levelExp'] @@ -34,34 +32,34 @@ async def cytoid_profile(msg: Bot.MessageSession): grade: dict = profile['grade'] grade_t = [] max = grade.get('MAX') - if max is not None: + if max: grade_t.append(f'MAX: {max}') sss = grade.get('SSS') - if sss is not None: + if sss: grade_t.append(f'SSS: {sss}') ss = grade.get('SS') - if ss is not None: + if ss: grade_t.append(f'SS: {ss}') s = grade.get('S') - if s is not None: + if s: grade_t.append(f'S: {s}') aa = grade.get('AA') - if aa is not None: + if aa: grade_t.append(f'AA: {aa}') a = grade.get('A') - if a is not None: + if a: grade_t.append(f'A: {a}') b = grade.get('B') - if b is not None: + if b: grade_t.append(f'B: {b}') c = grade.get('C') - if c is not None: + if c: grade_t.append(f'C: {c}') d = grade.get('D') - if d is not None: + if d: grade_t.append(f'D: {d}') f = grade.get('F') - if f is not None: + if f: grade_t.append(f'F: {f}') text = f'UID: {uid}\n' + \ (f'Nickname: {nick}\n' if nick else '') + \ diff --git a/modules/cytoid/rating.py b/modules/cytoid/rating.py index b7c066efe1..db231bd071 100644 --- a/modules/cytoid/rating.py +++ b/modules/cytoid/rating.py @@ -3,7 +3,7 @@ import time import traceback import uuid -from datetime import datetime, timedelta +from datetime import datetime from os.path import abspath import aiohttp @@ -18,9 +18,10 @@ from core.logger import Logger from core.utils.http import get_url from core.utils.html2text import html2text +from core.utils.text import parse_time_string -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' @@ -36,7 +37,7 @@ async def get_rating(uid, query_type, msg: Bot.MessageSession): profile_level = profile_json['exp']['currentLevel'] profile_uid = profile_json['user']['uid'] nick = profile_json['user']['name'] - if nick is None: + if not nick: nick = profile_uid if 'avatar' in profile_json['user']: avatar_img = profile_json['user']['avatar']['medium'] @@ -88,7 +89,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 +99,7 @@ async def mkresources(x, rank): rt = x['rating'] details = x['details'] _date = datetime.strptime(x['date'], "%Y-%m-%dT%H:%M:%S.%fZ") - local_time = _date + timedelta(hours=8) + local_time = _date + parse_time_string(msg.options.get('timezone_offset', Config('timezone_offset', '+8'))) playtime = local_time.timestamp() nowtime = time.time() playtime = playtime - nowtime @@ -125,7 +126,7 @@ async def mkresources(x, rank): 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) @@ -162,7 +163,7 @@ async def mkresources(x, rank): drawtext.text((get_img_width - get_name_width - 150, 30), nick, '#ffffff', font=font4) font5 = ImageFont.truetype(os.path.abspath('./assets/Noto Sans CJK DemiLight.otf'), 20) - level_text = f'等级 {profile_level}' + level_text = f'{msg.locale.t("cytoid.message.b30.level")} {profile_level}' level_text_width = font5.getsize(level_text)[0] level_text_height = font5.getsize(level_text)[1] img_level = Image.new("RGBA", (level_text_width + 20, 40), '#050a1a') @@ -210,7 +211,7 @@ async def mkresources(x, rank): # shutil.rmtree(workdir) return {'status': True, 'path': savefilename} except Exception as e: - if str(e).startswith('404'): + if e.args == (404,): await msg.finish(msg.locale.t("cytoid.message.user_not_found")) traceback.print_exc() return {'status': False, 'text': msg.locale.t("error") + str(e)} diff --git a/modules/cytoid/utils.py b/modules/cytoid/utils.py index e50b4025c5..29172d7c6b 100644 --- a/modules/cytoid/utils.py +++ b/modules/cytoid/utils.py @@ -7,11 +7,11 @@ async def get_profile_name(userid): try: profile_url = 'http://services.cytoid.io/profile/' + userid profile = json.loads(await get_url(profile_url, 200)) - except: + except BaseException: return False uid = profile['user']['uid'] nick = profile['user']['name'] - if nick is None: + if not nick: nick = False return uid, nick diff --git a/modules/dice/__init__.py b/modules/dice/__init__.py index ff0c32179d..3bcd2b1e93 100644 --- a/modules/dice/__init__.py +++ b/modules/dice/__init__.py @@ -18,9 +18,9 @@ }) async def _(msg: Bot.MessageSession, dices, dc='0'): times = '1' - if '#' in dices: - times = dices.partition('#')[0] - dices = dices.partition('#')[2] + if 'x' in dices: + times = dices.partition('x')[0] + dices = dices.partition('x')[2] if not times.isdigit(): await msg.finish(msg.locale.t('dice.message.error.N.invalid') + times) if not dc.isdigit(): diff --git a/modules/dice/dice.py b/modules/dice/dice.py index 6cff8de6f0..58852e76f1 100644 --- a/modules/dice/dice.py +++ b/modules/dice/dice.py @@ -27,7 +27,7 @@ class DiceValueError(Exception): """骰子参数值错误""" def __init__(self, session, message, value=None): - if value is not None: + if value: self.message = session.locale.t("dice.message.error.value.invalid", value=value) + message else: self.message = session.locale.t("dice.message.error.value") + message diff --git a/modules/dice/locales/en_us.json b/modules/dice/locales/en_us.json index 30dfb1da1b..169adc2d17 100644 --- a/modules/dice/locales/en_us.json +++ b/modules/dice/locales/en_us.json @@ -8,7 +8,7 @@ "dice.help.option.m": "表示骰子数量,输出其所有点数之和(省略则默认 1)。", "dice.help.option.N": "将多项式的操作重复 N 次(投骰 N 次),之后输出 N 个结果。", "dice.help.option.n": "表示骰子面数。", - "dice.help.option.polynomial": "格式为“[#][]d[k(l)][±]...”,如“10d4-2d20”会输出 10 个 4 面骰所有点数之和减去 2 个 20 面骰点数之和。", + "dice.help.option.polynomial": "格式为“[x][]d[k(l)][±]...”,如“10d4-2d20”会输出 10 个 4 面骰所有点数之和减去 2 个 20 面骰点数之和。", "dice.help.option.polynomial.title": "多项式", "dice.help.option.y": "多项式中可以是整数(即调节值),如“d20+5”会输出 1 个 20 面骰的点数加上 5 的结果。", "dice.help.regex.desc": "(扔|投|掷|丢)个[面]骰子 Roll the specified dice.", @@ -16,7 +16,7 @@ "dice.message.dc.check": "▷ 判定成功数量:${success} 判定失败数量:${failed}", "dice.message.dc.failed": ",判定失败!", "dice.message.dc.success": ",判定成功!", - "dice.message.error": "解析骰子多项式时存在以下错误:", + "dice.message.error": "解析骰子多项式时发生以下错误:", "dice.message.error.dc.invalid": "发生错误:无效的 dc:", "dice.message.error.N.invalid": "发生错误:无效的投骰次数:", "dice.message.error.prompt": "第 ${i} 项发生", diff --git a/modules/dice/locales/zh_cn.json b/modules/dice/locales/zh_cn.json index 8f2e9d4c53..58292694a4 100644 --- a/modules/dice/locales/zh_cn.json +++ b/modules/dice/locales/zh_cn.json @@ -8,7 +8,7 @@ "dice.help.option.m": "表示骰子数量,输出其所有点数之和(省略则默认 1)。", "dice.help.option.N": "将多项式的操作重复 N 次(投骰 N 次),之后输出 N 个结果。", "dice.help.option.n": "表示骰子面数。", - "dice.help.option.polynomial": "格式为“[#][]d[k(l)][±]...”,如“10d4-2d20”会输出 10 个 4 面骰所有点数之和减去 2 个 20 面骰点数之和。", + "dice.help.option.polynomial": "格式为“[x][]d[k(l)][±]...”,如“10d4-2d20”会输出 10 个 4 面骰所有点数之和减去 2 个 20 面骰点数之和。", "dice.help.option.polynomial.title": "多项式", "dice.help.option.y": "多项式中可以是整数(即调节值),如“d20+5”会输出 1 个 20 面骰的点数加上 5 的结果。", "dice.help.regex.desc": "(扔|投|掷|丢)个[面]骰子[次] 投掷指定骰子。", @@ -16,7 +16,7 @@ "dice.message.dc.check": "▷ 判定成功数量:${success} 判定失败数量:${failed}", "dice.message.dc.failed": ",判定失败!", "dice.message.dc.success": ",判定成功!", - "dice.message.error": "解析骰子多项式时存在以下错误:", + "dice.message.error": "解析骰子多项式时发生以下错误:", "dice.message.error.dc.invalid": "发生错误:无效的 dc:", "dice.message.error.N.invalid": "发生错误:无效的投骰次数:", "dice.message.error.prompt": "第 ${i} 项发生", diff --git a/modules/dice/locales/zh_tw.json b/modules/dice/locales/zh_tw.json index 8cbe7b9ea3..eb0a98a696 100644 --- a/modules/dice/locales/zh_tw.json +++ b/modules/dice/locales/zh_tw.json @@ -8,7 +8,7 @@ "dice.help.option.m": "表示骰子數量,輸出其所有點數之和(省略則預設 1)。", "dice.help.option.N": "將多項式的行為重複 N 次(投骰 N 次),之後輸出 N 個結果。", "dice.help.option.n": "表示骰子面數。", - "dice.help.option.polynomial": "格式為「[#][]d[k(l)][±]...」,如「10d4-2d20」會輸出 10 個 4 面骰所有點數之和減去 2 個 20 面骰點數之和。", + "dice.help.option.polynomial": "格式為「[x][]d[k(l)][±]...」,如「10d4-2d20」會輸出 10 個 4 面骰所有點數之和減去 2 個 20 面骰點數之和。", "dice.help.option.polynomial.title": "多項式", "dice.help.option.y": "多項式中可以是整數(即調整值),如「d20+5」會輸出 1 個 20 面骰的點數加上 5 的結果。", "dice.help.regex.desc": "(扔|投|擲|丟)個[面]骰子[次] 投擲指定骰子。", @@ -16,7 +16,7 @@ "dice.message.dc.check": "▷ 判定成功數量:${success} 判定失敗數量:${failed}", "dice.message.dc.failed": ",判定失敗!", "dice.message.dc.success": ",判定成功!", - "dice.message.error": "解析骰子多項式時存在以下錯誤:", + "dice.message.error": "解析骰子多項式時發生以下錯誤:", "dice.message.error.dc.invalid": "發生錯誤:無效的 dc:", "dice.message.error.N.invalid": "發生錯誤:無效的投骰次數:", "dice.message.error.prompt": "第 ${i} 項發生", diff --git a/modules/dictionary/screenshot.py b/modules/dictionary/screenshot.py index 145d415c3c..eddfb5eae7 100644 --- a/modules/dictionary/screenshot.py +++ b/modules/dictionary/screenshot.py @@ -97,7 +97,7 @@ def join_url(base, target): '.dictionaries > .dictionary, .dictionaries.dictionary') trash = content.select( '.hwd_sound, .cobuild-logo, .pronIPASymbol, .title_frequency_container') - if trash is not None: + if trash: for x in trash: x.decompose() elif source == 'yd': diff --git a/modules/exchange_rate/__init__.py b/modules/exchange_rate/__init__.py index c9193e6a39..bfb9ccfaa1 100644 --- a/modules/exchange_rate/__init__.py +++ b/modules/exchange_rate/__init__.py @@ -14,9 +14,9 @@ @excr.command(' {{exchange_rate.help}}') -async def _(msg: Bot.MessageSession): - base = msg.parsed_msg[''].upper() - target = msg.parsed_msg[''].upper() +async def _(msg: Bot.MessageSession, base: str, target: str): + base = base.upper() + target = target.upper() amount_str = base[:-3] base_currency = base[-3:] @@ -54,18 +54,22 @@ async def exchange(base_currency, target_currency, amount: float, msg): else: unsupported_currencies.append(target_currency) if unsupported_currencies: - await msg.finish(f"{msg.locale.t('exchange_rate.message.error.invalid')}{' '.join(unsupported_currencies)}") + await msg.finish(f"{msg.locale.t('exchange_rate.message.error.unit')}{' '.join(unsupported_currencies)}") else: raise Exception(data['error-type']) url = f'https://v6.exchangerate-api.com/v6/{api_key}/pair/{base_currency}/{target_currency}/{amount}' data = await get_url(url, 200, fmt='json') - current_time = datetime.datetime.now().strftime("%Y-%m-%d") + time = msg.ts2strftime(datetime.datetime.now().timestamp(), time=False, timezone=False) if data['result'] == "success": exchange_rate = data['conversion_result'] await msg.finish( - msg.locale.t('exchange_rate.message', amount=amount, base=base_currency, exchange_rate=exchange_rate, - target=target_currency, time=current_time)) + msg.locale.t('exchange_rate.message', + amount=amount, + base=base_currency, + exchange_rate=exchange_rate, + target=target_currency, + time=time)) else: raise Exception(data['error-type']) diff --git a/modules/exchange_rate/locales/en_us.json b/modules/exchange_rate/locales/en_us.json index 31003fd07b..59aa45bc7f 100644 --- a/modules/exchange_rate/locales/en_us.json +++ b/modules/exchange_rate/locales/en_us.json @@ -1,9 +1,9 @@ { "exchange_rate.help": "Convert currency prices according to the exchange rate of the day.", "exchange_rate.help.desc": "Exchange rate conversion tool.", - "exchange_rate.help.regex.desc": "[] (兑|换)", + "exchange_rate.help.regex.desc": "[] (兑|换) Convert currency prices.", "exchange_rate.message": "${amount} ${base} = ${exchange_rate} ${target}\n(${time}, for reference only)", - "exchange_rate.message.error.invalid": "发生错误:无效的货币单位:", - "exchange_rate.message.error.non_digital": "An error occurred: Invalid amount, must be numeric.", - "exchange_rate.message.error.non_positive": "An error occurred: The amount must be positive." + "exchange_rate.message.invalid.unit": "Invalid currency unit: ", + "exchange_rate.message.invalid.non_digital": "Invalid amount, must be numeric.", + "exchange_rate.message.invalid.non_positive": "The amount must be positive." } \ No newline at end of file diff --git a/modules/exchange_rate/locales/zh_cn.json b/modules/exchange_rate/locales/zh_cn.json index c16592c23e..95eb8dba71 100644 --- a/modules/exchange_rate/locales/zh_cn.json +++ b/modules/exchange_rate/locales/zh_cn.json @@ -1,9 +1,9 @@ { "exchange_rate.help": "根据当日汇率转换货币价格。", "exchange_rate.help.desc": "汇率转换工具。", - "exchange_rate.help.regex.desc": "[] (兑|换)", + "exchange_rate.help.regex.desc": "[] (兑|换) 转换货币价格。", "exchange_rate.message": "${amount} ${base} = ${exchange_rate} ${target}\n(${time},仅供参考)", - "exchange_rate.message.error.invalid": "发生错误:无效的货币单位:", - "exchange_rate.message.error.non_digital": "发生错误:无效的金额,必须为数字。", - "exchange_rate.message.error.non_positive": "发生错误:金额必须为正数。" + "exchange_rate.message.invalid.unit": "无效的货币单位:", + "exchange_rate.message.invalid.non_digital": "无效的金额,必须为数字。", + "exchange_rate.message.invalid.non_positive": "金额必须为正数。" } \ No newline at end of file diff --git a/modules/exchange_rate/locales/zh_tw.json b/modules/exchange_rate/locales/zh_tw.json index 0156590ce0..fc491ad52f 100644 --- a/modules/exchange_rate/locales/zh_tw.json +++ b/modules/exchange_rate/locales/zh_tw.json @@ -1,9 +1,9 @@ { "exchange_rate.help": "依據當日匯率轉換貨幣價格。", "exchange_rate.help.desc": "匯率轉換工具。", - "exchange_rate.help.regex.desc": "[] (兌|換)", + "exchange_rate.help.regex.desc": "[] (兌|換) 轉換貨幣價格。", "exchange_rate.message": "${amount} ${base} = ${exchange_rate} ${target}\n(${time},仅供參考)", - "exchange_rate.message.error.invalid": "發生錯誤:無效的貨幣單位:", - "exchange_rate.message.error.non_digital": "發生錯誤:無效的金額,必須為數字。", - "exchange_rate.message.error.non_positive": "發生錯誤:金額必須為正數。" + "exchange_rate.message.invalid.unit": "無效的貨幣單位:", + "exchange_rate.message.invalid.non_digital": "無效的金額,必須為數字。", + "exchange_rate.message.invalid.non_positive": "金額必須為正數。" } \ No newline at end of file diff --git a/modules/github/__init__.py b/modules/github/__init__.py index 9c254ef7ba..05e1c3a4dd 100644 --- a/modules/github/__init__.py +++ b/modules/github/__init__.py @@ -2,10 +2,10 @@ from core.component import module from modules.github import repo, user, search -github = module('github', alias='gh', developers=['Dianliang233']) +github = module('github', alias='gh', developers=['Dianliang233'], desc='{github.help.desc}') -@github.handle(' {{github.help}}') +@github.command(' {{github.help}}') async def _(msg: Bot.MessageSession, name: str): if '/' in name: await repo.repo(msg) @@ -13,16 +13,16 @@ async def _(msg: Bot.MessageSession, name: str): await user.user(msg) -@github.handle('repo {{github.help.repo}}') +@github.command('repo {{github.help.repo}}') async def _(msg: Bot.MessageSession): await repo.repo(msg) -@github.handle(('user {{github.help.user}}')) +@github.command('user {{github.help.user}}') async def _(msg: Bot.MessageSession): await user.user(msg) -@github.handle('search {{github.help.search}}') +@github.command('search {{github.help.search}}') async def _(msg: Bot.MessageSession): await search.search(msg) diff --git a/modules/github/locales/en_us.json b/modules/github/locales/en_us.json index 18a4153e5a..ac7f9c3962 100644 --- a/modules/github/locales/en_us.json +++ b/modules/github/locales/en_us.json @@ -1,12 +1,13 @@ { + "github.help.desc": "Github query tool.", "github.help": "Trying to automatically identifying and distinguishing repo/user.", - "github.help.repo": "Getting GitHub repository information.", - "github.help.search": "Searching repositories on GitHub.", - "github.help.user": "Getting GitHub user or organization information.", - "github.message.repo.not_found": "The repository does not exist, please check your input.", + "github.help.repo": "Get GitHub repository information.", + "github.help.search": "Search repositories on GitHub.", + "github.help.user": "Get GitHub user or organization information.", + "github.message.repo.not_found": "The repository does not exist. Please check your input.", "github.message.search": "Successfully to search for ${result} results:", - "github.message.search.none": "搜索成功,未找到结果。", + "github.message.search.none": "No results found.", "github.message.search.more_information": "Truncated due to chat limits; ${more_result} more results may not be shown.", - "github.message.search.not_found": "The repository cannot be found; please check your input.", - "github.message.user.not_found": "The user cannot be found; please check your input." + "github.message.search.not_found": "The repository cannot be found. Please check your input.", + "github.message.user.not_found": "The user cannot be found. Please check your input." } \ No newline at end of file diff --git a/modules/github/locales/zh_cn.json b/modules/github/locales/zh_cn.json index 9375af76dd..d9a047803a 100644 --- a/modules/github/locales/zh_cn.json +++ b/modules/github/locales/zh_cn.json @@ -1,12 +1,13 @@ { "github.help": "尝试自动识别并区分 repo/user。", + "github.help.desc": "GitHub 查询工具。", "github.help.repo": "获取 GitHub 存储库信息。", "github.help.search": "搜索 GitHub 上的存储库。", "github.help.user": "获取 GitHub 用户或组织信息。", "github.message.repo.not_found": "此存储库不存在,请检查输入。", "github.message.search": "搜索成功,共 ${result} 条结果:", - "github.message.search.none": "搜索成功,未找到结果。", "github.message.search.more_information": "另有 ${more_result} 条结果未显示。", + "github.message.search.none": "搜索成功,未找到结果。", "github.message.search.not_found": "未找到存储库,请检查输入。", "github.message.user.not_found": "查无此人,请检查输入。" } \ No newline at end of file diff --git a/modules/github/locales/zh_tw.json b/modules/github/locales/zh_tw.json index 739ab9b880..92bb1374bd 100644 --- a/modules/github/locales/zh_tw.json +++ b/modules/github/locales/zh_tw.json @@ -1,4 +1,5 @@ { + "github.help.desc": "GitHub 查詢工具。", "github.help": "嘗試自動辨識並區分 repo/user。", "github.help.repo": "取得 Github 儲存庫資訊。", "github.help.search": "搜尋 Github 上的儲存庫。", @@ -8,5 +9,5 @@ "github.message.search.none": "搜尋成功,未找到結果。", "github.message.search.more_information": "另有 ${more_result} 條結果未顯示。", "github.message.search.not_found": "未找到儲存庫,請校對輸入。", - "github.message.user.not_found": "找不到使用者,請校對輸入。" + "github.message.user.not_found": "未找到使用者,請校對輸入。" } \ No newline at end of file diff --git a/modules/github/repo.py b/modules/github/repo.py index 773800fc0f..53d15ed620 100644 --- a/modules/github/repo.py +++ b/modules/github/repo.py @@ -11,18 +11,18 @@ async def repo(msg: Bot.MessageSession): try: result = await get_url('https://api.github.com/repos/' + msg.parsed_msg[''], 200, fmt='json') rlicense = 'Unknown' - if 'license' in result and result['license'] is not None: + if 'license' in result and result['license']: if 'spdx_id' in result['license']: rlicense = result['license']['spdx_id'] is_fork = result['fork'] parent = False - if result['homepage'] is not None: + if result['homepage']: website = 'Website: ' + str(Url(result['homepage'])) + '\n' else: website = '' - if result['mirror_url'] is not None: + if result['mirror_url']: mirror = f' (This is a mirror of {str(Url(result["mirror_url"]))} )' else: mirror = '' @@ -32,7 +32,7 @@ async def repo(msg: Bot.MessageSession): parent = f' (This is a fork of {parent_name} )' desc = result['description'] - if desc is None: + if not desc: desc = '' else: desc = '\n' + result['description'] diff --git a/modules/github/user.py b/modules/github/user.py index 2ef88adb29..b262b99ee2 100644 --- a/modules/github/user.py +++ b/modules/github/user.py @@ -10,21 +10,21 @@ async def user(msg: Bot.MessageSession): try: result = await get_url('https://api.github.com/users/' + msg.parsed_msg[''], 200, fmt='json') optional = [] - if 'hireable' in result and result['hireable'] is True: + if 'hireable' in result and result['hireable']: optional.append('Hireable') - if 'is_staff' in result and result['is_staff'] is True: + if 'is_staff' in result and result['is_staff']: optional.append('GitHub Staff') - if 'company' in result and result['company'] is not None: + if 'company' in result and result['company']: optional.append('Work · ' + result['company']) - if 'twitter_username' in result and result['twitter_username'] is not None: + if 'twitter_username' in result and result['twitter_username']: optional.append('Twitter · ' + result['twitter_username']) - if 'blog' in result and result['blog'] is not None: + if 'blog' in result and result['blog']: optional.append('Site · ' + result['blog']) - if 'location' in result and result['location'] is not None: + if 'location' in result and result['location']: optional.append('Location · ' + result['location']) bio = result['bio'] - if bio is None: + if not bio: bio = '' else: bio = '\n' + result['bio'] diff --git a/modules/idlist/__init__.py b/modules/idlist/__init__.py index 81c2f2935e..53ca741702 100644 --- a/modules/idlist/__init__.py +++ b/modules/idlist/__init__.py @@ -21,7 +21,7 @@ async def _(msg: Bot.MessageSession, query: str): for x in result[0:5]: plain_texts.append(f'{x["enumName"]}:{x["key"]} -> {x["value"]}') if resp['data']['count'] > 5: - plain_texts.append(msg.locale.t('idlist.message.collapse')) + plain_texts.append(msg.locale.t('message.collapse', amount='5') + msg.locale.t('idlist.message.collapse')) plain_texts.append('https://ca.projectxero.top/idlist/' + resp['data']['hash']) await msg.finish('\n'.join(plain_texts)) else: diff --git a/modules/idlist/locales/en_us.json b/modules/idlist/locales/en_us.json index 04fd7083eb..d88696aac7 100644 --- a/modules/idlist/locales/en_us.json +++ b/modules/idlist/locales/en_us.json @@ -1,5 +1,5 @@ { "idlist.help": "Query ID table of Minecraft: Bedrock Edition, supported only in Chinese.", - "idlist.message.collapse": "...show only the first 5 results, see more: ", + "idlist.message.collapse": "See more: ", "idlist.message.none": "No results found." } \ No newline at end of file diff --git a/modules/idlist/locales/zh_cn.json b/modules/idlist/locales/zh_cn.json index 6c183a1ea3..efa7023918 100644 --- a/modules/idlist/locales/zh_cn.json +++ b/modules/idlist/locales/zh_cn.json @@ -1,5 +1,5 @@ { "idlist.help": "查询 Minecraft:基岩版的 ID 表。", - "idlist.message.collapse": "...仅显示前 5 条结果,查看更多:", + "idlist.message.collapse": "查看更多:", "idlist.message.none": "没有找到结果。" } \ No newline at end of file diff --git a/modules/idlist/locales/zh_tw.json b/modules/idlist/locales/zh_tw.json index 1dae22979b..ca4f4a7d2a 100644 --- a/modules/idlist/locales/zh_tw.json +++ b/modules/idlist/locales/zh_tw.json @@ -1,5 +1,5 @@ { "idlist.help": "查詢 Minecraft:基岩版的 ID 表。", - "idlist.message.collapse": "...僅顯示前 5 條結果,檢視更多:", + "idlist.message.collapse": "檢視更多:", "idlist.message.none": "沒有找到結果。" } \ No newline at end of file diff --git a/modules/ip/__init__.py b/modules/ip/__init__.py index bbd25a7691..4b88ecd766 100644 --- a/modules/ip/__init__.py +++ b/modules/ip/__init__.py @@ -116,10 +116,10 @@ async def format_ip(msg, info: Dict[str, Any]): return f'''\ {info['ip']} {msg.locale.t('ip.message.type')}IPv{info['version']} {ip_property[info['ip_property']]}{msg.locale.t('ip.message.ip_property')}{f""" -{msg.locale.t('ip.message.real_ip')}{info['real_ip']}""" if info['real_ip'] is not None else ''}{f""" -{msg.locale.t('ip.message.location')}{f"{info['city']}, " if info['city'] is not None else ''}{f"{info['region']}, " if info['region'] is not None else ''}{info['country']}""" if info['country'] is not None else ''}{f" ({parse_coordinate('longitude', info['longitude'])}, {parse_coordinate('latitude', info['latitude'])})" if info['longitude'] is not None and info['latitude'] is not None else ''}{f""" -{msg.locale.t('ip.message.postal_code')}{info['postal_code']}""" if info['postal_code'] is not None else ''}{f""" -{msg.locale.t('ip.message.organization')}{info['organization']}""" if info['organization'] is not None else ''}{f""" -{msg.locale.t('ip.message.asn')}{info['asn']}""" if info['asn'] is not None else ''}{f" ({info['asn_organization']}) " if info['asn_organization'] is not None else ''}{f""" -{msg.locale.t('ip.message.utc')}UTC{(info['offset'] / 3600):+g}""" if info['offset'] is not None else ''}{f""" -{msg.locale.t('ip.message.reverse')}{info['reverse']}""" if info['reverse'] is not None and info['reverse'] != info['ip'] else ''}''' +{msg.locale.t('ip.message.real_ip')}{info['real_ip']}""" if info['real_ip'] else ''}{f""" +{msg.locale.t('ip.message.location')}{f"{info['city']}, " if info['city'] else ''}{f"{info['region']}, " if info['region'] else ''}{info['country']}""" if info['country'] else ''}{f" ({parse_coordinate('longitude', info['longitude'])}, {parse_coordinate('latitude', info['latitude'])})" if info['longitude'] and info['latitude'] else ''}{f""" +{msg.locale.t('ip.message.postal_code')}{info['postal_code']}""" if info['postal_code'] else ''}{f""" +{msg.locale.t('ip.message.organization')}{info['organization']}""" if info['organization'] else ''}{f""" +{msg.locale.t('ip.message.asn')}{info['asn']}""" if info['asn'] else ''}{f" ({info['asn_organization']}) " if info['asn_organization'] else ''}{f""" +{msg.locale.t('ip.message.utc')}UTC{(info['offset'] / 3600):+g}""" if info['offset'] else ''}{f""" +{msg.locale.t('ip.message.reverse')}{info['reverse']}""" if info['reverse'] and info['reverse'] != info['ip'] else ''}''' diff --git a/modules/langconv/locales/en_us.json b/modules/langconv/locales/en_us.json index 79fccb265e..57bad53748 100644 --- a/modules/langconv/locales/en_us.json +++ b/modules/langconv/locales/en_us.json @@ -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。" } diff --git a/modules/langconv/locales/zh_cn.json b/modules/langconv/locales/zh_cn.json index 79fccb265e..57bad53748 100644 --- a/modules/langconv/locales/zh_cn.json +++ b/modules/langconv/locales/zh_cn.json @@ -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。" } diff --git a/modules/langconv/locales/zh_tw.json b/modules/langconv/locales/zh_tw.json index d25aaa96f6..0dba8448a2 100644 --- a/modules/langconv/locales/zh_tw.json +++ b/modules/langconv/locales/zh_tw.json @@ -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。" } diff --git a/modules/maimai/__init__.py b/modules/maimai/__init__.py index 8d207d1c72..e25e7db4a6 100644 --- a/modules/maimai/__init__.py +++ b/modules/maimai/__init__.py @@ -1,13 +1,13 @@ -from core.builtins import command_prefix +import math import traceback from config import Config -from core.builtins import Bot, command_prefix, Plain, Image as BImage +from core.builtins import Bot, Plain, Image as BImage from core.scheduler import CronTrigger from core.utils.image import msgchain2image from modules.maimai.libraries.maimai_best_50 import generate -from modules.maimai.libraries.maimaidx_api_data import get_alias, search_by_alias, update_alias, update_covers -from modules.maimai.libraries.maimaidx_music import get_cover_len5_id, Music, TotalList +from modules.maimai.libraries.maimaidx_api_data import get_alias, get_info, search_by_alias, update_alias, update_covers +from modules.maimai.libraries.maimaidx_music import get_cover_len5_id, TotalList from modules.maimai.libraries.maimaidx_project import get_level_process, \ get_plate_process, get_player_score, get_rank, get_score_list from .regex import * @@ -23,12 +23,6 @@ diff_label_zht = ['綠', '黃', '紅'] -def song_txt(music: Music): - return [Plain(f"{music.id}\u200B. {music.title}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music.id)}.png"), - Plain(f"\n{'/'.join(str(ds) for ds in music.ds)}")] - - def get_diff(diff): diff = diff.lower() diff_label_lower = [label.lower() for label in diff_label] @@ -48,12 +42,12 @@ def get_diff(diff): mai = module('maimai', recommend_modules='maimai_regex', developers=['mai-bot', 'OasisAkari', 'DoroWolf'], - alias='mai', desc='{maimai.help.desc}') + alias='mai', support_languages=['zh_cn'], desc='{maimai.help.desc}') @mai.command('base [] {{maimai.help.base}}') async def _(msg: Bot.MessageSession, constant: float, constant_max: float = None): - if constant_max is not None: + if constant_max: if constant > constant_max: await msg.finish(msg.locale.t('error.range.invalid')) result_set = await base_level_q(constant, constant_max) @@ -65,7 +59,7 @@ async def _(msg: Bot.MessageSession, constant: float, constant_max: float = None result_set = await base_level_q(constant) s = msg.locale.t("maimai.message.base", constant=round(constant, 1)) + "\n" for elem in result_set: - s += f"{elem[0]}\u200B. {elem[1]}{' (DX)' if elem[5] == 'DX' else ''} {elem[3]} {elem[4]} ({elem[2]})\n" + s += f"{elem[0]}\u200B. {elem[1]}{msg.locale.t('message.brackets', msg='DX') if elem[5] == 'DX' else ''} {elem[3]} {elem[4]} ({elem[2]})\n" if len(result_set) == 0: await msg.finish(msg.locale.t("maimai.message.music_not_found")) elif len(result_set) > 200: @@ -79,7 +73,7 @@ async def _(msg: Bot.MessageSession, constant: float, constant_max: float = None async def base_level_q(ds1, ds2=None): result_set = [] - if ds2 is not None: + if ds2: music_data = (await total_list.get()).filter(ds=(ds1, ds2)) else: music_data = (await total_list.get()).filter(ds=ds1) @@ -100,7 +94,7 @@ async def _(msg: Bot.MessageSession, level: str): result_set = await diff_level_q(level) s = msg.locale.t("maimai.message.level", level=level) + "\n" for elem in result_set: - s += f"{elem[0]}\u200B. {elem[1]}{' (DX)' if elem[5] == 'DX' else ''} {elem[3]} {elem[4]} ({elem[2]})\n" + s += f"{elem[0]}\u200B. {elem[1]}{msg.locale.t('message.brackets', msg='DX') if elem[5] == 'DX' else ''} {elem[3]} {elem[4]} ({elem[2]})\n" if len(result_set) == 0: await msg.finish(msg.locale.t("maimai.message.music_not_found")) elif len(result_set) <= 10: @@ -136,7 +130,7 @@ async def _(msg: Bot.MessageSession, keyword: str): else: search_result = msg.locale.t("maimai.message.search", keyword=name) + "\n" for music in sorted(res, key=lambda i: int(i['id'])): - search_result += f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}\n" + search_result += f"{music['id']}\u200B. {music['title']}{msg.locale.t('message.brackets', msg='DX') if music['type'] == 'DX' else ''}\n" if len(res) <= 10: await msg.finish([Plain(search_result.strip())]) else: @@ -151,10 +145,12 @@ async def _(msg: Bot.MessageSession, sid: str): sid = sid[2:] else: await msg.finish(msg.locale.t('maimai.message.error.non_digital')) + music = (await total_list.get()).by_id(sid) if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - title = f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}" + + title = await get_info(msg, music, cover=False) alias = await get_alias(msg, sid) if len(alias) == 0: await msg.finish(msg.locale.t("maimai.message.alias.alias_not_found")) @@ -166,10 +162,10 @@ async def _(msg: Bot.MessageSession, sid: str): @mai.command('b50 [] {{maimai.help.b50}}') async def _(msg: Bot.MessageSession, username: str = None): - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender, 'b50': True} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username, 'b50': True} img = await generate(msg, payload) @@ -191,7 +187,7 @@ async def _(msg: Bot.MessageSession, id_or_alias: str, diff: str = None): res = msg.locale.t("maimai.message.song.prompt") + "\n" for sid in sorted(sid_list, key=int): s = (await total_list.get()).by_id(sid) - res += f"{s['id']}\u200B. {s['title']}{' (DX)' if s['type'] == 'DX' else ''}\n" + res += f"{s['id']}\u200B. {s['title']}{msg.locale.t('message.brackets', msg='DX') if s['type'] == 'DX' else ''}\n" await msg.finish(res.strip()) else: sid = str(sid_list[0]) @@ -199,7 +195,7 @@ async def _(msg: Bot.MessageSession, id_or_alias: str, diff: str = None): if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - if diff is not None: + if diff: diff_index = get_diff(diff) if not diff_index or (len(music['ds']) == 4 and diff_index == 4): await msg.finish(msg.locale.t("maimai.message.chart_not_found")) @@ -229,17 +225,16 @@ async def _(msg: Bot.MessageSession, id_or_alias: str, diff: str = None): touch=chart['notes'][3], brk=chart['notes'][4], charter=chart['charter']) - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), Plain(message)]) + await msg.finish(await get_info(msg, music, Plain(message))) else: - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), - Plain(msg.locale.t("maimai.message.song", - artist=music['basic_info']['artist'], genre=music['basic_info']['genre'], - bpm=music['basic_info']['bpm'], version=music['basic_info']['from'], - level='/'.join((str(ds) for ds in music['ds']))))]) + message = msg.locale.t( + "maimai.message.song", + artist=music['basic_info']['artist'], + genre=music['basic_info']['genre'], + bpm=music['basic_info']['bpm'], + version=music['basic_info']['from'], + level='/'.join((str(ds) for ds in music['ds']))) + await msg.finish(await get_info(msg, music, Plain(message))) @mai.command('info [] {{maimai.help.info}}') @@ -255,7 +250,7 @@ async def _(msg: Bot.MessageSession, id_or_alias: str, username: str = None): res = msg.locale.t("maimai.message.song.prompt") + "\n" for sid in sorted(sid_list, key=int): s = (await total_list.get()).by_id(sid) - res += f"{s['id']}\u200B. {s['title']}{' (DX)' if s['type'] == 'DX' else ''}\n" + res += f"{s['id']}\u200B. {s['title']}{msg.locale.t('message.brackets', msg='DX') if s['type'] == 'DX' else ''}\n" await msg.finish(res.strip()) else: sid = str(sid_list[0]) @@ -264,26 +259,24 @@ async def _(msg: Bot.MessageSession, id_or_alias: str, username: str = None): if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} output = await get_player_score(msg, payload, sid) - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), Plain(output)]) + await msg.finish(await get_info(msg, music, Plain(output))) @mai.command('plate [] {{maimai.help.plate}}') async def _(msg: Bot.MessageSession, plate: str, username: str = None): - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -320,10 +313,10 @@ async def _(msg: Bot.MessageSession, level: str, goal: str, username: str = None "FDX", "FDX+"] - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -332,10 +325,10 @@ async def _(msg: Bot.MessageSession, level: str, goal: str, username: str = None if level_num < 8: await msg.finish(msg.locale.t("maimai.message.process.less_than_8")) else: - await msg.finish(msg.locale.t("maimai.message.process.error.goal_invalid")) + await msg.finish(msg.locale.t("maimai.message.level_invalid")) if goal.upper() not in goal_list: - await msg.finish(msg.locale.t("maimai.message.process.error.goal_invalid")) + await msg.finish(msg.locale.t("maimai.message.goal_invalid")) output, get_img = await get_level_process(msg, payload, level, goal) @@ -348,10 +341,10 @@ async def _(msg: Bot.MessageSession, level: str, goal: str, username: str = None @mai.command('rank [] {{maimai.help.rank}}') async def _(msg: Bot.MessageSession, username: str = None): - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -360,10 +353,10 @@ async def _(msg: Bot.MessageSession, username: str = None): @mai.command('scorelist [] {{maimai.help.scorelist}}') async def _(msg: Bot.MessageSession, level: str, username: str = None): - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -407,22 +400,22 @@ async def _(msg: Bot.MessageSession, dx_type: str = None): music_data = (await total_list.get()).filter(level=level, diff=[get_diff(diff)], type=dx_type) if len(music_data) == 0: - rand_result = msg.locale.t("maimai.message.music_not_found") + await msg.finish(msg.locale.t("maimai.message.music_not_found")) else: - rand_result = song_txt(music_data.random()) - await msg.finish(rand_result) - except Exception: - Logger.error(traceback.format_exc()) + music = music_data.random() + await msg.finish(await get_info(msg, music, Plain(f"\n{'/'.join(str(ds) for ds in music.ds)}"))) + except ValueError: await msg.finish(msg.locale.t("maimai.message.random.error")) @mai.command('random {{maimai.help.random}}') async def _(msg: Bot.MessageSession): - await msg.finish(song_txt((await total_list.get()).random())) + music = (await total_list.get()).random() + await msg.finish(await get_info(msg, music, Plain(f"\n{'/'.join(str(ds) for ds in music.ds)}"))) -@mai.command('scoreline {{maimai.help.scoreline}}') -async def _(msg: Bot.MessageSession, diff: str, sid: str, scoreline: float): +@mai.command('scoreline {{maimai.help.scoreline}}') +async def _(msg: Bot.MessageSession, diff: str, sid: str, score: float): try: if not sid.isdigit(): if sid[:2].lower() == "id": @@ -441,7 +434,7 @@ async def _(msg: Bot.MessageSession, diff: str, sid: str, scoreline: float): bonus_score = total_score * 0.01 / brk # 奖励分 break_2550_reduce = bonus_score * 0.25 # 一个 BREAK 2550 减少 25% 奖励分 break_2000_reduce = bonus_score * 0.6 + 500 # 一个 BREAK 2000 减少 500 基础分和 60% 奖励分 - reduce = 101 - scoreline # 理论值与给定完成率的差,以百分比计 + reduce = 101 - score # 理论值与给定完成率的差,以百分比计 if reduce <= 0 or reduce >= 101: raise ValueError tap_great = "{:.2f}".format(total_score * reduce / 10000) # 一个 TAP GREAT 减少 100 分 @@ -450,9 +443,9 @@ async def _(msg: Bot.MessageSession, diff: str, sid: str, scoreline: float): b2t_2550_great_prop = "{:.4f}".format(break_2550_reduce / total_score * 100) b2t_2000_great = "{:.3f}".format(break_2000_reduce / 100) # 一个 TAP GREAT 减少 100 分 b2t_2000_great_prop = "{:.4f}".format(break_2000_reduce / total_score * 100) - await msg.finish(f'''{music['title']}{' (DX)' if music['type'] == 'DX' else ''} {diff_label[diff_index]} + await msg.finish(f'''{music['title']}{msg.locale.t('message.brackets', msg='DX') if music['type'] == 'DX' else ''} {diff_label[diff_index]} {msg.locale.t('maimai.message.scoreline', - scoreline=scoreline, + scoreline=score, tap_great=tap_great, tap_great_prop=tap_great_prop, brk=brk, @@ -461,7 +454,46 @@ async def _(msg: Bot.MessageSession, diff: str, sid: str, scoreline: float): b2t_2000_great=b2t_2000_great, b2t_2000_great_prop=b2t_2000_great_prop)}''') except ValueError: - await msg.finish(msg.locale.t('maimai.message.scoreline.error', prefix=command_prefix[0])) + await msg.finish(msg.locale.t('maimai.message.scoreline.error', prefix=msg.prefixes[0])) + + +@mai.command('rating {{maimai.help.rating}}') +async def _(msg: Bot.MessageSession, base: float, score: float): + if score: + await msg.finish([Plain(max(0, computeRa(base, score)))]) + + +def computeRa(base: float, achievement: float) -> int: + if achievement < 50: + baseRa = 7.0 + elif achievement < 60: + baseRa = 8.0 + elif achievement < 70: + baseRa = 9.6 + elif achievement < 75: + baseRa = 11.2 + elif achievement < 80: + baseRa = 12.0 + elif achievement < 90: + baseRa = 13.6 + elif achievement < 94: + baseRa = 15.2 + elif achievement < 97: + baseRa = 16.8 + elif achievement < 98: + baseRa = 20.0 + elif achievement < 99: + baseRa = 20.3 + elif achievement < 99.5: + baseRa = 20.8 + elif achievement < 100: + baseRa = 21.1 + elif achievement < 100.5: + baseRa = 21.6 + else: + baseRa = 22.4 + + return math.floor(base * (min(100.5, achievement) / 100) * baseRa) @mai.command('update', required_superuser=True) @@ -471,6 +503,7 @@ async def _(msg: Bot.MessageSession): else: await msg.finish(msg.locale.t("failed")) + @mai.schedule(CronTrigger.from_crontab('0 0 * * *')) async def _(): Logger.info('Updating maimai alias...') @@ -478,4 +511,4 @@ async def _(): await update_alias() except Exception: if Config('debug'): - Logger.error(traceback.format_exc()) \ No newline at end of file + Logger.error(traceback.format_exc()) diff --git a/modules/maimai/libraries/maimai_best_50.py b/modules/maimai/libraries/maimai_best_50.py index ac3d7ad48c..87727f1465 100644 --- a/modules/maimai/libraries/maimai_best_50.py +++ b/modules/maimai/libraries/maimai_best_50.py @@ -362,7 +362,6 @@ def getDir(self): def computeRa(ds: float, achievement: float) -> int: - baseRa = 22.4 if achievement < 50: baseRa = 7.0 elif achievement < 60: @@ -389,6 +388,8 @@ def computeRa(ds: float, achievement: float) -> int: baseRa = 21.1 elif achievement < 100.5: baseRa = 21.6 + else: + baseRa = 22.4 return math.floor(ds * (min(100.5, achievement) / 100) * baseRa) diff --git a/modules/maimai/libraries/maimaidx_api_data.py b/modules/maimai/libraries/maimaidx_api_data.py index 6843b77c92..ae2e2c57c2 100644 --- a/modules/maimai/libraries/maimaidx_api_data.py +++ b/modules/maimai/libraries/maimaidx_api_data.py @@ -1,42 +1,32 @@ import os import shutil +import traceback import ujson as json +from core.builtins import Bot, Plain, Image from core.logger import Logger from core.utils.cache import random_cache_path from core.utils.http import get_url, post_url, download_to_cache -from .maimaidx_music import get_cover_len5_id, TotalList +from .maimaidx_music import get_cover_len5_id, Music, TotalList total_list = TotalList() - assets_path = os.path.abspath('./assets/maimai') -cover_dir = f"{assets_path}/static/mai/cover" async def update_alias(): url = "https://download.fanyu.site/maimai/alias.json" - input_data = await get_url(url, 200, fmt='json') - - output_data = {} - for key, values in input_data.items(): - for value in values: - if value == "未找到": - continue - if value not in output_data: - output_data[value] = [] - output_data[value].append(key) - - output_data = {k: output_data[k] for k in sorted(output_data)} - + data = await get_url(url, 200, fmt='json') + file_path = os.path.join(assets_path, "mai_alias.json") with open(file_path, 'w') as file: - json.dump(output_data, file) + json.dump(data, file) return True async def update_covers(): + cover_dir = f"{assets_path}/static/mai/cover" url = f"https://www.diving-fish.com/maibot/static.zip" download_file = await download_to_cache(url, timeout=60) @@ -56,7 +46,20 @@ async def update_covers(): return True -async def get_alias(msg, input_): +async def get_info(msg: Bot.MessageSession, music: Music, *details): + info = [Plain(f"{music.id}\u200B. {music.title}{msg.locale.t('message.brackets', msg='DX') if music['type'] == 'DX' else ''}")] + try: + img = f"https://www.diving-fish.com/covers/{get_cover_len5_id(music.id)}.png" + await get_url(img, 200, attempt=1, fmt='read') + info.append(Image(img)) + except: + info.append(Image("https://www.diving-fish.com/covers/00000.png")) + if details: + info.extend(details) + return info + + +async def get_alias(msg, sid): file_path = os.path.join(assets_path, "mai_alias.json") if not os.path.exists(file_path): @@ -65,15 +68,15 @@ async def get_alias(msg, input_): data = json.load(file) result = [] - if input_ in data: - result = data[input_] # 此处的列表是歌曲别名列表 + if sid in data: + result = data[sid] # 此处的列表是歌曲别名列表 return result async def search_by_alias(msg, input_): result = [] - input_ = input_.replace("_", " ").strip() + input_ = input_.replace("_", " ").strip().lower() res = (await total_list.get()).filter(title=input_) for s in res: result.append(s['id']) @@ -86,11 +89,12 @@ async def search_by_alias(msg, input_): with open(file_path, 'r') as file: data = json.load(file) - for alias, ids in data.items(): - if input_ in ids: - if alias in result: - result.remove(alias) - result.append(alias) # 此处的列表是歌曲 ID 列表 + for sid, aliases in data.items(): + aliases = [alias.lower() for alias in aliases] + if input_ in aliases: + if sid in result: + result.remove(sid) + result.append(sid) # 此处的列表是歌曲 ID 列表 return result @@ -102,7 +106,7 @@ async def get_record(msg, payload): data=json.dumps(payload), status_code=200, headers={'Content-Type': 'application/json', 'accept': '*/*'}, fmt='json') - except Exception as e: + except ValueError as e: if str(e).startswith('400'): if "qq" in payload: await msg.finish(msg.locale.t("maimai.message.user_unbound")) @@ -111,7 +115,8 @@ async def get_record(msg, payload): elif str(e).startswith('403'): await msg.finish(msg.locale.t("maimai.message.forbidden")) else: - raise + traceback.print_exc() + return data @@ -122,7 +127,7 @@ async def get_plate(msg, payload): data=json.dumps(payload), status_code=200, headers={'Content-Type': 'application/json', 'accept': '*/*'}, fmt='json') - except Exception as e: + except ValueError as e: if str(e).startswith('400'): if "qq" in payload: await msg.finish(msg.locale.t("maimai.message.user_unbound")) @@ -131,5 +136,6 @@ async def get_plate(msg, payload): elif str(e).startswith('403'): await msg.finish(msg.locale.t("maimai.message.forbidden")) else: - raise + traceback.print_exc() + return data diff --git a/modules/maimai/libraries/maimaidx_music.py b/modules/maimai/libraries/maimaidx_music.py index 17a9935b6f..bc55f8edc3 100644 --- a/modules/maimai/libraries/maimaidx_music.py +++ b/modules/maimai/libraries/maimaidx_music.py @@ -160,7 +160,7 @@ def __init__(self): self.total_list = None async def get(self): - if self.total_list is None: + if not self.total_list: obj = await get_url('https://www.diving-fish.com/api/maimaidxprober/music_data', 200, fmt='json') total_list: MusicList = MusicList(obj) for __i in range(len(total_list)): diff --git a/modules/maimai/libraries/maimaidx_project.py b/modules/maimai/libraries/maimaidx_project.py index 9dfe9e5164..5b44f2b5f7 100644 --- a/modules/maimai/libraries/maimaidx_project.py +++ b/modules/maimai/libraries/maimaidx_project.py @@ -89,19 +89,21 @@ async def get_rank(msg, payload): - player_data = await get_record(msg, payload) + time = msg.ts2strftime(datetime.now().timestamp(), timezone=False) - username = player_data['username'] url = f"https://www.diving-fish.com/api/maimaidxprober/rating_ranking" rank_data = await get_url(url, 200, fmt='json') - sorted_data = sorted(rank_data, key=lambda x: x['ra'], reverse=True) # 根据rating排名并倒序 + rank_data = sorted(rank_data, key=lambda x: x['ra'], reverse=True) # 根据rating排名并倒序 + + player_data = await get_record(msg, payload) + username = player_data['username'] rating = 0 rank = None total_rating = 0 - total_rank = len(sorted_data) + total_rank = len(rank_data) - for i, scoreboard in enumerate(sorted_data): + for i, scoreboard in enumerate(rank_data): if scoreboard['username'] == username: rank = i + 1 rating = scoreboard['ra'] @@ -112,14 +114,15 @@ async def get_rank(msg, payload): average_rating = total_rating / total_rank surpassing_rate = (total_rank - rank) / total_rank * 100 - time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - formatted_average_rating = "{:.4f}".format(average_rating) - formatted_surpassing_rate = "{:.2f}".format(surpassing_rate) - await msg.finish(msg.locale.t('maimai.message.rank', time=time, total_rank=total_rank, user=username, - rating=rating, rank=rank, average_rating=formatted_average_rating, - surpassing_rate=formatted_surpassing_rate)) + await msg.finish(msg.locale.t('maimai.message.rank', + time=time, + total_rank=total_rank, + user=username, + rating=rating, + rank=rank, + average_rating="{:.4f}".format(average_rating), + surpassing_rate="{:.2f}".format(surpassing_rate))) async def get_player_score(msg, payload, input_id): @@ -228,7 +231,7 @@ async def get_level_process(msg, payload, process, goal): elif goal in syncRank: if verlist[record_index]['fs']: self_record = syncRank[sync_rank.index(verlist[record_index]['fs'])] - output += f"{s[0]}\u200B. {s[1]}{' (DX)' if s[5] == 'DX' else ''} {s[2]} {s[3]} {self_record}\n" + output += f"{s[0]}\u200B. {s[1]}{msg.locale.t('message.brackets', msg='DX') if s[5] == 'DX' else ''} {s[2]} {s[3]} {self_record}\n" if len(song_remain) > 10: # 若剩余歌曲大于10个则使用图片形式 get_img = True else: @@ -255,7 +258,7 @@ async def get_score_list(msg, payload, level): output_lines = [] for s in enumerate(sorted(song_list, key=lambda i: i['achievements'], reverse=True)): # 根据成绩排序 music = (await total_list.get()).by_id(str(s[1]['id'])) - output = f"{music.id}\u200B. {music.title}{' (DX)' if music.type == 'DX' else ''} {diffs[s[1]['level_index']]} {music.ds[s[1]['level_index']]} {s[1]['achievements']}%" + output = f"{music.id}\u200B. {music.title}{msg.locale.t('message.brackets', msg='DX') if music.type == 'DX' else ''} {diffs[s[1]['level_index']]} {music.ds[s[1]['level_index']]} {s[1]['achievements']}%" if s[1]["fc"] and s[1]["fs"]: output += f" {combo_conversion.get(s[1]['fc'], '')} {sync_conversion.get(s[1]['fs'], '')}" elif s[1]["fc"] or s[1]["fs"]: @@ -420,7 +423,7 @@ async def get_plate_process(msg, payload, plate): elif goal == '舞舞': if verlist[record_index]['fs']: self_record = syncRank[sync_rank.index(verlist[record_index]['fs'])] - output += f"{s[0]}\u200B. {s[1]}{' (DX)' if s[5] == 'DX' else ''} {s[2]} {s[3]} {self_record}".strip() + '\n' + output += f"{s[0]}\u200B. {s[1]}{msg.locale.t('message.brackets', msg='DX') if s[5] == 'DX' else ''} {s[2]} {s[3]} {self_record}".strip() + '\n' if len(song_remain_difficult) > 10: # 若剩余歌曲大于10个则使用图片形式 get_img = True else: @@ -445,7 +448,7 @@ async def get_plate_process(msg, payload, plate): elif goal == '舞舞': if verlist[record_index]['fs']: self_record = syncRank[sync_rank.index(verlist[record_index]['fs'])] - output += f"{m.id}\u200B. {m.title}{' (DX)' if m.type == 'DX' else ''} {diffs[s[1]]} {m.ds[s[1]]} {self_record}".strip( + output += f"{m.id}\u200B. {m.title}{msg.locale.t('message.brackets', msg='DX') if m.type == 'DX' else ''} {diffs[s[1]]} {m.ds[s[1]]} {self_record}".strip( ) + '\n' if len(song_remain) > 10: # 若剩余歌曲大于10个则使用图片形式 get_img = True diff --git a/modules/maimai/locales/en_us.json b/modules/maimai/locales/en_us.json index 963261807a..157f70f1b3 100644 --- a/modules/maimai/locales/en_us.json +++ b/modules/maimai/locales/en_us.json @@ -30,11 +30,10 @@ "maimai.message.chart_not_found": "未找到符合要求的谱面。", "maimai.message.error.non_digital": "发生错误:歌曲 ID 必须为数字!", "maimai.message.forbidden": "此用户禁止了其他人获取数据。", - "maimai.message.global": "游玩次数:${count}\n拟合难度:${fit_diff}\n平均达成率:${avg_score}\n平均 DX 分数:${avg_dx}\n谱面成绩标准差:${std_dev}", - "maimai.message.global.music_not_found": "未找到该曲目的统计信息。", - "maimai.message.global.level_not_found": "未找到该难度的统计信息。", + "maimai.message.goal_invalid": "无效的目标评级,请检查输入。", "maimai.message.info.no_record": "未游玩过此谱面", "maimai.message.level": "以下为 ${level} 级的曲目列表:", + "maimai.message.level_invalid": "无效的等级,请检查输入。", "maimai.message.music_not_found": "未找到符合要求的歌曲。", "maimai.message.no_username": "请提供用户名!", "maimai.message.plate": "您的${plate}剩余进度如下:\nBasic 剩余 ${song_remain_basic} 首,\nAdvanced 剩余 ${song_remain_advanced} 首,\nExpert 剩余 ${song_remain_expert} 首,\nMaster 剩余 ${song_remain_master} 首", @@ -47,8 +46,6 @@ "maimai.message.plate.remaster": ",\nRe:MASTER 剩余 ${song_remain_remaster} 首", "maimai.message.process": "您还有 ${song_remain} 首 ${process} 级曲目没有达成 ${goal}。", "maimai.message.process.completed": "您已达成 ${process} 级曲目全谱面 ${goal}。", - "maimai.message.process.error.goal_invalid": "发生错误:无效的目标评级,请检查输入。", - "maimai.message.process.error.level_invalid": "发生错误:无效的等级,请检查输入。", "maimai.message.process.last": "您的 ${process} 级歌曲全谱面 ${goal} 剩余曲目如下:", "maimai.message.process.less_than_8": "歌曲等级必须大于 8 级。", "maimai.message.random.error": "发生错误:随机歌曲失败,请检查输入。", diff --git a/modules/maimai/locales/zh_cn.json b/modules/maimai/locales/zh_cn.json index 1f51986032..d2183f5135 100644 --- a/modules/maimai/locales/zh_cn.json +++ b/modules/maimai/locales/zh_cn.json @@ -18,6 +18,7 @@ "maimai.help.random": "随机一首歌曲。", "maimai.help.random.filter": "随机一首指定条件的歌曲,输入为“*”则表示无条件。", "maimai.help.rank": "查看用户在查分器上的分数排行。", + "maimai.help.rating": "根据定数计算 Rating。", "maimai.help.scoreline": "查询歌曲的分数线。", "maimai.help.scorelist": "查看用户在对应等级的分数列表。", "maimai.help.search": "根据歌名(或一部分)搜索歌曲。", @@ -30,11 +31,10 @@ "maimai.message.chart_not_found": "未找到符合要求的谱面。", "maimai.message.error.non_digital": "发生错误:歌曲 ID 必须为数字!", "maimai.message.forbidden": "此用户禁止了其他人获取数据。", - "maimai.message.global": "游玩次数:${count}\n拟合难度:${fit_diff}\n平均达成率:${avg_score}\n平均 DX 分数:${avg_dx}\n谱面成绩标准差:${std_dev}", - "maimai.message.global.music_not_found": "未找到该曲目的统计信息。", - "maimai.message.global.level_not_found": "未找到该难度的统计信息。", + "maimai.message.goal_invalid": "无效的目标评级,请检查输入。", "maimai.message.info.no_record": "未游玩过此谱面", "maimai.message.level": "以下为 ${level} 级的曲目列表:", + "maimai.message.level_invalid": "无效的等级,请检查输入。", "maimai.message.music_not_found": "未找到符合要求的歌曲。", "maimai.message.no_username": "请提供用户名!", "maimai.message.plate": "您的${plate}剩余进度如下:\nBasic 剩余 ${song_remain_basic} 首,\nAdvanced 剩余 ${song_remain_advanced} 首,\nExpert 剩余 ${song_remain_expert} 首,\nMaster 剩余 ${song_remain_master} 首", @@ -47,15 +47,13 @@ "maimai.message.plate.remaster": ",\nRe:MASTER 剩余 ${song_remain_remaster} 首", "maimai.message.process": "您还有 ${song_remain} 首 ${process} 级曲目没有达成 ${goal}。", "maimai.message.process.completed": "您已达成 ${process} 级曲目全谱面 ${goal}。", - "maimai.message.process.error.goal_invalid": "发生错误:无效的目标评级,请检查输入。", - "maimai.message.process.error.level_invalid": "发生错误:无效的等级,请检查输入。", "maimai.message.process.last": "您的 ${process} 级歌曲全谱面 ${goal} 剩余曲目如下:", "maimai.message.process.less_than_8": "歌曲等级必须大于 8 级。", "maimai.message.random.error": "发生错误:随机歌曲失败,请检查输入。", "maimai.message.rank": "截至 ${time},\n查分器共有 ${total_rank} 名用户,平均分数为 ${average_rating}。\n${user} 的分数为 ${rating},排名为 #${rank},\n已经超越了 ${surpassing_rate}% 的玩家。", "maimai.message.scoreline": "分数线 ${scoreline}% 允许的最多 TAP GREAT 数量为 ${tap_great}(每个-${tap_great_prop}%),\nBREAK 2550(一共 ${brk} 个)等价于 ${b2t_2550_great} 个 TAP GREAT(-${b2t_2550_great_prop}%),\nBREAK 2000(一共 ${brk} 个)等价于 ${b2t_2000_great} 个 TAP GREAT(-${b2t_2000_great_prop}%)", "maimai.message.scoreline.error": "发生错误:计算结果失败,请检查输入。", - "maimai.message.scorelist": "以下为 ${user} 的 ${level} 级分数列表:", + "maimai.message.scorelist": "以下为 ${user} 的 ${level} 级成绩列表:", "maimai.message.search": "“${keyword}”的搜索结果:", "maimai.message.song": "艺术家:${artist}\n分类:${genre}\nBPM:${bpm}\n版本:${version}\n难度:${level}", "maimai.message.song.dx": "${diff} ${level} (${ds})\nTAP:${tap}\nHOLD:${hold}\nSLIDE:${slide}\nTOUCH:${touch}\nBREAK:${brk}\n谱师:${charter}", diff --git a/modules/maimai/locales/zh_tw.json b/modules/maimai/locales/zh_tw.json index 0948d7c176..40c8dd1920 100644 --- a/modules/maimai/locales/zh_tw.json +++ b/modules/maimai/locales/zh_tw.json @@ -18,6 +18,7 @@ "maimai.help.random": "隨機一首歌曲。", "maimai.help.random.filter": "隨機一首指定條件的歌曲,輸入「*」則表示無條件。", "maimai.help.rank": "查看使用者在查分器上的分數排行。", + "maimai.help.rating": "依據定數計算 Rating。", "maimai.help.scoreline": "查詢歌曲的分數線。", "maimai.help.scorelist": "查看使用者在對應等級的分數列表。", "maimai.help.search": "依據歌名(或一部分)搜尋歌曲。", @@ -30,11 +31,10 @@ "maimai.message.chart_not_found": "未找到符合要求的譜面。", "maimai.message.error.non_digital": "發生錯誤:歌曲 ID 必須為數字!", "maimai.message.forbidden": "此使用者禁止了其他人取得資料。", - "maimai.message.global": "游玩次数:${count}\n拟合难度:${fit_diff}\n平均达成率:${avg_score}\n平均 DX 分数:${avg_dx}\n谱面成绩标准差:${std_dev}", - "maimai.message.global.music_not_found": "未找到该曲目的统计信息。", - "maimai.message.global.level_not_found": "未找到该难度的统计信息。", + "maimai.message.goal_invalid": "無效的目標評級,請校對輸入。", "maimai.message.info.no_record": "未遊玩過此譜面", "maimai.message.level": "以下為難度 ${level} 的曲目列表:", + "maimai.message.level_invalid": "無效的等級,請校對輸入。", "maimai.message.music_not_found": "未找到符合要求的歌曲。", "maimai.message.no_username": "請提供使用者名稱!", "maimai.message.plate": "您的${plate}剩餘進度如下:\nBasic 剩餘 ${song_remain_basic} 首,\nAdvanced 剩餘 ${song_remain_advanced} 首,\nExpert 剩餘 ${song_remain_expert} 首,\nMaster 剩餘 ${song_remain_master} 首", @@ -47,15 +47,13 @@ "maimai.message.plate.remaster": ",Re:MASTER 剩餘 ${song_remain_remaster} 首", "maimai.message.process": "您還有 ${song_remain} 首 ${process} 級曲目沒有達成 ${goal}。", "maimai.message.process.completed": "您已達成 ${process} 級曲目全譜面 ${goal}。", - "maimai.message.process.error.goal_invalid": "發生錯誤:無效的目標評級,請校對輸入。", - "maimai.message.process.error.level_invalid": "發生錯誤:無效的等級,請校對輸入。", "maimai.message.process.last": "您的 ${process} 級歌曲全譜面 ${goal} 剩餘曲目如下:", "maimai.message.process.less_than_8": "歌曲等級必須大於 8 級。", "maimai.message.random.error": "發生錯誤:無法隨機歌曲,請校對輸入。", "maimai.message.rank": "截至 ${time},\n查分器共有 ${total_rank} 名使用者,平均分數為 ${average_rating},\n${user} 的分數為 ${rating},排名為 #${rank},\n已經超越了 ${surpassing_rate}% 的玩家。", "maimai.message.scoreline": "分數線 ${scoreline}% 允許的最多 TAP GREAT 數量為 ${tap_great}(每個-${tap_great_prop}%)。\nBREAK 2550(一共 ${brk} 個)相當於 ${b2t_2550_great} 個 TAP GREAT(-${b2t_2550_great_prop}%),\nBREAK 2000(一共 ${brk} 個)相當於 ${b2t_2000_great} 個 TAP GREAT(-${b2t_2000_great_prop}%)", "maimai.message.scoreline.error": "發生錯誤:無法計算結果,請校對輸入。", - "maimai.message.scorelist": "以下為 ${user} 的 ${level} 級分數列表:", + "maimai.message.scorelist": "以下為 ${user} 的 ${level} 級成績列表:", "maimai.message.search": "「${keyword}」的搜尋結果:", "maimai.message.song": "藝術家:${artist}\n分類:${genre}\nBPM:${bpm}\n版本:${version}\n難度:${level}", "maimai.message.song.dx": "${diff} ${level} (${ds})\nTAP:${tap}\nHOLD:${hold}\nSLIDE:${slide}\nTOUCH:${touch}\nBREAK:${brk}\n譜師:${charter}", diff --git a/modules/maimai/regex.py b/modules/maimai/regex.py index 05fc718890..6d6c7696b0 100644 --- a/modules/maimai/regex.py +++ b/modules/maimai/regex.py @@ -5,8 +5,8 @@ from core.component import module from core.logger import Logger from core.utils.image import msgchain2image -from modules.maimai.libraries.maimaidx_api_data import get_alias, search_by_alias -from modules.maimai.libraries.maimaidx_music import get_cover_len5_id, Music, TotalList +from modules.maimai.libraries.maimaidx_api_data import get_alias, get_info, search_by_alias +from modules.maimai.libraries.maimaidx_music import get_cover_len5_id, TotalList from modules.maimai.libraries.maimaidx_project import get_level_process, get_plate_process, get_player_score total_list = TotalList() @@ -20,12 +20,6 @@ diff_label_zht = ['綠', '黃', '紅'] -def song_txt(music: Music): - return [Plain(f"{music.id}\u200B. {music.title}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music.id)}.png"), - Plain(f"\n{'/'.join(str(ds) for ds in music.ds)}")] - - def get_diff(diff): diff = diff.lower() diff_label_lower = [label.lower() for label in diff_label] @@ -64,20 +58,19 @@ async def _(msg: Bot.MessageSession): res = msg.locale.t("maimai.message.song.prompt") + "\n" for sid in sorted(sid_list, key=int): s = (await total_list.get()).by_id(sid) - res += f"{s['id']}\u200B. {s['title']}{' (DX)' if s['type'] == 'DX' else ''}\n" + res += f"{s['id']}\u200B. {s['title']}{msg.locale.t('message.brackets', msg='DX') if s['type'] == 'DX' else ''}\n" await msg.finish(res.strip()) else: music = (await total_list.get()).by_id(str(sid_list[0])) if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']} {' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), - Plain(msg.locale.t("maimai.message.song", - artist=music['basic_info']['artist'], genre=music['basic_info']['genre'], - bpm=music['basic_info']['bpm'], version=music['basic_info']['from'], - level='/'.join((str(ds) for ds in music['ds']))))]) + await msg.finish(await get_info(msg, music, Plain(msg.locale.t("maimai.message.song", + artist=music['basic_info']['artist'], + genre=music['basic_info']['genre'], + bpm=music['basic_info']['bpm'], + version=music['basic_info']['from'], + level='/'.join((str(ds) for ds in music['ds'])))))) @mai_regex.regex(re.compile(r"(.+)\s?有什[么麼]分\s?(.+)?"), desc='{maimai.help.maimai_regex.info}') @@ -94,7 +87,7 @@ async def _(msg: Bot.MessageSession): res = msg.locale.t("maimai.message.song.prompt") + "\n" for sid in sorted(sid_list, key=int): s = (await total_list.get()).by_id(sid) - res += f"{s['id']}\u200B. {s['title']}{' (DX)' if s['type'] == 'DX' else ''}\n" + res += f"{s['id']}\u200B. {s['title']}{msg.locale.t('message.brackets', msg='DX') if s['type'] == 'DX' else ''}\n" await msg.finish(res.strip()) else: sid = str(sid_list[0]) @@ -103,18 +96,16 @@ async def _(msg: Bot.MessageSession): if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} output = await get_player_score(msg, payload, sid) - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), Plain(output)]) + await msg.finish(await get_info(msg, music, Plain(output))) @mai_regex.regex(re.compile(r"(?:id)?(\d+)\s?有什(?:么别|麼別)名", flags=re.I), desc='{maimai.help.maimai_regex.alias}') @@ -123,7 +114,7 @@ async def _(msg: Bot.MessageSession): music = (await total_list.get()).by_id(sid) if not music: await msg.finish(msg.locale.t("maimai.message.music_not_found")) - title = f"{music['id']}\u200B. {music['title']}{' (DX)' if music['type'] == 'DX' else ''}" + title = f"{music['id']}\u200B. {music['title']}{msg.locale.t('message.brackets', msg='DX') if music['type'] == 'DX' else ''}" alias = await get_alias(msg, sid) if len(alias) == 0: await msg.finish(msg.locale.t("maimai.message.alias.alias_not_found")) @@ -132,14 +123,6 @@ async def _(msg: Bot.MessageSession): result += "\n".join(alias) await msg.finish([Plain(result.strip())]) - await msg.finish( - [Plain(f"{music['id']}\u200B. {music['title']} {' (DX)' if music['type'] == 'DX' else ''}\n"), - BImage(f"https://www.diving-fish.com/covers/{get_cover_len5_id(music['id'])}.png"), - Plain(msg.locale.t("maimai.message.song", - artist=music['basic_info']['artist'], genre=music['basic_info']['genre'], - bpm=music['basic_info']['bpm'], version=music['basic_info']['from'], - level='/'.join((str(ds) for ds in music['ds']))))]) - @mai_regex.regex(re.compile(r"(?:随个|隨個)\s?((?:dx|DX|sd|SD|标准|標準)\s?)?([绿綠黄黃红紅紫白]?)\s?([0-9]+\+?)"), desc="{maimai.help.maimai_regex.random}") @@ -160,12 +143,11 @@ async def _(msg: Bot.MessageSession): music_data = (await total_list.get()).filter(level=level, diff=[get_diff(res.groups()[1])], type=tp) if len(music_data) == 0: - rand_result = msg.locale.t("maimai.message.music_not_found") + await msg.finish(msg.locale.t("maimai.message.music_not_found")) else: - rand_result = song_txt(music_data.random()) - await msg.finish(rand_result) - except Exception as e: - Logger.error(traceback.format_exc()) + music = music_data.random() + await msg.finish(await get_info(msg, music, Plain(f"\n{'/'.join(str(ds) for ds in music.ds)}"))) + except ValueError: await msg.finish(msg.locale.t("maimai.message.random.error")) @@ -173,10 +155,10 @@ async def _(msg: Bot.MessageSession): async def _(msg: Bot.MessageSession): plate = msg.matched_msg.groups()[0] + msg.matched_msg.groups()[1] username = msg.matched_msg.groups()[2] - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -217,10 +199,10 @@ async def _(msg: Bot.MessageSession): username = msg.matched_msg.groups()[2] if not goal: return - if username is None and msg.target.sender_from == "QQ": + if not username and msg.target.sender_from == "QQ": payload = {'qq': msg.session.sender} else: - if username is None: + if not username: await msg.finish(msg.locale.t("maimai.message.no_username")) payload = {'username': username} @@ -232,7 +214,7 @@ async def _(msg: Bot.MessageSession): return if goal.upper() not in goal_list: - await msg.finish(msg.locale.t("maimai.message.process.error.goal_invalid")) + await msg.finish(msg.locale.t("maimai.message.goal_invalid")) output, get_img = await get_level_process(msg, payload, level, goal) diff --git a/modules/mcbbs_news/__init__.py b/modules/mcbbs_news/__init__.py index 2c280c7bd2..924496b561 100644 --- a/modules/mcbbs_news/__init__.py +++ b/modules/mcbbs_news/__init__.py @@ -15,7 +15,7 @@ async def main(msg: Bot.MessageSession): res = await news(msg) Logger.debug('res' + str(res)) - if res is None: + if not res: message = msg.locale.t('mcbbs_news.message.not_found') else: lst = [] diff --git a/modules/mcbbs_news/mcbbs_news.py b/modules/mcbbs_news/mcbbs_news.py index 8a1f07ab34..b62ac54039 100644 --- a/modules/mcbbs_news/mcbbs_news.py +++ b/modules/mcbbs_news/mcbbs_news.py @@ -10,17 +10,18 @@ web_render = CFG.get_url('web_render') web_render_local = CFG.get_url('web_render_local') + async def news(msg): api = 'https://www.mcbbs.net/forum-news-1.html' if web_render: use_local = True if web_render_local else False api = (web_render_local if use_local else web_render) + 'source?url=' + api - html = await get_url(api, 200) + html = await get_url(api, 200, request_private_ip=True) Logger.debug(html) bs = BeautifulSoup(html, 'html.parser') results = bs.select('#threadlisttableid > tbody[id^="normalthread_"]') res = [] - if results is not None: + if results: for i in results: if len(res) == 5: break diff --git a/modules/mcv/locales/en_us.json b/modules/mcv/locales/en_us.json index a2a89361d7..f4a38fd73d 100644 --- a/modules/mcv/locales/en_us.json +++ b/modules/mcv/locales/en_us.json @@ -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." -} \ No newline at end of file +} diff --git a/modules/mcv/locales/zh_cn.json b/modules/mcv/locales/zh_cn.json index 3b0780d79d..9d1a24634a 100644 --- a/modules/mcv/locales/zh_cn.json +++ b/modules/mcv/locales/zh_cn.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 失败。" -} \ No newline at end of file +} diff --git a/modules/mcv/locales/zh_tw.json b/modules/mcv/locales/zh_tw.json index 28e2ecb27b..1bc6f7f870 100644 --- a/modules/mcv/locales/zh_tw.json +++ b/modules/mcv/locales/zh_tw.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 失敗。" -} \ No newline at end of file +} diff --git a/modules/mcv/mcv.py b/modules/mcv/mcv.py index 9a2716bf27..4e58eb2703 100644 --- a/modules/mcv/mcv.py +++ b/modules/mcv/mcv.py @@ -1,5 +1,6 @@ import json import re +import datetime from google_play_scraper import app as google_play_scraper @@ -18,14 +19,16 @@ async def mcv(msg): time_snapshot = None for v in data['versions']: if v['id'] == release: - time_release = v['releaseTime'] - elif v['id'] == snapshot: - time_snapshot = v['releaseTime'] + time_release = datetime.datetime.fromisoformat(v['releaseTime']).timestamp() + if v['id'] == snapshot: + time_snapshot = datetime.datetime.fromisoformat(v['releaseTime']).timestamp() message1 = msg.locale.t( "mcv.message.mcv.launcher", release=data['latest']['release'], - snapshot=data['latest']['snapshot']) + snapshot=data['latest']['snapshot'], + release_time=msg.ts2strftime(time_release), + snapshot_time=msg.ts2strftime(time_snapshot)) except (ConnectionError, OSError): # Probably... message1 = msg.locale.t("mcv.message.mcv.launcher.failed") try: @@ -79,10 +82,10 @@ async def mcbv(msg): msg2 = f'Beta: {fix.join(beta)}\nPreview: {fix.join(preview)}\nRelease: {fix.join(release)}' return \ (f"""{msg.locale.t("mcv.message.mcbv.play_store")} -{play_store_version if play_store_version is not None else msg.locale.t('mcv.message.mcbv.get_failed')} +{play_store_version if play_store_version else msg.locale.t('mcv.message.mcbv.get_failed')} """ if IP.country != 'China' else '') + \ f"""{msg.locale.t("mcv.message.mcbv.ms_store")} -{ms_store_version if ms_store_version is not None else msg.locale.t('mcv.message.mcbv.get_failed')} +{ms_store_version if ms_store_version else msg.locale.t('mcv.message.mcbv.get_failed')} """ + \ msg.locale.t("mcv.message.mcbv", jira_ver=msg2) diff --git a/modules/mcv_rss/locales/en_us.json b/modules/mcv_rss/locales/en_us.json index 52d1e3b12e..d4bb6045c0 100644 --- a/modules/mcv_rss/locales/en_us.json +++ b/modules/mcv_rss/locales/en_us.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." -} \ No newline at end of file +} diff --git a/modules/mcv_rss/locales/zh_cn.json b/modules/mcv_rss/locales/zh_cn.json index d720c3dc84..211ec484c8 100644 --- a/modules/mcv_rss/locales/zh_cn.json +++ b/modules/mcv_rss/locales/zh_cn.json @@ -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} 快照。" -} \ No newline at end of file + "mcv_rss.message.mcv_rss.release": "启动器已更新 ${version} 正式版。\n更新时间:", + "mcv_rss.message.mcv_rss.snapshot": "启动器已更新 ${version} 快照。\n更新时间:" +} diff --git a/modules/mcv_rss/locales/zh_tw.json b/modules/mcv_rss/locales/zh_tw.json index 972bf85250..b5a8982f45 100644 --- a/modules/mcv_rss/locales/zh_tw.json +++ b/modules/mcv_rss/locales/zh_tw.json @@ -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} 快照。" -} \ No newline at end of file + "mcv_rss.message.mcv_rss.release": "啟動器已更新 ${version} 正式版。\n更新時間:", + "mcv_rss.message.mcv_rss.snapshot": "啟動器已更新 ${version} 快照。\n更新時間:" +} diff --git a/modules/meme/nbnhhsh.py b/modules/meme/nbnhhsh.py index 8d75615635..6a63c12154 100644 --- a/modules/meme/nbnhhsh.py +++ b/modules/meme/nbnhhsh.py @@ -25,7 +25,7 @@ async def nbnhhsh(term: str, locale: Locale): trans = result['trans'] count = trans.__len__() return f'[{locale.t("meme.message.nbnhhsh")}] {locale.t("meme.message.nbnhhsh.result", result=count)}{"、".join(trans)}' - elif 'inputting' in result and result['inputting'] != []: + elif 'inputting' in result and result['inputting']: inputting = result['inputting'] count = inputting.__len__() return f'[{locale.t("meme.message.nbnhhsh")}] {locale.t("meme.message.nbnhhsh.result.ai", result=count)}{"、".join(inputting)}' diff --git a/modules/meme/urban.py b/modules/meme/urban.py index 115b2e8ca4..ef56eef25d 100644 --- a/modules/meme/urban.py +++ b/modules/meme/urban.py @@ -11,6 +11,7 @@ web_render = CFG.get_url('web_render') web_render_local = CFG.get_url('web_render_local') + async def urban(term: str, locale: Locale): '''查询urban dictionary。 @@ -29,7 +30,7 @@ async def urban(term: str, locale: Locale): 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'}) Logger.debug(text) data = json.loads(text)['list'] - if data == []: + if not data: return f'[{locale.t("meme.message.urban")}] {locale.t("meme.message.not_found")}' else: count = data.__len__() diff --git a/modules/mkey/__init__.py b/modules/mkey/__init__.py index 975b52c956..f1100481b1 100644 --- a/modules/mkey/__init__.py +++ b/modules/mkey/__init__.py @@ -25,7 +25,7 @@ async def mkey(msg: Bot.MessageSession, device: str, month: int, day: int, inqui if len(inquiry_num) not in [6, 8, 10]: await msg.finish(msg.locale.t('mkey.message.error.inquiry_num')) device_code = device_codes[device.lower()] - if device_id is None and device_code == "HAC": + if not device_id and device_code == "HAC": await msg.finish(msg.locale.t('mkey.message.error.hal')) result = get_mkey(inquiry_num, month, day, device_id, device_code) diff --git a/modules/mkey/generator.py b/modules/mkey/generator.py index ca1b6b0981..6316d6088a 100644 --- a/modules/mkey/generator.py +++ b/modules/mkey/generator.py @@ -417,9 +417,9 @@ def generate(self, inquiry, month=None, day=None, aux=None, device=None): if not inquiry.isdigit(): raise ValueError("Inquiry string must represent a decimal number.") - if month is None: + if not month: month = datetime.date.today().month - if day is None: + if not day: day = datetime.date.today().day if month < 1 or month > 12: diff --git a/modules/mod_dl/__init__.py b/modules/mod_dl/__init__.py index c361f0a130..eb6148cbbc 100644 --- a/modules/mod_dl/__init__.py +++ b/modules/mod_dl/__init__.py @@ -17,28 +17,19 @@ x_api_key = Config("curseforge_api_key") if not x_api_key: # CurseForge API Key 未配置,使用镜像 https://mcim.z0z0r4.top ...(z0z0r4 不想解析网页) - MCIM_MODE = True + enable_mirror = True else: - MCIM_MODE = False - - -def cn_chk(string: str): - for word in string: - if u'\u4e00' <= word <= u'\u9fff': - return True - return False + enable_mirror = False @mod_dl.handle(' [] {{mod_dl.help}}') async def main(msg: Bot.MessageSession, mod_name: str, version: str = None): ver = version - if version is not None: + if version: match_ver = re.match(r'^\d+\.\d+\.\d+$|^\d+\.\d+$|\d+w\d+[abcd]', version) - if match_ver is None: + if not match_ver: mod_name += ' ' + version ver = False - if cn_chk(mod_name): - await msg.finish(msg.locale.t("mod_dl.message.unsupport")) async def search_modrinth(name: str, ver: str): url = f'https://api.modrinth.com/v2/search?query={name}&limit=10' @@ -47,7 +38,7 @@ async def search_modrinth(name: str, ver: str): else: url += f'&facets=[["project_type:mod"]]' resp = await get_url(url, 200, fmt="json", timeout=5, attempt=3) - if resp is not None: + if resp: results = [] if len(resp["hits"]) == 0: return None @@ -56,7 +47,7 @@ async def search_modrinth(name: str, ver: str): return results async def search_curseforge(name: str, ver: str): - if MCIM_MODE: + if enable_mirror: # https://mcim.z0z0r4.top/docs#/Curseforge/curseforge_search_curseforge_search_get url = f'https://mcim.z0z0r4.top/curseforge/search?gameId=432&searchFilter={name}&sortField=2&sortOrder=desc&pageSize=10&classId=6' headers = None @@ -72,8 +63,8 @@ async def search_curseforge(name: str, ver: str): results = [] try: resp = await get_url(url, 200, fmt="json", timeout=5, attempt=3, headers=headers) - if resp is not None: - if not MCIM_MODE: # 没提供 pagination + if resp: + if not enable_mirror: # 没提供 pagination if resp["pagination"]["resultCount"] == 0: return None for mod in resp["data"]: @@ -85,11 +76,11 @@ async def search_curseforge(name: str, ver: str): async def get_modrinth_project_version(project_id: str, ver: str): url = f'https://api.modrinth.com/v2/project/{project_id}/version?game_versions=["{ver}"]&featured=true' resp = (await get_url(url, 200, fmt="json", timeout=5, attempt=3))[0] - if resp is not None: + if resp: return resp async def get_curseforge_mod_version_index(modid: str): - if MCIM_MODE: + if enable_mirror: # https://mcim.z0z0r4.top/docs#/Curseforge/get_mod_curseforge_mod__modid_slug__get url = f'https://mcim.z0z0r4.top/curseforge/mod/{modid}' headers = None @@ -100,11 +91,11 @@ async def get_curseforge_mod_version_index(modid: str): } url = f'https://api.curseforge.com/v1/mods/{modid}' resp = await get_url(url, 200, fmt="json", timeout=5, attempt=3, headers=headers) - if resp is not None: + if resp: return resp["data"]['latestFilesIndexes'] async def get_curseforge_mod_file(modid: str, ver: str): - if MCIM_MODE: + if enable_mirror: url = f'https://mcim.z0z0r4.top/curseforge/mod/{modid}/files?gameVersion={ver}' headers = None else: @@ -116,7 +107,7 @@ async def get_curseforge_mod_file(modid: str, ver: str): try: resp = await get_url(url, 200, fmt="json", timeout=5, attempt=3, headers=headers) - if resp is not None: + if resp: return resp["data"][0] except Exception: traceback.print_exc() @@ -124,14 +115,14 @@ async def get_curseforge_mod_file(modid: str, ver: str): # 搜索 Mod result = await asyncio.gather(*(search_modrinth(mod_name, ver), search_curseforge(mod_name, ver))) cache_result = [] - if result[0] is None and result[1] is None: + if not result[0] and not result[1]: await msg.finish(msg.locale.t("mod_dl.message.not_found")) else: # 合并搜索结果 reply_text, count = [], 0 # 先显示 CurseForge 的结果 - if result[1] is None: + if not result[1]: reply_text.append(msg.locale.t("mod_dl.message.curseforge.not_found")) else: reply_text.append(msg.locale.t("mod_dl.message.curseforge.result")) @@ -140,7 +131,7 @@ async def get_curseforge_mod_file(modid: str, ver: str): reply_text.append(f"{count}. {mod[1]}") cache_result.append(mod) - if result[0] is None: + if not result[0]: reply_text.append(msg.locale.t("mod_dl.message.modrinth.not_found")) reply_text.append(msg.locale.t("mod_dl.message.modrinth.result")) for mod in result[0]: @@ -148,35 +139,35 @@ async def get_curseforge_mod_file(modid: str, ver: str): reply_text.append(f"{count}. {mod[1]}") cache_result.append(mod) - reply = await msg.wait_reply('\n'.join(reply_text) + '\n' + msg.locale.t("mod_dl.message.prompt")) + reply = await msg.wait_reply('\n'.join(reply_text) + '\n' + msg.locale.t("mod_dl.message.prompt"), delete=True) replied = reply.as_display(text_only=True) # 查找 Mod if replied.isdigit(): replied = int(replied) if replied > len(cache_result): - return await msg.finish(msg.locale.t("mod_dl.message.invalid.out_of_range")) + await msg.finish(msg.locale.t("mod_dl.message.invalid.out_of_range")) else: mod_info = cache_result[replied - 1] else: - return await msg.finish(msg.locale.t("mod_dl.message.invalid.non_digital")) + await msg.finish(msg.locale.t("mod_dl.message.invalid.non_digital")) if mod_info[0] == "modrinth": # modrinth mod - if ver is None: + if not ver: reply2 = await msg.wait_reply(f'{msg.locale.t("mod_dl.message.version")}\n' + "\n".join(mod_info[3]) - + f'\n{msg.locale.t("mod_dl.message.version.prompt")}') + + f'\n{msg.locale.t("mod_dl.message.version.prompt")}', delete=True) replied2 = reply2.as_display(text_only=True) if replied2 in mod_info[3]: version_info = await get_modrinth_project_version(mod_info[2], replied2) - if version_info is not None: + if version_info: await msg.finish( f'{" ".join(version_info["loaders"])}\n{msg.locale.t("mod_dl.message.download_url")}{version_info["files"][0]["url"]}\n{msg.locale.t("mod_dl.message.filename")}{version_info["files"][0]["filename"]}') elif ver not in mod_info[3]: await msg.finish(msg.locale.t("mod_dl.message.version.not_found")) elif ver in mod_info[3]: version_info = await get_modrinth_project_version(mod_info[2], ver) - if version_info is not None: + if version_info: await msg.finish( f'{" ".join(version_info["loaders"])}\n{msg.locale.t("mod_dl.message.download_url")}{version_info["files"][0]["url"]}\n{msg.locale.t("mod_dl.message.filename")}{version_info["files"][0]["filename"]}') else: # curseforge mod @@ -184,18 +175,18 @@ async def get_curseforge_mod_file(modid: str, ver: str): for version in version_index: if version["gameVersion"] not in ver_list: ver_list.append(version["gameVersion"]) - if version_index is not None: - if ver is None: + if version_index: + if not ver: reply2 = await msg.wait_reply(f'{msg.locale.t("mod_dl.message.version")}\n' + '\n'.join(ver_list) + - f'\n{msg.locale.t("mod_dl.message.version.prompt")}') + f'\n{msg.locale.t("mod_dl.message.version.prompt")}', delete=True) ver = reply2.as_display(text_only=True) elif ver not in ver_list: await msg.finish(msg.locale.t("mod_dl.message.version.not_found")) if ver in ver_list: file_info = await get_curseforge_mod_file(mod_info[2], ver) - if file_info is not None: + if file_info: await msg.finish(f'{" ".join(file_info["gameVersions"])} \ \n{msg.locale.t("mod_dl.message.download_url")}{file_info["downloadUrl"]} \ \n{msg.locale.t("mod_dl.message.filename")}{file_info["fileName"]}') diff --git a/modules/mod_dl/locales/en_us.json b/modules/mod_dl/locales/en_us.json index 1c36a83fc8..70b6a91d38 100644 --- a/modules/mod_dl/locales/en_us.json +++ b/modules/mod_dl/locales/en_us.json @@ -11,7 +11,6 @@ "mod_dl.message.modrinth.result": "Modrinth 结果:", "mod_dl.message.not_found": "未找到结果。", "mod_dl.message.prompt": "请回复编号来选择 Mod。", - "mod_dl.message.unsupport": "不支持中文搜索,请检查输入。", "mod_dl.message.version": "此 Mod 拥有以下版本:", "mod_dl.message.version.not_found": "未找到指定版本。", "mod_dl.message.version.prompt": "请回复版本号来选择版本。" diff --git a/modules/mod_dl/locales/zh_cn.json b/modules/mod_dl/locales/zh_cn.json index 1c36a83fc8..70b6a91d38 100644 --- a/modules/mod_dl/locales/zh_cn.json +++ b/modules/mod_dl/locales/zh_cn.json @@ -11,7 +11,6 @@ "mod_dl.message.modrinth.result": "Modrinth 结果:", "mod_dl.message.not_found": "未找到结果。", "mod_dl.message.prompt": "请回复编号来选择 Mod。", - "mod_dl.message.unsupport": "不支持中文搜索,请检查输入。", "mod_dl.message.version": "此 Mod 拥有以下版本:", "mod_dl.message.version.not_found": "未找到指定版本。", "mod_dl.message.version.prompt": "请回复版本号来选择版本。" diff --git a/modules/mod_dl/locales/zh_tw.json b/modules/mod_dl/locales/zh_tw.json index a71302d9d3..9e3af0386f 100644 --- a/modules/mod_dl/locales/zh_tw.json +++ b/modules/mod_dl/locales/zh_tw.json @@ -11,7 +11,6 @@ "mod_dl.message.modrinth.result": "Modrinth 結果:", "mod_dl.message.not_found": "未找到結果。", "mod_dl.message.prompt": "請回覆編號來選擇 Mod。", - "mod_dl.message.unsupport": "不支援中文檢索,請校對輸入。", "mod_dl.message.version": "此 Mod 擁有以下版本:", "mod_dl.message.version.not_found": "未找到指定版本。", "mod_dl.message.version.prompt": "請回覆版本號來選擇版本。" diff --git a/modules/ncmusic/__init__.py b/modules/ncmusic/__init__.py index e3173a8eff..9e6db98fbc 100644 --- a/modules/ncmusic/__init__.py +++ b/modules/ncmusic/__init__.py @@ -1,4 +1,4 @@ -from core.builtins import Bot, Plain, Image +from core.builtins import Bot, Plain, Image, Url from core.component import module from core.utils.image_table import image_table_render, ImageTable from core.utils.http import get_url @@ -6,6 +6,7 @@ ncmusic = module('ncmusic', developers=['bugungu', 'DoroWolf'], + desc='{ncmusic.help.desc}', support_languages=['zh_cn']) @@ -14,9 +15,10 @@ async def search(msg: Bot.MessageSession, keyword: str): url = f"https://ncmusic.akari-bot.top/search?keywords={keyword}" result = await get_url(url, 200, fmt='json') + song_count = result['result']['songCount'] legacy = True - if result['result']['songCount'] == 0: + if song_count == 0: await msg.finish(msg.locale.t('ncmusic.message.search.not_found')) songs = result['result']['songs'][:10] @@ -46,30 +48,34 @@ async def search(msg: Bot.MessageSession, keyword: str): legacy = False send_msg.append(Image(img)) - if len(result['result']['songs']) > 10: - send_msg.append(Plain(msg.locale.t('ncmusic.message.search.collapse'))) + if song_count > 10: + song_count = 10 + send_msg.append(Plain(msg.locale.t("message.collapse", amount="10"))) + + if song_count == 1: + send_msg.append(Plain(msg.locale.t('ncmusic.message.search.confirm'))) + query = await msg.wait_confirm(send_msg, delete=False) + if query: + sid = result['result']['songs'][0]['id'] + else: + return + + else: send_msg.append(Plain(msg.locale.t('ncmusic.message.search.prompt'))) query, _ = await msg.wait_next_message(send_msg) query = query.as_display(text_only=True) - try: + + if query.isdigit(): query = int(query) - if query > 10: - await msg.finish(msg.locale.t('ncmusic.message.search.invalid.out_of_range')) - sid = result['result']['songs'][query - 1]['id'] - url = f"https://ncmusic.akari-bot.top/song/detail?ids={sid}" - info = await get_url(url, 200, fmt='json') - info = info['songs'][0] - artist = ' / '.join([ar['name'] for ar in info['ar']]) - song_page = f"https://music.163.com/#/song?id={info['id']}" - - send_msg = msg.locale.t('ncmusic.message.info', - name=info['name'], id=info['id'], - album=info['al']['name'], album_id=info['al']['id'], - artists=artist, detail=song_page) - await msg.finish([Image(info['al']['picUrl']), Plain(send_msg)]) - except Exception: + if query > song_count: + await msg.finish(msg.locale.t("mod_dl.message.invalid.out_of_range")) + else: + sid = result['result']['songs'][query - 1]['id'] + else: await msg.finish(msg.locale.t('ncmusic.message.search.invalid.non_digital')) + await info(msg, sid) + if legacy: send_msg = msg.locale.t('ncmusic.message.search.result') + '\n' @@ -78,50 +84,56 @@ async def search(msg: Bot.MessageSession, keyword: str): if 'transNames' in song: send_msg += f"({' / '.join(song['transNames'])})" send_msg += f"——{' / '.join(artist['name'] for artist in song['artists'])}" - send_msg += f"《{song['album']['name']}》" + if song['album']['name']: + send_msg += f"《{song['album']['name']}》" if 'transNames' in song['album']: send_msg += f"({' / '.join(song['album']['transNames'])})" send_msg += f"({song['id']})\n" + if song_count > 10: + song_count = 10 + send_msg += msg.locale.t("message.collapse", amount="10") - if len(result['result']['songs']) > 10: - send_msg += msg.locale.t('ncmusic.message.search.collapse') - send_msg += '\n' - send_msg += msg.locale.t('ncmusic.message.search.prompt') - query, _ = await msg.wait_next_message(send_msg) - query = query.as_display(text_only=True) - try: - query = int(query) - if query > 10: - await msg.finish(msg.locale.t('ncmusic.message.search.invalid.out_of_range')) - sid = result['result']['songs'][query - 1]['id'] - url = f"https://ncmusic.akari-bot.top/song/detail?ids={sid}" - logger.info(url) - info = await get_url(url, 200, fmt='json') - info = info['songs'][0] - artist = ' / '.join([ar['name'] for ar in info['ar']]) - song_page = f"https://music.163.com/#/song?id={info['id']}" - - send_msg = msg.locale.t('ncmusic.message.info', - name=info['name'], id=info['id'], - album=info['al']['name'], album_id=info['al']['id'], - artists=artist, detail=song_page) - await msg.finish([Image(info['al']['picUrl']), Plain(send_msg)]) - except Exception: - await msg.finish(msg.locale.t('ncmusic.message.search.invalid.non_digital')) + if song_count == 1: + send_msg += '\n' + msg.locale.t('ncmusic.message.search.confirm') + query, _ = await msg.wait_next_message(send_msg) + if query: + sid = result['result']['songs'][0]['id'] + else: + return + else: + send_msg += '\n' + msg.locale.t('ncmusic.message.search.prompt') + query, _ = await msg.wait_next_message(send_msg) + query = query.as_display(text_only=True) + + if query.isdigit(): + query = int(query) + if query > song_count: + await msg.finish(msg.locale.t("mod_dl.message.invalid.out_of_range")) + else: + sid = result['result']['songs'][query - 1]['id'] + else: + await msg.finish(msg.locale.t('ncmusic.message.search.invalid.non_digital')) + await info(msg, sid) + @ncmusic.handle('info {{ncmusic.help.info}}') async def info(msg: Bot.MessageSession, sid: str): url = f"https://ncmusic.akari-bot.top/song/detail?ids={sid}" result = await get_url(url, 200, fmt='json') - info = result['songs'][0] - artist = ' / '.join([ar['name'] for ar in info['ar']]) - song_page = f"https://music.163.com/#/song?id={info['id']}" - - send_msg = msg.locale.t('ncmusic.message.info', - name=info['name'], id=info['id'], - album=info['al']['name'], album_id=info['al']['id'], - artists=artist, detail=song_page) - - await msg.finish([Image(info['al']['picUrl']), Plain(send_msg)]) + if result['songs']: + info = result['songs'][0] + artist = ' / '.join([ar['name'] for ar in info['ar']]) + song_url = f"https://music.163.com/#/song?id={info['id']}" + + send_msg = msg.locale.t('ncmusic.message.info', + name=info['name'], + id=info['id'], + artists=artist, + album=info['al']['name'], + album_id=info['al']['id']) + + await msg.finish([Image(info['al']['picUrl']), Url(song_url), Plain(send_msg)]) + else: + await msg.finish(msg.locale.t('ncmusic.message.info.not_found')) \ No newline at end of file diff --git a/modules/ncmusic/locales/en_us.json b/modules/ncmusic/locales/en_us.json index 4fabd6a210..6d4c3be51d 100644 --- a/modules/ncmusic/locales/en_us.json +++ b/modules/ncmusic/locales/en_us.json @@ -1,16 +1,18 @@ { + "ncmusic.help.desc": "网易云音乐相关工具。", + "ncmusic.help.info": "获取音乐详细信息。", "ncmusic.help.search": "搜索网易云音乐。", "ncmusic.help.search.legacy": "搜索网易云音乐。(旧版)", - "ncmusic.help.info": "获取音乐详细信息。", - "ncmusic.message.info": "歌名:${name}(${id})\n专辑名:${album}(${album_id})\n歌手:${artists}\n歌曲详情页:${detail}", - "ncmusic.message.search.collapse": "…仅显示前 10 条内容。", + "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": "歌名" +} \ No newline at end of file diff --git a/modules/ncmusic/locales/zh_cn.json b/modules/ncmusic/locales/zh_cn.json index 4fabd6a210..595930c300 100644 --- a/modules/ncmusic/locales/zh_cn.json +++ b/modules/ncmusic/locales/zh_cn.json @@ -1,16 +1,18 @@ { + "ncmusic.help.desc": "网易云音乐相关工具。", + "ncmusic.help.info": "获取音乐详细信息。", "ncmusic.help.search": "搜索网易云音乐。", "ncmusic.help.search.legacy": "搜索网易云音乐。(旧版)", - "ncmusic.help.info": "获取音乐详细信息。", - "ncmusic.message.info": "歌名:${name}(${id})\n专辑名:${album}(${album_id})\n歌手:${artists}\n歌曲详情页:${detail}", - "ncmusic.message.search.collapse": "…仅显示前 10 条内容。", + "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": "编号超出范围。", "ncmusic.message.search.not_found": "未找到结果。", - "ncmusic.message.search.prompt": "输入对应的序号以查看歌曲信息。", + "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": "歌名" +} \ No newline at end of file diff --git a/modules/ncmusic/locales/zh_tw.json b/modules/ncmusic/locales/zh_tw.json index f2c8a2778a..0656e53aae 100644 --- a/modules/ncmusic/locales/zh_tw.json +++ b/modules/ncmusic/locales/zh_tw.json @@ -1,16 +1,18 @@ { + "ncmusic.help.desc": "網易云音樂相關工具。", + "ncmusic.help.info": "取得音樂詳細資訊。", "ncmusic.help.search": "搜尋網易雲音樂。", "ncmusic.help.search.legacy": "搜尋網易雲音樂。(舊版)", - "ncmusic.help.info": "取得音樂詳細資訊。", - "ncmusic.message.info": "歌名:${name}(${id})\n專輯名:${album}(${album_id})\n歌手:${artists}\n歌曲詳情頁:${detail}", - "ncmusic.message.search.collapse": "…僅顯示前 10 條內容。", + "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": "編號超出範圍。", "ncmusic.message.search.not_found": "未找到結果。", - "ncmusic.message.search.prompt": "輸入對應編號取得歌曲資訊。", + "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": "歌名" +} \ No newline at end of file diff --git a/modules/nintendo_err/__init__.py b/modules/nintendo_err/__init__.py index 62c72e7302..4cc652fa8b 100644 --- a/modules/nintendo_err/__init__.py +++ b/modules/nintendo_err/__init__.py @@ -98,7 +98,7 @@ async def result(msg: Bot.MessageSession): results = Results() err = msg.parsed_msg[''] err = results.fixup_input(err) - if (meme := results.check_meme(err)) is not None: + if meme := results.check_meme(err): await msg.finish(meme) try: ret = results.fetch(err) diff --git a/modules/nintendo_err/ctr_results.py b/modules/nintendo_err/ctr_results.py index df8c72af3b..d982f92885 100644 --- a/modules/nintendo_err/ctr_results.py +++ b/modules/nintendo_err/ctr_results.py @@ -337,9 +337,9 @@ def construct_result(ret, mod, summary, level, desc): ret.add_field(ConsoleErrorField('Summary', message_str=summaries.get(summary, ''), supplementary_value=summary)) ret.add_field(ConsoleErrorField('Level', message_str=levels.get(level, ''), supplementary_value=level)) description = module.get_error(desc) - if description is None: + if not description: description = common.get_error(desc) - if description is None: + if not description: ret.add_field(ConsoleErrorField('Description', supplementary_value=desc)) else: ret.add_field( diff --git a/modules/nintendo_err/ctr_support.py b/modules/nintendo_err/ctr_support.py index f60eea509b..14dd557b1d 100644 --- a/modules/nintendo_err/ctr_support.py +++ b/modules/nintendo_err/ctr_support.py @@ -272,9 +272,9 @@ def construct_result(ret, mod, desc): module = ctr_results_modules.get(mod, Module('')) ret.add_field(ConsoleErrorField('模组', message_str=module.name, supplementary_value=mod)) description = module.get_error(desc) - if description is None or not description.description: + if not description or not description.description: description = ctr_results_modules[0].get_error(desc) - if description is None or not description.description: + if not description or not description.description: ret.add_field(ConsoleErrorField('描述', supplementary_value=desc)) else: ret.add_field(ConsoleErrorField('描述', message_str=description.description, supplementary_value=desc)) @@ -294,9 +294,9 @@ def construct_result_range(ret, mod, range_desc): continue description = module.get_error(desc) - if description is None or not description.description: + if not description or not description.description: description = ctr_results_modules[0].get_error(desc) - if description is None or not description.description: + if not description or not description.description: unknown_descs.append(str(desc)) else: found_descs.append( @@ -320,7 +320,7 @@ def construct_support(ret, mod, desc): else: ret.add_field(ConsoleErrorField('分类', supplementary_value=mod)) description = category.get_error(desc) - if description is not None and description.description: + if description and description.description: ret.add_field(ConsoleErrorField('描述', message_str=description.description)) if description.support_url: ret.add_field(ConsoleErrorField('更多描述', message_str=description.support_url)) @@ -349,7 +349,7 @@ def nim_handler(ret, description): # If we have a specific description for it in our knowledgebase, # show it instead of doing the rest of the processing. error = nim.get_error(description) - if error is not None and error.description: + if error and error.description: return construct_support(ret, 5, description) elif 2000 <= description < 3024: diff --git a/modules/nintendo_err/switch.py b/modules/nintendo_err/switch.py index b171734793..c2da32d133 100644 --- a/modules/nintendo_err/switch.py +++ b/modules/nintendo_err/switch.py @@ -1783,7 +1783,7 @@ def get(error): if summary: ret.add_field(ConsoleErrorField('Summary', message_str=summary)) description = module.get_error(code) - if description is None or not description.description: + if not description or not description.description: ret.add_field(ConsoleErrorField('Description', supplementary_value=code)) else: ret.add_field(ConsoleErrorField('Description', message_str=description.description, supplementary_value=code)) diff --git a/modules/nintendo_err/types.py b/modules/nintendo_err/types.py index 0308bd371c..42fdc4293d 100644 --- a/modules/nintendo_err/types.py +++ b/modules/nintendo_err/types.py @@ -17,7 +17,7 @@ def __init__(self, name, data={}, summaries={}): def get_error(self, error: int): value = self.data.get(error, None) - if value is not None: + if value: return value for key, value in self.data.items(): if isinstance(key, tuple) and key[0] <= error <= key[1]: @@ -28,7 +28,7 @@ def get_error(self, error: int): # If your modules require specific extra info for error ranges, add it here def get_summary(self, summary: int): value = self.summaries.get(summary, None) - if value is not None: + if value: return value for key, value in self.summaries.items(): if isinstance(key, tuple) and key[0] <= summary <= key[1]: diff --git a/modules/nintendo_err/wiiu_results.py b/modules/nintendo_err/wiiu_results.py index 438f520485..99acf78551 100644 --- a/modules/nintendo_err/wiiu_results.py +++ b/modules/nintendo_err/wiiu_results.py @@ -3323,15 +3323,15 @@ def construct_result(ret, mod, summary, level, desc, is_legacy): ret.add_field(ConsoleErrorField('Module', message_str=module.name, supplementary_value=mod)) if is_legacy: ret.extra_description = "Legacy result" - if is_legacy and summary is not None: + if is_legacy and summary: ret.add_field( ConsoleErrorField('Summary', message_str=legacy_summary.get(summary, ''), supplementary_value=summary)) ret.add_field(ConsoleErrorField('Level', message_str=(legacy_levels if is_legacy else levels).get(level, ''), supplementary_value=level)) description = module.get_error(desc) - if description is None: + if not description: description = common.get_error(desc) - if description is None: + if not description: ret.add_field(ConsoleErrorField('Description', supplementary_value=desc)) else: ret.add_field( diff --git a/modules/nintendo_err/wiiu_support.py b/modules/nintendo_err/wiiu_support.py index 84b4178454..6a8d468008 100644 --- a/modules/nintendo_err/wiiu_support.py +++ b/modules/nintendo_err/wiiu_support.py @@ -503,7 +503,7 @@ def construct_support(ret, mod, desc): if summary: ret.add_field(ConsoleErrorField('Summary', message_str=summary)) description = category.get_error(desc) - if description is not None and description.description: + if description and description.description: ret.add_field(ConsoleErrorField('Description', message_str=description.description)) if description.support_url: ret.add_field(ConsoleErrorField('Further information', message_str=description.support_url)) diff --git a/modules/phigros/__init__.py b/modules/phigros/__init__.py index 35ac19c95e..5d9ef97b99 100644 --- a/modules/phigros/__init__.py +++ b/modules/phigros/__init__.py @@ -25,7 +25,7 @@ async def _(msg: Bot.MessageSession, sessiontoken: str): 'Discord|Channel', 'Telegram|group', 'Telegram|supergroup', - 'Kook|GROUP']: + 'Kook|GROUP']: send_msg.append(await msg.send_message(msg.locale.t("phigros.message.bind.warning"), quote=False)) need_revoke = True headers = p_headers.copy() @@ -50,7 +50,7 @@ async def _(msg: Bot.MessageSession): @phi.command('b19 {{phigros.help.b19}}') async def _(msg: Bot.MessageSession): - if (bind := PgrBindInfoManager(msg).get_bind_info()) is None: + if not (bind := PgrBindInfoManager(msg).get_bind_info()): await msg.finish(msg.locale.t("phigros.message.user_unbound", prefix=msg.prefixes[0])) else: try: diff --git a/modules/phigros/dbutils.py b/modules/phigros/dbutils.py index 97b46f2636..4ea8abeca4 100644 --- a/modules/phigros/dbutils.py +++ b/modules/phigros/dbutils.py @@ -11,7 +11,7 @@ class PgrBindInfoManager: def __init__(self, msg: Bot.MessageSession): self.target_id = msg.target.sender_id self.query = session.query(PgrBindInfo).filter_by(targetId=self.target_id).first() - if self.query is None: + if not self.query: session.add_all([PgrBindInfo(targetId=self.target_id, sessiontoken='', username='Guest')]) session.commit() self.query = session.query(PgrBindInfo).filter_by(targetId=self.target_id).first() diff --git a/modules/phigros/genb19.py b/modules/phigros/genb19.py index d558c91af3..1ce7a178d2 100644 --- a/modules/phigros/genb19.py +++ b/modules/phigros/genb19.py @@ -39,7 +39,7 @@ def drawb19(username, rks_acc, b19data): song_acc = song_[1]['accuracy'] song_base_rks = song_[1]['base_rks'] - if song_id == '': + if not song_id: cardimg = Image.new('RGBA', (384, 240), 'black') else: imgpath = os.path.abspath(f'{assets_path}/illustration/{song_id.split(".")[0].lower()}') diff --git a/modules/random/__init__.py b/modules/random/__init__.py index ffd2aa76ec..d2b7cb7b14 100644 --- a/modules/random/__init__.py +++ b/modules/random/__init__.py @@ -36,31 +36,6 @@ async def _(msg: Bot.MessageSession): await msg.finish(', '.join(x)) -@r.command('password [-u] [-l] [-n] [-s] {{random.help.password}}', - options_desc={'-u': '{random.help.option.password.u}', - '-l': '{random.help.option.password.l}', - '-n': '{random.help.option.password.n}', - '-s': '{random.help.option.password.s}'}) -async def _(msg: Bot.MessageSession, length: int): - if length < 1 or length > 100: - return await msg.finish(msg.locale.t('random.message.password.error.invalid')) - characters = "" - if msg.parsed_msg.get('-u', False): - characters += string.ascii_uppercase - if msg.parsed_msg.get('-l', False): - characters += string.ascii_lowercase - if msg.parsed_msg.get('-n', False): - characters += string.digits - if msg.parsed_msg.get('-s', False): - characters += "!@#$%^&*-_+=?" - - if not characters: - characters = string.ascii_letters + string.digits - - random = ''.join(secrets.choice(characters) for _ in range(length)) - await msg.finish(random) - - @r.command('uuid {{random.help.uuid}}', ) async def _(msg: Bot.MessageSession): await msg.finish(str(uuid.uuid4())) diff --git a/modules/random/locales/en_us.json b/modules/random/locales/en_us.json index b7744e5f85..86e82d41eb 100644 --- a/modules/random/locales/en_us.json +++ b/modules/random/locales/en_us.json @@ -2,12 +2,6 @@ "random.help.choice": "Select an element from a given set.", "random.help.desc": "Random number generator (cryptographically secure)", "random.help.number": "Generate a random integer within a given range.", - "random.help.option.password.l": "在随机字符串中使用小写字母。", - "random.help.option.password.n": "在随机字符串中使用数字。", - "random.help.option.password.s": "在随机字符串中使用特殊符号。", - "random.help.option.password.u": "在随机字符串中使用大写字母。", "random.help.shuffle": "Shuffle the order of a given set.", - "random.help.password": "生成随机字符串,默认为字母和数字组合。", - "random.help.uuid": "Generate random UUID (v4).", - "random.message.password.error.invalid": "发生错误:长度必须为小于 100 的正整数。" + "random.help.uuid": "Generate random UUID (v4)." } \ No newline at end of file diff --git a/modules/random/locales/zh_cn.json b/modules/random/locales/zh_cn.json index 08c6d0c955..3cb98f5c00 100644 --- a/modules/random/locales/zh_cn.json +++ b/modules/random/locales/zh_cn.json @@ -2,12 +2,6 @@ "random.help.choice": "从集合中选择元素。", "random.help.desc": "随机数生成器(密码学安全)", "random.help.number": "生成区间内的随机整数。", - "random.help.option.password.l": "在随机字符串中使用小写字母。", - "random.help.option.password.n": "在随机字符串中使用数字。", - "random.help.option.password.s": "在随机字符串中使用特殊符号。", - "random.help.option.password.u": "在随机字符串中使用大写字母。", "random.help.shuffle": "打乱集合的顺序。", - "random.help.password": "生成随机字符串,默认为字母和数字组合。", - "random.help.uuid": "生成随机 UUID(v4)。", - "random.message.password.error.invalid": "发生错误:长度必须为小于 100 的正整数。" + "random.help.uuid": "生成随机 UUID(v4)。" } \ No newline at end of file diff --git a/modules/random/locales/zh_tw.json b/modules/random/locales/zh_tw.json index c24f318f75..543d507cba 100644 --- a/modules/random/locales/zh_tw.json +++ b/modules/random/locales/zh_tw.json @@ -2,12 +2,6 @@ "random.help.choice": "從集合中選擇元素。", "random.help.desc": "隨機數產生器(密碼學安全)", "random.help.number": "產生區間內的隨機整數。", - "random.help.option.password.l": "在隨機字串中使用小寫字母。", - "random.help.option.password.n": "在隨機字串中使用數字。", - "random.help.option.password.s": "在隨機字串中使用特殊符號。", - "random.help.option.password.u": "在隨機字串中使用大寫字母。", "random.help.shuffle": "打亂集合的順序。", - "random.help.password": "產生隨機字串,預設為字母或數字組合。", - "random.help.uuid": "產生隨機 UUID(v4)。", - "random.message.password.error.invalid": "發生錯誤:長度必須為小於 100 的正整數。" + "random.help.uuid": "產生隨機 UUID(v4)。" } \ No newline at end of file diff --git a/modules/secret/__init__.py b/modules/secret/__init__.py index 06fba72c83..1bf6456d1d 100644 --- a/modules/secret/__init__.py +++ b/modules/secret/__init__.py @@ -40,7 +40,7 @@ async def check_abuse(): f'过滤器名:{y["filter"]}\n' \ f'操作:{y["action"]}\n' result = y['result'] - if result == '': + if not result: result = 'pass' s += '处理结果:' + result diff --git a/modules/server/__init__.py b/modules/server/__init__.py index f3509512ae..8d3f8fcd2b 100644 --- a/modules/server/__init__.py +++ b/modules/server/__init__.py @@ -9,13 +9,13 @@ s = module('server', alias='s', developers=['_LittleC_', 'OasisAkari']) -@s.command(' [-r] [-p] {{server.help}}', +@s.command(' [-r] [-p] {{server.help}}', options_desc={'-r': '{server.help.option.r}', '-p': '{server.help.option.p}'}) async def main(msg: Bot.MessageSession): gather_list = [] - match_object = re.match(r'(.*)[\s:](.*)', msg.parsed_msg[""], re.M | re.I) + match_object = re.match(r'(.*)[\s:](.*)', msg.parsed_msg[""], re.M | re.I) is_local_ip = False - server_address = msg.parsed_msg[""] + server_address = msg.parsed_msg[""] if match_object: server_address = match_object.group(1) @@ -45,7 +45,7 @@ async def main(msg: Bot.MessageSession): sm = ['j', 'b'] for x in sm: gather_list.append(asyncio.ensure_future(s( - msg, f'{msg.parsed_msg[""]}', msg.parsed_msg.get('-r', False), msg.parsed_msg.get('-p', False + msg, f'{msg.parsed_msg[""]}', msg.parsed_msg.get('-r', False), msg.parsed_msg.get('-p', False ), x))) g = await asyncio.gather(*gather_list) if g == ['', '']: diff --git a/modules/tic_tac_toe/__init__.py b/modules/tic_tac_toe/__init__.py index 21a4a3ed68..f35b25625d 100644 --- a/modules/tic_tac_toe/__init__.py +++ b/modules/tic_tac_toe/__init__.py @@ -79,7 +79,7 @@ async def callback(board: List[List[int]]): while True: if not play_state[msg.target.target_id]['active']: raise TerminationError - wait = await msg.wait_anyone() + wait = await msg.wait_anyone(timeout=3600) text = wait.as_display(text_only=True) if text == 'stop': raise TerminationError diff --git a/modules/tweet/__init__.py b/modules/tweet/__init__.py index 8b090ed515..a478b2e443 100644 --- a/modules/tweet/__init__.py +++ b/modules/tweet/__init__.py @@ -3,7 +3,7 @@ from config import CFG from core.builtins import Bot -from core.builtins.message import Image +from core.builtins.message import Image, Url from core.component import module from core.dirty_check import check_bool, rickroll from core.logger import Logger @@ -88,4 +88,4 @@ async def _(msg: Bot.MessageSession, tweet: str, use_local=True): {'url': f'https://react-tweet-next.vercel.app/light/{tweet_id}', 'css': css, 'mw': False, 'element': 'article'}), request_private_ip=True) await msg.finish( - [Image(pic), f"https://twitter.com/{res_json['data']['user']['screen_name']}/status/{tweet_id}"]) + [Image(pic), Url(f"https://twitter.com/{res_json['data']['user']['screen_name']}/status/{tweet_id}")]) diff --git a/modules/twenty_four/__init__.py b/modules/twenty_four/__init__.py index 747c28c9c0..3c07a4e9fd 100644 --- a/modules/twenty_four/__init__.py +++ b/modules/twenty_four/__init__.py @@ -107,8 +107,8 @@ async def _(msg: Bot.MessageSession): numbers = [random.randint(1, 13) for _ in range(4)] has_solution_flag = await has_solution(numbers) - - answer, _ = await msg.wait_next_message(msg.locale.t('twenty_four.message', numbers=numbers), append_instruction=False) + + answer, _ = await msg.wait_next_message(msg.locale.t('twenty_four.message', numbers=numbers), timeout=3600, append_instruction=False) expression = answer.as_display(text_only=True) if play_state[msg.target.target_id]['active']: if expression.lower() in no_solution: diff --git a/modules/user/tpg.py b/modules/user/tpg.py index 1ce8a9cd8e..a530dced57 100644 --- a/modules/user/tpg.py +++ b/modules/user/tpg.py @@ -12,7 +12,7 @@ def tpg(msg, favicon, wikiname, username, gender, registertime, contributionwiki blockreason='0', bantype=None): font = ImageFont.truetype(abspath('./assets/SourceHanSansCN-Normal.ttf'), 40) font1 = ImageFont.truetype(abspath('./assets/SourceHanSansCN-Normal.ttf'), 70) - if bantype is None: + if not bantype: img = Image.open(abspath('./assets/user/base.png')) elif bantype == 'Y' or bantype == 'YN': img = Image.open(abspath('./assets/user/ban.png')) diff --git a/modules/user/user.py b/modules/user/user.py index f36134257f..89134ede2d 100644 --- a/modules/user/user.py +++ b/modules/user/user.py @@ -52,7 +52,7 @@ async def get_user_info(msg: MessageSession, wikiurl, username, pic=False): data['global_users_groups'].append(groups[g] if g in groups else g) data['registration_time'] = base_user_info['registration'] data['registration_time'] = msg.ts2strftime(strptime2ts(data['registration_time'])) if data[ - 'registration_time'] is not None else msg.locale.t( + 'registration_time'] else msg.locale.t( 'unknown') data['edited_count'] = str(base_user_info['editcount']) data['gender'] = base_user_info['gender'] @@ -87,17 +87,17 @@ async def get_user_info(msg: MessageSession, wikiurl, username, pic=False): data['blocked_time'] = '无限期' else: data['blocked_time'] = msg.ts2strftime(strptime2ts(data['blocked_time'])) \ - if data['blocked_time'] is not None else msg.locale.t( + if data['blocked_time'] else msg.locale.t( 'unknown') data['blocked_expires'] = base_user_info.get('blockexpiry', None) if data['blocked_expires'] in ['infinity', 'infinite']: data['blocked_expires'] = '无限期' else: data['blocked_expires'] = msg.ts2strftime(strptime2ts(data['blocked_expires'])) if data[ - 'blocked_expires'] is not None else msg.locale.t( + 'blocked_expires'] else msg.locale.t( 'unknown') data['blocked_reason'] = base_user_info['blockreason'] - data['blocked_reason'] = data['blocked_reason'] if data['blocked_reason'] is not None else msg.locale.t( + data['blocked_reason'] = data['blocked_reason'] if data['blocked_reason'] else msg.locale.t( 'unknown') if pic: diff --git a/modules/weekly/__init__.py b/modules/weekly/__init__.py index b536a560e9..ef2bf03244 100644 --- a/modules/weekly/__init__.py +++ b/modules/weekly/__init__.py @@ -46,12 +46,12 @@ async def get_weekly(with_img=False, zh_tw=False): Plain( locale.t( "weekly.message.link", - img=imglink if imglink is not None else locale.t("none"), + img=imglink if imglink else locale.t("none"), article=str( Url(f'https://zh.minecraft.wiki{page[0]}')), link=str( Url(f'https://zh.minecraft.wiki/wiki/?oldid={str(result["parse"]["revid"])}'))))) - if imglink is not None and with_img: + if imglink and with_img: msg_list.append(Image(path=imglink)) return msg_list diff --git a/modules/weekly_rss/__init__.py b/modules/weekly_rss/__init__.py index f362647bc6..c61700480d 100644 --- a/modules/weekly_rss/__init__.py +++ b/modules/weekly_rss/__init__.py @@ -1,4 +1,4 @@ -from core.builtins import Bot, Image, Plain, command_prefix +from core.builtins import Bot, Image, Plain, command_prefix, I18NContext from core.component import module from core.logger import Logger from core.utils.i18n import Locale @@ -19,7 +19,7 @@ async def weekly_rss(fetch: Bot.FetchTarget, ctx: Bot.ModuleHookContext): weekly_cn = Image(await msgchain2image(weekly_cn)) weekly_tw = Image(await msgchain2image(weekly_tw)) post_msg = {'zh_cn': weekly_cn, 'zh_tw': weekly_tw, 'fallback': weekly_cn} - await fetch.post_message('weekly_rss', post_msg, i18n=True) + await fetch.post_message('weekly_rss', I18NContext(post_msg), i18n=True) Logger.info('Weekly checked.') @@ -50,7 +50,7 @@ async def weekly_rss(fetch: Bot.FetchTarget, ctx: Bot.ModuleHookContext): weekly_cn = Image(await msgchain2image(weekly_cn)) weekly_tw = Image(await msgchain2image(weekly_tw)) post_msg = {'zh_cn': weekly_cn, 'zh_tw': weekly_tw, 'fallback': weekly_cn} - await fetch.post_message('teahouse_weekly_rss', post_msg, i18n=True) + await fetch.post_message('teahouse_weekly_rss', I18NContext(post_msg), i18n=True) else: await fetch.post_message('teahouse_weekly_rss', weekly) Logger.info('Teahouse Weekly checked.') diff --git a/modules/whois/__init__.py b/modules/whois/__init__.py index 576ec91026..140dd3d01f 100644 --- a/modules/whois/__init__.py +++ b/modules/whois/__init__.py @@ -1,15 +1,19 @@ +from datetime import datetime + from whois import whois +from config import Config from core.builtins import Bot, Plain, Image from core.component import module from core.utils.image import msgchain2image +from core.utils.text import parse_time_string -def process(input): - if isinstance(input, list) and len(input) > 0: - return input[0] +def process(input_): + if isinstance(input_, list) and len(input_) > 0: + return input_[0] else: - return input + return input_ def get_value(dict, key): @@ -59,6 +63,16 @@ async def get_whois(msg, domain): if whois_server: whois_server = whois_server.lower() + if updated_date: # 此时间为UTC时间 + updated_date = (process(updated_date) + parse_time_string(Config('timezone_offset', '+8'))).timestamp() + + if creation_date: # 此时间为UTC时间 + creation_date = (process(creation_date) + parse_time_string(Config('timezone_offset', '+8'))).timestamp() + + if expiration_date: # 此时间为UTC时间 + expiration_date = (process(expiration_date) + parse_time_string(Config('timezone_offset', '+8'))).timestamp() + + if name_servers: name_servers_list = list(set([i.lower() for i in name_servers])) else: @@ -68,9 +82,9 @@ async def get_whois(msg, domain): {msg.locale.t('whois.message.domain_name')}{process(domain_name).lower()}{f""" {msg.locale.t('whois.message.registrar')}{registrar}""" if registrar else ''}{f""" {msg.locale.t('whois.message.whois_server')}{whois_server}""" if whois_server else ''}{f""" -{msg.locale.t('whois.message.updated_date')}{str(process(updated_date))}""" if updated_date else ''}{f""" -{msg.locale.t('whois.message.creation_date')}{str(process(creation_date))}""" if creation_date else ''}{f""" -{msg.locale.t('whois.message.expiration_date')}{str(process(expiration_date))}""" if expiration_date else ''}{f""" +{msg.locale.t('whois.message.updated_date')}{msg.ts2strftime(updated_date)}""" if updated_date else ''}{f""" +{msg.locale.t('whois.message.creation_date')}{msg.ts2strftime(creation_date)}""" if creation_date else ''}{f""" +{msg.locale.t('whois.message.expiration_date')}{msg.ts2strftime(expiration_date)}""" if expiration_date else ''}{f""" {msg.locale.t('whois.message.name_servers')}{', '.join(name_servers_list)}""" if name_servers else ''}{f""" {msg.locale.t('whois.message.dnssec')}{process(dnssec)}""" if dnssec else ''}{f""" {msg.locale.t('whois.message.name')}{process(name)}""" if name else ''}{f""" diff --git a/modules/wiki/audit.py b/modules/wiki/audit.py index b45f2787ec..1acf7befa0 100644 --- a/modules/wiki/audit.py +++ b/modules/wiki/audit.py @@ -8,109 +8,112 @@ 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 ', 'block ']) - async def _(msg: Bot.MessageSession): - date = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - api = msg.parsed_msg[''] - 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 ', 'unblock ']) - async def _(msg: Bot.MessageSession): - api = msg.parsed_msg[''] # 已关闭的站点无法验证有效性 - 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 ', 'block ']) +async def _(msg: Bot.MessageSession): + date = msg.ts2strftime(datetime.now().timestamp(), timezone=False) + api = msg.parsed_msg[''] + 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(['distrust ', 'unblock ']) +async def _(msg: Bot.MessageSession): + api = msg.parsed_msg[''] # 已关闭的站点无法验证有效性 + if msg.parsed_msg.get('distrust', False): + res = Audit(api).remove_from_AllowList() + if not res: + 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 ') - async def _(msg: Bot.MessageSession): - api = msg.parsed_msg[''] - 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('query ') +async def _(msg: Bot.MessageSession): + api = msg.parsed_msg[''] + 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)) diff --git a/modules/wiki/inline.py b/modules/wiki/inline.py index cf126de6d8..f79b07d112 100644 --- a/modules/wiki/inline.py +++ b/modules/wiki/inline.py @@ -9,6 +9,7 @@ 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.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: @@ -94,11 +95,11 @@ async def bgtask(): if not result['status']: return get_page = await wiki_.parse_page_info(title) - if get_page is not None: - if get_page.status and get_page.file is not None: + if get_page: + if get_page.status and get_page.file: dl = await download_to_cache(get_page.file) guess_type = filetype.guess(dl) - if guess_type is not None: + if guess_type: if guess_type.extension in ["png", "gif", "jpg", "jpeg", "webp", "bmp", "ico"]: if msg.Feature.image: await msg.send_message( @@ -130,6 +131,27 @@ async def bgtask(): 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: diff --git a/modules/wiki/locales/en_us.json b/modules/wiki/locales/en_us.json index 6333f9cd36..1e4564d61e 100644 --- a/modules/wiki/locales/en_us.json +++ b/modules/wiki/locales/en_us.json @@ -4,11 +4,11 @@ "wiki.help": "Query a wiki page.", "wiki.help.ab": "Get recent abuse logs for the default wiki.", "wiki.help.ab.legacy": "Get recent abuse logs for the default wiki. (Legacy)", - "wiki.help.fandom": "Toggles whether to use Fandom global Interwiki queries.", + "wiki.help.fandom": "Toggle whether to use Fandom global Interwiki queries.", "wiki.help.headers.remove": "Remove custom request headers.", "wiki.help.headers.reset": "Reset custom request headers.", "wiki.help.headers.add": "Add custom request headers.", - "wiki.help.headers.show": "Lists the currently set request headers.", + "wiki.help.headers.show": "View the currently set request headers.", "wiki.help.id": "Query a Wiki page based on page ID.", "wiki.help.iw.add": "Add custom Interwiki.", "wiki.help.iw.get": "Get the API address corresponding to the set Interwiki.", @@ -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 \" to get the corresponding link to Interwiki.", @@ -78,7 +83,6 @@ "wiki.message.set.default": "There is no start wiki set. Defaulted to the Chinese Minecraft Wiki. Use \"${prefix}wiki set\" to set a custom start wiki.\nExample: ${prefix}wiki set https://zh.minecraft.wiki/", "wiki.message.set.success": "Successfully set up start wiki: ${name}", "wiki.message.utils.banned": "检测到外来信息介入,请前往滥用日志查看所有消息。", - "wiki.message.utils.collapse": "…show only the first 5 items.", "wiki.message.utils.qq.prompt": "Tips:复制粘贴下面的任一消息到聊天窗口发送可获取此次改动详细信息的截图。", "wiki.message.utils.time.infinity": "Infinity", "wiki.message.utils.wikilib.error.empty": "An error occurred: API returned no contents. Please contact the site administrator for reasons.", diff --git a/modules/wiki/locales/zh_cn.json b/modules/wiki/locales/zh_cn.json index 7d9a2d53a2..1871ac8f02 100644 --- a/modules/wiki/locales/zh_cn.json +++ b/modules/wiki/locales/zh_cn.json @@ -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 对应的链接。", @@ -78,7 +83,6 @@ "wiki.message.set.default": "没有设置起始 Wiki,已默认为中文 Minecraft Wiki,使用“${prefix}wiki set”设置自定义起始 Wiki。\n示例:${prefix}wiki set https://zh.minecraft.wiki/", "wiki.message.set.success": "成功设置起始 Wiki:${name}", "wiki.message.utils.banned": "检测到外来信息介入,请前往滥用日志查看所有消息。", - "wiki.message.utils.collapse": "…仅显示前 5 条内容。", "wiki.message.utils.qq.prompt": "Tips:复制粘贴下面的任一消息到聊天窗口发送可获取此次改动详细信息的截图。", "wiki.message.utils.time.infinity": "无限期", "wiki.message.utils.wikilib.error.empty": "发生错误:API 未返回任何内容,请联系此站点管理员获取原因。", diff --git a/modules/wiki/locales/zh_tw.json b/modules/wiki/locales/zh_tw.json index 715d60d322..d3e155adff 100644 --- a/modules/wiki/locales/zh_tw.json +++ b/modules/wiki/locales/zh_tw.json @@ -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 對應的連結。", @@ -78,7 +83,6 @@ "wiki.message.set.default": "沒有設定起始 Wiki,已預設為中文 Minecraft Wiki,使用「${prefix}wiki set」設定自訂起始 Wiki。\n示例:${prefix}wiki set https://zh.minecraft.wiki/", "wiki.message.set.success": "成功設定起始 Wiki:${name}", "wiki.message.utils.banned": "檢測到外來訊息介入,請前往過濾器日誌查看所有訊息。", - "wiki.message.utils.collapse": "…僅顯示前 5 條內容。", "wiki.message.utils.qq.prompt": "Tips:複製並貼上下方的任意訊息到聊天視窗並傳送可取得此次改動詳細訊息的截圖。", "wiki.message.utils.time.infinity": "無限期", "wiki.message.utils.wikilib.error.empty": "發生錯誤:API 未返回任何內容,請聯絡此站點管理員取得原因。", diff --git a/modules/wiki/search.py b/modules/wiki/search.py index 530bb54d7c..e880c47055 100644 --- a/modules/wiki/search.py +++ b/modules/wiki/search.py @@ -21,14 +21,14 @@ async def search_pages(session: Bot.MessageSession, title: Union[str, list, tupl headers = target.get_headers() prefix = target.get_prefix() enabled_fandom_addon = session.options.get('wiki_fandom_addon') - if start_wiki is None: + if not start_wiki: await session.send_message(session.locale.t('wiki.message.set.default', prefix=session.prefixes[0])) start_wiki = 'https://zh.minecraft.wiki/api.php' if isinstance(title, str): title = [title] query_task = {start_wiki: {'query': [], 'iw_prefix': ''}} for t in title: - if prefix is not None and use_prefix: + if prefix and use_prefix: t = prefix + t if t[0] == ':': if len(t) > 1: diff --git a/modules/wiki/set.py b/modules/wiki/set.py index 99ad7311bf..fa5d285e76 100644 --- a/modules/wiki/set.py +++ b/modules/wiki/set.py @@ -69,19 +69,20 @@ async def _(msg: Bot.MessageSession): query = target.get_interwikis() start_wiki = target.get_start_wiki() base_interwiki_link = None - if start_wiki is not None: + if start_wiki: base_interwiki_link_ = await WikiLib(start_wiki, target.get_headers()).parse_page_info('Special:Interwiki') if base_interwiki_link_.status: base_interwiki_link = base_interwiki_link_.link + result = '' if query != {}: if not msg.parsed_msg.get('legacy', False) and msg.Feature.image: columns = [[x, query[x]] for x in query] img = await image_table_render(ImageTable(columns, ['Interwiki', 'Url'])) else: - img = False + img = None if img: mt = msg.locale.t("wiki.message.iw.list", prefix=msg.prefixes[0]) - if base_interwiki_link is not None: + if base_interwiki_link: mt += '\n' + msg.locale.t("wiki.message.iw.list.prompt", url=str(Url(base_interwiki_link))) await msg.finish([Image(img), Plain(mt)]) else: @@ -89,7 +90,7 @@ async def _(msg: Bot.MessageSession): '\n'.join([f'{x}: {query[x]}' for x in query]) else: result = msg.locale.t("wiki.message.iw.list.none", prefix=msg.prefixes[0]) - if base_interwiki_link is not None: + if base_interwiki_link: result += '\n' + msg.locale.t("wiki.message.iw.list.prompt", url=str(Url(base_interwiki_link))) await msg.finish(result) @@ -161,7 +162,7 @@ async def _(msg: Bot.MessageSession): @wiki.command('fandom {{wiki.help.fandom}}', - required_admin=True) + required_admin=True) async def _(msg: Bot.MessageSession): fandom_addon_state = msg.data.options.get('wiki_fandom_addon') @@ -174,7 +175,7 @@ async def _(msg: Bot.MessageSession): @wiki.command('redlink {{wiki.help.redlink}}', - required_admin=True) + required_admin=True) async def _(msg: Bot.MessageSession): redlink_state = msg.data.options.get('wiki_redlink') diff --git a/modules/wiki/utils/__init__.py b/modules/wiki/utils/__init__.py index c7d03a9e06..c564fb3e66 100644 --- a/modules/wiki/utils/__init__.py +++ b/modules/wiki/utils/__init__.py @@ -17,7 +17,7 @@ 'legacy {{wiki.help.rc.legacy}}']) async def rc_loader(msg: Bot.MessageSession): start_wiki = WikiTargetInfo(msg).get_start_wiki() - if start_wiki is None: + if not start_wiki: return await msg.finish(msg.locale.t('wiki.message.not_set')) legacy = True if not msg.parsed_msg and msg.Feature.forward and msg.target.target_from == 'QQ|Group': @@ -40,7 +40,7 @@ async def rc_loader(msg: Bot.MessageSession): 'legacy {{wiki.help.ab.legacy}}']) async def ab_loader(msg: Bot.MessageSession): start_wiki = WikiTargetInfo(msg).get_start_wiki() - if start_wiki is None: + if not start_wiki: return await msg.finish(msg.locale.t('wiki.message.not_set')) legacy = True if not msg.parsed_msg and msg.Feature.forward and msg.target.target_from == 'QQ|Group': @@ -62,7 +62,7 @@ async def ab_loader(msg: Bot.MessageSession): @n.command() async def newbie_loader(msg: Bot.MessageSession): start_wiki = WikiTargetInfo(msg).get_start_wiki() - if start_wiki is None: + if not start_wiki: return await msg.finish(msg.locale.t('wiki.message.not_set')) res = await newbie(msg, start_wiki) await msg.finish(res) diff --git a/modules/wiki/utils/ab.py b/modules/wiki/utils/ab.py index 062c0e60a0..1a8e8e9b6e 100644 --- a/modules/wiki/utils/ab.py +++ b/modules/wiki/utils/ab.py @@ -17,6 +17,8 @@ async def ab(msg: Bot.MessageSession, wiki_url): y = await check(*d) y = '\n'.join(z['content'] for z in y) if y.find("<吃掉了>") != -1 or y.find("<全部吃掉了>") != -1: - return f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("wiki.message.utils.collapse")}\n{msg.locale.t("wiki.message.utils.banned")}' + y = y.replace("<吃掉了>", msg.locale.t("check.redacted")) + y = y.replace("<全部吃掉了>", msg.locale.t("check.redacted.all")) + return f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("message.collapse", amount="5")}\n{msg.locale.t("wiki.message.utils.banned")}' else: - return f'{str(Url(pageurl))}\n{y}\n' + msg.locale.t("wiki.message.utils.collapse") + return f'{str(Url(pageurl))}\n{y}\n' + msg.locale.t("message.collapse", amount="5") diff --git a/modules/wiki/utils/ab_qq.py b/modules/wiki/utils/ab_qq.py index bcf51bbdcf..c3bd4ff47a 100644 --- a/modules/wiki/utils/ab_qq.py +++ b/modules/wiki/utils/ab_qq.py @@ -44,7 +44,7 @@ async def ab_qq(msg: MessageSession, wiki_url): t.append(f"过滤器名:{x['filter']}") t.append(f"操作:{x['action']}") result = x['result'] - if result == '': + if not result: result = 'pass' t.append(f"处理结果:{result}") t.append(msg.ts2strftime(strptime2ts(x['timestamp']))) diff --git a/modules/wiki/utils/dbutils.py b/modules/wiki/utils/dbutils.py index f3bba78adb..556f499646 100644 --- a/modules/wiki/utils/dbutils.py +++ b/modules/wiki/utils/dbutils.py @@ -23,7 +23,7 @@ def __init__(self, msg: [MessageSession, str]): else: target_id = msg self.query = session.query(WikiTargetSetInfo).filter_by(targetId=target_id).first() - if self.query is None: + if not self.query: session.add_all([WikiTargetSetInfo(targetId=target_id, iws='{}', headers='{}')]) session.commit() self.query = session.query(WikiTargetSetInfo).filter_by(targetId=target_id).first() @@ -37,8 +37,8 @@ def add_start_wiki(self, url): return True def get_start_wiki(self) -> Union[str, None]: - if self.query is not None: - return self.query.link if self.query.link is not None else None + if self.query: + return self.query.link if self.query.link else None @retry(stop=stop_after_attempt(3), reraise=True) @auto_rollback_error @@ -56,7 +56,7 @@ def config_interwikis(self, iw: str, iwlink: str = None, let_it=True): def get_interwikis(self) -> dict: q = self.query.iws - if q is not None: + if q: iws = json.loads(q) return iws else: @@ -71,18 +71,18 @@ def config_headers(self, headers, let_it: [bool, None] = True): headers = json.loads(headers) for x in headers: headers_[x] = headers[x] - elif let_it is None: + elif not let_it: headers_ = {} else: headers_ = {k: v for k, v in headers_.items() if k not in headers} self.query.headers = json.dumps(headers_) session.commit() return True - except: + except BaseException: return False def get_headers(self): - if self.query is not None: + if self.query: q = self.query.headers headers = json.loads(q) else: @@ -115,14 +115,14 @@ def __init__(self, api_link): self.query = session.query(WikiInfo).filter_by(apiLink=api_link).first() def get(self): - if self.query is not None: + if self.query: return self.query.siteInfo, self.query.timestamp return False @retry(stop=stop_after_attempt(3), reraise=True) @auto_rollback_error def update(self, info: dict): - if self.query is None: + if not self.query: session.add_all([WikiInfo(apiLink=self.api_link, siteInfo=json.dumps(info))]) else: self.query.siteInfo = json.dumps(info) @@ -170,7 +170,7 @@ def add_to_AllowList(self, date) -> bool: def remove_from_AllowList(self) -> Union[bool, None]: if not self.inAllowList: return False - if (query := session.query(WikiAllowList).filter_by(apiLink=self.api_link).first()) is not None: + if query := session.query(WikiAllowList).filter_by(apiLink=self.api_link).first(): session.delete(query) session.commit() session.expire_all() diff --git a/modules/wiki/utils/newbie.py b/modules/wiki/utils/newbie.py index 405f3f15ce..c38990127c 100644 --- a/modules/wiki/utils/newbie.py +++ b/modules/wiki/utils/newbie.py @@ -14,7 +14,9 @@ async def newbie(msg: Bot.MessageSession, wiki_url): d.append(x['title']) y = await check(*d) y = '\n'.join(z['content'] for z in y) - g = f'{pageurl}\n{y}\n{msg.locale.t("wiki.message.utils.collapse")}' + g = f'{pageurl}\n{y}\n{msg.locale.t("message.collapse", amount="5")}' if g.find("<吃掉了>") != -1 or g.find("<全部吃掉了>") != -1: + g = g.replace("<吃掉了>", msg.locale.t("check.redacted")) + g = g.replace("<全部吃掉了>", msg.locale.t("check.redacted.all")) g += f'\n{msg.locale.t("wiki.message.utils.banned")}' return g diff --git a/modules/wiki/utils/rc.py b/modules/wiki/utils/rc.py index 3d3acb9391..230baee0ef 100644 --- a/modules/wiki/utils/rc.py +++ b/modules/wiki/utils/rc.py @@ -16,7 +16,9 @@ async def rc(msg: Bot.MessageSession, wiki_url): y = await check(*d) y = '\n'.join(z['content'] for z in y) if y.find("<吃掉了>") != -1 or y.find("<全部吃掉了>") != -1: - msg = f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("wiki.message.utils.collapse")}\n{msg.locale.t("wiki.message.utils.banned")}' + y = y.replace("<吃掉了>", msg.locale.t("check.redacted")) + y = y.replace("<全部吃掉了>", msg.locale.t("check.redacted.all")) + msg = f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("message.collapse", amount="5")}\n{msg.locale.t("wiki.message.utils.banned")}' else: - msg = f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("wiki.message.utils.collapse")}' + msg = f'{str(Url(pageurl))}\n{y}\n{msg.locale.t("message.collapse", amount="5")}' return msg diff --git a/modules/wiki/utils/rc_qq.py b/modules/wiki/utils/rc_qq.py index 430bd2bf4a..7b7db20258 100644 --- a/modules/wiki/utils/rc_qq.py +++ b/modules/wiki/utils/rc_qq.py @@ -57,7 +57,7 @@ async def rc_qq(msg: MessageSession, wiki_url): count = str(count) t.append(f"{title_checked_map[x['title']]}({count})") comment = x['comment'] - if comment == '': + if not comment: comment = '(无摘要内容)' t.append(comment) t.append( @@ -70,14 +70,14 @@ async def rc_qq(msg: MessageSession, wiki_url): r = '(新重定向)' t.append(f"{title_checked_map[x['title']]}{r}") comment = x['comment'] - if comment == '': + if not comment: comment = '(无摘要内容)' t.append(comment) if x['type'] == 'log': log = x['logaction'] + '了' + title_checked_map[x['title']] if x['logtype'] in action: a = action[x['logtype']].get(x['logaction']) - if a is not None: + if a: log = a % title_checked_map[x['title']] t.append(log) params = x['logparams'] diff --git a/modules/wiki/utils/screenshot_image.py b/modules/wiki/utils/screenshot_image.py index 55a2133e7c..5059496488 100644 --- a/modules/wiki/utils/screenshot_image.py +++ b/modules/wiki/utils/screenshot_image.py @@ -23,14 +23,14 @@ async def generate_screenshot_v2(page_link, section=None, allow_special_page=False, content_mode=False, use_local=True, element=None): elements_ = elements.copy() - if element is not None and isinstance(element, List): + if element and isinstance(element, List): elements_ += element if not web_render_local: if not web_render: Logger.warn('[Webrender] Webrender is not configured.') return False use_local = False - if section is None: + if not section: if allow_special_page and content_mode: elements_.insert(0, '.mw-body-content') if allow_special_page and not content_mode: @@ -148,11 +148,11 @@ def join_url(base, target): for x in soup.find_all('style'): open_file.write(str(x)) - if section is None: + if not section: find_diff = None if allow_special_page: find_diff = soup.find('table', class_=re.compile('diff')) - if find_diff is not None: + if find_diff: Logger.info('Found diff...') for x in soup.find_all('body'): if x.has_attr('class'): @@ -180,14 +180,14 @@ def join_url(base, target): open_file.write(f'
') open_file.write(str(find_diff)) w = 2000 - if find_diff is None: + if not find_diff: infoboxes = elements.copy() find_infobox = None for i in infoboxes: find_infobox = soup.find(class_=i[1:]) - if find_infobox is not None: + if find_infobox: break - if find_infobox is None: + if not find_infobox: Logger.info('Found nothing...') return False else: @@ -275,7 +275,7 @@ def is_comment(e): bl = [] while True: b = b.next_sibling - if b is None: + if not b: break if b.name == selected_hx: diff --git a/modules/wiki/utils/wikilib.py b/modules/wiki/utils/wikilib.py index ca805be144..879ffe0e1d 100644 --- a/modules/wiki/utils/wikilib.py +++ b/modules/wiki/utils/wikilib.py @@ -53,10 +53,10 @@ class WhatAreUDoingError(Exception): class QueryInfo: def __init__(self, api, headers=None, prefix=None, locale=None): self.api = api - self.headers = headers if headers is not None else { + self.headers = headers if headers else { 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'} self.prefix = prefix - self.locale = Locale(locale if locale is not None else default_locale) + self.locale = Locale(locale if locale else default_locale) class WikiInfo: @@ -74,9 +74,9 @@ def __init__(self, in_blocklist=False, script: str = '', logo_url: str = ''): - if extensions is None: + if not extensions: extensions = [] - if interwiki is None: + if not interwiki: interwiki = {} self.api = api self.articlepath = articlepath @@ -114,7 +114,8 @@ def __init__(self, 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 @@ def __init__(self, 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 @@ def __init__(self, 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 @@ def __init__(self, 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: @@ -154,7 +157,7 @@ def __init__(self, url: str, headers=None, locale='zh_cn'): async def get_json_from_api(self, api, **kwargs) -> dict: if api in redirect_list: api = redirect_list[api] - if kwargs is not None: + if kwargs: api = api + '?' + urllib.parse.urlencode(kwargs) + '&format=json' Logger.debug(api) else: @@ -284,7 +287,7 @@ async def check_wiki_info_from_database_cache(self): """检查wiki信息是否已记录在数据库缓存(由于部分wiki通过path区分语言,此处仅模糊查询域名部分,返回结果可能不准确)""" parse_url = urllib.parse.urlparse(self.url) get = DBSiteInfo.get_like_this(parse_url.netloc) - if get is not None: + if get: api_link = get.apiLink if api_link in redirect_list: api_link = redirect_list[api_link] @@ -293,7 +296,7 @@ async def check_wiki_info_from_database_cache(self): return WikiStatus(available=False, value=False, message='') async def fixup_wiki_info(self): - if self.wiki_info.api == '': + if not self.wiki_info.api: wiki_info = await self.check_wiki_available() if wiki_info.available: self.wiki_info = wiki_info.value @@ -330,7 +333,7 @@ def parse_text(text): ell = True split_desc = desc.split('\n') for d in split_desc: - if d == '': + if not d: split_desc.remove('') if len(split_desc) > 5: split_desc = split_desc[0:5] @@ -348,7 +351,7 @@ async def get_html_to_text(self, page_name, section=None): h.ignore_tables = True h.single_line_break = True t = h.handle(get_parse['parse']['text']['*']) - if section is not None: + if section: for i in range(1, 7): s = re.split(r'(.*' + '#' * i + r'[^#].*\[.*?])', t, re.M | re.S) ls = len(s) @@ -429,7 +432,7 @@ async def parse_page_info(self, title: str = None, pageid: int = None, inline=Fa link = None if self.url.find('$1') != -1: link = self.url.replace('$1', title) - return PageInfo(title=title if title is not None else pageid, id=pageid, + return PageInfo(title=title if title else pageid, id=pageid, link=link, desc=self.locale.t("error") + str(e), info=self.wiki_info, templates=[]) ban = False if self.wiki_info.in_blocklist and not self.wiki_info.in_allowlist: @@ -437,11 +440,8 @@ async def parse_page_info(self, title: str = None, pageid: int = None, inline=Fa if _tried > 5: if Config('enable_tos'): raise WhatAreUDoingError - section = None - if title is not None: - if title == '': - return PageInfo(title='', link=self.wiki_info.articlepath.replace("$1", ""), info=self.wiki_info, - interwiki_prefix=_prefix, templates=[]) + selected_section = None + if title: if inline: split_name = re.split(r'(#)', title) else: @@ -450,11 +450,13 @@ async def parse_page_info(self, title: str = None, pageid: int = None, inline=Fa 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,43 +473,47 @@ async def parse_page_info(self, title: str = None, pageid: int = None, inline=Fa 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} - elif pageid is not None: + elif pageid: page_info = PageInfo(info=self.wiki_info, title=title, args='', interwiki_prefix=_prefix) query_string = {'action': 'query', 'prop': 'info|imageinfo|langlinks|templates', 'llprop': 'url', 'inprop': 'url', 'iiprop': 'url', 'redirects': 'True', 'pageids': pageid} else: - raise ValueError('title and pageid cannot be both None') + return PageInfo(title='', link=self.wiki_info.articlepath.replace("$1", ""), info=self.wiki_info, + interwiki_prefix=_prefix, templates=[]) use_textextracts = True if 'TextExtracts' in self.wiki_info.extensions else False - if use_textextracts and section is None: + if use_textextracts and not selected_section: query_string.update({'prop': 'info|imageinfo|langlinks|templates|extracts|pageprops', 'ppprop': 'description|displaytitle|disambiguation|infoboxes', 'explaintext': 'true', 'exsectionformat': 'plain', 'exchars': '200'}) get_page = await self.get_json(**query_string) query = get_page.get('query') - if query is None: + if not query: 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: + if redirects_: for r in redirects_: if r['from'] == title: page_info.before_title = r['from'] page_info.title = r['to'] normalized_: List[Dict[str, str]] = query.get('normalized') - if normalized_ is not None: + if normalized_: for n in normalized_: if n['from'] == title: page_info.before_title = n['from'] page_info.title = n['to'] pages: Dict[str, dict] = query.get('pages') # print(pages) - if pages is not None: + if pages: for page_id in pages: page_info.status = False page_info.id = int(page_id) @@ -585,10 +591,10 @@ async def search_something(srwhat): searches.append(search_something(srwhat)) gather_search = await asyncio.gather(*searches) for search in gather_search: - if search[0] is not None and search[0] not in searched_result: + if search[0] and search[0] not in searched_result: searched_result.append(search[0]) - if preferred is None and searched_result: + if not preferred and searched_result: preferred = searched_result[0] page_info.before_title = page_info.title @@ -598,6 +604,17 @@ async def search_something(srwhat): 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')), @@ -606,7 +623,7 @@ async def search_something(srwhat): page_info.status = True else: query_langlinks = False - if lang is not None: + if lang: langlinks_ = {} for x in page_raw['langlinks']: langlinks_[x['lang']] = x['url'] @@ -671,16 +688,16 @@ async def search_something(srwhat): get_desc = False get_doc_desc = await self.parse_page_info(get_doc, _doc=True) page_desc = get_doc_desc.desc - if page_desc is not None: + if page_desc: 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 (not selected_section or page_info.invalid_section): raw_desc = page_raw.get('extract') - if raw_desc is not None: + if raw_desc: 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: @@ -689,7 +706,7 @@ async def search_something(srwhat): page_info.link = full_url page_info.file = file page_info.desc = page_desc - if not _iw and page_info.args == '': + if not _iw and not page_info.args: page_info.link = self.wiki_info.script + f'?curid={page_info.id}' else: page_info.title = query_langlinks.title @@ -699,7 +716,7 @@ async def search_something(srwhat): page_info.file = query_langlinks.file page_info.desc = query_langlinks.desc interwiki_: List[Dict[str, str]] = query.get('interwiki') - if interwiki_ is not None: + if interwiki_: for i in interwiki_: if i['title'] == page_info.title: iw_title = re.match(r'^' + i['iw'] + ':(.*)', i['title']) @@ -713,38 +730,38 @@ async def search_something(srwhat): _iw=_iw) before_page_info = page_info page_info = iw_query - if iw_query.title == '': + if not iw_query.title: page_info.title = '' else: page_info.before_title = before_page_info.title t = page_info.title - if t != '' and t is not None: - if before_page_info.args is not None: + if t: + if before_page_info.args: page_info.before_title += urllib.parse.unquote(before_page_info.args) t += urllib.parse.unquote(before_page_info.args) - if page_info.link is not None: + if page_info.link: page_info.link += before_page_info.args else: page_info.link = self.wiki_info.script + f'?curid={page_info.id}' if _tried == 0: - if lang is not None and page_info.status: + if lang and page_info.status: page_info.before_title = page_info.title else: page_info.title = page_info.interwiki_prefix + t - if page_info.possible_research_title is not None: + if page_info.possible_research_title: page_info.possible_research_title = [page_info.interwiki_prefix + possible_title 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: + page_info.selected_section = before_page_info.selected_section if not self.wiki_info.in_allowlist: checklist = [] - if page_info.title is not None: + if page_info.title: checklist.append(page_info.title) - if page_info.before_title is not None: + if page_info.before_title: checklist.append(page_info.before_title) - if page_info.desc is not None: + if page_info.desc: checklist.append(page_info.desc) chk = await check(*checklist) for x in chk: @@ -754,7 +771,7 @@ async def search_something(srwhat): page_info.status = False page_info.title = page_info.before_title = None page_info.id = -1 - if page_info.link is not None: + if page_info.link: page_info.desc = str(Url(page_info.link, use_mm=True)) page_info.link = None return page_info diff --git a/modules/wiki/wiki.py b/modules/wiki/wiki.py index b8aa0f6c0b..53617dab83 100644 --- a/modules/wiki/wiki.py +++ b/modules/wiki/wiki.py @@ -5,6 +5,7 @@ import filetype from core.builtins import Bot, Plain, Image, Voice, Url, confirm_command, quick_confirm +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 @@ -26,26 +27,24 @@ @wiki.command(' [-l ] {{wiki.help}}', options_desc={'-l': '{wiki.help.option.l}'}) -async def _(msg: Bot.MessageSession): +async def _(msg: Bot.MessageSession, PageName: str): get_lang = msg.parsed_msg.get('-l', False) if get_lang: lang = get_lang[''] else: lang = None - await query_pages(msg, msg.parsed_msg[''], lang=lang) + await query_pages(msg, PageName, lang=lang) @wiki.command('id {{wiki.help.id}}') -async def _(msg: Bot.MessageSession): - page_id: str = msg.parsed_msg[''] +async def _(msg: Bot.MessageSession, PageID: str): iw = None - if match_iw := re.match(r'(.*?):(.*)', page_id): + if match_iw := re.match(r'(.*?):(.*)', PageID): iw = match_iw.group(1) - page_id = match_iw.group(2) - if not page_id.isdigit(): + PageID = match_iw.group(2) + if not PageID.isdigit(): await msg.finish(msg.locale.t('wiki.message.id.error')) - Logger.debug(msg.parsed_msg) - await query_pages(msg, pageid=page_id, iw=iw) + await query_pages(msg, pageid=PageID, iw=iw) async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Union[str, list, tuple] = None, @@ -58,8 +57,6 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio headers = target.get_headers() prefix = target.get_prefix() enabled_fandom_addon = session.options.get('wiki_fandom_addon') - if enabled_fandom_addon is None: - enabled_fandom_addon = False elif isinstance(session, QueryInfo): start_wiki = session.api interwiki_list = {} @@ -69,21 +66,21 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio else: raise TypeError('session must be Bot.MessageSession or QueryInfo.') - if start_wiki is None: + if not start_wiki: if isinstance(session, Bot.MessageSession): await session.send_message(session.locale.t('wiki.message.set.default', prefix=session.prefixes[0])) start_wiki = 'https://zh.minecraft.wiki/api.php' if lang in interwiki_list: start_wiki = interwiki_list[lang] lang = None - if title is not None: + if title: if isinstance(title, str): title = [title] if len(title) > 15: raise AbuseWarning(session.locale.t('tos.reason.wiki_abuse')) query_task = {start_wiki: {'query': [], 'iw_prefix': ''}} for t in title: - if prefix is not None and use_prefix: + if prefix and use_prefix: t = prefix + t if not t: continue @@ -125,8 +122,8 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio matched = True if not matched: query_task[start_wiki]['query'].append(t) - elif pageid is not None: - if iw == '': + elif pageid: + if not iw: query_task = {start_wiki: {'queryid': [pageid], 'iw_prefix': ''}} else: if iw in interwiki_list: @@ -150,7 +147,7 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio render_infobox_list = [] render_section_list = [] dl_list = [] - if preset_message is not None: + if preset_message: msg_list.append(Plain(preset_message)) for q in query_task: current_task = query_task[q] @@ -182,18 +179,18 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio r: PageInfo = result display_title = None display_before_title = None - if r.title is not None: + if r.title: display_title = iw_prefix + r.title - if r.before_title is not None: + if r.before_title: display_before_title = iw_prefix + r.before_title new_possible_title_list = [] - if r.possible_research_title is not None: + if r.possible_research_title: for possible in r.possible_research_title: new_possible_title_list.append(iw_prefix + possible) r.possible_research_title = new_possible_title_list if r.status: plain_slice = [] - if display_before_title is not None and display_before_title != display_title: + if display_before_title and display_before_title != display_title: if r.before_page_property == 'template' and r.page_property == 'page': plain_slice.append(session.locale.t('wiki.message.redirect.template_to_page', title=display_before_title, @@ -201,33 +198,61 @@ 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: + if (r.link and r.selected_section 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: + plain_slice.append(r.desc) + + if r.link: plain_slice.append( str(Url(r.link, use_mm=not r.info.in_allowlist))) - if r.file is not None: + if r.file: 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 and not r.selected_section: 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 + (r.templates 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 = [] - if display_title is not None and display_before_title is not None: + if display_title and display_before_title: if isinstance(session, Bot.MessageSession) and session.Feature.wait: if not session.options.get('wiki_redlink', False): if len(r.possible_research_title) > 1: @@ -250,7 +275,7 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio redirected_title=display_title)) wait_plain_slice.append(session.locale.t("message.wait.confirm.prompt.type1")) else: - if r.edit_link is not None: + if r.edit_link: plain_slice.append(r.edit_link + session.locale.t('wiki.message.redlink.not_found')) else: plain_slice.append(session.locale.t('wiki.message.redlink.not_found.uneditable', @@ -261,13 +286,13 @@ async def query_pages(session: Union[Bot.MessageSession, QueryInfo], title: Unio redirected_title=display_title)) if len(r.possible_research_title) == 1: wait_list.append({display_title: display_before_title}) - elif r.before_title is not None: + elif r.before_title: plain_slice.append(session.locale.t('wiki.message.not_found', title=display_before_title)) elif r.id != -1: plain_slice.append(session.locale.t('wiki.message.id.not_found', id=str(r.id))) - if r.desc is not None and r.desc != '': + if r.desc: plain_slice.append(r.desc) - if r.invalid_namespace and r.before_title is not None: + if r.invalid_namespace and r.before_title: plain_slice.append( session.locale.t('wiki.message.invalid_namespace', namespace=r.invalid_namespace)) if r.before_page_property == 'template': @@ -323,11 +348,17 @@ async def section(): 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) @@ -336,7 +367,7 @@ async def image_and_voice(): for f in dl_list: dl = await download_to_cache(f) guess_type = filetype.guess(dl) - if guess_type is not None: + if guess_type: if guess_type.extension in ["png", "gif", "jpg", "jpeg", "webp", "bmp", "ico"]: if session.Feature.image: await session.send_message(Image(dl), quote=False) diff --git a/modules/wolframalpha/locales/en_us.json b/modules/wolframalpha/locales/en_us.json index f8d15b4393..795030033d 100644 --- a/modules/wolframalpha/locales/en_us.json +++ b/modules/wolframalpha/locales/en_us.json @@ -1,6 +1,6 @@ { "wolframalpha.help": "Input a question or formula to search for WolframAlpha.", "wolframalpha.help.ask": "Answer the question via WolframAlpha.", - "wolframalpha.help.desc": "搜索 WolframAlpha。", + "wolframalpha.help.desc": "Use WolframAlpha.", "wolframalpha.message.incomprehensible": "WolframAlpha can't understand your question." -} +} diff --git a/modules/wolframalpha/locales/zh_cn.json b/modules/wolframalpha/locales/zh_cn.json index be47546eab..8bc8b4a7ed 100644 --- a/modules/wolframalpha/locales/zh_cn.json +++ b/modules/wolframalpha/locales/zh_cn.json @@ -1,6 +1,6 @@ { "wolframalpha.help": "输入问题或公式以搜索 WolframAlpha。", "wolframalpha.help.ask": "通过 WolframAlpha 回答问题。", - "wolframalpha.help.desc": "WolframAlpha 相关工具。", + "wolframalpha.help.desc": "使用 WolframAlpha。", "wolframalpha.message.incomprehensible": "WolframAlpha 无法理解你的问题,请尝试使用英语提问。" } diff --git a/modules/wolframalpha/locales/zh_tw.json b/modules/wolframalpha/locales/zh_tw.json index 76f8fb537d..6d3dc1b858 100644 --- a/modules/wolframalpha/locales/zh_tw.json +++ b/modules/wolframalpha/locales/zh_tw.json @@ -1,6 +1,6 @@ { "wolframalpha.help": "輸入問題或公式以搜尋 WolframAlpha。", "wolframalpha.help.ask": "透過 WolframAlpha 回答問題。", - "wolframalpha.help.desc": "WolframAlpha 相關工具。", + "wolframalpha.help.desc": "使用 WolframAlpha。", "wolframalpha.message.incomprehensible": "WolframAlpha 無法理解你的問題,請嘗試使用英語提問。" -} +} diff --git a/schedulers/mcv_rss.py b/schedulers/mcv_rss.py index 9b32ee6c26..ef79b8c25a 100644 --- a/schedulers/mcv_rss.py +++ b/schedulers/mcv_rss.py @@ -1,3 +1,4 @@ +import datetime import re import traceback from urllib.parse import quote @@ -7,6 +8,7 @@ from google_play_scraper import app as google_play_scraper from config import CFG, Config +from core.builtins import I18NContext, FormattedTime from core.logger import Logger from core.queue import JobQueue from core.scheduler import Scheduler, IntervalTrigger @@ -74,32 +76,50 @@ 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=[I18NContext('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=[I18NContext('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=[I18NContext('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=[I18NContext('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: @@ -108,14 +128,15 @@ async def mcv_rss(): @Scheduler.scheduled_job(IntervalTrigger(seconds=180)) async def mcbv_rss(): - if IP.country == 'China' or IP.country is None: + if IP.country == 'China' or not IP.country: return # 中国大陆无法访问Google Play商店 try: verlist = get_stored_list('scheduler', 'mcbv_rss') version = google_play_scraper('com.mojang.minecraftpe')['version'] if version not in verlist: Logger.info(f'huh, we find bedrock {version}.') - await JobQueue.trigger_hook_all('mcbv_rss', message='mcv_rss.message.mcbv_rss', i18n=True, version=version) + await JobQueue.trigger_hook_all('mcbv_rss', message=[I18NContext('mcv_rss.message.mcbv_rss', + version=version).to_dict()]) verlist.append(version) update_stored_list('scheduler', 'mcbv_rss', verlist) except Exception: @@ -138,11 +159,12 @@ async def mcv_jira_rss(): if release not in verlist: Logger.info(f'huh, we find {release}.') if release.lower().find('future version') != -1: - await JobQueue.trigger_hook_all('mcv_jira_rss', message='mcv_rss.message.mcv_jira_rss.future', - i18n=True, version=release) + await JobQueue.trigger_hook_all('mcv_jira_rss', + message=[I18NContext('mcv_rss.message.mcv_jira_rss.future', + version=release).to_dict()]) else: - await JobQueue.trigger_hook_all('mcv_jira_rss', message='mcv_rss.message.mcv_jira_rss', i18n=True, - version=release) + await JobQueue.trigger_hook_all('mcv_jira_rss', message=[I18NContext('mcv_rss.message.mcv_jira_rss', + version=release).to_dict()]) verlist.append(release) update_stored_list('scheduler', 'mcv_jira_rss', verlist) @@ -166,8 +188,8 @@ async def mcbv_jira_rss(): if release not in verlist: Logger.info(f'huh, we find {release}.') - await JobQueue.trigger_hook_all('mcbv_jira_rss', message='mcv_rss.message.mcbv_jira_rss', - i18n=True, version=release) + await JobQueue.trigger_hook_all('mcbv_jira_rss', message=[I18NContext('mcv_rss.message.mcbv_jira_rss', + version=release).to_dict()]) verlist.append(release) update_stored_list('scheduler', 'mcbv_jira_rss', verlist) except Exception: @@ -190,8 +212,8 @@ async def mcdv_jira_rss(): if release not in verlist: Logger.info(f'huh, we find {release}.') - await JobQueue.trigger_hook_all('mcdv_jira_rss', message='mcv_rss.message.mcdv_jira_rss', - i18n=True, version=release) + await JobQueue.trigger_hook_all('mcdv_jira_rss', message=[I18NContext('mcv_rss.message.mcdv_jira_rss', + version=release).to_dict()]) verlist.append(release) update_stored_list('scheduler', 'mcdv_jira_rss', verlist) except Exception: @@ -214,8 +236,8 @@ async def mclgv_jira_rss(): if release not in verlist: Logger.info(f'huh, we find {release}.') - await JobQueue.trigger_hook_all('mclgv_jira_rss', message='mcv_rss.message.mclgv_jira_rss', - i18n=True, version=release) + await JobQueue.trigger_hook_all('mclgv_jira_rss', message=[I18NContext('mcv_rss.message.mclgv_jira_rss', + version=release).to_dict()]) verlist.append(release) update_stored_list('scheduler', 'mclgv_jira_rss', verlist) except Exception: diff --git a/schedulers/minecraft_news.py b/schedulers/minecraft_news.py index fb47a7efbf..ac1e7c4de9 100644 --- a/schedulers/minecraft_news.py +++ b/schedulers/minecraft_news.py @@ -6,7 +6,7 @@ import ujson as json from config import Config, CFG -from core.builtins import Url +from core.builtins import Url, I18NContext from core.logger import Logger from core.queue import JobQueue from core.scheduler import Scheduler, IntervalTrigger @@ -70,9 +70,8 @@ async def start_check_news(use_local=True): publish_date = datetime.strptime(o_article['publish_date'], '%d %B %Y %H:%M:%S %Z') now = datetime.now() if now - publish_date < timedelta(days=2): - await JobQueue.trigger_hook_all('minecraft_news', - message='minecraft_news.message.minecraft_news', i18n=True, - title=title, desc=desc, link=str(Url(link))) + await JobQueue.trigger_hook_all('minecraft_news', message=[I18NContext('minecraft_news.message.minecraft_news', + title=title, desc=desc, link=link).to_dict()]) alist.append(title) update_stored_list('scheduler', 'mcnews', alist) except Exception: @@ -100,9 +99,8 @@ async def feedback_news(): link = article['html_url'] Logger.info(f'huh, we find {name}.') await JobQueue.trigger_hook_all('feedback_news', - message='minecraft_news.message.feedback_news', - i18n=True, - name=name, link=str(Url(link))) + message=[I18NContext('minecraft_news.message.feedback_news', + name=name, link=str(Url(link))).to_dict()]) alist.append(name) update_stored_list('scheduler', 'mcfeedbacknews', alist) except Exception: diff --git a/schedulers/purge.py b/schedulers/purge.py new file mode 100644 index 0000000000..30be86d333 --- /dev/null +++ b/schedulers/purge.py @@ -0,0 +1,15 @@ +import os +import shutil + +from config import Config +from core.logger import Logger +from core.scheduler import Scheduler, CronTrigger + + +@Scheduler.scheduled_job(CronTrigger.from_crontab('0 0 * * *')) +async def _(): + cache_path = os.path.abspath(Config('cache_path')) + Logger.info('Start purging cache...') + if os.path.exists(cache_path): + shutil.rmtree(cache_path) + os.mkdir(cache_path)