#coding=utf-8 require 'base64' class WechatsController < ActionController::Base wechat_responder include ApplicationHelper ROOT_URL = ENV["wechat_url"] || "#{Setting.protocol}://#{Setting.host_name}" #ROOT_URL = "http://www.trustie.net" # default text responder when no other match on :text do |request, content| #邀请码 # if join_class_request(request) # sendBindClass(request, {invite_code: content}) # elsif join_project_request(request) # sendBindProject(request, {invite_code: content}) # else request.reply.text "您的意见已收到,非常感谢~ \n更多问题可以通过以下方式联系我们:\n官方QQ群:373967360\n我们会认真聆听您的意见和建议。" # end end # When receive 'help', will trigger this responder on :text, with: 'help' do |request| request.reply.text 'help content' end # When receive 'news', will match and will got count as as parameter on :text, with: /^(\d+) news$/ do |request, count| # Wechat article can only contain max 10 items, large than 10 will dropped. news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } } request.reply.news(news) do |article, n, index| # article is return object article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/' end end on :event, with: 'subscribe' do |request| default_msg(request) end # When unsubscribe user scan qrcode qrscene_xxxxxx to subscribe in public account # notice user will subscribe public account at same time, so wechat won't trigger subscribe event any more on :scan, with: 'qrscene_xxxxxx' do |request, ticket| request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}" end # When subscribe user scan scene_id in public account on :scan, with: 'scene_id' do |request, ticket| sendBindClass(request, {ticket: ticket}) end # When no any on :scan responder can match subscribe user scaned scene_id on :event, with: 'scan' do |request| if request[:EventKey].present? checkTicket(request, {ticket: request[:Ticket]}) end end # When enterprise user press menu BINDING_QR_CODE and success to scan bar code on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type| request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}" end # Except QR code, wechat can also scan CODE_39 bar code in enterprise account on :scan, with: 'BINDING_BARCODE' do |message, scan_result| if scan_result.start_with? 'CODE_39,' message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}" end end # When user click the menu button on :click, with: 'BOOK_LUNCH' do |request, key| request.reply.text "User: #{request[:FromUserName]} click #{key}" end on :click, with: 'DEV' do |request, key| uw = user_binded?(request[:FromUserName]) unless uw sendBind(request) else request.reply.text "此功能正在开发中,很快就会上线,谢谢!" end end # When user view URL in the menu button on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view| request.reply.text "#{request[:FromUserName]} view #{view}" end # When user sent the imsage on :image do |request| request.reply.image(request[:MediaId]) # Echo the sent image to user end # When user sent the voice on :voice do |request| request.reply.voice(request[:MediaId]) # Echo the sent voice to user end # When user sent the video on :video do |request| nickname = wechat.user(request[:FromUserName])['nickname'] # Call wechat api to get sender nickname request.reply.video(request[:MediaId], title: 'Echo', description: "Got #{nickname} sent video") # Echo the sent video to user end # When user sent location on :location do |request| request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}") end on :event, with: 'unsubscribe' do |request| unBind(request) request.reply.success # user can not receive this message end # When user enter the app / agent app on :event, with: 'enter_agent' do |request| request.reply.text "#{request[:FromUserName]} enter agent app now" end # When batch job create/update user (incremental) finished. on :batch_job, with: 'sync_user' do |request, batch_job| request.reply.text "sync_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" end # When batch job replace user (full sync) finished. on :batch_job, with: 'replace_user' do |request, batch_job| request.reply.text "replace_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" end # When batch job invent user finished. on :batch_job, with: 'invite_user' do |request, batch_job| request.reply.text "invite_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" end # When batch job replace department (full sync) finished. on :batch_job, with: 'replace_party' do |request, batch_job| request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" end # Any not match above will fail to below on :fallback, respond: 'fallback message' on :click, with: 'FEEDBACK' do |request, key| request.reply.text "如有问题反馈,请您:\n1、直接切换至输入框,发微信给我们。\n2、加入QQ群:373967360,直接互动。\n3、登录网站:www.trustie.net,给我们留言。\n\n如您有合作事宜洽谈,请联系:\n王林春 老师\n手机:13467631747\nQQ:494496321" end on :click, with: 'MY_NEWS' do |request, key| default_msg(request) end on :click, with: 'PROJECT' do |request, key| request.reply.text "该功能将在近日开放,敬请期待!" end on :click, with: 'JOIN_PROJECT' do |request, key| # request.reply.text "该功能将在近日开放,敬请期待!" uw = user_binded?(request[:FromUserName]) unless uw sendBind(request) else request.reply.text "请直接回复6位项目邀请码\n(不区分大小写):" end end on :click, with: 'JOIN_CLASS' do |request, key| uw = user_binded?(request[:FromUserName]) unless uw sendBind(request) else request.reply.text "请直接回复5位班级邀请码\n(不区分大小写):" end end on :click, with: 'UNBIND' do |request, key| uw = user_binded?(request[:FromUserName]) unless uw request.reply.text "您还未绑定帐号" else #解除绑定 us = UsersService.new us.wechat_unbind uw tmpurl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{ROOT_URL+'/wechat/user_activities'}&response_type=code&scope=snsapi_base&state=login&connect_redirect=1#wechat_redirect" news = (1..1).each_with_object([]) { |n, memo| memo << { title: '重新绑定提醒', content: "尊敬的用户,您已解除绑定。\n解除时间:#{format_time(Time.now)}\n点击进入重新绑定!"} } request.reply.news(news) do |article, n, index| # article is return object url = tmpurl article.item title: "#{n[:title]}", description: n[:content], url: url end end end def join_class_request(request) openid = request[:FromUserName] wl = WechatLog.where("openid = '#{openid}' and request_raw like '%\"Event\":\"click\"%'").order('id desc').first wl && JSON(wl.request_raw)["EventKey"] == 'JOIN_CLASS' end def join_project_request(request) openid = request[:FromUserName] wl = WechatLog.where("openid = '#{openid}' and request_raw like '%\"Event\":\"click\"%'").order('id desc').first wl && JSON(wl.request_raw)["EventKey"] == 'JOIN_PROJECT' end def sendBindClass(request, params) begin uw = user_binded?(request[:FromUserName]) if !uw return sendBind(request) else return join_class(params, uw.user, request) end rescue => e logger.error e.inspect logger.error e.backtrace.join("\n") return request.reply.text e end end def sendBindProject(request, params) begin uw = user_binded?(request[:FromUserName]) if !uw return sendBind(request) else return join_project(params, uw.user, request) end rescue => e logger.error e.inspect logger.error e.backtrace.join("\n") return request.reply.text e end end def checkTicket(request,params) begin uw = user_binded?(request[:FromUserName]) if !uw return sendBind(request) end course = nil course = Course.where(qrcode: params[:ticket]).first if params[:ticket] course = Course.where(invite_code: params[:invite_code]).first if params[:invite_code] if course return join_class(params, uw.user, request) else project = nil project = Project.where(qrcode: params[:ticket]).first if params[:ticket] project = Project.where(invite_code: params[:invite_code]).first if params[:invite_code] if project return join_project(params, uw.user, request) end end raise "该二维码已失效" rescue => e logger.error e.inspect logger.error e.backtrace.join("\n") return request.reply.text e end end def default_msg(request) uw = user_binded?(request[:FromUserName]) if uw && uw.user request.reply.text "欢迎回来, #{uw.user.show_name}" else sendBind(request) end end def unBind(request) uw = user_binded?(request[:FromUserName]) uw.try(:subscribe!) end def sendBind(request) tmpurl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{ROOT_URL+'/wechat/user_activities'}&response_type=code&scope=snsapi_base&state=login&connect_redirect=1#wechat_redirect" logger.info "tmpurl!!!!!!!!!!!!!!" logger.info tmpurl news = (1..1).each_with_object([]) { |n, memo| memo << { title: '绑定登录', content: "欢迎使用Trustie创新实践服务平台! 在这里您可以随时了解您的课程和项目动态,随时点赞和回复。交作业、代码提交等更多功能,请前往 www.trustie.net 我们将会与微信不断结合,为您提供更有价值的服务。 您还未绑定确实的用户,请先绑定,谢谢!" } } request.reply.news(news) do |article, n, index| # article is return object url = tmpurl pic_url = "#{ROOT_URL}/images/weixin_pic.jpg" article.item title: "#{n[:title]}", description: n[:content], pic_url: pic_url, url: url end end def join_class(params, user, request) course = nil course = Course.where(qrcode: params[:ticket]).first if params[:ticket] course = Course.where(invite_code: params[:invite_code]).first if params[:invite_code] raise "班级不存在,请确认您的邀请码是否输入正确,谢谢!" unless course #取出用户角色类型 role = 10 cs = CoursesService.new status = cs.join_course({invite_code: course.invite_code}, user) logger.info status if status[:state] != 0 raise CoursesService::JoinCourseError.message(status[:state]) end news = (1..1).each_with_object([]) { |n, memo| memo << { title: '恭喜您成功加入班级,开始学习吧!', content: "课程名称:#{course.syllabus.title}\n班级名称:#{course.name}\n任课老师:#{course.teacher.show_name}\n进入班级,和小伙伴愉快的学习吧!"} } return request.reply.news(news) do |article, n, index| # article is return object url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{ROOT_URL+'/wechat/user_activities#/class?id='+course.id.to_s}&response_type=code&scope=snsapi_base&state=myclass#wechat_redirect" pic_url = "#{ROOT_URL}/images/wechat/class.jpg" article.item title: "#{n[:title]}", description: n[:content], pic_url: pic_url, url: url end end def join_project(params, user, request) project = nil project = Project.where(qrcode: params[:ticket]).first if params[:ticket] project = Project.where(invite_code: params[:invite_code]).first if params[:invite_code] raise "项目不存在,请确认您的邀请码是否输入正确,谢谢!" unless project #取出用户角色类型 role = 5 ps = ProjectsService.new status = ps.join_project({role:5, invite_code: project.invite_code}, user) if status != 0 raise ProjectsService::JoinProjectError.message(status) end creator = User.find(project.user_id) news = (1..1).each_with_object([]) { |n, memo| memo << { title: '恭喜您成功加入项目,开始研发吧!', content: "项目名称:#{project.name}\n发起人:#{creator.show_name}\n进入项目,和小伙伴轻松的研发吧!"} } return request.reply.news(news) do |article, n, index| # article is return object url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{ROOT_URL+'/wechat/user_activities#/project?id='+project.id.to_s}&response_type=code&scope=snsapi_base&state=myproject#wechat_redirect" pic_url = "#{ROOT_URL}/images/wechat/project.jpg" article.item title: "#{n[:title]}", description: n[:content], pic_url: pic_url, url: url end end # 用于权限跳转 def auth state = params[:state] url = "#{ROOT_URL}/wechat/auth_callback" authorize_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{url}&response_type=code&scope=snsapi_base&state=#{state}&connect_redirect=1#wechat_redirect" redirect_to authorize_url end def auth_callback path = Base64.urlsafe_decode64(params[:state]) open_id = get_openid_from_code(params[:code]) unless open_id render 'wechats/open_wechat', layout: nil and return end redirect_to "/wechat/user_activities##{path}" end ### controller method module Controllers def get_bind begin code = params[:code] || session[:wechat_code] openid = get_openid_from_code(code) # raise "无法获取到微信openid" unless openid raise "请在微信中关注Trustie创新实践平台后再打开本页面" unless openid uw = UserWechat.where(openid: openid).first raise "还未绑定trustie帐户" unless (uw && uw.bindtype == 0) logger.debug "get_bind ============= #{uw}" user = uw.user ::ApiKey.delete_all(user_id: user.id) key = ::ApiKey.create!(user_id: user.id) render :json =>{status: 0, token: key.access_token} rescue Exception=>e render :json => {status: -1, message: e.message} end end def is_bind begin code = params[:code] || session[:wechat_code] open_id = get_openid_from_code(code) raise "还未绑定trustie帐户" unless user_binded?(open_id) render :json => {status: 0} rescue Exception=>e render :json => {status: -1, message: e.message} end end def bind begin code = params[:code] || session[:wechat_code] openid = get_openid_from_code(code) # raise "无法获取到openid,请在微信中打开本页面" unless openid raise "请在微信中关注Trustie创新实践平台后再打开本页面" unless openid raise "此微信号已绑定用户,不能重复绑定" if user_binded?(openid) user, last_login_on = User.try_to_login(params[:username], params[:password]) raise "用户名或密码错误,请重新输入" unless user #补全用户信息 raise "此用户已经绑定过公众号,请换一个帐户试试" if user.user_wechat UserWechat.create!( openid: openid, user: user ) ws = WechatService.new ws.binding_succ_notice(user.id, "您已成功绑定Trustie平台!", user.show_name+"("+user.login+")", format_time(Time.now)) render :json => {status:0, msg: "绑定成功"} rescue Exception=>e render :json => {status: -1, msg: e.message} end end def login session[:wechat_code] = params[:code] if params[:code] openid = get_openid_from_code(params[:code]) @wechat_user = user_binded?(openid) render 'wechats/user_activities', layout: nil end def user_activities @appid = Wechat.config.appid ## sign @sign_params = wechat.jsapi_ticket.signature(current_url) session[:wechat_code] = params[:code] if params[:code] @path = '/'+(params[:state] || '') open_id = get_openid_from_code(params[:code]) rescue unless open_id render 'wechats/open_wechat', layout: nil and return end logger.info "user_activities!!!!!!!!!!!!!!" logger.info params #保证下面的redirect_to "/wechat/user_activities##{@path}?id=...不会往下走 if params[:state] == nil return end unless (user_binded?(open_id) || params[:state] == "invite_code" || params[:state] == "project_invite_code" || params[:state] == "blog_comment" || params[:state] == "course_notice" || params[:state] == "project_discussion" || params[:state] == "course_discussion" || params[:state] == "homework" || params[:state] == "issues" || params[:state] == "journal_for_message") @path = '/login' else if params[:state] == 'myclass' @course_id = params[:id]; elsif params[:state] == 'myproject' @project_id = params[:id]; end session[:wechat_openid] = open_id if params[:code] if !(params[:state] == "invite_code" || params[:state] == "project_invite_code" || params[:state] == "blog_comment" || params[:state] == "course_notice" || params[:state] == "project_discussion" || params[:state] == "course_discussion" || params[:state] == "homework" || params[:state] == "issues" || params[:state] == "journal_for_message") uw = user_binded?(open_id) if uw user = uw.user lastname = user.lastname end end if lastname && lastname == "" @path = '/edit_userinfo' else @path = params[:state].split('/')[0] useridstr = params[:state].split('/')[1] if useridstr redirect_to "/wechat/user_activities##{@path}?id=#{params[:id]}&#{useridstr}" and return elsif params[:id] redirect_to "/wechat/user_activities##{@path}?id=#{params[:id]}" and return else redirect_to "/wechat/user_activities##{@path}" and return end # redirect_to "/wechat/user_activities##{@path}?id=#{params[:id]}" and return end end end render 'wechats/user_activities', layout: nil end private def get_openid_from_code(code) if code =='test' openid = 'orgVLv8TlS6e7FDiI6xdTGHRaaRo' session[:wechat_openid] = openid return openid end openid = session[:wechat_openid] unless openid if code #不能联系调两次web_access_token 否则会提示请在微信客户端打开次链接 info = wechat.web_access_token(code) openid =info["openid"] access_token =info["access_token"] if access_token session[:access_token] = access_token end refresh_token = info["refresh_token"] if refresh_token session[:refresh_token] = refresh_token end end end if openid session[:wechat_openid] = openid end return openid end ## 能进来的就是已关注 ## 因为取消订阅的记录被删除了 def user_binded?(openid) uw = UserWechat.where(openid: openid).first if uw && uw.bindtype == 0 uw else nil end end def current_url "#{request.protocol}#{request.host_with_port}#{request.fullpath}" end end include Controllers end