论坛首页 编程语言技术论坛

最小的ruby飞信客户端

浏览 14489 次
精华帖 (7) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-08-09   最后修改:2010-07-29
最小的飞信的ruby客户端,功能很简单,目前只能给自己和飞信好友发短信。使用TCP连接,没有走HTTP通道。

更新部分内容应对2010年7月25日飞信升级。有图形验证码了?没有时间管它,先hack一下绕过去。
google code在家暂时无法访问了。更新后代码如下(也可以去http://gist.github.com/498262 下载):

#!/usr/bin/ruby
# Using GPL v2
# Author:: DongYuwei(mailto:newdongyuwei@gmail.com)
# 更新部分内容应对2010年7月25日飞信升级 

require 'uri'
require 'net/http'
require 'net/https'
require "socket"
require 'rexml/document'
require 'digest/md5'
require 'digest/sha1'
require "iconv"

class Fetion
	def initialize(phone_num , password)
		@phone_num = phone_num;
		@password = password;
		@domain = "fetion.com.cn";
		@login_xml = '<args><device type="PC" version="0" client-version="3.5.2540" /><caps value="simple-im;im-session;temp-group;personal-group" /><events value="contact;permission;system-message;personal-group" /><user-info attributes="all" /><presence><basic value="400" desc="" /></presence></args>';
		self.init
	end
	
	def init
		doc = REXML::Document.new(self.get_system_config())
		sipc_proxy = ""
		doc.elements.each("//sipc-proxy") do |element|  # using regexp should be faster
			sipc_proxy = element.text
		end
		@SIPC = SIPC.new(sipc_proxy);
		
		sipc_url = ""
		#ssi-app-sign-in
		doc.elements.each("//ssi-app-sign-in-v2") do |element|
			sipc_url = element.text
		end
		@fetion_num = self.get_fetion_num(self.SSIAppSignIn(sipc_url))
	end
	
	def login()
		request1 = sprintf("R %s SIP-C/2.0\r\nF: %s\r\nI: 1\r\nQ: 1 R\r\nL: %s\r\n\r\n",@domain, @fetion_num, @login_xml.length)
		request1 = request1 + @login_xml
		server_response = @SIPC.request(request1)
		@nonce = server_response.scan(/nonce="(.*)"/)[0][0]
		
		request2 = sprintf("R %s SIP-C/2.0\r\nF: %s\r\nI: 1\r\nQ: 2 R\r\nA: Digest response=\"%s\",cnonce=\"%s\"\r\nL: %s\r\n\r\n", @domain, @fetion_num, self.get_response(), @cnonce, @login_xml.length)
		request2 = request2 + @login_xml
		@SIPC.request(request2)
	end
	
	def send_sms(phone, sms_text)
		sms_text = Iconv.iconv("UTF-8","UTF-8",sms_text)[0]
		request = sprintf("M %s SIP-C/2.0\r\nF: %s\r\nI: 2\r\nQ: 1 M\r\nT: tel:%s\r\nN: SendSMS\r\nL: %s\r\n\r\n",@domain, @fetion_num, phone, sms_text.length)
		request = request + sms_text
		@SIPC.request(request)
	end
	
	def send_sms_to_self(sms_text)
		sms_text = Iconv.iconv("UTF-8","UTF-8",sms_text)[0]
		request = sprintf("M %s SIP-C/2.0\r\nF: %s\r\nI: 2\r\nQ: 1 M\r\nT: %s\r\nN: SendCatSMS\r\nL: %s\r\n\r\n",@domain, @fetion_num, @uri, sms_text.length)
		request = request + sms_text
		@SIPC.request(request)
	end

	def logout()
		logout_request = sprintf("R %s SIP-C/2.0\r\nF: %s\r\nI: 1 \r\nQ: 3 R\r\nX: 0\r\n\r\n", @domain, @fetion_num)
		@SIPC.request(logout_request)
	end
	
	def get_response()
		@cnonce = Digest::MD5.hexdigest(rand.to_s)
		key = Digest::MD5.digest(@fetion_num + ":" + @domain + ":" + @password)
		h1 = Digest::MD5.hexdigest(key + ":" + @nonce + ":" + @cnonce).upcase
		h2 = Digest::MD5.hexdigest("REGISTER:" + @fetion_num).upcase
		return Digest::MD5.hexdigest(h1+":" + @nonce + ":" + h2).upcase
	end
	
	def get_system_config()
		uri = URI.parse("http://nav.fetion.com.cn/nav/getsystemconfig.aspx")
		http = Net::HTTP.new(uri.host, uri.port)
		params = sprintf('<config><user mobile-no="%s" /><client type="PC" version="3.5.2540" platform="W5.1" /><servers version="0" /><service-no version="0" /><parameters version="0" /><hints version="0" /><http-applications version="0" /><client-config version="0" /></config>',@phone_num)
		headers = {
		  'Content-Type' => 'application/x-www-form-urlencoded'
		}
		resp = http.post(uri.path, params, headers)
		return resp.body
	end
	
	def SSIAppSignIn(url)
		uri = URI.parse(url)
		path = uri.path + "?mobileno=" + @phone_num + "&pwd=" + @password
		http = Net::HTTP.new(uri.host,uri.port)
		http.use_ssl = true
		http.verify_mode = OpenSSL::SSL::VERIFY_NONE # turn off SSL warning
		resp, xml = http.get(path, nil)
		
		ok = "200"
		doc = REXML::Document.new(xml)
		doc.elements.each("//results") do|element|
           ok = element.attribute("status-code").value
       end
       if ok != "200"#421 verification picture?
            return self.SSIAppSignIn(url)
       end
		return xml
	end
    
	def get_fetion_num(xml)
		@uri = ""
		doc = REXML::Document.new(xml)
		doc.elements.each("//results/user") do |element|
		  @uri = element.attribute("uri").value
		end	
		return @uri.scan(/sip:([0-9]+)@/)[0][0]
	end
end

class SIPC
	def initialize(sipc_addr)
		uri = sipc_addr.split(":")
		@socket = TCPSocket.new(uri[0], uri[1].to_i)
	end

	# send SIP request
	def request(sip_request)
		puts sip_request
		@socket.write_nonblock(sip_request)
		#select read_nonblock and rescue is the key
		IO.select [@socket]
		res = ""
		begin
			while chunk = @socket.read_nonblock(4096)
				res = res + chunk
			end
		rescue
		        puts "Error: #{$!}"
		end
		puts res 
		return res 
	end
end

#for test
if __FILE__ == $0
    fetion = Fetion.new("13651368727","password")
    fetion.login()
    fetion.send_sms_to_self("test-ruby-fetion")
    #fetion.send_sms("mobileID","any sms")
end
   发表时间:2009-08-31  
呵呵,浏览这么多咋没人说句话呢?
0 请登录后投票
   发表时间:2009-09-02  
大部分直接翻译自可可熊的PyFetion,协议分析使用fiddle(需要先强迫飞信走HTTP代理)简单看了看。ruby代码写的少,难入法眼,见笑,嘿嘿。
0 请登录后投票
   发表时间:2009-09-02  
这段代码还要求gbk编码,utf8编码的时候,
sms_text = Iconv.iconv("UTF-8","GB2312",sms_text)[0],
这行代码要注释掉。
0 请登录后投票
   发表时间:2009-09-21  
鹤惊昆仑 写道
大部分直接翻译自可可熊的PyFetion,协议分析使用fiddle(需要先强迫飞信走HTTP代理)简单看了看。ruby代码写的少,难入法眼,见笑,嘿嘿。

协议分析可以使用 nathan 提供的工具 http://hi.baidu.com/nathan2007/blog/item/095a7d66088d2825aa184c20.html
0 请登录后投票
   发表时间:2010-02-25  
感觉ruby写出来的东西就是比python写的,看起来要顺眼一点!
最近我也想写一个练习一下呢,没想到就看到你写的了!



0 请登录后投票
   发表时间:2010-02-25  
我放到google code上了(http://fetion-ruby.googlecode.com/svn/trunk/Fetion.rb)
0 请登录后投票
   发表时间:2010-06-25  
那位大大能给小弟讲讲如何开始分析一款软件?
0 请登录后投票
   发表时间:2010-06-29   最后修改:2010-06-29
写PHP的路过,rails的语法竟如此奇怪!
0 请登录后投票
   发表时间:2010-07-26  
lz,今早运行程序时报错了。
  ./fetion.rb:103:in `SSIAppSignIn': private method `split' called for nil:NilClas
s (NoMethodError)
        from ./fetion.rb:32:in `init'
        from ./fetion.rb:17:in `initialize'


看了下程序,SSIAppSignIn方法中的
  resp, xml = http.get(path, nil) 

有异常了
Net:HTTPNotFound
不知是网络问题呢还是飞信进行了调整
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics