基于C語言Dll調用的pythonqq的一個應用拜年消息群發(fā)器的開發(fā)
文件: CQQlib&pythonqq.rar
大小: 262KB
下載:
下載源碼共享之一:python dll調用,QQ消息群發(fā),QQ自動化處理
QQ上的好友一大堆,很長時間以來想要群發(fā)消息給所有用戶,但是搜索過無數次,尚沒有發(fā)現一款好用免費的軟件來群發(fā)消息,而且還要提心木馬在程序中安插又不敢隨便安裝使用各種群發(fā)器。其實好用的群發(fā)器肯定是存在的,并且有些甚至不用加為好友就能發(fā)送廣告消息,這是我還沒有掌握的技術。不過多謝一些朋友的督促和幫助,我找一種給自己QQ好友群發(fā)消息的開源技術,在此公布出來與大家分享。
這項技術的來源是由于一次偶然的開發(fā)任務,那時一個好友提出需要QQ自動接收處理消息,我利用一個多月的時間找尋所有可能的實現方案,其中包括,
1、JAVA QQ,我下載QQ的JAVA開源版本,其中最容易找到的是LUMAQQ,但是已不能使用,停止了開發(fā),但是我相信JAVA的QQ肯定有一些還是能用的,因為手機版的JAVA支持不會停止,
2、NET平臺的QQ。我隨后找一份開源的項目,LUMAQQ.NET是針對java lumaQQ轉換到C#代碼的。后來也是故障連連。
3、針對標準QQ程序的窗口監(jiān)控,可能是就是傳說中的鉤子,由于以我的水平發(fā)現QQ這種隱藏較深的窗口消息事件,并處理這些事件難度太大,在我SPY++定位一個消息窗口失敗后放棄了,后來我見到過其它人用這種方式截取QQ消息,當其我只是用模擬鼠標鍵盤的形式,群發(fā)了一堆消息就停止研究了。
4、WEBQQ,后來當我無計可施,打開了WEBQQ正在測試的頁面,打算從WEB頁面的跳動中的消息圖標中找到突破口,當然這要用,.net來內嵌一個IE控件.并對DOCUMENT對象進行檢索并做出處理.后來我才知道,WXPYTHON也能做這方面的工作.我不知道這個方式發(fā)展下去會怎么樣,但這個時候光明出現了.當我和QQ機器人研究群中的一個程序員討論WEBQQ機器人的問題時,他提到WEBQQ已經有人實現了啊.名叫myqq并給了我一下地址,我十分感謝他的提供,這是這篇文章所以后續(xù)內空的必要前提 ,所以我們必須記住并感謝的還有MYQQ的作者,09年的一個高三學生. 可是那我對他所說的WEB實現尚有疑問,那是需要QQ的WEB服務器的啊, 可是當我下載回他所說的源碼及可運行程序的時候.我按說明運行了他的WEB服務,并打了網頁,原來這是一個處理WEB請求的本地DLL控件.并在本地打開了一個服務端口.界面HTML+JAVASCRITP實現的. 我登錄成功后心里充滿了歡喜,并且?guī)е芏嗟陌С?那是因為C語言真難.
以上是幾種償試帶著未了的愿望迎來了最終本文的重點.這里有兩種核心方案分別是
1、C語言包裝QQ協(xié)議,這是本文的重點,當我研究myqq 的代碼及說明時我發(fā)現了這樣的提示,WEB界面的QQ只是控制臺QQ的一個包裝,WEBQQ經常自己退出,而且不能登錄,我當時還在想通過對JAVASCRIPT代碼的更改達到QQ消息群發(fā)及消息到達回調函數的實現. 這樣我無路可走了. 必須研究C語言的控制臺代碼,因為當時控制臺的QQ相當穩(wěn)定了.這樣我操起十分不熟練的C,安裝了一個蹩腳的C開發(fā)工具,DEV C++4.9. 他的蹩腳部分是因為他不能顯不中文字符,即使是UNICODE編碼的.這個問題只到現在困擾著我. 但在當時還有另一個問題,根本不能通過編譯. 事情往往來得十分湊巧.第一次使用這個工具的我,居然很快又發(fā)現了需要進行一些必要的編譯參數設置,其中關鍵的是,PGTHREAD這個參數,還有對,LIB庫文件路徑的引用.當我把所有這些做好后. 生成了第一個可以運行的C程序這是在我十年來生成的第一個C的EXE. 剩下的找編所有網站,把一段用C來請求WEB的代碼寫進來, 因為原來的代碼中有一段取得QQ驗證圖片的代碼,可是不能直接使用,但是為我節(jié)省了很多的時間.因為同樣的SOCKET的調用.這一組代碼為我?guī)砹藥譑的收入,也是我從沒預料的結果.因為我十分想找到myqq的作者分享這一點點收入.但是他還在專心考試,后來才有他考試結束的結果.當然這點收入太微不足道,始終沒有跟他聯(lián)系并提示一方面的信息,這成為我自己的小貓膩.
2、最后本文的所要分享的PYTHON的QQ,由于python的易用性和跨平臺性,多年前我接觸了一個PYTHON的QQ在這次開發(fā)過程中我又找到了它,但是這個項目和多年前基本狀態(tài)沒變,而QQ的協(xié)議已經千變萬化了,我想myqq的成功運行就在于作者協(xié)議分析與實現的超強能力.我一直以來想把myqq與python的調用相結合.方式一通過python調用C的函數,比如登錄和消息發(fā)送,方式二,通過C語言調用python的httplib然而這兩種方式,相當難處理.在數年前我偶然的興趣,曾實現過,python調用一個自已生成的C語言弄DLL.以提高圖片分析對比的效率.這讓我有機會憑直覺發(fā)現了一個myqq 作者安排的小竅門.作者在源碼中安置了一個libqq.c其中生成文件中也有一個,libqq.dll,仿佛這就是那種公布出來的DLL接口,而python調用它應該不成問題.在我所有的QQ相當工作勉強提交完成后,我操起稍顯熟悉的DEV C++.用默認的模版生成了一個,DLL hellworld 并且用,python的ctype去訪問它.很順利,我隨后戰(zhàn)戰(zhàn)兢兢的打開myqq的源本,引入一個空白的DEV C++ DLL項目,通過LIB添加,生成參數的設置.我勇敢的把它進行再生成,當然出現了一些小錯誤還有異常,我順著這棵向上延伸的青藤,處理了它上面生成的小蟲們.這樣我的項目,完全地成功的,在目的地址上產生了一個,顯示更新日期為當前時間的, mydlldym.dll這是我自己起的名字.這樣我的第一步完成了.
雖然如此我懷著對它能正常入駐python空間的懷疑,這種懷疑是因為myqq運行空間的不確定性,因為它的內部的處理可能并不合乎,python ctype的要求,有一些接口或參數類型可能會被擋在C語言的門外,讓python望而興嘆,這是我所準備的,我只是在玩一個不知道結果的游戲.因為我對它們兩個的交談基本是門處漢.事情沒有想像的糟糕,myqq的作者功夫了得,C的接口安排的有如天衣.登錄和訪問都沒有問題.我的膽子放大了.回調函數是我要處理的關鍵. 我想要把python的回調函數指針傳遞給C的接口,這樣我的工作就完成了.而在這以前我只知道,C的函數只是指針,而python的函數也只是一個空間的啟始位置. 這次我要依靠Ctype了,我勇敢的碰一次運氣吧. 我在DEV C++中笨捉地定義了接收接口,它的參數是*func類型,我的說法不夠規(guī)范,但還能用并且不遠處的時間點又一次給了我次一級的驚喜.因為高一級的驚喜是剛才的步驟所完成的python可以調用這個毛頭小子的,libqq.原來python和C加在一起可能完成這項了不以的任務,其中有勞了ctype來搭起通信的橋梁. 我想其它的語言也是這可以使用libqq的,畢竟它的作者并沒有預期python 會來訪問它.所以這個跨語言的接入,在C的發(fā)展過程中已經按排好,并且肯定有相應的文檔來規(guī)范這種行為,只是我當時并沒有接觸過官方說明.我感覺這些說明肯定會讓人頭大,而且如果我看過了也就只能剩下按部就班的驗證了.而不會帶給我什么喜悅之類的感觸了.
至此我的python 和c 語言的dll正式會面并且貢獻出來一個消息群發(fā)的免費的程序實現.相信對python愛好者是個好事.也會較有興趣繼續(xù)償試的.謝謝大家觀看,更謝謝那些編程資源的提供者們.
下面是libqq.c的源碼,也就是調用之接口.開發(fā)工具,dev C++4.9 ,win XP
/*
* libqq.c
*
* LibQQ Program
*
* Copyright (C) 2009 Huang Guan
*
* 2009-5-6 23:23:51 Created.
*
* Description: This file mainly includes the functions about
*
*/
#define LIBQQLIB
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#ifdef __WIN32__
#include <winsock.h>
#include <wininet.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
#include "debug.h"
#include "memory.h"
#include "qqclient.h"
#include "libqq.h"
#include "loop.h"
#include "buddy.h"
#include "qun.h"
#include "group.h"
#include "config.h"
#include "qqsocket.h"
#include "utf8.h"
#define MAX_USERS 1000
static int if_init = 0;
int (*FunP)(char * msg);
static char* util_escape( char* str )
{
unsigned char* ch;
ch = (unsigned char*)str;
while( *ch )
{
if( *ch < 0x80 ) //ASCII??
{
if( !isprint(*ch) ) //if not printable??
*ch = ' '; //use space instead..
ch ++; //skip one
}else{
ch +=2; //skip two
}
}
return str;
}
static char* mode_string( int mode )
{
switch( mode ){
case QQ_ONLINE:
return "Online";
case QQ_AWAY:
return "Away";
case QQ_BUSY:
return "Busy";
case QQ_OFFLINE:
return "Offline";
case QQ_HIDDEN:
return "Hidden";
case QQ_KILLME:
return "Kill Me";
case QQ_QUIET:
return "Quiet";
}
return "Unknown";
}
static void* login_ex( void* data )
{
qqclient* qq = (qqclient*) data;
pthread_detach(pthread_self());
DBG("login: %u", qq->number );
qqclient_login( qq );
return NULL;
}
EXPORT void libqq_setcallbackfunc(int (*pyFunP)(char * msg))
{
FunP=pyFunP;
FunP("set qq callback ok \n");
}
EXPORT int libqq_getstatus( qqclient* qq,qqstatus * pystatus)
{ pystatus->proxy_server_ip=qq->proxy_server_ip;
pystatus->number=qq->number;
pystatus->proxy_server_port=qq->proxy_server_port;
pystatus->last_login_time=qq->last_login_time;
pystatus->process=qq->process;
return 0;
}
EXPORT int libqq_getfriends(qqclient* qq, char* buf, int size, char online)
{
int i, len = 0;
//avoid editing the array
buf[0] = 0;
pthread_mutex_lock( &qq->buddy_list.mutex );
int ln = 1;
for( i=0; i<qq->buddy_list.count; i++ )
{
qqbuddy * b = (qqbuddy*)qq->buddy_list.items[i];
if( online && b->status == QQ_OFFLINE ) continue;
if( *b->alias ){
len = sprintf( buf, "%s%-8d%-16d%-16s%-16.64s\n", buf, ln ++, b->number,
mode_string( (int) b->status ), util_escape( b->alias ) );
}else{
len = sprintf( buf, "%s%-8d%-16d%-16s%-16.64s\n", buf, ln ++, b->number,
mode_string( (int) b->status ), util_escape( b->nickname ) );
}
if( len + 80 > size ) break;
}
pthread_mutex_unlock( &qq->buddy_list.mutex );
return len;
}
EXPORT void libqq_init()
{
config_init();
if_init = 1;
qqsocket_init();
FunP=NULL;
}
EXPORT void libqq_cleanup()
{
config_end();
qqsocket_end();
}
EXPORT qqclient* libqq_create( uint number, char* pass )
{
if (FunP!=NULL)
{
(*FunP)("create qq --test call back ok\n");
}
qqclient* qq;
if( !if_init )
libqq_init();
NEW( qq, sizeof(qqclient) );
if( !qq ){
DEL( qq );
return NULL;
}
qqclient_create( qq, number, pass );
qq->auto_accept = 1; //temporarily do this
return qq;
}
EXPORT int libqq_login( qqclient* qq )
{
int ret;
pthread_t ptr;
ret = pthread_create( &ptr, NULL, login_ex, (void*)qq );
if( ret != 0 ){
DBG("thread creation failed. ret=%d", ret );
}
return ret;
}
EXPORT int libqq_logout( qqclient* qq )
{
DBG("logout %u", qq->number );
qqclient_logout( qq );
return 0;
}
EXPORT int libqq_detach( qqclient* qq )
{
DBG("detach %u", qq->number );
qqclient_detach( qq );
return 0;
}
EXPORT int libqq_getmessage( qqclient* qq, char* buf, int size, int wait )
{
int ret;
ret = qqclient_get_event( qq, buf, size, wait );
utf8_to_gb( buf, buf, size );
return ret;
}
EXPORT int libqq_sendmessage( qqclient* qq, uint to, char* buf, char qun_msg )
{
char* tmp;
int len = strlen(buf);
if( len<1 ) return -2;
NEW( tmp, len*2 );
gb_to_utf8( buf, tmp, len*2-1 );
if( qun_msg ){
qqqun* q = qun_get_by_ext( qq, to );
if( q )
qun_send_message( qq, q->number, tmp );
}else{
buddy_send_message( qq, to, tmp );
}
DEL( tmp );
return 0;
}
EXPORT void libqq_updatelist( qqclient* qq )
{
buddy_update_list( qq );
group_update_list( qq );
}
// 090622 by Huang Guan. change the type of code from uint to const char*
EXPORT void libqq_verify( qqclient* qq, const char* code )
{
if( code ){
qqclient_verify( qq, *(uint*)code );
}else{
qqclient_verify( qq, 0x00000000 ); //this will make the server change another png
}
}
EXPORT void libqq_remove( qqclient* qq )
{
qqclient_cleanup( qq ); //will call qqclient_logout if necessary
DEL( qq );
}
EXPORT void libqq_status( qqclient* qq, int st, uchar has_camera )
{
qq->has_camera = has_camera;
qqclient_change_status( qq, st );
}
EXPORT void libqq_addbuddy( qqclient* qq, uint uid, char* msg )
{
qqclient_add( qq, uid, msg );
}
EXPORT void libqq_delbuddy( qqclient* qq, uint uid )
{
qqclient_del( qq, uid );
}
void buddy_msg_callback ( qqclient* qq, uint uid, time_t t, char* msg )
{
char timestr[24];
struct tm * timeinfo;
char* str;
int len;
timeinfo = localtime ( &t );
strftime( timestr, 24, "%Y-%m-%d %H:%M:%S", timeinfo );
len = strlen( msg );
NEW( str, len+64 );
if( uid == 10000 ){
sprintf( str, "broadcast^$System^$%s", msg );
}else{
sprintf( str, "buddymsg^$%u^$%s^$%s", uid, timestr, msg );
}
if (FunP!=NULL)
{
(*FunP)(str);
}
// printf("%s",str);
qqclient_put_message( qq, str );
}
void qun_msg_callback ( qqclient* qq, uint uid, uint int_uid,
time_t t, char* msg )
{
qqqun* q;
char timestr[24];
struct tm * timeinfo;
char* str;
int len;
timeinfo = localtime ( &t );
strftime( timestr, 24, "%Y-%m-%d %H:%M:%S", timeinfo );
q = qun_get( qq, int_uid, 1 );
if( !q ){
DBG("error: q=NULL");
return;
}
len = strlen( msg );
NEW( str, len+64 );
sprintf( str, "clustermsg^$%u^$%u^$%s^$%s", q->ext_number, uid, timestr, msg );
qqclient_put_message( qq, str );
}
EXPORT uint libqq_refresh( qqclient* qq )
{
char event[16];
qqclient_set_process( qq, qq->process );
sprintf( event, "status^$%d", qq->mode );
qqclient_put_event( qq, event );
buddy_put_event( qq );
group_put_event( qq );
qun_put_event( qq );
qqclient_set_process( qq, qq->process );
return qq->number;
}
EXPORT void libqq_getqunname( qqclient* qq, uint ext_id, char* buf )
{
qqqun* q = qun_get_by_ext( qq, ext_id );
if( q ){
strncpy( buf, q->name, 15 );
}else{
if( ext_id != 0 ){
sprintf( buf, "%u" , ext_id );
}
}
}
EXPORT void libqq_getqunmembername( qqclient* qq, uint ext_id, uint uid, char* buf )
{
qqqun* q = qun_get_by_ext( qq, ext_id );
if( q ){
qunmember* m = qun_member_get( qq, q, uid, 0 );
if( m ){
strncpy( buf, m->nickname, 15 );
return;
}
}
if( uid != 0 ){
sprintf( buf, "%u" , uid );
}
}
EXPORT void libqq_getbuddyname( qqclient* qq, uint uid, char* buf )
{
qqbuddy* b = buddy_get( qq, uid, 0 );
if( b ){
strncpy( buf, b->nickname, 15 );
}else{
if( uid != 0 ){
sprintf( buf, "%u" , uid );
}
}
}
// 090706 by HG
EXPORT void libqq_sethttpproxy( struct qqclient* qq, char* ip, ushort port )
{
struct sockaddr_in addr;
qq->network = PROXY_HTTP;
netaddr_set( ip, &addr );
qq->proxy_server_ip = ntohl( addr.sin_addr.s_addr );
qq->proxy_server_port = port;
}
EXPORT void libqq_getextrainfo( struct qqclient* qq, uint uid )
{
prot_buddy_get_extra_info( qq, uid );
}
下面是pythonqq.py也就是主程序 開發(fā)環(huán)境pytho2.6 ctype1.1 win XP ,
# -*- coding: cp936 -*-
from ctypes import *
import sys,os,getpass
import time
import autoreload
import cPickle
qq=None
m=None
sysct = sys.getfilesystemencoding()
def callback(abc):
global m
s="%s"%abc
ct = sys.getfilesystemencoding()
msg=s.decode('UTF-8').encode(sysct)
if (msg.find("$verf")==0 and qq):
m=msg
if msg.endswith("back "):
m=msg
print msg
return 0
CMPFUNC = CFUNCTYPE(c_int,c_char_p)
_callback = CMPFUNC(callback)
process=('P_INIT','P_LOGGING','P_VERIFYING','P_LOGIN','P_ERROR','P_DENIED','P_WRONGPASS','P_BUSY',)
class pyStatus(Structure):
_fields_ = [
('number',c_uint),
('proxy_server_ip',c_uint),
('proxy_server_port',c_ushort),
('last_login_time',c_uint),
('process',c_int),
]
class myqq():
def __init__(self,uid,pw):
self.xpuser=getpass.getuser()
self.conffile=self.xpuser+"/data.ini"
self.load()
self.dll = WinDLL("mydlldym.dll")
self.dll.libqq_init()
self.qq= self.dll.libqq_create(uid,pw)
self.dll.libqq_getstatus.argtypes = [c_int,POINTER(pyStatus)]
self.setcallback(_callback)
self.dll.libqq_login(self.qq)
self.status=pyStatus()
self.getstatus()
#buf='\0'*1024
#self.dll.libqq_getmessage(self.qq,buf,len(buf),-0)
def getstatus(self):
self.dll.libqq_getstatus(self.qq,byref(self.status))
return self.status
def setcallback(self,cbk):
self.dll.libqq_setcallbackfunc(cbk)
def sendmsg(self,uid,msg):
self.dll.libqq_sendmessage(self.qq,uid,msg,False )
def verify(self ,vstr):
self.dll.libqq_verify(self.qq,vstr)
def getfriends(self,online=0):
global sysct
buf='\0\0'*1024*10
self.dll.libqq_getfriends(self.qq,buf,len(buf),online)
self.buf=buf[:buf.find('\0')-1]
self.buf=self.buf.strip().decode('UTF-8').encode(sysct)
del buf
self.friends=[one.split() for one in self.buf.split('\n') ]
return len(self.friends)
def sendtoall(self,msg="天氣不錯。",wait=2,refresh=1):
if refresh:
self.getfriends()
self.havasend=[]
else :
self.load()
for i in qq.friends:
print i
if len(i)==4 and (i not in self.havasend):
print "begin send!"
self.sendmsg(int(i[1]),"@%s~:%s"%(i[3],msg))
self.havasend.append(i)
self.save()
print "have send!"
time.sleep(wait)
def close(self):
self.dll.libqq_logout(self.qq)
self.dll.libqq_cleanup()
self.save()
def load(self):
if os.path.exists(self.conffile):
self.friends,self.havasend=cPickle.load(open(self.conffile,'r'))
else:
self.friends,self.havasend=None,None
def save(self):
if not os.path.exists(self.xpuser):
os.mkdir(self.xpuser)
cPickle.dump((self.friends,self.havasend),open(self.conffile,'w'))
#qq=myqq(739825735,"w123456")
if __name__=='__main__':
qq=myqq(33322222,"pwassword")
#qq.setcallback(_callback)
import time
while True :
if m:
print m ,m.endswith("back "),m[-4:]
if m.find('$verf:')==0:
a=str(raw_input(m+':'))
qq.verify(a)
m=None
elif m.endswith("back "):
import callback
callback.set(qq)
print "change success"
m=None
time.sleep(1)
p= qq.getstatus().process
print '>>',
if p>=0 and p<len(process):
print process[p]
if p==3:
qq.sendtoall(" 電影《本杰明。巴頓》 每天一個晚安,-來自我的機器。")
break ;
# into=str(raw_input('>>'))
"""
for i in range(2):
print 'dfd'
dll.libqq_sendmessage(qq,1154628187,"我錯了,"*((i+1)%6),False )
"""
發(fā)表于: 2010-02-13 ,修改于: 2010-02-13 12:13,已瀏覽208次,有評論5條
推薦投訴網友評論
內容: 不會沒人頂吧,我對這個非常有興趣.
我也想實現提取QQ消息再處理,不過我看不懂你的文章啊.我看到發(fā)送消息的函數是在那個libqq.c里了.不過我看程序里并沒有調用的地方啊.
still_linux 評論于:2010-02-27 14:57:56 (221.207.251.★)
內容: 環(huán)境設置. DEV C++ 4.9
tools-->compiler options -- Comiler 頁標簽下,
打勾v Add following command s when compiler ..
-c -Wall -s -Werror
打勾 v Add these commands to the linker command line
-lpthreadGC2 -lws2_32
剩下的在工程屬性中,工程文件中包括了.
在 project opthins -->parameters 頁標簽下.
Compiler 項
-DBUILDING_DLL=1
Linker 項.
--no-export-all-symbols --add-stdcall-alias
lib/pthread/libpthreadGC2.a
在driectories頁下.:
library Directries 添加, pthread目錄.
include Directories添加,pthread目錄.
這是DEV C++設置參數的幾個地方,最終都會在,
生成命令中體現出來.
窗口下方的,
Complie log 中有所顯示.
其它的就是,GCC的設置.我用的是默認的,
本站網友評論于:2010-02-28 10:40:31 (60.28.180.★)
內容: 非常好的東西,整個項目文件都有了,很好的進行二次開發(fā),我最近沉醉于這個.
still_linux 評論于:2010-03-01 20:17:47 (221.208.81.★)
內容: 看了好像又更新了呵.這個機器人其實改改還是很穩(wěn)定的,如果有機器掛VPS上就更好了.我最近研究了很多版本的機器人了.
起點就是這里.
還有其他種類的.比如WEB QQ機器人,可以去im.qq.com/webqq 提取消息,也可以用3gqq做機器人.后者比較強大.
這種基于協(xié)議的QQ反應是最快的.相對也比較穩(wěn)定.
still_linux 評論于:2010-05-07 07:57:10 (122.159.18.★)
內容: 對,這個QQ確識強大,我把它的源碼放在,Eclipse CDT下編譯了一下。想做成個多開的,帶界面的,防掉線的。但一直沒時間做這個大改動。但我想,3GQQ是比較持久和穩(wěn)定可靠的。因為這個怕協(xié)議更改。
本站網友評論于:2010-06-01 22:27:13 (118.249.238.★)