├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── package.json └── src ├── album └── .gitkeep ├── blessing.php ├── config.php ├── css ├── inline-home.css ├── inline-invitation.css ├── inline-map.css └── magnific-popup.css ├── img ├── baidumap_location.png ├── banner.png ├── banner@2x.png ├── bg │ ├── noisy_net.png │ └── noisy_net@2x.png ├── header.png ├── header@2x.png ├── home │ ├── banner_date.png │ ├── banner_logo.png │ ├── banner_name.png │ ├── bg.png │ ├── click_tip.png │ ├── follow.png │ ├── photo_frame.png │ ├── pic_1.jpg │ ├── pic_2.jpg │ ├── pic_3.jpg │ ├── pic_4.jpg │ ├── pic_5.jpg │ ├── pic_6.jpg │ ├── pic_7.jpg │ ├── pyq.png │ └── share.png ├── invitation.jpg ├── load.png ├── load@2x.png ├── qrcode_8cm.png ├── share_arrow.png └── touch-icon-iphone.png ├── index.php ├── invitation.php ├── js ├── inline-home.js ├── inline-invitation.js ├── inline-map.js ├── magnific-popup.min.js └── plugins │ ├── WeixinApi.js │ ├── preloadjs-0.4.1.combined.js │ ├── preloadjs-0.4.1.min.js │ ├── zepto-touch.js │ ├── zepto.js │ └── zepto.min.js ├── map.php ├── public ├── fun.php └── invitees.php ├── sign.php ├── wechat-img └── .gitkeep ├── wechat-log └── .gitkeep ├── wechat-photo └── .gitkeep ├── wechat-php-sdk ├── errCode.php ├── qyerrCode.php ├── qywechat.class.php ├── snoopy.class.php ├── wechat.class.php ├── wechat.js ├── wechatauth.class.php └── wechatext.class.php ├── wechat-push.php └── wechat.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Image PSD file 2 | *.psd 3 | 4 | # Private file 5 | *.ciaoca.* 6 | data/ 7 | src/album/*/*.jpg 8 | src/wechat-img/*.jpg 9 | src/wechat-img/*.png 10 | 11 | # Other files and folders 12 | .tmp/ 13 | dist/ 14 | node_modules/ 15 | _*/ -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | 5 | project: { 6 | name: 'wedding', 7 | srcPath: 'src', 8 | distPath: 'dist' 9 | }, 10 | 11 | watch: { 12 | bower: { 13 | options: { 14 | livereload: true 15 | }, 16 | files: ['<%=project.srcPath%>/**.html', '<%=project.srcPath%>/css/**.css', '<%=project.srcPath%>/js/**.js'] 17 | } 18 | }, 19 | clean: { 20 | start: ['<%=project.distPath%>/'], 21 | end: [ 22 | '<%=project.distPath%>/js/plugins/', 23 | '<%=project.distPath%>/js/_**', 24 | '<%=project.distPath%>/js/inline-*.js', 25 | '<%=project.distPath%>/css/_**', 26 | '<%=project.distPath%>/css/inline-*.css', 27 | '<%=project.distPath%>/img/_**', 28 | '<%=project.distPath%>/img/**/**.psd' 29 | ] 30 | }, 31 | copy: { 32 | page: { 33 | files: [ 34 | { 35 | expand: true, 36 | filter: 'isFile', 37 | cwd: '<%=project.srcPath%>/', 38 | src: ['*.html', '*.php'], 39 | dest: '<%=project.distPath%>/' 40 | } 41 | ] 42 | }, 43 | css: { 44 | expand: true, 45 | cwd: '<%=project.srcPath%>/css/', 46 | src: '**', 47 | dest: '<%=project.distPath%>/css/' 48 | }, 49 | js: { 50 | expand: true, 51 | cwd: '<%=project.srcPath%>/js/', 52 | src: '**', 53 | dest: '<%=project.distPath%>/js/' 54 | }, 55 | img: { 56 | expand: true, 57 | cwd: '<%=project.srcPath%>/img/', 58 | src: '**', 59 | dest: '<%=project.distPath%>/img/' 60 | }, 61 | album: { 62 | files: [ 63 | { 64 | expand: true, 65 | cwd: '<%=project.srcPath%>/album/', 66 | src: '**', 67 | dest: '<%=project.distPath%>/album/' 68 | } 69 | ] 70 | }, 71 | php: { 72 | files: [ 73 | { 74 | expand: true, 75 | cwd: '<%=project.srcPath%>/public/', 76 | src: '**', 77 | dest: '<%=project.distPath%>/public/' 78 | } 79 | ] 80 | }, 81 | wechat: { 82 | files: [ 83 | { 84 | expand: true, 85 | cwd: '<%=project.srcPath%>/wechat-img/', 86 | src: '**', 87 | dest: '<%=project.distPath%>/wechat-img/' 88 | }, { 89 | expand: true, 90 | cwd: '<%=project.srcPath%>/wechat-log/', 91 | src: '**', 92 | dest: '<%=project.distPath%>/wechat-log/' 93 | }, { 94 | expand: true, 95 | cwd: '<%=project.srcPath%>/wechat-photo/', 96 | src: '**', 97 | dest: '<%=project.distPath%>/wechat-photo/' 98 | }, { 99 | expand: true, 100 | cwd: '<%=project.srcPath%>/wechat-php-sdk/', 101 | src: '**', 102 | dest: '<%=project.distPath%>/wechat-php-sdk/' 103 | } 104 | ] 105 | } 106 | }, 107 | useminPrepare: { 108 | html: ['<%=project.distPath%>/*.html', '<%=project.distPath%>/*.php'] 109 | }, 110 | usemin: { 111 | html: ['<%=project.distPath%>/*.html', '<%=project.distPath%>/*.php'] 112 | }, 113 | inline: { 114 | dist: { 115 | options:{ 116 | cssmin: true, 117 | uglify: true, 118 | exts: ['php'] 119 | }, 120 | src: ['<%=project.distPath%>/*.html', '<%=project.distPath%>/*.php'] 121 | } 122 | } 123 | }); 124 | 125 | require('load-grunt-tasks')(grunt); 126 | 127 | grunt.registerTask('default', ['watch']); 128 | grunt.registerTask('build', [ 129 | 'clean:start', 130 | 'copy:page', 131 | 'copy:css', 132 | 'copy:js', 133 | 'copy:img', 134 | 'copy:album', 135 | 'copy:php', 136 | 'copy:wechat', 137 | 'useminPrepare', 138 | 'concat:generated', 139 | 'uglify:generated', 140 | 'usemin', 141 | 'inline:dist', 142 | 'clean:end' 143 | ]); 144 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 小叉 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #wedding 2 | Wu & Xiong's Wedding 基于微信公众号开发的微婚礼 3 | 4 | 没有使用任何数据库,所有涉及到的数据都使用文件保存,或在文件中手动设置。 5 | 6 | ##微信公众号:fensalir 7 | 8 | ![fensalir](http://wedding.ciaoca.com/img/qrcode_8cm.png) 9 | 10 | ##体验 11 | 使用移动设备访问:http://wedding.ciaoca.com/ 12 | 13 | ![首页](http://qr.liantu.com/api.php?text=http://wedding.ciaoca.com/&w=258) 14 | 15 | 16 | ##初始化 17 | ``` 18 | npm install 19 | ``` 20 | 21 | ##项目打包 22 | ``` 23 | grunt build 24 | ``` 25 | 26 | ##目录结构 27 | ``` 28 | src 29 | ├─album // 首页相册的大图 30 | │ ├─name1 31 | │ │ ├─1.jpg 32 | │ │ ├─2.jpg 33 | │ │ └─N.jpg 34 | │ ├─name2 35 | │ └─nameN 36 | │ 37 | ├─public 38 | │ └─invitees.php // 被邀请人名单 39 | │ 40 | ├─wechat-img // 微信消息推送使用的图片 41 | ├─wechat-log // 微信消息日志 42 | ├─wechat-photo // 微信接收到的图片 43 | │ 44 | ├─blessing.php // 祝福墙 45 | ├─index.php // 首页 46 | ├─invitation.php // 喜帖 47 | ├─map.php // 婚宴酒店地图 48 | ├─sign.php // 现场签到 49 | ├─wechat-push.php // 微信推送消息 50 | └─wechat.php // 微信接收消息 51 | ``` 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wedding", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "grunt": "^0.4.5", 6 | "grunt-contrib-clean": "^0.6.0", 7 | "grunt-contrib-concat": "^0.5.0", 8 | "grunt-contrib-copy": "^0.7.0", 9 | "grunt-contrib-cssmin": "^0.10.0", 10 | "grunt-contrib-uglify": "^0.6.0", 11 | "grunt-contrib-watch": "^0.6.1", 12 | "grunt-filerev": "^2.1.0", 13 | "grunt-inline": "^0.3.2", 14 | "grunt-usemin": "^2.4.0", 15 | "load-grunt-tasks": "^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/album/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/album/.gitkeep -------------------------------------------------------------------------------- /src/blessing.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | <?=$siteData['title']?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 |

祝福墙

22 |

该功能即将上线,尽请期待

23 | 24 | -------------------------------------------------------------------------------- /src/config.php: -------------------------------------------------------------------------------- 1 | 'Wu & Xiong\'s Wedding', 5 | 'timezone' => 'Asia/Shanghai', 6 | 'root' => $_SERVER['DOCUMENT_ROOT'], 7 | 'host' => $_SERVER['HTTP_HOST'], 8 | 'path' => '', 9 | 'getKey' => '', 10 | 'cookieTime' => 259200, 11 | 'wechatToken' => 'token', 12 | 'wechatAppID' => 'appid', 13 | 'wechatAppSecret' => 'appsecret', 14 | ); 15 | $siteData['root'] .= $siteData['path']; 16 | $siteData['homePage'] = $siteData['path'] . '/'; 17 | $siteData['loginPage'] = $siteData['path'] . '/login'; 18 | $siteData['tipsPage'] = $siteData['path'] . '/tips'; 19 | 20 | date_default_timezone_set($siteData['timezone']); 21 | session_start(); 22 | ?> -------------------------------------------------------------------------------- /src/css/inline-home.css: -------------------------------------------------------------------------------- 1 | html,body,div,ul,ol,li,dl,dt,dd,h1,h2,h3,h4,h5,h6,p,table,th,td,fieldset,form,input,textarea,select,hr,blockquote,pre{margin:0;padding:0;} 2 | h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:bold;line-height:2;} 3 | img{border:none;vertical-align:top;} 4 | html{background:#191e23 url(../img/bg/noisy_net.png) repeat;font-size:10px;} 5 | body{color:#333;font-family:Arial,'\5fae\8f6f\96c5\9ed1',sans-serif;line-height:1.6;} 6 | 7 | /** 8 | * 默认布局 9 | * ------------------------------ */ 10 | .wrap{position:relative;min-width:320px;max-width:640px;margin:0 auto;background:#84d8d1;} 11 | .wrap:after{content:'';visibility:hidden;display:block;clear:both;height:0;font-size:0;} 12 | 13 | 14 | /** 15 | * loading 16 | * ------------------------------ */ 17 | .loader_lock{overflow:hidden;position:relative;width:100%;height:100%;background:#84d8d1;} 18 | .loader_lock:after{content:'Tip: 建议在 WIFI 环境下浏览';position:absolute;bottom:10px;left:10px;color:#eaf8f6;} 19 | 20 | /* 非 WIFI 环境访问 */ 21 | .loader_wifi{overflow:hidden;position:absolute;top:55%;left:50%;padding:10px;border-radius:10px;background:#41c1ca;color:#eaf8f6;text-align:center; 22 | -webkit-transform:translateX(-50%); 23 | transform:translateX(-50%); 24 | } 25 | .loader_wifi:before{content:'当前正在使用 2G/3G 网络';display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;} 26 | .loader_wifi:after{content:'点击继续浏览';display:block;} 27 | 28 | .loader{overflow:hidden;position:absolute;bottom:50%;left:50%;width:120px;height:100px;opacity:1; 29 | -webkit-transform:translateX(-50%); 30 | transform:translateX(-50%); 31 | } 32 | .loader:before{content:'';display:block;position:relative;width:120px;height:100px;background:url(../img/load.png) no-repeat;background-size:cover;z-index:2;} 33 | .loader:after{content:attr(title);position:absolute;bottom:50%;left:0;width:100%;color:#41c1ca;font:16px/1 Arial;text-align:center;text-shadow:0 0 6px #fff;z-index:3;} 34 | .loader i{position:absolute;bottom:0;left:0;width:100%;height:0%;background:rgba(255,255,255,0.8);z-index:1;} 35 | 36 | .loader.end{opacity:1; 37 | -webkit-animation:loaderEnd 0.5s 0.6s ease-out both; 38 | } 39 | .loader.end:after{content:'100%';} 40 | .loader.end i{height:100% !important; 41 | -webkit-transition:height 0.5s ease-in; 42 | transition:height 0.5s ease-in; 43 | } 44 | @-webkit-keyframes loaderEnd{ 45 | 0%{opacity:1;} 46 | 100%{opacity:0;} 47 | } 48 | 49 | /* PC 访问显示二维码 */ 50 | .loader_qrcode{overflow:hidden;position:absolute;top:50%;left:50%;width:258px;height:288px;margin:-144px 0 0 -129px;padding:10px;border-radius:10px;background:#41c1ca;color:#eaf8f6;text-align:center;} 51 | .loader_qrcode:before{content:'';display:block;width:258px;height:258px;border-radius:10px;background:url(../img/qrcode_8cm.png) no-repeat;} 52 | .loader_qrcode:after{content:'请使用微信扫一扫功能,扫描上面的二维码。';display:block;margin-top:12px;} 53 | 54 | /* 微信分享提示 */ 55 | .share_tip{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:99;} 56 | .share_tip:before{content:'';position:absolute;top:10px;right:10px;width:32px;height:51px;background:url(../img/share_arrow.png) no-repeat;background-size:contain;} 57 | .share_tip:after{content:'请点击操作菜单,即可发送给朋友或者分享到朋友圈。';display:block;padding:70px 10% 0;color:#fff;font-size:2.6rem;font-style:italic;} 58 | 59 | /** 60 | * loading 61 | * ------------------------------ */ 62 | .wed_main .inbox{position:relative;opacity:0;} 63 | .wed_main.coming .inbox{opacity:1; 64 | -webkit-transition:opacity 0.8s ease-in; 65 | transition:opacity 0.8s ease-in; 66 | } 67 | .wed_main.coming .wed_banner{-webkit-animation:BannerShow 0.6s 1s ease-in-out both;} 68 | .wed_main.coming .wed_banner .logo{-webkit-animation:BannerLogoShow 1.4s 1.6s ease-out both;} 69 | .wed_main.coming .wed_banner .date{-webkit-animation:BannerDateShow 1.4s 1.6s ease-out both;} 70 | .wed_main.coming .wed_banner .name{-webkit-animation:BannerNameShow 1s 3s ease-in both;} 71 | .wed_main.coming .wed_timeline .tail{-webkit-animation:TimelineTailShow 2s 4s ease-in both;} 72 | .wed_main.coming .wed_timeline .wedding{-webkit-animation:TimelineItemShow 1s 4.2s ease-in both;} 73 | .wed_main.coming .wed_timeline .happiness{-webkit-animation:TimelineItemShow 0.5s 4.4s ease-in both;} 74 | .wed_main.coming .wed_timeline .happy{-webkit-animation:TimelineItemShow 0.5s 4.7s ease-in both;} 75 | 76 | @-webkit-keyframes BannerShow{ 77 | 0%{padding-top:0;} 78 | 100%{padding-top:45%;} 79 | } 80 | @-webkit-keyframes BannerLogoShow{ 81 | 0%{opacity:0;-webkit-transform:rotateY(-270deg);} 82 | 60%{opacity:1;-webkit-transform:rotateY(0);} 83 | 100%{opacity:1;-webkit-transform:rotateY(0);} 84 | } 85 | @-webkit-keyframes BannerDateShow{ 86 | 0%{right:30%;opacity:0;} 87 | 60%{right:30%;opacity:0;} 88 | 100%{right:3%;opacity:1;} 89 | } 90 | @-webkit-keyframes BannerNameShow{ 91 | 0%{opacity:0;-webkit-transform:translateY(-10px);} 92 | 100%{opacity:1;-webkit-transform:translateY(0);} 93 | } 94 | @-webkit-keyframes TimelineTailShow{ 95 | 0%{height:0;} 96 | 100%{height:100%;} 97 | } 98 | @-webkit-keyframes TimelineItemShow{ 99 | 0%{opacity:0;} 100 | 100%{opacity:1;} 101 | } 102 | 103 | 104 | /** 105 | * Banner 106 | * ------------------------------ */ 107 | .wed_banner{position:relative;height:0;padding-top:0;background:#ecf9f8;z-index:1;} 108 | .wed_banner .logo{position:absolute;top:14%;left:4%;width:120px;height:84px;background:url(../img/home/banner_logo.png) no-repeat;background-size:contain;opacity:0;} 109 | .wed_banner .date{position:absolute;top:19%;right:3%;width:175px;height:70px;background:url(../img/home/banner_date.png) no-repeat;background-size:contain;opacity:0;} 110 | .wed_banner .name{position:absolute;top:71%;left:50%;width:180px;height:36px;margin-left:-90px;background:url(../img/home/banner_name.png) no-repeat;background-size:contain;opacity:0;} 111 | 112 | 113 | /** 114 | * Timeline 115 | * ------------------------------ */ 116 | .wed_timeline{position:relative;height:0;padding-top:425%;background:url(../img/home/bg.png) no-repeat center top;background-size:cover;font-size:1.2rem;} 117 | .wed_timeline h2, 118 | .wed_timeline h3{color:#0aa0ab;font:bold 1.8rem/1.4 'Hiragino Kaku Gothic ProN','\5fae\8f6f\96c5\9ed1',sans-serif;text-shadow:1px 0 2px #ecf9f8,0 1px 2px #ecf9f8;} 119 | .wed_timeline p{color:#01909a;line-height:1.5;text-shadow:1px 0 0 #d0f5f2,0 1px 0 #d0f5f2;white-space:nowrap;text-overflow:ellipsis;} 120 | .wed_timeline .tail{position:absolute;top:0;left:50%;width:24px;height:0;margin-left:-12px;background:#ecf9f8;} 121 | .wed_timeline .tail:before{content:'';position:absolute;top:0;left:50%;width:18px;height:100%;margin-left:-10px;border-left:1px dashed #84d8d1;border-right:1px dashed #84d8d1;} 122 | .wed_timeline .tail:after{content:'';position:absolute;bottom:0;left:50%;width:0;height:0;margin-left:-12px;border:12px solid transparent;border-bottom-color:#84d8d1;} 123 | .wed_timeline .item{position:absolute;width:43%;} 124 | .wed_timeline .photo{display:block;width:100px;height:75px;margin-left:2px;margin-bottom:5px;padding:7px;background:url(../img/home/photo_frame.png) no-repeat center top;background-size:cover;box-shadow:1px 1px 1px rgba(0,0,0,0.3);} 125 | .wed_timeline .photo:after{content:'';display:block;width:100%;height:100%; background-repeat:no-repeat;background-size:cover; box-shadow:inset 1px 1px 1px rgba(0,0,0,0.3);} 126 | .wed_timeline .info{overflow:hidden;} 127 | 128 | .wed_timeline .wedding{top:2%;left:2%;opacity:0;} 129 | .wed_timeline .wedding .photo:after{background-image:url(../img/home/pic_7.jpg);} 130 | .wed_timeline .happiness{top:10%;left:56%;opacity:0;} 131 | .wed_timeline .happiness .photo:after{background-image:url(../img/home/pic_6.jpg);} 132 | .wed_timeline .happy{top:19%;left:2%;opacity:0;} 133 | .wed_timeline .happy:before{content:'';position:absolute;top:-30px;right:0;width:100px;height:28px;background:url(../img/home/click_tip.png) no-repeat; background-size:contain;} 134 | .wed_timeline .happy .photo:after{background-image:url(../img/home/pic_5.jpg);} 135 | .wed_timeline .hangzhou{top:41%;left:56%;} 136 | .wed_timeline .hangzhou .photo:after{background-image:url(../img/home/pic_4.jpg);} 137 | .wed_timeline .expo{top:47%;left:2%;} 138 | .wed_timeline .expo .photo:after{background-image:url(../img/home/pic_3.jpg);} 139 | .wed_timeline .graduate{top:61%;left:56%;} 140 | .wed_timeline .graduate .photo:after{background-image:url(../img/home/pic_2.jpg);} 141 | .wed_timeline .express{top:72%;left:2%;} 142 | .wed_timeline .express .photo:after{background-image:url(../img/home/pic_1.jpg);} 143 | 144 | .wed_timeline .wedding h2, 145 | .wed_timeline .wedding h3{font-style:italic;} 146 | .wed_timeline .graduate h2, 147 | .wed_timeline .express h2{font-size:1.6rem;} 148 | 149 | .wed_timeline .follow{position:absolute;bottom:10px;right:10px;width:80px;padding:4px 6px 6px 6px;border-radius:5px;background:rgba(255,255,255,0.6);background-size:contain;text-align:center;} 150 | .wed_timeline .qrcode{display:block;width:80px;height:80px;background:url(../img/qrcode_8cm.png) no-repeat;background-size:contain;} 151 | .wed_timeline .share{position:absolute;bottom:10px;left:10px;width:120px;height:30px;background:url(../img/home/pyq.png) no-repeat;background-size:contain;} 152 | 153 | 154 | 155 | 156 | /** 157 | * Responsive 158 | * ------------------------------ */ 159 | @media screen and (min-width:480px){ 160 | .share_tip:after{text-align:center;} 161 | 162 | .wed_banner .logo{width:180px;height:126px;} 163 | .wed_banner .date{width:262px;height:105px;} 164 | .wed_banner .name{width:270px;height:54px;margin-left:-135px;} 165 | 166 | .wed_timeline{font-size:1.6rem;} 167 | .wed_timeline .tail{width:36px;margin-left:-18px;} 168 | .wed_timeline .tail:before{width:28px;margin-left:-15px;} 169 | .wed_timeline .tail:after{margin-left:-18px;border-width:18px;} 170 | .wed_timeline h2, 171 | .wed_timeline h3, 172 | .wed_timeline .graduate h2, 173 | .wed_timeline .express h2{font-size:2.4rem;} 174 | .wed_timeline p{text-shadow:1px 0 2px #d0f5f2,0 1px 2px #d0f5f2;} 175 | .wed_timeline .item{width:40%;} 176 | .wed_timeline .photo{width:150px;height:112px;padding:10px;} 177 | 178 | .wed_timeline .wedding, 179 | .wed_timeline .happy, 180 | .wed_timeline .expo, 181 | .wed_timeline .express{left:4%;} 182 | .wed_timeline .happiness, 183 | .wed_timeline .hangzhou, 184 | .wed_timeline .graduate{left:58%;} 185 | 186 | .wed_timeline .follow{right:20px;width:120px;} 187 | .wed_timeline .qrcode{width:120px;height:120px;} 188 | .wed_timeline .share{left:20px;width:180px;height:45px;} 189 | } 190 | 191 | @media screen and (min-width:568px){ 192 | .wed_banner .logo{width:213px;height:149px;} 193 | .wed_banner .date{width:310px;height:124px;} 194 | .wed_banner .name{width:320px;height:64px;margin-left:-160px;} 195 | } 196 | 197 | @media screen and (min-width:640px){ 198 | .wed_banner .logo{width:240px;height:168px;} 199 | .wed_banner .date{width:350px;height:140px;} 200 | .wed_banner .name{width:360px;height:72px;margin-left:-180px;} 201 | 202 | .wed_timeline{font-size:1.8rem;} 203 | .wed_timeline .tail{width:40px;margin-left:-20px;} 204 | .wed_timeline .tail:before{width:32px;margin-left:-17px;} 205 | .wed_timeline .tail:after{margin-left:-20px;border-width:20px;} 206 | .wed_timeline h2, 207 | .wed_timeline h3, 208 | .wed_timeline .graduate h2, 209 | .wed_timeline .express h2{margin-bottom:5px;font-size:2.4rem;} 210 | .wed_timeline .wedding h2{margin-bottom:0;} 211 | .wed_timeline p{text-shadow:1px 0 2px #d0f5f2,0 1px 2px #d0f5f2;} 212 | .wed_timeline .photo{width:200px;height:150px;padding:15px;} 213 | 214 | .wed_timeline .follow{padding:10px;} 215 | } 216 | 217 | 218 | /** 219 | * Retina 220 | * ------------------------------ */ 221 | @media only screen and (-webkit-min-device-pixel-ratio:1.5){ 222 | html{background-image:url(../img/bg/noisy_net@2x.png);} 223 | .loader:before{background-image:url(../img/load@2x.png);} 224 | } -------------------------------------------------------------------------------- /src/css/inline-invitation.css: -------------------------------------------------------------------------------- 1 | html,body,div,ul,ol,li,dl,dt,dd,h1,h2,h3,h4,h5,h6,p,table,th,td,fieldset,form,input,textarea,select,hr,blockquote,pre{margin:0;padding:0;} 2 | h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:bold;line-height:2;} 3 | img{border:none;vertical-align:top;} 4 | html{background:#c1fbff;font-size:10px;} 5 | body{color:#333;font-family:Arial,'\5fae\8f6f\96c5\9ed1',sans-serif;line-height:1.6;} 6 | 7 | a, 8 | a:hover{color:#41c1ca;text-decoration:none;} 9 | 10 | .wrap{position:relative;max-width:640px;margin:0 auto;} 11 | .box{height:0;padding-top:276%;background:url(../img/invitation.jpg) no-repeat center top;background-size:cover;} 12 | .invitee{position:absolute;top:14%;left:0;width:100%;color:#014289;font-size:1.8rem;font-weight:bold;line-height:30px;text-align:center;} 13 | .info{display:none;position:absolute;top:41%;left:0;width:100%;color:#014289;font-size:1.4rem;font-weight:bold;line-height:20px;text-align:center;} 14 | .qrcode{position:absolute;bottom:2%;left:0;width:100%;color:#380d05;font-size:1.2rem;font-weight:bold;text-align:center;line-height:20px;} 15 | .qrcode .title{font-size:1.4rem;} 16 | .qrcode .pic{display:block;width:130px;height:130px;margin:3px auto;background:url(../img/qrcode_8cm.png) no-repeat;background-size:contain;} 17 | .qrcode .tip{color:#fff;font-weight:normal;} 18 | 19 | 20 | /** 21 | * Responsive 22 | * ------------------------------ */ 23 | @media screen and (min-width:480px){ 24 | .invitee{font-size:2.4rem;} 25 | .info{font-size:1.8rem;line-height:30px;} 26 | 27 | .qrcode{font-size:1.6rem;line-height:30px;} 28 | .qrcode .title{font-size:2rem;} 29 | .qrcode .pic{width:200px;height:200px;} 30 | } 31 | 32 | @media screen and (min-width:600px){ 33 | .invitee{font-size:3rem;line-height:40px;} 34 | .info{font-size:2.4rem;line-height:36px;} 35 | } 36 | 37 | @media screen and (min-width:641px){ 38 | html{background:#191e23 url(../img/bg/noisy_net.png) repeat;} 39 | } 40 | 41 | @media only screen and (-webkit-min-device-pixel-ratio:2) and (min-width:641px){ 42 | html{background-image:url(../img/bg/noisy_net@2x.png);} 43 | } -------------------------------------------------------------------------------- /src/css/inline-map.css: -------------------------------------------------------------------------------- 1 | html,body,div,ul,ol,li,dl,dt,dd,h1,h2,h3,h4,h5,h6,p,table,th,td,fieldset,form,input,textarea,select,hr,blockquote,pre{margin:0;padding:0;} 2 | h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:bold;line-height:2;} 3 | img{border:none;vertical-align:top;} 4 | html{background:#84d8d1;font-size:10px;} 5 | body{color:#333;font-family:Arial,'\5fae\8f6f\96c5\9ed1',sans-serif;line-height:1.6;} 6 | 7 | a, 8 | a:hover{color:#41c1ca;text-decoration:none;} 9 | 10 | h1{color:#41c1ca;font-size:2.4rem;line-height:50px;text-shadow:0 0 10px #fff,0 0 10px #fff,0 0 10px #fff;text-align:center;} 11 | h2{font-size:1.6rem;line-height:20px;} 12 | 13 | .wrap{position:relative;background:url(../img/header.png) no-repeat;background-size:contain;} 14 | .hotel{margin:0 10px;padding:10px;border-radius:10px;background-color:rgba(255,255,255,0.7);color:#41c1ca;font-size:1.4rem;line-height:20px;} 15 | 16 | .map{margin-top:10px;} 17 | .map img{margin:0 auto;border-radius:10px;background:#fff;} 18 | 19 | .btns{overflow:hidden;margin-top:16px;text-align:center;line-height:44px; 20 | background:rgba(255,255,255,1); 21 | background:-moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(234,248,246,1) 100%); 22 | background:-webkit-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(234,248,246,1) 100%); 23 | background:-o-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(234,248,246,1) 100%); 24 | background:-ms-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(234,248,246,1) 100%); 25 | background:linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(234,248,246,1) 100%); 26 | } 27 | .btns a{float:left;position:relative;width:34%;font-size:1.8rem;} 28 | .btns a + a{width:33%;} 29 | .btns a + a:before{content:'';position:absolute;top:0;left:0;width:1px;height:100%;background-color:#84d8d1;} 30 | 31 | 32 | 33 | /** 34 | * Responsive 35 | * ------------------------------ */ 36 | @media screen and (min-width:641px){ 37 | html{background:#191e23 url(../img/bg/noisy_net.png) repeat;} 38 | .wrap{width:640px;margin:0 auto;background-color:#84d8d1;} 39 | } 40 | 41 | 42 | 43 | /** 44 | * Retina 45 | * ------------------------------ */ 46 | @media only screen and (-webkit-min-device-pixel-ratio:2){ 47 | .wrap{background-image:url(../img/header@2x.png);} 48 | } 49 | 50 | @media only screen and (-webkit-min-device-pixel-ratio:2) and (min-width:641px){ 51 | html{background-image:url(../img/bg/noisy_net@2x.png);} 52 | } -------------------------------------------------------------------------------- /src/css/magnific-popup.css: -------------------------------------------------------------------------------- 1 | /* Magnific Popup CSS */ 2 | .mfp-bg{top:0;left:0;width:100%;height:100%;z-index:1042;overflow:hidden;position:fixed;background:#0b0b0b;opacity:0.8;filter:alpha(opacity=80);} 3 | .mfp-wrap{top:0;left:0;width:100%;height:100%;z-index:1043;position:fixed;outline:none !important;-webkit-backface-visibility:hidden;} 4 | .mfp-container{text-align:center;position:absolute;width:100%;height:100%;left:0;top:0;padding:0 8px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} 5 | .mfp-container:before{content:'';display:inline-block;height:100%;vertical-align:middle;} 6 | .mfp-align-top .mfp-container:before{display:none;} 7 | .mfp-content{position:relative;display:inline-block;vertical-align:middle;margin:0 auto;text-align:left;z-index:1045;} 8 | .mfp-inline-holder .mfp-content, 9 | .mfp-ajax-holder .mfp-content{width:100%;cursor:auto;} 10 | .mfp-ajax-cur{cursor:progress;} 11 | .mfp-zoom-out-cur, 12 | .mfp-zoom-out-cur .mfp-image-holder .mfp-close{cursor:-moz-zoom-out;cursor:-webkit-zoom-out;cursor:zoom-out;} 13 | .mfp-zoom{cursor:pointer;cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in;} 14 | .mfp-auto-cursor .mfp-content{cursor:auto;} 15 | .mfp-close, 16 | .mfp-arrow, 17 | .mfp-preloader, 18 | .mfp-counter{-webkit-user-select:none;-moz-user-select:none;user-select:none;} 19 | .mfp-loading.mfp-figure{display:none;} 20 | .mfp-hide{display:none !important;} 21 | .mfp-preloader{color:#cccccc;position:absolute;top:50%;width:auto;text-align:center;margin-top:-0.8em;left:8px;right:8px;z-index:1044;} 22 | .mfp-preloader a{color:#cccccc;} 23 | .mfp-preloader a:hover{color:white;} 24 | .mfp-s-ready .mfp-preloader{display:none;} 25 | .mfp-s-error .mfp-content{display:none;} 26 | button.mfp-close, 27 | button.mfp-arrow{overflow:visible;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;display:block;outline:none;padding:0;z-index:1046;-webkit-box-shadow:none;box-shadow:none;} 28 | button.mfp-arrow{background-color:rgba(0,0,0,0.6);} 29 | button::-moz-focus-inner{padding:0;border:0;} 30 | .mfp-close{width:44px;height:44px;line-height:44px;position:absolute;right:0;top:0;text-decoration:none;text-align:center;opacity:0.65;filter:alpha(opacity=65);padding:0 0 18px 10px;color:white;font-style:normal;font-size:28px;font-family:Arial, Baskerville, monospace;} 31 | .mfp-close:hover, 32 | .mfp-close:focus{opacity:1;filter:alpha(opacity=100);} 33 | .mfp-close:active{top:1px;} 34 | .mfp-close-btn-in .mfp-close{color:#333333;} 35 | .mfp-image-holder .mfp-close, 36 | .mfp-iframe-holder .mfp-close{color:white;right:-6px;text-align:right;padding-right:6px;width:100%;} 37 | .mfp-counter{position:absolute;top:0;right:0;color:#cccccc;font-size:12px;line-height:18px;} 38 | .mfp-arrow{position:absolute;margin:0;top:50%;margin-top:-40px;padding:0;width:50px;height:80px;-webkit-tap-highlight-color:rgba(0, 0, 0, 0);} 39 | .mfp-arrow:after{content:'';display:block;position:absolute;top:50%;left:20px;margin-top:-20px;color:#fff;font:24px/40px Arial;} 40 | .mfp-arrow-left{left:10px;border-radius:40px 10px 10px 40px;} 41 | .mfp-arrow-right{right:10px;border-radius:10px 40px 40px 10px;} 42 | .mfp-iframe-holder{padding-top:40px;padding-bottom:40px;} 43 | .mfp-iframe-holder .mfp-content{line-height:0;width:100%;max-width:900px;} 44 | .mfp-iframe-holder .mfp-close{top:-40px;} 45 | .mfp-iframe-scaler{width:100%;height:0;overflow:hidden;padding-top:56.25%;} 46 | .mfp-iframe-scaler iframe{position:absolute;display:block;top:0;left:0;width:100%;height:100%;box-shadow:0 0 8px rgba(0, 0, 0, 0.6);background:black;} 47 | /* Main image in popup */ 48 | img.mfp-img{width:auto;max-width:100%;height:auto;display:block;line-height:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:40px 0 40px;margin:0 auto;} 49 | /* The shadow behind the image */ 50 | .mfp-figure{line-height:0;} 51 | .mfp-figure:after{content:'';position:absolute;left:0;top:40px;bottom:40px;display:block;right:0;width:auto;height:auto;z-index:-1;box-shadow:0 0 8px rgba(0, 0, 0, 0.6);background:#444444;} 52 | .mfp-figure small{color:#bdbdbd;display:block;font-size:12px;line-height:14px;} 53 | .mfp-figure figure{margin:0;} 54 | .mfp-bottom-bar{margin-top:-36px;position:absolute;top:100%;left:0;width:100%;cursor:auto;} 55 | .mfp-title{text-align:left;line-height:18px;color:#f3f3f3;word-wrap:break-word;padding-right:36px;} 56 | .mfp-image-holder .mfp-content{max-width:100%;} 57 | .mfp-gallery .mfp-image-holder .mfp-figure{cursor:pointer;} 58 | @media screen and (max-width:800px) and (orientation:landscape), screen and (max-height:300px){ 59 | /** 60 | * Remove all paddings around the image on small screen 61 | */ 62 | .mfp-img-mobile .mfp-image-holder{padding-left:0;padding-right:0;} 63 | .mfp-img-mobile img.mfp-img{padding:0;} 64 | .mfp-img-mobile .mfp-figure:after{top:0;bottom:0;} 65 | .mfp-img-mobile .mfp-figure small{display:inline;margin-left:5px;} 66 | .mfp-img-mobile .mfp-bottom-bar{background:rgba(0, 0, 0, 0.6);bottom:0;margin:0;top:auto;padding:3px 5px;position:fixed;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} 67 | .mfp-img-mobile .mfp-bottom-bar:empty{padding:0;} 68 | .mfp-img-mobile .mfp-counter{right:5px;top:3px;} 69 | .mfp-img-mobile .mfp-close{top:0;right:0;width:35px;height:35px;line-height:35px;background:rgba(0, 0, 0, 0.6);position:fixed;text-align:center;padding:0;} 70 | } 71 | 72 | @media all and (max-width:900px){ 73 | .mfp-arrow{-webkit-transform:scale(0.75);transform:scale(0.75);} 74 | .mfp-arrow-left{-webkit-transform-origin:0;transform-origin:0;} 75 | .mfp-arrow-right{-webkit-transform-origin:100%;transform-origin:100%;} 76 | .mfp-container{padding-left:6px;padding-right:6px;} 77 | } 78 | 79 | .mfp-ie7 .mfp-img{padding:0;} 80 | .mfp-ie7 .mfp-bottom-bar{width:600px;left:50%;margin-left:-300px;margin-top:5px;padding-bottom:5px;} 81 | .mfp-ie7 .mfp-container{padding:0;} 82 | .mfp-ie7 .mfp-content{padding-top:44px;} 83 | .mfp-ie7 .mfp-close{top:0;right:0;padding-top:0;} 84 | -------------------------------------------------------------------------------- /src/img/baidumap_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/baidumap_location.png -------------------------------------------------------------------------------- /src/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/banner.png -------------------------------------------------------------------------------- /src/img/banner@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/banner@2x.png -------------------------------------------------------------------------------- /src/img/bg/noisy_net.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/bg/noisy_net.png -------------------------------------------------------------------------------- /src/img/bg/noisy_net@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/bg/noisy_net@2x.png -------------------------------------------------------------------------------- /src/img/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/header.png -------------------------------------------------------------------------------- /src/img/header@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/header@2x.png -------------------------------------------------------------------------------- /src/img/home/banner_date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/banner_date.png -------------------------------------------------------------------------------- /src/img/home/banner_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/banner_logo.png -------------------------------------------------------------------------------- /src/img/home/banner_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/banner_name.png -------------------------------------------------------------------------------- /src/img/home/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/bg.png -------------------------------------------------------------------------------- /src/img/home/click_tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/click_tip.png -------------------------------------------------------------------------------- /src/img/home/follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/follow.png -------------------------------------------------------------------------------- /src/img/home/photo_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/photo_frame.png -------------------------------------------------------------------------------- /src/img/home/pic_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_1.jpg -------------------------------------------------------------------------------- /src/img/home/pic_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_2.jpg -------------------------------------------------------------------------------- /src/img/home/pic_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_3.jpg -------------------------------------------------------------------------------- /src/img/home/pic_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_4.jpg -------------------------------------------------------------------------------- /src/img/home/pic_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_5.jpg -------------------------------------------------------------------------------- /src/img/home/pic_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_6.jpg -------------------------------------------------------------------------------- /src/img/home/pic_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pic_7.jpg -------------------------------------------------------------------------------- /src/img/home/pyq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/pyq.png -------------------------------------------------------------------------------- /src/img/home/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/home/share.png -------------------------------------------------------------------------------- /src/img/invitation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/invitation.jpg -------------------------------------------------------------------------------- /src/img/load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/load.png -------------------------------------------------------------------------------- /src/img/load@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/load@2x.png -------------------------------------------------------------------------------- /src/img/qrcode_8cm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/qrcode_8cm.png -------------------------------------------------------------------------------- /src/img/share_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/share_arrow.png -------------------------------------------------------------------------------- /src/img/touch-icon-iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/img/touch-icon-iphone.png -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | <?=$siteData['title']?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/invitation.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | <?=$siteData['title']?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
敬邀:
30 |
31 |

2014年11月29日 星期六

32 |

时间:17:30

33 |

地点:广西柳州市跃进路40号

34 |

京都宾馆三楼

35 |
36 |
37 |

点击或扫一扫

38 |

幸福微信号:fensalir

39 | 40 |

关注我们的公众号
随时获取酒店导航,还有更多精彩互动。

41 |
42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/js/inline-home.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var app = { 3 | dom: {}, 4 | files: [ 5 | {id: 'bg', src: 'img/home/bg.png'}, 6 | {id: 'logo', src: 'img/home/banner_logo.png'}, 7 | {id: 'date', src: 'img/home/banner_date.png'}, 8 | {id: 'name', src: 'img/home/banner_name.png'}, 9 | {id: 'frame', src: 'img/home/photo_frame.png'} 10 | ], 11 | album: { 12 | path: 'http://wedding.ciaoca.com/album', 13 | list: { 14 | 'wedding': 4, 15 | 'happiness': 9, 16 | 'happy': 5, 17 | 'hangzhou': 3, 18 | 'expo': 9, 19 | 'graduate': 9, 20 | 'express': 5 21 | } 22 | }, 23 | photos: {}, 24 | magnificPhotos: {}, 25 | shareData: { 26 | link: location.href, 27 | title: 'Wu & Xiong\'s Wedding', 28 | intro: '七年我们终于修成正果,愿把我们点点滴滴的爱与你们分享,邀您一起见证。', 29 | cover: 'http://wedding.ciaoca.com/img/touch-icon-iphone.png' 30 | } 31 | }; 32 | 33 | app.init = function(){ 34 | var _this = this; 35 | 36 | _this.dom.win = $(window); 37 | _this.dom.main = $('
'); 38 | _this.dom.loadLock = $('
'); 39 | _this.dom.loadWifi = $('
'); 40 | _this.dom.loadQrcode = $('
'); 41 | _this.dom.loadBox = $('
').appendTo(_this.dom.loadLock); 42 | _this.dom.loadPer = $('').appendTo(_this.dom.loadBox); 43 | _this.dom.loadLock.css('height', _this.dom.win.height()).appendTo('body'); 44 | _this.dom.shareTip = $('
'); 45 | 46 | // PC or 移动端检测,只允许在移动端访问 47 | if (!navigator.userAgent.match(/(iPhone|iPod|Android|ios|windows mobile|Windows Phone|ucweb|rv:1.2.3.4|midp|SymbianOS)/i)) { 48 | _this.dom.loadBox.remove(); 49 | _this.dom.loadQrcode.appendTo(_this.dom.loadLock); 50 | return; 51 | }; 52 | 53 | // 是否在微信中运行 54 | _this.inWechat = false; 55 | 56 | /* 57 | * 加载状态: 58 | * 0: 未加载 59 | * 1: 加载完成 60 | * 2: 加载中 61 | * 3: 异常(如:处于非 WIFI 状态下) 62 | */ 63 | _this.loadStatus = 0; 64 | 65 | _this.loadNow = 0; 66 | _this.loadProgress = 0; 67 | 68 | _this.loadLoop = setInterval(function(){ 69 | if (_this.loadNow < _this.loadProgress) { 70 | _this.loadNow++; 71 | _this.dom.loadBox.attr('title', _this.loadNow + '%'); 72 | _this.dom.loadPer.css('height', _this.loadNow + '%'); 73 | }; 74 | }, 100); 75 | 76 | _this.dom.loadWifi.on('tap', function(){ 77 | _this.dom.loadWifi.remove(); 78 | _this.loadStatus = 0; 79 | _this.loadFile(); 80 | }); 81 | 82 | WeixinApi.ready(function(Api){ 83 | _this.inWechat = true; 84 | 85 | // 微信分享 86 | var _shareData = { 87 | link: _this.shareData.link, 88 | title: _this.shareData.title, 89 | desc: _this.shareData.intro, 90 | imgUrl: _this.shareData.cover 91 | }; 92 | 93 | Api.shareToFriend(_shareData); 94 | Api.shareToTimeline(_shareData); 95 | 96 | // 非 WIFI 网络环境下访问时进行提示 97 | Api.getNetworkType(function(status){ 98 | if (status === 'network_type:wifi') { 99 | _this.loadFile(); 100 | } else { 101 | if (_this.loadStatus === 0) { 102 | _this.loadStatus = 3; 103 | _this.dom.loadWifi.appendTo(_this.dom.loadLock); 104 | }; 105 | }; 106 | }); 107 | }); 108 | 109 | setTimeout(function(){ 110 | _this.loadFile(); 111 | }, 2000); 112 | }; 113 | 114 | app.loadFile = function(){ 115 | var _this = this; 116 | 117 | if (_this.loadStatus !== 0) {return}; 118 | _this.loadStatus = 2; 119 | 120 | _this.loader = new createjs.LoadQueue(false); 121 | _this.loaderError = []; 122 | 123 | _this.loader.on('complete', function(){ 124 | /* 125 | if (_this.loaderError.length) { 126 | console.log(_this.loaderError.join('\r\n')); 127 | }; 128 | */ 129 | 130 | _this.loadStatus = 1; 131 | _this.build(); 132 | }); 133 | 134 | _this.loader.on('progress', function(o){ 135 | _this.loadProgress = Math.round(_this.loader.progress * 100); 136 | }); 137 | 138 | _this.loader.on('error', function(o){ 139 | if (o.item && o.item.src) { 140 | _this.loaderError.push('Error: File "' + o.item.src + '" failed to load.'); 141 | }; 142 | }); 143 | 144 | if (_this.files.length) { 145 | _this.loader.loadManifest(_this.files); 146 | } else { 147 | _this.loadStatus = 1; 148 | _this.build(); 149 | }; 150 | }; 151 | 152 | app.build = function(){ 153 | var _this = this; 154 | var _template = '
' 155 | + '
' 156 | + '' 157 | + '
' 158 | + '
' 159 | + '
' 160 | + '
' 161 | + '

今年

我們結婚吧

' 162 | + '

小小的幸福

每年都一起去旅游;
每年生日都会准备礼物;
每天都希望看到妳的笑脸。

' 163 | + '

快楽的時光

今天没吃药,感觉萌萌哒。

' 164 | + '

2011年

为了梦想,我离开了家乡,
来到了杭州。
妳,也跟随着我,
一起来到这个美丽的城市。
谢谢妳,一路陪我走过。

' 165 | + '

2010年

第一次长途旅游,
第一次乘坐飞机,
去参观世博会,
现在回想起来,
那时候有点傻天真,
但是和妳在一起很开心。

' 166 | + '

2008年7月

我毕业了,
妳送我出了校园,
感觉到你有些不安。
但我相信,来年的今天,
我会回来迎接妳毕业。

' 167 | + '

2008年1月

那天,山上下了雪,
我约着妳去山上看雪,
我们玩得非常开心。
晚上,我向妳表白,
妳接受了。
妳不知道那晚我有多兴奋,
我想,
也许妳和我一样兴奋。

' 168 | + '' 169 | + '' 170 | + '' 171 | + '
' 172 | + '
'; 173 | 174 | clearInterval(_this.loadLoop); 175 | 176 | _this.dom.loadBox.addClass('end'); 177 | _this.dom.main.html(_template).appendTo('body'); 178 | 179 | // 预留 1s 的动画时间 180 | setTimeout(function(){ 181 | _this.dom.loadLock.remove(); 182 | _this.showTime(); 183 | }, 1000); 184 | }; 185 | 186 | app.showTime = function(){ 187 | var _this = this; 188 | 189 | _this.dom.main.addClass('coming'); 190 | 191 | // 滚动到底部,向上浏览 192 | // _this.dom.win.scrollTop(document.body.clientHeight); 193 | 194 | // 统计照片 195 | $.each(_this.album.list, function(i, v){ 196 | _this.photos[i] = []; 197 | _this.magnificPhotos[i] = []; 198 | for (var j = 1; j <= v; j++) { 199 | _this.photos[i].push(_this.album.path + '/' + i +'/' + j + '.jpg'); 200 | _this.magnificPhotos[i].push({src: _this.album.path + '/' + i +'/' + j + '.jpg'}); 201 | }; 202 | }); 203 | 204 | if (_this.inWechat) { 205 | // 微信图片浏览器 206 | WeixinApi.ready(function(Api){ 207 | _this.dom.main.on('tap', 'a', function(){ 208 | var _rel = this.rel; 209 | var _rev = this.rev; 210 | 211 | if (_rel === 'album' && _rev && _rev.length && Array.isArray(_this.photos[_rev])) { 212 | Api.imagePreview(_this.photos[_rev][0], _this.photos[_rev]); 213 | 214 | } else if (_rel === 'share' && _rev === 'wechat') { 215 | _this.dom.shareTip.appendTo('body'); 216 | }; 217 | }); 218 | }); 219 | 220 | } else { 221 | $('', {rel: 'stylesheet', href: 'css/magnific-popup.css'}).appendTo('body'); 222 | $(' 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/public/fun.php: -------------------------------------------------------------------------------- 1 | 5 && strtolower(substr($url, 0, 5)) === 'https') { 14 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 15 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 16 | }; 17 | 18 | // 超时设置 19 | if($connect){ 20 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect); 21 | }; 22 | if($timeout){ 23 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 24 | }; 25 | 26 | if (is_array($postFields) && count($postFields) > 0) { 27 | $postBodyString = ''; 28 | $postMultipart = false; 29 | 30 | foreach ($postFields as $k => $v) { 31 | // 判断是不是文件上传 32 | if ('@' != substr($v, 0, 1)) { 33 | $postBodyString .= ($k. '=' .urlencode($v) . '&'); 34 | 35 | // 文件上传用multipart/form-data,否则用www-form-urlencoded 36 | } else { 37 | $postMultipart=true; 38 | } 39 | } 40 | unset($k, $v); 41 | 42 | curl_setopt($ch, CURLOPT_POST, true); 43 | 44 | if ($postMultipart) { 45 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); 46 | } else { 47 | curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1)); 48 | }; 49 | }; 50 | 51 | $reponse = curl_exec($ch); 52 | 53 | if (curl_errno($ch)) { 54 | return 'curl_error:'.curl_errno($ch); 55 | //throw new Exception(curl_error($ch), 0); 56 | 57 | } else { 58 | $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 59 | 60 | if ($httpStatusCode !== 200) { 61 | return 'curl_error:' . $httpStatusCode; 62 | //throw new Exception($reponse, $httpStatusCode); 63 | }; 64 | }; 65 | curl_close($ch); 66 | return $reponse; 67 | }; 68 | ?> -------------------------------------------------------------------------------- /src/public/invitees.php: -------------------------------------------------------------------------------- 1 | array('Title'[, 'Key1' , 'Key2', ...]) 5 | */ 6 | $WEDDING_INVITEES = array( 7 | /* Demo */ 8 | 'zs' => array('张三', '小张'), 9 | 'lisi' => array('李四', '小李') 10 | ); 11 | 12 | /* 测试重复项 13 | $log = '

测试重复项:

'; 14 | $pinYin = array_keys($WEDDING_INVITEES); 15 | $log .= '

原始项共计:' . count($pinYin) . '条

'; 16 | 17 | array_unique($pinYin); 18 | $log .= '

过滤后剩余:' . count($pinYin) . '条

'; 19 | 20 | echo $log; 21 | */ 22 | ?> -------------------------------------------------------------------------------- /src/sign.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | <?=$siteData['title']?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 |

现场签到

22 |

该功能即将上线,尽请期待

23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/wechat-img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/wechat-img/.gitkeep -------------------------------------------------------------------------------- /src/wechat-log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/wechat-log/.gitkeep -------------------------------------------------------------------------------- /src/wechat-photo/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaoca/wedding/7a4fd5b51ce639908f290839506476f3569faebf/src/wechat-photo/.gitkeep -------------------------------------------------------------------------------- /src/wechat-php-sdk/errCode.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/binsee/wechat-php-sdk 6 | * @version 1.0 7 | * usage: 8 | * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 9 | * if ($ret) 10 | * echo $ret; 11 | * else 12 | * echo "未找到对应的内容"; 13 | */ 14 | class ErrCode 15 | { 16 | public static $errCode=array( 17 | '-1'=>'系统繁忙', 18 | '0'=>'请求成功', 19 | '40001'=>'获取access_token时AppSecret错误,或者access_token无效', 20 | '40002'=>'不合法的凭证类型', 21 | '40003'=>'不合法的OpenID', 22 | '40004'=>'不合法的媒体文件类型', 23 | '40005'=>'不合法的文件类型', 24 | '40006'=>'不合法的文件大小', 25 | '40007'=>'不合法的媒体文件id', 26 | '40008'=>'不合法的消息类型', 27 | '40009'=>'不合法的图片文件大小', 28 | '40010'=>'不合法的语音文件大小', 29 | '40011'=>'不合法的视频文件大小', 30 | '40012'=>'不合法的缩略图文件大小', 31 | '40013'=>'不合法的APPID', 32 | '40014'=>'不合法的access_token', 33 | '40015'=>'不合法的菜单类型', 34 | '40016'=>'不合法的按钮个数', 35 | '40017'=>'不合法的按钮类型', 36 | '40018'=>'不合法的按钮名字长度', 37 | '40019'=>'不合法的按钮KEY长度', 38 | '40020'=>'不合法的按钮URL长度', 39 | '40021'=>'不合法的菜单版本号', 40 | '40022'=>'不合法的子菜单级数', 41 | '40023'=>'不合法的子菜单按钮个数', 42 | '40024'=>'不合法的子菜单按钮类型', 43 | '40025'=>'不合法的子菜单按钮名字长度', 44 | '40026'=>'不合法的子菜单按钮KEY长度', 45 | '40027'=>'不合法的子菜单按钮URL长度', 46 | '40028'=>'不合法的自定义菜单使用用户', 47 | '40029'=>'不合法的oauth_code', 48 | '40030'=>'不合法的refresh_token', 49 | '40031'=>'不合法的openid列表', 50 | '40032'=>'不合法的openid列表长度', 51 | '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', 52 | '40035'=>'不合法的参数', 53 | '40038'=>'不合法的请求格式', 54 | '40039'=>'不合法的URL长度', 55 | '40050'=>'不合法的分组id', 56 | '40051'=>'分组名字不合法', 57 | '41001'=>'缺少access_token参数', 58 | '41002'=>'缺少appid参数', 59 | '41003'=>'缺少refresh_token参数', 60 | '41004'=>'缺少secret参数', 61 | '41005'=>'缺少多媒体文件数据', 62 | '41006'=>'缺少media_id参数', 63 | '41007'=>'缺少子菜单数据', 64 | '41008'=>'缺少oauth code', 65 | '41009'=>'缺少openid', 66 | '42001'=>'access_token超时', 67 | '42002'=>'refresh_token超时', 68 | '42003'=>'oauth_code超时', 69 | '43001'=>'需要GET请求', 70 | '43002'=>'需要POST请求', 71 | '43003'=>'需要HTTPS请求', 72 | '43004'=>'需要接收者关注', 73 | '43005'=>'需要好友关系', 74 | '44001'=>'多媒体文件为空', 75 | '44002'=>'POST的数据包为空', 76 | '44003'=>'图文消息内容为空', 77 | '44004'=>'文本消息内容为空', 78 | '45001'=>'多媒体文件大小超过限制', 79 | '45002'=>'消息内容超过限制', 80 | '45003'=>'标题字段超过限制', 81 | '45004'=>'描述字段超过限制', 82 | '45005'=>'链接字段超过限制', 83 | '45006'=>'图片链接字段超过限制', 84 | '45007'=>'语音播放时间超过限制', 85 | '45008'=>'图文消息超过限制', 86 | '45009'=>'接口调用超过限制', 87 | '45010'=>'创建菜单个数超过限制', 88 | '45015'=>'回复时间超过限制', 89 | '45016'=>'系统分组,不允许修改', 90 | '45017'=>'分组名字过长', 91 | '45018'=>'分组数量超过上限', 92 | '45024'=>'账号数量超过上限', 93 | '46001'=>'不存在媒体数据', 94 | '46002'=>'不存在的菜单版本', 95 | '46003'=>'不存在的菜单数据', 96 | '46004'=>'不存在的用户', 97 | '47001'=>'解析JSON/XML内容错误', 98 | '48001'=>'api功能未授权', 99 | '50001'=>'用户未授权该api', 100 | '7000000'=>'请求正常,无语义结果', 101 | '7000001'=>'缺失请求参数', 102 | '7000002'=>'signature 参数无效', 103 | '7000003'=>'地理位置相关配置 1 无效', 104 | '7000004'=>'地理位置相关配置 2 无效', 105 | '7000005'=>'请求地理位置信息失败', 106 | '7000006'=>'地理位置结果解析失败', 107 | '7000007'=>'内部初始化失败', 108 | '7000008'=>'非法 appid(获取密钥失败)', 109 | '7000009'=>'请求语义服务失败', 110 | '7000010'=>'非法 post 请求', 111 | '7000011'=>'post 请求 json 字段无效', 112 | '7000030'=>'查询 query 太短', 113 | '7000031'=>'查询 query 太长', 114 | '7000032'=>'城市、经纬度信息缺失', 115 | '7000033'=>'query 请求语义处理失败', 116 | '7000034'=>'获取天气信息失败', 117 | '7000035'=>'获取股票信息失败', 118 | '7000036'=>'utf8 编码转换失败', 119 | ); 120 | 121 | public static function getErrText($err) { 122 | if (isset(self::$errCode[$err])) { 123 | return self::$errCode[$err]; 124 | }else { 125 | return false; 126 | }; 127 | } 128 | } 129 | 130 | ?> -------------------------------------------------------------------------------- /src/wechat-php-sdk/qyerrCode.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/binsee/wechat-php-sdk 6 | * @version 1.0 7 | * usage: 8 | * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 9 | * if ($ret) 10 | * echo $ret; 11 | * else 12 | * echo "未找到对应的内容"; 13 | */ 14 | class ErrCode 15 | { 16 | public static $errCode=array( 17 | '-1'=>'系统繁忙', 18 | '0'=>'请求成功', 19 | '40001'=>'获取access_token时AppSecret错误,或者access_token无效', 20 | '40002'=>'不合法的凭证类型', 21 | '40003'=>'不合法的UserID', 22 | '40004'=>'不合法的媒体文件类型', 23 | '40005'=>'不合法的文件类型', 24 | '40006'=>'不合法的文件大小', 25 | '40007'=>'不合法的媒体文件id', 26 | '40008'=>'不合法的消息类型', 27 | '40013'=>'不合法的corpid', 28 | '40014'=>'不合法的access_token', 29 | '40015'=>'不合法的菜单类型', 30 | '40016'=>'不合法的按钮个数', 31 | '40017'=>'不合法的按钮类型', 32 | '40018'=>'不合法的按钮名字长度', 33 | '40019'=>'不合法的按钮KEY长度', 34 | '40020'=>'不合法的按钮URL长度', 35 | '40021'=>'不合法的菜单版本号', 36 | '40022'=>'不合法的子菜单级数', 37 | '40023'=>'不合法的子菜单按钮个数', 38 | '40024'=>'不合法的子菜单按钮类型', 39 | '40025'=>'不合法的子菜单按钮名字长度', 40 | '40026'=>'不合法的子菜单按钮KEY长度', 41 | '40027'=>'不合法的子菜单按钮URL长度', 42 | '40028'=>'不合法的自定义菜单使用员工', 43 | '40029'=>'不合法的oauth_code', 44 | '40031'=>'不合法的UserID列表', 45 | '40032'=>'不合法的UserID列表长度', 46 | '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', 47 | '40035'=>'不合法的参数', 48 | '40038'=>'不合法的请求格式', 49 | '40039'=>'不合法的URL长度', 50 | '40040'=>'不合法的插件token', 51 | '40041'=>'不合法的插件id', 52 | '40042'=>'不合法的插件会话', 53 | '40048'=>'url中包含不合法domain', 54 | '40054'=>'不合法的子菜单url域名', 55 | '40055'=>'不合法的按钮url域名', 56 | '40056'=>'不合法的agentid', 57 | '40057'=>'不合法的callbackurl', 58 | '40058'=>'不合法的红包参数', 59 | '40059'=>'不合法的上报地理位置标志位', 60 | '40060'=>'设置上报地理位置标志位时没有设置callbackurl', 61 | '40061'=>'设置应用头像失败', 62 | '40062'=>'不合法的应用模式', 63 | '40063'=>'红包参数为空', 64 | '40064'=>'管理组名字已存在', 65 | '40065'=>'不合法的管理组名字长度', 66 | '40066'=>'不合法的部门列表', 67 | '40067'=>'标题长度不合法', 68 | '40068'=>'不合法的标签ID', 69 | '40069'=>'不合法的标签ID列表', 70 | '40070'=>'列表中所有标签(用户)ID都不合法', 71 | '40071'=>'不合法的标签名字,标签名字已经存在', 72 | '40072'=>'不合法的标签名字长度', 73 | '40073'=>'不合法的openid', 74 | '40074'=>'news消息不支持指定为高保密消息', 75 | '41001'=>'缺少access_token参数', 76 | '41002'=>'缺少corpid参数', 77 | '41003'=>'缺少refresh_token参数', 78 | '41004'=>'缺少secret参数', 79 | '41005'=>'缺少多媒体文件数据', 80 | '41006'=>'缺少media_id参数', 81 | '41007'=>'缺少子菜单数据', 82 | '41008'=>'缺少oauth code', 83 | '41009'=>'缺少UserID', 84 | '41010'=>'缺少url', 85 | '41011'=>'缺少agentid', 86 | '41012'=>'缺少应用头像mediaid', 87 | '41013'=>'缺少应用名字', 88 | '41014'=>'缺少应用描述', 89 | '41015'=>'缺少Content', 90 | '41016'=>'缺少标题', 91 | '41017'=>'缺少标签ID', 92 | '41018'=>'缺少标签名字', 93 | '42001'=>'access_token超时', 94 | '42002'=>'refresh_token超时', 95 | '42003'=>'oauth_code超时', 96 | '42004'=>'插件token超时', 97 | '43001'=>'需要GET请求', 98 | '43002'=>'需要POST请求', 99 | '43003'=>'需要HTTPS', 100 | '43004'=>'需要接收者关注', 101 | '43005'=>'需要好友关系', 102 | '43006'=>'需要订阅', 103 | '43007'=>'需要授权', 104 | '43008'=>'需要支付授权', 105 | '43009'=>'需要员工已关注', 106 | '43010'=>'需要处于企业模式', 107 | '43011'=>'需要企业授权', 108 | '44001'=>'多媒体文件为空', 109 | '44002'=>'POST的数据包为空', 110 | '44003'=>'图文消息内容为空', 111 | '44004'=>'文本消息内容为空', 112 | '45001'=>'多媒体文件大小超过限制', 113 | '45002'=>'消息内容超过限制', 114 | '45003'=>'标题字段超过限制', 115 | '45004'=>'描述字段超过限制', 116 | '45005'=>'链接字段超过限制', 117 | '45006'=>'图片链接字段超过限制', 118 | '45007'=>'语音播放时间超过限制', 119 | '45008'=>'图文消息超过限制', 120 | '45009'=>'接口调用超过限制', 121 | '45010'=>'创建菜单个数超过限制', 122 | '45015'=>'回复时间超过限制', 123 | '45016'=>'系统分组,不允许修改', 124 | '45017'=>'分组名字过长', 125 | '45018'=>'分组数量超过上限', 126 | '46001'=>'不存在媒体数据', 127 | '46002'=>'不存在的菜单版本', 128 | '46003'=>'不存在的菜单数据', 129 | '46004'=>'不存在的员工', 130 | '47001'=>'解析JSON/XML内容错误', 131 | '48002'=>'Api禁用', 132 | '50001'=>'redirect_uri未授权', 133 | '50002'=>'员工不在权限范围', 134 | '50003'=>'应用已停用', 135 | '50004'=>'员工状态不正确(未关注状态)', 136 | '50005'=>'企业已禁用', 137 | '60001'=>'部门长度不符合限制', 138 | '60002'=>'部门层级深度超过限制', 139 | '60003'=>'部门不存在', 140 | '60004'=>'父亲部门不存在', 141 | '60005'=>'不允许删除有成员的部门', 142 | '60006'=>'不允许删除有子部门的部门', 143 | '60007'=>'不允许删除根部门', 144 | '60008'=>'部门名称已存在', 145 | '60009'=>'部门名称含有非法字符', 146 | '60010'=>'部门存在循环关系', 147 | '60011'=>'管理员权限不足,(user/department/agent)无权限', 148 | '60012'=>'不允许删除默认应用', 149 | '60013'=>'不允许关闭应用', 150 | '60014'=>'不允许开启应用', 151 | '60015'=>'不允许修改默认应用可见范围', 152 | '60016'=>'不允许删除存在成员的标签', 153 | '60017'=>'不允许设置企业', 154 | '60102'=>'UserID已存在', 155 | '60103'=>'手机号码不合法', 156 | '60104'=>'手机号码已存在', 157 | '60105'=>'邮箱不合法', 158 | '60106'=>'邮箱已存在', 159 | '60107'=>'微信号不合法', 160 | '60108'=>'微信号已存在', 161 | '60109'=>'QQ号已存在', 162 | '60110'=>'部门个数超出限制', 163 | '60111'=>'UserID不存在', 164 | '60112'=>'成员姓名不合法', 165 | '60113'=>'身份认证信息(微信号/手机/邮箱)不能同时为空', 166 | '60114'=>'性别不合法', 167 | 168 | ); 169 | 170 | public static function getErrText($err) { 171 | if (isset(self::$errCode[$err])) { 172 | return self::$errCode[$err]; 173 | }else { 174 | return false; 175 | }; 176 | } 177 | } 178 | 179 | ?> -------------------------------------------------------------------------------- /src/wechat-php-sdk/wechat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 微信网页端调用JS 3 | * @author dodge 4 | * @contact dodgepudding@gmail.com 5 | * @link http://blog.4wer.com/wechat-timeline-share 6 | * @version 1.1 7 | * 8 | * 自定义分享使用: 9 | * WeixinJS.hideOptionMenu() 隐藏右上角按钮 10 | * WeixinJS.showOptionMenu() 显示右上角按钮 11 | * WeixinJS.hideToolbar() 隐藏工具栏 12 | * WeixinJS.showToolbar() 显示工具栏 13 | * WeixinJS.getNetworkType() 获取网络状态 14 | * WeixinJS.closeWindow() 关闭窗口 15 | * WeixinJS.scanQRCode() 扫描二维码 16 | * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 17 | * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 18 | * WeixinJS.sendEmail(title,content) 发送邮件 19 | * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 20 | * WeixinJS.addContact(username) 添加微信账号 21 | * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 22 | * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 23 | * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 24 | * 自定义分享内容数据格式: 25 | * var dataForWeixin={ 26 | appId:"", 27 | MsgImg:"消息图片路径", 28 | TLImg:"时间线图路径", 29 | url:"分享url路径", 30 | title:"标题", 31 | desc:"描述", 32 | fakeid:"", 33 | prepare:function(argv){ 34 | if (typeof argv.shareTo!='undefined') 35 | switch(argv.shareTo) { 36 | case 'friend': 37 | //发送给朋友 38 | alert(argv.scene); //friend 39 | break; 40 | case 'timeline': 41 | //发送给朋友 42 | break; 43 | case 'weibo': 44 | //发送到微博 45 | alert(argv.url); 46 | break; 47 | case 'favorite': 48 | //收藏 49 | alert(argv.scene);//favorite 50 | break; 51 | case 'connector': 52 | //分享到第三方应用 53 | alert(argv.scene);//connector 54 | break; 55 | default: 56 | } 57 | }, 58 | callback:function(res){ 59 | //发送给好友或应用 60 | if (res.err_msg=='send_app_msg:confirm') { 61 | //todo:func1(); 62 | alert(res.err_desc); 63 | } 64 | if (res.err_msg=='send_app_msg:cancel') { 65 | //todo:func2(); 66 | alert(res.err_desc); 67 | } 68 | //分享到朋友圈 69 | if (res.err_msg=='share_timeline:confirm') { 70 | //todo:func1(); 71 | alert(res.err_desc); 72 | } 73 | if (res.err_msg=='share_timeline:cancel') { 74 | //todo:func1(); 75 | alert(res.err_desc); 76 | } 77 | //分享到微博 78 | if (res.err_msg=='share_weibo:confirm') { 79 | //todo:func1(); 80 | alert(res.err_desc); 81 | } 82 | if (res.err_msg=='share_weibo:cancel') { 83 | //todo:func1(); 84 | alert(res.err_desc); 85 | } 86 | //收藏或分享到应用 87 | if (res.err_msg=='send_app_msg:ok') { 88 | //todo:func1(); 89 | alert(res.err_desc); 90 | } 91 | } 92 | }; 93 | */ 94 | 95 | WeixinJS = typeof WeixinJS!='undefined' || {}; 96 | //隐藏右上角按钮 97 | WeixinJS.hideOptionMenu = function() { 98 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 99 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideOptionMenu'); 100 | }); 101 | }; 102 | //显示右上角按钮 103 | WeixinJS.showOptionMenu = function() { 104 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 105 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showOptionMenu'); 106 | }); 107 | }; 108 | //隐藏底部导航栏 109 | WeixinJS.hideToolbar = function() { 110 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 111 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideToolbar'); 112 | }); 113 | }; 114 | //显示底部导航栏 115 | WeixinJS.showToolbar = function() { 116 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 117 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showToolbar'); 118 | }); 119 | }; 120 | //网页获取用户网络状态 121 | netType={"network_type:wifi":"wifi网络","network_type:edge":"非wifi,包含3G/2G","network_type:fail":"网络断开连接","network_type:wwan":"2g或者3g"}; 122 | WeixinJS.getNetworkType = function(callback) { 123 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 124 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getNetworkType',{}, 125 | function(res){ 126 | //result: network_type:wifi,network_type:edge,network_type:fail,network_type:wwan 127 | //netType[e.err_msg] 128 | callback(res.err_msg); 129 | }); 130 | }); 131 | }; 132 | //关闭窗口 133 | WeixinJS.closeWindow = function() { 134 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("closeWindow", {}); 135 | }; 136 | //扫描二维码 137 | WeixinJS.scanQRCode = function() { 138 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("scanQRCode", {}); 139 | }; 140 | //使用浏览器打开网址 141 | WeixinJS.openUrlByExtBrowser=function(url){ 142 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openUrlByExtBrowser",{"url" : url}); 143 | }; 144 | //跳转到指定公众账号页面 145 | WeixinJS.jumpToBizProfile=function(username){ 146 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("jumpToBizProfile",{"tousername" : username}); 147 | }; 148 | //发送邮件 149 | WeixinJS.sendEmail=function(title,content){ 150 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("sendEmail",{ 151 | "title" : title, 152 | "content" : content 153 | }); 154 | }; 155 | //查看地图 156 | WeixinJS.openProductView=function(latitude,longitude,name,address,scale,infoUrl){ 157 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openProductView",{ 158 | "latitude" : latitude, //纬度 159 | "longitude" : longitude, //经度 160 | "name" : name, //名称 161 | "address" : address, //地址 162 | "scale" : scale, //地图缩放级别 163 | "infoUrl" : infoUrl, //查看位置界面底部的超链接 164 | }); 165 | }; 166 | //添加微信账号 167 | WeixinJS.addContact=function weixinAddContact(username){ 168 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("addContact", { 169 | "webtype": "1", 170 | "username": username 171 | }, function(e) { 172 | WeixinJSBridge.log(e.err_msg); 173 | //e.err_msg:add_contact:added 已经添加 174 | //e.err_msg:add_contact:cancel 取消添加 175 | //e.err_msg:add_contact:ok 添加成功 176 | if(e.err_msg == 'add_contact:added' || e.err_msg == 'add_contact:ok'){ 177 | //关注成功,或者已经关注过 178 | } 179 | }); 180 | }; 181 | 182 | /** 183 | * 调出微信内图片预览scrollview 184 | * @param array urls 图片url数组 185 | * @param string current 当前图片url 186 | */ 187 | WeixinJS.imagePreview = function(urls,current) { 188 | if (typeof WeixinJSBridge!='undefined') 189 | WeixinJSBridge.invoke("imagePreview", { 190 | current: current, 191 | urls: urls 192 | }); 193 | }; 194 | 195 | //微信JsApi支付接口 196 | WeixinJS.payCallback = function(appId,package,timeStamp,nonceStr,signType,paySign,callback){ 197 | if (typeof WeixinJSBridge!='undefined') 198 | WeixinJSBridge.invoke('getBrandWCPayRequest',{ 199 | "appId" : appId.toString(), 200 | "timeStamp" : timeStamp.toString(), 201 | "nonceStr" : nonceStr.toString(), 202 | "package" : package.toString(), 203 | "signType" : signType.toString(), 204 | "paySign" : paySign.toString() 205 | 206 | },function(res){ 207 | // res.err_msg == "get_brand_wcpay_request:ok" return true; 208 | // res.err_msg == "get_brand_wcpay_request:cancel" return false; 209 | callback(res); 210 | }); 211 | }; 212 | //编辑收货地址 213 | WeixinJS.editAddress = function(appId,addrSign,timeStamp,nonceStr,callback){ 214 | var postdata = { 215 | "appId" : appId.toString(), 216 | "scope" : "jsapi_address", 217 | "signType" : "sha1", 218 | "addrSign" : addrSign.toString(), 219 | "timeStamp" : timeStamp.toString(), 220 | "nonceStr" : nonceStr.toString() 221 | }; 222 | if (typeof WeixinJSBridge!='undefined') 223 | WeixinJSBridge.invoke('editAddress',postdata, function(res){ 224 | //return res.proviceFirstStageName,res.addressCitySecondStageName,res.addressCountiesThirdStageName,res.addressDetailInfo,res.userName,res.addressPostalCode,res.telNumber 225 | //error return res.err_msg 226 | callback(res); 227 | }); 228 | }; 229 | 230 | (function(){ 231 | if (typeof dataForWeixin=="undefined") return; 232 | var onBridgeReady=function(){ 233 | WeixinJSBridge.on('menu:share:appmessage', function(argv){ 234 | (dataForWeixin.prepare)(argv); 235 | WeixinJSBridge.invoke('sendAppMessage',{ 236 | "appid":dataForWeixin.appId, 237 | "img_url":dataForWeixin.MsgImg, 238 | "img_width":"120", 239 | "img_height":"120", 240 | "link":dataForWeixin.url, 241 | "desc":dataForWeixin.desc, 242 | "title":dataForWeixin.title 243 | }, function(res){(dataForWeixin.callback)(res);}); 244 | }); 245 | WeixinJSBridge.on('menu:share:timeline', function(argv){ 246 | (dataForWeixin.prepare)(argv); 247 | WeixinJSBridge.invoke('shareTimeline',{ 248 | "img_url":dataForWeixin.TLImg, 249 | "img_width":"120", 250 | "img_height":"120", 251 | "link":dataForWeixin.url, 252 | "desc":dataForWeixin.desc, 253 | "title":dataForWeixin.title 254 | }, function(res){(dataForWeixin.callback)(res);}); 255 | }); 256 | WeixinJSBridge.on('menu:share:weibo', function(argv){ 257 | (dataForWeixin.prepare)(argv); 258 | WeixinJSBridge.invoke('shareWeibo',{ 259 | "content":dataForWeixin.title, 260 | "url":dataForWeixin.url 261 | }, function(res){(dataForWeixin.callback)(res);}); 262 | }); 263 | WeixinJSBridge.on('menu:share:facebook', function(argv){ 264 | (dataForWeixin.prepare)(argv); 265 | WeixinJSBridge.invoke('shareFB',{ 266 | "img_url":dataForWeixin.TLImg, 267 | "img_width":"120", 268 | "img_height":"120", 269 | "link":dataForWeixin.url, 270 | "desc":dataForWeixin.desc, 271 | "title":dataForWeixin.title 272 | }, function(res){(dataForWeixin.callback)(res);}); 273 | }); 274 | }; 275 | if(document.addEventListener){ 276 | document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); 277 | }else if(document.attachEvent){ 278 | document.attachEvent('WeixinJSBridgeReady' , onBridgeReady); 279 | document.attachEvent('onWeixinJSBridgeReady' , onBridgeReady); 280 | } 281 | })(); -------------------------------------------------------------------------------- /src/wechat-php-sdk/wechatauth.class.php: -------------------------------------------------------------------------------- 1 | 13 | * @link https://github.com/dodgepudding/wechat-php-sdk 14 | * @version 1.1 15 | * 16 | */ 17 | include "snoopy.class.php"; 18 | class Wechatauth 19 | { 20 | private $cookie; 21 | private $skey; 22 | private $_cookiename; 23 | private $_cookieexpired = 3600; 24 | private $_account = 'test'; 25 | private $_datapath = './data/cookie_'; 26 | private $debug; 27 | private $_logcallback; 28 | public $login_user; //当前登陆用户, 调用get_login_info后获取 29 | 30 | public function __construct($options) 31 | { 32 | $this->_account = isset($options['account'])?$options['account']:''; 33 | $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; 34 | $this->debug = isset($options['debug'])?$options['debug']:false; 35 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 36 | $this->_cookiename = $this->_datapath.$this->_account; 37 | $this->getCookie($this->_cookiename); 38 | } 39 | /** 40 | * 把cookie写入缓存 41 | * @param string $filename 缓存文件名 42 | * @param string $content 文件内容 43 | * @return bool 44 | */ 45 | public function saveCookie($filename,$content){ 46 | return file_put_contents($filename,$content); 47 | } 48 | 49 | /** 50 | * 读取cookie缓存内容 51 | * @param string $filename 缓存文件名 52 | * @return string cookie 53 | */ 54 | public function getCookie($filename){ 55 | if (file_exists($filename)) { 56 | $mtime = filemtime($filename); 57 | if ($mtime_cookieexpired) return false; 58 | $data = file_get_contents($filename); 59 | if ($data) $this->cookie = $data; 60 | } 61 | return $this->cookie; 62 | } 63 | 64 | /* 65 | * 删除cookie 66 | */ 67 | public function deleteCookie($filename) { 68 | $this->cookie = ''; 69 | @unlink($filename); 70 | return true; 71 | } 72 | 73 | private function log($log){ 74 | if ($this->debug && function_exists($this->_logcallback)) { 75 | if (is_array($log)) $log = print_r($log,true); 76 | return call_user_func($this->_logcallback,$log); 77 | } 78 | } 79 | 80 | /** 81 | * 获取登陆二维码对应的授权码 82 | */ 83 | public function get_login_code(){ 84 | if ($this->_logincode) return $this->_logincode; 85 | $t = time().strval(mt_rand(100,999)); 86 | $codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t; 87 | $send_snoopy = new Snoopy; 88 | $send_snoopy->fetch($codeurl); 89 | $result = $send_snoopy->results; 90 | if ($result) { 91 | preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches); 92 | if(count($matches)>1) { 93 | $this->_logincode = $matches[1]; 94 | $_SESSION['login_step'] = 0; 95 | return $this->_logincode; 96 | } 97 | } 98 | return $result; 99 | } 100 | 101 | /** 102 | * 通过授权码获取对应的二维码图片地址 103 | * @param string $code 104 | * @return string image url 105 | */ 106 | public function get_code_image($code=''){ 107 | if ($code=='') $code = $this->_logincode; 108 | if (!$code) return false; 109 | return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx'; 110 | } 111 | 112 | /** 113 | * 设置二维码对应的授权码 114 | * @param string $code 115 | * @return class $this 116 | */ 117 | public function set_login_code($code) { 118 | $this->_logincode = $code; 119 | return $this; 120 | } 121 | 122 | /** 123 | * 二维码登陆验证 124 | * 125 | * @return status: 126 | * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired 127 | * 201: just scaned but not confirm 128 | * 200: confirm then you can get user info 129 | */ 130 | public function verify_code() { 131 | if (!$this->_logincode) return false; 132 | $t = time().strval(mt_rand(100,999)); 133 | 134 | $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t; 135 | $send_snoopy = new Snoopy; 136 | $send_snoopy->referer = "https://wx.qq.com/"; 137 | $send_snoopy->fetch($url); 138 | $result = $send_snoopy->results; 139 | $this->log('step1:'.$result); 140 | if ($result) { 141 | preg_match("/window\.code=(\d+)/",$result,$matches); 142 | if(count($matches)>1) { 143 | $status = intval($matches[1]); 144 | if ($status==201) $_SESSION['login_step'] = 1; 145 | if ($status==200) { 146 | preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches); 147 | preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl); 148 | $this->log('step2:'.print_r($matches,true)); 149 | if (count($matcheurl)>1) { 150 | $ticket = $matches[1]; 151 | $scan = $matches[2]; 152 | //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new'; 153 | $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old'; 154 | $urlpart = parse_url($loginurl); 155 | $send_snoopy = new Snoopy; 156 | $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat"; 157 | $send_snoopy->fetch($loginurl); 158 | $result = $send_snoopy->results; 159 | $xml = simplexml_load_string($result); 160 | if ($xml->ret=="0") $this->skey = $xml->skey; 161 | foreach ($send_snoopy->headers as $key => $value) { 162 | $value = trim($value); 163 | if(strpos($value,'Set-Cookie: ') !== false){ 164 | $tmp = str_replace("Set-Cookie: ","",$value); 165 | $tmparray = explode(';', $tmp); 166 | $item = trim($tmparray[0]); 167 | $cookie.=$item.';'; 168 | } 169 | } 170 | $cookie .="Domain=.qq.com;"; 171 | $this->cookie = $cookie; 172 | $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result); 173 | 174 | $this->saveCookie($this->_cookiename,$this->cookie); 175 | } 176 | } 177 | return $status; 178 | } 179 | } 180 | return false; 181 | } 182 | 183 | /** 184 | * 获取登陆的cookie 185 | * 186 | * @param bool $is_array 是否以数值方式返回,默认否,返回字符串 187 | * @return string|array 188 | */ 189 | public function get_login_cookie($is_array = false){ 190 | if (!$is_array) return $this->cookie; 191 | $c_arr = explode(';',$this->cookie); 192 | $cookie = array(); 193 | foreach($c_arr as $item) { 194 | $kitem = explode('=',trim($item)); 195 | if (count($kitem)>1) { 196 | $key = trim($kitem[0]); 197 | $val = trim($kitem[1]); 198 | if (!empty($val)) $cookie[$key] = $val; 199 | } 200 | } 201 | return $cookie; 202 | } 203 | 204 | /** 205 | * 授权登陆后获取用户登陆信息 206 | */ 207 | public function get_login_info(){ 208 | if (!$this->cookie) return false; 209 | $t = time().strval(mt_rand(100,999)); 210 | $send_snoopy = new Snoopy; 211 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey); 212 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 213 | $send_snoopy->referer = "https://wx2.qq.com/"; 214 | $citems = $this->get_login_cookie(true); 215 | $post = array( 216 | "BaseRequest"=>array( 217 | array( 218 | "Uin"=>$citems['wxuin'], 219 | "Sid"=>$citems['wxsid'], 220 | "Skey"=>$this->skey, 221 | "DeviceID"=>'' 222 | ) 223 | ) 224 | ); 225 | $send_snoopy->submit($submit,json_encode($post)); 226 | $this->log('login_info:'.$send_snoopy->results); 227 | $result = json_decode($send_snoopy->results,true); 228 | if ($result['BaseResponse']['Ret']<0) return false; 229 | $this->_login_user = $result['User']; 230 | return $result; 231 | } 232 | 233 | /** 234 | * 获取头像 235 | * @param string $url 传入从用户信息接口获取到的头像地址 236 | */ 237 | public function get_avatar($url) { 238 | if (!$this->cookie) return false; 239 | if (strpos($url, 'http')===false) { 240 | $url = 'http://wx2.qq.com'.$url; 241 | } 242 | $send_snoopy = new Snoopy; 243 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 244 | $send_snoopy->referer = "https://wx2.qq.com/"; 245 | $send_snoopy->fetch($url); 246 | $result = $send_snoopy->results; 247 | if ($result) 248 | return $result; 249 | else 250 | return false; 251 | } 252 | 253 | /** 254 | * 登出当前登陆用户 255 | */ 256 | public function logout(){ 257 | if (!$this->cookie) return false; 258 | preg_match("/wxuin=(\w+);/",$this->cookie,$matches); 259 | if (count($matches)>1) $uid = $matches[1]; 260 | preg_match("/wxsid=(\w+);/",$this->cookie,$matches); 261 | if (count($matches)>1) $sid = $matches[1]; 262 | $this->log('logout: uid='.$uid.';sid='.$sid); 263 | $send_snoopy = new Snoopy; 264 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1'; 265 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 266 | $send_snoopy->referer = "https://wx2.qq.com/"; 267 | $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid)); 268 | $this->deleteCookie($this->_cookiename); 269 | return true; 270 | } 271 | } -------------------------------------------------------------------------------- /src/wechat-php-sdk/wechatext.class.php: -------------------------------------------------------------------------------- 1 | 26 | * @link https://github.com/dodgepudding/wechat-php-sdk 27 | * @version 1.2 28 | * 29 | */ 30 | 31 | include "snoopy.class.php"; 32 | class Wechatext 33 | { 34 | private $cookie; 35 | private $_cookiename; 36 | private $_cookieexpired = 3600; 37 | private $_account; 38 | private $_password; 39 | private $_datapath = './data/cookie_'; 40 | private $debug; 41 | private $_logcallback; 42 | private $_token; 43 | 44 | public function __construct($options) 45 | { 46 | $this->_account = isset($options['account'])?$options['account']:''; 47 | $this->_password = isset($options['password'])?$options['password']:''; 48 | $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; 49 | $this->debug = isset($options['debug'])?$options['debug']:false; 50 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 51 | $this->_cookiename = $this->_datapath.$this->_account; 52 | $this->cookie = $this->getCookie($this->_cookiename); 53 | } 54 | 55 | /** 56 | * 主动发消息 57 | * @param string $id 用户的uid(即FakeId) 58 | * @param string $content 发送的内容 59 | */ 60 | public function send($id,$content) 61 | { 62 | $send_snoopy = new Snoopy; 63 | $post = array(); 64 | $post['tofakeid'] = $id; 65 | $post['type'] = 1; 66 | $post['token'] = $this->_token; 67 | $post['content'] = $content; 68 | $post['ajax'] = 1; 69 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN"; 70 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 71 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 72 | $send_snoopy->submit($submit,$post); 73 | $this->log($send_snoopy->results); 74 | return $send_snoopy->results; 75 | } 76 | 77 | /** 78 | * 群发功能 纯文本 79 | * @param string $content 80 | * @return string 81 | */ 82 | public function mass($content) { 83 | $send_snoopy = new Snoopy; 84 | $post = array(); 85 | $post['type'] = 1; 86 | $post['token'] = $this->_token; 87 | $post['content'] = $content; 88 | $post['ajax'] = 1; 89 | $post['city']=''; 90 | $post['country']=''; 91 | $post['f']='json'; 92 | $post['groupid']='-1'; 93 | $post['imgcode']=''; 94 | $post['lang']='zh_CN'; 95 | $post['province']=''; 96 | $post['random']= rand(0, 1); 97 | $post['sex']=0; 98 | $post['synctxnews']=0; 99 | $post['synctxweibo']=0; 100 | $post['t']='ajax-response'; 101 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; 102 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 103 | $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; 104 | $send_snoopy->submit($submit,$post); 105 | $this->log($send_snoopy->results); 106 | return $send_snoopy->results; 107 | } 108 | 109 | /** 110 | * 群发功能 图文素材 111 | * @param int $appmsgid 图文素材ID 112 | * @return string 113 | */ 114 | function massNews($appmsgid){ 115 | $send_snoopy = new Snoopy; 116 | $post = array(); 117 | $post['type'] = 10; 118 | $post['token'] = $this->_token; 119 | $post['appmsgid'] = $appmsgid; 120 | $post['ajax'] = 1; 121 | $post['city']=''; 122 | $post['country']=''; 123 | $post['f']='json'; 124 | $post['groupid']='-1'; 125 | $post['imgcode']=''; 126 | $post['lang']='zh_CN'; 127 | $post['province']=''; 128 | $post['random']= rand(0, 1); 129 | $post['sex']=0; 130 | $post['synctxnews']=0; 131 | $post['synctxweibo']=0; 132 | $post['t']='ajax-response'; 133 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; 134 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 135 | $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; 136 | $send_snoopy->submit($submit,$post); 137 | $this->log($send_snoopy->results); 138 | return $send_snoopy->results; 139 | } 140 | 141 | /** 142 | * 获取用户列表列表 143 | * @param $page 页码(从0开始) 144 | * @param $pagesize 每页大小 145 | * @param $groupid 分组id 146 | * @return array ({contacts:[{id:12345667,nick_name:"昵称",remark_name:"备注名",group_id:0},{}....]}) 147 | */ 148 | function getUserList($page=0,$pagesize=10,$groupid=0){ 149 | $send_snoopy = new Snoopy; 150 | $t = time().strval(mt_rand(100,999)); 151 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=0&lang=zh_CN&token=".$this->_token; 152 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 153 | $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=$groupid&lang=zh_CN&f=json&token=".$this->_token; 154 | $send_snoopy->fetch($submit); 155 | $result = $send_snoopy->results; 156 | $this->log('userlist:'.$result); 157 | $json = json_decode($result,true); 158 | if (isset($json['contact_list'])) { 159 | $json = json_decode($json['contact_list'],true); 160 | if (isset($json['contacts'])) 161 | return $json['contacts']; 162 | } 163 | return false; 164 | } 165 | 166 | /** 167 | * 获取分组列表 168 | * 169 | */ 170 | function getGroupList(){ 171 | $send_snoopy = new Snoopy; 172 | $t = time().strval(mt_rand(100,999)); 173 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&token=".$this->_token; 174 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 175 | $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&f=json&token=".$this->_token; 176 | $send_snoopy->fetch($submit); 177 | $result = $send_snoopy->results; 178 | $this->log('userlist:'.$result); 179 | $json = json_decode($result,true); 180 | if (isset($json['group_list'])){ 181 | $json = json_decode($json['group_list'],true); 182 | if (isset($json['groups'])) 183 | return $json['groups']; 184 | } 185 | return false; 186 | } 187 | 188 | /** 189 | * 获取图文信息列表 190 | * @param $page 页码(从0开始) 191 | * @param $pagesize 每页大小 192 | * @return array 193 | */ 194 | public function getNewsList($page,$pagesize=10) { 195 | $send_snoopy = new Snoopy; 196 | $t = time().strval(mt_rand(100,999)); 197 | $type=10; 198 | $begin = $page*$pagesize; 199 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 200 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 201 | $submit = "https://mp.weixin.qq.com/cgi-bin/appmsg?token=".$this->_token."&lang=zh_CN&type=$type&action=list&begin=$begin&count=$pagesize&f=json&random=0.".$t; 202 | $send_snoopy->fetch($submit); 203 | $result = $send_snoopy->results; 204 | $this->log('newslist:'.$result); 205 | $json = json_decode($result,true); 206 | if (isset($json['app_msg_info'])) { 207 | return $json['app_msg_info']; 208 | } 209 | return false; 210 | } 211 | 212 | /** 213 | * 获取与指定用户的对话内容 214 | * @param $fakeid 215 | * @return array 216 | */ 217 | public function getDialogMsg($fakeid) { 218 | $send_snoopy = new Snoopy; 219 | $t = time().strval(mt_rand(100,999)); 220 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 221 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 222 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=".$fakeid."&token=".$this->_token."&lang=zh_CN&f=json&random=".$t; 223 | $send_snoopy->fetch($submit); 224 | $result = $send_snoopy->results; 225 | $this->log('DialogMsg:'.$result); 226 | $json = json_decode($result,true); 227 | if (isset($json['page_info'])) { 228 | return $json['page_info']; 229 | } 230 | return false; 231 | } 232 | 233 | /** 234 | * 发送图文信息,必须从图文库里选取消息ID发送 235 | * @param string $id 用户的uid(即FakeId) 236 | * @param string $msgid 图文消息id 237 | */ 238 | public function sendNews($id,$msgid) 239 | { 240 | $send_snoopy = new Snoopy; 241 | $post = array(); 242 | $post['tofakeid'] = $id; 243 | $post['type'] = 10; 244 | $post['token'] = $this->_token; 245 | $post['fid'] = $msgid; 246 | $post['appmsgid'] = $msgid; 247 | $post['error'] = 'false'; 248 | $post['ajax'] = 1; 249 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; 250 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 251 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 252 | $send_snoopy->submit($submit,$post); 253 | $this->log($send_snoopy->results); 254 | return $send_snoopy->results; 255 | } 256 | 257 | /** 258 | * 上传附件(图片/音频/视频) 259 | * @param string $filepath 本地文件地址 260 | * @param int $type 文件类型: 2:图片 3:音频 4:视频 261 | */ 262 | public function uploadFile($filepath,$type=2) { 263 | $send_snoopy = new Snoopy; 264 | $send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1"; 265 | $t = time().strval(mt_rand(100,999)); 266 | $post = array('formId'=>''); 267 | $postfile = array('uploadfile'=>$filepath); 268 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 269 | $send_snoopy->set_submit_multipart(); 270 | $submit = "http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=".$this->_token."&t=iframe-uploadfile&lang=zh_CN&formId= file_from_".$t; 271 | $send_snoopy->submit($submit,$post,$postfile); 272 | $tmp = $send_snoopy->results; 273 | $this->log('upload:'.$tmp); 274 | preg_match("/formId,.*?\'(\d+)\'/",$tmp,$matches); 275 | if (isset($matches[1])) { 276 | return $matches[1]; 277 | } 278 | return false; 279 | } 280 | 281 | /** 282 | * 创建图文消息 283 | * @param array $title 标题 284 | * @param array $summary 摘要 285 | * @param array $content 内容 286 | * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取) 287 | * @param array $srcurl 原文链接 288 | * @return json 289 | */ 290 | public function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') { 291 | $send_snoopy = new Snoopy; 292 | $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&token='.$this->_token; 293 | 294 | $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=".$this->_token; 295 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 296 | 297 | $send_snoopy->set_submit_normal(); 298 | $post = array( 299 | 'token'=>$this->_token, 300 | 'type'=>10, 301 | 'lang'=>'zh_CN', 302 | 'sub'=>'create', 303 | 'ajax'=>1, 304 | 'AppMsgId'=>'', 305 | 'error'=>'false', 306 | ); 307 | if (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid)) 308 | { 309 | $i = 0; 310 | foreach($title as $v) { 311 | $post['title'.$i] = $title[$i]; 312 | $post['author'.$i] = $author[$i]; 313 | $post['digest'.$i] = $summary[$i]; 314 | $post['content'.$i] = $content[$i]; 315 | $post['fileid'.$i] = $photoid[$i]; 316 | if ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i]; 317 | 318 | $i++; 319 | } 320 | } 321 | $post['count'] = $i; 322 | $post['token'] = $this->_token; 323 | $send_snoopy->submit($submit,$post); 324 | $tmp = $send_snoopy->results; 325 | $this->log('step2:'.$tmp); 326 | $json = json_decode($tmp,true); 327 | return $json; 328 | } 329 | 330 | /** 331 | * 发送媒体文件 332 | * @param $id 用户的uid(即FakeId) 333 | * @param $fid 文件id 334 | * @param $type 文件类型 335 | */ 336 | public function sendFile($id,$fid,$type) { 337 | $send_snoopy = new Snoopy; 338 | $post = array(); 339 | $post['tofakeid'] = $id; 340 | $post['type'] = $type; 341 | $post['token'] = $this->_token; 342 | $post['fid'] = $fid; 343 | $post['fileid'] = $fid; 344 | $post['error'] = 'false'; 345 | $post['ajax'] = 1; 346 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; 347 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 348 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 349 | $send_snoopy->submit($submit,$post); 350 | $result = $send_snoopy->results; 351 | $this->log('sendfile:'.$result); 352 | $json = json_decode($result,true); 353 | if ($json && $json['ret']==0) 354 | return true; 355 | else 356 | return false; 357 | } 358 | 359 | /** 360 | * 获取素材库文件列表 361 | * @param $type 文件类型: 2:图片 3:音频 4:视频 362 | * @param $page 页码(从0开始) 363 | * @param $pagesize 每页大小 364 | * @return array 365 | */ 366 | public function getFileList($type,$page,$pagesize=10) { 367 | $send_snoopy = new Snoopy; 368 | $t = time().strval(mt_rand(100,999)); 369 | $begin = $page*$pagesize; 370 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 371 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 372 | $submit = "https://mp.weixin.qq.com/cgi-bin/filepage?token=".$this->_token."&lang=zh_CN&type=$type&random=0.".$t."&begin=$begin&count=$pagesize&f=json"; 373 | $send_snoopy->fetch($submit); 374 | $result = $send_snoopy->results; 375 | $this->log('filelist:'.$result); 376 | $json = json_decode($result,true); 377 | if (isset($json['page_info'])) 378 | return $json['page_info']; 379 | else 380 | return false; 381 | } 382 | 383 | /** 384 | * 发送图文信息,必须从库里选取文件ID发送 385 | * @param string $id 用户的uid(即FakeId) 386 | * @param string $fid 文件id 387 | */ 388 | public function sendImage($id,$fid) 389 | { 390 | return $this->sendFile($id,$fid,2); 391 | } 392 | 393 | /** 394 | * 发送语音信息,必须从库里选取文件ID发送 395 | * @param string $id 用户的uid(即FakeId) 396 | * @param string $fid 语音文件id 397 | */ 398 | public function sendAudio($id,$fid) 399 | { 400 | return $this->sendFile($id,$fid,3); 401 | } 402 | 403 | /** 404 | * 发送视频信息,必须从库里选取文件ID发送 405 | * @param string $id 用户的uid(即FakeId) 406 | * @param string $fid 视频文件id 407 | */ 408 | public function sendVideo($id,$fid) 409 | { 410 | return $this->sendFile($id,$fid,4); 411 | } 412 | 413 | /** 414 | * 发送预览图文消息 415 | * @param string $account 账户名称(user_name) 416 | * @param string $title 标题 417 | * @param string $summary 摘要 418 | * @param string $content 内容 419 | * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取) 420 | * @param string $srcurl 原文链接 421 | * @return json 422 | */ 423 | public function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') { 424 | $send_snoopy = new Snoopy; 425 | $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview"; 426 | $send_snoopy->set_submit_normal(); 427 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 428 | $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN'; 429 | $post = array( 430 | 'AppMsgId'=>'', 431 | 'ajax'=>1, 432 | 'content0'=>$content, 433 | 'count'=>1, 434 | 'digest0'=>$summary, 435 | 'error'=>'false', 436 | 'fileid0'=>$photoid, 437 | 'preusername'=>$account, 438 | 'sourceurl0'=>$srcurl, 439 | 'title0'=>$title, 440 | ); 441 | $post['token'] = $this->_token; 442 | $send_snoopy->submit($submit,$post); 443 | $tmp = $send_snoopy->results; 444 | $this->log('sendpreview:'.$tmp); 445 | $json = json_decode($tmp,true); 446 | return $json; 447 | } 448 | 449 | /** 450 | * 获取用户的信息 451 | * @param string $id 用户的uid(即FakeId) 452 | * @return array {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]} 453 | */ 454 | public function getInfo($id) 455 | { 456 | $send_snoopy = new Snoopy; 457 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 458 | $t = time().strval(mt_rand(100,999)); 459 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 460 | $submit = "https://mp.weixin.qq.com/cgi-bin/getcontactinfo"; 461 | $post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id); 462 | $send_snoopy->submit($submit,$post); 463 | $this->log($send_snoopy->results); 464 | $result = json_decode($send_snoopy->results,true); 465 | if(isset($result['contact_info'])){ 466 | return $result['contact_info']; 467 | } 468 | return false; 469 | } 470 | 471 | /** 472 | * 获得头像数据 473 | * 474 | * @param FakeId $fakeid 475 | * @return JPG二进制数据 476 | */ 477 | public function getHeadImg($fakeid){ 478 | $send_snoopy = new Snoopy; 479 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 480 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 481 | $url = "https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=".$this->_token."&lang=zh_CN"; 482 | $send_snoopy->fetch($url); 483 | $result = $send_snoopy->results; 484 | $this->log('Head image:'.$fakeid.'; length:'.strlen($result)); 485 | if(!$result){ 486 | return false; 487 | } 488 | return $result; 489 | } 490 | 491 | /** 492 | * 获取消息更新数目 493 | * @param int $lastid 最近获取的消息ID,为0时获取总消息数目 494 | * @return int 数目 495 | */ 496 | public function getNewMsgNum($lastid=0){ 497 | $send_snoopy = new Snoopy; 498 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 499 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 500 | $submit = "https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=".$lastid; 501 | $post = array('ajax'=>1,'token'=>$this->_token); 502 | $send_snoopy->submit($submit,$post); 503 | $this->log($send_snoopy->results); 504 | $result = json_decode($send_snoopy->results,1); 505 | if(!$result){ 506 | return false; 507 | } 508 | return intval($result['newTotalMsgCount']); 509 | } 510 | 511 | /** 512 | * 获取最新一条消息 513 | * @return array {"id":"最新一条id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容","playLength":"0","length":"0","source":"","starred":"0","status":"4"} 514 | */ 515 | public function getTopMsg(){ 516 | $send_snoopy = new Snoopy; 517 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 518 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; 519 | $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=".$this->_token; 520 | $send_snoopy->fetch($submit); 521 | $this->log($send_snoopy->results); 522 | $result = $send_snoopy->results; 523 | $json = json_decode($result,true); 524 | if (isset($json['msg_items'])) { 525 | $json = json_decode($json['msg_items'],true); 526 | if(isset($json['msg_item'])) 527 | return array_shift($json['msg_item']); 528 | } 529 | return false; 530 | } 531 | 532 | /** 533 | * 获取新消息 534 | * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取 535 | * @param $offset lastid起算第一条的偏移量 536 | * @param $perpage 每页获取多少条 537 | * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内) 538 | * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0 539 | * @param $star 是否星标组信息 540 | * @return array[] 同getTopMsg()返回的字段结构相同 541 | */ 542 | public function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){ 543 | $send_snoopy = new Snoopy; 544 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 545 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=".$this->_token; 546 | $lastid = $lastid===0 ? '':$lastid; 547 | $addstar = $star?'&action=star':''; 548 | $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&lang=zh_CN{$addstar}&count=$perpage&timeline=$today&day=$day&frommsgid=$lastid&offset=$offset&token=".$this->_token; 549 | $send_snoopy->fetch($submit); 550 | $this->log($send_snoopy->results); 551 | $result = $send_snoopy->results; 552 | $json = json_decode($result,true); 553 | if (isset($json['msg_items'])) { 554 | $json = json_decode($json['msg_items'],true); 555 | if(isset($json['msg_item'])) 556 | return $json['msg_item']; 557 | } 558 | return false; 559 | } 560 | 561 | /** 562 | * 获取图片消息 563 | * @param int $msgid 消息id 564 | * @param string $mode 图片尺寸(large/small) 565 | * @return jpg二进制文件 566 | */ 567 | public function getMsgImage($msgid,$mode='large'){ 568 | $send_snoopy = new Snoopy; 569 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 570 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 571 | $url = "https://mp.weixin.qq.com/cgi-bin/getimgdata?token=".$this->_token."&msgid=$msgid&mode=$mode&source=&fileId=0"; 572 | $send_snoopy->fetch($url); 573 | $result = $send_snoopy->results; 574 | $this->log('msg image:'.$msgid.';length:'.strlen($result)); 575 | if(!$result){ 576 | return false; 577 | } 578 | return $result; 579 | } 580 | 581 | /** 582 | * 获取语音消息 583 | * @param int $msgid 消息id 584 | * @return mp3二进制文件 585 | */ 586 | public function getMsgVoice($msgid){ 587 | $send_snoopy = new Snoopy; 588 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 589 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 590 | $url = "https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=".$this->_token."&msgid=$msgid&fileId=0"; 591 | $send_snoopy->fetch($url); 592 | $result = $send_snoopy->results; 593 | $this->log('msg voice:'.$msgid.';length:'.strlen($result)); 594 | if(!$result){ 595 | return false; 596 | } 597 | return $result; 598 | } 599 | 600 | /** 601 | * 模拟登录获取cookie 602 | * @return [type] [description] 603 | */ 604 | public function login(){ 605 | $snoopy = new Snoopy; 606 | $submit = "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"; 607 | $post["username"] = $this->_account; 608 | $post["pwd"] = md5($this->_password); 609 | $post["f"] = "json"; 610 | $post["imgcode"] = ""; 611 | $snoopy->referer = "https://mp.weixin.qq.com/"; 612 | $snoopy->submit($submit,$post); 613 | $cookie = ''; 614 | $this->log($snoopy->results); 615 | $result = json_decode($snoopy->results,true); 616 | 617 | if (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) { 618 | return false; 619 | } 620 | 621 | foreach ($snoopy->headers as $key => $value) { 622 | $value = trim($value); 623 | if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $value,$match)) 624 | $cookie .=$match[1].'='.$match[2].'; '; 625 | } 626 | 627 | preg_match("/token=(\d+)/i",$result['redirect_url'],$matches); 628 | if($matches){ 629 | $this->_token = $matches[1]; 630 | $this->log('token:'.$this->_token); 631 | } 632 | $cookies='{"cookie":"'.$cookie.'","token":"'.$this->_token.'"}'; 633 | $this->saveCookie($this->_cookiename,$cookies); 634 | return $cookie; 635 | } 636 | 637 | /** 638 | * 把cookie写入缓存 639 | * @param string $filename 缓存文件名 640 | * @param string $content 文件内容 641 | * @return bool 642 | */ 643 | public function saveCookie($filename,$content){ 644 | return file_put_contents($filename,$content); 645 | } 646 | 647 | /** 648 | * 读取cookie缓存内容 649 | * @param string $filename 缓存文件名 650 | * @return string cookie 651 | */ 652 | public function getCookie($filename){ 653 | if (file_exists($filename)) { 654 | $mtime = filemtime($filename); 655 | if ($mtime_cookieexpired) 656 | $data = ''; 657 | else 658 | $data = file_get_contents($filename); 659 | } else 660 | $data = ''; 661 | if($data){ 662 | $login=json_decode($data,true); 663 | $send_snoopy = new Snoopy; 664 | $send_snoopy->rawheaders['Cookie']= $login['cookie']; 665 | $send_snoopy->maxredirs = 0; 666 | $url = "https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=".$login['token']; 667 | $send_snoopy->fetch($url); 668 | $header = $send_snoopy->headers; 669 | $this->log('header:'.print_r($send_snoopy->headers,true)); 670 | if( strstr($header[3], 'EXPIRED')){ 671 | return $this->login(); 672 | }else{ 673 | $this->_token =$login['token']; 674 | $this->log('token:'.$this->_token); 675 | return $login['cookie']; 676 | } 677 | }else{ 678 | return $this->login(); 679 | } 680 | } 681 | 682 | /** 683 | * 验证cookie的有效性 684 | * @return bool 685 | */ 686 | public function checkValid() 687 | { 688 | if (!$this->cookie || !$this->_token) return false; 689 | $send_snoopy = new Snoopy; 690 | $post = array('ajax'=>1,'token'=>$this->_token); 691 | $submit = "https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN"; 692 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 693 | $send_snoopy->submit($submit,$post); 694 | $result = $send_snoopy->results; 695 | if(json_decode($result,1)){ 696 | return true; 697 | }else{ 698 | return false; 699 | } 700 | } 701 | 702 | private function log($log){ 703 | if ($this->debug && function_exists($this->_logcallback)) { 704 | if (is_array($log)) $log = print_r($log,true); 705 | return call_user_func($this->_logcallback,$log); 706 | } 707 | } 708 | 709 | } 710 | -------------------------------------------------------------------------------- /src/wechat-push.php: -------------------------------------------------------------------------------- 1 | $siteData['wechatToken'], 15 | 'appid' => $siteData['wechatAppID'], 16 | 'appsecret' => $siteData['wechatAppSecret'] 17 | ); 18 | $weObj = new Wechat($options); 19 | 20 | 21 | // 查询菜单 22 | if ($act === 'getMenu') { 23 | var_dump($weObj->getMenu()); 24 | exit; 25 | }; 26 | 27 | if ($act === 'createMenu') { 28 | $data = array( 29 | 'button' => array( 30 | array( 31 | 'type' => 'view', 32 | 'name' => '伍 & 熊', 33 | 'url' => 'http://wedding.ciaoca.com/' 34 | ), 35 | array( 36 | 'name' => '参加婚礼', 37 | 'sub_button' => array( 38 | array( 39 | 'type' => 'click', 40 | 'name' => '我的喜帖', 41 | 'key' => '喜帖' 42 | ), 43 | array( 44 | 'type' => 'click', 45 | 'name' => '婚宴酒店', 46 | 'key' => '地址' 47 | ) 48 | ), 49 | ), 50 | array( 51 | 'name' => '精彩互动', 52 | 'sub_button' => array( 53 | array( 54 | 'type' => 'view', 55 | 'name' => '祝福新人', 56 | 'url' => 'http://wedding.ciaoca.com/blessing.php' 57 | ), 58 | array( 59 | 'type' => 'click', 60 | 'name' => '幸福瞬间', 61 | 'key' => '照片分享' 62 | ), 63 | array( 64 | 'type' => 'pic_photo_or_album', 65 | 'name' => '照片分享', 66 | 'key' => 'photo_share' 67 | ) 68 | ), 69 | ) 70 | ) 71 | ); 72 | 73 | $weObj->createMenu($data); 74 | exit; 75 | }; 76 | ?> -------------------------------------------------------------------------------- /src/wechat.php: -------------------------------------------------------------------------------- 1 | $siteData['wechatToken'], 9 | 'appid' => $siteData['wechatAppID'], 10 | 'appsecret' => $siteData['wechatAppSecret'] 11 | ); 12 | $weObj = new Wechat($options); 13 | $weObj->valid(); 14 | $weObj->getRev(); 15 | 16 | 17 | /** 18 | * 自动回复 19 | */ 20 | function msgSend($key){ 21 | Global $WEDDING_INVITEES; 22 | Global $weObj; 23 | 24 | $key = strtolower($key); 25 | 26 | switch($key) { 27 | case 'welcome': 28 | // 回复文字信息 29 | $weObj->text("欢迎关注")->reply(); 30 | 31 | /* 回复图文信息 32 | $weObj->news( 33 | array( 34 | '0' => array( 35 | 'Title' => '标题', 36 | 'PicUrl' => '图片地址', 37 | 'Url' => '网址' 38 | ) 39 | ) 40 | )->reply(); 41 | */ 42 | break; 43 | 44 | case 'test': 45 | case '测试': 46 | $weObj->text("收到消息")->reply(); 47 | break; 48 | 49 | default: 50 | // 检测是否在被邀请人名单 51 | $alias = ''; 52 | $name = ''; 53 | foreach ($WEDDING_INVITEES as $n => $v) { 54 | if (in_array($key, $v)) { 55 | $alias = $n; 56 | $name = $v[0]; 57 | break; 58 | }; 59 | }; 60 | 61 | if (strlen($alias) && strlen($name)) { 62 | $weObj->news( 63 | array( 64 | '0' => array( 65 | 'Title' => '送呈' . $name . '台启', 66 | 'Description' => "谨定于X年X月X日星期X,为XXX和XXX举办婚礼,恭请" . $name . "光临。\r\n时间:X时X分\r\n地点:XXXXXXXXXX", 67 | 'PicUrl' => '图片地址', 68 | 'Url' => 'http://wedding.ciaoca.com/invitation.php?name=' . $alias 69 | ) 70 | ) 71 | )->reply(); 72 | }; 73 | } 74 | }; 75 | 76 | $msgType = $weObj->getRevType(); 77 | 78 | switch($msgType) { 79 | // 收到事件消息 80 | case Wechat::MSGTYPE_EVENT: 81 | $msgEvent = $weObj->getRevEvent(); 82 | 83 | // 关注自动回复 84 | if (strtolower($msgEvent['event']) === 'subscribe') { 85 | msgSend('welcome'); 86 | 87 | } elseif (strtolower($msgEvent['event']) === 'click') { 88 | msgSend($msgEvent['key']); 89 | }; 90 | break; 91 | 92 | // 收到文本消息 93 | case Wechat::MSGTYPE_TEXT: 94 | $msgKey = $weObj->getRevContent(); 95 | 96 | if (strlen($msgKey) > 20) { 97 | exit; 98 | }; 99 | 100 | msgSend($msgKey); 101 | break; 102 | 103 | // 收到图片消息 104 | case Wechat::MSGTYPE_IMAGE: 105 | $msgImage = $weObj->getRevPic(); 106 | 107 | // 下载图片 108 | $fileData = xcurl($msgImage['picurl']); 109 | if (is_string($fileData) && substr($fileData, 0, 10) === 'curl_error') { 110 | exit; 111 | }; 112 | 113 | // 写入图片 114 | $fileFolder = 'wechat-photo/'; 115 | $fileName = date('Ymd-His') . '-' . $weObj->getRevID(); 116 | 117 | file_put_contents($fileFolder . $fileName, $fileData); 118 | $fileInfo = getimagesize($fileFolder. $fileName); 119 | 120 | switch($fileInfo['mime']){ 121 | case 'image/jpeg': 122 | rename($fileFolder. $fileName, $fileFolder. $fileName . '.jpg'); 123 | break; 124 | case 'image/png': 125 | rename($fileFolder. $fileName, $fileFolder. $fileName . '.png'); 126 | break; 127 | case 'image/gif': 128 | rename($fileFolder. $fileName, $fileFolder. $fileName . '.gif'); 129 | break; 130 | }; 131 | 132 | break; 133 | 134 | // 自动回复 135 | // not default: 136 | }; 137 | ?> --------------------------------------------------------------------------------