diff --git a/README.md b/README.md index 44f6bc0..805bffa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,64 @@ # PagerMaid_Plugins -PagerMaid Telegram utility daemon plugins. + +这个 repo 用于存储 PagerMaid-Modify 的插件。 + +## 如何上传插件? + +欢迎加入 [讨论群](https://t.me/joinchat/FLV4ZFXq9nUFLLe0HDxfQQ) 探讨你的疑问。 + +> 开始编写 PagerMaid 插件前请确认 repo 没有对应功能的插件。 + +### pypi 包引入须知 + +额外不在 PagerMaid-Modify `requirements.txt` 下的包,请通过 `try` 来进行引入,在用户运行命令时判断包是否引入,如未引入则编辑消息提醒用户安装相应的 pypi 包。 + +代码参照:https://github.com/xtaodada/PagerMaid_Plugins/blob/master/sendat.py + +### 调试 + +使用 `-apt install` 回复你的插件即可进行本地安装,请在本地测试基本无报错后进行下一步。 + +### 添加文件 + +您可以使用的文件目录为: + - `/` 根目录放置 插件的 python 源文件 + - `/插件名/` 子目录放置 插件的资源文件(可选) + +### 添加插件到库 + +您需要参照 `list.json` 的相关格式,在 `list` (`object`) 下 创建一个 `list` + +下面是对应参数的介绍: + - `name` : 插件名 + - `version` : 版本号 + - `section` : 分类 + - `maintainer` : 作者 + - `size` : 插件大小 + - `supported` : 插件是否允许 issue + - `des-short` : 短介绍(用于 `-apt search`) + - `des` : 详细介绍(用于 `-apt show`) + +## Plugins 文件结构介绍 + +- 插件名 + - `*.*` : 插件对应的资源文件 +- `插件名.py` : 插件源文件 +- `version.json` : 通过 `-apt install` 命令安装的插件版本记录文件 + +## 目前已有的插件 + +- chat (聊天类) + - `autorespond` : 自动回复。 + - `dme` : 反 TG desktop 防撤回插件。 + - `autorm` : 在指定的时间后删除自己的消息。 + - `sendat` : 定时发送消息。 +- profile (资料类) + - `autochangename` : 自动更新 last_name 为时间等。 + - `throwit` : 生成一张 扔头像 图片。 +- daily (便民类) + - `weather` : 查询天气。 + - `xtao-some` : 一大堆便民功能。 + - `yb-dl` : 上传 Youtube、Bilibili 视频到 telegram。 + - `rate` : 汇率转换 + - `netease` : 网易云热评 + diff --git a/list.json b/list.json index c0ba7ef..b9ed133 100644 --- a/list.json +++ b/list.json @@ -38,7 +38,7 @@ "size": "6.7 kb", "supported": true, "des-short": "在指定的时间后删除自己的消息。", - "des": "安装后使用 -help autorm 查看更多。" + "des": "这个人很懒,什么都没有留下。" }, { "name": "sendat", @@ -48,7 +48,7 @@ "size": "13.0 kb", "supported": true, "des-short": "定时发送消息。", - "des": "安装后使用 -help sendat 查看更多。" + "des": "这个人很懒,什么都没有留下。" }, { "name": "weather", @@ -58,7 +58,7 @@ "size": "1.5 kb", "supported": true, "des-short": "查询天气。", - "des": "安装后使用 -help weather 查看更多。" + "des": "这个人很懒,什么都没有留下。" }, { "name": "xtao-some", @@ -78,7 +78,37 @@ "size": "18.8 kb", "supported": false, "des-short": "上传 Youtube、Bilibili 视频到 telegram。", - "des": "安装后使用 -help yb-dl 查看更多。" + "des": "这个人很懒,什么都没有留下。" + }, + { + "name": "throwit", + "version": "1.3", + "section": "profile", + "maintainer": "xtaodada", + "size": "6.9 kb", + "supported": true, + "des-short": "生成一张 扔头像 图片。", + "des": "支持旋转图片,随机生成丢头像的人物。" + }, + { + "name": "rate", + "version": "1.0", + "section": "daily", + "maintainer": "fruitymelon", + "size": "1.7 kb", + "supported": true, + "des-short": "汇率转换。", + "des": "这个人很懒,什么都没有留下。" + }, + { + "name": "netease", + "version": "1.0", + "section": "daily", + "maintainer": "xtaodada、KorenKrita", + "size": "0.7 kb", + "supported": true, + "des-short": "随机一条网易云音乐评论。", + "des": "这个人很懒,什么都没有留下。" } ] } \ No newline at end of file diff --git a/netease.py b/netease.py new file mode 100644 index 0000000..94ff008 --- /dev/null +++ b/netease.py @@ -0,0 +1,17 @@ +import json +from requests import get +from pagermaid import bot, log +from pagermaid.listener import listener + + +@listener(is_plugin=True, outgoing=True, command="netease", + description="随机一条网易云音乐评论。") +async def netease(context): + await context.edit("获取中 . . .") + req = get("https://api.oioweb.cn/api/wyypl.php") + if req.status_code == 200: + data = json.loads(req.text) + res = data['Comment'] + '\n\n来自 @' + data['UserName'] + ' 在鸽曲"' + data['SongName'] + '"-' + data['SongAutho'] + '下方的评论' + await context.edit(res, parse_mode='html', link_preview=True) + else: + await context.edit("出错了呜呜呜 ~ 无法访问到 API 服务器 。") \ No newline at end of file diff --git a/rate.py b/rate.py new file mode 100644 index 0000000..f77a76c --- /dev/null +++ b/rate.py @@ -0,0 +1,54 @@ +""" Pagermaid currency exchange rates plugin. Plugin by @fruitymelon """ + +import asyncio, json +from json.decoder import JSONDecodeError +import urllib.request + +from pagermaid import bot, log +from pagermaid.listener import listener + +API = "https://api.exchangeratesapi.io/latest" +currencies = [] +data = {} + +inited = False + +def init(): + with urllib.request.urlopen(API) as response: + result = response.read() + try: + global data + data = json.loads(result) + data["rates"][data["base"]] = 1.0 + for key in list(enumerate(data["rates"])): + currencies.append(key[1]) + currencies.sort() + except JSONDecodeError as e: + raise e + global inited + inited = True + +init() + +@listener(is_plugin=True, outgoing=True, command="rate", + description="Currency exchange rate plugin.", + parameters=" ") +async def rate(context): + while not inited: + await asyncio.sleep(1) + if not context.parameter: + await context.edit(f"This is the currency exchange rate plugin.\n\nUsage: `-rate `\n\nAvailable currencies: {', '.join(currencies)}") + return + if len(context.parameter) != 2: + await context.edit(f"Usage: `-rate `\n\n`{', '.join(currencies)}`") + return + FROM = context.parameter[0].upper().strip() + TO = context.parameter[1].upper().strip() + if currencies.count(FROM) == 0: + await context.edit(f"Currency type {FROM} is not supported. Choose one among `{', '.join(currencies)}` instead.") + return + if currencies.count(TO) == 0: + await context.edit(f"Currency type {TO} is not supported. Choose one among `{', '.join(currencies)}` instead.") + return + await context.edit(f'{FROM} : {TO} = 1 : {int(10000*data["rates"][FROM]/data["rates"][TO])/10000}') + diff --git a/throwit.py b/throwit.py new file mode 100644 index 0000000..a5d5fbd --- /dev/null +++ b/throwit.py @@ -0,0 +1,158 @@ +""" PagerMaid module to handle sticker collection. """ + +from PIL import Image, ImageDraw, ImageFilter +from os.path import exists +from os import remove +from requests import get +from random import randint +from telethon.tl.functions.users import GetFullUserRequest +from telethon.tl.types import MessageEntityMentionName +from struct import error as StructError +from pagermaid.listener import listener + +def crop_max_square(pil_img): + return crop_center(pil_img, min(pil_img.size), min(pil_img.size)) + +def crop_center(pil_img, crop_width, crop_height): + img_width, img_height = pil_img.size + return pil_img.crop(((img_width - crop_width) // 2, + (img_height - crop_height) // 2, + (img_width + crop_width) // 2, + (img_height + crop_height) // 2)) + +def mask_circle_transparent(pil_img, blur_radius, offset=0): + offset = blur_radius * 2 + offset + mask = Image.new("L", pil_img.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((offset, offset, pil_img.size[0] - offset, pil_img.size[1] - offset), fill=255) + mask = mask.filter(ImageFilter.GaussianBlur(blur_radius)) + + result = pil_img.copy() + result.putalpha(mask) + return result + +@listener(is_plugin=True, outgoing=True, command="diu", + description="生成一张 扔头像 图片,(可选:当第二个参数存在时,旋转用户头像 180°)", + parameters=" [随意内容]") +async def throwit(context): + if len(context.parameter) > 2: + await context.edit("出错了呜呜呜 ~ 无效的参数。") + return + diu_round = False + await context.edit("正在生成 扔头像 图片中 . . .") + if context.reply_to_msg_id: + reply_message = await context.get_reply_message() + user_id = reply_message.from_id + target_user = await context.client(GetFullUserRequest(user_id)) + if len(context.parameter) == 1: + diu_round = True + else: + if len(context.parameter) == 1 or len(context.parameter) == 2: + user = context.parameter[0] + if user.isnumeric(): + user = int(user) + else: + user_object = await context.client.get_me() + user = user_object.id + if context.message.entities is not None: + if isinstance(context.message.entities[0], MessageEntityMentionName): + return await context.client(GetFullUserRequest(context.message.entities[0].user_id)) + try: + user_object = await context.client.get_entity(user) + target_user = await context.client(GetFullUserRequest(user_object.id)) + except (TypeError, ValueError, OverflowError, StructError) as exception: + if str(exception).startswith("Cannot find any entity corresponding to"): + await context.edit("出错了呜呜呜 ~ 指定的用户不存在。") + return + if str(exception).startswith("No user has"): + await context.edit("出错了呜呜呜 ~ 指定的道纹不存在。") + return + if str(exception).startswith("Could not find the input entity for") or isinstance(exception, StructError): + await context.edit("出错了呜呜呜 ~ 无法通过此 UserID 找到对应的用户。") + return + if isinstance(exception, OverflowError): + await context.edit("出错了呜呜呜 ~ 指定的 UserID 已超出长度限制,您确定输对了?") + return + raise exception + photo = await context.client.download_profile_photo( + target_user.user.id, + "plugins/throwit/" + str(target_user.user.id) + ".jpg", + download_big=True + ) + reply_to = context.message.reply_to_msg_id + if exists("plugins/throwit/" + str(target_user.user.id) + ".jpg"): + if not exists('plugins/throwit/1.png'): + r = get('https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master/throwit/1.png') + with open("plugins/throwit/1.png", "wb") as code: + code.write(r.content) + if not exists('plugins/throwit/2.png'): + r = get('https://raw.githubusercontent.com/xtaodada/PagerMaid_Plugins/master/throwit/2.png') + with open("plugins/throwit/2.png", "wb") as code: + code.write(r.content) + # 随机数生成 + randint_r = randint(1,2) + # 将头像转为圆形 + markImg = Image.open("plugins/throwit/" + str(target_user.user.id) + ".jpg") + if randint_r == 1: + thumb_width = 136 + elif randint_r == 2: + thumb_width = 122 + im_square = crop_max_square(markImg).resize((thumb_width, thumb_width), Image.LANCZOS) + im_thumb = mask_circle_transparent(im_square, 0) + im_thumb.save("plugins/throwit/" + str(target_user.user.id) + ".png") + # 将头像复制到模板上 + if randint_r == 1: + background = Image.open("plugins/throwit/2.png") + elif randint_r == 2: + background = Image.open("plugins/throwit/2.png") + foreground = Image.open("plugins/throwit/" + str(target_user.user.id) + ".png") + if len(context.parameter) == 2: + diu_round = True + if diu_round: + foreground = foreground.rotate(180) # 对图片进行旋转 + background.paste(foreground, (19, 181), foreground) + background.save('plugins/throwit/throwout.webp') + target_file = await context.client.upload_file('plugins/throwit/throwout.webp') + try: + remove("plugins/throwit/" + str(target_user.user.id) + ".jpg") + remove("plugins/throwit/" + str(target_user.user.id) + ".png") + remove("plugins/throwit/throwout.webp") + remove(photo) + except: + pass + else: + await context.edit("此用户未设置头像或头像对您不可见。") + return + if reply_to: + try: + await context.client.send_file( + context.chat_id, + target_file, + link_preview=False, + force_document=False, + reply_to=reply_to + ) + await context.delete() + try: + remove(photo) + except: + pass + return + except TypeError: + await context.edit("此用户未设置头像或头像对您不可见。") + else: + try: + await context.client.send_file( + context.chat_id, + target_file, + link_preview=False, + force_document=False + ) + await context.delete() + try: + remove(photo) + except: + pass + return + except TypeError: + await context.edit("此用户未设置头像或头像对您不可见。") \ No newline at end of file diff --git a/throwit/1.png b/throwit/1.png new file mode 100644 index 0000000..c638e40 Binary files /dev/null and b/throwit/1.png differ diff --git a/throwit/2.png b/throwit/2.png new file mode 100644 index 0000000..d6aad34 Binary files /dev/null and b/throwit/2.png differ