├── .gitignore ├── FlaskApp ├── __init__.py ├── sae_py.py └── views.py ├── Handlers ├── __init__.py ├── api_handler.py └── weixin_handler.py ├── OnHomeward.jpg ├── README.md ├── Test.py ├── WeiXinCore ├── WeiXin.py ├── WeiXinMsg.py └── __init__.py ├── config.yaml ├── index.py └── index.wsgi /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | .idea/ 61 | tools.py 62 | -------------------------------------------------------------------------------- /FlaskApp/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | import FlaskApp.views 5 | from WeiXinCore.WeiXin import echo 6 | try: 7 | import sae 8 | import FlaskApp.sae_py 9 | except: 10 | print("no in sae.") -------------------------------------------------------------------------------- /FlaskApp/sae_py.py: -------------------------------------------------------------------------------- 1 | import MySQLdb 2 | import sae 3 | from sae.const import (MYSQL_HOST, MYSQL_HOST_S, 4 | MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB 5 | ) 6 | 7 | from flask import Flask, g, request 8 | from FlaskApp import app 9 | #app = Flask(__name__) 10 | #app.debug = True 11 | 12 | @app.before_request 13 | def before_request(): 14 | g.db = MySQLdb.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, 15 | MYSQL_DB, port=int(MYSQL_PORT)) 16 | 17 | 18 | @app.teardown_request 19 | def teardown_request(exception): 20 | if hasattr(g, 'db'): g.db.close() 21 | 22 | # @app.route('/') 23 | # def hello(): 24 | # return "Hello, I'm LinHy!" 25 | 26 | @app.route('/douban', methods=['GET', 'POST']) 27 | def douban(): 28 | return str( dict(request.args)) 29 | 30 | 31 | 32 | 33 | @app.route('/fankui', methods=['GET', 'POST']) 34 | def greeting(): 35 | html = '' 36 | 37 | if request.method == 'POST': 38 | c = g.db.cursor() 39 | c.execute("insert into demo(text) values(%s)", (request.form['text'])) 40 | 41 | html += """ 42 |
46 | """ 47 | c = g.db.cursor() 48 | c.execute('select time,content from fankui') 49 | msgs = list(c.fetchall()) 50 | msgs.reverse() 51 | for row in msgs: 52 | #ltime = time.localtime(float(row[0])) 53 | #dt = time.strftime("%Y-%m-%d %H:%M:%S", ltime) 54 | #html += '' + str(dt) + '<->' + row[-1] + '
' 55 | html += '' + row[-1] + '
' 56 | return html -------------------------------------------------------------------------------- /FlaskApp/views.py: -------------------------------------------------------------------------------- 1 | from FlaskApp import app 2 | 3 | 4 | 5 | 6 | @app.route('/') 7 | def index(): 8 | return "Hello, World!" 9 | 10 | # @app.route('/wx', methods = ['GET', 'POST'] ) 11 | # def wx(): 12 | # from WeiXinCore.WeiXin import check_signature,echo 13 | # from flask import request 14 | # if not check_signature(request.args) and not app.debug: 15 | # return "" 16 | # return echo -------------------------------------------------------------------------------- /Handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lhysrc/weixin_python/af45232bee7485294cb9eb634ffc97cc1c91b983/Handlers/__init__.py -------------------------------------------------------------------------------- /Handlers/api_handler.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import json 3 | import urllib2 4 | import zlib 5 | from urllib import * 6 | 7 | from WeiXinCore.WeiXinMsg import * 8 | 9 | 10 | def getJson(url): 11 | request = urllib2.Request(url) 12 | request.add_header('Accept-encoding', 'gzip') 13 | opener = urllib2.build_opener() 14 | response = opener.open(request) 15 | html = response.read()#.decode('gbk').encode('utf-8') 16 | gzipped = response.headers.get('Content-Encoding') 17 | if gzipped: 18 | html = zlib.decompress(html, 16+zlib.MAX_WBITS) 19 | return json.loads(html) 20 | #resp = urllib2.urlopen(url) 21 | #return json.loads(resp.read()) 22 | 23 | def youdao(text): 24 | url = 'http://fanyi.youdao.com/openapi.do?keyfrom=onHomeward&key=2010176806&type=data&doctype=json&version=1.1&q=%s' \ 25 | % quote_plus(text.encode('utf-8')) 26 | 27 | description = getJson(url)# json.loads(html) 28 | 29 | #resp = urllib2.urlopen(url) 30 | #description = json.loads(resp.read()) 31 | if description['errorCode'] is not 0: 32 | return u'**无法翻译**' 33 | return u' '.join(description['translation']) 34 | #result = [] 35 | #for k in description['subjects']: 36 | # item = {} 37 | # item["title"] = k['title']#.encode('gbk') 38 | # result.append(item) 39 | #return result 40 | 41 | 42 | 43 | def douban_dianying(text): 44 | url = 'http://api.douban.com/v2/movie/search?q=%s' % quote_plus(text.encode('utf-8')) 45 | description = getJson(url) 46 | 47 | if description['total'] is 0: 48 | return '' 49 | res = description['subjects'] 50 | items = [] 51 | for i in res: 52 | title = i['title'] 53 | rating = i['rating']['average'] 54 | picurl = i['images']['medium'] 55 | url = i['alt'] 56 | genres = '|'.join(i['genres']) 57 | items.append(NewsItem(u"%s (%s)" % (title,rating),genres,picurl,url)) 58 | return items[:5] 59 | -------------------------------------------------------------------------------- /Handlers/weixin_handler.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | from api_handler import * 4 | 5 | TOKEN = 'weixin' 6 | def onText(wxmsg): 7 | '''收到文本 8 | Content 文本消息内容''' 9 | inTxt = wxmsg.Content 10 | #return wxmsg.resp_text(wxmsg['FromUserName']) 11 | 12 | if inTxt.lower().startswith('fy'): 13 | return wxmsg.resp_text(youdao(inTxt[2:])) 14 | elif inTxt.startswith('?') or inTxt.startswith(u'?'): 15 | return wxmsg.resp_text(u'''通过发送以下列字符开头的消息可查询相关信息: 16 | fy 翻译(来自有道) 17 | dy 电影(来自豆瓣) 18 | 无前缀默认为查询电影''') 19 | 20 | # elif inTxt.startswith('!') or inTxt.startswith(u'!'): 21 | # c = g.db.cursor() 22 | # c.execute("insert into fankui(userid,time,content) values(%s,%s,%s)", \ 23 | # (wxmsg['FromUserName'],wxmsg['CreateTime'],inTxt[1:].encode('utf-8'))) 24 | # return wxmsg.resp_text(u'反馈已记录。') 25 | 26 | #c.execute('select content from fankui') 27 | #msgs = list(c.fetchall()) 28 | #msgs.reverse() 29 | #res = '' 30 | #for row in msgs: 31 | # res += row[-1] + ',' 32 | #return wxmsg.resp_text(res) 33 | 34 | 35 | 36 | else:# 37 | if inTxt.startswith('dy'): 38 | inTxt = inTxt[2:] 39 | newsItems = douban_dianying(inTxt) 40 | return wxmsg.resp_text(u'找不到') if not newsItems else wxmsg.resp_news(newsItems) 41 | 42 | #news1 = NewsItem(wxmsg.Content,youdao(wxmsg.Content),"","") 43 | #news2 = NewsItem("title2","yyyyyyyyyyy","picurl","url") 44 | #newsItems = [news1] 45 | #return wxmsg.resp_news(newsItems) 46 | 47 | def onImage(wxmsg): 48 | '''收到图片 49 | PicUrl 图片链接 50 | MediaId 图片消息媒体id,可以调用多媒体文件下载接口拉取数据。''' 51 | #return wxmsg.resp_music('Sorry','对不起,我还识别不了,来听首歌吧。',r'http://7s1r1i.com1.z0.glb.clouddn.com/小皮%20-%20村庄.mp3','') 52 | return wxmsg.resp_text(u'对不起,我还识别不了……') 53 | 54 | def onVoice(wxmsg): 55 | '''收到语音 56 | MediaId 语音消息媒体id,可以调用多媒体文件下载接口拉取数据。 57 | Format 语音格式,如amr,speex等 58 | Recognition为语音识别结果''' 59 | return wxmsg.resp_text(wxmsg.Recognition if wxmsg.Recognition is not 'None' else u"没听懂……") 60 | 61 | def onVideo(wxmsg): 62 | '''收到视频 63 | MediaId 视频消息媒体id,可以调用多媒体文件下载接口拉取数据。 64 | ThumbMediaId 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。''' 65 | return wxmsg.resp_text(u'对不起,我还识别不了……') 66 | 67 | def onShortVideo(wxmsg): 68 | '''收到小视频''' 69 | return wxmsg.resp_text(u'对不起,我还识别不了……') 70 | 71 | def onLocation(wxmsg): 72 | '''收到位置信息 73 | Location_X 地理位置维度 74 | Location_Y 地理位置经度 75 | Scale 地图缩放大小 76 | Label 地理位置信息''' 77 | txt = u"这是您所在位置:\nX:%s\nY:%s" % (wxmsg['Location_X'],wxmsg['Location_Y']) 78 | return wxmsg.resp_text(txt) 79 | 80 | def onLink(wxmsg): 81 | '''收到链接 82 | Title 消息标题 83 | Description 消息描述 84 | Url 消息链接''' 85 | return wxmsg.resp_text('url') 86 | 87 | def onSubscribe(wxmsg): 88 | '''关注''' 89 | return wxmsg.resp_text(u'感谢您的关注。你可以发“?”给我查看帮助。') 90 | 91 | def onUnsubscribe(wxmsg): 92 | '''取消关注''' 93 | return wxmsg.resp_text(u'oh,漏,你还没说为什么!') 94 | 95 | def onScan(wxmsg): 96 | '''扫描二维码''' 97 | return wxmsg.resp_text(wxmsg.self.Ticket) 98 | 99 | def onClick(wxmsg): 100 | '''点击菜单拉取消息时 101 | EventKey 事件KEY值,与自定义菜单接口中KEY值对应''' 102 | return wxmsg.resp_text('onClick') 103 | 104 | def onEventLocation(wxmsg): 105 | '''用户同意上报地理位置后,每次进入公众号会话时,都会在进入时上报地理位置 106 | 或在进入会话后每5秒上报一次地理位置,公众号可以在公众平台网站中修改以上设置。 107 | 上报地理位置时,微信会将上报地理位置事件推送到开发者填写的URL 108 | Latitude 地理位置纬度 109 | Longitude 地理位置经度 110 | Precision 地理位置精度''' 111 | return wxmsg.resp_text('xy') 112 | 113 | def onView(wxmsg): 114 | '''点击菜单跳转链接 115 | EventKey 事件KEY值,设置的跳转URL''' 116 | return wxmsg.resp_text('onView') 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /OnHomeward.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lhysrc/weixin_python/af45232bee7485294cb9eb634ffc97cc1c91b983/OnHomeward.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # weixin_python 2 | 微信公众号开发 3 | 基于python以及Flask 4 | ## 使用方法 5 | 6 | ### 修改weixin_handler文件: 7 | - 更改`\Handlers\weixin_handler.py`下的`TOKEN` 8 | - 实现`Handlers`下的`weixin_handler`相关方法 9 | 10 | ### 可直接上传至新浪sae使用 11 | - 修改`config.yaml`的`name`为自己的`appname` 12 | 13 | ## 尝试 14 | 可扫描关注此公众号了解功能。 15 |  -------------------------------------------------------------------------------- /Test.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | 4 | from WeiXinCore.WeiXinMsg import * 5 | 6 | 7 | weixin = WeiXinMsg(u''' 8 |