").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
diff --git a/docs/bootstrap/main.html b/docs/bootstrap/main.html
new file mode 100644
index 00000000..94d9808c
--- /dev/null
+++ b/docs/bootstrap/main.html
@@ -0,0 +1 @@
+{% extends "base.html" %}
diff --git a/docs/bootstrap/nav.html b/docs/bootstrap/nav.html
new file mode 100644
index 00000000..9cd9ed39
--- /dev/null
+++ b/docs/bootstrap/nav.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+ {%- block site_nav %}
+
+
+ {%- endblock %}
+
+
+
+
+
+
diff --git a/docs/bootstrap/toc.html b/docs/bootstrap/toc.html
new file mode 100644
index 00000000..7389b605
--- /dev/null
+++ b/docs/bootstrap/toc.html
@@ -0,0 +1,12 @@
+
diff --git a/docs/index.md b/docs/index.md
index b82207f7..f4778020 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -168,35 +168,45 @@ def download_files(msg):
f.write(msg['Text']())
```
-## 常见问题与解答
+### 用户多开
-Q: 为什么中文的文件没有办法上传?
+使用如下命令可以完成多开的操作:
-A: 这是由于`requests`的编码问题导致的。若需要支持中文文件传输,将[fields.py][fields.py-2](py3版本见[这里][fields.py-3])文件放入requests包的packages/urllib3下即可
-
-Q: 为什么我在设定了`itchat.auto_login()`的`enableCmdQR`为`True`后还是没有办法在命令行显示二维码?
+```python
+import itchat
-A: 这是由于没有安装可选的包`pillow`,可以使用右边的命令安装:`pip install pillow`
+newInstance = itchat.new_instance()
+newInstance.auto_login(hotReload=True, statusStorageDir='newInstance.pkl')
-Q: 如何通过这个包将自己的微信号变为控制器?
+@newInstance.msg_register(TEXT)
+def reply(msg):
+ return msg['Text']
-A: 有两种方式:发送、接受自己UserName的消息;发送接收文件传输助手(filehelper)的消息
+newInstance.run()
+```
-Q: 为什么我发送信息的时候部分信息没有成功发出来?
+### 退出及登陆完成后调用特定方法
-A: 有些账号是天生无法给自己的账号发送信息的,建议使用`filehelper`代替。另外,接口调用是有频率限制,限制一下连续发送信息之间的时间间隔即可。
+登陆完成后的方法需要赋值在`loginCallback`中。
-## 作者
+而退出后的方法需要赋值在`exitCallback`中。
-[LittleCoder][littlecodersh]: 整体构架及完成Python2 Python3版本。
+```python
+import time
-[Chyroc][Chyroc]: 完成第一版本的Python3构架。
+import itchat
-## 参考资料
+def lc():
+ print('finish login')
+def ec():
+ print('exit')
-[liuwons/wxBot][liuwons-wxBot]: 类似的基于Python的微信机器人
+itchat.auto_login(loginCallback=lc, exitCallback=ec)
+time.sleep(3)
+itchat.logout()
+```
-[zixia/wechaty][zixia-wechaty]: 基于Javascript(ES6)的微信个人账号机器人NodeJS框架/库
+若不设置loginCallback的值,则将会自动删除二维码图片并清空命令行显示。
## 问题和建议
@@ -220,7 +230,9 @@ A: 有些账号是天生无法给自己的账号发送信息的,建议使用`f
[fields.py-2]: https://gist.github.com/littlecodersh/9a0c5466f442d67d910f877744011705
[fields.py-3]: https://gist.github.com/littlecodersh/e93532d5e7ddf0ec56c336499165c4dc
[littlecodersh]: https://github.com/littlecodersh
+[tempdban]: https://github.com/tempdban
[Chyroc]: https://github.com/Chyroc
[liuwons-wxBot]: https://github.com/liuwons/wxBot
[zixia-wechaty]: https://github.com/zixia/wechaty
+[Mojo-Weixin]: https://github.com/sjdy521/Mojo-Weixin
[issue#1]: https://github.com/littlecodersh/ItChat/issues/1
diff --git a/docs/6.Member stuff.md b/docs/intro/contact.md
similarity index 100%
rename from docs/6.Member stuff.md
rename to docs/intro/contact.md
diff --git a/docs/7.Deploy.md b/docs/intro/deploy.md
similarity index 100%
rename from docs/7.Deploy.md
rename to docs/intro/deploy.md
diff --git a/docs/3.Handler.md b/docs/intro/handler.md
similarity index 100%
rename from docs/3.Handler.md
rename to docs/intro/handler.md
diff --git a/docs/intro/index.md b/docs/intro/index.md
new file mode 100644
index 00000000..f4778020
--- /dev/null
+++ b/docs/intro/index.md
@@ -0,0 +1,238 @@
+# itchat
+
+[![Gitter][gitter-picture]][gitter] ![py27][py27] ![py35][py35] [English version][english-version]
+
+itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。
+
+使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。
+
+当然,该api的使用远不止一个机器人,更多的功能等着你来发现,比如[这些][tutorial2]。
+
+如今微信已经成为了个人社交的很大一部分,希望这个项目能够帮助你扩展你的个人的微信号、方便自己的生活。
+
+## 安装
+
+可以通过本命令安装itchat:
+
+```python
+pip install itchat
+```
+
+## 简单入门实例
+
+有了itchat,如果你想要给文件传输助手发一条信息,只需要这样:
+
+```python
+import itchat
+
+itchat.auto_login()
+
+itchat.send('Hello, filehelper', toUserName='filehelper')
+```
+
+如果你想要回复发给自己的文本消息,只需要这样:
+
+```python
+import itchat
+
+@itchat.msg_register(itchat.content.TEXT)
+def text_reply(msg):
+ return msg['Text']
+
+itchat.auto_login()
+itchat.run()
+```
+
+一些进阶应用可以在下面的开源机器人的源码和进阶应用中看到,或者你也可以阅览[文档][document]。
+
+## 试一试
+
+这是一个基于这一项目的[开源小机器人][robot-source-code],百闻不如一见,有兴趣可以尝试一下。
+
+![QRCode][robot-qr]
+
+## 截屏
+
+![file-autoreply][robot-demo-file] ![login-page][robot-demo-login]
+
+## 进阶应用
+
+### 各类型消息的注册
+
+通过如下代码,微信已经可以就日常的各种信息进行获取与回复。
+
+```python
+#coding=utf8
+import itchat, time
+from itchat.content import *
+
+@itchat.msg_register([TEXT, MAP, CARD, NOTE, SHARING])
+def text_reply(msg):
+ itchat.send('%s: %s' % (msg['Type'], msg['Text']), msg['FromUserName'])
+
+@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO])
+def download_files(msg):
+ msg['Text'](msg['FileName'])
+ return '@%s@%s' % ({'Picture': 'img', 'Video': 'vid'}.get(msg['Type'], 'fil'), msg['FileName'])
+
+@itchat.msg_register(FRIENDS)
+def add_friend(msg):
+ itchat.add_friend(**msg['Text']) # 该操作会自动将新好友的消息录入,不需要重载通讯录
+ itchat.send_msg('Nice to meet you!', msg['RecommendInfo']['UserName'])
+
+@itchat.msg_register(TEXT, isGroupChat=True)
+def text_reply(msg):
+ if msg['isAt']:
+ itchat.send(u'@%s\u2005I received: %s' % (msg['ActualNickName'], msg['Content']), msg['FromUserName'])
+
+itchat.auto_login(True)
+itchat.run()
+```
+
+### 命令行二维码
+
+通过以下命令可以在登陆的时候使用命令行显示二维码:
+
+```python
+itchat.auto_login(enableCmdQR=True)
+```
+
+部分系统可能字幅宽度有出入,可以通过将enableCmdQR赋值为特定的倍数进行调整:
+
+```python
+# 如部分的linux系统,块字符的宽度为一个字符(正常应为两字符),故赋值为2
+itchat.auto_login(enableCmdQR=2)
+```
+
+默认控制台背景色为暗色(黑色),若背景色为浅色(白色),可以将enableCmdQR赋值为负值:
+
+```python
+itchat.auto_login(enableCmdQR=-1)
+```
+
+### 退出程序后暂存登陆状态
+
+通过如下命令登陆,即使程序关闭,一定时间内重新开启也可以不用重新扫码。
+
+```python
+itchat.auto_login(hotReload=True)
+```
+
+### 用户搜索
+
+使用`search_friends`方法可以搜索用户,有四种搜索方式:
+1. 仅获取自己的用户信息
+2. 获取特定`UserName`的用户信息
+3. 获取备注、微信号、昵称中的任何一项等于`name`键值的用户
+4. 获取备注、微信号、昵称分别等于相应键值的用户
+
+其中三、四项可以一同使用,下面是示例程序:
+
+```python
+# 获取自己的用户信息,返回自己的属性字典
+itchat.search_friends()
+# 获取特定UserName的用户信息
+itchat.search_friends(userName='@abcdefg1234567')
+# 获取任何一项等于name键值的用户
+itchat.search_friends(name='littlecodersh')
+# 获取分别对应相应键值的用户
+itchat.search_friends(wechatAccount='littlecodersh')
+# 三、四项功能可以一同使用
+itchat.search_friends(name='LittleCoder机器人', wechatAccount='littlecodersh')
+```
+
+关于公众号、群聊的获取与搜索在文档中有更加详细的介绍。
+
+### 附件的下载与发送
+
+itchat的附件下载方法存储在msg的Text键中。
+
+发送的文件的文件名(图片给出的默认文件名)都存储在msg的FileName键中。
+
+下载方法接受一个可用的位置参数(包括文件名),并将文件相应的存储。
+
+```python
+@itchat.msg_register(['Picture', 'Recording', 'Attachment', 'Video'])
+def download_files(msg):
+ msg['Text'](msg['FileName'])
+ itchat.send('@%s@%s'%('img' if msg['Type'] == 'Picture' else 'fil', msg['FileName']), msg['FromUserName'])
+ return '%s received'%msg['Type']
+```
+
+如果你不需要下载到本地,仅想要读取二进制串进行进一步处理可以不传入参数,方法将会返回图片的二进制串。
+
+```python
+@itchat.msg_register(['Picture', 'Recording', 'Attachment', 'Video'])
+def download_files(msg):
+ with open(msg['FileName'], 'wb') as f:
+ f.write(msg['Text']())
+```
+
+### 用户多开
+
+使用如下命令可以完成多开的操作:
+
+```python
+import itchat
+
+newInstance = itchat.new_instance()
+newInstance.auto_login(hotReload=True, statusStorageDir='newInstance.pkl')
+
+@newInstance.msg_register(TEXT)
+def reply(msg):
+ return msg['Text']
+
+newInstance.run()
+```
+
+### 退出及登陆完成后调用特定方法
+
+登陆完成后的方法需要赋值在`loginCallback`中。
+
+而退出后的方法需要赋值在`exitCallback`中。
+
+```python
+import time
+
+import itchat
+
+def lc():
+ print('finish login')
+def ec():
+ print('exit')
+
+itchat.auto_login(loginCallback=lc, exitCallback=ec)
+time.sleep(3)
+itchat.logout()
+```
+
+若不设置loginCallback的值,则将会自动删除二维码图片并清空命令行显示。
+
+## 问题和建议
+
+如果有什么问题或者建议都可以在这个[Issue][issue#1]和我讨论
+
+或者也可以在gitter上交流:[![Gitter][gitter-picture]][gitter]
+
+当然也可以加入我们新建的QQ群讨论:549762872
+
+[gitter-picture]: https://badges.gitter.im/littlecodersh/ItChat.svg
+[gitter]: https://gitter.im/littlecodersh/ItChat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
+[py27]: https://img.shields.io/badge/python-2.7-ff69b4.svg
+[py35]: https://img.shields.io/badge/python-3.5-red.svg
+[english-version]: https://github.com/littlecodersh/ItChat/blob/master/README_EN.md
+[document]: https://itchat.readthedocs.org/zh/latest/
+[tutorial2]: http://python.jobbole.com/86532/
+[robot-source-code]: https://gist.github.com/littlecodersh/ec8ddab12364323c97d4e36459174f0d
+[robot-qr]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FQRCode2.jpg?imageView/2/w/400/
+[robot-demo-file]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E5%BE%AE%E4%BF%A1%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%9B%BE%E7%89%87.png?imageView/2/w/300/
+[robot-demo-login]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E7%99%BB%E5%BD%95%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.jpg?imageView/2/w/450/
+[fields.py-2]: https://gist.github.com/littlecodersh/9a0c5466f442d67d910f877744011705
+[fields.py-3]: https://gist.github.com/littlecodersh/e93532d5e7ddf0ec56c336499165c4dc
+[littlecodersh]: https://github.com/littlecodersh
+[tempdban]: https://github.com/tempdban
+[Chyroc]: https://github.com/Chyroc
+[liuwons-wxBot]: https://github.com/liuwons/wxBot
+[zixia-wechaty]: https://github.com/zixia/wechaty
+[Mojo-Weixin]: https://github.com/sjdy521/Mojo-Weixin
+[issue#1]: https://github.com/littlecodersh/ItChat/issues/1
diff --git a/docs/2.Login.md b/docs/intro/login.md
similarity index 100%
rename from docs/2.Login.md
rename to docs/intro/login.md
diff --git a/docs/4.Message content.md b/docs/intro/messages.md
similarity index 100%
rename from docs/4.Message content.md
rename to docs/intro/messages.md
diff --git a/docs/5.Reply.md b/docs/intro/reply.md
similarity index 100%
rename from docs/5.Reply.md
rename to docs/intro/reply.md
diff --git a/docs/1.Start.md b/docs/intro/start.md
similarity index 100%
rename from docs/1.Start.md
rename to docs/intro/start.md
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..b672f4aa
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1 @@
+mkdocs==0.16.0
diff --git a/docs/tutorial/tutorial0.md b/docs/tutorial/tutorial0.md
new file mode 100644
index 00000000..3a77552b
--- /dev/null
+++ b/docs/tutorial/tutorial0.md
@@ -0,0 +1,355 @@
+## 一、课程介绍
+
+本文最佳阅读方式为[实验楼的会员课程][vip],建议点击链接在实验楼内阅读。
+
+### 1. 课程来源
+
+关于itchat进一步使用的问题你可以在[主页][itchat]加入官方交流群也可以在课程下面向我提问。
+
+课程在常见的三种系统中都可以进行操作,itchat建议使用可以安装的最新版本。
+
+本课程通过聊天机器人为例,介绍如何使用Python完成微信的点对点信息交互。
+
+### 2. 内容简介
+
+* 课程实现微信个人号聊天机器人
+* 通过自定义消息处理方法加入聊天功能
+
+### 3. 课程知识点
+
+本课程项目完成过程中将学习:
+* 微信消息的基本获取与处理
+* 微信消息的指定发送
+
+其中将重点介绍微信消息的获取与处理。
+
+## 二、实验环境
+
+在终端中输入以下命令,完成微信的API包itchat的安装。
+
+我们这里使用python3的环境(python2也是可行的):
+
+```bash
+sudo pip3 install itchat --upgrade
+```
+
+通过该命令判断是否安装成功:
+
+```bash
+python3 -c "import itchat"
+```
+
+如果没有报错信息说明你已经将实验环境安装完成。
+
+![install][install]
+
+## 三、实验原理
+
+通过微信的Python接口itchat获取微信消息。
+
+将微信消息传输到机器人接口(这里以图灵为例),获取机器人的返回消息。
+
+将返回消息返回给微信消息的发送人。
+
+实现将微信个人号变为聊天机器人的目的。
+
+## 四、实验步骤
+
+### 0. 基础知识
+
+为了照顾一些从未使用过Python的新用户与使用其他语言的用户,这里简单的讲一下以下的代码如何使用。
+
+下面的每一段描述都给出了相应的测试代码,如果没有特殊说明这段代码可以这样使用:
+
+打开桌面的Xfce终端,先将目录通过以下命令切到桌面。
+
+```bash
+cd Desktop
+```
+
+之后使用gedit编辑器编辑我们的主程序。
+
+你也完全可以使用vim,会使用vim的话想必也知道这里应该输入什么命令了。
+
+```bash
+gedit test.py
+```
+
+最后将给出的代码复制进编辑器,保存并退出,使用如下命令就可以使用了。
+
+```bash
+python3 test.py
+```
+
+那么,就让我们开始正式进入Python操作微信的探索之旅吧。
+
+### 1. 实现微信消息的获取
+
+itchat的注册时根据类型注册的。
+
+在获取相应类型的信息时会调用该函数。
+
+我们现在只需要获取最简单的文本消息,那么只需要这样注册:
+
+```python
+import itchat
+
+@itchat.msg_register(itchat.content.TEXT)
+def print_content(msg):
+ print(msg['Text'])
+
+itchat.auto_login()
+itchat.run()
+```
+
+其中第三行即注册的操作,通过装饰符将`print_content`注册为处理文本消息的函数。
+
+微信有各种类型的数据,例如图片、语音、名片、分享等,也对应不同的注册参数:
+* 图片对应`itchat.content.PICTURE`
+* 语音对应`itchat.content.RECORDING`
+* 名片对应`itchat.content.CARD`
+* 其余的这里就不一一列举,更具体的内容可以自行搜索itchat阅读[文档][document]
+
+执行命令
+```
+python3 test.py
+```
+
+就可看到我们开始登陆微信:
+
+![login][login]
+
+扫码完成以后最基础的文本信息的接收就完成了,你可以尝试用他人的微信给自己发一条信息。
+
+如果你不想要每次运行程序都扫码,可以在登陆命令中进行设置:
+
+```python
+itchat.auto_login(hotReload=True)
+```
+
+### 2. 实现微信消息的发送
+
+微信可以发送各类消息,文本、图片、文件等,不过我们现在只需要使用文本的发送。
+
+其余的消息的发送有兴趣可以自行阅读。
+
+```python
+itchat.send('Message Content', 'toUserName')
+```
+
+该发送消息的函数需要两个参数,消息的内容与接受者的UserName,即标识符。
+
+那么我们试着向文件传输助手发送一条消息:
+
+```python
+#coding=utf8
+import itchat
+
+itchat.auto_login(hotReload=True)
+
+# 注意实验楼环境的中文输入切换
+itchat.send(u'测试消息发送', 'filehelper')
+```
+
+打开手机看一下是否就完成了消息的发送。
+
+保存代码后,执行命令:
+
+```
+python3 test.py
+```
+
+扫描登录后的效果如下:
+
+![send-hello][send-hello]
+
+当然,还有一种更加快捷的回复方法就是在注册函数中直接回复。
+
+例如下面的例子将会将文本消息原封不动的返回。
+
+```python
+import itchat
+
+@itchat.msg_register(itchat.content.TEXT)
+def print_content(msg):
+ return msg['Text']
+
+itchat.auto_login()
+itchat.run()
+```
+
+这种方式显然更加直观也更加简单(不需要输入接受者的UserName)
+
+我们本次实践将会采用这种方式。
+
+### 3. 实现最简单的与图灵机器人的交互
+
+要做一个能够与人交流的机器人有很多种方法,最简单的莫过于使用他人提供的接口。
+
+我们这里以图灵机器人为例,演示这一功能。
+
+图灵机器人简单而言就是以一定的规则给图灵的服务器发送数据包(包含你对他说的话)
+
+图灵的服务器会以一定的规则给你返回数据包(包含他回复你的话)
+
+你需要一个Tuling Key来告诉图灵服务器你有权和他对话,我这里免费提供一些:
+
+```bash
+8edce3ce905a4c1dbb965e6b35c3834d
+eb720a8970964f3f855d863d24406576
+1107d5601866433dba9599fac1bc0083
+71f28bf79c820df10d39b4074345ef8c
+```
+
+下面我做一个配置图灵机器人的简单介绍,你想要自行了解或者申请Tuling Key可以看[这里][tuling]
+
+发送的规则简而言之是这样的:
+
+```json
+{
+ 'key' : 'TULING_KEY',
+ 'info' : 'YOUR_MSG',
+ 'userid' : 'USERID',
+}
+```
+
+其中userId是用户的标志,让机器人知道你是你。(也就是一个Tuling Key可以有多个用户)
+
+而返回的内容基本是这样的:
+
+```json
+{
+ 'code': 0,
+ 'text': 'RETURN_MSG',
+}
+```
+
+我们需要的内容就在text键里面。
+
+这里我们使用requests包完成整个操作(已经包含在itchat包的安装中了)。
+
+最后值得一提的就是这是一个post请求,那么直接上代码应该比我絮絮叨叨的说要直观很多。
+
+```python
+#coding=utf8
+import requests
+
+apiUrl = 'http://www.tuling123.com/openapi/api'
+data = {
+ 'key' : '8edce3ce905a4c1dbb965e6b35c3834d', # 如果这个Tuling Key不能用,那就换一个
+ 'info' : 'hello', # 这是我们发出去的消息
+ 'userid' : 'wechat-robot', # 这里你想改什么都可以
+}
+# 我们通过如下命令发送一个post请求
+r = requests.post(apiUrl, data=data).json()
+
+# 让我们打印一下返回的值,看一下我们拿到了什么
+print(r)
+```
+
+我们可以看到他回复了你好。
+
+![reply-hello][reply-hello]
+
+至此我们已经理解并掌握了所有需要的内容,下面将其组装起来即可。
+
+## 五、实验程序
+
+我先从概念上说一下组装是一个怎么样的过程。
+
+当然,如果你觉得代码更直观,我也在代码中为你写好了注释。
+
+这里我们首先将与图灵服务器的交互定义为一个函数。
+
+我们需要这个函数接收我们要发送给图灵的消息,返回图灵返回给我们的消息。
+
+再将与图灵交互并返回图灵返回结果的操作写成函数并在itchat中注册。
+
+最后启动itchat,我们的程序就完成了。
+
+```python
+#coding=utf8
+import requests
+import itchat
+
+KEY = '8edce3ce905a4c1dbb965e6b35c3834d'
+
+def get_response(msg):
+ # 这里我们就像在“3. 实现最简单的与图灵机器人的交互”中做的一样
+ # 构造了要发送给服务器的数据
+ apiUrl = 'http://www.tuling123.com/openapi/api'
+ data = {
+ 'key' : KEY,
+ 'info' : msg,
+ 'userid' : 'wechat-robot',
+ }
+ try:
+ r = requests.post(apiUrl, data=data).json()
+ # 字典的get方法在字典没有'text'值的时候会返回None而不会抛出异常
+ return r.get('text')
+ # 为了防止服务器没有正常响应导致程序异常退出,这里用try-except捕获了异常
+ # 如果服务器没能正常交互(返回非json或无法连接),那么就会进入下面的return
+ except:
+ # 将会返回一个None
+ return
+
+# 这里是我们在“1. 实现微信消息的获取”中已经用到过的同样的注册方法
+@itchat.msg_register(itchat.content.TEXT)
+def tuling_reply(msg):
+ # 为了保证在图灵Key出现问题的时候仍旧可以回复,这里设置一个默认回复
+ defaultReply = 'I received: ' + msg['Text']
+ # 如果图灵Key出现问题,那么reply将会是None
+ reply = get_response(msg['Text'])
+ # a or b的意思是,如果a有内容,那么返回a,否则返回b
+ # 有内容一般就是指非空或者非None,你可以用`if a: print('True')`来测试
+ return reply or defaultReply
+
+# 为了让实验过程更加方便(修改程序不用多次扫码),我们使用热启动
+itchat.auto_login(hotReload=True)
+itchat.run()
+```
+
+## 六、实验结果
+
+在本机上通过如下命令可以运行该程序
+
+```bash
+python3 main.py
+```
+
+扫码登陆后程序就成功运行了。
+
+之后在手机上使用别的账号给自己的微信号发送消息即可获得机器人的回复。
+
+这里给出使用的效果图:
+
+![demo][demo]
+
+如果你想要通过与其他用户的交互完成该操作,自行在注册的函数中进行修改即可。
+
+如果你想要进一步了解使用Python控制微信的细节,你也可以去到项目主页[itchat][itchat]。
+
+或者直接阅读[文档][document]也是不错的选择。
+
+如果你的本地环境并非Python3也没有关系,itchat同样完美支持Python2。
+
+## 七、代码获取
+
+我将整个项目目录做了一个打包,你可以直接下载后运行。
+
+你可以在[这里][code-package]下载。
+
+如果有什么问题,欢迎在我的[主页][author]留言或者[邮件][email]联系我。
+
+[vip]: https://www.shiyanlou.com/courses/684
+[author]: https://github.com/littlecodersh
+[install]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/install.png?imageView/2/h/300/
+[tuling]: http://tuling123.com/help/h_cent_webapi.jhtml
+[login]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/login.png?imageView/2/h/300/
+[send-hello]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/send-hello.png?imageView/2/h/400
+[reply-hello]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/reply-hello.png?imageView/2/h/300/
+[demo]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/demo.png?imageView/2/h/400/
+[code-package]: http://7xrip4.com1.z0.glb.clouddn.com/shiyanlou/itchat/2/main.py
+[email]: mailto:i7meavnktqegm1b@qq.com
+[itchat]: https://github.com/littlecodersh/itchat
+[document]: http://itchat.readthedocs.io/zh/latest/
diff --git a/docs/Tutorial/Tutorial1.md b/docs/tutorial/tutorial1.md
similarity index 95%
rename from docs/Tutorial/Tutorial1.md
rename to docs/tutorial/tutorial1.md
index 3758abb9..f60408d1 100644
--- a/docs/Tutorial/Tutorial1.md
+++ b/docs/tutorial/tutorial1.md
@@ -1,4 +1,4 @@
-#手把手教你扩展个人微信号(1)
+# 手把手教你扩展个人微信号(1)
现在的日常生活已经离不开微信,难免会生出微信有没有什么API可以使用的想法。
@@ -14,7 +14,7 @@ Python与基本的网络基础都不困难,所以即使没有这方面基础
关于本教程有任何建议或者疑问,都欢迎邮件与我联系,或者在[github上提出](https://github.com/littlecodersh/ItChat)(i7meavnktqegm1b@qq.com)
-##教程流程简介
+## 教程流程简介
教程将会从如何分析微信协议开始,第一部分将教你如何从零开始获取并模拟扩展个人微信号所需要的协议。
@@ -22,7 +22,7 @@ Python与基本的网络基础都不困难,所以即使没有这方面基础
第三部分就项目基本框架开发插件,以消息聚合等功能为例对框架做进一步介绍与扩展。
-##简单成果展示
+## 简单成果展示
目前的样例微信号被扩展为了能够完成信息上传下载的机器人,用于展示信息交互功能。
@@ -40,7 +40,7 @@ Python与基本的网络基础都不困难,所以即使没有这方面基础
* Wireshark 2.0.2
* 微信版本6.3.15
-###Wireshark配置
+### Wireshark配置
Wireshark是常见的抓包软件,这里通过一些配置抓取微信网页端的流量。
@@ -48,11 +48,11 @@ Wireshark是常见的抓包软件,这里通过一些配置抓取微信网页
配置完成以后开始抓包,载入`https://www.baidu.com`后若能看到http请求则配置成功。
-##分析并模拟扫码,并获取登录状态
+## 分析并模拟扫码,并获取登录状态
微信网页端登陆分为很多步,这里以第一步扫码为例讲解如何从抓包开始完成模拟。
-###分析过程
+### 分析过程
在抓包以前,我们需要先想清楚这是一个什么样的过程。
@@ -66,7 +66,7 @@ Wireshark是常见的抓包软件,这里通过一些配置抓取微信网页
有了这些概念以后就可以开始将这四步和包对应起来。
-###对应过程与实际的包
+### 对应过程与实际的包
开启wireshark抓包后登陆网页端微信,完成扫码登陆,然后关闭wireshark抓包。
@@ -129,7 +129,7 @@ Wireshark是常见的抓包软件,这里通过一些配置抓取微信网页
这里做一个简单的小结,这一部分简单的介绍了分析数据包的基本思路,以及一些小的技巧。当然这些仅供参考,在具体的抓包中完全可以根据具体的交互过程自由发挥。而目前留下来的问题有:第一步时的appid与第三步时的r,留待模拟时在做研究。
-###使用Python模拟扫码
+### 使用Python模拟扫码
这一部分我们使用python的requests模块,可以通过`pip install requests`安装。
@@ -303,7 +303,7 @@ print('Log in as %s'%dic['User']['NickName'])
* session的get,post方法返回一个量,可以通过r.text自动编码显示。
* 存储图片有特殊的方式与配置。
-##小结
+## 小结
到现在为止我展示了一个完整的抓包、分析、模拟的过程完成了模拟登陆,其他一些事情其实也都是类似的过程,想清楚每一步要做些什么即可。
@@ -321,7 +321,7 @@ print('Log in as %s'%dic['User']['NickName'])
另外,每次获取数据时(webwxsync)记得更新SyncKey。
-###某个特定请求不知道如何模拟
+### 某个特定请求不知道如何模拟
在项目中已经模拟好了几乎所有的请求,你可以通过参考我的方法与数据包。
@@ -329,7 +329,7 @@ print('Log in as %s'%dic['User']['NickName'])
项目中的微信网页端接口见[这里](https://github.com/littlecodersh/ItChat/blob/master/itchat/client.py)
-###无法上传中文文件名的文件与图片
+### 无法上传中文文件名的文件与图片
这是因为使用requests包会自动将中文文件名编码为服务器端无法识别的格式,所以需要修改requests包或者使用别的方法上传文件。
@@ -352,15 +352,15 @@ def format_header_param(name, value):
return value
```
-###登录时出现不安全的提示
+### 登录时出现不安全的提示
建议更新Python版本至2.7.11
-##小练习答案
+## 小练习答案
源码可在该地址下载:[这里](https://github.com/littlecodersh/EasierLife/blob/master/Scripts/SendToMyself.py)
-##结束语
+## 结束语
希望读完这篇文章能对你有帮助,有什么不足之处万望指正(鞠躬)。
diff --git a/docs/Tutorial/Tutorial2.md b/docs/tutorial/tutorial2.md
similarity index 100%
rename from docs/Tutorial/Tutorial2.md
rename to docs/tutorial/tutorial2.md
diff --git a/itchat/core.py b/itchat/core.py
index 5d79f292..f3c8a36d 100644
--- a/itchat/core.py
+++ b/itchat/core.py
@@ -435,7 +435,7 @@ def run(self, debug=True):
''' start auto respond
for option
- debug: if set, debug info will be shown on screen
- it is defined in components/hotreload.py
+ it is defined in components/register.py
'''
raise NotImplementedError()
def search_friends(self, name=None, userName=None, remarkName=None, nickName=None,
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..13fb84c1
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,21 @@
+site_name: 'itchat'
+pages:
+- '介绍':
+ - '项目简介': 'index.md'
+ - '快速入门': 'intro/start.md'
+ - '登陆配置': 'intro/login.md'
+ - '回复方法': 'intro/reply.md'
+ - '注册方法': 'intro/handler.md'
+ - '消息内容': 'intro/messages.md'
+ - '各类账号': 'intro/contact.md'
+ - '部署程序': 'intro/deploy.md'
+- 'API列表': 'api.md'
+- 'FAQ': 'FAQ.md'
+- '教程文章':
+ - '基础新手入门': 'tutorial/tutorial0.md'
+ - '实践项目分享': 'tutorial/tutorial2.md'
+ - '抓包及原理入门': 'tutorial/tutorial1.md'
+extra_css: ['docs/bootstrap/css']
+extra_javascript: ['docs/bootstrap/js']
+theme_dir: 'docs/bootstrap'
+repo_url: 'https://github.com/littlecodersh/itchat'