No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

127 líneas
5.1KB

  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. def reset(self):
  18. self.bot.sessions.clear_session(self.sessionid)
  19. def action(self, user_action):
  20. session = self.bot.sessions.build_session(self.sessionid, self.desc)
  21. if session[0]['role'] == 'system' and session[0]['content'] != self.desc: # 目前没有触发session过期事件,这里先简单判断,然后重置
  22. self.reset()
  23. self.bot.sessions.build_session(self.sessionid, self.desc)
  24. prompt = self.wrapper % user_action
  25. return prompt
  26. @plugins.register(name="Role", desc="为你的Bot设置预设角色", version="1.0", author="lanvent", desire_priority= 0)
  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.error(f"[Role] init failed, {config_path} not found")
  43. except Exception as e:
  44. logger.error("[Role] init failed, exception: %s" % 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 != const.CHATGPT:
  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. sessionid = e_context['context']['session_id']
  74. if clist[0] == "$停止扮演":
  75. if sessionid in self.roleplays:
  76. self.roleplays[sessionid].reset()
  77. del self.roleplays[sessionid]
  78. reply = Reply(ReplyType.INFO, "角色扮演结束!")
  79. e_context['reply'] = reply
  80. e_context.action = EventAction.BREAK_PASS
  81. return
  82. elif clist[0] == "$角色":
  83. desckey = "descn"
  84. elif clist[0].lower() == "$role":
  85. desckey = "description"
  86. elif sessionid not in self.roleplays:
  87. return
  88. logger.debug("[Role] on_handle_context. content: %s" % content)
  89. if desckey is not None:
  90. if len(clist) == 1 or (len(clist) > 1 and clist[1].lower() in ["help", "帮助"]):
  91. reply = Reply(ReplyType.INFO, self.get_help_text())
  92. e_context['reply'] = reply
  93. e_context.action = EventAction.BREAK_PASS
  94. return
  95. role = self.get_role(clist[1])
  96. if role is None:
  97. reply = Reply(ReplyType.ERROR, "角色不存在")
  98. e_context['reply'] = reply
  99. e_context.action = EventAction.BREAK_PASS
  100. return
  101. else:
  102. self.roleplays[sessionid] = RolePlay(bot, sessionid, self.roles[role][desckey], self.roles[role].get("wrapper","%s"))
  103. reply = Reply(ReplyType.INFO, f"角色设定为 {role} :\n"+self.roles[role][desckey])
  104. e_context['reply'] = reply
  105. e_context.action = EventAction.BREAK_PASS
  106. else:
  107. prompt = self.roleplays[sessionid].action(content)
  108. e_context['context'].type = ContextType.TEXT
  109. e_context['context'].content = prompt
  110. e_context.action = EventAction.BREAK
  111. def get_help_text(self, **kwargs):
  112. help_text = "输入\"$角色 {角色名}\"或\"$role {角色名}\"为我设定角色吧,\"$停止扮演 \" 可以清除设定的角色。\n\n目前可用角色列表:\n"
  113. for role in self.roles:
  114. help_text += f"[{role}]: {self.roles[role]['remark']}\n"
  115. return help_text