kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp

微信公众号获取OpenId(未授权)(需要关注公众号)

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

https://blog.csdn.net/HanKin_/article/details/79010789

openId是微信对于用户的唯一辨识,只要有用户关注你的公众号,微信会通过公众号和用户微信号通过一种算法生成一种唯一标识码

1.在公众号后台设置回调的域名(大多情况下是你的项目)

开发-接口权限-网页账号-用户授权获取用户基础信息中修改

2.在页面上访问下面的网址

1
https://open.weixin.qq.com/connect/oauth2/authorize?appid=*******&redirect_uri=你回调的地址&response_type=code&scope=snsapi_base&state=1#wechat_redirect

上面的会返回到你回调的地址?code=xxxxxxxxx

也就是说它会返回到你回调的地址并且带上code参数,一般情况下,回调的地址设置为后端的action即可,然后就可以获取到code了,当你获取到code,已经成功了一大半。

3.然后在后端访问下面的网址

1
https://api.weixin.qq.com/sns/oauth2/access_token?appid=***&secret=***&code=***&grant_type=authorization_code

code的参数用刚刚返回的code值。

4.上面网页将会返回json,其中openId就在其中。

至此,openId就获取到了,但是要注意:此处只获取到了openId,也就是不需要用户授权的,要获取用户其它的信息请参考接口文档。还有就是每次获取openId请重新获取code,code值不要重复使用,防止出错。

在微信页面上我是把获取到的openId保存在cookie中的,这样就可以防止openId的重复获取,我只在用户第一次进入或者cookie中的openId失效的时候进行再次获取的。


code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php
class Wxmsg extends CI_Controller {
	function __construct()
	{
		parent::__construct();
		$this->load->model('Follow_model');
		$this->load->helper('url');
		$this->load->helper('xml');
	}

	function index($check = 1)
	{
		$echostr = $this->input->get('echostr');
		if (!empty($echostr)) {
			echo $echostr;
			return;
		}

		$token = "hello";

		# step 1: get args
		$slug = array('signature', 'timestamp', 'nonce', 'openid');
		foreach ($slug as $key) {
			$args[$key] = $this->input->get($key);
			if ($check && empty($args[$key]))
				die($key.' 不能为空');
		}

		# step 2: signature check
		$lists = array($args['timestamp'], $args['nonce'], $token);
		sort($lists, SORT_STRING);
		$hashcode = sha1(implode('', $lists));
		if ($check && $hashcode != $args['signature'])
			die('signature 校验错误');

		# step 3: webdata decode
		$webdata = $this->input->raw_input_stream;
		$xml = simplexml_load_string($webdata);
		foreach ($xml->children() as $child) {
			$arr[$child->getName()] = trim((string)$child);
		}

		if (empty($arr['FromUserName']) || empty($arr['ToUserName']))
			die('FromUserName、ToUserName 不能为空');

		if (empty($arr['MsgType']))
			die('MsgType 不能为空');

		if ($arr['MsgType'] == 'text') {
			if (empty($arr['CreateTime']) || empty($arr['Content']) || empty($arr['MsgId']))
				die('CreateTime、Content、MsgId 不能为空');

			# step 4.1 text msg
			$Content = self::msg_text($arr);

		} elseif ($arr['MsgType'] == 'event') {
			if (empty($arr['Event']))
				die('Event 不能为空');

			# step 4.2 event msg
			$Content = self::msg_event($arr);

		} else {
			die('不处理事件:'.$arr['MsgType']);
		}

		self::reply_msg($arr, $Content);
	}

	function reply_msg($arr, $Content)
	{
		$FromUserName = $arr['FromUserName'];
		$ToUserName = $arr['ToUserName'];
		$CreateTime = time();
		$reply =
<<<XML
	<xml>
		<ToUserName><![CDATA[$FromUserName]]></ToUserName>
		<FromUserName><![CDATA[$ToUserName]]></FromUserName>
		<CreateTime>$CreateTime</CreateTime>
		<MsgType><![CDATA[text]]></MsgType>
		<Content><![CDATA[$Content]]></Content>
	</xml>
XML;
		$reply_xml = new SimpleXMLElement($reply);
		echo $reply_xml->asXML();
	}

	function msg_text($arr)
	{
		$FromUserName = $arr['FromUserName'];
		$key = $arr['Content'];

		$Content = '萌萌嗒';
		return $Content;
	}

	function msg_event($arr)
	{
		$event = $arr['Event'];
		if ($event == 'subscribe') {
			$this->Follow_model->change($arr['FromUserName'], $arr['ToUserName'], 1);
			return "功能添加中\n敬请等待\n";
			#return '输入姓名或电话查询单号,输入单号查询物流信息';
		} elseif ($event == 'unsubscribe') {
			$this->Follow_model->change($arr['FromUserName'], $arr['ToUserName'], 0);
		}
		return '';
	}

	function test()
	{
		$openid = $this->input->ip_address();

		$openid = $this->input->cookie("openid");
		if (empty($openid)) {
			$appid = appId;
			$redirect_uri = "http://".HOST.ROOT."/wxmsg/openid";
			$URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=snsapi_base&state=1#wechat_redirect";
			redirect($URL);
		}

		echo "test";
	}

	function openid()
	{
		$code = $this->input->get('code');
		if (empty($code)) {
			die('无法获取code');
		}
		//echo $code;
		#$fp = fopen('/tmp/a.txt', 'w');
		#fwrite($fp, $code);

		$appid = appId;
		$secret = appSecret;
		$URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code";

		$furl = fopen($URL, 'r') or exit('Open url faild!');
		$res = fgets($furl);
		fclose($furl);

		$res = json_decode($res, True);

//        var_dump($res);
//        echo $res['openid'];

		if (empty($res['openid'])) {
			die('无法获取openid');
		}
		$this->input->set_cookie('openid', $res['openid'], 86400 * 7);

		$URL = ROOT."/wxmsg/test";
		redirect($URL);
	}
}

微信公众平台获取网页授权(测试号)

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

https://blog.csdn.net/luopotaotao/article/details/50611317

1.登陆微信公众号后台,左下角找到

2.进入之后,会看到测试号的一些基本信息,用你的微信号扫描二维码关注测试号

3.网页账号权限处,右侧点击修改,此处填入你的网页域名

确认之后,后台配置就完成了,此时,再按照微信开发者文档中所写的流程便可正确获得授权,跳转到你的网页了.

后台获取到code,向微信服务器交换到openId,可以将该openId放到当前request的session中,此时用户在你开发的页面上的操作,都可以用session中的openId来确定用户了

微信公众号自定义菜单

https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html

basic.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf-8 -*-
# filename: basic.py
import urllib
import time
import json
class Basic:
	def __init__(self):
		self.__accessToken = ''
		self.__leftTime = 0
	def __real_get_access_token(self):
		appId = ""    # TODO
		appSecret = ""    # TODO
		postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type="
			   "client_credential&appid=%s&secret=%s" % (appId, appSecret))
		urlResp = urllib.urlopen(postUrl)
		urlResp = json.loads(urlResp.read())
		self.__accessToken = urlResp['access_token']
		self.__leftTime = urlResp['expires_in']
	def get_access_token(self):
		if self.__leftTime < 10:
			self.__real_get_access_token()
		return self.__accessToken
	def run(self):
		while(True):
			if self.__leftTime > 10:
				time.sleep(2)
				self.__leftTime -= 2
			else:
				self.__real_get_access_token()

menu.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# -*- coding: utf-8 -*-
# filename: menu.py
import urllib
from basic import Basic

class Menu(object):
	def __init__(self):
		pass
	def create(self, postData, accessToken):
		postUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s" % accessToken
		if isinstance(postData, unicode):
			postData = postData.encode('utf-8')
		urlResp = urllib.urlopen(url=postUrl, data=postData)
		print urlResp.read()

	def query(self, accessToken):
		postUrl = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s" % accessToken
		urlResp = urllib.urlopen(url=postUrl)
		print urlResp.read()

	def delete(self, accessToken):
		postUrl = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s" % accessToken
		urlResp = urllib.urlopen(url=postUrl)
		print urlResp.read()

	#获取自定义菜单配置接口
	def get_current_selfmenu_info(self, accessToken):
		postUrl = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=%s" % accessToken
		urlResp = urllib.urlopen(url=postUrl)
		print urlResp.read()

if __name__ == '__main__':
	myMenu = Menu()
	appid = ""    # TODO
	uri = ""  # TODO exp: http://www.abcxyz.xyz/ci/wx/openid
	postJson = """
	{
		"button":
		[
			{
		"type": "view",
		"name": "openid",
		"url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=1#wechat_redirect"
			},
			{
				"type": "view",
				"name": "测试",
				"url": "http://www.abcxyzkk.xyz/rs/wxmsg/test"
			}
		  ]
	}
	""" % (appid, uri)
	accessToken = Basic().get_access_token()
	#myMenu.delete(accessToken)
	myMenu.create(postJson, accessToken)

Python & tushare 实现命令行盯盘

https://blog.csdn.net/u011323949/article/details/102937856

依赖

1
2
pip install tushare
pip install pandas

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# -*- coding:utf-8 -*-
import tushare as ts
import os
import threading
import time
from datetime import datetime

codes = ['000756', '601288', '601988', '601319', '600929']

while 1:
	try:
		df = ts.get_realtime_quotes(codes);
	except:
		print "get err\n";
		time.sleep(3);
		continue;

	os.system("clear")
	print datetime.now()
	for k in range(0, len(codes)):
		p1 = float(df['price'][k])
		p2 = float(df['pre_close'][k])
		print "%s %s %.3f %.3f %.3f%%" % (df['code'][k], df['name'][k], p1, p2, (p1 - p2) / p2 * 100)

	time.sleep(10);

udp vpn, 代理dota2

问题

手机端MTU有时是1300???,改大了似乎tcp不通,改成1350似乎没问题。 ifconfig rmnet_data0 mtu 1350

方案

用udp fullnat,起始点在包的末尾加上最终目标IP,中间点以目标IP为VIP建立session,最后点去除额外加的数据,然后发给目标IP

方法一

client(dota2):

1
2
3
4
5
6
7
8
9
10
11
12
ip route add default via 192.168.56.3 table 1
ip rule add to 103.10.0.0/16 table 1
ip rule add to 106.52.0.0/16 table 1
ip rule add to 139.45.0.0/16 table 1
ip rule add to 146.66.0.0/16 table 1
ip rule add to 153.254.0.0/16 table 1
ip rule add to 155.133.0.0/16 table 1
ip rule add to 162.254.0.0/16 table 1
ip rule add to 185.25.0.0/16 table 1
ip rule add to 190.217.0.0/16 table 1
ip rule add to 205.185.0.0/16 table 1
ip rule add to 205.196.0.0/16 table 1

client不用安装模块,通过策略路由将包导到本地虚拟机, 发出包 192.168.8.100:12345 –> 153.254.86.167:27023

start_point(local vm):

1
2
3
insmod ip_vs.ko; insmod ip_vs_wrr.ko
echo 192.168.8.103 > /proc/sys/net/ipv4/vs/laddr_v4_str
echo 106.52.108.171 > /proc/sys/net/ipv4/vs/default_dest_v4_str

收到包: 192.168.8.100:12345 –> 153.254.86.167:27023 发出包: 192.168.8.103:5001 –> 106.52.108.171:27023 在包的末尾加上153.254.86.167 收包和发包可以不在同一网卡

middle_point(gz):

1
2
insmod ip_vs.ko; insmod ip_vs_wrr.ko
echo 101.32.220.184 > /proc/sys/net/ipv4/vs/default_dest_v4_str

单纯转发

end_point(hk):

1
insmod ip_vs.ko; insmod ip_vs_wrr.ko

取出包的末尾153.254.86.167作为rs,去除末尾自己加的数据,然后转发

用法二:

client(dota2):

1
2
insmod ip_vs.ko local_out=1
echo 119.29.157.106 > /proc/sys/net/ipv4/vs/default_dest_v4_str

在包过local_out时修改目标IP为middle_ip、末尾加数据、建立session,session按cip:cport –> middle_ip:dport建,所以两条连接的cport、dport相同而dip不同则会冲突(TODO) 只换ip,没有重新路由,所以dip和middle_ip的出口需要在一张网卡上

middle_point(gz):

1
2
insmod ip_vs.ko; insmod ip_vs_wrr.ko
echo 101.32.220.184 > /proc/sys/net/ipv4/vs/default_dest_v4_str

end_point(hk):

1
insmod ip_vs.ko; insmod ip_vs_wrr.ko