美文网首页
自 动 成 语 接 龙

自 动 成 语 接 龙

作者: 粽子和小恺 | 来源:发表于2022-08-27 13:32 被阅读0次

    成语接龙

    如题,QQ群里可以加入一个机器人小冰,有一个“成语接龙”的功能,并且在群里经常用于比赛。
    那么,我们是否可以做一个自动成语接龙的小东西呢?
    答案是可以的。

    数据准备

    为了完成成语接龙的任务,我们首先需要准备好成语数据。我从网上搞到了一个成语解析文档,通过简单的数据处理之后得到了这样一个文件:


    成语文档

    这个文档一共有30806行,我们需要将文档读取进来并存储。
    我们使用一个泛型列表实现存储。

    using System.Collections.Generic;
    List<string> idiom = NULL;
    

    然后从指定路径读取文档。

      using (StreamReader sr = new StreamReader("E:\\jb.txt"))
      {
          string t = sr.ReadToEnd();
          idiom = new List<string>(t.Split('\n'));
      }
    

    现在我们的idiom列表中就存储了所有常用成语,万事俱备,现在开整!

    信息获取

    QQ没有任何的接口让我们获取数据,所以我们只能通过模拟鼠标键盘来获取信息。
    为了模拟鼠标操作,我们需要设置鼠标的位置。这个功能可以通过SetCursorPos的来实现,将该函数声明到C#中。

    [DllImport("User32.dll")]
    public extern static void SetCursorPos(int x, int y);
    

    同时我们还需要模拟鼠标拖动等,我们可以通过mouse_event实现,同样声明到C#中。

    [DllImport("User32.dll")]
    private static extern int mouse_event
    (
        int dwFlags, int dx, int dy, int dwData, int dwExtraInfo
    );
    

    通过计算坐标值,我们可以实现拖动选中消息功能。
    附上相应的常量值。

    const int MOUSEEVENTF_MOVE = 0x0001;
    const int MOUSEEVENTF_LEFTDOWN = 0x0002;
    const int MOUSEEVENTF_LEFTUP = 0x0004;
    

    至于是什么意思都能看出来,就不再赘述。

    SetCursorPos(58, 869);
    Thread.Sleep(150);
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    Thread.Sleep(150);
    mouse_event(MOUSEEVENTF_MOVE, 0, 600, 0, 0);
    Thread.Sleep(100);
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
    Thread.Sleep(100);
    

    之后我们模拟Ctrl+C复制内容。

    SendKeys.SendWait("^{C}");
    

    但此时我们不能直接使用剪贴板获取信息,否则会造成消息队列堵塞。此时我们有两种解决方案:造一个文本框Ctrl+V,或者...
    当然要或者!Ctrl+V多么麻烦!
    我们将SendMessage声明。

    public extern static /*LRESULT*/ int SendMessage
    (
        int* hWnd, uint Msg, int* wParam, int* lParam
    );
    

    其中hWnd代表句柄,你可以想象成身份证号,Msg代表消息类型,wParamlParam是两个附加参数。
    获取承装数据文本框的句柄。

    int* hWnd = textBox_Get.Handle
    

    我们要在系统层面进行粘贴,这并不困难,只要告诉系统即可。

    const int WM_PASTE = 0x0302;
    

    这就是消息了,我们把它发送给系统。

    SendMessage(hWnd,WM_PASTE,0,0);
    

    该函数返回一个LRESULT类型的值,只要不为零就表示成功,现在我们得到了数据,其中成语使用一对中括号包裹。这是绝佳的条件,我们很快就能从中分割出成语。

    string result = "";
    result = textBox_Get.Text;
    result = result.Split('【')[1];
    result = result.Split('】')[0];
    

    然后进行搜索。

    string back = "";
    list.Remove(result);
    foreach (var item in list)
    {
    
        if (item.StartsWith(result[result.Length - 1]))
        {
            back = item;
            break;
        }
     }
    list.Remove(back);
    

    现在我们获得了结果变量back,因为QQ的设定,我们需要恢复必须选中人之后按空格,然而SendKey.SendWait会堵塞卡死。所以我们需要系统层面的模拟键盘操作。
    继续引入API。

    [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event
    (
        byte bVk, byte bScan, uint dwFlags, uint* dwExtraInfo
    );
    

    我们可以使用这个函数轻松的模拟按键,这里给出我们需要的按键(Enter,用于@小冰,Alt,用于发送信息)的值。

    const int VK_RETURN = 0x0D;
    const int VK_MENU = 0x12;
    

    同时为了粘贴信息到QQ,我们需要获取输入框的句柄,但那很麻烦。于是我们可以曲线救国,先在输入框点击一下(自动),然后获取QQ的句柄就可以了。
    想要得到窗口句柄,我们一般使用窗口名字来获得,于是我们又需要引入这样的系统API。

     [DllImport("User32.dll")]
     public extern static IntPtr FindWindowA
    (
        string? lpClassName, 
        string lpWindowName
    );
    

    lpClassName标记为可空是因为我懒得填(逃)也不用填。该函数返回指定句柄。
    现在我们可以轻松的完成下面的部分了。

     SetCursorPos(65, 984);
    Thread.Sleep(100);
    mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    Clipboard.SetText($"@小冰");
    Thread.Sleep(100);
    //或者你的群聊名称
    IntPtr QQhWnd = FindWindowA(null, "一个一个盒子的群聊");
    SendMessage(QQhWnd,WM_PASTE,new IntPtr(0), new IntPtr(0));
    Thread.Sleep(100);
    PressKey(Keys.Enter, false);
    Thread.Sleep(150);
    Clipboard.SetText(back);
    mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    Thread.Sleep(100);
    SendMessage(QQhWnd, WM_PASTE, new IntPtr(0), new IntPtr(0));
    Thread.Sleep(100);
    keybd_event(VK_ALT, 0, 0, new UIntPtr(0));                                     
    keybd_event(0x53,0,0, new UIntPtr(0));
    //松开键盘
    keybd_event(0x53, 0, KEYEVENTF_KEYUP, new UIntPtr(0));
    keybd_event(VK_ALT, 0, KEYEVENTF_KEYUP, new UIntPtr(0));
    result = "";
    back = "";
    //等待下一条
    Thread.Sleep(2500);
    

    测试一切成功,没有出现任何问题。


    自动发送

    相关文章

      网友评论

          本文标题:自 动 成 语 接 龙

          本文链接:https://www.haomeiwen.com/subject/ibrfnrtx.html