diff --git a/py.typed b/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/requirements.txt b/requirements.txt index 2a219ab8..046b3b9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ aiofiles>=23.2.1 colored>=2.2.4 GitPython>=3.1.43 httpx>=0.27.0 -liteyukibot-plugin-htmlrender>=0.1.0 +nonebot-plugin-htmlrender>=0.1.0 nonebot2[fastapi,httpx,websockets]>=2.3.3 nonebot-adapter-onebot>=2.4.3 nonebot-plugin-alconna>=0.46.3 diff --git a/src/liteyuki_main/core.py b/src/liteyuki_main/core.py index 4e0ec948..cd965456 100644 --- a/src/liteyuki_main/core.py +++ b/src/liteyuki_main/core.py @@ -18,11 +18,11 @@ from src.utils.base.config import get_config from src.utils.base.data_manager import TempConfig, common_db from src.utils.base.language import get_user_lang from src.utils.base.ly_typing import T_Bot, T_MessageEvent -from src.utils.htmlrender import md_to_pic from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers -from .api import update_liteyuki -from ..utils.base import reload -from ..utils.base.ly_function import get_function +from .api import update_liteyuki # type: ignore +from ..utils.base import reload # type: ignore +from ..utils.base.ly_function import get_function # type: ignore +from ..utils.message.html_tool import md_to_pic require("nonebot_plugin_alconna") require("nonebot_plugin_apscheduler") @@ -42,8 +42,8 @@ driver = get_driver() ).handle() # Satori OK async def _(bot: T_Bot, matcher: Matcher, result: Arparma): - if result.main_args.get("text"): - await matcher.finish(Message(unescape(result.main_args.get("text")))) + if text := result.main_args.get("text"): + await matcher.finish(Message(unescape(text))) else: await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}") diff --git a/src/nonebot_plugins/liteyuki_pacman/npm.py b/src/nonebot_plugins/liteyuki_pacman/npm.py index 0b92d6bf..8a8d9d5c 100644 --- a/src/nonebot_plugins/liteyuki_pacman/npm.py +++ b/src/nonebot_plugins/liteyuki_pacman/npm.py @@ -22,7 +22,7 @@ from src.utils.base.permission import GROUP_ADMIN, GROUP_OWNER from src.utils.message.tools import clamp from src.utils.message.message import MarkdownMessage as md from src.utils.message.markdown import MarkdownComponent as mdc, compile_md, escape_md -from src.utils.htmlrender import md_to_pic +from src.utils.message.html_tool import md_to_pic from .common import * diff --git a/src/nonebot_plugins/liteyuki_user/profile_manager.py b/src/nonebot_plugins/liteyuki_user/profile_manager.py index 403083e0..7a44f490 100644 --- a/src/nonebot_plugins/liteyuki_user/profile_manager.py +++ b/src/nonebot_plugins/liteyuki_user/profile_manager.py @@ -8,7 +8,7 @@ from src.utils.base.data_manager import User, user_db, group_db from src.utils.base.language import Language, change_user_lang, get_all_lang, get_user_lang from src.utils.base.ly_typing import T_Bot, T_MessageEvent from src.utils.message.message import MarkdownMessage as md -from src.utils.htmlrender import md_to_pic +from src.utils.message.html_tool import md_to_pic from .const import representative_timezones_list from src.utils import event as event_utils diff --git a/src/utils/htmlrender/LICENSE.md b/src/utils/htmlrender/LICENSE.md deleted file mode 100644 index 30fd3251..00000000 --- a/src/utils/htmlrender/LICENSE.md +++ /dev/null @@ -1,155 +0,0 @@ -**当前目录(htmlrender)内之全部文件(除 Licenses\ 目录下的文件外) 须依照此文件所规定之特别声明进行授权**\ -**该目录下所有文件(除 Licenses\ 目录下的文件外)所形成的整体(后文称“页面渲染组件”) 不可以从 轻雪(Liteyuki) 分离而单独运行**\ -**且 页面渲染组件 本身为一不可割裂之整体独立存在** - -**当文件所规定之本协议与 轻雪 项目之协议存在冲突,应遵循本文件中所述的协议进行行为** - -**当前目录内 Licenses 目录下的文件,系 页面渲染组件 之制作过程中所使用、参考或改写的第三方项目所采用的协议。**\ -**二次分发时应予以保留,并遵循相关协议的规定**\ -**本文件所规定之协议与上述协议若存在冲突部分,应遵循本文件中所述的协议进行行为** - --------- - -版权所有 © 2024 金羿Eilles(EillesWan@outlook.com) -Copyright (R) 2024 EillesWan(EillesWan@outlook.com) - -页面渲染组件 的 协议颁发者 是 金羿Eilles(EillesWan@outlook.com) - -页面渲染组件 根据 第一版 汉钰律许可协议(“本协议”)授权。 -任何人皆可从以下地址获得本协议副本:[轻雪仓库](https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/src/utils/htmlrender/LICENSE.md)。 -若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。 -详细的准许和限制条款请见原协议文本。 - - --------- - -以下为协议源文本 - --------- - -# 汉钰律许可协议 - -**总第一版 · 二〇二四年七月七日编** - -## 一、重要须知 - -1. 为保护采用本协议的作品在开源过程中,其著作权人所应有的权益,根据**中华人民共和国著作权法和相关法律法规**,制定本协议。 - -2. 本协议履行过程中,请注意本协议中**免除或限制**民事主体**责任或权利**的条款、法律适用和争议解决条款(尤其是加有特殊标记的条款),这些条款应在中国法律所允许的范围内最大程度地适用。 - -3. 若本协议所涉及的自然人**未满 18 周岁**,该自然人应在监护人的陪同下阅读本协议及有关本协议的条款内容,并在取得其监护人同意后开始或继续应用本协议所授权的行为。 - -4. 由于互联网服务、互联网内容的特殊性,若本协议以电子协议形式分发并签订,其依然有效。您一旦开始对本协议所授权之作品进行本协议所授权的行为,即视为您已经阅读、理解并同意并已经接受本协议的全部条款。 - -5. 本协议的订立、履行、解释及争议的解决均**适用中华人民共和国法律并排除其他一切冲突法的适用**。_本协议订立于许可证最初的颁发者的地址。若其为自然人,则订立于该自然人户籍所在地;若为法人或非法人组织,则订立于其注册地_。本协议的订立各方应友好协商解决于协议所规定之行为的履行相关的争议;如协商不成,任何一方均可向合同签订地有管辖权的人民法院提起诉讼。 - -6. 本协议的原本仅为现代汉语,书写于简体中文。若存在其他语言的翻译或其他同等语言但非简体中文文本的版本,应当无法律效力。 - -## 二、术语定义 - -1. “**许可证**”、“**协议**”(后文称“本协议”)是指根据本文档中所列举的全部术语、定义、条款、限制等文本,是本合同的简称称谓。本合同全称是“汉钰律许可协议”。 - -2. “**协议颁发者**”(后文称“颁发者”)是将条款或协议应用于其拥有著作财产权的作品的民事主体,或由其指定从而拥有颁发者身份的民事主体。 - -3. “**源**”形式是指对包括但不限于 软件、硬件、文档、配置项 等种类的作品进行修改、编辑的首选形式;若不存在首选形式,则初次编辑该作品所需的形式即为源形式。 - -4. “**目标**”形式是指对源形式进行机械转换、翻译、打印、制造、加工等同类型活动后形成的结果形式,包括但不限于源代码编译后的目标软件、生成的文件、转换出的媒体、制造出的机械、打印出的实体文本、加工后的零件。 - -5. “**采用本协议的作品**”(后文称“此作品”)是指经颁发者授权而使用本协议进行授权的任何作品,该作品应在自然人可见处明确附加一个自然人可读的版权通知(可以参考文末附录中提供的示例);若在一个可分割的作品中,部分地采用本协议进行授权,则该部分应当视为一个独立的采用本协议的作品,该作品应当在自然人可见处明确附加一个自然人可读的范围限定和版权通知(同样可以参考文末附录中提供的示例)。 - -6. “**贡献**”是指对作品进行的,意在提交给此作品颁发者以让著作权人包含在其作品中的任何修订或补充,该修订或补充同样属于一种作品。依据此定义,“提交”一词表示经由此作品颁发者所指定的形式,将其所进行的修改发送给此作品颁发者。该形式应当包括在此作品颁发者指定的平台内发送易于编辑的修改信息、在此作品颁发者指定的电子邮箱中发送易于编辑的修改信息、在此作品颁发者指定的源码控制系统或发布跟踪系统上提交的易于编辑的修改信息,但由著作权人以明显标注或指定为“非贡献”的活动除外。颁发者自己对作品进行的修改同样视作对作品的贡献。 - -7. “**贡献者**”是指此作品颁发者接受的贡献的提交者,或包含在作品的贡献清单中的民事主体。贡献者在提交贡献并经此作品颁发者通过且该贡献已经被应用于此作品中后,该贡献者应当视为此作品的著作权人之一,但不应视为此作品非其贡献的部分的著作权人。一个作品的颁发者同样属于其贡献者。**请注意**,针对贡献者提交的贡献,该贡献者应被视为该贡献的协议颁发者,但不应视作本作品的颁发者。 - -8. “**用户**”、“**使用者**”是指行使本协议所授权之行为的民事主体。据此,贡献者亦属于用户。 - -9. “**商业性使用**”、“**商用**”是指任何以谋取利益为目的的使用,包括但不限于以贩卖、出租的形式对作品进行使用;但若将该牟利活动明确指示为“捐赠”,且在牟利者进行本协议所授权的活动时不以捐赠数额为标准,则此种的获取利益的“捐赠”行为不属于商业性使用。 - -## 三、权利授予 - -1. 任何由颁发者所进行的特殊声明、特别注意等此类内容,应当在法律效力上高于本协议的条款或声明;这些声明若与本协议冲突,本协议的该冲突部分无效;本协议与这些声明共同构成颁发者与用户之间的合同。 - -2. 此作品的贡献者享有其贡献的完整著作权。 - -3. 此作品的贡献者将自己的贡献的全部著作财产权,免费、公开、不可撤销、无限期、非专有地授予此作品的全部著作权人,并准许其在全世界范围内使用上述权利;若无明确的标识,贡献者允许此作品的颁发者对其贡献进行免费、公开、不可撤销、无限期、非专有、世界范围内的商业性使用。 - -4. 此作品的著作权人及贡献者授予用户**免费、公开、不可撤销、非专有、非商用**地以任意形式**复制、发行、展览、表演、放映、广播、信息网络传播、摄制、改编、翻译、汇编、二次授权**的权利,准许其在此作品颁发者所指定的区域与时间内行使上述权利;若此作品颁发者未特别指定的,则视作在全世界范围内无限期地授权;若此作品颁发者特别指定在特定情况下可以商用,则应当按照其所指定的条件进行商业性使用,商用的过程中,应当明确标识此作品的著作权人。 - -5. 一旦此作品有任意由非贡献形式而产生的更改,更改的部分将不视为此作品的一部分,除非该部分不可离开此作品单独存在;若该部分必须依赖此作品而不可与此作品分离从而单独存在,则更改后的作品不视作此作品,在这种情况下,除非此更改后的作品已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人,否则对该作品进行的任何活动都应当遵守本协议。 - -6. 经贡献而产生的对此作品的更改,属于此作品的一部分;在此情况下,更改后的作品,依旧视作此作品。 - -7. 依据本款的第 4 条,若用户在本协议的授权下,将此作品授予他人进行任何形式的活动(即“二次授权”、“二次分发”),则应确保其使用的协议或授权内容,与本协议的条款不冲突;当存在与本协议条款的冲突时,则该冲突内容无效,被授权的第三方应依照本协议的条款进行活动;除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。 - -8. 依据本款的第 5 条,若由非贡献形式而产生更改的部分是可分割而不需依赖此作品即可单独存在的,若该部分明确注明不使用本协议进行授权或明确声明了其他授权条款,则该部分不视作采用本协议;但未更改的部分仍应视作原此作品的一部分,需要采用本协议进行授权,除非此更改后的作品已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人。 - -9. 若此作品或所提交的贡献包含其著作权人的专利,则该专利所有人即此作品的著作权人应准许此作品全体著作权人**免费、公开、不可撤销、非专有、无版权费的专利许可**,以便贡献者对作品进行本协议所授权进行的活动。 - -10. 上述专利许可的授予,仅适用于在所提交的贡献中,可由专利所有者授予的,且在对此作品进行本协议所授权的活动中,必须使用的专利。 - -11. 如果用户对任何民事主体,因其在进行本协议所授权进行的活动中侵犯该用户的专利而提起诉讼,那么根据本协议授予该用户的所有关于此作品的任何其他专利许可将在提起上述诉讼之日起终止。 - -12. 如果本作品作为用户的其他作品的不可分割的一部分进行任何民事活动,本协议依旧对本作品(即该用户的其他作品的一部分)生效;若本作品完全融入该用户的其他作品之中而不可独立存在,则该用户需要保证其作品存在与本协议冲突的条款;除非该作品已获得此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。 - -## 四、使用条件 - -在对此作品进行本协议所授权的民事活动中,应当同时满足以下条款: - -1. 用户必须为此作品的任何其他接收者提供本协议的副本,在不得已无法提供副本的情况下,也应明确指示其他接收者可查阅本协议的位置。 - -2. 用户必须在修改后的作品中附带明显的通知,声明用户已更改文件,并注明更改位置。 - -3. 若用户二次分发此作品,可以选择向此作品的接收者提供无偿或有偿的担保维修、支持服务或其他责任、义务。但是,该用户只可以其自己的名义提供上述内容,不得以任何其他贡献者的名义。且该用户必须明确表明任何此类责任或义务是由其个人独立提供,且其同意并应当承担赔偿此作品的全体贡献者因其个人承担上述责任义务而产生的任何赔偿责任。 - -4. 用户不得删除或更改此作品中包含的任何许可声明(包括版权声明,专利声明,免责声明,或赔偿责任限制),除非该更改是对已知事实错误的修补、或其已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人。 - -5. 若此作品将权益的声明通知作为一部分,那么由用户分发的任何版本的作品中须至少在下列三处之一包含该声明通知的自然人可读副本: - - - 该作品的权益声明通知中 - - 在源形式的文件中(当且仅当该作品开放源代码) - - 在惯例中作为第三方通知出现之处(当且仅当该作品会产生画面,且该画面可被自然人详细观察) - - 该通知的内容仅供信息提供,不应对许可证进行任何文字上的修改。用户可在其分发的作品中,在不构成修改本协议的前提下,在作品自身的声明通知或属性描述后或作为附录添加。 - -6. 依据本款第3条,若用户二次分发此作品时,选择向作品的接收者提供收费的担保服务,则必须明确告知该接收者本协议全部内容与此作品原出处,并确保其知悉上述内容;但若用户在二次分发此作品是,不选择提供任何服务,则该用户不允许向作品的接收者收取任何费用,除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。 - -## 五、提交贡献 - -除非贡献者明确声明,在本作品中由该贡献者向颁发者的提供的提交,必须符合本协议的条款,并与本协议的条款不存在冲突;除非此贡献中与本协议冲突的附加条款已获得颁发者的特殊许可、或贡献者即为此作品颁发者本人。 - -## 六、商标相关 - -本协议并未授予用户,将颁发者的商标、专属标记或特定产品名称,用于合理的或惯例性的描述或此类声明之外其他任何位置的权利。 - -## 七、免责声明 - -1. 若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,**不予提供任何形式的担保、任何明示、任何暗示或类似承诺**,此类包括但不限于担保此作品毫无缺陷、担保此作品适于贩卖、担保此作品适于特定目的、担保使用此作品绝不侵权。用户将自行承担因此作品的质量或性能问题而产生的全部风险。若此作品在任何方面欠妥,将由用户(而非任何贡献者、而非任何颁发者)承担所有必要的服务、维修或除错的任何成本。本免责声明本许可的重要组成部分。当且仅当遵守本免责声明时,本协议的其他条款中对本作品的使用授权方可生效。 - -2. 无论是因何种原因,如果不是在法律规定的特殊情况(如,确为贡献者的故意或重大过失)下或者经过了特殊准许,即使贡献者事先已知发生损害的可能,在使用本作品时,用户产生的任何直接、间接、特殊、偶然或必然造成的损失(包括但不限于商誉损失、工作延误、计算机系统故障等),**均不由任一贡献者承担**。 - -**以上是本许可协议的全部条款** - ---- - -附录 - -**如何在自己的作品中应用 汉钰律许可协议** - -若要在自己源形式的作品应用本协议,请在其中附加下面的通知模板,并将六角括号“〔〕”中的字段替换成自身的实际信息来替换(不包括括号本身)。这些文本必须以对应文件格式适当的注释句法包含在其中,可以是实体的纸质文档、也可以是网络公告或者计算机文件;或者脱离该源之外,另起一个新的文件,使之指向要应用本协议的那个作品。同时也建议将作品名或类别名以及目的说明之类的声明囊括在同一个可被打印的页面上作为版权通知的整体,这样更加容易的区分出第三方内容。 - -若需要在自己以目标形式存在的作品中应用本协议,同样需要附加下面的通知模板并更改六角括号中的字样。但是,这些文本可以是位于作品的标签上、位于作品的用户可见且能被自然人详细观察的画面之中、或者按照惯例中许可协议应该出现的位置;同时,这些文本的所处位置应当能够明确指示到本协议应用的那个作品。另外,建议将作品名或类别名以及目的说明之类的声明囊括在同一个可被打印的位置上作为版权通知的整体,这样更加容易的区分出第三方内容。 - -**通知模板** - -``` -版权所有 © 〔年份〕 〔著作权人〕 -〔或者:版权所有 (C) 〔年份〕 〔著作权人〕〕 - -〔该作品〕根据 第一版 汉钰律许可协议(“本协议”)授权。 -任何人皆可从以下地址获得本协议副本:〔本协议副本所在地址〕。 -若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。 -详细的准许和限制条款请见原协议文本。 -``` - - - diff --git a/src/utils/htmlrender/Licenses/LICENSE(Liteyuki) b/src/utils/htmlrender/Licenses/LICENSE(Liteyuki) deleted file mode 100644 index 8bbd0e87..00000000 --- a/src/utils/htmlrender/Licenses/LICENSE(Liteyuki) +++ /dev/null @@ -1,31 +0,0 @@ -LSO license -LiteyukiStudio Opensource license - ---- - -版权所有 © 2024 Snowykami - ---- - -免费向任何获得副本的人或组织授予以相同许可为基础的权利 - -包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件的副本 - -本软件及相关文档文件(以下简称"本软件")在相同方式许可为基础, 以开源的形式发布于互联网抑或其他媒体介质平台 - -任何人都有权利获取副本并以上述方式获取许可传播和/或使用 - -但获取副本时仍需注意: - -- 上述版权声明和本许可声明应包含在本软件的副本中 - - 使用本软件及其副本时仍需保持与原有形式相同 - -- 在使用时仍需将本软件的副本以相同许可公开表现: - - 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利 - ---- - -该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示: -包括但不限于适销性保证, 适用于特定目的非侵权 - -在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付 diff --git a/src/utils/htmlrender/Licenses/LICENSE(nonebot-plugin-htmlrender) b/src/utils/htmlrender/Licenses/LICENSE(nonebot-plugin-htmlrender) deleted file mode 100644 index ef5c4f41..00000000 --- a/src/utils/htmlrender/Licenses/LICENSE(nonebot-plugin-htmlrender) +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 kexue - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/utils/htmlrender/__init__.py b/src/utils/htmlrender/__init__.py deleted file mode 100644 index 27b06685..00000000 --- a/src/utils/htmlrender/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ - -# from .control import * -from .function import * \ No newline at end of file diff --git a/src/utils/htmlrender/control.py b/src/utils/htmlrender/control.py deleted file mode 100644 index 783e3173..00000000 --- a/src/utils/htmlrender/control.py +++ /dev/null @@ -1,95 +0,0 @@ -import platform -from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator - -import pyppeteer -import pyppeteer.browser -import pyppeteer.errors -import pyppeteer.page - -from liteyuki.log import logger - -# from liteyuki.plugin import PluginMetadata, PluginType -from src.utils.base.config import get_config - -# __plugin_meta__ = PluginMetadata( -# name="页面渲染组件", -# description="提供跨平台的多用途页面渲染功能,是nontbot-plugin-htmlrender的高级替代", -# type=PluginType.MODULE, -# author="金羿Eilles", -# extra={ -# "license": "汉钰律许可协议 第一版", -# }, -# ) - -_browser: Optional[pyppeteer.browser.Browser] = None - - -async def init(**kwargs) -> pyppeteer.browser.Browser: - global _browser - logger.info("正在初始化浏览器") - - chromium_path = get_config("chromium_path") - - if chromium_path: - try: - _browser = await pyppeteer.launch(executablePath=chromium_path, **kwargs) - except pyppeteer.errors.PyppeteerError as e: - logger.error(f"浏览器启动失败:{e}") - raise - - logger.success("浏览器注册成功") - return _browser - else: - - logger.error("请在配置文件中设置 chromium_path") - raise pyppeteer.errors.BrowserError( - "未配置浏览器地址;若曾用过nonebot-plugin-htmlrender,则可在 {} 处寻得一可用之chromium".format( - "%USERPROFILE%\\AppData\\Local\\ms-playwright" - if platform.system() == "Windows" - else ( - "~/Library/Caches/ms-playwright" - if platform.system() == "Darwin" - else "~/.cache/ms-playwright" - ) - ) - ) - - -async def get_browser(**kwargs) -> pyppeteer.browser.Browser: - return ( - _browser - if _browser and _browser._connection._connected - else await init(**kwargs) - ) - - -@asynccontextmanager -async def get_new_page( - viewport: Optional[dict] = None, - cookie: Optional[dict] = None, - useragent: Optional[str] = None, -) -> AsyncIterator[pyppeteer.page.Page]: - - browser = await get_browser() - page = await browser.newPage() - # device_scale_factor=device_scale_factor, **kwargs - if viewport: - await page.setViewport(viewport) - if cookie: - await page.setCookie(cookie) - if useragent: - await page.setUserAgent(useragent) - - try: - yield page - finally: - await page.close() - - -async def shutdown_browser(): - global _browser - if _browser: - if _browser._connection._connected: - await _browser.close() - _browser = None diff --git a/src/utils/htmlrender/function.py b/src/utils/htmlrender/function.py deleted file mode 100644 index ffa79a23..00000000 --- a/src/utils/htmlrender/function.py +++ /dev/null @@ -1,319 +0,0 @@ -import os -import uuid -from typing import Any, Dict, Literal, Optional, Union - -import jinja2 -import aiofiles -import markdown - -import pyppeteer.errors - -# from pathlib import Path - -from liteyuki.log import logger - -from src.utils.base.resource import get_resource_path # , temp_extract_root - -from .control import get_new_page - -TEMPLATES_PATH = get_resource_path("templates", abs_path=True) - -env = jinja2.Environment( # noqa: S701 - extensions=["jinja2.ext.loopcontrols"], - loader=jinja2.FileSystemLoader(TEMPLATES_PATH), - enable_async=True, -) - - -async def read_any(path: str | os.PathLike[str], mode_: str = "r") -> str | bytes: - async with aiofiles.open(path, mode=mode_) as f: # type: ignore - return await f.read() - - -async def read_template(path: str) -> str: - return await read_any(TEMPLATES_PATH / path) # type: ignore - - -async def write_any(path: str | os.PathLike[str], content: str): - async with aiofiles.open(path, mode="w", encoding="utf-8") as f: - await f.write(content) - - -async def template_to_html( - template_path: str, - template_name: str, - **kwargs, -) -> str: - """使用jinja2模板引擎通过html生成图片 - - Args: - template_path (str): 模板路径 - template_name (str): 模板名 - **kwargs: 模板内容 - Returns: - str: html - """ - - template_env = jinja2.Environment( - loader=jinja2.FileSystemLoader(template_path), - enable_async=True, - ) - template = template_env.get_template(template_name) - - return await template.render_async(**kwargs) - - -async def html_to_pic( - html_path: str, - html: str = "", - wait: int = 0, - # template_path: str = "file://{}".format(os.getcwd()), - type_: Literal["jpeg", "png"] = "png", # noqa: A002 - quality: Union[int, None] = None, - viewport: Optional[Dict[str, Any]] = None, - cookie: Optional[Dict[str, Any]] = None, - user_agent: Optional[str] = None, - device_scale_factor: float = 2, -) -> bytes: - """html转图片 - - Args: - html (str): html文本,若存在 JavaScript 脚本则无效 - html_path (str, optional): HTML路径 如 "file:///path/to/template.html" - wait (int, optional): 等待时间,单位毫秒,默认为 0. - type (Literal["jpeg", "png"]): 图片类型,默认 png - quality (int, optional): 图片质量 0-100 当为`png`时无效 - viewport: (Dict[str, Any], optional): viewport 参数 - cookie: (Dict[str, Any], optional): 页面 cookie - user_agent: (str, optional): 页面 UA - device_scale_factor: 缩放比例,类型为float,值越大越清晰(真正想让图片清晰更优先请调整此选项) - **kwargs: 传入 page 的参数 - - Returns: - bytes: 图片, 可直接发送 - """ - # logger.debug(f"html:\n{html}") - if "file:" not in html_path: - raise Exception("html_path 应为 file:/// 协议之文件传递") - - # open( - # filename := os.path.join( - # template_path, - # str(uuid.uuid4()) + ".html", - # ), - # "w", - # ).write(html) - - logger.info("截入浏览器运作") - - try: - async with get_new_page(viewport, cookie, user_agent) as page: - page.on("console", lambda msg: logger.debug(f"浏览器控制台: {msg.text}")) - await page.goto(html_path, waitUntil="networkidle0") - if html: - await page.setContent( - html, - ) - await page.waitFor(wait) - - logger.info("页面截屏") - - return await page.screenshot( - fullPage=True, - type=type_, - quality=quality, - scale=device_scale_factor, - encoding="binary", - ) # type: ignore - except pyppeteer.errors.PyppeteerError as e: - logger.error(f"浏览器页面获取出错: {e}") - return await read_any(TEMPLATES_PATH / "chromium_error.png", "rb") # type: ignore - - -async def template_to_pic( - template_path: str, - template_name: str, - templates: Dict[Any, Any], - pages: Optional[Dict[Any, Any]] = None, - wait: int = 0, - type_: Literal["jpeg", "png"] = "png", # noqa: A002 - quality: Union[int, None] = None, - viewport: Optional[Dict[str, Any]] = None, - cookie: Optional[Dict[str, Any]] = None, - user_agent: Optional[str] = None, - device_scale_factor: float = 2, -) -> bytes: - """使用jinja2模板引擎通过html生成图片 - - Args: - template_path (str): 模板路径 - template_name (str): 模板名 - templates (Dict[Any, Any]): 模板内参数 如: {"name": "abc"} - pages (Optional[Dict[Any, Any]]): 网页参数(已弃用) - wait (int, optional): 网页载入等待时间. Defaults to 0. - type (Literal["jpeg", "png"]): 图片类型, 默认 png - quality (int, optional): 图片质量 0-100 当为`png`时无效 - viewport: (Dict[str, Any], optional): viewport 参数 - cookie: (Dict[str, Any], optional): 页面 cookie - user_agent: (str, optional): 页面 UA - device_scale_factor: 缩放比例,类型为float,值越大越清晰(真正想让图片清晰更优先请调整此选项) - Returns: - bytes: 图片 可直接发送 - """ - if not viewport: - viewport = {"width": 500, "height": 10} - - if pages and "viewport" in pages: - viewport.update(pages["viewport"]) - - if device_scale_factor: - viewport["deviceScaleFactor"] = device_scale_factor - - template_env = jinja2.Environment( # noqa: S701 - loader=jinja2.FileSystemLoader(template_path), - enable_async=True, - ) - - logger.info( - "template_name:{},template_path:{}".format(template_name, template_path) - ) - - template = template_env.get_template(template_name, template_path) - - await write_any( - html_path_ := os.path.join(template_path, "{}.html".format(uuid.uuid4())), - await template.render_async(**templates), - ) - - picture_raw = await html_to_pic( - # html=html_content, - html_path="file://{}".format(html_path_), - wait=wait, - type_=type_, - quality=quality, - viewport=viewport, - cookie=cookie, - user_agent=user_agent, - ) - - os.remove(html_path_) - - return picture_raw - - -async def text_to_pic( - text: str, - css_path: str = "", - width: int = 500, - type_: Literal["jpeg", "png"] = "png", # noqa: A002 - quality: Union[int, None] = None, - device_scale_factor: float = 2, -) -> bytes: - """多行文本转图片 - - Args: - text (str): 纯文本, 可多行 - css_path (str, optional): css文件 - width (int, optional): 图片宽度,默认为 500 - type (Literal["jpeg", "png"]): 图片类型, 默认 png - quality (int, optional): 图片质量 0-100 当为`png`时无效 - device_scale_factor: 缩放比例,类型为float,值越大越清晰(真正想让图片清晰更优先请调整此选项) - - Returns: - bytes: 图片, 可直接发送 - """ - template = env.get_template("text.html") - - return await html_to_pic( - html=await template.render_async( - text=text, - css=( - await read_any(css_path) - if css_path - else await read_template("text.css") - ), - ), - html_path=f"file://{css_path if css_path else TEMPLATES_PATH}", - viewport={ - "width": width, - "height": 10, - "deviceScaleFactor": device_scale_factor, - }, - type_=type_, - quality=quality, - ) - - -async def md_to_pic( - md: str = "", - md_path: str = "", - css_path: str = "", - width: int = 500, - type_: Literal["jpeg", "png"] = "png", # noqa: A002 - quality: Union[int, None] = None, - device_scale_factor: float = 2, -) -> bytes: - """markdown 转 图片 - - Args: - md (str, optional): markdown 格式文本 - md_path (str, optional): markdown 文件路径 - css_path (str, optional): css文件路径. Defaults to None. - width (int, optional): 图片宽度,默认为 500 - type (Literal["jpeg", "png"]): 图片类型, 默认 png - quality (int, optional): 图片质量 0-100 当为`png`时无效 - device_scale_factor: 缩放比例,类型为float,值越大越清晰(真正想让图片清晰更优先请调整此选项) - - Returns: - bytes: 图片, 可直接发送 - """ - template = env.get_template("markdown.html") - if not md: - if md_path: - md = await read_any(md_path) # type: ignore - else: - raise Exception("必须输入 md 或 md_path") - logger.debug(md) - md = markdown.markdown( - md, - extensions=[ - "pymdownx.tasklist", - "tables", - "fenced_code", - "codehilite", - "mdx_math", - "pymdownx.tilde", - ], - extension_configs={"mdx_math": {"enable_dollar_delimiter": True}}, - ) - - logger.debug(md) - extra = "" - if "math/tex" in md: - katex_css = await read_template("katex/katex.min.b64_fonts.css") - katex_js = await read_template("katex/katex.min.js") - mathtex_js = await read_template("katex/mathtex-script-type.min.js") - extra = ( - f'' - f"" - f"" - ) - - if css_path: - css = await read_any(css_path) - else: - css = await read_template("github-markdown-light.css") + await read_template( - "pygments-default.css", - ) - - return await html_to_pic( - html=await template.render_async(md=md, css=css, extra=extra), - html_path=f"file://{css_path if css_path else TEMPLATES_PATH}", - viewport={ - "width": width, - "height": 10, - "deviceScaleFactor": device_scale_factor, - }, - type_=type_, - quality=quality, - ) diff --git a/src/utils/message/html_tool.py b/src/utils/message/html_tool.py index d1d657ef..bd51852f 100644 --- a/src/utils/message/html_tool.py +++ b/src/utils/message/html_tool.py @@ -1,14 +1,14 @@ -import os.path - +import os import aiofiles # type: ignore import nonebot from nonebot import require -require("nonebot_plugin_htmlrender") +# require("nonebot_plugin_htmlrender") from nonebot_plugin_htmlrender import ( # type: ignore template_to_html, template_to_pic, + md_to_pic ) # type: ignore @@ -85,3 +85,5 @@ async def template2image( device_scale_factor=scale_factor ### ) + + diff --git a/src/utils/message/message.py b/src/utils/message/message.py index 1864a8c9..f177f0a2 100644 --- a/src/utils/message/message.py +++ b/src/utils/message/message.py @@ -7,16 +7,12 @@ import aiofiles import aiohttp import nonebot from PIL import Image -from nonebot import require -from nonebot.adapters import satori from nonebot.adapters.onebot import v11 -from src.utils.htmlrender import md_to_pic - +from .html_tool import md_to_pic from .. import load_from_yaml from ..base.ly_typing import T_Bot, T_Message, T_MessageEvent - config = load_from_yaml("config.yml")