├── LICENSE ├── README.md ├── nonebot_plugin_impact ├── __init__.py ├── data_sheet.py ├── draw_img.py ├── fonts │ └── SIMYOU.TTF ├── handle.py ├── txt2img.py └── utils.py ├── pyproject.toml └── requirements.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Special-Week 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nonebot_plugin_impact 2 | 3 | 让群友们眼前一黑的nonebot2淫趴插件 (牛牛比拼) 4 | 群友在群里开淫趴 5 | 6 | 7 | ### 警告!!! 在群内大量使用本插件可能被网警封群!!!请慎用!!! 8 | ### 使用插件是你自己的事情, 产生的一切后果本人概不负责 9 | 10 | 11 | 响应器如下 12 | 13 | ```python 14 | on_command("pk", aliases={"对决"}, rule=utils.rule, priority=20, block=False, handlers=[impart.pk]) 15 | on_regex("^(打胶|开导)$", priority=20, block=True, handlers=[impart.dajiao]) 16 | on_command("嗦牛子", priority=20, block=True, handlers=[impart.suo]) 17 | on_command("查询", priority=20, block=False, handlers=[impart.queryjj]) 18 | on_command("jj排行榜", aliases={"jj排名", "jj榜单", "jjrank"}, priority=20, block=True, handlers=[impart.jjrank]) 19 | on_regex(r"^(日群友|透群友|日群主|透群主|日管理|透管理)", flags=I, priority=20, block=True, handlers=[impart.yinpa]) 20 | on_regex(r"^(开始银趴|关闭银趴|开启淫趴|禁止淫趴|开启银趴|禁止银趴)", permission=SUPERUSER | GROUP_ADMIN | GROUP_OWNER, flags=I, priority=20, block=True, handlers=[impart.open_module]) 21 | on_command("注入查询", aliases={"摄入查询", "射入查询"}, priority=20, block=True, handlers=[impart.query_injection]) 22 | on_command("淫趴介绍", priority=20, block=True) 23 | ``` 24 | 25 | ### 安装方法: 26 | 27 | nb plugin install nonebot-plugin-impact (推荐: 安装即用) 28 | 29 | pip install nonebot-plugin-impact (需要自己加载插件) 30 | 31 | git clone https://github.com/Special-Week/nonebot_plugin_impact.git (需要自己安装依赖和加载插件) 32 | 33 | download zip (需要自己安装依赖和加载插件) 34 | 35 | 36 | 37 | ### 注意: 38 | 39 | 有bug火速提, 有自己的想法自己写 40 | 使用on_command的响应器, 指令需要带上自己env的COMMAND_START, 默认为"/" 41 | 42 | 43 | 44 | 指令1: 嗦牛子 (给目标牛牛增加长度, 自己或者他人, 通过艾特选择对象, 没有at时目标是自己) 45 | 46 | 指令2: 打胶 | 开导 (给自己牛牛增加长度) 47 | 48 | 指令3: pk | 对决 (普通的pk,单纯的random实现输赢, 胜利方获取败方随机数/2的牛牛长度) 49 | 50 | 指令4: 查询 (目标牛牛长度, 自己或者他人, 通过艾特选择对象, 没有at时目标是自己) 51 | 52 | 指令5: jj排行榜 | jj排名 | jj榜单 | jjrank (字面意思, 输出倒数五位和前五位, 以及自己的排名) 53 | 54 | 指令6: 开始银趴 | 关闭银趴 | 开启淫趴 | 禁止淫趴 | 开启银趴 | 禁止银趴 (由管理员 | 群主 | SUPERUSERS开启或者关闭淫趴) 55 | 56 | 指令7: 日群友 | 透群友 | 日群主 | 透群主 | 日管理 | 透管理 (字面意思, 当使用透群友的时候如果at了人那么直接指定) 57 | 58 | 指令8: 注入查询 | 摄入查询 | 射入查询 (查询目标被透注入的量,后接(历史|全部), 可查看总被摄入的量, 无艾特的时候是自己, 有at的时候是目标) 59 | 60 | 指令9: 淫趴介绍 (输出淫趴插件的命令列表) 61 | 62 | ### env配置项: 63 | | config | type | default | example | usage | 64 | | ------------ | ---- | ------- | -------------------- | ---------- | 65 | | djCDtime | int | 300 | djCDtime = 300 | 打胶的CD | 66 | | pkCDTime | int | 60 | pkCDTime = 60 | pk的CD | 67 | | suoCDTime | int | 300 | suoCDTime = 300 | 嗦牛子的CD | 68 | | fuckCDTime | int | 3600 | fuckCDTime = 3600 | 透群友的CD | 69 | | isalive | bool | False | isalive = True | 不活跃惩罚 | 70 | 71 | 以上配置项单位为秒或bool值, 大小写不敏感(反正env里面拿到dict都是小写的key) 72 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/__init__.py: -------------------------------------------------------------------------------- 1 | """插件入口""" 2 | 3 | import contextlib 4 | from re import I 5 | 6 | from nonebot import on_command, on_regex, require 7 | from nonebot.adapters.onebot.v11.permission import GROUP_ADMIN, GROUP_OWNER 8 | from nonebot.permission import SUPERUSER 9 | 10 | from .handle import impart 11 | from .utils import utils 12 | 13 | require("nonebot_plugin_apscheduler") 14 | 15 | from nonebot_plugin_apscheduler import scheduler 16 | 17 | scheduler.add_job(impart.penalties_and_resets, "cron", hour=0, misfire_grace_time=600) 18 | 19 | on_command( 20 | "pk", 21 | aliases={"对决"}, 22 | rule=utils.rule, 23 | priority=20, 24 | block=False, 25 | handlers=[impart.pk], 26 | ) 27 | 28 | on_regex("^(打胶|开导)$", priority=20, block=True, handlers=[impart.dajiao]) 29 | 30 | on_command("嗦牛子", priority=20, block=True, handlers=[impart.suo]) 31 | 32 | on_command("查询", priority=20, block=False, handlers=[impart.queryjj]) 33 | 34 | on_command( 35 | "jj排行榜", 36 | aliases={"jj排名", "jj榜单", "jjrank"}, 37 | priority=20, 38 | block=True, 39 | handlers=[impart.jjrank], 40 | ) 41 | on_regex( 42 | r"^(日群友|透群友|日群主|透群主|日管理|透管理)", 43 | flags=I, 44 | priority=20, 45 | block=True, 46 | handlers=[impart.yinpa], 47 | ) 48 | 49 | on_regex( 50 | r"^(开始银趴|关闭银趴|开启淫趴|禁止淫趴|开启银趴|禁止银趴)", 51 | permission=SUPERUSER | GROUP_ADMIN | GROUP_OWNER, 52 | flags=I, 53 | priority=20, 54 | block=True, 55 | handlers=[impart.open_module], 56 | ) 57 | 58 | on_command( 59 | "注入查询", 60 | aliases={"摄入查询", "射入查询"}, 61 | priority=20, 62 | block=True, 63 | handlers=[impart.query_injection], 64 | ) 65 | 66 | on_command("淫趴介绍", priority=20, block=True, handlers=[impart.yinpa_introduce]) 67 | 68 | with contextlib.suppress(Exception): 69 | from nonebot.plugin import PluginMetadata 70 | 71 | __plugin_meta__ = PluginMetadata( 72 | name="impact", 73 | description="让群友们眼前一黑的nonebot2淫趴插件", 74 | usage=utils.usage, 75 | type="application", 76 | homepage="https://github.com/Special-Week/nonebot_plugin_impact", 77 | supported_adapters={"~onebot.v11"}, 78 | extra={ 79 | "author": "Special-Week", 80 | "version": "0.12.114514", 81 | "priority": 20, 82 | }, 83 | ) 84 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/data_sheet.py: -------------------------------------------------------------------------------- 1 | """数据库操作模块, 用于存储用户的jj长度, 以及群聊是否允许银趴, 以及每天的被注入量""" 2 | import os 3 | import random 4 | import time 5 | from typing import Dict, List 6 | 7 | from sqlalchemy import ( 8 | Boolean, 9 | Column, 10 | Engine, 11 | Float, 12 | Integer, 13 | String, 14 | create_engine, 15 | orm, 16 | ) 17 | from sqlalchemy.orm import sessionmaker 18 | 19 | # 数据库路径 20 | DATA_PATH = "data/impact" 21 | 22 | # 不存在则创建文件夹 23 | if not os.path.exists("data"): 24 | os.mkdir("data") 25 | if not os.path.exists(DATA_PATH): 26 | os.mkdir(DATA_PATH) 27 | 28 | 29 | engine: Engine = create_engine(f"sqlite:///{DATA_PATH}/impact.db") 30 | session = sessionmaker(engine) 31 | Base = orm.declarative_base() 32 | 33 | 34 | class UserData(Base): 35 | """用户数据表""" 36 | 37 | __tablename__: str = "userdata" 38 | 39 | userid = Column(Integer, primary_key=True, index=True) 40 | jj_length = Column(Float, nullable=False) 41 | last_masturbation_time = Column(Integer, nullable=False, default=0) 42 | 43 | 44 | class GroupData(Base): 45 | """群数据表""" 46 | 47 | __tablename__: str = "groupdata" 48 | 49 | groupid = Column(Integer, primary_key=True, index=True) 50 | allow = Column(Boolean, nullable=False) 51 | 52 | 53 | class EjaculationData(Base): 54 | """被注入数据表""" 55 | 56 | __tablename__: str = "ejaculation_data" 57 | 58 | id = Column(Integer, primary_key=True) 59 | userid = Column(Integer, nullable=False, index=True) 60 | date = Column(String(20), nullable=False) 61 | volume = Column(Float, nullable=False) 62 | 63 | 64 | Base.metadata.create_all(engine) 65 | 66 | 67 | def is_in_table(userid: int) -> bool: 68 | """传入一个userid,判断是否在表中""" 69 | with session() as s: 70 | return bool(s.query(UserData).filter(UserData.userid == userid).first()) 71 | 72 | 73 | def add_new_user(userid: int) -> None: 74 | """插入一个新用户, 默认长度是10.0""" 75 | with session() as s: 76 | s.add( 77 | UserData( 78 | userid=userid, jj_length=10.0, last_masturbation_time=int(time.time()) 79 | ) 80 | ) 81 | s.commit() 82 | 83 | 84 | def update_activity(userid: int) -> None: 85 | """更新用户活跃时间""" 86 | # 如果用户不在表中, 则插入一条记录 87 | if not is_in_table(userid): 88 | add_new_user(userid) 89 | with session() as s: 90 | s.query(UserData).filter(UserData.userid == userid).update( 91 | {UserData.last_masturbation_time: int(time.time())} 92 | ) 93 | s.commit() 94 | 95 | 96 | def get_jj_length(userid: int) -> float: 97 | """传入用户id, 返还数据库中对应的jj长度""" 98 | with session() as s: 99 | return s.query(UserData).filter(UserData.userid == userid).first().jj_length # type: ignore 100 | 101 | 102 | def set_jj_length(userid: int, length: float) -> None: 103 | """传入一个用户id以及需要增加的长度, 在数据库内累加, 用这个函数前一定要先判断用户是否在表中""" 104 | with session() as s: 105 | # 先获取当前的长度, 然后再累加 106 | current_length = ( 107 | s.query(UserData).filter(UserData.userid == userid).first().jj_length 108 | ) # type: ignore 109 | s.query(UserData).filter(UserData.userid == userid).update( 110 | { 111 | UserData.jj_length: round(current_length + length, 3), 112 | UserData.last_masturbation_time: int(time.time()), 113 | } 114 | ) 115 | s.commit() 116 | 117 | 118 | def check_group_allow(groupid: int) -> bool: 119 | """检查群是否允许, 传入群号, 类型是int""" 120 | with session() as s: 121 | if s.query(GroupData).filter(GroupData.groupid == groupid).first(): 122 | return s.query(GroupData).filter(GroupData.groupid == groupid).first().allow # type: ignore 123 | else: 124 | return False 125 | 126 | 127 | def set_group_allow(groupid: int, allow: bool) -> None: 128 | """设置群聊开启或者禁止银趴, 传入群号, 类型是int, 以及allow, 类型是bool""" 129 | with session() as s: 130 | # 如果群号不存在, 则插入一条记录, 默认是禁止 131 | if not s.query(GroupData).filter(GroupData.groupid == groupid).first(): 132 | s.add(GroupData(groupid=groupid, allow=False)) 133 | # 然后再根据传入的allow来更新 134 | s.query(GroupData).filter(GroupData.groupid == groupid).update( 135 | {GroupData.allow: allow} 136 | ) 137 | s.commit() 138 | 139 | 140 | def get_today() -> str: 141 | """获取当前年月日格式: 2023-02-04""" 142 | return time.strftime("%Y-%m-%d", time.localtime()) 143 | 144 | 145 | def insert_ejaculation(userid: int, volume: float) -> None: 146 | """插入一条注入的记录""" 147 | now_date = get_today() 148 | with session() as s: 149 | # 如果没有这个用户的记录, 则插入一条 150 | if ( 151 | not s.query(EjaculationData) 152 | .filter(EjaculationData.userid == userid) 153 | .first() 154 | ): 155 | s.add(EjaculationData(userid=userid, date=now_date, volume=volume)) 156 | # 如果有这个用户以及这一天的记录, 则累加 157 | elif ( 158 | s.query(EjaculationData) 159 | .filter(EjaculationData.userid == userid, EjaculationData.date == now_date) 160 | .first() 161 | ): 162 | # 当前的值 163 | current_volume = ( 164 | s.query(EjaculationData) 165 | .filter( 166 | EjaculationData.userid == userid, EjaculationData.date == now_date 167 | ) 168 | .first() 169 | .volume 170 | ) # type: ignore 171 | s.query(EjaculationData).filter( 172 | EjaculationData.userid == userid, EjaculationData.date == now_date 173 | ).update({EjaculationData.volume: round(current_volume + volume, 3)}) 174 | # 如果有这个用户但是没有这一天的记录, 则插入一条 175 | else: 176 | s.add(EjaculationData(userid=userid, date=now_date, volume=volume)) 177 | s.commit() 178 | 179 | 180 | def get_ejaculation_data(userid: int) -> List[Dict]: 181 | """获取一个用户的所有注入记录""" 182 | with session() as s: 183 | return [ 184 | {"date": i.date, "volume": i.volume} 185 | for i in s.query(EjaculationData).filter(EjaculationData.userid == userid) 186 | ] 187 | 188 | 189 | def get_today_ejaculation_data(userid: int) -> float: 190 | """获取用户当日的注入量""" 191 | with session() as s: 192 | # 如果找得到这个用户的id以及今天的日期, 则返回注入量 193 | if ( 194 | s.query(EjaculationData) 195 | .filter( 196 | EjaculationData.userid == userid, EjaculationData.date == get_today() 197 | ) 198 | .first() 199 | ): 200 | return ( 201 | s.query(EjaculationData) 202 | .filter( 203 | EjaculationData.userid == userid, 204 | EjaculationData.date == get_today(), 205 | ) 206 | .first() 207 | .volume 208 | ) # type: ignore 209 | # 否则返回0 210 | else: 211 | return 0.0 212 | 213 | 214 | def punish_all_inactive_users() -> None: 215 | """所有不活跃的用户, 即上次打胶时间超过一天的用户, 所有jj_length大于1将受到减少0--1随机的惩罚""" 216 | with session() as s: 217 | for i in s.query(UserData).all(): 218 | if time.time() - i.last_masturbation_time > 86400 and i.jj_length > 1: 219 | i.jj_length = round(i.jj_length - random.random(), 3) 220 | s.commit() 221 | 222 | 223 | def get_sorted() -> List[Dict]: 224 | """获取所有用户的jj长度, 并且按照从大到小排序""" 225 | with session() as s: 226 | return [ 227 | {"userid": i.userid, "jj_length": i.jj_length} 228 | for i in s.query(UserData).order_by(UserData.jj_length.desc()) 229 | ] 230 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/draw_img.py: -------------------------------------------------------------------------------- 1 | """绘画图表模块""" 2 | import random 3 | from io import BytesIO 4 | from pathlib import Path 5 | from typing import Dict, List, Tuple 6 | 7 | from PIL import Image, ImageDraw, ImageFilter, ImageFont 8 | 9 | 10 | class DrawBarChart: 11 | def __init__(self) -> None: 12 | self.colors: List[Tuple[int, int, int]] = [ 13 | (31, 119, 180), # 蓝色 14 | (44, 160, 44), # 绿色 15 | (214, 39, 40), # 红色 16 | (255, 127, 14), # 橙色 17 | (148, 103, 189), # 紫色 18 | (23, 190, 207), # 青色 19 | (188, 189, 34), # 黄色 20 | (227, 119, 194), # 粉色 21 | (255, 99, 71), # 红橙色 22 | (255, 215, 0), # 金色 23 | ] 24 | self.module_path: Path = Path(__file__).parent 25 | self.font = str(self.module_path / "fonts" / "SIMYOU.TTF") 26 | 27 | async def draw_bar_chart(self, data: Dict[str, float]) -> bytes: 28 | """画柱状图, 传入一个字典, key是str类型的用户名字, value是对应用户的注入量""" 29 | values = list(data.values()) 30 | keys = list(data.keys()) 31 | image = Image.new("RGBA", (1920, 1080), (255, 255, 255, 255)) 32 | draw = ImageDraw.Draw(image) 33 | 34 | # ------------------------ 画一个框框 ------------------------ 35 | 36 | image_new = Image.new("RGBA", (1920, 1080), (255, 255, 255, 0)) 37 | draw_new = ImageDraw.Draw(image_new) 38 | draw_new.rectangle((420, 25, 1860, 1060), fill=(255, 255, 255, 220)) 39 | image_new = image_new.filter(ImageFilter.GaussianBlur(radius=0.1)) 40 | image.paste(image_new, (0, 0), image_new) 41 | 42 | # ------------------------ 画一个坐标轴 ------------------------ 43 | 44 | draw.line((490, 770, 1800, 770), fill="black", width=2) 45 | draw.line((500, 50, 500, 1030), fill="black", width=2) 46 | maxnum_scale = abs(max(values) / 9) 47 | minnum_scale = abs(min(values) / 3) 48 | 49 | # ------------------------ 画一些虚线 ------------------------ 50 | 51 | def draw_dotted_line(y): 52 | x_start, x_end = 500, 1800 53 | dash_length = 10 54 | gap_length = 5 55 | x = x_start 56 | while x < x_end: 57 | x_dash_end = min(x + dash_length, x_end) 58 | draw.line((x, y, x_dash_end, y), fill="black", width=1) 59 | x += dash_length + gap_length 60 | 61 | for i in range(10): 62 | draw_dotted_line(770 - 780 * i / 10) 63 | draw_dotted_line(770 + 780 * i / 10) 64 | maxnum_scale = int(maxnum_scale / 50 + 1) * 50 65 | minnum_scale = int(minnum_scale / 50 + 1) * 50 66 | 67 | if maxnum_scale == 0: 68 | maxnum_scale = 50 69 | if minnum_scale == 0: 70 | minnum_scale = 50 71 | for i in range(10): 72 | draw.text( 73 | (450, 770 - 780 * i / 10 - 10), 74 | str(maxnum_scale * i), 75 | fill="black", 76 | font=ImageFont.truetype(self.font, 20), 77 | ) 78 | for i in range(1, 4): 79 | draw.text( 80 | (450, 770 + 780 * i / 10 - 10), 81 | f"-{str(minnum_scale * i)}", 82 | fill="black", 83 | font=ImageFont.truetype(self.font, 20), 84 | ) 85 | 86 | # ------------------------ 画柱状图 ------------------------ 87 | 88 | columns = Image.new("RGBA", (1920, 1080), (255, 255, 255, 0)) 89 | draw = ImageDraw.Draw(columns) 90 | random.shuffle(self.colors) 91 | for i, value in enumerate(values): 92 | color = self.colors[i] 93 | draw.rectangle((50, 50 + 70 * i, 100, 100 + 70 * i), fill=color + (250,)) 94 | draw.text( 95 | (120, 60 + 70 * i), 96 | keys[i] if len(keys[i]) < 8 else f"{keys[i][:8]}...", 97 | fill="black", 98 | font=ImageFont.truetype(self.font, 28), 99 | ) 100 | if value > 0: 101 | draw.rectangle( 102 | ( 103 | 540 + 120 * i, 104 | 770 - 78 * (value / maxnum_scale), 105 | 635 + 120 * i, 106 | 770, 107 | ), 108 | fill=color + (200,), 109 | ) 110 | draw.text( 111 | (540 + 120 * i, 770 - 78 * (value / maxnum_scale) - 30), 112 | str(value), 113 | fill="black", 114 | font=ImageFont.truetype(self.font, 26), 115 | ) 116 | else: 117 | draw.rectangle( 118 | ( 119 | 540 + 120 * i, 120 | 770, 121 | 635 + 120 * i, 122 | 770 - 78 * (value / minnum_scale), 123 | ), 124 | fill=color + (200,), 125 | ) 126 | draw.text( 127 | (540 + 120 * i, 770 - 78 * (value / minnum_scale) + 10), 128 | str(value), 129 | fill="black", 130 | font=ImageFont.truetype(self.font, 26), 131 | ) 132 | 133 | # ------------------------ 粘贴上来 ------------------------ 134 | image.paste(columns, (0, 0), columns) 135 | img_byte = BytesIO() 136 | image.save(img_byte, format="PNG") 137 | return img_byte.getvalue() 138 | 139 | 140 | async def draw_line_chart(self, data: Dict[str, float]): 141 | """画折线图, 传入一个字典, key是str类型的用户名字, value是对应用户的注入量""" 142 | values = list(data.values()) 143 | keys = list(data.keys()) 144 | image = Image.new("RGBA", (1920, 1080), (255, 255, 255, 255)) 145 | draw = ImageDraw.Draw(image) 146 | 147 | # ------------------------ 画一个框框 ------------------------ 148 | 149 | image_new = Image.new("RGBA", (1920, 1080), (255, 255, 255, 0)) 150 | draw_new = ImageDraw.Draw(image_new) 151 | draw_new.rectangle((420, 25, 1860, 1060), fill=(255, 255, 255, 220)) 152 | image_new = image_new.filter(ImageFilter.GaussianBlur(radius=0.1)) 153 | image.paste(image_new, (0, 0), image_new) 154 | 155 | # ------------------------ 画一个坐标轴 ------------------------ 156 | 157 | draw.line((490, 1000, 1800, 1000), fill="black", width=2) 158 | draw.line((500, 50, 500, 1030), fill="black", width=2) 159 | maxnum_scale = max(values) / 9 160 | 161 | # ------------------------ 画一些虚线 ------------------------ 162 | 163 | def draw_dotted_line(y): 164 | x_start, x_end = 500, 1800 165 | dash_length = 10 166 | gap_length = 5 167 | x = x_start 168 | while x < x_end: 169 | x_dash_end = min(x + dash_length, x_end) 170 | draw.line((x, y, x_dash_end, y), fill="black", width=1) 171 | x += dash_length + gap_length 172 | 173 | for i in range(10): 174 | draw_dotted_line(1000 - 950 * i / 10) 175 | maxnum_scale = int((maxnum_scale / 20 + 1) * 20) 176 | if maxnum_scale == 0: 177 | maxnum_scale = 20 178 | 179 | for i in range(10): 180 | draw.text( 181 | (450, 1000 - 950 * i / 10 - 10), 182 | str(maxnum_scale * i), 183 | fill="black", 184 | font=ImageFont.truetype(self.font, 20), 185 | ) 186 | 187 | # ------------------------ 画折线图 ------------------------ 188 | 189 | def draw_line_chart(): 190 | x_start = 540 191 | x_gap = (1800 - x_start) / (len(values) - 1) 192 | x = x_start 193 | y = 1000 - 95 * (values[0] / maxnum_scale) 194 | draw.ellipse((x - 5, y - 5, x + 5, y + 5), fill="black", width=2) 195 | draw.text( 196 | (x - 20, y - 30), 197 | str(values[0]), 198 | fill="black", 199 | font=ImageFont.truetype(self.font, 32), 200 | ) 201 | for i in range(1, len(values)): 202 | x_new = x_start + x_gap * i 203 | y_new = 1000 - 95 * (values[i] / maxnum_scale) 204 | draw.line((x, y, x_new, y_new), fill="black", width=2) 205 | x, y = x_new, y_new 206 | draw.ellipse((x - 5, y - 5, x + 5, y + 5), fill="black", width=2) 207 | draw.text( 208 | (x - 20, y - 30), 209 | str(values[i]), 210 | fill="black", 211 | font=ImageFont.truetype(self.font, 26), 212 | ) 213 | 214 | draw_line_chart() 215 | if len(values) > 18: 216 | keys = keys[:9] + keys[-9:] 217 | values = values[:9] + values[-9:] 218 | position = 0 219 | for i in range(len(values)): 220 | position += 50 221 | if i == 9: 222 | draw.text( 223 | (50, position), 224 | ".........\n.........", 225 | fill="black", 226 | font=ImageFont.truetype(self.font, 34), 227 | ) 228 | position += 100 229 | draw.text( 230 | (50, position), 231 | f"{keys[i]} {values[i]}ml", 232 | fill="black", 233 | font=ImageFont.truetype(self.font, 34), 234 | ) 235 | bytes_io = BytesIO() 236 | image.save(bytes_io, format="PNG") 237 | return bytes_io.getvalue() 238 | 239 | 240 | draw_bar_chart = DrawBarChart() 241 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/fonts/SIMYOU.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Special-Week/nonebot_plugin_impact/48ca0db0760772465ed607863871564885ce5f8b/nonebot_plugin_impact/fonts/SIMYOU.TTF -------------------------------------------------------------------------------- /nonebot_plugin_impact/handle.py: -------------------------------------------------------------------------------- 1 | """matcher的handle模块""" 2 | 3 | import asyncio 4 | import random 5 | import time 6 | from random import choice 7 | from typing import Dict, List, Tuple 8 | 9 | from nonebot import get_driver 10 | from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment 11 | from nonebot.matcher import Matcher 12 | from nonebot.params import CommandArg, RegexGroup 13 | 14 | from .data_sheet import ( 15 | add_new_user, 16 | check_group_allow, 17 | get_ejaculation_data, 18 | get_jj_length, 19 | get_sorted, 20 | get_today_ejaculation_data, 21 | insert_ejaculation, 22 | is_in_table, 23 | punish_all_inactive_users, 24 | set_group_allow, 25 | set_jj_length, 26 | update_activity, 27 | ) 28 | from .draw_img import draw_bar_chart 29 | from .utils import utils 30 | 31 | 32 | class Impart: 33 | penalties_impact: bool = getattr( 34 | get_driver().config, "isalive", False 35 | ) # 重置每日活跃度 36 | 37 | @staticmethod 38 | def penalties_and_resets() -> None: 39 | """重置每日活跃度""" 40 | if Impart.penalties_impact: 41 | punish_all_inactive_users() 42 | 43 | @staticmethod 44 | async def pk(matcher: Matcher, event: GroupMessageEvent) -> None: 45 | """pk的响应器""" 46 | if not check_group_allow(event.group_id): 47 | await matcher.finish(utils.not_allow, at_sender=True) 48 | uid: str = event.get_user_id() 49 | allow: bool = await utils.pkcd_check(uid) # CD是否允许pk 50 | if not allow: # 如果不允许pk, 则返回 51 | await matcher.finish( 52 | f"你已经pk不动了喵, 请等待{round(utils.pk_cd_time-(time.time() - utils.pk_cd_data[uid]),3)}秒后再pk喵", 53 | at_sender=True, 54 | ) 55 | utils.pk_cd_data.update({uid: time.time()}) # 更新CD时间 56 | at = await utils.get_at(event) # 获取at的id, 类型为str 57 | if at == uid: # 如果at的id和uid相同, 则返回 58 | await matcher.finish("你不能pk自己喵", at_sender=True) 59 | # rule规定了必须有at, 所以不用判断at是否为寄 60 | if is_in_table(userid=int(uid)) and is_in_table( 61 | int(at) 62 | ): # 如果两个都在userdata里面 63 | random_num = random.random() # 生成一个随机数 64 | # 如果random_num大于0.5, 则胜利, 否则失败 65 | if random_num > 0.5: 66 | random_num: float = utils.get_random_num() # 重新生成一个随机数 67 | set_jj_length(int(uid), random_num / 2) 68 | set_jj_length(int(at), -random_num) 69 | await matcher.finish( 70 | f"对决胜利喵, 你的{choice(utils.jj_variable)}增加了{round(random_num/2,3)}cm喵, 对面则在你的阴影笼罩下减小了{random_num}cm喵", 71 | at_sender=True, 72 | ) 73 | else: 74 | random_num: float = utils.get_random_num() # 重新生成一个随机数 75 | set_jj_length(int(uid), -random_num) 76 | set_jj_length(int(at), random_num / 2) 77 | await matcher.finish( 78 | f"对决失败喵, 在对面牛子的阴影笼罩下你的{choice(utils.jj_variable)}减小了{random_num}cm喵, 对面增加了{round(random_num/2,3)}cm喵", 79 | at_sender=True, 80 | ) 81 | else: 82 | # 谁不在userdata里面, 就创建谁 83 | if is_in_table(userid=int(uid)): 84 | add_new_user(int(at)) 85 | if is_in_table(userid=int(at)): 86 | add_new_user(int(uid)) 87 | del utils.pk_cd_data[uid] # 删除CD时间 88 | await matcher.finish( 89 | f"你或对面还没有创建{choice(utils.jj_variable)}喵, 咱全帮你创建了喵, 你们的{choice(utils.jj_variable)}长度都是10cm喵", 90 | at_sender=True, 91 | ) 92 | 93 | @staticmethod 94 | async def dajiao(matcher: Matcher, event: GroupMessageEvent) -> None: 95 | """打胶的响应器""" 96 | if not check_group_allow(event.group_id): 97 | await matcher.finish(utils.not_allow, at_sender=True) 98 | uid: str = event.get_user_id() 99 | allow = await utils.cd_check(uid) # CD是否允许打胶 100 | if not allow: # 如果不允许打胶, 则返回 101 | await matcher.finish( 102 | f"你已经打不动了喵, 请等待{round(utils.dj_cd_time-(time.time() - utils.cd_data[uid]),3)}秒后再打喵", 103 | at_sender=True, 104 | ) 105 | utils.cd_data.update({uid: time.time()}) # 更新CD时间 106 | if is_in_table(userid=int(uid)): # 如果在userdata里面 107 | random_num = utils.get_random_num() # 生成一个随机数 108 | set_jj_length(int(uid), random_num) # 更新userdata 109 | await matcher.finish( 110 | f"打胶结束喵, 你的{choice(utils.jj_variable)}很满意喵, 长了{random_num}cm喵, 目前长度为{get_jj_length(int(uid))}cm喵", 111 | at_sender=True, 112 | ) 113 | else: 114 | add_new_user(int(uid)) # 创建用户 115 | await matcher.finish( 116 | f"你还没有创建{choice(utils.jj_variable)}, 咱帮你创建了喵, 目前长度是10cm喵", 117 | at_sender=True, 118 | ) 119 | 120 | @staticmethod 121 | async def suo(matcher: Matcher, event: GroupMessageEvent) -> None: 122 | """嗦牛子的响应器""" 123 | if not check_group_allow(event.group_id): 124 | await matcher.finish(utils.not_allow, at_sender=True) 125 | uid: str = event.get_user_id() 126 | allow = await utils.suo_cd_check(uid) # CD是否允许嗦 127 | if not allow: # 如果不允许嗦, 则返回 128 | await matcher.finish( 129 | f"你已经嗦不动了喵, 请等待{round(utils.suo_cd_time-(time.time() - utils.suo_cd_data[uid]),3)}秒后再嗦喵", 130 | at_sender=True, 131 | ) 132 | utils.suo_cd_data.update({uid: time.time()}) # 更新CD时间 133 | at: str = await utils.get_at(event) # 获取at的用户id, 类型为str 134 | if at == "寄": # 如果没有at 135 | if is_in_table(userid=int(uid)): # 如果在userdata里面 136 | random_num = utils.get_random_num() # 生成一个随机数 137 | set_jj_length(int(uid), random_num) 138 | await matcher.finish( 139 | f"你的{choice(utils.jj_variable)}很满意喵, 嗦长了{random_num}cm喵, 目前长度为{get_jj_length(int(uid))}cm喵", 140 | at_sender=True, 141 | ) 142 | else: # 如果不在userdata里面 143 | add_new_user(int(uid)) # 创建用户 144 | del utils.suo_cd_data[uid] # 删除CD时间 145 | await matcher.finish( 146 | f"你还没有创建{choice(utils.jj_variable)}喵, 咱帮你创建了喵, 目前长度是10cm喵", 147 | at_sender=True, 148 | ) 149 | elif is_in_table(userid=int(at)): # 如果在userdata里面 150 | random_num = utils.get_random_num() # 生成一个随机数 151 | # 更新userdata 152 | set_jj_length(int(at), random_num) 153 | await matcher.finish( 154 | f"对方的{choice(utils.jj_variable)}很满意喵, 嗦长了{random_num}cm喵, 目前长度为{get_jj_length(int(at))}cm喵", 155 | at_sender=True, 156 | ) 157 | else: 158 | add_new_user(int(at)) # 创建用户 159 | del utils.suo_cd_data[uid] # 删除CD时间 160 | await matcher.finish( 161 | f"TA还没有创建{choice(utils.jj_variable)}喵, 咱帮TA创建了喵, 目前长度是10cm喵", 162 | at_sender=True, 163 | ) 164 | 165 | @staticmethod 166 | async def queryjj(matcher: Matcher, event: GroupMessageEvent) -> None: 167 | """查询某人jj的响应器""" 168 | if not check_group_allow(event.group_id): 169 | await matcher.finish(utils.not_allow, at_sender=True) 170 | uid: str = event.get_user_id() # 获取用户id, 类型为str 171 | at: str = await utils.get_at(event) # 获取at的用户id, 类型为str 172 | if at == "寄": # 如果没有at 173 | if is_in_table(userid=int(uid)): # 如果在userdata里面 174 | await matcher.finish( 175 | f"你的{choice(utils.jj_variable)}目前长度为{get_jj_length(int(uid))}cm喵", 176 | at_sender=True, 177 | ) 178 | else: 179 | add_new_user(int(uid)) # 创建用户 180 | await matcher.finish( 181 | f"你还没有创建{choice(utils.jj_variable)}喵, 咱帮你创建了喵, 目前长度是10cm喵", 182 | at_sender=True, 183 | ) 184 | elif is_in_table(userid=int(at)): # 如果在userdata里面 185 | await matcher.finish( 186 | f"TA的{choice(utils.jj_variable)}目前长度为{get_jj_length(int(at))}cm喵", 187 | at_sender=True, 188 | ) 189 | else: 190 | add_new_user(int(at)) # 创建用户 191 | await matcher.finish( 192 | f"TA还没有创建{choice(utils.jj_variable)}喵, 咱帮他创建了喵, 目前长度是10cm喵", 193 | at_sender=True, 194 | ) 195 | 196 | @staticmethod 197 | async def jjrank(bot: Bot, matcher: Matcher, event: GroupMessageEvent) -> None: 198 | """输出前五后五和自己的排名""" 199 | if not check_group_allow(event.group_id): 200 | await matcher.finish(utils.not_allow, at_sender=True) 201 | uid: int = event.user_id 202 | rankdata: List[Dict] = get_sorted() 203 | if len(rankdata) < 5: 204 | await matcher.finish("目前记录的数据量小于5, 无法显示rank喵") 205 | top5: List = rankdata[:5] # 取前5 206 | last5: List = rankdata[-5:] # 取后5 207 | # 获取自己的排名 208 | index: List = [i for i in range(len(rankdata)) if rankdata[i]["userid"] == uid] 209 | if not index: # 如果用户没有创建JJ 210 | add_new_user(uid) 211 | await matcher.finish( 212 | f"你还没有创建{choice(utils.jj_variable)}看不到rank喵, 咱帮你创建了喵, 目前长度是10cm喵", 213 | at_sender=True, 214 | ) 215 | # top5和end5的信息,然后获取其网名 216 | top5names = await asyncio.gather( 217 | *[utils.get_stranger_info(bot, name["userid"]) for name in top5] 218 | ) 219 | last5names = await asyncio.gather( 220 | *[utils.get_stranger_info(bot, name["userid"]) for name in last5] 221 | ) 222 | 223 | data = {top5names[i]: top5[i]["jj_length"] for i in range(len(top5))} 224 | for i in range(len(last5)): 225 | data[last5names[i]] = last5[i]["jj_length"] 226 | img_bytes = await draw_bar_chart.draw_bar_chart(data) 227 | reply2 = f"你的排名为{index[0]+1}喵" 228 | await matcher.finish(MessageSegment.image(img_bytes) + reply2, at_sender=True) 229 | 230 | @staticmethod 231 | async def yinpa_prehandle( 232 | bot: Bot, 233 | args: Tuple, 234 | matcher: Matcher, 235 | event: GroupMessageEvent, 236 | ) -> Tuple[int, str, str, list]: 237 | """透群员的预处理环节""" 238 | gid, uid = event.group_id, event.user_id 239 | if not check_group_allow(event.group_id): 240 | await matcher.finish(utils.not_allow, at_sender=True) 241 | allow = await utils.fuck_cd_check(event) # CD检查是否允许 242 | if not allow: 243 | await matcher.finish( 244 | f"你已经榨不出来任何东西了, 请先休息{round(utils.fuck_cd_time-(time.time() - utils.ejaculation_cd[str(uid)]),3)}秒", 245 | at_sender=True, 246 | ) 247 | utils.ejaculation_cd.update({str(uid): time.time()}) # 记录时间 248 | req_user_card: str = str(event.sender.card or event.sender.nickname) 249 | prep_list = await bot.get_group_member_list(group_id=gid) 250 | return uid, req_user_card, args[0], prep_list 251 | 252 | @staticmethod 253 | async def yinpa_member_handle( 254 | prep_list: list, 255 | req_user_card: str, 256 | matcher: Matcher, 257 | event: GroupMessageEvent, 258 | ) -> str: 259 | prep_list = [prep.get("user_id", 114514) for prep in prep_list] # 群友列表 260 | target = await utils.get_at(event) # 获取消息有没有at 261 | if target == "寄": # 没有的话 262 | # 随机抽取幸运成员 263 | prep_list.remove(event.user_id) 264 | lucky_user = choice(prep_list) 265 | await matcher.send( 266 | f"现在咱将随机抽取一位幸运群友\n送给{req_user_card}色色!" 267 | ) 268 | else: # 有的话lucky user就是at的人 269 | lucky_user = target 270 | return lucky_user 271 | 272 | @staticmethod 273 | async def yinpa_owner_handle( 274 | uid: int, 275 | prep_list: list, 276 | req_user_card: str, 277 | matcher: Matcher, 278 | ) -> str: 279 | lucky_user: str = next( 280 | ((prep["user_id"]) for prep in prep_list if prep["role"] == "owner"), 281 | str(uid), 282 | ) 283 | if int(lucky_user) == uid: # 如果群主是自己 284 | del utils.ejaculation_cd[str(uid)] 285 | await matcher.finish("你透你自己?") 286 | await matcher.send(f"现在咱将把群主\n送给{req_user_card}色色!") 287 | return lucky_user 288 | 289 | @staticmethod 290 | async def yinpa_admin_handle( 291 | uid: int, 292 | prep_list: list, 293 | req_user_card: str, 294 | matcher: Matcher, 295 | ) -> str: 296 | admin_id: list = [ 297 | prep["user_id"] for prep in prep_list if prep["role"] == "admin" 298 | ] 299 | if uid in admin_id: # 如果自己是管理的话, 移除自己 300 | admin_id.remove(uid) 301 | if not admin_id: # 如果没有管理的话, del cd信息, 然后finish 302 | del utils.ejaculation_cd[str(uid)] 303 | await matcher.finish("喵喵喵? 找不到群管理!") 304 | lucky_user: str = choice(admin_id) # random抽取一个管理 305 | await matcher.send(f"现在咱将随机抽取一位幸运管理\n送给{req_user_card}色色!") 306 | return lucky_user 307 | 308 | async def yinpa_identity_handle( 309 | self, 310 | command: str, 311 | prep_list: list, 312 | req_user_card: str, 313 | matcher: Matcher, 314 | event: GroupMessageEvent, 315 | ) -> str: 316 | uid: int = event.user_id 317 | if "群主" in command: # 如果发送的命令里面含有群主, 说明在透群主 318 | return await self.yinpa_owner_handle(uid, prep_list, req_user_card, matcher) 319 | elif "管理" in command: # 如果发送的命令里面含有管理, 说明在透管理 320 | return await self.yinpa_admin_handle(uid, prep_list, req_user_card, matcher) 321 | else: # 最后是群员 322 | return await self.yinpa_member_handle( 323 | prep_list, req_user_card, matcher, event 324 | ) 325 | 326 | async def yinpa( 327 | self, 328 | bot: Bot, 329 | matcher: Matcher, 330 | event: GroupMessageEvent, 331 | args: Tuple = RegexGroup(), 332 | ) -> None: 333 | if not check_group_allow(event.group_id): 334 | await matcher.finish(utils.not_allow, at_sender=True) 335 | uid, req_user_card, command, prep_list = await self.yinpa_prehandle( 336 | matcher=matcher, bot=bot, args=args, event=event 337 | ) 338 | lucky_user: str = await self.yinpa_identity_handle( 339 | command=command, 340 | prep_list=prep_list, 341 | req_user_card=req_user_card, 342 | matcher=matcher, 343 | event=event, 344 | ) 345 | lucky_user_card = next( 346 | ( 347 | prep["card"] or prep["nickname"] 348 | for prep in prep_list 349 | if prep["user_id"] == int(lucky_user) 350 | ), 351 | "群友", 352 | ) 353 | # 1--100的随机数, 保留三位 354 | ejaculation = round(random.uniform(1, 100), 3) 355 | insert_ejaculation(int(lucky_user), ejaculation) 356 | await asyncio.sleep(2) # 休眠2秒, 更有效果 357 | update_activity(int(lucky_user)) # 更新活跃度 358 | update_activity(uid) # 更新活跃度 359 | # 准备调用api, 用来获取头像 360 | repo_1 = f"好欸!{req_user_card}({uid})用时{random.randint(1, 20)}秒 \n给 {lucky_user_card}({lucky_user}) 注入了{ejaculation}毫升的脱氧核糖核酸, 当日总注入量为:{get_today_ejaculation_data(int(lucky_user))}毫升\n" 361 | await matcher.send( 362 | repo_1 363 | + MessageSegment.image(f"https://q1.qlogo.cn/g?b=qq&nk={lucky_user}&s=640") 364 | ) # 结束 365 | 366 | @staticmethod 367 | async def open_module( 368 | matcher: Matcher, event: GroupMessageEvent, args: Tuple = RegexGroup() 369 | ) -> None: 370 | """开关""" 371 | gid: int = event.group_id 372 | command: str = args[0] 373 | if "开启" in command or "开始" in command: 374 | set_group_allow(gid, True) 375 | await matcher.finish("功能已开启喵") 376 | elif "禁止" in command or "关闭" in command: 377 | set_group_allow(gid, False) 378 | await matcher.finish("功能已禁用喵") 379 | 380 | @staticmethod 381 | async def query_injection( 382 | matcher: Matcher, event: GroupMessageEvent, args: Message = CommandArg() 383 | ) -> None: 384 | """查询某人的注入量""" 385 | if not check_group_allow(event.group_id): 386 | await matcher.finish(utils.not_allow, at_sender=True) 387 | target = args.extract_plain_text() # 获取命令参数 388 | user_id: str = event.get_user_id() 389 | # 判断带不带at 390 | [object_id, replay1] = ( 391 | [await utils.get_at(event), "该用户"] 392 | if await utils.get_at(event) != "寄" 393 | else [user_id, "您"] 394 | ) 395 | # 获取用户的所有注入数据 396 | data: List[Dict] = get_ejaculation_data(int(object_id)) 397 | ejaculation = 0 # 先初始化0 398 | if "历史" in target or "全部" in target: 399 | if not data: 400 | await matcher.finish(f"{replay1}历史总被注射量为0ml") 401 | inject_data = {} 402 | for item in data: # 遍历所有的日期 403 | temp: float = item["volume"] # 获取注入量 404 | ejaculation += temp # 注入量求和 405 | date: str = item["date"] # 获取日期 406 | inject_data[date] = temp 407 | if len(inject_data) < 2: 408 | await matcher.finish(f"{replay1}历史总被注射量为{ejaculation}ml") 409 | 410 | await matcher.finish( 411 | MessageSegment.text(f"{replay1}历史总被注射量为{ejaculation}ml") 412 | + MessageSegment.image( 413 | await draw_bar_chart.draw_line_chart(inject_data) 414 | ) 415 | ) 416 | else: 417 | ejaculation: float = get_today_ejaculation_data(int(object_id)) 418 | await matcher.finish(f"{replay1}当日总被注射量为{ejaculation}ml") 419 | 420 | @staticmethod 421 | async def yinpa_introduce(matcher: Matcher) -> None: 422 | """输出用法""" 423 | await matcher.send(MessageSegment.image(await utils.plugin_usage())) 424 | 425 | 426 | impart = Impart() 427 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/txt2img.py: -------------------------------------------------------------------------------- 1 | """一个工具类, 将文本转换为图片, 根据文本长度自动换行""" 2 | from io import BytesIO 3 | from pathlib import Path 4 | 5 | from PIL import Image, ImageDraw, ImageFont 6 | 7 | 8 | class TxtToImg: 9 | def __init__(self) -> None: 10 | self.LINE_CHAR_COUNT = 30 * 2 11 | self.CHAR_SIZE = 30 12 | self.TABLE_WIDTH = 4 13 | self.module_path: Path = Path(__file__).parent 14 | self.font = str(self.module_path / "fonts" / "SIMYOU.TTF") 15 | 16 | async def line_break(self, line: str) -> str: 17 | """将一行文本按照指定宽度进行换行""" 18 | ret = "" 19 | width = 0 20 | for c in line: 21 | if len(c.encode("utf8")) == 3: # 中文 22 | if self.LINE_CHAR_COUNT == width + 1: # 剩余位置不够一个汉字 23 | width = 2 24 | ret += "\n" + c 25 | else: # 中文宽度加2,注意换行边界 26 | width += 2 27 | ret += c 28 | elif c == "\n": 29 | width = 0 30 | ret += c 31 | elif c == "\t": 32 | space_c = ( 33 | self.TABLE_WIDTH - width % self.TABLE_WIDTH 34 | ) # 已有长度对TABLE_WIDTH取余 35 | ret += " " * space_c 36 | width += space_c 37 | else: 38 | width += 1 39 | ret += c 40 | if width >= self.LINE_CHAR_COUNT: 41 | ret += "\n" 42 | width = 0 43 | return ret if ret.endswith("\n") else ret + "\n" 44 | 45 | async def txt_to_img(self, text: str, font_size: int = 30) -> bytes: 46 | """将文本转换为图片""" 47 | text = await self.line_break(text) 48 | d_font = ImageFont.truetype(self.font, font_size) 49 | lines = text.count("\n") 50 | image = Image.new( 51 | "L", 52 | (self.LINE_CHAR_COUNT * font_size // 2 + 50, font_size * lines + 50), 53 | "white", 54 | ) 55 | draw_table = ImageDraw.Draw(im=image) 56 | draw_table.text(xy=(25, 25), text=text, fill="#000000", font=d_font, spacing=4) 57 | new_img = image.convert("RGB") 58 | img_byte = BytesIO() 59 | new_img.save(img_byte, format="PNG") 60 | return img_byte.getvalue() 61 | 62 | 63 | # 创建一个实例 64 | txt_to_img = TxtToImg() 65 | -------------------------------------------------------------------------------- /nonebot_plugin_impact/utils.py: -------------------------------------------------------------------------------- 1 | """放一点工具函数""" 2 | import random 3 | import time 4 | 5 | import nonebot 6 | from nonebot.adapters.onebot.v11 import GroupMessageEvent,Bot 7 | 8 | from .txt2img import txt_to_img 9 | 10 | 11 | class Utils: 12 | def __init__(self) -> None: 13 | """读取配置""" 14 | self.usage = """指令1: 嗦牛子 (给目标牛牛增加长度, 自己或者他人, 通过艾特选择对象, 没有at时目标是自己) 15 | 指令2: 打胶 | 开导 (给自己牛牛增加长度) 16 | 指令3: pk | 对决 (普通的pk,单纯的random实现输赢, 胜利方获取败方随机数/2的牛牛长度) 17 | 指令4: 查询 (目标牛牛长度, 自己或者他人, 通过艾特选择对象, 没有at时目标是自己) 18 | 指令5: jj排行榜 | jj排名 | jj榜单 | jjrank (字面意思, 输出倒数五位和前五位, 以及自己的排名) 19 | 指令6: 开始银趴 | 关闭银趴 | 开启淫趴 | 禁止淫趴 | 开启银趴 | 禁止银趴 (由管理员 | 群主 | SUPERUSERS开启或者关闭淫趴) 20 | 指令7: 日群友 | 透群友 | 日群主 | 透群主 | 日管理 | 透管理 (字面意思, 当使用透群友的时候如果at了人那么直接指定) 21 | 指令8: 注入查询 | 摄入查询 | 射入查询 (查询目标被透注入的量,后接(历史|全部), 可查看总被摄入的量, 无艾特的时候是自己, 有at的时候是目标) 22 | 指令9: 淫趴介绍 (输出淫趴插件的命令列表)""" 23 | self.not_allow = '群内还未开启淫趴游戏, 请管理员或群主发送"开启淫趴", "禁止淫趴"以开启/关闭该功能' # 未开启该功能的send信息 24 | self.jj_variable = ["牛子", "牛牛", "丁丁", "JJ"] # JJ变量 25 | self.cd_data = {} # 冷却数据 26 | self.pk_cd_data = {} # pk冷却数据 27 | self.suo_cd_data = {} # 嗦牛子冷却数据 28 | self.ejaculation_cd = {} # 射精CD 29 | config = nonebot.get_driver().config # 获取配置 30 | self.dj_cd_time: int = getattr(config, "djcdtime", 300) # 打胶冷却时间 31 | self.pk_cd_time: int = getattr(config, "pkcdtime", 60) # pk冷却时间 32 | self.suo_cd_time: int = getattr(config, "suocdtime", 300) # 嗦牛子冷却时间 33 | self.fuck_cd_time: int = getattr(config, "fuckcdtime", 3600) # 透群友冷却时间 34 | 35 | @staticmethod 36 | async def rule(event: GroupMessageEvent) -> bool: 37 | """rule检查, 是否有at""" 38 | msg = event.get_message() 39 | return next( 40 | (msg_seg.data["qq"] != "all" for msg_seg in msg if msg_seg.type == "at"), 41 | False, 42 | ) 43 | 44 | @staticmethod 45 | async def get_at(event: GroupMessageEvent) -> str: 46 | """获取at的qq号, 不存在则返回寄, 类型为str""" 47 | msg = event.get_message() 48 | return next( 49 | ( 50 | "寄" if msg_seg.data["qq"] == "all" else str(msg_seg.data["qq"]) 51 | for msg_seg in msg 52 | if msg_seg.type == "at" 53 | ), 54 | "寄", 55 | ) 56 | 57 | async def cd_check(self, uid: str) -> bool: 58 | """打胶的冷却检查""" 59 | cd = ( 60 | time.time() - self.cd_data[uid] 61 | if uid in self.cd_data 62 | else self.dj_cd_time + 1 63 | ) 64 | return cd > self.dj_cd_time 65 | 66 | async def pkcd_check(self, uid: str) -> bool: 67 | """pk冷却检查""" 68 | cd = ( 69 | time.time() - self.pk_cd_data[uid] 70 | if uid in self.pk_cd_data 71 | else self.pk_cd_time + 1 72 | ) 73 | return cd > self.pk_cd_time 74 | 75 | async def suo_cd_check(self, uid: str) -> bool: 76 | """嗦牛子冷却检查""" 77 | cd = ( 78 | time.time() - self.suo_cd_data[uid] 79 | if uid in self.suo_cd_data 80 | else self.suo_cd_time + 1 81 | ) 82 | return cd > self.suo_cd_time 83 | 84 | async def fuck_cd_check(self, event: GroupMessageEvent) -> bool: 85 | """透群友检查""" 86 | uid = event.get_user_id() 87 | cd = ( 88 | time.time() - self.ejaculation_cd[uid] 89 | if uid in self.ejaculation_cd 90 | else self.fuck_cd_time + 1 91 | ) 92 | return ( 93 | cd > self.fuck_cd_time 94 | or event.get_user_id() in nonebot.get_driver().config.superusers 95 | ) 96 | 97 | @staticmethod 98 | def get_random_num() -> float: 99 | """获取随机数 0.1的概率是1-2随机获取, 剩下0.9是0-1""" 100 | rand_num = random.random() 101 | rand_num = random.uniform(0, 1) if rand_num > 0.1 else random.uniform(1, 2) 102 | return round(rand_num, 3) 103 | 104 | async def plugin_usage(self) -> bytes: 105 | return await txt_to_img.txt_to_img(self.usage) 106 | 107 | @staticmethod 108 | async def get_stranger_info(bot: Bot, uid: int) -> str: 109 | try: 110 | stranger_info = await bot.call_api("get_stranger_info", user_id=uid, no_cache=False) 111 | return stranger_info["nickname"] 112 | except Exception: 113 | return "获取用户id失败" 114 | 115 | 116 | utils = Utils() 117 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "nonebot_plugin_impact" 3 | version = "0.11.114514" 4 | description = "让群友们眼前一黑的nonebot2淫趴插件" 5 | authors = [{ name = "Special-Week", email = "HuaMing27499@gmail.com" }] 6 | dependencies = [ 7 | "pillow>=9.1.1", 8 | "nonebot2>=2.0.0b5", 9 | "sqlalchemy>=2.0.19", 10 | "nonebot-adapter-onebot>=2.2.3", 11 | "nonebot-plugin-apscheduler>=0.1.3", 12 | "httpx" 13 | ] 14 | requires-python = ">=3.8.0" 15 | readme = "README.md" 16 | license = { text = "MIT" } 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pillow>=9.1.1 2 | nonebot2>=2.0.0b5 3 | nonebot-adapter-onebot>=2.2.3 4 | sqlalchemy>=2.0.19 5 | nonebot-plugin-apscheduler>=0.1.3 6 | httpx --------------------------------------------------------------------------------