Python搭建微信后台实现图片加V功能

python学习 沙发 4月月 28日, 2017

最近对Python感兴趣,看到各位大神用Python搭建微信公共平台,本着爱专研精神,就开始研究自己搭建,并且实现小功能,比如给头像加V。

首先你需要一个微信公共平台测试号(ps:只要是能获取素材管理权限就行),然后你需要一台服务器VPS,或者本机搭建也行,需要搭配端口映射比如ngrok能让微信服务器访问你本机。我的环境是Python3.6+Flask,前期的微信地址验证什么的相信大家都会,我这里直接就放我的解决过程吧。

整体流程:

为什么要流程图?这里我觉得大家无论做什么事都要先花个80%的时间去想,再20%时间去做,一定做得不会差。通过我上面的流程图可以分析出功能:

  • 用户首次关注、退订(关注和退订是Event事件)我们需要回复相关信息
  • 用户发送视屏、语音返回信息:暂未开发
  • 用户发送文本信息,就返回图灵机器人说的话
  • 用户发送图片信息,就调用加logo的功能,再把图片返回用户

我把APPID,APPSECRET,和微信公共平台的请求地址单独放在config里,方便更换

config.py:

APPID = 'wx622b294726c11'
APPSECRET = '48395037d9a55404cac7ffdc4826611'
WeiXin_ImgUrl='https://api.weixin.qq.com/cgi-bin/media/upload' #临时素材URL
WeiXin_Final_ImgUrl='https://api.weixin.qq.com/cgi-bin/material/add_material'#永久素材URL

(主程序位置)hello.py:

from flask import Flask,g,request,make_response
import requests
import xml.etree.ElementTree as ET
import  hashlib,os
import  time
import json
from PIL import Image, ImageEnhance, ImageFilter
from config import *

app = Flask(__name__)

@app.route('/weixin',methods=['GET','POST'])


def wexin():
    global content
    if request.method=='GET':
        token = 'weixin'
        data = request.args
        signature = data.get('signature', '')
        timestamp = data.get('timestamp', '')
        nonce = data.get('nonce', '')
        echostr = data.get('echostr', '')
        s = [timestamp, nonce, token]
        s.sort()
        s = ''.join(s)
        s = s.encode('utf-8')
        if (hashlib.sha1(s).hexdigest() == signature):
            return make_response(echostr)
    else:
        rec = request.stream.read()
        global xml_rec
        xml_rec = ET.fromstring(rec)
        print(xml_rec)
        tou = xml_rec.find('ToUserName').text
        fromu = xml_rec.find('FromUserName').text
        msgtype = xml_rec.find('MsgType').text
        print(msgtype)
        if msgtype=='text':
            content = xml_rec.find('Content').text
            xml_rep = "<xml>" \
                      "<ToUserName><![CDATA[%s]]></ToUserName>" \
                      "<FromUserName><![CDATA[%s]]></FromUserName>" \
                      "<CreateTime>%s</CreateTime>" \
                      "<MsgType><![CDATA[text]]></MsgType>" \
                      "<Content><![CDATA[%s]]></Content>" \
                      "<FuncFlag>0</FuncFlag>" \
                      "</xml>"
            response = make_response(xml_rep % (fromu, tou, str(int(time.time())), gettuling(content, fromu)))
            response.content_type = 'application/xml'
            return response
        elif msgtype=='event':
            event =xml_rec.find('Event').text
            if event =='subscribe':
                xml_rep = "<xml>" \
                          "<ToUserName><![CDATA[%s]]></ToUserName>" \
                          "<FromUserName><![CDATA[%s]]></FromUserName>" \
                          "<CreateTime>%s</CreateTime>" \
                          "<MsgType><![CDATA[text]]></MsgType>" \
                          "<Content><![CDATA[%s]]></Content>" \
                          "<FuncFlag>0</FuncFlag>" \
                          "</xml>"
                response = make_response(xml_rep % (fromu, tou, str(int(time.time())), '欢迎订阅我的测试公共账号!'))
                response.content_type = 'application/xml'
                return response
        elif msgtype == 'image':
            MediaId = xml_rec.find('MediaId').text
            print("kaishishiwuchuli====="+MediaId)  #开始事务处理
            access_token=get_acces_token() #获取access_token
            download_imge(access_token,MediaId)# 下载图片到本地
            orginal = 'logo_' + MediaId[0:10] + '.jpg' #把图片加水印好要保存的文件名
            watermark(MediaId + '.jpg', 'water.png', orginal, 200, 200) #加水印方法
            filename = MediaId + '.jpg'   #删除用户上传的图片
            if os.path.exists(filename):
                os.remove(filename)
            dict = get_media_ID(orginal,access_token) #返回mediaid及url

            xml_rep2 = "<xml>" \
                       "<ToUserName><![CDATA[%s]]></ToUserName>" \
                       "<FromUserName><![CDATA[%s]]></FromUserName>" \
                       "<CreateTime>%s</CreateTime>" \
                       "<MsgType><![CDATA[image]]></MsgType>" \
                       "<Image>" \
                       "<MediaId><![CDATA[%s]]></MediaId>"\
                       "</Image>"\
                        "</xml>"

            response = make_response(xml_rep2 % (fromu, tou, str(int(time.time())), dict['media_id']))
            response.content_type = 'application/xml'

        return response

@app.route('/li',methods=['GET','POST'])



def gettuling(txt, usrid):
    """
    图灵机器人API
    :param txt:传入的文字 
    :param usrid: 用户id 上下文需要用到,这里传入微信用户的ID
    :return: 经过API转化后的语言
    """
    da = {'key': '6946b4a1f7b847edb300d63b3499a5dc', 'info': txt, 'userid': usrid}
    date = json.dumps(da)
    response = requests.post('http://www.tuling123.com/openapi/api', data=date)
    str = json.loads(response.text)
    code = str['code']
    if code == 100000:
        recontent = str['text']
    elif code == 200000:
        recontent = str['text'] + str['url']
    elif code == 302000:
        recontent = str['text'] + str['list'][0]['article'] + str['list'][0]['detailurl']

    return recontent
def watermark(img_source, img_water, img_new, offset_x, offset_y):
    """
    给图片加水印    
    """
    print("water img ")
    try:
        im = Image.open(img_source)
        wm = Image.open(img_water)
        layer = Image.new('RGBA', im.size, (0,0,0,0))
        layer.paste(wm, (im.size[0] - offset_x, im.size[1] - offset_y))
        newIm = Image.composite(layer, im, layer)
        newIm.save(img_new)

    except Exception as e:
        print(">>>>>>>>>>> WaterMark EXCEPTION:  " + str(e))
        return False
    else:
        return True

def get_media_ID(path,acces_token):
    """
    上传加过v的图片到微信服务器并返回对应id和url
    :param path 文件名称
    :arg acces_token token值
    """
    print('upload img ')
    payload_img={
        'access_token':acces_token,
        'type':'image'
    }
    data ={'media':open(path,'rb')}

    r=requests.post(url=WeiXin_ImgUrl,params=payload_img,files=data)
    dict =r.json()
    print(dict)
    return dict#['media_id']

def download_imge(ACCESS_TOKEN,MEDIA_ID):
    """
    获取临时素材到本地
    """
    print('download img')
    img=requests.get('https://api.weixin.qq.com/cgi-bin/media/get?access_token='+ACCESS_TOKEN+'&media_id='+MEDIA_ID)
    file_path = '{0}/{1}.{2}'.format(os.getcwd(), MEDIA_ID, 'jpg')
    print(os.getcwd())
    if not os.path.exists(file_path):
        with open(file_path, 'wb') as f:
            f.write(img.content)
            f.close()



def get_acces_token():
    """
    获取access_token
    """
    response = requests.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+APPID+'&secret='+APPSECRET)
    acces_token=json.loads(response.text).get('access_token')
    return acces_token

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='80')

代码有点长,毕竟刚开始学10来天,这个小项目还有一些缺点:

  1. 某些模块没有捕获异常,比如API次数访问受限了。应该返回相关信息。
  2. XML应该整成一个模板。
  3. 没有加入判断语音,和视屏的功能

最后给大家再放一下效果图:

回复的是图文消息,我刚刚改进了。这个也很简单,去改下xml,和上传永久图片素材就可以

点开效果图:

后期可以扩展的有:让用户选择水印模板,水印的位置,比如右上角加一个1字。可以逼死强迫症了。呵呵,类似这样:

后期我还会放出一些用Python做出来的好玩的东西,希望大家多多关注我,也欢迎大神们指导一二。

发表评论

昵称*

邮箱*

网址