👉实现戳一戳响应和其它配置项

This commit is contained in:
Asankilp 2024-10-24 16:45:06 +08:00
parent 66844b197d
commit acdf1d1ed2
6 changed files with 116 additions and 27 deletions

View File

@ -45,6 +45,26 @@ _✨ 使用 Azure OpenAI 推理服务的聊天机器人(施工中) ✨_
发送`marsho`指令可以获取使用说明
#### 👉 戳一戳
当 nonebot 连接到支持的 OneBot v11 实现端时,可以接收头像双击戳一戳消息并进行响应。详见`MARSHOAI_POKE_SUFFIX`配置项。
## ⚙️ 配置
`bot.py` 所在目录的`.env`文件中添加下表中的配置
| 配置项 | 必填 | 默认值 | 说明 |
| :---------------: | :--: | :----: | :----------------------------------------------------------: |
| MARSHOAI_TOKEN | 是 | 无 | 调用 API 必需的访问 token |
| MARSHOAI_DEFAULT_MODEL | 否 | `gpt-4o-mini` | Marsho 默认调用的模型 |
| MARSHOAI_PROMPT | 否 | 猫娘 Marsho 人设提示词 | Marsho 的基本系统提示词 |
| MARSHOAI_ADDITIONAL_PROMPT | 否 | 无 | Marsho 的扩展系统提示词 |
| MARSHOAI_POKE_SUFFIX | 否 | `揉了揉你的猫耳` | 对 Marsho 所连接的 OneBot 用户进行双击戳一戳时,构建的聊天内容。此配置项为空字符串时,戳一戳响应功能会被禁用。例如,默认值构建的聊天内容将为`*[昵称]揉了揉你的猫耳`。 |
| MARSHOAI_ENABLE_PRAISES | 否 | `true` | 是否启用夸赞名单功能(未实现) |
| MARSHOAI_ENABLE_TIME_PROMPT | 否 | `true` | 是否启用实时更新的日期与时间(精确到秒)与农历日期系统提示词 |
| MARSHOAI_AZURE_ENDPOINT | 否 | `https://models.inference.ai.azure.com` | 调用 Azure OpenAI 服务的 API 终结点 |
| MARSHOAI_TEMPERATURE | 否 | 无 | 进行推理时的温度参数 |
| MARSHOAI_TOP_P | 否 | 无 | 进行推理时的核采样参数 |
| MARSHOAI_MAX_TOKENS | 否 | 无 | 返回消息的最大 token 数 |
## © 版权说明
"Marsho" logo 由 [@Asankilp](https://github.com/Asankilp) 绘制,基于 [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 许可下提供。
"Melobot" logo 由 [@mldkouo](https://github.com/mldkouo) 绘制,版权归属于 [@Meloland](https://github.com/meloland)。

2
bot.py
View File

@ -22,5 +22,5 @@ if __name__ == "__main__":
.add_io(ForwardWebSocketIO("ws://127.0.0.1:8081"))
.add_adapter(Adapter())
.load_plugin("./marshoai")
.run()
.run(debug=True)
)

View File

@ -2,17 +2,18 @@ import traceback
from azure.ai.inference.aio import ChatCompletionsClient
from azure.ai.inference.models import UserMessage, TextContentItem, ImageContentItem, ImageUrl, CompletionsFinishReason
from melobot import Plugin, send_text
from melobot.protocols.onebot.v11 import on_start_match, on_message, on_command
from melobot.protocols.onebot.v11 import on_start_match, on_message, on_command, on_notice, on_event, Adapter
from melobot.protocols.onebot.v11.handle import Args
from melobot.protocols.onebot.v11.utils import MsgChecker, LevelRole, MsgCheckerFactory, StartMatcher, ParseArgs
from melobot.protocols.onebot.v11.adapter.event import MessageEvent
from melobot.protocols.onebot.v11.utils import MsgChecker, LevelRole, MsgCheckerFactory, StartMatcher, ParseArgs, Parser
from melobot.protocols.onebot.v11.adapter.event import MessageEvent, PokeNotifyEvent, GroupMessageEvent, PrivateMessageEvent
from melobot.protocols.onebot.v11.adapter.segment import PokeSegment
from azure.core.credentials import AzureKeyCredential
from typing import Union
from .constants import *
from .config import Config
from .util import *
from .models import MarshoContext
from .checkers import superuser_checker, PokeMarshoChecker
config = Config()
model_name = config.marshoai_default_model
context = MarshoContext()
@ -22,11 +23,8 @@ client = ChatCompletionsClient(
endpoint=endpoint,
credential=AzureKeyCredential(token)
)
checker_ft = MsgCheckerFactory(
owner= config.owner,
super_users=config.superusers
)
superuser_checker: MsgChecker = checker_ft.get_base(LevelRole.SU)
@on_command(checker=superuser_checker, cmd_start="/", cmd_sep=" ", targets="changemodel")
async def changemodel(args: ParseArgs = Args()):
@ -35,13 +33,25 @@ async def changemodel(args: ParseArgs = Args()):
await send_text("已切换")
@on_start_match("reset")
async def reset(event: MessageEvent):
async def reset_group(event: GroupMessageEvent):
context.reset(event.group_id, event.is_private)
await send_text("上下文已重置")
@on_start_match("reset")
async def reset_private(event: PrivateMessageEvent):
context.reset(event.user_id, event.is_private)
await send_text("上下文已重置")
@on_start_match("marsho")
async def marsho(event: MessageEvent):
async def marsho_group(event: GroupMessageEvent):
await marsho_main(event, True)
@on_start_match("marsho")
async def marsho_private(event: PrivateMessageEvent):
await marsho_main(event, False)
async def marsho_main(event: Union[GroupMessageEvent, PrivateMessageEvent], is_group: bool):
if event.text.lstrip("marsho") == "":
await send_text(USAGE)
await send_text(INTRODUCTION)
@ -51,6 +61,7 @@ async def marsho(event: MessageEvent):
is_support_image_model = model_name.lower() in SUPPORT_IMAGE_MODELS
usermsg = [] if is_support_image_model else ""
user_id = event.sender.user_id
target_id = event.group_id if is_group else event.user_id
nickname_prompt = ""
marsho_string_removed = False
for i in event.get_segments("image"):
@ -76,11 +87,11 @@ async def marsho(event: MessageEvent):
response = await make_chat(
client=client,
model_name=model_name,
msg=context.build(event.user_id, event.is_private)+[UserMessage(content=usermsg)])
msg=context.build(target_id, event.is_private)+[UserMessage(content=usermsg)])
choice = response.choices[0]
if choice["finish_reason"] == CompletionsFinishReason.STOPPED: # 当对话成功时将dict的上下文添加到上下文类中
context.append(UserMessage(content=usermsg).as_dict(), event.user_id, event.is_private)
context.append(choice.message.as_dict(), event.user_id, event.is_private)
context.append(UserMessage(content=usermsg).as_dict(), target_id, event.is_private)
context.append(choice.message.as_dict(), target_id, event.is_private)
elif choice["finish_reason"] == CompletionsFinishReason.CONTENT_FILTERED:
await send_text("*已被内容过滤器过滤。请调整聊天内容后重试。")
return
@ -90,6 +101,28 @@ async def marsho(event: MessageEvent):
traceback.print_exc()
return
@on_event(checker=PokeMarshoChecker())
async def poke(event: PokeNotifyEvent, adapter: Adapter): # 尚未实现私聊戳一戳 QwQ
#await adapter.send_custom(str(event.user_id),group_id=event.group_id)
user_id = event.user_id
# nicknames = await get_nicknames()
# nickname = nicknames.get(user_id, "")
nickname = ""
try:
if config.marshoai_poke_suffix != "":
response = await make_chat(
client=client,
model_name=model_name,
msg=[get_prompt(),UserMessage(content=f"*{nickname}{config.marshoai_poke_suffix}")]
)
choice = response.choices[0]
if choice["finish_reason"] == CompletionsFinishReason.STOPPED:
await adapter.send_custom(" "+str(choice.message.content),group_id=event.group_id)
except Exception as e:
await adapter.send_custom(str(e)+suggest_solution(str(e)),group_id=event.group_id)
traceback.print_exc()
return
class MarshoAI(Plugin):
version = VERSION
flows = [changemodel,marsho,reset]
flows = [changemodel,marsho_group,marsho_private,reset_group,reset_private,poke]

28
marshoai/checkers.py Normal file
View File

@ -0,0 +1,28 @@
from melobot.protocols.onebot.v11.adapter.event import Event, MessageEvent, PokeNotifyEvent
from melobot.protocols.onebot.v11.utils import MsgChecker, LevelRole, MsgCheckerFactory, StartMatcher, ParseArgs, Checker
from melobot.protocols.onebot.v11.adapter.segment import PokeRecvSegment
from .config import Config
from .extra_segment import TouchSegment
config = Config()
superuser_checker_ft = MsgCheckerFactory(
owner= config.owner,
super_users=config.superusers
)
superuser_checker: MsgChecker = superuser_checker_ft.get_base(LevelRole.SU) # 超级用户检查器
class PokeMarshoChecker(Checker):
"""
戳一戳 Bot 检查器戳一戳对象为 Bot 自身时检查通过
"""
def __init__(self) -> None:
super().__init__()
async def check(self, event: PokeNotifyEvent) -> bool:
try:
if event.target_id == event.self_id:
return True
else:
return False
except AttributeError:
return False

View File

@ -0,0 +1,8 @@
from melobot.protocols.onebot.v11.adapter.segment import Segment
from typing import Literal
from typing_extensions import TypedDict
class _TouchData(TypedDict):
id: str
TouchSegment = Segment.add_type(Literal['touch'], _TouchData)

View File

@ -33,10 +33,10 @@ async def get_image_b64(url):
async def make_chat(client: ChatCompletionsClient, msg, model_name: str):
return await client.complete(
messages=msg,
model=model_name
# temperature=config.marshoai_temperature,
# max_tokens=config.marshoai_max_tokens,
# top_p=config.marshoai_top_p
model=model_name,
temperature=config.marshoai_temperature,
max_tokens=config.marshoai_max_tokens,
top_p=config.marshoai_top_p
)
# def get_praises():
# praises_file = store.get_plugin_data_file("praises.json") # 夸赞名单文件使用localstore存储
@ -96,15 +96,15 @@ async def make_chat(client: ChatCompletionsClient, msg, model_name: str):
def get_prompt():
prompts = ""
# prompts += config.marshoai_additional_prompt
prompts += config.marshoai_additional_prompt
# if config.marshoai_enable_praises:
# praises_prompt = build_praises()
# prompts += praises_prompt
# if config.marshoai_enable_time_prompt:
# current_time = datetime.now().strftime('%Y.%m.%d %H:%M:%S')
# current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:] #库更新之前使用切片
# time_prompt = f"现在的时间是{current_time},农历{current_lunar_date}。"
# prompts += time_prompt
if config.marshoai_enable_time_prompt:
current_time = datetime.now().strftime('%Y.%m.%d %H:%M:%S')
current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:] #库更新之前使用切片
time_prompt = f"现在的时间是{current_time},农历{current_lunar_date}"
prompts += time_prompt
marsho_prompt = config.marshoai_prompt
spell = SystemMessage(content=marsho_prompt+prompts).as_dict()
return spell