(1条消息)微信Hook实战记录2:动手实现恶意dll内存插入器_C/C++_github_39463644的博客-CSDN博客

mikel阅读(929)

来源: (1条消息)微信Hook实战记录2:动手实现恶意dll内存插入器_C/C++_github_39463644的博客-CSDN博客

Hook任何软件,整体思路都是通过内存调试软件对软件运行时内存进行断点调试,找到想要hook的内存地址,转为可以通过程序主dll可以获得的相对地址,然后再此处插入自己的恶意汇编代码,如果代码比较复杂,还需要编写寄存器保护逻辑,避免自己的恶意代码修改了寄存器中的值后,程序正常的逻辑无法运行。

前言
在上一篇文章「微信Hook实战记录 1:找到个人信息」中,介绍了如何使用OD与CE来找到微信中存放个人信息的内存位置,本篇文章尝试通过C++编写一个简单的内存注入工具,将我们自己的恶意dll注入到微信的内存中。

本文记录了大量细节,完全可以模仿复现相同的效果。

内存注入工具的编写
打开 Visual Studio 2019(下载的时候勾选 C++ 桌面支持,6.9G左右),选择创建新项目

选择创建 Windows 桌面向导

然后创建就可以了,这里创面项目名为 Project1

创建完后,VS 会为我们创建一个默认的界面,我们在 解决方案 -> 源文件 中找到 Project1.cpp,这个文件就是我们要写逻辑的主要界面

将其中多余的代码删除,就留下如下代码

// Project1.cpp : 定义应用程序的入口点。
//

#include “framework.h”
#include “Project1.h”

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
wWinMain 是界面程序的入口,所有不必删除

接着将 解决方案 –> 资源文件 中的内容删除,这是 VS 默认创建好的,删除后,自己新建一个资源文件

选择 Dialog,即弹出框

调整一下创建出的按钮位置以及整体大小

要设置按钮与整体的属性,如按钮要设置其内容与ID,而整体Dialog也要设置一下其ID,可以右键点击 –> 属性

其他控制以相同的方式去设置,设置后后,就可以写代码了

首先修改一下 Project1.h 中的内容,如下

#pragma once

#include “resource1.h”
1
2
3
具体内容要看你VS为你生成了什么,前面我们通过图像界面操作后,VS会生成的相应的头文件,这里我生成了 resource1.h 的头文件,其内容如下:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Project1.rc 使用
//
#define ID_MAIN 101
#define UN_DLL 1001
#define INJECT_DLL 1002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看出,其实就是我们设置的ID等内容

接着来修改一下 Project1.cpp, 其 wWinMain 方法修改如下:

#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>

// 微信进程名
#define WECHAT_PROCESS_NAME “WeChat.exe”

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{

// hInstance 将句柄传递给弹窗
// 传入 Dialgo 主体ID
// DialogProc 回调函数,是一个指针,即该函数的地址
DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
因为创建的资源是 Dialog ,所以这里通过 DialogBox 来使用它,它在 Windows.h 的头文件中,如果你使用的 VS 2019,其 framework.h 文件就包含Windows.h了。

DialogBox的参数可以通过微软的文档来查看

void DialogBoxA(
hInstance,
lpTemplate,
hWndParent,
lpDialogFunc
);
1
2
3
4
5
6
hInstance,类型:HINSTANCE,包含对话框模板的模块句柄。如果此参数为 NULL,则使用当前可执行文件。
lpTemplate,类型:LPCTSTR,对话框模板。可以使用MAKEINTRESOURCE宏来创建此值。
hWndParent,类型:HWND,拥有该对话框的窗口的句柄。
lpDialogFunc,类型:DLGPROC,指向对话框过程的指针。

根据文档提示,完成代码

#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>

#define WECHAT_PROCESS_NAME “WeChat.exe”

// 注册函数,方便其他模块使用
INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
VOID InjectDll();

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{

DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}

INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (uMsg == WM_INITDIALOG) {
MessageBox(NULL, “首次加载”, “标题”, 0);
}
// 关闭界面操作
if (uMsg == WM_CLOSE) {
EndDialog(hwndDlg, 0);
}

// 所有的界面上的按钮事件都会走这个宏
if (uMsg == WM_COMMAND) {
if (wParam == INJECT_DLL) {
InjectDll(); //注入 Dll
}

if (wParam == UN_DLL) {

}
}

return FALSE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
接着来实现 InjectDll() 方法,完成注入 DLL 的功能,整体分 3 步走。

1.获取微信的PID,通过PID获得微信进程的句柄,从而拥有了权限
2.申请内存,内存大小为 DLL 路径大小
3.将 DLL 路径注入到微信进程中

DWORD ProcessNameFindPID(LPCSTR ProcessName)
{
// #include <TlHelp32.h>
// 获取进程快照
HANDLE ProcessAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);// 获取整个系统的进程快照
// 在快照中对比过 processname 进程名称
PROCESSENTRY32 processInfo = { 0 };
processInfo.dwSize = sizeof(PROCESSENTRY32);
do
{
// WeChat.exe
// th32ProcessID 进程ID, szExeFile进程名
if (strcmp(ProcessName, (char*)processInfo.szExeFile) == 0) {
return processInfo.th32ProcessID;
}
} while (Process32Next(ProcessAll, &processInfo));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上述方法,需要 windows 平台 C++ 基础。其实主要就是对 Windows.h 中方法的调用,接着完成如下效果

VOID InjectDll()
{
// 自己恶意dll的路径
CHAR pathStr[0x100] = { “D://GetWeChatInfo.dll” };
// 获取微信PID
DWORD PID = ProcessNameFindPID(WECHAT_PROCESS_NAME);
if (PID == 0) {
MessageBox(NULL, “没有找到微信进程或微信没有启动”, “错误”, 0);
return;
}
// 通过PID获取句柄 C++中句柄类型为 HANDLE
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (NULL == hProcess) {
MessageBox(NULL, “进程打开失败,可能权限不足或关闭了微信应用”, “错误”, 0);
return;
}

//申请内存,内存大小为我们要注入 DLL 的大小
LPVOID dllAdd = VirtualAllocEx(hProcess, NULL, sizeof(pathStr), MEM_COMMIT, PAGE_READWRITE);
if (NULL == dllAdd) {
MessageBox(NULL, “内存分配失败”, “错误”, 0);
return;
}

// 将 DLL 写入到对应的内存地址中
if(WriteProcessMemory(hProcess, dllAdd, pathStr, strlen(pathStr), NULL) == 0) {
MessageBox(NULL, “路径写入失败”, “错误”, 0);
return;
}

// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
完成后,直接编译运行,如果编译后的效果依旧是之前代码的效果,你可以 重新生成解决方案,然后再编译。

点击注入,获得注入的位置,通过 Cheat Engine 来查看该位置的内容

注入dll成功

注入成功后,还需要调用 Kernel32.dll 中的方法,调用该路径下的 dll 就完成 dll的注入了,代码如下:

// 省略显示前面的代码

// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);

//将地址写入后,通过 Kernel32.dll 调用该地址的 DLL 就完成了
HMODULE k32 = GetModuleHandle(“Kernel32.dll”);
LPVOID loadAdd = GetProcAddress(k32, “LoadLibraryA”);
// 远程执行我们的dll,通过注入的dll地址以及CreateRemoteThread方法让微信进程调用起我们的进程
HANDLE exec = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadAdd, dllAdd, 0, NULL);
if (exec == NULL) {
MessageBox(NULL, “远程注入失败”, “错误”, 0);
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
此时 DLL 就相当于在微信进程中了。

DLL中的函数就可以操作微信进程中的内存了,从而实现获取微信信息的目的。

如果没有调用其DLL,则需要注意一下 LoadLibraryA 方法,该方法会调用 ASCII的路径地址,如果你是unicode的话,则需要调用 LoadLibraryW 方法,如果不成功,可以尝试替换一下。

没有Windows平台开发C++的朋友看完上面代码可能会感到疑惑,为什么将恶意dll路径插入内存后,可以让微信进程自己去载入这个恶意dll呢?

简单解释一下,在windows中,每个进程在启动时都会载入Kernel32.dll,微信进程会载入Kernel32.dll,浏览器进程也会载入Kernel32.dll,而我们可以通过Kernel32.dll中的LoadLibraryA()方法或LoadLibraryW()方法去动态载入新的dll,需要解释一下,我们在自己的进程中获取LoadLibraryA()方法的内存位置,这个内存位置对windows中其他进程而言是相同的。

但关键点是通过微信进程去使用Kernel32.dll中的LoadLibraryA()方法载入新的dll,为了实现这个目的,就需要使用CreateRemoteThread()方法,该方法可以远程调用其他进程的中的方法,前提是要有该进程的句柄(即执行权限),而我们在一开始就获得了微信进程的执行权限。

总结一下整个流程:

1.将恶意dll路径插入到微信进程运行内存中
2.获取当前进程(我们自己的进程,而非微信进程)Kernel32.dll中LoadLibraryA()方法或LoadLibraryW()方法,因为微信进程也载入了Kernel32.dll,所以微信进程可以在相同的内存地址处找到LoadLibraryA()方法或LoadLibraryW()方法
3.通过CreateRemoteThread()方法,让微信进程去调用LoadLibraryA()方法载入恶意dll
结尾
本文并没有给出恶意dll的具体实现逻辑,后面将实现一个简单的dll,来获取微信的个人信息,其实基于上一篇文章,找到了个人信息的具体微信红藕,该dll要做的只是从中读取信息,仅此而已。

但恶意dll还可以实现很多复杂的功能,比如监控聊天信息、自动收红包、自动踢人等各种功能,这些功能在后面有机会再分享给大家。

如果本文对你有帮助,点击「在看」支持二两,下篇文章见。
————————————————
版权声明:本文为CSDN博主「二两hackpython」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/github_39463644/article/details/103451010

API Hook PC微信防多开 - Crack_Me - 博客园

mikel阅读(1614)

来源: API Hook PC微信防多开 – Crack_Me – 博客园

1.首先确定微信放多开是如何确定的,大多数程序的防多开都是使用互斥体来完成,所以就从创建互斥体的API开始下手(CreateMutexA/CreateMutexW)

image

可以看到创建互斥体的名称

image

一般程序在防多开zhong 都是判断判断互斥体的名称是否相同,所以我们做个实验来验证我们猜想的是否正确

(使用x32dbg直接修改内存效果一样)

image

修改值直接运行,并接着在打开一个微信,结果如下

image

,双开成果,说明我们的猜测是正确的,所以直接用apiHook,Hook CreateMutexW函数,修改chau纳入的第三个参数,就可以过掉微信防多开,dll代码如下

void HookApi()
{
//获取CreateMutexW函数地址
Addr = GetProcAddress(LoadLibraryA(“Kernel32.dll”), “CreateMutexW”);
//保存原始的指令
memcpy(OldOpcode, Addr, 5);
//计算跳转偏移
DWORD Offset = (DWORD)HookCreateMutexA – (DWORD)Addr – 5;

//组合新的Opcode
*(DWORD*)&JmpOpcode[1] = Offset;

}

HANDLE WINAPI HookCreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL                  bInitialOwner,
LPCSTR                lpName
)
{
HANDLE Handle = 0;
//调用CreateMutexW创建不同名称的互斥体
Handle = CreateMutexW(lpMutexAttributes, bInitialOwner, L”bbbbbbbb23333333″);
return Handle;

}

void EnableHook(BOOL Enable = TRUE)
{
DWORD OldProtect = 0;
//修改内存页属性,可读写
VirtualProtect(Addr, 5, PAGE_EXECUTE_READWRITE, &OldProtect);
//填充新的Opcode
memcpy(Addr,Enable ? JmpOpcode:OldOpcode, 5);
//还原目标地址所在分页的属性
VirtualProtect(Addr, 5, OldProtect, &OldProtect);
}

通过对微信pc hook实现微信助手 - 腾格里

mikel阅读(1390)

来源: 通过对微信pc hook实现微信助手 – 腾格里

本软件主要通过对pc端微信hook来实现的,微信版本2.6.8.52。
软件下载地址:
链接: https://pan.baidu.com/s/1hre17eW3vOO3O8FcnMulaQ
提取码: 2qih

软件实现功能:

  1. 支持爆粉
  2. 支持文本消息群发
  3. 支持自动收款
  4. 支持自动加好友
  5. 支持消息防撤回
  6. 支持消息回复接口

操作视频

软件图片:
baofeng.PNG

qunfa.PNG

autoreply.PNG

set.PNG

微信自动回复接口api:http://blog.yshizi.cn/115.html

联系方式
微信:znana2019
邮件:xuhang1108@126.com

C# PC版微信消息监听自动回复 - 小赫赫加油 - 博客园

mikel阅读(3362)

来源: C# PC版微信消息监听自动回复 – 小赫赫加油 – 博客园

最近有个微商客户需要搞个 个人微信监听群消息关键字并实现自动回复功能, 因为他有很多群  很多买家咨询的话 一个个回复太麻烦, 客户要求 比如群里有人发 关键字 产品1  则自动回复产品1的相关描述

 

首先设置关键字,将关键字和回复内容存到一个txt就行

 

然后就是微信消息监听(windows的消息  句柄不懂的自己 google):

上监听的关键代码 :

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/// 消息主要接受程序
       /// </summary>
       /// <param name="m"></param>
       protected override void DefWndProc(ref Message m)
       {
           switch (m.Msg)
           {
               case 74:    //74代表WM_COPYDATA                   
                   string message = "";
                   int f = (int)m.WParam;
                   try
                   {
                       COPYDATASTRUCT2 cds2 = (COPYDATASTRUCT2)m.GetLParam(typeof(COPYDATASTRUCT2));
                       message = Marshal.PtrToStringAnsi(cds2.lpData);
                   }
                   catch (Exception e)
                   {
                       LogHelper.WriteLog("解析微信消息报错", e);  //发文字带空格报错
                   }
                   switch (f)
                   {
                       //登录时解析用户信息
                       case 10003:
                           try
                           {
                               if (!string.IsNullOrEmpty(message))
                               {
                                   MessageHandler.Parse10003(message);
                               }
                               this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText("【" + MessageHandler.myUserInfo.UserName + "】登录成功!\r\n"); }));
                           }
                           catch (Exception e1)
                           {<br>                                LogHelper.WriteLog("解析微信登录用户信息错误", e1);
                           }
                           return;<br>
                       //文字,图片,视频,文件 解析
                       case 10012:
                           try
                           {
                               if (message.Contains("msgtyp:"))
                               {
                                   string[] datas = Regex.Split(message, "msgtyp:", RegexOptions.IgnoreCase);
                                   if (datas.Count() >= 2)
                                   {
                                       string msgtyp = datas[1].Split(',')[0];
                                       switch (msgtyp.Replace(" """))
                                       {
                                           case "1":  //文字
                                               if (message.Contains("[群-->]") && message.Contains("[消息内容-->]"))
                                               {
                                                   string word = "";      //发送内容
                                                   //取消息内容
                                                   string[] wordArr = Regex.Split(message, @"消息内容-->]", RegexOptions.IgnoreCase);
                                                   string[] aa = wordArr[1].Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries);
                                                   if (aa[0].StartsWith(" "))
                                                   {
                                                       word = aa[0].Substring(1);
                                                   }
                                                   else
                                                   {
                                                       word = aa[0];
                                                   }
                                                   string groupId = WxMessageHelper.GetChatRoom(message);
                                                   string groupName = WxMessageHelper.GetGroupName(message);
                                                   string nickName = WxMessageHelper.GetNickName(message);
                                                   string memberpuid = WxMessageHelper.GetWxId(message);
                                                   List<string> data = ReadFile();
                                                   foreach(var str in data)
                                                   {
                                                       string key = str.Split(';')[0];
                                                       string value = str.Split(';')[1];
                                                       if (word.Contains(key))
                                                       {
                                                           string[] sarr = Regex.Split(word, str, RegexOptions.IgnoreCase);
                                                           this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText("【" + nickName + "】在【" + groupName + "】发送了关键词消息:"+ sarr[0]);
                                                               richTextBox1.AppendTextColorful(key, Color.Red, false);
                                                               richTextBox1.AppendText(key + "\r\n");
                                                           }));
                                                           //调用回复
                                                           SendMessage(groupId + "||" + value, 20001);
                                                       }
                                                   }
                                               }
                                               break;
                                       }
                                   }
                               }
                           }
                           catch (Exception e2)
                           {
                               this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "10012 : 解析微信群信息错误\r\n\r\n"); }));
                           }
                           break;
                       default:
                           break;
                   }
                   //消息内容  cds.lpData
                   break;
               default:
                   base.DefWndProc(ref m);
                   break;
           }
       }

 

 

1
WParam=10003 时,监听的是微信登录的操作, 这时给前台窗体一个提示, 提示登录成功 如下图

 

登录时message 消息体的值  ||分割  第一个就是微信昵称,  wxid_ 开头的就是微信唯一标识 , 最后是登录用户的头像

 

 

 

然后用 另一个号往群里发消息 (当前登录用户和那个号必须在一个群 )

发送  : 产品1  这时观察message 的值

 

@chatroom 结尾的是 群的唯一标识id   群聊就是群名称,   发送人昵称也有 发送的消息内容也有, 拿正则取一下就完事。

 

1
word就是提取的发送内容, 然后判断如果发送的内容包含关键字 则调用SendMessage方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
       /// 发送消息
       /// </summary>
       /// <param name="message">消息内容</param>
       /// <param name="wParam">通讯号</param>
       /// <returns></returns>
       private int SendMessage(string message, int wParam)
       {
           byte[] sarr = System.Text.Encoding.Default.GetBytes(message);
           int len = sarr.Length;
           COPYDATASTRUCT cds;
           cds.dwData = (IntPtr)Convert.ToInt16(1);    //可以是任意值
           cds.cbData = len + 1;   //指定lpData内存区域的字节数
           cds.lpData = message;  //发送给目标窗口所在进程的数据
           return myapi.SendMessage(common.微信句柄, 74, wParam, ref cds);  //74代表WM_COPYDATA
       }

 

 

1
2
3
4
5
6
7
8
9
10
[StructLayout(LayoutKind.Sequential, Size=1)]
public struct myapi
{
    [DllImport("user32", EntryPoint="SetWindowLong")]
    public static extern int SetWindowLongA(int hwnd, int nIndex, int dwNewLong);
    [DllImport("User32.dll")]
    public static extern int SendMessage(int hwnd, int msg, int wParam, ref COPYDATASTRUCT IParam);
    [DllImport("User32.dll")]
    public static extern int FindWindow(string lpClassName, string lpWindowName);
}

 

来2个效果图

 

 

 

 

这是用客户微信号  在群里 发 产品1   则当前微信立马回复  前面设置的回复内容

 

博客经个人辛苦努力所得,如有转载会特别申明,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律

原文地址: https://www.cnblogs.com/alonglonga/p/11876300.html    小赫赫首发

有问题联系Q: 591811930

利用SqlDataSourceEnumerator获取局域网SQL主机 - K8哥哥 - 博客园

mikel阅读(882)

来源: 利用SqlDataSourceEnumerator获取局域网SQL主机 – K8哥哥 – 博客园

前言
SQL Server 允许应用程序在当前网络中查找 SQL Server 实例。 SQLDataSourceEnumerator 类向应用程序开发人员公开此信息,提供包含所有可见服务器的信息的 DataTable。 此返回的表包含网络上可用的服务器实例的列表,该列表与用户尝试创建新连接时提供的列表匹配,并展开包含连接属性上所有可用服务器的下拉列表。对话框。 显示的结果并非总是完整的。

SqlDataSourceEnumerator
命名空间: System.Data.Sql
程序集: System.Data.dll

提供了一种枚举本地网络内的所有可用 SQL Server 实例的机制。

通过方法调用返回的表包含以下列,所有列均包含 string 值:
列 描述
ServerName 服务器的名称。
InstanceName 服务器实例的名称。 如果服务器作为默认实例运行,则为空白。
IsClustered 指示服务器是否属于群集。
Version 服务器的版本。 例如:

-9.00 (SQL Server 2005)
-10.0. xx (SQL Server 2008)
-10.50 (SQL Server 2008 R2)
-11.0. xx (SQL Server 2012)

C#示例代码
.NET与PowerShell谁的兼容性和执行效率更好就不用我说了

using System.Data.Sql;

class Program
{
static void Main()
{
// Retrieve the enumerator instance and then the data.
SqlDataSourceEnumerator instance =
SqlDataSourceEnumerator.Instance;
System.Data.DataTable table = instance.GetDataSources();

// Display the contents of the table.
DisplayData(table);

Console.WriteLine(“Press any key to continue.”);
Console.ReadKey();
}

private static void DisplayData(System.Data.DataTable table)
{
foreach (System.Data.DataRow row in table.Rows)
{
foreach (System.Data.DataColumn col in table.Columns)
{
Console.WriteLine(“{0} = {1}”, col.ColumnName, row[col]);
}
Console.WriteLine(“============================”);
}
}
}
PowerShell命令
系统未安装或禁用PowerShell会失效,但也比国人故意吹捧的PowerUPSQL好很多倍。

C:\Users\k8gege\Desktop>PowerShell -Command “[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()”

ServerName InstanceName IsClustered Version
———- ———— ———– ——-
K82003-77562E10
WIN-OLDM1T2H9M4
PowerUPSQL
此工具也是基于SqlDataSourceEnumerator实现的,兼容性较差,很多系统下运行各种报错。

PS C:\Users\k8gege\Desktop\PowerUpSQL-master> Get-SQLInstanceBroadcast -Verbose
详细信息: Attempting to identify SQL Server instances on the broadcast domain.
详细信息: 2 SQL Server instances were found.

ComputerName Instance IsClustered Version
———— ——– ———– ——-
K82003-77562E10 K82003-77562E10
WIN-OLDM1T2H9M4 WIN-OLDM1T2H9M4
Ladon EnumMssql
Ladon的EnumMssql模块不支持扫描,若内网存在多个网段,可使用PortScan扫描探测。

Ladon与PowerUPSQL对比,Ladon支持任意Win系统,而PowerUPSQL就未必能用了

参考
https://github.com/k8gege/Ladon
https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/sql/enumerating-instances-of-sql-server

——————————————————
作者: K8哥哥
出处: https://www.cnblogs.com/k8gege/p/12307388.html
版权: 商业转载请联系作者获得授权,非商业转载请注明出处!

window下安装itchat库 - JasonHU - 博客园

mikel阅读(999)

来源: window下安装itchat库 – JasonHU – 博客园

itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。

pip 是 Python 著名的包管理工具,在 Python 开发中必不可少。

1、安装

检查你有没有安装了pip:运行,输入cmd,回车,输入pip,回车。如果提示“’pip’ 不是内部或外部命令”,原因是电脑本地没有安装pip命令。

这有两种办法安装pip:

A、cd索引到Python安装目录Scripts中,easy_install.exe所在的地方,执行指令“easy_install.exe pip”,就可以安装pip。

B、首先到https://pypi.python.org/pypi/pip#downloads 下载pip包解压到一个文件夹,用控制台进入解压目录,输入:python setup.py install

安装好之后将pip.exe的路径添加到环境变量PATH中。

接下来我们就可以安装itchat

用命令行安装:

pip install itchat

经过一系列文件下载完成就安装好了。

注:pip参数解释

pip 最常用命令

获取帮助

pip –help

升级 pip
pip install -U pip

安装包
pip install SomePackage

卸载包
pip uninstall SomePackage

升级指定的包
pip install -U SomePackage

搜索包
pip search SomePackage

查看指定包的详细信息
pip show -f SomePackage

列出已安装的包
pip freeze or pip list

查看可升级的包
pip list -o

如果想知道安装的包在哪个位置,再执行一次安装就提示出来了。

 

2、测试

用开发工具创建一个Python文件,输入以下代码:

 

复制代码
import itchat

itchat.auto_login()
#itchat.auto_login(enableCmdQR=True)#登录的时候在命令行显示二维码
#itchat.send('Hello, filehelper', toUserName='filehelper') #给文件传输助手发一条信息
friends = itchat.get_friends(update=True)  # 获取微信好友列表,如果设置update=True将从服务器刷新列表
for friend in friends[1:]:# 好友列表第一个是自己,所以统计真正好友要从第二个开始
    nickName = friend['NickName']#好友昵称
    remarkName = friend['RemarkName']#好友备注
    sex = "男" if(friend['Sex']==1) else "女"
    province = friend['Province']
    city = friend['City']
    alias = friend['Alias']
    if nickName=="流年":
        print("昵称:%s  备注:%s  性别:%s  省市:%s" % (nickName,remarkName,sex,province+city))
        break
复制代码

 

运行,需要扫码登录:

 

扫码登录后的到运行结果:

 

测试完成!

 

转载请注明出处!

宝塔—python管理器使用教程-长沙SEO霜天

mikel阅读(4965)

来源: 宝塔—python管理器使用教程-长沙SEO霜天

python管理器使用教程

1、请确认项目根目录下已经存在 requirements.txt 文件,用于安装依赖包。

2、选择gunicorn或uwsgi模式启动时会先在根目录创建默认配置,如果需要修改配置,请在创建完成后点击配置修改。

宝塔—python管理器使用教程

3、在版本管理可以安装你想使用的版本

宝塔—python管理器使用教程
4、每个项目都使用独立的虚拟环境,所以创建时需要稍等,虚拟环境的目录会在你项目的根目录下以项目名_venv的名字呈现
宝塔—python管理器使用教程
5、如果需要在现有项目添加模块请点击对应的模块按钮来安装
宝塔—python管理器使用教程
6、管理器gunicorn,uwsgi启动方式为

django

gunicorn -c 项目路径/gunicorn.conf hellodj.wsgi:application,其中hellodj为项目运行文件夹或文件

uwsgi -d –ini 项目路径/uwsgi.ini -w hellodj.wsgi.application

flask

gunicorn -c 项目路径/gunicorn.conf hellodj:app,其中hellodj为项目运行文件夹或文件

uwsgi -d –ini 项目路径/uwsgi.ini -w hellodj:app

宝塔—python管理器使用教程

宝塔—python管理器使用教程

4、如果需要使用域名或80端口访问项目,点击映射按钮即可,操作完成后会在网站处显示

宝塔—python管理器使用教程

宝塔—python管理器使用教程

原创文章,作者:霜天,如若转载,请注明出处:https://www.i5SEO.com/python-manager-use-tutorial.html

阿里云ECS建立微信公众号----以itchatmp为例【python】 - 简书

mikel阅读(1293)

来源: 阿里云ECS建立微信公众号—-以itchatmp为例【python】 – 简书

阿里云ECS建立微信公众号—-以itchatmp为例#

1、由于近期发现itchat开源项目要升级到itchatmp , 所以考虑下把自己的阿里云ECS利用起来,不然有点浪费了。

2、首先熟悉下flask的hello world实现,把这段代码放到ECS上:

#encoding:utf8
from flask import Flask, render_template

app = Flask(__name__)
@app.route('/')
def hello_world():
    content = 'Hello World!'
    return render_template('hello.html', content = content)

if __name__ == '__main__':
    app.run('0.0.0.0')

其中:@app.route(‘/’) 是定义的路由,表示对应网址的根目录。
然后在本机输入ECS的ip加上端口5000,应该就能看到hello world了。

3、

flask的 中文文档资料: http://dormousehole.readthedocs.io/en/latest/

flask的基础视频参考: http://www.jikexueyuan.com/course/943.html

4、 以itchatmp为例:

首先根据itchatmp的github网址: https://github.com/littlecodersh/itchatmp

下载到本地,然后

pip install itchatmp

在这个过程中,很可能安装失败,是因为墙的因素,建议开启翻墙工具

在安装好之后,把对应的示范脚本,根据你的微信订阅号的相关参数进行修改:

import itchatmp

itchatmp.update_config(itchatmp.WechatConfig(
    token='xxxxxxxxxx',
    appId = 'xxxxxxxxxx',
    appSecret = 'xxxxxxxxxx'))

@itchatmp.msg_register(itchatmp.content.TEXT)
def text_reply(msg):
    return msg['content']

itchatmp.run()

5、然后到微信订阅号的后台,把url部分,修改成你的ECS的ip。

6、运行一个形如weixinchat.py的脚本,把上述代码复制,并运行。一切顺利的话,你的订阅号就工作了,此时,他的默认功能是,当你输入信息后,订阅号自动回复相同内容。

7、本文案例是在Win10,Win NT 2008下都测试通过,

8、如果你需要一些Linux相关的参考资料,如下:

作者:vansnowpea
链接:https://www.jianshu.com/p/6f386659f5aa
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

基于itchat实现微信群消息同步机器人 - 简书

mikel阅读(1248)

来源: 基于itchat实现微信群消息同步机器人 – 简书

最近 全栈数据工程师养成攻略 的微信群已经将近500人,开了二群之后为了打通不同微信群之间的消息,花了点时间做了个消息同步机器人,在任意群收到消息时同步到其他群,并且将聊天内容上传至数据库,以供进一步分析、统计和展示。

基本思路是,用 Python 模拟微信网页版登陆,接收到群里消息后,对文本、图片、分享等各类消息类型分别处理,并转发至其他群。

前期准备

首先得有一个微信号,用于代码模拟登陆。由于我的微信号得自己留着用,现阶段注册微信又必须要手机号,于是只好特意办了个电信号,用来申请了一个新的微信,微信号是 honlanbot。虽说似乎可以用阿里小号来注册微信,不过听说存在反复回收和安全隐患问题,故不采用。

其次,需要用到一个Python库 itchat,这个库已经做好了用代码调用微信的大多数功能,非常好用,官方文档在这里,安装的时候使用 pip 即可。

pip install itchat

我的手机支持双卡双待,于是把两张卡都装手机里,再双开微信,同时保持两个微信号手机在线,差不多就可以开始写代码了。用 itchat 调用微信主要是模拟微信网页版登陆,所以必须保持微信号手机在线,因为手机端微信一旦退出,其在网页、PC、MAC、IPAD等相应终端认证的账号也会随之退出。

初步尝试

itchat 提供了一些官方代码,让我们在自己的本本或电脑上新建一个 py 文件,初步尝试一下。

运行以下代码,会出现出现一张二维码,扫码登陆之后将会给“文件传输助手”发送一条消息。

# 加载包
import itchat
# 登陆
itchat.auto_login()
# 发送文本消息,发送目标是“文件传输助手”
itchat.send('Hello, filehelper', toUserName='filehelper')

以下代码则注册了一个消息响应事件,用来定义接收到文本消息后如何处理。在 itchat 里可以定义文本、图片、名片、位置、通知、分享、文件等多种消息类型,从而分别执行不同的处理。

import itchat
# 注册消息响应事件,消息类型为itchat.content.TEXT,即文本消息
@itchat.msg_register(itchat.content.TEXT)
def text_reply(msg):
    # 返回同样的文本消息
    return msg['Text']

itchat.auto_login()
# 绑定消息响应事件后,让itchat运行起来,监听消息
itchat.run()

再来看看如何处理其他类型消息,可以把在消息响应事件里把 msg 打印出来,是一个字典,看看有哪些感兴趣的字段。

import itchat
# import全部消息类型
from itchat.content import *

# 处理文本类消息
# 包括文本、位置、名片、通知、分享
@itchat.msg_register([TEXT, MAP, CARD, NOTE, SHARING])
def text_reply(msg):
    # 微信里,每个用户和群聊,都使用很长的ID来区分
    # msg['FromUserName']就是发送者的ID
    # 将消息的类型和文本内容返回给发送者
    itchat.send('%s: %s' % (msg['Type'], msg['Text']), msg['FromUserName'])

# 处理多媒体类消息
# 包括图片、录音、文件、视频
@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO])
def download_files(msg):
    # msg['Text']是一个文件下载函数
    # 传入文件名,将文件下载下来
    msg['Text'](msg['FileName'])
    # 把下载好的文件再发回给发送者
    return '@%s@%s' % ({'Picture': 'img', 'Video': 'vid'}.get(msg['Type'], 'fil'), msg['FileName'])

# 处理好友添加请求
@itchat.msg_register(FRIENDS)
def add_friend(msg):
    # 该操作会自动将新好友的消息录入,不需要重载通讯录
    itchat.add_friend(**msg['Text']) 
    # 加完好友后,给好友打个招呼
    itchat.send_msg('Nice to meet you!', msg['RecommendInfo']['UserName'])

# 处理群聊消息
@itchat.msg_register(TEXT, isGroupChat=True)
def text_reply(msg):
    if msg['isAt']:
        itchat.send(u'@%s\u2005I received: %s' % (msg['ActualNickName'], msg['Content']), msg['FromUserName'])

# 在auto_login()里面提供一个True,即hotReload=True
# 即可保留登陆状态
# 即使程序关闭,一定时间内重新开启也可以不用重新扫码
itchat.auto_login(True)
itchat.run()

开发消息同步机器人

经过以上示例代码,可以总结出消息同步机器人的开发思路:

  • 登陆后使用get_chatrooms()获取全部群聊的数据,包括每个群聊的ID和昵称,可以将需要同步消息的群聊保存至通讯录;
  • 接收到群聊消息时,如果消息来自于需要同步消息的群聊,就根据消息类型进行处理,同时转发到其他需要同步的群聊。

直接上代码好了,首先定义一个消息响应函数,文本类消息我感兴趣的是 TEXTSHARING 两类,使用 isGroupChat=True 指定消息来自于群聊,这个参数默认为 False

# 自动回复文本等类别的群聊消息
# isGroupChat=True表示为群聊消息
@itchat.msg_register([TEXT, SHARING], isGroupChat=True)
def group_reply_text(msg):
    # 消息来自于哪个群聊
    chatroom_id = msg['FromUserName']
    # 发送者的昵称
    username = msg['ActualNickName']

    # 消息并不是来自于需要同步的群
    if not chatroom_id in chatroom_ids:
        return

    if msg['Type'] == TEXT:
        content = msg['Content']
    elif msg['Type'] == SHARING:
        content = msg['Text']

    # 根据消息类型转发至其他群
    if msg['Type'] == TEXT:
        for item in chatrooms:
            if not item['UserName'] == chatroom_id:
                itchat.send('%s\n%s' % (username, msg['Content']), item['UserName'])
    elif msg['Type'] == SHARING:
        for item in chatrooms:
            if not item['UserName'] == chatroom_id:
                itchat.send('%s\n%s\n%s' % (username, msg['Text'], msg['Url']), item['UserName'])

再来处理下图片等多媒体类消息。

# 自动回复图片等类别的群聊消息
# isGroupChat=True表示为群聊消息          
@itchat.msg_register([PICTURE, ATTACHMENT, VIDEO], isGroupChat=True)
def group_reply_media(msg):
    # 消息来自于哪个群聊
    chatroom_id = msg['FromUserName']
    # 发送者的昵称
    username = msg['ActualNickName']

    # 消息并不是来自于需要同步的群
    if not chatroom_id in chatroom_ids:
        return

    # 如果为gif图片则不转发
    if msg['FileName'][-4:] == '.gif':
        return

    # 下载图片等文件
    msg['Text'](msg['FileName'])
    # 转发至其他需要同步消息的群聊
    for item in chatrooms:
        if not item['UserName'] == chatroom_id:
            itchat.send('@%s@%s' % ({'Picture': 'img', 'Video': 'vid'}.get(msg['Type'], 'fil'), msg['FileName']), item['UserName'])

以上代码实现了对文本、分享、图片、视频四类消息的处理,如果对其他类型的消息也感兴趣,进行相应的处理即可。在前面补上 import 的代码,在后面补上登陆、获取群聊数据和开始监测的代码,就大功告成了。

完整代码在这里:https://pan.baidu.com/s/1bpAJk0B

成果展示

目前两个群之间可以进行消息同步了,一群和二群的小伙伴终于可以畅快地聊了起来(当群主不容易,经常要发很多红包 = =)。

进一步工作

当然,我不可能一直在笔记本上运行这么个 py 代码,所以把它部署到服务器上运行就好了,开个 screen 或者用 IPython 都可以。如果账号偶尔下线了,再运行一下就好。

另外,我还写了个 API,响应消息的时候会把相应的数据 POST 到我的服务器并存到数据库,以供进一步的分析、统计和展示,这也是我身为一个群主应尽的职责~

作者:宏伦工作室
链接:https://www.jianshu.com/p/7aeadca0c9bd#
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

微信群信息采集_大数据_w_t_y_y的博客-CSDN博客

mikel阅读(1699)

来源: 微信群信息采集_大数据_w_t_y_y的博客-CSDN博客

一、数据库:

1、微信账号表

CREATE TABLE `t_wechat_robot` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增长id’,
`robot_id` varchar(255) DEFAULT NULL COMMENT ‘机器Id’,
`robot_name` varchar(255) DEFAULT NULL COMMENT ‘机器名称’,
`robot_nick_name` varchar(255) DEFAULT NULL COMMENT ‘机器微信昵称’,
`status` tinyint(1) DEFAULT NULL COMMENT ‘状态,0在线,1离线’,
`is_delete` tinyint(1) DEFAULT NULL COMMENT ‘删除标志,1 删除,0未删除’,
`create_time` timestamp NULL DEFAULT NULL COMMENT ‘创建时间’,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=’微信群聊机器表’;
并手动新增一条我的微信号记录:

INSERT INTO `t_wechat_robot` VALUES (‘1’, ‘1’, ‘机器1’, MYWXNICKNAME, ‘0’, ‘0’, null, ‘2019-04-24 14:57:00’);
2、微信群表

CREATE TABLE `t_wechat_group` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增长id’,
`robot_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘机器id’,
`group_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘群Id’,
`group_user_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘群名’,
`group_nick_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`head_img_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘群头像’,
`member_count` int(11) DEFAULT ‘0’ COMMENT ‘群成员数’,
`owner_nick_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘群主’,
`chat_room_id` int(11) DEFAULT NULL COMMENT ‘聊天室id’,
`create_time` timestamp NULL DEFAULT NULL COMMENT ‘创建时间’,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间’,
`is_delete` tinyint(1) DEFAULT NULL COMMENT ‘删除标志’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=861 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT=’微信群’;
3、微信群成员表

CREATE TABLE `t_wechat_group_member` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增长id’,
`group_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘群id’,
`user_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘用户名’,
`nick_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`mem_status` int(11) DEFAULT NULL COMMENT ‘状态’,
`uin` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘uin’,
`create_time` timestamp NULL DEFAULT NULL COMMENT ‘创建时间’,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间’,
`is_delete` tinyint(4) DEFAULT NULL COMMENT ‘删除标志,1 删除,0未删除’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mongo群消息表:

项目:

 

二、api

此处省略部分get和set方法:

1、PageParam:

package com.wtyy.common;
import java.io.Serializable;
/**
* 传统的分页参数
*/
public class PageParam implements Serializable {

private static final long serialVersionUID = -5552159160388121108L;
private static final int MIN_PAGE_SIZE = 1;
private static final int MAX_PAGE_SIZE = 1000;
private static final int DEFAULT_PAGE_SIZE = 10;
/**
* 当前页码,从1开始。
*/
private int pageIndex;

/**
* 每页记录数。
*/
private int pageSize = DEFAULT_PAGE_SIZE;

public PageParam(int pageIndex, int pageSize) {
setPageIndex(pageIndex);
setPageSize(pageSize);
}

/**
* 得到开始记录index。
*/
public int getStartIndex() {
return (pageIndex – 1) * pageSize;
}

public int getPageIndex() {
return pageIndex;
}

public void setPageIndex(int pageIndex) {
if (pageIndex < 1){
throw new RuntimeException(“page index should > 0”);
}
this.pageIndex = pageIndex;
}

public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
if (pageSize >= MIN_PAGE_SIZE && pageSize <= MAX_PAGE_SIZE){
this.pageSize = pageSize;
}else {
throw new RuntimeException(String.format(“page size should be ranged in [%s, %s]”,MIN_PAGE_SIZE,MAX_PAGE_SIZE));
}
}

}
2、Pager:

 

package com.wtyy.common;
import java.io.Serializable;
import java.util.List;

/**
* 传统分页
*/
public class Pager<T> implements Serializable {

private static final long serialVersionUID = -9134108412928477507L;

/**
* 总记录数。
*/
private int totalCount;

/**
* 当前页的记录列表。
*/
private List<T> list;

public Pager(int totalCount, List<T> list){
this.totalCount = totalCount;
this.list = list;
}
}
3、WechatMsgType微信群消息类型(整型):

package com.wtyy.constant;

/**
* @Description: 微信群消息类型
*/
public enum WechatMsgType {

TEXT(“文本”,1),

PICTURE(“图片”,3),

RECORDING(“语音”,34),

Video(“视频”,43);

/**
* @param typeName
* @param typeCode
*/
private WechatMsgType(String typeName, Integer typeCode) {
this.typeName = typeName;
this.typeCode = typeCode;
}

private String typeName;

private Integer typeCode;

public String getTypeName() {
return typeName;
}

public void setTypeName(String typeName) {
this.typeName = typeName;
}

public Integer getTypeCode() {
return typeCode;
}

public void setTypeCode(Integer typeCode) {
this.typeCode = typeCode;
}

}
4、WechatType微信群消息类型(字符型):

package com.wtyy.constant;

/**
* 微信群消息类型
*/
public enum WechatType {
//文本消息
Text,

//图片消息
Picture,

//语音消息
Recording,

//视频消息
Video
}
5、WechatRobot登录机器:

package com.wtyy.module;

import java.io.Serializable;
import java.util.Date;

public class WechatRobot implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8066995470069020705L;

private Integer id;

private String robotId;

private String robotName;

private String robotNickName;

/**
* 状态,0在线,1离线
*/
private Short status;

private Short isDelete;

private Date createTime;

private Date updateTime;

}
6、WechatGroup微信群:

package com.wtyy.module;

import java.io.Serializable;
import java.util.Date;

public class WechatGroup implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7954472287669464909L;

private Integer id;

private String robotId;

private String groupId;

private String groupUserName;

private String groupNickName;

private String headImgUrl;

private Integer memberCount;

private String ownerNickName;

private Integer chatRoomId;

private Date createTime;

private Date updateTime;

private Short isDelete;

}
7、WechatGroupMember微信群成员:

package com.wtyy.module;

import java.io.Serializable;
import java.util.Date;

public class WechatGroupMember implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3937137164822326424L;

private Integer id;

private String groupId;

private String userName;

private String nickName;

private Integer memStatus;

private Long uin;

private Date createTime;

private Date updateTime;

private Short isDelete;

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((nickName == null) ? 0 : nickName.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
WechatGroupMember other = (WechatGroupMember) obj;
if (nickName == null) {
if (other.nickName != null)
return false;
} else if (!nickName.equals(other.nickName))
return false;
return true;
}

}
8、WechatGroupMsg微信群消息:

package com.wtyy.module;

import java.io.Serializable;
import java.util.Date;
public class WechatGroupMsg implements Serializable{

private static final long serialVersionUID = -8707880890289759728L;

private String robotId;

private String groupId;

private String msgId;

private String fromUserName;

private String toUserName;

/**消息类型
* @see
* com.iflytek.edu.zx.table.admin.constant.WechatMsgType
*/
private Integer msgType;

/**
* 消息类型
* @see
* com.iflytek.edu.zx.table.admin.constant.WechatType
*/
private String type;

/**
* 文件路径
*/
private String url;

/**
* 消息内容
*/
private String content;

/**
* 消息内容
*/
private String text;

/**
* 文件消息 文件名称
*/
private String fileName;

private Integer status;

private Integer imgStatus;

private Date createTime;

/**
* 发送者昵称
*/
private String actualNickName;

private String actualUserName;

private Integer statusNotifyCode;

private String statusNotifyUserName;

RecommendInfo recommendInfo;

}
9、RecommendInfo:

package com.wtyy.module;
import java.io.Serializable;

public class RecommendInfo implements Serializable{
private static final long serialVersionUID = 6421951838838130816L;

private String userName;

private String nickName;

private String qQNum;

private String content;
}
10、WechatService微信群信息服务:

package com.wtyy.service;
import java.util.List;
import com.wtyy.common.PageParam;
import com.wtyy.common.Pager;
import com.wtyy.module.WechatGroup;
import com.wtyy.module.WechatGroupMember;
import com.wtyy.module.WechatRobot;

public interface WechatService {

/**
* 新增机器
*/
public void addWechatRobot(WechatRobot wechatRobot);

/**
* 修改机器
*/
public void updateWechatRobot(WechatRobot wechatRobot);

/**
* 删除机器
*/
public void deleteWechatRobot(Integer id);

/**
* 根据昵称获取robotId
*/
public String getRobotIdByNickName(String nickName);

/**
* 分页获取机器
*/
public Pager<WechatRobot> getPagerWechatRobot(PageParam pageParam);

/**
* 添加群
*/
public void addWechatGroup(WechatGroup wechatGroup);

/**
* 根据群昵称获取groupId
*/
public String getGroupIdByGroupNickName(String groupNickName);

/**
* 修改群
*/
public void updateWechatGroup(WechatGroup wechatGroup);

/**
* 分页获取群
*/
public Pager<WechatGroup> getPagerWechatGroup(PageParam pageParam,
String robotId);

/**
* 添加群成员
*/
public void batchInsertWechatGroupMember(List<WechatGroupMember> groupMembers,
String groupId);

/**
* 获取群成员列表,为null返回空集合[]
*/
public List<WechatGroupMember> getMembersByGroupId(String groupId);

/**
* 批量删除群成员
*/
public void batchDeleteWechatGroupMember(List<Integer> ids, String groupId);

/**
* 更新群成员数
*/
public void updateWechatGroupSize(String groupId, int size);

}
11、WechatGroupMsgService微信群消息服务:

package com.wtyy.service;
import com.wtyy.common.PageParam;
import com.wtyy.common.Pager;
import com.wtyy.module.WechatGroupMsg;

public interface WechatGroupMsgService {
/**
* 添加群消息
*/
public void addGroupMsg(WechatGroupMsg wechatGroupMsg);

/**
* 分页获取群消息
*/
public Pager<WechatGroupMsg> getPagerWechatGroupMsg(PageParam pageParam,
String robotId,String groupId);
}
三、service:

配置文件:

spring.datasource.driver-class-name=com.mySQL.jdbc.Driver
spring.datasource.url=jdbc:mySQL://localhost:3307/itchat?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy

mybatis.mapper-locations=classpath*:Mapper/*Mapper.xml
server.port=8081

1、Dao:

看下ConfigDAO:这是因为存在特殊符号和一些表情,utf-8存不了(会报错\xF0\x9F\x91\x8D05…),需要utf8mb4。具体可见我的博客 mybatis分类下的异常处理。

package com.wtyy.dao;

import org.apache.ibatis.annotations.Update;

public interface ConfigDAO {

@Update(“set names utf8mb4”)
public void setCharsetToUtf8mb4();
}
其他的dao都是对mySQL的增删改查,就不粘了。

2、impl:

1、看下mongo的dao,其他的都是对mysql表的增删改查,不粘了。

package com.wtyy.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.wtyy.module.WechatGroupMsg;
import com.wtyy.util.MongodbManager;

/**
* @Description: mongo存储微信群消息
*/
@Repository(“wechatGroupMsgDao”)
public class WechatGroupMsgDao {

///collection名
private String wechatMsgMongoCollection =”wechatmsg”;

public void addGroupMsg(WechatGroupMsg wechatGroupMsg) {
DBCollection dbCollection = MongodbManager.DB_TL.getCollection(wechatMsgMongoCollection);
DBObject documents = new BasicDBObject();
documents.put(“robot_id”, wechatGroupMsg.getRobotId());
documents.put(“group_id”, wechatGroupMsg.getGroupId());
documents.put(“msg_id”, wechatGroupMsg.getMsgId());
documents.put(“from_user_name”, wechatGroupMsg.getFromUserName());
documents.put(“to_user_name”, wechatGroupMsg.getToUserName());
documents.put(“msg_type”, wechatGroupMsg.getMsgType());
documents.put(“type”, wechatGroupMsg.getType());
documents.put(“content”, wechatGroupMsg.getContent());
documents.put(“text”, wechatGroupMsg.getText());
documents.put(“status”, wechatGroupMsg.getStatus());
documents.put(“img_status”, wechatGroupMsg.getImgStatus());
documents.put(“create_time”, wechatGroupMsg.getCreateTime());
documents.put(“file_name”, wechatGroupMsg.getFileName());
documents.put(“actual_nick_name”, wechatGroupMsg.getActualNickName());
documents.put(“actual_user_name”, wechatGroupMsg.getActualUserName());
documents.put(“status_notify_code”, wechatGroupMsg.getStatusNotifyCode());
documents.put(“status_notify_user_name”, wechatGroupMsg.getStatusNotifyUserName());
documents.put(“url”, wechatGroupMsg.getUrl());
dbCollection.insert(documents );
}

}
3、util:

package com.wtyy.util;

import java.net.UnknownHostException;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;

/**
* mongodb管理器,提供DB对象。
*/
public class MongodbManager {

private static final Logger LOGGER = LoggerFactory.getLogger(“mongodbManager”);

private static MongoClient client;

//数据库名
private static String wechatMsgMongoDB = “wechatMsg”;

static {
String adressList = “localhost:27017”;

try {

String[] addresses = adressList.split(“,”);
ArrayList<ServerAddress> serverAddressList = new ArrayList<ServerAddress>();
for(int j=0; j < addresses.length; j++){
String[] address = addresses[j].split(“:”);
ServerAddress mongoAddress = new ServerAddress(address[0], Integer.parseInt(address[1]));
serverAddressList.add(mongoAddress);
}
client = new MongoClient(serverAddressList);
client.setReadPreference(ReadPreference.primaryPreferred());
LOGGER.info(“读写分离未启动”);

client.setWriteConcern(WriteConcern.SAFE);
LOGGER.info(“写入安全模式启动”);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}

public static final DB DB_TL = client.getDB(wechatMsgMongoDB);
}
4、启动类:

package com.wtyy;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubboConfig;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;

@SpringBootApplication
@MapperScan(“com.wtyy.dao”)
@ImportResource(“classpath:provider.xml”)
public class ServiceStart {
public static void main(String[] args) {
SpringApplication.run(ServiceStart.class, args);
}

}
四、rest:

1、配置文件:

application.properties:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3307/itchat?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy

server.port=8082

spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=
spring.redis.database=1
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=0

#kafka
# 指定kafka 代理地址,可以多个
spring.kafka.bootstrap-servers=localhost:9092
# 指定默认消费者group id
spring.kafka.consumer.group-id=myGroup
# 指定默认topic id
spring.kafka.template.default-topic= my-replicated-topic
# 指定listener 容器中的线程数,用于提高并发量
spring.kafka.listener.concurrency= 3
# 每次批量发送消息的数量
spring.kafka.producer.batch-size= 1000
#key-value序列化反序列化
#spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
#spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
#spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
#spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.buffer-memory=524288
#群消息
wechat.msg = wechatmsg
#群列表
wechat.group = wechatgroup
pom:

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>com.wtyy</groupId>
<artifactId>itchat-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath /> <!– lookup parent from repository –>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<!– api –>
<dependency>
<groupId>com.wtyy</groupId>
<artifactId>itchat-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<!– mybatis –>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>

<!– dubbo –>
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>

<!– redis –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

</project>
2、dto:是微信消息的原格式,这里封装成对象,后面可以直接用json转成dto,由于转成的dto首字母全部是大写,所以存储的时候又按照阿里的开发规范转换了一次。至于dto的封装,看消息的原格式就很清晰了:

{
“MsgId”: “5689968435146263788”,
“FromUserName”: “@863e84f7210f6ca0da17b018aedf81bcf8a173fee1d811d2769f101c4267e020”,
“ToUserName”: “@@c25f17fed47b6946321965da58fab144f674975dafd780a9e2e173de307e60b6”,
“MsgType”: 1,
“Content”: “我是消息”,
“Status”: 3,
“ImgStatus”: 1,
“CreateTime”: 1555677369,
“VoiceLength”: 0,
“PlayLength”: 0,
“FileName”: “”,
“FileSize”: “”,
“MediaId”: “”,
“Url”: “”,
“AppMsgType”: 0,
“StatusNotifyCode”: 0,
“StatusNotifyUserName”: “”,
“RecommendInfo”: {
“UserName”: “”,
“NickName”: “”,
“QQNum”: 0,
“Province”: “”,
“City”: “”,
“Content”: “”,
“Signature”: “”,
“Alias”: “”,
“Scene”: 0,
“VerifyFlag”: 0,
“AttrStatus”: 0,
“Sex”: 0,
“Ticket”: “”,
“OpCode”: 0
},
“ForwardFlag”: 0,
“AppInfo”: {
“AppID”: “”,
“Type”: 0
},
“HasProductId”: 0,
“Ticket”: “”,
“ImgHeight”: 0,
“ImgWidth”: 0,
“SubMsgType”: 0,
“NewMsgId”: 5689968435146263788,
“OriContent”: “”,
“EncryFileName”: “”,
“ActualNickName”: “发送人”,
“IsAt”: false,
“ActualUserName”: “@863e84f7210f6ca0da17b018aedf81bcf8a173fee1d811d2769f101c4267e020”,
“User”: {
“MemberList”: [
{
“MemberList”: [],
“Uin”: 0,
“UserName”: “@45161ba956dbd3d622499558e649c4beddee71fcd9277b4c8274c10f61295821”,
“NickName”: “成员1”,
“AttrStatus”: 102501,
“PYInitial”: “”,
“PYQuanPin”: “”,
“RemarkPYInitial”: “”,
“RemarkPYQuanPin”: “”,
“MemberStatus”: 0,
“DisplayName”: “”,
“KeyWord”: “”
}, {
“MemberList”: [],
“Uin”: 0,
“UserName”: “@d24ebe966bca4fbb6f08a886d3f2960b1cdfb5fa65113f7081e40b1b1bebbfcb”,
“NickName”: “成员2”,
“AttrStatus”: 233509,
“PYInitial”: “”,
“PYQuanPin”: “”,
“RemarkPYInitial”: “”,
“RemarkPYQuanPin”: “”,
“MemberStatus”: 0,
“DisplayName”: “”,
“KeyWord”: “”
}, {
“MemberList”: [],
“Uin”: 0,
“UserName”: “@863e84f7210f6ca0da17b018aedf81bcf8a173fee1d811d2769f101c4267e020”,
“NickName”: “成员3”,
“AttrStatus”: 16912485,
“PYInitial”: “”,
“PYQuanPin”: “”,
“RemarkPYInitial”: “”,
“RemarkPYQuanPin”: “”,
“MemberStatus”: 0,
“DisplayName”: “”,
“KeyWord”: “”
}
],
“Uin”: 0,
“UserName”: “@@c25f17fed47b6946321965da58fab144f674975dafd780a9e2e173de307e60b6”,
“NickName”: “我是群名称”,
“HeadImgUrl”: “/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=0&username=@@c25f17fed47b6946321965da58fab144f674975dafd780a9e2e173de307e60b6&skey=@crypt_15c50994_8b9ef531e394f9e342c232850ba31c0f”,
“ContactFlag”: 2,
“MemberCount”: 3,
“RemarkName”: “”,
“HideInputBarFlag”: 0,
“Sex”: 0,
“Signature”: “”,
“VerifyFlag”: 0,
“OwnerUin”: 0,
“PYInitial”: “”,
“PYQuanPin”: “”,
“RemarkPYInitial”: “”,
“RemarkPYQuanPin”: “”,
“StarFriend”: 0,
“AppAccountFlag”: 0,
“Statues”: 1,
“AttrStatus”: 0,
“Province”: “”,
“City”: “”,
“Alias”: “”,
“SnsFlag”: 0,
“UniFriend”: 0,
“DisplayName”: “”,
“ChatRoomId”: 0,
“KeyWord”: “”,
“EncryChatRoomId”: “”,
“IsOwner”: 1,
“IsAdmin”: null,
“Self”: {
“MemberList”: [],
“Uin”: 0,
“UserName”: “@863e84f7210f6ca0da17b018aedf81bcf8a173fee1d811d2769f101c4267e020”,
“NickName”: “成员3”,
“AttrStatus”: 16912485,
“PYInitial”: “”,
“PYQuanPin”: “”,
“RemarkPYInitial”: “”,
“RemarkPYQuanPin”: “”,
“MemberStatus”: 0,
“DisplayName”: “”,
“KeyWord”: “”
},
“HeadImgUpdateFlag”: 1,
“ContactType”: 0,
“ChatRoomOwner”: “@863e84f7210f6ca0da17b018aedf81bcf8a173fee1d811d2769f101c4267e020”
},
“Type”: “Text”,
“Text”: “我是消息”
}
(1) BaseMsg群消息的原格式:

package com.wtyy.dto;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.wtyy.module.WechatGroup;
import com.wtyy.module.WechatGroupMember;
import com.wtyy.module.WechatGroupMsg;

public class BaseMsg implements Serializable{

protected static Logger Logger=LoggerFactory.getLogger(BaseMsg.class);

private static final long serialVersionUID = -948732742619659228L;

private String MsgId;

private String FromUserName;

private String ToUserName;

private Integer MsgType;

private String Content;

private Integer Status;

private Integer ImgStatus;

private Date CreateTime;

private Integer VoiceLength;

private Integer PlayLength;

private String FileName;

private String FileSize;

private String MediaId;

private String Url;

private Integer AppMsgType;

private Integer StatusNotifyCode;

private String StatusNotifyUserName;

private Integer ForwardFlag;

private String Ticket;

private Integer SubMsgType;

private String OriContent;

private Boolean IsAt;

//发送者
private String ActualNickName;

private String ActualUserName;

private String Type;

//同content
private String Text;

/** 推荐消息报文 **/
private RecommendInfo RecommendInfo;

/**
* 成员
*/
private WechatGroupDTO User;

/*省略所有get和set方法*/

/**
* WechatGroupDTO 转化成WechatGroup
*/
public static WechatGroup toWehatGroup(WechatGroupDTO wechatGroupDTO) {

List<Member> memberList = wechatGroupDTO.getMemberList();
WechatGroup wechatGroup = new WechatGroup();
wechatGroup.setChatRoomId(wechatGroupDTO.getChatRoomId());
wechatGroup.setGroupNickName(wechatGroupDTO.getNickName());
wechatGroup.setGroupUserName(wechatGroupDTO.getUserName());
wechatGroup.setHeadImgUrl(wechatGroupDTO.getHeadImgUrl());
wechatGroup.setMemberCount(wechatGroupDTO.getMemberCount());
String chatRoomOwner = wechatGroupDTO.getChatRoomOwner();
if(!StringUtils.isEmpty(chatRoomOwner)){
for(Member member:memberList){
if(chatRoomOwner.equals(member.getUserName())){
wechatGroup.setOwnerNickName(member.getNickName());
break;
}
}

}

return wechatGroup;
}

/**
* WechatGroupDTO 转化成 List<WechatGroupMember> 群成员
*/
public static List<WechatGroupMember> toWechatGroupMembers(WechatGroupDTO wechatGroupDTO) {
List<WechatGroupMember> members = new ArrayList<>();
List<Member> membersObj = wechatGroupDTO.getMemberList();
for(Member member : membersObj){
WechatGroupMember wechatGroupMember = new WechatGroupMember();
wechatGroupMember.setNickName(member.getNickName());
/*try {
//wechatGroupMember.setNickName(URLDecoder.decode(member.getNickName(), “utf-8”));
} catch (UnsupportedEncodingException e) {
Logger.error(“解码异常”+member.getNickName());
}*/
//wechatGroupMember.setUin(member.getUin());
wechatGroupMember.setUserName(member.getUserName());
members.add(wechatGroupMember);
}
return members;
}

/**
* 原生消息转化成WechatGroupMsg 消息实体
*/
public static WechatGroupMsg toWechatGroupMsg(BaseMsg msgObj, String groupId,String robotId) {
WechatGroupMsg wechatGroupMsg = new WechatGroupMsg();
wechatGroupMsg.setActualNickName(msgObj.getActualNickName());
wechatGroupMsg.setActualUserName(msgObj.getActualUserName());
wechatGroupMsg.setContent(msgObj.getContent());
wechatGroupMsg.setFileName(msgObj.getFileName());
wechatGroupMsg.setFromUserName(msgObj.getFromUserName());
wechatGroupMsg.setGroupId(groupId);
wechatGroupMsg.setImgStatus(msgObj.getImgStatus());
wechatGroupMsg.setMsgId(msgObj.getMsgId());
wechatGroupMsg.setMsgType(msgObj.getMsgType());
wechatGroupMsg.setRobotId(robotId);
wechatGroupMsg.setStatus(msgObj.getStatus());
wechatGroupMsg.setStatusNotifyCode(msgObj.getStatusNotifyCode());
wechatGroupMsg.setStatusNotifyUserName(msgObj.getStatusNotifyUserName());
wechatGroupMsg.setText(msgObj.getText());
wechatGroupMsg.setType(msgObj.getType());
wechatGroupMsg.setCreateTime(msgObj.getCreateTime());
wechatGroupMsg.setUrl(msgObj.getUrl());
return wechatGroupMsg;
}

}
(2)微信消息体中的User对象:

package com.wtyy.dto;

import java.io.Serializable;
import java.util.List;

public class WechatGroupDTO implements Serializable{

/**
*
*/
private static final long serialVersionUID = 3919060806498433575L;

private List<Member> MemberList;

private Long Uin;

/**
* 群名
*/
private String UserName;

/**
* 群昵称
*/
private String NickName;

/**
* 群头像
*/
private String HeadImgUrl;

private Integer ChatRoomId;

/**
* 群成员数
*/
private Integer MemberCount;

/**
* 群主(用户名)
*/
private String ChatRoomOwner;

/**
* 当前登录用户
*/
private Member Self;

/*省略所有get和set方法*/
}
(3)、微信成员对象:

 

package com.wtyy.dto;
import java.io.Serializable;
public class Member implements Serializable{

private static final long serialVersionUID = 4300987291874273446L;

//private Integer Uin;

private String UserName;

private String NickName;

}
(4)RecommendInfo:

package com.wtyy.dto;

import java.io.Serializable;
public class RecommendInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;

private String Province;
private String Ticket;
private String UserName;
private int Sex;
private int AttrStatus;
private String City;
private String NickName;
private int Scene;
private String Content;
private String Alias;
private String Signature;
private int OpCode;
private int QQNum;
private int VerifyFlag;
}
3、kafka监听:(一般是写成一个单独的job,这里暂时和controller放在一个工程里面)

package com.wtyy.consume;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.spec.MGF1ParameterSpec;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import ch.qos.logback.core.db.dialect.MsSQLDialect;

import com.alibaba.fastjson.JSON;
import com.wtyy.dto.BaseMsg;
import com.wtyy.dto.WechatGroupDTO;
import com.wtyy.module.WechatGroup;
import com.wtyy.module.WechatGroupMember;
import com.wtyy.module.WechatGroupMsg;
import com.wtyy.service.WechatGroupMsgService;
import com.wtyy.service.WechatService;
import com.wtyy.util.RedisUtil;

/**
*
* @Description: kafka消息监听
*/
@Service
public class WechatGroupConsumer {

protected static Logger logger=LoggerFactory.getLogger(WechatGroupConsumer.class);

@Autowired
private WechatService wechatService;

@Autowired
private WechatGroupMsgService wechatGroupMsgService;

@Autowired
private RedisUtil redisClient;

//redis缓存过期时间,一天
private static final int EXPIRE_TIME = 60*60*24;

//机器昵称-机器id
private static final String WECHAT_ROBOT_NICKNAME_ID = “wechat:robot:{nickName}”;

//群昵称-群id
private static final String WECHAT_GROUP_NICKNAME_ID = “wechatGroup:{nickName}:{robotId}”;

/**
* 1、监听群和群成员
*/
@KafkaListener(topics = {“${wechat.group}”})
public void consumeGroup(ConsumerRecord<?, ?> consumerRecord) throws UnsupportedEncodingException{
String group = (String) consumerRecord.value();
/*List<WechatGroupDTO> groupList = JSONUtils.parseList(groups, WechatGroupDTO.class);

for(WechatGroupDTO wechatGroupDTO : groupList){
handleWechatGroup(wechatGroupDTO);
}*/
WechatGroupDTO wechatGroupDTO = null;
try{
wechatGroupDTO = JSON.parSEObject(group, WechatGroupDTO.class);
}catch (Exception e) {
logger.error(“群成员信息获取异常”);
}
handleWechatGroup(wechatGroupDTO);
}

/**
* 2、监听群消息
*/
@KafkaListener(topics = {“${wechat.msg}”})
public void consumeMsg(ConsumerRecord<?, ?> consumerRecord) throws UnsupportedEncodingException{

try{
String message = (String) consumerRecord.value();
System.err.println(message);
BaseMsg msgObj = JSON.parSEObject(message, BaseMsg.class);
//System.err.println(“时间”+msgObj.getCreateTime());
WechatGroupDTO wechatGroupDTO = msgObj.getUser();
//群昵称
String groupNickName = wechatGroupDTO.getNickName();
//当前登录用户昵称
String currentRobotNickName = wechatGroupDTO.getSelf().getNickName();

String robotId = redisClient.get(WECHAT_ROBOT_NICKNAME_ID, currentRobotNickName);
if(StringUtils.isEmpty(robotId)){
robotId = wechatService.getRobotIdByNickName(currentRobotNickName);
if(!StringUtils.isEmpty(robotId)){
redisClient.setex(WECHAT_ROBOT_NICKNAME_ID, EXPIRE_TIME, robotId, currentRobotNickName);
}else{
logger.error(“robotId为空”);
}
}

if(!StringUtils.isEmpty(robotId)){

//根据群昵称查询群是否存在
String groupId = redisClient.get(WECHAT_GROUP_NICKNAME_ID, groupNickName,robotId);
if(StringUtils.isEmpty(groupId)){
groupId = wechatService.getGroupIdByGroupNickName(groupNickName);
if(!StringUtils.isEmpty(groupId)){
redisClient.setex(WECHAT_GROUP_NICKNAME_ID, EXPIRE_TIME, groupId, groupNickName,robotId);
}
}

if(groupId == null){
//群不存在,插入群与群成员
groupId = addWechatGroupAndMembers(msgObj.getUser(),robotId,groupId);
redisClient.setex(WECHAT_GROUP_NICKNAME_ID, EXPIRE_TIME, groupId, groupNickName,robotId);

}

//插入群消息
WechatGroupMsg wechatGroupMsg = BaseMsg.toWechatGroupMsg(msgObj,groupId,robotId);
wechatGroupMsgService.addGroupMsg(wechatGroupMsg);
}

}catch (Exception e) {
logger.error(e+e.getMessage());
}
}

/**
* @author: tingzhang7
* @param msgObj 原生消息DTO
* @param robotId 机器id
* @param groupId groupId,为空则新增群
* @Description: 新增群
* @param 返回群id
*/
private String addWechatGroupAndMembers(WechatGroupDTO wechatGroupDTO, String robotId,String groupId) {

if(groupId == null){
//新增群
groupId = UUID.randomUUID().toString();
WechatGroup wechatGroup = BaseMsg.toWehatGroup(wechatGroupDTO);
wechatGroup.setGroupId(groupId);
wechatGroup.setRobotId(robotId);
wechatService.addWechatGroup(wechatGroup);
}

//新增群成员
List<WechatGroupMember> groupMembers = BaseMsg.toWechatGroupMembers(wechatGroupDTO);
wechatService.batchInsertWechatGroupMember(groupMembers, groupId);
return groupId;
}

/**
* @author: tingzhang7
* @Description: 处理群
*/
private void handleWechatGroup(WechatGroupDTO wechatGroupDTO) {

if(wechatGroupDTO == null){
logger.error(“群解析后为空”);
return ;
}
String groupNickName = wechatGroupDTO.getNickName();
if(StringUtils.isEmpty(groupNickName)){
logger.error(“群名称为空”);
return;
}
//机器昵称
String robotNickName = wechatGroupDTO.getSelf().getNickName();

//获取机器id
String robotId = redisClient.get(WECHAT_ROBOT_NICKNAME_ID, robotNickName);
if(StringUtils.isEmpty(robotId)){
robotId = wechatService.getRobotIdByNickName(robotNickName);
if(!StringUtils.isEmpty(robotId)){
redisClient.setex(WECHAT_ROBOT_NICKNAME_ID, EXPIRE_TIME, robotId, robotNickName);
}else{
logger.error(“获取不到机器robotId “+robotNickName);
return ;
}
}

//判断该群有没有记录过
String groupId = redisClient.get(WECHAT_GROUP_NICKNAME_ID, groupNickName,robotId);
if(StringUtils.isEmpty(groupId)){
groupId = wechatService.getGroupIdByGroupNickName(groupNickName);
if(!StringUtils.isEmpty(groupId)){
redisClient.setex(WECHAT_GROUP_NICKNAME_ID, EXPIRE_TIME, groupId, groupNickName,robotId);
}
}
//新增群
if(groupId == null){
//群不存在,插入群与群成员
groupId = addWechatGroupAndMembers(wechatGroupDTO,robotId,groupId);
redisClient.setex(WECHAT_GROUP_NICKNAME_ID, EXPIRE_TIME, groupId, groupNickName,robotId);
}else{
//群存在,更新群成员(按照nickName比较)
List<WechatGroupMember> oldMembers = wechatService.getMembersByGroupId(groupId);
List<WechatGroupMember> newMembers = BaseMsg.toWechatGroupMembers(wechatGroupDTO);
if(newMembers.size() != oldMembers.size()){
wechatService.updateWechatGroupSize(groupId,newMembers.size());
}
//newMembers备份
List<WechatGroupMember> copyOfNewMembers = BaseMsg.toWechatGroupMembers(wechatGroupDTO);
//新增的 = newMembers-old,newMembers为新增的差集
newMembers.removeAll(oldMembers);

List<WechatGroupMember> members = new ArrayList<>();
for(WechatGroupMember member : newMembers){
members.add(member);
if(members.size() == 200){
wechatService.batchInsertWechatGroupMember(members, groupId);
members.clear();
}
}
if(!CollectionUtils.isEmpty(members)){
wechatService.batchInsertWechatGroupMember(members, groupId);
}

//减少的 = old-copyOfNewMembers,oldMembers为减少的差集
oldMembers.removeAll(copyOfNewMembers);
List<Integer> ids = new ArrayList<>();
for(WechatGroupMember groupMember : oldMembers){
ids.add(groupMember.getId());
if(ids.size() == 200){
wechatService.batchDeleteWechatGroupMember(ids,groupId);
ids.clear();
}
}
if(!CollectionUtils.isEmpty(ids)){
wechatService.batchDeleteWechatGroupMember(ids,groupId);
}
}
}

}
4、启动类:

package com.wtyy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
import org.springframework.scheduling.annotation.EnableScheduling;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubboConfig;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;

@SpringBootApplication
@EnableDubboConfiguration
@ImportResource(“classpath:consumer.xml”)
@EnableScheduling
public class RestStart {
public static void main(String[] args) {
SpringApplication.run(RestStart.class, args);
}

}
五、最后看下Python脚本:

import itchat, time
from itchat.content import *
from kafka import KafkaProducer
import json
import threading
import oss2
import os
import uuid
producer = KafkaProducer(
value_serializer=lambda v: json.dumps(v).encode(‘utf-8’),
bootstrap_servers=[‘localhost:9092’]
)

#获取群列表
def getChatRooms() :
list = itchat.get_chatrooms(update=True)
itchat.dump_login_status()
for i in list:
# if i[‘MemberCount’] == 0:
# memberList = itchat.update_chatroom(i[‘UserName’], detailedMember=True)
# producer.send(‘wechatgroup’, memberList)
# print(i[‘NickName’],”查询”)
# else:
# producer.send(‘wechatgroup’, i)
# print(i[‘NickName’],”不查询”)
memberList = itchat.update_chatroom(i[‘UserName’], detailedMember=True)
producer.send(‘wechatgroup’, memberList)
timer = threading.Timer(10.5, getChatRooms)
timer.start()

#接收群-文本消息
@itchat.msg_register(TEXT, isGroupChat=True)
def text_reply(msg):
print(“文本消息”)
producer.send(‘wechatmsg’, msg)
# if msg.isAt:
# msg.user.send(u’@%s\u2005I received: %s’ % (
# msg.actualNickName, msg.text))

#接收群 分享消息
@itchat.msg_register([SHARING],isGroupChat=True)
def recieve_sharing(msg):
print(“分享消息”)
producer.send(‘wechatmsg’,msg)

#接收群 文件消息
@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO],isGroupChat=True)
def download_files(msg):
msg.download(msg.fileName)
url = uploadFile(msg.fileName)
msg[‘Text’] = “”
msg[‘Url’] = url
producer.send(‘wechatmsg’,msg)

#上传文件到阿里云并返回url
def uploadFile(fileName):
url = ”
auth = oss2.Auth(xxx, xxx)
endpoint = ‘https://oss-cn-hangzhou.aliyuncs.com’
bucket = oss2.Bucket(auth, endpoint, xxx, connect_timeout=30)
fileUrl = os.getcwd()+os.path.sep+fileName
result = bucket.put_object_from_file(fileName, fileUrl)

if result.status == 200:
# 阿里返回一个关于fileName的url地址 ,最后一个参数是以秒为单位的过期时间
url = bucket.sign_url(‘GET’, fileName, 60 * 60 * 24)
print(‘上传成功’)
#上传成功后删除文件
os.remove(fileUrl)
return url

@itchat.msg_register([TEXT, MAP, CARD, NOTE, SHARING])
def text_reply(msg):
msg.user.send(‘%s: %s’ % (msg.type, msg.text))

@itchat.msg_register(FRIENDS)
def add_friend(msg):
msg.user.verify()
msg.user.send(‘Nice to meet you!’)

# 通过群昵称获得群聊userName
def get_group_userName(group_name):
group_list = itchat.search_chatrooms(name=group_name)
return group_list[0][‘UserName’]

# 通过好友昵称获得好友userName
def get_friend_userName(friend_name):
friend = itchat.search_friends(name=friend_name)
return friend[0][‘UserName’]

#自动拉取好友进群,传入昵称
def addFriendToChatRoom(group_name,friend_name):
groupUserName = get_group_userName(group_name)
print(“群”+groupUserName)
friendUserName = get_friend_userName(friend_name)
print(“好友”+friendUserName)
status = itchat.add_member_into_chatroom(groupUserName,
[{‘UserName’: friendUserName}],
useInvitation=False)
print(“状态”)
print(status)

#多用户登录
instanceDir = ‘%s.pkl’ % uuid.uuid4()
itchat.auto_login(hotReload=True,enableCmdQR =True,statusStorageDir=instanceDir)
#定时获取群列表
timer = threading.Timer(2, getChatRooms)
timer.start()
itchat.run(True)

注:将用户加入群聊有直接加入和发送邀请两种,通过 useInvitation设置,True为发送邀请False为直接加入,超过40人的群只能使用邀请的方式。

********************************************************分割线********************************************************************************

好了,现在启动本地redis、kafka后,再分别启动service项目和rest项目。

准备扫码登录:

登录成功后可以看到会自动生成一个.pkl的文:

就是这个文件缓存了登录状态,所以这个文件的名称我生成了一个随机数,否则

itchat.auto_login(hotReload=True,enableCmdQR =True)
这样的话只允许一个用户登录。登录后就可以定时的更新群成员、获取各类群消息了:

 

————————————————
版权声明:本文为CSDN博主「w_t_y_y」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w_t_y_y/article/details/89512517