Browse Source

using pickle instead of redis

master
JS00000 1 year ago
parent
commit
21a3b0d9a1
7 changed files with 57 additions and 26 deletions
  1. +1
    -0
      .gitignore
  2. +10
    -1
      app.py
  3. +0
    -1
      bot/chatgpt/chat_gpt_bot.py
  4. +3
    -6
      channel/wechatmp/README.md
  5. +2
    -7
      channel/wechatmp/wechatmp_channel.py
  6. +31
    -0
      config.py
  7. +10
    -11
      plugins/godcmd/godcmd.py

+ 1
- 0
.gitignore View File

@@ -10,3 +10,4 @@ nohup.out
tmp tmp
plugins.json plugins.json
itchat.pkl itchat.pkl
user_datas.pkl

+ 10
- 1
app.py View File

@@ -4,13 +4,22 @@ import os
from config import conf, load_config from config import conf, load_config
from channel import channel_factory from channel import channel_factory
from common.log import logger from common.log import logger

from plugins import * from plugins import *
import signal
import sys

def sigterm_handler(_signo, _stack_frame):
conf().save_user_datas()
sys.exit(0)


def run(): def run():
try: try:
# load config # load config
load_config() load_config()
# ctrl + c
signal.signal(signal.SIGINT, sigterm_handler)
# kill signal
signal.signal(signal.SIGTERM, sigterm_handler)


# create channel # create channel
channel_name=conf().get('channel_type', 'wx') channel_name=conf().get('channel_type', 'wx')


+ 0
- 1
bot/chatgpt/chat_gpt_bot.py View File

@@ -13,7 +13,6 @@ from common.expired_dict import ExpiredDict
import openai import openai
import openai.error import openai.error
import time import time
import redis


# OpenAI对话模型API (可用) # OpenAI对话模型API (可用)
class ChatGPTBot(Bot,OpenAIImage): class ChatGPTBot(Bot,OpenAIImage):


+ 3
- 6
channel/wechatmp/README.md View File

@@ -8,18 +8,15 @@


在开始部署前,你需要一个拥有公网IP的服务器,以提供微信服务器和我们自己服务器的连接。或者你需要进行内网穿透,否则微信服务器无法将消息发送给我们的服务器。 在开始部署前,你需要一个拥有公网IP的服务器,以提供微信服务器和我们自己服务器的连接。或者你需要进行内网穿透,否则微信服务器无法将消息发送给我们的服务器。


此外,需要在我们的服务器上安装额外的依赖web.py和redis,其中redis用来储存用户私有的配置信息
此外,需要在我们的服务器上安装python的web框架web.py
以ubuntu为例(在ubuntu 22.04上测试): 以ubuntu为例(在ubuntu 22.04上测试):
``` ```
sudo apt-get install redis
sudo systemctl start redis
pip3 install redis
pip3 install web.py pip3 install web.py
``` ```


然后在[微信公众平台](mp.weixin.qq.com)注册一个自己的公众号,类型选择订阅号,主体为个人即可。 然后在[微信公众平台](mp.weixin.qq.com)注册一个自己的公众号,类型选择订阅号,主体为个人即可。


然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址(URL)和令牌(Token)。这个Token是你自己编的一个特定的令牌。消息加解密方式目前选择的是明文模式。相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要将本项目根目录的`app.py`中channel_name改成"mp",将上述的Token填写在本项目根目录的`config.json`中,例如`"wechatmp_token": "Your Token",` 然后运行`python3 app.py`启动web服务器,然后在刚才的“服务器配置”中点击`提交`即可验证你的服务器。
然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址(URL)和令牌(Token)。这个Token是你自己编的一个特定的令牌。消息加解密方式目前选择的是明文模式。相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要在本项目根目录的`config.json`中添加`"channel_type": "wechatmp", "wechatmp_token": "your Token", ` 然后运行`python3 app.py`启动web服务器,然后在刚才的“服务器配置”中点击`提交`即可验证你的服务器。


随后在[微信公众平台](mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。 随后在[微信公众平台](mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。


@@ -29,7 +26,7 @@ pip3 install web.py
另外,由于微信官方的限制,自动回复有长度限制。因此这里将ChatGPT的回答拆分,分成每段600字回复(限制大约在700字)。 另外,由于微信官方的限制,自动回复有长度限制。因此这里将ChatGPT的回答拆分,分成每段600字回复(限制大约在700字)。


## 私有api_key ## 私有api_key
公共api有访问频率限制(免费账号每分钟最多20次ChatGPT的API调用),这在服务多人的时候会遇到问题。因此这里多加了一个设置私有api_key的功能,私有的api_key将储存在redis中。另外后续计划利用redis储存更多的用户个人配置。目前通过godcmd插件的命令来设置私有api_key。
公共api有访问频率限制(免费账号每分钟最多20次ChatGPT的API调用),这在服务多人的时候会遇到问题。因此这里多加了一个设置私有api_key的功能。目前通过godcmd插件的命令来设置私有api_key。


## 命令优化 ## 命令优化
之前plugin中#和$符号混用,且$这个符号在微信中和中文会有较大间隔,体验实在不好。这里我将所有命令更改成了以#开头。添加了一个叫finish的plugin来最后处理所有#结尾的命令,防止未知命令变成ChatGPT的query。 之前plugin中#和$符号混用,且$这个符号在微信中和中文会有较大间隔,体验实在不好。这里我将所有命令更改成了以#开头。添加了一个叫finish的plugin来最后处理所有#结尾的命令,防止未知命令变成ChatGPT的query。


+ 2
- 7
channel/wechatmp/wechatmp_channel.py View File

@@ -13,7 +13,6 @@ from bridge.reply import *
from bridge.context import * from bridge.context import *
from plugins import * from plugins import *
import traceback import traceback
import redis


# If using SSL, uncomment the following lines, and modify the certificate path. # If using SSL, uncomment the following lines, and modify the certificate path.
# from cheroot.server import HTTPServer # from cheroot.server import HTTPServer
@@ -181,12 +180,8 @@ class WechatMPChannel(Channel):
context = Context() context = Context()
context.kwargs = {'isgroup': False, 'receiver': fromUser, 'session_id': fromUser} context.kwargs = {'isgroup': False, 'receiver': fromUser, 'session_id': fromUser}


R = redis.Redis(host='localhost', port=6379, db=0)
user_openai_api_key = "openai_api_key_" + fromUser
api_key = R.get(user_openai_api_key)
if api_key != None:
api_key = api_key.decode("utf-8")
context['openai_api_key'] = api_key # None or user openai_api_key
user_data = conf().get_user_data(fromUser)
context['openai_api_key'] = user_data.get('openai_api_key') # None or user openai_api_key


img_match_prefix = check_prefix(message, conf().get('image_create_prefix')) img_match_prefix = check_prefix(message, conf().get('image_create_prefix'))
if img_match_prefix: if img_match_prefix:


+ 31
- 0
config.py View File

@@ -3,6 +3,7 @@
import json import json
import os import os
from common.log import logger from common.log import logger
import pickle


# 将所有可用的配置项写在字典里, 请使用小写字母 # 将所有可用的配置项写在字典里, 请使用小写字母
available_setting = { available_setting = {
@@ -88,6 +89,11 @@ available_setting = {




class Config(dict): class Config(dict):
def __init__(self, d:dict={}):
super().__init__(d)
# user_datas: 用户数据,key为用户名,value为用户数据,也是dict
self.user_datas = {}

def __getitem__(self, key): def __getitem__(self, key):
if key not in available_setting: if key not in available_setting:
raise Exception("key {} not in available_setting".format(key)) raise Exception("key {} not in available_setting".format(key))
@@ -106,6 +112,30 @@ class Config(dict):
except Exception as e: except Exception as e:
raise e raise e


# Make sure to return a dictionary to ensure atomic
def get_user_data(self, user) -> dict:
if self.user_datas.get(user) is None:
self.user_datas[user] = {}
return self.user_datas[user]

def load_user_datas(self):
try:
with open('user_datas.pkl', 'rb') as f:
self.user_datas = pickle.load(f)
logger.info("[Config] User datas loaded.")
except FileNotFoundError as e:
logger.info("[Config] User datas file not found, ignore.")
except Exception as e:
logger.info("[Config] User datas error: {}".format(e))
self.user_datas = {}

def save_user_datas(self):
try:
with open('user_datas.pkl', 'wb') as f:
pickle.dump(self.user_datas, f)
logger.info("[Config] User datas saved.")
except Exception as e:
logger.info("[Config] User datas error: {}".format(e))


config = Config() config = Config()


@@ -142,6 +172,7 @@ def load_config():


logger.info("[INIT] load config: {}".format(config)) logger.info("[INIT] load config: {}".format(config))


config.load_user_datas()


def get_root(): def get_root():
return os.path.dirname(os.path.abspath(__file__)) return os.path.dirname(os.path.abspath(__file__))


+ 10
- 11
plugins/godcmd/godcmd.py View File

@@ -7,11 +7,12 @@ from typing import Tuple
from bridge.bridge import Bridge from bridge.bridge import Bridge
from bridge.context import ContextType from bridge.context import ContextType
from bridge.reply import Reply, ReplyType from bridge.reply import Reply, ReplyType
from config import load_config
from config import conf, load_config
import plugins import plugins
from plugins import * from plugins import *
from common import const from common import const
from common.log import logger from common.log import logger
import pickle


# 定义指令集 # 定义指令集
COMMANDS = { COMMANDS = {
@@ -195,20 +196,18 @@ class Godcmd(Plugin):
ok, result = False, "unknown args" ok, result = False, "unknown args"
elif cmd == "set_openai_api_key": elif cmd == "set_openai_api_key":
if len(args) == 1: if len(args) == 1:
import redis
R = redis.Redis(host='localhost', port=6379, db=0)
user_openai_api_key = "openai_api_key_" + user
R.set(user_openai_api_key, args[0])
# R.sadd("openai_api_key", args[0])
user_data = conf().get_user_data(user)
user_data['openai_api_key'] = args[0]
ok, result = True, "你的OpenAI私有api_key已设置为" + args[0] ok, result = True, "你的OpenAI私有api_key已设置为" + args[0]
else: else:
ok, result = False, "请提供一个api_key" ok, result = False, "请提供一个api_key"
elif cmd == "reset_openai_api_key": elif cmd == "reset_openai_api_key":
import redis
R = redis.Redis(host='localhost', port=6379, db=0)
user_openai_api_key = "openai_api_key_" + user
R.delete(user_openai_api_key)
ok, result = True, "OpenAI的api_key已重置"
try:
user_data = conf().get_user_data(user)
user_data.pop('openai_api_key')
except Exception as e:
ok, result = False, "你没有设置私有api_key"
ok, result = True, "你的OpenAI私有api_key已清除"
# elif cmd == "helpp": # elif cmd == "helpp":
# if len(args) != 1: # if len(args) != 1:
# ok, result = False, "请提供插件名" # ok, result = False, "请提供插件名"


Loading…
Cancel
Save