Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

config.py 16KB

10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
9 miesięcy temu
11 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
11 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
10 miesięcy temu
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # encoding:utf-8
  2. import json
  3. import logging
  4. import os
  5. import pickle
  6. import copy
  7. from common.log import logger
  8. # 将所有可用的配置项写在字典里, 请使用小写字母
  9. # 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到config.json中
  10. available_setting = {
  11. # openai api配置
  12. "open_ai_api_key": "", # openai api key
  13. # openai apibase,当use_azure_chatgpt为true时,需要设置对应的api base
  14. "open_ai_api_base": "https://api.openai.com/v1",
  15. "proxy": "", # openai使用的代理
  16. # chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称
  17. "model": "gpt-3.5-turbo", # 可选择: gpt-4o, pt-4o-mini, gpt-4-turbo, claude-3-sonnet, wenxin, moonshot, qwen-turbo, xunfei, glm-4, minimax, gemini等模型,全部可选模型详见common/const.py文件
  18. "bot_type": "", # 可选配置,使用兼容openai格式的三方服务时候,需填"chatGPT"。bot具体名称详见common/const.py文件列出的bot_type,如不填根据model名称判断,
  19. "use_azure_chatgpt": False, # 是否使用azure的chatgpt
  20. "azure_deployment_id": "", # azure 模型部署名称
  21. "azure_api_version": "", # azure api版本
  22. # Bot触发配置
  23. "single_chat_prefix": ["bot", "@bot"], # 私聊时文本需要包含该前缀才能触发机器人回复
  24. "single_chat_reply_prefix": "[bot] ", # 私聊时自动回复的前缀,用于区分真人
  25. "single_chat_reply_suffix": "", # 私聊时自动回复的后缀,\n 可以换行
  26. "group_chat_prefix": ["@bot"], # 群聊时包含该前缀则会触发机器人回复
  27. "group_chat_reply_prefix": "", # 群聊时自动回复的前缀
  28. "group_chat_reply_suffix": "", # 群聊时自动回复的后缀,\n 可以换行
  29. "group_chat_keyword": [], # 群聊时包含该关键词则会触发机器人回复
  30. "group_at_off": False, # 是否关闭群聊时@bot的触发
  31. "group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"], # 开启自动回复的群名称列表
  32. "group_name_keyword_white_list": [], # 开启自动回复的群名称关键词列表
  33. "group_chat_in_one_session": ["ChatGPT测试群"], # 支持会话上下文共享的群名称
  34. "nick_name_black_list": [], # 用户昵称黑名单
  35. "group_welcome_msg": "", # 配置新人进群固定欢迎语,不配置则使用随机风格欢迎
  36. "trigger_by_self": False, # 是否允许机器人触发
  37. "text_to_image": "dall-e-2", # 图片生成模型,可选 dall-e-2, dall-e-3
  38. # Azure OpenAI dall-e-3 配置
  39. "dalle3_image_style": "vivid", # 图片生成dalle3的风格,可选有 vivid, natural
  40. "dalle3_image_quality": "hd", # 图片生成dalle3的质量,可选有 standard, hd
  41. # Azure OpenAI DALL-E API 配置, 当use_azure_chatgpt为true时,用于将文字回复的资源和Dall-E的资源分开.
  42. "azure_openai_dalle_api_base": "", # [可选] azure openai 用于回复图片的资源 endpoint,默认使用 open_ai_api_base
  43. "azure_openai_dalle_api_key": "", # [可选] azure openai 用于回复图片的资源 key,默认使用 open_ai_api_key
  44. "azure_openai_dalle_deployment_id":"", # [可选] azure openai 用于回复图片的资源 deployment id,默认使用 text_to_image
  45. "image_proxy": True, # 是否需要图片代理,国内访问LinkAI时需要
  46. "image_create_prefix": ["画", "看", "找"], # 开启图片回复的前缀
  47. "concurrency_in_session": 1, # 同一会话最多有多少条消息在处理中,大于1可能乱序
  48. "image_create_size": "256x256", # 图片大小,可选有 256x256, 512x512, 1024x1024 (dall-e-3默认为1024x1024)
  49. "group_chat_exit_group": False,
  50. # chatgpt会话参数
  51. "expires_in_seconds": 3600, # 无操作会话的过期时间
  52. # 人格描述
  53. "character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
  54. "conversation_max_tokens": 1000, # 支持上下文记忆的最多字符数
  55. # chatgpt限流配置
  56. "rate_limit_chatgpt": 20, # chatgpt的调用频率限制
  57. "rate_limit_dalle": 50, # openai dalle的调用频率限制
  58. # chatgpt api参数 参考https://platform.openai.com/docs/api-reference/chat/create
  59. "temperature": 0.9,
  60. "top_p": 1,
  61. "frequency_penalty": 0,
  62. "presence_penalty": 0,
  63. "request_timeout": 180, # chatgpt请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
  64. "timeout": 120, # chatgpt重试超时时间,在这个时间内,将会自动重试
  65. # Baidu 文心一言参数
  66. "baidu_wenxin_model": "eb-instant", # 默认使用ERNIE-Bot-turbo模型
  67. "baidu_wenxin_api_key": "", # Baidu api key
  68. "baidu_wenxin_secret_key": "", # Baidu secret key
  69. # 讯飞星火API
  70. "xunfei_app_id": "", # 讯飞应用ID
  71. "xunfei_api_key": "", # 讯飞 API key
  72. "xunfei_api_secret": "", # 讯飞 API secret
  73. "xunfei_domain": "", # 讯飞模型对应的domain参数,Spark4.0 Ultra为 4.0Ultra,其他模型详见: https://www.xfyun.cn/doc/spark/Web.html
  74. "xunfei_spark_url": "", # 讯飞模型对应的请求地址,Spark4.0 Ultra为 wss://spark-api.xf-yun.com/v4.0/chat,其他模型参考详见: https://www.xfyun.cn/doc/spark/Web.html
  75. # claude 配置
  76. "claude_api_cookie": "",
  77. "claude_uuid": "",
  78. # claude api key
  79. "claude_api_key": "",
  80. # 通义千问API, 获取方式查看文档 https://help.aliyun.com/document_detail/2587494.html
  81. "qwen_access_key_id": "",
  82. "qwen_access_key_secret": "",
  83. "qwen_agent_key": "",
  84. "qwen_app_id": "",
  85. "qwen_node_id": "", # 流程编排模型用到的id,如果没有用到qwen_node_id,请务必保持为空字符串
  86. # 阿里灵积(通义新版sdk)模型api key
  87. "dashscope_api_key": "",
  88. # Google Gemini Api Key
  89. "gemini_api_key": "",
  90. # wework的通用配置
  91. "wework_smart": True, # 配置wework是否使用已登录的企业微信,False为多开
  92. # 语音设置
  93. "speech_recognition": True, # 是否开启语音识别
  94. "group_speech_recognition": False, # 是否开启群组语音识别
  95. "voice_reply_voice": False, # 是否使用语音回复语音,需要设置对应语音合成引擎的api key
  96. "always_reply_voice": False, # 是否一直使用语音回复
  97. "voice_to_text": "openai", # 语音识别引擎,支持openai,baidu,google,azure,xunfei,ali
  98. "text_to_voice": "openai", # 语音合成引擎,支持openai,baidu,google,azure,xunfei,ali,pytts(offline),elevenlabs,edge(online)
  99. "text_to_voice_model": "tts-1",
  100. "tts_voice_id": "alloy",
  101. # baidu 语音api配置, 使用百度语音识别和语音合成时需要
  102. "baidu_app_id": "",
  103. "baidu_api_key": "",
  104. "baidu_secret_key": "",
  105. # 1536普通话(支持简单的英文识别) 1737英语 1637粤语 1837四川话 1936普通话远场
  106. "baidu_dev_pid": 1536,
  107. # azure 语音api配置, 使用azure语音识别和语音合成时需要
  108. "azure_voice_api_key": "",
  109. "azure_voice_region": "japaneast",
  110. # elevenlabs 语音api配置
  111. "xi_api_key": "", # 获取ap的方法可以参考https://docs.elevenlabs.io/api-reference/quick-start/authentication
  112. "xi_voice_id": "", # ElevenLabs提供了9种英式、美式等英语发音id,分别是“Adam/Antoni/Arnold/Bella/Domi/Elli/Josh/Rachel/Sam”
  113. # 服务时间限制,目前支持itchat
  114. "chat_time_module": False, # 是否开启服务时间限制
  115. "chat_start_time": "00:00", # 服务开始时间
  116. "chat_stop_time": "24:00", # 服务结束时间
  117. # 翻译api
  118. "translate": "baidu", # 翻译api,支持baidu
  119. # baidu翻译api的配置
  120. "baidu_translate_app_id": "", # 百度翻译api的appid
  121. "baidu_translate_app_key": "", # 百度翻译api的秘钥
  122. # itchat的配置
  123. "hot_reload": False, # 是否开启热重载
  124. # wechaty的配置
  125. "wechaty_puppet_service_token": "", # wechaty的token
  126. # wechatmp的配置
  127. "wechatmp_token": "", # 微信公众平台的Token
  128. "wechatmp_port": 8080, # 微信公众平台的端口,需要端口转发到80或443
  129. "wechatmp_app_id": "", # 微信公众平台的appID
  130. "wechatmp_app_secret": "", # 微信公众平台的appsecret
  131. "wechatmp_aes_key": "", # 微信公众平台的EncodingAESKey,加密模式需要
  132. # wechatcom的通用配置
  133. "wechatcom_corp_id": "", # 企业微信公司的corpID
  134. # wechatcomapp的配置
  135. "wechatcomapp_token": "", # 企业微信app的token
  136. "wechatcomapp_port": 9898, # 企业微信app的服务端口,不需要端口转发
  137. "wechatcomapp_secret": "", # 企业微信app的secret
  138. "wechatcomapp_agent_id": "", # 企业微信app的agent_id
  139. "wechatcomapp_aes_key": "", # 企业微信app的aes_key
  140. # 飞书配置
  141. "feishu_port": 80, # 飞书bot监听端口
  142. "feishu_app_id": "", # 飞书机器人应用APP Id
  143. "feishu_app_secret": "", # 飞书机器人APP secret
  144. "feishu_token": "", # 飞书 verification token
  145. "feishu_bot_name": "", # 飞书机器人的名字
  146. # 钉钉配置
  147. "dingtalk_client_id": "", # 钉钉机器人Client ID
  148. "dingtalk_client_secret": "", # 钉钉机器人Client Secret
  149. "dingtalk_card_enabled": False,
  150. # chatgpt指令自定义触发词
  151. "clear_memory_commands": ["#清除记忆"], # 重置会话指令,必须以#开头
  152. # channel配置
  153. "channel_type": "", # 通道类型,支持:{wx,wxy,terminal,wechatmp,wechatmp_service,wechatcom_app,dingtalk}
  154. "subscribe_msg": "", # 订阅消息, 支持: wechatmp, wechatmp_service, wechatcom_app
  155. "debug": False, # 是否开启debug模式,开启后会打印更多日志
  156. "appdata_dir": "", # 数据目录
  157. # 插件配置
  158. "plugin_trigger_prefix": "$", # 规范插件提供聊天相关指令的前缀,建议不要和管理员指令前缀"#"冲突
  159. # 是否使用全局插件配置
  160. "use_global_plugin_config": False,
  161. "max_media_send_count": 3, # 单次最大发送媒体资源的个数
  162. "media_send_interval": 1, # 发送图片的事件间隔,单位秒
  163. # 智谱AI 平台配置
  164. "zhipu_ai_api_key": "",
  165. "zhipu_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
  166. "moonshot_api_key": "",
  167. "moonshot_base_url": "https://api.moonshot.cn/v1/chat/completions",
  168. # LinkAI平台配置
  169. "use_linkai": False,
  170. "linkai_api_key": "",
  171. "linkai_app_code": "",
  172. "linkai_api_base": "https://api.link-ai.tech", # linkAI服务地址
  173. "Minimax_api_key": "",
  174. "Minimax_group_id": "",
  175. "Minimax_base_url": "",
  176. }
  177. class Config(dict):
  178. def __init__(self, d=None):
  179. super().__init__()
  180. if d is None:
  181. d = {}
  182. for k, v in d.items():
  183. self[k] = v
  184. # user_datas: 用户数据,key为用户名,value为用户数据,也是dict
  185. self.user_datas = {}
  186. def __getitem__(self, key):
  187. if key not in available_setting:
  188. raise Exception("key {} not in available_setting".format(key))
  189. return super().__getitem__(key)
  190. def __setitem__(self, key, value):
  191. if key not in available_setting:
  192. raise Exception("key {} not in available_setting".format(key))
  193. return super().__setitem__(key, value)
  194. def get(self, key, default=None):
  195. try:
  196. return self[key]
  197. except KeyError as e:
  198. return default
  199. except Exception as e:
  200. raise e
  201. # Make sure to return a dictionary to ensure atomic
  202. def get_user_data(self, user) -> dict:
  203. if self.user_datas.get(user) is None:
  204. self.user_datas[user] = {}
  205. return self.user_datas[user]
  206. def load_user_datas(self):
  207. try:
  208. with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "rb") as f:
  209. self.user_datas = pickle.load(f)
  210. logger.info("[Config] User datas loaded.")
  211. except FileNotFoundError as e:
  212. logger.info("[Config] User datas file not found, ignore.")
  213. except Exception as e:
  214. logger.info("[Config] User datas error: {}".format(e))
  215. self.user_datas = {}
  216. def save_user_datas(self):
  217. try:
  218. with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "wb") as f:
  219. pickle.dump(self.user_datas, f)
  220. logger.info("[Config] User datas saved.")
  221. except Exception as e:
  222. logger.info("[Config] User datas error: {}".format(e))
  223. config = Config()
  224. def drag_sensitive(config):
  225. try:
  226. if isinstance(config, str):
  227. conf_dict: dict = json.loads(config)
  228. conf_dict_copy = copy.deepcopy(conf_dict)
  229. for key in conf_dict_copy:
  230. if "key" in key or "secret" in key:
  231. if isinstance(conf_dict_copy[key], str):
  232. conf_dict_copy[key] = conf_dict_copy[key][0:3] + "*" * 5 + conf_dict_copy[key][-3:]
  233. return json.dumps(conf_dict_copy, indent=4)
  234. elif isinstance(config, dict):
  235. config_copy = copy.deepcopy(config)
  236. for key in config:
  237. if "key" in key or "secret" in key:
  238. if isinstance(config_copy[key], str):
  239. config_copy[key] = config_copy[key][0:3] + "*" * 5 + config_copy[key][-3:]
  240. return config_copy
  241. except Exception as e:
  242. logger.exception(e)
  243. return config
  244. return config
  245. def load_config():
  246. global config
  247. config_path = "./config.json"
  248. if not os.path.exists(config_path):
  249. logger.info("配置文件不存在,将使用config-template.json模板")
  250. config_path = "./config-template.json"
  251. config_str = read_file(config_path)
  252. logger.debug("[INIT] config str: {}".format(drag_sensitive(config_str)))
  253. # 将json字符串反序列化为dict类型
  254. config = Config(json.loads(config_str))
  255. # override config with environment variables.
  256. # Some online deployment platforms (e.g. Railway) deploy project from github directly. So you shouldn't put your secrets like api key in a config file, instead use environment variables to override the default config.
  257. for name, value in os.environ.items():
  258. name = name.lower()
  259. if name in available_setting:
  260. logger.info("[INIT] override config by environ args: {}={}".format(name, value))
  261. try:
  262. config[name] = eval(value)
  263. except:
  264. if value == "false":
  265. config[name] = False
  266. elif value == "true":
  267. config[name] = True
  268. else:
  269. config[name] = value
  270. if config.get("debug", False):
  271. logger.setLevel(logging.DEBUG)
  272. logger.debug("[INIT] set log level to DEBUG")
  273. logger.info("[INIT] load config: {}".format(drag_sensitive(config)))
  274. config.load_user_datas()
  275. def get_root():
  276. return os.path.dirname(os.path.abspath(__file__))
  277. def read_file(path):
  278. with open(path, mode="r", encoding="utf-8") as f:
  279. return f.read()
  280. def conf():
  281. return config
  282. def get_appdata_dir():
  283. data_path = os.path.join(get_root(), conf().get("appdata_dir", ""))
  284. if not os.path.exists(data_path):
  285. logger.info("[INIT] data path not exists, create it: {}".format(data_path))
  286. os.makedirs(data_path)
  287. return data_path
  288. def subscribe_msg():
  289. trigger_prefix = conf().get("single_chat_prefix", [""])[0]
  290. msg = conf().get("subscribe_msg", "")
  291. return msg.format(trigger_prefix=trigger_prefix)
  292. # global plugin config
  293. plugin_config = {}
  294. def write_plugin_config(pconf: dict):
  295. """
  296. 写入插件全局配置
  297. :param pconf: 全量插件配置
  298. """
  299. global plugin_config
  300. for k in pconf:
  301. plugin_config[k.lower()] = pconf[k]
  302. def pconf(plugin_name: str) -> dict:
  303. """
  304. 根据插件名称获取配置
  305. :param plugin_name: 插件名称
  306. :return: 该插件的配置项
  307. """
  308. return plugin_config.get(plugin_name.lower())
  309. # 全局配置,用于存放全局生效的状态
  310. global_config = {"admin_users": []}