diff --git a/WXBotDemo.py b/WXBotDemo.py index 4bf2b94..e3421f7 100644 --- a/WXBotDemo.py +++ b/WXBotDemo.py @@ -25,8 +25,8 @@ def hello(): return json.dumps({"Code": 0}) +# 发送小程序模版,建议远程更新 def sendminiProgram(data, account_wxid, wxid, msgXml): - # 发送小程序 if data.get('isSender') != 1: root = ET.fromstring(msgXml) #xml-字符串形式 @@ -163,6 +163,10 @@ def kp(event_data): xml_msg.get('encryptusername'), xml_msg.get('ticket'), xml_msg.get('scene')) + newFriendWxid=xml_msg.get('alias') + # 将好友信息=>保存到数据库 + newFriendDict=WXBotService.get_friend_info(account_wxid, newFriendWxid) + elif msg_type == 42: # 42 名片消息 xml_msg = ET.fromstring(msg) kp_log = f'{type_str} 企业微信名片消息 {xml_msg.get("nickname")}({xml_msg.get("username")})' diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/crud.cpython-312.pyc b/__pycache__/crud.cpython-312.pyc new file mode 100644 index 0000000..d9b7e3e Binary files /dev/null and b/__pycache__/crud.cpython-312.pyc differ diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000..7ea8681 Binary files /dev/null and b/__pycache__/database.cpython-312.pyc differ diff --git a/__pycache__/main.cpython-310.pyc b/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000..aed2ba3 Binary files /dev/null and b/__pycache__/main.cpython-310.pyc differ diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..1d75b9f Binary files /dev/null and b/__pycache__/main.cpython-312.pyc differ diff --git a/__pycache__/models.cpython-312.pyc b/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..3a29d88 Binary files /dev/null and b/__pycache__/models.cpython-312.pyc differ diff --git a/__pycache__/planet.cpython-312.pyc b/__pycache__/planet.cpython-312.pyc new file mode 100644 index 0000000..971014e Binary files /dev/null and b/__pycache__/planet.cpython-312.pyc differ diff --git a/__pycache__/schemas.cpython-312.pyc b/__pycache__/schemas.cpython-312.pyc new file mode 100644 index 0000000..e3fa546 Binary files /dev/null and b/__pycache__/schemas.cpython-312.pyc differ diff --git a/app.db b/app.db new file mode 100644 index 0000000..2c38aca Binary files /dev/null and b/app.db differ diff --git a/crud.py b/crud.py new file mode 100644 index 0000000..af462f3 --- /dev/null +++ b/crud.py @@ -0,0 +1,41 @@ +from sqlalchemy.orm import Session + +# from . import models, schemas +import models, schemas + + +# 通过id获取用户 +def get_user(db: Session, user_id: int): + return db.query(models.User).filter(models.User.id == user_id).first() + + +# 通过openId岔村用户 +def get_user_by_openId(db: Session, openId: str): + return db.query(models.User).filter(models.User.openId == openId).first() + + +# 分页查询-用户all +def get_users(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.User).offset(skip).limit(limit).all() + + +# 创建用户 +# user:schemas.UserCreate +def create_user(db: Session, user:schemas.UserCreate): + db_user = models.User( + wxid=user.wxid, + openId=user.openId, + sex=user.sex, + country=user.country, + province=user.province, + sourceStr=user.sourceStr, + usertype=user.usertype, + nickname=user.nickname, + wxaccount=user.wxaccount, + remark=user.remark, + smallhead=user.smallhead, + ) + db.add(db_user) + db.commit() + db.refresh(db_user) + return db_user diff --git a/database.py b/database.py new file mode 100644 index 0000000..23bb2c2 --- /dev/null +++ b/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///./kunpeng.db" +# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() \ No newline at end of file diff --git a/kunpeng.db b/kunpeng.db new file mode 100644 index 0000000..efbb397 Binary files /dev/null and b/kunpeng.db differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..ca7325e --- /dev/null +++ b/main.py @@ -0,0 +1,321 @@ +from fastapi import Depends, FastAPI, HTTPException +from sqlalchemy.orm import Session + +# from . import crud, models, schemas +import crud, models, schemas +from database import SessionLocal, engine +# from .database import SessionLocal, engine + +import json +import os +import threading +import time + +from flask import request, Flask +try: + import xml.etree.cElementTree as ET +except ImportError: + import xml.etree.ElementTree as ET + +import WXBotService +from planet import planetXml +from lxml import etree +import xml.dom.minidom +from pydantic import BaseModel + + +models.Base.metadata.create_all(bind=engine) + +app = FastAPI() + + +# Dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +# 默认首页 +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +# 创建用户 +@app.post("/users/") +# schemas.UserCreate决定了swagger文档 +def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): + db_user = crud.create_user(db, user) + if db_user: + raise HTTPException(status_code=400, + detail="openId already registered") + return crud.create_user(db=db, user=user) + + +# 分页查询-用户 +@app.get("/users/") +def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + users = crud.get_users(db, skip=skip, limit=limit) + return users + + +# 根据openId查询用户 +@app.get("/users/{openId}") +def read_user(openId, db: Session = Depends(get_db)): + db_user = crud.get_user_by_openId(db, openId=openId) + if db_user is None: + raise HTTPException(status_code=404, detail="User not found") + return db_user + + +@app.get("/wxbot") +def helloget(q:dict): + data = q + t = threading.Thread(target=kp, args=(data, )) # 通过当前线程开启新的线程去执行 + t.start() + return json.dumps({"Code": 0}) + +@app.post("/wxbot") +# def hellopost(requestData:schemas.User): +def hellopost(requestData:dict): + + # data = request.get_json() + print('requestData---',requestData) + data = requestData + t = threading.Thread(target=kp, args=(data, )) # 通过当前线程开启新的线程去执行 + t.start() + return json.dumps({"Code": 0}) + + +# 发送小程序模版,建议远程更新 +def sendminiProgram(data, account_wxid, wxid, msgXml): + # 发送小程序 + if data.get('isSender') != 1: + root = ET.fromstring(msgXml) #xml-字符串形式 + + #打开xml文档 + # current_dir = os.path.dirname(os.path.abspath(__file__)) + # print('current_dir+/planet.xml',current_dir+'\planet.xml') + # tree = ET.parse(current_dir+'\planet.xml') + # root = tree.getroot() + + # 微信好友的wxid + # wxid='wxid_w2imh7ki5s4q12' + for pagepath in root.iter('pagepath'): + print(pagepath.text) # 打印名字 + pagepath.text = pagepath.text + f'?wxid={wxid}' + print('修改path', pagepath.text) # 打印名字 + + # 修改后的xml转成string , method='xml' + msgXml = ET.tostring(root, encoding='utf-8').decode('utf-8') + WXBotService.send_xml_message( + account_wxid, wxid, msgXml + # account_wxid, "wxid_w2imh7ki5s4q12", planetXml + ) + + +def kp(event_data): + # unicode [\\uxxxxx] 转 emoji 表情 + event_data = json.loads( + WXBotService.unicode_to_emoji( + json.dumps(event_data, ensure_ascii=False), 'json')) + + kp_log = '' # 定义鲲鹏日志消息 + event_type = event_data.get('event_type') #事件类型 + account_wxid = event_data.get('account_wxid') #机器人id + data = event_data.get('data') + msg_type = data.get('msgtype') #消息类型 + msg = data.get('content') + to_wxid = data.get('to_wxid') #发送给 + to_name = data.get('to_name') + filepath = data.get("filepath") + subtype = data.get('subtype') #类型 + thumbpath = data.get('thumbpath') #图片,存在电脑绝对路径 + + wxid = data.get('final_from_wxid') #消息来源用户 + wx_name = data.get('final_from_name') + + group = None if data.get('from_wxid') == wxid else data.get('from_wxid') + group_name = None if data.get('from_name') == wx_name else data.get( + 'from_name') + '''event类型判断''' + if event_type == 1001: # 1001 创建WeChat进程成功,通讯连接成功 + kp_log = f'创建WeChat进程成功,通讯连接成功' + elif event_type == 1002: # 1002 登录成功 + kp_log = f'登录成功 {data.get("nickname")}({data.get("wxid")})' + elif event_type == 1003: # 1003 账号注销登录 + kp_log = f'账号注销登录 {data.get("wxid")} {data.get("reason")}' + elif event_type == 1004: # 1004 切换聊天对象事件 + kp_log = f'切换聊天对象事件 {data.get("nickname")}({data.get("wxid")})' + elif event_type == 1005: # 1005 切换联系人事件 + kp_log = f'切换联系人事件 {data.get("nickname")}({data.get("wxid")})' + elif event_type == 1007: # 1005 二维码刷新事件 + kp_log = f'二维码刷新事件 {data.get("qrcode")}' + elif event_type == 1008: # 1008 连接断开 + kp_log = f'连接断开' + elif event_type == 1009: # 1009 群员退群通知 + member_list = [] + for member in data.get('memberlist'): + member_list.append( + f'{member.get("nickname")}({member.get("wxid")})') + kp_log = f'群员退群通知 {",".join(member_list)} 退出了群聊 {data.get("roomname")}({data.get("roomid")}) ' \ + f'人数:{data.get("membercount")}' + elif event_type == 1010: # 1010 群员进群通知 + member_list = [] + for member in data.get('memberlist'): + member_list.append( + f'{member.get("nickName")}({member.get("userName")})') + kp_log = f'群员进群通知 {",".join(member_list)} 加入了群聊 {data.get("roomname")}({data.get("roomid")})' + elif event_type == 1011: # 1011 收款消息 + pay_msg = ET.fromstring(msg).find('paymsg') + pay_msg_type = pay_msg.findtext('PayMsgType') + if pay_msg_type == '9': # 二维码收款 + status = pay_msg.findtext('status') + username = pay_msg.findtext('username') + display_name = pay_msg.findtext('displayname') + fee = pay_msg.findtext('fee') + if status == '0': + kp_log = f'收款消息 {display_name}({username}) 扫描收款二维码' + elif status == '1': + kp_log = f'收款消息 {display_name}({username}) 付款成功 {int(fee) / 100} 元' + elif status == '2': + kp_log = f'收款消息 {display_name}({username}) 取消付款' + elif pay_msg_type == '45': # 貌似有未收款转账或转账红包过期会触发 42 和 45 类型 + pass # kp_log = f'收款消息 {pay_msg.findtext("fromusername")} {pay_msg.findtext("content")}' + elif event_type == 1040: # 1040 撤回消息事件 + if not group: + kp_log = f'撤回消息事件 {wx_name}({wxid}) 撤回了 {msg}' + else: + kp_log = f'撤回消息事件 {wx_name}({wxid}) 从群 {group_name}({group}) 撤回了 {msg}' + elif event_type == 1100: # 1100 同步消息 + if group: + type_str = f'同步消息 群聊消息 群聊:{group_name}({group}) 发送者:{wx_name}({wxid})' + elif 'gh_' in wxid: + type_str = f'同步消息 公众号消息 {wx_name}({wxid})' + elif '@openim' in wxid: + type_str = f'同步消息 企业微信消息 {wx_name}({wxid})' + else: + type_str = f'同步消息 私聊消息 {wx_name}({wxid})' + + if data.get('isSender') == 1: + # 手机发送还是电脑发送这里判断不是很准确 + if to_wxid == account_wxid: + type_str = f'机器人手机端 发送的 ' + type_str + else: + type_str = f'机器人电脑端 发送的 ' + type_str + + if msg_type == 1: # 1 文本消息/自带表情 + kp_log = f'{type_str} 文本消息 内容:{msg}' + elif msg_type == 3: # 3 图片消息 + kp_log = f'{type_str} 图片消息 内容:{filepath}' + # 本地dat图片解密 + decrypt_path = WXBotService.local_decrypt_image( + filepath, os.path.join(os.getcwd(), '解密图片')) + print(f'{type_str} 图片消息 解密路径:{decrypt_path}') + elif msg_type == 34: # 34 语言消息(需要在设置中打开语言自动转文字) + xml_msg = ET.fromstring(msg) + kp_log = f'{type_str} 语音消息 内容:{xml_msg.find("voicetrans").get("transtext")}' + elif msg_type == 37: # 37 好友请求 + xml_msg = ET.fromstring(msg) + kp_log = f'同步消息 好友请求 {xml_msg.get("fromnickname")}({xml_msg.get("fromusername")}) ' \ + f'验证内容:{xml_msg.get("content")}' + # 同意好友请求 + WXBotService.agree_friend_request(account_wxid, + xml_msg.get('alias'), + xml_msg.get('encryptusername'), + xml_msg.get('ticket'), + xml_msg.get('scene')) + newFriendWxid=xml_msg.get('alias') + # 将好友信息=>保存到数据库 + newFriendDict=WXBotService.get_friend_info(account_wxid, newFriendWxid) + + elif msg_type == 42: # 42 名片消息 + xml_msg = ET.fromstring(msg) + kp_log = f'{type_str} 企业微信名片消息 {xml_msg.get("nickname")}({xml_msg.get("username")})' + elif msg_type == 43: # 43 视频消息 + kp_log = f'{type_str} 视频消息 内容:{filepath}' + elif msg_type == 47: # 47 表情包 + kp_log = f'{type_str} 表情包消息 网址:{ET.fromstring(msg).find("emoji").get("cdnurl".replace("amp;", ""))}' + # 本地下载图片 + down_path = WXBotService.local_download_image( + msg, os.path.join(os.getcwd(), '下载图片')) + print(f'{type_str} 表情包消息 下载路径:{down_path}') + elif msg_type == 48: # 48 地理位置 + kp_log = f'{type_str} 地理位置 内容:{msg}' + + elif msg_type == 49: # 49 xml消息 + # 获取根节点 + root = ET.fromstring(msg) + xml_appmsg = root.find('appmsg') + + xml_title = xml_appmsg.findtext('title') + xml_type = xml_appmsg.findtext('type') + xml_des = xml_appmsg.findtext('des') + xml_url = xml_appmsg.findtext('url') + + if xml_type == '1': # 1 文本 + kp_log = f'收到 {wxid} xml文本消息' + elif xml_type == '3': # 3 歌曲 + kp_log = f'{type_str} 音乐消息 歌曲:{xml_title} 歌手:{xml_des} 网址:{xml_url}' + elif xml_type == '5': # 5 链接 + kp_log = f'{type_str} 链接消息 标题:{xml_title} 内容:{xml_des} 网址:{xml_url}' + print("kp_log--------------------------------", msg) + # 本身也是系统消息-link,会无线循环 + # 判断是不是自己发的消息 + # if data.get('isSender') != 1: + # WXBotService.send_link_message( + # account_wxid, "wxid_w2imh7ki5s4q12", "title", "dec", + # "https://www.baidu.com", + # "https://p1-q.mafengwo.net/s17/M00/E3/9C/CoUBXl_cawCAQWMcAAGGhEoBcRU77.jpeg") + + elif xml_type == '6': # 6 文件 + kp_log = f'{type_str} 文件消息 文件名:{xml_title} 文件大小:{xml_des} 文件路径:{filepath}' + elif xml_type == '19': # 19 合并转发聊天记录 + kp_log = f'{type_str} 合并转发聊天记录 文件名:{xml_title} 文件大小:{xml_des} 文件路径:{filepath}' + elif xml_type == '33': # 33 小程序 + kp_log = f'{type_str} 小程序消息 小程序名:{xml_appmsg.findtext("sourcedisplayname")} 标题:{xml_title} ' \ + f'appid:{xml_appmsg.findtext("weappinfo/appid")} ' \ + f'pagepath:{xml_appmsg.findtext("weappinfo/pagepath")}' + print("---------------------------kp_log", msg) + # 发送小程序 + sendminiProgram(data, account_wxid, wxid, planetXml) + + elif xml_type == '57': # 引用消息 + kp_log = f'{type_str} 引用消息 内容:{msg}' + elif xml_type == '2000': # 2000 转账消息 + money = xml_appmsg.findtext('wcpayinfo/feedesc').replace( + '¥', '') + pay_subtype = xml_appmsg.findtext('wcpayinfo/paysubtype') + transfer_id = xml_appmsg.findtext('wcpayinfo/transferid') + if pay_subtype == '1': + kp_log = f'{type_str} 转账消息 {money} 元' + # 自动接收转账 + WXBotService.transfer_receipt(account_wxid, wxid, + transfer_id) + elif pay_subtype == '3': + kp_log = f'{type_str} 已收款 {money} 元 转账' + elif msg_type == 66: # 66 企业微信名片消息 + xml_msg = ET.fromstring(msg) + kp_log = f'企业微信名片消息 {xml_msg.get("nickname")}({xml_msg.get("username")})' + elif msg_type == 10000: # 10000 系统消息 + kp_log = f'系统消息 {wx_name}({wxid}) {msg}' + + #获取通讯录 + # allFriends=WXBotService.get_address_list(account_wxid) + # print('allFriends',allFriends) + # 发送H5链接 + + if not kp_log: + print( + '====================================================================================' + ) + print(event_data) + print( + '====================================================================================' + ) + else: + print(kp_log) + diff --git a/models.py b/models.py new file mode 100644 index 0000000..7bc5a26 --- /dev/null +++ b/models.py @@ -0,0 +1,22 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from database import Base +# from .database import Base + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True) + wxid = Column(String) + openId = Column(String) + sex = Column(String) #性别 + country = Column(String) #"CN" + province = Column(String) #"Hubei" + sourceStr = Column(String) #:"通过朋友验证消息添加" + usertype = Column(String) + nickname = Column(String) #昵称 + wxaccount = Column(String) #微信号 + remark = Column(String) #备注 + smallhead = Column(String) #头像 + diff --git a/schemas.py b/schemas.py new file mode 100644 index 0000000..d7c3fab --- /dev/null +++ b/schemas.py @@ -0,0 +1,28 @@ +from pydantic import BaseModel + + + + +# 用户信息 +class UserCreate(BaseModel): + password: str + wxid: str + openId: str + sex: str + country: str + province: str + sourceStr: str + usertype: str + nickname: str + wxaccount: str + remark: str + smallhead: str + + + +class User(UserCreate): + id: int + is_active: bool + + class Config: + orm_mode = True \ No newline at end of file