Chinaunix首页 | 论坛 | 博客
  • 博客访问: 126484
  • 博文数量: 36
  • 博客积分: 2174
  • 博客等级: 大尉
  • 技术积分: 437
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-28 21:50
文章存档

2012年(4)

2010年(5)

2009年(15)

2008年(12)

分类: Python/Ruby

2010-02-13 12:13:49

文件: CQQlib&pythonqq.rar
大小: 262KB
下载: 下载
源码共享之一:python dll调用,QQ消息群发,QQ自动化处理
 
       QQ上的好友一大堆,很长时间以来想要群发消息给所有用户,但是搜索过无数次,尚没有发现一款好用免费的软件来群发消息,而且还要提心木马在程序中安插又不敢随便安装使用各种群发器。其实好用的群发器肯定是存在的,并且有些甚至不用加为好友就能发送广告消息,这是我还没有掌握的技术。不过多谢一些朋友的督促和帮助,我找一种给自己QQ好友群发消息的开源技术,在此公布出来与大家分享。
    这项技术的来源是由于一次偶然的开发任务,那时一个好友提出需要QQ自动接收处理消息,我利用一个多月的时间找寻所有可能的实现方案,其中包括,
1JAVA QQ,我下载QQJAVA开源版本,其中最容易找到的是LUMAQQ,但是已不能使用,停止了开发,但是我相信JAVAQQ肯定有一些还是能用的,因为手机版的JAVA支持不会停止,
2NET平台的QQ。我随后找一份开源的项目,LUMAQQ.NET是针对java lumaQQ转换到C#代码的。后来也是故障连连。
3、针对标准QQ程序的窗口监控,可能是就是传说中的钩子,由于以我的水平发现QQ这种隐藏较深的窗口消息事件,并处理这些事件难度太大,在我SPY++定位一个消息窗口失败后放弃了,后来我见到过其它人用这种方式截取QQ消息,当其我只是用模拟鼠标键盘的形式,群发了一堆消息就停止研究了。
4WEBQQ,后来当我无计可施,打开了WEBQQ正在测试的页面,打算从WEB页面的跳动中的消息图标中找到突破口,当然这要用,.net来内嵌一个IE控件.并对DOCUMENT对象进行检索并做出处理.后来我才知道,WXPYTHON也能做这方面的工作.我不知道这个方式发展下去会怎么样,但这个时候光明出现了.当我和QQ机器人研究群中的一个程序员讨论WEBQQ机器人的问题时,他提到WEBQQ已经有人实现了啊.名叫myqq并给了我一下地址,我十分感谢他的提供,这是这篇文章所以后续内空的必要前提 ,所以我们必须记住并感谢的还有MYQQ的作者,09年的一个高三学生. 可是那我对他所说的WEB实现尚有疑问,那是需要QQWEB服务器的啊, 可是当我下载回他所说的源码及可运行程序的时候.我按说明运行了他的WEB服务,并打了网页,原来这是一个处理WEB请求的本地DLL控件.并在本地打开了一个服务端口.界面HTML+JAVASCRITP实现的. 我登录成功后心里充满了欢喜,并且带着很多的哀愁.那是因为C语言真难.
 
   以上是几种偿试带着未了的愿望迎来了最终本文的重点.这里有两种核心方案分别是
1C语言包装QQ协议,这是本文的重点,当我研究myqq 的代码及说明时我发现了这样的提示,WEB界面的QQ只是控制台QQ的一个包装,WEBQQ经常自己退出,而且不能登录,我当时还在想通过对JAVASCRIPT代码的更改达到QQ消息群发及消息到达回调函数的实现. 这样我无路可走了. 必须研究C语言的控制台代码,因为当时控制台的QQ相当稳定了.这样我操起十分不熟练的C,安装了一个蹩脚的C开发工具,DEV C++4.9. 他的蹩脚部分是因为他不能显不中文字符,即使是UNICODE编码的.这个问题只到现在困扰着我. 但在当时还有另一个问题,根本不能通过编译. 事情往往来得十分凑巧.第一次使用这个工具的我,居然很快又发现了需要进行一些必要的编译参数设置,其中关键的是,PGTHREAD这个参数,还有对,LIB库文件路径的引用.当我把所有这些做好后. 生成了第一个可以运行的C程序这是在我十年来生成的第一个CEXE. 剩下的找编所有网站,把一段用C来请求WEB的代码写进来, 因为原来的代码中有一段取得QQ验证图片的代码,可是不能直接使用,但是为我节省了很多的时间.因为同样的SOCKET的调用.这一组代码为我带来了几K的收入,也是我从没预料的结果.因为我十分想找到myqq的作者分享这一点点收入.但是他还在专心考试,后来才有他考试结束的结果.当然这点收入太微不足道,始终没有跟他联系并提示一方面的信息,这成为我自己的小猫腻.
2、最后本文的所要分享的PYTHONQQ,由于python的易用性和跨平台性,多年前我接触了一个PYTHONQQ在这次开发过程中我又找到了它,但是这个项目和多年前基本状态没变,而QQ的协议已经千变万化了,我想myqq的成功运行就在于作者协议分析与实现的超强能力.我一直以来想把myqqpython的调用相结合.方式一通过python调用C的函数,比如登录和消息发送,方式二,通过C语言调用pythonhttplib然而这两种方式,相当难处理.在数年前我偶然的兴趣,曾实现过,python调用一个自已生成的C语言弄DLL.以提高图片分析对比的效率.这让我有机会凭直觉发现了一个myqq 作者安排的小窍门.作者在源码中安置了一个libqq.c其中生成文件中也有一个,libqq.dll,仿佛这就是那种公布出来的DLL接口,而python调用它应该不成问题.在我所有的QQ相当工作勉强提交完成后,我操起稍显熟悉的DEV C++.用默认的模版生成了一个,DLL hellworld 并且用,pythonctype去访问它.很顺利,我随后战战兢兢的打开myqq的源本,引入一个空白的DEV C++ DLL项目,通过LIB添加,生成参数的设置.我勇敢的把它进行再生成,当然出现了一些小错误还有异常,我顺着这棵向上延伸的青藤,处理了它上面生成的小虫们.这样我的项目,完全地成功的,在目的地址上产生了一个,显示更新日期为当前时间的, mydlldym.dll这是我自己起的名字.这样我的第一步完成了.
虽然如此我怀着对它能正常入驻python空间的怀疑,这种怀疑是因为myqq运行空间的不确定性,因为它的内部的处理可能并不合乎,python ctype的要求,有一些接口或参数类型可能会被挡在C语言的门外,python望而兴叹,这是我所准备的,我只是在玩一个不知道结果的游戏.因为我对它们两个的交谈基本是门处汉.事情没有想像的糟糕,myqq的作者功夫了得,C的接口安排的有如天衣.登录和访问都没有问题.我的胆子放大了.回调函数是我要处理的关键. 我想要把python的回调函数指针传递给C的接口,这样我的工作就完成了.而在这以前我只知道,C的函数只是指针,python的函数也只是一个空间的启始位置. 这次我要依靠Ctype,我勇敢的碰一次运气吧. 我在DEV C++中笨捉地定义了接收接口,它的参数是*func类型,我的说法不够规范,但还能用并且不远处的时间点又一次给了我次一级的惊喜.因为高一级的惊喜是刚才的步骤所完成的python可以调用这个毛头小子的,libqq.原来pythonC加在一起可能完成这项了不以的任务,其中有劳了ctype来搭起通信的桥梁. 我想其它的语言也是这可以使用libqq,毕竟它的作者并没有预期python 会来访问它.所以这个跨语言的接入,C的发展过程中已经按排好,并且肯定有相应的文档来规范这种行为,只是我当时并没有接触过官方说明.我感觉这些说明肯定会让人头大,而且如果我看过了也就只能剩下按部就班的验证了.而不会带给我什么喜悦之类的感触了.
至此我的python c 语言的dll正式会面并且贡献出来一个消息群发的免费的程序实现.相信对python爱好者是个好事.也会较有兴趣继续偿试的.谢谢大家观看,更谢谢那些编程资源的提供者们.
 
 
 
下面是libqq.c的源码,也就是调用之接口.开发工具,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也就是主程序 开发环境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 )

"
""


阅读(2436) | 评论(5) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-06-01 22:27:13

对,这个QQ确识强大,我把它的源码放在,Eclipse CDT下编译了一下。想做成个多开的,带界面的,防掉线的。但一直没时间做这个大改动。但我想,3GQQ是比较持久和稳定可靠的。因为这个怕协议更改。

still_linux2010-05-07 07:57:10

看了好像又更新了呵.这个机器人其实改改还是很稳定的,如果有机器挂VPS上就更好了.我最近研究了很多版本的机器人了. 起点就是这里. 还有其他种类的.比如WEB QQ机器人,可以去im.qq.com/webqq 提取消息,也可以用3gqq做机器人.后者比较强大. 这种基于协议的QQ反应是最快的.相对也比较稳定.

still_linux2010-03-01 20:17:47

非常好的东西,整个项目文件都有了,很好的进行二次开发,我最近沉醉于这个.

chinaunix网友2010-02-28 10:40:31

环境设置. 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++设置参数的几个地方,最终都

still_linux2010-02-27 14:57:56

不会没人顶吧,我对这个非常有兴趣. 我也想实现提取QQ消息再处理,不过我看不懂你的文章啊.我看到发送消息的函数是在那个libqq.c里了.不过我看程序里并没有调用的地方啊.