You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 satır
6.0KB

  1. # encoding:utf-8
  2. import json
  3. import os
  4. from bridge.bridge import Bridge
  5. from bridge.context import ContextType
  6. from bridge.reply import Reply, ReplyType
  7. from common import const
  8. import plugins
  9. from plugins import *
  10. from common.log import logger
  11. class RolePlay():
  12. def __init__(self, bot, sessionid, desc, wrapper=None):
  13. self.bot = bot
  14. self.sessionid = sessionid
  15. self.wrapper = wrapper or "%s" # 用于包装用户输入
  16. self.desc = desc
  17. self.bot.sessions.build_session(self.sessionid, system_prompt=self.desc)
  18. def reset(self):
  19. self.bot.sessions.clear_session(self.sessionid)
  20. def action(self, user_action):
  21. session = self.bot.sessions.build_session(self.sessionid)
  22. if session.system_prompt != self.desc: # 目前没有触发session过期事件,这里先简单判断,然后重置
  23. session.set_system_prompt(self.desc)
  24. prompt = self.wrapper % user_action
  25. return prompt
  26. @plugins.register(name="Role", desire_priority=0, namecn="角色扮演", desc="为你的Bot设置预设角色", version="1.0", author="lanvent")
  27. class Role(Plugin):
  28. def __init__(self):
  29. super().__init__()
  30. curdir = os.path.dirname(__file__)
  31. config_path = os.path.join(curdir, "roles.json")
  32. try:
  33. with open(config_path, "r", encoding="utf-8") as f:
  34. config = json.load(f)
  35. self.roles = {role["title"].lower(): role for role in config["roles"]}
  36. if len(self.roles) == 0:
  37. raise Exception("no role found")
  38. self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context
  39. self.roleplays = {}
  40. logger.info("[Role] inited")
  41. except FileNotFoundError:
  42. logger.warn(f"[Role] init failed, {config_path} not found, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/role .")
  43. except Exception as e:
  44. logger.warn("[Role] init failed, exception: %s, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/role ." % e)
  45. def get_role(self, name, find_closest=True):
  46. name = name.lower()
  47. found_role = None
  48. if name in self.roles:
  49. found_role = name
  50. elif find_closest:
  51. import difflib
  52. def str_simularity(a, b):
  53. return difflib.SequenceMatcher(None, a, b).ratio()
  54. max_sim = 0.0
  55. max_role = None
  56. for role in self.roles:
  57. sim = str_simularity(name, role)
  58. if sim >= max_sim:
  59. max_sim = sim
  60. max_role = role
  61. found_role = max_role
  62. return found_role
  63. def on_handle_context(self, e_context: EventContext):
  64. if e_context['context'].type != ContextType.TEXT:
  65. return
  66. bottype = Bridge().get_bot_type("chat")
  67. if bottype not in (const.CHATGPT, const.OPEN_AI):
  68. return
  69. bot = Bridge().get_bot("chat")
  70. content = e_context['context'].content[:]
  71. clist = e_context['context'].content.split(maxsplit=1)
  72. desckey = None
  73. customize = False
  74. sessionid = e_context['context']['session_id']
  75. if clist[0] == "$停止扮演":
  76. if sessionid in self.roleplays:
  77. self.roleplays[sessionid].reset()
  78. del self.roleplays[sessionid]
  79. reply = Reply(ReplyType.INFO, "角色扮演结束!")
  80. e_context['reply'] = reply
  81. e_context.action = EventAction.BREAK_PASS
  82. return
  83. elif clist[0] == "$角色":
  84. desckey = "descn"
  85. elif clist[0].lower() == "$role":
  86. desckey = "description"
  87. elif clist[0] == "$设定扮演":
  88. customize = True
  89. elif sessionid not in self.roleplays:
  90. return
  91. logger.debug("[Role] on_handle_context. content: %s" % content)
  92. if desckey is not None:
  93. if len(clist) == 1 or (len(clist) > 1 and clist[1].lower() in ["help", "帮助"]):
  94. reply = Reply(ReplyType.INFO, self.get_help_text(verbose=True))
  95. e_context['reply'] = reply
  96. e_context.action = EventAction.BREAK_PASS
  97. return
  98. role = self.get_role(clist[1])
  99. if role is None:
  100. reply = Reply(ReplyType.ERROR, "角色不存在")
  101. e_context['reply'] = reply
  102. e_context.action = EventAction.BREAK_PASS
  103. return
  104. else:
  105. self.roleplays[sessionid] = RolePlay(bot, sessionid, self.roles[role][desckey], self.roles[role].get("wrapper","%s"))
  106. reply = Reply(ReplyType.INFO, f"预设角色为 {role}:\n"+self.roles[role][desckey])
  107. e_context['reply'] = reply
  108. e_context.action = EventAction.BREAK_PASS
  109. elif customize == True:
  110. self.roleplays[sessionid] = RolePlay(bot, sessionid, clist[1], "%s")
  111. reply = Reply(ReplyType.INFO, f"角色设定为:\n{clist[1]}")
  112. e_context['reply'] = reply
  113. e_context.action = EventAction.BREAK_PASS
  114. else:
  115. prompt = self.roleplays[sessionid].action(content)
  116. e_context['context'].type = ContextType.TEXT
  117. e_context['context'].content = prompt
  118. e_context.action = EventAction.BREAK
  119. def get_help_text(self, verbose=False, **kwargs):
  120. help_text = "让机器人扮演不同的角色。\n"
  121. if not verbose:
  122. return help_text
  123. help_text = "使用方法:\n$开始扮演 {预设角色名}: 设定为预设角色\n$role {预设角色名}: 同上,但使用英文设定\n"
  124. help_text += "$设定扮演 {角色设定}: 设定自定义角色\n"
  125. help_text += "$停止扮演: 清除设定的角色。\n"
  126. help_text += "\n目前可用的预设角色名列表: \n"
  127. for role in self.roles:
  128. help_text += f"{role}: {self.roles[role]['remark']}\n"
  129. help_text += "\n命令例子: '$开始扮演 写作助理'"
  130. return help_text