mirror of
https://github.com/smallevilbeast/ntchat.git
synced 2025-05-23 02:46:06 +08:00
新增web接口完整例子
This commit is contained in:
parent
9505e69779
commit
708cc9e2d9
|
@ -120,6 +120,10 @@ except KeyboardInterrupt:
|
|||
sys.exit()
|
||||
```
|
||||
|
||||
## 使用fastapi框架实现的web api接口
|
||||
|
||||
[查看fastapi_example例子](./fastapi_example/README.md)
|
||||
|
||||
|
||||
## 使用pyxcgui界面库实现的简单例子
|
||||
|
||||
|
|
26
fastapi_example/README.md
Normal file
26
fastapi_example/README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
## NtChat fastapi完整示例
|
||||
|
||||
通过fastapi的swagger在线文档可以很方便的管理NtChat接口
|
||||

|
||||
|
||||
## 安装依赖
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 运行例子
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
## 访问api在线文档
|
||||
[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
||||
|
||||
|
||||
## 如何调用
|
||||
|
||||
可以使用requests库去访问接口
|
||||
|
||||
/client/create 是创建一个微信的实例,返回guid,标识实例的id, 后面所有的接口都要用到
|
||||
|
||||
/client/open 是打开并管理上微信实例
|
0
fastapi_example/__init__.py
Normal file
0
fastapi_example/__init__.py
Normal file
17
fastapi_example/down.py
Normal file
17
fastapi_example/down.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import os.path
|
||||
import time
|
||||
import requests
|
||||
from xdg import get_download_dir
|
||||
from models import SendMediaReqModel
|
||||
|
||||
|
||||
def get_local_path(model: SendMediaReqModel):
|
||||
if os.path.isfile(model.file_path):
|
||||
return model.file_path
|
||||
if not model.url:
|
||||
return None
|
||||
data = requests.get(model.url).content
|
||||
temp_file = os.path.join(get_download_dir(), str(time.time_ns()))
|
||||
with open(temp_file, 'wb') as fp:
|
||||
fp.write(data)
|
||||
return temp_file
|
8
fastapi_example/exception.py
Normal file
8
fastapi_example/exception.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
class ClientNotExists(Exception):
|
||||
guid = ""
|
||||
def __init__(self, guid):
|
||||
self.guid = guid
|
||||
|
||||
|
||||
class MediaNotExistsError(Exception):
|
||||
pass
|
240
fastapi_example/main.py
Normal file
240
fastapi_example/main.py
Normal file
|
@ -0,0 +1,240 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import uvicorn
|
||||
from functools import wraps
|
||||
from fastapi import FastAPI
|
||||
from mgr import ClientManager
|
||||
from typing import List
|
||||
from down import get_local_path
|
||||
from exception import MediaNotExistsError, ClientNotExists
|
||||
import models
|
||||
import ntchat
|
||||
|
||||
|
||||
def response_json(status=0, data=None, msg=""):
|
||||
return {
|
||||
"status": status,
|
||||
"data": {} if data is None else data,
|
||||
"msg": msg
|
||||
}
|
||||
|
||||
|
||||
class catch_exception:
|
||||
def __call__(self, f):
|
||||
@wraps(f)
|
||||
async def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return await f(*args, **kwargs)
|
||||
except ntchat.WeChatNotLoginError:
|
||||
return response_json(msg="wechat instance not login")
|
||||
except ntchat.WeChatBindError:
|
||||
return response_json(msg="wechat bind error")
|
||||
except ntchat.WeChatVersionNotMatchError:
|
||||
return response_json(msg="wechat version not match, install require wechat version")
|
||||
except MediaNotExistsError:
|
||||
return response_json(msg="file_path or url error")
|
||||
except ClientNotExists as e:
|
||||
return response_json(msg="client not exists, guid: %s" % e.guid)
|
||||
except Exception as e:
|
||||
return response_json(msg=str(e))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
client_mgr = ClientManager()
|
||||
app = FastAPI(title="NtChat fastapi完整示例",
|
||||
description="NtChat项目地址: https://github.com/smallevilbeast/ntchat")
|
||||
|
||||
|
||||
@app.post("/client/create", summary="创建实例", tags=["Client"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def client_create():
|
||||
guid = client_mgr.create_client()
|
||||
return response_json(1, {"guid": guid})
|
||||
|
||||
|
||||
@app.post("/client/open", summary="打开微信", tags=["Client"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def client_open(model: models.ClientOpenReqModel):
|
||||
ret = client_mgr.get_client(model.guid).open(model.smart)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/global/set_callback_url", summary="设置接收通知地址", tags=["Global"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def client_set_callback_url(model: models.CallbackUrlReqModel):
|
||||
client_mgr.callback_url = model.callback_url
|
||||
return response_json(1)
|
||||
|
||||
|
||||
@app.post("/user/get_profile", summary="获取自己的信息", tags=["User"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def user_get_profile(model: models.ClientReqModel):
|
||||
data = client_mgr.get_client(model.guid).get_self_info()
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/contact/get_contacts", summary="获取联系人列表", tags=["Contact"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def get_contacts(model: models.ClientReqModel):
|
||||
data = client_mgr.get_client(model.guid).get_contacts()
|
||||
print(data)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/contact/get_contact_detail", summary="获取指定联系人详细信息", tags=["Contact"],
|
||||
response_model=models.ContactDetailModel)
|
||||
@catch_exception()
|
||||
async def get_contact_detail(model: models.ContactDetailReqModel):
|
||||
data = client_mgr.get_client(model.guid).get_contact_detail(model.wxid)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/get_rooms", summary="获取群列表", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def get_rooms(model: models.ClientReqModel):
|
||||
data = client_mgr.get_client(model.guid).get_rooms()
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/get_room_members", summary="获取群成员列表", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def get_room_members(model: models.GetRoomMembersReqModel):
|
||||
data = client_mgr.get_client(model.guid).get_room_members(model.room_wxid)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/create_room", summary="创建群", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def create_room(model: models.CreateRoomReqModel):
|
||||
ret = client_mgr.get_client(model.guid).create_room(model.member_list)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/room/add_room_member", summary="添加好友入群", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def add_room_member(model: models.RoomMembersReqModel):
|
||||
data = client_mgr.get_client(model.guid).add_room_member(model.room_wxid, model.member_list)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/invite_room_member", summary="邀请好友入群", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def invite_room_member(model: models.RoomMembersReqModel):
|
||||
data = client_mgr.get_client(model.guid).invite_room_member(model.room_wxid, model.member_list)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/del_room_member", summary="删除群成员", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def del_room_member(model: models.RoomMembersReqModel):
|
||||
data = client_mgr.get_client(model.guid).del_room_member(model.room_wxid, model.member_list)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/add_room_friend", summary="添加群成员为好友", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def add_room_friend(model: models.AddRoomFriendReqModel):
|
||||
data = client_mgr.get_client(model.guid).add_room_friend(model.room_wxid,
|
||||
model.wxid,
|
||||
model.verify)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/room/modify_name", summary="修改群名", tags=["Room"],
|
||||
response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def add_room_friend(model: models.ModifyRoomNameReqModel):
|
||||
data = client_mgr.get_client(model.guid).modify_room_name(model.room_wxid,
|
||||
model.name)
|
||||
return response_json(1, data)
|
||||
|
||||
|
||||
@app.post("/msg/send_text", summary="发送文本消息", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def msg_send_text(model: models.SendTextReqModel):
|
||||
ret = client_mgr.get_client(model.guid).send_text(model.to_wxid, model.content)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_room_at", summary="发送群@消息", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_room_at(model: models.SendRoomAtReqModel):
|
||||
ret = client_mgr.get_client(model.guid).send_room_at_msg(model.to_wxid,
|
||||
model.content,
|
||||
model.at_list)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_card", summary="发送名片", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_card(model: models.SendCardReqModel):
|
||||
ret = client_mgr.get_client(model.guid).send_card(model.to_wxid,
|
||||
model.card_wxid)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_link_card", summary="发送链接卡片消息", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_link_card(model: models.SendLinkCardReqModel):
|
||||
ret = client_mgr.get_client(model.guid).send_link_card(model.to_wxid,
|
||||
model.title,
|
||||
model.desc,
|
||||
model.url,
|
||||
model.image_url)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_image", summary="发送图片", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_image(model: models.SendMediaReqModel):
|
||||
file_path = get_local_path(model)
|
||||
if file_path is None:
|
||||
raise MediaNotExistsError()
|
||||
ret = client_mgr.get_client(model.guid).send_image(model.to_wxid, file_path)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_file", summary="发送文件", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_file(model: models.SendMediaReqModel):
|
||||
file_path = get_local_path(model)
|
||||
if file_path is None:
|
||||
raise MediaNotExistsError()
|
||||
ret = client_mgr.get_client(model.guid).send_file(model.to_wxid, file_path)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_video", summary="发送视频", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_video(model: models.SendMediaReqModel):
|
||||
file_path = get_local_path(model)
|
||||
if file_path is None:
|
||||
raise MediaNotExistsError()
|
||||
ret = client_mgr.get_client(model.guid).send_video(model.to_wxid, file_path)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
@app.post("/msg/send_gif", summary="发送GIF", tags=["Msg"], response_model=models.ResponseModel)
|
||||
@catch_exception()
|
||||
async def send_gif(model: models.SendMediaReqModel):
|
||||
file_path = get_local_path(model)
|
||||
if file_path is None:
|
||||
raise MediaNotExistsError()
|
||||
ret = client_mgr.get_client(model.guid).send_gif(model.to_wxid, file_path)
|
||||
return response_json(1 if ret else 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app=app)
|
55
fastapi_example/mgr.py
Normal file
55
fastapi_example/mgr.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import ntchat
|
||||
import requests
|
||||
from typing import Dict, Union
|
||||
from ntchat.utils.singleton import Singleton
|
||||
from utils import generate_guid
|
||||
from exception import ClientNotExists
|
||||
|
||||
|
||||
class ClientWeChat(ntchat.WeChat):
|
||||
guid: str = ""
|
||||
|
||||
|
||||
class ClientManager(metaclass=Singleton):
|
||||
__client_map: Dict[str, ntchat.WeChat] = {}
|
||||
callback_url: str = ""
|
||||
|
||||
def new_guid(self):
|
||||
"""
|
||||
生成新的guid
|
||||
"""
|
||||
while True:
|
||||
guid = generate_guid("wechat")
|
||||
if guid not in self.__client_map:
|
||||
return guid
|
||||
|
||||
def create_client(self):
|
||||
guid = self.new_guid()
|
||||
wechat = ClientWeChat()
|
||||
wechat.guid = guid
|
||||
self.__client_map[guid] = wechat
|
||||
|
||||
# 注册回调
|
||||
wechat.on(ntchat.MT_ALL, self.__on_callback)
|
||||
return guid
|
||||
|
||||
def get_client(self, guid: str) -> Union[None, ntchat.WeChat]:
|
||||
client = self.__client_map.get(guid, None)
|
||||
if client is None:
|
||||
raise ClientNotExists(guid)
|
||||
return client
|
||||
|
||||
def remove_client(self, guid):
|
||||
if guid in self.__client_map:
|
||||
del self.__client_map[guid]
|
||||
|
||||
def __on_callback(self, wechat, message):
|
||||
if not self.callback_url:
|
||||
return
|
||||
|
||||
client_message = {
|
||||
"guid": wechat.guid,
|
||||
"message": message
|
||||
}
|
||||
requests.post(self.callback_url, json=client_message)
|
134
fastapi_example/models.py
Normal file
134
fastapi_example/models.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
from typing import Optional, List, Any, Union, Dict
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ClientReqModel(BaseModel):
|
||||
guid: str
|
||||
|
||||
|
||||
class ResponseModel(BaseModel):
|
||||
status: int
|
||||
msg: Optional[str] = ""
|
||||
data: Optional[Any] = None
|
||||
|
||||
|
||||
class ClientOpenReqModel(ClientReqModel):
|
||||
smart: Optional[bool] = True
|
||||
show_login_qrcode: Optional[bool] = False
|
||||
|
||||
|
||||
class CallbackUrlReqModel(BaseModel):
|
||||
callback_url: Optional[str] = ""
|
||||
|
||||
|
||||
class UserProfileModel(BaseModel):
|
||||
wxid: str
|
||||
nickname: str
|
||||
account: str
|
||||
avatar: str
|
||||
|
||||
|
||||
class ContactModel(BaseModel):
|
||||
account: str
|
||||
avatar: str
|
||||
city: str
|
||||
country: str
|
||||
nickname: str
|
||||
province: str
|
||||
remark: str
|
||||
sex: int
|
||||
wxid: str
|
||||
|
||||
|
||||
class ContactDetailReqModel(ClientReqModel):
|
||||
wxid: str
|
||||
|
||||
|
||||
class ContactDetailModel(BaseModel):
|
||||
account: str
|
||||
avatar: str
|
||||
city: str
|
||||
country: str
|
||||
nickname: str
|
||||
province: str
|
||||
remark: str
|
||||
sex: int
|
||||
wxid: str
|
||||
signature: str
|
||||
small_avatar: str
|
||||
sns_pic: str
|
||||
source_type: int
|
||||
status: int
|
||||
v1: str
|
||||
v2: str
|
||||
|
||||
|
||||
class AcceptFriendReqModel(ClientReqModel):
|
||||
encryptusername: str
|
||||
ticket: str
|
||||
scene: int
|
||||
|
||||
|
||||
class RoomModel(BaseModel):
|
||||
wxid: str
|
||||
nickname: str
|
||||
avatar: str
|
||||
is_manager: int
|
||||
manager_wxid: str
|
||||
total_member: int
|
||||
member_list: List[str]
|
||||
|
||||
|
||||
class RoomMemberModel(ContactModel):
|
||||
display_name: str
|
||||
|
||||
|
||||
class GetRoomMembersReqModel(ClientReqModel):
|
||||
room_wxid: str
|
||||
|
||||
|
||||
class CreateRoomReqModel(ClientReqModel):
|
||||
member_list: List[str]
|
||||
|
||||
|
||||
class RoomMembersReqModel(CreateRoomReqModel):
|
||||
room_wxid: str
|
||||
|
||||
|
||||
class AddRoomFriendReqModel(ClientReqModel):
|
||||
room_wxid: str
|
||||
wxid: str
|
||||
verify: str
|
||||
|
||||
|
||||
class ModifyRoomNameReqModel(ClientReqModel):
|
||||
room_wxid: str
|
||||
name: str
|
||||
|
||||
|
||||
class SendMsgReqModel(ClientReqModel):
|
||||
to_wxid: str
|
||||
|
||||
|
||||
class SendTextReqModel(SendMsgReqModel):
|
||||
content: str
|
||||
|
||||
|
||||
class SendRoomAtReqModel(SendTextReqModel):
|
||||
at_list: List[str]
|
||||
|
||||
|
||||
class SendCardReqModel(SendMsgReqModel):
|
||||
card_wxid: str
|
||||
|
||||
|
||||
class SendLinkCardReqModel(SendMsgReqModel):
|
||||
title: str
|
||||
desc: str
|
||||
url: str
|
||||
image_url: str
|
||||
|
||||
|
||||
class SendMediaReqModel(SendMsgReqModel):
|
||||
file_path: Optional[str] = ""
|
||||
url: Optional[str] = ""
|
4
fastapi_example/requirements.txt
Normal file
4
fastapi_example/requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
ntchat
|
||||
fastapi
|
||||
requests
|
||||
uvicorn
|
6
fastapi_example/utils.py
Normal file
6
fastapi_example/utils.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
import uuid
|
||||
import time
|
||||
|
||||
|
||||
def generate_guid(prefix=''):
|
||||
return str(uuid.uuid3(uuid.NAMESPACE_URL, prefix + str(time.time())))
|
14
fastapi_example/xdg.py
Normal file
14
fastapi_example/xdg.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import os
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
|
||||
def get_exec_dir():
|
||||
return os.path.dirname(sys.argv[0])
|
||||
|
||||
|
||||
def get_download_dir():
|
||||
user_dir = os.path.join(get_exec_dir(), 'download')
|
||||
if not os.path.isdir(user_dir):
|
||||
os.makedirs(user_dir)
|
||||
return user_dir
|
|
@ -335,7 +335,7 @@ class WeChat:
|
|||
"room_wxid": room_wxid,
|
||||
"notice": notice
|
||||
}
|
||||
return self.__send_sync(send_type.MT_MOD_ROOM_NAME_MSG, data)
|
||||
return self.__send_sync(send_type.MT_MOD_ROOM_NOTICE_MSG, data)
|
||||
|
||||
def add_room_friend(self, room_wxid: str, wxid: str, verify: str):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user