Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加机器人回复用户(好友)相应权限功能 #58

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ logs/
*.log.*

config.yaml

*.db
21 changes: 21 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ python main.py -c 2
>> 2. chatgpt 模型
>> 3. 讯飞星火模型
>> 4. chatglm 模型
>> 7. Azure 模型

6. 停止

Expand All @@ -103,12 +104,32 @@ groups:
enable: [] # 允许响应的群 roomId,大概长这样:2xxxxxxxxx3@chatroom, 多个群用 `,` 分隔
```

#### 管理用户(好友)的相应权限
第一次运行前设置,所有通讯录中的好友都将初次设置为此权限。以后每次打开会更新好友列表,但是不会更新权限。如果需要重新配置默认权限,请删除**你的微信id_permission.db**文件,再重新运行程序。
```yaml
permission:
default: 2 # 默认权限
# 0:群聊和私聊都不响应; 1:群聊不响应,私聊响应; 2:群聊响应,私聊不响应; 3:群聊和私聊都响应
```
如果要更新某用户(好友)的权限,可以在浏览器打开 http://127.0.0.1:5000 查看用户(好友)的微信id,然后在点击Update 或者访问 http://127.0.0.1:5000/update 填入新的权限信息,然后进行更新。

如果某用户不是好友,它第一次在群里中@你时,他的用户id会被记录到数据库中,默认权限为2,默认权限有效期为2099年。

不在数据库中的用户无法使用Web更新权限,但可以使用别的方法向数据库中添加记录。

#### 配置 AI 模型
为了使用 AI 模型,需要对相应模型并进行配置。

使用 ChatGLM 见注意事项 [README.MD](base/chatglm/README.MD)

```yaml
azure:
key: # 填写你 Azure OpenAI API key
endpoint: https://XXXXXX.openai.azure.com/
deployment_name: XXXXXX
api_version: 2024-02-15-preview
prompt: 你是智能聊天机器人,你叫 wcferry # 根据需要对角色进行设定

chatgpt: # -----chatgpt配置这行不填-----
key: # 填写你 ChatGPT 的 key
api: https://api.openai.com/v1 # 如果你不知道这是干嘛的,就不要改
Expand Down
98 changes: 98 additions & 0 deletions base/func_azure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import logging
from datetime import datetime

import httpx
from openai import APIConnectionError, APIError, AuthenticationError, AzureOpenAI


class Azure():
def __init__(self, conf: dict) -> None:
key = conf.get("key")
endpoint = conf.get("endpoint")
prompt = conf.get("prompt")
api_version = conf.get("api_version")
self.model = conf.get("deployment_name")
self.LOG = logging.getLogger("Azure")
self.client = AzureOpenAI(api_key=key, azure_endpoint=endpoint, api_version=api_version)
self.conversation_list = {}
self.system_content_msg = {"role": "system", "content": prompt}

def __repr__(self):
return 'Azure'

@staticmethod
def value_check(conf: dict) -> bool:
if conf:
if conf.get("key") and conf.get("endpoint") and conf.get("prompt") and conf.get("deployment_name") and conf.get("api_version"):
return True
return False

def get_answer(self, question: str, wxid: str) -> str:
# wxid或者roomid,个人时为微信id,群消息时为群id
self.updateMessage(wxid, question, "user")
rsp = ""
try:
ret = self.client.chat.completions.create(model=self.model,
messages=self.conversation_list[wxid],
temperature=0.5)
rsp = ret.choices[0].message.content
rsp = rsp[2:] if rsp.startswith("\n\n") else rsp
rsp = rsp.replace("\n\n", "\n")
self.updateMessage(wxid, rsp, "assistant")
except AuthenticationError:
self.LOG.error("Azure API 认证失败,请检查 API 密钥是否正确")
except APIConnectionError:
self.LOG.error("无法连接到 Azure API,请检查网络连接")
except APIError as e1:
self.LOG.error(f"Azure API 返回了错误:{str(e1)}")
except Exception as e0:
self.LOG.error(f"发生未知错误:{str(e0)}")

return rsp

def updateMessage(self, wxid: str, question: str, role: str) -> None:
time_mk = "-"
# 初始化聊天记录,组装系统信息
if wxid not in self.conversation_list.keys():
question_ = [
self.system_content_msg,
{"role": "system", "content": "" + time_mk}
]
self.conversation_list[wxid] = question_

# 当前问题
content_question_ = {"role": role, "content": question}
self.conversation_list[wxid].append(content_question_)
# for cont in self.conversation_list[wxid]:
# if cont["role"] != "system":
# continue
# if cont["content"].startswith(time_mk):
# cont["content"] = time_mk
# 只存储10条记录,超过滚动清除
i = len(self.conversation_list[wxid])
if i > 10:
print("滚动清除微信记录:" + wxid)
# 删除多余的记录,倒着删,且跳过第一个的系统消息
del self.conversation_list[wxid][1]


if __name__ == "__main__":
from configuration import Config
config = Config().AZURE
if not config:
exit(0)

chat = Azure(config)

while True:
q = input(">>> ")
try:
time_start = datetime.now() # 记录开始时间
print(chat.get_answer(q, "wxid"))
time_end = datetime.now() # 记录结束时间
print(f"{round((time_end - time_start).total_seconds(), 2)}s") # 计算的时间差为程序的执行时间,单位为秒/s
except Exception as e:
print(e)
11 changes: 11 additions & 0 deletions config.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,23 @@ logging:
groups:
enable: [] # 允许响应的群 roomId,大概长这样:2xxxxxxxxx3@chatroom

permission:
default: 2 # 默认权限
# 0:群聊和私聊都不响应; 1:群聊不响应,私聊响应; 2:群聊响应,私聊不响应; 3:群聊和私聊都响应

news:
receivers: [] # 定时新闻接收人(roomid 或者 wxid)

report_reminder:
receivers: [] # 定时日报周报月报提醒(roomid 或者 wxid)

azure:
key: # 填写你 Azure OpenAI API key
endpoint: https://XXXXXX.openai.azure.com/
deployment_name: XXXXXX
api_version: 2024-02-15-preview
prompt: 你是智能聊天机器人,你叫 wcferry # 根据需要对角色进行设定

chatgpt: # -----chatgpt配置这行不填-----
key: # 填写你 ChatGPT 的 key
api: https://api.openai.com/v1 # 如果你不知道这是干嘛的,就不要改
Expand Down
2 changes: 2 additions & 0 deletions configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ def reload(self) -> None:
yconfig = self._load_config()
logging.config.dictConfig(yconfig["logging"])
self.GROUPS = yconfig["groups"]["enable"]
self.PERMISSION = yconfig["permission"]["default"]
self.NEWS = yconfig["news"]["receivers"]
self.REPORT_REMINDERS = yconfig["report_reminder"]["receivers"]

self.AZURE = yconfig.get("azure", {})
self.CHATGPT = yconfig.get("chatgpt", {})
self.TIGERBOT = yconfig.get("tigerbot", {})
self.XINGHUO_WEB = yconfig.get("xinghuo_web", {})
Expand Down
1 change: 1 addition & 0 deletions constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ChatType(IntEnum):
CHATGLM = 4 # ChatGLM
BardAssistant = 5 # Google Bard
ZhiPu = 6 # ZhiPu
Azure = 7 # Azure

@staticmethod
def is_in_chat_types(chat_type: int) -> bool:
Expand Down
15 changes: 14 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
# -*- coding: utf-8 -*-

import signal
import threading
from argparse import ArgumentParser

from wcferry import Wcf

from base.func_report_reminder import ReportReminder
from configuration import Config
from constants import ChatType
from permission import init_permission, update_permission
from robot import Robot, __version__
from wcferry import Wcf


def weather_report(robot: Robot) -> None:
Expand Down Expand Up @@ -42,6 +45,16 @@ def handler(sig, frame):
# 机器人启动发送测试消息
robot.sendTextMsg("机器人启动成功!", "filehelper")

# 更新机器人回复用户(好友)响应权限
init_permission(wcf, config)
user_id = wcf.get_user_info()
user_id = user_id['wxid']
database_file = "{}_permission.db".format(user_id)

# add a threading to run update_permission
t = threading.Thread(target=update_permission, args=(database_file,))
t.start()

# 接收消息
# robot.enableRecvMsg() # 可能会丢消息?
robot.enableReceivingMsg() # 加队列
Expand Down
106 changes: 106 additions & 0 deletions permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import datetime
import os
import signal
import sqlite3
import threading

from flask import Flask, redirect, render_template, request, url_for
from wcferry import Wcf, WxMsg

from configuration import Config


def init_permission(wcf: Wcf, config: Config):
# permission:
# 0->群聊和私聊都不响应
# 1->群聊不响应,私聊响应
# 2->群聊响应,私聊不响应
# 3->群聊和私聊都响应
user_id = wcf.get_user_info()
user_id = user_id['wxid']

database_file = "{}_permission.db".format(user_id)
# 检查数据库是否存在,不存在以user_id为名创建,存在则打开数据库
if not os.path.exists(database_file):
conn = sqlite3.connect(database_file)
cursor = conn.cursor()
cursor.execute('''CREATE TABLE permission
(wxid text, code text, remark text, name text, country text, province text,city text, gender text, permission int, permission_end_time timestamp)''')
conn.commit()
# 将微信通讯录导入数据库,permission_end_time为2099-12-31 23:59:59
default_permission = config.PERMISSION
default_permission_end_time = '2099-12-31 23:59:59'
wcf.get_contacts()
contact_list = wcf.contacts
for contact in contact_list:
cursor.execute("INSERT INTO permission VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",(
contact['wxid'], contact['code'], contact['remark'], contact['name'], contact['country'], contact['province'], contact['city'], contact['gender'],default_permission, default_permission_end_time))
conn.commit()
# 关闭数据库
conn.close()
else:
conn = sqlite3.connect(database_file)
cursor = conn.cursor()
# 将更新后的微信通讯录存入数据库
wcf.get_contacts()
contact_list = wcf.contacts
for contact in contact_list:
# 查询数据库中是否存在该联系人
cursor.execute("SELECT * FROM permission WHERE wxid = ?", (contact['wxid'],))
result = cursor.fetchone()
if result is None:
cursor.execute("INSERT INTO permission VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",(
contact['wxid'], contact['code'], contact['remark'], contact['name'], contact['country'], contact['province'], contact['city'], contact['gender'], config.PERMISSION, '2099-12-31 23:59:59'))
else:
cursor.execute("UPDATE permission SET code = ?, remark = ?, name = ?, country = ?,province = ?, city = ?, gender = ? WHERE wxid = ?", (contact['code'], contact['remark'], contact['name'], contact['country'], contact['province'], contact['city'], contact['gender'], contact['wxid']))
conn.commit()
# 关闭数据库
conn.close()

def update_permission(DATABASE_URI):
app = Flask(__name__)

@app.route('/')
def index():
conn = sqlite3.connect(DATABASE_URI)
cursor = conn.cursor()
cursor.execute("SELECT * FROM permission")
rows = cursor.fetchall()
conn.close()
return render_template('index.html', users=rows)

@app.route('/update', methods=['POST', 'GET'])
def update():
if request.method == 'POST':
wxid = request.form['wxid_field']
permission = int(request.form['permission_field'])
permission_end_time = request.form['permission_end_time_field']
permission_end_time = permission_end_time.replace('T', ' ') + ":00"
conn = sqlite3.connect(DATABASE_URI)
cursor = conn.cursor()
cursor.execute("UPDATE permission SET permission = ?, permission_end_time = ? WHERE wxid = ?", (permission, permission_end_time, wxid))
conn.commit()
conn.close()
return redirect(url_for('index'))
else:
return render_template('update.html')

app.run(debug=False)

if __name__ == "__main__":
config = Config()
wcf = Wcf(debug=True)
def handler(sig, frame):
wcf.cleanup() # 退出前清理环境
exit(0)

signal.signal(signal.SIGINT, handler)

init_permission(wcf, config)
user_id = wcf.get_user_info()
user_id = user_id['wxid']
database_file = "{}_permission.db".format(user_id)

# add a threading to run update_permission
t = threading.Thread(target=update_permission, args=(database_file,))
t.start()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ zhdate
ipykernel
google-generativeai
zhipuai
Flask
Loading