微信小程序实践 – 陌生人地图

共享位置信息的小程序。

3.21 实现动态加载 markers

3.22 增加login & 通过服务器获取 openid

3.23 从服务器获取最新位置并更新 marker 位置

坑:

很多 function 里,需要 bind(this)。

map 的 translateMarker 方法,在动态加载 marker 时,需要等大概10秒以上才能成功找到markerId ,因为这个问题被坑了好久。


微信小程序代码:https://github.com/endpang/maps.cc

服务端代码

get.php

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6688);
$redis->select(4);
/**/
file_put_contents("xx.log","[get]".json_encode($_REQUEST).PHP_EOL,FILE_APPEND);
$_REQUEST["id"] = 1;
$_REQUEST["latitude"] = round($_REQUEST["latitude"],4);
$_REQUEST["longitude"] = round($_REQUEST["longitude"],4);
$result = [
    'status' => 1,
    'count' => 1,
    'friend' => [$_REQUEST],
];
file_put_contents("xx.log","[response]".json_encode($result).PHP_EOL,FILE_APPEND);
//*/
echo json_encode($result);

test.php

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6688);
$redis->select(4);
/**
$result = [
    'status' => 1,
    'userInfo' => "test",
];
//*/
$file = file_get_contents("https://api.weixin.qq.com/sns/jscode2session?appid=xxx&secret=xxx&js_code=".$_REQUEST['code']."&grant_type=authorization_code");
$user_array = json_decode($file,true);
//file_put_contents("xx.log",$file);
if(!empty($user_array["openid"])){
    $result["status"] = 1;
    $result["userInfo"]["openid"] = $user_array["openid"];
    $redis->set("user_".$user_array["openid"],$file);
}
echo json_encode($result);

PHP微信机器人

github https://github.com/HanSon/vbot
https://github.com/HanSon/my-vbot

修改文件 Example.php

$this->config =  $default_config;//array_merge($default_config, $this->config);

修改了一个文件,以实现收到文字回复笔画的功能
MessageHandler.php

如需主动发起消息请安装swoole,并修改config文件。

pecl install swoole

<?php

namespace Hanson\MyVbot;

use Hanson\MyVbot\Handlers\Contact\ColleagueGroup;
use Hanson\MyVbot\Handlers\Contact\ExperienceGroup;
use Hanson\MyVbot\Handlers\Contact\FeedbackGroup;
use Hanson\MyVbot\Handlers\Contact\Hanson;
use Hanson\MyVbot\Handlers\Type\RecallType;
use Hanson\MyVbot\Handlers\Type\TextType;
use Hanson\Vbot\Contact\Friends;
use Hanson\Vbot\Contact\Groups;
use Hanson\Vbot\Contact\Members;

use Hanson\Vbot\Message\Emoticon;
use Hanson\Vbot\Message\Text;
use Illuminate\Support\Collection;

class MessageHandler
{
    public static function messageHandler(Collection $message)
    {
        /** @var Friends $friends */
        $friends = vbot('friends');

        /** @var Members $members */
        $members = vbot('members');

        /** @var Groups $groups */
        $groups = vbot('groups');

        Hanson::messageHandler($message, $friends, $groups);
        ColleagueGroup::messageHandler($message, $friends, $groups);
        FeedbackGroup::messageHandler($message, $friends, $groups);
        ExperienceGroup::messageHandler($message, $friends, $groups);

        TextType::messageHandler($message, $friends, $groups);
        RecallType::messageHandler($message);

        if ($message['type'] === 'new_friend') {
            Text::send($message['from']['UserName'], '客官,等你很久了!感谢跟 vbot 交朋友,如果可以帮我点个star,谢谢了!https://github.com/HanSon/vbot');
            $groups->addMember($groups->getUsernameByNickname('Vbot 体验群'), $message['from']['UserName']);
            Text::send($message['from']['UserName'], '现在拉你进去vbot的测试群,进去后为了避免轰炸记得设置免骚扰哦!如果被不小心踢出群,跟我说声“拉我”我就会拉你进群的了。');
        }

        if ($message['type'] === 'emoticon' && random_int(0, 1)) {
            Emoticon::sendRandom($message['from']['UserName']);
        }

        // @todo
        if ($message['type'] === 'official') {
            vbot('console')->log('收到公众号消息:'.$message['title'].$message['description'].
                $message['app'].$message['url']);
        }

        if ($message['type'] === 'request_friend') {
            vbot('console')->log('收到好友申请:'.$message['info']['Content'].$message['avatar']);
            if (in_array($message['info']['Content'], ['echo', 'print_r', 'var_dump', 'print'])) {
                $friends->approve($message);
            }
        }
        //print_r($message);
        $re = 0;
        if($message["fromType"] == "Friend"){
            $nick = $message['from']['NickName'];
            $re = 1;
        }

        if($message["fromType"] == "Group"){
            $nick = $message['sender']['NickName'];
            if(@$message['isAt']){
                $re = 1;
            }
        }
        if($re ==1 ){

            $zi = mb_substr($message["message"],0,1,'utf-8');
            $uni = self::unicode_encode($zi);


            $var = trim($uni);
            $len = strlen($var)-1;
            $las = $var{$len};
            $url = "http://www.shufaji.com/datafile/bd/gif/".$las."/".$uni.".gif";
            //Text::send($message['from']['UserName'], "@".$nick." ".$url);
            if(!is_file(__DIR__."/img/".$uni.'.gif')){

                $img = @file_get_contents($url);

                if(!empty($img)){
                    file_put_contents(__DIR__."/img/".$uni.'.gif',$img);
                    Emoticon::send($message['from']['UserName'], __DIR__."/img/".$uni.".gif");

                }else{
                    Text::send($message['from']['UserName'], "@".$nick." 找不到这个字的笔顺".$url);
                }
            }else{
                Emoticon::send($message['from']['UserName'], __DIR__."/img/".$uni.".gif");
            }
        }


    }
    private static function unicode_encode($name)
    {
        $name = iconv('UTF-8', 'UCS-2', $name);
        $len = strlen($name);
        $str = '';
        for ($i = 0; $i < $len - 1; $i = $i + 2)
        {
            $c = $name[$i];
            $c2 = $name[$i + 1];
            if (ord($c) > 0)
            {    // 两个字节的文字
                $s1 = base_convert(ord($c), 10, 16);
                $s2 = base_convert(ord($c2), 10, 16);

                if(ord($c) < 16){
                    $s1 = "0".$s1;
                }
                if(ord($c2) < 16){
                    $s2 = "0".$s2;
                }
                $str .= $s1 . $s2;
            }
            else
            {
                $str .= $c2;
            }

        }
        return $str;
    }
}

itchat 调试完毕后,开始折腾聊天的server

https://ask.julyedu.com/question/7410

首先准备好  torch 环境,然后安装 nn,rnn,async

sudo ~/torch/install/bin/luarocks install nn
sudo ~/torch/install/bin/luarocks install rnn
sudo ~/torch/install/bin/luarocks install async penlight cutorch cunn

下载程序和语料

git clone --recursive https://github.com/rustcbf/chatbot-zh-torch7 #代码
git clone --recursive https://github.com/rustcbf/dgk_lost_conv #语料
git clone --recursive https://github.com/chenb67/neuralconvo #以上两个在此源码进行改进,可作为参考

将 dgk_lost_conv 里的  xiaohuangji50w_fenciA.zip 解压放到外层目录

th train.lua –cuda –dataset 5000 –hiddenSize 100

报错

-- Epoch 1 / 30

/root/torch/install/bin/luajit: ./seq2seq.lua:50: attempt to call field 'recursiveCopy' (a nil value)
stack traceback:
	./seq2seq.lua:50: in function 'forwardConnect'
	./seq2seq.lua:67: in function 'train'
	train.lua:90: in main chunk
	[C]: in function 'dofile'
	/root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
	[C]: at 0x00405d50

修改 seq2seq.lua 如下 (50 – 70 行间)

function Seq2Seq:forwardConnect(inputSeqLen)
  self.decoderLSTM.userPrevOutput =
    --nn.rnn.recursiveCopy(self.decoderLSTM.userPrevOutput, self.encoderLSTM.outputs[inputSeqLen])
    nn.utils.recursiveCopy(self.decoderLSTM.userPrevOutput, self.encoderLSTM.outputs[inputSeqLen])
  self.decoderLSTM.userPrevCell =
    nn.utils.recursiveCopy(self.decoderLSTM.userPrevCell, self.encoderLSTM.cells[inputSeqLen])
end

--[[ Backward coupling: Copy decoder gradients to encoder LSTM ]]--
function Seq2Seq:backwardConnect()
  if(self.encoderLSTM.userNextGradCell ~= nil) then
    self.encoderLSTM.userNextGradCell =
      nn.utils.recursiveCopy(self.encoderLSTM.userNextGradCell, self.decoderLSTM.userGradPrevCell)
  end
  if(self.encoderLSTM.gradPrevOutput ~= nil) then
    self.encoderLSTM.gradPrevOutput =
      nn.utils.recursiveCopy(self.encoderLSTM.gradPrevOutput, self.decoderLSTM.userGradPrevOutput)
  end
end

训练之,1080ti 一轮大概 两个多小时。。。 30轮估计需要70小时。妇女节后见了。

eval.lua 的时候报错,不明所以,先放弃这个了,试试别的。

/root/torch/install/bin/luajit: /root/torch/install/share/lua/5.1/nn/Container.lua:67:
In 3 module of nn.Sequential:
/root/torch/install/share/lua/5.1/torch/Tensor.lua:466: Wrong size for view. Input size: 100. Output size: 6561
stack traceback:
 [C]: in function 'error'
 /root/torch/install/share/lua/5.1/torch/Tensor.lua:466: in function 'view'
 /root/torch/install/share/lua/5.1/rnn/utils.lua:191: in function 'recursiveZeroMask'
 /root/torch/install/share/lua/5.1/rnn/MaskZero.lua:37: in function 'updateOutput'
 /root/torch/install/share/lua/5.1/rnn/Recursor.lua:13: in function '_updateOutput'
 /root/torch/install/share/lua/5.1/rnn/AbstractRecurrent.lua:50: in function 'updateOutput'
 /root/torch/install/share/lua/5.1/rnn/Sequencer.lua:53: in function </root/torch/install/share/lua/5.1/rnn/Sequencer.lua:34>
 [C]: in function 'xpcall'
 /root/torch/install/share/lua/5.1/nn/Container.lua:63: in function 'rethrowErrors'
 /root/torch/install/share/lua/5.1/nn/Sequential.lua:44: in function 'forward'
 ./seq2seq.lua:115: in function 'eval'
 eval.lua:90: in function 'say'
 eval.lua:105: in main chunk
 [C]: in function 'dofile'
 /root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
 [C]: at 0x00405d50

WARNING: If you see a stack trace below, it doesn't point to the place where this error occurred. Please use only the one above.
stack traceback:
 [C]: in function 'error'
 /root/torch/install/share/lua/5.1/nn/Container.lua:67: in function 'rethrowErrors'
 /root/torch/install/share/lua/5.1/nn/Sequential.lua:44: in function 'forward'
 ./seq2seq.lua:115: in function 'eval'
 eval.lua:90: in function 'say'
 eval.lua:105: in main chunk
 [C]: in function 'dofile'
 /root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
 [C]: at 0x00405d50

换一个试试 ,tensorflow 的,而且还比较新

git:   https://github.com/qhduan/just_another_seq2seq

包含还有一个英译汉的功能。

把之前下载的预料库中的 dgk_shooter_min.conv.zip 解压缩放到 chatbot 目录里

python3 train.py 训练之。

20轮,一轮大概30-40分钟(1070 显卡)

训练完之后执行  Python3 test.py 测试。

注意,tensorflow 在 1.4.1 下。

并修改 test.py  否则 sequence_to_sequence   会报

NotFoundError (see above for traceback): Key decoder/multi_rnn_cell/cell_0/Attention_Wrapper/lstm_cell/bias not found in checkpoint
    test(
        bidirectional=False,
        cell_type='lstm',
        depth=2,
        attention_type='Bahdanau',
        use_residual=False,
        use_dropout=False,
        time_major=False,
        hidden_units=512
    )

聊天效果,chatbot_ad 的还行

训练chatbot_ad 的时候,readme 少了一步  train_tfidf.py  。

Input Chat Sentence:我生病了
rl: ['我', '睡', '着', '了', '</s>', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:我想我爱你
rl: ['我', '不', '知', '道', '你', '在', '说', '什', '么', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你多大了?
rl: ['你', '看', '上', '去', '不', '错', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你好吗?
rl: ['很', '高', '兴', '见', '到', '你', '</s>', '</s>', '<unk>', '<unk>']
Input Chat Sentence:什么时间了
rl: ['你', '要', '去', '哪', '儿', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:去北京
rl: ['维', '克', '多', ',', '过', '来', '</s>', '</s>', '</s>', '</s>', '<unk>', '<unk>']
Input Chat Sentence:去哪?
rl: ['我', '们', '得', '走', '了', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:走
rl: ['我', '们', '得', '走', '了', '</s>', '</s>', '<unk>']
Input Chat Sentence:走了
rl: ['你', '们', '都', '走', '了', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:去哪
rl: ['我', '也', '不', '知', '道', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:干啥
rl: ['你', '在', '干', '啥', '啊', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:他是谁?
rl: ['不', '知', '道', '为', '什', '么', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你是谁?
rl: ['我', '是', '麦', '克', '墨', '菲', '医', '生', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你哎我 吗?
rl: ['我', '有', '话', '跟', '你', '说', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你爱我 吗?
rl: ['什', '么', '东', '西', '?', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你爱我吗?
rl: ['我', '爱', '你', ',', '宝', '贝', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:

chatbot_ad  用 bottle 改造了一个 url api接口用于和 itchat 对接。代码如下。

# -*- coding: utf-8 -*-
"""
对SequenceToSequence模型进行基本的参数组合测试
"""

import sys
import random
import pickle

import numpy as np
import tensorflow as tf
import bottle

sys.path.append('..')

from data_utils import batch_flow
from sequence_to_sequence import SequenceToSequence
from word_sequence import WordSequence # pylint: disable=unused-variable
random.seed(0)
np.random.seed(0)
tf.set_random_seed(0)
_, _, ws = pickle.load(open('chatbot.pkl', 'rb'))
config = tf.ConfigProto(
        device_count={'CPU': 1, 'GPU': 0},
        allow_soft_placement=True,
        log_device_placement=False
    )
save_path_rl = './s2ss_chatbot_ad.ckpt'
graph_rl = tf.Graph()

with graph_rl.as_default():
        model_rl = SequenceToSequence(
            input_vocab_size=len(ws),
            target_vocab_size=len(ws),
            batch_size=1,
            mode='decode',
            beam_width=12,
            bidirectional=False,
            cell_type='lstm',
            depth=1,
            attention_type='Bahdanau',
            use_residual=False,
            use_dropout=False,
            parallel_iterations=1,
            time_major=False,
            hidden_units=1024,
            share_embedding=True
        )
        init = tf.global_variables_initializer()
        sess_rl = tf.Session(config=config)
        sess_rl.run(init)
        model_rl.load(sess_rl, save_path_rl)


@bottle.route('/login/<w>', method='GET')
def do_login(w):
    user_text = w
    x_test = list(user_text.lower())
    x_test = [x_test]
    bar = batch_flow([x_test], [ws], 1)
    x, xl = next(bar)
    pred_rl = model_rl.predict(
            sess_rl,
            np.array(x),
            np.array(xl)
        ) 
    #word = bottle.request.forms.get("word")
    str2 = ''.join(str(i) for i in ws.inverse_transform(pred_rl[0]))
    return str2


bottle.run(host='0.0.0.0', port=8080)                                          #表示本机,接口是8080

注意不要聊的太猛,容易被腾讯封了。

[2018-03-12 02:34:54][INFO] please scan the qrCode with wechat.
[2018-03-12 02:35:01][INFO] please confirm login in wechat.
Array
(
    [ret] => 1203
    [message] => 当前登录环境异常。为了你的帐号安全,暂时不能登录web微信。你可以通过Windows微信、Mac微信或者手机客户端微信登录。
)
[2018-03-12 02:35:03] vbot.ERROR: Undefined index: skey [] []
PHP Fatal error:  Uncaught ErrorException: Undefined index: skey in /Users/zhiweipang/my-vbot/vendor/hanson/vbot/src/Core/Server.php:194

用 Python 分析自己的微信好友

#coding:utf-8 
import itchat
import re
from snownlp import SnowNLP
import jieba
import jieba.analyse
import numpy as np 
from collections import Counter
import matplotlib
from PIL import Image
from wordcloud import WordCloud, STOPWORDS
#matplotlib.use('qt4agg')  
from matplotlib.font_manager import *  
import matplotlib.pyplot as plt
itchat.auto_login(hotReload = True)
friends = itchat.get_friends(update = True)
#print(friends)

matplotlib.rcParams['axes.unicode_minus']=False

def analyseSex(friends):
    sexs = list(map(lambda x:x['Sex'],friends[1:]))
    counts = list(map(lambda x:x[1],Counter(sexs).items()))
    labels = ['Unknow','Male','Female']
    colors = ['red','yellowgreen','lightskyblue']
    plt.figure(figsize=(8,5), dpi=80)
    plt.axes(aspect=1)
    plt.pie(counts, #性别统计结果
            labels=labels, #性别展示标签
            colors=colors, #饼图区域配色
            labeldistance = 1.1, #标签距离圆点距离
            autopct = '%3.1f%%', #饼图区域文本格式
            shadow = False, #饼图是否显示阴影
            startangle = 90, #饼图起始角度
            pctdistance = 0.6 #饼图区域文本距离圆点距离
    )
    myfont = FontProperties(fname='/Library/Fonts/Microsoft/Microsoft Yahei.ttf')
    plt.legend(loc='upper right',)
    plt.title(u'%s的微信好友性别组成' % friends[0]['NickName'],fontproperties=myfont)
    plt.show()

def analyseSignature(friends):
    signatures = ''
    emotions = []
    pattern = re.compile("1f\d.+")
    for friend in friends:
        signature = friend['Signature']
        if(signature != None):
            signature = signature.strip().replace('span', '').replace('class', '').replace('emoji', '')
            signature = re.sub(r'1f(\d.+)','',signature)
            if(len(signature)>0):
                nlp = SnowNLP(signature)
                emotions.append(nlp.sentiments)
                signatures += ' '.join(jieba.analyse.extract_tags(signature,5))
    with open('signatures.txt','wt',encoding='utf-8') as file:
         file.write(signatures)

    # Sinature WordCloud
    back_coloring = np.array(Image.open('true.jpeg')) #随便找张图
    wordcloud = WordCloud(
        font_path='/Library/Fonts/Microsoft/Microsoft Yahei.ttf',  #字体文件
        background_color="white",
        max_words=1200,
        mask=back_coloring, 
        max_font_size=75,
        random_state=45,
        width=960, 
        height=720, 
        margin=15
    )

    wordcloud.generate(signatures)
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()
    wordcloud.to_file('signatures.jpg')

    # Signature Emotional Judgment
    count_good = len(list(filter(lambda x:x>0.66,emotions)))
    count_normal = len(list(filter(lambda x:x>=0.33 and x<=0.66,emotions)))
    count_bad = len(list(filter(lambda x:x<0.33,emotions)))
    labels = [u'负面消极',u'中性',u'正面积极']
    values = (count_bad,count_normal,count_good)
    plt.rcParams['font.sans-serif'] = ['simHei'] 
    plt.rcParams['axes.unicode_minus'] = False
    plt.xlabel(u'情感判断')
    plt.ylabel(u'频数')
    plt.xticks(range(3),labels)
    plt.legend(loc='upper right',)
    plt.bar(range(3), values, color = 'rgb')
    plt.title(u'%s的微信好友签名信息情感分析' % friends[0]['NickName'])
    plt.show()
def analyseLocation(friends):   #生成一个所在城市 csv
    headers = ['NickName','Province','City']
    with open('location.csv','w',encoding='utf-8',newline='',) as csvFile:
        writer = csv.DictWriter(csvFile, headers)
        writer.writeheader()
        for friend in friends[1:]:
           row = {}
           row['NickName'] = friend['NickName']
           row['Province'] = friend['Province']
           row['City'] = friend['City']
           writer.writerow(row)
analyseLocation(friends)
analyseSignature(friends) 
#analyseSex(friends)

会弹出一个二维码用微信扫码登录。

等一会就能看到好友分布图了