Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81aac10665 | |||
| 3ce230adfe | |||
| 4f885554ca | |||
| 7ebcb8add4 | |||
| e18cc82792 | |||
| eb28cd0a0c | |||
| 2d688a6ed6 | |||
| e9aac52200 |
BIN
assets/img/meme/snaur_1_base.png
Executable file
BIN
assets/img/meme/snaur_1_base.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/img/meme/snaur_1_top.png
Executable file
BIN
assets/img/meme/snaur_1_top.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1008 KiB |
@ -45,7 +45,7 @@
|
||||
- 帧数必须为正整数(> 0)。
|
||||
- 若原始帧数 ≤ 指定帧数,则保留全部帧。
|
||||
|
||||
--s <速度>(可选)
|
||||
--speed <速度>(可选)
|
||||
- 调整 gif 图的速度。若为负数,则代表倒放
|
||||
|
||||
使用方式
|
||||
|
||||
20
konabot/docs/user/卵总展示.txt
Normal file
20
konabot/docs/user/卵总展示.txt
Normal file
@ -0,0 +1,20 @@
|
||||
指令介绍
|
||||
卵总展示 - 让卵总举起你的图片
|
||||
|
||||
格式
|
||||
<引用图片> 卵总展示 [选项]
|
||||
卵总展示 [选项] <图片>
|
||||
|
||||
选项
|
||||
`--whiteness <number>` 白度
|
||||
将原图进行指数变换,以调整它的白的程度,默认为 0.0
|
||||
|
||||
`--black-level <number>` 黑色等级
|
||||
将原图减淡,数值越大越淡,范围 0.0-1.0,默认 0.2
|
||||
|
||||
`--opacity <number>` 不透明度
|
||||
将你的图片叠放在图片上的不透明度,默认为 0.8
|
||||
|
||||
`--saturation <number>` 饱和度
|
||||
调整原图的饱和度,应该要大于 0.0,默认为 0.85
|
||||
|
||||
39
konabot/plugins/bilibili_fetch/__init__.py
Normal file
39
konabot/plugins/bilibili_fetch/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
import re
|
||||
|
||||
from nonebot import on_message
|
||||
from nonebot_plugin_alconna import Reference, Reply, UniMsg
|
||||
|
||||
from nonebot.adapters import Event
|
||||
|
||||
|
||||
matcher_fix = on_message()
|
||||
|
||||
pattern = (
|
||||
r"^(?:(?:av|cv)\d+|BV[a-zA-Z0-9]{10})|"
|
||||
r"(?:b23\.tv|bili(?:22|23|33|2233)\.cn|\.bilibili\.com|QQ小程序(?:&#93;|]|\])哔哩哔哩).{0,500}"
|
||||
)
|
||||
|
||||
|
||||
@matcher_fix.handle()
|
||||
async def _(msg: UniMsg, event: Event):
|
||||
to_search = msg.exclude(Reply, Reference).dump(json=True)
|
||||
to_search2 = msg.exclude(Reply, Reference).extract_plain_text()
|
||||
if not re.search(pattern, to_search) and not re.search(pattern, to_search2):
|
||||
return
|
||||
|
||||
from nonebot_plugin_analysis_bilibili import handle_analysis
|
||||
|
||||
await handle_analysis(event)
|
||||
|
||||
# b_url: str
|
||||
# b_page: str | None
|
||||
# b_time: str | None
|
||||
#
|
||||
# from nonebot_plugin_analysis_bilibili.analysis_bilibili import extract as bilibili_extract
|
||||
#
|
||||
# b_url, b_page, b_time = bilibili_extract(to_search)
|
||||
# if b_url is None:
|
||||
# return
|
||||
#
|
||||
# await matcher_fix.send(await UniMessage().text(b_url).export())
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import re
|
||||
from loguru import logger
|
||||
from nonebot import on_message
|
||||
from nonebot_plugin_alconna import Reference, Reply, UniMsg
|
||||
|
||||
from nonebot.adapters import Event
|
||||
|
||||
|
||||
matcher_fix = on_message()
|
||||
|
||||
pattern = (
|
||||
r"^(?:(?:av|cv)\d+|BV[a-zA-Z0-9]{10})|"
|
||||
r"(?:b23\.tv|bili(?:22|23|33|2233)\.cn|\.bilibili\.com|QQ小程序(?:&#93;|]|\])哔哩哔哩).{0,500}"
|
||||
)
|
||||
|
||||
|
||||
@matcher_fix.handle()
|
||||
async def _(msg: UniMsg, event: Event):
|
||||
to_search = msg.exclude(Reply, Reference).dump(json=True)
|
||||
if not re.search(pattern, to_search):
|
||||
return
|
||||
logger.info("检测到有 Bilibili 相关的消息,直接进行一个调用")
|
||||
_module = __import__("nonebot_plugin_analysis_bilibili")
|
||||
await _module.handle_analysis(event)
|
||||
|
||||
@ -75,19 +75,41 @@ LAST_CHAR = ""
|
||||
|
||||
USER_NAME_CACHE = {} # 缓存用户名称,避免多次获取
|
||||
|
||||
REMAIN_PLAYING_TIMES = 1
|
||||
LAST_PLAY_DATE = ""
|
||||
|
||||
LOCK = False
|
||||
|
||||
ALL_BUFF_SCORE = 0 # 全体分数
|
||||
|
||||
import datetime
|
||||
|
||||
def be_able_to_play():
|
||||
global REMAIN_PLAYING_TIMES, LAST_PLAY_DATE
|
||||
if(LAST_PLAY_DATE != datetime.date.today()):
|
||||
LAST_PLAY_DATE = datetime.date.today()
|
||||
REMAIN_PLAYING_TIMES = 1
|
||||
if(REMAIN_PLAYING_TIMES > 0):
|
||||
REMAIN_PLAYING_TIMES -= 1
|
||||
return True
|
||||
return False
|
||||
|
||||
evt = on_alconna(Alconna(
|
||||
"我要玩成语接龙"
|
||||
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=True)
|
||||
|
||||
@evt.handle()
|
||||
async def _(event: BaseEvent):
|
||||
async def play_game(event: BaseEvent, force = False):
|
||||
global NOW_PLAYING, LAST_CHAR, INITED
|
||||
if not INITED:
|
||||
init_lexicon()
|
||||
INITED = True
|
||||
if NOW_PLAYING:
|
||||
await evt.send(await UniMessage().text("当前已有成语接龙游戏在进行中,请稍后再试!").export())
|
||||
return
|
||||
if not be_able_to_play() and not force:
|
||||
await evt.send(await UniMessage().text("玩玩玩,就知道玩,快去睡觉!").export())
|
||||
return
|
||||
if not INITED:
|
||||
init_lexicon()
|
||||
INITED = True
|
||||
NOW_PLAYING = True
|
||||
await evt.send(await UniMessage().text("你小子,还真有意思!\n好,成语接龙游戏开始!我说一个成语,请大家接下去!").export())
|
||||
# 选择一个随机成语
|
||||
@ -96,13 +118,22 @@ async def _(event: BaseEvent):
|
||||
# 发布成语
|
||||
await evt.send(await UniMessage().text(f"第一个成语:「{idiom}」,请接!").export())
|
||||
|
||||
evt = on_alconna(Alconna(
|
||||
"老子就是要玩成语接龙!!!"
|
||||
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=True)
|
||||
|
||||
@evt.handle()
|
||||
async def force_play_game(event: BaseEvent):
|
||||
await play_game(event, force=True)
|
||||
|
||||
|
||||
evt = on_alconna(Alconna(
|
||||
"不玩了"
|
||||
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=True)
|
||||
|
||||
@evt.handle()
|
||||
async def _(event: BaseEvent):
|
||||
global NOW_PLAYING, SCORE_BOARD, LAST_CHAR
|
||||
global NOW_PLAYING, SCORE_BOARD, LAST_CHAR, ALL_BUFF_SCORE
|
||||
if NOW_PLAYING:
|
||||
NOW_PLAYING = False
|
||||
# 发送好吧狗图片
|
||||
@ -117,7 +148,7 @@ async def _(event: BaseEvent):
|
||||
# 按分数排序,名字用 at 的方式
|
||||
sorted_score = sorted(SCORE_BOARD.items(), key=lambda x: x[1]["score"], reverse=True)
|
||||
for i, (user_id, info) in enumerate(sorted_score):
|
||||
result_text += f"{i+1}. " + UniMessage().at(user_id) + f": {info['score']} 分\n"
|
||||
result_text += f"{i+1}. " + UniMessage().at(user_id) + f": {info['score'] + ALL_BUFF_SCORE} 分\n"
|
||||
await evt.send(await result_text.export())
|
||||
# 重置分数板
|
||||
SCORE_BOARD = {}
|
||||
@ -132,12 +163,11 @@ evt = on_alconna(Alconna(
|
||||
|
||||
@evt.handle()
|
||||
async def _(event: BaseEvent):
|
||||
global NOW_PLAYING, LAST_CHAR
|
||||
global NOW_PLAYING, LAST_CHAR, ALL_BUFF_SCORE
|
||||
if not NOW_PLAYING:
|
||||
return
|
||||
await evt.send(await UniMessage().text("你们太菜了!全部扣100分!").export())
|
||||
for user_id in SCORE_BOARD:
|
||||
SCORE_BOARD[user_id]["score"] -= 100
|
||||
ALL_BUFF_SCORE -= 100
|
||||
# 选择下一个成语
|
||||
idiom = secrets.choice(ALL_IDIOMS)
|
||||
LAST_CHAR = idiom[-1]
|
||||
@ -148,14 +178,36 @@ evt = on_message()
|
||||
|
||||
@evt.handle()
|
||||
async def _(event: BaseEvent, msg: UniMsg):
|
||||
global NOW_PLAYING, LAST_CHAR, SCORE_BOARD
|
||||
global NOW_PLAYING, LAST_CHAR, LOCK
|
||||
if not NOW_PLAYING:
|
||||
return
|
||||
user_idiom = msg.extract_plain_text().strip()
|
||||
if(user_idiom[0] != LAST_CHAR):
|
||||
return
|
||||
if LOCK:
|
||||
return
|
||||
LOCK = True
|
||||
await handle_send_info(event, msg)
|
||||
LOCK = False
|
||||
|
||||
async def handle_send_info(event: BaseEvent, msg: UniMsg):
|
||||
global NOW_PLAYING, LAST_CHAR, SCORE_BOARD, ALL_BUFF_SCORE
|
||||
user_idiom = msg.extract_plain_text().strip()
|
||||
if(user_idiom not in ALL_IDIOMS and user_idiom not in ALL_WORDS):
|
||||
await evt.send(await UniMessage().text("接不上!这个不一样!").export())
|
||||
# 扣0.1分
|
||||
if isinstance(event, DiscordMessageEvent):
|
||||
user_id = str(event.author.id)
|
||||
user_name = str(event.author.name)
|
||||
else:
|
||||
user_id = str(event.get_user_id())
|
||||
user_name = str(event.get_user_id())
|
||||
if user_id not in SCORE_BOARD:
|
||||
SCORE_BOARD[user_id] = {
|
||||
"name": user_name,
|
||||
"score": 0
|
||||
}
|
||||
SCORE_BOARD[user_id]["score"] -= 0.1
|
||||
await evt.send(await UniMessage().at(user_id).text("接不上!这个不一样!你被扣了 0.1 分!").export())
|
||||
return
|
||||
# 成功接上
|
||||
if isinstance(event, DiscordMessageEvent):
|
||||
@ -172,6 +224,6 @@ async def _(event: BaseEvent, msg: UniMsg):
|
||||
}
|
||||
SCORE_BOARD[user_id]["score"] += 1
|
||||
# at 指定玩家
|
||||
await evt.send(await UniMessage().at(user_id).text(f"接对了!你有 {SCORE_BOARD[user_id]['score']} 分!").export())
|
||||
await evt.send(await UniMessage().at(user_id).text(f"接对了!你有 {SCORE_BOARD[user_id]['score'] + ALL_BUFF_SCORE} 分!").export())
|
||||
LAST_CHAR = user_idiom[-1]
|
||||
await evt.send(await UniMessage().text(f"下一个成语请以「{LAST_CHAR}」开头!").export())
|
||||
@ -2,11 +2,11 @@ from io import BytesIO
|
||||
from typing import Iterable, cast
|
||||
|
||||
from nonebot import on_message
|
||||
from nonebot_plugin_alconna import (Alconna, Args, Field, MultiVar, Text,
|
||||
from nonebot_plugin_alconna import (Alconna, Args, Field, Image, MultiVar, Option, Text,
|
||||
UniMessage, UniMsg, on_alconna)
|
||||
|
||||
from konabot.common.nb.extract_image import extract_image_from_message
|
||||
from konabot.plugins.memepack.drawing.display import draw_cao_display
|
||||
from konabot.common.nb.extract_image import PIL_Image, extract_image_from_message
|
||||
from konabot.plugins.memepack.drawing.display import draw_cao_display, draw_snaur_display
|
||||
from konabot.plugins.memepack.drawing.saying import (draw_cute_ten,
|
||||
draw_geimao, draw_mnk,
|
||||
draw_pt, draw_suan)
|
||||
@ -139,3 +139,24 @@ async def _(msg: UniMsg, evt: Event, bot: Bot):
|
||||
.text(err)
|
||||
.export()
|
||||
)
|
||||
|
||||
|
||||
snaur_display_cmd = on_alconna(Alconna(
|
||||
"卵总展示",
|
||||
Option("--whiteness", Args["whiteness", float], alias=["-w"]),
|
||||
Option("--black-level", Args["black_level", float], alias=["-b"]),
|
||||
Option("--opacity", Args["opacity", float], alias=["-o"]),
|
||||
Option("--saturation", Args["saturation", float], alias=["-s"]),
|
||||
Args["image", Image | None],
|
||||
))
|
||||
|
||||
@snaur_display_cmd.handle()
|
||||
async def _(img: PIL_Image, whiteness: float = 0.0, black_level: float = 0.2,
|
||||
opacity: float = 0.8, saturation: float = 0.85):
|
||||
img_processed = await draw_snaur_display(
|
||||
img, whiteness, black_level, opacity, saturation,
|
||||
)
|
||||
img_data = BytesIO()
|
||||
img_processed.save(img_data, "PNG")
|
||||
await snaur_display_cmd.send(await UniMessage().image(raw=img_data).export())
|
||||
|
||||
|
||||
@ -4,10 +4,12 @@ from typing import Any, cast
|
||||
import cv2
|
||||
import numpy as np
|
||||
import PIL.Image
|
||||
import PIL.ImageChops
|
||||
import PIL.ImageEnhance
|
||||
|
||||
from konabot.common.path import ASSETS_PATH
|
||||
|
||||
cao_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "caoimg1.png")
|
||||
cao_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "caoimg1.png")
|
||||
CAO_QUAD_POINTS = np.float32(cast(Any, [
|
||||
[392, 540],
|
||||
[577, 557],
|
||||
@ -15,6 +17,16 @@ CAO_QUAD_POINTS = np.float32(cast(Any, [
|
||||
[381, 687],
|
||||
]))
|
||||
|
||||
snaur_image_base = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "snaur_1_base.png")
|
||||
snaur_image_top = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "snaur_1_top.png")
|
||||
SNAUR_RATIO = (1 / 2) ** .5
|
||||
SNAUR_QUAD_POINTS = np.float32(cast(Any, [
|
||||
[0, 466 ],
|
||||
[673, 471 ],
|
||||
[640, 1196],
|
||||
[106, 1280],
|
||||
]))
|
||||
|
||||
def _draw_cao_display(image: PIL.Image.Image):
|
||||
src = np.array(image.convert("RGB"))
|
||||
h, w = src.shape[:2]
|
||||
@ -43,3 +55,87 @@ def _draw_cao_display(image: PIL.Image.Image):
|
||||
|
||||
async def draw_cao_display(image: PIL.Image.Image):
|
||||
return await asyncio.to_thread(_draw_cao_display, image)
|
||||
|
||||
|
||||
def _draw_snaur_display(
|
||||
image : PIL.Image.Image,
|
||||
whiteness : float = 0.0 ,
|
||||
black_level: float = 0.2 ,
|
||||
opacity : float = 0.8 ,
|
||||
saturation : float = 0.85 ,
|
||||
):
|
||||
src = np.array(image.convert("RGBA"))
|
||||
_h, _w = src.shape[:2]
|
||||
|
||||
if _w / _h < SNAUR_RATIO:
|
||||
_w_target = _w
|
||||
_h_target = int(_w / SNAUR_RATIO)
|
||||
else:
|
||||
_w_target = int(_h * SNAUR_RATIO)
|
||||
_h_target = _h
|
||||
|
||||
x_center = _w / 2
|
||||
y_center = _h / 2
|
||||
|
||||
x1 = int(x_center - _w_target / 2)
|
||||
x2 = int(x_center + _w_target / 2)
|
||||
y1 = int(y_center - _h_target / 2)
|
||||
y2 = int(y_center + _h_target / 2)
|
||||
|
||||
src = src[y1:y2, x1:x2, :]
|
||||
|
||||
h, w = src.shape[:2]
|
||||
src_points = np.float32(cast(Any, [
|
||||
[0, 0],
|
||||
[w, 0],
|
||||
[w, h],
|
||||
[0, h],
|
||||
]))
|
||||
dst_points = SNAUR_QUAD_POINTS
|
||||
M = cv2.getPerspectiveTransform(cast(Any, src_points), cast(Any, dst_points))
|
||||
output_size = snaur_image_top.size
|
||||
output_w, output_h = output_size
|
||||
warped = cv2.warpPerspective(
|
||||
src,
|
||||
M,
|
||||
(output_w, output_h),
|
||||
flags=cv2.INTER_LINEAR,
|
||||
borderMode=cv2.BORDER_CONSTANT,
|
||||
borderValue=(0, 0, 0)
|
||||
)
|
||||
|
||||
result = PIL.Image.fromarray(warped, 'RGBA')
|
||||
|
||||
r, g, b, a = result.split()
|
||||
a = a.point(lambda p: int(p * opacity))
|
||||
f2 = lambda p: int(
|
||||
((p / 255) ** (2 ** whiteness)) * 255 * (1 - black_level)
|
||||
+ 255 * black_level
|
||||
)
|
||||
r = r.point(f2)
|
||||
g = g.point(f2)
|
||||
b = b.point(f2)
|
||||
result = PIL.Image.merge('RGBA', (r, g, b, a))
|
||||
|
||||
enhancer = PIL.ImageEnhance.Color(result)
|
||||
result = enhancer.enhance(saturation)
|
||||
|
||||
result = PIL.ImageChops.multiply(result, snaur_image_base)
|
||||
|
||||
result = PIL.Image.alpha_composite(snaur_image_base, result)
|
||||
result = PIL.Image.alpha_composite(result, snaur_image_top)
|
||||
return result
|
||||
|
||||
|
||||
async def draw_snaur_display(
|
||||
image : PIL.Image.Image,
|
||||
whiteness : float = 0.0 ,
|
||||
black_level: float = 0.2 ,
|
||||
opacity : float = 0.8 ,
|
||||
saturation : float = 0.85 ,
|
||||
) -> PIL.Image.Image:
|
||||
return await asyncio.to_thread(
|
||||
_draw_snaur_display, image, whiteness, black_level,
|
||||
opacity, saturation,
|
||||
)
|
||||
|
||||
|
||||
486
poetry.lock
generated
486
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -37,5 +37,10 @@ name = "pt-gitea-pypi"
|
||||
url = "https://gitea.service.jazzwhom.top/api/packages/Passthem/pypi/simple/"
|
||||
priority = "supplemental"
|
||||
|
||||
[[tool.poetry.source]]
|
||||
name = "mirrors"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
|
||||
priority = "primary"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
ptimeparse = {source = "pt-gitea-pypi"}
|
||||
|
||||
Reference in New Issue
Block a user