This commit is contained in:
2025-10-13 22:02:44 +08:00
9 changed files with 295 additions and 1 deletions

1
assets/json/poll.json Normal file
View File

@ -0,0 +1 @@
{"poll": {"0": {"create": 1760357553, "expiry": 1760443953, "options": {"0": "此方bot", "1": "testpilot", "2": "小镜bot", "3": "可怜bot"}, "polldata": {}, "qq": "2975499623", "title": "我~是~谁~"}}}

View File

@ -0,0 +1,11 @@
指令介绍
发起投票 - 发起一个投票
格式
发起投票 <投票标题> <选项1> <选项2> ...
示例
`发起投票 这是一个投票 A B C` 发起标题为“这是一个投票”选项为“A”、“B”、“C”的投票
说明
投票各个选项之间用空格分隔选项数量为2-15项。投票的默认有效期为24小时。

View File

@ -0,0 +1,12 @@
指令介绍
投票 - 参与已发起的投票
格式
投票 <投票ID/标题> <选项文本>
示例
`投票 1 A` 在ID为1的投票中投给“A”
`投票 这是一个投票 B` 在标题为“这是一个投票”的投票中投给“B”
说明
目前不支持单人多投,每个人只能投一项。

View File

@ -0,0 +1,12 @@
指令介绍
查看投票 - 查看已发起的投票
格式
查看投票 <投票ID或标题>
示例
`查看投票 1` 查看ID为1的投票
`查看投票 这是一个投票` 查看标题为“这是一个投票”的投票
说明
投票在进行时,使用此命令可以看到投票的各个选项;投票结束后,则可以看到各项的票数。

View File

@ -0,0 +1,8 @@
指令介绍
生成二维码 - 将文本内容转换为二维码
格式
生成二维码 <文本内容>
示例
`生成二维码 嗨嗨嗨` 生成扫描结果为“嗨嗨嗨”的二维码图片

View File

@ -0,0 +1,69 @@
import qrcode
# from pyzbar.pyzbar import decode
# from PIL import Image
import requests
from io import BytesIO
from nonebot_plugin_alconna import (Alconna, Args, Field, MultiVar, UniMessage,
on_alconna)
from nonebot_plugin_alconna.uniseg import UniMsg, At, Reply
async def download_img(url):
resp = requests.get(url.replace("https://multimedia.nt.qq","http://multimedia.nt.qq")) # bim获取QQ的图片时避免SSLv3报错
img_bytes = BytesIO()
with open(img_bytes,"wb") as f:
f.write(resp.content)
return img_bytes
def genqr(data):
qr = qrcode.QRCode(version=1,error_correction=qrcode.constants.ERROR_CORRECT_L,box_size=8,border=4)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img_bytes = BytesIO()
img.save(img_bytes, format="PNG")
return img_bytes
"""
async def recqr(url):
im_path = "assets/img/qrcode/2.jpg"
data = await download_img(url)
img = Image.open(im_path)
decoded_objects = decode(img)
data = ""
for obj in decoded_objects:
data += obj.data.decode('utf-8')
return data
"""
gqrc = on_alconna(Alconna(
"genqr",
Args["saying", MultiVar(str, '+'), Field(
missing_tips=lambda: "请输入你要转换为二维码的文字!"
)],
# UniMessage[]
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=False, aliases={"生成二维码","genqrcode"})
@gqrc.handle()
async def _(saying: list):
"""
img = await draw_pt("\n".join(saying))
img_bytes = BytesIO()
img.save(img_bytes, format="PNG")
await pt.send(await UniMessage().image(raw=img_bytes).export())
# print(saying)
# 二维码识别
if type(saying[0]) == 'image':
data = await recqr(saying[0].data['url'])
if data == "":
await gqrc.send("二维码图片解析失败!")
else:
await gqrc.send(recqr(saying[0].data['url']))
# 二维码生成
else:
"""
# genqr("\n".join(saying))
await gqrc.send(await UniMessage().image(raw=genqr("\n".join(saying))).export())

View File

@ -0,0 +1,160 @@
import json, time
from nonebot_plugin_alconna import (Alconna, Args, Field, MultiVar, UniMessage,
on_alconna)
from nonebot_plugin_alconna.uniseg import UniMsg, At, Reply
from nonebot.adapters.onebot.v11 import Event
poll_json_path = "assets/json/poll.json"
poll_file = open(poll_json_path,"r",encoding="utf-8")
poll_list_raw = poll_file.read()
poll_file.close()
poll_list = json.loads(poll_list_raw)['poll']
async def createpoll(title,qqid,options):
polllength = len(poll_list)
pollid = str(polllength)
poll_create = int(time.time())
poll_expiry = poll_create + 24*3600
polljson = {"title":title,"qq":qqid,"create":poll_create,"expiry":poll_expiry,"options":options,"polldata":{}}
poll_list[pollid] = polljson
writeback()
return pollid
def getpolldata(pollid_or_title):
# 初始化“被指定的投票项目”
thepoll = {}
polnum = -1
# 判断是ID还是标题
if str.isdigit(pollid_or_title):
if pollid_or_title in poll_list:
thepoll = poll_list[pollid_or_title]
polnum = pollid_or_title
else:
return [{},-1]
else:
for i in poll_list:
if poll_list[i]["title"] == pollid_or_title:
thepoll = poll_list[i]
polnum = i
break
if polnum == -1:
return [{},-1]
return [thepoll,polnum]
def writeback():
file = open(poll_json_path,"w",encoding="utf-8")
json.dump({'poll':poll_list},file,ensure_ascii=False,sort_keys=True)
async def pollvote(polnum,optionnum,qqnum):
optiond = poll_list[polnum]["polldata"]
if optionnum not in optiond:
poll_list[polnum]["polldata"][optionnum] = []
poll_list[polnum]["polldata"][optionnum].append(qqnum)
writeback()
return
poll = on_alconna(Alconna(
"poll",
Args["saying", MultiVar(str, '+'), Field(
missing_tips=lambda: "参数错误。用法:发起投票 <投票标题> <选项1> <选项2> ..."
)],
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=False, aliases={"发起投票","createpoll"})
@poll.handle()
async def _(saying: list, event: Event):
if (len(saying) < 3):
await poll.send("请提供至少两个投票选项!")
elif (len(saying) < 17):
title = saying[0]
saying.remove(title)
options = {}
for i in saying:
options[saying.index(i)] = i
qqid = event.get_user_id()
result = await createpoll(title,qqid,options)
await poll.send("已创建投票。回复 查看投票 "+str(result)+" 查看该投票。")
else:
await poll.send("投票选项太多了请减少到15个选项以内。")
viewpoll = on_alconna(Alconna(
"viewpoll",
Args["saying", MultiVar(str, '+'), Field(
missing_tips=lambda: "请指定投票ID或标题。用法查看投票 <投票ID或标题>"
)],
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=False, aliases={"查看投票"})
@viewpoll.handle()
async def _(saying: list):
# 参数投票ID或者标题
# pollid_or_title = params[0]
polldata = getpolldata(saying[0])
# 被指定的投票项目
thepoll = polldata[0]
polnum = polldata[1]
if polnum == -1:
await viewpoll.send("该投票不存在!")
else:
# 检查投票是否已结束
pollended = 0
if time.time() > thepoll["expiry"]:
pollended = 1
# 回复内容
reply = "投票:"+thepoll["title"]+" [ID: "+str(polnum)+"]"
# 如果投票已结束
if pollended:
for i in thepoll["options"]:
reply += "\n"
# 检查该选项是否有人投票
if i in thepoll["polldata"]:
reply += "["+str(len(thepoll["polldata"][i]))+" 票]"
else:
reply += "[0 票]"
reply += " "+thepoll["options"][i]
reply += "\n\n此投票已结束。"
else:
for i in thepoll["options"]:
reply += "\n"
reply += "- "+thepoll["options"][i]
# reply += "\n\n小提示向bot私聊发送 /viewpoll "+str(polnum)+" 可查看已投票数哦!"
reply += "\n\n发送 投票 "+str(polnum)+" <选项文本> 即可参与投票!"
await viewpoll.send(reply)
vote = on_alconna(Alconna(
"vote",
Args["saying", MultiVar(str, '+'), Field(
missing_tips=lambda: "参数错误。用法:投票 <投票ID/标题> <选项文本>"
)],
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=False, aliases={"投票","参与投票"})
@vote.handle()
async def _(saying: list, event: Event):
if (len(saying) < 2):
await vote.send("请指定投给哪一项!")
else:
polldata = getpolldata(saying[0])
# 被指定的投票项目
thepoll = polldata[0]
polnum = polldata[1]
if polnum == -1:
await viewpoll.finish("没有找到这个投票!")
# thepolldata = thepoll["polldata"]
# 查找对应的投票项
optionnum = -1
for i in thepoll["options"]:
if saying[1] == thepoll["options"][i]:
optionnum = i
break
if optionnum == -1:
reply = "此投票里面没有这一项!可用的选项有:"
for i in thepoll["options"]:
reply += "\n"
reply += "- "+thepoll["options"][i]
await viewpoll.send(reply)
# 检查是否符合投票条件该qq号是否已参与过投票、投票是否过期
elif time.time() > thepoll["expiry"]:
await viewpoll.send("此投票已经结束!请发送 查看投票 "+polnum+" 查看结果。")
elif str(event.get_user_id()) in str(thepoll["polldata"]):
await viewpoll.send("你已参与过此投票!请在投票结束后发送 查看投票 "+polnum+" 查看结果。")
# 写入项目
else:
await pollvote(polnum,optionnum,event.get_user_id())
await viewpoll.send("投票成功!你投给了 "+saying[1])

22
poetry.lock generated
View File

@ -2518,6 +2518,26 @@ files = [
{file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"},
]
[[package]]
name = "qrcode"
version = "8.2"
description = "QR Code image generator"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
{file = "qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f"},
{file = "qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
all = ["pillow (>=9.1.0)", "pypng"]
pil = ["pillow (>=9.1.0)"]
png = ["pypng"]
[[package]]
name = "requests"
version = "2.32.5"
@ -3325,4 +3345,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.12,<4.0"
content-hash = "013942994a91012f285305194d73b6609fe71bbd214a63dce443877853dcd764"
content-hash = "6fc63a138508a779d47346e0186b4c771ed17b10f278a0e094c6994c1d99a877"

View File

@ -25,6 +25,7 @@ dependencies = [
"ptimeparse (>=0.1.1,<0.2.0)",
"skia-python (>=138.0,<139.0)",
"nonebot-plugin-analysis-bilibili (>=2.8.1,<3.0.0)",
"qrcode (>=8.2,<9.0)",
]
[build-system]