首页 > 其他分享 >基于FastApi的微信公众号开发

基于FastApi的微信公众号开发

时间:2022-10-22 20:22:58浏览次数:72  
标签:media FastApi content 公众 微信 msg rec wx

个人申请的订阅号,未认证,可用功能可在 微信公众号平台 - 接口权限 处查看
使用代码开发,首先需要在 基础配置-服务器配置 中进行设置

 填写服务器地址(URL)、Token和EncodingAESKey,其中 URL 是用来接收微信消息和事件的接口URL。Token可以任意填写,用作生成签名(该 Token 会和接口 URL 中包含的 Token 进行比对,从而验证安全性)。EncodingAESKey手动填写或随机生成,将用作消息体加解密密钥在点击【提交】按钮后,微信会向填写的服务器地址发送一个GET请求,携带signature、timestamp、nonce、echostr四个参数

 微信开发文档上给的加密规则:

  1)将token、timestamp、nonce三个参数进行字典序排序     

  2)将三个参数字符串拼接成一个字符串进行sha1加密     

  3)开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信

def sign_sha1(signature, timestamp, nonce):
    """
    服务器配置 验证
    :param signature:
    :param timestamp:
    :param nonce:
    :return:
    """
    temp = [TOKEN, timestamp, nonce]
    temp.sort()
    hashcode = hashlib.sha1("".join(temp).encode('utf-8')).hexdigest()
    logger.info(f"加密:{hashcode},微信返回:{signature}")
    if hashcode == signature:
        return True
 # 返回的 echostr 需要是整型。int(echostr)

fastapi接收接口

# main.py
from fastapi import FastAPI
from api import wechat

app = FastAPI(title="EasyTest接口项目", description="这是一个接口文档", version="1.0.0", docs_url=None, redoc_url=None)

app.include_router(
    wechat.router,
    prefix="/wx",
    tags=["wx"],
    responses=response_error
)
# wechat.py
from fastapi import APIRouter
from public.wx_public import sign_sha1

router = APIRouter()

@router.get("/", summary="微信服务器配置验证")
async def handle_wx(signature, timestamp, nonce, echostr):
    try:
        if sign_sha1(signature, timestamp, nonce):
            return int(echostr)
        else:
            logger.error("加密字符串 不等于 微信返回字符串,验证失败!!!")
            return "验证失败!"
    except Exception as error:
        return f"微信服务器配置验证出现异常:{error}"

关注/取消事件和被动回复用户消息

用户在关注与取消关注公众号时,微信会发送一个POST请求,携带xml数据包,把这个事件推送给服务器地址

 fastapi接收接口

import aiohttp

from starlette.responses import HTMLResponse, Response
# parse_xml, Message 参考地址:https://github.com/vastsa/Wechat-Fastapi
from public.wx_message import parse_xml, Message
from public.wx_public import sign_sha1, send_wx_msg

@router.post("/", summary="回复微信消息")
async def wx_msg(request: Request, signature, timestamp, nonce, openid):
    if sign_sha1(signature, timestamp, nonce):
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get("http://121.41.54.234/wx/login") as resp:
                    res = await resp.json()
            token = res["result"]["access_token"]
        except Exception as error:
            logger.error(f"获取微信登录token出现异常:{error}")
            token = ""
        try:
            rec_msg = parse_xml(await request.body())
            to_user = rec_msg.FromUserName
            from_user = rec_msg.ToUserName
            content, media_id = send_wx_msg(rec_msg, token)
            if rec_msg.MsgType == 'text' and not media_id:
                return Response(
                    Message(to_user, from_user, content=content).send(),
                    media_type="application/xml")
            elif rec_msg.MsgType == 'event' and content:
                return Response(
                    Message(to_user, from_user, content=content).send(), media_type="application/xml")
            elif rec_msg.MsgType == "image" or media_id:
                return Response(
                    Message(to_user, from_user, media_id=media_id, msg_type="image").send(),
                    media_type="application/xml")
        except Exception as error:
            logger.error(f"微信回复信息报错:{error}")
            return HTMLResponse('success')
  
  # wx_public.py
  def send_wx_msg(rec_msg, token):
    content, media_id = "", ""
    if rec_msg.MsgType == 'text':
        logger.info(f"文本信息:{rec_msg.Content}")
        patt = r"[\d+]{4}.[\d+]{1,2}.[\d+]{1,2}"
        content = re.findall(patt, rec_msg.Content)
        if content:
            # 输入出生年月日,回复信息
            content = age_content(content)
        else:
            content = rec_msg.Content
            if content in ["图片", "小七"] and token:
                # 输入 "图片", "小七" 回复图片
                media_id = wx_media(token)
            elif content in ["今天", "today"]:
                # 输入 "今天", "today" 回复信息
                content = fishing(make=True)
            elif content == "放假":
                # 输入 "放假" 回复信息
                content = fishing()
            else:
                # 输入 股票名称、股票代码 回复信息
                content = shares(stock_code=rec_msg.Content)
                if not content:
                    # 根据输入值返回
                    content = rec_msg.Content
    elif rec_msg.MsgType == 'event':
        if rec_msg.Event == "subscribe":
            # 关注公众号回复信息
            content = FOLLOW
        elif rec_msg.Event == "unsubscribe":
            # 取消关注
            logger.info(f"用户 {rec_msg.FromUserName} 取消关注了!!!")
    elif rec_msg.MsgType == "image":
        # 收到图片返回
        if token:
            media_id = wx_media(token)
        else:
            media_id = rec_msg.MediaId
    return content, media_id

log日志打印信息

 公众号效果

 

标签:media,FastApi,content,公众,微信,msg,rec,wx
From: https://www.cnblogs.com/changqing8023/p/16817207.html

相关文章