Chengqi
なぜなら、私は以前にユーザーの記事に応答する WeChat ロボットを設計したことがあるため、このアプリは非常にシンプルで、特に綿密な設計を必要としません。さらに、私のアイデアは、それを原則として使用することです。 Python で書かれた多くのブログ システムでは、データベースから記事データを取得し、記事のタイトル、URL、写真、その他の情報を XML 形式にパッケージ化して返す WeChat 応答部分のみを実装する必要があります。それを WeChat サーバーに送信し、ユーザーに返します。そして、厳格に返信するのではなく、直接クリックして記事を表示できる、完全なアプリのようなメニューがあったほうがはるかに優れていることがわかりました。他の人が書いたブログ システム (saepy-log) を使用して改造しました。このブログ システムはトルネード フレームワークに基づいています。私は当初トルネードに参加するつもりはありませんでしたが、思い切ってそれを掘り下げる必要がありました。 。たくさんの困難に遭遇しましたが、SQL ステートメントなどの記述やドキュメントの表示に関して多くのことを得ることができました。
デプロイと開発
色々苦労したのでこの記事の通りにできない可能性があることを予めご了承ください。 saepy-log のソース コードをダウンロードして、ここでの操作に従ってアップロードした後、sae プラットフォームにブログ システムをインストールし、svn を使用してコードをローカルの作業ディレクトリに同期すれば、準備は完了です。
変更したいのは、blog.py がブログのコア関数であり、model.py がデータ モデルのキーであるということです。データ モデル関数を拡張して WeChat 機能を完成させます。
WeChat 関数クラス weixin.py を blog.py に追加します (Tornado フレームワークを使用しているため、方法が django のものとは少し異なります):
使用する必要があるパッケージをインポートします
# weixin used package import xml.etree.ElementTree as ET import urllib,urllib2,time,hashlib import tornado.wsgi import tornado.escape
主に XML 解析次に、weixin クラスの本体を定義します:
# 添加微信推送帐号 class WeiXinPoster(BaseHandler): #----------------------------------------------------------------------- # 处理get方法 对应check_signature def get(self): global TOKEN signature = self.get_argument("signature") timestamp = self.get_argument("timestamp") nonce = self.get_argument("nonce") echoStr = self.get_argument("echostr") token = TOKEN tmpList = [token,timestamp,nonce] tmpList.sort() tmpstr = "%s%s%s" % tuple(tmpList) tmpstr = hashlib.sha1(tmpstr).hexdigest() if tmpstr == signature: self.write(echoStr) #return echoStr else: self.write(None); #return None # 处理post方法,对应response_msg def post(self): global SORRY # 从request中获取请求文本 rawStr = self.request.body # 将文本进行解析,得到请求的数据 msg = self.parse_request_xml(ET.fromstring(rawStr)) # 根据请求消息来处理内容返回 query_str = msg.get("Content") query_str = tornado.escape.utf8(query_str) # TODO 用户发来的数据类型可能多样,所以需要判别 response_msg = "" return_data = "" # 使用简单的处理逻辑,有待扩展 if query_str[0] == "h": # send help menu to user response_msg = self.get_help_menu() # 返回消息 # 包括post_msg,和对应的 response_msg if response_msg: return_data = self.pack_text_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) # 分类 elif query_str[0] =="c": category = query_str[1:] response_msg = self.get_category_articles(category) if response_msg: return_data = self.pack_news_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) # 列出文章列表 elif query_str[0] =="l": response_msg = self.get_article_list() if response_msg: return_data = self.pack_text_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) # 直接获取某篇文章 elif query_str[0] == "a": # 直接获取文章的id,然后在数据库中查询 article_id = int(query_str[1:]) # 进行操作 response_msg = self.get_response_article_by_id(article_id) if response_msg: return_data = self.pack_news_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) # 还要考虑其他 elif query_str[0] == "s": keyword = str(query_str[1:]) # 搜索关键词,返回相关文章 response_msg = self.get_response_article(keyword) # 返回图文信息 if response_msg: return_data = self.pack_news_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) elif query_str[0] == "n": response_msg = self.get_latest_articles() # 返回图文信息 if response_msg: return_data = self.pack_news_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data) # 如果找不到,返回帮助信息 else: response_msg = get_help_menu() if response_msg: return_data = response_msg else: return_data = SORRY self.write(return_data) # n for 获取最新的文章 def get_latest_articles(self): global MAX_ARTICLE global PIC_URL article_list = Article.get_articles_by_latest() article_list_length = len(article_list) count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE if article_list: # 构造图文消息 articles_msg = {'articles':[]} for i in range(0,count): article = { 'title': article_list[i].slug, 'description':article_list[i].description, 'picUrl':PIC_URL, 'url':article_list[i].absolute_url } # 插入文章 articles_msg['articles'].append(article) article = {} # 返回文章 return articles_msg #----------------------------------------------------------------------- # 解析请求,拆解到一个字典里 def parse_request_xml(self,root_elem): msg = {} if root_elem.tag == 'xml': for child in root_elem: msg[child.tag] = child.text # 获得内容 return msg #----------------------------------------------------------------------- def get_help_menu(self): menu_msg = '''欢迎关注南苑随笔,在这里你能获得关于校园的资讯和故事。回复如下按键则可以完成得到相应的回应 h :帮助(help) l :文章列表(article list) f : 获得分类列表 n : 获取最新文章 a + 数字 :察看某篇文章 a2 察看第2篇文章 s + 关键字 : 搜索相关文章 s科研 察看科研相关 c + 分类名 : 获取分类文章 c校园生活 察看校园生活分类 其他 : 功能有待丰富''' return menu_msg #----------------------------------------------------------------------- # 获取文章列表 def get_article_list(self): # 查询数据库获取文章列表 article_list = Article.get_all_article_list() article_list_str = "最新文章列表供您点阅,回复a+数字即可阅读: \n" for i in range(len(article_list)): art_id = str(article_list[i].id) art_id = tornado.escape.native_str(art_id) art_title = article_list[i].title art_title = tornado.escape.native_str(art_title) art_category = article_list[i].category art_category = tornado.escape.native_str(art_category) article_list_str += art_id + ' ' + art_title + ' ' + art_category + '\n' return article_list_str # 按照分类查找 def get_category_articles(self, category): global MAX_ARTICLE global PIC_URL article_list = Article.get_articles_by_category(category) article_list_length = len(article_list) count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE if article_list: # 构造图文消息 articles_msg = {'articles':[]} for i in range(0,count): article = { 'title': article_list[i].slug, 'description':article_list[i].description, 'picUrl':PIC_URL, 'url':article_list[i].absolute_url } # 插入文章 articles_msg['articles'].append(article) article = {} # 返回文章 return articles_msg #----------------------------------------------------------------------- # 获取用于返回的msg def get_response_article(self, keyword): global PIC_URL keyword = str(keyword) # 从数据库查询得到若干文章 article = Article.get_article_by_keyword(keyword) # 这里先用测试数据 if article: title = article.slug description = article.description picUrl = PIC_URL url = article.absolute_url count = 1 # 也有可能是若干篇 # 这里实现相关逻辑,从数据库中获取内容 # 构造图文消息 articles_msg = {'articles':[]} for i in range(0,count): article = { 'title':title, 'description':description, 'picUrl':picUrl, 'url':url } # 插入文章 articles_msg['articles'].append(article) article = {} # 返回文章 return articles_msg else: return def get_response_article_by_id(self, post_id): global PIC_URL # 从数据库查询得到若干文章 article = Article.get_article_by_id_detail(post_id) # postId为文章id if article: title = article.slug description = article.description picUrl = PIC_URL url = article.absolute_url count = 1 # 这里实现相关逻辑,从数据库中获取内容 # 构造图文消息 articles_msg = {'articles':[]} for i in range(0,count): article = { 'title':title, 'description':description, 'picUrl':picUrl, 'url':url } # 插入文章 articles_msg['articles'].append(article) article = {} # 返回文章 return articles_msg else: return
アプリの難易度はそれほど高くなく、前回 WeChat API を使用したときとまだ近いことがわかります。 PIC_URL などのトークンなど、いくつかのグローバル変数を使用する必要があります。このプログラムの原理は、実際にはユーザーのリクエストを解析し、数字で始まる場合はヘルプ メニューを提供し、対応する機能を提供します。ここでの説明はより複雑なので、分類を取得するだけです。記事について話しましょう:
ユーザーリクエスト文字列を分析する必要があります:
# 分类 elif query_str[0] =="c": category = query_str[1:] response_msg = self.get_category_articles(category) if response_msg: return_data = self.pack_news_xml(msg, response_msg) else: response_msg = SORRY return_data = self.pack_text_xml(msg, response_msg) self.write(return_data)
get_category_articles(category) の関数がここで提供されているため、次のような実装が必要ですweixin クラスの関数:
# 按照分类查找 def get_category_articles(self, category): global MAX_ARTICLE global PIC_URL article_list = Article.get_articles_by_category(category) article_list_length = len(article_list) count = (article_list_length < MAX_ARTICLE) and article_list_length or MAX_ARTICLE if article_list: # 构造图文消息 articles_msg = {'articles':[]} for i in range(0,count): article = { 'title': article_list[i].slug, 'description':article_list[i].description, 'picUrl':PIC_URL, 'url':article_list[i].absolute_url } # 插入文章 articles_msg['articles'].append(article) article = {} # 返回文章 return articles_msg
明らかに、ここでは、データベース モデルの Article を処理して、Article がこの関数を実装しているかどうかを確認する必要があります。残念ながら、実装されていないため、力を合わせて自分で行う必要があります - Article を拡張します。なので、model.py ファイルに移動して次のコードを書きます:
# 返回一个包含若干篇文章的数组 limit 5 def get_articles_by_category(self, category): sdb._ensure_connected() article_list = sdb.query('SELECT * FROM `sp_posts` WHERE `category` = %s LIMIT 5', str(category)) for i in range(len(article_list)): article_list[i] = post_detail_formate(article_list[i]) return article_list
ここでデータベース クエリが実行され、カテゴリ パラメータが渡され、パラメータとしてカテゴリを持つ 5 つの記事が選択され、パッケージが返されます。 post_detail_formate はブログ システムにすでに記述されているので、それを使用するだけです。 Python では、SQL ステートメントを記述するときは、渡す必要があるパラメーターに遭遇した場合、% を使用してパラメーターを入力するのではなく、カンマで区切ることをお勧めします。特に like を使用する場合、次のような SQL ステートメントを記述する必要があることがよくあります:
SELECT * FROM `sp_posts` WHERE `category` LIKE '%study%'
しかし、 %s は Python のパラメーター プレースホルダーとして使用されるため、多くの不要なステートメントが発生します。ここなどのエラー。つまり、それらを安全に使用するには、Python がそれらを元の文字列の % から分離するパラメーターとして渡すのが最善です。
以上がWeChatメッセージ受信を利用した公開開発方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。